@gaganref/confect-test 10.3.1-fork.89 → 10.3.1-fork.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/TestConfect.d.ts +33 -7
- package/dist/TestConfect.d.ts.map +1 -1
- package/dist/TestConfect.js +11 -6
- package/dist/TestConfect.js.map +1 -1
- package/dist/internal/Attempt.js.map +1 -1
- package/dist/internal/Clock.d.ts +29 -0
- package/dist/internal/Clock.d.ts.map +1 -0
- package/dist/internal/Clock.js +66 -0
- package/dist/internal/Clock.js.map +1 -0
- package/dist/internal/ConvexTest.d.ts +33 -1
- package/dist/internal/ConvexTest.d.ts.map +1 -1
- package/dist/internal/ConvexTest.js +8 -5
- package/dist/internal/ConvexTest.js.map +1 -1
- package/dist/internal/Execution.d.ts +85 -5
- package/dist/internal/Execution.d.ts.map +1 -1
- package/dist/internal/Execution.js +285 -51
- package/dist/internal/Execution.js.map +1 -1
- package/dist/internal/Globals.d.ts.map +1 -1
- package/dist/internal/Globals.js +12 -0
- package/dist/internal/Globals.js.map +1 -1
- package/dist/internal/Invocation.d.ts +11 -0
- package/dist/internal/Invocation.d.ts.map +1 -1
- package/dist/internal/Invocation.js +34 -21
- package/dist/internal/Invocation.js.map +1 -1
- package/dist/internal/Kernel.d.ts +3 -1
- package/dist/internal/Kernel.d.ts.map +1 -1
- package/dist/internal/Kernel.js +2 -2
- package/dist/internal/Kernel.js.map +1 -1
- package/dist/internal/KernelRefusal.d.ts +25 -0
- package/dist/internal/KernelRefusal.d.ts.map +1 -1
- package/dist/internal/KernelRefusal.js +22 -1
- package/dist/internal/KernelRefusal.js.map +1 -1
- package/dist/internal/KernelValues.d.ts +6 -4
- package/dist/internal/KernelValues.d.ts.map +1 -1
- package/dist/internal/KernelValues.js +20 -5
- package/dist/internal/KernelValues.js.map +1 -1
- package/dist/internal/MountedHttp.d.ts.map +1 -1
- package/dist/internal/MountedHttp.js +3 -3
- package/dist/internal/MountedHttp.js.map +1 -1
- package/dist/internal/Scheduling.d.ts +71 -0
- package/dist/internal/Scheduling.d.ts.map +1 -0
- package/dist/internal/Scheduling.js +60 -0
- package/dist/internal/Scheduling.js.map +1 -0
- package/package.json +3 -3
- package/src/TestConfect.ts +62 -15
- package/src/internal/Attempt.ts +1 -1
- package/src/internal/Clock.ts +95 -0
- package/src/internal/ConvexTest.ts +51 -6
- package/src/internal/Execution.ts +415 -50
- package/src/internal/Globals.ts +20 -0
- package/src/internal/Invocation.ts +38 -13
- package/src/internal/Kernel.ts +5 -5
- package/src/internal/KernelRefusal.ts +24 -0
- package/src/internal/KernelValues.ts +44 -4
- package/src/internal/MountedHttp.ts +13 -6
- package/src/internal/Scheduling.ts +126 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Invocation.js","names":[],"sources":["../../src/internal/Invocation.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport type { FunctionMetadata, RequestMetadata } from \"convex/server\";\nimport type * as Context from \"effect/Context\";\n\n/**\n * Four re-executions after the first try. A conflict on attempt 5 exhausts.\n */\nexport type Attempt = 1 | 2 | 3 | 4 | 5;\n\n/**\n * One test backend. Its `services`, `clock` and `random` reach every invocation\n * it starts on its own, such as a scheduled job.\n */\nexport interface World {\n readonly name: string;\n readonly services: Context.Context<never>;\n /**\n * The world's controlled clock in epoch milliseconds. An invocation the world\n * starts on its own pins its start time from one reading. A bound action's\n * `Date` reads it live.\n */\n readonly clock: () => number;\n /**\n * The random stream, uniform on [0, 1), that an invocation the world starts\n * on its own draws from.\n */\n readonly random: () => number;\n}\n\nexport interface Invocation {\n readonly world: World;\n readonly componentPath: string;\n readonly attempt: Attempt;\n readonly request: Omit<RequestMetadata, \"authToken\">;\n readonly services: Context.Context<never>;\n /**\n * The start time in epoch milliseconds. Every query or mutation of the\n * invocation reads it from `Date.now()` and `new Date()`, so Effect's default\n * `Clock` reads it too. An action reads its world's clock instead, as it\n * runs, and a query or mutation an action calls starts at the action's time\n * then.\n */\n readonly startedAt: number;\n /**\n * The random stream, uniform on [0, 1). Every handler of the invocation draws\n * from it through `Math.random` and `crypto.getRandomValues`, so Effect's\n * default `Random` draws from it too.\n */\n readonly random: () => number;\n}\n\ntype FunctionType = \"query\" | \"mutation\" | \"action\";\n\n/**\n * The invocation a handler runs under, and the type of the function it runs.\n * Test code that binds an invocation runs no function, so it has no type.\n */\ninterface Running {\n readonly invocation: Invocation;\n readonly functionType: FunctionType | undefined;\n}\n\n/**\n * What `runHandlerPromise` in `@confect/server` reads from `globalThis` under\n * the same key. `start` answers `undefined` when no invocation is bound.\n */\ninterface Bridge {\n readonly storage: AsyncLocalStorage<Running>;\n readonly start: <A>(\n meta: {\n readonly getFunctionMetadata: () => Promise<FunctionMetadata>;\n readonly getRequestMetadata?: () => Promise<RequestMetadata>;\n },\n run: (services: Context.Context<never>) => Promise<A>,\n functionType: FunctionType,\n ) => Promise<A> | undefined;\n}\n\nconst key = Symbol.for(\"@confect/test/Invocation\");\n\n/**\n * The invocation a function of `functionType` called from `running` joins. A\n * query or mutation an action calls is a transaction of its own, so it starts\n * at the world's time when it begins. Any other call shares its caller's\n * start.\n */\nconst caller = (running: Running, functionType: FunctionType): Invocation =>\n running.functionType === \"action\" && functionType !== \"action\"\n ? { ...running.invocation, startedAt: running.invocation.world.clock() }\n : running.invocation;\n\n/**\n * The invocation a handler runs under. A call nested in the bound request joins\n * it at the callee's component path. A job Convex started from a timer set\n * inside the bound request is a request of its own: its scheduled function ID\n * differs, so it gets a fresh record with the world's services, clock and\n * random stream.\n */\nconst resolve = (\n bound: Invocation,\n componentPath: string,\n request: RequestMetadata | undefined,\n): Invocation => {\n if (\n request !== undefined &&\n request.scheduledFunctionId !== bound.request.scheduledFunctionId\n ) {\n const { ip, userAgent, requestId, scheduledFunctionId } = request;\n return {\n world: bound.world,\n componentPath,\n attempt: 1,\n request: { ip, userAgent, requestId, scheduledFunctionId },\n services: bound.world.services,\n startedAt: bound.world.clock(),\n random: bound.world.random,\n };\n }\n return componentPath === bound.componentPath\n ? bound\n : { ...bound, componentPath };\n};\n\n/**\n * What `Date` answers for a function of `functionType` under `invocation`: a\n * query's or mutation's start, or the world's clock as it runs in an action.\n */\nconst clockOf = (\n invocation: Invocation,\n functionType: FunctionType,\n): (() => number) =>\n functionType === \"action\"\n ? invocation.world.clock\n : () => invocation.startedAt;\n\nconst pinnedDate = (\n base: DateConstructor,\n now: () => number,\n): DateConstructor =>\n new Proxy(base, {\n get: (target, property, receiver) =>\n property === \"now\" ? now : Reflect.get(target, property, receiver),\n construct: (target, args, newTarget) =>\n Reflect.construct(target, args.length === 0 ? [now()] : args, newTarget),\n apply: () => new base(now()).toString(),\n });\n\nconst drawingMath = (base: Math, random: () => number): Math =>\n new Proxy(base, {\n get: (target, property, receiver) =>\n property === \"random\" ? random : Reflect.get(target, property, receiver),\n });\n\nconst drawingCrypto = (base: Crypto, random: () => number): Crypto => {\n function getRandomValues<T extends Parameters<Crypto[\"getRandomValues\"]>[0]>(\n this: unknown,\n array: T,\n ): T {\n // The host call checks the receiver and the argument and throws the host's\n // errors, so a detached call fails as it does in production.\n // oxlint-disable-next-line typescript/unbound-method -- Applied with the caller's receiver.\n Reflect.apply(base.getRandomValues, this === drawing ? base : this, [\n array,\n ]);\n if (array === null) return array;\n const bytes = new Uint8Array(\n array.buffer,\n array.byteOffset,\n array.byteLength,\n );\n for (let index = 0; index < bytes.length; index++) {\n bytes[index] = Math.floor(random() * 256);\n }\n return array;\n }\n const drawing: Crypto = new Proxy(base, {\n get: (target, property) => {\n if (property === \"getRandomValues\") return getRandomValues;\n // Crypto's methods and getters need the host object as `this`.\n const value: unknown = Reflect.get(target, property, target);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n return drawing;\n};\n\n/**\n * Point the calling handler's `Date`, `Math` and `crypto` at `invocation`, its\n * `Date` through {@link clockOf}. convex-test gives each handler call its own\n * copy of these globals, and an assignment inside a handler lands in that copy\n * only, so overlapping handlers and the handler's own nested calls keep\n * theirs.\n *\n * `outside` reads in the test's own async context. Where the handler has no\n * copy, an assignment either throws or reaches test code, so `pin` puts back\n * each global that reached it and throws.\n */\nconst pin = (\n invocation: Invocation,\n functionType: FunctionType,\n outside: <R>(read: () => R) => R,\n): void => {\n const host = globalThis;\n const previous = { Date: host.Date, Math: host.Math, crypto: host.crypto };\n const pinned = {\n Date: pinnedDate(previous.Date, clockOf(invocation, functionType)),\n Math: drawingMath(previous.Math, invocation.random),\n crypto: drawingCrypto(previous.crypto, invocation.random),\n };\n try {\n host.Date = pinned.Date;\n host.Math = pinned.Math;\n host.crypto = pinned.crypto;\n } catch {\n // A global without a per-handler copy can be read-only. The check below\n // refuses the handler.\n }\n const reached = outside(() => ({\n Date: host.Date === pinned.Date,\n Math: host.Math === pinned.Math,\n crypto: host.crypto === pinned.crypto,\n }));\n if (\n host.Date === pinned.Date &&\n host.Math === pinned.Math &&\n host.crypto === pinned.crypto &&\n !reached.Date &&\n !reached.Math &&\n !reached.crypto\n ) {\n return;\n }\n if (reached.Date) host.Date = previous.Date;\n if (reached.Math) host.Math = previous.Math;\n if (reached.crypto) host.crypto = previous.crypto;\n throw new Error(\n \"@confect/test cannot pin this handler's Date, Math and crypto: it runs without convex-test's per-handler globals, so pinning would change them for every caller\",\n );\n};\n\nconst make = (): Bridge => {\n const storage = new AsyncLocalStorage<Running>();\n // Taken at the first `bind`, which test code calls outside any handler, so\n // it reads the test environment's globals.\n const outside = AsyncLocalStorage.snapshot();\n return {\n storage,\n start: (meta, run, functionType) => {\n const running = storage.getStore();\n if (running === undefined) return undefined;\n return Promise.all([\n meta.getFunctionMetadata(),\n meta.getRequestMetadata?.(),\n ]).then(([{ componentPath }, request]) => {\n const invocation = resolve(\n caller(running, functionType),\n componentPath,\n request,\n );\n pin(invocation, functionType, outside);\n return storage.run({ invocation, functionType }, () =>\n run(invocation.services),\n );\n });\n },\n };\n};\n\n// One bridge per test runtime, so every copy of this module that a test loads\n// binds through the store `runHandlerPromise` reads.\nconst installed = globalThis as { [key]?: Bridge };\n\n/**\n * Run `start` with `invocation` bound. Every registered handler it reaches\n * through convex-test runs with the invocation's services, clock and random\n * stream.\n */\nexport const bind = <A>(\n invocation: Invocation,\n start: () => Promise<A>,\n): Promise<A> =>\n (installed[key] ??= make()).storage.run(\n { invocation, functionType: undefined },\n start,\n );\n\n/**\n * The invocation the calling handler runs under, if one is bound.\n */\nexport const current = (): Invocation | undefined =>\n installed[key]?.storage.getStore()?.invocation;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Invocation.js","names":["Clock.answersInvocation"],"sources":["../../src/internal/Invocation.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport type { FunctionMetadata, RequestMetadata } from \"convex/server\";\nimport type * as Context from \"effect/Context\";\nimport * as Clock from \"./Clock.js\";\n\n/**\n * Four re-executions after the first try. A conflict on attempt 5 exhausts.\n */\nexport type Attempt = 1 | 2 | 3 | 4 | 5;\n\n/**\n * One test backend. Its `services`, `clock` and `random` reach every invocation\n * it starts on its own, such as a scheduled job.\n */\nexport interface World {\n readonly name: string;\n readonly services: Context.Context<never>;\n /**\n * The world's controlled clock in epoch milliseconds. An invocation the world\n * starts on its own pins its start time from one reading. A bound action's\n * `Date` reads it live.\n */\n readonly clock: () => number;\n /**\n * The random stream, uniform on [0, 1), that an invocation the world starts\n * on its own draws from.\n */\n readonly random: () => number;\n}\n\nexport interface Invocation {\n readonly world: World;\n readonly componentPath: string;\n readonly attempt: Attempt;\n readonly request: Omit<RequestMetadata, \"authToken\">;\n readonly services: Context.Context<never>;\n /**\n * The start time in epoch milliseconds. Every query or mutation of the\n * invocation reads it from `Date.now()` and `new Date()`, so Effect's default\n * `Clock` reads it too. An action reads its world's clock instead, as it\n * runs, and a query or mutation an action calls starts at the action's time\n * then.\n */\n readonly startedAt: number;\n /**\n * The random stream, uniform on [0, 1). Every handler of the invocation draws\n * from it through `Math.random` and `crypto.getRandomValues`, so Effect's\n * default `Random` draws from it too.\n */\n readonly random: () => number;\n}\n\ntype FunctionType = \"query\" | \"mutation\" | \"action\";\n\n/**\n * The invocation a handler runs under, and the type of the function it runs.\n * Test code that binds an invocation runs no function, so it has no type.\n */\ninterface Running {\n readonly invocation: Invocation;\n readonly functionType: FunctionType | undefined;\n}\n\n/**\n * What `runHandlerPromise` in `@confect/server` reads from `globalThis` under\n * the same key. `start` answers `undefined` when no invocation is bound.\n */\ninterface Bridge {\n readonly storage: AsyncLocalStorage<Running>;\n readonly start: <A>(\n meta: {\n readonly getFunctionMetadata: () => Promise<FunctionMetadata>;\n readonly getRequestMetadata?: () => Promise<RequestMetadata>;\n },\n run: (services: Context.Context<never>) => Promise<A>,\n functionType: FunctionType,\n ) => Promise<A> | undefined;\n}\n\nconst key = Symbol.for(\"@confect/test/Invocation\");\n\n/**\n * A fresh invocation of `world` for a request of its own, as an action or a\n * scheduled job starts: attempt 1, the world's services and random stream, and\n * a start time read from the world's clock.\n */\nexport const fresh = (\n world: World,\n componentPath: string,\n { ip, userAgent, requestId, scheduledFunctionId }: Invocation[\"request\"],\n): Invocation => ({\n world,\n componentPath,\n attempt: 1,\n request: { ip, userAgent, requestId, scheduledFunctionId },\n services: world.services,\n startedAt: world.clock(),\n random: world.random,\n});\n\n/**\n * The invocation a function of `functionType` called from `running` joins. A\n * query or mutation an action calls is a transaction of its own, so it starts\n * at the world's time when it begins. Any other call shares its caller's\n * start.\n */\nconst caller = (running: Running, functionType: FunctionType): Invocation =>\n running.functionType === \"action\" && functionType !== \"action\"\n ? { ...running.invocation, startedAt: running.invocation.world.clock() }\n : running.invocation;\n\n/**\n * The invocation a handler runs under. A call nested in the bound request joins\n * it at the callee's component path. A job Convex started from a timer set\n * inside the bound request is a request of its own: its scheduled function ID\n * differs, so it gets a fresh record with the world's services, clock and\n * random stream.\n */\nconst resolve = (\n bound: Invocation,\n componentPath: string,\n request: RequestMetadata | undefined,\n): Invocation => {\n if (\n request !== undefined &&\n request.scheduledFunctionId !== bound.request.scheduledFunctionId\n ) {\n return fresh(bound.world, componentPath, request);\n }\n return componentPath === bound.componentPath\n ? bound\n : { ...bound, componentPath };\n};\n\n/**\n * What `Date` answers for a function of `functionType` under `invocation`: a\n * query's or mutation's start, or the world's clock as it runs in an action.\n */\nconst clockOf = (\n invocation: Invocation,\n functionType: FunctionType,\n): (() => number) =>\n functionType === \"action\"\n ? invocation.world.clock\n : () => invocation.startedAt;\n\nconst pinnedDate = (\n base: DateConstructor,\n now: () => number,\n): DateConstructor =>\n new Proxy(base, {\n get: (target, property, receiver) =>\n property === \"now\" ? now : Reflect.get(target, property, receiver),\n construct: (target, args, newTarget) =>\n Reflect.construct(target, args.length === 0 ? [now()] : args, newTarget),\n apply: () => new base(now()).toString(),\n });\n\nconst drawingMath = (base: Math, random: () => number): Math =>\n new Proxy(base, {\n get: (target, property, receiver) =>\n property === \"random\" ? random : Reflect.get(target, property, receiver),\n });\n\nconst drawingCrypto = (base: Crypto, random: () => number): Crypto => {\n function getRandomValues<T extends Parameters<Crypto[\"getRandomValues\"]>[0]>(\n this: unknown,\n array: T,\n ): T {\n // The host call checks the receiver and the argument and throws the host's\n // errors, so a detached call fails as it does in production.\n // oxlint-disable-next-line typescript/unbound-method -- Applied with the caller's receiver.\n Reflect.apply(base.getRandomValues, this === drawing ? base : this, [\n array,\n ]);\n if (array === null) return array;\n const bytes = new Uint8Array(\n array.buffer,\n array.byteOffset,\n array.byteLength,\n );\n for (let index = 0; index < bytes.length; index++) {\n bytes[index] = Math.floor(random() * 256);\n }\n return array;\n }\n const drawing: Crypto = new Proxy(base, {\n get: (target, property) => {\n if (property === \"getRandomValues\") return getRandomValues;\n // Crypto's methods and getters need the host object as `this`.\n const value: unknown = Reflect.get(target, property, target);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n });\n return drawing;\n};\n\n/**\n * Point the calling handler's `Date`, `Math` and `crypto` at `invocation`, its\n * `Date` through {@link clockOf}. convex-test gives each handler call its own\n * copy of these globals, and an assignment inside a handler lands in that copy\n * only, so overlapping handlers and the handler's own nested calls keep\n * theirs.\n *\n * `outside` reads in the test's own async context. Where the handler has no\n * copy, an assignment either throws or reaches test code, so `pin` puts back\n * each global that reached it and throws.\n */\nconst pin = (\n invocation: Invocation,\n functionType: FunctionType,\n outside: <R>(read: () => R) => R,\n): void => {\n const host = globalThis;\n const previous = { Date: host.Date, Math: host.Math, crypto: host.crypto };\n // On the kernel, `Date` already answers the running function's time on the\n // kernel's clock, so `runAfter` schedules on it. The bridge leaves it there.\n const kernelDate = Clock.answersInvocation(previous.Date);\n const pinned = {\n Date: kernelDate\n ? previous.Date\n : pinnedDate(previous.Date, clockOf(invocation, functionType)),\n Math: drawingMath(previous.Math, invocation.random),\n crypto: drawingCrypto(previous.crypto, invocation.random),\n };\n try {\n if (!kernelDate) host.Date = pinned.Date;\n host.Math = pinned.Math;\n host.crypto = pinned.crypto;\n } catch {\n // A global without a per-handler copy can be read-only. The check below\n // refuses the handler.\n }\n const reached = outside(() => ({\n Date: !kernelDate && host.Date === pinned.Date,\n Math: host.Math === pinned.Math,\n crypto: host.crypto === pinned.crypto,\n }));\n if (\n host.Date === pinned.Date &&\n host.Math === pinned.Math &&\n host.crypto === pinned.crypto &&\n !reached.Date &&\n !reached.Math &&\n !reached.crypto\n ) {\n return;\n }\n if (reached.Date) host.Date = previous.Date;\n if (reached.Math) host.Math = previous.Math;\n if (reached.crypto) host.crypto = previous.crypto;\n throw new Error(\n \"@confect/test cannot pin this handler's Date, Math and crypto: it runs without convex-test's per-handler globals, so pinning would change them for every caller\",\n );\n};\n\nconst make = (): Bridge => {\n const storage = new AsyncLocalStorage<Running>();\n // Taken at the first `bind`, which test code calls outside any handler, so\n // it reads the test environment's globals.\n const outside = AsyncLocalStorage.snapshot();\n return {\n storage,\n start: (meta, run, functionType) => {\n const running = storage.getStore();\n if (running === undefined) return undefined;\n return Promise.all([\n meta.getFunctionMetadata(),\n meta.getRequestMetadata?.(),\n ]).then(([{ componentPath }, request]) => {\n const invocation = resolve(\n caller(running, functionType),\n componentPath,\n request,\n );\n pin(invocation, functionType, outside);\n return storage.run({ invocation, functionType }, () =>\n run(invocation.services),\n );\n });\n },\n };\n};\n\n// One bridge per test runtime, so every copy of this module that a test loads\n// binds through the store `runHandlerPromise` reads.\nconst installed = globalThis as { [key]?: Bridge };\n\n/**\n * Run `start` with `invocation` bound. Every registered handler it reaches\n * through convex-test runs with the invocation's services, clock and random\n * stream.\n */\nexport const bind = <A>(\n invocation: Invocation,\n start: () => Promise<A>,\n): Promise<A> =>\n (installed[key] ??= make()).storage.run(\n { invocation, functionType: undefined },\n start,\n );\n\n/**\n * Run `start` with no invocation bound, so every registered handler it reaches\n * keeps its production runtime, whatever the caller has bound.\n */\nexport const unbound = <A>(start: () => Promise<A>): Promise<A> => {\n const bridge = installed[key];\n return bridge === undefined ? start() : bridge.storage.exit(start);\n};\n\n/**\n * The invocation the calling handler runs under, if one is bound.\n */\nexport const current = (): Invocation | undefined =>\n installed[key]?.storage.getStore()?.invocation;\n"],"mappings":"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"}
|
|
@@ -74,7 +74,9 @@ export declare const conflict: (state: State, snapshot: Version, reads: Readonly
|
|
|
74
74
|
*/
|
|
75
75
|
export interface KernelRuntime {
|
|
76
76
|
/**
|
|
77
|
-
* The time in milliseconds
|
|
77
|
+
* The time in milliseconds. The kernel's clock never reads earlier than the
|
|
78
|
+
* latest deadline `advance` reached, so a runtime that stands still still
|
|
79
|
+
* lets jobs run on time.
|
|
78
80
|
*/
|
|
79
81
|
readonly now: () => number;
|
|
80
82
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Kernel.d.ts","sourceRoot":"","sources":["../../src/internal/Kernel.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAExC;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,GAAG;IAClB,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,MAAM,MAAM,GAAG,WAAW,CAC9B,MAAM,EACN,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CAClD,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACzC;AAED;;;GAGG;AACH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACrC;AAED,eAAO,MAAM,KAAK,EAAE,KAKnB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,GAAG,UACP,KAAK,iBACG,MAAM,MACjB,UAAU,KACb,GAAG,GAAG,SAKR,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,IAAI,UACR,KAAK,iBACG,MAAM,aACV,MAAM,KAChB,QAAQ,CAAC,GAAG,CACkD,CAAC;AAElE;;;;;GAKG;AACH,eAAO,MAAM,KAAK,UACT,KAAK,UACJ,aAAa,CAAC,KAAK,CAAC,YAClB,MAAM,KACf,KAgCF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,UACZ,KAAK,YACF,OAAO,SACV,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,KACjC,MAAM,GAAG,SAOX,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B
|
|
1
|
+
{"version":3,"file":"Kernel.d.ts","sourceRoot":"","sources":["../../src/internal/Kernel.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAExC;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,GAAG;IAClB,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,MAAM,MAAM,GAAG,WAAW,CAC9B,MAAM,EACN,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CAClD,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACzC;AAED;;;GAGG;AACH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACrC;AAED,eAAO,MAAM,KAAK,EAAE,KAKnB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,GAAG,UACP,KAAK,iBACG,MAAM,MACjB,UAAU,KACb,GAAG,GAAG,SAKR,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,IAAI,UACR,KAAK,iBACG,MAAM,aACV,MAAM,KAChB,QAAQ,CAAC,GAAG,CACkD,CAAC;AAElE;;;;;GAKG;AACH,eAAO,MAAM,KAAK,UACT,KAAK,UACJ,aAAa,CAAC,KAAK,CAAC,YAClB,MAAM,KACf,KAgCF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,UACZ,KAAK,YACF,OAAO,SACV,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,KACjC,MAAM,GAAG,SAOX,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC;CACjD;AAED;;;;GAIG;AACH,eAAO,MAAM,SAAS,QAAO,aAW5B,CAAC"}
|
package/dist/internal/Kernel.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { hostNow } from "./Clock.js";
|
|
2
2
|
import { resolveCommittedValues, tableNameFromId } from "./KernelValues.js";
|
|
3
3
|
import { covers } from "./ReadSet.js";
|
|
4
4
|
//#region src/internal/Kernel.ts
|
|
@@ -75,7 +75,7 @@ const conflict = (state, snapshot, reads) => {
|
|
|
75
75
|
*/
|
|
76
76
|
const wallClock = () => {
|
|
77
77
|
let latest = 0n;
|
|
78
|
-
const now =
|
|
78
|
+
const now = hostNow;
|
|
79
79
|
return {
|
|
80
80
|
now,
|
|
81
81
|
commitTs: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Kernel.js","names":["ReadSet.covers","
|
|
1
|
+
{"version":3,"file":"Kernel.js","names":["ReadSet.covers","Clock.hostNow"],"sources":["../../src/internal/Kernel.ts"],"sourcesContent":["import * as Clock from \"./Clock.js\";\nimport type { DocumentId, StoredDocument } from \"./KernelValues.js\";\nimport { resolveCommittedValues, tableNameFromId } from \"./KernelValues.js\";\nimport * as ReadSet from \"./ReadSet.js\";\n\n/**\n * The number of commits the kernel has published. The empty world is 0.\n */\nexport type Version = number;\n\n/**\n * A committed document and the version that wrote it.\n */\nexport interface Row {\n readonly doc: StoredDocument;\n readonly version: Version;\n}\n\n/**\n * Committed rows, keyed by component path, then table name, then document ID. A\n * published state is never changed. A commit makes a new one.\n */\nexport type Tables = ReadonlyMap<\n string,\n ReadonlyMap<string, ReadonlyMap<DocumentId, Row>>\n>;\n\n/**\n * One document's final value in a commit. `null` deletes it.\n */\nexport interface Write {\n readonly componentPath: string;\n readonly id: DocumentId;\n readonly doc: StoredDocument | null;\n}\n\n/**\n * One document a commit changed: its value before and after. `null` is absent.\n */\nexport interface Change {\n readonly componentPath: string;\n readonly id: DocumentId;\n readonly before: StoredDocument | null;\n readonly after: StoredDocument | null;\n}\n\nexport interface Commit {\n readonly version: Version;\n readonly commitTs: bigint;\n readonly changes: ReadonlyArray<Change>;\n}\n\n/**\n * The kernel's committed state: rows, the latest version and commit timestamp,\n * and the write log, oldest first.\n */\nexport interface State {\n readonly tables: Tables;\n readonly version: Version;\n readonly commitTs: bigint;\n readonly log: ReadonlyArray<Commit>;\n}\n\nexport const empty: State = {\n tables: new Map(),\n version: 0,\n commitTs: 0n,\n log: [],\n};\n\n/**\n * The committed row for `id` in `componentPath`, if any.\n */\nexport const get = (\n state: State,\n componentPath: string,\n id: DocumentId,\n): Row | undefined => {\n const tableName = tableNameFromId(id);\n return tableName === null\n ? undefined\n : state.tables.get(componentPath)?.get(tableName)?.get(id);\n};\n\n/**\n * Every committed row of one table in one component.\n */\nexport const rows = (\n state: State,\n componentPath: string,\n tableName: string,\n): Iterable<Row> =>\n state.tables.get(componentPath)?.get(tableName)?.values() ?? [];\n\n/**\n * Publish one attempt's writes as a single commit at `commitTs`. Every\n * component's writes land in the same version, so a reader sees all of them or\n * none. A commit with no writes publishes nothing and leaves the state as it\n * was.\n */\nexport const apply = (\n state: State,\n writes: ReadonlyArray<Write>,\n commitTs: bigint,\n): State => {\n if (writes.length === 0) return state;\n const version = state.version + 1;\n const tables = new Map(\n Array.from(state.tables, ([path, byTable]) => [path, new Map(byTable)]),\n );\n const changes = writes.map(({ componentPath, id, doc }): Change => ({\n componentPath,\n id,\n before: get(state, componentPath, id)?.doc ?? null,\n after: doc === null ? null : resolveCommittedValues(doc, commitTs),\n }));\n const touched = new Set<string>();\n for (const { componentPath, id, after } of changes) {\n const tableName = tableNameFromId(id)!;\n const byTable = tables.get(componentPath) ?? new Map();\n tables.set(componentPath, byTable);\n const key = `${componentPath}\\u0000${tableName}`;\n const table = touched.has(key)\n ? byTable.get(tableName)!\n : new Map(byTable.get(tableName) ?? []);\n touched.add(key);\n byTable.set(tableName, table);\n if (after === null) table.delete(id);\n else table.set(id, { doc: after, version });\n }\n return {\n tables,\n version,\n commitTs,\n log: [...state.log, { version, commitTs, changes }],\n };\n};\n\n/**\n * The first change published after version `snapshot` that invalidates one of\n * `reads`, if any.\n */\nexport const conflict = (\n state: State,\n snapshot: Version,\n reads: ReadonlyArray<ReadSet.Read>,\n): Change | undefined => {\n for (const commit of state.log.slice(snapshot)) {\n for (const change of commit.changes) {\n if (reads.some((read) => ReadSet.covers(read, change))) return change;\n }\n }\n return undefined;\n};\n\n/**\n * What the driver supplies. The kernel holds no clock of its own.\n */\nexport interface KernelRuntime {\n /**\n * The time in milliseconds. The kernel's clock never reads earlier than the\n * latest deadline `advance` reached, so a runtime that stands still still\n * lets jobs run on time.\n */\n readonly now: () => number;\n /**\n * The commit timestamp of `version`, asked once as it is published. The\n * answers strictly increase. A mutation that wrote nothing publishes no\n * version and asks nothing.\n */\n readonly commitTs: (version: Version) => bigint;\n}\n\n/**\n * The wall clock, as convex-test reads it. Each answer is the time in\n * nanoseconds when it is asked for, bumped past the previous answer when the\n * clock stands still.\n */\nexport const wallClock = (): KernelRuntime => {\n let latest = 0n;\n const now = Clock.hostNow;\n return {\n now,\n commitTs: () => {\n const nanos = BigInt(now()) * 1_000_000n;\n latest = nanos > latest ? nanos : latest + 1n;\n return latest;\n },\n };\n};\n"],"mappings":";;;;AA+DA,MAAa,QAAe;CAC1B,wBAAQ,IAAI,IAAI;CAChB,SAAS;CACT,UAAU;CACV,KAAK,CAAC;AACR;;;;AAKA,MAAa,OACX,OACA,eACA,OACoB;CACpB,MAAM,YAAY,gBAAgB,EAAE;CACpC,OAAO,cAAc,OACjB,KAAA,IACA,MAAM,OAAO,IAAI,aAAa,CAAC,EAAE,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE;AAC7D;;;;AAKA,MAAa,QACX,OACA,eACA,cAEA,MAAM,OAAO,IAAI,aAAa,CAAC,EAAE,IAAI,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;;;;;;;AAQhE,MAAa,SACX,OACA,QACA,aACU;CACV,IAAI,OAAO,WAAW,GAAG,OAAO;CAChC,MAAM,UAAU,MAAM,UAAU;CAChC,MAAM,SAAS,IAAI,IACjB,MAAM,KAAK,MAAM,SAAS,CAAC,MAAM,aAAa,CAAC,MAAM,IAAI,IAAI,OAAO,CAAC,CAAC,CACxE;CACA,MAAM,UAAU,OAAO,KAAK,EAAE,eAAe,IAAI,WAAmB;EAClE;EACA;EACA,QAAQ,IAAI,OAAO,eAAe,EAAE,CAAC,EAAE,OAAO;EAC9C,OAAO,QAAQ,OAAO,OAAO,uBAAuB,KAAK,QAAQ;CACnE,EAAE;CACF,MAAM,0BAAU,IAAI,IAAY;CAChC,KAAK,MAAM,EAAE,eAAe,IAAI,WAAW,SAAS;EAClD,MAAM,YAAY,gBAAgB,EAAE;EACpC,MAAM,UAAU,OAAO,IAAI,aAAa,qBAAK,IAAI,IAAI;EACrD,OAAO,IAAI,eAAe,OAAO;EACjC,MAAM,MAAM,GAAG,cAAc,QAAQ;EACrC,MAAM,QAAQ,QAAQ,IAAI,GAAG,IACzB,QAAQ,IAAI,SAAS,IACrB,IAAI,IAAI,QAAQ,IAAI,SAAS,KAAK,CAAC,CAAC;EACxC,QAAQ,IAAI,GAAG;EACf,QAAQ,IAAI,WAAW,KAAK;EAC5B,IAAI,UAAU,MAAM,MAAM,OAAO,EAAE;OAC9B,MAAM,IAAI,IAAI;GAAE,KAAK;GAAO;EAAQ,CAAC;CAC5C;CACA,OAAO;EACL;EACA;EACA;EACA,KAAK,CAAC,GAAG,MAAM,KAAK;GAAE;GAAS;GAAU;EAAQ,CAAC;CACpD;AACF;;;;;AAMA,MAAa,YACX,OACA,UACA,UACuB;CACvB,KAAK,MAAM,UAAU,MAAM,IAAI,MAAM,QAAQ,GAC3C,KAAK,MAAM,UAAU,OAAO,SAC1B,IAAI,MAAM,MAAM,SAASA,OAAe,MAAM,MAAM,CAAC,GAAG,OAAO;AAIrE;;;;;;AAyBA,MAAa,kBAAiC;CAC5C,IAAI,SAAS;CACb,MAAM,MAAMC;CACZ,OAAO;EACL;EACA,gBAAgB;GACd,MAAM,QAAQ,OAAO,IAAI,CAAC,IAAI;GAC9B,SAAS,QAAQ,SAAS,QAAQ,SAAS;GAC3C,OAAO;EACT;CACF;AACF"}
|
|
@@ -71,6 +71,31 @@ declare const CommitTsNotIncreasing_base: Schema.Class<CommitTsNotIncreasing, Sc
|
|
|
71
71
|
*/
|
|
72
72
|
export declare class CommitTsNotIncreasing extends CommitTsNotIncreasing_base {
|
|
73
73
|
}
|
|
74
|
+
declare const DrainBudgetExceeded_base: Schema.Class<DrainBudgetExceeded, Schema.TaggedStruct<"DrainBudgetExceeded", {
|
|
75
|
+
readonly budget: Schema.Int;
|
|
76
|
+
readonly pending: Schema.$Array<Schema.Struct<{
|
|
77
|
+
readonly componentPath: Schema.String;
|
|
78
|
+
readonly name: Schema.String;
|
|
79
|
+
}>>;
|
|
80
|
+
readonly message: Schema.String;
|
|
81
|
+
}>, import("effect/Cause").YieldableError>;
|
|
82
|
+
/**
|
|
83
|
+
* Draining scheduled functions ran its budget of jobs and work still remained.
|
|
84
|
+
* A self-rescheduling function keeps the drain from ending unless its kit
|
|
85
|
+
* declares it standing.
|
|
86
|
+
*/
|
|
87
|
+
export declare class DrainBudgetExceeded extends DrainBudgetExceeded_base {
|
|
88
|
+
}
|
|
89
|
+
declare const UnknownStandingSource_base: Schema.Class<UnknownStandingSource, Schema.TaggedStruct<"UnknownStandingSource", {
|
|
90
|
+
readonly componentPath: Schema.String;
|
|
91
|
+
readonly name: Schema.String;
|
|
92
|
+
readonly message: Schema.String;
|
|
93
|
+
}>, import("effect/Cause").YieldableError>;
|
|
94
|
+
/**
|
|
95
|
+
* A component declared a standing source its modules do not export.
|
|
96
|
+
*/
|
|
97
|
+
export declare class UnknownStandingSource extends UnknownStandingSource_base {
|
|
98
|
+
}
|
|
74
99
|
export declare const requiresBinary: (operation: RequiresBinary["operation"]) => RequiresBinary;
|
|
75
100
|
export declare const rowPending: (operation: string, row: number) => KernelRowPending;
|
|
76
101
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KernelRefusal.d.ts","sourceRoot":"","sources":["../../src/internal/KernelRefusal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;;;;;AAExC;;GAEG;AACH,qBAAa,cAAe,SAAQ,mBAMnC;CAAG;;;;;;AAEJ;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,qBAOrC;CAAG;;;;;AAEJ;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,wBAGxC;CAAG;;;;;AAEJ;;;GAGG;AACH,qBAAa,mCAAoC,SAAQ,wCAGxD;CAAG;;;;;AAEJ;;;GAGG;AACH,qBAAa,aAAc,SAAQ,kBAGlC;CAAG;;;;;AAEJ;;;GAGG;AACH,qBAAa,YAAa,SAAQ,iBAGjC;CAAG;;;;;;;AAEJ;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,0BAQ1C;CAAG;AAEJ,eAAO,MAAM,cAAc,cAAe,cAAc,CAAC,WAAW,CAAC,mBAIjE,CAAC;AAEL,eAAO,MAAM,UAAU,cAAe,MAAM,OAAO,MAAM,qBAKrD,CAAC"}
|
|
1
|
+
{"version":3,"file":"KernelRefusal.d.ts","sourceRoot":"","sources":["../../src/internal/KernelRefusal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;;;;;AAExC;;GAEG;AACH,qBAAa,cAAe,SAAQ,mBAMnC;CAAG;;;;;;AAEJ;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,qBAOrC;CAAG;;;;;AAEJ;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,wBAGxC;CAAG;;;;;AAEJ;;;GAGG;AACH,qBAAa,mCAAoC,SAAQ,wCAGxD;CAAG;;;;;AAEJ;;;GAGG;AACH,qBAAa,aAAc,SAAQ,kBAGlC;CAAG;;;;;AAEJ;;;GAGG;AACH,qBAAa,YAAa,SAAQ,iBAGjC;CAAG;;;;;;;AAEJ;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,0BAQ1C;CAAG;;;;;;;;;AAEJ;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,wBASxC;CAAG;;;;;;AAEJ;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,0BAG1C;CAAG;AAEJ,eAAO,MAAM,cAAc,cAAe,cAAc,CAAC,WAAW,CAAC,mBAIjE,CAAC;AAEL,eAAO,MAAM,UAAU,cAAe,MAAM,OAAO,MAAM,qBAKrD,CAAC"}
|
|
@@ -58,6 +58,27 @@ var CommitTsNotIncreasing = class extends Schema.TaggedError()("CommitTsNotIncre
|
|
|
58
58
|
commitTs: Schema.BigInt,
|
|
59
59
|
message: Schema.String
|
|
60
60
|
}) {};
|
|
61
|
+
/**
|
|
62
|
+
* Draining scheduled functions ran its budget of jobs and work still remained.
|
|
63
|
+
* A self-rescheduling function keeps the drain from ending unless its kit
|
|
64
|
+
* declares it standing.
|
|
65
|
+
*/
|
|
66
|
+
var DrainBudgetExceeded = class extends Schema.TaggedError()("DrainBudgetExceeded", {
|
|
67
|
+
budget: Schema.Int,
|
|
68
|
+
pending: Schema.Array(Schema.Struct({
|
|
69
|
+
componentPath: Schema.String,
|
|
70
|
+
name: Schema.String
|
|
71
|
+
})),
|
|
72
|
+
message: Schema.String
|
|
73
|
+
}) {};
|
|
74
|
+
/**
|
|
75
|
+
* A component declared a standing source its modules do not export.
|
|
76
|
+
*/
|
|
77
|
+
var UnknownStandingSource = class extends Schema.TaggedError()("UnknownStandingSource", {
|
|
78
|
+
componentPath: Schema.String,
|
|
79
|
+
name: Schema.String,
|
|
80
|
+
message: Schema.String
|
|
81
|
+
}) {};
|
|
61
82
|
const requiresBinary = (operation) => new RequiresBinary({
|
|
62
83
|
operation,
|
|
63
84
|
message: `The test kernel does not simulate ${operation}. Run this case against the Convex binary.`
|
|
@@ -68,6 +89,6 @@ const rowPending = (operation, row) => new KernelRowPending({
|
|
|
68
89
|
message: `The test kernel supplies ${operation} in build-order row ${row}.`
|
|
69
90
|
});
|
|
70
91
|
//#endregion
|
|
71
|
-
export { ClosedAttempt, CommitTsNotIncreasing, KernelRowPending, OptimisticConcurrencyControlFailure, RegisteredAfterOpen, RequiresBinary, StaleAttempt, requiresBinary, rowPending };
|
|
92
|
+
export { ClosedAttempt, CommitTsNotIncreasing, DrainBudgetExceeded, KernelRowPending, OptimisticConcurrencyControlFailure, RegisteredAfterOpen, RequiresBinary, StaleAttempt, UnknownStandingSource, requiresBinary, rowPending };
|
|
72
93
|
|
|
73
94
|
//# sourceMappingURL=KernelRefusal.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KernelRefusal.js","names":[],"sources":["../../src/internal/KernelRefusal.ts"],"sourcesContent":["import * as Schema from \"effect/Schema\";\n\n/**\n * The kernel never answers this operation in process. Only the binary does.\n */\nexport class RequiresBinary extends Schema.TaggedError<RequiresBinary>()(\n \"RequiresBinary\",\n {\n operation: Schema.Literals([\"textSearch\", \"vectorSearch\"]),\n message: Schema.String,\n },\n) {}\n\n/**\n * A later kernel row supplies this operation. Until then the kernel refuses it\n * rather than answer with convex-test's behaviour.\n */\nexport class KernelRowPending extends Schema.TaggedError<KernelRowPending>()(\n \"KernelRowPending\",\n {\n operation: Schema.String,\n row: Schema.Int,\n message: Schema.String,\n },\n) {}\n\n/**\n * A component was registered after the test backend opened, so its modules\n * would load after a function had already run.\n */\nexport class RegisteredAfterOpen extends Schema.TaggedError<RegisteredAfterOpen>()(\n \"RegisteredAfterOpen\",\n { componentPath: Schema.String, message: Schema.String },\n) {}\n\n/**\n * Every attempt of a mutation read data a commit changed before it could\n * publish. The kernel gives up on attempt 5.\n */\nexport class OptimisticConcurrencyControlFailure extends Schema.TaggedError<OptimisticConcurrencyControlFailure>()(\n \"OptimisticConcurrencyControlFailure\",\n { attempts: Schema.Int, message: Schema.String },\n) {}\n\n/**\n * A syscall reached a mutation's attempt after the mutation returned and its\n * syscalls drained, so the attempt was already held or published.\n */\nexport class ClosedAttempt extends Schema.TaggedError<ClosedAttempt>()(\n \"ClosedAttempt\",\n { operation: Schema.String, message: Schema.String },\n) {}\n\n/**\n * `apply` named an attempt the kernel does not hold: it was already applied, or\n * never prepared.\n */\nexport class StaleAttempt extends Schema.TaggedError<StaleAttempt>()(\n \"StaleAttempt\",\n { id: Schema.String, message: Schema.String },\n) {}\n\n/**\n * The runtime answered a commit timestamp no greater than the previous\n * commit's.\n */\nexport class CommitTsNotIncreasing extends Schema.TaggedError<CommitTsNotIncreasing>()(\n \"CommitTsNotIncreasing\",\n {\n version: Schema.Int,\n previous: Schema.BigInt,\n commitTs: Schema.BigInt,\n message: Schema.String,\n },\n) {}\n\nexport const requiresBinary = (operation: RequiresBinary[\"operation\"]) =>\n new RequiresBinary({\n operation,\n message: `The test kernel does not simulate ${operation}. Run this case against the Convex binary.`,\n });\n\nexport const rowPending = (operation: string, row: number) =>\n new KernelRowPending({\n operation,\n row,\n message: `The test kernel supplies ${operation} in build-order row ${row}.`,\n });\n"],"mappings":";;;;;AAKA,IAAa,iBAAb,cAAoC,OAAO,YAA4B,CAAC,CACtE,kBACA;CACE,WAAW,OAAO,SAAS,CAAC,cAAc,cAAc,CAAC;CACzD,SAAS,OAAO;AAClB,CACF,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,mBAAb,cAAsC,OAAO,YAA8B,CAAC,CAC1E,oBACA;CACE,WAAW,OAAO;CAClB,KAAK,OAAO;CACZ,SAAS,OAAO;AAClB,CACF,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,sBAAb,cAAyC,OAAO,YAAiC,CAAC,CAChF,uBACA;CAAE,eAAe,OAAO;CAAQ,SAAS,OAAO;AAAO,CACzD,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,sCAAb,cAAyD,OAAO,YAAiD,CAAC,CAChH,uCACA;CAAE,UAAU,OAAO;CAAK,SAAS,OAAO;AAAO,CACjD,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,gBAAb,cAAmC,OAAO,YAA2B,CAAC,CACpE,iBACA;CAAE,WAAW,OAAO;CAAQ,SAAS,OAAO;AAAO,CACrD,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,eAAb,cAAkC,OAAO,YAA0B,CAAC,CAClE,gBACA;CAAE,IAAI,OAAO;CAAQ,SAAS,OAAO;AAAO,CAC9C,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,wBAAb,cAA2C,OAAO,YAAmC,CAAC,CACpF,yBACA;CACE,SAAS,OAAO;CAChB,UAAU,OAAO;CACjB,UAAU,OAAO;CACjB,SAAS,OAAO;AAClB,CACF,CAAC,CAAC,CAAC;AAEH,MAAa,kBAAkB,cAC7B,IAAI,eAAe;CACjB;CACA,SAAS,qCAAqC,UAAU;AAC1D,CAAC;AAEH,MAAa,cAAc,WAAmB,QAC5C,IAAI,iBAAiB;CACnB;CACA;CACA,SAAS,4BAA4B,UAAU,sBAAsB,IAAI;AAC3E,CAAC"}
|
|
1
|
+
{"version":3,"file":"KernelRefusal.js","names":[],"sources":["../../src/internal/KernelRefusal.ts"],"sourcesContent":["import * as Schema from \"effect/Schema\";\n\n/**\n * The kernel never answers this operation in process. Only the binary does.\n */\nexport class RequiresBinary extends Schema.TaggedError<RequiresBinary>()(\n \"RequiresBinary\",\n {\n operation: Schema.Literals([\"textSearch\", \"vectorSearch\"]),\n message: Schema.String,\n },\n) {}\n\n/**\n * A later kernel row supplies this operation. Until then the kernel refuses it\n * rather than answer with convex-test's behaviour.\n */\nexport class KernelRowPending extends Schema.TaggedError<KernelRowPending>()(\n \"KernelRowPending\",\n {\n operation: Schema.String,\n row: Schema.Int,\n message: Schema.String,\n },\n) {}\n\n/**\n * A component was registered after the test backend opened, so its modules\n * would load after a function had already run.\n */\nexport class RegisteredAfterOpen extends Schema.TaggedError<RegisteredAfterOpen>()(\n \"RegisteredAfterOpen\",\n { componentPath: Schema.String, message: Schema.String },\n) {}\n\n/**\n * Every attempt of a mutation read data a commit changed before it could\n * publish. The kernel gives up on attempt 5.\n */\nexport class OptimisticConcurrencyControlFailure extends Schema.TaggedError<OptimisticConcurrencyControlFailure>()(\n \"OptimisticConcurrencyControlFailure\",\n { attempts: Schema.Int, message: Schema.String },\n) {}\n\n/**\n * A syscall reached a mutation's attempt after the mutation returned and its\n * syscalls drained, so the attempt was already held or published.\n */\nexport class ClosedAttempt extends Schema.TaggedError<ClosedAttempt>()(\n \"ClosedAttempt\",\n { operation: Schema.String, message: Schema.String },\n) {}\n\n/**\n * `apply` named an attempt the kernel does not hold: it was already applied, or\n * never prepared.\n */\nexport class StaleAttempt extends Schema.TaggedError<StaleAttempt>()(\n \"StaleAttempt\",\n { id: Schema.String, message: Schema.String },\n) {}\n\n/**\n * The runtime answered a commit timestamp no greater than the previous\n * commit's.\n */\nexport class CommitTsNotIncreasing extends Schema.TaggedError<CommitTsNotIncreasing>()(\n \"CommitTsNotIncreasing\",\n {\n version: Schema.Int,\n previous: Schema.BigInt,\n commitTs: Schema.BigInt,\n message: Schema.String,\n },\n) {}\n\n/**\n * Draining scheduled functions ran its budget of jobs and work still remained.\n * A self-rescheduling function keeps the drain from ending unless its kit\n * declares it standing.\n */\nexport class DrainBudgetExceeded extends Schema.TaggedError<DrainBudgetExceeded>()(\n \"DrainBudgetExceeded\",\n {\n budget: Schema.Int,\n pending: Schema.Array(\n Schema.Struct({ componentPath: Schema.String, name: Schema.String }),\n ),\n message: Schema.String,\n },\n) {}\n\n/**\n * A component declared a standing source its modules do not export.\n */\nexport class UnknownStandingSource extends Schema.TaggedError<UnknownStandingSource>()(\n \"UnknownStandingSource\",\n { componentPath: Schema.String, name: Schema.String, message: Schema.String },\n) {}\n\nexport const requiresBinary = (operation: RequiresBinary[\"operation\"]) =>\n new RequiresBinary({\n operation,\n message: `The test kernel does not simulate ${operation}. Run this case against the Convex binary.`,\n });\n\nexport const rowPending = (operation: string, row: number) =>\n new KernelRowPending({\n operation,\n row,\n message: `The test kernel supplies ${operation} in build-order row ${row}.`,\n });\n"],"mappings":";;;;;AAKA,IAAa,iBAAb,cAAoC,OAAO,YAA4B,CAAC,CACtE,kBACA;CACE,WAAW,OAAO,SAAS,CAAC,cAAc,cAAc,CAAC;CACzD,SAAS,OAAO;AAClB,CACF,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,mBAAb,cAAsC,OAAO,YAA8B,CAAC,CAC1E,oBACA;CACE,WAAW,OAAO;CAClB,KAAK,OAAO;CACZ,SAAS,OAAO;AAClB,CACF,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,sBAAb,cAAyC,OAAO,YAAiC,CAAC,CAChF,uBACA;CAAE,eAAe,OAAO;CAAQ,SAAS,OAAO;AAAO,CACzD,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,sCAAb,cAAyD,OAAO,YAAiD,CAAC,CAChH,uCACA;CAAE,UAAU,OAAO;CAAK,SAAS,OAAO;AAAO,CACjD,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,gBAAb,cAAmC,OAAO,YAA2B,CAAC,CACpE,iBACA;CAAE,WAAW,OAAO;CAAQ,SAAS,OAAO;AAAO,CACrD,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,eAAb,cAAkC,OAAO,YAA0B,CAAC,CAClE,gBACA;CAAE,IAAI,OAAO;CAAQ,SAAS,OAAO;AAAO,CAC9C,CAAC,CAAC,CAAC;;;;;AAMH,IAAa,wBAAb,cAA2C,OAAO,YAAmC,CAAC,CACpF,yBACA;CACE,SAAS,OAAO;CAChB,UAAU,OAAO;CACjB,UAAU,OAAO;CACjB,SAAS,OAAO;AAClB,CACF,CAAC,CAAC,CAAC;;;;;;AAOH,IAAa,sBAAb,cAAyC,OAAO,YAAiC,CAAC,CAChF,uBACA;CACE,QAAQ,OAAO;CACf,SAAS,OAAO,MACd,OAAO,OAAO;EAAE,eAAe,OAAO;EAAQ,MAAM,OAAO;CAAO,CAAC,CACrE;CACA,SAAS,OAAO;AAClB,CACF,CAAC,CAAC,CAAC;;;;AAKH,IAAa,wBAAb,cAA2C,OAAO,YAAmC,CAAC,CACpF,yBACA;CAAE,eAAe,OAAO;CAAQ,MAAM,OAAO;CAAQ,SAAS,OAAO;AAAO,CAC9E,CAAC,CAAC,CAAC;AAEH,MAAa,kBAAkB,cAC7B,IAAI,eAAe;CACjB;CACA,SAAS,qCAAqC,UAAU;AAC1D,CAAC;AAEH,MAAa,cAAc,WAAmB,QAC5C,IAAI,iBAAiB;CACnB;CACA;CACA,SAAS,4BAA4B,UAAU,sBAAsB,IAAI;AAC3E,CAAC"}
|
|
@@ -115,6 +115,11 @@ export type ValidatorJSON = {
|
|
|
115
115
|
type: "record";
|
|
116
116
|
keys: ValidatorJSON;
|
|
117
117
|
values: ObjectFieldType;
|
|
118
|
+
} | {
|
|
119
|
+
type: "function";
|
|
120
|
+
fnType?: string;
|
|
121
|
+
args?: ValidatorJSON;
|
|
122
|
+
returns?: ValidatorJSON;
|
|
118
123
|
} | {
|
|
119
124
|
type: "union";
|
|
120
125
|
value: ValidatorJSON[];
|
|
@@ -128,9 +133,6 @@ export declare function parseArgs(args: Record<string, Value> | undefined): Reco
|
|
|
128
133
|
export declare function validateFunctionArgs(argsValidator: ValidatorJSON, args: any, functionPath: FunctionPath, allowPendingValues: boolean): void;
|
|
129
134
|
export declare function validateReturnValue(returnsValidator: ValidatorJSON | null, result: any, functionType: "query" | "mutation" | "action", functionPath: FunctionPath): void;
|
|
130
135
|
export declare function createFunctionHandle(functionPath: FunctionPath): string;
|
|
131
|
-
export declare function parseFunctionHandle(handle: string):
|
|
132
|
-
componentPath: string;
|
|
133
|
-
udfPath: string;
|
|
134
|
-
};
|
|
136
|
+
export declare function parseFunctionHandle(handle: string): FunctionPath;
|
|
135
137
|
export declare function simpleHash(string: string): number;
|
|
136
138
|
//# sourceMappingURL=KernelValues.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KernelValues.d.ts","sourceRoot":"","sources":["../../src/internal/KernelValues.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"KernelValues.d.ts","sourceRoot":"","sources":["../../src/internal/KernelValues.ts"],"names":[],"mappings":"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"}
|
|
@@ -172,6 +172,9 @@ function validateValidator(validator, value) {
|
|
|
172
172
|
} else validateValidator(fieldType, value[k]);
|
|
173
173
|
for (const k of Object.keys(value)) if (validator.value[k] === void 0) throw new Error(`Validator error: Unexpected field \`${k}\` in object`);
|
|
174
174
|
return;
|
|
175
|
+
case "function":
|
|
176
|
+
if (typeof value !== "string" || readFunctionHandle(value) === void 0) throw new Error(`Validator error: Expected a function handle, got \`${value}\``);
|
|
177
|
+
return;
|
|
175
178
|
case "record":
|
|
176
179
|
if (value === null || typeof value !== "object" || !isSimpleObject(value)) throw new Error(`Validator error: Expected a plain old JavaScript \`object\` for a record, got \`${value}\``);
|
|
177
180
|
for (const [k, fieldValue] of Object.entries(value)) {
|
|
@@ -241,16 +244,28 @@ function validateReturnValue(returnsValidator, result, functionType, functionPat
|
|
|
241
244
|
throw new Error(`Return value validation failed for ${functionType} "${functionPath.udfPath}":\n${e.message}`, { cause: e });
|
|
242
245
|
}
|
|
243
246
|
}
|
|
247
|
+
const FUNCTION_HANDLE_PREFIX = "function://";
|
|
244
248
|
function createFunctionHandle(functionPath) {
|
|
245
|
-
return
|
|
249
|
+
return `${FUNCTION_HANDLE_PREFIX}${functionPath.componentPath};${functionPath.udfPath}`;
|
|
246
250
|
}
|
|
247
|
-
|
|
248
|
-
|
|
251
|
+
/**
|
|
252
|
+
* The function a handle the kernel minted names, or `undefined` for a string
|
|
253
|
+
* that is not `function://<componentPath>;<udfPath>` with a function path.
|
|
254
|
+
*/
|
|
255
|
+
function readFunctionHandle(handle) {
|
|
256
|
+
if (!handle.startsWith(FUNCTION_HANDLE_PREFIX)) return void 0;
|
|
257
|
+
const parts = handle.slice(11).split(";");
|
|
258
|
+
if (parts.length !== 2 || parts[1] === "") return void 0;
|
|
249
259
|
return {
|
|
250
|
-
componentPath,
|
|
251
|
-
udfPath
|
|
260
|
+
componentPath: parts[0],
|
|
261
|
+
udfPath: parts[1]
|
|
252
262
|
};
|
|
253
263
|
}
|
|
264
|
+
function parseFunctionHandle(handle) {
|
|
265
|
+
const functionPath = readFunctionHandle(handle);
|
|
266
|
+
if (functionPath === void 0) throw new Error(`Invalid function handle \`${handle}\``);
|
|
267
|
+
return functionPath;
|
|
268
|
+
}
|
|
254
269
|
function simpleHash(string) {
|
|
255
270
|
let hash = 0;
|
|
256
271
|
for (let i = 0; i < string.length; i++) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KernelValues.js","names":["unknown"],"sources":["../../src/internal/KernelValues.ts"],"sourcesContent":["// Derived from convex-test 0.0.65 (index.ts), Copyright Convex, Inc.,\n// licensed under the Apache License, Version 2.0\n// (https://www.apache.org/licenses/LICENSE-2.0). The value, filter, range,\n// validator and argument rules are kept as convex-test states them, so the\n// kernel answers a function the way convex-test does. The storage, attempts\n// and execution around them are the kernel's own.\n\nimport type { GenericDocument } from \"convex/server\";\nimport {\n CommitTsPlaceholder,\n type GenericId,\n type JSONValue,\n type Value,\n compareValues,\n convexToJson,\n jsonToConvex,\n} from \"convex/values\";\n\n/**\n * A function address inside one test backend: its component and its\n * `module:export` path.\n */\nexport type FunctionPath = {\n readonly componentPath: string;\n readonly udfPath: string;\n};\n\nexport type FilterJson =\n | { $eq: [FilterJson, FilterJson] }\n | { $field: string }\n | JSONValue;\n\nexport type QueryOperator = { filter: FilterJson } | { limit: number };\nexport type Source =\n | { type: \"FullTableScan\"; tableName: string; order: \"asc\" | \"desc\" | null }\n | {\n type: \"IndexRange\";\n indexName: string;\n range: ReadonlyArray<SerializedRangeExpression>;\n order: \"asc\" | \"desc\" | null;\n }\n | {\n type: \"Search\";\n indexName: string;\n filters: ReadonlyArray<SerializedSearchFilter>;\n };\n\nexport type SerializedQuery = {\n source: Source;\n operators: Array<QueryOperator>;\n};\n\nexport type SerializedRangeExpression = {\n type: \"Eq\" | \"Gt\" | \"Gte\" | \"Lt\" | \"Lte\";\n fieldPath: string;\n value: JSONValue;\n};\n\nexport type SerializedSearchFilter =\n | {\n type: \"Search\";\n fieldPath: string;\n value: string;\n }\n | {\n type: \"Eq\";\n fieldPath: string;\n value: JSONValue;\n };\n\nexport type StoredDocument = GenericDocument & {\n _id: DocumentId;\n _creationTime: number;\n};\n\nexport type Index = {\n indexDescriptor: string;\n fields: string[];\n};\n\nexport type TableName = string;\n\nexport type DocumentId = GenericId<TableName>;\n\nexport function tableNameFromId(id: string) {\n const match = id.match(/^[0-9]+/);\n if (!match || match[0].length === id.length) {\n return null;\n }\n return id.slice(match[0].length);\n}\nexport function deserializeConvexErrorData(thrown: unknown) {\n if (\n typeof thrown === \"object\" &&\n thrown !== null &&\n Symbol.for(\"ConvexError\") in thrown &&\n typeof (thrown as { data: unknown }).data === \"string\"\n ) {\n try {\n (thrown as { data: unknown }).data = jsonToConvex(\n JSON.parse((thrown as { data: string }).data),\n );\n } catch {\n // Data wasn't a serialized JSON string — leave it untouched.\n }\n }\n return thrown;\n}\n\nexport function isSimpleObject(value: unknown) {\n const isObject = typeof value === \"object\";\n const prototype = Object.getPrototypeOf(value);\n const isSimple =\n prototype === null ||\n prototype === Object.prototype ||\n // Objects generated from other contexts (e.g. across Node.js `vm` modules) will not satisfy the previous\n // conditions but are still simple objects.\n prototype?.constructor?.name === \"Object\";\n return isObject && isSimple;\n}\n\n// Commit timestamp placeholders are resolved to `i64::MAX` before commit.\nexport const MAX_COMMIT_TS = 2n ** 63n - 1n;\n\nexport function hasPendingValues(value: Value): boolean {\n if (value instanceof CommitTsPlaceholder) {\n return true;\n }\n if (Array.isArray(value)) {\n return value.some(hasPendingValues);\n }\n if (value !== null && value !== undefined && isSimpleObject(value)) {\n return Object.values(value).some(hasPendingValues);\n }\n return false;\n}\n\n// The value as the transaction sees it before committing.\nexport function resolvePendingValues(value: Value): Value {\n return substituteCommitTs(value, MAX_COMMIT_TS);\n}\n\n// The value as it is committed.\nexport function resolveCommittedValues<T extends Value>(\n value: T,\n commitTs: bigint,\n): T {\n return substituteCommitTs(value, commitTs);\n}\n\nexport function substituteCommitTs<T extends Value>(\n value: T,\n commitTs: bigint,\n): T {\n if (!hasPendingValues(value)) {\n return value;\n }\n if (value instanceof CommitTsPlaceholder) {\n return commitTs as T;\n }\n if (Array.isArray(value)) {\n return value.map((v) => substituteCommitTs(v, commitTs)) as T;\n }\n return Object.fromEntries(\n Object.entries(value as object).map(([k, v]) => [\n k,\n substituteCommitTs(v, commitTs),\n ]),\n ) as T;\n}\n\nexport function evaluateFieldPath(fieldPath: string, document: any) {\n const pathParts = fieldPath.split(\".\");\n let result: Value | undefined = document;\n for (const p of pathParts) {\n result =\n result !== undefined && result !== null && isSimpleObject(result)\n ? (result as any)[p]\n : undefined;\n }\n return result === undefined ? undefined : resolvePendingValues(result);\n}\n\nexport function evaluateFilter(\n document: GenericDocument,\n filter: any,\n): Value | undefined {\n if (filter.$eq !== undefined) {\n return (\n compareValues(\n evaluateFilter(document, filter.$eq[0]),\n evaluateFilter(document, filter.$eq[1]),\n ) === 0\n );\n }\n if (filter.$neq !== undefined) {\n return (\n compareValues(\n evaluateFilter(document, filter.$neq[0]),\n evaluateFilter(document, filter.$neq[1]),\n ) !== 0\n );\n }\n if (filter.$and !== undefined) {\n return filter.$and.every((child: any) => evaluateFilter(document, child));\n }\n if (filter.$or !== undefined) {\n return filter.$or.some((child: any) => evaluateFilter(document, child));\n }\n if (filter.$not !== undefined) {\n return !evaluateFilter(document, filter.$not);\n }\n if (filter.$gt !== undefined) {\n return (\n evaluateFilter(document, filter.$gt[0])! >\n evaluateFilter(document, filter.$gt[1])!\n );\n }\n if (filter.$gte !== undefined) {\n return (\n evaluateFilter(document, filter.$gte[0])! >=\n evaluateFilter(document, filter.$gte[1])!\n );\n }\n if (filter.$lt !== undefined) {\n return (\n evaluateFilter(document, filter.$lt[0])! <\n evaluateFilter(document, filter.$lt[1])!\n );\n }\n if (filter.$lte !== undefined) {\n return (\n evaluateFilter(document, filter.$lte[0])! <=\n evaluateFilter(document, filter.$lte[1])!\n );\n }\n if (filter.$add !== undefined) {\n return (\n (evaluateFilter(document, filter.$add[0]) as number) +\n (evaluateFilter(document, filter.$add[1]) as number)\n );\n }\n if (filter.$sub !== undefined) {\n return (\n (evaluateFilter(document, filter.$sub[0]) as number) -\n (evaluateFilter(document, filter.$sub[1]) as number)\n );\n }\n if (filter.$mul !== undefined) {\n return (\n (evaluateFilter(document, filter.$mul[0]) as number) *\n (evaluateFilter(document, filter.$mul[1]) as number)\n );\n }\n if (filter.$div !== undefined) {\n return (\n (evaluateFilter(document, filter.$div[0]) as number) /\n (evaluateFilter(document, filter.$div[1]) as number)\n );\n }\n if (filter.$mod !== undefined) {\n return (\n (evaluateFilter(document, filter.$mod[0]) as number) %\n (evaluateFilter(document, filter.$mod[1]) as number)\n );\n }\n if (filter.$field !== undefined) {\n return evaluateFieldPath(filter.$field, document);\n }\n if (filter.$literal !== undefined) {\n return evaluateQueryValue(filter.$literal);\n }\n throw new Error(`not implemented: ${JSON.stringify(filter)}`);\n}\n\nexport function evaluateRangeFilter(\n document: GenericDocument,\n expr: SerializedRangeExpression,\n) {\n const result = evaluateFieldPath(expr.fieldPath, document);\n const value = evaluateQueryValue(expr.value);\n switch (expr.type) {\n case \"Eq\":\n return compareValues(result, value) === 0;\n case \"Gt\":\n return compareValues(result, value) > 0;\n case \"Gte\":\n return compareValues(result, value) >= 0;\n case \"Lt\":\n return compareValues(result, value) < 0;\n case \"Lte\":\n return compareValues(result, value) <= 0;\n }\n}\n\n// A value used as a written field.\nexport function evaluateValue(value: JSONValue) {\n if (typeof value === \"object\" && value !== null && \"$undefined\" in value) {\n return undefined;\n }\n return jsonToConvex(value);\n}\n\n// A value used as a query bound or literal.\nexport function evaluateQueryValue(value: JSONValue) {\n const evaluatedValue = evaluateValue(value);\n return evaluatedValue === undefined\n ? undefined\n : resolvePendingValues(evaluatedValue);\n}\n\n// An index range expression is always a chained list of:\n//\n// 0 or more equality expressions defined with .eq.\n// [Optionally] A lower bound expression defined with .gt or .gte.\n// [Optionally] An upper bound expression defined with .lt or .lte.\nexport function validateIndexRangeExpression(\n source: Source & { type: \"IndexRange\" },\n fields: string[],\n) {\n let fieldIndex = 0;\n let state: \"eq\" | \"gt\" | \"lt\" | \"done\" = \"eq\";\n for (const [filterIndex, filter] of source.range.entries()) {\n if (state === \"done\") {\n throw new Error(\n `Incorrect operator used in \\`withIndex\\`, cannot chain ` +\n `more operators after both \\`.gt\\` and \\`.lt\\` were already used, ` +\n `got \\`${printIndexOperator(filter)}\\`.`,\n );\n }\n\n const filterType: \"eq\" | \"gt\" | \"lt\" =\n filter.type == \"Gt\" || filter.type == \"Gte\"\n ? \"gt\"\n : filter.type == \"Lt\" || filter.type == \"Lte\"\n ? \"lt\"\n : \"eq\";\n\n switch (`${state}|${filterType}`) {\n // Allow to operate on the current indexed field\n case \"eq|eq\":\n case \"eq|gt\":\n case \"eq|lt\":\n if (filter.fieldPath === fields[fieldIndex]) {\n fieldIndex += 1;\n state = filterType;\n continue;\n }\n throw new Error(\n `Incorrect field used in \\`withIndex\\`, ` +\n `expected \"${fields[fieldIndex]}\", got \"${filter.fieldPath}\"`,\n );\n\n // Allow to operate on the previous field (gt and lt must operate on same field)\n case \"lt|gt\":\n case \"gt|lt\":\n if (fieldIndex > 0 && filter.fieldPath === fields[fieldIndex - 1]) {\n state = \"done\";\n continue;\n }\n throw new Error(\n `Incorrect field used in \\`withIndex\\`, ` +\n `\\`.gt\\` and \\`.lt\\` must operate on the same field, ` +\n `expected \"${fields[fieldIndex - 1]}\", got \"${filter.fieldPath}\"`,\n );\n\n default:\n throw new Error(\n `Incorrect operator used in \\`withIndex\\`, ` +\n `cannot chain \\`.${filter.type.toLowerCase()}()\\` ` +\n `after \\`.${source.range[filterIndex - 1].type.toLowerCase()}()\\``,\n );\n }\n }\n}\n\nexport function printIndexOperator(filter: SerializedRangeExpression) {\n return `.${filter.type.toLowerCase()}(${filter.fieldPath}, ${JSON.stringify(filter.value)})`;\n}\n\nexport function validateFieldNames(validator: ValidatorJSON) {\n if (validator.type === \"object\") {\n Object.keys(validator.value).forEach((fieldName) => {\n if (!isValidIdentifier(fieldName)) {\n throw new Error(\n `Field names must be valid identifiers, got \"${fieldName}\"`,\n );\n }\n });\n }\n\n if (validator.type === \"union\") {\n validator.value.forEach(validateFieldNames);\n }\n}\n\nexport function isValidIdentifier(name: string) {\n return /(^_(id|creationTime)$)|^[a-zA-Z][a-zA-Z0-9_]*$/.test(name);\n}\n\nexport type ObjectFieldType = { fieldType: ValidatorJSON; optional: boolean };\n\nexport type ValidatorJSON =\n | {\n type: \"null\";\n }\n | { type: \"number\" }\n | { type: \"bigint\" }\n | { type: \"commitTs\" }\n | { type: \"boolean\" }\n | { type: \"string\" }\n | { type: \"bytes\" }\n | { type: \"any\" }\n | {\n type: \"literal\";\n value: JSONValue;\n }\n | { type: \"id\"; tableName: string }\n | { type: \"array\"; value: ValidatorJSON }\n | { type: \"object\"; value: Record<string, ObjectFieldType> }\n | { type: \"record\"; keys: ValidatorJSON; values: ObjectFieldType }\n | { type: \"union\"; value: ValidatorJSON[] };\n\nexport function validateValidator(validator: ValidatorJSON, value: any) {\n switch (validator.type) {\n case \"null\": {\n if (value !== null) {\n throw new Error(`Validator error: Expected \\`null\\`, got \\`${value}\\``);\n }\n return;\n }\n case \"number\": {\n if (typeof value !== \"number\") {\n throw new Error(\n `Validator error: Expected \\`number\\`, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"bigint\": {\n if (typeof value !== \"bigint\") {\n throw new Error(\n `Validator error: Expected \\`bigint\\`, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"commitTs\": {\n if (\n typeof value !== \"bigint\" &&\n !(value instanceof CommitTsPlaceholder)\n ) {\n throw new Error(\n `Validator error: Expected \\`v.commitTs()\\`, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"boolean\": {\n if (typeof value !== \"boolean\") {\n throw new Error(\n `Validator error: Expected \\`boolean\\`, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"string\": {\n if (typeof value !== \"string\") {\n throw new Error(\n `Validator error: Expected \\`string\\`, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"bytes\": {\n if (!(value instanceof ArrayBuffer)) {\n throw new Error(\n `Validator error: Expected \\`ArrayBuffer\\`, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"any\": {\n return;\n }\n case \"literal\": {\n if (value !== validator.value) {\n throw new Error(\n `Validator error: Expected \\`${\n validator.value as any\n }\\`, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"id\": {\n if (typeof value !== \"string\") {\n throw new Error(\n `Validator error: Expected \\`string\\`, got \\`${value}\\``,\n );\n }\n if (tableNameFromId(value) !== validator.tableName) {\n throw new Error(\n `Validator error: Expected ID for table \"${validator.tableName}\", got \\`${value}\\``,\n );\n }\n return;\n }\n case \"array\": {\n if (!Array.isArray(value)) {\n throw new Error(\n `Validator error: Expected \\`Array\\`, got \\`${value}\\``,\n );\n }\n for (const v of value) {\n validateValidator(validator.value, v);\n }\n return;\n }\n case \"union\": {\n let isValid = false;\n for (const v of validator.value) {\n try {\n validateValidator(v, value);\n isValid = true;\n break;\n } catch {\n // Ignore\n }\n }\n if (!isValid) {\n throw new Error(\n `Validator error: Expected one of ${validator.value.map((v) => v.type).join(\", \")}, got \\`${JSON.stringify(convexToJson(value))}\\``,\n );\n }\n return;\n }\n case \"object\": {\n if (typeof value !== \"object\") {\n throw new Error(\n `Validator error: Expected \\`object\\`, got \\`${value}\\``,\n );\n }\n if (!isSimpleObject(value)) {\n throw new Error(\n `Validator error: Expected a plain old JavaScript \\`object\\`, got \\`${value}\\``,\n );\n }\n for (const [k, { fieldType, optional }] of Object.entries(\n validator.value,\n )) {\n if (value[k] === undefined) {\n if (!optional) {\n throw new Error(\n `Validator error: Missing required field \\`${k}\\` in object`,\n );\n }\n } else {\n validateValidator(fieldType, value[k]);\n }\n }\n for (const k of Object.keys(value)) {\n if (validator.value[k] === undefined) {\n throw new Error(\n `Validator error: Unexpected field \\`${k}\\` in object`,\n );\n }\n }\n return;\n }\n case \"record\": {\n if (\n value === null ||\n typeof value !== \"object\" ||\n !isSimpleObject(value)\n ) {\n throw new Error(\n `Validator error: Expected a plain old JavaScript \\`object\\` for a record, got \\`${value}\\``,\n );\n }\n for (const [k, fieldValue] of Object.entries(value)) {\n validateValidator(validator.keys, k);\n if (fieldValue !== undefined) {\n validateValidator(validator.values.fieldType, fieldValue);\n }\n }\n return;\n }\n default: {\n const unknown: never = validator;\n throw new Error(\n `The test kernel cannot check a validator of type \\`${(unknown as { type: string }).type}\\`.`,\n );\n }\n }\n}\n\nexport const JWT_CLAIM_BY_IDENTITY_ATTRIBUTE: Record<string, string> = {\n subject: \"sub\",\n issuer: \"iss\",\n name: \"name\",\n givenName: \"given_name\",\n familyName: \"family_name\",\n nickname: \"nickname\",\n preferredUsername: \"preferred_username\",\n profileUrl: \"profile\",\n pictureUrl: \"picture\",\n email: \"email\",\n emailVerified: \"email_verified\",\n gender: \"gender\",\n birthday: \"birthdate\",\n timezone: \"zoneinfo\",\n language: \"locale\",\n phoneNumber: \"phone_number\",\n phoneNumberVerified: \"phone_number_verified\",\n address: \"address\",\n updatedAt: \"updated_at\",\n};\n\nexport function base64UrlEncode(value: string) {\n return base64UrlEncodeBytes(new TextEncoder().encode(value));\n}\n\nexport function base64UrlEncodeBytes(bytes: Uint8Array) {\n let binary = \"\";\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\n// Builds the token `withIdentity` pretends the request was authenticated with.\n// Real tokens are signed by an identity provider; this one is unsecured\n// (`\"alg\": \"none\"`, empty signature).\nexport function createTestJwt(identity: Record<string, any>) {\n const claims: Record<string, any> = Object.create(null);\n for (const [attribute, value] of Object.entries(identity)) {\n if (value === undefined || attribute === \"tokenIdentifier\") {\n continue;\n }\n // Custom claims are used as is, known attributes use their claim name.\n const claimName = Object.hasOwn(JWT_CLAIM_BY_IDENTITY_ATTRIBUTE, attribute)\n ? JWT_CLAIM_BY_IDENTITY_ATTRIBUTE[attribute]\n : attribute;\n claims[claimName] = value;\n }\n // The identity provider always determines who the user is.\n claims.iss = identity.issuer;\n claims.sub = identity.subject;\n const header = base64UrlEncode(JSON.stringify({ alg: \"none\", typ: \"JWT\" }));\n const payload = base64UrlEncode(JSON.stringify(claims));\n return `${header}.${payload}.`;\n}\n\n// Limits the backend puts on tokens from `ctx.auth.signJwt`.\nexport function parseArgs(\n args: Record<string, Value> | undefined,\n): Record<string, Value> {\n if (args === undefined) {\n return {};\n }\n if (!isSimpleObject(args)) {\n throw new Error(\n `The arguments to a Convex function must be an object. Received: ${\n args as any\n }`,\n );\n }\n return args;\n}\n\nexport function validateFunctionArgs(\n argsValidator: ValidatorJSON,\n args: any,\n functionPath: FunctionPath,\n allowPendingValues: boolean,\n) {\n if (!allowPendingValues && hasPendingValues(args)) {\n throw new Error(\n `Invalid arguments for \"${functionPath.udfPath}\": an unresolved ` +\n `placeholder value can only be passed to a query or mutation ` +\n `called from a mutation`,\n );\n }\n validateValidator(argsValidator, resolvePendingValues(args));\n}\n\nexport function validateReturnValue(\n returnsValidator: ValidatorJSON | null,\n result: any,\n functionType: \"query\" | \"mutation\" | \"action\",\n functionPath: FunctionPath,\n) {\n if (returnsValidator !== null) {\n try {\n validateValidator(returnsValidator, resolvePendingValues(result));\n } catch (e) {\n throw new Error(\n `Return value validation failed for ${functionType} \"${functionPath.udfPath}\":\\n${(e as Error).message}`,\n { cause: e },\n );\n }\n }\n}\n\nexport function createFunctionHandle(functionPath: FunctionPath) {\n return `function://${functionPath.componentPath};${functionPath.udfPath}`;\n}\n\nexport function parseFunctionHandle(handle: string) {\n const [componentPath, udfPath] = handle.split(\"function://\")[1].split(\";\");\n return { componentPath, udfPath };\n}\nexport function simpleHash(string: string) {\n let hash = 0;\n for (let i = 0; i < string.length; i++) {\n const char = string.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return hash;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"KernelValues.js","names":["unknown"],"sources":["../../src/internal/KernelValues.ts"],"sourcesContent":["// Derived from convex-test 0.0.65 (index.ts), Copyright Convex, Inc.,\n// licensed under the Apache License, Version 2.0\n// (https://www.apache.org/licenses/LICENSE-2.0). The value, filter, range,\n// validator and argument rules are kept as convex-test states them, so the\n// kernel answers a function the way convex-test does. The storage, attempts\n// and execution around them are the kernel's own.\n\nimport type { GenericDocument } from \"convex/server\";\nimport {\n CommitTsPlaceholder,\n type GenericId,\n type JSONValue,\n type Value,\n compareValues,\n convexToJson,\n jsonToConvex,\n} from \"convex/values\";\n\n/**\n * A function address inside one test backend: its component and its\n * `module:export` path.\n */\nexport type FunctionPath = {\n readonly componentPath: string;\n readonly udfPath: string;\n};\n\nexport type FilterJson =\n | { $eq: [FilterJson, FilterJson] }\n | { $field: string }\n | JSONValue;\n\nexport type QueryOperator = { filter: FilterJson } | { limit: number };\nexport type Source =\n | { type: \"FullTableScan\"; tableName: string; order: \"asc\" | \"desc\" | null }\n | {\n type: \"IndexRange\";\n indexName: string;\n range: ReadonlyArray<SerializedRangeExpression>;\n order: \"asc\" | \"desc\" | null;\n }\n | {\n type: \"Search\";\n indexName: string;\n filters: ReadonlyArray<SerializedSearchFilter>;\n };\n\nexport type SerializedQuery = {\n source: Source;\n operators: Array<QueryOperator>;\n};\n\nexport type SerializedRangeExpression = {\n type: \"Eq\" | \"Gt\" | \"Gte\" | \"Lt\" | \"Lte\";\n fieldPath: string;\n value: JSONValue;\n};\n\nexport type SerializedSearchFilter =\n | {\n type: \"Search\";\n fieldPath: string;\n value: string;\n }\n | {\n type: \"Eq\";\n fieldPath: string;\n value: JSONValue;\n };\n\nexport type StoredDocument = GenericDocument & {\n _id: DocumentId;\n _creationTime: number;\n};\n\nexport type Index = {\n indexDescriptor: string;\n fields: string[];\n};\n\nexport type TableName = string;\n\nexport type DocumentId = GenericId<TableName>;\n\nexport function tableNameFromId(id: string) {\n const match = id.match(/^[0-9]+/);\n if (!match || match[0].length === id.length) {\n return null;\n }\n return id.slice(match[0].length);\n}\nexport function deserializeConvexErrorData(thrown: unknown) {\n if (\n typeof thrown === \"object\" &&\n thrown !== null &&\n Symbol.for(\"ConvexError\") in thrown &&\n typeof (thrown as { data: unknown }).data === \"string\"\n ) {\n try {\n (thrown as { data: unknown }).data = jsonToConvex(\n JSON.parse((thrown as { data: string }).data),\n );\n } catch {\n // Data wasn't a serialized JSON string — leave it untouched.\n }\n }\n return thrown;\n}\n\nexport function isSimpleObject(value: unknown) {\n const isObject = typeof value === \"object\";\n const prototype = Object.getPrototypeOf(value);\n const isSimple =\n prototype === null ||\n prototype === Object.prototype ||\n // Objects generated from other contexts (e.g. across Node.js `vm` modules) will not satisfy the previous\n // conditions but are still simple objects.\n prototype?.constructor?.name === \"Object\";\n return isObject && isSimple;\n}\n\n// Commit timestamp placeholders are resolved to `i64::MAX` before commit.\nexport const MAX_COMMIT_TS = 2n ** 63n - 1n;\n\nexport function hasPendingValues(value: Value): boolean {\n if (value instanceof CommitTsPlaceholder) {\n return true;\n }\n if (Array.isArray(value)) {\n return value.some(hasPendingValues);\n }\n if (value !== null && value !== undefined && isSimpleObject(value)) {\n return Object.values(value).some(hasPendingValues);\n }\n return false;\n}\n\n// The value as the transaction sees it before committing.\nexport function resolvePendingValues(value: Value): Value {\n return substituteCommitTs(value, MAX_COMMIT_TS);\n}\n\n// The value as it is committed.\nexport function resolveCommittedValues<T extends Value>(\n value: T,\n commitTs: bigint,\n): T {\n return substituteCommitTs(value, commitTs);\n}\n\nexport function substituteCommitTs<T extends Value>(\n value: T,\n commitTs: bigint,\n): T {\n if (!hasPendingValues(value)) {\n return value;\n }\n if (value instanceof CommitTsPlaceholder) {\n return commitTs as T;\n }\n if (Array.isArray(value)) {\n return value.map((v) => substituteCommitTs(v, commitTs)) as T;\n }\n return Object.fromEntries(\n Object.entries(value as object).map(([k, v]) => [\n k,\n substituteCommitTs(v, commitTs),\n ]),\n ) as T;\n}\n\nexport function evaluateFieldPath(fieldPath: string, document: any) {\n const pathParts = fieldPath.split(\".\");\n let result: Value | undefined = document;\n for (const p of pathParts) {\n result =\n result !== undefined && result !== null && isSimpleObject(result)\n ? (result as any)[p]\n : undefined;\n }\n return result === undefined ? undefined : resolvePendingValues(result);\n}\n\nexport function evaluateFilter(\n document: GenericDocument,\n filter: any,\n): Value | undefined {\n if (filter.$eq !== undefined) {\n return (\n compareValues(\n evaluateFilter(document, filter.$eq[0]),\n evaluateFilter(document, filter.$eq[1]),\n ) === 0\n );\n }\n if (filter.$neq !== undefined) {\n return (\n compareValues(\n evaluateFilter(document, filter.$neq[0]),\n evaluateFilter(document, filter.$neq[1]),\n ) !== 0\n );\n }\n if (filter.$and !== undefined) {\n return filter.$and.every((child: any) => evaluateFilter(document, child));\n }\n if (filter.$or !== undefined) {\n return filter.$or.some((child: any) => evaluateFilter(document, child));\n }\n if (filter.$not !== undefined) {\n return !evaluateFilter(document, filter.$not);\n }\n if (filter.$gt !== undefined) {\n return (\n evaluateFilter(document, filter.$gt[0])! >\n evaluateFilter(document, filter.$gt[1])!\n );\n }\n if (filter.$gte !== undefined) {\n return (\n evaluateFilter(document, filter.$gte[0])! >=\n evaluateFilter(document, filter.$gte[1])!\n );\n }\n if (filter.$lt !== undefined) {\n return (\n evaluateFilter(document, filter.$lt[0])! <\n evaluateFilter(document, filter.$lt[1])!\n );\n }\n if (filter.$lte !== undefined) {\n return (\n evaluateFilter(document, filter.$lte[0])! <=\n evaluateFilter(document, filter.$lte[1])!\n );\n }\n if (filter.$add !== undefined) {\n return (\n (evaluateFilter(document, filter.$add[0]) as number) +\n (evaluateFilter(document, filter.$add[1]) as number)\n );\n }\n if (filter.$sub !== undefined) {\n return (\n (evaluateFilter(document, filter.$sub[0]) as number) -\n (evaluateFilter(document, filter.$sub[1]) as number)\n );\n }\n if (filter.$mul !== undefined) {\n return (\n (evaluateFilter(document, filter.$mul[0]) as number) *\n (evaluateFilter(document, filter.$mul[1]) as number)\n );\n }\n if (filter.$div !== undefined) {\n return (\n (evaluateFilter(document, filter.$div[0]) as number) /\n (evaluateFilter(document, filter.$div[1]) as number)\n );\n }\n if (filter.$mod !== undefined) {\n return (\n (evaluateFilter(document, filter.$mod[0]) as number) %\n (evaluateFilter(document, filter.$mod[1]) as number)\n );\n }\n if (filter.$field !== undefined) {\n return evaluateFieldPath(filter.$field, document);\n }\n if (filter.$literal !== undefined) {\n return evaluateQueryValue(filter.$literal);\n }\n throw new Error(`not implemented: ${JSON.stringify(filter)}`);\n}\n\nexport function evaluateRangeFilter(\n document: GenericDocument,\n expr: SerializedRangeExpression,\n) {\n const result = evaluateFieldPath(expr.fieldPath, document);\n const value = evaluateQueryValue(expr.value);\n switch (expr.type) {\n case \"Eq\":\n return compareValues(result, value) === 0;\n case \"Gt\":\n return compareValues(result, value) > 0;\n case \"Gte\":\n return compareValues(result, value) >= 0;\n case \"Lt\":\n return compareValues(result, value) < 0;\n case \"Lte\":\n return compareValues(result, value) <= 0;\n }\n}\n\n// A value used as a written field.\nexport function evaluateValue(value: JSONValue) {\n if (typeof value === \"object\" && value !== null && \"$undefined\" in value) {\n return undefined;\n }\n return jsonToConvex(value);\n}\n\n// A value used as a query bound or literal.\nexport function evaluateQueryValue(value: JSONValue) {\n const evaluatedValue = evaluateValue(value);\n return evaluatedValue === undefined\n ? undefined\n : resolvePendingValues(evaluatedValue);\n}\n\n// An index range expression is always a chained list of:\n//\n// 0 or more equality expressions defined with .eq.\n// [Optionally] A lower bound expression defined with .gt or .gte.\n// [Optionally] An upper bound expression defined with .lt or .lte.\nexport function validateIndexRangeExpression(\n source: Source & { type: \"IndexRange\" },\n fields: string[],\n) {\n let fieldIndex = 0;\n let state: \"eq\" | \"gt\" | \"lt\" | \"done\" = \"eq\";\n for (const [filterIndex, filter] of source.range.entries()) {\n if (state === \"done\") {\n throw new Error(\n `Incorrect operator used in \\`withIndex\\`, cannot chain ` +\n `more operators after both \\`.gt\\` and \\`.lt\\` were already used, ` +\n `got \\`${printIndexOperator(filter)}\\`.`,\n );\n }\n\n const filterType: \"eq\" | \"gt\" | \"lt\" =\n filter.type == \"Gt\" || filter.type == \"Gte\"\n ? \"gt\"\n : filter.type == \"Lt\" || filter.type == \"Lte\"\n ? \"lt\"\n : \"eq\";\n\n switch (`${state}|${filterType}`) {\n // Allow to operate on the current indexed field\n case \"eq|eq\":\n case \"eq|gt\":\n case \"eq|lt\":\n if (filter.fieldPath === fields[fieldIndex]) {\n fieldIndex += 1;\n state = filterType;\n continue;\n }\n throw new Error(\n `Incorrect field used in \\`withIndex\\`, ` +\n `expected \"${fields[fieldIndex]}\", got \"${filter.fieldPath}\"`,\n );\n\n // Allow to operate on the previous field (gt and lt must operate on same field)\n case \"lt|gt\":\n case \"gt|lt\":\n if (fieldIndex > 0 && filter.fieldPath === fields[fieldIndex - 1]) {\n state = \"done\";\n continue;\n }\n throw new Error(\n `Incorrect field used in \\`withIndex\\`, ` +\n `\\`.gt\\` and \\`.lt\\` must operate on the same field, ` +\n `expected \"${fields[fieldIndex - 1]}\", got \"${filter.fieldPath}\"`,\n );\n\n default:\n throw new Error(\n `Incorrect operator used in \\`withIndex\\`, ` +\n `cannot chain \\`.${filter.type.toLowerCase()}()\\` ` +\n `after \\`.${source.range[filterIndex - 1].type.toLowerCase()}()\\``,\n );\n }\n }\n}\n\nexport function printIndexOperator(filter: SerializedRangeExpression) {\n return `.${filter.type.toLowerCase()}(${filter.fieldPath}, ${JSON.stringify(filter.value)})`;\n}\n\nexport function validateFieldNames(validator: ValidatorJSON) {\n if (validator.type === \"object\") {\n Object.keys(validator.value).forEach((fieldName) => {\n if (!isValidIdentifier(fieldName)) {\n throw new Error(\n `Field names must be valid identifiers, got \"${fieldName}\"`,\n );\n }\n });\n }\n\n if (validator.type === \"union\") {\n validator.value.forEach(validateFieldNames);\n }\n}\n\nexport function isValidIdentifier(name: string) {\n return /(^_(id|creationTime)$)|^[a-zA-Z][a-zA-Z0-9_]*$/.test(name);\n}\n\nexport type ObjectFieldType = { fieldType: ValidatorJSON; optional: boolean };\n\nexport type ValidatorJSON =\n | {\n type: \"null\";\n }\n | { type: \"number\" }\n | { type: \"bigint\" }\n | { type: \"commitTs\" }\n | { type: \"boolean\" }\n | { type: \"string\" }\n | { type: \"bytes\" }\n | { type: \"any\" }\n | {\n type: \"literal\";\n value: JSONValue;\n }\n | { type: \"id\"; tableName: string }\n | { type: \"array\"; value: ValidatorJSON }\n | { type: \"object\"; value: Record<string, ObjectFieldType> }\n | { type: \"record\"; keys: ValidatorJSON; values: ObjectFieldType }\n | {\n type: \"function\";\n fnType?: string;\n args?: ValidatorJSON;\n returns?: ValidatorJSON;\n }\n | { type: \"union\"; value: ValidatorJSON[] };\n\nexport function validateValidator(validator: ValidatorJSON, value: any) {\n switch (validator.type) {\n case \"null\": {\n if (value !== null) {\n throw new Error(`Validator error: Expected \\`null\\`, got \\`${value}\\``);\n }\n return;\n }\n case \"number\": {\n if (typeof value !== \"number\") {\n throw new Error(\n `Validator error: Expected \\`number\\`, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"bigint\": {\n if (typeof value !== \"bigint\") {\n throw new Error(\n `Validator error: Expected \\`bigint\\`, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"commitTs\": {\n if (\n typeof value !== \"bigint\" &&\n !(value instanceof CommitTsPlaceholder)\n ) {\n throw new Error(\n `Validator error: Expected \\`v.commitTs()\\`, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"boolean\": {\n if (typeof value !== \"boolean\") {\n throw new Error(\n `Validator error: Expected \\`boolean\\`, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"string\": {\n if (typeof value !== \"string\") {\n throw new Error(\n `Validator error: Expected \\`string\\`, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"bytes\": {\n if (!(value instanceof ArrayBuffer)) {\n throw new Error(\n `Validator error: Expected \\`ArrayBuffer\\`, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"any\": {\n return;\n }\n case \"literal\": {\n if (value !== validator.value) {\n throw new Error(\n `Validator error: Expected \\`${\n validator.value as any\n }\\`, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"id\": {\n if (typeof value !== \"string\") {\n throw new Error(\n `Validator error: Expected \\`string\\`, got \\`${value}\\``,\n );\n }\n if (tableNameFromId(value) !== validator.tableName) {\n throw new Error(\n `Validator error: Expected ID for table \"${validator.tableName}\", got \\`${value}\\``,\n );\n }\n return;\n }\n case \"array\": {\n if (!Array.isArray(value)) {\n throw new Error(\n `Validator error: Expected \\`Array\\`, got \\`${value}\\``,\n );\n }\n for (const v of value) {\n validateValidator(validator.value, v);\n }\n return;\n }\n case \"union\": {\n let isValid = false;\n for (const v of validator.value) {\n try {\n validateValidator(v, value);\n isValid = true;\n break;\n } catch {\n // Ignore\n }\n }\n if (!isValid) {\n throw new Error(\n `Validator error: Expected one of ${validator.value.map((v) => v.type).join(\", \")}, got \\`${JSON.stringify(convexToJson(value))}\\``,\n );\n }\n return;\n }\n case \"object\": {\n if (typeof value !== \"object\") {\n throw new Error(\n `Validator error: Expected \\`object\\`, got \\`${value}\\``,\n );\n }\n if (!isSimpleObject(value)) {\n throw new Error(\n `Validator error: Expected a plain old JavaScript \\`object\\`, got \\`${value}\\``,\n );\n }\n for (const [k, { fieldType, optional }] of Object.entries(\n validator.value,\n )) {\n if (value[k] === undefined) {\n if (!optional) {\n throw new Error(\n `Validator error: Missing required field \\`${k}\\` in object`,\n );\n }\n } else {\n validateValidator(fieldType, value[k]);\n }\n }\n for (const k of Object.keys(value)) {\n if (validator.value[k] === undefined) {\n throw new Error(\n `Validator error: Unexpected field \\`${k}\\` in object`,\n );\n }\n }\n return;\n }\n case \"function\": {\n // The pinned binary, `precompiled-2026-10-28-cccbade`, accepts a string\n // that parses as a function handle\n // (`common/src/schemas/validator.rs:156-164`), and its parser requires a\n // document id after the prefix\n // (`common/src/bootstrap_model/components/handles.rs:100-112`). The\n // kernel mints convex-test's handle format instead, so it accepts a\n // string that parses as that format.\n if (\n typeof value !== \"string\" ||\n readFunctionHandle(value) === undefined\n ) {\n throw new Error(\n `Validator error: Expected a function handle, got \\`${value}\\``,\n );\n }\n return;\n }\n case \"record\": {\n if (\n value === null ||\n typeof value !== \"object\" ||\n !isSimpleObject(value)\n ) {\n throw new Error(\n `Validator error: Expected a plain old JavaScript \\`object\\` for a record, got \\`${value}\\``,\n );\n }\n for (const [k, fieldValue] of Object.entries(value)) {\n validateValidator(validator.keys, k);\n if (fieldValue !== undefined) {\n validateValidator(validator.values.fieldType, fieldValue);\n }\n }\n return;\n }\n default: {\n const unknown: never = validator;\n throw new Error(\n `The test kernel cannot check a validator of type \\`${(unknown as { type: string }).type}\\`.`,\n );\n }\n }\n}\n\nexport const JWT_CLAIM_BY_IDENTITY_ATTRIBUTE: Record<string, string> = {\n subject: \"sub\",\n issuer: \"iss\",\n name: \"name\",\n givenName: \"given_name\",\n familyName: \"family_name\",\n nickname: \"nickname\",\n preferredUsername: \"preferred_username\",\n profileUrl: \"profile\",\n pictureUrl: \"picture\",\n email: \"email\",\n emailVerified: \"email_verified\",\n gender: \"gender\",\n birthday: \"birthdate\",\n timezone: \"zoneinfo\",\n language: \"locale\",\n phoneNumber: \"phone_number\",\n phoneNumberVerified: \"phone_number_verified\",\n address: \"address\",\n updatedAt: \"updated_at\",\n};\n\nexport function base64UrlEncode(value: string) {\n return base64UrlEncodeBytes(new TextEncoder().encode(value));\n}\n\nexport function base64UrlEncodeBytes(bytes: Uint8Array) {\n let binary = \"\";\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\n// Builds the token `withIdentity` pretends the request was authenticated with.\n// Real tokens are signed by an identity provider; this one is unsecured\n// (`\"alg\": \"none\"`, empty signature).\nexport function createTestJwt(identity: Record<string, any>) {\n const claims: Record<string, any> = Object.create(null);\n for (const [attribute, value] of Object.entries(identity)) {\n if (value === undefined || attribute === \"tokenIdentifier\") {\n continue;\n }\n // Custom claims are used as is, known attributes use their claim name.\n const claimName = Object.hasOwn(JWT_CLAIM_BY_IDENTITY_ATTRIBUTE, attribute)\n ? JWT_CLAIM_BY_IDENTITY_ATTRIBUTE[attribute]\n : attribute;\n claims[claimName] = value;\n }\n // The identity provider always determines who the user is.\n claims.iss = identity.issuer;\n claims.sub = identity.subject;\n const header = base64UrlEncode(JSON.stringify({ alg: \"none\", typ: \"JWT\" }));\n const payload = base64UrlEncode(JSON.stringify(claims));\n return `${header}.${payload}.`;\n}\n\n// Limits the backend puts on tokens from `ctx.auth.signJwt`.\nexport function parseArgs(\n args: Record<string, Value> | undefined,\n): Record<string, Value> {\n if (args === undefined) {\n return {};\n }\n if (!isSimpleObject(args)) {\n throw new Error(\n `The arguments to a Convex function must be an object. Received: ${\n args as any\n }`,\n );\n }\n return args;\n}\n\nexport function validateFunctionArgs(\n argsValidator: ValidatorJSON,\n args: any,\n functionPath: FunctionPath,\n allowPendingValues: boolean,\n) {\n if (!allowPendingValues && hasPendingValues(args)) {\n throw new Error(\n `Invalid arguments for \"${functionPath.udfPath}\": an unresolved ` +\n `placeholder value can only be passed to a query or mutation ` +\n `called from a mutation`,\n );\n }\n validateValidator(argsValidator, resolvePendingValues(args));\n}\n\nexport function validateReturnValue(\n returnsValidator: ValidatorJSON | null,\n result: any,\n functionType: \"query\" | \"mutation\" | \"action\",\n functionPath: FunctionPath,\n) {\n if (returnsValidator !== null) {\n try {\n validateValidator(returnsValidator, resolvePendingValues(result));\n } catch (e) {\n throw new Error(\n `Return value validation failed for ${functionType} \"${functionPath.udfPath}\":\\n${(e as Error).message}`,\n { cause: e },\n );\n }\n }\n}\n\nconst FUNCTION_HANDLE_PREFIX = \"function://\";\n\nexport function createFunctionHandle(functionPath: FunctionPath) {\n return `${FUNCTION_HANDLE_PREFIX}${functionPath.componentPath};${functionPath.udfPath}`;\n}\n\n/**\n * The function a handle the kernel minted names, or `undefined` for a string\n * that is not `function://<componentPath>;<udfPath>` with a function path.\n */\nfunction readFunctionHandle(handle: string): FunctionPath | undefined {\n if (!handle.startsWith(FUNCTION_HANDLE_PREFIX)) return undefined;\n const parts = handle.slice(FUNCTION_HANDLE_PREFIX.length).split(\";\");\n if (parts.length !== 2 || parts[1] === \"\") return undefined;\n return { componentPath: parts[0]!, udfPath: parts[1]! };\n}\n\nexport function parseFunctionHandle(handle: string): FunctionPath {\n const functionPath = readFunctionHandle(handle);\n if (functionPath === undefined) {\n throw new Error(`Invalid function handle \\`${handle}\\``);\n }\n return functionPath;\n}\nexport function simpleHash(string: string) {\n let hash = 0;\n for (let i = 0; i < string.length; i++) {\n const char = string.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return hash;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MountedHttp.d.ts","sourceRoot":"","sources":["../../src/internal/MountedHttp.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EACV,gBAAgB,EAKjB,MAAM,eAAe,CAAC;AAIvB,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAmFtE,eAAO,MAAM,IAAI,WACP,aAAa,CAAC,SAAS,CAAC,qBACb,iBAAiB;;
|
|
1
|
+
{"version":3,"file":"MountedHttp.d.ts","sourceRoot":"","sources":["../../src/internal/MountedHttp.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EACV,gBAAgB,EAKjB,MAAM,eAAe,CAAC;AAIvB,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAmFtE,eAAO,MAAM,IAAI,WACP,aAAa,CAAC,SAAS,CAAC,qBACb,iBAAiB;;YA2GrB,SAAS,SAAS,gBAAgB,YACrC,sBAAsB,CAAC,SAAS,CAAC,QACrC,MAAM,SACL,WAAW,KACjB,OAAO,CAAC,QAAQ,CAAC;CAgErB,CAAC"}
|
|
@@ -42,9 +42,9 @@ const make = (mounts, registerComponent) => {
|
|
|
42
42
|
if (Result.isFailure(normalized)) throw normalized.failure;
|
|
43
43
|
const registered = /* @__PURE__ */ new Set();
|
|
44
44
|
let tail = Promise.resolve();
|
|
45
|
-
const register = (componentPath, schema, modules) => {
|
|
45
|
+
const register = (componentPath, schema, modules, options) => {
|
|
46
46
|
if (!normalized.success.some((mount) => mount.componentPath === componentPath)) {
|
|
47
|
-
registerComponent(componentPath, schema, modules);
|
|
47
|
+
registerComponent(componentPath, schema, modules, options);
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
const root = Object.keys(modules).find((key) => key.includes("_generated"))?.split("_generated", 2)[0];
|
|
@@ -98,7 +98,7 @@ const make = (mounts, registerComponent) => {
|
|
|
98
98
|
registerComponent(componentPath, schema, {
|
|
99
99
|
...modules,
|
|
100
100
|
[bridgePath]: () => Promise.resolve({ invoke })
|
|
101
|
-
});
|
|
101
|
+
}, options);
|
|
102
102
|
registered.add(componentPath);
|
|
103
103
|
};
|
|
104
104
|
const fetch = (accessor, path, init) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MountedHttp.js","names":[],"sources":["../../src/internal/MountedHttp.ts"],"sourcesContent":["/* oxlint-disable effecttsgo/async-function -- This convex-test adapter uses Promise callbacks and temporarily overrides the real process environment consumed by HTTP module construction. */\nimport type { TestConvexForDataModel } from \"convex-test\";\nimport type {\n GenericDataModel,\n HttpRouter,\n FunctionReference,\n RoutableMethod,\n PublicHttpAction,\n} from \"convex/server\";\nimport { actionGeneric } from \"convex/server\";\nimport * as Result from \"effect/Result\";\nimport * as Schema from \"effect/Schema\";\nimport type { HttpMount, RegisterComponent } from \"../TestConfect.js\";\n\ndeclare const process: { env: Record<string, string | undefined> };\n\nclass InvalidHttpMount extends Schema.TaggedError<InvalidHttpMount>()(\n \"InvalidHttpMount\",\n {\n componentPath: Schema.String,\n httpPrefix: Schema.String,\n message: Schema.String,\n },\n) {}\n\nclass ComponentHttpError extends Schema.TaggedError<ComponentHttpError>()(\n \"ComponentHttpError\",\n { componentPath: Schema.String, message: Schema.String },\n) {}\n\nclass InvalidFetchPath extends Schema.TaggedError<InvalidFetchPath>()(\n \"InvalidFetchPath\",\n { path: Schema.String, message: Schema.String },\n) {}\n\ntype NormalizedMount = {\n readonly componentPath: string;\n readonly prefix: string;\n};\n\nconst normalizeMounts = (mounts: ReadonlyArray<HttpMount>) => {\n const normalized: Array<NormalizedMount> = [];\n for (const mount of mounts) {\n if (!mount.httpPrefix.startsWith(\"/\") || /[?#]/.test(mount.httpPrefix)) {\n return Result.fail(\n new InvalidHttpMount({\n ...mount,\n message:\n \"httpPrefix must be an absolute path without a query or fragment.\",\n }),\n );\n }\n const prefix = mount.httpPrefix.replace(/\\/+$/, \"\");\n if (normalized.some((previous) => previous.prefix === prefix)) {\n return Result.fail(\n new InvalidHttpMount({\n ...mount,\n message: \"Each httpPrefix must select one component installation.\",\n }),\n );\n }\n normalized.push({ componentPath: mount.componentPath, prefix });\n }\n return Result.succeed(\n normalized.sort((a, b) => b.prefix.length - a.prefix.length),\n );\n};\n\ntype HttpRequest = {\n siteUrl: string;\n url: string;\n pathname: string;\n method: string;\n headers: Array<[string, string]>;\n body: ArrayBuffer | null;\n};\n\ntype HttpResponse = {\n status: number;\n statusText: string;\n headers: Array<[string, string]>;\n body: ArrayBuffer;\n};\n\n// Convex omits this runtime entry point from its published declarations.\nconst isInvokable = (\n action: PublicHttpAction,\n): action is PublicHttpAction & {\n invokeHttpAction(request: Request): Promise<Response>;\n} =>\n \"invokeHttpAction\" in action && typeof action.invokeHttpAction === \"function\";\n\nconst bridgeModule = \"__confect_test_http\";\nconst defaultSiteUrl = \"https://some.convex.site\";\n\nexport const make = (\n mounts: ReadonlyArray<HttpMount>,\n registerComponent: RegisterComponent,\n) => {\n const normalized = normalizeMounts(mounts);\n if (Result.isFailure(normalized)) throw normalized.failure;\n const registered = new Set<string>();\n let tail = Promise.resolve();\n\n const register: RegisterComponent = (componentPath, schema, modules) => {\n if (\n !normalized.success.some((mount) => mount.componentPath === componentPath)\n ) {\n registerComponent(componentPath, schema, modules);\n return;\n }\n const generated = Object.keys(modules).find((key) =>\n key.includes(\"_generated\"),\n );\n const root = generated?.split(\"_generated\", 2)[0];\n const moduleLoaders = Object.fromEntries(\n Object.entries(modules).map(([path, load]) => [\n path.replace(/\\.[^.]+$/, \"\"),\n load,\n ]),\n );\n const loadHttp =\n root === undefined ? undefined : moduleLoaders[`${root}http`];\n if (root === undefined || loadHttp === undefined) {\n throw new ComponentHttpError({\n componentPath,\n message:\n \"Mounted components need an HTTP module and Convex _generated modules.\",\n });\n }\n const bridgePath = `${root}${bridgeModule}.ts`;\n if (`${root}${bridgeModule}` in moduleLoaders) {\n throw new ComponentHttpError({\n componentPath,\n message: `${bridgeModule} is reserved for mounted HTTP tests.`,\n });\n }\n const routers = new Map<string, Promise<HttpRouter>>();\n const invoke = actionGeneric({\n handler: async (_ctx, args: HttpRequest): Promise<HttpResponse> => {\n const previousSiteUrl = process.env.CONVEX_SITE_URL;\n try {\n process.env.CONVEX_SITE_URL = args.siteUrl;\n let router = routers.get(args.siteUrl);\n if (router === undefined) {\n router = loadHttp().then(\n (module: { default: HttpRouter }) => module.default,\n );\n routers.set(args.siteUrl, router);\n }\n const route = (await router).lookup(\n args.pathname,\n args.method as RoutableMethod,\n );\n let response: Response;\n if (route === null) {\n response = new Response(\n `No HttpAction routed for ${args.pathname}`,\n { status: 404 },\n );\n } else {\n const action = route[0];\n if (!isInvokable(action)) {\n throw new ComponentHttpError({\n componentPath,\n message:\n \"This Convex version does not expose invokeHttpAction for HTTP tests.\",\n });\n }\n // Convex selects on the relative path and passes the full URL to the handler.\n response = await action.invokeHttpAction(\n new Request(args.url, {\n method: args.method,\n headers: args.headers,\n ...(args.body === null ? {} : { body: args.body }),\n }),\n );\n }\n return {\n status: response.status,\n statusText: response.statusText,\n headers: [...response.headers],\n body: await response.arrayBuffer(),\n };\n } finally {\n if (previousSiteUrl === undefined) delete process.env.CONVEX_SITE_URL;\n else process.env.CONVEX_SITE_URL = previousSiteUrl;\n }\n },\n });\n registerComponent(componentPath, schema, {\n ...modules,\n [bridgePath]: () => Promise.resolve({ invoke }),\n });\n registered.add(componentPath);\n };\n\n const fetch = <DataModel extends GenericDataModel>(\n accessor: TestConvexForDataModel<DataModel>,\n path: string,\n init?: RequestInit,\n ): Promise<Response> => {\n if (normalized.success.length === 0) return accessor.fetch(path, init);\n const run = async () => {\n if (!path.startsWith(\"/\")) {\n throw new InvalidFetchPath({\n path,\n message: 'Path given to fetch must start with a \"/\".',\n });\n }\n const pathname = new URL(`${defaultSiteUrl}${path}`).pathname;\n const mount = normalized.success.find(\n ({ prefix }) =>\n pathname === prefix || pathname.startsWith(`${prefix}/`),\n );\n if (mount === undefined) return accessor.fetch(path, init);\n if (!registered.has(mount.componentPath)) {\n throw new ComponentHttpError({\n componentPath: mount.componentPath,\n message: \"Register the mounted component before calling fetch.\",\n });\n }\n const appSiteUrl = (\n process.env.CONVEX_SITE_URL || defaultSiteUrl\n ).replace(/\\/$/, \"\");\n const request = new Request(new URL(`${appSiteUrl}${path}`), init);\n // convex-test encodes exact component paths in test-only function handles.\n const reference =\n `function://${mount.componentPath};${bridgeModule}:invoke` as unknown as FunctionReference<\n \"action\",\n \"public\",\n HttpRequest,\n HttpResponse\n >;\n const response = await accessor.action(reference, {\n siteUrl: `${appSiteUrl}${mount.prefix}`,\n url: request.url,\n pathname: pathname.slice(mount.prefix.length) || \"/\",\n method: request.method,\n headers: [...request.headers],\n body:\n request.method === \"GET\" || request.method === \"HEAD\"\n ? null\n : await request.arrayBuffer(),\n });\n return new Response(\n request.method === \"HEAD\" || [204, 205, 304].includes(response.status)\n ? null\n : response.body,\n {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n },\n );\n };\n const result = tail.then(run);\n tail = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n };\n\n return { register, fetch };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MountedHttp.js","names":[],"sources":["../../src/internal/MountedHttp.ts"],"sourcesContent":["/* oxlint-disable effecttsgo/async-function -- This convex-test adapter uses Promise callbacks and temporarily overrides the real process environment consumed by HTTP module construction. */\nimport type { TestConvexForDataModel } from \"convex-test\";\nimport type {\n GenericDataModel,\n HttpRouter,\n FunctionReference,\n RoutableMethod,\n PublicHttpAction,\n} from \"convex/server\";\nimport { actionGeneric } from \"convex/server\";\nimport * as Result from \"effect/Result\";\nimport * as Schema from \"effect/Schema\";\nimport type { HttpMount, RegisterComponent } from \"../TestConfect.js\";\n\ndeclare const process: { env: Record<string, string | undefined> };\n\nclass InvalidHttpMount extends Schema.TaggedError<InvalidHttpMount>()(\n \"InvalidHttpMount\",\n {\n componentPath: Schema.String,\n httpPrefix: Schema.String,\n message: Schema.String,\n },\n) {}\n\nclass ComponentHttpError extends Schema.TaggedError<ComponentHttpError>()(\n \"ComponentHttpError\",\n { componentPath: Schema.String, message: Schema.String },\n) {}\n\nclass InvalidFetchPath extends Schema.TaggedError<InvalidFetchPath>()(\n \"InvalidFetchPath\",\n { path: Schema.String, message: Schema.String },\n) {}\n\ntype NormalizedMount = {\n readonly componentPath: string;\n readonly prefix: string;\n};\n\nconst normalizeMounts = (mounts: ReadonlyArray<HttpMount>) => {\n const normalized: Array<NormalizedMount> = [];\n for (const mount of mounts) {\n if (!mount.httpPrefix.startsWith(\"/\") || /[?#]/.test(mount.httpPrefix)) {\n return Result.fail(\n new InvalidHttpMount({\n ...mount,\n message:\n \"httpPrefix must be an absolute path without a query or fragment.\",\n }),\n );\n }\n const prefix = mount.httpPrefix.replace(/\\/+$/, \"\");\n if (normalized.some((previous) => previous.prefix === prefix)) {\n return Result.fail(\n new InvalidHttpMount({\n ...mount,\n message: \"Each httpPrefix must select one component installation.\",\n }),\n );\n }\n normalized.push({ componentPath: mount.componentPath, prefix });\n }\n return Result.succeed(\n normalized.sort((a, b) => b.prefix.length - a.prefix.length),\n );\n};\n\ntype HttpRequest = {\n siteUrl: string;\n url: string;\n pathname: string;\n method: string;\n headers: Array<[string, string]>;\n body: ArrayBuffer | null;\n};\n\ntype HttpResponse = {\n status: number;\n statusText: string;\n headers: Array<[string, string]>;\n body: ArrayBuffer;\n};\n\n// Convex omits this runtime entry point from its published declarations.\nconst isInvokable = (\n action: PublicHttpAction,\n): action is PublicHttpAction & {\n invokeHttpAction(request: Request): Promise<Response>;\n} =>\n \"invokeHttpAction\" in action && typeof action.invokeHttpAction === \"function\";\n\nconst bridgeModule = \"__confect_test_http\";\nconst defaultSiteUrl = \"https://some.convex.site\";\n\nexport const make = (\n mounts: ReadonlyArray<HttpMount>,\n registerComponent: RegisterComponent,\n) => {\n const normalized = normalizeMounts(mounts);\n if (Result.isFailure(normalized)) throw normalized.failure;\n const registered = new Set<string>();\n let tail = Promise.resolve();\n\n const register: RegisterComponent = (\n componentPath,\n schema,\n modules,\n options,\n ) => {\n if (\n !normalized.success.some((mount) => mount.componentPath === componentPath)\n ) {\n registerComponent(componentPath, schema, modules, options);\n return;\n }\n const generated = Object.keys(modules).find((key) =>\n key.includes(\"_generated\"),\n );\n const root = generated?.split(\"_generated\", 2)[0];\n const moduleLoaders = Object.fromEntries(\n Object.entries(modules).map(([path, load]) => [\n path.replace(/\\.[^.]+$/, \"\"),\n load,\n ]),\n );\n const loadHttp =\n root === undefined ? undefined : moduleLoaders[`${root}http`];\n if (root === undefined || loadHttp === undefined) {\n throw new ComponentHttpError({\n componentPath,\n message:\n \"Mounted components need an HTTP module and Convex _generated modules.\",\n });\n }\n const bridgePath = `${root}${bridgeModule}.ts`;\n if (`${root}${bridgeModule}` in moduleLoaders) {\n throw new ComponentHttpError({\n componentPath,\n message: `${bridgeModule} is reserved for mounted HTTP tests.`,\n });\n }\n const routers = new Map<string, Promise<HttpRouter>>();\n const invoke = actionGeneric({\n handler: async (_ctx, args: HttpRequest): Promise<HttpResponse> => {\n const previousSiteUrl = process.env.CONVEX_SITE_URL;\n try {\n process.env.CONVEX_SITE_URL = args.siteUrl;\n let router = routers.get(args.siteUrl);\n if (router === undefined) {\n router = loadHttp().then(\n (module: { default: HttpRouter }) => module.default,\n );\n routers.set(args.siteUrl, router);\n }\n const route = (await router).lookup(\n args.pathname,\n args.method as RoutableMethod,\n );\n let response: Response;\n if (route === null) {\n response = new Response(\n `No HttpAction routed for ${args.pathname}`,\n { status: 404 },\n );\n } else {\n const action = route[0];\n if (!isInvokable(action)) {\n throw new ComponentHttpError({\n componentPath,\n message:\n \"This Convex version does not expose invokeHttpAction for HTTP tests.\",\n });\n }\n // Convex selects on the relative path and passes the full URL to the handler.\n response = await action.invokeHttpAction(\n new Request(args.url, {\n method: args.method,\n headers: args.headers,\n ...(args.body === null ? {} : { body: args.body }),\n }),\n );\n }\n return {\n status: response.status,\n statusText: response.statusText,\n headers: [...response.headers],\n body: await response.arrayBuffer(),\n };\n } finally {\n if (previousSiteUrl === undefined) delete process.env.CONVEX_SITE_URL;\n else process.env.CONVEX_SITE_URL = previousSiteUrl;\n }\n },\n });\n registerComponent(\n componentPath,\n schema,\n { ...modules, [bridgePath]: () => Promise.resolve({ invoke }) },\n options,\n );\n registered.add(componentPath);\n };\n\n const fetch = <DataModel extends GenericDataModel>(\n accessor: TestConvexForDataModel<DataModel>,\n path: string,\n init?: RequestInit,\n ): Promise<Response> => {\n if (normalized.success.length === 0) return accessor.fetch(path, init);\n const run = async () => {\n if (!path.startsWith(\"/\")) {\n throw new InvalidFetchPath({\n path,\n message: 'Path given to fetch must start with a \"/\".',\n });\n }\n const pathname = new URL(`${defaultSiteUrl}${path}`).pathname;\n const mount = normalized.success.find(\n ({ prefix }) =>\n pathname === prefix || pathname.startsWith(`${prefix}/`),\n );\n if (mount === undefined) return accessor.fetch(path, init);\n if (!registered.has(mount.componentPath)) {\n throw new ComponentHttpError({\n componentPath: mount.componentPath,\n message: \"Register the mounted component before calling fetch.\",\n });\n }\n const appSiteUrl = (\n process.env.CONVEX_SITE_URL || defaultSiteUrl\n ).replace(/\\/$/, \"\");\n const request = new Request(new URL(`${appSiteUrl}${path}`), init);\n // convex-test encodes exact component paths in test-only function handles.\n const reference =\n `function://${mount.componentPath};${bridgeModule}:invoke` as unknown as FunctionReference<\n \"action\",\n \"public\",\n HttpRequest,\n HttpResponse\n >;\n const response = await accessor.action(reference, {\n siteUrl: `${appSiteUrl}${mount.prefix}`,\n url: request.url,\n pathname: pathname.slice(mount.prefix.length) || \"/\",\n method: request.method,\n headers: [...request.headers],\n body:\n request.method === \"GET\" || request.method === \"HEAD\"\n ? null\n : await request.arrayBuffer(),\n });\n return new Response(\n request.method === \"HEAD\" || [204, 205, 304].includes(response.status)\n ? null\n : response.body,\n {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n },\n );\n };\n const result = tail.then(run);\n tail = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n };\n\n return { register, fetch };\n};\n"],"mappings":"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"}
|