cvx-kit 0.1.6 → 0.1.8

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 (116) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +63 -38
  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 +16 -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 +38 -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 +63 -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 +69 -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/workflow.d.mts +2 -0
  98. package/dist/workflow.mjs +1 -0
  99. package/docs/architecture.md +3 -3
  100. package/docs/auth.md +71 -0
  101. package/docs/command-context.md +150 -0
  102. package/docs/commands-legacy.md +511 -0
  103. package/docs/commands.md +143 -363
  104. package/docs/conventions.md +15 -5
  105. package/docs/crud.md +4 -0
  106. package/docs/domain-operations.md +615 -0
  107. package/docs/effect.md +395 -0
  108. package/docs/llms.md +69 -45
  109. package/docs/migration.md +1 -1
  110. package/docs/upgrading.md +9 -0
  111. package/package.json +33 -1
  112. package/scripts/convex-smoke-env.mjs +33 -0
  113. package/scripts/smoke-packed-components.mjs +9 -2
  114. package/scripts/smoke-packed-effect.mjs +439 -0
  115. package/scripts/test-packed-effect.mjs +460 -0
  116. package/scripts/test-packed-ids.mjs +1 -0
@@ -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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.mjs","names":[],"sources":["../../../../../src/components/foundation/modules/command/lifecycle.ts"],"sourcesContent":["import type { AuditEntryInput } from '../../client'\n\n/** Internal sequencing only: interpreters own callback normalization and observation. */\nexport interface ExecutionKind {\n\treadonly value: unknown\n\treadonly type: unknown\n}\nexport type ExecutionValue<Kind extends ExecutionKind, Value> = (Kind & {\n\treadonly value: Value\n})['type']\nexport interface LifecycleAlgebra<Kind extends ExecutionKind> {\n\tsucceed<Value>(value: Value): ExecutionValue<Kind, Value>\n\tsuspend<Value>(execute: () => ExecutionValue<Kind, Value>): ExecutionValue<Kind, Value>\n\tflatMap<Input, Output>(\n\t\texecution: ExecutionValue<Kind, Input>,\n\t\tnext: (input: Input) => ExecutionValue<Kind, Output>,\n\t): ExecutionValue<Kind, Output>\n}\nexport interface PromiseExecution extends ExecutionKind {\n\treadonly type: Promise<this['value']>\n}\nexport const promiseLifecycle: LifecycleAlgebra<PromiseExecution> = {\n\tsucceed: (value) => Promise.resolve(value),\n\tsuspend: (execute) => Promise.resolve().then(execute),\n\tflatMap: (execution, next) => execution.then(next),\n}\nexport type LifecyclePreparation<Kind extends ExecutionKind, Result> =\n\t| { kind: 'replay'; result: unknown }\n\t| { kind: 'execute'; complete?: (result: Result) => ExecutionValue<Kind, void> }\nexport type LifecycleMiddleware<Kind extends ExecutionKind, Context, Input, Raw> = (input: {\n\toperation: string\n\tcontext: Context\n\tcommand: Input\n\tnext: (options?: { context?: object }) => ExecutionValue<Kind, Raw>\n}) => ExecutionValue<Kind, Raw>\nexport type LifecycleExecution<Kind extends ExecutionKind, Context, Input, Raw, Result> = {\n\toperation: string\n\tclassification: string\n\tcontext: Context\n\tcommand: Input\n\tpermission?: () => ExecutionValue<Kind, void>\n\tprepare: () => ExecutionValue<Kind, LifecyclePreparation<Kind, Result> | undefined>\n\tmiddleware: readonly LifecycleMiddleware<Kind, Context, Input, Raw>[]\n\tdefaultGuard: (context: Context) => ExecutionValue<Kind, void>\n\tguard: (context: Context) => ExecutionValue<Kind, void>\n\trun: (context: Context) => ExecutionValue<Kind, Raw>\n\taudit: (result: Result) => ExecutionValue<Kind, Omit<AuditEntryInput, 'classification'> | null>\n\taggregates?: readonly string[]\n\twriteAudit: (entry: AuditEntryInput) => ExecutionValue<Kind, unknown>\n} & (\n\t| {\n\t\t\tparseReplay: <Value>(value: Value) => Result\n\t\t\t// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Replay decoding validates untrusted stored output at the interpreter boundary.\n\t\t\tdecodeReplay?: (value: unknown) => ExecutionValue<Kind, Result>\n\t }\n\t// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Replay decoding validates untrusted stored output at the interpreter boundary.\n\t| { parseReplay?: never; decodeReplay: (value: unknown) => ExecutionValue<Kind, Result> }\n) &\n\t(\n\t\t| {\n\t\t\t\tparseResult: (value: Raw) => Result\n\t\t\t\tdecodeResult?: (value: Raw) => ExecutionValue<Kind, Result>\n\t\t }\n\t\t| { parseResult?: never; decodeResult: (value: Raw) => ExecutionValue<Kind, Result> }\n\t)\n\n/** One owner for the command protocol; every interpretation allocates fresh dispatch state. */\nexport function executeCommandLifecycle<Kind extends ExecutionKind, Context, Input, Raw, Result>(\n\talgebra: LifecycleAlgebra<Kind>,\n\texecution: LifecycleExecution<Kind, Context, Input, Raw, Result>,\n): ExecutionValue<Kind, Result> {\n\treturn algebra.suspend(() => {\n\t\tlet deepest = -1\n\t\tconst terminal = (context: Context): ExecutionValue<Kind, Raw> =>\n\t\t\talgebra.flatMap(execution.defaultGuard(context), () =>\n\t\t\t\talgebra.flatMap(execution.guard(context), () => execution.run(context)),\n\t\t\t)\n\t\tconst dispatch = (index: number, context: Context): ExecutionValue<Kind, Raw> =>\n\t\t\talgebra.suspend(() => {\n\t\t\t\tif (index <= deepest) throw new CommandMiddlewareError(execution.operation)\n\t\t\t\tdeepest = index\n\t\t\t\tconst layer = execution.middleware[index]\n\t\t\t\tif (!layer) return terminal(context)\n\t\t\t\treturn layer({\n\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\tcommand: execution.command,\n\t\t\t\t\tcontext,\n\t\t\t\t\tnext: (options) =>\n\t\t\t\t\t\tdispatch(index + 1, options?.context ? { ...context, ...options.context } : context),\n\t\t\t\t})\n\t\t\t})\n\t\treturn algebra.flatMap(execution.permission?.() ?? algebra.succeed(undefined), () =>\n\t\t\talgebra.flatMap(execution.prepare(), (preparation) => {\n\t\t\t\tif (preparation?.kind === 'replay') {\n\t\t\t\t\treturn algebra.suspend(() =>\n\t\t\t\t\t\texecution.decodeReplay\n\t\t\t\t\t\t\t? execution.decodeReplay(preparation.result)\n\t\t\t\t\t\t\t: algebra.succeed(execution.parseReplay!(preparation.result)),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn algebra.flatMap(dispatch(0, execution.context), (raw) => {\n\t\t\t\t\treturn algebra.flatMap(\n\t\t\t\t\t\texecution.decodeResult\n\t\t\t\t\t\t\t? execution.decodeResult(raw)\n\t\t\t\t\t\t\t: algebra.succeed(execution.parseResult!(raw)),\n\t\t\t\t\t\t(result) =>\n\t\t\t\t\t\t\talgebra.flatMap(execution.audit(result), (audit) => {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\taudit &&\n\t\t\t\t\t\t\t\t\texecution.aggregates &&\n\t\t\t\t\t\t\t\t\t!execution.aggregates.includes(audit.aggregate.type)\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tthrow new CommandAggregateError(execution.operation, audit.aggregate.type)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn algebra.flatMap(\n\t\t\t\t\t\t\t\t\taudit\n\t\t\t\t\t\t\t\t\t\t? execution.writeAudit({ ...audit, classification: execution.classification })\n\t\t\t\t\t\t\t\t\t\t: algebra.succeed(undefined),\n\t\t\t\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\t\t\t\talgebra.flatMap(\n\t\t\t\t\t\t\t\t\t\t\tpreparation?.complete?.(result) ?? algebra.succeed(undefined),\n\t\t\t\t\t\t\t\t\t\t\t() => algebra.succeed(result),\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t})\n\t\t\t}),\n\t\t)\n\t})\n}\n\nexport class CommandMiddlewareError extends Error {\n\treadonly code = 'COMMAND_MIDDLEWARE_NEXT_REUSED'\n\treadonly name = 'CommandMiddlewareError'\n\tconstructor(operation: string) {\n\t\tsuper(`A middleware for operation \"${operation}\" called next() more than once`)\n\t}\n}\nexport class CommandAggregateError extends Error {\n\treadonly code = 'COMMAND_AGGREGATE_NOT_DECLARED'\n\treadonly name = 'CommandAggregateError'\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}\nexport class CommandPermissionError extends Error {\n\treadonly code = 'COMMAND_PERMISSION_NOT_CONFIGURED'\n\treadonly name = 'CommandPermissionError'\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"],"mappings":"AAqBA,MAAa,EAAuD,CACnE,QAAU,GAAU,QAAQ,QAAQ,CAAK,EACzC,QAAU,GAAY,QAAQ,QAAQ,CAAC,CAAC,KAAK,CAAO,EACpD,SAAU,EAAW,IAAS,EAAU,KAAK,CAAI,CAClD,EA0CA,SAAgB,EACf,EACA,EAC+B,CAC/B,OAAO,EAAQ,YAAc,CAC5B,IAAI,EAAU,GACR,EAAY,GACjB,EAAQ,QAAQ,EAAU,aAAa,CAAO,MAC7C,EAAQ,QAAQ,EAAU,MAAM,CAAO,MAAS,EAAU,IAAI,CAAO,CAAC,CACvE,EACK,GAAY,EAAe,IAChC,EAAQ,YAAc,CACrB,GAAI,GAAS,EAAS,MAAM,IAAI,EAAuB,EAAU,SAAS,EAC1E,EAAU,EACV,IAAM,EAAQ,EAAU,WAAW,GAEnC,OADK,EACE,EAAM,CACZ,UAAW,EAAU,UACrB,QAAS,EAAU,QACnB,UACA,KAAO,GACN,EAAS,EAAQ,EAAG,GAAS,QAAU,CAAE,GAAG,EAAS,GAAG,EAAQ,OAAQ,EAAI,CAAO,CACrF,CAAC,EAPkB,EAAS,CAAO,CAQpC,CAAC,EACF,OAAO,EAAQ,QAAQ,EAAU,aAAa,GAAK,EAAQ,QAAQ,IAAA,EAAS,MAC3E,EAAQ,QAAQ,EAAU,QAAQ,EAAI,GACjC,GAAa,OAAS,SAClB,EAAQ,YACd,EAAU,aACP,EAAU,aAAa,EAAY,MAAM,EACzC,EAAQ,QAAQ,EAAU,YAAa,EAAY,MAAM,CAAC,CAC9D,EAEM,EAAQ,QAAQ,EAAS,EAAG,EAAU,OAAO,EAAI,GAChD,EAAQ,QACd,EAAU,aACP,EAAU,aAAa,CAAG,EAC1B,EAAQ,QAAQ,EAAU,YAAa,CAAG,CAAC,EAC7C,GACA,EAAQ,QAAQ,EAAU,MAAM,CAAM,EAAI,GAAU,CACnD,GACC,GACA,EAAU,YACV,CAAC,EAAU,WAAW,SAAS,EAAM,UAAU,IAAI,EAEnD,MAAM,IAAI,EAAsB,EAAU,UAAW,EAAM,UAAU,IAAI,EAE1E,OAAO,EAAQ,QACd,EACG,EAAU,WAAW,CAAE,GAAG,EAAO,eAAgB,EAAU,cAAe,CAAC,EAC3E,EAAQ,QAAQ,IAAA,EAAS,MAE3B,EAAQ,QACP,GAAa,WAAW,CAAM,GAAK,EAAQ,QAAQ,IAAA,EAAS,MACtD,EAAQ,QAAQ,CAAM,CAC7B,CACF,CACD,CAAC,CACH,CACA,CACD,CACF,CACD,CAAC,CACF,CAEA,IAAa,EAAb,cAA4C,KAAM,CACjD,KAAgB,iCAChB,KAAgB,yBAChB,YAAY,EAAmB,CAC9B,MAAM,+BAA+B,EAAU,+BAA+B,CAC/E,CACD,EACa,EAAb,cAA2C,KAAM,CAChD,KAAgB,iCAChB,KAAgB,wBAChB,YAAY,EAAmB,EAAuB,CACrD,MACC,cAAc,EAAU,4BAA4B,EAAc,kCACnE,CACD,CACD,EACa,EAAb,cAA4C,KAAM,CACjD,KAAgB,oCAChB,KAAgB,yBAChB,YAAY,EAAmB,CAC9B,MACC,cAAc,EAAU,kEACzB,CACD,CACD"}
@@ -25,6 +25,14 @@ declare class Observability {
25
25
  operation: string;
26
26
  classification: string;
27
27
  }>, execute: () => Result | Promise<Result>): Promise<Result>;
28
+ /** Inert synchronous exit hooks for execution interpreters that own their runtime. */
29
+ start(activation: Readonly<{
30
+ operation: string;
31
+ classification: string;
32
+ }>): {
33
+ completed: () => void;
34
+ failed: (cause: unknown) => void;
35
+ };
28
36
  }
