cvx-kit 0.1.5 → 0.1.7

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.
Files changed (124) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +21 -3
  3. package/dist/agent-tools.d.mts +2 -1
  4. package/dist/agent-tools.mjs +1 -1
  5. package/dist/agent-tools.mjs.map +1 -1
  6. package/dist/components/approvals/audit.mjs +1 -1
  7. package/dist/components/foundation/client.d.mts +8 -7
  8. package/dist/components/foundation/client.mjs +1 -1
  9. package/dist/components/foundation/client.mjs.map +1 -1
  10. package/dist/components/foundation/modules/command/command.d.mts +50 -10
  11. package/dist/components/foundation/modules/command/command.mjs +1 -1
  12. package/dist/components/foundation/modules/command/command.mjs.map +1 -1
  13. package/dist/components/foundation/modules/command/lifecycle.d.mts +81 -0
  14. package/dist/components/foundation/modules/command/lifecycle.mjs +2 -0
  15. package/dist/components/foundation/modules/command/lifecycle.mjs.map +1 -0
  16. package/dist/components/foundation/modules/observability/observability.d.mts +8 -0
  17. package/dist/components/foundation/modules/observability/observability.mjs +1 -1
  18. package/dist/components/foundation/modules/observability/observability.mjs.map +1 -1
  19. package/dist/components/foundation/operation.d.mts +6 -6
  20. package/dist/components/foundation/operation.mjs.map +1 -1
  21. package/dist/contracts.d.mts +4 -0
  22. package/dist/contracts.mjs +1 -0
  23. package/dist/crud.d.mts +2 -2
  24. package/dist/crud.mjs.map +1 -1
  25. package/dist/effect.d.mts +12 -0
  26. package/dist/effect.mjs +1 -0
  27. package/dist/errors.d.mts +2 -1
  28. package/dist/errors.mjs +1 -1
  29. package/dist/errors.mjs.map +1 -1
  30. package/dist/idempotency.d.mts +2 -0
  31. package/dist/idempotency.mjs +1 -0
  32. package/dist/modules/contracts/contract.d.mts +70 -0
  33. package/dist/modules/contracts/contract.mjs +2 -0
  34. package/dist/modules/contracts/contract.mjs.map +1 -0
  35. package/dist/modules/contracts/convex.d.mts +22 -0
  36. package/dist/modules/contracts/convex.mjs +2 -0
  37. package/dist/modules/contracts/convex.mjs.map +1 -0
  38. package/dist/modules/contracts/errors.d.mts +44 -0
  39. package/dist/modules/contracts/errors.mjs +2 -0
  40. package/dist/modules/contracts/errors.mjs.map +1 -0
  41. package/dist/modules/contracts/exposure.d.mts +105 -0
  42. package/dist/modules/contracts/exposure.mjs +2 -0
  43. package/dist/modules/contracts/exposure.mjs.map +1 -0
  44. package/dist/modules/contracts/idempotency.d.mts +87 -0
  45. package/dist/modules/contracts/idempotency.mjs +2 -0
  46. package/dist/modules/contracts/idempotency.mjs.map +1 -0
  47. package/dist/modules/contracts/standard.d.mts +16 -0
  48. package/dist/modules/contracts/standard.mjs +2 -0
  49. package/dist/modules/contracts/standard.mjs.map +1 -0
  50. package/dist/modules/contracts/workflow.d.mts +167 -0
  51. package/dist/modules/contracts/workflow.mjs +2 -0
  52. package/dist/modules/contracts/workflow.mjs.map +1 -0
  53. package/dist/modules/effect/agent-tools.d.mts +2 -0
  54. package/dist/modules/effect/agent-tools.mjs +1 -0
  55. package/dist/modules/effect/api-runtime.d.mts +28 -0
  56. package/dist/modules/effect/api-runtime.mjs +2 -0
  57. package/dist/modules/effect/api-runtime.mjs.map +1 -0
  58. package/dist/modules/effect/api-zod.d.mts +35 -0
  59. package/dist/modules/effect/api-zod.mjs +2 -0
  60. package/dist/modules/effect/api-zod.mjs.map +1 -0
  61. package/dist/modules/effect/api.d.mts +21 -0
  62. package/dist/modules/effect/api.mjs +2 -0
  63. package/dist/modules/effect/api.mjs.map +1 -0
  64. package/dist/modules/effect/client.d.mts +11 -0
  65. package/dist/modules/effect/client.mjs +1 -0
  66. package/dist/modules/effect/command.d.mts +59 -0
  67. package/dist/modules/effect/command.mjs +2 -0
  68. package/dist/modules/effect/command.mjs.map +1 -0
  69. package/dist/modules/effect/crud.d.mts +785 -0
  70. package/dist/modules/effect/crud.mjs +2 -0
  71. package/dist/modules/effect/crud.mjs.map +1 -0
  72. package/dist/modules/effect/errors.d.mts +8 -0
  73. package/dist/modules/effect/errors.mjs +2 -0
  74. package/dist/modules/effect/errors.mjs.map +1 -0
  75. package/dist/modules/effect/foundation.d.mts +21 -0
  76. package/dist/modules/effect/foundation.mjs +2 -0
  77. package/dist/modules/effect/foundation.mjs.map +1 -0
  78. package/dist/modules/effect/idempotency.d.mts +31 -0
  79. package/dist/modules/effect/idempotency.mjs +2 -0
  80. package/dist/modules/effect/idempotency.mjs.map +1 -0
  81. package/dist/modules/effect/normalize.d.mts +10 -0
  82. package/dist/modules/effect/normalize.mjs +2 -0
  83. package/dist/modules/effect/normalize.mjs.map +1 -0
  84. package/dist/modules/effect/operation.d.mts +141 -0
  85. package/dist/modules/effect/operation.mjs +2 -0
  86. package/dist/modules/effect/operation.mjs.map +1 -0
  87. package/dist/modules/effect/query.d.mts +65 -0
  88. package/dist/modules/effect/query.mjs +2 -0
  89. package/dist/modules/effect/query.mjs.map +1 -0
  90. package/dist/modules/effect/schema.d.mts +33 -0
  91. package/dist/modules/effect/schema.mjs +2 -0
  92. package/dist/modules/effect/schema.mjs.map +1 -0
  93. package/dist/modules/effect/workflow.d.mts +77 -0
  94. package/dist/modules/effect/workflow.mjs +2 -0
  95. package/dist/modules/effect/workflow.mjs.map +1 -0
  96. package/dist/node_modules/@standard-schema/spec/dist/index.d.mts +79 -0
  97. package/dist/oxlint/architecture.d.mts +20 -0
  98. package/dist/oxlint/architecture.mjs +2 -0
  99. package/dist/oxlint/architecture.mjs.map +1 -0
  100. package/dist/oxlint/ast.d.mts +33 -0
  101. package/dist/oxlint/ast.mjs +2 -0
  102. package/dist/oxlint/ast.mjs.map +1 -0
  103. package/dist/oxlint/ownership.d.mts +62 -0
  104. package/dist/oxlint/ownership.mjs +2 -0
  105. package/dist/oxlint/ownership.mjs.map +1 -0
  106. package/dist/oxlint.d.mts +5 -2
  107. package/dist/oxlint.mjs +1 -1
  108. package/dist/oxlint.mjs.map +1 -1
  109. package/dist/workflow.d.mts +2 -0
  110. package/dist/workflow.mjs +1 -0
  111. package/docs/auth.md +65 -0
  112. package/docs/commands.md +118 -0
  113. package/docs/conventions.md +49 -21
  114. package/docs/domain-operations.md +615 -0
  115. package/docs/effect.md +384 -0
  116. package/docs/oxlint.md +74 -26
  117. package/docs/upgrading.md +13 -0
  118. package/package.json +37 -3
  119. package/scripts/convex-smoke-env.mjs +33 -0
  120. package/scripts/smoke-packed-components.mjs +9 -2
  121. package/scripts/smoke-packed-effect.mjs +439 -0
  122. package/scripts/test-packed-effect.mjs +457 -0
  123. package/scripts/test-packed-ids.mjs +1 -0
  124. package/scripts/test-packed-oxlint.mjs +24 -2
package/CHANGELOG.md CHANGED
@@ -12,6 +12,47 @@ are this file's matching section.
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ## [0.1.7] - 2026-10-07
16
+
17
+ ### Added
18
+
19
+ - Add optional Effect 4 command and query registries with cohesive ordinary,
20
+ Promise, generator, and function handlers, injected services, and scoped API
21
+ execution through native and custom Convex builders.
22
+ - Add asynchronous operation contracts, explicit schema adapters, and checked
23
+ transport for declared safe errors.
24
+ - Add injectable audited Effect CRUD, host-owned transactional idempotency
25
+ receipts, native durable workflow bindings, and selected domain operation tools.
26
+ - Document server-side declaration, composition, API consumption, receipts,
27
+ workflows, and tools with compile-checked public-package examples.
28
+
29
+ ### Fixed
30
+
31
+ - Preserve native validators, trusted authorization, RLS, triggers, audit, and
32
+ rollback across API, workflow, and tool integrations.
33
+ - Reconcile late workflow outcomes without allowing stale generations or late
34
+ completion to overwrite canceled runs.
35
+ - Validate retained receipt versions before fingerprints and replay, and require
36
+ current authorization before disclosing replayed results.
37
+
38
+ ## [0.1.6] - 2026-09-19
39
+
40
+ ### Added
41
+
42
+ - Add five opt-in consumer Oxlint rules for project structure, root wiring,
43
+ application orchestration, internal function ownership, and domain file
44
+ responsibilities. The packaged plugin now includes 20 rules.
45
+ - Check colocated test presence, runtime import cycles, and shared-code ownership.
46
+ Export `checkArchitecture` for standalone checks without copying architecture
47
+ test suites into applications. These static checks do not replace behavior tests.
48
+
49
+ ### Fixed
50
+
51
+ - Resolve tsconfig aliases when checking consumer import boundaries and reject
52
+ migration dependencies from application code.
53
+ - Enforce documented ownership of domain implementations, table aggregation,
54
+ query side effects, and component boundaries.
55
+
15
56
  ## [0.1.5] - 2026-09-18
16
57
 
