Compare commits

...

7 Commits

Author SHA1 Message Date
Max Koon
74f4da1d3d feat: rpc client 2025-11-29 00:57:32 -05:00
Max Koon
3ebb7ee796 feat: add @effect/rpc 2025-11-28 15:04:42 -05:00
Max Koon
02dd064d99 chore: remove hono 2025-11-26 12:10:28 -05:00
Max Koon
cbc220a968 refactor: api routes 2025-11-26 12:09:12 -05:00
Max Koon
ed3e6df4d2 feat: better auth api handler 2025-11-26 02:38:00 -05:00
Max Koon
371f5e879b feat: add effect api 2025-11-26 00:32:18 -05:00
Max Koon
2df7f2d924 feat: refresh transactions on table 2025-11-24 23:05:34 -05:00
28 changed files with 796 additions and 284 deletions

View File

@@ -7,10 +7,12 @@
"start": "tsx src/index.ts" "start": "tsx src/index.ts"
}, },
"dependencies": { "dependencies": {
"@hono/node-server": "^1.19.5", "@effect/platform": "^0.93.2",
"@effect/platform-node": "^0.101.1",
"@effect/rpc": "^0.72.2",
"@money/shared": "workspace:*", "@money/shared": "workspace:*",
"better-auth": "^1.3.27", "better-auth": "^1.3.27",
"hono": "^4.9.12", "effect": "^3.19.4",
"plaid": "^39.0.0", "plaid": "^39.0.0",
"tsx": "^4.20.6" "tsx": "^4.20.6"
}, },

View File

@@ -0,0 +1,23 @@
import { Effect, Layer } from "effect";
import { Authorization } from "./context";
import { auth } from "./config";
import { AuthorizationError, AuthorizationUnknownError } from "./errors";
export const make = () =>
Effect.gen(function* () {
return Authorization.of({
use: (fn) =>
Effect.gen(function* () {
const data = yield* Effect.tryPromise({
try: () => fn(auth),
catch: (error) =>
error instanceof Error
? new AuthorizationError({ message: error.message })
: new AuthorizationUnknownError(),
});
return data;
}),
});
});
export const BetterAuthLive = Layer.scoped(Authorization, make());

View File

