cvx-kit 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +10 -1
  3. package/dist/agent-tools.d.mts +2 -1
  4. package/dist/agent-tools.mjs +1 -1
  5. package/dist/agent-tools.mjs.map +1 -1
  6. package/dist/components/approvals/audit.mjs +1 -1
  7. package/dist/components/foundation/client.d.mts +8 -7
  8. package/dist/components/foundation/client.mjs +1 -1
  9. package/dist/components/foundation/client.mjs.map +1 -1
  10. package/dist/components/foundation/modules/command/command.d.mts +50 -10
  11. package/dist/components/foundation/modules/command/command.mjs +1 -1
  12. package/dist/components/foundation/modules/command/command.mjs.map +1 -1
  13. package/dist/components/foundation/modules/command/lifecycle.d.mts +81 -0
  14. package/dist/components/foundation/modules/command/lifecycle.mjs +2 -0
  15. package/dist/components/foundation/modules/command/lifecycle.mjs.map +1 -0
  16. package/dist/components/foundation/modules/observability/observability.d.mts +8 -0
  17. package/dist/components/foundation/modules/observability/observability.mjs +1 -1
  18. package/dist/components/foundation/modules/observability/observability.mjs.map +1 -1
  19. package/dist/components/foundation/operation.d.mts +6 -6
  20. package/dist/components/foundation/operation.mjs.map +1 -1
  21. package/dist/contracts.d.mts +4 -0
  22. package/dist/contracts.mjs +1 -0
  23. package/dist/crud.d.mts +2 -2
  24. package/dist/crud.mjs.map +1 -1
  25. package/dist/effect.d.mts +12 -0
  26. package/dist/effect.mjs +1 -0
  27. package/dist/errors.d.mts +2 -1
  28. package/dist/errors.mjs +1 -1
  29. package/dist/errors.mjs.map +1 -1
  30. package/dist/idempotency.d.mts +2 -0
  31. package/dist/idempotency.mjs +1 -0
  32. package/dist/modules/contracts/contract.d.mts +70 -0
  33. package/dist/modules/contracts/contract.mjs +2 -0
  34. package/dist/modules/contracts/contract.mjs.map +1 -0
  35. package/dist/modules/contracts/convex.d.mts +22 -0
  36. package/dist/modules/contracts/convex.mjs +2 -0
  37. package/dist/modules/contracts/convex.mjs.map +1 -0
  38. package/dist/modules/contracts/errors.d.mts +44 -0
  39. package/dist/modules/contracts/errors.mjs +2 -0
  40. package/dist/modules/contracts/errors.mjs.map +1 -0
  41. package/dist/modules/contracts/exposure.d.mts +105 -0
  42. package/dist/modules/contracts/exposure.mjs +2 -0
  43. package/dist/modules/contracts/exposure.mjs.map +1 -0
  44. package/dist/modules/contracts/idempotency.d.mts +87 -0
  45. package/dist/modules/contracts/idempotency.mjs +2 -0
  46. package/dist/modules/contracts/idempotency.mjs.map +1 -0
  47. package/dist/modules/contracts/standard.d.mts +16 -0
  48. package/dist/modules/contracts/standard.mjs +2 -0
  49. package/dist/modules/contracts/standard.mjs.map +1 -0
  50. package/dist/modules/contracts/workflow.d.mts +167 -0
  51. package/dist/modules/contracts/workflow.mjs +2 -0
  52. package/dist/modules/contracts/workflow.mjs.map +1 -0
  53. package/dist/modules/effect/agent-tools.d.mts +2 -0
  54. package/dist/modules/effect/agent-tools.mjs +1 -0
  55. package/dist/modules/effect/api-runtime.d.mts +28 -0
  56. package/dist/modules/effect/api-runtime.mjs +2 -0
  57. package/dist/modules/effect/api-runtime.mjs.map +1 -0
  58. package/dist/modules/effect/api-zod.d.mts +35 -0
  59. package/dist/modules/effect/api-zod.mjs +2 -0
  60. package/dist/modules/effect/api-zod.mjs.map +1 -0
  61. package/dist/modules/effect/api.d.mts +21 -0
  62. package/dist/modules/effect/api.mjs +2 -0
  63. package/dist/modules/effect/api.mjs.map +1 -0
  64. package/dist/modules/effect/client.d.mts +11 -0
  65. package/dist/modules/effect/client.mjs +1 -0
  66. package/dist/modules/effect/command.d.mts +59 -0
  67. package/dist/modules/effect/command.mjs +2 -0
  68. package/dist/modules/effect/command.mjs.map +1 -0
  69. package/dist/modules/effect/crud.d.mts +785 -0
  70. package/dist/modules/effect/crud.mjs +2 -0
  71. package/dist/modules/effect/crud.mjs.map +1 -0
  72. package/dist/modules/effect/errors.d.mts +8 -0
  73. package/dist/modules/effect/errors.mjs +2 -0
  74. package/dist/modules/effect/errors.mjs.map +1 -0
  75. package/dist/modules/effect/foundation.d.mts +21 -0
  76. package/dist/modules/effect/foundation.mjs +2 -0
  77. package/dist/modules/effect/foundation.mjs.map +1 -0
  78. package/dist/modules/effect/idempotency.d.mts +31 -0
  79. package/dist/modules/effect/idempotency.mjs +2 -0
  80. package/dist/modules/effect/idempotency.mjs.map +1 -0
  81. package/dist/modules/effect/normalize.d.mts +10 -0
  82. package/dist/modules/effect/normalize.mjs +2 -0
  83. package/dist/modules/effect/normalize.mjs.map +1 -0
  84. package/dist/modules/effect/operation.d.mts +141 -0
  85. package/dist/modules/effect/operation.mjs +2 -0
  86. package/dist/modules/effect/operation.mjs.map +1 -0
  87. package/dist/modules/effect/query.d.mts +65 -0
  88. package/dist/modules/effect/query.mjs +2 -0
  89. package/dist/modules/effect/query.mjs.map +1 -0
  90. package/dist/modules/effect/schema.d.mts +33 -0
  91. package/dist/modules/effect/schema.mjs +2 -0
  92. package/dist/modules/effect/schema.mjs.map +1 -0
  93. package/dist/modules/effect/workflow.d.mts +77 -0
  94. package/dist/modules/effect/workflow.mjs +2 -0
  95. package/dist/modules/effect/workflow.mjs.map +1 -0
  96. package/dist/node_modules/@standard-schema/spec/dist/index.d.mts +79 -0
  97. package/dist/workflow.d.mts +2 -0
  98. package/dist/workflow.mjs +1 -0
  99. package/docs/auth.md +65 -0
  100. package/docs/commands.md +118 -0
  101. package/docs/domain-operations.md +615 -0
  102. package/docs/effect.md +384 -0
  103. package/package.json +33 -1
  104. package/scripts/convex-smoke-env.mjs +33 -0
  105. package/scripts/smoke-packed-components.mjs +9 -2
  106. package/scripts/smoke-packed-effect.mjs +439 -0
  107. package/scripts/test-packed-effect.mjs +457 -0
  108. package/scripts/test-packed-ids.mjs +1 -0