17
58
  ### Fixed
package/README.md CHANGED
@@ -13,7 +13,7 @@ bun add cvx-kit
13
13
  # or: npm install cvx-kit
14
14
  ```
15
15
 
16
- Supported versions: Convex `^1.43.0`, Zod `^4.0.0`, Bun `1.3+`, and Node.js
16
+ Supported versions: Convex `^1.45.0`, Zod `^4.5.4`, Bun `1.3+`, and Node.js
17
17
  `24+` with npm `11+`. The library runtime is ESM-only.
18
18
 
19
19
  ## Toolchain
@@ -36,6 +36,8 @@ authored in [`src/docs/`](./src/docs/):
36
36
  - [`zod-table.md`](./src/docs/zod-table.md) — one zod shape per entity, masked into every boundary
37
37
  - [`auth.md`](./src/docs/auth.md) — auth-aware function constructors from injected policy
38
38
  - [`commands.md`](./src/docs/commands.md) — Foundation, the audited command protocol, observability
39
+ - [`effect.md`](./src/docs/effect.md) — opt-in Effect v4 registries, injected services, and shared API builders
40
+ - [`domain-operations.md`](./src/docs/domain-operations.md) — server domain/API examples, contracts, errors, receipts, workflow steps, and selected tools
39
41
  - [`triggers.md`](./src/docs/triggers.md) — trigger registry, timestamps/append-only/no-delete
40
42
  - [`approvals.md`](./src/docs/approvals.md) — declarative approval workflows
41
43
  - [`tenancy.md`](./src/docs/tenancy.md) — row-level security (roles) with opt-in multi-tenant isolation
@@ -171,6 +173,13 @@ itself owns zero tables and ships `executeResultBoundary` (typed failures
171
173
  only while the transaction has no effects; otherwise rethrow so Convex
172
174
  rolls back).
173
175
 
176
+ For cohesive command/query entries that include their handlers, use
177
+ [`cvx-kit/effect`](./src/docs/effect.md). Install Effect `>=4.0.1 <5` separately;
178
+ ordinary handlers, `Effect.gen`, and `Effect.fn` compose through the same
179
+ registry. Shared API builders provide invocation-local services and execute
180
+ the Effect. Existing Foundation declarations and Promise executors remain
181
+ supported, so domains can migrate independently.
182
+
174
183
  ### `cvx-kit/components/approvals` (component)
175
184
 
176
185
  Declarative approval workflows (decision / branch / mutation / action /
@@ -196,6 +205,11 @@ Load it in your application's `.oxlintrc.json` and select rules:
196
205
  {
197
206
  "jsPlugins": [{ "name": "cvx", "specifier": "cvx-kit/oxlint" }],
198
207
  "rules": {
208
+ "cvx/project-structure": "error",
209
+ "cvx/root-wiring-only": "error",
210
+ "cvx/application-orchestration": "error",
211
+ "cvx/internal-function-ownership": "error",
212
+ "cvx/domain-file-responsibilities": "error",
199
213
  "cvx/no-raw-builders": "error",
200
214
  "cvx/no-handwritten-references": "error",
201
215
  "cvx/domain-import-boundaries": "error",
@@ -217,8 +231,12 @@ Load it in your application's `.oxlintrc.json` and select rules:
217
231
 
218
232
  With Vite+, put those fields under `lint` in `vite.config.ts`. Each rule accepts
219
233
  `{ "convexDir": "path/to/convex" }` when your backend directory differs from
220
- `convex/`. Generated files and tests are excluded. The plugin exports its
221
- default plugin object, named `rules`, and the `RuleName` type. Verified with
234
+ `convex/`. Generated files and tests are excluded from source-code rules. `project-structure`
235
+ inspects the whole backend for directory ownership, colocated runnable tests, and
236
+ module cycles. Run Oxlint on the whole backend, including `convex.config.ts`;
237
+ changed-file-only lint cannot guarantee a project scan. No application-owned
238
+ architecture test framework is required. The plugin also exports `checkArchitecture()`
239
+ for tools that need project diagnostics directly, named `rules`, and `RuleName`. Verified with
222
240
  Oxlint 1.79.0 and Vite+ 0.3.0. See [rule coverage and limitations](./src/docs/oxlint.md)
223
241
  and the [migration from the initial 0.1.4 plugin](./src/docs/upgrading.md).
224
242
 
@@ -1,3 +1,4 @@
1
+ import { OperationExecutor, OperationPublicResult, OperationTool, OperationToolConverter, OperationToolDefinition, OperationToolJsonSchema, OperationToolOutcome, SelectedOperation, bindOperationExecutor, createOperationTools, operationToolDialect, selectOperation } from "./modules/contracts/exposure.mjs";
1
2
  import { z } from "zod";
2
3
  //#region src/agent-tools.d.ts
3
4
  /**
@@ -57,5 +58,5 @@ type AgentTools<Table extends ToolTable, Handlers extends AgentToolHandlers> = P
57
58
  handler: NonNullable<Handlers[Verb]>;
58
59
  }; }>;
59
60
  //#endregion
60
- export { AgentToolHandlers, AgentToolRecord, AgentTools, createAgentTools };
61
+ export { AgentToolHandlers, AgentToolRecord, AgentTools, type OperationExecutor, type OperationPublicResult, type OperationTool, type OperationToolConverter, type OperationToolDefinition, type OperationToolJsonSchema, type OperationToolOutcome, type SelectedOperation, bindOperationExecutor, createAgentTools, createOperationTools, operationToolDialect, selectOperation };
61
62
  //# sourceMappingURL=agent-tools.d.mts.map
@@ -1,2 +1,2 @@
1
- import{convexToZod as e}from"convex-helpers/server/zod4";import{paginationOptsValidator as t}from"convex/server";import{z as n}from"zod";const r=n.object({paginationOpts:e(t)}).strict();function i(e,t,n){let i=e.tableName,a={create:e.tools.insert,update:e.tools.update,archive:e.tools.id,get:e.tools.id,list:r},o={create:`Create a ${i} record`,update:`Update fields of an existing ${i} record`,archive:`Archive (soft-delete) a ${i} record`,get:`Fetch one ${i} record by id`,list:`List ${i} records, paginated`},s={};for(let e of[`create`,`update`,`archive`,`get`,`list`]){let r=t[e];r&&(s[`${i}_${e}`]={name:`${i}_${e}`,description:n?.descriptions?.[e]??o[e],args:a[e],handler:r})}return s}export{i as createAgentTools};
1
+ import{bindOperationExecutor as e,createOperationTools as t,operationToolDialect as n,selectOperation as r}from"./modules/contracts/exposure.mjs";import{convexToZod as i}from"convex-helpers/server/zod4";import{paginationOptsValidator as a}from"convex/server";import{z as o}from"zod";const s=o.object({paginationOpts:i(a)}).strict();function c(e,t,n){let r=e.tableName,i={create:e.tools.insert,update:e.tools.update,archive:e.tools.id,get:e.tools.id,list:s},a={create:`Create a ${r} record`,update:`Update fields of an existing ${r} record`,archive:`Archive (soft-delete) a ${r} record`,get:`Fetch one ${r} record by id`,list:`List ${r} records, paginated`},o={};for(let e of[`create`,`update`,`archive`,`get`,`list`]){let s=t[e];s&&(o[`${r}_${e}`]={name:`${r}_${e}`,description:n?.descriptions?.[e]??a[e],args:i[e],handler:s})}return o}export{e as bindOperationExecutor,c as createAgentTools,t as createOperationTools,n as operationToolDialect,r as selectOperation};
2
2
  //# sourceMappingURL=agent-tools.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-tools.mjs","names":[],"sources":["../src/agent-tools.ts"],"sourcesContent":["import { convexToZod } from 'convex-helpers/server/zod4'\nimport { paginationOptsValidator } from 'convex/server'\nimport { z } from 'zod'\n\n/**\n * A framework-agnostic tool record: directly spreadable into\n * @convex-dev/agent's createTool ({ description, args, handler }) and\n * adaptable to ai-sdk tool(). The kit imports neither framework — checked\n * against registry snapshots on 2026-08-14; re-verify at install time.\n */\nexport type AgentToolRecord<Result = unknown, Context = never, Input = never> = {\n\tname: string\n\tdescription: string\n\targs: z.ZodType\n\thandler: (ctx: Context, input: Input) => Promise<Result>\n}\n\ntype ToolTable = {\n\ttableName: string\n\ttools: {\n\t\tinsert: z.ZodType\n\t\tupdate: z.ZodType\n\t\tid: z.ZodType\n\t}\n}\n\ntype ToolHandler<Result> = (ctx: never, input: never) => Promise<Result>\n\nexport type AgentToolHandlers<Result = unknown> = {\n\t/** Mutations: route through command executors — agents get audited commands. */\n\tcreate?: ToolHandler<Result>\n\tupdate?: ToolHandler<Result>\n\tarchive?: ToolHandler<Result>\n\t/** Reads: caller-supplied query handlers (reads do not route through commands). */\n\tget?: ToolHandler<Result>\n\tlist?: ToolHandler<Result>\n}\n\nconst listArgs = z.object({ paginationOpts: convexToZod(paginationOptsValidator) }).strict()\n\n/**\n * Emits agent tool definitions from the existing table tool masks: args are\n * the masks (jsonSafeZid keeps ids primitive in generated JSON schemas),\n * names are `<tableName>_<verb>`, and only the kinds you supply handlers for\n * are emitted. Wire mutation handlers to createCrudCommands executors (or\n * your own) so every agent action is an audited command.\n */\nexport function createAgentTools<Table extends ToolTable, Handlers extends AgentToolHandlers>(\n\ttable: Table,\n\thandlers: Handlers,\n\toptions?: { descriptions?: Partial<Record<keyof AgentToolHandlers, string>> },\n) {\n\tconst name = table.tableName\n\tconst masks: Record<keyof AgentToolHandlers, z.ZodType> = {\n\t\tcreate: table.tools.insert,\n\t\tupdate: table.tools.update,\n\t\tarchive: table.tools.id,\n\t\tget: table.tools.id,\n\t\tlist: listArgs,\n\t}\n\tconst defaults: Record<keyof AgentToolHandlers, string> = {\n\t\tcreate: `Create a ${name} record`,\n\t\tupdate: `Update fields of an existing ${name} record`,\n\t\tarchive: `Archive (soft-delete) a ${name} record`,\n\t\tget: `Fetch one ${name} record by id`,\n\t\tlist: `List ${name} records, paginated`,\n\t}\n\tconst tools: Record<string, AgentToolRecord> = {}\n\tfor (const verb of ['create', 'update', 'archive', 'get', 'list'] as const) {\n\t\tconst handler = handlers[verb]\n\t\tif (!handler) continue\n\t\ttools[`${name}_${verb}`] = {\n\t\t\tname: `${name}_${verb}`,\n\t\t\tdescription: options?.descriptions?.[verb] ?? defaults[verb],\n\t\t\targs: masks[verb],\n\t\t\thandler,\n\t\t}\n\t}\n\t// SAFETY: each emitted key combines this table name with a supplied verb; its handler and schema come from those same inputs.\n\treturn tools as AgentTools<Table, Handlers>\n}\n\nexport type AgentTools<Table extends ToolTable, Handlers extends AgentToolHandlers> = Partial<{\n\t[Verb in keyof Handlers & keyof AgentToolHandlers as `${Table['tableName']}_${Verb}`]: {\n\t\tname: string\n\t\tdescription: string\n\t\targs: Verb extends 'create'\n\t\t\t? Table['tools']['insert']\n\t\t\t: Verb extends 'update'\n\t\t\t\t? Table['tools']['update']\n\t\t\t\t: Verb extends 'list'\n\t\t\t\t\t? typeof listArgs\n\t\t\t\t\t: Table['tools']['id']\n\t\thandler: NonNullable<Handlers[Verb]>\n\t}\n}>\n"],"mappings":"yIAsCA,MAAM,EAAW,EAAE,OAAO,CAAE,eAAgB,EAAY,CAAuB,CAAE,CAAC,CAAC,CAAC,OAAO,EAS3F,SAAgB,EACf,EACA,EACA,EACC,CACD,IAAM,EAAO,EAAM,UACb,EAAoD,CACzD,OAAQ,EAAM,MAAM,OACpB,OAAQ,EAAM,MAAM,OACpB,QAAS,EAAM,MAAM,GACrB,IAAK,EAAM,MAAM,GACjB,KAAM,CACP,EACM,EAAoD,CACzD,OAAQ,YAAY,EAAK,SACzB,OAAQ,gCAAgC,EAAK,SAC7C,QAAS,2BAA2B,EAAK,SACzC,IAAK,aAAa,EAAK,eACvB,KAAM,QAAQ,EAAK,oBACpB,EACM,EAAyC,CAAC,EAChD,IAAK,IAAM,IAAQ,CAAC,SAAU,SAAU,UAAW,MAAO,MAAM,EAAY,CAC3E,IAAM,EAAU,EAAS,GACpB,IACL,EAAM,GAAG,EAAK,GAAG,KAAU,CAC1B,KAAM,GAAG,EAAK,GAAG,IACjB,YAAa,GAAS,eAAe,IAAS,EAAS,GACvD,KAAM,EAAM,GACZ,SACD,EACD,CAEA,OAAO,CACR"}
1
+ {"version":3,"file":"agent-tools.mjs","names":[],"sources":["../src/agent-tools.ts"],"sourcesContent":["import { convexToZod } from 'convex-helpers/server/zod4'\nimport { paginationOptsValidator } from 'convex/server'\nimport { z } from 'zod'\n\n/**\n * A framework-agnostic tool record: directly spreadable into\n * @convex-dev/agent's createTool ({ description, args, handler }) and\n * adaptable to ai-sdk tool(). The kit imports neither framework — checked\n * against registry snapshots on 2026-08-14; re-verify at install time.\n */\nexport type AgentToolRecord<Result = unknown, Context = never, Input = never> = {\n\tname: string\n\tdescription: string\n\targs: z.ZodType\n\thandler: (ctx: Context, input: Input) => Promise<Result>\n}\n\ntype ToolTable = {\n\ttableName: string\n\ttools: {\n\t\tinsert: z.ZodType\n\t\tupdate: z.ZodType\n\t\tid: z.ZodType\n\t}\n}\n\ntype ToolHandler<Result> = (ctx: never, input: never) => Promise<Result>\n\nexport type AgentToolHandlers<Result = unknown> = {\n\t/** Mutations: route through command executors — agents get audited commands. */\n\tcreate?: ToolHandler<Result>\n\tupdate?: ToolHandler<Result>\n\tarchive?: ToolHandler<Result>\n\t/** Reads: caller-supplied query handlers (reads do not route through commands). */\n\tget?: ToolHandler<Result>\n\tlist?: ToolHandler<Result>\n}\n\nconst listArgs = z.object({ paginationOpts: convexToZod(paginationOptsValidator) }).strict()\n\n/**\n * Emits agent tool definitions from the existing table tool masks: args are\n * the masks (jsonSafeZid keeps ids primitive in generated JSON schemas),\n * names are `<tableName>_<verb>`, and only the kinds you supply handlers for\n * are emitted. Wire mutation handlers to createCrudCommands executors (or\n * your own) so every agent action is an audited command.\n */\nexport function createAgentTools<Table extends ToolTable, Handlers extends AgentToolHandlers>(\n\ttable: Table,\n\thandlers: Handlers,\n\toptions?: { descriptions?: Partial<Record<keyof AgentToolHandlers, string>> },\n) {\n\tconst name = table.tableName\n\tconst masks: Record<keyof AgentToolHandlers, z.ZodType> = {\n\t\tcreate: table.tools.insert,\n\t\tupdate: table.tools.update,\n\t\tarchive: table.tools.id,\n\t\tget: table.tools.id,\n\t\tlist: listArgs,\n\t}\n\tconst defaults: Record<keyof AgentToolHandlers, string> = {\n\t\tcreate: `Create a ${name} record`,\n\t\tupdate: `Update fields of an existing ${name} record`,\n\t\tarchive: `Archive (soft-delete) a ${name} record`,\n\t\tget: `Fetch one ${name} record by id`,\n\t\tlist: `List ${name} records, paginated`,\n\t}\n\tconst tools: Record<string, AgentToolRecord> = {}\n\tfor (const verb of ['create', 'update', 'archive', 'get', 'list'] as const) {\n\t\tconst handler = handlers[verb]\n\t\tif (!handler) continue\n\t\ttools[`${name}_${verb}`] = {\n\t\t\tname: `${name}_${verb}`,\n\t\t\tdescription: options?.descriptions?.[verb] ?? defaults[verb],\n\t\t\targs: masks[verb],\n\t\t\thandler,\n\t\t}\n\t}\n\t// SAFETY: each emitted key combines this table name with a supplied verb; its handler and schema come from those same inputs.\n\treturn tools as AgentTools<Table, Handlers>\n}\n\nexport type AgentTools<Table extends ToolTable, Handlers extends AgentToolHandlers> = Partial<{\n\t[Verb in keyof Handlers & keyof AgentToolHandlers as `${Table['tableName']}_${Verb}`]: {\n\t\tname: string\n\t\tdescription: string\n\t\targs: Verb extends 'create'\n\t\t\t? Table['tools']['insert']\n\t\t\t: Verb extends 'update'\n\t\t\t\t? Table['tools']['update']\n\t\t\t\t: Verb extends 'list'\n\t\t\t\t\t? typeof listArgs\n\t\t\t\t\t: Table['tools']['id']\n\t\thandler: NonNullable<Handlers[Verb]>\n\t}\n}>\nexport {\n\tselectOperation,\n\tbindOperationExecutor,\n\tcreateOperationTools,\n\toperationToolDialect,\n\ttype SelectedOperation,\n\ttype OperationExecutor,\n\ttype OperationPublicResult,\n\ttype OperationToolJsonSchema,\n\ttype OperationToolConverter,\n\ttype OperationToolDefinition,\n\ttype OperationToolOutcome,\n\ttype OperationTool,\n} from './modules/contracts/exposure'\n"],"mappings":"2RAsCA,MAAM,EAAW,EAAE,OAAO,CAAE,eAAgB,EAAY,CAAuB,CAAE,CAAC,CAAC,CAAC,OAAO,EAS3F,SAAgB,EACf,EACA,EACA,EACC,CACD,IAAM,EAAO,EAAM,UACb,EAAoD,CACzD,OAAQ,EAAM,MAAM,OACpB,OAAQ,EAAM,MAAM,OACpB,QAAS,EAAM,MAAM,GACrB,IAAK,EAAM,MAAM,GACjB,KAAM,CACP,EACM,EAAoD,CACzD,OAAQ,YAAY,EAAK,SACzB,OAAQ,gCAAgC,EAAK,SAC7C,QAAS,2BAA2B,EAAK,SACzC,IAAK,aAAa,EAAK,eACvB,KAAM,QAAQ,EAAK,oBACpB,EACM,EAAyC,CAAC,EAChD,IAAK,IAAM,IAAQ,CAAC,SAAU,SAAU,UAAW,MAAO,MAAM,EAAY,CAC3E,IAAM,EAAU,EAAS,GACpB,IACL,EAAM,GAAG,EAAK,GAAG,KAAU,CAC1B,KAAM,GAAG,EAAK,GAAG,IACjB,YAAa,GAAS,eAAe,IAAS,EAAS,GACvD,KAAM,EAAM,GACZ,SACD,EACD,CAEA,OAAO,CACR"}
@@ -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{z as a}from"zod";import{AuditLog as o}from"convex-audit-log";import{v as s}from"convex/values";const c=new o(e.auditLog,{samplingEnabled:!1}),l=n({args:{resourceType:s.string(),resourceRef:s.string(),limit:s.optional(s.number())},returns:s.array(s.object({action:s.string(),actorRef:s.optional(s.string()),correlationKey:s.string(),transition:s.string()})),handler:async(e,t)=>{let n=Math.max(1,Math.min(Math.trunc(t.limit??100),100)),r=await c.queryByResource(e,{resourceType:t.resourceType,resourceId:t.resourceRef,limit:n}),i=new Map;for(let e of r){let t=h(e);t&&i.set(t.correlationKey,t)}return[...i.values()]}}),u=t({args:{olderThanDays:s.number(),batchSize:s.number()},returns:s.number(),handler:(e,t)=>{let n=p(t.olderThanDays,1,i,`olderThanDays`),a=p(t.batchSize,1,100,`batchSize`);return c.cleanup(e,{olderThanDays:n,batchSize:a,retentionCategory:r})}});function d(e){return{action:e.action,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 f(e,t){await c.log(e,d(t))}function p(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}const m=a.object({action:a.string(),actorId:a.string().optional().catch(void 0),metadata:a.object({correlationKey:a.string(),transition:a.string()})});function h(e){let t=m.safeParse(e);if(!t.success)return null;let{action:n,actorId:r,metadata:i}=t.data;return{action:n,actorRef:r,...i}}export{c as approvalAudit,d as approvalAuditEvent,u as cleanup,l as history,f 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{z as a}from"zod";import{v as o}from"convex/values";import{AuditLog as s}from"convex-audit-log";const c=new s(e.auditLog,{samplingEnabled:!1}),l=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 c.queryByResource(e,{resourceType:t.resourceType,resourceId:t.resourceRef,limit:n}),i=new Map;for(let e of r){let t=h(e);t&&i.set(t.correlationKey,t)}return[...i.values()]}}),u=t({args:{olderThanDays:o.number(),batchSize:o.number()},returns:o.number(),handler:(e,t)=>{let n=p(t.olderThanDays,1,i,`olderThanDays`),a=p(t.batchSize,1,100,`batchSize`);return c.cleanup(e,{olderThanDays:n,batchSize:a,retentionCategory:r})}});function d(e){return{action:e.action,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 f(e,t){await c.log(e,d(t))}function p(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}const m=a.object({action:a.string(),actorId:a.string().optional().catch(void 0),metadata:a.object({correlationKey:a.string(),transition:a.string()})});function h(e){let t=m.safeParse(e);if(!t.success)return null;let{action:n,actorId:r,metadata:i}=t.data;return{action:n,actorRef:r,...i}}export{c as approvalAudit,d as approvalAuditEvent,u as cleanup,l as history,f as logApprovalTransition};
2
2
  //# sourceMappingURL=audit.mjs.map
@@ -1,12 +1,13 @@
1
- import { CommandArgument, CommandExecution, CommandHandlerResult, CommandInput, CommandRegistry, CommandResult, Parseable, SchemaInput } from "./modules/command/command.mjs";
1
+ import { CommandArgument, CommandExecution, CommandHandlerResult, CommandInput, CommandRegistry, CommandResult, CommandSchema, Parseable, SchemaInput, SchemaOutput } 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
6
  import { CommandPreparation, OperationExtension, OperationHostContext, TypedOperation, operationFactory } from "./operation.mjs";
7
+ import "./client.mjs";
7
8
  import { Value } from "convex/values";
8
9
  declare namespace client_d_exports {
9
- 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 };
10
+ 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, SchemaOutput, TransactionMetricsContext, TypedOperation };
10
11
  }
11
12
  /** Shape handed to the injected audit writer; classification comes from the operation. */
12
13
  type AuditEntryInput = {
@@ -23,8 +24,8 @@ type AuditWriter<Result = unknown> = (context: never, entry: AuditEntryInput) =>
23
24
  type MaybePromise<Value> = Value | Promise<Value>;
24
25
  type OperationKey<Operations extends CommandRegistry> = Extract<keyof Operations, string>;
25
26
  type AuditedOperation = Readonly<{
26
- command: Parseable<unknown>;
27
- result: Parseable<unknown>;
27
+ command: CommandSchema;
28
+ result: CommandSchema;
28
29
  classification: string;
29
30
  /**
30
31
  * Permission slug required to execute this operation. Checked through the
@@ -33,7 +34,7 @@ type AuditedOperation = Readonly<{
33
34
  */
34
35
  permission?: string;
35
36
  prepare?: (context: never, command: never) => MaybePromise<CommandPreparation<never>>;
36
- replayResult?: Parseable<unknown>;
37
+ replayResult?: CommandSchema;
37
38
  /**
38
39
  * Per-operation precondition, after the permission check and the registry
39
40
  * default guard, before the handler. Throw to deny — nothing has run yet.
@@ -138,7 +139,7 @@ declare class BoundCommand<Context, const Operations extends AuditedRegistry> {
138
139
  exec<const Key extends OperationKey<Operations>>(executor: {
139
140
  operation: Key;
140
141
  handler: (context: Context & OperationExtension<Operations[Key]>, command: CommandInput<Operations, Key>) => MaybePromise<CommandHandlerResult<Operations, Key>>;
141
- }): (context: Context, command: SchemaInput<Operations[Key]["command"]>) => Promise<ReturnType<Operations[Key]["result"]["parse"]>>;
142
+ }): (context: Context, command: SchemaInput<Operations[Key]["command"]>) => Promise<SchemaOutput<Operations[Key]["result"]>>;
142
143
  }
143
144
  type ApplicationCommand<Context, Operations extends AuditedRegistry> = BoundCommand<Context, Operations>;
144
145
  /**
@@ -169,5 +170,5 @@ declare class Foundation<Component extends FoundationComponentApi = FoundationCo
169
170
  constructor(component: Component, options: FoundationOptions);
170
171
  }
171
172
  //#endregion
172
- 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 };
173
+ 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 SchemaOutput, type TransactionMetricsContext, type TypedOperation, client_d_exports };
173
174
  //# 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";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;class o extends s{constructor(e,t){super(e,{observability:r,writeAudit:i,checkPermission:a,defaults:t})}}this.Command=o}};export{d as Foundation};
1
+ import{Observability as e}from"./modules/observability/observability.mjs";import{CommandPermissionError as t,executeCommandLifecycle as n,promiseLifecycle as r}from"./modules/command/lifecycle.mjs";import{Command as i,decodeCommandSchema as a,parseCommandSchema as o}from"./modules/command/command.mjs";import{Query as s}from"./modules/query/query.mjs";import{executeResultBoundary as c,projectResult as l}from"./result.mjs";import{emitSemanticEvent as u}from"./telemetry.mjs";import{operationFactory as d}from"./operation.mjs";var f=class{static withContext=d;#e;#t;#n;#r;#i;aggregates;static operation(e){return e}static middleware(e){return e}constructor(e,t){this.#t=t.observability,this.#n=t.writeAudit,this.#r=t.checkPermission,this.#i=t.defaults??{},this.aggregates=Object.freeze(Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t.aggregates]))),this.#e=new i({operations:e,execute:e=>this.#a(e)})}exec(e){return this.#e.exec({operation:e.operation,handler:(t,n)=>e.handler(t,n)})}async#a(e){let i=e.definition;return this.#t.observe({operation:e.operation,classification:i.classification},()=>n(r,{operation:e.operation,classification:i.classification,context:e.context,command:e.command,permission:i.permission===void 0?void 0:async()=>{if(!this.#r)throw new t(e.operation);await this.#r(e.context,{permission:i.permission,operation:e.operation})},prepare:async()=>{let t=await i.prepare?.(e.context,e.command);return t?.kind===`execute`?{kind:`execute`,complete:t.complete?async e=>{await t.complete(e)}:void 0}:t},parseReplay:t=>i.replayResult?o(i.replayResult,t):e.parseResult(t),decodeReplay:async t=>{if(!i.replayResult)return e.decodeResult?e.decodeResult(t):e.parseResult(t);let n=i.replayResult;return await a(n,t)},middleware:[...this.#i.middleware??[],...i.middleware??[]].map(e=>async t=>e({...t,definition:i,command:t.command,context:t.context,next:t.next})),defaultGuard:async e=>{await this.#i.guard?.(e)},guard:async t=>{await i.guard?.(t,e.command)},run:t=>e.runUnparsed(t),parseResult:e.parseResult,decodeResult:e.decodeResult,audit:async t=>i.audit({command:e.command,result:t},e.context),aggregates:i.aggregates,writeAudit:async t=>this.#n(e.context,t)}))}},p=class{status;Command;Query=s;observability;executeResultBoundary=c;projectResult=l;emitSemanticEvent=u;constructor(t,n){this.status=t.functions.status;let r=new e(n.observability);this.observability=r;let i=n.observability.writeAudit??(()=>void 0),a=n.checkPermission;class o extends f{constructor(e,t){super(e,{observability:r,writeAudit:i,checkPermission:a,defaults:t})}}this.Command=o}};export{p 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 type { Value } from 'convex/values'\nimport {\n\tCommand as CommandKernelClass,\n\ttype CommandExecution,\n\ttype CommandHandlerResult,\n\ttype CommandInput,\n\ttype CommandRegistry,\n\ttype CommandResult,\n\ttype Parseable,\n} from './modules/command/command'\nimport { Observability, type ObservabilityOptions } 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, Value>\n\tclassification: string\n}\n\nexport type AuditWriter<Result = unknown> = (\n\tcontext: never,\n\tentry: AuditEntryInput,\n) => MaybePromise<Result>\n\ntype MaybePromise<Value> = Value | Promise<Value>\ntype OperationKey<Operations extends CommandRegistry> = Extract<keyof Operations, string>\n\nexport type AuditedOperation = Readonly<{\n\tcommand: Parseable<unknown>\n\tresult: Parseable<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?: (context: never, command: never) => MaybePromise<CommandPreparation<never>>\n\treplayResult?: Parseable<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 object = object,\n\tInput = unknown,\n\tResult = unknown,\n> = (input: {\n\toperation: string\n\tcommand: Input\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<Result>\n}) => Promise<Result>\n\n/** Any-context middleware — what registries and operations accept. */\n// Contravariant seam: the supertype every typed CommandMiddleware<C, E>\n// assigns to. Context, command, and next are contravariant storage slots;\n// dispatch restores the selected operation's types before invocation.\nexport type AnyCommandMiddleware<Input = never, Result = unknown, NextResult = never> = (input: {\n\toperation: string\n\tdefinition?: AuditedOperation\n\tcommand: Input\n\tcontext: never\n\tnext: (options?: { context?: object }) => Promise<NextResult>\n}) => Promise<Result>\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>(definition: Definition): 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 object = object,\n\t\tInput = unknown,\n\t\tResult = unknown,\n\t>(\n\t\tmiddleware: CommandMiddleware<Context, Extension, Input, Result>,\n\t): CommandMiddleware<Context, Extension, Input, Result> {\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\t// SAFETY: this map preserves every operations key and selects its aggregates property.\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\t// SAFETY: operationFactory requires enrichment middleware before this 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\t// SAFETY: the injected policy receives the host context used by this Command instance.\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\t// SAFETY: prepare belongs to this definition; its context and parsed command are paired by the kernel.\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\t// SAFETY: replayResult validates the stored output for this selected operation.\n\t\t\t\t\treturn execution.definition.replayResult\n\t\t\t\t\t\t? (execution.definition.replayResult.parse(preparation.result) as CommandResult<\n\t\t\t\t\t\t\t\tOperations,\n\t\t\t\t\t\t\t\tKey\n\t\t\t\t\t\t\t>)\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: Context) => {\n\t\t\t\t\t// SAFETY: registry guards receive the same host context, enriched by its middleware.\n\t\t\t\t\tawait this.#defaults.guard?.(context as never)\n\t\t\t\t\t// SAFETY: guard is called with the selected operation's parsed command and middleware context.\n\t\t\t\t\tawait execution.definition.guard?.(context as never, execution.command as never)\n\t\t\t\t\treturn execution.runUnparsed(context)\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: Context,\n\t\t\t\t): Promise<CommandHandlerResult<Operations, Key>> => {\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\t// SAFETY: this chain belongs to the selected operation; parseResult validates its final output before audit.\n\t\t\t\t\tconst layer = middleware[index] as\n\t\t\t\t\t\t| AnyCommandMiddleware<\n\t\t\t\t\t\t\t\tCommandInput<Operations, Key>,\n\t\t\t\t\t\t\t\tCommandHandlerResult<Operations, Key>,\n\t\t\t\t\t\t\t\tCommandHandlerResult<Operations, Key>\n\t\t\t\t\t\t >\n\t\t\t\t\t\t| undefined\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\t// SAFETY: middleware context is erased only in the heterogeneous registry.\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,\n\t\t\t\t\t\t\t\t\t\t\t...options.context,\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(await dispatch(0, execution.context))\n\t\t\t\t// SAFETY: the selected audit callback receives its validated command/result and original host context.\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(execution.operation, audit.aggregate.type)\n\t\t\t\t\t}\n\t\t\t\t\t// SAFETY: the host injected this writer for the context supplied to its Command.\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\t// SAFETY: complete comes from this invocation's prepare hook and receives its validated result.\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(`A middleware for operation \"${operation}\" called next() more than once`)\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<Context, Operations extends AuditedRegistry> = BoundCommand<\n\tContext,\n\tOperations\n>\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<Component extends FoundationComponentApi = FoundationComponentApi> {\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\tclass HostCommand<Context, const Operations extends AuditedRegistry> extends BoundCommand<\n\t\t\tContext,\n\t\t\tOperations\n\t\t> {\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\tthis.Command = HostCommand\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 { AnyQueryMiddleware, QueryExecution, QueryMiddleware } from './modules/query/query'\nexport type { Result, ResultBoundary, TransactionMetricsContext } 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 type { Value } from 'convex/values'\nimport {\n\tCommand as CommandKernelClass,\n\ttype CommandExecution,\n\ttype CommandHandlerResult,\n\ttype CommandInput,\n\ttype CommandRegistry,\n\ttype CommandResult,\n\ttype CommandSchema,\n\tdecodeCommandSchema,\n\tparseCommandSchema,\n} from './modules/command/command'\nimport { Observability, type ObservabilityOptions } from './modules/observability/observability'\nimport { Query } from './modules/query/query'\nimport {\n\texecuteCommandLifecycle,\n\tpromiseLifecycle,\n\tCommandPermissionError,\n\ttype PromiseExecution,\n} from './modules/command/lifecycle'\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, Value>\n\tclassification: string\n}\n\nexport type AuditWriter<Result = unknown> = (\n\tcontext: never,\n\tentry: AuditEntryInput,\n) => MaybePromise<Result>\n\ntype MaybePromise<Value> = Value | Promise<Value>\ntype OperationKey<Operations extends CommandRegistry> = Extract<keyof Operations, string>\n\nexport type AuditedOperation = Readonly<{\n\tcommand: CommandSchema\n\tresult: CommandSchema\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?: (context: never, command: never) => MaybePromise<CommandPreparation<never>>\n\treplayResult?: CommandSchema\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 object = object,\n\tInput = unknown,\n\tResult = unknown,\n> = (input: {\n\toperation: string\n\tcommand: Input\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<Result>\n}) => Promise<Result>\n\n/** Any-context middleware — what registries and operations accept. */\n// Contravariant seam: the supertype every typed CommandMiddleware<C, E>\n// assigns to. Context, command, and next are contravariant storage slots;\n// dispatch restores the selected operation's types before invocation.\nexport type AnyCommandMiddleware<Input = never, Result = unknown, NextResult = never> = (input: {\n\toperation: string\n\tdefinition?: AuditedOperation\n\tcommand: Input\n\tcontext: never\n\tnext: (options?: { context?: object }) => Promise<NextResult>\n}) => Promise<Result>\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>(definition: Definition): 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 object = object,\n\t\tInput = unknown,\n\t\tResult = unknown,\n\t>(\n\t\tmiddleware: CommandMiddleware<Context, Extension, Input, Result>,\n\t): CommandMiddleware<Context, Extension, Input, Result> {\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\t// SAFETY: this map preserves every operations key and selects its aggregates property.\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\t// SAFETY: operationFactory requires enrichment middleware before this 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\tconst definition = execution.definition\n\t\treturn this.#observability.observe(\n\t\t\t{ operation: execution.operation, classification: definition.classification },\n\t\t\t() =>\n\t\t\t\texecuteCommandLifecycle<\n\t\t\t\t\tPromiseExecution,\n\t\t\t\t\tContext,\n\t\t\t\t\tCommandInput<Operations, Key>,\n\t\t\t\t\tCommandHandlerResult<Operations, Key>,\n\t\t\t\t\tCommandResult<Operations, Key>\n\t\t\t\t>(promiseLifecycle, {\n\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\tclassification: definition.classification,\n\t\t\t\t\tcontext: execution.context,\n\t\t\t\t\tcommand: execution.command,\n\t\t\t\t\tpermission:\n\t\t\t\t\t\tdefinition.permission === undefined\n\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t: async () => {\n\t\t\t\t\t\t\t\t\tif (!this.#checkPermission) throw new CommandPermissionError(execution.operation)\n\t\t\t\t\t\t\t\t\t// SAFETY: injected policy consumes this invocation's original host context.\n\t\t\t\t\t\t\t\t\tawait this.#checkPermission(execution.context as never, {\n\t\t\t\t\t\t\t\t\t\tpermission: definition.permission!,\n\t\t\t\t\t\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\tprepare: async () => {\n\t\t\t\t\t\t// SAFETY: selected definition and parsed input are paired by the kernel.\n\t\t\t\t\t\tconst preparation = await definition.prepare?.(\n\t\t\t\t\t\t\texecution.context as never,\n\t\t\t\t\t\t\texecution.command as never,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (preparation?.kind !== 'execute') return preparation\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tkind: 'execute' as const,\n\t\t\t\t\t\t\tcomplete: preparation.complete\n\t\t\t\t\t\t\t\t? async (result: CommandResult<Operations, Key>) => {\n\t\t\t\t\t\t\t\t\t\t// SAFETY: completion belongs to this operation's validated final result.\n\t\t\t\t\t\t\t\t\t\tawait preparation.complete!(result as never)\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tparseReplay: (value) => {\n\t\t\t\t\t\tif (!definition.replayResult) return execution.parseResult(value)\n\t\t\t\t\t\t// SAFETY: selected replay schema validates this operation's stored output.\n\t\t\t\t\t\treturn parseCommandSchema(definition.replayResult, value) as CommandResult<\n\t\t\t\t\t\t\tOperations,\n\t\t\t\t\t\t\tKey\n\t\t\t\t\t\t>\n\t\t\t\t\t},\n\t\t\t\t\tdecodeReplay: async (value) => {\n\t\t\t\t\t\tif (!definition.replayResult) {\n\t\t\t\t\t\t\treturn execution.decodeResult\n\t\t\t\t\t\t\t\t? execution.decodeResult(value)\n\t\t\t\t\t\t\t\t: execution.parseResult(value)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst replay = definition.replayResult\n\t\t\t\t\t\t// SAFETY: selected replay decoder owns the final output representation.\n\t\t\t\t\t\treturn (await decodeCommandSchema(replay, value)) as CommandResult<Operations, Key>\n\t\t\t\t\t},\n\t\t\t\t\tmiddleware: [...(this.#defaults.middleware ?? []), ...(definition.middleware ?? [])].map(\n\t\t\t\t\t\t(layer) => async (input) => {\n\t\t\t\t\t\t\t// SAFETY: selected definition owns this heterogeneous middleware's pre-validation output.\n\t\t\t\t\t\t\treturn layer({\n\t\t\t\t\t\t\t\t...input,\n\t\t\t\t\t\t\t\tdefinition,\n\t\t\t\t\t\t\t\t// SAFETY: selected command schema owns this heterogeneous storage value.\n\t\t\t\t\t\t\t\tcommand: input.command as never,\n\t\t\t\t\t\t\t\t// SAFETY: only heterogeneous storage erases the selected middleware context.\n\t\t\t\t\t\t\t\tcontext: input.context as never,\n\t\t\t\t\t\t\t\t// SAFETY: selected operation pairs downstream output with its final schema.\n\t\t\t\t\t\t\t\tnext: input.next as (options?: { context?: object }) => Promise<never>,\n\t\t\t\t\t\t\t}) as Promise<CommandHandlerResult<Operations, Key>>\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t\t\tdefaultGuard: async (context) => {\n\t\t\t\t\t\t// SAFETY: default guards consume the downstream middleware context.\n\t\t\t\t\t\tawait this.#defaults.guard?.(context as never)\n\t\t\t\t\t},\n\t\t\t\t\tguard: async (context) => {\n\t\t\t\t\t\t// SAFETY: selected guard consumes its parsed input and enriched context.\n\t\t\t\t\t\tawait definition.guard?.(context as never, execution.command as never)\n\t\t\t\t\t},\n\t\t\t\t\trun: (context) => execution.runUnparsed(context),\n\t\t\t\t\tparseResult: execution.parseResult,\n\t\t\t\t\tdecodeResult: execution.decodeResult,\n\t\t\t\t\taudit: async (result) => {\n\t\t\t\t\t\t// SAFETY: audit receives this selected parsed command/result and original context.\n\t\t\t\t\t\treturn definition.audit(\n\t\t\t\t\t\t\t{ command: execution.command, result } as never,\n\t\t\t\t\t\t\texecution.context as never,\n\t\t\t\t\t\t)\n\t\t\t\t\t},\n\t\t\t\t\taggregates: definition.aggregates,\n\t\t\t\t\twriteAudit: async (entry) => {\n\t\t\t\t\t\t// SAFETY: host supplied writer for this invocation's original context.\n\t\t\t\t\t\treturn this.#writeAudit(execution.context as never, entry)\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t)\n\t}\n}\n\nexport type ApplicationCommand<Context, Operations extends AuditedRegistry> = BoundCommand<\n\tContext,\n\tOperations\n>\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<Component extends FoundationComponentApi = FoundationComponentApi> {\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\tclass HostCommand<Context, const Operations extends AuditedRegistry> extends BoundCommand<\n\t\t\tContext,\n\t\t\tOperations\n\t\t> {\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\tthis.Command = HostCommand\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\tSchemaOutput,\n\tParseable,\n} from './modules/command/command'\nexport type {\n\tCommandObservation,\n\tObservabilityOptions,\n} from './modules/observability/observability'\nexport type { AnyQueryMiddleware, QueryExecution, QueryMiddleware } from './modules/query/query'\nexport type { Result, ResultBoundary, TransactionMetricsContext } 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,36 +1,72 @@
1
1
  declare namespace command_d_exports {
2
- export { Command, CommandArgument, CommandExecution, CommandHandlerResult, CommandInput, CommandRegistry, CommandResult, Parseable, SchemaInput };
2
+ export { Command, CommandArgument, CommandExecution, CommandHandlerResult, CommandInput, CommandRegistry, CommandResult, CommandSchema, Parseable, SchemaInput, SchemaOutput, decodeCommandSchema, parseCommandSchema };
3
3
  }