@@ -3,7 +3,7 @@ import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { bearer, deviceAuthorization, genericOAuth } from "better-auth/plugins"; import { bearer, deviceAuthorization, genericOAuth } from "better-auth/plugins";
import { expo } from "@better-auth/expo"; import { expo } from "@better-auth/expo";
import { drizzleSchema } from "@money/shared/db"; import { drizzleSchema } from "@money/shared/db";
import { db } from "./db"; import { db } from "../db";
import { BASE_URL, HOST } from "@money/shared"; import { BASE_URL, HOST } from "@money/shared";
export const auth = betterAuth({ export const auth = betterAuth({

View File

@@ -0,0 +1,14 @@
import { Context, type Effect } from "effect";
import type { AuthorizationError, AuthorizationUnknownError } from "./errors";
import type { auth } from "./config";
export class Authorization extends Context.Tag("Authorization")<
Authorization,
AuthorizationImpl
>() {}
export interface AuthorizationImpl {
use: <T>(
fn: (client: typeof auth) => Promise<T>,
) => Effect.Effect<T, AuthorizationUnknownError | AuthorizationError, never>;
}

View File

@@ -0,0 +1,8 @@
import { Data } from "effect";
export class AuthorizationUnknownError extends Data.TaggedError(
"AuthClientUnknownError",
) {}
export class AuthorizationError extends Data.TaggedError("AuthorizationError")<{
message: string;
}> {}

View File

@@ -0,0 +1,70 @@
import {
HttpApi,
HttpApiBuilder,
HttpApiEndpoint,
HttpApiGroup,
HttpLayerRouter,
HttpServerRequest,
} from "@effect/platform";
import * as NodeHttpServerRequest from "@effect/platform-node/NodeHttpServerRequest";
import { toNodeHandler } from "better-auth/node";
import { Effect, Layer } from "effect";
import { AuthorizationError } from "./errors";
import { auth } from "./config";
const authHandler = Effect.gen(function* () {
const request = yield* HttpServerRequest.HttpServerRequest;
const nodeRequest = NodeHttpServerRequest.toIncomingMessage(request);
const nodeResponse = NodeHttpServerRequest.toServerResponse(request);
nodeResponse.setHeader("Access-Control-Allow-Origin", "http://laptop:8081");
nodeResponse.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS",
);
nodeResponse.setHeader(
"Access-Control-Allow-Headers",
"Content-Type, Authorization, B3, traceparent, Cookie",
);
nodeResponse.setHeader("Access-Control-Max-Age", "600");
nodeResponse.setHeader("Access-Control-Allow-Credentials", "true");
// Handle preflight requests
if (nodeRequest.method === "OPTIONS") {
nodeResponse.statusCode = 200;
nodeResponse.end();
return;
// return nodeResponse;
}
yield* Effect.tryPromise({
try: () => toNodeHandler(auth)(nodeRequest, nodeResponse),
catch: (error) => {
return new AuthorizationError({ message: `${error}` });
},
});
// return nodeResponse;
});
export class AuthContractGroup extends HttpApiGroup.make("auth")
.add(HttpApiEndpoint.get("get", "/*"))
.add(HttpApiEndpoint.post("post", "/*"))
.add(HttpApiEndpoint.options("options", "/*"))
.prefix("/api/auth") {}
export class DomainApi extends HttpApi.make("domain").add(AuthContractGroup) {}
export const Api = HttpApi.make("api").addHttpApi(DomainApi);
const AuthLive = HttpApiBuilder.group(Api, "auth", (handlers) =>
handlers
.handle("get", () => authHandler.pipe(Effect.orDie))
.handle("post", () => authHandler.pipe(Effect.orDie))
.handle("options", () => authHandler.pipe(Effect.orDie)),
);
export const AuthRoute = HttpLayerRouter.addHttpApi(Api).pipe(
Layer.provide(AuthLive),
);

View File

@@ -1,5 +0,0 @@
import type { AuthData } from "@money/shared/auth";
import { Hono } from "hono";
export const getHono = () =>
new Hono<{ Variables: { auth: AuthData | null } }>();

View File

@@ -1,58 +1,49 @@
import { serve } from "@hono/node-server"; import * as Layer from "effect/Layer";
import { authDataSchema } from "@money/shared/auth"; import * as Effect from "effect/Effect";
import * as HttpLayerRouter from "@effect/platform/HttpLayerRouter";
import * as HttpServerResponse from "@effect/platform/HttpServerResponse";
import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer";
import { createServer } from "http";
import { AuthRoute } from "./auth/handler";
import { BetterAuthLive } from "./auth/better-auth";
import { WebhookReceiverRoute } from "./webhook";
import { ZeroMutateRoute, ZeroQueryRoute } from "./zero/handler";
import { RpcRoute } from "./rpc/handler";
import { BASE_URL } from "@money/shared"; import { BASE_URL } from "@money/shared";
import { cors } from "hono/cors"; import { CurrentSession, SessionMiddleware } from "./middleware/session";
import { auth } from "./auth";
import { getHono } from "./hono";
import { zero } from "./zero";
import { webhook } from "./webhook";
const app = getHono(); const RootRoute = HttpLayerRouter.add(
"GET",
"/",
Effect.gen(function* () {
const d = yield* CurrentSession;
app.use( return HttpServerResponse.text("OK");
"/api/*",
cors({
origin: ["https://money.koon.us", `${BASE_URL}:8081`],
allowMethods: ["POST", "GET", "OPTIONS"],
allowHeaders: ["Content-Type", "Authorization"],
credentials: true,
}), }),
); );
app.on(["GET", "POST"], "/api/auth/*", (c) => auth.handler(c.req.raw)); const AllRoutes = Layer.mergeAll(
RootRoute,
app.use("*", async (c, next) => { AuthRoute,
const authHeader = c.req.raw.headers.get("Authorization"); ZeroQueryRoute,
const cookie = authHeader?.split("Bearer ")[1]; ZeroMutateRoute,
RpcRoute,
const newHeaders = new Headers(c.req.raw.headers); WebhookReceiverRoute,
).pipe(
if (cookie) { Layer.provide(SessionMiddleware.layer),
newHeaders.set("Cookie", cookie); Layer.provide(
} HttpLayerRouter.cors({
allowedOrigins: ["https://money.koon.us", `${BASE_URL}:8081`],
const session = await auth.api.getSession({ headers: newHeaders }); allowedMethods: ["POST", "GET", "OPTIONS"],
credentials: true,
if (!session) { }),
c.set("auth", null); ),
return next(); );
}
c.set("auth", authDataSchema.parse(session)); HttpLayerRouter.serve(AllRoutes).pipe(
return next(); Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),
}); Layer.provide(BetterAuthLive),
Layer.launch,
app.route("/api/zero", zero); NodeRuntime.runMain,
app.get("/api", (c) => c.text("OK"));
app.get("/api/webhook_receiver", webhook);
app.get("/", (c) => c.text("OK"));
serve(
{
fetch: app.fetch,
port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
},
(info) => {
console.log(`Server is running on ${info.address}:${info.port}`);
},
); );

View File

@@ -0,0 +1,12 @@
import { HttpLayerRouter, HttpMiddleware } from "@effect/platform";
import { BASE_URL } from "@money/shared";
export const CorsMiddleware = HttpLayerRouter.middleware(
HttpMiddleware.cors({
allowedOrigins: ["https://money.koon.us", `${BASE_URL}:8081`],
allowedMethods: ["POST", "GET", "OPTIONS"],
allowedHeaders: ["Content-Type", "Authorization"],
credentials: true,
}),
{ global: true },
);

View File

@@ -0,0 +1,61 @@
import { AuthSchema } from "@money/shared/auth";
import { Context, Effect, Schema, Console } from "effect";
import { Authorization } from "../auth/context";
import { HttpLayerRouter, HttpServerRequest } from "@effect/platform";
export class CurrentSession extends Context.Tag("CurrentSession")<
CurrentSession,
{ readonly auth: Schema.Schema.Type<typeof AuthSchema> | null }
>() {}
export const SessionMiddleware = HttpLayerRouter.middleware<{
provides: CurrentSession;
}>()(
Effect.gen(function* () {
const auth = yield* Authorization;
return (httpEffect) =>
Effect.gen(function* () {
const request = yield* HttpServerRequest.HttpServerRequest;
const headers = { ...request.headers };
const token = yield* HttpServerRequest.schemaHeaders(
Schema.Struct({
authorization: Schema.optional(Schema.String),
}),
).pipe(
Effect.tap(Console.debug),
Effect.flatMap(({ authorization }) =>
authorization != undefined
? parseAuthorization(authorization)
: Effect.succeed(undefined),
),
);
if (token) {
headers["cookie"] = token;
}
const session = yield* auth
.use((auth) => auth.api.getSession({ headers }))
.pipe(
Effect.flatMap((s) =>
s == null ? Effect.succeed(null) : Schema.decode(AuthSchema)(s),
),
// Effect.tap((s) => Console.debug("Auth result", s)),
);
return yield* Effect.provideService(httpEffect, CurrentSession, {
auth: session,
});
});
}),
);
const parseAuthorization = (input: string) =>
Effect.gen(function* () {
const m = /^Bearer\s+(.+)$/.exec(input);
if (!m) {
return yield* Effect.fail(new Error("Invalid token"));
}
return m[1];
});

View File

@@ -0,0 +1,76 @@
import { RpcSerialization, RpcServer } from "@effect/rpc";
import { Console, Effect, Layer, Schema } from "effect";
import { LinkRpcs, Link, AuthMiddleware } from "@money/shared/rpc";
import { CurrentSession } from "../middleware/session";
import { Authorization } from "../auth/context";
import { HttpServerRequest } from "@effect/platform";
import { AuthSchema } from "@money/shared/auth";
const parseAuthorization = (input: string) =>
Effect.gen(function* () {
const m = /^Bearer\s+(.+)$/.exec(input);
if (!m) {
return yield* Effect.fail(new Error("Invalid token"));
}
return m[1];
});
export const AuthLive = Layer.scoped(
AuthMiddleware,
Effect.gen(function* () {
const auth = yield* Authorization;
return AuthMiddleware.of(({ headers, payload, rpc }) =>
Effect.gen(function* () {
const newHeaders = { ...headers };
const token = yield* Schema.decodeUnknown(
Schema.Struct({
authorization: Schema.optional(Schema.String),
}),
)(headers).pipe(
// Effect.tap(Console.debug),
Effect.flatMap(({ authorization }) =>
authorization != undefined
? parseAuthorization(authorization)
: Effect.succeed(undefined),
),
);
if (token) {
newHeaders["cookie"] = token;
}
const session = yield* auth
.use((auth) => auth.api.getSession({ headers: newHeaders }))
.pipe(
Effect.flatMap((s) =>
s == null ? Effect.succeed(null) : Schema.decode(AuthSchema)(s),
),
Effect.tap((s) => Console.debug("Auth result", s)),
);
return { auth: session };
}).pipe(Effect.orDie),
);
}),
);
const LinkHandlers = LinkRpcs.toLayer({
CreateLink: () =>
Effect.gen(function* () {
const session = yield* CurrentSession;
return new Link({ href: session.auth?.user.name || "anon" });
}),
});
export const RpcRoute = RpcServer.layerHttpRouter({
group: LinkRpcs,
path: "/rpc",
protocol: "http",
}).pipe(
Layer.provide(LinkHandlers),
Layer.provide(RpcSerialization.layerJson),
Layer.provide(AuthLive),
);

View File

@@ -1,11 +1,21 @@
import type { Context } from "hono"; import {
HttpLayerRouter,
HttpServerRequest,
HttpServerResponse,
} from "@effect/platform";
import { Effect } from "effect";
import { plaidClient } from "./plaid"; import { plaidClient } from "./plaid";
// import { LinkSessionFinishedWebhook, WebhookType } from "plaid"; // import { LinkSessionFinishedWebhook, WebhookType } from "plaid";
export const webhook = async (c: Context) => { export const WebhookReceiverRoute = HttpLayerRouter.add(
console.log("Got webhook"); "*",
const b = await c.req.text(); "/api/webhook_receiver",
console.log("body:", b);
return c.text("Hi"); Effect.gen(function* () {
}; const request = yield* HttpServerRequest.HttpServerRequest;
const body = yield* request.json;
Effect.log("Got a webhook!", body);
return HttpServerResponse.text("HELLO THERE");
}),
);

View File

@@ -0,0 +1,6 @@
import { Data } from "effect";
export class ZeroUnknownError extends Data.TaggedError("ZeroUnknownError") {}
export class ZeroError extends Data.TaggedError("ZeroError")<{
error: Error;
}> {}

View File

@@ -0,0 +1,106 @@
import {
HttpLayerRouter,
HttpServerRequest,
HttpServerResponse,
Url,
} from "@effect/platform";
import { Console, Effect, Layer } from "effect";
import { CurrentSession, SessionMiddleware } from "../middleware/session";
import { ZeroError, ZeroUnknownError } from "./errors";
import { withValidation, type ReadonlyJSONValue } from "@rocicorp/zero";
import {
handleGetQueriesRequest,
PushProcessor,
ZQLDatabase,
} from "@rocicorp/zero/server";
import { BASE_URL, queries, schema } from "@money/shared";
import type { AuthSchemaType } from "@money/shared/auth";
import { PostgresJSConnection } from "@rocicorp/zero/pg";
import postgres from "postgres";
import { createMutators } from "./mutators";
const processor = new PushProcessor(
new ZQLDatabase(
new PostgresJSConnection(postgres(process.env.ZERO_UPSTREAM_DB! as string)),
schema,
),
);
export const ZeroQueryRoute = HttpLayerRouter.add(
"POST",
"/api/zero/get-queries",
Effect.gen(function* () {
const { auth } = yield* CurrentSession;
const request = yield* HttpServerRequest.HttpServerRequest;
const body = yield* request.json;
const result = yield* Effect.tryPromise({
try: () =>
handleGetQueriesRequest(
(name, args) => ({ query: getQuery(auth, name, args) }),
schema,
body as ReadonlyJSONValue,
),
catch: (error) =>
error instanceof Error
? new ZeroError({ error })
: new ZeroUnknownError(),
}).pipe(
Effect.tapErrorTag("ZeroError", (err) =>
Console.error("Zero Error", err.error),
),
);
return yield* HttpServerResponse.json(result);
}),
).pipe(Layer.provide(SessionMiddleware.layer));
function getQuery(
authData: AuthSchemaType | null,
name: string,
args: readonly ReadonlyJSONValue[],
) {
if (name in validatedQueries) {
const q = validatedQueries[name];
return q(authData, ...args);
}
throw new Error(`Unknown query: ${name}`);
}
export const validatedQueries = Object.fromEntries(
Object.values(queries).map((q) => [q.queryName, withValidation(q)]),
);
export const ZeroMutateRoute = HttpLayerRouter.add(
"POST",
"/api/zero/mutate",
Effect.gen(function* () {
const { auth } = yield* CurrentSession;
const request = yield* HttpServerRequest.HttpServerRequest;
const url = yield* Url.fromString(`${BASE_URL}${request.url}`);
const body = yield* request.json;
const result = yield* Effect.tryPromise({
try: () =>
processor.process(
createMutators(auth),
url.searchParams,
body as ReadonlyJSONValue,
),
catch: (error) =>
error instanceof Error
? new ZeroError({ error })
: new ZeroUnknownError(),
}).pipe(
Effect.tapErrorTag("ZeroError", (err) =>
Console.error("Zero Error", err.error),
),
);
return yield* HttpServerResponse.json(result);
}),
).pipe(Layer.provide(SessionMiddleware.layer));

View File

@@ -1,15 +1,3 @@
import {
type ReadonlyJSONValue,
type Transaction,
withValidation,
} from "@rocicorp/zero";
import {
handleGetQueriesRequest,
PushProcessor,
ZQLDatabase,
} from "@rocicorp/zero/server";
import { PostgresJSConnection } from "@rocicorp/zero/pg";
import postgres from "postgres";
import { import {
createMutators as createMutatorsShared, createMutators as createMutatorsShared,
isLoggedIn, isLoggedIn,
@@ -18,36 +6,27 @@ import {
type Mutators, type Mutators,
type Schema, type Schema,
} from "@money/shared"; } from "@money/shared";
import type { AuthData } from "@money/shared/auth"; import type { AuthSchemaType } from "@money/shared/auth";
import { getHono } from "./hono";
import { import {
Configuration, type ReadonlyJSONValue,
CountryCode, type Transaction,
PlaidApi, withValidation,
PlaidEnvironments, } from "@rocicorp/zero";
Products, import { plaidClient } from "../plaid";
} from "plaid"; import { CountryCode, Products } from "plaid";
import { randomUUID } from "crypto";
import { db } from "./db";
import { import {
balance, balance,
plaidAccessTokens, plaidAccessTokens,
plaidLink, plaidLink,
transaction, transaction,
} from "@money/shared/db"; } from "@money/shared/db";
import { db } from "../db";
import { randomUUID } from "crypto";
import { and, eq, inArray, sql, type InferInsertModel } from "drizzle-orm"; import { and, eq, inArray, sql, type InferInsertModel } from "drizzle-orm";
import { plaidClient } from "./plaid";
const processor = new PushProcessor(
new ZQLDatabase(
new PostgresJSConnection(postgres(process.env.ZERO_UPSTREAM_DB! as string)),
schema,
),
);
type Tx = Transaction<Schema>; type Tx = Transaction<Schema>;
const createMutators = (authData: AuthData | null) => { export const createMutators = (authData: AuthSchemaType | null) => {
const mutators = createMutatorsShared(authData); const mutators = createMutatorsShared(authData);
return { return {
...mutators, ...mutators,
@@ -221,41 +200,3 @@ const createMutators = (authData: AuthData | null) => {
}, },
} as const satisfies Mutators; } as const satisfies Mutators;
}; };
const zero = getHono()
.post("/mutate", async (c) => {
const authData = c.get("auth");
const result = await processor.process(createMutators(authData), c.req.raw);
return c.json(result);
})
.post("/get-queries", async (c) => {
const authData = c.get("auth");
const result = await handleGetQueriesRequest(
(name, args) => ({ query: getQuery(authData, name, args) }),
schema,
c.req.raw,
);
return c.json(result);
});
const validatedQueries = Object.fromEntries(
Object.values(queries).map((q) => [q.queryName, withValidation(q)]),
);
function getQuery(
authData: AuthData | null,
name: string,
args: readonly ReadonlyJSONValue[],
) {
if (name in validatedQueries) {
const q = validatedQueries[name];
return q(authData, ...args);
}
throw new Error(`Unknown query: ${name}`);
}
export { zero };

View File

@@ -4,7 +4,7 @@ import "react-native-reanimated";
import { authClient } from "@/lib/auth-client"; import { authClient } from "@/lib/auth-client";
import { ZeroProvider } from "@rocicorp/zero/react"; import { ZeroProvider } from "@rocicorp/zero/react";
import { useMemo } from "react"; import { useMemo } from "react";
import { authDataSchema } from "@money/shared/auth"; import { AuthSchema } from "@money/shared/auth";
import { Platform } from "react-native"; import { Platform } from "react-native";
import type { ZeroOptions } from "@rocicorp/zero"; import type { ZeroOptions } from "@rocicorp/zero";
import { import {
@@ -15,6 +15,7 @@ import {
BASE_URL, BASE_URL,
} from "@money/shared"; } from "@money/shared";
import { expoSQLiteStoreProvider } from "@rocicorp/zero/react-native"; import { expoSQLiteStoreProvider } from "@rocicorp/zero/react-native";
import { Schema as S } from "effect";
export const unstable_settings = { export const unstable_settings = {
anchor: "index", anchor: "index",
@@ -26,8 +27,8 @@ export default function RootLayout() {
const { data: session, isPending } = authClient.useSession(); const { data: session, isPending } = authClient.useSession();
const authData = useMemo(() => { const authData = useMemo(() => {
const result = authDataSchema.safeParse(session); const result = session ? S.decodeSync(AuthSchema)(session) : null;
return result.success ? result.data : null; return result ? result : null;
}, [session]); }, [session]);
const cookie = useMemo(() => { const cookie = useMemo(() => {

View File

@@ -15,6 +15,7 @@
}, },
"dependencies": { "dependencies": {
"@better-auth/expo": "^1.3.27", "@better-auth/expo": "^1.3.27",
"@effect-atom/atom-react": "^0.4.0",
"@expo/vector-icons": "^15.0.2", "@expo/vector-icons": "^15.0.2",
"@money/shared": "workspace:*", "@money/shared": "workspace:*",
"@money/ui": "workspace:*", "@money/ui": "workspace:*",

View File

@@ -11,9 +11,10 @@ import {
} from "effect"; } from "effect";
import { FileSystem } from "@effect/platform"; import { FileSystem } from "@effect/platform";
import { config } from "./config"; import { config } from "./config";
import { AuthState } from "./schema";
import { authClient } from "@/lib/auth-client"; import { authClient } from "@/lib/auth-client";
import type { BetterFetchResponse } from "@better-fetch/fetch"; import type { BetterFetchResponse } from "@better-fetch/fetch";
import { AuthSchema } from "@money/shared/auth";
import { encode } from "node:punycode";
class AuthClientUnknownError extends Data.TaggedError( class AuthClientUnknownError extends Data.TaggedError(
"AuthClientUnknownError", "AuthClientUnknownError",
@@ -129,7 +130,7 @@ const pollToken = ({ device_code }: { device_code: string }) =>
const getFromFromDisk = Effect.gen(function* () { const getFromFromDisk = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem; const fs = yield* FileSystem.FileSystem;
const content = yield* fs.readFileString(config.authPath); const content = yield* fs.readFileString(config.authPath);
const auth = yield* Schema.decode(Schema.parseJson(AuthState))(content); const auth = yield* Schema.decode(Schema.parseJson(AuthSchema))(content);
if (auth.session.expiresAt < new Date()) if (auth.session.expiresAt < new Date())
yield* Effect.fail(new AuthClientExpiredToken()); yield* Effect.fail(new AuthClientExpiredToken());
return auth; return auth;
@@ -160,10 +161,11 @@ const requestAuth = Effect.gen(function* () {
); );
if (sessionData == null) return yield* Effect.fail(new AuthClientNoData()); if (sessionData == null) return yield* Effect.fail(new AuthClientNoData());
const result = yield* Schema.decodeUnknown(AuthState)(sessionData); const result = yield* Schema.decodeUnknown(AuthSchema)(sessionData);
const encoded = yield* Schema.encode(AuthSchema)(result);
const fs = yield* FileSystem.FileSystem; const fs = yield* FileSystem.FileSystem;
yield* fs.writeFileString(config.authPath, JSON.stringify(result)); yield* fs.writeFileString(config.authPath, JSON.stringify(encoded));
return result; return result;
}); });

View File

@@ -5,13 +5,13 @@ import { ZeroProvider } from "@rocicorp/zero/react";
import { schema } from "@money/shared"; import { schema } from "@money/shared";
import { useState } from "react"; import { useState } from "react";
import { AuthClientLayer, getAuth } from "./auth"; import { AuthClientLayer, getAuth } from "./auth";
import { Effect } from "effect"; import { Effect, Redacted } from "effect";
import { BunContext } from "@effect/platform-bun"; import { BunContext } from "@effect/platform-bun";
import type { AuthData } from "./schema";
import { kvStore } from "./store"; import { kvStore } from "./store";
import { config } from "./config"; import { config } from "./config";
import { type AuthSchemaType } from "@money/shared/auth";
function Main({ auth }: { auth: AuthData }) { function Main({ auth }: { auth: AuthSchemaType }) {
const [route, setRoute] = useState<Route>("/"); const [route, setRoute] = useState<Route>("/");
useKeyboard((key) => { useKeyboard((key) => {
@@ -22,7 +22,7 @@ function Main({ auth }: { auth: AuthData }) {
<ZeroProvider <ZeroProvider
{...{ {...{
userID: auth.user.id, userID: auth.user.id,
auth: auth.session.token, auth: Redacted.value(auth.session.token),
server: config.zeroUrl, server: config.zeroUrl,
schema, schema,
kvStore, kvStore,

View File

@@ -1,34 +0,0 @@
import { Schema } from "effect";
const DateFromDateOrString = Schema.Union(
Schema.DateFromString,
Schema.DateFromSelf,
);
const SessionSchema = Schema.Struct({
expiresAt: DateFromDateOrString,
token: Schema.String,
createdAt: DateFromDateOrString,
updatedAt: DateFromDateOrString,
ipAddress: Schema.optional(Schema.NullishOr(Schema.String)),
userAgent: Schema.optional(Schema.NullishOr(Schema.String)),
userId: Schema.String,
id: Schema.String,
});
const UserSchema = Schema.Struct({
name: Schema.String,
email: Schema.String,
emailVerified: Schema.Boolean,
image: Schema.optional(Schema.NullishOr(Schema.String)),
createdAt: DateFromDateOrString,
updatedAt: DateFromDateOrString,
id: Schema.String,
});
export const AuthState = Schema.Struct({
session: SessionSchema,
user: UserSchema,
});
export type AuthData = typeof AuthState.Type;

View File

@@ -6,6 +6,7 @@
"exports": { "exports": {
".": "./src/index.ts", ".": "./src/index.ts",
"./auth": "./src/auth.ts", "./auth": "./src/auth.ts",
"./rpc": "./src/rpc.ts",
"./db": "./src/db/index.ts" "./db": "./src/db/index.ts"
}, },
"dependencies": { "dependencies": {

View File

@@ -1,25 +1,35 @@
import { z } from "zod"; import { z } from "zod";
import { Schema } from "effect";
export const sessionSchema = z.object({ const DateFromDateOrString = Schema.Union(
id: z.string(), Schema.DateFromString,
userId: z.string(), Schema.DateFromSelf,
expiresAt: z.date(), );
export const SessionSchema = Schema.Struct({
expiresAt: DateFromDateOrString,
token: Schema.Redacted(Schema.String),
createdAt: DateFromDateOrString,
updatedAt: DateFromDateOrString,
ipAddress: Schema.optional(Schema.NullishOr(Schema.String)),
userAgent: Schema.optional(Schema.NullishOr(Schema.String)),
userId: Schema.String,
id: Schema.String,
}); });
export const userSchema = z.object({ export const UserSchema = Schema.Struct({
id: z.string(), name: Schema.String,
email: z.string(), email: Schema.String,
emailVerified: z.boolean(), emailVerified: Schema.Boolean,
name: z.string(), image: Schema.optional(Schema.NullishOr(Schema.String)),
createdAt: z.date(), createdAt: DateFromDateOrString,
updatedAt: z.date(), updatedAt: DateFromDateOrString,
id: Schema.String,
}); });
export const authDataSchema = z.object({ export const AuthSchema = Schema.Struct({
session: sessionSchema, session: SessionSchema,
user: userSchema, user: UserSchema,
}); });
export type Session = z.infer<typeof sessionSchema>; export type AuthSchemaType = Schema.Schema.Type<typeof AuthSchema>;
export type User = z.infer<typeof userSchema>;
export type AuthData = z.infer<typeof authDataSchema>;

View File

@@ -0,0 +1,29 @@
import { Context, Schema } from "effect";
import { Rpc, RpcGroup, RpcMiddleware } from "@effect/rpc";
import type { AuthSchema } from "./auth";
export class Link extends Schema.Class<Link>("Link")({
href: Schema.String,
}) {}
export class CurrentSession extends Context.Tag("CurrentSession")<
CurrentSession,
{ readonly auth: Schema.Schema.Type<typeof AuthSchema> | null }
>() {}
export class AuthMiddleware extends RpcMiddleware.Tag<AuthMiddleware>()(
"AuthMiddleware",
{
// This middleware will provide the current user context
provides: CurrentSession,
// This middleware requires a client implementation too
requiredForClient: true,
},
) {}
export class LinkRpcs extends RpcGroup.make(
Rpc.make("CreateLink", {
success: Link,
error: Schema.String,
}),
).middleware(AuthMiddleware) {}

View File

@@ -3,7 +3,7 @@ import { Transactions } from "./transactions";
import { View, Text } from "react-native"; import { View, Text } from "react-native";
import { Settings } from "./settings"; import { Settings } from "./settings";
import { useKeyboard } from "./useKeyboard"; import { useKeyboard } from "./useKeyboard";
import type { AuthData } from "@money/shared/auth"; import type { AuthSchemaType } from "@money/shared/auth";
const PAGES = { const PAGES = {
"/": { "/": {
@@ -39,7 +39,7 @@ type Routes<T> = {
export type Route = Routes<typeof PAGES>; export type Route = Routes<typeof PAGES>;
interface RouterContextType { interface RouterContextType {
auth: AuthData | null; auth: AuthSchemaType | null;
route: Route; route: Route;
setRoute: (route: Route) => void; setRoute: (route: Route) => void;
} }
@@ -51,7 +51,7 @@ export const RouterContext = createContext<RouterContextType>({
}); });
type AppProps = { type AppProps = {
auth: AuthData | null; auth: AuthSchemaType | null;
route: Route; route: Route;
setRoute: (page: Route) => void; setRoute: (page: Route) => void;
}; };

50
packages/ui/src/rpc.ts Normal file
View File

@@ -0,0 +1,50 @@
import { AtomRpc } from "@effect-atom/atom-react";
import { AuthMiddleware, LinkRpcs } from "@money/shared/rpc";
import { FetchHttpClient, Headers } from "@effect/platform";
import { Rpc, RpcClient, RpcMiddleware, RpcSerialization } from "@effect/rpc";
import * as Layer from "effect/Layer";
import { Effect } from "effect";
import { use } from "react";
import { RouterContext } from "./index";
import * as Redacted from "effect/Redacted";
import { Platform } from "react-native";
const protocol = RpcClient.layerProtocolHttp({
url: "http://laptop:3000/rpc",
}).pipe(
Layer.provide([
RpcSerialization.layerJson,
FetchHttpClient.layer.pipe(
Layer.provide(
Layer.succeed(FetchHttpClient.RequestInit, {
credentials: "include",
}),
),
),
]),
);
export const useRpc = () => {
const { auth } = use(RouterContext);
return class Client extends AtomRpc.Tag<Client>()("RpcClient", {
group: LinkRpcs,
protocol: Layer.merge(
protocol,
RpcMiddleware.layerClient(AuthMiddleware, ({ request }) =>
Effect.succeed({
...request,
...(auth && Platform.OS == ("TUI" as any)
? {
headers: Headers.set(
request.headers,
"authorization",
"Bearer " + Redacted.value(auth.session.token),
),
}
: {}),
}),
),
),
}) {};
};

View File

@@ -7,6 +7,9 @@ import { useKeyboard } from "../useKeyboard";
import { Button } from "../../components/Button"; import { Button } from "../../components/Button";
import * as Table from "../../components/Table"; import * as Table from "../../components/Table";
import * as Dialog from "../../components/Dialog"; import * as Dialog from "../../components/Dialog";
import { useAtomSet } from "@effect-atom/atom-react";
import { useRpc } from "../rpc";
import * as Exit from "effect/Exit";
const COLUMNS: Table.Column[] = [ const COLUMNS: Table.Column[] = [
{ name: "name", label: "Name" }, { name: "name", label: "Name" },
@@ -116,52 +119,79 @@ export function Accounts() {
} }
function AddAccount() { function AddAccount() {
const { auth } = use(RouterContext); const rpc = useRpc();
const [link, details] = useQuery(queries.getPlaidLink(auth)); const createLink = useAtomSet(rpc.mutation("CreateLink"), {
mode: "promiseExit",
});
const [href, setHref] = useState("");
// const [link, details] = useQuery(queries.getPlaidLink(auth));
const { close } = use(Dialog.Context); const { close } = use(Dialog.Context);
const openLink = () => { const init = () => {
if (!link) return; console.log("INIT");
Linking.openURL(link.link); const p = createLink({ payload: void 0 })
.then((link) => {
console.log("my link", link);
if (Exit.isSuccess(link)) {
setHref(link.value.href);
}
})
.finally(() => {
console.log("WHAT");
});
console.log(p);
}; };
const z = useZero<Schema, Mutators>();
useEffect(() => { useEffect(() => {
console.log(link, details); console.log("useEffect");
if (details.type != "complete") return; init();
if (link != undefined) { }, []);
if (!link.completeAt) {
const timer = setInterval(() => { // const openLink = () => {
console.log("Checking for link"); // if (!link) return;
z.mutate.link.get({ link_token: link.token }); // Linking.openURL(link.link);
}, 1000 * 5); // };
return () => clearInterval(timer);
} else { // const z = useZero<Schema, Mutators>();
if (close) close();
return; // useEffect(() => {
} // console.log(link, details);
} // if (details.type != "complete") return;
console.log("Creating new link"); // if (link != undefined) {
z.mutate.link.create(); // if (!link.completeAt) {
}, [link, details]); // const timer = setInterval(() => {
// console.log("Checking for link");
// z.mutate.link.get({ link_token: link.token });
// }, 1000 * 5);
// return () => clearInterval(timer);
// } else {
// if (close) close();
// return;
// }
// }
// console.log("Creating new link");
// z.mutate.link.create();
// }, [link, details]);
return ( return (
<> <>
<Text>Href: {href}</Text>
<Button onPress={() => close && close()}>close</Button> <Button onPress={() => close && close()}>close</Button>
{link ? ( {/* {link ? ( */}
<> {/* <> */}
<Text style={{ fontFamily: "mono" }}> {/* <Text style={{ fontFamily: "mono" }}> */}
Please click the button to complete setup. {/* Please click the button to complete setup. */}
</Text> {/* </Text> */}
{/**/}
<Button shortcut="return" onPress={openLink}> {/* <Button shortcut="return" onPress={openLink}> */}
Open Plaid {/* Open Plaid */}
</Button> {/* </Button> */}
</> {/* </> */}
) : ( {/* ) : ( */}
<Text style={{ fontFamily: "mono" }}>Loading Plaid Link</Text> {/* <Text style={{ fontFamily: "mono" }}>Loading Plaid Link</Text> */}
)} {/* )} */}
</> </>
); );
} }

View File

@@ -1,6 +1,11 @@
import * as Table from "../components/Table"; import * as Table from "../components/Table";
import { useQuery } from "@rocicorp/zero/react"; import { useQuery, useZero } from "@rocicorp/zero/react";
import { queries, type Transaction } from "@money/shared"; import {
queries,
type Mutators,
type Schema,
type Transaction,
} from "@money/shared";
import { use } from "react"; import { use } from "react";
import { View, Text } from "react-native"; import { View, Text } from "react-native";
import { RouterContext } from "."; import { RouterContext } from ".";
@@ -29,8 +34,18 @@ export function Transactions() {
const { auth } = use(RouterContext); const { auth } = use(RouterContext);
const [items] = useQuery(queries.allTransactions(auth)); const [items] = useQuery(queries.allTransactions(auth));
const z = useZero<Schema, Mutators>();
return ( return (
<Table.Provider data={items} columns={COLUMNS}> <Table.Provider
data={items}
columns={COLUMNS}
onKey={(key) => {
if (key.name == "r" && key.shift) {
z.mutate.link.updateTransactions();
}
}}
>
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<View style={{ flexShrink: 0 }}> <View style={{ flexShrink: 0 }}>
<Table.Body /> <Table.Body />

180
pnpm-lock.yaml generated
View File

@@ -10,18 +10,24 @@ importers:
apps/api: apps/api:
dependencies: dependencies:
'@hono/node-server': '@effect/platform':
specifier: ^1.19.5 specifier: ^0.93.2
version: 1.19.6(hono@4.10.4) version: 0.93.2(effect@3.19.4)
'@effect/platform-node':
specifier: ^0.101.1
version: 0.101.1(@effect/cluster@0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)
'@effect/rpc':
specifier: ^0.72.2
version: 0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
'@money/shared': '@money/shared':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/shared version: link:../../packages/shared
better-auth: better-auth:
specifier: ^1.3.27 specifier: ^1.3.27
version: 1.3.34(react-dom@19.1.0(react@19.1.0))(react@19.1.0) version: 1.3.34(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
hono: effect:
specifier: ^4.9.12 specifier: ^3.19.4
version: 4.10.4 version: 3.19.4
plaid: plaid:
specifier: ^39.0.0 specifier: ^39.0.0
version: 39.1.0 version: 39.1.0
@@ -38,6 +44,9 @@ importers:
'@better-auth/expo': '@better-auth/expo':
specifier: ^1.3.27 specifier: ^1.3.27
version: 1.3.34(better-auth@1.3.34(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(expo-constants@18.0.10)(expo-crypto@15.0.7(expo@54.0.23))(expo-linking@8.0.8)(expo-secure-store@15.0.7(expo@54.0.23))(expo-web-browser@15.0.9(expo@54.0.23)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))) version: 1.3.34(better-auth@1.3.34(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(expo-constants@18.0.10)(expo-crypto@15.0.7(expo@54.0.23))(expo-linking@8.0.8)(expo-secure-store@15.0.7(expo@54.0.23))(expo-web-browser@15.0.9(expo@54.0.23)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)))
'@effect-atom/atom-react':
specifier: ^0.4.0
version: 0.4.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)(react@19.1.0)(scheduler@0.26.0)
'@expo/vector-icons': '@expo/vector-icons':
specifier: ^15.0.2 specifier: ^15.0.2
version: 15.0.3(expo-font@14.0.9(expo@54.0.23)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) version: 15.0.3(expo-font@14.0.9(expo@54.0.23)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
@@ -167,7 +176,7 @@ importers:
version: 0.93.2(effect@3.19.4) version: 0.93.2(effect@3.19.4)
'@effect/platform-bun': '@effect/platform-bun':
specifier: ^0.83.0 specifier: ^0.83.0
version: 0.83.0(@effect/cluster@0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4) version: 0.83.0(@effect/cluster@0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)
'@money/shared': '@money/shared':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/shared version: link:../../packages/shared
@@ -798,6 +807,21 @@ packages:
peerDependencies: peerDependencies:
'@noble/ciphers': ^1.0.0 '@noble/ciphers': ^1.0.0
'@effect-atom/atom-react@0.4.0':
resolution: {integrity: sha512-5HpKLgXEG8EWr4sBDl7BZjm6koO/5HSb94C9+OkRLDE4mhH2357vNl4uPNqid0ZNGwVvS6bAvKFmBzc0bZU6yg==}
peerDependencies:
effect: ^3.19
react: '>=18 <20'
scheduler: '*'
'@effect-atom/atom@0.4.3':
resolution: {integrity: sha512-0XOngJ+oDuJW7/Hgt09Kl8QunF1bGlEtV/K9hMB1MmQPUGb+ZtxfJwZkBeXjMPEL1Lgm04TDBlqB8+qgHz+y0w==}
peerDependencies:
'@effect/experimental': ^0.57.0
'@effect/platform': ^0.93.0
'@effect/rpc': ^0.72.1
effect: ^3.19.0
'@effect/cluster@0.52.10': '@effect/cluster@0.52.10':
resolution: {integrity: sha512-csmU+4h2MXdxsFKF5eY4N52LDcjdpQp//QivOKNL9yNySUBVz/UrBr1FRgvbfHk+sxY03SNcoTNgkcbUaIp2Pg==} resolution: {integrity: sha512-csmU+4h2MXdxsFKF5eY4N52LDcjdpQp//QivOKNL9yNySUBVz/UrBr1FRgvbfHk+sxY03SNcoTNgkcbUaIp2Pg==}
peerDependencies: peerDependencies:
@@ -838,16 +862,34 @@ packages:
'@effect/sql': ^0.48.0 '@effect/sql': ^0.48.0
effect: ^3.19.0 effect: ^3.19.0
'@effect/platform-node-shared@0.54.0':
resolution: {integrity: sha512-prTgG3CXqmrxB4Rg6utfwCTqjlGwjAEvK7R4g3HzVdFpfFRum+FQBpGHUcjyz7EejkDtBY2MWJC3Wr1QKDPjPw==}
peerDependencies:
'@effect/cluster': ^0.53.0
'@effect/platform': ^0.93.3
'@effect/rpc': ^0.72.2
'@effect/sql': ^0.48.0
effect: ^3.19.5
'@effect/platform-node@0.101.1':
resolution: {integrity: sha512-uShujtpWU0VbdhRKhoo6tXzTG1xT0bnj8u5Q1BHpanwKPmzOhf4n0XLlMl5PaihH5Cp7xHuQlwgZlqHzhqSHzw==}
peerDependencies:
'@effect/cluster': ^0.53.4
'@effect/platform': ^0.93.3
'@effect/rpc': ^0.72.2
'@effect/sql': ^0.48.0
effect: ^3.19.6
'@effect/platform@0.93.2': '@effect/platform@0.93.2':
resolution: {integrity: sha512-IFWF2xuz37tZbyEsf3hwBlcYYqbqJho+ZM871CG92lWJSjcTgvmjCy77qnV0QhTWVdh9BMs12QKzQCMlqz4cJQ==} resolution: {integrity: sha512-IFWF2xuz37tZbyEsf3hwBlcYYqbqJho+ZM871CG92lWJSjcTgvmjCy77qnV0QhTWVdh9BMs12QKzQCMlqz4cJQ==}
peerDependencies: peerDependencies:
effect: ^3.19.3 effect: ^3.19.3
'@effect/rpc@0.72.1': '@effect/rpc@0.72.2':
resolution: {integrity: sha512-crpiAxDvFxM/fGhLuAgB1V8JOtfCm8/6ZdOP4MIdkz14I/ff3LdLJpf8hHJpYIbwYXypglAeAaHpfuZOt5f+SA==} resolution: {integrity: sha512-BmTXybXCOq96D2r9mvSW/YdiTQs5CStnd4II+lfVKrMr3pMNERKLZ2LG37Tfm4Sy3Q8ire6IVVKO/CN+VR0uQQ==}
peerDependencies: peerDependencies:
'@effect/platform': ^0.93.0 '@effect/platform': ^0.93.3
effect: ^3.19.0 effect: ^3.19.5
'@effect/sql@0.48.0': '@effect/sql@0.48.0':
resolution: {integrity: sha512-tubdizHriDwzHUnER9UsZ/0TtF6O2WJckzeYDbVSRPeMkrpdpyEzEsoKctechTm65B3Bxy6JIixGPg2FszY72A==} resolution: {integrity: sha512-tubdizHriDwzHUnER9UsZ/0TtF6O2WJckzeYDbVSRPeMkrpdpyEzEsoKctechTm65B3Bxy6JIixGPg2FszY72A==}
@@ -1534,12 +1576,6 @@ packages:
'@hexagon/base64@1.1.28': '@hexagon/base64@1.1.28':
resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==} resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==}
'@hono/node-server@1.19.6':
resolution: {integrity: sha512-Shz/KjlIeAhfiuE93NDKVdZ7HdBVLQAfdbaXEaoAVO3ic9ibRSLGIQGkcBbFyuLr+7/1D5ZCINM8B+6IvXeMtw==}
engines: {node: '>=18.14.1'}
peerDependencies:
hono: ^4
'@humanfs/core@0.19.1': '@humanfs/core@0.19.1':
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
engines: {node: '>=18.18.0'} engines: {node: '>=18.18.0'}
@@ -4867,10 +4903,6 @@ packages:
hoist-non-react-statics@3.3.2: hoist-non-react-statics@3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
hono@4.10.4:
resolution: {integrity: sha512-YG/fo7zlU3KwrBL5vDpWKisLYiM+nVstBQqfr7gCPbSYURnNEP9BDxEMz8KfsDR9JX0lJWDRNc6nXX31v7ZEyg==}
engines: {node: '>=16.9.0'}
hosted-git-info@7.0.2: hosted-git-info@7.0.2:
resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==}
engines: {node: ^16.14.0 || >=18.0.0} engines: {node: ^16.14.0 || >=18.0.0}
@@ -6888,6 +6920,10 @@ packages:
resolution: {integrity: sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==} resolution: {integrity: sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==}
engines: {node: '>=18.17'} engines: {node: '>=18.17'}
undici@7.16.0:
resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==}
engines: {node: '>=20.18.1'}
unicode-canonical-property-names-ecmascript@2.0.1: unicode-canonical-property-names-ecmascript@2.0.1:
resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==}
engines: {node: '>=4'} engines: {node: '>=4'}
@@ -7895,12 +7931,30 @@ snapshots:
dependencies: dependencies:
'@noble/ciphers': 1.3.0 '@noble/ciphers': 1.3.0
'@effect/cluster@0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)': '@effect-atom/atom-react@0.4.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)(react@19.1.0)(scheduler@0.26.0)':
dependencies:
'@effect-atom/atom': 0.4.3(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)
effect: 3.19.4
react: 19.1.0
scheduler: 0.26.0
transitivePeerDependencies:
- '@effect/experimental'
- '@effect/platform'
- '@effect/rpc'
'@effect-atom/atom@0.4.3(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)':
dependencies:
'@effect/experimental': 0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
'@effect/platform': 0.93.2(effect@3.19.4)
'@effect/rpc': 0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
effect: 3.19.4
'@effect/cluster@0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)':
dependencies: dependencies:
'@effect/platform': 0.93.2(effect@3.19.4) '@effect/platform': 0.93.2(effect@3.19.4)
'@effect/rpc': 0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4) '@effect/rpc': 0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
'@effect/sql': 0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4) '@effect/sql': 0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
'@effect/workflow': 0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4) '@effect/workflow': 0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)
effect: 3.19.4 effect: 3.19.4
'@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)': '@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)':
@@ -7909,12 +7963,12 @@ snapshots:
effect: 3.19.4 effect: 3.19.4
uuid: 11.1.0 uuid: 11.1.0
'@effect/platform-bun@0.83.0(@effect/cluster@0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)': '@effect/platform-bun@0.83.0(@effect/cluster@0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)':
dependencies: dependencies:
'@effect/cluster': 0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4) '@effect/cluster': 0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)
'@effect/platform': 0.93.2(effect@3.19.4) '@effect/platform': 0.93.2(effect@3.19.4)
'@effect/platform-node-shared': 0.53.0(@effect/cluster@0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4) '@effect/platform-node-shared': 0.53.0(@effect/cluster@0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)
'@effect/rpc': 0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4) '@effect/rpc': 0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
'@effect/sql': 0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4) '@effect/sql': 0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
effect: 3.19.4 effect: 3.19.4
multipasta: 0.2.7 multipasta: 0.2.7
@@ -7922,11 +7976,11 @@ snapshots:
- bufferutil - bufferutil
- utf-8-validate - utf-8-validate
'@effect/platform-node-shared@0.53.0(@effect/cluster@0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)': '@effect/platform-node-shared@0.53.0(@effect/cluster@0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)':
dependencies: dependencies:
'@effect/cluster': 0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4) '@effect/cluster': 0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)
'@effect/platform': 0.93.2(effect@3.19.4) '@effect/platform': 0.93.2(effect@3.19.4)
'@effect/rpc': 0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4) '@effect/rpc': 0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
'@effect/sql': 0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4) '@effect/sql': 0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
'@parcel/watcher': 2.5.1 '@parcel/watcher': 2.5.1
effect: 3.19.4 effect: 3.19.4
@@ -7936,6 +7990,35 @@ snapshots:
- bufferutil - bufferutil
- utf-8-validate - utf-8-validate
'@effect/platform-node-shared@0.54.0(@effect/cluster@0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)':
dependencies:
'@effect/cluster': 0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)
'@effect/platform': 0.93.2(effect@3.19.4)
'@effect/rpc': 0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
'@effect/sql': 0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
'@parcel/watcher': 2.5.1
effect: 3.19.4
multipasta: 0.2.7
ws: 8.18.3
transitivePeerDependencies:
- bufferutil
- utf-8-validate
'@effect/platform-node@0.101.1(@effect/cluster@0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)':
dependencies:
'@effect/cluster': 0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)
'@effect/platform': 0.93.2(effect@3.19.4)
'@effect/platform-node-shared': 0.54.0(@effect/cluster@0.52.10(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/sql@0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)
'@effect/rpc': 0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
'@effect/sql': 0.48.0(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
effect: 3.19.4
mime: 3.0.0
undici: 7.16.0
ws: 8.18.3
transitivePeerDependencies:
- bufferutil
- utf-8-validate
'@effect/platform@0.93.2(effect@3.19.4)': '@effect/platform@0.93.2(effect@3.19.4)':
dependencies: dependencies:
effect: 3.19.4 effect: 3.19.4
@@ -7943,7 +8026,7 @@ snapshots:
msgpackr: 1.11.5 msgpackr: 1.11.5
multipasta: 0.2.7 multipasta: 0.2.7
'@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)': '@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)':
dependencies: dependencies:
'@effect/platform': 0.93.2(effect@3.19.4) '@effect/platform': 0.93.2(effect@3.19.4)
effect: 3.19.4 effect: 3.19.4
@@ -7956,11 +8039,11 @@ snapshots:
effect: 3.19.4 effect: 3.19.4
uuid: 11.1.0 uuid: 11.1.0
'@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)': '@effect/workflow@0.12.5(@effect/experimental@0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(@effect/platform@0.93.2(effect@3.19.4))(@effect/rpc@0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4))(effect@3.19.4)':
dependencies: dependencies:
'@effect/experimental': 0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4) '@effect/experimental': 0.57.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
'@effect/platform': 0.93.2(effect@3.19.4) '@effect/platform': 0.93.2(effect@3.19.4)
'@effect/rpc': 0.72.1(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4) '@effect/rpc': 0.72.2(@effect/platform@0.93.2(effect@3.19.4))(effect@3.19.4)
effect: 3.19.4 effect: 3.19.4
'@egjs/hammerjs@2.0.17': '@egjs/hammerjs@2.0.17':
@@ -8414,6 +8497,7 @@ snapshots:
- graphql - graphql
- supports-color - supports-color
- utf-8-validate - utf-8-validate
optional: true
'@expo/code-signing-certificates@0.0.5': '@expo/code-signing-certificates@0.0.5':
dependencies: dependencies:
@@ -8480,6 +8564,7 @@ snapshots:
optionalDependencies: optionalDependencies:
react: 19.2.0 react: 19.2.0
react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0)
optional: true
'@expo/env@2.0.7': '@expo/env@2.0.7':
dependencies: dependencies:
@@ -8558,7 +8643,7 @@ snapshots:
postcss: 8.4.49 postcss: 8.4.49
resolve-from: 5.0.0 resolve-from: 5.0.0
optionalDependencies: optionalDependencies:
expo: 54.0.23(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) expo: 54.0.23(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- supports-color - supports-color
@@ -8637,7 +8722,7 @@ snapshots:
'@expo/json-file': 10.0.7 '@expo/json-file': 10.0.7
'@react-native/normalize-colors': 0.81.5 '@react-native/normalize-colors': 0.81.5
debug: 4.4.3 debug: 4.4.3
expo: 54.0.23(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) expo: 54.0.23(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
resolve-from: 5.0.0 resolve-from: 5.0.0
semver: 7.7.3 semver: 7.7.3
xml2js: 0.6.0 xml2js: 0.6.0
@@ -8665,6 +8750,7 @@ snapshots:
expo-font: 14.0.9(expo@54.0.23)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) expo-font: 14.0.9(expo@54.0.23)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)
react: 19.2.0 react: 19.2.0
react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0)
optional: true
'@expo/ws-tunnel@1.0.6': {} '@expo/ws-tunnel@1.0.6': {}
@@ -8736,10 +8822,6 @@ snapshots:
'@hexagon/base64@1.1.28': {} '@hexagon/base64@1.1.28': {}
'@hono/node-server@1.19.6(hono@4.10.4)':
dependencies:
hono: 4.10.4
'@humanfs/core@0.19.1': {} '@humanfs/core@0.19.1': {}
'@humanfs/node@0.16.7': '@humanfs/node@0.16.7':
@@ -10671,6 +10753,7 @@ snapshots:
react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0)
optionalDependencies: optionalDependencies:
'@types/react': 19.1.17 '@types/react': 19.1.17
optional: true
'@react-navigation/bottom-tabs@7.8.4(@react-navigation/native@7.1.19(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': '@react-navigation/bottom-tabs@7.8.4(@react-navigation/native@7.1.19(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)':
dependencies: dependencies:
@@ -11536,7 +11619,7 @@ snapshots:
resolve-from: 5.0.0 resolve-from: 5.0.0
optionalDependencies: optionalDependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
expo: 54.0.23(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) expo: 54.0.23(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@babel/core' - '@babel/core'
- supports-color - supports-color
@@ -12537,6 +12620,7 @@ snapshots:
react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
optional: true
expo-constants@18.0.10(expo@54.0.23)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)): expo-constants@18.0.10(expo@54.0.23)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)):
dependencies: dependencies:
@@ -12555,6 +12639,7 @@ snapshots:
react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
optional: true
expo-crypto@15.0.7(expo@54.0.23): expo-crypto@15.0.7(expo@54.0.23):
dependencies: dependencies:
@@ -12570,6 +12655,7 @@ snapshots:
dependencies: dependencies:
expo: 54.0.23(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) expo: 54.0.23(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)
react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0)
optional: true
expo-font@14.0.9(expo@54.0.23)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): expo-font@14.0.9(expo@54.0.23)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
dependencies: dependencies:
@@ -12584,6 +12670,7 @@ snapshots:
fontfaceobserver: 2.3.0 fontfaceobserver: 2.3.0
react: 19.2.0 react: 19.2.0
react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0)
optional: true
expo-haptics@15.0.7(expo@54.0.23): expo-haptics@15.0.7(expo@54.0.23):
dependencies: dependencies:
@@ -12606,6 +12693,7 @@ snapshots:
dependencies: dependencies:
expo: 54.0.23(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) expo: 54.0.23(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)
react: 19.2.0 react: 19.2.0
optional: true
expo-linking@8.0.8(expo@54.0.23)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): expo-linking@8.0.8(expo@54.0.23)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
dependencies: dependencies:
@@ -12647,6 +12735,7 @@ snapshots:
invariant: 2.2.4 invariant: 2.2.4
react: 19.2.0 react: 19.2.0
react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) react-native: 0.82.1(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0)
optional: true
expo-router@6.0.14(@expo/metro-runtime@6.1.2)(@types/react@19.1.17)(expo-constants@18.0.10)(expo-linking@8.0.8)(expo@54.0.23)(react-dom@19.1.0(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.3(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): expo-router@6.0.14(@expo/metro-runtime@6.1.2)(@types/react@19.1.17)(expo-constants@18.0.10)(expo-linking@8.0.8)(expo@54.0.23)(react-dom@19.1.0(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.3(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
dependencies: dependencies:
@@ -12862,6 +12951,7 @@ snapshots:
- graphql - graphql
- supports-color - supports-color
- utf-8-validate - utf-8-validate
optional: true
exponential-backoff@3.1.3: {} exponential-backoff@3.1.3: {}
@@ -13214,8 +13304,6 @@ snapshots:
dependencies: dependencies:
react-is: 16.13.1 react-is: 16.13.1
hono@4.10.4: {}
hosted-git-info@7.0.2: hosted-git-info@7.0.2:
dependencies: dependencies:
lru-cache: 10.4.3 lru-cache: 10.4.3
@@ -14991,6 +15079,7 @@ snapshots:
- bufferutil - bufferutil
- supports-color - supports-color
- utf-8-validate - utf-8-validate
optional: true
react-reconciler@0.32.0(react@19.1.0): react-reconciler@0.32.0(react@19.1.0):
dependencies: dependencies:
@@ -15058,7 +15147,8 @@ snapshots:
react@19.1.0: {} react@19.1.0: {}
react@19.2.0: {} react@19.2.0:
optional: true
readable-stream@3.6.2: readable-stream@3.6.2:
dependencies: dependencies:
@@ -15764,6 +15854,8 @@ snapshots:
undici@6.22.0: {} undici@6.22.0: {}
undici@7.16.0: {}
unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-canonical-property-names-ecmascript@2.0.1: {}
unicode-match-property-ecmascript@2.0.0: unicode-match-property-ecmascript@2.0.0: