cvx-kit 0.1.8 → 0.1.11
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/CHANGELOG.md +32 -0
- package/README.md +28 -28
- package/dist/agent-tools.d.mts +5 -5
- package/dist/agent-tools.mjs.map +1 -1
- package/dist/auth.d.mts +39 -289
- package/dist/auth.mjs.map +1 -1
- package/dist/components/approvals/_generated/api.d.mts +3 -4
- package/dist/components/approvals/_generated/api.mjs.map +1 -1
- package/dist/components/approvals/_generated/component.d.mts +1 -2
- package/dist/components/approvals/_generated/dataModel.d.mts +4 -5
- package/dist/components/approvals/_generated/server.d.mts +12 -13
- package/dist/components/approvals/_generated/server.mjs.map +1 -1
- package/dist/components/approvals/audit.d.mts +8 -8
- package/dist/components/approvals/audit.mjs +1 -1
- package/dist/components/approvals/audit.mjs.map +1 -1
- package/dist/components/approvals/client.d.mts +10 -10
- package/dist/components/approvals/client.mjs.map +1 -1
- package/dist/components/approvals/constants.d.mts +30 -30
- package/dist/components/approvals/constants.mjs.map +1 -1
- package/dist/components/approvals/convex.config.mjs.map +1 -1
- package/dist/components/approvals/decisions.d.mts +6 -6
- package/dist/components/approvals/decisions.mjs.map +1 -1
- package/dist/components/approvals/functions.d.mts +3 -3
- package/dist/components/approvals/functions.mjs.map +1 -1
- package/dist/components/approvals/health.d.mts +2 -2
- package/dist/components/approvals/health.mjs.map +1 -1
- package/dist/components/approvals/requests.d.mts +8 -8
- package/dist/components/approvals/requests.mjs.map +1 -1
- package/dist/components/approvals/validators.d.mts +29 -29
- package/dist/components/approvals/validators.mjs +1 -1
- package/dist/components/approvals/validators.mjs.map +1 -1
- package/dist/components/approvals/workflow.d.mts +3 -3
- package/dist/components/approvals/workflow.mjs.map +1 -1
- package/dist/components/approvals/workflow_steps.d.mts +15 -15
- package/dist/components/approvals/workflow_steps.mjs.map +1 -1
- package/dist/components/foundation/_generated/api.d.mts +3 -4
- package/dist/components/foundation/_generated/api.mjs.map +1 -1
- package/dist/components/foundation/_generated/component.d.mts +1 -2
- package/dist/components/foundation/_generated/dataModel.d.mts +4 -5
- package/dist/components/foundation/_generated/server.d.mts +12 -13
- package/dist/components/foundation/_generated/server.mjs.map +1 -1
- package/dist/components/foundation/client.d.mts +13 -13
- package/dist/components/foundation/client.mjs.map +1 -1
- package/dist/components/foundation/convex.config.mjs.map +1 -1
- package/dist/components/foundation/functions.d.mts +2 -2
- package/dist/components/foundation/functions.mjs.map +1 -1
- package/dist/components/foundation/modules/command/command.d.mts +14 -14
- package/dist/components/foundation/modules/command/command.mjs.map +1 -1
- package/dist/components/foundation/modules/command/lifecycle.d.mts +12 -13
- package/dist/components/foundation/modules/command/lifecycle.mjs.map +1 -1
- package/dist/components/foundation/modules/observability/observability.d.mts +4 -4
- package/dist/components/foundation/modules/observability/observability.mjs.map +1 -1
- package/dist/components/foundation/modules/query/query.d.mts +5 -5
- package/dist/components/foundation/modules/query/query.mjs.map +1 -1
- package/dist/components/foundation/operation.d.mts +5 -6
- package/dist/components/foundation/operation.mjs.map +1 -1
- package/dist/components/foundation/result.d.mts +6 -6
- package/dist/components/foundation/result.mjs.map +1 -1
- package/dist/components/foundation/telemetry.d.mts +2 -2
- package/dist/components/foundation/telemetry.mjs.map +1 -1
- package/dist/crud.d.mts +6 -5
- package/dist/crud.mjs.map +1 -1
- package/dist/effect.d.mts +3 -2
- package/dist/effect.mjs +1 -1
- package/dist/errors.d.mts +4 -4
- package/dist/errors.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/middleware.d.mts +2 -3
- package/dist/middleware.mjs.map +1 -1
- package/dist/modules/contracts/contract.d.mts +12 -13
- package/dist/modules/contracts/contract.mjs.map +1 -1
- package/dist/modules/contracts/convex.d.mts +3 -5
- package/dist/modules/contracts/convex.mjs.map +1 -1
- package/dist/modules/contracts/errors.d.mts +8 -9
- package/dist/modules/contracts/errors.mjs +1 -1
- package/dist/modules/contracts/errors.mjs.map +1 -1
- package/dist/modules/contracts/exposure.d.mts +13 -14
- package/dist/modules/contracts/exposure.mjs +1 -1
- package/dist/modules/contracts/exposure.mjs.map +1 -1
- package/dist/modules/contracts/idempotency.d.mts +15 -16
- package/dist/modules/contracts/idempotency.mjs +1 -1
- package/dist/modules/contracts/idempotency.mjs.map +1 -1
- package/dist/modules/contracts/standard.d.mts +2 -3
- package/dist/modules/contracts/standard.mjs.map +1 -1
- package/dist/modules/contracts/workflow.d.mts +26 -27
- package/dist/modules/contracts/workflow.mjs.map +1 -1
- package/dist/modules/effect/api-runtime.d.mts +10 -11
- package/dist/modules/effect/api-runtime.mjs +1 -1
- package/dist/modules/effect/api-runtime.mjs.map +1 -1
- package/dist/modules/effect/api-zod.d.mts +2 -3
- package/dist/modules/effect/api-zod.mjs.map +1 -1
- package/dist/modules/effect/api.d.mts +4 -5
- package/dist/modules/effect/api.mjs.map +1 -1
- package/dist/modules/effect/auth.d.mts +43 -0
- package/dist/modules/effect/auth.mjs +2 -0
- package/dist/modules/effect/auth.mjs.map +1 -0
- package/dist/modules/effect/client.d.mts +1 -1
- package/dist/modules/effect/client.mjs +1 -1
- package/dist/modules/effect/command.d.mts +4 -5
- package/dist/modules/effect/command.mjs +1 -1
- package/dist/modules/effect/command.mjs.map +1 -1
- package/dist/modules/effect/crud.d.mts +7 -8
- package/dist/modules/effect/crud.mjs +1 -1
- package/dist/modules/effect/crud.mjs.map +1 -1
- package/dist/modules/effect/errors.d.mts +1 -2
- package/dist/modules/effect/errors.mjs.map +1 -1
- package/dist/modules/effect/foundation.d.mts +3 -4
- package/dist/modules/effect/foundation.mjs.map +1 -1
- package/dist/modules/effect/idempotency.d.mts +2 -3
- package/dist/modules/effect/idempotency.mjs.map +1 -1
- package/dist/modules/effect/normalize.d.mts +2 -3
- package/dist/modules/effect/normalize.mjs.map +1 -1
- package/dist/modules/effect/operation.d.mts +17 -18
- package/dist/modules/effect/operation.mjs.map +1 -1
- package/dist/modules/effect/query.d.mts +3 -4
- package/dist/modules/effect/query.mjs +1 -1
- package/dist/modules/effect/query.mjs.map +1 -1
- package/dist/modules/effect/schema.d.mts +8 -9
- package/dist/modules/effect/schema.mjs.map +1 -1
- package/dist/modules/effect/workflow.d.mts +5 -6
- package/dist/modules/effect/workflow.mjs +1 -1
- package/dist/modules/effect/workflow.mjs.map +1 -1
- package/dist/oxlint/architecture.d.mts +6 -7
- package/dist/oxlint/architecture.mjs +1 -1
- package/dist/oxlint/architecture.mjs.map +1 -1
- package/dist/oxlint/ast.d.mts +22 -23
- package/dist/oxlint/ast.mjs +1 -1
- package/dist/oxlint/ast.mjs.map +1 -1
- package/dist/oxlint/ownership.d.mts +2 -3
- package/dist/oxlint/ownership.mjs +1 -1
- package/dist/oxlint/ownership.mjs.map +1 -1
- package/dist/oxlint.d.mts +4 -4
- package/dist/oxlint.mjs +1 -1
- package/dist/oxlint.mjs.map +1 -1
- package/dist/state-machine.d.mts +1 -2
- package/dist/state-machine.mjs.map +1 -1
- package/dist/tenancy.d.mts +6 -6
- package/dist/tenancy.mjs.map +1 -1
- package/dist/triggers.d.mts +6 -6
- package/dist/triggers.mjs.map +1 -1
- package/dist/webhooks.d.mts +5 -6
- package/dist/webhooks.mjs.map +1 -1
- package/dist/zod-table.d.mts +10 -10
- package/dist/zod-table.mjs.map +1 -1
- package/docs/auth.md +131 -1
- package/docs/effect.md +8 -2
- package/docs/llms.md +41 -20
- package/docs/oxlint.md +1 -1
- package/docs/upgrading.md +9 -1
- package/docs/zod-table.md +1 -1
- package/package.json +21 -15
- package/scripts/test-packed-auth.mjs +72 -0
- package/scripts/test-packed-effect.mjs +2 -1
- package/scripts/test-packed-ids.mjs +2 -2
- package/dist/node_modules/@standard-schema/spec/dist/index.d.mts +0 -79
|
@@ -10,13 +10,13 @@ type Definition = {
|
|
|
10
10
|
schema: ContractSchema;
|
|
11
11
|
};
|
|
12
12
|
};
|
|
13
|
-
type OperationPublicResult<D extends Definition> = D extends {
|
|
13
|
+
export type OperationPublicResult<D extends Definition> = D extends {
|
|
14
14
|
wire: {
|
|
15
15
|
schema: infer Schema extends ContractSchema;
|
|
16
16
|
};
|
|
17
17
|
} ? ContractInput<Schema> : ContractOutput<D['result']>;
|
|
18
18
|
/** Only the selected input/public contracts are exposed, never handlers or other operations. */
|
|
19
|
-
type SelectedOperation<Owner extends symbol, Key extends string, Input extends ContractSchema, Public> = Readonly<{
|
|
19
|
+
export type SelectedOperation<Owner extends symbol, Key extends string, Input extends ContractSchema, Public> = Readonly<{
|
|
20
20
|
owner: Owner;
|
|
21
21
|
key: Key;
|
|
22
22
|
kind: 'mutation' | 'query';
|
|
@@ -41,31 +41,31 @@ type Selection = Readonly<{
|
|
|
41
41
|
type InputOf<S extends Selection> = S[typeof selectedTypes]['input'];
|
|
42
42
|
type PublicOf<S extends Selection> = S[typeof selectedTypes]['public'];
|
|
43
43
|
/** Registry integrators must check membership before selecting their own definition. */
|
|
44
|
-
declare function selectOperation<const Owner extends symbol, const Key extends string, const D extends Definition>(owner: Owner, kind: 'mutation' | 'query', key: Key, definition: D): SelectedOperation<Owner, Key, D['input'], OperationPublicResult<D>>;
|
|
45
|
-
type OperationExecutor<S extends Selection> = Readonly<{
|
|
44
|
+
export declare function selectOperation<const Owner extends symbol, const Key extends string, const D extends Definition>(owner: Owner, kind: 'mutation' | 'query', key: Key, definition: D): SelectedOperation<Owner, Key, D['input'], OperationPublicResult<D>>;
|
|
45
|
+
export type OperationExecutor<S extends Selection> = Readonly<{
|
|
46
46
|
owner: S['owner'];
|
|
47
47
|
key: S['key'];
|
|
48
48
|
readonly [executorTypes]: S;
|
|
49
49
|
}>;
|
|
50
50
|
/** The host captures authority in an already authorized native Promise call. */
|
|
51
|
-
declare function bindOperationExecutor<const S extends Selection>(selected: S, configuration: {
|
|
51
|
+
export declare function bindOperationExecutor<const S extends Selection>(selected: S, configuration: {
|
|
52
52
|
owner: NoInfer<S['owner']>;
|
|
53
53
|
key: NoInfer<S['key']>;
|
|
54
54
|
execute: (raw: NoInfer<InputOf<S>>) => Promise<NoInfer<PublicOf<S>>>;
|
|
55
55
|
}): OperationExecutor<S>;
|
|
56
|
-
declare const operationToolDialect: 'https://json-schema.org/draft/2020-12/schema';
|
|
56
|
+
export declare const operationToolDialect: 'https://json-schema.org/draft/2020-12/schema';
|
|
57
57
|
type Json = null | boolean | number | string | readonly Json[] | {
|
|
58
58
|
readonly [key: string]: Json;
|
|
59
59
|
};
|
|
60
|
-
type OperationToolJsonSchema = Readonly<{
|
|
60
|
+
export type OperationToolJsonSchema = Readonly<{
|
|
61
61
|
[keyword: string]: Json;
|
|
62
62
|
}>;
|
|
63
|
-
type OperationToolConverter<Input extends ContractSchema = ContractSchema> = Readonly<{
|
|
63
|
+
export type OperationToolConverter<Input extends ContractSchema = ContractSchema> = Readonly<{
|
|
64
64
|
dialect: typeof operationToolDialect;
|
|
65
65
|
/** Represent raw transport input. Unsupported IDs/transforms must throw, never be approximated. */
|
|
66
66
|
convert: (input: Input) => OperationToolJsonSchema;
|
|
67
67
|
}>;
|
|
68
|
-
type OperationToolDefinition<S extends Selection = Selection, Errors extends ErrorDefinitions = ErrorDefinitions> = Readonly<{
|
|
68
|
+
export type OperationToolDefinition<S extends Selection = Selection, Errors extends ErrorDefinitions = ErrorDefinitions> = Readonly<{
|
|
69
69
|
operation: S;
|
|
70
70
|
executor: OperationExecutor<S>;
|
|
71
71
|
description: string;
|
|
@@ -86,20 +86,19 @@ type Checked<T extends ToolConstraint> = T & OperationToolDefinition<T['operatio
|
|
|
86
86
|
type FailureOf<T> = T extends {
|
|
87
87
|
errors: Pick<ErrorContract<infer E>, 'decode'>;
|
|
88
88
|
} ? DecodedFailure<E> : UnknownFailure;
|
|
89
|
-
type OperationToolOutcome<Public, Failure = UnknownFailure> = Readonly<{
|
|
89
|
+
export type OperationToolOutcome<Public, Failure = UnknownFailure> = Readonly<{
|
|
90
90
|
_tag: 'Success';
|
|
91
91
|
value: Public;
|
|
92
92
|
}> | Failure;
|
|
93
|
-
type OperationTool<T extends ToolConstraint> = Readonly<{
|
|
93
|
+
export type OperationTool<T extends ToolConstraint> = Readonly<{
|
|
94
94
|
name: string;
|
|
95
95
|
description: string;
|
|
96
96
|
args: OperationToolJsonSchema;
|
|
97
97
|
invoke: (raw: InputOf<T['operation']>) => Promise<OperationToolOutcome<PublicOf<T['operation']>, FailureOf<T>>>;
|
|
98
98
|
}>;
|
|
99
|
-
declare function createOperationTools<const T extends Readonly<Record<string, ToolConstraint>>>(definitions: T & { [K in keyof T]: Checked<T[K]>; }): Readonly<{ [K in keyof T]: OperationTool<T[K]>; }>;
|
|
100
|
-
declare function createOperationTools<const T extends readonly (ToolConstraint & {
|
|
99
|
+
export declare function createOperationTools<const T extends Readonly<Record<string, ToolConstraint>>>(definitions: T & { [K in keyof T]: Checked<T[K]>; }): Readonly<{ [K in keyof T]: OperationTool<T[K]>; }>;
|
|
100
|
+
export declare function createOperationTools<const T extends readonly (ToolConstraint & {
|
|
101
101
|
name: string;
|
|
102
102
|
})[]>(definitions: T & { [K in keyof T]: Checked<T[K]>; }): Readonly<{ [D in T[number] as D['name']]: OperationTool<D>; }>;
|
|
103
103
|
//#endregion
|
|
104
|
-
export { OperationExecutor, OperationPublicResult, OperationTool, OperationToolConverter, OperationToolDefinition, OperationToolJsonSchema, OperationToolOutcome, SelectedOperation, bindOperationExecutor, createOperationTools, operationToolDialect, selectOperation };
|
|
105
104
|
//# sourceMappingURL=exposure.d.mts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ConvexError as e}from"convex/values";const t
|
|
1
|
+
import{ConvexError as e}from"convex/values";const t=/* @__PURE__ */ new WeakSet,n=/* @__PURE__ */ new WeakMap;function r(e,n,r,i){if(typeof e!=`symbol`||!r||n!==`mutation`&&n!==`query`||!i?.input||!i.result)throw Error(`Invalid selected operation`);let a=Object.freeze({owner:e,key:r,kind:n,input:i.input,result:i.wire?.schema??i.result});return t.add(a),a}function i(e,r){if(!t.has(e)||r.owner!==e.owner||r.key!==e.key||typeof r.execute!=`function`)throw Error(`Executor does not own the selected operation`);let i=Object.freeze({owner:e.owner,key:e.key});return n.set(i,{selection:e,execute:r.execute}),i}const a=`https://json-schema.org/draft/2020-12/schema`;function o(r){let i=Array.isArray(r)?r.map(e=>[e.name,e]):Object.entries(r),a=Object.create(null);for(let[r,o]of i){if(!r||Object.hasOwn(a,r))throw Error(`Duplicate or empty operation tool name`);let i=o.executor&&n.get(o.executor);if(!t.has(o.operation)||!i||i.selection!==o.operation)throw Error(`Tool requires its selected operation executor`);if(!o.description||o.converter?.dialect!==`https://json-schema.org/draft/2020-12/schema`||typeof o.converter.convert!=`function`)throw Error(`Unsupported operation tool converter`);let s=p(o.converter.convert(o.operation.input)),c=o.errors;a[r]=Object.freeze({name:r,description:o.description,args:s,async invoke(t){try{return{_tag:`Success`,value:await i.execute(t)}}catch(t){try{if(t instanceof e&&c)return c.decode(t.data)}catch{}return{_tag:`UnknownFailure`,version:1}}}})}return Object.freeze(a)}const s=/* @__PURE__ */ new Set([`properties`,`$defs`,`items`,`additionalProperties`,`anyOf`,`oneOf`,`allOf`,`not`,`required`,`enum`,`const`,`default`,`examples`]),c=/* @__PURE__ */ new Set([`object`,`array`,`string`,`number`,`integer`,`boolean`,`null`]),l=/* @__PURE__ */ new Set([`title`,`description`,`format`,`pattern`,`$ref`,`$id`,`x-convex-table`]),u=/* @__PURE__ */ new Set([`minLength`,`maxLength`,`minItems`,`maxItems`,`minProperties`,`maxProperties`]),d=/* @__PURE__ */ new Set([`minimum`,`maximum`,`exclusiveMinimum`,`exclusiveMaximum`,`multipleOf`]),f=/* @__PURE__ */ new Set([...l,...u,...d,`type`,`uniqueItems`,`$schema`]);function p(e){let t=/* @__PURE__ */ new Set;function n(e){if(e===null||typeof e==`string`||typeof e==`boolean`||typeof e==`number`&&Number.isFinite(e))return e;if(typeof e!=`object`||t.has(e))throw Error(`Converter schema must be JSON safe`);t.add(e);try{if(Array.isArray(e))return Object.freeze(e.map(n));if(!m(e))throw Error(`Converter schema must be a plain JSON object`);return Object.freeze(Object.fromEntries(Object.entries(e).map(([e,t])=>[e,n(t)])))}finally{t.delete(e)}}function r(e){if(typeof e!=`boolean`){if(!m(e))throw Error(`Unsupported JSON Schema shape`);for(let[t,n]of Object.entries(e)){if(!f.has(t)&&!s.has(t))throw Error(`Unsupported JSON Schema keyword`);if(l.has(t)&&typeof n!=`string`)throw Error(`Invalid JSON Schema text keyword`);if(u.has(t)&&(typeof n!=`number`||!Number.isInteger(n)||n<0))throw Error(`Invalid JSON Schema count keyword`);if(d.has(t)&&(typeof n!=`number`||!Number.isFinite(n)||t===`multipleOf`&&n<=0))throw Error(`Invalid JSON Schema number keyword`);if(t===`uniqueItems`&&typeof n!=`boolean`)throw Error(`Invalid JSON Schema boolean keyword`);if(t===`pattern`)try{new RegExp(n)}catch{throw Error(`Invalid JSON Schema pattern`)}if((t===`enum`||t===`examples`)&&!Array.isArray(n))throw Error(`Invalid JSON Schema array keyword`);if(t===`enum`&&Array.isArray(n)&&!n.length)throw Error(`Invalid JSON Schema enum`);if(t===`$schema`&&n!==`https://json-schema.org/draft/2020-12/schema`)throw Error(`Unsupported JSON Schema dialect`);if(t===`type`&&!(typeof n==`string`&&c.has(n))&&!(Array.isArray(n)&&n.length>0&&n.every(e=>typeof e==`string`&&c.has(e))))throw Error(`Unsupported JSON Schema type`);if(t===`properties`||t===`$defs`){if(!m(n))throw Error(`Unsupported JSON Schema properties`);Object.values(n).forEach(r)}else if(t===`items`||t===`not`||t===`additionalProperties`)r(n);else if(t===`anyOf`||t===`oneOf`||t===`allOf`){if(!Array.isArray(n)||!n.length)throw Error(`Unsupported JSON Schema alternatives`);n.forEach(r)}else if(t===`required`&&(!Array.isArray(n)||n.some(e=>typeof e!=`string`)||new Set(n).size!==n.length))throw Error(`Unsupported JSON Schema required fields`)}}}let i=n(e);if(!m(i)||i.type!==`object`)throw Error(`Operation tool arguments require an object JSON Schema`);return r(i),i}function m(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}export{i as bindOperationExecutor,o as createOperationTools,a as operationToolDialect,r as selectOperation};
|
|
2
2
|
//# sourceMappingURL=exposure.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exposure.mjs","names":[],"sources":["../../../src/modules/contracts/exposure.ts"],"sourcesContent":["/* oxlint-disable anti-slop/no-unknown-parameters, anti-slop/no-unknown-type-aliases, anti-slop/no-runtime-typeof, anti-slop/no-unsafe-dictionary-type -- Selected native transport boundaries validate opaque identities and converter JSON without decoding domain inputs. */\nimport { ConvexError } from 'convex/values'\nimport type { ContractInput, ContractOutput, ContractSchema } from './contract'\nimport type { DecodedFailure, ErrorContract, ErrorDefinitions, UnknownFailure } from './errors'\n\ndeclare const selectedTypes: unique symbol\ndeclare const executorTypes: unique symbol\n\ntype Definition = {\n\tinput: ContractSchema\n\tresult: ContractSchema\n\twire?: { schema: ContractSchema }\n}\nexport type OperationPublicResult<D extends Definition> = D extends {\n\twire: { schema: infer Schema extends ContractSchema }\n}\n\t? ContractInput<Schema>\n\t: ContractOutput<D['result']>\n\n/** Only the selected input/public contracts are exposed, never handlers or other operations. */\nexport type SelectedOperation<\n\tOwner extends symbol,\n\tKey extends string,\n\tInput extends ContractSchema,\n\tPublic,\n> = Readonly<{\n\towner: Owner\n\tkey: Key\n\tkind: 'mutation' | 'query'\n\tinput: Input\n\tresult: ContractSchema\n\treadonly [selectedTypes]: { readonly input: ContractInput<Input>; readonly public: Public }\n}>\ntype Selection = Readonly<{\n\towner: symbol\n\tkey: string\n\tkind: 'mutation' | 'query'\n\tinput: ContractSchema\n\tresult: ContractSchema\n\treadonly [selectedTypes]: { readonly input: unknown; readonly public: unknown }\n}>\ntype InputOf<S extends Selection> = S[typeof selectedTypes]['input']\ntype PublicOf<S extends Selection> = S[typeof selectedTypes]['public']\n\nconst selections = new WeakSet<object>()\nconst executors = new WeakMap<\n\tobject,\n\t{\n\t\tselection: Selection\n\t\t// oxlint-disable-next-line anti-slop/no-unknown-returns -- Private heterogeneous storage erases public result types; selected identity checks recover the exact raw/public pair at invocation.\n\t\texecute: (input: never) => Promise<unknown>\n\t}\n>()\n\n/** Registry integrators must check membership before selecting their own definition. */\nexport function selectOperation<\n\tconst Owner extends symbol,\n\tconst Key extends string,\n\tconst D extends Definition,\n>(\n\towner: Owner,\n\tkind: 'mutation' | 'query',\n\tkey: Key,\n\tdefinition: D,\n): SelectedOperation<Owner, Key, D['input'], OperationPublicResult<D>> {\n\tif (\n\t\ttypeof owner !== 'symbol' ||\n\t\t!key ||\n\t\t(kind !== 'mutation' && kind !== 'query') ||\n\t\t!definition?.input ||\n\t\t!definition.result\n\t) {\n\t\tthrow Error('Invalid selected operation')\n\t}\n\t// SAFETY: private identity records this exact paired schema; its type-only slots are never read at runtime.\n\tconst selected = Object.freeze({\n\t\towner,\n\t\tkey,\n\t\tkind,\n\t\tinput: definition.input,\n\t\tresult: definition.wire?.schema ?? definition.result,\n\t}) as SelectedOperation<Owner, Key, D['input'], OperationPublicResult<D>>\n\tselections.add(selected)\n\treturn selected\n}\n\nexport type OperationExecutor<S extends Selection> = Readonly<{\n\towner: S['owner']\n\tkey: S['key']\n\treadonly [executorTypes]: S\n}>\n\n/** The host captures authority in an already authorized native Promise call. */\nexport function bindOperationExecutor<const S extends Selection>(\n\tselected: S,\n\tconfiguration: {\n\t\towner: NoInfer<S['owner']>\n\t\tkey: NoInfer<S['key']>\n\t\texecute: (raw: NoInfer<InputOf<S>>) => Promise<NoInfer<PublicOf<S>>>\n\t},\n): OperationExecutor<S> {\n\tif (\n\t\t!selections.has(selected) ||\n\t\tconfiguration.owner !== selected.owner ||\n\t\tconfiguration.key !== selected.key ||\n\t\ttypeof configuration.execute !== 'function'\n\t) {\n\t\tthrow Error('Executor does not own the selected operation')\n\t}\n\t// SAFETY: the private map restores the concrete selected raw/public pair only after identity checks.\n\tconst executor = Object.freeze({\n\t\towner: selected.owner,\n\t\tkey: selected.key,\n\t}) as OperationExecutor<S>\n\texecutors.set(executor, {\n\t\tselection: selected,\n\t\texecute: configuration.execute,\n\t})\n\treturn executor\n}\n\nexport const operationToolDialect = 'https://json-schema.org/draft/2020-12/schema' as const\ntype Json = null | boolean | number | string | readonly Json[] | { readonly [key: string]: Json }\nexport type OperationToolJsonSchema = Readonly<{ [keyword: string]: Json }>\nexport type OperationToolConverter<Input extends ContractSchema = ContractSchema> = Readonly<{\n\tdialect: typeof operationToolDialect\n\t/** Represent raw transport input. Unsupported IDs/transforms must throw, never be approximated. */\n\tconvert: (input: Input) => OperationToolJsonSchema\n}>\nexport type OperationToolDefinition<\n\tS extends Selection = Selection,\n\tErrors extends ErrorDefinitions = ErrorDefinitions,\n> = Readonly<{\n\toperation: S\n\texecutor: OperationExecutor<S>\n\tdescription: string\n\tconverter: OperationToolConverter<S['input']>\n\terrors?: Pick<ErrorContract<Errors>, 'decode'>\n}>\ntype ToolConstraint = Readonly<{\n\toperation: Selection\n\texecutor: OperationExecutor<Selection>\n\tdescription: string\n\tconverter: {\n\t\tdialect: typeof operationToolDialect\n\t\tconvert: (input: never) => OperationToolJsonSchema\n\t}\n\terrors?: Pick<ErrorContract<ErrorDefinitions>, 'decode'>\n}>\ntype Checked<T extends ToolConstraint> = T & OperationToolDefinition<T['operation']>\ntype FailureOf<T> = T extends { errors: Pick<ErrorContract<infer E>, 'decode'> }\n\t? DecodedFailure<E>\n\t: UnknownFailure\nexport type OperationToolOutcome<Public, Failure = UnknownFailure> =\n\t| Readonly<{ _tag: 'Success'; value: Public }>\n\t| Failure\nexport type OperationTool<T extends ToolConstraint> = Readonly<{\n\tname: string\n\tdescription: string\n\targs: OperationToolJsonSchema\n\tinvoke: (\n\t\traw: InputOf<T['operation']>,\n\t) => Promise<OperationToolOutcome<PublicOf<T['operation']>, FailureOf<T>>>\n}>\n\nexport function createOperationTools<const T extends Readonly<Record<string, ToolConstraint>>>(\n\tdefinitions: T & { [K in keyof T]: Checked<T[K]> },\n): Readonly<{ [K in keyof T]: OperationTool<T[K]> }>\nexport function createOperationTools<\n\tconst T extends readonly (ToolConstraint & { name: string })[],\n>(\n\tdefinitions: T & { [K in keyof T]: Checked<T[K]> },\n): Readonly<{ [D in T[number] as D['name']]: OperationTool<D> }>\nexport function createOperationTools(\n\tdefinitions:\n\t\t| Readonly<Record<string, ToolConstraint>>\n\t\t| readonly (ToolConstraint & { name: string })[],\n): Readonly<Record<string, OperationTool<ToolConstraint>>> {\n\tconst entries = Array.isArray(definitions)\n\t\t? definitions.map((definition) => [definition.name, definition] as const)\n\t\t: Object.entries(definitions)\n\tconst tools: Record<string, OperationTool<ToolConstraint>> = Object.create(null)\n\tfor (const [name, definition] of entries) {\n\t\tif (!name || Object.hasOwn(tools, name)) throw Error('Duplicate or empty operation tool name')\n\t\tconst paired = definition.executor && executors.get(definition.executor)\n\t\tif (\n\t\t\t!selections.has(definition.operation) ||\n\t\t\t!paired ||\n\t\t\tpaired.selection !== definition.operation\n\t\t)\n\t\t\tthrow Error('Tool requires its selected operation executor')\n\t\tif (\n\t\t\t!definition.description ||\n\t\t\tdefinition.converter?.dialect !== operationToolDialect ||\n\t\t\ttypeof definition.converter.convert !== 'function'\n\t\t)\n\t\t\tthrow Error('Unsupported operation tool converter')\n\t\t// Conversion never executes the input decoder, and invocation never executes conversion.\n\t\t// SAFETY: the checked tool overload pairs the converter with this selected input schema; the never cast bridges heterogeneous iteration without changing the schema.\n\t\tconst args = schemaSnapshot(definition.converter.convert(definition.operation.input as never))\n\t\tconst errors = definition.errors\n\t\ttools[name] = Object.freeze({\n\t\t\tname,\n\t\t\tdescription: definition.description,\n\t\t\targs,\n\t\t\tasync invoke(raw: unknown) {\n\t\t\t\ttry {\n\t\t\t\t\t// SAFETY: binding owns this raw/public pair and its native boundary performs decoding/policy/projection.\n\t\t\t\t\treturn { _tag: 'Success' as const, value: await paired.execute(raw as never) }\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// This catch runs only after the native call rejects. Native mutations retain rollback semantics.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (error instanceof ConvexError && errors) return errors.decode(error.data)\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t/* Invalid host error decoders cannot publish diagnostics. */\n\t\t\t\t\t}\n\t\t\t\t\treturn { _tag: 'UnknownFailure' as const, version: 1 as const }\n\t\t\t\t}\n\t\t\t},\n\t\t})\n\t}\n\treturn Object.freeze(tools)\n}\n\nconst schemaKeywords = new Set([\n\t'properties',\n\t'$defs',\n\t'items',\n\t'additionalProperties',\n\t'anyOf',\n\t'oneOf',\n\t'allOf',\n\t'not',\n\t'required',\n\t'enum',\n\t'const',\n\t'default',\n\t'examples',\n])\nconst types = new Set(['object', 'array', 'string', 'number', 'integer', 'boolean', 'null'])\nconst textKeywords = new Set([\n\t'title',\n\t'description',\n\t'format',\n\t'pattern',\n\t'$ref',\n\t'$id',\n\t'x-convex-table',\n])\nconst countKeywords = new Set([\n\t'minLength',\n\t'maxLength',\n\t'minItems',\n\t'maxItems',\n\t'minProperties',\n\t'maxProperties',\n])\nconst numberKeywords = new Set([\n\t'minimum',\n\t'maximum',\n\t'exclusiveMinimum',\n\t'exclusiveMaximum',\n\t'multipleOf',\n])\nconst scalarKeywords = new Set([\n\t...textKeywords,\n\t...countKeywords,\n\t...numberKeywords,\n\t'type',\n\t'uniqueItems',\n\t'$schema',\n])\n\nfunction schemaSnapshot(value: OperationToolJsonSchema): OperationToolJsonSchema {\n\tconst seen = new Set<object>()\n\tfunction copy(value: Json): Json {\n\t\tif (value === null || typeof value === 'string' || typeof value === 'boolean') return value\n\t\tif (typeof value === 'number' && Number.isFinite(value)) return value\n\t\tif (typeof value !== 'object' || seen.has(value))\n\t\t\tthrow Error('Converter schema must be JSON safe')\n\t\tseen.add(value)\n\t\ttry {\n\t\t\tif (Array.isArray(value)) return Object.freeze(value.map(copy))\n\t\t\tif (!isRecord(value)) throw Error('Converter schema must be a plain JSON object')\n\t\t\treturn Object.freeze(\n\t\t\t\tObject.fromEntries(Object.entries(value).map(([key, item]) => [key, copy(item)])),\n\t\t\t)\n\t\t} finally {\n\t\t\tseen.delete(value)\n\t\t}\n\t}\n\tfunction check(schema: Json): void {\n\t\tif (typeof schema === 'boolean') return\n\t\tif (!isRecord(schema)) throw Error('Unsupported JSON Schema shape')\n\t\tfor (const [key, child] of Object.entries(schema)) {\n\t\t\tif (!scalarKeywords.has(key) && !schemaKeywords.has(key))\n\t\t\t\tthrow Error('Unsupported JSON Schema keyword')\n\t\t\tif (textKeywords.has(key) && typeof child !== 'string')\n\t\t\t\tthrow Error('Invalid JSON Schema text keyword')\n\t\t\tif (\n\t\t\t\tcountKeywords.has(key) &&\n\t\t\t\t(typeof child !== 'number' || !Number.isInteger(child) || child < 0)\n\t\t\t)\n\t\t\t\tthrow Error('Invalid JSON Schema count keyword')\n\t\t\tif (\n\t\t\t\tnumberKeywords.has(key) &&\n\t\t\t\t(typeof child !== 'number' ||\n\t\t\t\t\t!Number.isFinite(child) ||\n\t\t\t\t\t(key === 'multipleOf' && child <= 0))\n\t\t\t)\n\t\t\t\tthrow Error('Invalid JSON Schema number keyword')\n\t\t\tif (key === 'uniqueItems' && typeof child !== 'boolean')\n\t\t\t\tthrow Error('Invalid JSON Schema boolean keyword')\n\t\t\tif (key === 'pattern') {\n\t\t\t\ttry {\n\t\t\t\t\t// SAFETY: pattern belongs to textKeywords and passed the string check above; this validates syntax without matching input.\n\t\t\t\t\tnew RegExp(child as string)\n\t\t\t\t} catch {\n\t\t\t\t\tthrow Error('Invalid JSON Schema pattern')\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((key === 'enum' || key === 'examples') && !Array.isArray(child))\n\t\t\t\tthrow Error('Invalid JSON Schema array keyword')\n\t\t\tif (key === 'enum' && Array.isArray(child) && !child.length)\n\t\t\t\tthrow Error('Invalid JSON Schema enum')\n\t\t\tif (key === '$schema' && child !== operationToolDialect)\n\t\t\t\tthrow Error('Unsupported JSON Schema dialect')\n\t\t\tif (\n\t\t\t\tkey === 'type' &&\n\t\t\t\t!(typeof child === 'string' && types.has(child)) &&\n\t\t\t\t!(\n\t\t\t\t\tArray.isArray(child) &&\n\t\t\t\t\tchild.length > 0 &&\n\t\t\t\t\tchild.every((type) => typeof type === 'string' && types.has(type))\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tthrow Error('Unsupported JSON Schema type')\n\t\t\tif (key === 'properties' || key === '$defs') {\n\t\t\t\tif (!isRecord(child)) throw Error('Unsupported JSON Schema properties')\n\t\t\t\tObject.values(child).forEach(check)\n\t\t\t} else if (key === 'items' || key === 'not' || key === 'additionalProperties') check(child)\n\t\t\telse if (key === 'anyOf' || key === 'oneOf' || key === 'allOf') {\n\t\t\t\tif (!Array.isArray(child) || !child.length)\n\t\t\t\t\tthrow Error('Unsupported JSON Schema alternatives')\n\t\t\t\tchild.forEach(check)\n\t\t\t} else if (\n\t\t\t\tkey === 'required' &&\n\t\t\t\t(!Array.isArray(child) ||\n\t\t\t\t\tchild.some((field) => typeof field !== 'string') ||\n\t\t\t\t\tnew Set(child).size !== child.length)\n\t\t\t)\n\t\t\t\tthrow Error('Unsupported JSON Schema required fields')\n\t\t}\n\t}\n\tconst snapshot = copy(value)\n\tif (!isRecord(snapshot) || snapshot.type !== 'object')\n\t\tthrow Error('Operation tool arguments require an object JSON Schema')\n\tcheck(snapshot)\n\treturn snapshot\n}\nfunction isRecord(value: Json): value is { readonly [key: string]: Json } {\n\tif (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n\tconst prototype = Object.getPrototypeOf(value)\n\treturn prototype === Object.prototype || prototype === null\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"exposure.mjs","names":["selections","executors","selectOperation","owner","key","kind","definition","selected","bindOperationExecutor","configuration","executor","operationToolDialect","createOperationTools","entries","definitions","tools","name","paired","args","schemaSnapshot","errors","raw","error","ConvexError","schemaKeywords","types","textKeywords","countKeywords","numberKeywords","scalarKeywords","seen","copy","value","isRecord","item","check","schema","child","type","field","snapshot","prototype"],"sources":["../../../src/modules/contracts/exposure.ts"],"sourcesContent":["/* oxlint-disable anti-slop/no-unknown-parameters, anti-slop/no-unknown-type-aliases, anti-slop/no-runtime-typeof, anti-slop/no-unsafe-dictionary-type -- Selected native transport boundaries validate opaque identities and converter JSON without decoding domain inputs. */\nimport { ConvexError } from 'convex/values'\nimport type { ContractInput, ContractOutput, ContractSchema } from './contract'\nimport type { DecodedFailure, ErrorContract, ErrorDefinitions, UnknownFailure } from './errors'\n\ndeclare const selectedTypes: unique symbol\ndeclare const executorTypes: unique symbol\n\ntype Definition = {\n\tinput: ContractSchema\n\tresult: ContractSchema\n\twire?: { schema: ContractSchema }\n}\nexport type OperationPublicResult<D extends Definition> = D extends {\n\twire: { schema: infer Schema extends ContractSchema }\n}\n\t? ContractInput<Schema>\n\t: ContractOutput<D['result']>\n\n/** Only the selected input/public contracts are exposed, never handlers or other operations. */\nexport type SelectedOperation<\n\tOwner extends symbol,\n\tKey extends string,\n\tInput extends ContractSchema,\n\tPublic,\n> = Readonly<{\n\towner: Owner\n\tkey: Key\n\tkind: 'mutation' | 'query'\n\tinput: Input\n\tresult: ContractSchema\n\treadonly [selectedTypes]: { readonly input: ContractInput<Input>; readonly public: Public }\n}>\ntype Selection = Readonly<{\n\towner: symbol\n\tkey: string\n\tkind: 'mutation' | 'query'\n\tinput: ContractSchema\n\tresult: ContractSchema\n\treadonly [selectedTypes]: { readonly input: unknown; readonly public: unknown }\n}>\ntype InputOf<S extends Selection> = S[typeof selectedTypes]['input']\ntype PublicOf<S extends Selection> = S[typeof selectedTypes]['public']\n\nconst selections = new WeakSet<object>()\nconst executors = new WeakMap<\n\tobject,\n\t{\n\t\tselection: Selection\n\t\t// oxlint-disable-next-line anti-slop/no-unknown-returns -- Private heterogeneous storage erases public result types; selected identity checks recover the exact raw/public pair at invocation.\n\t\texecute: (input: never) => Promise<unknown>\n\t}\n>()\n\n/** Registry integrators must check membership before selecting their own definition. */\nexport function selectOperation<\n\tconst Owner extends symbol,\n\tconst Key extends string,\n\tconst D extends Definition,\n>(\n\towner: Owner,\n\tkind: 'mutation' | 'query',\n\tkey: Key,\n\tdefinition: D,\n): SelectedOperation<Owner, Key, D['input'], OperationPublicResult<D>> {\n\tif (\n\t\ttypeof owner !== 'symbol' ||\n\t\t!key ||\n\t\t(kind !== 'mutation' && kind !== 'query') ||\n\t\t!definition?.input ||\n\t\t!definition.result\n\t) {\n\t\tthrow Error('Invalid selected operation')\n\t}\n\t// SAFETY: private identity records this exact paired schema; its type-only slots are never read at runtime.\n\tconst selected = Object.freeze({\n\t\towner,\n\t\tkey,\n\t\tkind,\n\t\tinput: definition.input,\n\t\tresult: definition.wire?.schema ?? definition.result,\n\t}) as SelectedOperation<Owner, Key, D['input'], OperationPublicResult<D>>\n\tselections.add(selected)\n\treturn selected\n}\n\nexport type OperationExecutor<S extends Selection> = Readonly<{\n\towner: S['owner']\n\tkey: S['key']\n\treadonly [executorTypes]: S\n}>\n\n/** The host captures authority in an already authorized native Promise call. */\nexport function bindOperationExecutor<const S extends Selection>(\n\tselected: S,\n\tconfiguration: {\n\t\towner: NoInfer<S['owner']>\n\t\tkey: NoInfer<S['key']>\n\t\texecute: (raw: NoInfer<InputOf<S>>) => Promise<NoInfer<PublicOf<S>>>\n\t},\n): OperationExecutor<S> {\n\tif (\n\t\t!selections.has(selected) ||\n\t\tconfiguration.owner !== selected.owner ||\n\t\tconfiguration.key !== selected.key ||\n\t\ttypeof configuration.execute !== 'function'\n\t) {\n\t\tthrow Error('Executor does not own the selected operation')\n\t}\n\t// SAFETY: the private map restores the concrete selected raw/public pair only after identity checks.\n\tconst executor = Object.freeze({\n\t\towner: selected.owner,\n\t\tkey: selected.key,\n\t}) as OperationExecutor<S>\n\texecutors.set(executor, {\n\t\tselection: selected,\n\t\texecute: configuration.execute,\n\t})\n\treturn executor\n}\n\nexport const operationToolDialect = 'https://json-schema.org/draft/2020-12/schema' as const\ntype Json = null | boolean | number | string | readonly Json[] | { readonly [key: string]: Json }\nexport type OperationToolJsonSchema = Readonly<{ [keyword: string]: Json }>\nexport type OperationToolConverter<Input extends ContractSchema = ContractSchema> = Readonly<{\n\tdialect: typeof operationToolDialect\n\t/** Represent raw transport input. Unsupported IDs/transforms must throw, never be approximated. */\n\tconvert: (input: Input) => OperationToolJsonSchema\n}>\nexport type OperationToolDefinition<\n\tS extends Selection = Selection,\n\tErrors extends ErrorDefinitions = ErrorDefinitions,\n> = Readonly<{\n\toperation: S\n\texecutor: OperationExecutor<S>\n\tdescription: string\n\tconverter: OperationToolConverter<S['input']>\n\terrors?: Pick<ErrorContract<Errors>, 'decode'>\n}>\ntype ToolConstraint = Readonly<{\n\toperation: Selection\n\texecutor: OperationExecutor<Selection>\n\tdescription: string\n\tconverter: {\n\t\tdialect: typeof operationToolDialect\n\t\tconvert: (input: never) => OperationToolJsonSchema\n\t}\n\terrors?: Pick<ErrorContract<ErrorDefinitions>, 'decode'>\n}>\ntype Checked<T extends ToolConstraint> = T & OperationToolDefinition<T['operation']>\ntype FailureOf<T> = T extends { errors: Pick<ErrorContract<infer E>, 'decode'> }\n\t? DecodedFailure<E>\n\t: UnknownFailure\nexport type OperationToolOutcome<Public, Failure = UnknownFailure> =\n\t| Readonly<{ _tag: 'Success'; value: Public }>\n\t| Failure\nexport type OperationTool<T extends ToolConstraint> = Readonly<{\n\tname: string\n\tdescription: string\n\targs: OperationToolJsonSchema\n\tinvoke: (\n\t\traw: InputOf<T['operation']>,\n\t) => Promise<OperationToolOutcome<PublicOf<T['operation']>, FailureOf<T>>>\n}>\n\nexport function createOperationTools<const T extends Readonly<Record<string, ToolConstraint>>>(\n\tdefinitions: T & { [K in keyof T]: Checked<T[K]> },\n): Readonly<{ [K in keyof T]: OperationTool<T[K]> }>\nexport function createOperationTools<\n\tconst T extends readonly (ToolConstraint & { name: string })[],\n>(\n\tdefinitions: T & { [K in keyof T]: Checked<T[K]> },\n): Readonly<{ [D in T[number] as D['name']]: OperationTool<D> }>\nexport function createOperationTools(\n\tdefinitions:\n\t\t| Readonly<Record<string, ToolConstraint>>\n\t\t| readonly (ToolConstraint & { name: string })[],\n): Readonly<Record<string, OperationTool<ToolConstraint>>> {\n\tconst entries = Array.isArray(definitions)\n\t\t? definitions.map((definition) => [definition.name, definition] as const)\n\t\t: Object.entries(definitions)\n\tconst tools: Record<string, OperationTool<ToolConstraint>> = Object.create(null)\n\tfor (const [name, definition] of entries) {\n\t\tif (!name || Object.hasOwn(tools, name)) throw Error('Duplicate or empty operation tool name')\n\t\tconst paired = definition.executor && executors.get(definition.executor)\n\t\tif (\n\t\t\t!selections.has(definition.operation) ||\n\t\t\t!paired ||\n\t\t\tpaired.selection !== definition.operation\n\t\t)\n\t\t\tthrow Error('Tool requires its selected operation executor')\n\t\tif (\n\t\t\t!definition.description ||\n\t\t\tdefinition.converter?.dialect !== operationToolDialect ||\n\t\t\ttypeof definition.converter.convert !== 'function'\n\t\t)\n\t\t\tthrow Error('Unsupported operation tool converter')\n\t\t// Conversion never executes the input decoder, and invocation never executes conversion.\n\t\t// SAFETY: the checked tool overload pairs the converter with this selected input schema; the never cast bridges heterogeneous iteration without changing the schema.\n\t\tconst args = schemaSnapshot(definition.converter.convert(definition.operation.input as never))\n\t\tconst errors = definition.errors\n\t\ttools[name] = Object.freeze({\n\t\t\tname,\n\t\t\tdescription: definition.description,\n\t\t\targs,\n\t\t\tasync invoke(raw: unknown) {\n\t\t\t\ttry {\n\t\t\t\t\t// SAFETY: binding owns this raw/public pair and its native boundary performs decoding/policy/projection.\n\t\t\t\t\treturn { _tag: 'Success' as const, value: await paired.execute(raw as never) }\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// This catch runs only after the native call rejects. Native mutations retain rollback semantics.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (error instanceof ConvexError && errors) return errors.decode(error.data)\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t/* Invalid host error decoders cannot publish diagnostics. */\n\t\t\t\t\t}\n\t\t\t\t\treturn { _tag: 'UnknownFailure' as const, version: 1 as const }\n\t\t\t\t}\n\t\t\t},\n\t\t})\n\t}\n\treturn Object.freeze(tools)\n}\n\nconst schemaKeywords = new Set([\n\t'properties',\n\t'$defs',\n\t'items',\n\t'additionalProperties',\n\t'anyOf',\n\t'oneOf',\n\t'allOf',\n\t'not',\n\t'required',\n\t'enum',\n\t'const',\n\t'default',\n\t'examples',\n])\nconst types = new Set(['object', 'array', 'string', 'number', 'integer', 'boolean', 'null'])\nconst textKeywords = new Set([\n\t'title',\n\t'description',\n\t'format',\n\t'pattern',\n\t'$ref',\n\t'$id',\n\t'x-convex-table',\n])\nconst countKeywords = new Set([\n\t'minLength',\n\t'maxLength',\n\t'minItems',\n\t'maxItems',\n\t'minProperties',\n\t'maxProperties',\n])\nconst numberKeywords = new Set([\n\t'minimum',\n\t'maximum',\n\t'exclusiveMinimum',\n\t'exclusiveMaximum',\n\t'multipleOf',\n])\nconst scalarKeywords = new Set([\n\t...textKeywords,\n\t...countKeywords,\n\t...numberKeywords,\n\t'type',\n\t'uniqueItems',\n\t'$schema',\n])\n\nfunction schemaSnapshot(value: OperationToolJsonSchema): OperationToolJsonSchema {\n\tconst seen = new Set<object>()\n\tfunction copy(value: Json): Json {\n\t\tif (value === null || typeof value === 'string' || typeof value === 'boolean') return value\n\t\tif (typeof value === 'number' && Number.isFinite(value)) return value\n\t\tif (typeof value !== 'object' || seen.has(value))\n\t\t\tthrow Error('Converter schema must be JSON safe')\n\t\tseen.add(value)\n\t\ttry {\n\t\t\tif (Array.isArray(value)) return Object.freeze(value.map(copy))\n\t\t\tif (!isRecord(value)) throw Error('Converter schema must be a plain JSON object')\n\t\t\treturn Object.freeze(\n\t\t\t\tObject.fromEntries(Object.entries(value).map(([key, item]) => [key, copy(item)])),\n\t\t\t)\n\t\t} finally {\n\t\t\tseen.delete(value)\n\t\t}\n\t}\n\tfunction check(schema: Json): void {\n\t\tif (typeof schema === 'boolean') return\n\t\tif (!isRecord(schema)) throw Error('Unsupported JSON Schema shape')\n\t\tfor (const [key, child] of Object.entries(schema)) {\n\t\t\tif (!scalarKeywords.has(key) && !schemaKeywords.has(key))\n\t\t\t\tthrow Error('Unsupported JSON Schema keyword')\n\t\t\tif (textKeywords.has(key) && typeof child !== 'string')\n\t\t\t\tthrow Error('Invalid JSON Schema text keyword')\n\t\t\tif (\n\t\t\t\tcountKeywords.has(key) &&\n\t\t\t\t(typeof child !== 'number' || !Number.isInteger(child) || child < 0)\n\t\t\t)\n\t\t\t\tthrow Error('Invalid JSON Schema count keyword')\n\t\t\tif (\n\t\t\t\tnumberKeywords.has(key) &&\n\t\t\t\t(typeof child !== 'number' ||\n\t\t\t\t\t!Number.isFinite(child) ||\n\t\t\t\t\t(key === 'multipleOf' && child <= 0))\n\t\t\t)\n\t\t\t\tthrow Error('Invalid JSON Schema number keyword')\n\t\t\tif (key === 'uniqueItems' && typeof child !== 'boolean')\n\t\t\t\tthrow Error('Invalid JSON Schema boolean keyword')\n\t\t\tif (key === 'pattern') {\n\t\t\t\ttry {\n\t\t\t\t\t// SAFETY: pattern belongs to textKeywords and passed the string check above; this validates syntax without matching input.\n\t\t\t\t\tnew RegExp(child as string)\n\t\t\t\t} catch {\n\t\t\t\t\tthrow Error('Invalid JSON Schema pattern')\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((key === 'enum' || key === 'examples') && !Array.isArray(child))\n\t\t\t\tthrow Error('Invalid JSON Schema array keyword')\n\t\t\tif (key === 'enum' && Array.isArray(child) && !child.length)\n\t\t\t\tthrow Error('Invalid JSON Schema enum')\n\t\t\tif (key === '$schema' && child !== operationToolDialect)\n\t\t\t\tthrow Error('Unsupported JSON Schema dialect')\n\t\t\tif (\n\t\t\t\tkey === 'type' &&\n\t\t\t\t!(typeof child === 'string' && types.has(child)) &&\n\t\t\t\t!(\n\t\t\t\t\tArray.isArray(child) &&\n\t\t\t\t\tchild.length > 0 &&\n\t\t\t\t\tchild.every((type) => typeof type === 'string' && types.has(type))\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tthrow Error('Unsupported JSON Schema type')\n\t\t\tif (key === 'properties' || key === '$defs') {\n\t\t\t\tif (!isRecord(child)) throw Error('Unsupported JSON Schema properties')\n\t\t\t\tObject.values(child).forEach(check)\n\t\t\t} else if (key === 'items' || key === 'not' || key === 'additionalProperties') check(child)\n\t\t\telse if (key === 'anyOf' || key === 'oneOf' || key === 'allOf') {\n\t\t\t\tif (!Array.isArray(child) || !child.length)\n\t\t\t\t\tthrow Error('Unsupported JSON Schema alternatives')\n\t\t\t\tchild.forEach(check)\n\t\t\t} else if (\n\t\t\t\tkey === 'required' &&\n\t\t\t\t(!Array.isArray(child) ||\n\t\t\t\t\tchild.some((field) => typeof field !== 'string') ||\n\t\t\t\t\tnew Set(child).size !== child.length)\n\t\t\t)\n\t\t\t\tthrow Error('Unsupported JSON Schema required fields')\n\t\t}\n\t}\n\tconst snapshot = copy(value)\n\tif (!isRecord(snapshot) || snapshot.type !== 'object')\n\t\tthrow Error('Operation tool arguments require an object JSON Schema')\n\tcheck(snapshot)\n\treturn snapshot\n}\nfunction isRecord(value: Json): value is { readonly [key: string]: Json } {\n\tif (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n\tconst prototype = Object.getPrototypeOf(value)\n\treturn prototype === Object.prototype || prototype === null\n}\n"],"mappings":"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"}
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
import { ContractInput, ContractOutput, NeutralSchema } from "./contract.mjs";
|
|
2
2
|
//#region src/modules/contracts/idempotency.d.ts
|
|
3
|
-
type IdempotencyErrorCode = 'unsupported-binding' | 'invalid-input' | 'corrupt' | 'ambiguous' | 'pending' | 'stale' | 'conflict';
|
|
4
|
-
declare class IdempotencyError extends Error {
|
|
3
|
+
export type IdempotencyErrorCode = 'unsupported-binding' | 'invalid-input' | 'corrupt' | 'ambiguous' | 'pending' | 'stale' | 'conflict';
|
|
4
|
+
export declare class IdempotencyError extends Error {
|
|
5
5
|
readonly code: IdempotencyErrorCode;
|
|
6
6
|
readonly _tag = "IdempotencyError";
|
|
7
7
|
constructor(code: IdempotencyErrorCode, message: string);
|
|
8
8
|
}
|
|
9
|
-
interface IdempotencyIdentity {
|
|
9
|
+
export interface IdempotencyIdentity {
|
|
10
10
|
readonly operation: string;
|
|
11
11
|
/** Stable trusted tenant/principal or delegated-capability scope, supplied by the host. */
|
|
12
12
|
readonly scope: string;
|
|
13
13
|
readonly key: string;
|
|
14
14
|
}
|
|
15
|
-
interface IdempotencyVersions {
|
|
15
|
+
export interface IdempotencyVersions {
|
|
16
16
|
readonly operation: string;
|
|
17
17
|
readonly contract: string;
|
|
18
18
|
readonly binding: string;
|
|
19
19
|
readonly fingerprintPolicy: string;
|
|
20
20
|
}
|
|
21
|
-
interface IdempotencyReceipt {
|
|
21
|
+
export interface IdempotencyReceipt {
|
|
22
22
|
readonly versions: IdempotencyVersions;
|
|
23
23
|
readonly fingerprint: string;
|
|
24
24
|
readonly state: 'pending' | 'completed';
|
|
25
25
|
readonly result?: unknown;
|
|
26
26
|
}
|
|
27
|
-
interface CanonicalConvexBounds {
|
|
27
|
+
export interface CanonicalConvexBounds {
|
|
28
28
|
readonly maxDepth: number;
|
|
29
29
|
readonly maxNodes: number;
|
|
30
30
|
readonly maxBytes: number;
|
|
@@ -32,14 +32,14 @@ interface CanonicalConvexBounds {
|
|
|
32
32
|
readonly maxObjectFields: number;
|
|
33
33
|
}
|
|
34
34
|
/** Bounded transport encoding, using the pinned Convex integer/binary/float representation. */
|
|
35
|
-
declare function canonicalConvexBytes(value: unknown, bounds: CanonicalConvexBounds): string;
|
|
35
|
+
export declare function canonicalConvexBytes(value: unknown, bounds: CanonicalConvexBounds): string;
|
|
36
36
|
declare const invocationBytes: unique symbol;
|
|
37
|
-
interface IdempotencyInvocation {
|
|
37
|
+
export interface IdempotencyInvocation {
|
|
38
38
|
readonly identity: IdempotencyIdentity;
|
|
39
39
|
readonly versions: IdempotencyVersions;
|
|
40
40
|
readonly [invocationBytes]: string;
|
|
41
41
|
}
|
|
42
|
-
interface IdempotencyCapture {
|
|
42
|
+
export interface IdempotencyCapture {
|
|
43
43
|
readonly binding: {
|
|
44
44
|
readonly kind: 'mutation';
|
|
45
45
|
readonly atomicity: 'same-mutation';
|
|
@@ -54,12 +54,12 @@ interface IdempotencyCapture {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
/** Call at the trusted native handler before domain decoding, once per invocation (including nested calls). */
|
|
57
|
-
declare function captureIdempotencyInvocation(capture: IdempotencyCapture): IdempotencyInvocation;
|
|
57
|
+
export declare function captureIdempotencyInvocation(capture: IdempotencyCapture): IdempotencyInvocation;
|
|
58
58
|
/** Shared protocol checks have no Effect dependency and never decode final wire data. */
|
|
59
|
-
declare function retainedIdempotencyReceipt(rows: readonly unknown[], invocation: IdempotencyInvocation): IdempotencyReceipt | undefined;
|
|
60
|
-
declare function idempotencyCanonicalInput(invocation: IdempotencyInvocation): string;
|
|
61
|
-
declare function idempotencyFingerprint(value: string, receipt?: IdempotencyReceipt): string;
|
|
62
|
-
type IdempotencyPreparation<Result> = {
|
|
59
|
+
export declare function retainedIdempotencyReceipt(rows: readonly unknown[], invocation: IdempotencyInvocation): IdempotencyReceipt | undefined;
|
|
60
|
+
export declare function idempotencyCanonicalInput(invocation: IdempotencyInvocation): string;
|
|
61
|
+
export declare function idempotencyFingerprint(value: string, receipt?: IdempotencyReceipt): string;
|
|
62
|
+
export type IdempotencyPreparation<Result> = {
|
|
63
63
|
readonly kind: 'replay';
|
|
64
64
|
readonly result: unknown;
|
|
65
65
|
} | {
|
|
@@ -68,7 +68,7 @@ type IdempotencyPreparation<Result> = {
|
|
|
68
68
|
};
|
|
69
69
|
type NeutralValue<A> = A | PromiseLike<A>;
|
|
70
70
|
/** Host callbacks close over the same secured mutation writer; native OCC/rollback remains host-owned. */
|
|
71
|
-
declare function transactionalIdempotency<const Replay extends NeutralSchema, Claim>(configuration: {
|
|
71
|
+
export declare function transactionalIdempotency<const Replay extends NeutralSchema, Claim>(configuration: {
|
|
72
72
|
readonly final: {
|
|
73
73
|
readonly replayResult: Replay;
|
|
74
74
|
readonly encode: (result: ContractOutput<Replay>) => NeutralValue<ContractInput<Replay>>;
|
|
@@ -83,5 +83,4 @@ declare function transactionalIdempotency<const Replay extends NeutralSchema, Cl
|
|
|
83
83
|
prepare(invocation: IdempotencyInvocation): Promise<IdempotencyPreparation<ContractOutput<Replay>>>;
|
|
84
84
|
};
|
|
85
85
|
//#endregion
|
|
86
|
-
export { CanonicalConvexBounds, IdempotencyCapture, IdempotencyError, IdempotencyErrorCode, IdempotencyIdentity, IdempotencyInvocation, IdempotencyPreparation, IdempotencyReceipt, IdempotencyVersions, canonicalConvexBytes, captureIdempotencyInvocation, idempotencyCanonicalInput, idempotencyFingerprint, retainedIdempotencyReceipt, transactionalIdempotency };
|
|
87
86
|
//# sourceMappingURL=idempotency.d.mts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{convexToJson as e}from"convex/values";var t=class extends Error{code;_tag=`IdempotencyError`;constructor(e,t){super(t),this.code=e,this.name=`IdempotencyError`}};const n=new TextEncoder;function r(t,r){for(let e of[r.maxDepth,r.maxNodes,r.maxBytes,r.maxArrayLength,r.maxObjectFields])(!Number.isSafeInteger(e)||e<1)&&d(`Canonical bounds must be positive safe integers`);let i
|
|
1
|
+
import{convexToJson as e}from"convex/values";var t=class extends Error{code;_tag=`IdempotencyError`;constructor(e,t){super(t),this.code=e,this.name=`IdempotencyError`}};const n=new TextEncoder;function r(t,r){for(let e of[r.maxDepth,r.maxNodes,r.maxBytes,r.maxArrayLength,r.maxObjectFields])(!Number.isSafeInteger(e)||e<1)&&d(`Canonical bounds must be positive safe integers`);let i=/* @__PURE__ */ new Set,a=0,o=0;function s(e){o+=e,o>r.maxBytes&&d(`Canonical input exceeds byte bound`)}function c(e){return e.length>r.maxBytes&&d(`Canonical input exceeds byte bound`),n.encode(e).length}function l(e,t){if((++a>r.maxNodes||t>r.maxDepth)&&d(`Canonical input exceeds traversal bound`),e===null||typeof e==`boolean`){s(1);return}if(typeof e==`number`){s(9);return}if(typeof e==`bigint`){(e<-(1n<<63n)||e>(1n<<63n)-1n)&&d(`Convex integer exceeds signed int64`),s(9);return}if(typeof e==`string`){s(2+c(e));return}if(e instanceof ArrayBuffer){s(2+e.byteLength);return}typeof e!=`object`&&d(`Unsupported canonical Convex value`),i.has(e)&&d(`Cyclic canonical input`),i.add(e);try{if(Array.isArray(e)){e.length>r.maxArrayLength&&d(`Canonical array exceeds length bound`),s(2);for(let n of e)l(n,t+1);return}let n=Object.getPrototypeOf(e);n!==Object.prototype&&n!==null&&d(`Canonical input requires plain objects`),Object.getOwnPropertySymbols(e).length&&d(`Unsupported canonical symbol keys`);let i=Object.keys(e);i.length>r.maxObjectFields&&d(`Canonical object exceeds field bound`),s(2);for(let n of i){(n.length>1024||n.startsWith(`$`)||/[^\x20-\x7e]/.test(n))&&d(`Invalid Convex object key`);let r=Object.getOwnPropertyDescriptor(e,n);`value`in r||d(`Canonical input cannot contain accessors`),r.value!==void 0&&(s(c(n)+1),l(r.value,t+1))}}finally{i.delete(e)}}l(t,0);function u(t){if(Array.isArray(t))return t.map(u);if(typeof t==`object`&&t&&!(t instanceof ArrayBuffer)){let e=Object.create(null);for(let n of Object.keys(t).sort()){let r=t[n];r!==void 0&&(e[n]=u(r))}return e}return e(t)}let f=JSON.stringify(u(t));return n.encode(f).length>r.maxBytes&&d(`Canonical encoding exceeds byte bound`),f}const i=[`operation`,`contract`,`binding`,`fingerprintPolicy`],a=Symbol(`idempotency invocation bytes`);function o(e){if(e.binding.kind!==`mutation`||e.binding.atomicity!==`same-mutation`)throw new t(`unsupported-binding`,`Idempotency requires the current native mutation transaction`);let n=Object.freeze({operation:e.identity.operation,scope:e.identity.scope,key:e.identity.key}),i=Object.freeze({operation:e.versions.operation,contract:e.versions.contract,binding:e.versions.binding,fingerprintPolicy:e.versions.fingerprintPolicy});(!f(n)||!p(i))&&d(`Invalid trusted idempotency metadata`);let o=r(e.canonical.normalize?e.canonical.normalize(e.rawInput):e.rawInput,e.canonical.bounds);return Object.freeze({identity:n,versions:i,[a]:o})}function s(e,n){if(!Array.isArray(e))throw new t(`corrupt`,`Receipt lookup must return rows`);if(e.length>1)throw new t(`ambiguous`,`Multiple receipts match the stable identity`);if(e.length===0)return;let r=e[0];if(typeof r!=`object`||!r||!Object.hasOwn(r,`versions`)||!Object.hasOwn(r,`fingerprint`)||!Object.hasOwn(r,`state`))throw new t(`corrupt`,`Malformed retained receipt`);let a=r;if(!p(a.versions)||typeof a.fingerprint!=`string`||![`pending`,`completed`].includes(a.state)||a.state===`completed`&&!Object.hasOwn(a,`result`))throw new t(`corrupt`,`Malformed retained receipt`);if(i.some(e=>a.versions[e]!==n.versions[e]))throw new t(`stale`,`Retained receipt version differs from the current operation`);if(a.state===`pending`)throw new t(`pending`,`Retained receipt is incomplete`);return a}function c(e){if(!Object.hasOwn(e,a))throw new t(`unsupported-binding`,`Missing trusted invocation capture`);return e[a]}function l(e,n){if(typeof e!=`string`||e.length===0)throw new t(`invalid-input`,`Fingerprint policy must return a nonempty string`);if(n&&n.fingerprint!==e)throw new t(`conflict`,`Caller key was already used with different input`);return e}function u(e){return{replayResult:e.final.replayResult,async prepare(t){let n=c(t);await e.authorize(t);let r=s(await e.lookup(t.identity),t),i=l(await e.fingerprint(n),r);if(r)return{kind:`replay`,result:r.result};let a=await e.claim(t.identity,{state:`pending`,versions:t.versions,fingerprint:i});return{kind:`execute`,complete:async n=>{let r=await e.final.encode(n);await e.complete(a,{state:`completed`,versions:t.versions,fingerprint:i,result:r})}}}}}function d(e){throw new t(`invalid-input`,e)}function f(e){return[e.operation,e.scope,e.key].every(e=>typeof e==`string`&&e.length>0)}function p(e){if(typeof e!=`object`||!e)return!1;let t=e;return i.every(e=>{let n=t[e];return Object.hasOwn(t,e)&&typeof n==`string`&&n.length>0})}export{t as IdempotencyError,r as canonicalConvexBytes,o as captureIdempotencyInvocation,c as idempotencyCanonicalInput,l as idempotencyFingerprint,s as retainedIdempotencyReceipt,u as transactionalIdempotency};
|
|
2
2
|
//# sourceMappingURL=idempotency.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idempotency.mjs","names":[],"sources":["../../../src/modules/contracts/idempotency.ts"],"sourcesContent":["/* oxlint-disable anti-slop/no-unknown-parameters, anti-slop/no-runtime-typeof -- Canonical transport and retained receipts are explicit untrusted value boundaries. */\nimport { convexToJson, type Value, type JSONValue } from 'convex/values'\nimport type { ContractInput, ContractOutput, NeutralSchema } from './contract'\n\nexport type IdempotencyErrorCode =\n\t| 'unsupported-binding'\n\t| 'invalid-input'\n\t| 'corrupt'\n\t| 'ambiguous'\n\t| 'pending'\n\t| 'stale'\n\t| 'conflict'\nexport class IdempotencyError extends Error {\n\treadonly _tag = 'IdempotencyError'\n\tconstructor(\n\t\treadonly code: IdempotencyErrorCode,\n\t\tmessage: string,\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'IdempotencyError'\n\t}\n}\nexport interface IdempotencyIdentity {\n\treadonly operation: string\n\t/** Stable trusted tenant/principal or delegated-capability scope, supplied by the host. */\n\treadonly scope: string\n\treadonly key: string\n}\nexport interface IdempotencyVersions {\n\treadonly operation: string\n\treadonly contract: string\n\treadonly binding: string\n\treadonly fingerprintPolicy: string\n}\nexport interface IdempotencyReceipt {\n\treadonly versions: IdempotencyVersions\n\treadonly fingerprint: string\n\treadonly state: 'pending' | 'completed'\n\treadonly result?: unknown\n}\nexport interface CanonicalConvexBounds {\n\treadonly maxDepth: number\n\treadonly maxNodes: number\n\treadonly maxBytes: number\n\treadonly maxArrayLength: number\n\treadonly maxObjectFields: number\n}\n\nconst encoder = new TextEncoder()\n/** Bounded transport encoding, using the pinned Convex integer/binary/float representation. */\nexport function canonicalConvexBytes(value: unknown, bounds: CanonicalConvexBounds): string {\n\tfor (const bound of [\n\t\tbounds.maxDepth,\n\t\tbounds.maxNodes,\n\t\tbounds.maxBytes,\n\t\tbounds.maxArrayLength,\n\t\tbounds.maxObjectFields,\n\t])\n\t\tif (!Number.isSafeInteger(bound) || bound < 1)\n\t\t\tinvalid('Canonical bounds must be positive safe integers')\n\tconst ancestors = new Set<object>()\n\tlet nodes = 0\n\tlet bytes = 0\n\tfunction spend(amount: number) {\n\t\tbytes += amount\n\t\tif (bytes > bounds.maxBytes) invalid('Canonical input exceeds byte bound')\n\t}\n\tfunction textBytes(text: string) {\n\t\tif (text.length > bounds.maxBytes) invalid('Canonical input exceeds byte bound')\n\t\treturn encoder.encode(text).length\n\t}\n\tfunction visit(current: unknown, depth: number): void {\n\t\tif (++nodes > bounds.maxNodes || depth > bounds.maxDepth)\n\t\t\tinvalid('Canonical input exceeds traversal bound')\n\t\tif (current === null || typeof current === 'boolean') {\n\t\t\tspend(1)\n\t\t\treturn\n\t\t}\n\t\tif (typeof current === 'number') {\n\t\t\tspend(9)\n\t\t\treturn\n\t\t}\n\t\tif (typeof current === 'bigint') {\n\t\t\tif (current < -(1n << 63n) || current > (1n << 63n) - 1n)\n\t\t\t\tinvalid('Convex integer exceeds signed int64')\n\t\t\tspend(9)\n\t\t\treturn\n\t\t}\n\t\tif (typeof current === 'string') {\n\t\t\tspend(2 + textBytes(current))\n\t\t\treturn\n\t\t}\n\t\tif (current instanceof ArrayBuffer) {\n\t\t\tspend(2 + current.byteLength)\n\t\t\treturn\n\t\t}\n\t\tif (typeof current !== 'object') invalid('Unsupported canonical Convex value')\n\t\tif (ancestors.has(current)) invalid('Cyclic canonical input')\n\t\tancestors.add(current)\n\t\ttry {\n\t\t\tif (Array.isArray(current)) {\n\t\t\t\tif (current.length > bounds.maxArrayLength) invalid('Canonical array exceeds length bound')\n\t\t\t\tspend(2)\n\t\t\t\tfor (const item of current) visit(item, depth + 1)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst prototype = Object.getPrototypeOf(current)\n\t\t\tif (prototype !== Object.prototype && prototype !== null)\n\t\t\t\tinvalid('Canonical input requires plain objects')\n\t\t\tif (Object.getOwnPropertySymbols(current).length) invalid('Unsupported canonical symbol keys')\n\t\t\tconst keys = Object.keys(current)\n\t\t\tif (keys.length > bounds.maxObjectFields) invalid('Canonical object exceeds field bound')\n\t\t\tspend(2)\n\t\t\tfor (const key of keys) {\n\t\t\t\tif (key.length > 1024 || key.startsWith('$') || /[^\\x20-\\x7e]/.test(key))\n\t\t\t\t\tinvalid('Invalid Convex object key')\n\t\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(current, key)!\n\t\t\t\tif (!('value' in descriptor)) invalid('Canonical input cannot contain accessors')\n\t\t\t\tif (descriptor.value === undefined) continue\n\t\t\t\tspend(textBytes(key) + 1)\n\t\t\t\tvisit(descriptor.value, depth + 1)\n\t\t\t}\n\t\t} finally {\n\t\t\tancestors.delete(current)\n\t\t}\n\t}\n\tvisit(value, 0)\n\t// Object projection retains own __proto__ keys which the SDK's assignment-based object converter drops.\n\t// Scalars still use the pinned SDK's exact integer/binary/float representation.\n\tfunction project(current: Value): JSONValue {\n\t\tif (Array.isArray(current)) return current.map(project)\n\t\tif (current !== null && typeof current === 'object' && !(current instanceof ArrayBuffer)) {\n\t\t\tconst output: Record<string, JSONValue> = Object.create(null)\n\t\t\tfor (const key of Object.keys(current).sort()) {\n\t\t\t\t// SAFETY: prior bounded traversal excluded every object type except plain Convex dictionaries.\n\t\t\t\tconst item = (current as Record<string, Value | undefined>)[key]\n\t\t\t\tif (item !== undefined) output[key] = project(item)\n\t\t\t}\n\t\t\treturn output\n\t\t}\n\t\treturn convexToJson(current)\n\t}\n\t// SAFETY: the traversal validates the bounded supported Convex value subset before SDK conversion.\n\tconst encoded = JSON.stringify(project(value as Value))\n\tif (encoder.encode(encoded).length > bounds.maxBytes)\n\t\tinvalid('Canonical encoding exceeds byte bound')\n\treturn encoded\n}\n\nconst versionFields = ['operation', 'contract', 'binding', 'fingerprintPolicy'] as const\nconst invocationBytes: unique symbol = Symbol('idempotency invocation bytes')\nexport interface IdempotencyInvocation {\n\treadonly identity: IdempotencyIdentity\n\treadonly versions: IdempotencyVersions\n\treadonly [invocationBytes]: string\n}\nexport interface IdempotencyCapture {\n\treadonly binding: { readonly kind: 'mutation'; readonly atomicity: 'same-mutation' }\n\treadonly identity: IdempotencyIdentity\n\treadonly versions: IdempotencyVersions\n\treadonly rawInput: unknown\n\treadonly canonical: {\n\t\treadonly bounds: CanonicalConvexBounds\n\t\t/** Explicit deterministic equivalence; the host versions this policy in fingerprintPolicy. */\n\t\t// oxlint-disable-next-line anti-slop/no-unknown-returns -- The explicit normalized transport policy is validated by canonicalConvexBytes before capture.\n\t\treadonly normalize?: (raw: unknown) => unknown\n\t}\n}\n/** Call at the trusted native handler before domain decoding, once per invocation (including nested calls). */\nexport function captureIdempotencyInvocation(capture: IdempotencyCapture): IdempotencyInvocation {\n\tif (capture.binding.kind !== 'mutation' || capture.binding.atomicity !== 'same-mutation')\n\t\tthrow new IdempotencyError(\n\t\t\t'unsupported-binding',\n\t\t\t'Idempotency requires the current native mutation transaction',\n\t\t)\n\tconst identity = Object.freeze({\n\t\toperation: capture.identity.operation,\n\t\tscope: capture.identity.scope,\n\t\tkey: capture.identity.key,\n\t})\n\tconst versions = Object.freeze({\n\t\toperation: capture.versions.operation,\n\t\tcontract: capture.versions.contract,\n\t\tbinding: capture.versions.binding,\n\t\tfingerprintPolicy: capture.versions.fingerprintPolicy,\n\t})\n\tif (!validIdentity(identity) || !validVersions(versions))\n\t\tinvalid('Invalid trusted idempotency metadata')\n\tconst bytes = canonicalConvexBytes(\n\t\tcapture.canonical.normalize ? capture.canonical.normalize(capture.rawInput) : capture.rawInput,\n\t\tcapture.canonical.bounds,\n\t)\n\treturn Object.freeze({ identity, versions, [invocationBytes]: bytes })\n}\n/** Shared protocol checks have no Effect dependency and never decode final wire data. */\nexport function retainedIdempotencyReceipt(\n\trows: readonly unknown[],\n\tinvocation: IdempotencyInvocation,\n): IdempotencyReceipt | undefined {\n\tif (!Array.isArray(rows)) throw new IdempotencyError('corrupt', 'Receipt lookup must return rows')\n\tif (rows.length > 1)\n\t\tthrow new IdempotencyError('ambiguous', 'Multiple receipts match the stable identity')\n\tif (rows.length === 0) return undefined\n\tconst receipt = rows[0]\n\tif (\n\t\ttypeof receipt !== 'object' ||\n\t\treceipt === null ||\n\t\t!Object.hasOwn(receipt, 'versions') ||\n\t\t!Object.hasOwn(receipt, 'fingerprint') ||\n\t\t!Object.hasOwn(receipt, 'state')\n\t)\n\t\tthrow new IdempotencyError('corrupt', 'Malformed retained receipt')\n\t// SAFETY: this temporary retained-row view is fully checked below before returning or using metadata.\n\tconst candidate = receipt as IdempotencyReceipt\n\tif (\n\t\t!validVersions(candidate.versions) ||\n\t\ttypeof candidate.fingerprint !== 'string' ||\n\t\t!['pending', 'completed'].includes(candidate.state) ||\n\t\t(candidate.state === 'completed' && !Object.hasOwn(candidate, 'result'))\n\t)\n\t\tthrow new IdempotencyError('corrupt', 'Malformed retained receipt')\n\tif (versionFields.some((key) => candidate.versions[key] !== invocation.versions[key]))\n\t\tthrow new IdempotencyError(\n\t\t\t'stale',\n\t\t\t'Retained receipt version differs from the current operation',\n\t\t)\n\tif (candidate.state === 'pending')\n\t\tthrow new IdempotencyError('pending', 'Retained receipt is incomplete')\n\treturn candidate\n}\nexport function idempotencyCanonicalInput(invocation: IdempotencyInvocation): string {\n\tif (!Object.hasOwn(invocation, invocationBytes))\n\t\tthrow new IdempotencyError('unsupported-binding', 'Missing trusted invocation capture')\n\treturn invocation[invocationBytes]\n}\nexport function idempotencyFingerprint(value: string, receipt?: IdempotencyReceipt): string {\n\tif (typeof value !== 'string' || value.length === 0)\n\t\tthrow new IdempotencyError('invalid-input', 'Fingerprint policy must return a nonempty string')\n\tif (receipt && receipt.fingerprint !== value)\n\t\tthrow new IdempotencyError('conflict', 'Caller key was already used with different input')\n\treturn value\n}\nexport type IdempotencyPreparation<Result> =\n\t| { readonly kind: 'replay'; readonly result: unknown }\n\t| { readonly kind: 'execute'; readonly complete: (result: Result) => Promise<void> }\ntype NeutralValue<A> = A | PromiseLike<A>\n/** Host callbacks close over the same secured mutation writer; native OCC/rollback remains host-owned. */\nexport function transactionalIdempotency<const Replay extends NeutralSchema, Claim>(configuration: {\n\treadonly final: {\n\t\treadonly replayResult: Replay\n\t\treadonly encode: (result: ContractOutput<Replay>) => NeutralValue<ContractInput<Replay>>\n\t}\n\treadonly authorize: (invocation: IdempotencyInvocation) => NeutralValue<void>\n\treadonly lookup: (identity: IdempotencyIdentity) => NeutralValue<readonly unknown[]>\n\treadonly fingerprint: (canonicalBytes: string) => NeutralValue<string>\n\treadonly claim: (\n\t\tidentity: IdempotencyIdentity,\n\t\treceipt: IdempotencyReceipt,\n\t) => NeutralValue<Claim>\n\treadonly complete: (claim: Claim, receipt: IdempotencyReceipt) => NeutralValue<void>\n}) {\n\treturn {\n\t\treplayResult: configuration.final.replayResult,\n\t\tasync prepare(\n\t\t\tinvocation: IdempotencyInvocation,\n\t\t): Promise<IdempotencyPreparation<ContractOutput<Replay>>> {\n\t\t\tconst canonical = idempotencyCanonicalInput(invocation)\n\t\t\tawait configuration.authorize(invocation)\n\t\t\tconst retained = retainedIdempotencyReceipt(\n\t\t\t\tawait configuration.lookup(invocation.identity),\n\t\t\t\tinvocation,\n\t\t\t)\n\t\t\tconst fingerprint = idempotencyFingerprint(\n\t\t\t\tawait configuration.fingerprint(canonical),\n\t\t\t\tretained,\n\t\t\t)\n\t\t\tif (retained) return { kind: 'replay', result: retained.result }\n\t\t\tconst claim = await configuration.claim(invocation.identity, {\n\t\t\t\tstate: 'pending',\n\t\t\t\tversions: invocation.versions,\n\t\t\t\tfingerprint,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tkind: 'execute',\n\t\t\t\tcomplete: async (result) => {\n\t\t\t\t\tconst wire = await configuration.final.encode(result)\n\t\t\t\t\tawait configuration.complete(claim, {\n\t\t\t\t\t\tstate: 'completed',\n\t\t\t\t\t\tversions: invocation.versions,\n\t\t\t\t\t\tfingerprint,\n\t\t\t\t\t\tresult: wire,\n\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t}\n}\n\nfunction invalid(message: string): never {\n\tthrow new IdempotencyError('invalid-input', message)\n}\nfunction validIdentity(value: IdempotencyIdentity): boolean {\n\treturn [value.operation, value.scope, value.key].every(\n\t\t(field) => typeof field === 'string' && field.length > 0,\n\t)\n}\nfunction validVersions(value: unknown): value is IdempotencyVersions {\n\tif (typeof value !== 'object' || value === null) return false\n\t// SAFETY: this temporary metadata view is checked field by field before the predicate certifies it.\n\tconst candidate = value as Partial<IdempotencyVersions>\n\treturn versionFields.every((field) => {\n\t\tconst item = candidate[field]\n\t\treturn Object.hasOwn(candidate, field) && typeof item === 'string' && item.length > 0\n\t})\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"idempotency.mjs","names":["IdempotencyError","message","code","encoder","canonicalConvexBytes","bound","bounds","invalid","ancestors","nodes","bytes","spend","amount","textBytes","text","visit","depth","current","item","prototype","keys","key","descriptor","value","project","output","convexToJson","encoded","versionFields","invocationBytes","captureIdempotencyInvocation","capture","identity","versions","validIdentity","validVersions","retainedIdempotencyReceipt","rows","receipt","candidate","invocation","idempotencyCanonicalInput","idempotencyFingerprint","transactionalIdempotency","configuration","canonical","retained","fingerprint","claim","wire","result","field"],"sources":["../../../src/modules/contracts/idempotency.ts"],"sourcesContent":["/* oxlint-disable anti-slop/no-unknown-parameters, anti-slop/no-runtime-typeof -- Canonical transport and retained receipts are explicit untrusted value boundaries. */\nimport { convexToJson, type Value, type JSONValue } from 'convex/values'\nimport type { ContractInput, ContractOutput, NeutralSchema } from './contract'\n\nexport type IdempotencyErrorCode =\n\t| 'unsupported-binding'\n\t| 'invalid-input'\n\t| 'corrupt'\n\t| 'ambiguous'\n\t| 'pending'\n\t| 'stale'\n\t| 'conflict'\nexport class IdempotencyError extends Error {\n\treadonly _tag = 'IdempotencyError'\n\tconstructor(\n\t\treadonly code: IdempotencyErrorCode,\n\t\tmessage: string,\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'IdempotencyError'\n\t}\n}\nexport interface IdempotencyIdentity {\n\treadonly operation: string\n\t/** Stable trusted tenant/principal or delegated-capability scope, supplied by the host. */\n\treadonly scope: string\n\treadonly key: string\n}\nexport interface IdempotencyVersions {\n\treadonly operation: string\n\treadonly contract: string\n\treadonly binding: string\n\treadonly fingerprintPolicy: string\n}\nexport interface IdempotencyReceipt {\n\treadonly versions: IdempotencyVersions\n\treadonly fingerprint: string\n\treadonly state: 'pending' | 'completed'\n\treadonly result?: unknown\n}\nexport interface CanonicalConvexBounds {\n\treadonly maxDepth: number\n\treadonly maxNodes: number\n\treadonly maxBytes: number\n\treadonly maxArrayLength: number\n\treadonly maxObjectFields: number\n}\n\nconst encoder = new TextEncoder()\n/** Bounded transport encoding, using the pinned Convex integer/binary/float representation. */\nexport function canonicalConvexBytes(value: unknown, bounds: CanonicalConvexBounds): string {\n\tfor (const bound of [\n\t\tbounds.maxDepth,\n\t\tbounds.maxNodes,\n\t\tbounds.maxBytes,\n\t\tbounds.maxArrayLength,\n\t\tbounds.maxObjectFields,\n\t])\n\t\tif (!Number.isSafeInteger(bound) || bound < 1)\n\t\t\tinvalid('Canonical bounds must be positive safe integers')\n\tconst ancestors = new Set<object>()\n\tlet nodes = 0\n\tlet bytes = 0\n\tfunction spend(amount: number) {\n\t\tbytes += amount\n\t\tif (bytes > bounds.maxBytes) invalid('Canonical input exceeds byte bound')\n\t}\n\tfunction textBytes(text: string) {\n\t\tif (text.length > bounds.maxBytes) invalid('Canonical input exceeds byte bound')\n\t\treturn encoder.encode(text).length\n\t}\n\tfunction visit(current: unknown, depth: number): void {\n\t\tif (++nodes > bounds.maxNodes || depth > bounds.maxDepth)\n\t\t\tinvalid('Canonical input exceeds traversal bound')\n\t\tif (current === null || typeof current === 'boolean') {\n\t\t\tspend(1)\n\t\t\treturn\n\t\t}\n\t\tif (typeof current === 'number') {\n\t\t\tspend(9)\n\t\t\treturn\n\t\t}\n\t\tif (typeof current === 'bigint') {\n\t\t\tif (current < -(1n << 63n) || current > (1n << 63n) - 1n)\n\t\t\t\tinvalid('Convex integer exceeds signed int64')\n\t\t\tspend(9)\n\t\t\treturn\n\t\t}\n\t\tif (typeof current === 'string') {\n\t\t\tspend(2 + textBytes(current))\n\t\t\treturn\n\t\t}\n\t\tif (current instanceof ArrayBuffer) {\n\t\t\tspend(2 + current.byteLength)\n\t\t\treturn\n\t\t}\n\t\tif (typeof current !== 'object') invalid('Unsupported canonical Convex value')\n\t\tif (ancestors.has(current)) invalid('Cyclic canonical input')\n\t\tancestors.add(current)\n\t\ttry {\n\t\t\tif (Array.isArray(current)) {\n\t\t\t\tif (current.length > bounds.maxArrayLength) invalid('Canonical array exceeds length bound')\n\t\t\t\tspend(2)\n\t\t\t\tfor (const item of current) visit(item, depth + 1)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst prototype = Object.getPrototypeOf(current)\n\t\t\tif (prototype !== Object.prototype && prototype !== null)\n\t\t\t\tinvalid('Canonical input requires plain objects')\n\t\t\tif (Object.getOwnPropertySymbols(current).length) invalid('Unsupported canonical symbol keys')\n\t\t\tconst keys = Object.keys(current)\n\t\t\tif (keys.length > bounds.maxObjectFields) invalid('Canonical object exceeds field bound')\n\t\t\tspend(2)\n\t\t\tfor (const key of keys) {\n\t\t\t\tif (key.length > 1024 || key.startsWith('$') || /[^\\x20-\\x7e]/.test(key))\n\t\t\t\t\tinvalid('Invalid Convex object key')\n\t\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(current, key)!\n\t\t\t\tif (!('value' in descriptor)) invalid('Canonical input cannot contain accessors')\n\t\t\t\tif (descriptor.value === undefined) continue\n\t\t\t\tspend(textBytes(key) + 1)\n\t\t\t\tvisit(descriptor.value, depth + 1)\n\t\t\t}\n\t\t} finally {\n\t\t\tancestors.delete(current)\n\t\t}\n\t}\n\tvisit(value, 0)\n\t// Object projection retains own __proto__ keys which the SDK's assignment-based object converter drops.\n\t// Scalars still use the pinned SDK's exact integer/binary/float representation.\n\tfunction project(current: Value): JSONValue {\n\t\tif (Array.isArray(current)) return current.map(project)\n\t\tif (current !== null && typeof current === 'object' && !(current instanceof ArrayBuffer)) {\n\t\t\tconst output: Record<string, JSONValue> = Object.create(null)\n\t\t\tfor (const key of Object.keys(current).sort()) {\n\t\t\t\t// SAFETY: prior bounded traversal excluded every object type except plain Convex dictionaries.\n\t\t\t\tconst item = (current as Record<string, Value | undefined>)[key]\n\t\t\t\tif (item !== undefined) output[key] = project(item)\n\t\t\t}\n\t\t\treturn output\n\t\t}\n\t\treturn convexToJson(current)\n\t}\n\t// SAFETY: the traversal validates the bounded supported Convex value subset before SDK conversion.\n\tconst encoded = JSON.stringify(project(value as Value))\n\tif (encoder.encode(encoded).length > bounds.maxBytes)\n\t\tinvalid('Canonical encoding exceeds byte bound')\n\treturn encoded\n}\n\nconst versionFields = ['operation', 'contract', 'binding', 'fingerprintPolicy'] as const\nconst invocationBytes: unique symbol = Symbol('idempotency invocation bytes')\nexport interface IdempotencyInvocation {\n\treadonly identity: IdempotencyIdentity\n\treadonly versions: IdempotencyVersions\n\treadonly [invocationBytes]: string\n}\nexport interface IdempotencyCapture {\n\treadonly binding: { readonly kind: 'mutation'; readonly atomicity: 'same-mutation' }\n\treadonly identity: IdempotencyIdentity\n\treadonly versions: IdempotencyVersions\n\treadonly rawInput: unknown\n\treadonly canonical: {\n\t\treadonly bounds: CanonicalConvexBounds\n\t\t/** Explicit deterministic equivalence; the host versions this policy in fingerprintPolicy. */\n\t\t// oxlint-disable-next-line anti-slop/no-unknown-returns -- The explicit normalized transport policy is validated by canonicalConvexBytes before capture.\n\t\treadonly normalize?: (raw: unknown) => unknown\n\t}\n}\n/** Call at the trusted native handler before domain decoding, once per invocation (including nested calls). */\nexport function captureIdempotencyInvocation(capture: IdempotencyCapture): IdempotencyInvocation {\n\tif (capture.binding.kind !== 'mutation' || capture.binding.atomicity !== 'same-mutation')\n\t\tthrow new IdempotencyError(\n\t\t\t'unsupported-binding',\n\t\t\t'Idempotency requires the current native mutation transaction',\n\t\t)\n\tconst identity = Object.freeze({\n\t\toperation: capture.identity.operation,\n\t\tscope: capture.identity.scope,\n\t\tkey: capture.identity.key,\n\t})\n\tconst versions = Object.freeze({\n\t\toperation: capture.versions.operation,\n\t\tcontract: capture.versions.contract,\n\t\tbinding: capture.versions.binding,\n\t\tfingerprintPolicy: capture.versions.fingerprintPolicy,\n\t})\n\tif (!validIdentity(identity) || !validVersions(versions))\n\t\tinvalid('Invalid trusted idempotency metadata')\n\tconst bytes = canonicalConvexBytes(\n\t\tcapture.canonical.normalize ? capture.canonical.normalize(capture.rawInput) : capture.rawInput,\n\t\tcapture.canonical.bounds,\n\t)\n\treturn Object.freeze({ identity, versions, [invocationBytes]: bytes })\n}\n/** Shared protocol checks have no Effect dependency and never decode final wire data. */\nexport function retainedIdempotencyReceipt(\n\trows: readonly unknown[],\n\tinvocation: IdempotencyInvocation,\n): IdempotencyReceipt | undefined {\n\tif (!Array.isArray(rows)) throw new IdempotencyError('corrupt', 'Receipt lookup must return rows')\n\tif (rows.length > 1)\n\t\tthrow new IdempotencyError('ambiguous', 'Multiple receipts match the stable identity')\n\tif (rows.length === 0) return undefined\n\tconst receipt = rows[0]\n\tif (\n\t\ttypeof receipt !== 'object' ||\n\t\treceipt === null ||\n\t\t!Object.hasOwn(receipt, 'versions') ||\n\t\t!Object.hasOwn(receipt, 'fingerprint') ||\n\t\t!Object.hasOwn(receipt, 'state')\n\t)\n\t\tthrow new IdempotencyError('corrupt', 'Malformed retained receipt')\n\t// SAFETY: this temporary retained-row view is fully checked below before returning or using metadata.\n\tconst candidate = receipt as IdempotencyReceipt\n\tif (\n\t\t!validVersions(candidate.versions) ||\n\t\ttypeof candidate.fingerprint !== 'string' ||\n\t\t!['pending', 'completed'].includes(candidate.state) ||\n\t\t(candidate.state === 'completed' && !Object.hasOwn(candidate, 'result'))\n\t)\n\t\tthrow new IdempotencyError('corrupt', 'Malformed retained receipt')\n\tif (versionFields.some((key) => candidate.versions[key] !== invocation.versions[key]))\n\t\tthrow new IdempotencyError(\n\t\t\t'stale',\n\t\t\t'Retained receipt version differs from the current operation',\n\t\t)\n\tif (candidate.state === 'pending')\n\t\tthrow new IdempotencyError('pending', 'Retained receipt is incomplete')\n\treturn candidate\n}\nexport function idempotencyCanonicalInput(invocation: IdempotencyInvocation): string {\n\tif (!Object.hasOwn(invocation, invocationBytes))\n\t\tthrow new IdempotencyError('unsupported-binding', 'Missing trusted invocation capture')\n\treturn invocation[invocationBytes]\n}\nexport function idempotencyFingerprint(value: string, receipt?: IdempotencyReceipt): string {\n\tif (typeof value !== 'string' || value.length === 0)\n\t\tthrow new IdempotencyError('invalid-input', 'Fingerprint policy must return a nonempty string')\n\tif (receipt && receipt.fingerprint !== value)\n\t\tthrow new IdempotencyError('conflict', 'Caller key was already used with different input')\n\treturn value\n}\nexport type IdempotencyPreparation<Result> =\n\t| { readonly kind: 'replay'; readonly result: unknown }\n\t| { readonly kind: 'execute'; readonly complete: (result: Result) => Promise<void> }\ntype NeutralValue<A> = A | PromiseLike<A>\n/** Host callbacks close over the same secured mutation writer; native OCC/rollback remains host-owned. */\nexport function transactionalIdempotency<const Replay extends NeutralSchema, Claim>(configuration: {\n\treadonly final: {\n\t\treadonly replayResult: Replay\n\t\treadonly encode: (result: ContractOutput<Replay>) => NeutralValue<ContractInput<Replay>>\n\t}\n\treadonly authorize: (invocation: IdempotencyInvocation) => NeutralValue<void>\n\treadonly lookup: (identity: IdempotencyIdentity) => NeutralValue<readonly unknown[]>\n\treadonly fingerprint: (canonicalBytes: string) => NeutralValue<string>\n\treadonly claim: (\n\t\tidentity: IdempotencyIdentity,\n\t\treceipt: IdempotencyReceipt,\n\t) => NeutralValue<Claim>\n\treadonly complete: (claim: Claim, receipt: IdempotencyReceipt) => NeutralValue<void>\n}) {\n\treturn {\n\t\treplayResult: configuration.final.replayResult,\n\t\tasync prepare(\n\t\t\tinvocation: IdempotencyInvocation,\n\t\t): Promise<IdempotencyPreparation<ContractOutput<Replay>>> {\n\t\t\tconst canonical = idempotencyCanonicalInput(invocation)\n\t\t\tawait configuration.authorize(invocation)\n\t\t\tconst retained = retainedIdempotencyReceipt(\n\t\t\t\tawait configuration.lookup(invocation.identity),\n\t\t\t\tinvocation,\n\t\t\t)\n\t\t\tconst fingerprint = idempotencyFingerprint(\n\t\t\t\tawait configuration.fingerprint(canonical),\n\t\t\t\tretained,\n\t\t\t)\n\t\t\tif (retained) return { kind: 'replay', result: retained.result }\n\t\t\tconst claim = await configuration.claim(invocation.identity, {\n\t\t\t\tstate: 'pending',\n\t\t\t\tversions: invocation.versions,\n\t\t\t\tfingerprint,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tkind: 'execute',\n\t\t\t\tcomplete: async (result) => {\n\t\t\t\t\tconst wire = await configuration.final.encode(result)\n\t\t\t\t\tawait configuration.complete(claim, {\n\t\t\t\t\t\tstate: 'completed',\n\t\t\t\t\t\tversions: invocation.versions,\n\t\t\t\t\t\tfingerprint,\n\t\t\t\t\t\tresult: wire,\n\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t}\n}\n\nfunction invalid(message: string): never {\n\tthrow new IdempotencyError('invalid-input', message)\n}\nfunction validIdentity(value: IdempotencyIdentity): boolean {\n\treturn [value.operation, value.scope, value.key].every(\n\t\t(field) => typeof field === 'string' && field.length > 0,\n\t)\n}\nfunction validVersions(value: unknown): value is IdempotencyVersions {\n\tif (typeof value !== 'object' || value === null) return false\n\t// SAFETY: this temporary metadata view is checked field by field before the predicate certifies it.\n\tconst candidate = value as Partial<IdempotencyVersions>\n\treturn versionFields.every((field) => {\n\t\tconst item = candidate[field]\n\t\treturn Object.hasOwn(candidate, field) && typeof item === 'string' && item.length > 0\n\t})\n}\n"],"mappings":"6CAYA,IAAaA,EAAb,cAAsC,KAAM,CAGjC,KAFV,KAAgB,mBAChB,YACC,EACA,EACC,CACD,MAAMC,CAAO,EAHJ,KAAA,KAAAC,EAIT,KAAK,KAAO,kBACb,CACD,EA2BA,MAAMC,EAAU,IAAI,YAEpB,SAAgBC,EAAqB,EAAgB,EAAuC,CAC3F,IAAK,IAAMC,IAAS,CACnBC,EAAO,SACPA,EAAO,SACPA,EAAO,SACPA,EAAO,eACPA,EAAO,eACR,GACK,CAAC,OAAO,cAAcD,CAAK,GAAKA,EAAQ,IAC3CE,EAAQ,iDAAiD,EAC3D,IAAMC,kBAAY,IAAI,IAClBC,EAAQ,EACRC,EAAQ,EACZ,SAASC,EAAM,EAAgB,CAC9B,GAASC,EACLF,EAAQJ,EAAO,UAAUC,EAAQ,oCAAoC,CAC1E,CACA,SAASM,EAAU,EAAc,CAEhC,OADIC,EAAK,OAASR,EAAO,UAAUC,EAAQ,oCAAoC,EACxEJ,EAAQ,OAAOW,CAAI,CAAC,CAAC,MAC7B,CACA,SAASC,EAAM,EAAkB,EAAqB,CAGrD,IAFI,EAAEN,EAAQH,EAAO,UAAYU,EAAQV,EAAO,WAC/CC,EAAQ,yCAAyC,EAC9CU,IAAY,MAAQ,OAAOA,GAAY,UAAW,CACrDN,EAAM,CAAC,EACP,MACD,CACA,GAAI,OAAOM,GAAY,SAAU,CAChCN,EAAM,CAAC,EACP,MACD,CACA,GAAI,OAAOM,GAAY,SAAU,EAC5BA,EAAU,EAAE,IAAM,MAAQA,GAAW,IAAM,KAAO,KACrDV,EAAQ,qCAAqC,EAC9CI,EAAM,CAAC,EACP,MACD,CACA,GAAI,OAAOM,GAAY,SAAU,CAChCN,EAAM,EAAIE,EAAUI,CAAO,CAAC,EAC5B,MACD,CACA,GAAIA,aAAmB,YAAa,CACnCN,EAAM,EAAIM,EAAQ,UAAU,EAC5B,MACD,CACI,OAAOA,GAAY,UAAUV,EAAQ,oCAAoC,EACzEC,EAAU,IAAIS,CAAO,GAAGV,EAAQ,wBAAwB,EAC5DC,EAAU,IAAIS,CAAO,EACrB,GAAI,CACH,GAAI,MAAM,QAAQA,CAAO,EAAG,CACvBA,EAAQ,OAASX,EAAO,gBAAgBC,EAAQ,sCAAsC,EAC1FI,EAAM,CAAC,EACP,IAAK,IAAMO,KAAQD,EAASF,EAAMG,EAAMF,EAAQ,CAAC,EACjD,MACD,CACA,IAAMG,EAAY,OAAO,eAAeF,CAAO,EAC3CE,IAAc,OAAO,WAAaA,IAAc,MACnDZ,EAAQ,wCAAwC,EAC7C,OAAO,sBAAsBU,CAAO,CAAC,CAAC,QAAQV,EAAQ,mCAAmC,EAC7F,IAAMa,EAAO,OAAO,KAAKH,CAAO,EAC5BG,EAAK,OAASd,EAAO,iBAAiBC,EAAQ,sCAAsC,EACxFI,EAAM,CAAC,EACP,IAAK,IAAMU,KAAOD,EAAM,EACnBC,EAAI,OAAS,MAAQA,EAAI,WAAW,GAAG,GAAK,eAAe,KAAKA,CAAG,IACtEd,EAAQ,2BAA2B,EACpC,IAAMe,EAAa,OAAO,yBAAyBL,EAASI,CAAG,EACzD,UAAWC,GAAaf,EAAQ,0CAA0C,EAC5Ee,EAAW,QAAU,IAAA,KACzBX,EAAME,EAAUQ,CAAG,EAAI,CAAC,EACxBN,EAAMO,EAAW,MAAON,EAAQ,CAAC,EAClC,CACD,QAAU,CACTR,EAAU,OAAOS,CAAO,CACzB,CACD,CACAF,EAAMQ,EAAO,CAAC,EAGd,SAASC,EAAQ,EAA2B,CAC3C,GAAI,MAAM,QAAQP,CAAO,EAAG,OAAOA,EAAQ,IAAIO,CAAO,EACtD,GAAwB,OAAOP,GAAY,UAAvCA,GAAmD,EAAEA,aAAmB,aAAc,CACzF,IAAMQ,EAAoC,OAAO,OAAO,IAAI,EAC5D,IAAK,IAAMJ,KAAO,OAAO,KAAKJ,CAAO,CAAC,CAAC,KAAK,EAAG,CAE9C,IAAMC,EAAQD,EAA8CI,GACxDH,IAAS,IAAA,KAAW,EAAOG,GAAOG,EAAQN,CAAI,EACnD,CACA,OAAOO,CACR,CACA,OAAOC,EAAaT,CAAO,CAC5B,CAEA,IAAMU,EAAU,KAAK,UAAUH,EAAQD,CAAc,CAAC,EAGtD,OAFIpB,EAAQ,OAAOwB,CAAO,CAAC,CAAC,OAASrB,EAAO,UAC3CC,EAAQ,uCAAuC,EACzCoB,CACR,CAEA,MAAMC,EAAgB,CAAC,YAAa,WAAY,UAAW,mBAAmB,EACxEC,EAAiC,OAAO,8BAA8B,EAmB5E,SAAgBC,EAA6B,EAAoD,CAChG,GAAIC,EAAQ,QAAQ,OAAS,YAAcA,EAAQ,QAAQ,YAAc,gBACxE,MAAM,IAAI/B,EACT,sBACA,8DACD,EACD,IAAMgC,EAAW,OAAO,OAAO,CAC9B,UAAWD,EAAQ,SAAS,UAC5B,MAAOA,EAAQ,SAAS,MACxB,IAAKA,EAAQ,SAAS,GACvB,CAAC,EACKE,EAAW,OAAO,OAAO,CAC9B,UAAWF,EAAQ,SAAS,UAC5B,SAAUA,EAAQ,SAAS,SAC3B,QAASA,EAAQ,SAAS,QAC1B,kBAAmBA,EAAQ,SAAS,iBACrC,CAAC,GACG,CAACG,EAAcF,CAAQ,GAAK,CAACG,EAAcF,CAAQ,IACtD1B,EAAQ,sCAAsC,EAC/C,IAAMG,EAAQN,EACb2B,EAAQ,UAAU,UAAYA,EAAQ,UAAU,UAAUA,EAAQ,QAAQ,EAAIA,EAAQ,SACtFA,EAAQ,UAAU,MACnB,EACA,OAAO,OAAO,OAAO,CAAE,WAAU,YAAWF,GAAkBnB,CAAM,CAAC,CACtE,CAEA,SAAgB0B,EACf,EACA,EACiC,CACjC,GAAI,CAAC,MAAM,QAAQC,CAAI,EAAG,MAAM,IAAIrC,EAAiB,UAAW,iCAAiC,EACjG,GAAIqC,EAAK,OAAS,EACjB,MAAM,IAAIrC,EAAiB,YAAa,6CAA6C,EACtF,GAAIqC,EAAK,SAAW,EAAG,OACvB,IAAMC,EAAUD,EAAK,GACrB,GACC,OAAOC,GAAY,WACnBA,GACA,CAAC,OAAO,OAAOA,EAAS,UAAU,GAClC,CAAC,OAAO,OAAOA,EAAS,aAAa,GACrC,CAAC,OAAO,OAAOA,EAAS,OAAO,EAE/B,MAAM,IAAItC,EAAiB,UAAW,4BAA4B,EAEnE,IAAMuC,EAAYD,EAClB,GACC,CAACH,EAAcI,EAAU,QAAQ,GACjC,OAAOA,EAAU,aAAgB,UACjC,CAAC,CAAC,UAAW,WAAW,CAAC,CAAC,SAASA,EAAU,KAAK,GACjDA,EAAU,QAAU,aAAe,CAAC,OAAO,OAAOA,EAAW,QAAQ,EAEtE,MAAM,IAAIvC,EAAiB,UAAW,4BAA4B,EACnE,GAAI4B,EAAc,KAAM,GAAQW,EAAU,SAASlB,KAASmB,EAAW,SAASnB,EAAI,EACnF,MAAM,IAAIrB,EACT,QACA,6DACD,EACD,GAAIuC,EAAU,QAAU,UACvB,MAAM,IAAIvC,EAAiB,UAAW,gCAAgC,EACvE,OAAOuC,CACR,CACA,SAAgBE,EAA0B,EAA2C,CACpF,GAAI,CAAC,OAAO,OAAOD,EAAYX,CAAe,EAC7C,MAAM,IAAI7B,EAAiB,sBAAuB,oCAAoC,EACvF,OAAOwC,EAAWX,EACnB,CACA,SAAgBa,EAAuB,EAAe,EAAsC,CAC3F,GAAI,OAAOnB,GAAU,UAAYA,EAAM,SAAW,EACjD,MAAM,IAAIvB,EAAiB,gBAAiB,kDAAkD,EAC/F,GAAIsC,GAAWA,EAAQ,cAAgBf,EACtC,MAAM,IAAIvB,EAAiB,WAAY,kDAAkD,EAC1F,OAAOuB,CACR,CAMA,SAAgBoB,EAAoE,EAajF,CACF,MAAO,CACN,aAAcC,EAAc,MAAM,aAClC,MAAM,QACL,EAC0D,CAC1D,IAAMC,EAAYJ,EAA0BD,CAAU,EACtD,MAAMI,EAAc,UAAUJ,CAAU,EACxC,IAAMM,EAAWV,EAChB,MAAMQ,EAAc,OAAOJ,EAAW,QAAQ,EAC9CA,CACD,EACMO,EAAcL,EACnB,MAAME,EAAc,YAAYC,CAAS,EACzCC,CACD,EACA,GAAIA,EAAU,MAAO,CAAE,KAAM,SAAU,OAAQA,EAAS,MAAO,EAC/D,IAAME,EAAQ,MAAMJ,EAAc,MAAMJ,EAAW,SAAU,CAC5D,MAAO,UACP,SAAUA,EAAW,SACrB,aACD,CAAC,EACD,MAAO,CACN,KAAM,UACN,SAAU,KAAO,IAAW,CAC3B,IAAMS,EAAO,MAAML,EAAc,MAAM,OAAOM,CAAM,EACpD,MAAMN,EAAc,SAASI,EAAO,CACnC,MAAO,YACP,SAAUR,EAAW,SACrB,cACA,OAAQS,CACT,CAAC,CACF,CACD,CACD,CACD,CACD,CAEA,SAAS1C,EAAQ,EAAwB,CACxC,MAAM,IAAIP,EAAiB,gBAAiBC,CAAO,CACpD,CACA,SAASiC,EAAc,EAAqC,CAC3D,MAAO,CAACX,EAAM,UAAWA,EAAM,MAAOA,EAAM,GAAG,CAAC,CAAC,MAC/C,GAAU,OAAO4B,GAAU,UAAYA,EAAM,OAAS,CACxD,CACD,CACA,SAAShB,EAAc,EAA8C,CACpE,GAAI,OAAOZ,GAAU,WAAYA,EAAgB,MAAO,GAExD,IAAMgB,EAAYhB,EAClB,OAAOK,EAAc,MAAO,GAAU,CACrC,IAAMV,EAAOqB,EAAUY,GACvB,OAAO,OAAO,OAAOZ,EAAWY,CAAK,GAAK,OAAOjC,GAAS,UAAYA,EAAK,OAAS,CACrF,CAAC,CACF"}
|
|
@@ -2,15 +2,14 @@ import { ContractInput, ContractOutput, Decoder, StandardSchema, ValidationResul
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/modules/contracts/standard.d.ts
|
|
4
4
|
/** Standard Schema validation executes exactly the validator supplied by its author. */
|
|
5
|
-
declare function standardContract<const S extends StandardSchema<unknown, unknown>>(schema: S): Decoder<ContractInput<S>, ContractOutput<S>, ValidationResult<ContractOutput<S>> | Promise<ValidationResult<ContractOutput<S>>>> & {
|
|
5
|
+
export declare function standardContract<const S extends StandardSchema<unknown, unknown>>(schema: S): Decoder<ContractInput<S>, ContractOutput<S>, ValidationResult<ContractOutput<S>> | Promise<ValidationResult<ContractOutput<S>>>> & {
|
|
6
6
|
readonly kind: 'contract';
|
|
7
7
|
readonly schema: S;
|
|
8
8
|
};
|
|
9
9
|
/** Zod's Standard bridge probes sync first; safeParseAsync executes transforms once. */
|
|
10
|
-
declare function zodContract<const S extends z.ZodType>(schema: S): Decoder<z.input<S>, z.output<S>, Promise<ValidationResult<z.output<S>>>> & {
|
|
10
|
+
export declare function zodContract<const S extends z.ZodType>(schema: S): Decoder<z.input<S>, z.output<S>, Promise<ValidationResult<z.output<S>>>> & {
|
|
11
11
|
readonly kind: 'contract';
|
|
12
12
|
readonly schema: S;
|
|
13
13
|
};
|
|
14
14
|
//#endregion
|
|
15
|
-
export { standardContract, zodContract };
|
|
16
15
|
//# sourceMappingURL=standard.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standard.mjs","names":[],"sources":["../../../src/modules/contracts/standard.ts"],"sourcesContent":["import type {\n\tContractInput,\n\tContractOutput,\n\tDecoder,\n\tStandardSchema,\n\tValidationResult,\n} from './contract'\nimport type { z } from 'zod'\n\n/** Standard Schema validation executes exactly the validator supplied by its author. */\nexport function standardContract<const S extends StandardSchema<unknown, unknown>>(\n\tschema: S,\n): Decoder<\n\tContractInput<S>,\n\tContractOutput<S>,\n\tValidationResult<ContractOutput<S>> | Promise<ValidationResult<ContractOutput<S>>>\n> & { readonly kind: 'contract'; readonly schema: S } {\n\treturn {\n\t\tkind: 'contract',\n\t\tschema,\n\t\t// SAFETY: structural Standard Schema inference associates validate's output with S.\n\t\tdecode: (value) =>\n\t\t\tschema['~standard'].validate(value) as\n\t\t\t\t| ValidationResult<ContractOutput<S>>\n\t\t\t\t| Promise<ValidationResult<ContractOutput<S>>>,\n\t}\n}\n\n/** Zod's Standard bridge probes sync first; safeParseAsync executes transforms once. */\nexport function zodContract<const S extends z.ZodType>(\n\tschema: S,\n): Decoder<z.input<S>, z.output<S>, Promise<ValidationResult<z.output<S>>>> & {\n\treadonly kind: 'contract'\n\treadonly schema: S\n} {\n\treturn {\n\t\tkind: 'contract',\n\t\tschema,\n\t\tdecode: async (value) => {\n\t\t\tconst result = await schema.safeParseAsync(value)\n\t\t\treturn result.success ? { value: result.data } : { issues: result.error.issues }\n\t\t},\n\t}\n}\n"],"mappings":"AAUA,
|
|
1
|
+
{"version":3,"file":"standard.mjs","names":["standardContract","schema","value","zodContract","result"],"sources":["../../../src/modules/contracts/standard.ts"],"sourcesContent":["import type {\n\tContractInput,\n\tContractOutput,\n\tDecoder,\n\tStandardSchema,\n\tValidationResult,\n} from './contract'\nimport type { z } from 'zod'\n\n/** Standard Schema validation executes exactly the validator supplied by its author. */\nexport function standardContract<const S extends StandardSchema<unknown, unknown>>(\n\tschema: S,\n): Decoder<\n\tContractInput<S>,\n\tContractOutput<S>,\n\tValidationResult<ContractOutput<S>> | Promise<ValidationResult<ContractOutput<S>>>\n> & { readonly kind: 'contract'; readonly schema: S } {\n\treturn {\n\t\tkind: 'contract',\n\t\tschema,\n\t\t// SAFETY: structural Standard Schema inference associates validate's output with S.\n\t\tdecode: (value) =>\n\t\t\tschema['~standard'].validate(value) as\n\t\t\t\t| ValidationResult<ContractOutput<S>>\n\t\t\t\t| Promise<ValidationResult<ContractOutput<S>>>,\n\t}\n}\n\n/** Zod's Standard bridge probes sync first; safeParseAsync executes transforms once. */\nexport function zodContract<const S extends z.ZodType>(\n\tschema: S,\n): Decoder<z.input<S>, z.output<S>, Promise<ValidationResult<z.output<S>>>> & {\n\treadonly kind: 'contract'\n\treadonly schema: S\n} {\n\treturn {\n\t\tkind: 'contract',\n\t\tschema,\n\t\tdecode: async (value) => {\n\t\t\tconst result = await schema.safeParseAsync(value)\n\t\t\treturn result.success ? { value: result.data } : { issues: result.error.issues }\n\t\t},\n\t}\n}\n"],"mappings":"AAUA,SAAgBA,EACf,EAKqD,CACrD,MAAO,CACN,KAAM,WACN,SAEA,OAAS,GACRC,EAAO,YAAY,CAAC,SAASC,CAAK,CAGpC,CACD,CAGA,SAAgBC,EACf,EAIC,CACD,MAAO,CACN,KAAM,WACN,SACA,OAAQ,KAAO,IAAU,CACxB,IAAMC,EAAS,MAAMH,EAAO,eAAeC,CAAK,EAChD,OAAOE,EAAO,QAAU,CAAE,MAAOA,EAAO,IAAK,EAAI,CAAE,OAAQA,EAAO,MAAM,MAAO,CAChF,CACD,CACD"}
|
|
@@ -3,11 +3,11 @@ import { CanonicalConvexBounds, IdempotencyInvocation, IdempotencyPreparation }
|
|
|
3
3
|
import { FunctionReference, FunctionType, FunctionVisibility } from "convex/server";
|
|
4
4
|
import { WorkflowCtx } from "@convex-dev/workflow";
|
|
5
5
|
//#region src/modules/contracts/workflow.d.ts
|
|
6
|
-
declare class WorkflowBindingError extends Error {
|
|
6
|
+
export declare class WorkflowBindingError extends Error {
|
|
7
7
|
readonly _tag = "WorkflowBindingError";
|
|
8
8
|
constructor(message: string);
|
|
9
9
|
}
|
|
10
|
-
interface WorkflowExecution<Args = unknown> {
|
|
10
|
+
export interface WorkflowExecution<Args = unknown> {
|
|
11
11
|
readonly version: 1;
|
|
12
12
|
readonly operation: string;
|
|
13
13
|
readonly operationVersion: string;
|
|
@@ -21,13 +21,13 @@ interface WorkflowExecution<Args = unknown> {
|
|
|
21
21
|
readonly occurrence: string;
|
|
22
22
|
readonly args: Args;
|
|
23
23
|
}
|
|
24
|
-
type WorkflowStepArguments<Args> = {
|
|
24
|
+
export type WorkflowStepArguments<Args> = {
|
|
25
25
|
execution: WorkflowExecution<Args>;
|
|
26
26
|
};
|
|
27
27
|
/** Strict plain, versioned transport. Validity does not confer authority; the host resolves capability anew. */
|
|
28
|
-
declare function workflowExecution<Args>(value: WorkflowExecution<Args>): WorkflowExecution<Args>;
|
|
29
|
-
declare function workflowExecution(value: unknown): WorkflowExecution;
|
|
30
|
-
interface WorkflowBinding<Kind extends FunctionType, Args extends NeutralSchema, Result extends NeutralSchema> {
|
|
28
|
+
export declare function workflowExecution<Args>(value: WorkflowExecution<Args>): WorkflowExecution<Args>;
|
|
29
|
+
export declare function workflowExecution(value: unknown): WorkflowExecution;
|
|
30
|
+
export interface WorkflowBinding<Kind extends FunctionType, Args extends NeutralSchema, Result extends NeutralSchema> {
|
|
31
31
|
readonly kind: Kind;
|
|
32
32
|
readonly operation: string;
|
|
33
33
|
readonly operationVersion: string;
|
|
@@ -44,8 +44,8 @@ interface WorkflowBinding<Kind extends FunctionType, Args extends NeutralSchema,
|
|
|
44
44
|
}[];
|
|
45
45
|
}
|
|
46
46
|
/** Both phantom reference args and results must match the explicit wire contracts. */
|
|
47
|
-
declare function workflowBinding<Kind extends FunctionType, const Args extends NeutralSchema, const Result extends NeutralSchema>(binding: WorkflowBinding<Kind, Args, Result>): WorkflowBinding<Kind, Args, Result>;
|
|
48
|
-
type WorkflowStepOptions = {
|
|
47
|
+
export declare function workflowBinding<Kind extends FunctionType, const Args extends NeutralSchema, const Result extends NeutralSchema>(binding: WorkflowBinding<Kind, Args, Result>): WorkflowBinding<Kind, Args, Result>;
|
|
48
|
+
export type WorkflowStepOptions = {
|
|
49
49
|
name?: string;
|
|
50
50
|
retry?: NonNullable<Parameters<WorkflowCtx['withOptions']>[0]['retry']>;
|
|
51
51
|
} & ({
|
|
@@ -58,20 +58,20 @@ type WorkflowStepOptions = {
|
|
|
58
58
|
runAfter?: never;
|
|
59
59
|
});
|
|
60
60
|
/** A structural seam allows invocation-scoped native WorkflowCtx without creating another durability runtime. */
|
|
61
|
-
interface WorkflowStepRunner {
|
|
61
|
+
export interface WorkflowStepRunner {
|
|
62
62
|
runQuery?: (reference: FunctionReference<'query', FunctionVisibility>, args: WorkflowStepArguments<unknown>, options?: WorkflowStepOptions) => Promise<unknown>;
|
|
63
63
|
runMutation?: (reference: FunctionReference<'mutation', FunctionVisibility>, args: WorkflowStepArguments<unknown>, options?: WorkflowStepOptions) => Promise<unknown>;
|
|
64
64
|
runAction?: (reference: FunctionReference<'action', FunctionVisibility>, args: WorkflowStepArguments<unknown>, options?: WorkflowStepOptions) => Promise<unknown>;
|
|
65
65
|
}
|
|
66
|
-
declare function runWorkflowStep<Kind extends FunctionType, Args extends NeutralSchema, Result extends NeutralSchema>(step: WorkflowCtx | WorkflowStepRunner, binding: WorkflowBinding<Kind, Args, Result>, input: WorkflowExecution<ContractInput<Args>>, options?: WorkflowStepOptions): Promise<ContractInput<Result>>;
|
|
67
|
-
declare function workflowDecode<S extends NeutralSchema>(schema: S, value: unknown): Promise<ContractOutput<S>>;
|
|
66
|
+
export declare function runWorkflowStep<Kind extends FunctionType, Args extends NeutralSchema, Result extends NeutralSchema>(step: WorkflowCtx | WorkflowStepRunner, binding: WorkflowBinding<Kind, Args, Result>, input: WorkflowExecution<ContractInput<Args>>, options?: WorkflowStepOptions): Promise<ContractInput<Result>>;
|
|
67
|
+
export declare function workflowDecode<S extends NeutralSchema>(schema: S, value: unknown): Promise<ContractOutput<S>>;
|
|
68
68
|
type Ordinary<A> = A | PromiseLike<A>;
|
|
69
69
|
/** This helper belongs inside a registered native function, never in the replaying workflow handler. */
|
|
70
|
-
declare function workflowAttempt<Args, Authority, Result>(configuration: {
|
|
70
|
+
export declare function workflowAttempt<Args, Authority, Result>(configuration: {
|
|
71
71
|
authorize: (execution: WorkflowExecution<Args>) => Ordinary<Authority>;
|
|
72
72
|
execute: (execution: WorkflowExecution<Args>, authority: Authority) => Ordinary<Result>;
|
|
73
73
|
}): (input: WorkflowExecution<Args>) => Promise<Result>;
|
|
74
|
-
type WorkflowOutcome<Result = unknown, Failure = unknown> = {
|
|
74
|
+
export type WorkflowOutcome<Result = unknown, Failure = unknown> = {
|
|
75
75
|
readonly version: 1;
|
|
76
76
|
readonly kind: 'succeeded';
|
|
77
77
|
readonly value: Result;
|
|
@@ -81,25 +81,25 @@ type WorkflowOutcome<Result = unknown, Failure = unknown> = {
|
|
|
81
81
|
readonly error: Failure;
|
|
82
82
|
};
|
|
83
83
|
declare const protectedMutation: unique symbol;
|
|
84
|
-
interface ReceiptProtectedWorkflowMutation<Args, Result> {
|
|
84
|
+
export interface ReceiptProtectedWorkflowMutation<Args, Result> {
|
|
85
85
|
readonly [protectedMutation]: {
|
|
86
86
|
args: Args;
|
|
87
87
|
result: Result;
|
|
88
88
|
};
|
|
89
89
|
readonly handler: (execution: WorkflowExecution<Args>) => Promise<Result>;
|
|
90
90
|
}
|
|
91
|
-
interface WorkflowReceiptAdapter<S extends NeutralSchema> {
|
|
91
|
+
export interface WorkflowReceiptAdapter<S extends NeutralSchema> {
|
|
92
92
|
readonly replayResult: S;
|
|
93
93
|
readonly prepare: (invocation: IdempotencyInvocation) => Promise<IdempotencyPreparation<ContractOutput<S>>>;
|
|
94
94
|
}
|
|
95
95
|
/** Host registers this handler as the inner native mutation. All callbacks share its transaction. */
|
|
96
|
-
declare function receiptWorkflowMutation<Args, Authority, const Replay extends NeutralSchema>(configuration: {
|
|
96
|
+
export declare function receiptWorkflowMutation<Args, Authority, const Replay extends NeutralSchema>(configuration: {
|
|
97
97
|
authorize: (execution: WorkflowExecution<Args>) => Ordinary<Authority>;
|
|
98
98
|
receipt: (authority: Authority) => WorkflowReceiptAdapter<Replay>;
|
|
99
99
|
invocation: (execution: WorkflowExecution<Args>, authority: Authority) => IdempotencyInvocation;
|
|
100
100
|
execute: (execution: WorkflowExecution<Args>, authority: Authority) => Ordinary<ContractOutput<Replay>>;
|
|
101
101
|
}): ReceiptProtectedWorkflowMutation<Args, ContractOutput<Replay>>;
|
|
102
|
-
declare function workflowReceiptInvocation(execution: WorkflowExecution, options: {
|
|
102
|
+
export declare function workflowReceiptInvocation(execution: WorkflowExecution, options: {
|
|
103
103
|
scope: string;
|
|
104
104
|
fingerprintPolicy: string;
|
|
105
105
|
bounds: CanonicalConvexBounds;
|
|
@@ -112,36 +112,36 @@ type TerminalMode<Args> = {
|
|
|
112
112
|
protectedMutation: ReceiptProtectedWorkflowMutation<Args, unknown>;
|
|
113
113
|
};
|
|
114
114
|
/** The native inner mutation rejection must cross its transaction boundary before terminal mapping. */
|
|
115
|
-
declare function terminalWorkflowAttempt<Args, Authority, const Result extends NeutralSchema, const Failure extends NeutralSchema>(configuration: TerminalMode<Args> & {
|
|
115
|
+
export declare function terminalWorkflowAttempt<Args, Authority, const Result extends NeutralSchema, const Failure extends NeutralSchema>(configuration: TerminalMode<Args> & {
|
|
116
116
|
authorize: (execution: WorkflowExecution<Args>) => Ordinary<Authority>;
|
|
117
117
|
invokeMutation: (execution: WorkflowExecution<Args>, authority: Authority) => Promise<ContractInput<Result>>;
|
|
118
118
|
terminal: (error: unknown) => Ordinary<ContractInput<Failure> | undefined>;
|
|
119
119
|
result: Result;
|
|
120
120
|
failure: Failure;
|
|
121
121
|
}): (input: WorkflowExecution<Args>) => Promise<WorkflowOutcome<ContractInput<Result>, ContractOutput<Failure>>>;
|
|
122
|
-
type WorkflowNativeStatus = 'running' | 'succeeded' | 'failed' | 'canceled';
|
|
123
|
-
interface WorkflowHostState<Result = unknown, Failure = unknown> {
|
|
122
|
+
export type WorkflowNativeStatus = 'running' | 'succeeded' | 'failed' | 'canceled';
|
|
123
|
+
export interface WorkflowHostState<Result = unknown, Failure = unknown> {
|
|
124
124
|
readonly generation: number;
|
|
125
125
|
readonly status: WorkflowNativeStatus;
|
|
126
126
|
readonly nativeStatus: WorkflowNativeStatus;
|
|
127
127
|
readonly outcome?: WorkflowOutcome<Result, Failure>;
|
|
128
128
|
}
|
|
129
|
-
interface WorkflowHostUpdate<Result = unknown, Failure = unknown> {
|
|
129
|
+
export interface WorkflowHostUpdate<Result = unknown, Failure = unknown> {
|
|
130
130
|
readonly generation: number;
|
|
131
131
|
readonly nativeStatus: WorkflowNativeStatus;
|
|
132
132
|
readonly outcome?: WorkflowOutcome<Result, Failure>;
|
|
133
133
|
}
|
|
134
|
-
type WorkflowTransition<Result = unknown, Failure = unknown> = {
|
|
134
|
+
export type WorkflowTransition<Result = unknown, Failure = unknown> = {
|
|
135
135
|
readonly kind: 'applied' | 'ignored';
|
|
136
136
|
readonly state: WorkflowHostState<Result, Failure>;
|
|
137
137
|
};
|
|
138
138
|
/** Call within a host transaction or compare-and-set. A late operation failure refines host success; cancellation and terminal native status stay fenced. */
|
|
139
|
-
declare function workflowTransition<Result, Failure>(current: WorkflowHostState<Result, Failure>, update: WorkflowHostUpdate<Result, Failure>): WorkflowTransition<Result, Failure>;
|
|
140
|
-
declare function workflowCancel<Result, Failure>(current: WorkflowHostState<Result, Failure>, expectedGeneration: number): WorkflowTransition<Result, Failure>;
|
|
139
|
+
export declare function workflowTransition<Result, Failure>(current: WorkflowHostState<Result, Failure>, update: WorkflowHostUpdate<Result, Failure>): WorkflowTransition<Result, Failure>;
|
|
140
|
+
export declare function workflowCancel<Result, Failure>(current: WorkflowHostState<Result, Failure>, expectedGeneration: number): WorkflowTransition<Result, Failure>;
|
|
141
141
|
/** Host calls after an authorized native restart. Receipt identity remains run plus occurrence. */
|
|
142
|
-
declare function workflowRestart<Result, Failure>(current: WorkflowHostState<Result, Failure>): WorkflowHostState<Result, Failure>;
|
|
142
|
+
export declare function workflowRestart<Result, Failure>(current: WorkflowHostState<Result, Failure>): WorkflowHostState<Result, Failure>;
|
|
143
143
|
/** Host closures capture the trusted run, secured storage and manager. `write` must enforce expected generation atomically. */
|
|
144
|
-
declare function workflowStatusBindings<Result, Failure>(configuration: {
|
|
144
|
+
export declare function workflowStatusBindings<Result, Failure>(configuration: {
|
|
145
145
|
authorize: () => Ordinary<void>;
|
|
146
146
|
read: () => Ordinary<WorkflowHostState<Result, Failure>>;
|
|
147
147
|
write: (expectedGeneration: number, state: WorkflowHostState<Result, Failure>) => Ordinary<void>;
|
|
@@ -163,5 +163,4 @@ declare function workflowStatusBindings<Result, Failure>(configuration: {
|
|
|
163
163
|
}>;
|
|
164
164
|
};
|
|
165
165
|
//#endregion
|
|
166
|
-
export { ReceiptProtectedWorkflowMutation, WorkflowBinding, WorkflowBindingError, WorkflowExecution, WorkflowHostState, WorkflowHostUpdate, WorkflowNativeStatus, WorkflowOutcome, WorkflowReceiptAdapter, WorkflowStepArguments, WorkflowStepOptions, WorkflowStepRunner, WorkflowTransition, receiptWorkflowMutation, runWorkflowStep, terminalWorkflowAttempt, workflowAttempt, workflowBinding, workflowCancel, workflowDecode, workflowExecution, workflowReceiptInvocation, workflowRestart, workflowStatusBindings, workflowTransition };
|
|
167
166
|
//# sourceMappingURL=workflow.d.mts.map
|