@@ -0,0 +1,77 @@
1
+ import { ContractInput, ContractOutput, ContractSchema } from "../contracts/contract.mjs";
2
+ import { ContractDecoderError, ContractDecoderRequirements } from "./schema.mjs";
3
+ import { CallbackError, CallbackRequirements, EffectValue } from "./operation.mjs";
4
+ import { IdempotencyInvocation } from "../contracts/idempotency.mjs";
5
+ import { EffectIdempotencyPreparation } from "./idempotency.mjs";
6
+ import { WorkflowBindingError, WorkflowExecution, WorkflowHostState, WorkflowNativeStatus, WorkflowOutcome, WorkflowTransition } from "../contracts/workflow.mjs";
7
+ import { Effect } from "effect";
8
+ //#region src/modules/effect/workflow.d.ts
9
+ type Supported<A> = A | PromiseLike<A> | Effect.Effect<A, unknown, unknown>;
10
+ /** Services exist only inside a new native invocation; ordinary throws/rejections remain defects. */
11
+ declare function effectWorkflowAttempt<Args, Authorize, Execute>(configuration: {
12
+ authorize: (execution: WorkflowExecution<Args>) => Authorize;
13
+ execute: (execution: WorkflowExecution<Args>, authority: EffectValue<Authorize>) => Execute;
14
+ }): (input: WorkflowExecution<Args>) => Effect.Effect<EffectValue<Execute>, WorkflowBindingError | CallbackError<Authorize | Execute>, CallbackRequirements<Authorize | Execute>>;
15
+ /** Native mutation promise rejects outside the child transaction before mapping declared terminal details. */
16
+ declare function effectTerminalWorkflowAttempt<Args, Authorize, const Result extends ContractSchema, const Failure extends ContractSchema, Terminal extends Supported<ContractInput<Failure> | undefined>>(configuration: ({
17
+ kind: 'mutation';
18
+ atomicJournalProof: 'host-verified';
19
+ } | {
20
+ kind: 'action';
21
+ protectedMutation: EffectReceiptProtectedWorkflowMutation<Args, unknown, unknown, unknown>;
22
+ }) & {
23
+ authorize: (execution: WorkflowExecution<Args>) => Authorize;
24
+ invokeMutation: (execution: WorkflowExecution<Args>, authority: EffectValue<Authorize>) => Promise<ContractInput<Result>>;
25
+ terminal: (error: unknown) => Terminal;
26
+ result: Result;
27
+ failure: Failure;
28
+ }): (input: WorkflowExecution<Args>) => Effect.Effect<WorkflowOutcome<ContractInput<Result>, ContractOutput<Failure>>, WorkflowBindingError | CallbackError<Authorize | Terminal> | ContractDecoderError<Result | Failure>, CallbackRequirements<Authorize | Terminal> | ContractDecoderRequirements<Result | Failure>>;
29
+ declare const protectedMutation: unique symbol;
30
+ interface EffectReceiptProtectedWorkflowMutation<Args, Result, Error, Requirements> {
31
+ readonly [protectedMutation]: {
32
+ args: Args;
33
+ result: Result;
34
+ };
35
+ readonly handler: (execution: WorkflowExecution<Args>) => Effect.Effect<Result, Error, Requirements>;
36
+ }
37
+ type Completion<Prepare> = Extract<EffectValue<Prepare>, {
38
+ kind: 'execute';
39
+ }> extends {
40
+ complete: (...arguments_: never[]) => infer Returned;
41
+ } ? Returned : never;
42
+ /** Host runs the handler in the secured inner native mutation transaction, including receipt completion. */
43
+ declare function effectReceiptWorkflowMutation<Args, Authorize, const Replay extends ContractSchema, Prepare extends Supported<EffectIdempotencyPreparation<ContractOutput<Replay>, unknown, unknown>>, Execute extends Supported<ContractOutput<Replay>>>(configuration: {
44
+ authorize: (execution: WorkflowExecution<Args>) => Authorize;
45
+ receipt: (authority: EffectValue<Authorize>) => {
46
+ replayResult: Replay;
47
+ prepare: (invocation: IdempotencyInvocation) => Prepare;
48
+ };
49
+ invocation: (execution: WorkflowExecution<Args>, authority: EffectValue<Authorize>) => IdempotencyInvocation;
50
+ execute: (execution: WorkflowExecution<Args>, authority: EffectValue<Authorize>) => Execute;
51
+ }): EffectReceiptProtectedWorkflowMutation<Args, ContractOutput<Replay>, WorkflowBindingError | CallbackError<Authorize | Prepare | Execute | Completion<Prepare>> | ContractDecoderError<Replay>, CallbackRequirements<Authorize | Prepare | Execute | Completion<Prepare>> | ContractDecoderRequirements<Replay>>;
52
+ type StateResult<Read> = EffectValue<Read> extends WorkflowHostState<infer Result, unknown> ? Result : never;
53
+ type StateFailure<Read> = EffectValue<Read> extends WorkflowHostState<unknown, infer Failure> ? Failure : never;
54
+ /** The host still owns native manager calls and transactional/CAS writes; each method retains only its used channels. */
55
+ declare function effectWorkflowStatusBindings<Authorize, Read extends Supported<WorkflowHostState>, Write, Native extends Supported<WorkflowNativeStatus>, Receipt extends Supported<WorkflowOutcome<StateResult<Read>, StateFailure<Read>> | undefined>, Cancel, Restart>(configuration: {
56
+ authorize: () => Authorize;
57
+ read: () => Read;
58
+ write: (expectedGeneration: number, state: WorkflowHostState<StateResult<Read>, StateFailure<Read>>) => Write;
59
+ nativeStatus: () => Native;
60
+ receiptOutcome: () => Receipt;
61
+ cancelNative: () => Cancel;
62
+ restartNative: () => Restart;
63
+ }): {
64
+ callback(expectedGeneration: number, nativeStatus: WorkflowNativeStatus, outcome?: WorkflowOutcome<StateResult<Read>, StateFailure<Read>>): Effect.Effect<WorkflowTransition<StateResult<Read>, StateFailure<Read>>, WorkflowBindingError | CallbackError<Authorize> | CallbackError<Read> | CallbackError<Write>, CallbackRequirements<Authorize> | CallbackRequirements<Read> | CallbackRequirements<Write>>;
65
+ reconcile(expectedGeneration: number): Effect.Effect<WorkflowTransition<StateResult<Read>, StateFailure<Read>>, WorkflowBindingError | CallbackError<Authorize> | CallbackError<Native> | CallbackError<Read> | CallbackError<Receipt> | CallbackError<Write>, CallbackRequirements<Authorize> | CallbackRequirements<Native> | CallbackRequirements<Read> | CallbackRequirements<Receipt> | CallbackRequirements<Write>>;
66
+ cancel(expectedGeneration: number): Effect.Effect<WorkflowTransition<StateResult<Read>, StateFailure<Read>>, WorkflowBindingError | CallbackError<Authorize> | CallbackError<Cancel> | CallbackError<Read> | CallbackError<Write>, CallbackRequirements<Authorize> | CallbackRequirements<Cancel> | CallbackRequirements<Read> | CallbackRequirements<Write>>;
67
+ restart(expectedGeneration: number): Effect.Effect<{
68
+ kind: 'ignored';
69
+ state: WorkflowHostState<StateResult<Read>, StateFailure<Read>>;
70
+ } | {
71
+ kind: 'applied';
72
+ state: WorkflowHostState<StateResult<Read>, StateFailure<Read>>;
73
+ }, WorkflowBindingError | CallbackError<Authorize> | CallbackError<Read> | CallbackError<Restart> | CallbackError<Write>, CallbackRequirements<Authorize> | CallbackRequirements<Read> | CallbackRequirements<Restart> | CallbackRequirements<Write>>;
74
+ };
75
+ //#endregion
76
+ export { EffectReceiptProtectedWorkflowMutation, effectReceiptWorkflowMutation, effectTerminalWorkflowAttempt, effectWorkflowAttempt, effectWorkflowStatusBindings };
77
+ //# sourceMappingURL=workflow.d.mts.map
@@ -0,0 +1,2 @@
1
+ import{decodeEffectContract as e}from"./schema.mjs";import{normalizeEffect as t}from"./normalize.mjs";import{WorkflowBindingError as n,workflowCancel as r,workflowExecution as i,workflowRestart as a,workflowTransition as o}from"../contracts/workflow.mjs";import{Effect as s}from"effect";function c(e){return n=>s.gen(function*(){let r=yield*f(n),i=yield*t(()=>e.authorize(r));return yield*t(()=>e.execute(r,i))})}function l(n){return r=>s.gen(function*(){let i=yield*f(r),a=yield*t(()=>n.authorize(i)),o=yield*s.tryPromise({try:()=>n.invokeMutation(i,a),catch:e=>e}).pipe(s.map(e=>({kind:`success`,value:e})),s.catch(e=>s.succeed({kind:`rejected`,error:e})));if(o.kind===`rejected`){let r=yield*t(()=>n.terminal(o.error));if(r===void 0)return yield*s.die(o.error);let a=yield*e(n.failure,r);return yield*f({...i,args:a}),{version:1,kind:`failed`,error:a}}return yield*e(n.result,o.value),yield*f({...i,args:o.value}),{version:1,kind:`succeeded`,value:o.value}})}function u(n){return{handler:r=>s.gen(function*(){let i=yield*f(r),a=yield*t(()=>n.authorize(i)),o=n.receipt(a),s=yield*t(()=>o.prepare(n.invocation(i,a)));if(s.kind===`replay`)return yield*e(o.replayResult,s.result);let c=yield*t(()=>n.execute(i,a)),l=s.complete;return yield*t(()=>l(c)),c})}}function d(e){function n(){return t(()=>e.read()).pipe(s.map(e=>e))}function i(r,i,a){return s.gen(function*(){let s=yield*n(),c={generation:r,nativeStatus:i},l=yield*p(()=>o(s,a===void 0?c:{...c,outcome:a}));return l.kind===`applied`&&(yield*t(()=>e.write(r,l.state))),l})}return{callback(n,r,a){return t(()=>e.authorize()).pipe(s.flatMap(()=>i(n,r,a)))},reconcile(n){return s.gen(function*(){return yield*t(()=>e.authorize()),yield*i(n,yield*t(()=>e.nativeStatus()),yield*t(()=>e.receiptOutcome()))})},cancel(i){return s.gen(function*(){yield*t(()=>e.authorize());let a=yield*n(),o=yield*p(()=>r(a,i));return o.kind===`applied`&&a.status===`running`&&(yield*t(()=>e.cancelNative()),yield*t(()=>e.write(i,o.state))),o})},restart(r){return s.gen(function*(){yield*t(()=>e.authorize());let i=yield*n();if(i.generation!==r)return{kind:`ignored`,state:i};let o=yield*p(()=>a(i));return yield*t(()=>e.restartNative()),yield*t(()=>e.write(r,o)),{kind:`applied`,state:o}})}}}function f(e){return p(()=>i(e))}function p(e){return s.try({try:e,catch:e=>{if(e instanceof n)return e;throw e}})}export{u as effectReceiptWorkflowMutation,l as effectTerminalWorkflowAttempt,c as effectWorkflowAttempt,d as effectWorkflowStatusBindings};
2
+ //# sourceMappingURL=workflow.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow.mjs","names":[],"sources":["../../../src/modules/effect/workflow.ts"],"sourcesContent":["import { Effect } from 'effect'\nimport type { ContractInput, ContractOutput, ContractSchema } from '../contracts/contract'\nimport {\n\tWorkflowBindingError,\n\tworkflowExecution,\n\tworkflowTransition,\n\tworkflowCancel,\n\tworkflowRestart,\n\ttype WorkflowExecution,\n\ttype WorkflowOutcome,\n\ttype WorkflowHostState,\n\ttype WorkflowNativeStatus,\n} from '../contracts/workflow'\nimport type { IdempotencyInvocation } from '../contracts/idempotency'\nimport type { EffectIdempotencyPreparation } from './idempotency'\nimport type { CallbackError, CallbackRequirements, EffectValue } from './operation'\nimport { normalizeEffect } from './normalize'\nimport {\n\tdecodeEffectContract,\n\ttype ContractDecoderError,\n\ttype ContractDecoderRequirements,\n} from './schema'\n\ntype Supported<A> = A | PromiseLike<A> | Effect.Effect<A, unknown, unknown>\n/** Services exist only inside a new native invocation; ordinary throws/rejections remain defects. */\nexport function effectWorkflowAttempt<Args, Authorize, Execute>(configuration: {\n\tauthorize: (execution: WorkflowExecution<Args>) => Authorize\n\texecute: (execution: WorkflowExecution<Args>, authority: EffectValue<Authorize>) => Execute\n}) {\n\treturn (\n\t\tinput: WorkflowExecution<Args>,\n\t): Effect.Effect<\n\t\tEffectValue<Execute>,\n\t\tWorkflowBindingError | CallbackError<Authorize | Execute>,\n\t\tCallbackRequirements<Authorize | Execute>\n\t> =>\n\t\tEffect.gen(function* () {\n\t\t\tconst execution = yield* descriptor(input)\n\t\t\tconst authority = yield* normalizeEffect(() => configuration.authorize(execution))\n\t\t\treturn yield* normalizeEffect(() => configuration.execute(execution, authority))\n\t\t})\n}\n/** Native mutation promise rejects outside the child transaction before mapping declared terminal details. */\nexport function effectTerminalWorkflowAttempt<\n\tArgs,\n\tAuthorize,\n\tconst Result extends ContractSchema,\n\tconst Failure extends ContractSchema,\n\tTerminal extends Supported<ContractInput<Failure> | undefined>,\n>(\n\tconfiguration: (\n\t\t| { kind: 'mutation'; atomicJournalProof: 'host-verified' }\n\t\t| {\n\t\t\t\tkind: 'action'\n\t\t\t\tprotectedMutation: EffectReceiptProtectedWorkflowMutation<Args, unknown, unknown, unknown>\n\t\t }\n\t) & {\n\t\tauthorize: (execution: WorkflowExecution<Args>) => Authorize\n\t\tinvokeMutation: (\n\t\t\texecution: WorkflowExecution<Args>,\n\t\t\tauthority: EffectValue<Authorize>,\n\t\t) => Promise<ContractInput<Result>>\n\t\t// oxlint-disable-next-line anti-slop/no-unknown-parameters -- A rejected native mutation can throw any value; the host terminal callback classifies it before failure contract validation.\n\t\tterminal: (error: unknown) => Terminal\n\t\tresult: Result\n\t\tfailure: Failure\n\t},\n) {\n\treturn (\n\t\tinput: WorkflowExecution<Args>,\n\t): Effect.Effect<\n\t\tWorkflowOutcome<ContractInput<Result>, ContractOutput<Failure>>,\n\t\t| WorkflowBindingError\n\t\t| CallbackError<Authorize | Terminal>\n\t\t| ContractDecoderError<Result | Failure>,\n\t\tCallbackRequirements<Authorize | Terminal> | ContractDecoderRequirements<Result | Failure>\n\t> =>\n\t\tEffect.gen(function* () {\n\t\t\tconst execution = yield* descriptor(input)\n\t\t\tconst authority = yield* normalizeEffect(() => configuration.authorize(execution))\n\t\t\tconst native = yield* Effect.tryPromise({\n\t\t\t\ttry: () => configuration.invokeMutation(execution, authority),\n\t\t\t\tcatch: (error) => error,\n\t\t\t}).pipe(\n\t\t\t\tEffect.map((value) => ({ kind: 'success' as const, value })),\n\t\t\t\tEffect.catch((error) => Effect.succeed({ kind: 'rejected' as const, error })),\n\t\t\t)\n\t\t\tif (native.kind === 'rejected') {\n\t\t\t\tconst failure = yield* normalizeEffect(() => configuration.terminal(native.error))\n\t\t\t\tif (failure === undefined) return yield* Effect.die(native.error)\n\t\t\t\t// SAFETY: Terminal is constrained to this exact failure wire input or undefined.\n\t\t\t\tconst error = yield* decodeEffectContract(\n\t\t\t\t\tconfiguration.failure,\n\t\t\t\t\tfailure as ContractInput<Failure>,\n\t\t\t\t)\n\t\t\t\tyield* descriptor({ ...execution, args: error })\n\t\t\t\treturn { version: 1 as const, kind: 'failed' as const, error }\n\t\t\t}\n\t\t\tyield* decodeEffectContract(configuration.result, native.value)\n\t\t\tyield* descriptor({ ...execution, args: native.value })\n\t\t\treturn { version: 1 as const, kind: 'succeeded' as const, value: native.value }\n\t\t})\n}\ndeclare const protectedMutation: unique symbol\nexport interface EffectReceiptProtectedWorkflowMutation<Args, Result, Error, Requirements> {\n\treadonly [protectedMutation]: { args: Args; result: Result }\n\treadonly handler: (\n\t\texecution: WorkflowExecution<Args>,\n\t) => Effect.Effect<Result, Error, Requirements>\n}\ntype Completion<Prepare> =\n\tExtract<EffectValue<Prepare>, { kind: 'execute' }> extends {\n\t\tcomplete: (...arguments_: never[]) => infer Returned\n\t}\n\t\t? Returned\n\t\t: never\n/** Host runs the handler in the secured inner native mutation transaction, including receipt completion. */\nexport function effectReceiptWorkflowMutation<\n\tArgs,\n\tAuthorize,\n\tconst Replay extends ContractSchema,\n\tPrepare extends Supported<EffectIdempotencyPreparation<ContractOutput<Replay>, unknown, unknown>>,\n\tExecute extends Supported<ContractOutput<Replay>>,\n>(configuration: {\n\tauthorize: (execution: WorkflowExecution<Args>) => Authorize\n\treceipt: (authority: EffectValue<Authorize>) => {\n\t\treplayResult: Replay\n\t\tprepare: (invocation: IdempotencyInvocation) => Prepare\n\t}\n\tinvocation: (\n\t\texecution: WorkflowExecution<Args>,\n\t\tauthority: EffectValue<Authorize>,\n\t) => IdempotencyInvocation\n\texecute: (execution: WorkflowExecution<Args>, authority: EffectValue<Authorize>) => Execute\n}): EffectReceiptProtectedWorkflowMutation<\n\tArgs,\n\tContractOutput<Replay>,\n\t| WorkflowBindingError\n\t| CallbackError<Authorize | Prepare | Execute | Completion<Prepare>>\n\t| ContractDecoderError<Replay>,\n\t| CallbackRequirements<Authorize | Prepare | Execute | Completion<Prepare>>\n\t| ContractDecoderRequirements<Replay>\n> {\n\tconst handler = (input: WorkflowExecution<Args>) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst execution = yield* descriptor(input)\n\t\t\tconst authority = yield* normalizeEffect(() => configuration.authorize(execution))\n\t\t\tconst adapter = configuration.receipt(authority)\n\t\t\tconst preparation = yield* normalizeEffect(() =>\n\t\t\t\tadapter.prepare(configuration.invocation(execution, authority)),\n\t\t\t)\n\t\t\tif (preparation.kind === 'replay')\n\t\t\t\treturn yield* decodeEffectContract(adapter.replayResult, preparation.result)\n\t\t\tconst value = yield* normalizeEffect(() => configuration.execute(execution, authority))\n\t\t\t// SAFETY: the execute branch came from this exact Prepare value; recover its complete callback rather than the broad constraint.\n\t\t\tconst complete = preparation.complete as (\n\t\t\t\tresult: ContractOutput<Replay>,\n\t\t\t) => Completion<Prepare>\n\t\t\t// SAFETY: Execute's supported success is the exact final replay output contract.\n\t\t\tyield* normalizeEffect(() => complete(value as ContractOutput<Replay>))\n\t\t\t// SAFETY: Execute's supported success is the exact final replay output contract.\n\t\t\treturn value as ContractOutput<Replay>\n\t\t})\n\t// SAFETY: only private receipt evidence is asserted; handler's inferred E/R remain checked against the public return.\n\treturn { handler } as ReceiptEvidence<Args, ContractOutput<Replay>, typeof handler>\n}\ntype ReceiptEvidence<Args, Result, Handler> = {\n\thandler: Handler\n\treadonly [protectedMutation]: { args: Args; result: Result }\n}\ntype StateResult<Read> =\n\tEffectValue<Read> extends WorkflowHostState<infer Result, unknown> ? Result : never\ntype StateFailure<Read> =\n\tEffectValue<Read> extends WorkflowHostState<unknown, infer Failure> ? Failure : never\n/** The host still owns native manager calls and transactional/CAS writes; each method retains only its used channels. */\nexport function effectWorkflowStatusBindings<\n\tAuthorize,\n\tRead extends Supported<WorkflowHostState>,\n\tWrite,\n\tNative extends Supported<WorkflowNativeStatus>,\n\tReceipt extends Supported<WorkflowOutcome<StateResult<Read>, StateFailure<Read>> | undefined>,\n\tCancel,\n\tRestart,\n>(configuration: {\n\tauthorize: () => Authorize\n\tread: () => Read\n\twrite: (\n\t\texpectedGeneration: number,\n\t\tstate: WorkflowHostState<StateResult<Read>, StateFailure<Read>>,\n\t) => Write\n\tnativeStatus: () => Native\n\treceiptOutcome: () => Receipt\n\tcancelNative: () => Cancel\n\trestartNative: () => Restart\n}) {\n\ttype State = WorkflowHostState<StateResult<Read>, StateFailure<Read>>\n\tfunction read() {\n\t\t// SAFETY: Read's supported success is the host state whose outcome types are extracted above.\n\t\treturn normalizeEffect(() => configuration.read()).pipe(Effect.map((state) => state as State))\n\t}\n\tfunction apply(\n\t\texpectedGeneration: number,\n\t\tnativeStatus: WorkflowNativeStatus,\n\t\toutcome?: WorkflowOutcome<StateResult<Read>, StateFailure<Read>>,\n\t) {\n\t\treturn Effect.gen(function* () {\n\t\t\tconst current = yield* read()\n\t\t\tconst update = { generation: expectedGeneration, nativeStatus }\n\t\t\tconst transition = yield* protocol(() =>\n\t\t\t\tworkflowTransition(current, outcome === undefined ? update : { ...update, outcome }),\n\t\t\t)\n\t\t\tif (transition.kind === 'applied')\n\t\t\t\tyield* normalizeEffect(() => configuration.write(expectedGeneration, transition.state))\n\t\t\treturn transition\n\t\t})\n\t}\n\treturn {\n\t\tcallback(\n\t\t\texpectedGeneration: number,\n\t\t\tnativeStatus: WorkflowNativeStatus,\n\t\t\toutcome?: WorkflowOutcome<StateResult<Read>, StateFailure<Read>>,\n\t\t) {\n\t\t\treturn normalizeEffect(() => configuration.authorize()).pipe(\n\t\t\t\tEffect.flatMap(() => apply(expectedGeneration, nativeStatus, outcome)),\n\t\t\t)\n\t\t},\n\t\treconcile(expectedGeneration: number) {\n\t\t\treturn Effect.gen(function* () {\n\t\t\t\tyield* normalizeEffect(() => configuration.authorize())\n\t\t\t\tconst native = yield* normalizeEffect(() => configuration.nativeStatus())\n\t\t\t\tconst outcome = yield* normalizeEffect(() => configuration.receiptOutcome())\n\t\t\t\t// SAFETY: Native and Receipt are constrained to these exact host transport values.\n\t\t\t\treturn yield* apply(\n\t\t\t\t\texpectedGeneration,\n\t\t\t\t\tnative as WorkflowNativeStatus,\n\t\t\t\t\toutcome as WorkflowOutcome<StateResult<Read>, StateFailure<Read>> | undefined,\n\t\t\t\t)\n\t\t\t})\n\t\t},\n\t\tcancel(expectedGeneration: number) {\n\t\t\treturn Effect.gen(function* () {\n\t\t\t\tyield* normalizeEffect(() => configuration.authorize())\n\t\t\t\tconst current = yield* read()\n\t\t\t\tconst transition = yield* protocol(() => workflowCancel(current, expectedGeneration))\n\t\t\t\tif (transition.kind === 'applied' && current.status === 'running') {\n\t\t\t\t\tyield* normalizeEffect(() => configuration.cancelNative())\n\t\t\t\t\tyield* normalizeEffect(() => configuration.write(expectedGeneration, transition.state))\n\t\t\t\t}\n\t\t\t\treturn transition\n\t\t\t})\n\t\t},\n\t\trestart(expectedGeneration: number) {\n\t\t\treturn Effect.gen(function* () {\n\t\t\t\tyield* normalizeEffect(() => configuration.authorize())\n\t\t\t\tconst current = yield* read()\n\t\t\t\tif (current.generation !== expectedGeneration)\n\t\t\t\t\treturn { kind: 'ignored' as const, state: current }\n\t\t\t\tconst state = yield* protocol(() => workflowRestart(current))\n\t\t\t\tyield* normalizeEffect(() => configuration.restartNative())\n\t\t\t\tyield* normalizeEffect(() => configuration.write(expectedGeneration, state))\n\t\t\t\treturn { kind: 'applied' as const, state }\n\t\t\t})\n\t\t},\n\t}\n}\nfunction descriptor<Args>(\n\tvalue: WorkflowExecution<Args>,\n): Effect.Effect<WorkflowExecution<Args>, WorkflowBindingError> {\n\treturn protocol(() => workflowExecution(value))\n}\nfunction protocol<A>(evaluate: () => A): Effect.Effect<A, WorkflowBindingError> {\n\treturn Effect.try({\n\t\ttry: evaluate,\n\t\tcatch: (error) => {\n\t\t\tif (error instanceof WorkflowBindingError) return error\n\t\t\tthrow error\n\t\t},\n\t})\n}\n"],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ //#region node_modules/@standard-schema/spec/dist/index.d.ts
2
+ /** The Standard Typed interface. This is a base type extended by other specs. */
3
+ interface StandardTypedV1<Input = unknown, Output = Input> {
4
+ /** The Standard properties. */
5
+ readonly "~standard": StandardTypedV1.Props<Input, Output>;
6
+ }
7
+ declare namespace StandardTypedV1 {
8
+ /** The Standard Typed properties interface. */
9
+ interface Props<Input = unknown, Output = Input> {
10
+ /** The version number of the standard. */
11
+ readonly version: 1;
12
+ /** The vendor name of the schema library. */
13
+ readonly vendor: string;
14
+ /** Inferred types associated with the schema. */
15
+ readonly types?: Types<Input, Output> | undefined;
16
+ }
17
+ /** The Standard Typed types interface. */
18
+ interface Types<Input = unknown, Output = Input> {
19
+ /** The input type of the schema. */
20
+ readonly input: Input;
21
+ /** The output type of the schema. */
22
+ readonly output: Output;
23
+ }
24
+ /** Infers the input type of a Standard Typed. */
25
+ type InferInput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["input"];
26
+ /** Infers the output type of a Standard Typed. */
27
+ type InferOutput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["output"];
28
+ }
29
+ /** The Standard Schema interface. */
30
+ interface StandardSchemaV1<Input = unknown, Output = Input> {
31
+ /** The Standard Schema properties. */
32
+ readonly "~standard": StandardSchemaV1.Props<Input, Output>;
33
+ }
34
+ declare namespace StandardSchemaV1 {
35
+ /** The Standard Schema properties interface. */
36
+ interface Props<Input = unknown, Output = Input> extends StandardTypedV1.Props<Input, Output> {
37
+ /** Validates unknown input values. */
38
+ readonly validate: (value: unknown, options?: StandardSchemaV1.Options | undefined) => Result<Output> | Promise<Result<Output>>;
39
+ }
40
+ /** The result interface of the validate function. */
41
+ type Result<Output> = SuccessResult<Output> | FailureResult;
42
+ /** The result interface if validation succeeds. */
43
+ interface SuccessResult<Output> {
44
+ /** The typed output value. */
45
+ readonly value: Output;
46
+ /** A falsy value for `issues` indicates success. */
47
+ readonly issues?: undefined;
48
+ }
49
+ interface Options {
50
+ /** Explicit support for additional vendor-specific parameters, if needed. */
51
+ readonly libraryOptions?: Record<string, unknown> | undefined;
52
+ }
53
+ /** The result interface if validation fails. */
54
+ interface FailureResult {
55
+ /** The issues of failed validation. */
56
+ readonly issues: ReadonlyArray<Issue>;
57
+ }
58
+ /** The issue interface of the failure output. */
59
+ interface Issue {
60
+ /** The error message of the issue. */
61
+ readonly message: string;
62
+ /** The path of the issue, if any. */
63
+ readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
64
+ }
65
+ /** The path segment interface of the issue. */
66
+ interface PathSegment {
67
+ /** The key representing a path segment. */
68
+ readonly key: PropertyKey;
69
+ }
70
+ /** The Standard types interface. */
71
+ interface Types<Input = unknown, Output = Input> extends StandardTypedV1.Types<Input, Output> {}
72
+ /** Infers the input type of a Standard. */
73
+ type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>;
74
+ /** Infers the output type of a Standard. */
75
+ type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>;
76
+ }
77
+ //#endregion
78
+ export { StandardSchemaV1, StandardTypedV1 };
79
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1,2 @@
1
+ import { ReceiptProtectedWorkflowMutation, WorkflowBinding, WorkflowBindingError, WorkflowExecution, WorkflowHostState, WorkflowHostUpdate, WorkflowNativeStatus, WorkflowOutcome, WorkflowReceiptAdapter, WorkflowStepArguments, WorkflowStepOptions, WorkflowStepRunner, WorkflowTransition, receiptWorkflowMutation, runWorkflowStep, terminalWorkflowAttempt, workflowAttempt, workflowBinding, workflowCancel, workflowExecution, workflowReceiptInvocation, workflowRestart, workflowStatusBindings, workflowTransition } from "./modules/contracts/workflow.mjs";
2
+ export { type ReceiptProtectedWorkflowMutation, type WorkflowBinding, WorkflowBindingError, type WorkflowExecution, type WorkflowHostState, type WorkflowHostUpdate, type WorkflowNativeStatus, type WorkflowOutcome, type WorkflowReceiptAdapter, type WorkflowStepArguments, type WorkflowStepOptions, type WorkflowStepRunner, type WorkflowTransition, receiptWorkflowMutation, runWorkflowStep, terminalWorkflowAttempt, workflowAttempt, workflowBinding, workflowCancel, workflowExecution, workflowReceiptInvocation, workflowRestart, workflowStatusBindings, workflowTransition };
@@ -0,0 +1 @@
1
+ import{WorkflowBindingError as e,receiptWorkflowMutation as t,runWorkflowStep as n,terminalWorkflowAttempt as r,workflowAttempt as i,workflowBinding as a,workflowCancel as o,workflowExecution as s,workflowReceiptInvocation as c,workflowRestart as l,workflowStatusBindings as u,workflowTransition as d}from"./modules/contracts/workflow.mjs";export{e as WorkflowBindingError,t as receiptWorkflowMutation,n as runWorkflowStep,r as terminalWorkflowAttempt,i as workflowAttempt,a as workflowBinding,o as workflowCancel,s as workflowExecution,c as workflowReceiptInvocation,l as workflowRestart,u as workflowStatusBindings,d as workflowTransition};
package/docs/auth.md CHANGED
@@ -64,6 +64,71 @@ export const {
64
64
  Every function in the app is then built from these — raw `query`/`mutation`
65
65
  imports from `_generated/server` appear **only** in this file.
66
66
 
67
+ ## Shared Effect execution after authentication
68
+
69
+ Wrap the constructors returned by `createAuthFunctions` with
70
+ `effectZodApiBuilder` from `cvx-kit/effect`. The original constructor resolves
71
+ authentication, authorization, tenant policy, and the wrapped database before
72
+ the service provider runs. Keep that ordering when adding Effect; use the Zod
73
+ adapter for these custom constructors.
74
+
75
+ This factory accepts the configured `authMutation` from the setup above. The
76
+ actor service comes from trusted `ctx.actor`, with no actor or organization
77
+ argument from callers:
78
+
79
+ <!-- packed-effect-example -->
80
+
81
+ ```ts
82
+ import type { GenericDataModel } from 'convex/server'
83
+ import { createAuthFunctions } from 'cvx-kit/auth'
84
+ import { effectZodApiBuilder } from 'cvx-kit/effect'
85
+ import { Context, Effect } from 'effect'
86
+ import { z } from 'zod'
87
+
88
+ class Actor extends Context.Service<
89
+ Actor,
90
+ { userId: string; organizationId: string; role: string }
91
+ >()('app/Actor') {}
92
+
93
+ export function createEffectAccountApi<DataModel extends GenericDataModel>(
94
+ authMutation: ReturnType<typeof createAuthFunctions<DataModel>>['authMutation'],
95
+ ) {
96
+ const effectAuthMutation = effectZodApiBuilder(authMutation, {
97
+ services: (ctx) => Context.make(Actor, ctx.actor),
98
+ })
99
+ const currentActor = effectAuthMutation({
100
+ args: {},
101
+ returns: z.object({ userId: z.string(), organizationId: z.string() }),
102
+ handler: () =>
103
+ Effect.gen(function* () {
104
+ const actor = yield* Actor
105
+ return { userId: actor.userId, organizationId: actor.organizationId }
106
+ }),
107
+ })
108
+ return { effectAuthMutation, currentActor }
109
+ }
110
+ ```
111
+
112
+ Call `createEffectAccountApi(authMutation)` with the constructor exported by
113
+ your `convex/functions.ts`, then export `currentActor` from the account API
114
+ module. The factory preserves the configured auth policy; it does not choose
115
+ an identity provider or membership authority.
116
+
117
+ Declare the provider once per constructor, then reuse the builder across
118
+ endpoints. Database services should capture the provider's `ctx.db`, so
119
+ trigger and RLS enforcement also applies to Effect-driven writes. Services
120
+ are created per invocation; do not cache an authenticated context in a module
121
+ singleton. Query providers receive readers; HTTP integrations belong in
122
+ actions.
123
+
124
+ The adapter runs the composed Effect within an invocation scope and awaits
125
+ finalizers before returning. Domain errors can be mapped with handler pipes
126
+ or shared `mapError`; they remain thrown failures at the native boundary so
127
+ mutation writes roll back. Native argument/result validators remain in place.
128
+ Effect's inferred server-side error and service channels are not a typed
129
+ transport error contract. See [the Effect guide](./effect.md) for complete
130
+ domain registries, error mapping, scoped services, and tracing.
131
+
67
132
  ## The constructor families
68
133
 
69
134
  | Constructor | Visibility | Auth | Extra |
package/docs/commands.md CHANGED
@@ -37,6 +37,124 @@ export const { Command, Query, observability } = new Foundation(components.found
37
37
  Host code consumes the Foundation **only through this facade** — never deep
38
38
  imports of the component's internal modules.
39
39
 
40
+ For new domains, [the optional Effect facade](./effect.md) offers cohesive
41
+ command/query entries with handlers in the declaration and injectable context.
42
+ The complete guide covers ordinary, `Effect.gen`, and `Effect.fn` handlers,
43
+ nested domain integrations, and shared API execution.
44
+
45
+ ## Incremental migration to Effect
46
+
47
+ The existing Foundation keeps separate operation and handler declarations,
48
+ context-first handler arguments, and Promise executors. No existing consumer
49
+ needs to change when another domain adopts `cvx-kit/effect`.
50
+
51
+ This legacy factory accepts the mounted Foundation component and defines all
52
+ of its domain dependencies through the host context:
53
+
54
+ <!-- packed-effect-example -->
55
+
56
+ ```ts
57
+ import { Foundation, type AuditEntryInput } from 'cvx-kit/components/foundation'
58
+ import { z } from 'zod'
59
+
60
+ type Host = {
61
+ actorId: string
62
+ rename: (id: string, title: string) => Promise<void>
63
+ appendAudit: (entry: AuditEntryInput) => Promise<void>
64
+ }
65
+ const input = z.object({ id: z.string(), title: z.string() }).strict()
66
+ const result = z.object({ ok: z.literal(true) }).strict()
67
+
68
+ export function createLegacyRename(component: ConstructorParameters<typeof Foundation>[0]) {
69
+ const { Command } = new Foundation(component, {
70
+ observability: {
71
+ enabled: false,
72
+ classifyError: () => ({ outcome: 'failed', errorCode: 'RENAME_FAILED' }),
73
+ writeAudit: (host: Host, entry) => host.appendAudit(entry),
74
+ },
75
+ })
76
+ const typed = Command.withContext<Host>()
77
+ const operations = {
78
+ rename: typed.operation({
79
+ command: input,
80
+ result,
81
+ classification: 'business',
82
+ audit: ({ command }, host) => ({
83
+ operation: 'documents.rename',
84
+ actorId: host.actorId,
85
+ aggregate: { type: 'document', id: command.id },
86
+ }),
87
+ }),
88
+ }
89
+ const commands = new Command<Host, typeof operations>(operations)
90
+ return commands.exec({
91
+ operation: 'rename',
92
+ handler: async (host, command) => {
93
+ await host.rename(command.id, command.title)
94
+ return { ok: true }
95
+ },
96
+ })
97
+ }
98
+ ```
99
+
100
+ The replacement keeps the handler beside its schemas and audit policy. It
101
+ uses input-first, context-second callbacks and returns a lazy Effect from
102
+ `exec(operation, input, host)`, including when the handler is ordinary async:
103
+
104
+ <!-- packed-effect-example -->
105
+
106
+ ```ts
107
+ import { type AuditEntryInput } from 'cvx-kit/components/foundation'
108
+ import { createEffectFoundation } from 'cvx-kit/effect'
109
+ import { z } from 'zod'
110
+
111
+ type Host = {
112
+ actorId: string
113
+ rename: (id: string, title: string) => Promise<void>
114
+ appendAudit: (entry: AuditEntryInput) => Promise<void>
115
+ }
116
+ const input = z.object({ id: z.string(), title: z.string() }).strict()
117
+ const result = z.object({ ok: z.literal(true) }).strict()
118
+ const foundation = createEffectFoundation({
119
+ observability: {
120
+ enabled: false,
121
+ classifyError: () => ({ outcome: 'failed', errorCode: 'RENAME_FAILED' }),
122
+ },
123
+ writeAudit: (host: Host, entry) => host.appendAudit(entry),
124
+ })
125
+ export const commands = foundation.Command({
126
+ context: (host: Host) => host,
127
+ operations: ({ command }) => ({
128
+ rename: command({
129
+ input,
130
+ result,
131
+ classification: 'business',
132
+ handler: async (input, context) => {
133
+ await context.rename(input.id, input.title)
134
+ return { ok: true as const }
135
+ },
136
+ audit: ({ command }, context) => ({
137
+ operation: 'documents.rename',
138
+ actorId: context.actorId,
139
+ aggregate: { type: 'document', id: command.id },
140
+ }),
141
+ }),
142
+ }),
143
+ })
144
+ ```
145
+
146
+ Pass the resulting Effect to a handler registered through a shared
147
+ `effectApiBuilder` or `effectZodApiBuilder`; see [the complete domain-to-API
148
+ example](./effect.md). Domain composition does not start a separate runner.
149
+ Ordinary throws and rejected Promises become defects; use `Effect.fail` or
150
+ `Effect.tryPromise` for an inferred typed error channel. The native Convex
151
+ reference retains argument/result types, but does not transport the server's
152
+ Effect error and service types.
153
+
154
+ Both facades share command lifecycle ordering, validation, audit, and replay
155
+ rules. Commands that write remain inside a Convex mutation, and failures must
156
+ escape that mutation to roll its writes back.
157
+
40
158
  ## Defining commands — per domain
41
159
 
42
160
  Each domain declares a frozen registry of operations and derives typed