cvx-kit 0.1.2 → 0.1.3

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 CHANGED
@@ -12,6 +12,24 @@ are this file's matching section.
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ## [0.1.3] - 2026-09-06
16
+
17
+ ### Fixed
18
+
19
+ - Fix Convex ID validation across package boundaries by sharing `convex-helpers`
20
+ as a peer dependency. Export `zid`, `zodToConvex`, and `convexToZod` from
21
+ `cvx-kit/zod-table`; verify differing compatible host versions under npm and Bun (#10).
22
+ - Add `Command.withContext<Context, Extension>()` for schema-inferred guards,
23
+ audit callbacks, operation middleware, and correlated registry middleware (#11).
24
+ - Add invocation-local `prepare` hooks with authorized, validated replay and
25
+ completion after audit, inside observation. Durable storage remains host-owned (#12).
26
+ - Accept concrete and generic `convex-test` backends directly in registration helpers (#13).
27
+ - Parse command results once after middleware; handler and middleware result types
28
+ describe schema inputs, while audit, completion, and executor results describe outputs.
29
+ - Accept raw command schema inputs at executor call sites while inferring parsed
30
+ outputs for callbacks; reject incompatible typed host contexts and registry middleware.
31
+ - See `docs/upgrading.md` for helper peer installation and transformed-result migration.
32
+
15
33
  ## [0.1.2] - 2026-08-21
16
34
 
17
35
  ### Added
@@ -1,2 +1,2 @@
1
- import{components as e}from"./_generated/api.mjs";import{mutation as t,query as n}from"./_generated/server.mjs";import{APPROVAL_AUDIT_RETENTION_CATEGORY as r,MAX_APPROVAL_AUDIT_RETENTION_DAYS as i}from"./constants.mjs";import{v as a}from"convex/values";import{AuditLog as o}from"convex-audit-log";const s=new o(e.auditLog,{samplingEnabled:!1}),c=n({args:{resourceType:a.string(),resourceRef:a.string(),limit:a.optional(a.number())},returns:a.array(a.object({action:a.string(),actorRef:a.optional(a.string()),correlationKey:a.string(),transition:a.string()})),handler:async(e,t)=>{let n=Math.max(1,Math.min(Math.trunc(t.limit??100),100)),r=await s.queryByResource(e,{resourceType:t.resourceType,resourceId:t.resourceRef,limit:n}),i=new Map;for(let e of r){let t=p(e);t&&i.set(t.correlationKey,t)}return[...i.values()]}}),l=t({args:{olderThanDays:a.number(),batchSize:a.number()},returns:a.number(),handler:(e,t)=>{let n=f(t.olderThanDays,1,i,`olderThanDays`),a=f(t.batchSize,1,100,`batchSize`);return s.cleanup(e,{olderThanDays:n,batchSize:a,retentionCategory:r})}});function u(e){return{action:e.action,...e.actorRef===void 0?{}:{actorId:e.actorRef},resourceType:e.resourceType,resourceId:e.resourceRef,severity:e.severity??`info`,tags:[`approvals`],retentionCategory:r,metadata:{correlationKey:`${e.runId}:${e.transition}`,runId:e.runId,transition:e.transition}}}async function d(e,t){await s.log(e,u(t))}function f(e,t,n,r){if(!Number.isInteger(e)||e<t||e>n)throw Error(`${r} must be an integer between ${t} and ${n}`);return e}function p(e){if(!e||typeof e!=`object`)return null;let t=e,n=t.metadata;if(!n||typeof n!=`object`)return null;let r=n;return typeof t.action!=`string`||typeof r.correlationKey!=`string`||typeof r.transition!=`string`?null:{action:t.action,...typeof t.actorId==`string`?{actorRef:t.actorId}:{},correlationKey:r.correlationKey,transition:r.transition}}export{s as approvalAudit,u as approvalAuditEvent,l as cleanup,c as history,d as logApprovalTransition};
1
+ import{components as e}from"./_generated/api.mjs";import{mutation as t,query as n}from"./_generated/server.mjs";import{APPROVAL_AUDIT_RETENTION_CATEGORY as r,MAX_APPROVAL_AUDIT_RETENTION_DAYS as i}from"./constants.mjs";import{AuditLog as a}from"convex-audit-log";import{v as o}from"convex/values";const s=new a(e.auditLog,{samplingEnabled:!1}),c=n({args:{resourceType:o.string(),resourceRef:o.string(),limit:o.optional(o.number())},returns:o.array(o.object({action:o.string(),actorRef:o.optional(o.string()),correlationKey:o.string(),transition:o.string()})),handler:async(e,t)=>{let n=Math.max(1,Math.min(Math.trunc(t.limit??100),100)),r=await s.queryByResource(e,{resourceType:t.resourceType,resourceId:t.resourceRef,limit:n}),i=new Map;for(let e of r){let t=p(e);t&&i.set(t.correlationKey,t)}return[...i.values()]}}),l=t({args:{olderThanDays:o.number(),batchSize:o.number()},returns:o.number(),handler:(e,t)=>{let n=f(t.olderThanDays,1,i,`olderThanDays`),a=f(t.batchSize,1,100,`batchSize`);return s.cleanup(e,{olderThanDays:n,batchSize:a,retentionCategory:r})}});function u(e){return{action:e.action,...e.actorRef===void 0?{}:{actorId:e.actorRef},resourceType:e.resourceType,resourceId:e.resourceRef,severity:e.severity??`info`,tags:[`approvals`],retentionCategory:r,metadata:{correlationKey:`${e.runId}:${e.transition}`,runId:e.runId,transition:e.transition}}}async function d(e,t){await s.log(e,u(t))}function f(e,t,n,r){if(!Number.isInteger(e)||e<t||e>n)throw Error(`${r} must be an integer between ${t} and ${n}`);return e}function p(e){if(!e||typeof e!=`object`)return null;let t=e,n=t.metadata;if(!n||typeof n!=`object`)return null;let r=n;return typeof t.action!=`string`||typeof r.correlationKey!=`string`||typeof r.transition!=`string`?null:{action:t.action,...typeof t.actorId==`string`?{actorRef:t.actorId}:{},correlationKey:r.correlationKey,transition:r.transition}}export{s as approvalAudit,u as approvalAuditEvent,l as cleanup,c as history,d as logApprovalTransition};
2
2
  //# sourceMappingURL=audit.mjs.map
@@ -5,7 +5,7 @@ declare namespace workflow_d_exports {
5
5
  declare const approvalWorkflow: WorkflowManager;
6
6
  declare const run: import("convex/server").RegisteredMutation<"internal", import("@convex-dev/workflow").WorkflowArgs<{
7
7
  runId: import("convex/values").VId<import("convex/values").GenericId<"approvalRuns">, "required">;
8
- }>, import("@convex-dev/workflow").WorkflowId>;
8
+ }>, import("@convex-dev/workflow").WorkflowMutationResult<null>>;
9
9
  //#endregion
10
10
  export { approvalWorkflow, run, workflow_d_exports };
11
11
  //# sourceMappingURL=workflow.d.mts.map
@@ -1,10 +1,11 @@
1
- import { CommandExecution, CommandInput, CommandRegistry, CommandResult, Parseable } from "./modules/command/command.mjs";
1
+ import { CommandArgument, CommandExecution, CommandHandlerResult, CommandInput, CommandRegistry, CommandResult, Parseable, SchemaInput } from "./modules/command/command.mjs";
2
2
  import { CommandObservation, Observability, ObservabilityOptions } from "./modules/observability/observability.mjs";
3
3
  import { AnyQueryMiddleware, Query, QueryExecution, QueryMiddleware } from "./modules/query/query.mjs";
4
4
  import { Result, ResultBoundary, TransactionMetricsContext, executeResultBoundary, projectResult } from "./result.mjs";
5
5
  import { emitSemanticEvent } from "./telemetry.mjs";
6
+ import { CommandPreparation, OperationExtension, OperationHostContext, TypedOperation, operationFactory } from "./operation.mjs";
6
7
  declare namespace client_d_exports {
7
- export { AnyCommandMiddleware, AnyQueryMiddleware, ApplicationCommand, AuditEntryInput, AuditWriter, AuditedOperation, AuditedRegistry, CommandConstructor, CommandDefaults, CommandExecution, CommandInput, CommandMiddleware, CommandObservation, CommandRegistry, CommandResult, Foundation, FoundationOptions, ObservabilityOptions, Parseable, PermissionChecker, QueryExecution, QueryMiddleware, Result, ResultBoundary, TransactionMetricsContext };
8
+ export { AnyCommandMiddleware, AnyQueryMiddleware, ApplicationCommand, AuditEntryInput, AuditWriter, AuditedOperation, AuditedRegistry, CommandArgument, CommandConstructor, CommandDefaults, CommandExecution, CommandHandlerResult, CommandInput, CommandMiddleware, CommandObservation, CommandPreparation, CommandRegistry, CommandResult, Foundation, FoundationOptions, ObservabilityOptions, Parseable, PermissionChecker, QueryExecution, QueryMiddleware, Result, ResultBoundary, TransactionMetricsContext, TypedOperation };
8
9
  }
9
10
  /** Shape handed to the injected audit writer; classification comes from the operation. */
10
11
  type AuditEntryInput = {
@@ -34,6 +35,10 @@ type AuditedOperation = Readonly<{
34
35
  * declaring a permission without injecting a checker fails closed.
35
36
  */
36
37
  permission?: string;
38
+ prepare?: (context: never, command: never) => MaybePromise<CommandPreparation<never>>;
39
+ replayResult?: Readonly<{
40
+ parse: (value: unknown) => unknown;
41
+ }>;
37
42
  /**
38
43
  * Per-operation precondition, after the permission check and the registry
39
44
  * default guard, before the handler. Throw to deny — nothing has run yet.
@@ -72,6 +77,7 @@ type CommandMiddleware<Context = never, Extension extends Record<string, unknown
72
77
  /** Any-context middleware — what registries and operations accept. */
73
78
  type AnyCommandMiddleware = (input: {
74
79
  operation: string;
80
+ definition?: AuditedOperation;
75
81
  command: unknown;
76
82
  context: never;
77
83
  next: (options?: {
@@ -119,6 +125,7 @@ type FoundationComponentApi = Readonly<{
119
125
  */
120
126
  declare class BoundCommand<Context, const Operations extends AuditedRegistry> {
121
127
  #private;
128
+ static withContext: typeof operationFactory;
122
129
  /** Declared aggregate types per operation — introspectable for tests. */
123
130
  readonly aggregates: Readonly<{ [Key in OperationKey<Operations>]: Operations[Key]['aggregates']; }>;
124
131
  static operation<const Definition extends AuditedOperation>(definition: Definition): Definition;
@@ -135,10 +142,8 @@ declare class BoundCommand<Context, const Operations extends AuditedRegistry> {
135
142
  });
136
143
  exec<const Key extends OperationKey<Operations>>(executor: {
137
144
  operation: Key;
138
- handler: (context: Context, command: CommandInput<Operations, Key>) => MaybePromise<CommandResult<Operations, Key>>;
139
- }): (context: Context, command: Operations[Key]["command"] extends (infer T) ? T extends Operations[Key]["command"] ? T extends Readonly<{
140
- parse: (value: unknown) => infer Output;
141
- }> ? Output : never : never : never) => Promise<Operations[Key]["result"] extends (infer T_1) ? T_1 extends Operations[Key]["result"] ? T_1 extends Readonly<{
145
+ handler: (context: Context & OperationExtension<Operations[Key]>, command: CommandInput<Operations, Key>) => MaybePromise<CommandHandlerResult<Operations, Key>>;
146
+ }): (context: Context, command: SchemaInput<Operations[Key]["command"]>) => Promise<Operations[Key]["result"] extends (infer T) ? T extends Operations[Key]["result"] ? T extends Readonly<{
142
147
  parse: (value: unknown) => infer Output;
143
148
  }> ? Output : never : never : never>;
144
149
  }
@@ -149,8 +154,9 @@ type ApplicationCommand<Context, Operations extends AuditedRegistry> = BoundComm
149
154
  * destructured facade class as input without importing any runtime kernel.
150
155
  */
151
156
  type CommandConstructor = {
152
- new <Context, const Operations extends AuditedRegistry>(operations: Operations, defaults?: CommandDefaults): BoundCommand<Context, Operations>;
157
+ new <Context, const Operations extends AuditedRegistry>(operations: Operations & { readonly [Key in keyof Operations]: OperationHostContext<Context>; }, defaults?: CommandDefaults): BoundCommand<Context, Operations>;
153
158
  operation: (typeof BoundCommand)['operation'];
159
+ withContext: (typeof BoundCommand)['withContext'];
154
160
  middleware: (typeof BoundCommand)['middleware'];
155
161
  };
156
162
  /**
@@ -170,5 +176,5 @@ declare class Foundation<Component extends FoundationComponentApi = FoundationCo
170
176
  constructor(component: Component, options: FoundationOptions);
171
177
  }
172
178
  //#endregion
173
- export { AnyCommandMiddleware, type AnyQueryMiddleware, ApplicationCommand, AuditEntryInput, AuditWriter, AuditedOperation, AuditedRegistry, CommandConstructor, CommandDefaults, type CommandExecution, type CommandInput, CommandMiddleware, type CommandObservation, type CommandRegistry, type CommandResult, Foundation, FoundationOptions, type ObservabilityOptions, type Parseable, PermissionChecker, type QueryExecution, type QueryMiddleware, type Result, type ResultBoundary, type TransactionMetricsContext, client_d_exports };
179
+ export { AnyCommandMiddleware, type AnyQueryMiddleware, ApplicationCommand, AuditEntryInput, AuditWriter, AuditedOperation, AuditedRegistry, type CommandArgument, CommandConstructor, CommandDefaults, type CommandExecution, type CommandHandlerResult, type CommandInput, CommandMiddleware, type CommandObservation, type CommandPreparation, type CommandRegistry, type CommandResult, Foundation, FoundationOptions, type ObservabilityOptions, type Parseable, PermissionChecker, type QueryExecution, type QueryMiddleware, type Result, type ResultBoundary, type TransactionMetricsContext, type TypedOperation, client_d_exports };
174
180
  //# sourceMappingURL=client.d.mts.map
@@ -1,2 +1,2 @@
1
- import{Command as e}from"./modules/command/command.mjs";import{Observability as t}from"./modules/observability/observability.mjs";import{Query as n}from"./modules/query/query.mjs";import{executeResultBoundary as r,projectResult as i}from"./result.mjs";import{emitSemanticEvent as a}from"./telemetry.mjs";var o=class{#e;#t;#n;#r;#i;aggregates;static operation(e){return e}static middleware(e){return e}constructor(t,n){this.#t=n.observability,this.#n=n.writeAudit,this.#r=n.checkPermission,this.#i=n.defaults??{},this.aggregates=Object.freeze(Object.fromEntries(Object.entries(t).map(([e,t])=>[e,t.aggregates]))),this.#e=new e({operations:t,execute:e=>this.#a(e)})}exec(e){return this.#e.exec(e)}async#a(e){return this.#t.observe({operation:e.operation,classification:e.definition.classification},async()=>{let t=e.definition.permission;if(t!==void 0){if(!this.#r)throw new l(e.operation);await this.#r(e.context,{permission:t,operation:e.operation})}let n=[...this.#i.middleware??[],...e.definition.middleware??[]],r=async t=>(await this.#i.guard?.(t),await e.definition.guard?.(t,e.command),e.run(t)),i=-1,a=async(t,o)=>{if(t<=i)throw new s(e.operation);i=t;let c=n[t];return c?c({operation:e.operation,command:e.command,context:o,next:e=>a(t+1,e?.context?{...o,...e.context}:o)}):r(o)},o=e.parseResult(await a(0,e.context)),u=await e.definition.audit({command:e.command,result:o},e.context);if(u){let t=e.definition.aggregates;if(t&&!t.includes(u.aggregate.type))throw new c(e.operation,u.aggregate.type);await this.#n(e.context,{...u,classification:e.definition.classification})}return o})}},s=class extends Error{code=`COMMAND_MIDDLEWARE_NEXT_REUSED`;name=`CommandMiddlewareError`;constructor(e){super(`A middleware for operation "${e}" called next() more than once`)}},c=class extends Error{code=`COMMAND_AGGREGATE_NOT_DECLARED`;name=`CommandAggregateError`;constructor(e,t){super(`Operation "${e}" audited aggregate type "${t}" outside its declared aggregates`)}},l=class extends Error{code=`COMMAND_PERMISSION_NOT_CONFIGURED`;name=`CommandPermissionError`;constructor(e){super(`Operation "${e}" declares a permission but the Foundation has no checkPermission`)}},u=class{status;Command;Query=n;observability;executeResultBoundary=r;projectResult=i;emitSemanticEvent=a;constructor(e,n){this.status=e.functions.status;let r=new t(n.observability);this.observability=r;let i=n.observability.writeAudit??(()=>void 0),a=n.checkPermission;this.Command=class extends o{constructor(e,t){super(e,{observability:r,writeAudit:i,checkPermission:a,defaults:t})}}}};export{u as Foundation};
1
+ import{Command as e}from"./modules/command/command.mjs";import{Observability as t}from"./modules/observability/observability.mjs";import{Query as n}from"./modules/query/query.mjs";import{executeResultBoundary as r,projectResult as i}from"./result.mjs";import{emitSemanticEvent as a}from"./telemetry.mjs";import{operationFactory as o}from"./operation.mjs";var s=class{static withContext=o;#e;#t;#n;#r;#i;aggregates;static operation(e){return e}static middleware(e){return e}constructor(t,n){this.#t=n.observability,this.#n=n.writeAudit,this.#r=n.checkPermission,this.#i=n.defaults??{},this.aggregates=Object.freeze(Object.fromEntries(Object.entries(t).map(([e,t])=>[e,t.aggregates]))),this.#e=new e({operations:t,execute:e=>this.#a(e)})}exec(e){return this.#e.exec({operation:e.operation,handler:(t,n)=>e.handler(t,n)})}async#a(e){return this.#t.observe({operation:e.operation,classification:e.definition.classification},async()=>{let t=e.definition.permission;if(t!==void 0){if(!this.#r)throw new u(e.operation);await this.#r(e.context,{permission:t,operation:e.operation})}let n=await e.definition.prepare?.(e.context,e.command);if(n?.kind===`replay`)return e.definition.replayResult?e.definition.replayResult.parse(n.result):e.parseResult(n.result);let r=[...this.#i.middleware??[],...e.definition.middleware??[]],i=async t=>(await this.#i.guard?.(t),await e.definition.guard?.(t,e.command),e.runUnparsed(t)),a=-1,o=async(t,n)=>{if(t<=a)throw new c(e.operation);a=t;let s=r[t];return s?s({operation:e.operation,definition:e.definition,command:e.command,context:n,next:e=>o(t+1,e?.context?{...n,...e.context}:n)}):i(n)},s=e.parseResult(await o(0,e.context)),d=await e.definition.audit({command:e.command,result:s},e.context);if(d){let t=e.definition.aggregates;if(t&&!t.includes(d.aggregate.type))throw new l(e.operation,d.aggregate.type);await this.#n(e.context,{...d,classification:e.definition.classification})}return await n?.complete?.(s),s})}},c=class extends Error{code=`COMMAND_MIDDLEWARE_NEXT_REUSED`;name=`CommandMiddlewareError`;constructor(e){super(`A middleware for operation "${e}" called next() more than once`)}},l=class extends Error{code=`COMMAND_AGGREGATE_NOT_DECLARED`;name=`CommandAggregateError`;constructor(e,t){super(`Operation "${e}" audited aggregate type "${t}" outside its declared aggregates`)}},u=class extends Error{code=`COMMAND_PERMISSION_NOT_CONFIGURED`;name=`CommandPermissionError`;constructor(e){super(`Operation "${e}" declares a permission but the Foundation has no checkPermission`)}},d=class{status;Command;Query=n;observability;executeResultBoundary=r;projectResult=i;emitSemanticEvent=a;constructor(e,n){this.status=e.functions.status;let r=new t(n.observability);this.observability=r;let i=n.observability.writeAudit??(()=>void 0),a=n.checkPermission;this.Command=class extends s{constructor(e,t){super(e,{observability:r,writeAudit:i,checkPermission:a,defaults:t})}}}};export{d as Foundation};
2
2
  //# sourceMappingURL=client.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.mjs","names":["#kernel","#observability","#writeAudit","#checkPermission","#defaults","CommandKernelClass","#execute"],"sources":["../../../src/components/foundation/client.ts"],"sourcesContent":["import {\n\tCommand as CommandKernelClass,\n\ttype CommandExecution,\n\ttype CommandInput,\n\ttype CommandRegistry,\n\ttype CommandResult,\n} from './modules/command/command'\nimport {\n\tObservability,\n\ttype ObservabilityOptions,\n} from './modules/observability/observability'\nimport { Query } from './modules/query/query'\nimport { executeResultBoundary, projectResult } from './result'\nimport { emitSemanticEvent } from './telemetry'\n\n/** Shape handed to the injected audit writer; classification comes from the operation. */\nexport type AuditEntryInput = {\n\toperation: string\n\tactorId: string\n\taggregate: { type: string; id: string }\n\tmetadata?: Record<string, unknown>\n\tclassification: string\n}\n\nexport type AuditWriter = (\n\tcontext: never,\n\tentry: AuditEntryInput,\n) => Promise<unknown> | unknown\n\ntype MaybePromise<Value> = Value | Promise<Value>\ntype OperationKey<Operations extends CommandRegistry> = Extract<\n\tkeyof Operations,\n\tstring\n>\n\nexport type AuditedOperation = Readonly<{\n\tcommand: Readonly<{ parse: (value: unknown) => unknown }>\n\tresult: Readonly<{ parse: (value: unknown) => unknown }>\n\tclassification: string\n\t/**\n\t * Permission slug required to execute this operation. Checked through the\n\t * Foundation's injected checkPermission BEFORE guards and the handler;\n\t * declaring a permission without injecting a checker fails closed.\n\t */\n\tpermission?: string\n\t/**\n\t * Per-operation precondition, after the permission check and the registry\n\t * default guard, before the handler. Throw to deny — nothing has run yet.\n\t */\n\tguard?: (context: never, command: never) => MaybePromise<void>\n\t/**\n\t * Allowlist of aggregate types this operation's audit may reference. When\n\t * declared, an audit entry whose aggregate.type is not listed throws —\n\t * the audit vocabulary is enforced, not advisory.\n\t */\n\taggregates?: readonly string[]\n\t/** Per-operation middleware, inside the registry-wide chain. */\n\tmiddleware?: readonly AnyCommandMiddleware[]\n\taudit: (\n\t\tresolution: Readonly<{ command: never; result: never }>,\n\t\tcontext: never,\n\t) => MaybePromise<Omit<AuditEntryInput, 'classification'> | null>\n}>\n\n/**\n * Composable, next()-based middleware wrapping [guards → handler]. Runs\n * INSIDE the pipeline's invariants: after the permission check, before the\n * result-schema parse, aggregate allowlist, and audit — so middleware can\n * time, trace, enrich context (`next({ context })`), short-circuit, or\n * transform results, but can never skip authorization, return an invalid\n * result, or desynchronize audit from effects.\n */\nexport type CommandMiddleware<\n\tContext = never,\n\tExtension extends Record<string, unknown> = Record<string, unknown>,\n> = (input: {\n\toperation: string\n\tcommand: unknown\n\tcontext: Context\n\tnext: (options?: {\n\t\t/** Merged into the context handed to inner middleware, guards, handler. */\n\t\tcontext?: Extension\n\t}) => Promise<unknown>\n}) => Promise<unknown>\n\n/** Any-context middleware — what registries and operations accept. */\n// Contravariant seam: the supertype every typed CommandMiddleware<C, E>\n// assigns to (context contra-narrows to never; next's options widen to\n// unknown). Definition sites stay typed via Command.middleware.\nexport type AnyCommandMiddleware = (input: {\n\toperation: string\n\tcommand: unknown\n\tcontext: never\n\tnext: (options?: { context?: unknown }) => Promise<unknown>\n}) => Promise<unknown>\n\n/** Registry-wide defaults applied to every operation of one Command. */\nexport type CommandDefaults = Readonly<{\n\t/** Runs before every operation's own guard. Throw to deny. */\n\tguard?: (context: never) => MaybePromise<void>\n\t/** Outermost middleware, in array order, around every operation. */\n\tmiddleware?: readonly AnyCommandMiddleware[]\n}>\n\n/** Injected permission policy: throw to deny. Host semantics, kit ordering. */\nexport type PermissionChecker = (\n\tcontext: never,\n\tinput: Readonly<{ permission: string; operation: string }>,\n) => MaybePromise<void>\n\nexport type AuditedRegistry = Readonly<Record<string, AuditedOperation>>\n\nexport type FoundationOptions = Readonly<{\n\tobservability: ObservabilityOptions &\n\t\tReadonly<{\n\t\t\t/**\n\t\t\t * In-transaction audit writer used by Command. Operations that\n\t\t\t * return an audit record get it written here automatically.\n\t\t\t */\n\t\t\twriteAudit?: AuditWriter\n\t\t}>\n\t/**\n\t * Permission policy for operations that declare a `permission`. Runs\n\t * before guards and the handler; throw to deny. An operation with a\n\t * permission but no injected checker fails closed.\n\t */\n\tcheckPermission?: PermissionChecker\n}>\n\ntype FoundationComponentApi = Readonly<{\n\tfunctions: Readonly<{ status: unknown }>\n}>\n\n/**\n * Application command protocol bound to a Foundation instance:\n * validate, observe, execute, and audit. `classification` and `audit()`\n * are mandatory per operation — auditing is type-enforced, not opt-in.\n * Obtain it by destructuring the Foundation: `const { Command } = new Foundation(...)`.\n */\nclass BoundCommand<Context, const Operations extends AuditedRegistry> {\n\treadonly #kernel: CommandKernelClass<Context, Operations>\n\treadonly #observability: Observability\n\treadonly #writeAudit: AuditWriter\n\treadonly #checkPermission: PermissionChecker | undefined\n\treadonly #defaults: CommandDefaults\n\n\t/** Declared aggregate types per operation — introspectable for tests. */\n\treadonly aggregates: Readonly<{\n\t\t[Key in OperationKey<Operations>]: Operations[Key]['aggregates']\n\t}>\n\n\tstatic operation<const Definition extends AuditedOperation>(\n\t\tdefinition: Definition,\n\t): Definition {\n\t\treturn definition\n\t}\n\n\t/**\n\t * Identity helper that types a middleware against its context and the\n\t * context extension it passes downstream via next({ context }).\n\t */\n\tstatic middleware<\n\t\tContext = never,\n\t\tconst Extension extends Record<string, unknown> = Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>,\n\t>(\n\t\tmiddleware: CommandMiddleware<Context, Extension>,\n\t): CommandMiddleware<Context, Extension> {\n\t\treturn middleware\n\t}\n\n\tconstructor(\n\t\toperations: Operations,\n\t\tdeps: {\n\t\t\tobservability: Observability\n\t\t\twriteAudit: AuditWriter\n\t\t\tcheckPermission?: PermissionChecker\n\t\t\tdefaults?: CommandDefaults\n\t\t},\n\t) {\n\t\tthis.#observability = deps.observability\n\t\tthis.#writeAudit = deps.writeAudit\n\t\tthis.#checkPermission = deps.checkPermission\n\t\tthis.#defaults = deps.defaults ?? {}\n\t\tthis.aggregates = Object.freeze(\n\t\t\tObject.fromEntries(\n\t\t\t\tObject.entries(operations).map(([operation, definition]) => [\n\t\t\t\t\toperation,\n\t\t\t\t\tdefinition.aggregates,\n\t\t\t\t]),\n\t\t\t),\n\t\t) as this['aggregates']\n\t\tthis.#kernel = new CommandKernelClass<Context, Operations>({\n\t\t\toperations,\n\t\t\texecute: (execution) => this.#execute(execution),\n\t\t})\n\t}\n\n\texec<const Key extends OperationKey<Operations>>(executor: {\n\t\toperation: Key\n\t\thandler: (\n\t\t\tcontext: Context,\n\t\t\tcommand: CommandInput<Operations, Key>,\n\t\t) => MaybePromise<CommandResult<Operations, Key>>\n\t}) {\n\t\treturn this.#kernel.exec(executor)\n\t}\n\n\tasync #execute<Key extends OperationKey<Operations>>(\n\t\texecution: CommandExecution<Context, Operations, Key>,\n\t): Promise<CommandResult<Operations, Key>> {\n\t\treturn this.#observability.observe(\n\t\t\t{\n\t\t\t\toperation: execution.operation,\n\t\t\t\tclassification: execution.definition.classification,\n\t\t\t},\n\t\t\tasync () => {\n\t\t\t\t// Guards run before the handler: permission → default → operation.\n\t\t\t\tconst permission = execution.definition.permission\n\t\t\t\tif (permission !== undefined) {\n\t\t\t\t\tif (!this.#checkPermission) {\n\t\t\t\t\t\tthrow new CommandPermissionError(execution.operation)\n\t\t\t\t\t}\n\t\t\t\t\tawait this.#checkPermission(execution.context as never, {\n\t\t\t\t\t\tpermission,\n\t\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tconst middleware = [\n\t\t\t\t\t...(this.#defaults.middleware ?? []),\n\t\t\t\t\t...(execution.definition.middleware ?? []),\n\t\t\t\t]\n\t\t\t\tconst terminal = async (context: unknown) => {\n\t\t\t\t\tawait this.#defaults.guard?.(context as never)\n\t\t\t\t\tawait execution.definition.guard?.(\n\t\t\t\t\t\tcontext as never,\n\t\t\t\t\t\texecution.command as never,\n\t\t\t\t\t)\n\t\t\t\t\treturn execution.run(context as never)\n\t\t\t\t}\n\t\t\t\tlet deepest = -1\n\t\t\t\tconst dispatch = async (\n\t\t\t\t\tindex: number,\n\t\t\t\t\tcontext: unknown,\n\t\t\t\t): Promise<unknown> => {\n\t\t\t\t\tif (index <= deepest) {\n\t\t\t\t\t\tthrow new CommandMiddlewareError(execution.operation)\n\t\t\t\t\t}\n\t\t\t\t\tdeepest = index\n\t\t\t\t\tconst layer = middleware[index]\n\t\t\t\t\tif (!layer) return terminal(context)\n\t\t\t\t\treturn layer({\n\t\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\t\tcommand: execution.command,\n\t\t\t\t\t\tcontext: context as never,\n\t\t\t\t\t\tnext: (options) =>\n\t\t\t\t\t\t\tdispatch(\n\t\t\t\t\t\t\t\tindex + 1,\n\t\t\t\t\t\t\t\toptions?.context\n\t\t\t\t\t\t\t\t\t? { ...(context as object), ...(options.context as object) }\n\t\t\t\t\t\t\t\t\t: context,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// Middleware may transform the return value, so the strict\n\t\t\t\t// result schema re-parses whatever leaves the chain.\n\t\t\t\tconst result = execution.parseResult(\n\t\t\t\t\tawait dispatch(0, execution.context),\n\t\t\t\t)\n\t\t\t\tconst audit = await execution.definition.audit(\n\t\t\t\t\t{ command: execution.command, result } as never,\n\t\t\t\t\texecution.context as never,\n\t\t\t\t)\n\t\t\t\tif (audit) {\n\t\t\t\t\tconst allowed = execution.definition.aggregates\n\t\t\t\t\tif (allowed && !allowed.includes(audit.aggregate.type)) {\n\t\t\t\t\t\tthrow new CommandAggregateError(\n\t\t\t\t\t\t\texecution.operation,\n\t\t\t\t\t\t\taudit.aggregate.type,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tawait this.#writeAudit(execution.context as never, {\n\t\t\t\t\t\t...audit,\n\t\t\t\t\t\tclassification: execution.definition.classification,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\treturn result\n\t\t\t},\n\t\t)\n\t}\n}\n\n/** A middleware called next() more than once. */\nclass CommandMiddlewareError extends Error {\n\treadonly code = 'COMMAND_MIDDLEWARE_NEXT_REUSED'\n\treadonly name = 'CommandMiddlewareError'\n\n\tconstructor(operation: string) {\n\t\tsuper(\n\t\t\t`A middleware for operation \"${operation}\" called next() more than once`,\n\t\t)\n\t}\n}\n\n/** The audit referenced an aggregate type outside the operation's allowlist. */\nclass CommandAggregateError extends Error {\n\treadonly code = 'COMMAND_AGGREGATE_NOT_DECLARED'\n\treadonly name = 'CommandAggregateError'\n\n\tconstructor(operation: string, aggregateType: string) {\n\t\tsuper(\n\t\t\t`Operation \"${operation}\" audited aggregate type \"${aggregateType}\" outside its declared aggregates`,\n\t\t)\n\t}\n}\n\n/** Fails closed: an operation declared a permission but no checker exists. */\nclass CommandPermissionError extends Error {\n\treadonly code = 'COMMAND_PERMISSION_NOT_CONFIGURED'\n\treadonly name = 'CommandPermissionError'\n\n\tconstructor(operation: string) {\n\t\tsuper(\n\t\t\t`Operation \"${operation}\" declares a permission but the Foundation has no checkPermission`,\n\t\t)\n\t}\n}\n\nexport type ApplicationCommand<\n\tContext,\n\tOperations extends AuditedRegistry,\n> = BoundCommand<Context, Operations>\n\n/**\n * The constructor shape a Foundation instance exposes as `Command`.\n * Exported as a TYPE ONLY so factories (e.g. cvx-kit/crud) can accept the\n * destructured facade class as input without importing any runtime kernel.\n */\nexport type CommandConstructor = {\n\tnew <Context, const Operations extends AuditedRegistry>(\n\t\toperations: Operations,\n\t\tdefaults?: CommandDefaults,\n\t): BoundCommand<Context, Operations>\n\toperation: (typeof BoundCommand)['operation']\n\tmiddleware: (typeof BoundCommand)['middleware']\n}\n\n/**\n * Host facade for a component that owns no application execution capability.\n * Declared once; the sole source of the command protocol, query kernel, and\n * observability: `const { Command, Query, observability } = new Foundation(...)`.\n */\nexport class Foundation<\n\tComponent extends FoundationComponentApi = FoundationComponentApi,\n> {\n\treadonly status: Component['functions']['status']\n\treadonly Command: CommandConstructor\n\treadonly Query = Query\n\treadonly observability: Observability\n\t/** Typed-failure boundary — see result.ts. Facade-bound like the kernels. */\n\treadonly executeResultBoundary = executeResultBoundary\n\treadonly projectResult = projectResult\n\treadonly emitSemanticEvent = emitSemanticEvent\n\n\tconstructor(component: Component, options: FoundationOptions) {\n\t\tthis.status = component.functions.status\n\t\tconst observability = new Observability(options.observability)\n\t\tthis.observability = observability\n\t\tconst writeAudit = options.observability.writeAudit ?? (() => undefined)\n\t\tconst checkPermission = options.checkPermission\n\t\tthis.Command = class <\n\t\t\tContext,\n\t\t\tconst Operations extends AuditedRegistry,\n\t\t> extends BoundCommand<Context, Operations> {\n\t\t\tconstructor(operations: Operations, defaults?: CommandDefaults) {\n\t\t\t\tsuper(operations, {\n\t\t\t\t\tobservability,\n\t\t\t\t\twriteAudit,\n\t\t\t\t\tcheckPermission,\n\t\t\t\t\tdefaults,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Kernel CLASSES are deliberately NOT exported: Command, Query, and\n// Observability exist only as capabilities of a Foundation instance —\n// `const { Command, Query, observability } = new Foundation(...)`. A loose\n// import would construct kernels without the injected observability, audit\n// writer, and permission checker. Types stay exported for signatures.\nexport type {\n\tCommandExecution,\n\tCommandInput,\n\tCommandRegistry,\n\tCommandResult,\n\tParseable,\n} from './modules/command/command'\nexport type {\n\tCommandObservation,\n\tObservabilityOptions,\n} from './modules/observability/observability'\nexport type {\n\tAnyQueryMiddleware,\n\tQueryExecution,\n\tQueryMiddleware,\n} from './modules/query/query'\nexport type {\n\tResult,\n\tResultBoundary,\n\tTransactionMetricsContext,\n} from './result'\n\n// The component definition is deliberately NOT re-exported here: Convex's\n// CLI only discovers a component whose resolved file is convex.config.js,\n// so hosts must mount via the dedicated subpath:\n// import foundation from 'cvx-kit/components/foundation/convex.config'\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.mjs","names":["#kernel","#observability","#writeAudit","#checkPermission","#defaults","CommandKernelClass","#execute"],"sources":["../../../src/components/foundation/client.ts"],"sourcesContent":["import {\n\tCommand as CommandKernelClass,\n\ttype CommandExecution,\n\ttype CommandHandlerResult,\n\ttype CommandInput,\n\ttype CommandRegistry,\n\ttype CommandResult,\n} from './modules/command/command'\nimport {\n\tObservability,\n\ttype ObservabilityOptions,\n} from './modules/observability/observability'\nimport { Query } from './modules/query/query'\nimport { executeResultBoundary, projectResult } from './result'\nimport { emitSemanticEvent } from './telemetry'\nimport {\n\toperationFactory,\n\ttype CommandPreparation,\n\ttype OperationExtension,\n\ttype OperationHostContext,\n} from './operation'\n\n/** Shape handed to the injected audit writer; classification comes from the operation. */\nexport type AuditEntryInput = {\n\toperation: string\n\tactorId: string\n\taggregate: { type: string; id: string }\n\tmetadata?: Record<string, unknown>\n\tclassification: string\n}\n\nexport type AuditWriter = (\n\tcontext: never,\n\tentry: AuditEntryInput,\n) => Promise<unknown> | unknown\n\ntype MaybePromise<Value> = Value | Promise<Value>\ntype OperationKey<Operations extends CommandRegistry> = Extract<\n\tkeyof Operations,\n\tstring\n>\n\nexport type AuditedOperation = Readonly<{\n\tcommand: Readonly<{ parse: (value: unknown) => unknown }>\n\tresult: Readonly<{ parse: (value: unknown) => unknown }>\n\tclassification: string\n\t/**\n\t * Permission slug required to execute this operation. Checked through the\n\t * Foundation's injected checkPermission BEFORE guards and the handler;\n\t * declaring a permission without injecting a checker fails closed.\n\t */\n\tpermission?: string\n\tprepare?: (\n\t\tcontext: never,\n\t\tcommand: never,\n\t) => MaybePromise<CommandPreparation<never>>\n\treplayResult?: Readonly<{ parse: (value: unknown) => unknown }>\n\t/**\n\t * Per-operation precondition, after the permission check and the registry\n\t * default guard, before the handler. Throw to deny — nothing has run yet.\n\t */\n\tguard?: (context: never, command: never) => MaybePromise<void>\n\t/**\n\t * Allowlist of aggregate types this operation's audit may reference. When\n\t * declared, an audit entry whose aggregate.type is not listed throws —\n\t * the audit vocabulary is enforced, not advisory.\n\t */\n\taggregates?: readonly string[]\n\t/** Per-operation middleware, inside the registry-wide chain. */\n\tmiddleware?: readonly AnyCommandMiddleware[]\n\taudit: (\n\t\tresolution: Readonly<{ command: never; result: never }>,\n\t\tcontext: never,\n\t) => MaybePromise<Omit<AuditEntryInput, 'classification'> | null>\n}>\n\n/**\n * Composable, next()-based middleware wrapping [guards → handler]. Runs\n * INSIDE the pipeline's invariants: after the permission check, before the\n * result-schema parse, aggregate allowlist, and audit — so middleware can\n * time, trace, enrich context (`next({ context })`), short-circuit, or\n * transform results, but can never skip authorization, return an invalid\n * result, or desynchronize audit from effects.\n */\nexport type CommandMiddleware<\n\tContext = never,\n\tExtension extends Record<string, unknown> = Record<string, unknown>,\n> = (input: {\n\toperation: string\n\tcommand: unknown\n\tcontext: Context\n\tnext: (options?: {\n\t\t/** Merged into the context handed to inner middleware, guards, handler. */\n\t\tcontext?: Extension\n\t}) => Promise<unknown>\n}) => Promise<unknown>\n\n/** Any-context middleware — what registries and operations accept. */\n// Contravariant seam: the supertype every typed CommandMiddleware<C, E>\n// assigns to (context contra-narrows to never; next's options widen to\n// unknown). Definition sites stay typed via Command.middleware.\nexport type AnyCommandMiddleware = (input: {\n\toperation: string\n\tdefinition?: AuditedOperation\n\tcommand: unknown\n\tcontext: never\n\tnext: (options?: { context?: unknown }) => Promise<unknown>\n}) => Promise<unknown>\n\n/** Registry-wide defaults applied to every operation of one Command. */\nexport type CommandDefaults = Readonly<{\n\t/** Runs before every operation's own guard. Throw to deny. */\n\tguard?: (context: never) => MaybePromise<void>\n\t/** Outermost middleware, in array order, around every operation. */\n\tmiddleware?: readonly AnyCommandMiddleware[]\n}>\n\n/** Injected permission policy: throw to deny. Host semantics, kit ordering. */\nexport type PermissionChecker = (\n\tcontext: never,\n\tinput: Readonly<{ permission: string; operation: string }>,\n) => MaybePromise<void>\n\nexport type AuditedRegistry = Readonly<Record<string, AuditedOperation>>\n\nexport type FoundationOptions = Readonly<{\n\tobservability: ObservabilityOptions &\n\t\tReadonly<{\n\t\t\t/**\n\t\t\t * In-transaction audit writer used by Command. Operations that\n\t\t\t * return an audit record get it written here automatically.\n\t\t\t */\n\t\t\twriteAudit?: AuditWriter\n\t\t}>\n\t/**\n\t * Permission policy for operations that declare a `permission`. Runs\n\t * before guards and the handler; throw to deny. An operation with a\n\t * permission but no injected checker fails closed.\n\t */\n\tcheckPermission?: PermissionChecker\n}>\n\ntype FoundationComponentApi = Readonly<{\n\tfunctions: Readonly<{ status: unknown }>\n}>\n\n/**\n * Application command protocol bound to a Foundation instance:\n * validate, observe, execute, and audit. `classification` and `audit()`\n * are mandatory per operation — auditing is type-enforced, not opt-in.\n * Obtain it by destructuring the Foundation: `const { Command } = new Foundation(...)`.\n */\nclass BoundCommand<Context, const Operations extends AuditedRegistry> {\n\tstatic withContext = operationFactory\n\treadonly #kernel: CommandKernelClass<Context, Operations>\n\treadonly #observability: Observability\n\treadonly #writeAudit: AuditWriter\n\treadonly #checkPermission: PermissionChecker | undefined\n\treadonly #defaults: CommandDefaults\n\n\t/** Declared aggregate types per operation — introspectable for tests. */\n\treadonly aggregates: Readonly<{\n\t\t[Key in OperationKey<Operations>]: Operations[Key]['aggregates']\n\t}>\n\n\tstatic operation<const Definition extends AuditedOperation>(\n\t\tdefinition: Definition,\n\t): Definition {\n\t\treturn definition\n\t}\n\n\t/**\n\t * Identity helper that types a middleware against its context and the\n\t * context extension it passes downstream via next({ context }).\n\t */\n\tstatic middleware<\n\t\tContext = never,\n\t\tconst Extension extends Record<string, unknown> = Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>,\n\t>(\n\t\tmiddleware: CommandMiddleware<Context, Extension>,\n\t): CommandMiddleware<Context, Extension> {\n\t\treturn middleware\n\t}\n\n\tconstructor(\n\t\toperations: Operations,\n\t\tdeps: {\n\t\t\tobservability: Observability\n\t\t\twriteAudit: AuditWriter\n\t\t\tcheckPermission?: PermissionChecker\n\t\t\tdefaults?: CommandDefaults\n\t\t},\n\t) {\n\t\tthis.#observability = deps.observability\n\t\tthis.#writeAudit = deps.writeAudit\n\t\tthis.#checkPermission = deps.checkPermission\n\t\tthis.#defaults = deps.defaults ?? {}\n\t\tthis.aggregates = Object.freeze(\n\t\t\tObject.fromEntries(\n\t\t\t\tObject.entries(operations).map(([operation, definition]) => [\n\t\t\t\t\toperation,\n\t\t\t\t\tdefinition.aggregates,\n\t\t\t\t]),\n\t\t\t),\n\t\t) as this['aggregates']\n\t\tthis.#kernel = new CommandKernelClass<Context, Operations>({\n\t\t\toperations,\n\t\t\texecute: (execution) => this.#execute(execution),\n\t\t})\n\t}\n\n\texec<const Key extends OperationKey<Operations>>(executor: {\n\t\toperation: Key\n\t\thandler: (\n\t\t\tcontext: Context & OperationExtension<Operations[Key]>,\n\t\t\tcommand: CommandInput<Operations, Key>,\n\t\t) => MaybePromise<CommandHandlerResult<Operations, Key>>\n\t}) {\n\t\treturn this.#kernel.exec({\n\t\t\toperation: executor.operation,\n\t\t\thandler: (context, command) =>\n\t\t\t\texecutor.handler(\n\t\t\t\t\tcontext as Context & OperationExtension<Operations[Key]>,\n\t\t\t\t\tcommand,\n\t\t\t\t),\n\t\t})\n\t}\n\n\tasync #execute<Key extends OperationKey<Operations>>(\n\t\texecution: CommandExecution<Context, Operations, Key>,\n\t): Promise<CommandResult<Operations, Key>> {\n\t\treturn this.#observability.observe(\n\t\t\t{\n\t\t\t\toperation: execution.operation,\n\t\t\t\tclassification: execution.definition.classification,\n\t\t\t},\n\t\t\tasync () => {\n\t\t\t\t// Guards run before the handler: permission → default → operation.\n\t\t\t\tconst permission = execution.definition.permission\n\t\t\t\tif (permission !== undefined) {\n\t\t\t\t\tif (!this.#checkPermission) {\n\t\t\t\t\t\tthrow new CommandPermissionError(execution.operation)\n\t\t\t\t\t}\n\t\t\t\t\tawait this.#checkPermission(execution.context as never, {\n\t\t\t\t\t\tpermission,\n\t\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tconst preparation = await execution.definition.prepare?.(\n\t\t\t\t\texecution.context as never,\n\t\t\t\t\texecution.command as never,\n\t\t\t\t)\n\t\t\t\tif (preparation?.kind === 'replay') {\n\t\t\t\t\treturn execution.definition.replayResult\n\t\t\t\t\t\t? (execution.definition.replayResult.parse(\n\t\t\t\t\t\t\t\tpreparation.result,\n\t\t\t\t\t\t\t) as CommandResult<Operations, Key>)\n\t\t\t\t\t\t: execution.parseResult(preparation.result)\n\t\t\t\t}\n\t\t\t\tconst middleware = [\n\t\t\t\t\t...(this.#defaults.middleware ?? []),\n\t\t\t\t\t...(execution.definition.middleware ?? []),\n\t\t\t\t]\n\t\t\t\tconst terminal = async (context: unknown) => {\n\t\t\t\t\tawait this.#defaults.guard?.(context as never)\n\t\t\t\t\tawait execution.definition.guard?.(\n\t\t\t\t\t\tcontext as never,\n\t\t\t\t\t\texecution.command as never,\n\t\t\t\t\t)\n\t\t\t\t\treturn execution.runUnparsed(context as never)\n\t\t\t\t}\n\t\t\t\tlet deepest = -1\n\t\t\t\tconst dispatch = async (\n\t\t\t\t\tindex: number,\n\t\t\t\t\tcontext: unknown,\n\t\t\t\t): Promise<unknown> => {\n\t\t\t\t\tif (index <= deepest) {\n\t\t\t\t\t\tthrow new CommandMiddlewareError(execution.operation)\n\t\t\t\t\t}\n\t\t\t\t\tdeepest = index\n\t\t\t\t\tconst layer = middleware[index]\n\t\t\t\t\tif (!layer) return terminal(context)\n\t\t\t\t\treturn layer({\n\t\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\t\tdefinition: execution.definition,\n\t\t\t\t\t\tcommand: execution.command,\n\t\t\t\t\t\tcontext: context as never,\n\t\t\t\t\t\tnext: (options) =>\n\t\t\t\t\t\t\tdispatch(\n\t\t\t\t\t\t\t\tindex + 1,\n\t\t\t\t\t\t\t\toptions?.context\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t...(context as object),\n\t\t\t\t\t\t\t\t\t\t\t...(options.context as object),\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t: context,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// Middleware may transform the return value, so the strict\n\t\t\t\t// result schema parses whatever leaves the chain exactly once.\n\t\t\t\tconst result = execution.parseResult(\n\t\t\t\t\tawait dispatch(0, execution.context),\n\t\t\t\t)\n\t\t\t\tconst audit = await execution.definition.audit(\n\t\t\t\t\t{ command: execution.command, result } as never,\n\t\t\t\t\texecution.context as never,\n\t\t\t\t)\n\t\t\t\tif (audit) {\n\t\t\t\t\tconst allowed = execution.definition.aggregates\n\t\t\t\t\tif (allowed && !allowed.includes(audit.aggregate.type)) {\n\t\t\t\t\t\tthrow new CommandAggregateError(\n\t\t\t\t\t\t\texecution.operation,\n\t\t\t\t\t\t\taudit.aggregate.type,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tawait this.#writeAudit(execution.context as never, {\n\t\t\t\t\t\t...audit,\n\t\t\t\t\t\tclassification: execution.definition.classification,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tawait preparation?.complete?.(result as never)\n\t\t\t\treturn result\n\t\t\t},\n\t\t)\n\t}\n}\n\n/** A middleware called next() more than once. */\nclass CommandMiddlewareError extends Error {\n\treadonly code = 'COMMAND_MIDDLEWARE_NEXT_REUSED'\n\treadonly name = 'CommandMiddlewareError'\n\n\tconstructor(operation: string) {\n\t\tsuper(\n\t\t\t`A middleware for operation \"${operation}\" called next() more than once`,\n\t\t)\n\t}\n}\n\n/** The audit referenced an aggregate type outside the operation's allowlist. */\nclass CommandAggregateError extends Error {\n\treadonly code = 'COMMAND_AGGREGATE_NOT_DECLARED'\n\treadonly name = 'CommandAggregateError'\n\n\tconstructor(operation: string, aggregateType: string) {\n\t\tsuper(\n\t\t\t`Operation \"${operation}\" audited aggregate type \"${aggregateType}\" outside its declared aggregates`,\n\t\t)\n\t}\n}\n\n/** Fails closed: an operation declared a permission but no checker exists. */\nclass CommandPermissionError extends Error {\n\treadonly code = 'COMMAND_PERMISSION_NOT_CONFIGURED'\n\treadonly name = 'CommandPermissionError'\n\n\tconstructor(operation: string) {\n\t\tsuper(\n\t\t\t`Operation \"${operation}\" declares a permission but the Foundation has no checkPermission`,\n\t\t)\n\t}\n}\n\nexport type ApplicationCommand<\n\tContext,\n\tOperations extends AuditedRegistry,\n> = BoundCommand<Context, Operations>\n\n/**\n * The constructor shape a Foundation instance exposes as `Command`.\n * Exported as a TYPE ONLY so factories (e.g. cvx-kit/crud) can accept the\n * destructured facade class as input without importing any runtime kernel.\n */\nexport type CommandConstructor = {\n\tnew <Context, const Operations extends AuditedRegistry>(\n\t\toperations: Operations & {\n\t\t\treadonly [Key in keyof Operations]: OperationHostContext<Context>\n\t\t},\n\t\tdefaults?: CommandDefaults,\n\t): BoundCommand<Context, Operations>\n\toperation: (typeof BoundCommand)['operation']\n\twithContext: (typeof BoundCommand)['withContext']\n\tmiddleware: (typeof BoundCommand)['middleware']\n}\n\n/**\n * Host facade for a component that owns no application execution capability.\n * Declared once; the sole source of the command protocol, query kernel, and\n * observability: `const { Command, Query, observability } = new Foundation(...)`.\n */\nexport class Foundation<\n\tComponent extends FoundationComponentApi = FoundationComponentApi,\n> {\n\treadonly status: Component['functions']['status']\n\treadonly Command: CommandConstructor\n\treadonly Query = Query\n\treadonly observability: Observability\n\t/** Typed-failure boundary — see result.ts. Facade-bound like the kernels. */\n\treadonly executeResultBoundary = executeResultBoundary\n\treadonly projectResult = projectResult\n\treadonly emitSemanticEvent = emitSemanticEvent\n\n\tconstructor(component: Component, options: FoundationOptions) {\n\t\tthis.status = component.functions.status\n\t\tconst observability = new Observability(options.observability)\n\t\tthis.observability = observability\n\t\tconst writeAudit = options.observability.writeAudit ?? (() => undefined)\n\t\tconst checkPermission = options.checkPermission\n\t\tthis.Command = class<\n\t\t\tContext,\n\t\t\tconst Operations extends AuditedRegistry,\n\t\t> extends BoundCommand<Context, Operations> {\n\t\t\tconstructor(operations: Operations, defaults?: CommandDefaults) {\n\t\t\t\tsuper(operations, {\n\t\t\t\t\tobservability,\n\t\t\t\t\twriteAudit,\n\t\t\t\t\tcheckPermission,\n\t\t\t\t\tdefaults,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Kernel CLASSES are deliberately NOT exported: Command, Query, and\n// Observability exist only as capabilities of a Foundation instance —\n// `const { Command, Query, observability } = new Foundation(...)`. A loose\n// import would construct kernels without the injected observability, audit\n// writer, and permission checker. Types stay exported for signatures.\nexport type { CommandPreparation, TypedOperation } from './operation'\nexport type {\n\tCommandArgument,\n\tCommandExecution,\n\tCommandHandlerResult,\n\tCommandInput,\n\tCommandRegistry,\n\tCommandResult,\n\tParseable,\n} from './modules/command/command'\nexport type {\n\tCommandObservation,\n\tObservabilityOptions,\n} from './modules/observability/observability'\nexport type {\n\tAnyQueryMiddleware,\n\tQueryExecution,\n\tQueryMiddleware,\n} from './modules/query/query'\nexport type {\n\tResult,\n\tResultBoundary,\n\tTransactionMetricsContext,\n} from './result'\n\n// The component definition is deliberately NOT re-exported here: Convex's\n// CLI only discovers a component whose resolved file is convex.config.js,\n// so hosts must mount via the dedicated subpath:\n// import foundation from 'cvx-kit/components/foundation/convex.config'\n"],"mappings":"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"}
@@ -1,10 +1,17 @@
1
1
  declare namespace command_d_exports {
2
- export { Command, CommandExecution, CommandInput, CommandRegistry, CommandResult, Parseable };
2
+ export { Command, CommandArgument, CommandExecution, CommandHandlerResult, CommandInput, CommandRegistry, CommandResult, Parseable, SchemaInput };
3
3
  }
4
4
  type Parseable<Output> = Readonly<{
5
5
  parse: (value: unknown) => Output;
6
6
  }>;
7
+ /** Zod exposes input separately for transforms; plain parsers retain their output contract. */
8
+ type SchemaInput<Schema> = Schema extends {
9
+ _input: infer Input;
10
+ } ? Input : Parsed<Schema>;
11
+ type CommandHandlerResult<Registry extends CommandRegistry, Key extends Operation<Registry>> = SchemaInput<Registry[Key]['result']>;
7
12
  type CommandRegistry = Readonly<Record<string, CommandDefinition>>;
13
+ /** Values accepted from callers before the command schema parses them. */
14
+ type CommandArgument<Registry extends CommandRegistry, Key extends Operation<Registry>> = SchemaInput<Registry[Key]['command']>;
8
15
  type CommandInput<Registry extends CommandRegistry, Key extends Operation<Registry>> = Parsed<Registry[Key]['command']>;
9
16
  type CommandResult<Registry extends CommandRegistry, Key extends Operation<Registry>> = Parsed<Registry[Key]['result']>;
10
17
  type CommandExecution<Context, Registry extends CommandRegistry, Key extends Operation<Registry>> = Readonly<{
@@ -15,6 +22,8 @@ type CommandExecution<Context, Registry extends CommandRegistry, Key extends Ope
15
22
  parseResult: (value: unknown) => CommandResult<Registry, Key>;
16
23
  /** Runs the handler (result-parsed). Accepts a middleware-enriched context. */
17
24
  run: (context?: Context) => Promise<CommandResult<Registry, Key>>;
25
+ /** Runs without parsing for owners that validate after their middleware chain. */
26
+ runUnparsed: (context?: Context) => Promise<CommandHandlerResult<Registry, Key>>;
18
27
  }>;
19
28
  type CommandDefinition = Readonly<{
20
29
  command: Parseable<unknown>;
@@ -26,12 +35,12 @@ type MaybePromise<Value> = Value | Promise<Value>;
26
35
  type Execute<Context, Registry extends CommandRegistry> = <Key extends Operation<Registry>>(execution: CommandExecution<Context, Registry, Key>) => Promise<CommandResult<Registry, Key>>;
27
36
  type FixedExecutor<Context, Registry extends CommandRegistry, Key extends Operation<Registry>> = Readonly<{
28
37
  operation: Key;
29
- handler: (context: Context, command: CommandInput<Registry, Key>) => MaybePromise<CommandResult<Registry, Key>>;
38
+ handler: (context: Context, command: CommandInput<Registry, Key>) => MaybePromise<CommandHandlerResult<Registry, Key>>;
30
39
  }>;
31
40
  type DynamicExecutor<Context, Registry extends CommandRegistry, Dispatcher, Key extends Operation<Registry>> = Readonly<{
32
41
  dispatcher: Parseable<Dispatcher>;
33
42
  select: (dispatcher: Dispatcher) => Key;
34
- handler: (context: Context, command: CommandInput<Registry, Key>) => MaybePromise<CommandResult<Registry, Key>>;
43
+ handler: (context: Context, command: CommandInput<Registry, Key>) => MaybePromise<CommandHandlerResult<Registry, Key>>;
35
44
  }>;
36
45
  /** Generic command kernel. Host policy is injected by the owning domain. */
37
46
  declare class Command<Context, const Registry extends CommandRegistry> {
@@ -40,9 +49,9 @@ declare class Command<Context, const Registry extends CommandRegistry> {
40
49
  readonly operations: Registry;
41
50
  readonly execute: Execute<Context, Registry>;
42
51
  });
43
- exec<const Key extends Operation<Registry>>(executor: FixedExecutor<Context, Registry, Key>): (context: Context, command: CommandInput<Registry, Key>) => Promise<CommandResult<Registry, Key>>;
52
+ exec<const Key extends Operation<Registry>>(executor: FixedExecutor<Context, Registry, Key>): (context: Context, command: CommandArgument<Registry, Key>) => Promise<CommandResult<Registry, Key>>;
44
53
  exec<Dispatcher, const Key extends Operation<Registry>>(executor: DynamicExecutor<Context, Registry, Dispatcher, Key>): (context: Context, command: Dispatcher) => Promise<CommandResult<Registry, Key>>;
45
54
  }
46
55
  //#endregion
47
- export { Command, CommandExecution, CommandInput, CommandRegistry, CommandResult, Parseable, command_d_exports };
56
+ export { Command, CommandArgument, CommandExecution, CommandHandlerResult, CommandInput, CommandRegistry, CommandResult, Parseable, SchemaInput, command_d_exports };
48
57
  //# sourceMappingURL=command.d.mts.map
@@ -1,2 +1,2 @@
1
- var e=class extends Error{code=`COMMAND_OPERATION_NOT_CONFIGURED`;name=`CommandConfigurationError`;constructor(){super(`The selected command operation is not configured`)}},t=class{#e;#t;constructor(e){this.#e=e.operations,this.#t=e.execute}exec(t){return async(n,r)=>{let i=`operation`in t?{operation:t.operation,value:r}:(()=>{let e=t.dispatcher.parse(r);return{operation:t.select(e),value:e}})();if(!Object.prototype.hasOwnProperty.call(this.#e,i.operation))throw new e;let a=i.operation,o=this.#e[a],s=o.command.parse(i.value),c=e=>o.result.parse(e),l=t.handler;return this.#t({context:n,operation:a,definition:o,command:s,parseResult:c,run:async e=>c(await l(e??n,s))})}}};export{t as Command};
1
+ var e=class extends Error{code=`COMMAND_OPERATION_NOT_CONFIGURED`;name=`CommandConfigurationError`;constructor(){super(`The selected command operation is not configured`)}},t=class{#e;#t;constructor(e){this.#e=e.operations,this.#t=e.execute}exec(t){return async(n,r)=>{let i=`operation`in t?{operation:t.operation,value:r}:(()=>{let e=t.dispatcher.parse(r);return{operation:t.select(e),value:e}})();if(!Object.prototype.hasOwnProperty.call(this.#e,i.operation))throw new e;let a=i.operation,o=this.#e[a],s=o.command.parse(i.value),c=e=>o.result.parse(e),l=t.handler;return this.#t({context:n,operation:a,definition:o,command:s,parseResult:c,runUnparsed:async e=>l(e??n,s),run:async e=>c(await l(e??n,s))})}}};export{t as Command};
2
2
  //# sourceMappingURL=command.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"command.mjs","names":["#operations","#execute"],"sources":["../../../../../src/components/foundation/modules/command/command.ts"],"sourcesContent":["export type Parseable<Output> = Readonly<{ parse: (value: unknown) => Output }>\n\nexport type CommandRegistry = Readonly<Record<string, CommandDefinition>>\n\nexport type CommandInput<\n\tRegistry extends CommandRegistry,\n\tKey extends Operation<Registry>,\n> = Parsed<Registry[Key]['command']>\n\nexport type CommandResult<\n\tRegistry extends CommandRegistry,\n\tKey extends Operation<Registry>,\n> = Parsed<Registry[Key]['result']>\n\nexport type CommandExecution<\n\tContext,\n\tRegistry extends CommandRegistry,\n\tKey extends Operation<Registry>,\n> = Readonly<{\n\tcontext: Context\n\toperation: Key\n\tdefinition: Registry[Key]\n\tcommand: CommandInput<Registry, Key>\n\tparseResult: (value: unknown) => CommandResult<Registry, Key>\n\t/** Runs the handler (result-parsed). Accepts a middleware-enriched context. */\n\trun: (context?: Context) => Promise<CommandResult<Registry, Key>>\n}>\n\ntype CommandDefinition = Readonly<{\n\tcommand: Parseable<unknown>\n\tresult: Parseable<unknown>\n}>\n\ntype Operation<Registry extends CommandRegistry> = Extract<\n\tkeyof Registry,\n\tstring\n>\ntype Parsed<Schema> = Schema extends Parseable<infer Output> ? Output : never\ntype MaybePromise<Value> = Value | Promise<Value>\ntype Execute<Context, Registry extends CommandRegistry> = <\n\tKey extends Operation<Registry>,\n>(\n\texecution: CommandExecution<Context, Registry, Key>,\n) => Promise<CommandResult<Registry, Key>>\n\ntype FixedExecutor<\n\tContext,\n\tRegistry extends CommandRegistry,\n\tKey extends Operation<Registry>,\n> = Readonly<{\n\toperation: Key\n\thandler: (\n\t\tcontext: Context,\n\t\tcommand: CommandInput<Registry, Key>,\n\t) => MaybePromise<CommandResult<Registry, Key>>\n}>\n\ntype DynamicExecutor<\n\tContext,\n\tRegistry extends CommandRegistry,\n\tDispatcher,\n\tKey extends Operation<Registry>,\n> = Readonly<{\n\tdispatcher: Parseable<Dispatcher>\n\tselect: (dispatcher: Dispatcher) => Key\n\thandler: (\n\t\tcontext: Context,\n\t\tcommand: CommandInput<Registry, Key>,\n\t) => MaybePromise<CommandResult<Registry, Key>>\n}>\n\nclass CommandConfigurationError extends Error {\n\treadonly code = 'COMMAND_OPERATION_NOT_CONFIGURED'\n\treadonly name = 'CommandConfigurationError'\n\n\tconstructor() {\n\t\tsuper('The selected command operation is not configured')\n\t}\n}\n\n/** Generic command kernel. Host policy is injected by the owning domain. */\nexport class Command<Context, const Registry extends CommandRegistry> {\n\treadonly #operations: Registry\n\treadonly #execute: Execute<Context, Registry>\n\n\tconstructor(configuration: {\n\t\treadonly operations: Registry\n\t\treadonly execute: Execute<Context, Registry>\n\t}) {\n\t\tthis.#operations = configuration.operations\n\t\tthis.#execute = configuration.execute\n\t}\n\n\texec<const Key extends Operation<Registry>>(\n\t\texecutor: FixedExecutor<Context, Registry, Key>,\n\t): (\n\t\tcontext: Context,\n\t\tcommand: CommandInput<Registry, Key>,\n\t) => Promise<CommandResult<Registry, Key>>\n\texec<Dispatcher, const Key extends Operation<Registry>>(\n\t\texecutor: DynamicExecutor<Context, Registry, Dispatcher, Key>,\n\t): (\n\t\tcontext: Context,\n\t\tcommand: Dispatcher,\n\t) => Promise<CommandResult<Registry, Key>>\n\texec(\n\t\texecutor:\n\t\t\t| FixedExecutor<Context, Registry, Operation<Registry>>\n\t\t\t| DynamicExecutor<Context, Registry, unknown, Operation<Registry>>,\n\t) {\n\t\treturn async (context: Context, value: unknown) => {\n\t\t\tconst selected =\n\t\t\t\t'operation' in executor\n\t\t\t\t\t? { operation: executor.operation, value }\n\t\t\t\t\t: (() => {\n\t\t\t\t\t\t\tconst dispatcher = executor.dispatcher.parse(value)\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\toperation: executor.select(dispatcher),\n\t\t\t\t\t\t\t\tvalue: dispatcher,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})()\n\t\t\tif (\n\t\t\t\t!Object.prototype.hasOwnProperty.call(\n\t\t\t\t\tthis.#operations,\n\t\t\t\t\tselected.operation,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthrow new CommandConfigurationError()\n\t\t\t}\n\t\t\tconst operation = selected.operation as Operation<Registry>\n\t\t\tconst definition = this.#operations[operation]\n\t\t\tconst command = definition.command.parse(selected.value) as CommandInput<\n\t\t\t\tRegistry,\n\t\t\t\tOperation<Registry>\n\t\t\t>\n\t\t\tconst parseResult = (result: unknown) =>\n\t\t\t\tdefinition.result.parse(result) as CommandResult<\n\t\t\t\t\tRegistry,\n\t\t\t\t\tOperation<Registry>\n\t\t\t\t>\n\t\t\tconst handler = executor.handler as (\n\t\t\t\tcontext: Context,\n\t\t\t\tcommand: CommandInput<Registry, Operation<Registry>>,\n\t\t\t) => MaybePromise<CommandResult<Registry, Operation<Registry>>>\n\t\t\treturn this.#execute({\n\t\t\t\tcontext,\n\t\t\t\toperation,\n\t\t\t\tdefinition,\n\t\t\t\tcommand,\n\t\t\t\tparseResult,\n\t\t\t\trun: async (contextOverride?: Context) =>\n\t\t\t\t\tparseResult(await handler(contextOverride ?? context, command)),\n\t\t\t})\n\t\t}\n\t}\n}\n"],"mappings":"AAuEA,IAAM,EAAN,cAAwC,KAAM,CAC7C,KAAgB,mCAChB,KAAgB,4BAEhB,aAAc,CACb,MAAM,kDAAkD,CACzD,CACD,EAGa,EAAb,KAAsE,CACrE,GACA,GAEA,YAAY,EAGT,CACF,KAAKA,GAAc,EAAc,WACjC,KAAKC,GAAW,EAAc,OAC/B,CAcA,KACC,EAGC,CACD,OAAO,MAAO,EAAkB,IAAmB,CAClD,IAAM,EACL,cAAe,EACZ,CAAE,UAAW,EAAS,UAAW,OAAM,OAChC,CACP,IAAM,EAAa,EAAS,WAAW,MAAM,CAAK,EAClD,MAAO,CACN,UAAW,EAAS,OAAO,CAAU,EACrC,MAAO,CACR,CACD,EAAA,CAAG,EACN,GACC,CAAC,OAAO,UAAU,eAAe,KAChC,KAAKD,GACL,EAAS,SACV,EAEA,MAAM,IAAI,EAEX,IAAM,EAAY,EAAS,UACrB,EAAa,KAAKA,GAAY,GAC9B,EAAU,EAAW,QAAQ,MAAM,EAAS,KAAK,EAIjD,EAAe,GACpB,EAAW,OAAO,MAAM,CAAM,EAIzB,EAAU,EAAS,QAIzB,OAAO,KAAKC,GAAS,CACpB,UACA,YACA,aACA,UACA,cACA,IAAK,KAAO,IACX,EAAY,MAAM,EAAQ,GAAmB,EAAS,CAAO,CAAC,CAChE,CAAC,CACF,CACD,CACD"}
1
+ {"version":3,"file":"command.mjs","names":["#operations","#execute"],"sources":["../../../../../src/components/foundation/modules/command/command.ts"],"sourcesContent":["export type Parseable<Output> = Readonly<{\n\tparse: (value: unknown) => Output\n}>\n\n/** Zod exposes input separately for transforms; plain parsers retain their output contract. */\nexport type SchemaInput<Schema> = Schema extends { _input: infer Input }\n\t? Input\n\t: Parsed<Schema>\nexport type CommandHandlerResult<\n\tRegistry extends CommandRegistry,\n\tKey extends Operation<Registry>,\n> = SchemaInput<Registry[Key]['result']>\n\nexport type CommandRegistry = Readonly<Record<string, CommandDefinition>>\n\n/** Values accepted from callers before the command schema parses them. */\nexport type CommandArgument<Registry extends CommandRegistry, Key extends Operation<Registry>> = SchemaInput<Registry[Key]['command']>\n\nexport type CommandInput<\n\tRegistry extends CommandRegistry,\n\tKey extends Operation<Registry>,\n> = Parsed<Registry[Key]['command']>\n\nexport type CommandResult<\n\tRegistry extends CommandRegistry,\n\tKey extends Operation<Registry>,\n> = Parsed<Registry[Key]['result']>\n\nexport type CommandExecution<\n\tContext,\n\tRegistry extends CommandRegistry,\n\tKey extends Operation<Registry>,\n> = Readonly<{\n\tcontext: Context\n\toperation: Key\n\tdefinition: Registry[Key]\n\tcommand: CommandInput<Registry, Key>\n\tparseResult: (value: unknown) => CommandResult<Registry, Key>\n\t/** Runs the handler (result-parsed). Accepts a middleware-enriched context. */\n\trun: (context?: Context) => Promise<CommandResult<Registry, Key>>\n\t/** Runs without parsing for owners that validate after their middleware chain. */\n\trunUnparsed: (\n\t\tcontext?: Context,\n\t) => Promise<CommandHandlerResult<Registry, Key>>\n}>\n\ntype CommandDefinition = Readonly<{\n\tcommand: Parseable<unknown>\n\tresult: Parseable<unknown>\n}>\n\ntype Operation<Registry extends CommandRegistry> = Extract<\n\tkeyof Registry,\n\tstring\n>\ntype Parsed<Schema> =\n\tSchema extends Parseable<infer Output> ? Output : never\ntype MaybePromise<Value> = Value | Promise<Value>\ntype Execute<Context, Registry extends CommandRegistry> = <\n\tKey extends Operation<Registry>,\n>(\n\texecution: CommandExecution<Context, Registry, Key>,\n) => Promise<CommandResult<Registry, Key>>\n\ntype FixedExecutor<\n\tContext,\n\tRegistry extends CommandRegistry,\n\tKey extends Operation<Registry>,\n> = Readonly<{\n\toperation: Key\n\thandler: (\n\t\tcontext: Context,\n\t\tcommand: CommandInput<Registry, Key>,\n\t) => MaybePromise<CommandHandlerResult<Registry, Key>>\n}>\n\ntype DynamicExecutor<\n\tContext,\n\tRegistry extends CommandRegistry,\n\tDispatcher,\n\tKey extends Operation<Registry>,\n> = Readonly<{\n\tdispatcher: Parseable<Dispatcher>\n\tselect: (dispatcher: Dispatcher) => Key\n\thandler: (\n\t\tcontext: Context,\n\t\tcommand: CommandInput<Registry, Key>,\n\t) => MaybePromise<CommandHandlerResult<Registry, Key>>\n}>\n\nclass CommandConfigurationError extends Error {\n\treadonly code = 'COMMAND_OPERATION_NOT_CONFIGURED'\n\treadonly name = 'CommandConfigurationError'\n\n\tconstructor() {\n\t\tsuper('The selected command operation is not configured')\n\t}\n}\n\n/** Generic command kernel. Host policy is injected by the owning domain. */\nexport class Command<Context, const Registry extends CommandRegistry> {\n\treadonly #operations: Registry\n\treadonly #execute: Execute<Context, Registry>\n\n\tconstructor(configuration: {\n\t\treadonly operations: Registry\n\t\treadonly execute: Execute<Context, Registry>\n\t}) {\n\t\tthis.#operations = configuration.operations\n\t\tthis.#execute = configuration.execute\n\t}\n\n\texec<const Key extends Operation<Registry>>(\n\t\texecutor: FixedExecutor<Context, Registry, Key>,\n\t): (\n\t\tcontext: Context,\n\t\tcommand: CommandArgument<Registry, Key>,\n\t) => Promise<CommandResult<Registry, Key>>\n\texec<Dispatcher, const Key extends Operation<Registry>>(\n\t\texecutor: DynamicExecutor<Context, Registry, Dispatcher, Key>,\n\t): (\n\t\tcontext: Context,\n\t\tcommand: Dispatcher,\n\t) => Promise<CommandResult<Registry, Key>>\n\texec(\n\t\texecutor:\n\t\t\t| FixedExecutor<Context, Registry, Operation<Registry>>\n\t\t\t| DynamicExecutor<Context, Registry, unknown, Operation<Registry>>,\n\t) {\n\t\treturn async (context: Context, value: unknown) => {\n\t\t\tconst selected =\n\t\t\t\t'operation' in executor\n\t\t\t\t\t? { operation: executor.operation, value }\n\t\t\t\t\t: (() => {\n\t\t\t\t\t\t\tconst dispatcher = executor.dispatcher.parse(value)\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\toperation: executor.select(dispatcher),\n\t\t\t\t\t\t\t\tvalue: dispatcher,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})()\n\t\t\tif (\n\t\t\t\t!Object.prototype.hasOwnProperty.call(\n\t\t\t\t\tthis.#operations,\n\t\t\t\t\tselected.operation,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthrow new CommandConfigurationError()\n\t\t\t}\n\t\t\tconst operation = selected.operation as Operation<Registry>\n\t\t\tconst definition = this.#operations[operation]\n\t\t\tconst command = definition.command.parse(\n\t\t\t\tselected.value,\n\t\t\t) as CommandInput<Registry, Operation<Registry>>\n\t\t\tconst parseResult = (result: unknown) =>\n\t\t\t\tdefinition.result.parse(result) as CommandResult<\n\t\t\t\t\tRegistry,\n\t\t\t\t\tOperation<Registry>\n\t\t\t\t>\n\t\t\tconst handler = executor.handler as (\n\t\t\t\tcontext: Context,\n\t\t\t\tcommand: CommandInput<Registry, Operation<Registry>>,\n\t\t\t) => MaybePromise<CommandHandlerResult<Registry, Operation<Registry>>>\n\t\t\treturn this.#execute({\n\t\t\t\tcontext,\n\t\t\t\toperation,\n\t\t\t\tdefinition,\n\t\t\t\tcommand,\n\t\t\t\tparseResult,\n\t\t\t\trunUnparsed: async (contextOverride?: Context) =>\n\t\t\t\t\thandler(contextOverride ?? context, command),\n\t\t\t\trun: async (contextOverride?: Context) =>\n\t\t\t\t\tparseResult(await handler(contextOverride ?? context, command)),\n\t\t\t})\n\t\t}\n\t}\n}\n"],"mappings":"AA0FA,IAAM,EAAN,cAAwC,KAAM,CAC7C,KAAgB,mCAChB,KAAgB,4BAEhB,aAAc,CACb,MAAM,kDAAkD,CACzD,CACD,EAGa,EAAb,KAAsE,CACrE,GACA,GAEA,YAAY,EAGT,CACF,KAAKA,GAAc,EAAc,WACjC,KAAKC,GAAW,EAAc,OAC/B,CAcA,KACC,EAGC,CACD,OAAO,MAAO,EAAkB,IAAmB,CAClD,IAAM,EACL,cAAe,EACZ,CAAE,UAAW,EAAS,UAAW,OAAM,OAChC,CACP,IAAM,EAAa,EAAS,WAAW,MAAM,CAAK,EAClD,MAAO,CACN,UAAW,EAAS,OAAO,CAAU,EACrC,MAAO,CACR,CACD,EAAA,CAAG,EACN,GACC,CAAC,OAAO,UAAU,eAAe,KAChC,KAAKD,GACL,EAAS,SACV,EAEA,MAAM,IAAI,EAEX,IAAM,EAAY,EAAS,UACrB,EAAa,KAAKA,GAAY,GAC9B,EAAU,EAAW,QAAQ,MAClC,EAAS,KACV,EACM,EAAe,GACpB,EAAW,OAAO,MAAM,CAAM,EAIzB,EAAU,EAAS,QAIzB,OAAO,KAAKC,GAAS,CACpB,UACA,YACA,aACA,UACA,cACA,YAAa,KAAO,IACnB,EAAQ,GAAmB,EAAS,CAAO,EAC5C,IAAK,KAAO,IACX,EAAY,MAAM,EAAQ,GAAmB,EAAS,CAAO,CAAC,CAChE,CAAC,CACF,CACD,CACD"}
@@ -0,0 +1,74 @@
1
+ import { CommandHandlerResult, CommandInput, Parseable, SchemaInput } from "./modules/command/command.mjs";
2
+ import { AnyCommandMiddleware, AuditEntryInput, AuditedOperation, AuditedRegistry } from "./client.mjs";
3
+ //#region src/components/foundation/operation.d.ts
4
+ type MaybePromise<T> = T | Promise<T>;
5
+ type CommandPreparation<Result> = {
6
+ kind: 'replay';
7
+ result: unknown;
8
+ } | {
9
+ kind: 'execute';
10
+ complete?: (result: Result) => MaybePromise<void>;
11
+ };
12
+ type Parsed<S extends Parseable<unknown>> = ReturnType<S['parse']>;
13
+ declare const extension: unique symbol;
14
+ declare const hostContext: unique symbol;
15
+ type OperationHostContext<Context> = {
16
+ readonly [hostContext]?: (context: Context) => void;
17
+ };
18
+ type OperationExtension<Definition> = Definition extends {
19
+ readonly [extension]?: infer Extension;
20
+ } ? Extension : unknown;
21
+ type MiddlewareInput<Context, Input, Output, Extension> = {
22
+ operation: string;
23
+ context: Context;
24
+ command: Input;
25
+ next: keyof Extension extends never ? (options?: {
26
+ context?: Extension;
27
+ }) => Promise<Output> : (options: {
28
+ context: Extension;
29
+ }) => Promise<Output>;
30
+ };
31
+ type LocalMiddleware<C, I, O, E> = (input: MiddlewareInput<C, I, O, E>) => Promise<O>;
32
+ type AuditFor<Aggregate extends string> = Omit<AuditEntryInput, 'classification' | 'aggregate'> & {
33
+ aggregate: {
34
+ type: Aggregate;
35
+ id: string;
36
+ };
37
+ };
38
+ type TypedOperation<Context, Input extends Parseable<unknown>, Output extends Parseable<unknown>, Aggregates extends readonly string[], Extension> = OperationHostContext<Context> & {
39
+ readonly command: Input;
40
+ readonly result: Output;
41
+ readonly classification: string;
42
+ readonly permission?: string;
43
+ /** Called after permission, inside observation; returns invocation-local hooks. */
44
+ readonly prepare?: (context: Context, command: Parsed<Input>) => MaybePromise<CommandPreparation<Parsed<Output>>>;
45
+ /** Validates stored outputs when result transforms accept a different input shape. */
46
+ readonly replayResult?: Parseable<Parsed<Output>>;
47
+ readonly aggregates?: Aggregates;
48
+ readonly guard?: (context: Context & Extension, command: Parsed<Input>) => MaybePromise<void>;
49
+ readonly audit: (resolution: {
50
+ command: Parsed<Input>;
51
+ result: Parsed<Output>;
52
+ }, context: Context) => MaybePromise<AuditFor<Aggregates[number]> | null>;
53
+ readonly [extension]?: Extension;
54
+ } & (keyof Extension extends never ? {
55
+ readonly middleware?: readonly LocalMiddleware<Context, Parsed<Input>, SchemaInput<Output>, Extension>[];
56
+ } : {
57
+ readonly middleware: readonly [LocalMiddleware<Context, Parsed<Input>, SchemaInput<Output>, Extension>, ...LocalMiddleware<Context & Extension, Parsed<Input>, SchemaInput<Output>, Record<never, never>>[]];
58
+ });
59
+ type RegistryInput<Context, Operations extends AuditedRegistry> = { [Key in Extract<keyof Operations, string>]: {
60
+ operation: Key;
61
+ definition: Operations[Key];
62
+ context: Context;
63
+ command: CommandInput<Operations, Key>;
64
+ next: () => Promise<CommandHandlerResult<Operations, Key>>;
65
+ }; }[Extract<keyof Operations, string>];
66
+ type StoredOperation<Context, Input extends Parseable<unknown>, Output extends Parseable<unknown>, Aggregates extends readonly string[], Extension> = Omit<TypedOperation<Context, Input, Output, Aggregates, Extension>, 'middleware'> & Pick<AuditedOperation, 'middleware'>;
67
+ /** Binds host context once; each operation infers its own parsed schemas. */
68
+ declare function operationFactory<Context, Extension extends Record<string, unknown> = Record<never, never>>(): {
69
+ operation<const Input extends Parseable<unknown>, const Output extends Parseable<unknown>, const Aggregates extends readonly string[] = readonly string[]>(definition: TypedOperation<Context, Input, Output, Aggregates, Extension>): StoredOperation<Context, Input, Output, Aggregates, Extension>;
70
+ registryMiddleware<const Operations extends AuditedRegistry>(operations: Operations & { readonly [Key in keyof Operations]: OperationHostContext<Context>; }, middleware: (input: RegistryInput<Context, Operations>) => Promise<unknown>): AnyCommandMiddleware;
71
+ };
72
+ //#endregion
73
+ export { CommandPreparation, OperationExtension, OperationHostContext, TypedOperation, operationFactory };
74
+ //# sourceMappingURL=operation.d.mts.map
@@ -0,0 +1,2 @@
1
+ function e(){return{operation(e){return e},registryMiddleware(e,n){function r(t){return Object.prototype.hasOwnProperty.call(e,t.operation)&&e[t.operation]===t.definition}return e=>{if(!r(e))throw new t(e.operation);return n(e)}}}}var t=class extends Error{code=`COMMAND_MIDDLEWARE_REGISTRY_MISMATCH`;constructor(e){super(`Typed middleware does not own the definition of operation "${e}"`),this.name=`CommandMiddlewareRegistryError`}};export{e as operationFactory};
2
+ //# sourceMappingURL=operation.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation.mjs","names":[],"sources":["../../../src/components/foundation/operation.ts"],"sourcesContent":["import type {\n\tAuditEntryInput,\n\tAuditedRegistry,\n\tAuditedOperation,\n\tAnyCommandMiddleware,\n} from './client'\nimport type {\n\tCommandInput,\n\tCommandHandlerResult,\n\tParseable,\n\tSchemaInput,\n} from './modules/command/command'\n\ntype MaybePromise<T> = T | Promise<T>\nexport type CommandPreparation<Result> =\n\t| { kind: 'replay'; result: unknown }\n\t| { kind: 'execute'; complete?: (result: Result) => MaybePromise<void> }\ntype Parsed<S extends Parseable<unknown>> = ReturnType<S['parse']>\ndeclare const extension: unique symbol\ndeclare const hostContext: unique symbol\nexport type OperationHostContext<Context> = {\n\treadonly [hostContext]?: (context: Context) => void\n}\nexport type OperationExtension<Definition> = Definition extends {\n\treadonly [extension]?: infer Extension\n}\n\t? Extension\n\t: unknown\n\ntype MiddlewareInput<Context, Input, Output, Extension> = {\n\toperation: string\n\tcontext: Context\n\tcommand: Input\n\tnext: keyof Extension extends never\n\t\t? (options?: { context?: Extension }) => Promise<Output>\n\t\t: (options: { context: Extension }) => Promise<Output>\n}\ntype LocalMiddleware<C, I, O, E> = (\n\tinput: MiddlewareInput<C, I, O, E>,\n) => Promise<O>\ntype AuditFor<Aggregate extends string> = Omit<\n\tAuditEntryInput,\n\t'classification' | 'aggregate'\n> & {\n\taggregate: { type: Aggregate; id: string }\n}\nexport type TypedOperation<\n\tContext,\n\tInput extends Parseable<unknown>,\n\tOutput extends Parseable<unknown>,\n\tAggregates extends readonly string[],\n\tExtension,\n> = OperationHostContext<Context> & {\n\treadonly command: Input\n\treadonly result: Output\n\treadonly classification: string\n\treadonly permission?: string\n\t/** Called after permission, inside observation; returns invocation-local hooks. */\n\treadonly prepare?: (\n\t\tcontext: Context,\n\t\tcommand: Parsed<Input>,\n\t) => MaybePromise<CommandPreparation<Parsed<Output>>>\n\t/** Validates stored outputs when result transforms accept a different input shape. */\n\treadonly replayResult?: Parseable<Parsed<Output>>\n\treadonly aggregates?: Aggregates\n\treadonly guard?: (\n\t\tcontext: Context & Extension,\n\t\tcommand: Parsed<Input>,\n\t) => MaybePromise<void>\n\treadonly audit: (\n\t\tresolution: { command: Parsed<Input>; result: Parsed<Output> },\n\t\tcontext: Context,\n\t) => MaybePromise<AuditFor<Aggregates[number]> | null>\n\treadonly [extension]?: Extension\n} & (keyof Extension extends never\n\t\t? {\n\t\t\t\treadonly middleware?: readonly LocalMiddleware<\n\t\t\t\t\tContext,\n\t\t\t\t\tParsed<Input>,\n\t\t\t\t\tSchemaInput<Output>,\n\t\t\t\t\tExtension\n\t\t\t\t>[]\n\t\t\t}\n\t\t: {\n\t\t\t\treadonly middleware: readonly [\n\t\t\t\t\tLocalMiddleware<\n\t\t\t\t\t\tContext,\n\t\t\t\t\t\tParsed<Input>,\n\t\t\t\t\t\tSchemaInput<Output>,\n\t\t\t\t\t\tExtension\n\t\t\t\t\t>,\n\t\t\t\t\t...LocalMiddleware<\n\t\t\t\t\t\tContext & Extension,\n\t\t\t\t\t\tParsed<Input>,\n\t\t\t\t\t\tSchemaInput<Output>,\n\t\t\t\t\t\tRecord<never, never>\n\t\t\t\t\t>[],\n\t\t\t\t]\n\t\t\t})\n\ntype RegistryInput<Context, Operations extends AuditedRegistry> = {\n\t[Key in Extract<keyof Operations, string>]: {\n\t\toperation: Key\n\t\tdefinition: Operations[Key]\n\t\tcontext: Context\n\t\tcommand: CommandInput<Operations, Key>\n\t\tnext: () => Promise<CommandHandlerResult<Operations, Key>>\n\t}\n}[Extract<keyof Operations, string>]\n\ntype StoredOperation<\n\tContext,\n\tInput extends Parseable<unknown>,\n\tOutput extends Parseable<unknown>,\n\tAggregates extends readonly string[],\n\tExtension,\n> = Omit<\n\tTypedOperation<Context, Input, Output, Aggregates, Extension>,\n\t'middleware'\n> &\n\tPick<AuditedOperation, 'middleware'>\n\n/** Binds host context once; each operation infers its own parsed schemas. */\nexport function operationFactory<\n\tContext,\n\tExtension extends Record<string, unknown> = Record<never, never>,\n>() {\n\treturn {\n\t\toperation<\n\t\t\tconst Input extends Parseable<unknown>,\n\t\t\tconst Output extends Parseable<unknown>,\n\t\t\tconst Aggregates extends readonly string[] = readonly string[],\n\t\t>(\n\t\t\tdefinition: TypedOperation<\n\t\t\t\tContext,\n\t\t\t\tInput,\n\t\t\t\tOutput,\n\t\t\t\tAggregates,\n\t\t\t\tExtension\n\t\t\t>,\n\t\t): StoredOperation<Context, Input, Output, Aggregates, Extension> {\n\t\t\t// The kernel selects this definition and parses its input before\n\t\t\t// invoking the heterogeneous middleware registry. Erase only that\n\t\t\t// storage seam; callbacks were checked against these schemas above.\n\t\t\treturn definition as StoredOperation<\n\t\t\t\tContext,\n\t\t\t\tInput,\n\t\t\t\tOutput,\n\t\t\t\tAggregates,\n\t\t\t\tExtension\n\t\t\t>\n\t\t},\n\t\tregistryMiddleware<const Operations extends AuditedRegistry>(\n\t\t\toperations: Operations & { readonly [Key in keyof Operations]: OperationHostContext<Context> },\n\t\t\tmiddleware: (\n\t\t\t\tinput: RegistryInput<Context, Operations>,\n\t\t\t) => Promise<unknown>,\n\t\t): AnyCommandMiddleware {\n\t\t\tfunction ownsDefinition(\n\t\t\t\tinput: Parameters<AnyCommandMiddleware>[0],\n\t\t\t): input is Parameters<AnyCommandMiddleware>[0] &\n\t\t\t\tRegistryInput<Context, Operations> {\n\t\t\t\treturn (\n\t\t\t\t\tObject.prototype.hasOwnProperty.call(\n\t\t\t\t\t\toperations,\n\t\t\t\t\t\tinput.operation,\n\t\t\t\t\t) && operations[input.operation] === input.definition\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn (input) => {\n\t\t\t\tif (!ownsDefinition(input)) {\n\t\t\t\t\tthrow new CommandMiddlewareRegistryError(input.operation)\n\t\t\t\t}\n\t\t\t\treturn middleware(input)\n\t\t\t}\n\t\t},\n\t}\n}\n\nclass CommandMiddlewareRegistryError extends Error {\n\treadonly code = 'COMMAND_MIDDLEWARE_REGISTRY_MISMATCH'\n\tconstructor(operation: string) {\n\t\tsuper(\n\t\t\t`Typed middleware does not own the definition of operation \"${operation}\"`,\n\t\t)\n\t\tthis.name = 'CommandMiddlewareRegistryError'\n\t}\n}\n"],"mappings":"AA2HA,SAAgB,GAGZ,CACH,MAAO,CACN,UAKC,EAOiE,CAIjE,OAAO,CAOR,EACA,mBACC,EACA,EAGuB,CACvB,SAAS,EACR,EAEmC,CACnC,OACC,OAAO,UAAU,eAAe,KAC/B,EACA,EAAM,SACP,GAAK,EAAW,EAAM,aAAe,EAAM,UAE7C,CACA,MAAQ,IAAU,CACjB,GAAI,CAAC,EAAe,CAAK,EACxB,MAAM,IAAI,EAA+B,EAAM,SAAS,EAEzD,OAAO,EAAW,CAAK,CACxB,CACD,CACD,CACD,CAEA,IAAM,EAAN,cAA6C,KAAM,CAClD,KAAgB,uCAChB,YAAY,EAAmB,CAC9B,MACC,8DAA8D,EAAU,EACzE,EACA,KAAK,KAAO,gCACb,CACD"}
@@ -1,4 +1,4 @@
1
- import { ConvexValidatorFromZod, zid } from "convex-helpers/server/zod4";
1
+ import { ConvexValidatorFromZod, convexToZod, zid, zid as zid$1, zodToConvex } from "convex-helpers/server/zod4";
2
2
  import { TableDefinition } from "convex/server";
3
3
  import { z } from "zod";
4
4
  import { GenericId } from "convex/values";
@@ -23,7 +23,7 @@ type TableBoundaryOptions<Fields extends Shape, ServerFields extends readonly Sh
23
23
  publicFields?: PublicFields;
24
24
  };
25
25
  /** Defines one table and derives its storage, document, write, and DTO boundaries. */
26
- declare function zodTable<Table extends string, Fields extends Shape, const ServerFields extends readonly ShapeKey<Fields>[] = readonly [], const CommandFields extends readonly ShapeKey<Fields>[] = readonly [], const PublicFields extends readonly ShapeKey<Fields & TimestampShape>[] = readonly []>(tableName: Table, fields: (id: typeof zid) => Fields, options?: TableBoundaryOptions<Fields, ServerFields, CommandFields, PublicFields>): {
26
+ declare function zodTable<Table extends string, Fields extends Shape, const ServerFields extends readonly ShapeKey<Fields>[] = readonly [], const CommandFields extends readonly ShapeKey<Fields>[] = readonly [], const PublicFields extends readonly ShapeKey<Fields & TimestampShape>[] = readonly []>(tableName: Table, fields: (id: typeof zid$1) => Fields, options?: TableBoundaryOptions<Fields, ServerFields, CommandFields, PublicFields>): {
27
27
  tableName: Table;
28
28
  schema: z.ZodObject<((keyof Fields & "_creationTime") | (keyof Fields & "_id") extends never ? (Fields & {
29
29
  createdAt: z.ZodOptional<z.ZodNumber>;
@@ -118,7 +118,7 @@ type TenantShape = typeof tenantShape;
118
118
  * trigger forbids reassigning it. Expose it via publicFields only when a DTO
119
119
  * genuinely needs it.
120
120
  */
121
- declare function tenantTable<Table extends string, Fields extends Shape, const ServerFields extends readonly ShapeKey<Fields>[] = readonly [], const CommandFields extends readonly ShapeKey<Fields>[] = readonly [], const PublicFields extends readonly ShapeKey<Fields & TenantShape & TimestampShape>[] = readonly []>(tableName: Table, fields: (id: typeof zid) => Fields, options?: TableBoundaryOptions<Fields & TenantShape, readonly (ServerFields[number] | 'tenant')[], CommandFields, PublicFields>): ReturnType<typeof zodTable<Table, Fields & TenantShape, readonly (ServerFields[number] | 'tenant')[], CommandFields, PublicFields>>;
121
+ declare function tenantTable<Table extends string, Fields extends Shape, const ServerFields extends readonly ShapeKey<Fields>[] = readonly [], const CommandFields extends readonly ShapeKey<Fields>[] = readonly [], const PublicFields extends readonly ShapeKey<Fields & TenantShape & TimestampShape>[] = readonly []>(tableName: Table, fields: (id: typeof zid$1) => Fields, options?: TableBoundaryOptions<Fields & TenantShape, readonly (ServerFields[number] | 'tenant')[], CommandFields, PublicFields>): ReturnType<typeof zodTable<Table, Fields & TenantShape, readonly (ServerFields[number] | 'tenant')[], CommandFields, PublicFields>>;
122
122
  /**
123
123
  * Merges per-module table maps into the application schema map, rejecting
124
124
  * duplicate table names across modules — the module-registry combinator for
@@ -171,5 +171,5 @@ declare function zodVariantTable<Table extends string, Schema extends z.ZodType>
171
171
  declare function jsonSafeZid<Table extends string>(tableName: Table): z.ZodType<GenericId<Table>, string>;
172
172
  type ZodObjectOmit<Schema extends z.ZodObject<any, any>, OmitMask extends z.util.Mask<keyof Schema['shape']>> = Schema extends z.ZodObject<infer ObjectShape, infer Config> ? z.ZodObject<z.util.Flatten<Omit<ObjectShape, Extract<keyof ObjectShape, keyof OmitMask>>>, Config> : never;
173
173
  //#endregion
174
- export { TIMESTAMP_FIELDS, TableBoundaryOptions, createModule, jsonSafeZid, paginated, tenantTable, zodTable, zodVariantTable };
174
+ export { TIMESTAMP_FIELDS, TableBoundaryOptions, convexToZod, createModule, jsonSafeZid, paginated, tenantTable, zid, zodTable, zodToConvex, zodVariantTable };
175
175
  //# sourceMappingURL=zod-table.d.mts.map
@@ -1,2 +1,2 @@
1
- import{convexToZod as e,zid as t,zodToConvex as n}from"convex-helpers/server/zod4";import{defineTable as r,paginationOptsValidator as i}from"convex/server";import{z as a}from"zod";const o={createdAt:a.number().optional(),updatedAt:a.number().optional(),archivedAt:a.number().optional()},s=[`createdAt`,`updatedAt`,`archivedAt`];function c(e,i,c={}){let l={...i(t),...o},u=a.object(l).strict(),d=u.extend({_id:t(e),_creationTime:a.number()}),f=a.object(g(l,[...s,...c.serverFields??[]])).strict(),p=f.partial(),_=a.object(h(l,c.commandFields??[])).strict(),v=a.object(h(l,c.publicFields??[])).strict(),y=e=>v.parse(Object.fromEntries(Object.keys(v.shape).map(t=>[t,e[t]])));function b(e){return e?f.omit(e):f}function x(e){return e?p.omit(e):p}return{tableName:e,schema:d,storage:u,insertSchema:f,updateSchema:p,commandInput:_,publicDto:v,toPublicDto:y,table:r(n(u)),insert:b,update:x,tools:{insert:_,update:a.object({data:_.partial(),id:m(e)}).strict(),id:a.object({id:m(e)}).strict()}}}const l={tenant:a.string().min(1)};function u(e,t,n={}){return c(e,e=>({...t(e),...l}),{...n,serverFields:[...n.serverFields??[],`tenant`]})}function d(...e){let t={};for(let n of e)for(let[e,r]of Object.entries(n)){if(e in t)throw Error(`Table "${e}" is declared by more than one module`);t[e]=r}return t}function f(t){return{args:{paginationOpts:e(i)},result:a.object({page:a.array(t),isDone:a.boolean(),continueCursor:a.string(),splitCursor:a.string().nullable().optional(),pageStatus:a.enum([`SplitRecommended`,`SplitRequired`]).nullable().optional()})}}function p(e,t){return{tableName:e,storage:t,insertSchema:t,table:r(n(t))}}function m(e){return a.string().describe(`Convex document id for table "${e}"`)}function h(e,t){return Object.fromEntries(t.map(t=>[t,e[t]]))}function g(e,t){let n=new Set(t);return Object.fromEntries(Object.entries(e).filter(([e])=>!n.has(e)))}export{s as TIMESTAMP_FIELDS,d as createModule,m as jsonSafeZid,f as paginated,u as tenantTable,c as zodTable,p as zodVariantTable};
1
+ import{convexToZod as e,convexToZod as t,zid as n,zid as r,zodToConvex as i,zodToConvex as a}from"convex-helpers/server/zod4";import{defineTable as o,paginationOptsValidator as s}from"convex/server";import{z as c}from"zod";const l={createdAt:c.number().optional(),updatedAt:c.number().optional(),archivedAt:c.number().optional()},u=[`createdAt`,`updatedAt`,`archivedAt`];function d(e,t,n={}){let i={...t(r),...l},s=c.object(i).strict(),d=s.extend({_id:r(e),_creationTime:c.number()}),f=c.object(y(i,[...u,...n.serverFields??[]])).strict(),p=f.partial(),m=c.object(v(i,n.commandFields??[])).strict(),h=c.object(v(i,n.publicFields??[])).strict(),g=e=>h.parse(Object.fromEntries(Object.keys(h.shape).map(t=>[t,e[t]])));function b(e){return e?f.omit(e):f}function x(e){return e?p.omit(e):p}return{tableName:e,schema:d,storage:s,insertSchema:f,updateSchema:p,commandInput:m,publicDto:h,toPublicDto:g,table:o(a(s)),insert:b,update:x,tools:{insert:m,update:c.object({data:m.partial(),id:_(e)}).strict(),id:c.object({id:_(e)}).strict()}}}const f={tenant:c.string().min(1)};function p(e,t,n={}){return d(e,e=>({...t(e),...f}),{...n,serverFields:[...n.serverFields??[],`tenant`]})}function m(...e){let t={};for(let n of e)for(let[e,r]of Object.entries(n)){if(e in t)throw Error(`Table "${e}" is declared by more than one module`);t[e]=r}return t}function h(e){return{args:{paginationOpts:t(s)},result:c.object({page:c.array(e),isDone:c.boolean(),continueCursor:c.string(),splitCursor:c.string().nullable().optional(),pageStatus:c.enum([`SplitRecommended`,`SplitRequired`]).nullable().optional()})}}function g(e,t){return{tableName:e,storage:t,insertSchema:t,table:o(a(t))}}function _(e){return c.string().describe(`Convex document id for table "${e}"`)}function v(e,t){return Object.fromEntries(t.map(t=>[t,e[t]]))}function y(e,t){let n=new Set(t);return Object.fromEntries(Object.entries(e).filter(([e])=>!n.has(e)))}export{u as TIMESTAMP_FIELDS,e as convexToZod,m as createModule,_ as jsonSafeZid,h as paginated,p as tenantTable,n as zid,d as zodTable,i as zodToConvex,g as zodVariantTable};
2
2
  //# sourceMappingURL=zod-table.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"zod-table.mjs","names":[],"sources":["../src/zod-table.ts"],"sourcesContent":["import {\n\ttype ConvexValidatorFromZod,\n\tconvexToZod,\n\tzid,\n\tzodToConvex,\n} from 'convex-helpers/server/zod4'\nimport {\n\tdefineTable,\n\tpaginationOptsValidator,\n\ttype TableDefinition,\n} from 'convex/server'\nimport type { GenericId } from 'convex/values'\nimport { z } from 'zod'\n\ntype Shape = Record<string, z.ZodType>\ntype ShapeKey<Fields extends Shape> = Extract<keyof Fields, string>\n\n/**\n * Opinionated lifecycle timestamps — every table has them, server-owned,\n * always excluded from insert/command boundaries. Maintained by the\n * `timestamps` trigger helper (see cvx-kit/triggers).\n */\nconst timestampShape = {\n\tcreatedAt: z.number().optional(),\n\tupdatedAt: z.number().optional(),\n\tarchivedAt: z.number().optional(),\n} satisfies Shape\n\ntype TimestampShape = typeof timestampShape\n\nexport const TIMESTAMP_FIELDS = [\n\t'createdAt',\n\t'updatedAt',\n\t'archivedAt',\n] as const\n\nexport type TableBoundaryOptions<\n\tFields extends Shape,\n\tServerFields extends readonly ShapeKey<Fields>[],\n\tCommandFields extends readonly ShapeKey<Fields>[],\n\tPublicFields extends readonly ShapeKey<Fields & TimestampShape>[],\n> = {\n\tserverFields?: ServerFields\n\tcommandFields?: CommandFields\n\tpublicFields?: PublicFields\n}\n\n/** Defines one table and derives its storage, document, write, and DTO boundaries. */\nexport function zodTable<\n\tTable extends string,\n\tFields extends Shape,\n\tconst ServerFields extends readonly ShapeKey<Fields>[] = readonly [],\n\tconst CommandFields extends readonly ShapeKey<Fields>[] = readonly [],\n\tconst PublicFields extends readonly ShapeKey<\n\t\tFields & TimestampShape\n\t>[] = readonly [],\n>(\n\ttableName: Table,\n\tfields: (id: typeof zid) => Fields,\n\toptions: TableBoundaryOptions<\n\t\tFields,\n\t\tServerFields,\n\t\tCommandFields,\n\t\tPublicFields\n\t> = {},\n) {\n\tconst shape = { ...fields(zid), ...timestampShape } as Fields &\n\t\tTimestampShape\n\tconst storage = z.object(shape).strict()\n\tconst schema = storage.extend({\n\t\t_id: zid(tableName),\n\t\t_creationTime: z.number(),\n\t})\n\tconst insertSchema = z\n\t\t.object(\n\t\t\tomitShape(shape, [\n\t\t\t\t...TIMESTAMP_FIELDS,\n\t\t\t\t...(options.serverFields ?? ([] as never)),\n\t\t\t] as never),\n\t\t)\n\t\t.strict() as z.ZodObject<\n\t\tOmit<Fields, ServerFields[number] | (typeof TIMESTAMP_FIELDS)[number]>\n\t>\n\tconst updateSchema = insertSchema.partial()\n\tconst commandInput = z\n\t\t.object(pickShape(shape, options.commandFields ?? ([] as never)))\n\t\t.strict()\n\tconst publicDto = z\n\t\t.object(pickShape(shape, options.publicFields ?? ([] as never)))\n\t\t.strict()\n\tconst toPublicDto = (row: z.input<typeof storage>) =>\n\t\tpublicDto.parse(\n\t\t\tObject.fromEntries(\n\t\t\t\tObject.keys(publicDto.shape).map((key) => [\n\t\t\t\t\tkey,\n\t\t\t\t\t(row as Record<string, unknown>)[key],\n\t\t\t\t]),\n\t\t\t),\n\t\t)\n\n\ttype InsertSchema = typeof insertSchema\n\ttype UpdateSchema = typeof updateSchema\n\ttype InsertMask = z.util.Mask<keyof InsertSchema['shape']>\n\ttype UpdateMask = z.util.Mask<keyof UpdateSchema['shape']>\n\n\tfunction insert(): InsertSchema\n\tfunction insert<OmitMask extends InsertMask>(\n\t\tomit: OmitMask,\n\t): ZodObjectOmit<InsertSchema, OmitMask>\n\tfunction insert(omit?: InsertMask) {\n\t\treturn omit ? insertSchema.omit(omit as never) : insertSchema\n\t}\n\n\tfunction update(): UpdateSchema\n\tfunction update<OmitMask extends UpdateMask>(\n\t\tomit: OmitMask,\n\t): ZodObjectOmit<UpdateSchema, OmitMask>\n\tfunction update(omit?: UpdateMask) {\n\t\treturn omit ? updateSchema.omit(omit as never) : updateSchema\n\t}\n\n\treturn {\n\t\ttableName,\n\t\tschema,\n\t\tstorage,\n\t\tinsertSchema,\n\t\tupdateSchema,\n\t\tcommandInput,\n\t\tpublicDto,\n\t\ttoPublicDto,\n\t\ttable: defineTable(zodToConvex(storage)) as TableDefinition<\n\t\t\tConvexValidatorFromZod<typeof storage, 'required'>\n\t\t>,\n\t\tinsert,\n\t\tupdate,\n\t\ttools: {\n\t\t\t// jsonSafeZid (not zid): tool masks feed generated JSON schemas, so\n\t\t\t// ids must present as plain strings while keeping the Id<...> type.\n\t\t\tinsert: commandInput,\n\t\t\tupdate: z\n\t\t\t\t.object({ data: commandInput.partial(), id: jsonSafeZid(tableName) })\n\t\t\t\t.strict(),\n\t\t\tid: z.object({ id: jsonSafeZid(tableName) }).strict(),\n\t\t},\n\t}\n}\n\nconst tenantShape = {\n\ttenant: z.string().min(1),\n} satisfies Shape\n\ntype TenantShape = typeof tenantShape\n\n/**\n * A zodTable whose rows are tenant-owned: injects the server-owned `tenant`\n * field (the tenancy boundary — see cvx-kit/tenancy). Like timestamps, it is\n * excluded from insert/update/command boundaries; handlers stamp it from\n * ctx.tenant, row-level security matches on it, and the tenantOwnership\n * trigger forbids reassigning it. Expose it via publicFields only when a DTO\n * genuinely needs it.\n */\nexport function tenantTable<\n\tTable extends string,\n\tFields extends Shape,\n\tconst ServerFields extends readonly ShapeKey<Fields>[] = readonly [],\n\tconst CommandFields extends readonly ShapeKey<Fields>[] = readonly [],\n\tconst PublicFields extends readonly ShapeKey<\n\t\tFields & TenantShape & TimestampShape\n\t>[] = readonly [],\n>(\n\ttableName: Table,\n\tfields: (id: typeof zid) => Fields,\n\toptions: TableBoundaryOptions<\n\t\tFields & TenantShape,\n\t\treadonly (ServerFields[number] | 'tenant')[],\n\t\tCommandFields,\n\t\tPublicFields\n\t> = {},\n) {\n\treturn zodTable(\n\t\ttableName,\n\t\t(id) => ({ ...fields(id), ...tenantShape }),\n\t\t{\n\t\t\t...options,\n\t\t\tserverFields: [\n\t\t\t\t...((options.serverFields ?? []) as readonly (\n\t\t\t\t\t| ServerFields[number]\n\t\t\t\t\t| 'tenant'\n\t\t\t\t)[]),\n\t\t\t\t'tenant',\n\t\t\t] as never,\n\t\t} as never,\n\t) as ReturnType<\n\t\ttypeof zodTable<\n\t\t\tTable,\n\t\t\tFields & TenantShape,\n\t\t\treadonly (ServerFields[number] | 'tenant')[],\n\t\t\tCommandFields,\n\t\t\tPublicFields\n\t\t>\n\t>\n}\n\n/**\n * Merges per-module table maps into the application schema map, rejecting\n * duplicate table names across modules — the module-registry combinator for\n * domain/table.ts: `defineSchema(createModule(catalogTables, salesTables))`.\n */\nexport function createModule<\n\tconst Maps extends readonly Record<string, TableDefinition<any>>[],\n>(...maps: Maps): UnionToIntersection<Maps[number]> {\n\tconst combined: Record<string, TableDefinition<any>> = {}\n\tfor (const map of maps) {\n\t\tfor (const [tableName, definition] of Object.entries(map)) {\n\t\t\tif (tableName in combined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Table \"${tableName}\" is declared by more than one module`,\n\t\t\t\t)\n\t\t\t}\n\t\t\tcombined[tableName] = definition\n\t\t}\n\t}\n\treturn combined as UnionToIntersection<Maps[number]>\n}\n\ntype UnionToIntersection<Union> = (\n\tUnion extends unknown\n\t\t? (member: Union) => void\n\t\t: never\n) extends (member: infer Intersection) => void\n\t? Intersection\n\t: never\n\n/**\n * Cursor-pagination boundary schemas bridged from Convex's own validators:\n * `args.paginationOpts` for function args, `result` for the page shape.\n * Pair with `include(...).paginate(opts)` (cvx-kit/auth). The result object\n * is deliberately non-strict — Convex may attach engine fields (splitCursor,\n * pageStatus); the page items themselves are validated against the dto.\n * Note: under row-level security, pages may be SHORTER than numItems when\n * rules reject rows mid-page; cursors remain correct.\n */\nexport function paginated<Dto extends z.ZodType>(dto: Dto) {\n\treturn {\n\t\targs: { paginationOpts: convexToZod(paginationOptsValidator) },\n\t\tresult: z.object({\n\t\t\tpage: z.array(dto),\n\t\t\tisDone: z.boolean(),\n\t\t\tcontinueCursor: z.string(),\n\t\t\tsplitCursor: z.string().nullable().optional(),\n\t\t\tpageStatus: z\n\t\t\t\t.enum(['SplitRecommended', 'SplitRequired'])\n\t\t\t\t.nullable()\n\t\t\t\t.optional(),\n\t\t}),\n\t}\n}\n\n/** Preserves discriminated-union storage while retaining the same table owner. */\nexport function zodVariantTable<Table extends string, Schema extends z.ZodType>(\n\ttableName: Table,\n\tstorage: Schema,\n) {\n\treturn {\n\t\ttableName,\n\t\tstorage,\n\t\tinsertSchema: storage,\n\t\ttable: defineTable(zodToConvex(storage)),\n\t}\n}\n\n/**\n * A zid that presents as a plain string in generated JSON schemas so\n * LLM-tool inputs stay primitive, while keeping the Id type at compile time.\n */\nexport function jsonSafeZid<Table extends string>(tableName: Table) {\n\treturn z\n\t\t.string()\n\t\t.describe(\n\t\t\t`Convex document id for table \"${tableName}\"`,\n\t\t) as unknown as z.ZodType<GenericId<Table>, string>\n}\n\ntype ZodObjectOmit<\n\tSchema extends z.ZodObject<any, any>,\n\tOmitMask extends z.util.Mask<keyof Schema['shape']>,\n> =\n\tSchema extends z.ZodObject<infer ObjectShape, infer Config>\n\t\t? z.ZodObject<\n\t\t\t\tz.util.Flatten<\n\t\t\t\t\tOmit<ObjectShape, Extract<keyof ObjectShape, keyof OmitMask>>\n\t\t\t\t>,\n\t\t\t\tConfig\n\t\t\t>\n\t\t: never\n\nfunction pickShape<\n\tFields extends Shape,\n\tconst Keys extends readonly ShapeKey<Fields>[],\n>(fields: Fields, keys: Keys) {\n\treturn Object.fromEntries(keys.map((key) => [key, fields[key]])) as Pick<\n\t\tFields,\n\t\tKeys[number]\n\t>\n}\n\nfunction omitShape<\n\tFields extends Shape,\n\tconst Keys extends readonly ShapeKey<Fields>[],\n>(fields: Fields, keys: Keys) {\n\tconst omitted = new Set<string>(keys)\n\treturn Object.fromEntries(\n\t\tObject.entries(fields).filter(([key]) => !omitted.has(key)),\n\t) as Omit<Fields, Keys[number]>\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"zod-table.mjs","names":["zid","zodToConvex","convexToZod"],"sources":["../src/zod-table.ts"],"sourcesContent":["import {\n\ttype ConvexValidatorFromZod,\n\tconvexToZod,\n\tzid,\n\tzodToConvex,\n} from 'convex-helpers/server/zod4'\nimport {\n\tdefineTable,\n\tpaginationOptsValidator,\n\ttype TableDefinition,\n} from 'convex/server'\nimport type { GenericId } from 'convex/values'\nimport { z } from 'zod'\n\n// Use this entry point when deriving validators from kit-owned schemas so\n// the producer and converter consult the same convex-helpers ID registry.\nexport { zid, zodToConvex, convexToZod } from 'convex-helpers/server/zod4'\n\ntype Shape = Record<string, z.ZodType>\ntype ShapeKey<Fields extends Shape> = Extract<keyof Fields, string>\n\n/**\n * Opinionated lifecycle timestamps — every table has them, server-owned,\n * always excluded from insert/command boundaries. Maintained by the\n * `timestamps` trigger helper (see cvx-kit/triggers).\n */\nconst timestampShape = {\n\tcreatedAt: z.number().optional(),\n\tupdatedAt: z.number().optional(),\n\tarchivedAt: z.number().optional(),\n} satisfies Shape\n\ntype TimestampShape = typeof timestampShape\n\nexport const TIMESTAMP_FIELDS = [\n\t'createdAt',\n\t'updatedAt',\n\t'archivedAt',\n] as const\n\nexport type TableBoundaryOptions<\n\tFields extends Shape,\n\tServerFields extends readonly ShapeKey<Fields>[],\n\tCommandFields extends readonly ShapeKey<Fields>[],\n\tPublicFields extends readonly ShapeKey<Fields & TimestampShape>[],\n> = {\n\tserverFields?: ServerFields\n\tcommandFields?: CommandFields\n\tpublicFields?: PublicFields\n}\n\n/** Defines one table and derives its storage, document, write, and DTO boundaries. */\nexport function zodTable<\n\tTable extends string,\n\tFields extends Shape,\n\tconst ServerFields extends readonly ShapeKey<Fields>[] = readonly [],\n\tconst CommandFields extends readonly ShapeKey<Fields>[] = readonly [],\n\tconst PublicFields extends readonly ShapeKey<\n\t\tFields & TimestampShape\n\t>[] = readonly [],\n>(\n\ttableName: Table,\n\tfields: (id: typeof zid) => Fields,\n\toptions: TableBoundaryOptions<\n\t\tFields,\n\t\tServerFields,\n\t\tCommandFields,\n\t\tPublicFields\n\t> = {},\n) {\n\tconst shape = { ...fields(zid), ...timestampShape } as Fields &\n\t\tTimestampShape\n\tconst storage = z.object(shape).strict()\n\tconst schema = storage.extend({\n\t\t_id: zid(tableName),\n\t\t_creationTime: z.number(),\n\t})\n\tconst insertSchema = z\n\t\t.object(\n\t\t\tomitShape(shape, [\n\t\t\t\t...TIMESTAMP_FIELDS,\n\t\t\t\t...(options.serverFields ?? ([] as never)),\n\t\t\t] as never),\n\t\t)\n\t\t.strict() as z.ZodObject<\n\t\tOmit<Fields, ServerFields[number] | (typeof TIMESTAMP_FIELDS)[number]>\n\t>\n\tconst updateSchema = insertSchema.partial()\n\tconst commandInput = z\n\t\t.object(pickShape(shape, options.commandFields ?? ([] as never)))\n\t\t.strict()\n\tconst publicDto = z\n\t\t.object(pickShape(shape, options.publicFields ?? ([] as never)))\n\t\t.strict()\n\tconst toPublicDto = (row: z.input<typeof storage>) =>\n\t\tpublicDto.parse(\n\t\t\tObject.fromEntries(\n\t\t\t\tObject.keys(publicDto.shape).map((key) => [\n\t\t\t\t\tkey,\n\t\t\t\t\t(row as Record<string, unknown>)[key],\n\t\t\t\t]),\n\t\t\t),\n\t\t)\n\n\ttype InsertSchema = typeof insertSchema\n\ttype UpdateSchema = typeof updateSchema\n\ttype InsertMask = z.util.Mask<keyof InsertSchema['shape']>\n\ttype UpdateMask = z.util.Mask<keyof UpdateSchema['shape']>\n\n\tfunction insert(): InsertSchema\n\tfunction insert<OmitMask extends InsertMask>(\n\t\tomit: OmitMask,\n\t): ZodObjectOmit<InsertSchema, OmitMask>\n\tfunction insert(omit?: InsertMask) {\n\t\treturn omit ? insertSchema.omit(omit as never) : insertSchema\n\t}\n\n\tfunction update(): UpdateSchema\n\tfunction update<OmitMask extends UpdateMask>(\n\t\tomit: OmitMask,\n\t): ZodObjectOmit<UpdateSchema, OmitMask>\n\tfunction update(omit?: UpdateMask) {\n\t\treturn omit ? updateSchema.omit(omit as never) : updateSchema\n\t}\n\n\treturn {\n\t\ttableName,\n\t\tschema,\n\t\tstorage,\n\t\tinsertSchema,\n\t\tupdateSchema,\n\t\tcommandInput,\n\t\tpublicDto,\n\t\ttoPublicDto,\n\t\ttable: defineTable(zodToConvex(storage)) as TableDefinition<\n\t\t\tConvexValidatorFromZod<typeof storage, 'required'>\n\t\t>,\n\t\tinsert,\n\t\tupdate,\n\t\ttools: {\n\t\t\t// jsonSafeZid (not zid): tool masks feed generated JSON schemas, so\n\t\t\t// ids must present as plain strings while keeping the Id<...> type.\n\t\t\tinsert: commandInput,\n\t\t\tupdate: z\n\t\t\t\t.object({ data: commandInput.partial(), id: jsonSafeZid(tableName) })\n\t\t\t\t.strict(),\n\t\t\tid: z.object({ id: jsonSafeZid(tableName) }).strict(),\n\t\t},\n\t}\n}\n\nconst tenantShape = {\n\ttenant: z.string().min(1),\n} satisfies Shape\n\ntype TenantShape = typeof tenantShape\n\n/**\n * A zodTable whose rows are tenant-owned: injects the server-owned `tenant`\n * field (the tenancy boundary — see cvx-kit/tenancy). Like timestamps, it is\n * excluded from insert/update/command boundaries; handlers stamp it from\n * ctx.tenant, row-level security matches on it, and the tenantOwnership\n * trigger forbids reassigning it. Expose it via publicFields only when a DTO\n * genuinely needs it.\n */\nexport function tenantTable<\n\tTable extends string,\n\tFields extends Shape,\n\tconst ServerFields extends readonly ShapeKey<Fields>[] = readonly [],\n\tconst CommandFields extends readonly ShapeKey<Fields>[] = readonly [],\n\tconst PublicFields extends readonly ShapeKey<\n\t\tFields & TenantShape & TimestampShape\n\t>[] = readonly [],\n>(\n\ttableName: Table,\n\tfields: (id: typeof zid) => Fields,\n\toptions: TableBoundaryOptions<\n\t\tFields & TenantShape,\n\t\treadonly (ServerFields[number] | 'tenant')[],\n\t\tCommandFields,\n\t\tPublicFields\n\t> = {},\n) {\n\treturn zodTable(\n\t\ttableName,\n\t\t(id) => ({ ...fields(id), ...tenantShape }),\n\t\t{\n\t\t\t...options,\n\t\t\tserverFields: [\n\t\t\t\t...((options.serverFields ?? []) as readonly (\n\t\t\t\t\t| ServerFields[number]\n\t\t\t\t\t| 'tenant'\n\t\t\t\t)[]),\n\t\t\t\t'tenant',\n\t\t\t] as never,\n\t\t} as never,\n\t) as ReturnType<\n\t\ttypeof zodTable<\n\t\t\tTable,\n\t\t\tFields & TenantShape,\n\t\t\treadonly (ServerFields[number] | 'tenant')[],\n\t\t\tCommandFields,\n\t\t\tPublicFields\n\t\t>\n\t>\n}\n\n/**\n * Merges per-module table maps into the application schema map, rejecting\n * duplicate table names across modules — the module-registry combinator for\n * domain/table.ts: `defineSchema(createModule(catalogTables, salesTables))`.\n */\nexport function createModule<\n\tconst Maps extends readonly Record<string, TableDefinition<any>>[],\n>(...maps: Maps): UnionToIntersection<Maps[number]> {\n\tconst combined: Record<string, TableDefinition<any>> = {}\n\tfor (const map of maps) {\n\t\tfor (const [tableName, definition] of Object.entries(map)) {\n\t\t\tif (tableName in combined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Table \"${tableName}\" is declared by more than one module`,\n\t\t\t\t)\n\t\t\t}\n\t\t\tcombined[tableName] = definition\n\t\t}\n\t}\n\treturn combined as UnionToIntersection<Maps[number]>\n}\n\ntype UnionToIntersection<Union> = (\n\tUnion extends unknown\n\t\t? (member: Union) => void\n\t\t: never\n) extends (member: infer Intersection) => void\n\t? Intersection\n\t: never\n\n/**\n * Cursor-pagination boundary schemas bridged from Convex's own validators:\n * `args.paginationOpts` for function args, `result` for the page shape.\n * Pair with `include(...).paginate(opts)` (cvx-kit/auth). The result object\n * is deliberately non-strict — Convex may attach engine fields (splitCursor,\n * pageStatus); the page items themselves are validated against the dto.\n * Note: under row-level security, pages may be SHORTER than numItems when\n * rules reject rows mid-page; cursors remain correct.\n */\nexport function paginated<Dto extends z.ZodType>(dto: Dto) {\n\treturn {\n\t\targs: { paginationOpts: convexToZod(paginationOptsValidator) },\n\t\tresult: z.object({\n\t\t\tpage: z.array(dto),\n\t\t\tisDone: z.boolean(),\n\t\t\tcontinueCursor: z.string(),\n\t\t\tsplitCursor: z.string().nullable().optional(),\n\t\t\tpageStatus: z\n\t\t\t\t.enum(['SplitRecommended', 'SplitRequired'])\n\t\t\t\t.nullable()\n\t\t\t\t.optional(),\n\t\t}),\n\t}\n}\n\n/** Preserves discriminated-union storage while retaining the same table owner. */\nexport function zodVariantTable<Table extends string, Schema extends z.ZodType>(\n\ttableName: Table,\n\tstorage: Schema,\n) {\n\treturn {\n\t\ttableName,\n\t\tstorage,\n\t\tinsertSchema: storage,\n\t\ttable: defineTable(zodToConvex(storage)),\n\t}\n}\n\n/**\n * A zid that presents as a plain string in generated JSON schemas so\n * LLM-tool inputs stay primitive, while keeping the Id type at compile time.\n */\nexport function jsonSafeZid<Table extends string>(tableName: Table) {\n\treturn z\n\t\t.string()\n\t\t.describe(\n\t\t\t`Convex document id for table \"${tableName}\"`,\n\t\t) as unknown as z.ZodType<GenericId<Table>, string>\n}\n\ntype ZodObjectOmit<\n\tSchema extends z.ZodObject<any, any>,\n\tOmitMask extends z.util.Mask<keyof Schema['shape']>,\n> =\n\tSchema extends z.ZodObject<infer ObjectShape, infer Config>\n\t\t? z.ZodObject<\n\t\t\t\tz.util.Flatten<\n\t\t\t\t\tOmit<ObjectShape, Extract<keyof ObjectShape, keyof OmitMask>>\n\t\t\t\t>,\n\t\t\t\tConfig\n\t\t\t>\n\t\t: never\n\nfunction pickShape<\n\tFields extends Shape,\n\tconst Keys extends readonly ShapeKey<Fields>[],\n>(fields: Fields, keys: Keys) {\n\treturn Object.fromEntries(keys.map((key) => [key, fields[key]])) as Pick<\n\t\tFields,\n\t\tKeys[number]\n\t>\n}\n\nfunction omitShape<\n\tFields extends Shape,\n\tconst Keys extends readonly ShapeKey<Fields>[],\n>(fields: Fields, keys: Keys) {\n\tconst omitted = new Set<string>(keys)\n\treturn Object.fromEntries(\n\t\tObject.entries(fields).filter(([key]) => !omitted.has(key)),\n\t) as Omit<Fields, Keys[number]>\n}\n"],"mappings":"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"}