4
4
  type Parseable<Output> = Readonly<{
5
5
  parse: <Input>(value: Input) => Output;
6
+ parseAsync?: <Input>(value: Input) => Promise<Output>;
6
7
  }>;
7
- /** Zod exposes input separately for transforms; plain parsers retain their output contract. */
8
- type SchemaInput<Schema extends Parseable<unknown>> = Schema extends {
8
+ /** Structural mirror of portable neutral contracts: components cannot import host modules. */
9
+ interface ValidationIssue {
10
+ readonly message: string;
11
+ readonly path?: readonly (PropertyKey | {
12
+ readonly key: PropertyKey;
13
+ })[];
14
+ }
15
+ type ValidationResult<Output> = {
16
+ readonly value: Output;
17
+ readonly issues?: undefined;
18
+ } | {
19
+ readonly issues: readonly ValidationIssue[];
20
+ };
21
+ interface StandardSchema<Input = unknown, Output = unknown> {
22
+ readonly '~standard': {
23
+ readonly version: 1;
24
+ readonly vendor: string;
25
+ readonly types?: {
26
+ readonly input: Input;
27
+ readonly output: Output;
28
+ };
29
+ readonly validate: (value: unknown) => ValidationResult<Output> | Promise<ValidationResult<Output>>;
30
+ };
31
+ }
32
+ interface NeutralDecoder<Input = unknown, Output = unknown, Execution = unknown> {
33
+ readonly kind: 'contract';
34
+ readonly types?: {
35
+ readonly input: Input;
36
+ readonly output: Output;
37
+ };
38
+ readonly decode: (value: unknown) => Execution;
39
+ }
40
+ type CommandSchema<Output = unknown> = Parseable<Output> | StandardSchema<unknown, Output> | NeutralDecoder<unknown, Output, ValidationResult<Output> | Promise<ValidationResult<Output>>>;
41
+ /** Raw values accepted before decoding, including transform inputs. */
42
+ type SchemaInput<Schema extends CommandSchema> = Schema extends NeutralDecoder<infer Input, unknown, unknown> ? Input : Schema extends {
9
43
  _input: infer Input;
10
- } ? Input : Parsed<Schema>;
44
+ } ? Input : Schema extends StandardSchema<infer Input, unknown> ? Input : SchemaOutput<Schema>;
45
+ type SchemaOutput<Schema extends CommandSchema> = Schema extends Parseable<unknown> ? ReturnType<Schema['parse']> : Schema extends NeutralDecoder<unknown, infer Output, unknown> ? Output : Schema extends StandardSchema<unknown, infer Output> ? Output : never;
11
46
  type CommandHandlerResult<Registry extends CommandRegistry, Key extends Operation<Registry>> = SchemaInput<Registry[Key]['result']>;
