diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..526e3ff --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +.convex/ +.env +*.local diff --git a/README.md b/README.md index a6dd180..d8ab920 100644 --- a/README.md +++ b/README.md @@ -1 +1,48 @@ -# obv-hackaton \ No newline at end of file +# obv-hackaton + +Shared Child Journal — AI dictation-powered agent companion monorepo. + +## deploy/thin-path branch — deployment-verification vehicle + +> **This branch is a deployment-verification vehicle, not a product stack.** It carries +> the minimal thin-path Convex functions used to prove deployment + synthetic write→read +> on the existing dev deployment. Candidates own their own stacks; the winning candidate +> replaces this branch's `convex/` + `package.json` at integration. + +Deployed to the **existing** Convex dev deployment `reliable-panther-823` +(https://reliable-panther-823.convex.cloud — created by Gil; the stored +`CONVEX_DEPLOY_KEY` scopes to it; no project was created, no login performed). + +- Evidence: [`deploy/thin-path-evidence.md`](deploy/thin-path-evidence.md) +- Deployed code commit: `94e706c` +- Contract: Shared Child Journal — Effect v4 Schema Contract v0.1 (`art_I2TCG08V`), `effect@4.0.0-rc.115` + +Functions (all evidence is **cloud dev + synthetic CLI data** — not live-LLM, not native +device, not browser UI): + +| Function | Type | Contract behavior | +|---|---|---| +| `children:create` | mutation | Non-empty name, optional `birthDate` (unix ms) | +| `entries:createEntry` | mutation | Raw transcript always preserved; events decoded through the canonical Effect schema; idempotent on `captureId` retry (original wins); invalid events return `captured_with_event_errors` — capture is never blocked | +| `timeline:list` | query | Per-child, chronological | + +## Commands + +```sh +npm install +npm run contract:smoke # local Effect contract decode/reject/encode checks +npm run deploy:thin-path # CONVEX_DEPLOY_KEY=... npx convex deploy +CONVEX_DEPLOY_KEY=... npx convex run children:create '{"name":"Ada (synthetic)"}' +CONVEX_DEPLOY_KEY=... npx convex run timeline:list '{"childId":""}' +``` + +## Deployment-verified findings for other candidates + +1. **Convex rejects stored fields starting with `_`** (reserved for system fields) — the + contract's wire-level `_tag` literal must be stripped on write and re-wrapped on read; + it cannot be persisted as the contract's mapping table assumed. See Finding F1 in + [`deploy/thin-path-evidence.md`](deploy/thin-path-evidence.md). +2. **`import { Schema } from 'effect/Schema'` does not resolve on `effect@4.0.0-rc.115`** — + the subpath exports members directly; use `import * as Schema from 'effect/Schema'`. +3. `convex codegen` / `convex deploy` typecheck against bindings generated from the + current schema — regenerate before judging type errors after a schema change. diff --git a/convex/_generated/api.d.ts b/convex/_generated/api.d.ts new file mode 100644 index 0000000..cdba2ac --- /dev/null +++ b/convex/_generated/api.d.ts @@ -0,0 +1,57 @@ +/* eslint-disable */ +/** + * Generated `api` utility. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * To regenerate, run `npx convex dev`. + * @module + */ + +import type * as children from "../children.js"; +import type * as contract from "../contract.js"; +import type * as entries from "../entries.js"; +import type * as timeline from "../timeline.js"; +import type * as wire from "../wire.js"; + +import type { + ApiFromModules, + FilterApi, + FunctionReference, +} from "convex/server"; + +declare const fullApi: ApiFromModules<{ + children: typeof children; + contract: typeof contract; + entries: typeof entries; + timeline: typeof timeline; + wire: typeof wire; +}>; + +/** + * A utility for referencing Convex functions in your app's public API. + * + * Usage: + * ```js + * const myFunctionReference = api.myModule.myFunction; + * ``` + */ +export declare const api: FilterApi< + typeof fullApi, + FunctionReference +>; + +/** + * A utility for referencing Convex functions in your app's internal API. + * + * Usage: + * ```js + * const myFunctionReference = internal.myModule.myFunction; + * ``` + */ +export declare const internal: FilterApi< + typeof fullApi, + FunctionReference +>; + +export declare const components: {}; diff --git a/convex/_generated/api.js b/convex/_generated/api.js new file mode 100644 index 0000000..44bf985 --- /dev/null +++ b/convex/_generated/api.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +/** + * Generated `api` utility. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * To regenerate, run `npx convex dev`. + * @module + */ + +import { anyApi, componentsGeneric } from "convex/server"; + +/** + * A utility for referencing Convex functions in your app's API. + * + * Usage: + * ```js + * const myFunctionReference = api.myModule.myFunction; + * ``` + */ +export const api = anyApi; +export const internal = anyApi; +export const components = componentsGeneric(); diff --git a/convex/_generated/dataModel.d.ts b/convex/_generated/dataModel.d.ts new file mode 100644 index 0000000..f97fd19 --- /dev/null +++ b/convex/_generated/dataModel.d.ts @@ -0,0 +1,60 @@ +/* eslint-disable */ +/** + * Generated data model types. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * To regenerate, run `npx convex dev`. + * @module + */ + +import type { + DataModelFromSchemaDefinition, + DocumentByName, + TableNamesInDataModel, + SystemTableNames, +} from "convex/server"; +import type { GenericId } from "convex/values"; +import schema from "../schema.js"; + +/** + * The names of all of your Convex tables. + */ +export type TableNames = TableNamesInDataModel; + +/** + * The type of a document stored in Convex. + * + * @typeParam TableName - A string literal type of the table name (like "users"). + */ +export type Doc = DocumentByName< + DataModel, + TableName +>; + +/** + * An identifier for a document in Convex. + * + * Convex documents are uniquely identified by their `Id`, which is accessible + * on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids). + * + * Documents can be loaded using `db.get(tableName, id)` in query and mutation functions. + * + * IDs are just strings at runtime, but this type can be used to distinguish them from other + * strings when type checking. + * + * @typeParam TableName - A string literal type of the table name (like "users"). + */ +export type Id = + GenericId; + +/** + * A type describing your Convex data model. + * + * This type includes information about what tables you have, the type of + * documents stored in those tables, and the indexes defined on them. + * + * This type is used to parameterize methods like `queryGeneric` and + * `mutationGeneric` to make them type-safe. + */ +export type DataModel = DataModelFromSchemaDefinition; diff --git a/convex/_generated/server.d.ts b/convex/_generated/server.d.ts new file mode 100644 index 0000000..f235db4 --- /dev/null +++ b/convex/_generated/server.d.ts @@ -0,0 +1,162 @@ +/* eslint-disable */ +/** + * Generated utilities for implementing server-side Convex query and mutation functions. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * To regenerate, run `npx convex dev`. + * @module + */ + +import { + ActionBuilder, + HttpActionBuilder, + MutationBuilder, + QueryBuilder, + GenericActionCtx, + GenericMutationCtx, + GenericQueryCtx, + GenericDatabaseReader, + GenericDatabaseWriter, +} from "convex/server"; +import type { DataModel } from "./dataModel.js"; + +/** + * Typesafe environment variables. + * + * This includes platform-provided env vars and any variables declared in + * `convex.config.ts`. + */ +type Env = { + readonly CONVEX_CLOUD_URL: string; + readonly CONVEX_SITE_URL: string; +}; + +/** + * Define a query in this Convex app's public API. + * + * This function will be allowed to read your Convex database and will be accessible from the client. + * + * @param func - The query function. It receives a {@link QueryCtx} as its first argument. + * @returns The wrapped query. Include this as an `export` to name it and make it accessible. + */ +export declare const query: QueryBuilder; + +/** + * Define a query that is only accessible from other Convex functions (but not from the client). + * + * This function will be allowed to read from your Convex database. It will not be accessible from the client. + * + * @param func - The query function. It receives a {@link QueryCtx} as its first argument. + * @returns The wrapped query. Include this as an `export` to name it and make it accessible. + */ +export declare const internalQuery: QueryBuilder; + +/** + * Define a mutation in this Convex app's public API. + * + * This function will be allowed to modify your Convex database and will be accessible from the client. + * + * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. + * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. + */ +export declare const mutation: MutationBuilder; + +/** + * Define a mutation that is only accessible from other Convex functions (but not from the client). + * + * This function will be allowed to modify your Convex database. It will not be accessible from the client. + * + * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. + * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. + */ +export declare const internalMutation: MutationBuilder; + +/** + * Define an action in this Convex app's public API. + * + * An action is a function which can execute any JavaScript code, including non-deterministic + * code and code with side-effects, like calling third-party services. + * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive. + * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}. + * + * @param func - The action. It receives an {@link ActionCtx} as its first argument. + * @returns The wrapped action. Include this as an `export` to name it and make it accessible. + */ +export declare const action: ActionBuilder; + +/** + * Define an action that is only accessible from other Convex functions (but not from the client). + * + * @param func - The function. It receives an {@link ActionCtx} as its first argument. + * @returns The wrapped function. Include this as an `export` to name it and make it accessible. + */ +export declare const internalAction: ActionBuilder; + +/** + * Define an HTTP action. + * + * The wrapped function will be used to respond to HTTP requests received + * by a Convex deployment if the requests matches the path and method where + * this action is routed. Be sure to route your httpAction in `convex/http.js`. + * + * @param func - The function. It receives an {@link ActionCtx} as its first argument + * and a Fetch API `Request` object as its second. + * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up. + */ +export declare const httpAction: HttpActionBuilder; + +/** + * Typesafe environment variables. + * + * This includes platform-provided env vars and any variables declared in + * `convex.config.ts`. + */ +export declare const env: Env; + +/** + * A set of services for use within Convex query functions. + * + * The query context is passed as the first argument to any Convex query + * function run on the server. + * + * This differs from the {@link MutationCtx} because all of the services are + * read-only. + */ +export type QueryCtx = GenericQueryCtx; + +/** + * A set of services for use within Convex mutation functions. + * + * The mutation context is passed as the first argument to any Convex mutation + * function run on the server. + */ +export type MutationCtx = GenericMutationCtx; + +/** + * A set of services for use within Convex action functions. + * + * The action context is passed as the first argument to any Convex action + * function run on the server. + */ +export type ActionCtx = GenericActionCtx; + +/** + * An interface to read from the database within Convex query functions. + * + * The two entry points are {@link DatabaseReader.get}, which fetches a single + * document by its {@link Id}, or {@link DatabaseReader.query}, which starts + * building a query. + */ +export type DatabaseReader = GenericDatabaseReader; + +/** + * An interface to read from and write to the database within Convex mutation + * functions. + * + * Convex guarantees that all writes within a single mutation are + * executed atomically, so you never have to worry about partial writes leaving + * your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control) + * for the guarantees Convex provides your functions. + */ +export type DatabaseWriter = GenericDatabaseWriter; diff --git a/convex/_generated/server.js b/convex/_generated/server.js new file mode 100644 index 0000000..2dbe0db --- /dev/null +++ b/convex/_generated/server.js @@ -0,0 +1,101 @@ +/* eslint-disable */ +/** + * Generated utilities for implementing server-side Convex query and mutation functions. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * To regenerate, run `npx convex dev`. + * @module + */ + +import { + actionGeneric, + httpActionGeneric, + queryGeneric, + mutationGeneric, + internalActionGeneric, + internalMutationGeneric, + internalQueryGeneric, +} from "convex/server"; + +/** + * Define a query in this Convex app's public API. + * + * This function will be allowed to read your Convex database and will be accessible from the client. + * + * @param func - The query function. It receives a {@link QueryCtx} as its first argument. + * @returns The wrapped query. Include this as an `export` to name it and make it accessible. + */ +export const query = queryGeneric; + +/** + * Define a query that is only accessible from other Convex functions (but not from the client). + * + * This function will be allowed to read from your Convex database. It will not be accessible from the client. + * + * @param func - The query function. It receives a {@link QueryCtx} as its first argument. + * @returns The wrapped query. Include this as an `export` to name it and make it accessible. + */ +export const internalQuery = internalQueryGeneric; + +/** + * Define a mutation in this Convex app's public API. + * + * This function will be allowed to modify your Convex database and will be accessible from the client. + * + * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. + * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. + */ +export const mutation = mutationGeneric; + +/** + * Define a mutation that is only accessible from other Convex functions (but not from the client). + * + * This function will be allowed to modify your Convex database. It will not be accessible from the client. + * + * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. + * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. + */ +export const internalMutation = internalMutationGeneric; + +/** + * Define an action in this Convex app's public API. + * + * An action is a function which can execute any JavaScript code, including non-deterministic + * code and code with side-effects, like calling third-party services. + * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive. + * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}. + * + * @param func - The action. It receives an {@link ActionCtx} as its first argument. + * @returns The wrapped action. Include this as an `export` to name it and make it accessible. + */ +export const action = actionGeneric; + +/** + * Define an action that is only accessible from other Convex functions (but not from the client). + * + * @param func - The function. It receives an {@link ActionCtx} as its first argument. + * @returns The wrapped function. Include this as an `export` to name it and make it accessible. + */ +export const internalAction = internalActionGeneric; + +/** + * Define an HTTP action. + * + * The wrapped function will be used to respond to HTTP requests received + * by a Convex deployment if the requests matches the path and method where + * this action is routed. Be sure to route your httpAction in `convex/http.js`. + * + * @param func - The function. It receives an {@link ActionCtx} as its first argument + * and a Fetch API `Request` object as its second. + * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up. + */ +export const httpAction = httpActionGeneric; + +/** + * Typesafe environment variables. + * + * This includes platform-provided env vars and any variables declared in + * `convex.config.ts`. + */ +export const env = process.env; diff --git a/convex/children.ts b/convex/children.ts new file mode 100644 index 0000000..b45ad44 --- /dev/null +++ b/convex/children.ts @@ -0,0 +1,26 @@ +import { ConvexError, v } from 'convex/values' +import { mutation } from './_generated/server' + +export const create = mutation({ + args: { + name: v.string(), + // Unix ms on the wire. + birthDate: v.optional(v.number()), + }, + handler: async (ctx, args) => { + const name = args.name.trim() + if (name.length === 0) { + throw new ConvexError({ + code: 'INVALID_NAME', + message: 'name must be a non-empty string', + }) + } + const createdAt = Date.now() + const child = + args.birthDate === undefined + ? { name, createdAt } + : { name, birthDate: args.birthDate, createdAt } + const childId = await ctx.db.insert('children', child) + return { status: 'created' as const, childId, name } + }, +}) diff --git a/convex/contract.ts b/convex/contract.ts new file mode 100644 index 0000000..7df116d --- /dev/null +++ b/convex/contract.ts @@ -0,0 +1,52 @@ +/** + * Canonical domain contract — "Shared Child Journal — Effect v4 Schema Contract + * (v0.1)" (project artifact art_I2TCG08V). Shapes mirror the spike-verified + * `spikes/effect-compat/src/schema.ts` (effect@4.0.0-rc.115). + * + * This module is the single source of truth for the thin path: decode incoming + * extraction output through these schemas; never hand-roll a second domain + * model. This branch is a deployment-verification vehicle — the winning + * candidate's shared domain package replaces it at integration. + */ +// effect@4.0.0-rc.115 exports the Schema members directly — namespace import. +import * as Schema from 'effect/Schema' + +export const EVENT_CATEGORIES = [ + 'potty', + 'meal', + 'sleep', + 'mood', + 'milestone', + 'school', +] as const + +export const Quantity = Schema.Struct({ + value: Schema.Number, + unit: Schema.optionalKey(Schema.String), +}) + +export const Event = Schema.TaggedStruct('Event', { + category: Schema.Literals([...EVENT_CATEGORIES]), + // Unix-ms number on the wire; Date in domain code. + occurredAt: Schema.DateFromMillis, + quantity: Schema.optional(Quantity), + // 1 = caregiver-confirmed; lower = raw extraction guess. + confidence: Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: 1 })), + authorId: Schema.NonEmptyString, + // Non-empty when present; explicit null is NOT in the contract. + note: Schema.optionalKey(Schema.NonEmptyString), +}) + +export const Entry = Schema.TaggedStruct('Entry', { + transcript: Schema.NonEmptyString, + authorId: Schema.NonEmptyString, + // Unix-ms number on the wire; Date in domain code. + createdAt: Schema.DateFromMillis, + status: Schema.Literals(['draft', 'published']), + // May be empty — extraction failure never blocks capture. + events: Schema.Array(Event), +}) + +export type Quantity = Schema.Schema.Type +export type Event = Schema.Schema.Type +export type Entry = Schema.Schema.Type diff --git a/convex/entries.ts b/convex/entries.ts new file mode 100644 index 0000000..1069bf3 --- /dev/null +++ b/convex/entries.ts @@ -0,0 +1,122 @@ +import { ConvexError, v } from 'convex/values' +import * as Schema from 'effect/Schema' +import { Entry, Event } from './contract' +import { mutation } from './_generated/server' + +/** + * Extraction output arrives unvalidated on purpose: the canonical Effect schema + * (contract.ts) is the validation authority, and a failed extraction must never + * block the capture — the raw transcript is preserved either way. + * + * The adapter wraps before decode: `_tag` is injected when the caller omitted + * it, and undefined-valued keys are dropped (explicit null/undefined is not in + * the contract; encode strips undefined optionals). + */ +function withEventTag(raw: unknown): unknown { + if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return raw + const cleaned: Record = {} + for (const [key, value] of Object.entries(raw as Record)) { + if (value !== undefined) cleaned[key] = value + } + if (cleaned._tag === undefined) cleaned._tag = 'Event' + return cleaned +} + +function errorText(err: unknown): string { + return err instanceof Error ? err.message : String(err) +} + +export const createEntry = mutation({ + args: { + childId: v.id('children'), + // Idempotency key: a retried capture must not double-write. + captureId: v.string(), + transcript: v.string(), + authorId: v.string(), + status: v.optional(v.union(v.literal('draft'), v.literal('published'))), + events: v.optional(v.array(v.any())), + }, + handler: async (ctx, args) => { + if (args.captureId.length === 0) { + throw new ConvexError({ + code: 'INVALID_CAPTURE_ID', + message: 'captureId must be a non-empty string', + }) + } + if (args.transcript.length === 0) { + throw new ConvexError({ + code: 'EMPTY_TRANSCRIPT', + message: 'transcript must be non-empty — there is no raw input to preserve', + }) + } + if (args.authorId.length === 0) { + throw new ConvexError({ + code: 'INVALID_AUTHOR_ID', + message: 'authorId must be a non-empty string', + }) + } + const status = args.status ?? 'draft' + + // Idempotency: the original capture wins. A retry with the same captureId + // returns the existing entry without writing, even if the payload changed. + const existing = await ctx.db + .query('entries') + .withIndex('by_capture_id', (q) => q.eq('captureId', args.captureId)) + .first() + if (existing !== null) { + return { + status: 'idempotent_hit' as const, + captureId: args.captureId, + entryId: existing._id, + } + } + + const domainEvents: Event[] = [] + const eventErrors: { index: number; error: string }[] = [] + for (const [index, raw] of (args.events ?? []).entries()) { + try { + domainEvents.push(Schema.decodeUnknownSync(Event)(withEventTag(raw))) + } catch (err) { + eventErrors.push({ index, error: errorText(err) }) + } + } + + // Assemble the domain entry and encode to wire form: unix-ms times, + // undefined optionals stripped. The Convex schema validator (entryValidator + // via schema.ts) guards the write itself. + const domainEntry: Entry = { + _tag: 'Entry', + transcript: args.transcript, + authorId: args.authorId, + createdAt: new Date(), + status, + events: domainEvents, + } + const wire = Schema.encodeSync(Entry)(domainEntry) + // Storage boundary: strip the wire-level `_tag` discriminators — Convex + // rejects `_`-prefixed stored fields. timeline:list re-wraps them on read. + const { _tag: _entryTag, ...entryWire } = wire + const events = entryWire.events.map(({ _tag: _eventTag, ...eventWire }) => eventWire) + const entryId = await ctx.db.insert('entries', { + ...entryWire, + events, + childId: args.childId, + captureId: args.captureId, + }) + + if (eventErrors.length > 0) { + return { + status: 'captured_with_event_errors' as const, + entryId, + rawPreserved: true as const, + eventErrors, + } + } + return { + status: 'created' as const, + entryId, + captureId: args.captureId, + eventCount: domainEvents.length, + } + }, +}) diff --git a/convex/schema.ts b/convex/schema.ts new file mode 100644 index 0000000..1ad433b --- /dev/null +++ b/convex/schema.ts @@ -0,0 +1,20 @@ +import { defineSchema, defineTable } from 'convex/server' +import { v } from 'convex/values' +import { entryValidator } from './wire' + +export default defineSchema({ + children: defineTable({ + name: v.string(), + birthDate: v.optional(v.number()), + createdAt: v.number(), + }), + // Storage superset of the Entry contract: childId + captureId are thin-path + // storage keys (per-child timeline + idempotent retry), not contract fields. + entries: defineTable({ + ...entryValidator.fields, + childId: v.id('children'), + captureId: v.string(), + }) + .index('by_capture_id', ['captureId']) + .index('by_child_createdAt', ['childId', 'createdAt']), +}) diff --git a/convex/timeline.ts b/convex/timeline.ts new file mode 100644 index 0000000..528f8de --- /dev/null +++ b/convex/timeline.ts @@ -0,0 +1,24 @@ +import { query } from './_generated/server' +import { v } from 'convex/values' + +export const list = query({ + args: { + childId: v.id('children'), + }, + handler: async (ctx, args) => { + // Chronological order (oldest first) — a journal reads forward in time. + const rows = await ctx.db + .query('entries') + .withIndex('by_child_createdAt', (q) => q.eq('childId', args.childId)) + .order('asc') + .collect() + // Read boundary: re-wrap the wire-level `_tag` discriminators that storage + // strips (Convex reserves `_`-prefixed fields), so returned objects decode + // through the canonical Entry/Event schemas unchanged. + return rows.map((row) => ({ + _tag: 'Entry' as const, + ...row, + events: row.events.map((event) => ({ _tag: 'Event' as const, ...event })), + })) + }, +}) diff --git a/convex/wire.ts b/convex/wire.ts new file mode 100644 index 0000000..ed73e76 --- /dev/null +++ b/convex/wire.ts @@ -0,0 +1,37 @@ +/** + * Convex validators — the one-directional mapping from the canonical Effect + * schema (contract.ts) to storage, per the contract's "Convex mapping" section. + * These enforce wire shape; range constraints (confidence ∈ [0,1]) and other + * refinements are enforced by the Effect schema at the mutation boundary. + * + * Storage deviation from the contract's mapping table (deployment-verified): + * Convex rejects stored fields starting with `_` (reserved for system fields), + * so the wire-level `_tag` discriminator is stripped on write and re-wrapped on + * read (see entries.ts / timeline.ts). Table + validator identity replaces the + * tag at rest; the wire format is unchanged in both directions. + */ +import { v } from 'convex/values' + +export const eventValidator = v.object({ + category: v.union( + v.literal('potty'), + v.literal('meal'), + v.literal('sleep'), + v.literal('mood'), + v.literal('milestone'), + v.literal('school'), + ), + occurredAt: v.number(), + quantity: v.optional(v.object({ value: v.number(), unit: v.optional(v.string()) })), + confidence: v.number(), + authorId: v.string(), + note: v.optional(v.string()), +}) + +export const entryValidator = v.object({ + transcript: v.string(), + authorId: v.string(), + createdAt: v.number(), + status: v.union(v.literal('draft'), v.literal('published')), + events: v.array(eventValidator), +}) diff --git a/deploy/contract-smoke.ts b/deploy/contract-smoke.ts new file mode 100644 index 0000000..2757dbf --- /dev/null +++ b/deploy/contract-smoke.ts @@ -0,0 +1,69 @@ +/** + * Local smoke test for the canonical contract — run with `npm run contract:smoke`. + * Verifies the Effect v4 API surface the thin path depends on BEFORE deploying: + * decode acceptance/rejection, refinement bounds, and encode wire rules + * (unix-ms times, undefined optionals stripped). + */ +import * as Schema from 'effect/Schema' +import { Entry, Event } from '../convex/contract' + +const show = (label: string, value: unknown) => console.log(`${label}: ${JSON.stringify(value)}`) + +const wireEvent = { + _tag: 'Event', + category: 'potty', + occurredAt: 1726500000000, + quantity: { value: 2 }, + confidence: 1, + authorId: 'gil_test_operator', + note: 'morning, before preschool', +} + +const decoded = Schema.decodeUnknownSync(Event)(wireEvent) +show('decode ok — encoded back to wire', Schema.encodeSync(Event)(decoded)) + +// Absent optionals stay absent on the wire after a decode/encode round trip. +show( + 'absent optionals stripped', + Schema.encodeSync( + Event, + )( + Schema.decodeUnknownSync(Event)({ + _tag: 'Event', + category: 'meal', + occurredAt: 1726500100000, + confidence: 0.5, + authorId: 'gil_test_operator', + }), + ), +) + +// Rejections: category outside the six literals, confidence out of [0,1], +// explicit null (not in the contract), occurredAt not unix-ms. +const rejections: [string, unknown][] = [ + ['category "nap" rejected', { ...wireEvent, category: 'nap' }], + ['confidence 1.5 rejected', { ...wireEvent, confidence: 1.5 }], + ['note null rejected', { ...wireEvent, note: null }], + ['occurredAt string rejected', { ...wireEvent, occurredAt: '2026-09-17' }], +] +for (const [label, bad] of rejections) { + try { + Schema.decodeUnknownSync(Event)(bad) + console.log(`${label}: UNEXPECTED PASS`) + } catch (err) { + const message = err instanceof Error ? err.message : String(err) + console.log(`${label}: ${message.split('\n').slice(0, 3).join(' | ')}`) + } +} + +// Entry contract: empty events allowed (extraction failure never blocks +// capture); round trip through the canonical Entry schema. +const wireEntry = { + _tag: 'Entry', + transcript: 'raw dictated text stays verbatim', + authorId: 'gil_test_operator', + createdAt: 1726500200000, + status: 'draft', + events: [], +} +show('entry wire round-trip', Schema.encodeSync(Entry)(Schema.decodeUnknownSync(Entry)(wireEntry))) diff --git a/deploy/thin-path-evidence.md b/deploy/thin-path-evidence.md new file mode 100644 index 0000000..5880eb6 --- /dev/null +++ b/deploy/thin-path-evidence.md @@ -0,0 +1,235 @@ +# Thin-Path Deployment Evidence — dev deployment `reliable-panther-823` + +> **What this is:** CLOUD DEV deployment evidence, produced with synthetic data via the +> Convex CLI (`npx convex run`). This is **not** live-LLM extraction, **not** a native +> device capture, and **not** browser app UI evidence. + +- **Deployment:** `reliable-panther-823` (Convex dev deployment, created by Gil) — https://reliable-panther-823.convex.cloud +- **Repo / branch:** `OCPdev25/obv-hackaton` @ `deploy/thin-path` (cut from `origin/master @ 49224e0`) +- **Deployed code commit:** `94e706c` — `fix(convex): strip wire _tag discriminators at the storage boundary` +- **Contract source:** Shared Child Journal — Effect v4 Schema Contract v0.1 (project artifact `art_I2TCG08V`) +- **Pins:** `convex@1.46.0`, `effect@4.0.0-rc.115` +- **All command transcripts below are redacted** — the deploy key never appears in any output (verified by scan). + +## Functions deployed (names + types) + +| Function | Type | Purpose | +|---|---|---| +| `children:create` | mutation | Create a child (non-empty name, optional `birthDate` unix-ms) | +| `entries:createEntry` | mutation | Capture entry: raw transcript always preserved; extraction events decoded through the canonical Effect schema before storage; **idempotent on `captureId` retry** (original capture wins; retried payload changes are absorbed); validation failures return `captured_with_event_errors` and never block the capture | +| `timeline:list` | query | Per-child entries, chronological (oldest first) | + +Storage: `entries` indexed by `captureId` (idempotency) and `[childId, createdAt]` (timeline). +Wire format per contract: unix-ms times, six category literals, confidence in [0,1], +explicit null rejected, empty `events` allowed. + +## Finding F1 — contract mapping correction (deployment-verified) + +The published contract's "Convex mapping" table maps `_tag` to `v.literal('Event')` for +storage. A real insert proves Convex rejects underscore-prefixed stored fields (reserved +for system fields like `_id`): + +``` +Uncaught Error: Document(value: {_tag: "Entry", ...}) isn't a valid document: +Field '_tag' starts with an underscore, which is only allowed for system fields like '_id' +``` + +(first deploy attempt, commit `960e9eb`; all three synthetic mutations failed identically +and rolled back — Convex mutations are atomic, so no partial state) + +**Resolution:** `_tag` stays on the wire in both directions (createEntry args/events in, +timeline output out) and is stripped after `Schema.encodeSync(Entry)` before insert; +`timeline:list` re-wraps stored rows on read so returned objects decode through the +canonical schemas unchanged. Table + validator identity replaces the tag at rest. +The spike that authored the mapping table never executed a real insert — candidates +should fold this correction into their storage adapters. + +## 0. Deployment-scope verification (before any write) + +`CONVEX_DEPLOY_KEY= npx convex env list`: + +``` +No environment variables set (on dev deployment reliable-panther-823) +``` + +Key scopes to the expected dev deployment — matches the verified fact; no mismatch. STOP +condition (different deployment / auth error) did not occur. + +## 1. Deploy transcript + +`CONVEX_DEPLOY_KEY= npx convex deploy`: + +``` +▌ Deploying code to deployment: +▌ [Development] ocpdev25:ocean-finder:dev/ocpdev25 (dev) (dashboard: https://dashboard.convex.dev/t/ocpdev25/ocean-finder/reliable-panther-823) +▌ └─ https://reliable-panther-823.convex.cloud +- Deploying to https://reliable-panther-823.convex.cloud... + +Convex AI files are not installed. Run npx convex ai-files install to get started or npx convex ai-files disable to hide this message. +✔ No indexes are deleted by this push +Uploading functions to Convex... +Generating TypeScript bindings... +Running TypeScript... +Pushing code to your Convex deployment... +Schema validation complete. +Finalizing push... +✔ Deployed Convex functions to https://reliable-panther-823.convex.cloud +``` + +## 2. Pre-deploy local contract smoke test (`npm run contract:smoke`, effect@4.0.0-rc.115) + +``` +decode ok — encoded back to wire: {"_tag":"Event","category":"potty","occurredAt":1726500000000,"quantity":{"value":2},"confidence":1,"authorId":"gil_test_operator","note":"morning, before preschool"} +absent optionals stripped: {"_tag":"Event","category":"meal","occurredAt":1726500100000,"confidence":0.5,"authorId":"gil_test_operator"} +category "nap" rejected: Expected "potty" | "meal" | "sleep" | "mood" | "milestone" | "school" | at ["category"] +confidence 1.5 rejected: Expected a value between 0 and 1 | at ["confidence"] +note null rejected: Expected string | at ["note"] +occurredAt string rejected: Expected number | at ["occurredAt"] +entry wire round-trip: {"_tag":"Entry","transcript":"raw dictated text stays verbatim","authorId":"gil_test_operator","createdAt":1726500200000,"status":"draft","events":[]} +``` + +## 3. Synthetic write→read (all data synthetic; operator id `gil_test_operator`) + +### 3.1 Child A create + +``` +{ "childId": "j977jhkzj2gmv9q2ap82p5cpw58eky9e", "name": "Ada Test (synthetic)", "status": "created" } +``` + +### 3.2 First submit — `cap-001-synthetic` (published, 1 potty event) + +``` +{ "captureId": "cap-001-synthetic", "entryId": "j579t1x2pxcx6vdfn0gn41k4498ej10p", "eventCount": 1, "status": "created" } +``` + +### 3.3 Duplicate submit — same `captureId`, changed transcript and status (retry must be absorbed) + +``` +{ "captureId": "cap-001-synthetic", "entryId": "j579t1x2pxcx6vdfn0gn41k4498ej10p", "status": "idempotent_hit" } +``` + +Same `entryId` as 3.2 — the original capture wins; no second row, no second event. + +### 3.4 Second entry — `cap-002-synthetic` (draft, 1 sleep event) + +``` +{ "captureId": "cap-002-synthetic", "entryId": "j578jzdkq72pgdghynd2ksncps8ekfp8", "eventCount": 1, "status": "created" } +``` + +### 3.5 Timeline for child A — **2 entries, not 3** (duplicate submit once → single event) + +``` +[ + { + "_creationTime": 1789667121425.508, + "_id": "j579t1x2pxcx6vdfn0gn41k4498ej10p", + "_tag": "Entry", + "authorId": "gil_test_operator", + "captureId": "cap-001-synthetic", + "childId": "j977jhkzj2gmv9q2ap82p5cpw58eky9e", + "createdAt": 1789667121425, + "events": [ + { + "_tag": "Event", + "authorId": "gil_test_operator", + "category": "potty", + "confidence": 1, + "note": "morning, before preschool", + "occurredAt": 1789633800000, + "quantity": { "value": 2 } + } + ], + "status": "published", + "transcript": "Ada used the potty twice this morning before preschool!" + }, + { + "_creationTime": 1789667123975.888, + "_id": "j578jzdkq72pgdghynd2ksncps8ekfp8", + "_tag": "Entry", + "authorId": "gil_test_operator", + "captureId": "cap-002-synthetic", + "childId": "j977jhkzj2gmv9q2ap82p5cpw58eky9e", + "createdAt": 1789667123975, + "events": [ + { + "_tag": "Event", + "authorId": "gil_test_operator", + "category": "sleep", + "confidence": 0.8, + "note": "night sleep", + "occurredAt": 1789628700000 + } + ], + "status": "draft", + "transcript": "Slept through the night, woke up happy at 7." + } +] +``` + +Note the read path: `_tag` discriminators re-wrapped, unix-ms wire times, absent +optionals absent (not null), transcripts verbatim — decodes through the canonical +schemas unchanged. + +### 3.6 Child B create + validation-failure case (two contract-invalid events: `category: "nap"`, `confidence: 1.5`) + +``` +{ "childId": "j97e27cg5j26b1s72ks4r023qd8ekej9", "name": "Ben Test (synthetic)", "status": "created" } +``` + +``` +{ + "entryId": "j57c9pwsws87h6h9vt6bkzh4258ej5st", + "eventErrors": [ + { + "error": "Expected \"potty\" | \"meal\" | \"sleep\" | \"mood\" | \"milestone\" | \"school\"\n at [\"category\"]", + "index": 0 + }, + { + "error": "Expected a value between 0 and 1\n at [\"confidence\"]", + "index": 1 + } + ], + "rawPreserved": true, + "status": "captured_with_event_errors" +} +``` + +Error returned per event with schema-precise messages; the capture itself was not blocked. + +### 3.7 Timeline for child B — raw preserved, zero events + +``` +[ + { + "_creationTime": 1789667153524.5132, + "_id": "j57c9pwsws87h6h9vt6bkzh4258ej5st", + "_tag": "Entry", + "authorId": "gil_test_operator", + "captureId": "cap-invalid-003", + "childId": "j97e27cg5j26b1s72ks4r023qd8ekej9", + "createdAt": 1789667153524, + "events": [], + "status": "draft", + "transcript": "Nap happened at 1pm, she was out for two hours." + } +] +``` + +## 4. Deviation from the contract text: import style + +The contract shows `import { Schema } from 'effect/Schema'`. On `effect@4.0.0-rc.115` +the `effect/Schema` subpath exports its members directly (no named `Schema` export — +verified via node ESM/CJS probes), so the deployed code uses +`import * as Schema from 'effect/Schema'`. All combinators used are exactly the +contract's expressions (`Schema.Literals`, `Schema.TaggedStruct`, `Schema.DateFromMillis`, +`Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: 1 }))`, `optional` / +`optionalKey`, `decodeUnknownSync`, `encodeSync`) and all decode/reject/encode behaviors +match the contract's rules. + +## 5. Status labels + +- Deployment: **CLOUD DEV** (`reliable-panther-823`), functions deployed and verified via CLI +- Data: **synthetic fixtures only** (`Ada Test (synthetic)`, `Ben Test (synthetic)`, `gil_test_operator`) +- NOT live-LLM extraction evidence +- NOT native device (iOS dictation) evidence +- NOT browser app UI evidence diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..3044d7a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1089 @@ +{ + "name": "obv-hackaton", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "obv-hackaton", + "version": "0.1.0", + "dependencies": { + "convex": "1.46.0", + "effect": "4.0.0-rc.115" + }, + "devDependencies": { + "tsx": "^4.19.0", + "typescript": "^5.6.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz", + "integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz", + "integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz", + "integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz", + "integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz", + "integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz", + "integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz", + "integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz", + "integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz", + "integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz", + "integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz", + "integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz", + "integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz", + "integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz", + "integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz", + "integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz", + "integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz", + "integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz", + "integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz", + "integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz", + "integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz", + "integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz", + "integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz", + "integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz", + "integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz", + "integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz", + "integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/convex": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/convex/-/convex-1.46.0.tgz", + "integrity": "sha512-dhBwDQTZDOhi5SUGAX17EWNehCdNq6/UFrSL0eLZuGfMkdsQbCVWVRZCzbZ5CF+/pgvoGxtApbEoTSipqL4JTw==", + "license": "Apache-2.0", + "dependencies": { + "esbuild": "0.27.0", + "prettier": "^3.0.0", + "ws": "8.21.0" + }, + "bin": { + "convex": "bin/main.js" + }, + "engines": { + "node": ">=20.0.0", + "npm": ">=7.0.0" + }, + "peerDependencies": { + "@auth0/auth0-react": "^2.0.1", + "@clerk/clerk-react": "^4.12.8 || ^5.0.0", + "@clerk/react": "^6.4.3", + "react": "^18.0.0 || ^19.0.0-0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@auth0/auth0-react": { + "optional": true + }, + "@clerk/clerk-react": { + "optional": true + }, + "@clerk/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/effect": { + "version": "4.0.0-rc.115", + "resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-rc.115.tgz", + "integrity": "sha512-ogYulZ5ffeOzrJqQrG0XOkDO5lKn2s9KNHhoxJy/6wKUkF0i12dlVXWA9TgsgQ+zV/JzizyG0+XMPepZEQX6vw==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", + "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.0", + "@esbuild/android-arm": "0.27.0", + "@esbuild/android-arm64": "0.27.0", + "@esbuild/android-x64": "0.27.0", + "@esbuild/darwin-arm64": "0.27.0", + "@esbuild/darwin-x64": "0.27.0", + "@esbuild/freebsd-arm64": "0.27.0", + "@esbuild/freebsd-x64": "0.27.0", + "@esbuild/linux-arm": "0.27.0", + "@esbuild/linux-arm64": "0.27.0", + "@esbuild/linux-ia32": "0.27.0", + "@esbuild/linux-loong64": "0.27.0", + "@esbuild/linux-mips64el": "0.27.0", + "@esbuild/linux-ppc64": "0.27.0", + "@esbuild/linux-riscv64": "0.27.0", + "@esbuild/linux-s390x": "0.27.0", + "@esbuild/linux-x64": "0.27.0", + "@esbuild/netbsd-arm64": "0.27.0", + "@esbuild/netbsd-x64": "0.27.0", + "@esbuild/openbsd-arm64": "0.27.0", + "@esbuild/openbsd-x64": "0.27.0", + "@esbuild/openharmony-arm64": "0.27.0", + "@esbuild/sunos-x64": "0.27.0", + "@esbuild/win32-arm64": "0.27.0", + "@esbuild/win32-ia32": "0.27.0", + "@esbuild/win32-x64": "0.27.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/prettier": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.7.tgz", + "integrity": "sha512-T3mF5P7HFzZVLDQuCUNtJj6WK1pcpLMweMNUVwGb01bLNkH9AkKxtZvmInw8K6bnn2O3d6/5RHl6zSHiBAD0Og==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "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/prettier/prettier?sponsor=1" + } + }, + "node_modules/tsx": { + "version": "4.23.13", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.13.tgz", + "integrity": "sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..3bc73ce --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "obv-hackaton", + "private": true, + "version": "0.1.0", + "scripts": { + "typecheck": "tsc --noEmit", + "contract:smoke": "tsx deploy/contract-smoke.ts", + "deploy:thin-path": "convex deploy" + }, + "dependencies": { + "convex": "1.46.0", + "effect": "4.0.0-rc.115" + }, + "devDependencies": { + "tsx": "^4.19.0", + "typescript": "^5.6.0" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..66ae380 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["ES2021", "dom"], + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "noEmit": true, + "isolatedModules": true, + "skipLibCheck": true, + "types": [] + }, + "include": ["convex/**/*.ts", "deploy/**/*.ts"] +}