29
37
  //#endregion
30
38
  export { CommandObservation, Observability, ObservabilityOptions, observability_d_exports };
@@ -1,2 +1,2 @@
1
- const e=/^[A-Za-z][A-Za-z0-9_.-]{0,159}$/,t=/^[A-Z][A-Z0-9_]{0,95}$/;var n=class{#e;constructor(e){this.#e=e}async observe(e,t){let n=this.#n();try{let r=await t();return this.#t({...e,outcome:`completed`,durationMs:this.#r(n)}),r}catch(t){try{this.#t({...e,...this.#e.classifyError(t),durationMs:this.#r(n)})}catch{}throw t}}#t(n){try{let r=this.#e.enabled;if((r===void 0||r===!0||r===!1?r:r())!==!0||!e.test(n.operation)||!e.test(n.classification)||n.errorCode!==void 0&&!t.test(n.errorCode))return;if(this.#e.emit){this.#e.emit(Object.freeze(n));return}console.info(JSON.stringify({event:`command.execution`,...n}))}catch{}}#n(){try{return this.#e.clock?.()??Date.now()}catch{return Date.now()}}#r(e){return Math.max(0,Math.min(6e5,this.#n()-e))}};export{n as Observability};
1
+ const e=/^[A-Za-z][A-Za-z0-9_.-]{0,159}$/,t=/^[A-Z][A-Z0-9_]{0,95}$/;var n=class{#e;constructor(e){this.#e=e}async observe(e,t){let n=this.start(e);try{let e=await t();return n.completed(),e}catch(e){throw n.failed(e),e}}start(e){let t=this.#n();return{completed:()=>this.#t({...e,outcome:`completed`,durationMs:this.#r(t)}),failed:n=>{try{this.#t({...e,...this.#e.classifyError(n),durationMs:this.#r(t)})}catch{}}}}#t(n){try{let r=this.#e.enabled;if((r===void 0||r===!0||r===!1?r:r())!==!0||!e.test(n.operation)||!e.test(n.classification)||n.errorCode!==void 0&&!t.test(n.errorCode))return;if(this.#e.emit){this.#e.emit(Object.freeze(n));return}console.info(JSON.stringify({event:`command.execution`,...n}))}catch{}}#n(){try{return this.#e.clock?.()??Date.now()}catch{return Date.now()}}#r(e){return Math.max(0,Math.min(6e5,this.#n()-e))}};export{n as Observability};
2
2
  //# sourceMappingURL=observability.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"observability.mjs","names":["#options","#now","#emit","#duration"],"sources":["../../../../../src/components/foundation/modules/observability/observability.ts"],"sourcesContent":["export type CommandObservation = Readonly<{\n\toperation: string\n\tclassification: string\n\toutcome: 'completed' | 'denied' | 'failed'\n\terrorCode?: string\n\tdurationMs: number\n}>\n\nexport type ObservabilityOptions = Readonly<{\n\tenabled?: boolean | (() => boolean)\n\tclassifyError: (cause: unknown) => Readonly<{\n\t\toutcome: 'denied' | 'failed'\n\t\terrorCode: string\n\t}>\n\temit?: (observation: CommandObservation) => void\n\tclock?: () => number\n}>\n\nconst identifier = /^[A-Za-z][A-Za-z0-9_.-]{0,159}$/\nconst errorCode = /^[A-Z][A-Z0-9_]{0,95}$/\n\n/** Payload-free command telemetry that cannot change command behavior. */\nexport class Observability {\n\treadonly #options: ObservabilityOptions\n\n\tconstructor(options: ObservabilityOptions) {\n\t\tthis.#options = options\n\t}\n\n\tasync observe<Result>(\n\t\tactivation: Readonly<{ operation: string; classification: string }>,\n\t\texecute: () => Result | Promise<Result>,\n\t): Promise<Result> {\n\t\tconst started = this.#now()\n\t\ttry {\n\t\t\tconst result = await execute()\n\t\t\tthis.#emit({\n\t\t\t\t...activation,\n\t\t\t\toutcome: 'completed',\n\t\t\t\tdurationMs: this.#duration(started),\n\t\t\t})\n\t\t\treturn result\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tthis.#emit({\n\t\t\t\t\t...activation,\n\t\t\t\t\t...this.#options.classifyError(error),\n\t\t\t\t\tdurationMs: this.#duration(started),\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\t// Preserve the original command failure when telemetry fails.\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t#emit(observation: CommandObservation): void {\n\t\ttry {\n\t\t\tconst option = this.#options.enabled\n\t\t\tconst enabled =\n\t\t\t\toption === undefined || option === true || option === false ? option : option()\n\t\t\tif (\n\t\t\t\tenabled !== true ||\n\t\t\t\t!identifier.test(observation.operation) ||\n\t\t\t\t!identifier.test(observation.classification) ||\n\t\t\t\t(observation.errorCode !== undefined && !errorCode.test(observation.errorCode))\n\t\t\t) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.#options.emit) {\n\t\t\t\tthis.#options.emit(Object.freeze(observation))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconsole.info(JSON.stringify({ event: 'command.execution', ...observation }))\n\t\t} catch {\n\t\t\t// Observability is intentionally behaviorally inert.\n\t\t}\n\t}\n\n\t#now(): number {\n\t\ttry {\n\t\t\treturn this.#options.clock?.() ?? Date.now()\n\t\t} catch {\n\t\t\treturn Date.now()\n\t\t}\n\t}\n\n\t#duration(started: number): number {\n\t\treturn Math.max(0, Math.min(600_000, this.#now() - started))\n\t}\n}\n"],"mappings":"AAkBA,MAAM,EAAa,kCACb,EAAY,yBAGlB,IAAa,EAAb,KAA2B,CAC1B,GAEA,YAAY,EAA+B,CAC1C,KAAKA,GAAW,CACjB,CAEA,MAAM,QACL,EACA,EACkB,CAClB,IAAM,EAAU,KAAKC,GAAK,EAC1B,GAAI,CACH,IAAM,EAAS,MAAM,EAAQ,EAM7B,OALA,KAAKC,GAAM,CACV,GAAG,EACH,QAAS,YACT,WAAY,KAAKC,GAAU,CAAO,CACnC,CAAC,EACM,CACR,OAAS,EAAO,CACf,GAAI,CACH,KAAKD,GAAM,CACV,GAAG,EACH,GAAG,KAAKF,GAAS,cAAc,CAAK,EACpC,WAAY,KAAKG,GAAU,CAAO,CACnC,CAAC,CACF,MAAQ,CAER,CACA,MAAM,CACP,CACD,CAEA,GAAM,EAAuC,CAC5C,GAAI,CACH,IAAM,EAAS,KAAKH,GAAS,QAG7B,IADC,IAAW,IAAA,IAAa,IAAW,IAAQ,IAAW,GAAQ,EAAS,EAAO,KAElE,IACZ,CAAC,EAAW,KAAK,EAAY,SAAS,GACtC,CAAC,EAAW,KAAK,EAAY,cAAc,GAC1C,EAAY,YAAc,IAAA,IAAa,CAAC,EAAU,KAAK,EAAY,SAAS,EAE7E,OAED,GAAI,KAAKA,GAAS,KAAM,CACvB,KAAKA,GAAS,KAAK,OAAO,OAAO,CAAW,CAAC,EAC7C,MACD,CACA,QAAQ,KAAK,KAAK,UAAU,CAAE,MAAO,oBAAqB,GAAG,CAAY,CAAC,CAAC,CAC5E,MAAQ,CAER,CACD,CAEA,IAAe,CACd,GAAI,CACH,OAAO,KAAKA,GAAS,QAAQ,GAAK,KAAK,IAAI,CAC5C,MAAQ,CACP,OAAO,KAAK,IAAI,CACjB,CACD,CAEA,GAAU,EAAyB,CAClC,OAAO,KAAK,IAAI,EAAG,KAAK,IAAI,IAAS,KAAKC,GAAK,EAAI,CAAO,CAAC,CAC5D,CACD"}
1
+ {"version":3,"file":"observability.mjs","names":["#options","#now","#emit","#duration"],"sources":["../../../../../src/components/foundation/modules/observability/observability.ts"],"sourcesContent":["export type CommandObservation = Readonly<{\n\toperation: string\n\tclassification: string\n\toutcome: 'completed' | 'denied' | 'failed'\n\terrorCode?: string\n\tdurationMs: number\n}>\n\nexport type ObservabilityOptions = Readonly<{\n\tenabled?: boolean | (() => boolean)\n\tclassifyError: (cause: unknown) => Readonly<{\n\t\toutcome: 'denied' | 'failed'\n\t\terrorCode: string\n\t}>\n\temit?: (observation: CommandObservation) => void\n\tclock?: () => number\n}>\n\nconst identifier = /^[A-Za-z][A-Za-z0-9_.-]{0,159}$/\nconst errorCode = /^[A-Z][A-Z0-9_]{0,95}$/\n\n/** Payload-free command telemetry that cannot change command behavior. */\nexport class Observability {\n\treadonly #options: ObservabilityOptions\n\n\tconstructor(options: ObservabilityOptions) {\n\t\tthis.#options = options\n\t}\n\n\tasync observe<Result>(\n\t\tactivation: Readonly<{ operation: string; classification: string }>,\n\t\texecute: () => Result | Promise<Result>,\n\t): Promise<Result> {\n\t\tconst observation = this.start(activation)\n\t\ttry {\n\t\t\tconst result = await execute()\n\t\t\tobservation.completed()\n\t\t\treturn result\n\t\t} catch (error) {\n\t\t\tobservation.failed(error)\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/** Inert synchronous exit hooks for execution interpreters that own their runtime. */\n\tstart(activation: Readonly<{ operation: string; classification: string }>) {\n\t\tconst started = this.#now()\n\t\treturn {\n\t\t\tcompleted: () =>\n\t\t\t\tthis.#emit({\n\t\t\t\t\t...activation,\n\t\t\t\t\toutcome: 'completed',\n\t\t\t\t\tdurationMs: this.#duration(started),\n\t\t\t\t}),\n\t\t\tfailed: (cause: unknown) => {\n\t\t\t\ttry {\n\t\t\t\t\tthis.#emit({\n\t\t\t\t\t\t...activation,\n\t\t\t\t\t\t...this.#options.classifyError(cause),\n\t\t\t\t\t\tdurationMs: this.#duration(started),\n\t\t\t\t\t})\n\t\t\t\t} catch {\n\t\t\t\t\t// Preserve the original command failure when telemetry fails.\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t}\n\n\t#emit(observation: CommandObservation): void {\n\t\ttry {\n\t\t\tconst option = this.#options.enabled\n\t\t\tconst enabled =\n\t\t\t\toption === undefined || option === true || option === false ? option : option()\n\t\t\tif (\n\t\t\t\tenabled !== true ||\n\t\t\t\t!identifier.test(observation.operation) ||\n\t\t\t\t!identifier.test(observation.classification) ||\n\t\t\t\t(observation.errorCode !== undefined && !errorCode.test(observation.errorCode))\n\t\t\t) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.#options.emit) {\n\t\t\t\tthis.#options.emit(Object.freeze(observation))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconsole.info(JSON.stringify({ event: 'command.execution', ...observation }))\n\t\t} catch {\n\t\t\t// Observability is intentionally behaviorally inert.\n\t\t}\n\t}\n\n\t#now(): number {\n\t\ttry {\n\t\t\treturn this.#options.clock?.() ?? Date.now()\n\t\t} catch {\n\t\t\treturn Date.now()\n\t\t}\n\t}\n\n\t#duration(started: number): number {\n\t\treturn Math.max(0, Math.min(600_000, this.#now() - started))\n\t}\n}\n"],"mappings":"AAkBA,MAAM,EAAa,kCACb,EAAY,yBAGlB,IAAa,EAAb,KAA2B,CAC1B,GAEA,YAAY,EAA+B,CAC1C,KAAKA,GAAW,CACjB,CAEA,MAAM,QACL,EACA,EACkB,CAClB,IAAM,EAAc,KAAK,MAAM,CAAU,EACzC,GAAI,CACH,IAAM,EAAS,MAAM,EAAQ,EAE7B,OADA,EAAY,UAAU,EACf,CACR,OAAS,EAAO,CAEf,MADA,EAAY,OAAO,CAAK,EAClB,CACP,CACD,CAGA,MAAM,EAAqE,CAC1E,IAAM,EAAU,KAAKC,GAAK,EAC1B,MAAO,CACN,cACC,KAAKC,GAAM,CACV,GAAG,EACH,QAAS,YACT,WAAY,KAAKC,GAAU,CAAO,CACnC,CAAC,EACF,OAAS,GAAmB,CAC3B,GAAI,CACH,KAAKD,GAAM,CACV,GAAG,EACH,GAAG,KAAKF,GAAS,cAAc,CAAK,EACpC,WAAY,KAAKG,GAAU,CAAO,CACnC,CAAC,CACF,MAAQ,CAER,CACD,CACD,CACD,CAEA,GAAM,EAAuC,CAC5C,GAAI,CACH,IAAM,EAAS,KAAKH,GAAS,QAG7B,IADC,IAAW,IAAA,IAAa,IAAW,IAAQ,IAAW,GAAQ,EAAS,EAAO,KAElE,IACZ,CAAC,EAAW,KAAK,EAAY,SAAS,GACtC,CAAC,EAAW,KAAK,EAAY,cAAc,GAC1C,EAAY,YAAc,IAAA,IAAa,CAAC,EAAU,KAAK,EAAY,SAAS,EAE7E,OAED,GAAI,KAAKA,GAAS,KAAM,CACvB,KAAKA,GAAS,KAAK,OAAO,OAAO,CAAW,CAAC,EAC7C,MACD,CACA,QAAQ,KAAK,KAAK,UAAU,CAAE,MAAO,oBAAqB,GAAG,CAAY,CAAC,CAAC,CAC5E,MAAQ,CAER,CACD,CAEA,IAAe,CACd,GAAI,CACH,OAAO,KAAKA,GAAS,QAAQ,GAAK,KAAK,IAAI,CAC5C,MAAQ,CACP,OAAO,KAAK,IAAI,CACjB,CACD,CAEA,GAAU,EAAyB,CAClC,OAAO,KAAK,IAAI,EAAG,KAAK,IAAI,IAAS,KAAKC,GAAK,EAAI,CAAO,CAAC,CAC5D,CACD"}
@@ -1,4 +1,4 @@
1
- import { CommandHandlerResult, CommandInput, Parseable, SchemaInput } from "./modules/command/command.mjs";
1
+ import { CommandHandlerResult, CommandInput, CommandSchema, SchemaInput, SchemaOutput } from "./modules/command/command.mjs";
2
2
  import { AnyCommandMiddleware, AuditEntryInput, AuditedOperation, AuditedRegistry } from "./client.mjs";
3
3
  //#region src/components/foundation/operation.d.ts
4
4
  type MaybePromise<T> = T | Promise<T>;
@@ -9,7 +9,7 @@ type CommandPreparation<Result> = {
9
9
  kind: 'execute';
10
10
  complete?: (result: Result) => MaybePromise<void>;
11
11
  };
12
- type Parsed<S extends Parseable<unknown>> = ReturnType<S['parse']>;
12
+ type Parsed<S extends CommandSchema> = SchemaOutput<S>;
13
13
  declare const extension: unique symbol;
14
14
  declare const hostContext: unique symbol;
15
15
  type OperationHostContext<Context> = {
@@ -35,7 +35,7 @@ type AuditFor<Aggregate extends string> = Omit<AuditEntryInput, 'classification'
35
35
  id: string;
36
36
  };
37
37
  };
38
- type TypedOperation<Context, Input extends Parseable<unknown>, Output extends Parseable<unknown>, Aggregates extends readonly string[], Extension> = OperationHostContext<Context> & {
38
+ type TypedOperation<Context, Input extends CommandSchema, Output extends CommandSchema, Aggregates extends readonly string[], Extension> = OperationHostContext<Context> & {
39
39
  readonly command: Input;
40
40
  readonly result: Output;
41
41
  readonly classification: string;
@@ -43,7 +43,7 @@ type TypedOperation<Context, Input extends Parseable<unknown>, Output extends Pa
43
43
  /** Called after permission, inside observation; returns invocation-local hooks. */
44
44
  readonly prepare?: (context: Context, command: Parsed<Input>) => MaybePromise<CommandPreparation<Parsed<Output>>>;
45
45
  /** Validates stored outputs when result transforms accept a different input shape. */
46
- readonly replayResult?: Parseable<Parsed<Output>>;
46
+ readonly replayResult?: CommandSchema<Parsed<Output>>;
47
47
  readonly aggregates?: Aggregates;
48
48
  readonly guard?: (context: Context & Extension, command: Parsed<Input>) => MaybePromise<void>;
49
49
  readonly audit: (resolution: {
@@ -63,10 +63,10 @@ type RegistryInput<Context, Operations extends AuditedRegistry> = { [Key in Extr
63
63
  command: CommandInput<Operations, Key>;
64
64
  next: () => Promise<CommandHandlerResult<Operations, Key>>;
65
65
  }; }[Extract<keyof Operations, string>];
66
- type StoredOperation<Context, Input extends Parseable<unknown>, Output extends Parseable<unknown>, Aggregates extends readonly string[], Extension> = Omit<TypedOperation<Context, Input, Output, Aggregates, Extension>, 'middleware'> & Pick<AuditedOperation, 'middleware'>;
66
+ type StoredOperation<Context, Input extends CommandSchema, Output extends CommandSchema, Aggregates extends readonly string[], Extension> = Omit<TypedOperation<Context, Input, Output, Aggregates, Extension>, 'middleware'> & Pick<AuditedOperation, 'middleware'>;
67
67
  /** Binds host context once; each operation infers its own parsed schemas. */
68
68
  declare function operationFactory<Context, Extension extends Record<string, unknown> = Record<never, never>>(): {
69
- operation<const Input extends Parseable<unknown>, const Output extends Parseable<unknown>, const Aggregates extends readonly string[] = readonly string[]>(definition: TypedOperation<Context, Input, Output, Aggregates, Extension>): StoredOperation<Context, Input, Output, Aggregates, Extension>;
69
+ operation<const Input extends CommandSchema, const Output extends CommandSchema, const Aggregates extends readonly string[] = readonly string[]>(definition: TypedOperation<Context, Input, Output, Aggregates, Extension>): StoredOperation<Context, Input, Output, Aggregates, Extension>;
70
70
  registryMiddleware<const Operations extends AuditedRegistry>(operations: Operations & { readonly [Key in keyof Operations]: OperationHostContext<Context>; }, middleware: (input: RegistryInput<Context, Operations>) => Promise<CommandHandlerResult<Operations, Extract<keyof Operations, string>>>): AnyCommandMiddleware;
71
71
  };
72
72
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"operation.mjs","names":[],"sources":["../../../src/components/foundation/operation.ts"],"sourcesContent":["import type {\n\tAuditEntryInput,\n\tAuditedRegistry,\n\tAuditedOperation,\n\tAnyCommandMiddleware,\n} from './client'\nimport type {\n\tCommandInput,\n\tCommandHandlerResult,\n\tParseable,\n\tSchemaInput,\n} from './modules/command/command'\n\ntype MaybePromise<T> = T | Promise<T>\nexport type CommandPreparation<Result> =\n\t| { kind: 'replay'; result: unknown }\n\t| { kind: 'execute'; complete?: (result: Result) => MaybePromise<void> }\ntype Parsed<S extends Parseable<unknown>> = ReturnType<S['parse']>\ndeclare const extension: unique symbol\ndeclare const hostContext: unique symbol\nexport type OperationHostContext<Context> = {\n\treadonly [hostContext]?: (context: Context) => void\n}\nexport type OperationExtension<Definition> = Definition extends {\n\treadonly [extension]?: infer Extension\n}\n\t? Extension\n\t: unknown\n\ntype MiddlewareInput<Context, Input, Output, Extension> = {\n\toperation: string\n\tcontext: Context\n\tcommand: Input\n\tnext: keyof Extension extends never\n\t\t? (options?: { context?: Extension }) => Promise<Output>\n\t\t: (options: { context: Extension }) => Promise<Output>\n}\ntype LocalMiddleware<C, I, O, E> = (input: MiddlewareInput<C, I, O, E>) => Promise<O>\ntype AuditFor<Aggregate extends string> = Omit<AuditEntryInput, 'classification' | 'aggregate'> & {\n\taggregate: { type: Aggregate; id: string }\n}\nexport type TypedOperation<\n\tContext,\n\tInput extends Parseable<unknown>,\n\tOutput extends Parseable<unknown>,\n\tAggregates extends readonly string[],\n\tExtension,\n> = OperationHostContext<Context> & {\n\treadonly command: Input\n\treadonly result: Output\n\treadonly classification: string\n\treadonly permission?: string\n\t/** Called after permission, inside observation; returns invocation-local hooks. */\n\treadonly prepare?: (\n\t\tcontext: Context,\n\t\tcommand: Parsed<Input>,\n\t) => MaybePromise<CommandPreparation<Parsed<Output>>>\n\t/** Validates stored outputs when result transforms accept a different input shape. */\n\treadonly replayResult?: Parseable<Parsed<Output>>\n\treadonly aggregates?: Aggregates\n\treadonly guard?: (context: Context & Extension, command: Parsed<Input>) => MaybePromise<void>\n\treadonly audit: (\n\t\tresolution: { command: Parsed<Input>; result: Parsed<Output> },\n\t\tcontext: Context,\n\t) => MaybePromise<AuditFor<Aggregates[number]> | null>\n\treadonly [extension]?: Extension\n} & (keyof Extension extends never\n\t\t? {\n\t\t\t\treadonly middleware?: readonly LocalMiddleware<\n\t\t\t\t\tContext,\n\t\t\t\t\tParsed<Input>,\n\t\t\t\t\tSchemaInput<Output>,\n\t\t\t\t\tExtension\n\t\t\t\t>[]\n\t\t\t}\n\t\t: {\n\t\t\t\treadonly middleware: readonly [\n\t\t\t\t\tLocalMiddleware<Context, Parsed<Input>, SchemaInput<Output>, Extension>,\n\t\t\t\t\t...LocalMiddleware<\n\t\t\t\t\t\tContext & Extension,\n\t\t\t\t\t\tParsed<Input>,\n\t\t\t\t\t\tSchemaInput<Output>,\n\t\t\t\t\t\tRecord<never, never>\n\t\t\t\t\t>[],\n\t\t\t\t]\n\t\t\t})\n\ntype RegistryInput<Context, Operations extends AuditedRegistry> = {\n\t[Key in Extract<keyof Operations, string>]: {\n\t\toperation: Key\n\t\tdefinition: Operations[Key]\n\t\tcontext: Context\n\t\tcommand: CommandInput<Operations, Key>\n\t\tnext: () => Promise<CommandHandlerResult<Operations, Key>>\n\t}\n}[Extract<keyof Operations, string>]\n\ntype StoredOperation<\n\tContext,\n\tInput extends Parseable<unknown>,\n\tOutput extends Parseable<unknown>,\n\tAggregates extends readonly string[],\n\tExtension,\n> = Omit<TypedOperation<Context, Input, Output, Aggregates, Extension>, 'middleware'> &\n\tPick<AuditedOperation, 'middleware'>\n\n/** Binds host context once; each operation infers its own parsed schemas. */\nexport function operationFactory<\n\tContext,\n\tExtension extends Record<string, unknown> = Record<never, never>,\n>() {\n\treturn {\n\t\toperation<\n\t\t\tconst Input extends Parseable<unknown>,\n\t\t\tconst Output extends Parseable<unknown>,\n\t\t\tconst Aggregates extends readonly string[] = readonly string[],\n\t\t>(\n\t\t\tdefinition: TypedOperation<Context, Input, Output, Aggregates, Extension>,\n\t\t): StoredOperation<Context, Input, Output, Aggregates, Extension> {\n\t\t\t// SAFETY: the kernel selects this definition and parses its input before\n\t\t\t// invoking the heterogeneous middleware registry. Erase only that\n\t\t\t// storage seam; callbacks were checked against these schemas above.\n\t\t\treturn definition as StoredOperation<Context, Input, Output, Aggregates, Extension>\n\t\t},\n\t\tregistryMiddleware<const Operations extends AuditedRegistry>(\n\t\t\toperations: Operations & {\n\t\t\t\treadonly [Key in keyof Operations]: OperationHostContext<Context>\n\t\t\t},\n\t\t\tmiddleware: (\n\t\t\t\tinput: RegistryInput<Context, Operations>,\n\t\t\t) => Promise<CommandHandlerResult<Operations, Extract<keyof Operations, string>>>,\n\t\t): AnyCommandMiddleware {\n\t\t\tfunction ownsDefinition(\n\t\t\t\tinput: Parameters<AnyCommandMiddleware>[0],\n\t\t\t): input is Parameters<AnyCommandMiddleware>[0] & RegistryInput<Context, Operations> {\n\t\t\t\treturn (\n\t\t\t\t\tObject.prototype.hasOwnProperty.call(operations, input.operation) &&\n\t\t\t\t\toperations[input.operation] === input.definition\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn (input) => {\n\t\t\t\tif (!ownsDefinition(input)) {\n\t\t\t\t\tthrow new CommandMiddlewareRegistryError(input.operation)\n\t\t\t\t}\n\t\t\t\treturn middleware(input)\n\t\t\t}\n\t\t},\n\t}\n}\n\nclass CommandMiddlewareRegistryError extends Error {\n\treadonly code = 'COMMAND_MIDDLEWARE_REGISTRY_MISMATCH'\n\tconstructor(operation: string) {\n\t\tsuper(`Typed middleware does not own the definition of operation \"${operation}\"`)\n\t\tthis.name = 'CommandMiddlewareRegistryError'\n\t}\n}\n"],"mappings":"AA2GA,SAAgB,GAGZ,CACH,MAAO,CACN,UAKC,EACiE,CAIjE,OAAO,CACR,EACA,mBACC,EAGA,EAGuB,CACvB,SAAS,EACR,EACoF,CACpF,OACC,OAAO,UAAU,eAAe,KAAK,EAAY,EAAM,SAAS,GAChE,EAAW,EAAM,aAAe,EAAM,UAExC,CACA,MAAQ,IAAU,CACjB,GAAI,CAAC,EAAe,CAAK,EACxB,MAAM,IAAI,EAA+B,EAAM,SAAS,EAEzD,OAAO,EAAW,CAAK,CACxB,CACD,CACD,CACD,CAEA,IAAM,EAAN,cAA6C,KAAM,CAClD,KAAgB,uCAChB,YAAY,EAAmB,CAC9B,MAAM,8DAA8D,EAAU,EAAE,EAChF,KAAK,KAAO,gCACb,CACD"}
1
+ {"version":3,"file":"operation.mjs","names":[],"sources":["../../../src/components/foundation/operation.ts"],"sourcesContent":["import type {\n\tAuditEntryInput,\n\tAuditedRegistry,\n\tAuditedOperation,\n\tAnyCommandMiddleware,\n} from './client'\nimport type {\n\tCommandInput,\n\tCommandHandlerResult,\n\tCommandSchema,\n\tSchemaOutput,\n\tSchemaInput,\n} from './modules/command/command'\n\ntype MaybePromise<T> = T | Promise<T>\nexport type CommandPreparation<Result> =\n\t| { kind: 'replay'; result: unknown }\n\t| { kind: 'execute'; complete?: (result: Result) => MaybePromise<void> }\ntype Parsed<S extends CommandSchema> = SchemaOutput<S>\ndeclare const extension: unique symbol\ndeclare const hostContext: unique symbol\nexport type OperationHostContext<Context> = {\n\treadonly [hostContext]?: (context: Context) => void\n}\nexport type OperationExtension<Definition> = Definition extends {\n\treadonly [extension]?: infer Extension\n}\n\t? Extension\n\t: unknown\n\ntype MiddlewareInput<Context, Input, Output, Extension> = {\n\toperation: string\n\tcontext: Context\n\tcommand: Input\n\tnext: keyof Extension extends never\n\t\t? (options?: { context?: Extension }) => Promise<Output>\n\t\t: (options: { context: Extension }) => Promise<Output>\n}\ntype LocalMiddleware<C, I, O, E> = (input: MiddlewareInput<C, I, O, E>) => Promise<O>\ntype AuditFor<Aggregate extends string> = Omit<AuditEntryInput, 'classification' | 'aggregate'> & {\n\taggregate: { type: Aggregate; id: string }\n}\nexport type TypedOperation<\n\tContext,\n\tInput extends CommandSchema,\n\tOutput extends CommandSchema,\n\tAggregates extends readonly string[],\n\tExtension,\n> = OperationHostContext<Context> & {\n\treadonly command: Input\n\treadonly result: Output\n\treadonly classification: string\n\treadonly permission?: string\n\t/** Called after permission, inside observation; returns invocation-local hooks. */\n\treadonly prepare?: (\n\t\tcontext: Context,\n\t\tcommand: Parsed<Input>,\n\t) => MaybePromise<CommandPreparation<Parsed<Output>>>\n\t/** Validates stored outputs when result transforms accept a different input shape. */\n\treadonly replayResult?: CommandSchema<Parsed<Output>>\n\treadonly aggregates?: Aggregates\n\treadonly guard?: (context: Context & Extension, command: Parsed<Input>) => MaybePromise<void>\n\treadonly audit: (\n\t\tresolution: { command: Parsed<Input>; result: Parsed<Output> },\n\t\tcontext: Context,\n\t) => MaybePromise<AuditFor<Aggregates[number]> | null>\n\treadonly [extension]?: Extension\n} & (keyof Extension extends never\n\t\t? {\n\t\t\t\treadonly middleware?: readonly LocalMiddleware<\n\t\t\t\t\tContext,\n\t\t\t\t\tParsed<Input>,\n\t\t\t\t\tSchemaInput<Output>,\n\t\t\t\t\tExtension\n\t\t\t\t>[]\n\t\t\t}\n\t\t: {\n\t\t\t\treadonly middleware: readonly [\n\t\t\t\t\tLocalMiddleware<Context, Parsed<Input>, SchemaInput<Output>, Extension>,\n\t\t\t\t\t...LocalMiddleware<\n\t\t\t\t\t\tContext & Extension,\n\t\t\t\t\t\tParsed<Input>,\n\t\t\t\t\t\tSchemaInput<Output>,\n\t\t\t\t\t\tRecord<never, never>\n\t\t\t\t\t>[],\n\t\t\t\t]\n\t\t\t})\n\ntype RegistryInput<Context, Operations extends AuditedRegistry> = {\n\t[Key in Extract<keyof Operations, string>]: {\n\t\toperation: Key\n\t\tdefinition: Operations[Key]\n\t\tcontext: Context\n\t\tcommand: CommandInput<Operations, Key>\n\t\tnext: () => Promise<CommandHandlerResult<Operations, Key>>\n\t}\n}[Extract<keyof Operations, string>]\n\ntype StoredOperation<\n\tContext,\n\tInput extends CommandSchema,\n\tOutput extends CommandSchema,\n\tAggregates extends readonly string[],\n\tExtension,\n> = Omit<TypedOperation<Context, Input, Output, Aggregates, Extension>, 'middleware'> &\n\tPick<AuditedOperation, 'middleware'>\n\n/** Binds host context once; each operation infers its own parsed schemas. */\nexport function operationFactory<\n\tContext,\n\tExtension extends Record<string, unknown> = Record<never, never>,\n>() {\n\treturn {\n\t\toperation<\n\t\t\tconst Input extends CommandSchema,\n\t\t\tconst Output extends CommandSchema,\n\t\t\tconst Aggregates extends readonly string[] = readonly string[],\n\t\t>(\n\t\t\tdefinition: TypedOperation<Context, Input, Output, Aggregates, Extension>,\n\t\t): StoredOperation<Context, Input, Output, Aggregates, Extension> {\n\t\t\t// SAFETY: the kernel selects this definition and parses its input before\n\t\t\t// invoking the heterogeneous middleware registry. Erase only that\n\t\t\t// storage seam; callbacks were checked against these schemas above.\n\t\t\treturn definition as StoredOperation<Context, Input, Output, Aggregates, Extension>\n\t\t},\n\t\tregistryMiddleware<const Operations extends AuditedRegistry>(\n\t\t\toperations: Operations & {\n\t\t\t\treadonly [Key in keyof Operations]: OperationHostContext<Context>\n\t\t\t},\n\t\t\tmiddleware: (\n\t\t\t\tinput: RegistryInput<Context, Operations>,\n\t\t\t) => Promise<CommandHandlerResult<Operations, Extract<keyof Operations, string>>>,\n\t\t): AnyCommandMiddleware {\n\t\t\tfunction ownsDefinition(\n\t\t\t\tinput: Parameters<AnyCommandMiddleware>[0],\n\t\t\t): input is Parameters<AnyCommandMiddleware>[0] & RegistryInput<Context, Operations> {\n\t\t\t\treturn (\n\t\t\t\t\tObject.prototype.hasOwnProperty.call(operations, input.operation) &&\n\t\t\t\t\toperations[input.operation] === input.definition\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn (input) => {\n\t\t\t\tif (!ownsDefinition(input)) {\n\t\t\t\t\tthrow new CommandMiddlewareRegistryError(input.operation)\n\t\t\t\t}\n\t\t\t\treturn middleware(input)\n\t\t\t}\n\t\t},\n\t}\n}\n\nclass CommandMiddlewareRegistryError extends Error {\n\treadonly code = 'COMMAND_MIDDLEWARE_REGISTRY_MISMATCH'\n\tconstructor(operation: string) {\n\t\tsuper(`Typed middleware does not own the definition of operation \"${operation}\"`)\n\t\tthis.name = 'CommandMiddlewareRegistryError'\n\t}\n}\n"],"mappings":"AA4GA,SAAgB,GAGZ,CACH,MAAO,CACN,UAKC,EACiE,CAIjE,OAAO,CACR,EACA,mBACC,EAGA,EAGuB,CACvB,SAAS,EACR,EACoF,CACpF,OACC,OAAO,UAAU,eAAe,KAAK,EAAY,EAAM,SAAS,GAChE,EAAW,EAAM,aAAe,EAAM,UAExC,CACA,MAAQ,IAAU,CACjB,GAAI,CAAC,EAAe,CAAK,EACxB,MAAM,IAAI,EAA+B,EAAM,SAAS,EAEzD,OAAO,EAAW,CAAK,CACxB,CACD,CACD,CACD,CAEA,IAAM,EAAN,cAA6C,KAAM,CAClD,KAAgB,uCAChB,YAAY,EAAmB,CAC9B,MAAM,8DAA8D,EAAU,EAAE,EAChF,KAAK,KAAO,gCACb,CACD"}
@@ -0,0 +1,4 @@
1
+ import { ContractExecution, ContractInput, ContractOutput, ContractSchema, Decoder, LegacyParser, NeutralSchema, StandardSchema, ValidationIssue, ValidationResult, decodeContract, operationContract } from "./modules/contracts/contract.mjs";
2
+ import { standardContract, zodContract } from "./modules/contracts/standard.mjs";
3
+ import { ConvexContractValue, convexContract } from "./modules/contracts/convex.mjs";
4
+ export { type ContractExecution, type ContractInput, type ContractOutput, type ContractSchema, type ConvexContractValue, type Decoder, type LegacyParser, type NeutralSchema, type StandardSchema, type ValidationIssue, type ValidationResult, convexContract, decodeContract, operationContract, standardContract, zodContract };
@@ -0,0 +1 @@
1
+ import{decodeContract as e,operationContract as t}from"./modules/contracts/contract.mjs";import{standardContract as n,zodContract as r}from"./modules/contracts/standard.mjs";import{convexContract as i}from"./modules/contracts/convex.mjs";export{i as convexContract,e as decodeContract,t as operationContract,n as standardContract,r as zodContract};
package/dist/crud.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ErrorFactory } from "./errors.mjs";
2
- import { SchemaInput } from "./components/foundation/modules/command/command.mjs";
2
+ import { SchemaInput, SchemaOutput } from "./components/foundation/modules/command/command.mjs";
3
3
  import { AnyCommandMiddleware, ApplicationCommand, AuditedOperation, CommandConstructor } from "./components/foundation/client.mjs";
4
4
  import { GenericDataModel, GenericMutationCtx } from "convex/server";
5
5
  import { z } from "zod";
@@ -33,7 +33,7 @@ type CrudConfig<Context extends CrudContext, Table extends CrudTable = CrudTable
33
33
  * REQUIRED when the table is a tenantTable — forgetting it would insert
34
34
  * untenanted rows; the factory fails fast at construction instead.
35
35
  */
36
- enrich?: (context: Context, command: ReturnType<Table['commandInput']['parse']>) => MaybePromise<Partial<z.input<Table['storage']>>>;
36
+ enrich?: (context: Context, command: SchemaOutput<Table['commandInput']>) => MaybePromise<Partial<z.input<Table['storage']>>>;
37
37
  /** Per-operation preconditions; run before the handlers. */
38
38
  guards?: {
39
39
  create?: (context: Context, command: never) => MaybePromise<void>;
package/dist/crud.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"crud.mjs","names":[],"sources":["../src/crud.ts"],"sourcesContent":["import { zid } from 'convex-helpers/server/zod4'\nimport { z } from 'zod'\nimport type { Value } from 'convex/values'\nimport type { GenericDataModel, GenericMutationCtx } from 'convex/server'\nimport type {\n\tAnyCommandMiddleware,\n\tApplicationCommand,\n\tAuditedOperation,\n\tCommandConstructor,\n} from './components/foundation/client'\nimport { defaultErrors, type ErrorFactory } from './errors'\n\n/**\n * The zodTable surface the factory consumes — structural, so any zodTable /\n * tenantTable return satisfies it.\n */\ntype CrudTable = {\n\ttableName: string\n\tcommandInput: z.ZodObject<Record<string, z.ZodType<Value | undefined>>>\n\tstorage: z.ZodObject<Record<string, z.ZodType<Value | undefined>>>\n}\n\ntype CrudContext = { db: Pick<GenericMutationCtx<GenericDataModel>['db'], 'insert' | 'patch'> }\n\ntype MaybePromise<Value> = Value | Promise<Value>\n\nexport type CrudConfig<Context extends CrudContext, Table extends CrudTable = CrudTable> = {\n\t/** The Foundation-bound Command class (destructured from the facade). */\n\tCommand: CommandConstructor\n\ttable: Table\n\t/** Audit aggregate type; enforced by the aggregates allowlist. */\n\taggregateType: string\n\t/** Observability/audit classification. Default 'business'. */\n\tclassification?: string\n\t/** Derives the audited actor id from the ctx (usually ctx.actor.userId). */\n\tactor: (context: Context) => string\n\t/**\n\t * Stamps server-owned fields onto creates (tenant, denormalized ids...).\n\t * REQUIRED when the table is a tenantTable — forgetting it would insert\n\t * untenanted rows; the factory fails fast at construction instead.\n\t */\n\tenrich?: (\n\t\tcontext: Context,\n\t\tcommand: ReturnType<Table['commandInput']['parse']>,\n\t) => MaybePromise<Partial<z.input<Table['storage']>>>\n\t/** Per-operation preconditions; run before the handlers. */\n\tguards?: {\n\t\tcreate?: (context: Context, command: never) => MaybePromise<void>\n\t\tupdate?: (context: Context, command: never) => MaybePromise<void>\n\t\tarchive?: (context: Context, command: never) => MaybePromise<void>\n\t}\n\t/** Per-operation middleware applied to all three generated operations. */\n\tmiddleware?: readonly AnyCommandMiddleware[]\n\terrors?: ErrorFactory\n}\n\n/**\n * Generates the standard create/update/archive operations from one zodTable\n * declaration, fully inside the audited command pipeline: strict command\n * inputs from `commandFields`, mandatory audit with the declared aggregate\n * type, soft delete only (archive sets `archivedAt` — no hard-delete\n * operation is generated, per kit doctrine).\n *\n * convex-helpers ships a same-named `crud` helper; it was deliberately not\n * used — it bypasses the command pipeline (no audits, no guards, and hard\n * deletes). See docs/crud.md.\n */\nexport type CrudCommands<\n\tContext extends CrudContext,\n\tTable extends CrudTable = CrudTable,\n> = ReturnType<typeof createCrudCommands<Context, Table>>\n\nexport function createCrudCommands<\n\tContext extends CrudContext,\n\tTable extends CrudTable = CrudTable,\n>(config: CrudConfig<Context, Table>) {\n\tconst errors = config.errors ?? defaultErrors\n\tconst name = config.table.tableName\n\tconst classification = config.classification ?? 'business'\n\tconst isTenantTable = 'tenant' in config.table.storage.shape\n\tif (isTenantTable && !config.enrich) {\n\t\treturn errors.throw({\n\t\t\tcode: 'CRUD_ENRICH_REQUIRED',\n\t\t\tmessage: `Table \"${name}\" is a tenantTable: createCrudCommands requires an enrich callback that stamps ctx.tenant`,\n\t\t})\n\t}\n\n\tconst commandSchema: Table['commandInput'] = config.table.commandInput\n\tconst id = zid(name)\n\tconst createResult = z.object({ id }).strict()\n\tconst okResult = z.object({ ok: z.literal(true) }).strict()\n\tconst updateInput = z.object({ id, data: commandSchema.partial().strict() }).strict()\n\tconst archiveInput = z.object({ id }).strict()\n\n\tconst createName = `${name}.create` as const\n\tconst updateName = `${name}.update` as const\n\tconst archiveName = `${name}.archive` as const\n\tconst operations = Object.assign(\n\t\t{},\n\t\tnamedOperation(createName, {\n\t\t\tcommand: commandSchema,\n\t\t\tresult: createResult,\n\t\t\tclassification,\n\t\t\taggregates: [config.aggregateType],\n\t\t\tmiddleware: config.middleware,\n\t\t\tguard: config.guards?.create,\n\t\t\taudit: (resolution: { result: z.output<typeof createResult> }, context: Context) => ({\n\t\t\t\toperation: createName,\n\t\t\t\tactorId: config.actor(context),\n\t\t\t\taggregate: { type: config.aggregateType, id: resolution.result.id },\n\t\t\t}),\n\t\t}),\n\t\tnamedOperation(updateName, {\n\t\t\tcommand: updateInput,\n\t\t\tresult: okResult,\n\t\t\tclassification,\n\t\t\taggregates: [config.aggregateType],\n\t\t\tmiddleware: config.middleware,\n\t\t\tguard: config.guards?.update,\n\t\t\taudit: (resolution: { command: z.output<typeof updateInput> }, context: Context) => ({\n\t\t\t\toperation: updateName,\n\t\t\t\tactorId: config.actor(context),\n\t\t\t\taggregate: { type: config.aggregateType, id: resolution.command.id },\n\t\t\t}),\n\t\t}),\n\t\tnamedOperation(archiveName, {\n\t\t\tcommand: archiveInput,\n\t\t\tresult: okResult,\n\t\t\tclassification,\n\t\t\taggregates: [config.aggregateType],\n\t\t\tmiddleware: config.middleware,\n\t\t\tguard: config.guards?.archive,\n\t\t\taudit: (resolution: { command: z.output<typeof archiveInput> }, context: Context) => ({\n\t\t\t\toperation: archiveName,\n\t\t\t\tactorId: config.actor(context),\n\t\t\t\taggregate: { type: config.aggregateType, id: resolution.command.id },\n\t\t\t}),\n\t\t}),\n\t)\n\tconst commands: ApplicationCommand<Context, typeof operations> = new config.Command<\n\t\tContext,\n\t\ttypeof operations\n\t>(operations)\n\tconst executeCreate = commands.exec({\n\t\toperation: createName,\n\t\thandler: async (context, command) => {\n\t\t\tconst enrichment = config.enrich ? await config.enrich(context, command) : {}\n\t\t\tconst values: Record<string, Value> = {}\n\t\t\tfor (const [field, value] of Object.entries({ ...command, ...enrichment })) {\n\t\t\t\tif (value !== undefined) values[field] = value\n\t\t\t}\n\t\t\tconst inserted = await context.db.insert(name, values)\n\t\t\treturn { id: inserted }\n\t\t},\n\t})\n\tconst executeUpdate = commands.exec({\n\t\toperation: updateName,\n\t\thandler: async (context, command) => {\n\t\t\tawait context.db.patch(command.id, command.data)\n\t\t\treturn { ok: true }\n\t\t},\n\t})\n\tconst executeArchive = commands.exec({\n\t\toperation: archiveName,\n\t\thandler: async (context, command) => {\n\t\t\tawait context.db.patch(command.id, { archivedAt: Date.now() })\n\t\t\treturn { ok: true }\n\t\t},\n\t})\n\n\treturn {\n\t\tcommands,\n\t\toperations,\n\t\texecuteCreate,\n\t\texecuteUpdate,\n\t\texecuteArchive,\n\t}\n}\n\nfunction namedOperation<Name extends string, Definition extends AuditedOperation>(\n\tname: Name,\n\tdefinition: Definition,\n) {\n\t// SAFETY: this object contains exactly the computed name and its corresponding definition.\n\treturn { [name]: definition } as NamedOperation<Name, Definition>\n}\n\ntype NamedOperation<Name extends string, Definition extends AuditedOperation> = {\n\t[Key in Name]: Definition\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"crud.mjs","names":[],"sources":["../src/crud.ts"],"sourcesContent":["import { zid } from 'convex-helpers/server/zod4'\nimport { z } from 'zod'\nimport type { Value } from 'convex/values'\nimport type { GenericDataModel, GenericMutationCtx } from 'convex/server'\nimport type {\n\tAnyCommandMiddleware,\n\tApplicationCommand,\n\tAuditedOperation,\n\tCommandConstructor,\n\tSchemaOutput,\n} from './components/foundation/client'\nimport { defaultErrors, type ErrorFactory } from './errors'\n\n/**\n * The zodTable surface the factory consumes — structural, so any zodTable /\n * tenantTable return satisfies it.\n */\ntype CrudTable = {\n\ttableName: string\n\tcommandInput: z.ZodObject<Record<string, z.ZodType<Value | undefined>>>\n\tstorage: z.ZodObject<Record<string, z.ZodType<Value | undefined>>>\n}\n\ntype CrudContext = { db: Pick<GenericMutationCtx<GenericDataModel>['db'], 'insert' | 'patch'> }\n\ntype MaybePromise<Value> = Value | Promise<Value>\n\nexport type CrudConfig<Context extends CrudContext, Table extends CrudTable = CrudTable> = {\n\t/** The Foundation-bound Command class (destructured from the facade). */\n\tCommand: CommandConstructor\n\ttable: Table\n\t/** Audit aggregate type; enforced by the aggregates allowlist. */\n\taggregateType: string\n\t/** Observability/audit classification. Default 'business'. */\n\tclassification?: string\n\t/** Derives the audited actor id from the ctx (usually ctx.actor.userId). */\n\tactor: (context: Context) => string\n\t/**\n\t * Stamps server-owned fields onto creates (tenant, denormalized ids...).\n\t * REQUIRED when the table is a tenantTable — forgetting it would insert\n\t * untenanted rows; the factory fails fast at construction instead.\n\t */\n\tenrich?: (\n\t\tcontext: Context,\n\t\tcommand: SchemaOutput<Table['commandInput']>,\n\t) => MaybePromise<Partial<z.input<Table['storage']>>>\n\t/** Per-operation preconditions; run before the handlers. */\n\tguards?: {\n\t\tcreate?: (context: Context, command: never) => MaybePromise<void>\n\t\tupdate?: (context: Context, command: never) => MaybePromise<void>\n\t\tarchive?: (context: Context, command: never) => MaybePromise<void>\n\t}\n\t/** Per-operation middleware applied to all three generated operations. */\n\tmiddleware?: readonly AnyCommandMiddleware[]\n\terrors?: ErrorFactory\n}\n\n/**\n * Generates the standard create/update/archive operations from one zodTable\n * declaration, fully inside the audited command pipeline: strict command\n * inputs from `commandFields`, mandatory audit with the declared aggregate\n * type, soft delete only (archive sets `archivedAt` — no hard-delete\n * operation is generated, per kit doctrine).\n *\n * convex-helpers ships a same-named `crud` helper; it was deliberately not\n * used — it bypasses the command pipeline (no audits, no guards, and hard\n * deletes). See docs/crud.md.\n */\nexport type CrudCommands<\n\tContext extends CrudContext,\n\tTable extends CrudTable = CrudTable,\n> = ReturnType<typeof createCrudCommands<Context, Table>>\n\nexport function createCrudCommands<\n\tContext extends CrudContext,\n\tTable extends CrudTable = CrudTable,\n>(config: CrudConfig<Context, Table>) {\n\tconst errors = config.errors ?? defaultErrors\n\tconst name = config.table.tableName\n\tconst classification = config.classification ?? 'business'\n\tconst isTenantTable = 'tenant' in config.table.storage.shape\n\tif (isTenantTable && !config.enrich) {\n\t\treturn errors.throw({\n\t\t\tcode: 'CRUD_ENRICH_REQUIRED',\n\t\t\tmessage: `Table \"${name}\" is a tenantTable: createCrudCommands requires an enrich callback that stamps ctx.tenant`,\n\t\t})\n\t}\n\n\tconst commandSchema: Table['commandInput'] = config.table.commandInput\n\tconst id = zid(name)\n\tconst createResult = z.object({ id }).strict()\n\tconst okResult = z.object({ ok: z.literal(true) }).strict()\n\tconst updateInput = z.object({ id, data: commandSchema.partial().strict() }).strict()\n\tconst archiveInput = z.object({ id }).strict()\n\n\tconst createName = `${name}.create` as const\n\tconst updateName = `${name}.update` as const\n\tconst archiveName = `${name}.archive` as const\n\tconst operations = Object.assign(\n\t\t{},\n\t\tnamedOperation(createName, {\n\t\t\tcommand: commandSchema,\n\t\t\tresult: createResult,\n\t\t\tclassification,\n\t\t\taggregates: [config.aggregateType],\n\t\t\tmiddleware: config.middleware,\n\t\t\tguard: config.guards?.create,\n\t\t\taudit: (resolution: { result: z.output<typeof createResult> }, context: Context) => ({\n\t\t\t\toperation: createName,\n\t\t\t\tactorId: config.actor(context),\n\t\t\t\taggregate: { type: config.aggregateType, id: resolution.result.id },\n\t\t\t}),\n\t\t}),\n\t\tnamedOperation(updateName, {\n\t\t\tcommand: updateInput,\n\t\t\tresult: okResult,\n\t\t\tclassification,\n\t\t\taggregates: [config.aggregateType],\n\t\t\tmiddleware: config.middleware,\n\t\t\tguard: config.guards?.update,\n\t\t\taudit: (resolution: { command: z.output<typeof updateInput> }, context: Context) => ({\n\t\t\t\toperation: updateName,\n\t\t\t\tactorId: config.actor(context),\n\t\t\t\taggregate: { type: config.aggregateType, id: resolution.command.id },\n\t\t\t}),\n\t\t}),\n\t\tnamedOperation(archiveName, {\n\t\t\tcommand: archiveInput,\n\t\t\tresult: okResult,\n\t\t\tclassification,\n\t\t\taggregates: [config.aggregateType],\n\t\t\tmiddleware: config.middleware,\n\t\t\tguard: config.guards?.archive,\n\t\t\taudit: (resolution: { command: z.output<typeof archiveInput> }, context: Context) => ({\n\t\t\t\toperation: archiveName,\n\t\t\t\tactorId: config.actor(context),\n\t\t\t\taggregate: { type: config.aggregateType, id: resolution.command.id },\n\t\t\t}),\n\t\t}),\n\t)\n\tconst commands: ApplicationCommand<Context, typeof operations> = new config.Command<\n\t\tContext,\n\t\ttypeof operations\n\t>(operations)\n\tconst executeCreate = commands.exec({\n\t\toperation: createName,\n\t\thandler: async (context, command) => {\n\t\t\tconst enrichment = config.enrich ? await config.enrich(context, command) : {}\n\t\t\tconst values: Record<string, Value> = {}\n\t\t\tfor (const [field, value] of Object.entries({ ...command, ...enrichment })) {\n\t\t\t\tif (value !== undefined) values[field] = value\n\t\t\t}\n\t\t\tconst inserted = await context.db.insert(name, values)\n\t\t\treturn { id: inserted }\n\t\t},\n\t})\n\tconst executeUpdate = commands.exec({\n\t\toperation: updateName,\n\t\thandler: async (context, command) => {\n\t\t\tawait context.db.patch(command.id, command.data)\n\t\t\treturn { ok: true }\n\t\t},\n\t})\n\tconst executeArchive = commands.exec({\n\t\toperation: archiveName,\n\t\thandler: async (context, command) => {\n\t\t\tawait context.db.patch(command.id, { archivedAt: Date.now() })\n\t\t\treturn { ok: true }\n\t\t},\n\t})\n\n\treturn {\n\t\tcommands,\n\t\toperations,\n\t\texecuteCreate,\n\t\texecuteUpdate,\n\t\texecuteArchive,\n\t}\n}\n\nfunction namedOperation<Name extends string, Definition extends AuditedOperation>(\n\tname: Name,\n\tdefinition: Definition,\n) {\n\t// SAFETY: this object contains exactly the computed name and its corresponding definition.\n\treturn { [name]: definition } as NamedOperation<Name, Definition>\n}\n\ntype NamedOperation<Name extends string, Definition extends AuditedOperation> = {\n\t[Key in Name]: Definition\n}\n"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { OperationExecutor, OperationPublicResult, OperationTool, OperationToolConverter, OperationToolDefinition, OperationToolJsonSchema, OperationToolOutcome, SelectedOperation, bindOperationExecutor, createOperationTools, operationToolDialect, selectOperation } from "./modules/contracts/exposure.mjs";
2
+ import { ContractDecoderError, ContractDecoderRequirements, ContractValidationError, EffectDecoder, decodeEffectContract, effectContract, effectSchema, effectStandardSchema } from "./modules/effect/schema.mjs";
3
+ import { CallbackError, CallbackRequirements, EffectMiddlewareInput, EffectOperationArgument, EffectOperationError, EffectOperationHandlerResult, EffectOperationInput, EffectOperationRequirements, EffectOperationResult, EffectPreparation, EffectValue, effectOperationFactory } from "./modules/effect/operation.mjs";
4
+ import { EffectFoundationOptions, createEffectFoundation } from "./modules/effect/foundation.mjs";
5
+ import { EffectIdempotencyPreparation, effectTransactionalIdempotency } from "./modules/effect/idempotency.mjs";
6
+ import { EffectCrudConfig, EffectCrudContext, EffectCrudPage, EffectCrudPagination, EffectCrudRepository, EffectCrudTable, createEffectCrud } from "./modules/effect/crud.mjs";
7
+ import { EffectApiBuilder, effectApiBuilder } from "./modules/effect/api.mjs";
8
+ import { EffectZodApiBuilder, effectZodApiBuilder } from "./modules/effect/api-zod.mjs";
9
+ import { projectErrorCause } from "./modules/effect/errors.mjs";
10
+ import { EffectReceiptProtectedWorkflowMutation, effectReceiptWorkflowMutation, effectTerminalWorkflowAttempt, effectWorkflowAttempt, effectWorkflowStatusBindings } from "./modules/effect/workflow.mjs";
11
+ import "./modules/effect/client.mjs";
12
+ export { type CallbackError, type CallbackRequirements, type ContractDecoderError, type ContractDecoderRequirements, ContractValidationError, type EffectApiBuilder, type EffectCrudConfig, type EffectCrudContext, type EffectCrudPage, type EffectCrudPagination, type EffectCrudRepository, type EffectCrudTable, type EffectDecoder, type EffectFoundationOptions, type EffectIdempotencyPreparation, type EffectMiddlewareInput, type EffectOperationArgument, type EffectOperationError, type EffectOperationHandlerResult, type EffectOperationInput, type EffectOperationRequirements, type EffectOperationResult, type EffectPreparation, type EffectReceiptProtectedWorkflowMutation, type EffectValue, type EffectZodApiBuilder, type OperationExecutor, type OperationPublicResult, type OperationTool, type OperationToolConverter, type OperationToolDefinition, type OperationToolJsonSchema, type OperationToolOutcome, type SelectedOperation, bindOperationExecutor, createEffectCrud, createEffectFoundation, createOperationTools, decodeEffectContract, effectApiBuilder, effectContract, effectOperationFactory, effectReceiptWorkflowMutation, effectSchema, effectStandardSchema, effectTerminalWorkflowAttempt, effectTransactionalIdempotency, effectWorkflowAttempt, effectWorkflowStatusBindings, effectZodApiBuilder, operationToolDialect, projectErrorCause, selectOperation };
@@ -0,0 +1 @@
1
+ import{bindOperationExecutor as e,createOperationTools as t,operationToolDialect as n,selectOperation as r}from"./modules/contracts/exposure.mjs";import{effectOperationFactory as i}from"./modules/effect/operation.mjs";import{ContractValidationError as a,decodeEffectContract as o,effectContract as s,effectSchema as c,effectStandardSchema as l}from"./modules/effect/schema.mjs";import{createEffectFoundation as u}from"./modules/effect/foundation.mjs";import{effectTransactionalIdempotency as d}from"./modules/effect/idempotency.mjs";import{createEffectCrud as f}from"./modules/effect/crud.mjs";import{projectErrorCause as p}from"./modules/effect/errors.mjs";import{effectApiBuilder as m}from"./modules/effect/api.mjs";import{effectZodApiBuilder as h}from"./modules/effect/api-zod.mjs";import{effectReceiptWorkflowMutation as g,effectTerminalWorkflowAttempt as _,effectWorkflowAttempt as v,effectWorkflowStatusBindings as y}from"./modules/effect/workflow.mjs";export{a as ContractValidationError,e as bindOperationExecutor,f as createEffectCrud,u as createEffectFoundation,t as createOperationTools,o as decodeEffectContract,m as effectApiBuilder,s as effectContract,i as effectOperationFactory,g as effectReceiptWorkflowMutation,c as effectSchema,l as effectStandardSchema,_ as effectTerminalWorkflowAttempt,d as effectTransactionalIdempotency,v as effectWorkflowAttempt,y as effectWorkflowStatusBindings,h as effectZodApiBuilder,n as operationToolDialect,p as projectErrorCause,r as selectOperation};
package/dist/errors.d.mts CHANGED
@@ -1,3 +1,4 @@
1
+ import { DeclaredError, DeclaredFailure, DecodedFailure, ErrorContract, ErrorDefinitions, ErrorDetailParser, UnknownFailure, defineErrorContract } from "./modules/contracts/errors.mjs";
1
2
  //#region src/errors.d.ts
2
3
  /** Structured application error carried across Convex function boundaries. */
3
4
  declare class KitError extends Error {
@@ -20,5 +21,5 @@ type ErrorFactory = {
20
21
  };
21
22
  declare const defaultErrors: ErrorFactory;
22
23
  //#endregion
23
- export { ErrorFactory, KitError, defaultErrors };
24
+ export { type DeclaredError, type DeclaredFailure, type DecodedFailure, type ErrorContract, type ErrorDefinitions, type ErrorDetailParser, ErrorFactory, KitError, type UnknownFailure, defaultErrors, defineErrorContract };
24
25
  //# sourceMappingURL=errors.d.mts.map
package/dist/errors.mjs CHANGED
@@ -1,2 +1,2 @@
1
- var e=class extends Error{code;constructor(e){super(e.message??e.code),this.name=`KitError`,this.code=e.code}};const t={throw(t){throw new e(t)}};export{e as KitError,t as defaultErrors};
1
+ import{defineErrorContract as e}from"./modules/contracts/errors.mjs";var t=class extends Error{code;constructor(e){super(e.message??e.code),this.name=`KitError`,this.code=e.code}};const n={throw(e){throw new t(e)}};export{t as KitError,n as defaultErrors,e as defineErrorContract};
2
2
  //# sourceMappingURL=errors.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Structured application error carried across Convex function boundaries. */\nexport class KitError extends Error {\n\treadonly code: string\n\n\tconstructor(input: { code: string; message?: string }) {\n\t\tsuper(input.message ?? input.code)\n\t\tthis.name = 'KitError'\n\t\tthis.code = input.code\n\t}\n}\n\n/**\n * Injection seam for host error policy. Hosts with their own error taxonomy\n * (e.g. an App.errors singleton) adapt it to this shape; everyone else uses\n * the default factory below.\n */\nexport type ErrorFactory = {\n\tthrow(input: { code: string; message?: string }): never\n}\n\nexport const defaultErrors: ErrorFactory = {\n\tthrow(input) {\n\t\tthrow new KitError(input)\n\t},\n}\n"],"mappings":"AACA,IAAa,EAAb,cAA8B,KAAM,CACnC,KAEA,YAAY,EAA2C,CACtD,MAAM,EAAM,SAAW,EAAM,IAAI,EACjC,KAAK,KAAO,WACZ,KAAK,KAAO,EAAM,IACnB,CACD,EAWA,MAAa,EAA8B,CAC1C,MAAM,EAAO,CACZ,MAAM,IAAI,EAAS,CAAK,CACzB,CACD"}
1
+ {"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Structured application error carried across Convex function boundaries. */\nexport class KitError extends Error {\n\treadonly code: string\n\n\tconstructor(input: { code: string; message?: string }) {\n\t\tsuper(input.message ?? input.code)\n\t\tthis.name = 'KitError'\n\t\tthis.code = input.code\n\t}\n}\n\nexport {\n\tdefineErrorContract,\n\ttype ErrorContract,\n\ttype ErrorDefinitions,\n\ttype ErrorDetailParser,\n\ttype DeclaredError,\n\ttype DeclaredFailure,\n\ttype DecodedFailure,\n\ttype UnknownFailure,\n} from './modules/contracts/errors'\n\n/**\n * Injection seam for host error policy. Hosts with their own error taxonomy\n * (e.g. an App.errors singleton) adapt it to this shape; everyone else uses\n * the default factory below.\n */\nexport type ErrorFactory = {\n\tthrow(input: { code: string; message?: string }): never\n}\n\nexport const defaultErrors: ErrorFactory = {\n\tthrow(input) {\n\t\tthrow new KitError(input)\n\t},\n}\n"],"mappings":"qEACA,IAAa,EAAb,cAA8B,KAAM,CACnC,KAEA,YAAY,EAA2C,CACtD,MAAM,EAAM,SAAW,EAAM,IAAI,EACjC,KAAK,KAAO,WACZ,KAAK,KAAO,EAAM,IACnB,CACD,EAsBA,MAAa,EAA8B,CAC1C,MAAM,EAAO,CACZ,MAAM,IAAI,EAAS,CAAK,CACzB,CACD"}
@@ -0,0 +1,2 @@
1
+ import { CanonicalConvexBounds, IdempotencyCapture, IdempotencyError, IdempotencyErrorCode, IdempotencyIdentity, IdempotencyInvocation, IdempotencyPreparation, IdempotencyReceipt, IdempotencyVersions, canonicalConvexBytes, captureIdempotencyInvocation, transactionalIdempotency } from "./modules/contracts/idempotency.mjs";
2
+ export { type CanonicalConvexBounds, type IdempotencyCapture, IdempotencyError, type IdempotencyErrorCode, type IdempotencyIdentity, type IdempotencyInvocation, type IdempotencyPreparation, type IdempotencyReceipt, type IdempotencyVersions, canonicalConvexBytes, captureIdempotencyInvocation, transactionalIdempotency };
@@ -0,0 +1 @@
1
+ import{IdempotencyError as e,canonicalConvexBytes as t,captureIdempotencyInvocation as n,transactionalIdempotency as r}from"./modules/contracts/idempotency.mjs";export{e as IdempotencyError,t as canonicalConvexBytes,n as captureIdempotencyInvocation,r as transactionalIdempotency};
@@ -0,0 +1,70 @@
1
+ //#region src/modules/contracts/contract.d.ts
2
+ /** Structural contracts keep the optional Effect peer out of this module. */
3
+ interface StandardSchema<Input = unknown, Output = Input> {
4
+ readonly '~standard': {
5
+ readonly version: 1;
6
+ readonly vendor: string;
7
+ readonly types?: {
8
+ readonly input: Input;
9
+ readonly output: Output;
10
+ };
11
+ readonly validate: (value: unknown) => ValidationResult<Output> | Promise<ValidationResult<Output>>;
12
+ };
13
+ }
14
+ interface ValidationIssue {
15
+ readonly message: string;
16
+ readonly path?: readonly (PropertyKey | {
17
+ readonly key: PropertyKey;
18
+ })[];
19
+ }
20
+ type ValidationResult<Output> = {
21
+ readonly value: Output;
22
+ readonly issues?: undefined;
23
+ } | {
24
+ readonly issues: readonly ValidationIssue[];
25
+ };
26
+ interface LegacyParser<Output = unknown> {
27
+ readonly parse: (value: unknown) => Output;
28
+ readonly parseAsync?: (value: unknown) => Promise<Output>;
29
+ }
30
+ /** Decode execution is an opaque channel here; optional adapters give it concrete semantics. */
31
+ interface Decoder<Input = unknown, Output = unknown, Execution = unknown> {
32
+ readonly kind: 'contract' | 'effect-contract';
33
+ readonly types?: {
34
+ readonly input: Input;
35
+ readonly output: Output;
36
+ };
37
+ readonly decode: (value: unknown) => Execution;
38
+ }
39
+ type ContractSchema = LegacyParser | StandardSchema<unknown, unknown> | Decoder;
40
+ type NeutralSchema = LegacyParser | StandardSchema<unknown, unknown> | (Decoder<unknown, unknown, ValidationResult<unknown> | Promise<ValidationResult<unknown>>> & {
41
+ readonly kind: 'contract';
42
+ });
43
+ type ContractInput<S> = S extends Decoder<infer Input, unknown, unknown> ? Input : S extends StandardSchema<infer Input, unknown> ? Input : S extends {
44
+ readonly _input: infer Input;
45
+ } ? Input : S extends LegacyParser<infer Output> ? Awaited<Output> : never;
46
+ type ContractOutput<S> = S extends Decoder<unknown, infer Output, unknown> ? Output : S extends StandardSchema<unknown, infer Output> ? Output : S extends LegacyParser<infer Output> ? Awaited<Output> : never;
47
+ type ContractExecution<S> = S extends Decoder<unknown, unknown, infer Execution> ? Execution : never;
48
+ /** Validation failures are values. Throws/rejections are deliberately left as defects. */
49
+ declare function decodeContract<S extends NeutralSchema>(schema: S, value: unknown): Promise<ValidationResult<ContractOutput<S>>>;
50
+ /** Independent final and wire contracts never reuse the fresh-result transform. */
51
+ declare function operationContract<const Input extends ContractSchema, const Result extends ContractSchema, const Replay extends ContractSchema, const Wire extends ContractSchema, const Definition extends object>(definition: Definition & {
52
+ input: Input;
53
+ result: Result;
54
+ replayResult: Replay & (ContractOutput<Replay> extends ContractOutput<Result> ? unknown : never);
55
+ wire: {
56
+ project: (value: ContractOutput<Result>) => ContractInput<Wire>;
57
+ schema: Wire;
58
+ };
59
+ }): Definition & {
60
+ input: Input;
61
+ result: Result;
62
+ replayResult: Replay & (ContractOutput<Replay> extends ContractOutput<Result> ? unknown : never);
63
+ wire: {
64
+ project: (value: ContractOutput<Result>) => ContractInput<Wire>;
65
+ schema: Wire;
66
+ };
67
+ };
68
+ //#endregion
69
+ export { ContractExecution, ContractInput, ContractOutput, ContractSchema, Decoder, LegacyParser, NeutralSchema, StandardSchema, ValidationIssue, ValidationResult, decodeContract, operationContract };
70
+ //# sourceMappingURL=contract.d.mts.map
@@ -0,0 +1,2 @@
1
+ async function e(e,t){let n;return n=`kind`in e?await e.decode(t):`parse`in e?{value:await(e.parseAsync?e.parseAsync(t):e.parse(t))}:await e[`~standard`].validate(t),n}function t(e){return e}export{e as decodeContract,t as operationContract};
2
+ //# sourceMappingURL=contract.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.mjs","names":[],"sources":["../../../src/modules/contracts/contract.ts"],"sourcesContent":["/* oxlint-disable anti-slop/no-unknown-parameters -- These explicit validation boundaries receive untrusted transport values before schema decoding. */\n/** Structural contracts keep the optional Effect peer out of this module. */\nexport interface StandardSchema<Input = unknown, Output = Input> {\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}\nexport interface ValidationIssue {\n\treadonly message: string\n\treadonly path?: readonly (PropertyKey | { readonly key: PropertyKey })[]\n}\nexport type ValidationResult<Output> =\n\t| { readonly value: Output; readonly issues?: undefined }\n\t| { readonly issues: readonly ValidationIssue[] }\nexport interface LegacyParser<Output = unknown> {\n\treadonly parse: (value: unknown) => Output\n\treadonly parseAsync?: (value: unknown) => Promise<Output>\n}\n/** Decode execution is an opaque channel here; optional adapters give it concrete semantics. */\nexport interface Decoder<Input = unknown, Output = unknown, Execution = unknown> {\n\treadonly kind: 'contract' | 'effect-contract'\n\treadonly types?: { readonly input: Input; readonly output: Output }\n\treadonly decode: (value: unknown) => Execution\n}\nexport type ContractSchema = LegacyParser | StandardSchema<unknown, unknown> | Decoder\nexport type NeutralSchema =\n\t| LegacyParser\n\t| StandardSchema<unknown, unknown>\n\t| (Decoder<unknown, unknown, ValidationResult<unknown> | Promise<ValidationResult<unknown>>> & {\n\t\t\treadonly kind: 'contract'\n\t })\nexport type ContractInput<S> =\n\tS extends Decoder<infer Input, unknown, unknown>\n\t\t? Input\n\t\t: S extends StandardSchema<infer Input, unknown>\n\t\t\t? Input\n\t\t\t: S extends { readonly _input: infer Input }\n\t\t\t\t? Input\n\t\t\t\t: S extends LegacyParser<infer Output>\n\t\t\t\t\t? Awaited<Output>\n\t\t\t\t\t: never\nexport type ContractOutput<S> =\n\tS extends Decoder<unknown, infer Output, unknown>\n\t\t? Output\n\t\t: S extends StandardSchema<unknown, infer Output>\n\t\t\t? Output\n\t\t\t: S extends LegacyParser<infer Output>\n\t\t\t\t? Awaited<Output>\n\t\t\t\t: never\nexport type ContractExecution<S> =\n\tS extends Decoder<unknown, unknown, infer Execution> ? Execution : never\n\n/** Validation failures are values. Throws/rejections are deliberately left as defects. */\nexport async function decodeContract<S extends NeutralSchema>(\n\tschema: S,\n\tvalue: unknown,\n): Promise<ValidationResult<ContractOutput<S>>> {\n\tlet result: ValidationResult<unknown>\n\tif ('kind' in schema) result = await schema.decode(value)\n\telse if ('parse' in schema)\n\t\tresult = { value: await (schema.parseAsync ? schema.parseAsync(value) : schema.parse(value)) }\n\telse result = await schema['~standard'].validate(value)\n\t// SAFETY: each branch returns the output associated with this exact schema.\n\treturn result as ValidationResult<ContractOutput<S>>\n}\n\n/** Independent final and wire contracts never reuse the fresh-result transform. */\nexport function operationContract<\n\tconst Input extends ContractSchema,\n\tconst Result extends ContractSchema,\n\tconst Replay extends ContractSchema,\n\tconst Wire extends ContractSchema,\n\tconst Definition extends object,\n>(\n\tdefinition: Definition & {\n\t\tinput: Input\n\t\tresult: Result\n\t\treplayResult: Replay & (ContractOutput<Replay> extends ContractOutput<Result> ? unknown : never)\n\t\twire: { project: (value: ContractOutput<Result>) => ContractInput<Wire>; schema: Wire }\n\t},\n) {\n\treturn definition\n}\n"],"mappings":"AA0DA,eAAsB,EACrB,EACA,EAC+C,CAC/C,IAAI,EAMJ,MALA,CAGK,EAHD,SAAU,EAAiB,MAAM,EAAO,OAAO,CAAK,EAC/C,UAAW,EACV,CAAE,MAAO,MAAO,EAAO,WAAa,EAAO,WAAW,CAAK,EAAI,EAAO,MAAM,CAAK,EAAG,EAChF,MAAM,EAAO,YAAY,CAAC,SAAS,CAAK,EAE/C,CACR,CAGA,SAAgB,EAOf,EAMC,CACD,OAAO,CACR"}
@@ -0,0 +1,22 @@
1
+ import { ValidationResult } from "./contract.mjs";
2
+ import { StandardSchemaV1 } from "../../node_modules/@standard-schema/spec/dist/index.mjs";
3
+ import { GenericDataModel, GenericDatabaseReader } from "convex/server";
4
+ import { Infer, Validator } from "convex/values";
5
+ //#region src/modules/contracts/convex.d.ts
6
+ /** Keep native registration authoritative, especially for actions with no db. */
7
+ declare function convexContract<const V extends Validator<unknown, 'required' | 'optional', string>>(validator: V, options?: {
8
+ db?: GenericDatabaseReader<GenericDataModel>;
9
+ }): {
10
+ kind: "contract";
11
+ types?: {
12
+ readonly input: Infer<V>;
13
+ readonly output: Infer<V>;
14
+ } | undefined;
15
+ decode: (value: unknown) => Promise<ValidationResult<Infer<V>>> | ValidationResult<Infer<V>>;
16
+ schema: StandardSchemaV1<Infer<V>, Infer<V>>;
17
+ nativeValidator: V;
18
+ };
19
+ type ConvexContractValue<V extends Validator<unknown, 'required' | 'optional', string>> = Infer<V>;
20
+ //#endregion
21
+ export { ConvexContractValue, convexContract };
22
+ //# sourceMappingURL=convex.d.mts.map
@@ -0,0 +1,2 @@
1
+ import{standardContract as e}from"./standard.mjs";import{toStandardSchema as t}from"convex-helpers/standardSchema";function n(n,r){return{...e(t(n,r)),nativeValidator:n}}export{n as convexContract};
2
+ //# sourceMappingURL=convex.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convex.mjs","names":[],"sources":["../../../src/modules/contracts/convex.ts"],"sourcesContent":["import type { GenericDatabaseReader, GenericDataModel } from 'convex/server'\nimport type { Infer, Validator } from 'convex/values'\nimport { toStandardSchema } from 'convex-helpers/standardSchema'\nimport { standardContract } from './standard'\n\n/** Keep native registration authoritative, especially for actions with no db. */\nexport function convexContract<const V extends Validator<unknown, 'required' | 'optional', string>>(\n\tvalidator: V,\n\toptions?: { db?: GenericDatabaseReader<GenericDataModel> },\n) {\n\treturn { ...standardContract(toStandardSchema(validator, options)), nativeValidator: validator }\n}\nexport type ConvexContractValue<V extends Validator<unknown, 'required' | 'optional', string>> =\n\tInfer<V>\n"],"mappings":"mHAMA,SAAgB,EACf,EACA,EACC,CACD,MAAO,CAAE,GAAG,EAAiB,EAAiB,EAAW,CAAO,CAAC,EAAG,gBAAiB,CAAU,CAChG"}
@@ -0,0 +1,44 @@
1
+ //#region src/modules/contracts/errors.d.ts
2
+ /** Explicit synchronous validators for the final public detail representation. */
3
+ type ErrorDetailParser<Output = unknown> = Readonly<{
4
+ parse: (value: unknown) => Output;
5
+ }>;
6
+ type ErrorDefinitions = Readonly<Record<string, Readonly<{
7
+ message: string;
8
+ details: Readonly<Record<string, ErrorDetailParser>>;
9
+ }>>>;
10
+ type Code<Definitions extends ErrorDefinitions> = Extract<keyof Definitions, string>;
11
+ type Details<Definition extends ErrorDefinitions[string]> = Definition extends ErrorDefinitions[string] ? { [Field in keyof Definition['details']]: ReturnType<Definition['details'][Field]['parse']>; } : never;
12
+ declare const declaredErrorBrand: unique symbol;
13
+ /** Server failures are trusted only by the contract that created them. */
14
+ type DeclaredError<Definitions extends ErrorDefinitions> = { [Key in Code<Definitions>]: Readonly<{
15
+ _tag: 'DeclaredError';
16
+ code: Key;
17
+ details: Details<Definitions[Key]>;
18
+ [declaredErrorBrand]: true;
19
+ }>; }[Code<Definitions>];
20
+ type DeclaredFailure<Definitions extends ErrorDefinitions> = { [Key in Code<Definitions>]: Readonly<{
21
+ _tag: 'DeclaredFailure';
22
+ version: 1;
23
+ code: Key;
24
+ message: string;
25
+ details: Details<Definitions[Key]>;
26
+ }>; }[Code<Definitions>];
27
+ type UnknownFailure = Readonly<{
28
+ _tag: 'UnknownFailure';
29
+ version: 1;
30
+ }>;
31
+ type DecodedFailure<Definitions extends ErrorDefinitions> = DeclaredFailure<Definitions> | UnknownFailure;
32
+ type ErrorContract<Definitions extends ErrorDefinitions> = Readonly<{
33
+ create: <Key extends Code<Definitions>>(code: Key, details: Details<Definitions[NoInfer<Key>]>) => Extract<DeclaredError<Definitions>, {
34
+ code: Key;
35
+ }>;
36
+ project: (error: unknown) => DecodedFailure<Definitions>;
37
+ /** Receives envelope data, e.g. ConvexError.data; never parses error messages. */
38
+ decode: (payload: unknown) => DecodedFailure<Definitions>;
39
+ }>;
40
+ /** Closed safe codes, allowlisted details, and a versioned checked transport union. */
41
+ declare function defineErrorContract<const Definitions extends ErrorDefinitions>(definitions: Definitions): ErrorContract<Definitions>;
42
+ //#endregion
43
+ export { DeclaredError, DeclaredFailure, DecodedFailure, ErrorContract, ErrorDefinitions, ErrorDetailParser, UnknownFailure, defineErrorContract };
44
+ //# sourceMappingURL=errors.d.mts.map
@@ -0,0 +1,2 @@
1
+ const e=Object.freeze({_tag:`UnknownFailure`,version:1});function t(t){let a=Object.fromEntries(Object.entries(t).map(([e,t])=>{if(!e||!t.message||t.message.length>200)throw Error(`Declared errors require a code and a public message of 1–200 characters`);return[e,{message:t.message,details:{...t.details}}]})),o=new WeakMap;return{create(e,t){let n;try{if(!Object.hasOwn(a,e))throw Error(`Undeclared code`);let r=a[e];n={_tag:`DeclaredFailure`,version:1,code:e,message:r.message,details:i(r,t,!1)}}catch{throw Error(`Invalid declared error details`)}let s=Object.freeze({_tag:`DeclaredError`,code:e,details:r(n.details)});return o.set(s,n),s},project(t){if(typeof t!=`object`||!t)return e;let n=o.get(t);return n?r(n):e},decode(t){try{if(!n(t)||t._tag!==`DeclaredFailure`||t.version!==1||typeof t.code!=`string`||!Object.hasOwn(a,t.code)||Object.keys(t).length!==5||![`message`,`details`,`_tag`,`version`,`code`].every(e=>Object.hasOwn(t,e)))return e;let o=a[t.code];return t.message===o.message?(r(t.details),{_tag:`DeclaredFailure`,version:1,code:t.code,message:o.message,details:i(o,t.details,!0)}):e}catch{return e}}}}function n(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function r(e,t=new Set){if(e===null||typeof e==`string`||typeof e==`boolean`||typeof e==`number`&&Number.isFinite(e))return e;if(typeof e!=`object`||!e||t.has(e))throw Error(`Invalid JSON`);t.add(e);try{if(Array.isArray(e)){let n=[];for(let i=0;i<e.length;i++)n.push(r(e[i],t));return n}if(!n(e))throw Error(`Invalid JSON`);return Object.fromEntries(Object.entries(e).map(([e,n])=>[e,r(n,t)]))}finally{t.delete(e)}}function i(e,t,i){if(!n(t))throw Error(`Invalid details`);let a=Object.keys(e.details);if(i&&Object.keys(t).some(t=>!Object.hasOwn(e.details,t)))throw Error(`Undeclared detail`);return Object.fromEntries(a.map(n=>{if(!Object.hasOwn(t,n))throw Error(`Missing detail`);return[n,r(e.details[n].parse(t[n]))]}))}export{t as defineErrorContract};
2
+ //# sourceMappingURL=errors.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.mjs","names":[],"sources":["../../../src/modules/contracts/errors.ts"],"sourcesContent":["/* oxlint-disable anti-slop/no-unknown-parameters, anti-slop/no-unknown-returns, anti-slop/no-unknown-type-aliases, anti-slop/no-runtime-typeof, anti-slop/no-unsafe-dictionary-type -- This neutral contract validates unknown transport data and an explicitly keyed code/detail declaration. */\n\n/** Explicit synchronous validators for the final public detail representation. */\nexport type ErrorDetailParser<Output = unknown> = Readonly<{\n\tparse: (value: unknown) => Output\n}>\n\nexport type ErrorDefinitions = Readonly<\n\tRecord<\n\t\tstring,\n\t\tReadonly<{\n\t\t\tmessage: string\n\t\t\tdetails: Readonly<Record<string, ErrorDetailParser>>\n\t\t}>\n\t>\n>\n\ntype Code<Definitions extends ErrorDefinitions> = Extract<keyof Definitions, string>\ntype Details<Definition extends ErrorDefinitions[string]> =\n\tDefinition extends ErrorDefinitions[string]\n\t\t? { [Field in keyof Definition['details']]: ReturnType<Definition['details'][Field]['parse']> }\n\t\t: never\n\ndeclare const declaredErrorBrand: unique symbol\n\n/** Server failures are trusted only by the contract that created them. */\nexport type DeclaredError<Definitions extends ErrorDefinitions> = {\n\t[Key in Code<Definitions>]: Readonly<{\n\t\t_tag: 'DeclaredError'\n\t\tcode: Key\n\t\tdetails: Details<Definitions[Key]>\n\t\t[declaredErrorBrand]: true\n\t}>\n}[Code<Definitions>]\n\nexport type DeclaredFailure<Definitions extends ErrorDefinitions> = {\n\t[Key in Code<Definitions>]: Readonly<{\n\t\t_tag: 'DeclaredFailure'\n\t\tversion: 1\n\t\tcode: Key\n\t\tmessage: string\n\t\tdetails: Details<Definitions[Key]>\n\t}>\n}[Code<Definitions>]\n\nexport type UnknownFailure = Readonly<{ _tag: 'UnknownFailure'; version: 1 }>\nexport type DecodedFailure<Definitions extends ErrorDefinitions> =\n\t| DeclaredFailure<Definitions>\n\t| UnknownFailure\n\nexport type ErrorContract<Definitions extends ErrorDefinitions> = Readonly<{\n\tcreate: <Key extends Code<Definitions>>(\n\t\tcode: Key,\n\t\tdetails: Details<Definitions[NoInfer<Key>]>,\n\t) => Extract<DeclaredError<Definitions>, { code: Key }>\n\tproject: (error: unknown) => DecodedFailure<Definitions>\n\t/** Receives envelope data, e.g. ConvexError.data; never parses error messages. */\n\tdecode: (payload: unknown) => DecodedFailure<Definitions>\n}>\n\nconst unknownFailure: UnknownFailure = Object.freeze({ _tag: 'UnknownFailure', version: 1 })\ntype Json = null | boolean | number | string | Json[] | { [key: string]: Json }\n\n/** Closed safe codes, allowlisted details, and a versioned checked transport union. */\nexport function defineErrorContract<const Definitions extends ErrorDefinitions>(\n\tdefinitions: Definitions,\n): ErrorContract<Definitions> {\n\t// SAFETY: the snapshot copies every declaration without changing its code, message or paired parsers.\n\tconst declarations = Object.fromEntries(\n\t\tObject.entries(definitions).map(([code, definition]) => {\n\t\t\tif (!code || !definition.message || definition.message.length > 200) {\n\t\t\t\tthrow Error('Declared errors require a code and a public message of 1–200 characters')\n\t\t\t}\n\t\t\treturn [code, { message: definition.message, details: { ...definition.details } }]\n\t\t}),\n\t) as Definitions\n\tconst trusted = new WeakMap<object, DeclaredFailure<Definitions>>()\n\treturn {\n\t\tcreate(code, details) {\n\t\t\tlet envelope: DeclaredFailure<Definitions>\n\t\t\ttry {\n\t\t\t\tif (!Object.hasOwn(declarations, code)) throw Error('Undeclared code')\n\t\t\t\tconst definition = declarations[code]\n\t\t\t\t// SAFETY: the selected closed declaration validates every field before construction.\n\t\t\t\tenvelope = {\n\t\t\t\t\t_tag: 'DeclaredFailure',\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tcode,\n\t\t\t\t\tmessage: definition.message,\n\t\t\t\t\tdetails: parseDetails(definition, details, false),\n\t\t\t\t} as DeclaredFailure<Definitions>\n\t\t\t} catch {\n\t\t\t\t// Validator messages, issue paths and provider causes remain private.\n\t\t\t\tthrow Error('Invalid declared error details')\n\t\t\t}\n\t\t\t// SAFETY: identity is registered below; the inaccessible brand cannot confer trust by itself.\n\t\t\tconst error = Object.freeze({\n\t\t\t\t_tag: 'DeclaredError',\n\t\t\t\tcode,\n\t\t\t\tdetails: copyJson(envelope.details),\n\t\t\t}) as Extract<DeclaredError<Definitions>, { code: typeof code }>\n\t\t\ttrusted.set(error, envelope)\n\t\t\treturn error\n\t\t},\n\t\tproject(error) {\n\t\t\tif (error === null || typeof error !== 'object') return unknownFailure\n\t\t\tconst envelope = trusted.get(error)\n\t\t\t// SAFETY: only validated contract-owned envelopes enter the private map.\n\t\t\treturn envelope ? (copyJson(envelope) as DeclaredFailure<Definitions>) : unknownFailure\n\t\t},\n\t\tdecode(payload) {\n\t\t\ttry {\n\t\t\t\tif (\n\t\t\t\t\t!isRecord(payload) ||\n\t\t\t\t\tpayload._tag !== 'DeclaredFailure' ||\n\t\t\t\t\tpayload.version !== 1 ||\n\t\t\t\t\ttypeof payload.code !== 'string' ||\n\t\t\t\t\t!Object.hasOwn(declarations, payload.code) ||\n\t\t\t\t\tObject.keys(payload).length !== 5 ||\n\t\t\t\t\t!['message', 'details', '_tag', 'version', 'code'].every((key) =>\n\t\t\t\t\t\tObject.hasOwn(payload, key),\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\treturn unknownFailure\n\t\t\t\t}\n\t\t\t\tconst definition = declarations[payload.code]\n\t\t\t\tif (payload.message !== definition.message) return unknownFailure\n\t\t\t\t// Validate raw transport JSON too: parsers must not hide non-JSON payloads.\n\t\t\t\tcopyJson(payload.details)\n\t\t\t\t// SAFETY: code membership and every allowlisted field have been checked independently.\n\t\t\t\treturn {\n\t\t\t\t\t_tag: 'DeclaredFailure',\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tcode: payload.code,\n\t\t\t\t\tmessage: definition.message,\n\t\t\t\t\tdetails: parseDetails(definition, payload.details, true),\n\t\t\t\t} as DeclaredFailure<Definitions>\n\t\t\t} catch {\n\t\t\t\treturn unknownFailure\n\t\t\t}\n\t\t},\n\t}\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\tif (value === null || typeof value !== 'object' || Array.isArray(value)) return false\n\tconst prototype = Object.getPrototypeOf(value)\n\treturn prototype === Object.prototype || prototype === null\n}\n\n/** Copy data without invoking toJSON or retaining mutable provider objects. */\nfunction copyJson(value: unknown, ancestors = new Set<object>()): Json {\n\tif (value === null || typeof value === 'string' || typeof value === 'boolean') return value\n\tif (typeof value === 'number' && Number.isFinite(value)) return value\n\tif (typeof value !== 'object' || value === null || ancestors.has(value))\n\t\tthrow Error('Invalid JSON')\n\tancestors.add(value)\n\ttry {\n\t\tif (Array.isArray(value)) {\n\t\t\tconst result: Json[] = []\n\t\t\tfor (let index = 0; index < value.length; index++)\n\t\t\t\tresult.push(copyJson(value[index], ancestors))\n\t\t\treturn result\n\t\t}\n\t\tif (!isRecord(value)) throw Error('Invalid JSON')\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(value).map(([key, child]) => [key, copyJson(child, ancestors)]),\n\t\t)\n\t} finally {\n\t\tancestors.delete(value)\n\t}\n}\n\nfunction parseDetails<const Definition extends ErrorDefinitions[string]>(\n\tdefinition: Definition,\n\tvalue: unknown,\n\tstrict: boolean,\n): Details<Definition> {\n\tif (!isRecord(value)) throw Error('Invalid details')\n\tconst fields = Object.keys(definition.details)\n\tif (strict && Object.keys(value).some((key) => !Object.hasOwn(definition.details, key))) {\n\t\tthrow Error('Undeclared detail')\n\t}\n\t// SAFETY: each declared field is validated by its paired parser before its JSON-safe copy is returned.\n\treturn Object.fromEntries(\n\t\tfields.map((field) => {\n\t\t\tif (!Object.hasOwn(value, field)) throw Error('Missing detail')\n\t\t\treturn [field, copyJson(definition.details[field].parse(value[field]))]\n\t\t}),\n\t) as Details<Definition>\n}\n"],"mappings":"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"}