12
47
  type CommandRegistry = Readonly<Record<string, CommandDefinition>>;
13
48
  /** Values accepted from callers before the command schema parses them. */
14
49
  type CommandArgument<Registry extends CommandRegistry, Key extends Operation<Registry>> = SchemaInput<Registry[Key]['command']>;
15
- type CommandInput<Registry extends CommandRegistry, Key extends Operation<Registry>> = Parsed<Registry[Key]['command']>;
16
- type CommandResult<Registry extends CommandRegistry, Key extends Operation<Registry>> = Parsed<Registry[Key]['result']>;
50
+ type CommandInput<Registry extends CommandRegistry, Key extends Operation<Registry>> = SchemaOutput<Registry[Key]['command']>;
51
+ type CommandResult<Registry extends CommandRegistry, Key extends Operation<Registry>> = SchemaOutput<Registry[Key]['result']>;
17
52
  type CommandExecution<Context, Registry extends CommandRegistry, Key extends Operation<Registry>> = Readonly<{
18
53
  context: Context;
19
54
  operation: Key;
20
55
  definition: Registry[Key];
21
56
  command: CommandInput<Registry, Key>;
22
57
  parseResult: <Input>(value: Input) => CommandResult<Registry, Key>;
58
+ /** Interpreter-owned asynchronous result decoding, preserving the legacy parser. */
59
+ decodeResult?: <Input>(value: Input) => Promise<CommandResult<Registry, Key>>;
23
60
  /** Runs the handler (result-parsed). Accepts a middleware-enriched context. */
24
61
  run: (context?: Context) => Promise<CommandResult<Registry, Key>>;
25
62
  /** Runs without parsing for owners that validate after their middleware chain. */
26
63
  runUnparsed: (context?: Context) => Promise<CommandHandlerResult<Registry, Key>>;
27
64
  }>;
28
65
  type CommandDefinition = Readonly<{
29
- command: Parseable<unknown>;
30
- result: Parseable<unknown>;
66
+ command: CommandSchema;
67
+ result: CommandSchema;
31
68
  }>;
32
69
  type Operation<Registry extends CommandRegistry> = Extract<keyof Registry, string>;
33
- type Parsed<Schema extends Parseable<unknown>> = ReturnType<Schema['parse']>;
34
70
  type MaybePromise<Value> = Value | Promise<Value>;
35
71
  type Execute<Context, Registry extends CommandRegistry> = <Key extends Operation<Registry>>(execution: CommandExecution<Context, Registry, Key>) => Promise<CommandResult<Registry, Key>>;
36
72
  type FixedExecutor<Context, Registry extends CommandRegistry, Key extends Operation<Registry>> = Readonly<{
@@ -52,6 +88,10 @@ declare class Command<Context, const Registry extends CommandRegistry> {
52
88
  exec<const Key extends Operation<Registry>>(executor: FixedExecutor<Context, Registry, Key>): (context: Context, command: CommandArgument<Registry, Key>) => Promise<CommandResult<Registry, Key>>;
53
89
  exec<Dispatcher, const Key extends Operation<Registry>>(executor: DynamicExecutor<Context, Registry, Dispatcher, Key>): (context: Context, command: Dispatcher) => Promise<CommandResult<Registry, Key>>;
54
90
  }
91
+ /** Legacy synchronous parsing remains available to existing kernel interpreters. */
92
+ declare function parseCommandSchema<S extends CommandSchema>(schema: S, value: unknown): SchemaOutput<S>;
93
+ /** Interpreter-owned decoding awaits validation and leaves thrown/rejected defects intact. */
94
+ declare function decodeCommandSchema<S extends CommandSchema>(schema: S, value: unknown): Promise<SchemaOutput<S>>;
55
95
  //#endregion
56
- export { Command, CommandArgument, CommandExecution, CommandHandlerResult, CommandInput, CommandRegistry, CommandResult, Parseable, SchemaInput, command_d_exports };
96
+ export { Command, CommandArgument, CommandExecution, CommandHandlerResult, CommandInput, CommandRegistry, CommandResult, CommandSchema, Parseable, SchemaInput, SchemaOutput, command_d_exports, decodeCommandSchema, parseCommandSchema };
57
97
  //# 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,runUnparsed:async e=>l(e??n,s),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(i,a)=>{let o=`operation`in t?{operation:t.operation,value:a}:await(async()=>{let e=await r(t.dispatcher,a);return{operation:t.select(e),value:e}})();if(!Object.prototype.hasOwnProperty.call(this.#e,o.operation))throw new e;let s=o.operation,c=this.#e[s],l=await r(c.command,o.value),u=e=>n(c.result,e),d=async e=>await r(c.result,e),f=t.handler;return this.#t({context:i,operation:s,definition:c,command:l,parseResult:u,decodeResult:d,runUnparsed:async e=>f(e??i,l),run:async e=>d(await f(e??i,l))})}}};function n(e,t){if(!(`parse`in e))throw TypeError(`This command schema requires asynchronous decoding`);return e.parse(t)}async function r(e,t){if(`parse`in e)return await(e.parseAsync?e.parseAsync(t):e.parse(t));let n=await(`kind`in e?e.decode(t):e[`~standard`].validate(t));if(n.issues!==void 0)throw new i(n.issues);return n.value}var i=class extends Error{issues;name=`CommandValidationError`;constructor(e){super(`Command contract validation failed`),this.issues=e}};export{t as Command,r as decodeCommandSchema,n as parseCommandSchema};
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<{\n\tparse: <Input>(value: Input) => Output\n}>\n\n/** Zod exposes input separately for transforms; plain parsers retain their output contract. */\nexport type SchemaInput<Schema extends Parseable<unknown>> = 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<\n\tRegistry extends CommandRegistry,\n\tKey extends Operation<Registry>,\n> = 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: <Input>(value: Input) => 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: (context?: Context) => 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<keyof Registry, string>\ntype Parsed<Schema extends Parseable<unknown>> = ReturnType<Schema['parse']>\ntype MaybePromise<Value> = Value | Promise<Value>\ntype Execute<Context, Registry extends CommandRegistry> = <Key extends Operation<Registry>>(\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): (context: Context, command: Dispatcher) => Promise<CommandResult<Registry, Key>>\n\texec<Dispatcher>(\n\t\texecutor:\n\t\t\t| FixedExecutor<Context, Registry, Operation<Registry>>\n\t\t\t| DynamicExecutor<Context, Registry, Dispatcher, Operation<Registry>>,\n\t) {\n\t\treturn async (\n\t\t\tcontext: Context,\n\t\t\tvalue: Dispatcher | CommandArgument<Registry, Operation<Registry>>,\n\t\t) => {\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 (!Object.prototype.hasOwnProperty.call(this.#operations, selected.operation)) {\n\t\t\t\tthrow new CommandConfigurationError()\n\t\t\t}\n\t\t\tconst operation = selected.operation\n\t\t\tconst definition = this.#operations[operation]\n\t\t\t// SAFETY: the selected registry entry owns this command schema and its output.\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\t// SAFETY: the selected entry also owns the result schema, including transforms.\n\t\t\tconst parseResult = <Input>(result: Input) =>\n\t\t\t\tdefinition.result.parse(result) as CommandResult<Registry, Operation<Registry>>\n\t\t\tconst handler = executor.handler\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":"AAqFA,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,CAWA,KACC,EAGC,CACD,OAAO,MACN,EACA,IACI,CACJ,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,GAAI,CAAC,OAAO,UAAU,eAAe,KAAK,KAAKD,GAAa,EAAS,SAAS,EAC7E,MAAM,IAAI,EAEX,IAAM,EAAY,EAAS,UACrB,EAAa,KAAKA,GAAY,GAE9B,EAAU,EAAW,QAAQ,MAAM,EAAS,KAAK,EAKjD,EAAsB,GAC3B,EAAW,OAAO,MAAM,CAAM,EACzB,EAAU,EAAS,QACzB,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"}
1
+ {"version":3,"file":"command.mjs","names":["#operations","#execute"],"sources":["../../../../../src/components/foundation/modules/command/command.ts"],"sourcesContent":["/* oxlint-disable anti-slop/no-unknown-parameters -- Schema validators and interpreter decoders own these untrusted input boundaries and validate before domain callbacks. */\nexport type Parseable<Output> = Readonly<{\n\tparse: <Input>(value: Input) => Output\n\tparseAsync?: <Input>(value: Input) => Promise<Output>\n}>\n\n/** Structural mirror of portable neutral contracts: components cannot import host modules. */\ninterface ValidationIssue {\n\treadonly message: string\n\treadonly path?: readonly (PropertyKey | { readonly key: PropertyKey })[]\n}\ntype ValidationResult<Output> =\n\t| { readonly value: Output; readonly issues?: undefined }\n\t| { readonly issues: readonly ValidationIssue[] }\ninterface StandardSchema<Input = unknown, Output = unknown> {\n\treadonly '~standard': {\n\t\treadonly version: 1\n\t\treadonly vendor: string\n\t\treadonly types?: { readonly input: Input; readonly output: Output }\n\t\treadonly validate: (\n\t\t\tvalue: unknown,\n\t\t) => ValidationResult<Output> | Promise<ValidationResult<Output>>\n\t}\n}\ninterface NeutralDecoder<Input = unknown, Output = unknown, Execution = unknown> {\n\treadonly kind: 'contract'\n\treadonly types?: { readonly input: Input; readonly output: Output }\n\treadonly decode: (value: unknown) => Execution\n}\nexport type CommandSchema<Output = unknown> =\n\t| Parseable<Output>\n\t| StandardSchema<unknown, Output>\n\t| NeutralDecoder<unknown, Output, ValidationResult<Output> | Promise<ValidationResult<Output>>>\n\n/** Raw values accepted before decoding, including transform inputs. */\nexport type SchemaInput<Schema extends CommandSchema> =\n\tSchema extends NeutralDecoder<infer Input, unknown, unknown>\n\t\t? Input\n\t\t: Schema extends { _input: infer Input }\n\t\t\t? Input\n\t\t\t: Schema extends StandardSchema<infer Input, unknown>\n\t\t\t\t? Input\n\t\t\t\t: SchemaOutput<Schema>\nexport type SchemaOutput<Schema extends CommandSchema> =\n\tSchema extends Parseable<unknown>\n\t\t? ReturnType<Schema['parse']>\n\t\t: Schema extends NeutralDecoder<unknown, infer Output, unknown>\n\t\t\t? Output\n\t\t\t: Schema extends StandardSchema<unknown, infer Output>\n\t\t\t\t? Output\n\t\t\t\t: never\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<\n\tRegistry extends CommandRegistry,\n\tKey extends Operation<Registry>,\n> = SchemaInput<Registry[Key]['command']>\n\nexport type CommandInput<\n\tRegistry extends CommandRegistry,\n\tKey extends Operation<Registry>,\n> = SchemaOutput<Registry[Key]['command']>\n\nexport type CommandResult<\n\tRegistry extends CommandRegistry,\n\tKey extends Operation<Registry>,\n> = SchemaOutput<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: <Input>(value: Input) => CommandResult<Registry, Key>\n\t/** Interpreter-owned asynchronous result decoding, preserving the legacy parser. */\n\tdecodeResult?: <Input>(value: Input) => Promise<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: (context?: Context) => Promise<CommandHandlerResult<Registry, Key>>\n}>\n\ntype CommandDefinition = Readonly<{\n\tcommand: CommandSchema\n\tresult: CommandSchema\n}>\n\ntype Operation<Registry extends CommandRegistry> = Extract<keyof Registry, string>\ntype MaybePromise<Value> = Value | Promise<Value>\ntype Execute<Context, Registry extends CommandRegistry> = <Key extends Operation<Registry>>(\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): (context: Context, command: Dispatcher) => Promise<CommandResult<Registry, Key>>\n\texec<Dispatcher>(\n\t\texecutor:\n\t\t\t| FixedExecutor<Context, Registry, Operation<Registry>>\n\t\t\t| DynamicExecutor<Context, Registry, Dispatcher, Operation<Registry>>,\n\t) {\n\t\treturn async (\n\t\t\tcontext: Context,\n\t\t\tvalue: Dispatcher | CommandArgument<Registry, Operation<Registry>>,\n\t\t) => {\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: await (async () => {\n\t\t\t\t\t\t\tconst dispatcher = await decodeCommandSchema(executor.dispatcher, 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 (!Object.prototype.hasOwnProperty.call(this.#operations, selected.operation)) {\n\t\t\t\tthrow new CommandConfigurationError()\n\t\t\t}\n\t\t\tconst operation = selected.operation\n\t\t\tconst definition = this.#operations[operation]\n\t\t\t// SAFETY: the selected registry entry owns this command schema and its output.\n\t\t\tconst command = (await decodeCommandSchema(\n\t\t\t\tdefinition.command,\n\t\t\t\tselected.value,\n\t\t\t)) as CommandInput<Registry, Operation<Registry>>\n\t\t\t// SAFETY: the selected entry also owns the result schema, including transforms.\n\t\t\tconst parseResult = <Input>(result: Input) =>\n\t\t\t\tparseCommandSchema(definition.result, result) as CommandResult<\n\t\t\t\t\tRegistry,\n\t\t\t\t\tOperation<Registry>\n\t\t\t\t>\n\t\t\t// SAFETY: selected result schema owns the decoded output, including async transforms.\n\t\t\tconst decodeResult = async <Input>(result: Input) =>\n\t\t\t\t(await decodeCommandSchema(definition.result, 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\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\tdecodeResult,\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\tdecodeResult(await handler(contextOverride ?? context, command)),\n\t\t\t})\n\t\t}\n\t}\n}\n\n/** Legacy synchronous parsing remains available to existing kernel interpreters. */\nexport function parseCommandSchema<S extends CommandSchema>(\n\tschema: S,\n\tvalue: unknown,\n): SchemaOutput<S> {\n\tif (!('parse' in schema))\n\t\tthrow new TypeError('This command schema requires asynchronous decoding')\n\t// SAFETY: this legacy parser belongs to the selected schema.\n\treturn schema.parse(value) as SchemaOutput<S>\n}\n\n/** Interpreter-owned decoding awaits validation and leaves thrown/rejected defects intact. */\nexport async function decodeCommandSchema<S extends CommandSchema>(\n\tschema: S,\n\tvalue: unknown,\n): Promise<SchemaOutput<S>> {\n\tif ('parse' in schema) {\n\t\t// SAFETY: parseAsync and parse share the selected legacy schema's output.\n\t\treturn (await (schema.parseAsync\n\t\t\t? schema.parseAsync(value)\n\t\t\t: schema.parse(value))) as SchemaOutput<S>\n\t}\n\tconst result = await ('kind' in schema\n\t\t? schema.decode(value)\n\t\t: schema['~standard'].validate(value))\n\tif (result.issues !== undefined) throw new CommandValidationError(result.issues)\n\t// SAFETY: this decoder owns the selected schema's output.\n\treturn result.value as SchemaOutput<S>\n}\n\nclass CommandValidationError extends Error {\n\treadonly name = 'CommandValidationError'\n\tconstructor(readonly issues: readonly ValidationIssue[]) {\n\t\tsuper('Command contract validation failed')\n\t}\n}\n"],"mappings":"AAiIA,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,CAWA,KACC,EAGC,CACD,OAAO,MACN,EACA,IACI,CACJ,IAAM,EACL,cAAe,EACZ,CAAE,UAAW,EAAS,UAAW,OAAM,EACvC,MAAO,SAAY,CACnB,IAAM,EAAa,MAAM,EAAoB,EAAS,WAAY,CAAK,EACvE,MAAO,CACN,UAAW,EAAS,OAAO,CAAU,EACrC,MAAO,CACR,CACD,EAAA,CAAG,EACN,GAAI,CAAC,OAAO,UAAU,eAAe,KAAK,KAAKD,GAAa,EAAS,SAAS,EAC7E,MAAM,IAAI,EAEX,IAAM,EAAY,EAAS,UACrB,EAAa,KAAKA,GAAY,GAE9B,EAAW,MAAM,EACtB,EAAW,QACX,EAAS,KACV,EAEM,EAAsB,GAC3B,EAAmB,EAAW,OAAQ,CAAM,EAKvC,EAAe,KAAc,IACjC,MAAM,EAAoB,EAAW,OAAQ,CAAM,EAI/C,EAAU,EAAS,QACzB,OAAO,KAAKC,GAAS,CACpB,UACA,YACA,aACA,UACA,cACA,eACA,YAAa,KAAO,IACnB,EAAQ,GAAmB,EAAS,CAAO,EAC5C,IAAK,KAAO,IACX,EAAa,MAAM,EAAQ,GAAmB,EAAS,CAAO,CAAC,CACjE,CAAC,CACF,CACD,CACD,EAGA,SAAgB,EACf,EACA,EACkB,CAClB,GAAI,EAAE,UAAW,GAChB,MAAU,UAAU,oDAAoD,EAEzE,OAAO,EAAO,MAAM,CAAK,CAC1B,CAGA,eAAsB,EACrB,EACA,EAC2B,CAC3B,GAAI,UAAW,EAEd,OAAQ,MAAO,EAAO,WACnB,EAAO,WAAW,CAAK,EACvB,EAAO,MAAM,CAAK,GAEtB,IAAM,EAAS,MAAO,SAAU,EAC7B,EAAO,OAAO,CAAK,EACnB,EAAO,YAAY,CAAC,SAAS,CAAK,GACrC,GAAI,EAAO,SAAW,IAAA,GAAW,MAAM,IAAI,EAAuB,EAAO,MAAM,EAE/E,OAAO,EAAO,KACf,CAEA,IAAM,EAAN,cAAqC,KAAM,CAErB,OADrB,KAAgB,yBAChB,YAAY,EAA6C,CACxD,MAAM,oCAAoC,EADtB,KAAA,OAAA,CAErB,CACD"}
@@ -0,0 +1,81 @@
1
+ import { AuditEntryInput } from "../../client.mjs";
2
+ //#region src/components/foundation/modules/command/lifecycle.d.ts
3
+ /** Internal sequencing only: interpreters own callback normalization and observation. */
4
+ interface ExecutionKind {
5
+ readonly value: unknown;
6
+ readonly type: unknown;
7
+ }
8
+ type ExecutionValue<Kind extends ExecutionKind, Value> = (Kind & {
9
+ readonly value: Value;
10
+ })['type'];
11
+ interface LifecycleAlgebra<Kind extends ExecutionKind> {
12
+ succeed<Value>(value: Value): ExecutionValue<Kind, Value>;
13
+ suspend<Value>(execute: () => ExecutionValue<Kind, Value>): ExecutionValue<Kind, Value>;
14
+ flatMap<Input, Output>(execution: ExecutionValue<Kind, Input>, next: (input: Input) => ExecutionValue<Kind, Output>): ExecutionValue<Kind, Output>;
15
+ }
16
+ interface PromiseExecution extends ExecutionKind {
17
+ readonly type: Promise<this['value']>;
18
+ }
19
+ declare const promiseLifecycle: LifecycleAlgebra<PromiseExecution>;
20
+ type LifecyclePreparation<Kind extends ExecutionKind, Result> = {
21
+ kind: 'replay';
22
+ result: unknown;
23
+ } | {
24
+ kind: 'execute';
25
+ complete?: (result: Result) => ExecutionValue<Kind, void>;
26
+ };
27
+ type LifecycleMiddleware<Kind extends ExecutionKind, Context, Input, Raw> = (input: {
28
+ operation: string;
29
+ context: Context;
30
+ command: Input;
31
+ next: (options?: {
32
+ context?: object;
33
+ }) => ExecutionValue<Kind, Raw>;
34
+ }) => ExecutionValue<Kind, Raw>;
35
+ type LifecycleExecution<Kind extends ExecutionKind, Context, Input, Raw, Result> = {
36
+ operation: string;
37
+ classification: string;
38
+ context: Context;
39
+ command: Input;
40
+ permission?: () => ExecutionValue<Kind, void>;
41
+ prepare: () => ExecutionValue<Kind, LifecyclePreparation<Kind, Result> | undefined>;
42
+ middleware: readonly LifecycleMiddleware<Kind, Context, Input, Raw>[];
43
+ defaultGuard: (context: Context) => ExecutionValue<Kind, void>;
44
+ guard: (context: Context) => ExecutionValue<Kind, void>;
45
+ run: (context: Context) => ExecutionValue<Kind, Raw>;
46
+ audit: (result: Result) => ExecutionValue<Kind, Omit<AuditEntryInput, 'classification'> | null>;
47
+ aggregates?: readonly string[];
48
+ writeAudit: (entry: AuditEntryInput) => ExecutionValue<Kind, unknown>;
49
+ } & ({
50
+ parseReplay: <Value>(value: Value) => Result;
51
+ decodeReplay?: (value: unknown) => ExecutionValue<Kind, Result>;
52
+ } | {
53
+ parseReplay?: never;
54
+ decodeReplay: (value: unknown) => ExecutionValue<Kind, Result>;
55
+ }) & ({
56
+ parseResult: (value: Raw) => Result;
57
+ decodeResult?: (value: Raw) => ExecutionValue<Kind, Result>;
58
+ } | {
59
+ parseResult?: never;
60
+ decodeResult: (value: Raw) => ExecutionValue<Kind, Result>;
61
+ });
62
+ /** One owner for the command protocol; every interpretation allocates fresh dispatch state. */
63
+ declare function executeCommandLifecycle<Kind extends ExecutionKind, Context, Input, Raw, Result>(algebra: LifecycleAlgebra<Kind>, execution: LifecycleExecution<Kind, Context, Input, Raw, Result>): ExecutionValue<Kind, Result>;
64
+ declare class CommandMiddlewareError extends Error {
65
+ readonly code = "COMMAND_MIDDLEWARE_NEXT_REUSED";
66
+ readonly name = "CommandMiddlewareError";
67
+ constructor(operation: string);
68
+ }
69
+ declare class CommandAggregateError extends Error {
70
+ readonly code = "COMMAND_AGGREGATE_NOT_DECLARED";
71
+ readonly name = "CommandAggregateError";
72
+ constructor(operation: string, aggregateType: string);
73
+ }
74
+ declare class CommandPermissionError extends Error {
75
+ readonly code = "COMMAND_PERMISSION_NOT_CONFIGURED";
76
+ readonly name = "CommandPermissionError";
77
+ constructor(operation: string);
78
+ }
79
+ //#endregion
80
+ export { CommandAggregateError, CommandMiddlewareError, CommandPermissionError, ExecutionKind, ExecutionValue, LifecycleAlgebra, LifecycleExecution, LifecycleMiddleware, LifecyclePreparation, PromiseExecution, executeCommandLifecycle, promiseLifecycle };
81
+ //# sourceMappingURL=lifecycle.d.mts.map
@@ -0,0 +1,2 @@
1
+ const e={succeed:e=>Promise.resolve(e),suspend:e=>Promise.resolve().then(e),flatMap:(e,t)=>e.then(t)};function t(e,t){return e.suspend(()=>{let i=-1,a=n=>e.flatMap(t.defaultGuard(n),()=>e.flatMap(t.guard(n),()=>t.run(n))),o=(r,s)=>e.suspend(()=>{if(r<=i)throw new n(t.operation);i=r;let e=t.middleware[r];return e?e({operation:t.operation,command:t.command,context:s,next:e=>o(r+1,e?.context?{...s,...e.context}:s)}):a(s)});return e.flatMap(t.permission?.()??e.succeed(void 0),()=>e.flatMap(t.prepare(),n=>n?.kind===`replay`?e.suspend(()=>t.decodeReplay?t.decodeReplay(n.result):e.succeed(t.parseReplay(n.result))):e.flatMap(o(0,t.context),i=>e.flatMap(t.decodeResult?t.decodeResult(i):e.succeed(t.parseResult(i)),i=>e.flatMap(t.audit(i),a=>{if(a&&t.aggregates&&!t.aggregates.includes(a.aggregate.type))throw new r(t.operation,a.aggregate.type);return e.flatMap(a?t.writeAudit({...a,classification:t.classification}):e.succeed(void 0),()=>e.flatMap(n?.complete?.(i)??e.succeed(void 0),()=>e.succeed(i)))})))))})}var n=class extends Error{code=`COMMAND_MIDDLEWARE_NEXT_REUSED`;name=`CommandMiddlewareError`;constructor(e){super(`A middleware for operation "${e}" called next() more than once`)}},r=class extends Error{code=`COMMAND_AGGREGATE_NOT_DECLARED`;name=`CommandAggregateError`;constructor(e,t){super(`Operation "${e}" audited aggregate type "${t}" outside its declared aggregates`)}},i=class extends Error{code=`COMMAND_PERMISSION_NOT_CONFIGURED`;name=`CommandPermissionError`;constructor(e){super(`Operation "${e}" declares a permission but the Foundation has no checkPermission`)}};export{r as CommandAggregateError,n as CommandMiddlewareError,i as CommandPermissionError,t as executeCommandLifecycle,e as promiseLifecycle};
2
+ //# sourceMappingURL=lifecycle.mjs.map