cvx-kit 0.1.3 → 0.1.4

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 (82) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +69 -24
  3. package/dist/agent-tools.d.mts +28 -12
  4. package/dist/agent-tools.mjs +1 -1
  5. package/dist/agent-tools.mjs.map +1 -1
  6. package/dist/auth.d.mts +4 -2
  7. package/dist/auth.mjs +1 -1
  8. package/dist/auth.mjs.map +1 -1
  9. package/dist/components/approvals/_generated/api.d.mts +1 -1
  10. package/dist/components/approvals/audit.mjs +1 -1
  11. package/dist/components/approvals/audit.mjs.map +1 -1
  12. package/dist/components/approvals/client.d.mts +1 -1
  13. package/dist/components/approvals/client.mjs +1 -1
  14. package/dist/components/approvals/client.mjs.map +1 -1
  15. package/dist/components/approvals/constants.mjs.map +1 -1
  16. package/dist/components/approvals/decisions.d.mts +6 -7
  17. package/dist/components/approvals/decisions.mjs +1 -1
  18. package/dist/components/approvals/decisions.mjs.map +1 -1
  19. package/dist/components/approvals/functions.mjs +1 -1
  20. package/dist/components/approvals/functions.mjs.map +1 -1
  21. package/dist/components/approvals/health.mjs.map +1 -1
  22. package/dist/components/approvals/requests.mjs.map +1 -1
  23. package/dist/components/approvals/validators.mjs.map +1 -1
  24. package/dist/components/approvals/workflow.mjs +1 -1
  25. package/dist/components/approvals/workflow.mjs.map +1 -1
  26. package/dist/components/approvals/workflow_steps.d.mts +3 -3
  27. package/dist/components/approvals/workflow_steps.mjs +1 -1
  28. package/dist/components/approvals/workflow_steps.mjs.map +1 -1
  29. package/dist/components/foundation/client.d.mts +17 -24
  30. package/dist/components/foundation/client.mjs +1 -1
  31. package/dist/components/foundation/client.mjs.map +1 -1
  32. package/dist/components/foundation/modules/command/command.d.mts +4 -4
  33. package/dist/components/foundation/modules/command/command.mjs.map +1 -1
  34. package/dist/components/foundation/modules/observability/observability.d.mts +1 -1
  35. package/dist/components/foundation/modules/observability/observability.mjs +1 -1
  36. package/dist/components/foundation/modules/observability/observability.mjs.map +1 -1
  37. package/dist/components/foundation/modules/query/query.d.mts +8 -8
  38. package/dist/components/foundation/modules/query/query.mjs +1 -1
  39. package/dist/components/foundation/modules/query/query.mjs.map +1 -1
  40. package/dist/components/foundation/operation.d.mts +1 -1
  41. package/dist/components/foundation/operation.mjs.map +1 -1
  42. package/dist/components/foundation/result.d.mts +1 -1
  43. package/dist/components/foundation/result.mjs.map +1 -1
  44. package/dist/components/foundation/telemetry.mjs +1 -1
  45. package/dist/components/foundation/telemetry.mjs.map +1 -1
  46. package/dist/crud.d.mts +177 -16
  47. package/dist/crud.mjs +1 -1
  48. package/dist/crud.mjs.map +1 -1
  49. package/dist/middleware.d.mts +7 -7
  50. package/dist/middleware.mjs +1 -1
  51. package/dist/middleware.mjs.map +1 -1
  52. package/dist/oxlint.d.mts +9 -0
  53. package/dist/oxlint.mjs +2 -0
  54. package/dist/oxlint.mjs.map +1 -0
  55. package/dist/tenancy.mjs +1 -1
  56. package/dist/tenancy.mjs.map +1 -1
  57. package/dist/triggers.mjs +1 -1
  58. package/dist/triggers.mjs.map +1 -1
  59. package/dist/webhooks.d.mts +17 -106
  60. package/dist/webhooks.mjs.map +1 -1
  61. package/dist/zod-table.d.mts +46 -90
  62. package/dist/zod-table.mjs +1 -1
  63. package/dist/zod-table.mjs.map +1 -1
  64. package/docs/approvals.md +57 -56
  65. package/docs/architecture.md +1 -1
  66. package/docs/auth.md +57 -33
  67. package/docs/commands.md +53 -53
  68. package/docs/conventions.md +40 -40
  69. package/docs/crud.md +5 -5
  70. package/docs/llms.md +117 -71
  71. package/docs/maintainability.md +4 -4
  72. package/docs/migration.md +33 -33
  73. package/docs/tenancy.md +5 -8
  74. package/docs/triggers.md +4 -2
  75. package/docs/upgrading.md +32 -7
  76. package/docs/zod-table.md +48 -45
  77. package/package.json +33 -22
  78. package/scripts/convex-config-bridge.mjs +7 -18
  79. package/scripts/smoke-packed-components.mjs +20 -61
  80. package/scripts/test-packed-ids.mjs +16 -24
  81. package/scripts/test-packed-oxlint.mjs +121 -0
  82. package/src/test.ts +8 -17
@@ -1,4 +1,3 @@
1
- import { ApprovalRunState } from "./constants.mjs";
2
1
  declare namespace decisions_d_exports {
3
2
  export { decide, enter, expire, list, listForWorkflow };
4
3
  }
@@ -15,7 +14,7 @@ declare const decide: import("convex/server").RegisteredMutation<"public", {
15
14
  reason?: string | undefined;
16
15
  runId: import("convex/values").GenericId<"approvalRuns">;
17
16
  }, Promise<{
18
- state: 'pending' | 'approved' | 'rejected' | 'expired';
17
+ state: "approved" | "canceled" | "expired" | "pending" | "rejected";
19
18
  }>>;
20
19
  declare const list: import("convex/server").RegisteredQuery<"public", {
21
20
  runId: import("convex/values").GenericId<"approvalRuns">;
@@ -23,7 +22,7 @@ declare const list: import("convex/server").RegisteredQuery<"public", {
23
22
  stepKey: string;
24
23
  actorRef: string;
25
24
  decision: "approved" | "rejected";
26
- reason?: string | undefined;
25
+ reason: string | undefined;
27
26
  decidedAt: number;
28
27
  }[]>>;
29
28
  declare const listForWorkflow: import("convex/server").RegisteredQuery<"internal", {
@@ -33,7 +32,7 @@ declare const listForWorkflow: import("convex/server").RegisteredQuery<"internal
33
32
  stepKey: string;
34
33
  actorRef: string;
35
34
  decision: "approved" | "rejected";
36
- reason?: string | undefined;
35
+ reason: string | undefined;
37
36
  decidedAt: number;
38
37
  }[]>>;
39
38
  declare const enter: import("convex/server").RegisteredMutation<"internal", {
@@ -42,11 +41,11 @@ declare const enter: import("convex/server").RegisteredMutation<"internal", {
42
41
  stepKey: string;
43
42
  workflowId: string;
44
43
  }, Promise<{
45
- state: ApprovalRunState;
44
+ state: "approved" | "canceled" | "expired" | "rejected";
46
45
  expiresAt: number | undefined;
47
46
  } | {
48
47
  state: 'pending';
49
- expiresAt?: number | undefined;
48
+ expiresAt: number | undefined;
50
49
  }>>;
51
50
  declare const expire: import("convex/server").RegisteredMutation<"internal", {
52
51
  runId: import("convex/values").GenericId<"approvalRuns">;
@@ -54,7 +53,7 @@ declare const expire: import("convex/server").RegisteredMutation<"internal", {
54
53
  }, Promise<{
55
54
  state: 'missing';
56
55
  } | {
57
- state: ApprovalRunState;
56
+ state: "approved" | "canceled" | "expired" | "pending" | "rejected";
58
57
  }>>;
59
58
  //#endregion
60
59
  export { decide, decisions_d_exports, enter, expire, list, listForWorkflow };
@@ -1,2 +1,2 @@
1
- import{components as e,internal as t}from"./_generated/api.mjs";import{internalMutation as n,internalQuery as r,mutation as i,query as a}from"./_generated/server.mjs";import{APPROVAL_ACTOR_DECISION_INDEX as o,APPROVAL_DECISIONS as s,APPROVAL_DECISION_HISTORY_INDEX as c,APPROVAL_RUN_STATES as l}from"./constants.mjs";import{logApprovalTransition as u}from"./audit.mjs";import{approvalActor as d,approvalDecision as f,approvalReason as p}from"./validators.mjs";import{approvalDecisionEventName as m,evaluateDecisionOutcome as h}from"./workflow_steps.mjs";import{canTransitionApprovalRun as g,classifyPendingRunAt as _}from"./functions.mjs";import{zodToConvex as v}from"convex-helpers/server/zod4";import{v as y}from"convex/values";import{WorkflowManager as b}from"@convex-dev/workflow";const x=i({args:{runId:y.id(`approvalRuns`),decision:v(f),reason:y.optional(v(p)),actor:v(d),compatibilityKey:y.string()},returns:y.object({state:y.union(y.literal(l[0]),y.literal(l[1]),y.literal(l[2]),y.literal(l[3]),y.literal(l[4]))}),handler:async(e,t)=>{let n=await e.db.get(t.runId);if(!n)throw Error(`Approval request not found`);k(n.workflow.compatibilityKey,t.compatibilityKey);let r=n.workflow.steps.find(e=>e.kind===`decision`&&e.key===n.currentStepKey);if(!r||r.kind!==`decision`)throw Error(`Approval request is not waiting for a decision`);let i=await e.db.query(`approvalDecisions`).withIndex(o,e=>e.eq(`runId`,n._id).eq(`stepKey`,r.key).eq(`actor.actorRef`,t.actor.actorRef)).unique();if(i){if(i.decision!==t.decision)throw Error(`Actor already submitted a contradictory decision`);return{state:n.state}}if(n.state!==`pending`)throw Error(`Approval request is already ${n.state}`);if(n.expiresAt!==void 0&&_(Date.now(),n.expiresAt)===`expired`)return D(e,n,r.key);if(!r.decisions.includes(t.decision))throw Error(`Decision is not allowed by this workflow step`);if(r.makerChecker&&t.actor.actorRef===n.requester.actorRef)throw Error(`Requester cannot decide this approval step`);let a=Date.now();await e.db.insert(`approvalDecisions`,{runId:n._id,stepKey:r.key,actor:t.actor,decision:t.decision,...t.reason===void 0?{}:{reason:t.reason},decidedAt:a});let s=await e.db.query(`approvalDecisions`).withIndex(`by_runId_and_stepKey_and_decidedAt`,e=>e.eq(`runId`,n._id).eq(`stepKey`,r.key)).collect(),c=h(s.map(e=>({actorRef:e.actor.actorRef,decision:e.decision})),r.quorum.approvals);if(await u(e,{action:`approval.decision.${t.decision}`,actorRef:t.actor.actorRef,runId:n._id,resourceType:n.resourceType,resourceRef:n.resourceRef,transition:`${r.key}:${t.actor.actorRef}:${t.decision}`}),!c)return{state:`pending`};let l=M(n,r.key,c);return await O(e,n,r.key,c,l,{actorRef:t.actor.actorRef,decision:t.decision,...t.reason===void 0?{}:{reason:t.reason},decidedAt:a}),l?{state:c}:{state:`pending`}}}),S=a({args:{runId:y.id(`approvalRuns`)},returns:y.array(y.object({stepKey:y.string(),actorRef:y.string(),decision:v(f),reason:y.optional(v(p)),decidedAt:y.number()})),handler:(e,t)=>A(e,t.runId)}),C=r({args:{runId:y.id(`approvalRuns`),stepKey:y.string()},returns:y.array(y.object({stepKey:y.string(),actorRef:y.string(),decision:v(f),reason:y.optional(v(p)),decidedAt:y.number()})),handler:(e,t)=>j(e,t.runId,t.stepKey)}),w=n({args:{runId:y.id(`approvalRuns`),stepKey:y.string(),workflowId:y.string(),expiresAfterMs:y.optional(y.number())},returns:y.object({state:y.union(y.literal(l[0]),y.literal(l[1]),y.literal(l[2]),y.literal(l[3]),y.literal(l[4])),expiresAt:y.optional(y.number())}),handler:async(e,n)=>{let r=await e.db.get(n.runId);if(!r)throw Error(`Approval request not found`);if(r.workflowId!==n.workflowId)throw Error(`Approval workflow linkage is incomplete`);if(r.state!==`pending`)return{state:r.state,expiresAt:r.expiresAt};let i=Number(r.metadata?.approvalExpiresAt),a=Number.isFinite(i)?i:n.expiresAfterMs===void 0?void 0:Date.now()+n.expiresAfterMs;return await e.db.patch(r._id,{currentStepKey:n.stepKey,expiresAt:a,updatedAt:Date.now()}),await u(e,{action:`approval.waiting`,runId:r._id,resourceType:r.resourceType,resourceRef:r.resourceRef,transition:`${n.stepKey}:waiting`}),a!==void 0&&await e.scheduler.runAt(a,t.decisions.expire,{runId:r._id,stepKey:n.stepKey}),{state:`pending`,...a===void 0?{}:{expiresAt:a}}}}),T=n({args:{runId:y.id(`approvalRuns`),stepKey:y.string()},returns:y.object({state:y.union(y.literal(`missing`),y.literal(l[0]),y.literal(l[1]),y.literal(l[2]),y.literal(l[3]),y.literal(l[4]))}),handler:async(e,t)=>{let n=await e.db.get(t.runId);return n?n.state!==`pending`||n.currentStepKey!==t.stepKey||n.expiresAt===void 0||_(Date.now(),n.expiresAt)===`pending`?{state:n.state}:D(e,n,t.stepKey):{state:`missing`}}}),E=new b(e.workflow);async function D(e,t,n){if(!g(t.state,`expired`))throw Error(`Approval request cannot transition from ${t.state}`);let r=Date.now();if(await e.db.patch(t._id,{state:`expired`,expiresAt:void 0,updatedAt:r,terminalAt:r}),await u(e,{action:`approval.expired`,runId:t._id,resourceType:t.resourceType,resourceRef:t.resourceRef,transition:`${n}:expired`}),!t.workflowId)throw Error(`Approval workflow is not linked`);return await E.sendEvent(e,{workflowId:t.workflowId,name:m(t._id,n),validator:y.object({outcome:y.literal(l[3])}),value:{outcome:`expired`}}),{state:`expired`}}async function O(e,t,n,r,i,a){if(i){if(!g(t.state,r))throw Error(`Approval request cannot transition from ${t.state}`);let i=Date.now();await e.db.patch(t._id,{state:r,expiresAt:void 0,updatedAt:i,terminalAt:i}),await u(e,{action:`approval.${r}`,runId:t._id,resourceType:t.resourceType,resourceRef:t.resourceRef,transition:`${n}:terminal:${r}`})}if(!t.workflowId)throw Error(`Approval workflow is not linked`);await E.sendEvent(e,{workflowId:t.workflowId,name:m(t._id,n),validator:y.object({outcome:y.union(y.literal(s[0]),y.literal(s[1])),terminalEvidence:y.object({actorRef:y.string(),decision:v(f),reason:y.optional(v(p)),decidedAt:y.number()})}),value:{outcome:r,terminalEvidence:a}})}function k(e,t){if(e!==t)throw Error(`Approval workflow compatibility mismatch: expected ${t}, received ${e}`)}async function A(e,t){return(await e.db.query(`approvalDecisions`).withIndex(c,e=>e.eq(`runId`,t)).collect()).map(e=>({stepKey:e.stepKey,actorRef:e.actor.actorRef,decision:e.decision,...e.reason===void 0?{}:{reason:e.reason},decidedAt:e.decidedAt}))}async function j(e,t,n){return(await e.db.query(`approvalDecisions`).withIndex(`by_runId_and_stepKey_and_decidedAt`,e=>e.eq(`runId`,t).eq(`stepKey`,n)).collect()).map(e=>({stepKey:e.stepKey,actorRef:e.actor.actorRef,decision:e.decision,...e.reason===void 0?{}:{reason:e.reason},decidedAt:e.decidedAt}))}function M(e,t,n){if(n===`rejected`)return!0;let r=e.workflow.steps.findIndex(e=>e.kind===`decision`&&e.key===t);return!e.workflow.steps.slice(r+1).some(e=>e.kind===`decision`)}export{x as decide,w as enter,T as expire,S as list,C as listForWorkflow};
1
+ import{components as e,internal as t}from"./_generated/api.mjs";import{internalMutation as n,internalQuery as r,mutation as i,query as a}from"./_generated/server.mjs";import{APPROVAL_ACTOR_DECISION_INDEX as o,APPROVAL_DECISIONS as s,APPROVAL_DECISION_HISTORY_INDEX as c,APPROVAL_RUN_STATES as l}from"./constants.mjs";import{logApprovalTransition as u}from"./audit.mjs";import{approvalActor as d,approvalDecision as f,approvalReason as p}from"./validators.mjs";import{approvalDecisionEventName as m,evaluateDecisionOutcome as h}from"./workflow_steps.mjs";import{canTransitionApprovalRun as g,classifyPendingRunAt as _}from"./functions.mjs";import{zodToConvex as v}from"convex-helpers/server/zod4";import{v as y}from"convex/values";import{WorkflowManager as b}from"@convex-dev/workflow";const x=i({args:{runId:y.id(`approvalRuns`),decision:v(f),reason:y.optional(v(p)),actor:v(d),compatibilityKey:y.string()},returns:y.object({state:y.union(y.literal(l[0]),y.literal(l[1]),y.literal(l[2]),y.literal(l[3]),y.literal(l[4]))}),handler:async(e,t)=>{let n=await e.db.get(t.runId);if(!n)throw Error(`Approval request not found`);k(n.workflow.compatibilityKey,t.compatibilityKey);let r=n.workflow.steps.find(e=>e.kind===`decision`&&e.key===n.currentStepKey);if(!r||r.kind!==`decision`)throw Error(`Approval request is not waiting for a decision`);let i=await e.db.query(`approvalDecisions`).withIndex(o,e=>e.eq(`runId`,n._id).eq(`stepKey`,r.key).eq(`actor.actorRef`,t.actor.actorRef)).unique();if(i){if(i.decision!==t.decision)throw Error(`Actor already submitted a contradictory decision`);return{state:n.state}}if(n.state!==`pending`)throw Error(`Approval request is already ${n.state}`);if(n.expiresAt!==void 0&&_(Date.now(),n.expiresAt)===`expired`)return D(e,n,r.key);if(!r.decisions.includes(t.decision))throw Error(`Decision is not allowed by this workflow step`);if(r.makerChecker&&t.actor.actorRef===n.requester.actorRef)throw Error(`Requester cannot decide this approval step`);let a=Date.now();await e.db.insert(`approvalDecisions`,{runId:n._id,stepKey:r.key,actor:t.actor,decision:t.decision,reason:t.reason,decidedAt:a});let s=await e.db.query(`approvalDecisions`).withIndex(`by_runId_and_stepKey_and_decidedAt`,e=>e.eq(`runId`,n._id).eq(`stepKey`,r.key)).collect(),c=h(s.map(e=>({actorRef:e.actor.actorRef,decision:e.decision})),r.quorum.approvals);if(await u(e,{action:`approval.decision.${t.decision}`,actorRef:t.actor.actorRef,runId:n._id,resourceType:n.resourceType,resourceRef:n.resourceRef,transition:`${r.key}:${t.actor.actorRef}:${t.decision}`}),!c)return{state:`pending`};let l=M(n,r.key,c);return await O(e,n,r.key,c,l,{actorRef:t.actor.actorRef,decision:t.decision,reason:t.reason,decidedAt:a}),l?{state:c}:{state:`pending`}}}),S=a({args:{runId:y.id(`approvalRuns`)},returns:y.array(y.object({stepKey:y.string(),actorRef:y.string(),decision:v(f),reason:y.optional(v(p)),decidedAt:y.number()})),handler:(e,t)=>A(e,t.runId)}),C=r({args:{runId:y.id(`approvalRuns`),stepKey:y.string()},returns:y.array(y.object({stepKey:y.string(),actorRef:y.string(),decision:v(f),reason:y.optional(v(p)),decidedAt:y.number()})),handler:(e,t)=>j(e,t.runId,t.stepKey)}),w=n({args:{runId:y.id(`approvalRuns`),stepKey:y.string(),workflowId:y.string(),expiresAfterMs:y.optional(y.number())},returns:y.object({state:y.union(y.literal(l[0]),y.literal(l[1]),y.literal(l[2]),y.literal(l[3]),y.literal(l[4])),expiresAt:y.optional(y.number())}),handler:async(e,n)=>{let r=await e.db.get(n.runId);if(!r)throw Error(`Approval request not found`);if(r.workflowId!==n.workflowId)throw Error(`Approval workflow linkage is incomplete`);if(r.state!==`pending`)return{state:r.state,expiresAt:r.expiresAt};let i=Number(r.metadata?.approvalExpiresAt),a=Number.isFinite(i)?i:n.expiresAfterMs===void 0?void 0:Date.now()+n.expiresAfterMs;return await e.db.patch(r._id,{currentStepKey:n.stepKey,expiresAt:a,updatedAt:Date.now()}),await u(e,{action:`approval.waiting`,runId:r._id,resourceType:r.resourceType,resourceRef:r.resourceRef,transition:`${n.stepKey}:waiting`}),a!==void 0&&await e.scheduler.runAt(a,t.decisions.expire,{runId:r._id,stepKey:n.stepKey}),{state:`pending`,expiresAt:a}}}),T=n({args:{runId:y.id(`approvalRuns`),stepKey:y.string()},returns:y.object({state:y.union(y.literal(`missing`),y.literal(l[0]),y.literal(l[1]),y.literal(l[2]),y.literal(l[3]),y.literal(l[4]))}),handler:async(e,t)=>{let n=await e.db.get(t.runId);return n?n.state!==`pending`||n.currentStepKey!==t.stepKey||n.expiresAt===void 0||_(Date.now(),n.expiresAt)===`pending`?{state:n.state}:D(e,n,t.stepKey):{state:`missing`}}}),E=new b(e.workflow);async function D(e,t,n){if(!g(t.state,`expired`))throw Error(`Approval request cannot transition from ${t.state}`);let r=Date.now();if(await e.db.patch(t._id,{state:`expired`,expiresAt:void 0,updatedAt:r,terminalAt:r}),await u(e,{action:`approval.expired`,runId:t._id,resourceType:t.resourceType,resourceRef:t.resourceRef,transition:`${n}:expired`}),!t.workflowId)throw Error(`Approval workflow is not linked`);return await E.sendEvent(e,{workflowId:t.workflowId,name:m(t._id,n),validator:y.object({outcome:y.literal(l[3])}),value:{outcome:`expired`}}),{state:`expired`}}async function O(e,t,n,r,i,a){if(i){if(!g(t.state,r))throw Error(`Approval request cannot transition from ${t.state}`);let i=Date.now();await e.db.patch(t._id,{state:r,expiresAt:void 0,updatedAt:i,terminalAt:i}),await u(e,{action:`approval.${r}`,runId:t._id,resourceType:t.resourceType,resourceRef:t.resourceRef,transition:`${n}:terminal:${r}`})}if(!t.workflowId)throw Error(`Approval workflow is not linked`);await E.sendEvent(e,{workflowId:t.workflowId,name:m(t._id,n),validator:y.object({outcome:y.union(y.literal(s[0]),y.literal(s[1])),terminalEvidence:y.object({actorRef:y.string(),decision:v(f),reason:y.optional(v(p)),decidedAt:y.number()})}),value:{outcome:r,terminalEvidence:a}})}function k(e,t){if(e!==t)throw Error(`Approval workflow compatibility mismatch: expected ${t}, received ${e}`)}async function A(e,t){return(await e.db.query(`approvalDecisions`).withIndex(c,e=>e.eq(`runId`,t)).collect()).map(e=>({stepKey:e.stepKey,actorRef:e.actor.actorRef,decision:e.decision,reason:e.reason,decidedAt:e.decidedAt}))}async function j(e,t,n){return(await e.db.query(`approvalDecisions`).withIndex(`by_runId_and_stepKey_and_decidedAt`,e=>e.eq(`runId`,t).eq(`stepKey`,n)).collect()).map(e=>({stepKey:e.stepKey,actorRef:e.actor.actorRef,decision:e.decision,reason:e.reason,decidedAt:e.decidedAt}))}function M(e,t,n){if(n===`rejected`)return!0;let r=e.workflow.steps.findIndex(e=>e.kind===`decision`&&e.key===t);return!e.workflow.steps.slice(r+1).some(e=>e.kind===`decision`)}export{x as decide,w as enter,T as expire,S as list,C as listForWorkflow};
2
2
  //# sourceMappingURL=decisions.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"decisions.mjs","names":[],"sources":["../../../src/components/approvals/decisions.ts"],"sourcesContent":["import { WorkflowManager, type WorkflowId } from '@convex-dev/workflow'\nimport { zodToConvex } from 'convex-helpers/server/zod4'\nimport { v } from 'convex/values'\n\nimport { components, internal } from './_generated/api'\nimport {\n\tinternalMutation,\n\tinternalQuery,\n\tmutation,\n\tquery,\n\ttype QueryCtx,\n} from './_generated/server'\nimport { logApprovalTransition } from './audit'\nimport {\n\tAPPROVAL_ACTOR_DECISION_INDEX,\n\tAPPROVAL_DECISION_HISTORY_INDEX,\n\tAPPROVAL_DECISIONS,\n\tAPPROVAL_RUN_STATES,\n\ttype ApprovalRunState,\n} from './constants'\nimport { canTransitionApprovalRun, classifyPendingRunAt } from './functions'\nimport { approvalActor, approvalDecision, approvalReason } from './validators'\nimport {\n\tapprovalDecisionEventName,\n\tevaluateDecisionOutcome,\n} from './workflow_steps'\n\nexport const decide = mutation({\n\targs: {\n\t\trunId: v.id('approvalRuns'),\n\t\tdecision: zodToConvex(approvalDecision),\n\t\treason: v.optional(zodToConvex(approvalReason)),\n\t\tactor: zodToConvex(approvalActor),\n\t\tcompatibilityKey: v.string(),\n\t},\n\treturns: v.object({\n\t\tstate: v.union(\n\t\t\tv.literal(APPROVAL_RUN_STATES[0]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[1]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[2]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[3]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[4]),\n\t\t),\n\t}),\n\thandler: async (ctx, input) => {\n\t\tconst run = await ctx.db.get(input.runId)\n\t\tif (!run) throw new Error('Approval request not found')\n\t\tassertCompatible(run.workflow.compatibilityKey, input.compatibilityKey)\n\t\tconst step = run.workflow.steps.find(\n\t\t\t(candidate) =>\n\t\t\t\tcandidate.kind === 'decision' && candidate.key === run.currentStepKey,\n\t\t)\n\t\tif (!step || step.kind !== 'decision')\n\t\t\tthrow new Error('Approval request is not waiting for a decision')\n\n\t\tconst prior = await ctx.db\n\t\t\t.query('approvalDecisions')\n\t\t\t.withIndex(APPROVAL_ACTOR_DECISION_INDEX, (query) =>\n\t\t\t\tquery\n\t\t\t\t\t.eq('runId', run._id)\n\t\t\t\t\t.eq('stepKey', step.key)\n\t\t\t\t\t.eq('actor.actorRef', input.actor.actorRef),\n\t\t\t)\n\t\t\t.unique()\n\t\tif (prior) {\n\t\t\tif (prior.decision !== input.decision)\n\t\t\t\tthrow new Error('Actor already submitted a contradictory decision')\n\t\t\treturn {\n\t\t\t\tstate: run.state as 'pending' | 'approved' | 'rejected' | 'expired',\n\t\t\t}\n\t\t}\n\t\tif (run.state !== 'pending')\n\t\t\tthrow new Error(`Approval request is already ${run.state}`)\n\t\tif (\n\t\t\trun.expiresAt !== undefined &&\n\t\t\tclassifyPendingRunAt(Date.now(), run.expiresAt) === 'expired'\n\t\t)\n\t\t\treturn claimExpiry(ctx, run, step.key)\n\t\tif (!step.decisions.includes(input.decision))\n\t\t\tthrow new Error('Decision is not allowed by this workflow step')\n\t\tif (step.makerChecker && input.actor.actorRef === run.requester.actorRef)\n\t\t\tthrow new Error('Requester cannot decide this approval step')\n\n\t\tconst decidedAt = Date.now()\n\t\tawait ctx.db.insert('approvalDecisions', {\n\t\t\trunId: run._id,\n\t\t\tstepKey: step.key,\n\t\t\tactor: input.actor,\n\t\t\tdecision: input.decision,\n\t\t\t...(input.reason === undefined ? {} : { reason: input.reason }),\n\t\t\tdecidedAt,\n\t\t})\n\t\tconst evidence = await ctx.db\n\t\t\t.query('approvalDecisions')\n\t\t\t.withIndex('by_runId_and_stepKey_and_decidedAt', (query) =>\n\t\t\t\tquery.eq('runId', run._id).eq('stepKey', step.key),\n\t\t\t)\n\t\t\t.collect()\n\t\tconst outcome = evaluateDecisionOutcome(\n\t\t\tevidence.map((item) => ({\n\t\t\t\tactorRef: item.actor.actorRef,\n\t\t\t\tdecision: item.decision,\n\t\t\t})),\n\t\t\tstep.quorum.approvals,\n\t\t)\n\t\tawait logApprovalTransition(ctx, {\n\t\t\taction: `approval.decision.${input.decision}`,\n\t\t\tactorRef: input.actor.actorRef,\n\t\t\trunId: run._id,\n\t\t\tresourceType: run.resourceType,\n\t\t\tresourceRef: run.resourceRef,\n\t\t\ttransition: `${step.key}:${input.actor.actorRef}:${input.decision}`,\n\t\t})\n\t\tif (!outcome) return { state: 'pending' as const }\n\t\tconst terminal = isTerminalDecision(run, step.key, outcome)\n\t\tawait finishDecision(ctx, run, step.key, outcome, terminal, {\n\t\t\tactorRef: input.actor.actorRef,\n\t\t\tdecision: input.decision,\n\t\t\t...(input.reason === undefined ? {} : { reason: input.reason }),\n\t\t\tdecidedAt,\n\t\t})\n\t\treturn terminal ? { state: outcome } : { state: 'pending' as const }\n\t},\n})\n\nexport const list = query({\n\targs: { runId: v.id('approvalRuns') },\n\treturns: v.array(\n\t\tv.object({\n\t\t\tstepKey: v.string(),\n\t\t\tactorRef: v.string(),\n\t\t\tdecision: zodToConvex(approvalDecision),\n\t\t\treason: v.optional(zodToConvex(approvalReason)),\n\t\t\tdecidedAt: v.number(),\n\t\t}),\n\t),\n\thandler: (ctx, input) => listDecisionEvidence(ctx, input.runId),\n})\n\nexport const listForWorkflow = internalQuery({\n\targs: { runId: v.id('approvalRuns'), stepKey: v.string() },\n\treturns: v.array(\n\t\tv.object({\n\t\t\tstepKey: v.string(),\n\t\t\tactorRef: v.string(),\n\t\t\tdecision: zodToConvex(approvalDecision),\n\t\t\treason: v.optional(zodToConvex(approvalReason)),\n\t\t\tdecidedAt: v.number(),\n\t\t}),\n\t),\n\thandler: (ctx, input) =>\n\t\tlistDecisionEvidenceForStep(ctx, input.runId, input.stepKey),\n})\n\nexport const enter = internalMutation({\n\targs: {\n\t\trunId: v.id('approvalRuns'),\n\t\tstepKey: v.string(),\n\t\tworkflowId: v.string(),\n\t\texpiresAfterMs: v.optional(v.number()),\n\t},\n\treturns: v.object({\n\t\tstate: v.union(\n\t\t\tv.literal(APPROVAL_RUN_STATES[0]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[1]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[2]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[3]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[4]),\n\t\t),\n\t\texpiresAt: v.optional(v.number()),\n\t}),\n\thandler: async (ctx, input) => {\n\t\tconst run = await ctx.db.get(input.runId)\n\t\tif (!run) throw new Error('Approval request not found')\n\t\tif (run.workflowId !== input.workflowId)\n\t\t\tthrow new Error('Approval workflow linkage is incomplete')\n\t\tif (run.state !== 'pending')\n\t\t\treturn {\n\t\t\t\tstate: run.state as ApprovalRunState,\n\t\t\t\texpiresAt: run.expiresAt,\n\t\t\t}\n\t\tconst configuredExpiresAt = Number(run.metadata?.approvalExpiresAt)\n\t\tconst expiresAt = Number.isFinite(configuredExpiresAt)\n\t\t\t? configuredExpiresAt\n\t\t\t: input.expiresAfterMs === undefined\n\t\t\t\t? undefined\n\t\t\t\t: Date.now() + input.expiresAfterMs\n\t\tawait ctx.db.patch(run._id, {\n\t\t\tcurrentStepKey: input.stepKey,\n\t\t\texpiresAt,\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t\tawait logApprovalTransition(ctx, {\n\t\t\taction: 'approval.waiting',\n\t\t\trunId: run._id,\n\t\t\tresourceType: run.resourceType,\n\t\t\tresourceRef: run.resourceRef,\n\t\t\ttransition: `${input.stepKey}:waiting`,\n\t\t})\n\t\tif (expiresAt !== undefined)\n\t\t\tawait ctx.scheduler.runAt(expiresAt, internal.decisions.expire, {\n\t\t\t\trunId: run._id,\n\t\t\t\tstepKey: input.stepKey,\n\t\t\t})\n\t\treturn {\n\t\t\tstate: 'pending' as const,\n\t\t\t...(expiresAt === undefined ? {} : { expiresAt }),\n\t\t}\n\t},\n})\n\nexport const expire = internalMutation({\n\targs: { runId: v.id('approvalRuns'), stepKey: v.string() },\n\treturns: v.object({\n\t\tstate: v.union(\n\t\t\tv.literal('missing'),\n\t\t\tv.literal(APPROVAL_RUN_STATES[0]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[1]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[2]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[3]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[4]),\n\t\t),\n\t}),\n\thandler: async (ctx, input) => {\n\t\tconst run = await ctx.db.get(input.runId)\n\t\tif (!run) return { state: 'missing' as const }\n\t\tif (\n\t\t\trun.state !== 'pending' ||\n\t\t\trun.currentStepKey !== input.stepKey ||\n\t\t\trun.expiresAt === undefined ||\n\t\t\tclassifyPendingRunAt(Date.now(), run.expiresAt) === 'pending'\n\t\t)\n\t\t\treturn { state: run.state as ApprovalRunState }\n\t\treturn claimExpiry(ctx, run, input.stepKey)\n\t},\n})\n\nconst approvalWorkflow = new WorkflowManager(components.workflow)\n\nasync function claimExpiry(\n\tctx: import('./_generated/server').MutationCtx,\n\trun: import('./_generated/dataModel').Doc<'approvalRuns'>,\n\tstepKey: string,\n): Promise<{ state: 'expired' }> {\n\tif (!canTransitionApprovalRun(run.state, 'expired'))\n\t\tthrow new Error(`Approval request cannot transition from ${run.state}`)\n\tconst now = Date.now()\n\tawait ctx.db.patch(run._id, {\n\t\tstate: 'expired',\n\t\texpiresAt: undefined,\n\t\tupdatedAt: now,\n\t\tterminalAt: now,\n\t})\n\tawait logApprovalTransition(ctx, {\n\t\taction: 'approval.expired',\n\t\trunId: run._id,\n\t\tresourceType: run.resourceType,\n\t\tresourceRef: run.resourceRef,\n\t\ttransition: `${stepKey}:expired`,\n\t})\n\tif (!run.workflowId) throw new Error('Approval workflow is not linked')\n\tawait approvalWorkflow.sendEvent(ctx, {\n\t\tworkflowId: run.workflowId as WorkflowId,\n\t\tname: approvalDecisionEventName(run._id, stepKey),\n\t\tvalidator: v.object({ outcome: v.literal(APPROVAL_RUN_STATES[3]) }),\n\t\tvalue: { outcome: 'expired' },\n\t})\n\treturn { state: 'expired' }\n}\n\nasync function finishDecision(\n\tctx: import('./_generated/server').MutationCtx,\n\trun: import('./_generated/dataModel').Doc<'approvalRuns'>,\n\tstepKey: string,\n\toutcome: 'approved' | 'rejected',\n\tterminal: boolean,\n\tterminalEvidence: {\n\t\tactorRef: string\n\t\tdecision: 'approved' | 'rejected'\n\t\treason?: string\n\t\tdecidedAt: number\n\t},\n): Promise<void> {\n\tif (terminal) {\n\t\tif (!canTransitionApprovalRun(run.state, outcome))\n\t\t\tthrow new Error(`Approval request cannot transition from ${run.state}`)\n\t\tconst now = Date.now()\n\t\tawait ctx.db.patch(run._id, {\n\t\t\tstate: outcome,\n\t\t\texpiresAt: undefined,\n\t\t\tupdatedAt: now,\n\t\t\tterminalAt: now,\n\t\t})\n\t\tawait logApprovalTransition(ctx, {\n\t\t\taction: `approval.${outcome}`,\n\t\t\trunId: run._id,\n\t\t\tresourceType: run.resourceType,\n\t\t\tresourceRef: run.resourceRef,\n\t\t\ttransition: `${stepKey}:terminal:${outcome}`,\n\t\t})\n\t}\n\tif (!run.workflowId) throw new Error('Approval workflow is not linked')\n\tawait approvalWorkflow.sendEvent(ctx, {\n\t\tworkflowId: run.workflowId as WorkflowId,\n\t\tname: approvalDecisionEventName(run._id, stepKey),\n\t\tvalidator: v.object({\n\t\t\toutcome: v.union(\n\t\t\t\tv.literal(APPROVAL_DECISIONS[0]),\n\t\t\t\tv.literal(APPROVAL_DECISIONS[1]),\n\t\t\t),\n\t\t\tterminalEvidence: v.object({\n\t\t\t\tactorRef: v.string(),\n\t\t\t\tdecision: zodToConvex(approvalDecision),\n\t\t\t\treason: v.optional(zodToConvex(approvalReason)),\n\t\t\t\tdecidedAt: v.number(),\n\t\t\t}),\n\t\t}),\n\t\tvalue: { outcome, terminalEvidence },\n\t})\n}\n\nfunction assertCompatible(actual: string, expected: string): void {\n\tif (actual !== expected)\n\t\tthrow new Error(\n\t\t\t`Approval workflow compatibility mismatch: expected ${expected}, received ${actual}`,\n\t\t)\n}\n\nasync function listDecisionEvidence(\n\tctx: QueryCtx,\n\trunId: import('./_generated/dataModel').Id<'approvalRuns'>,\n) {\n\tconst decisions = await ctx.db\n\t\t.query('approvalDecisions')\n\t\t.withIndex(APPROVAL_DECISION_HISTORY_INDEX, (query) =>\n\t\t\tquery.eq('runId', runId),\n\t\t)\n\t\t.collect()\n\treturn decisions.map((decision) => ({\n\t\tstepKey: decision.stepKey,\n\t\tactorRef: decision.actor.actorRef,\n\t\tdecision: decision.decision,\n\t\t...(decision.reason === undefined ? {} : { reason: decision.reason }),\n\t\tdecidedAt: decision.decidedAt,\n\t}))\n}\n\nasync function listDecisionEvidenceForStep(\n\tctx: QueryCtx,\n\trunId: import('./_generated/dataModel').Id<'approvalRuns'>,\n\tstepKey: string,\n) {\n\tconst decisions = await ctx.db\n\t\t.query('approvalDecisions')\n\t\t.withIndex('by_runId_and_stepKey_and_decidedAt', (query) =>\n\t\t\tquery.eq('runId', runId).eq('stepKey', stepKey),\n\t\t)\n\t\t.collect()\n\treturn decisions.map((decision) => ({\n\t\tstepKey: decision.stepKey,\n\t\tactorRef: decision.actor.actorRef,\n\t\tdecision: decision.decision,\n\t\t...(decision.reason === undefined ? {} : { reason: decision.reason }),\n\t\tdecidedAt: decision.decidedAt,\n\t}))\n}\n\nfunction isTerminalDecision(\n\trun: import('./_generated/dataModel').Doc<'approvalRuns'>,\n\tstepKey: string,\n\toutcome: 'approved' | 'rejected',\n): boolean {\n\tif (outcome === 'rejected') return true\n\tconst currentIndex = run.workflow.steps.findIndex(\n\t\t(step) => step.kind === 'decision' && step.key === stepKey,\n\t)\n\treturn !run.workflow.steps\n\t\t.slice(currentIndex + 1)\n\t\t.some((step) => step.kind === 'decision')\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"decisions.mjs","names":[],"sources":["../../../src/components/approvals/decisions.ts"],"sourcesContent":["import { WorkflowManager, type WorkflowId } from '@convex-dev/workflow'\nimport { zodToConvex } from 'convex-helpers/server/zod4'\nimport { v } from 'convex/values'\n\nimport { components, internal } from './_generated/api'\nimport {\n\tinternalMutation,\n\tinternalQuery,\n\tmutation,\n\tquery,\n\ttype QueryCtx,\n} from './_generated/server'\nimport { logApprovalTransition } from './audit'\nimport {\n\tAPPROVAL_ACTOR_DECISION_INDEX,\n\tAPPROVAL_DECISION_HISTORY_INDEX,\n\tAPPROVAL_DECISIONS,\n\tAPPROVAL_RUN_STATES,\n} from './constants'\nimport { canTransitionApprovalRun, classifyPendingRunAt } from './functions'\nimport { approvalActor, approvalDecision, approvalReason } from './validators'\nimport { approvalDecisionEventName, evaluateDecisionOutcome } from './workflow_steps'\n\nexport const decide = mutation({\n\targs: {\n\t\trunId: v.id('approvalRuns'),\n\t\tdecision: zodToConvex(approvalDecision),\n\t\treason: v.optional(zodToConvex(approvalReason)),\n\t\tactor: zodToConvex(approvalActor),\n\t\tcompatibilityKey: v.string(),\n\t},\n\treturns: v.object({\n\t\tstate: v.union(\n\t\t\tv.literal(APPROVAL_RUN_STATES[0]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[1]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[2]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[3]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[4]),\n\t\t),\n\t}),\n\thandler: async (ctx, input) => {\n\t\tconst run = await ctx.db.get(input.runId)\n\t\tif (!run) throw new Error('Approval request not found')\n\t\tassertCompatible(run.workflow.compatibilityKey, input.compatibilityKey)\n\t\tconst step = run.workflow.steps.find(\n\t\t\t(candidate) => candidate.kind === 'decision' && candidate.key === run.currentStepKey,\n\t\t)\n\t\tif (!step || step.kind !== 'decision')\n\t\t\tthrow new Error('Approval request is not waiting for a decision')\n\n\t\tconst prior = await ctx.db\n\t\t\t.query('approvalDecisions')\n\t\t\t.withIndex(APPROVAL_ACTOR_DECISION_INDEX, (query) =>\n\t\t\t\tquery\n\t\t\t\t\t.eq('runId', run._id)\n\t\t\t\t\t.eq('stepKey', step.key)\n\t\t\t\t\t.eq('actor.actorRef', input.actor.actorRef),\n\t\t\t)\n\t\t\t.unique()\n\t\tif (prior) {\n\t\t\tif (prior.decision !== input.decision)\n\t\t\t\tthrow new Error('Actor already submitted a contradictory decision')\n\t\t\treturn {\n\t\t\t\tstate: run.state,\n\t\t\t}\n\t\t}\n\t\tif (run.state !== 'pending') throw new Error(`Approval request is already ${run.state}`)\n\t\tif (\n\t\t\trun.expiresAt !== undefined &&\n\t\t\tclassifyPendingRunAt(Date.now(), run.expiresAt) === 'expired'\n\t\t)\n\t\t\treturn claimExpiry(ctx, run, step.key)\n\t\tif (!step.decisions.includes(input.decision))\n\t\t\tthrow new Error('Decision is not allowed by this workflow step')\n\t\tif (step.makerChecker && input.actor.actorRef === run.requester.actorRef)\n\t\t\tthrow new Error('Requester cannot decide this approval step')\n\n\t\tconst decidedAt = Date.now()\n\t\tawait ctx.db.insert('approvalDecisions', {\n\t\t\trunId: run._id,\n\t\t\tstepKey: step.key,\n\t\t\tactor: input.actor,\n\t\t\tdecision: input.decision,\n\t\t\treason: input.reason,\n\t\t\tdecidedAt,\n\t\t})\n\t\tconst evidence = await ctx.db\n\t\t\t.query('approvalDecisions')\n\t\t\t.withIndex('by_runId_and_stepKey_and_decidedAt', (query) =>\n\t\t\t\tquery.eq('runId', run._id).eq('stepKey', step.key),\n\t\t\t)\n\t\t\t.collect()\n\t\tconst outcome = evaluateDecisionOutcome(\n\t\t\tevidence.map((item) => ({\n\t\t\t\tactorRef: item.actor.actorRef,\n\t\t\t\tdecision: item.decision,\n\t\t\t})),\n\t\t\tstep.quorum.approvals,\n\t\t)\n\t\tawait logApprovalTransition(ctx, {\n\t\t\taction: `approval.decision.${input.decision}`,\n\t\t\tactorRef: input.actor.actorRef,\n\t\t\trunId: run._id,\n\t\t\tresourceType: run.resourceType,\n\t\t\tresourceRef: run.resourceRef,\n\t\t\ttransition: `${step.key}:${input.actor.actorRef}:${input.decision}`,\n\t\t})\n\t\tif (!outcome) return { state: 'pending' as const }\n\t\tconst terminal = isTerminalDecision(run, step.key, outcome)\n\t\tawait finishDecision(ctx, run, step.key, outcome, terminal, {\n\t\t\tactorRef: input.actor.actorRef,\n\t\t\tdecision: input.decision,\n\t\t\treason: input.reason,\n\t\t\tdecidedAt,\n\t\t})\n\t\treturn terminal ? { state: outcome } : { state: 'pending' as const }\n\t},\n})\n\nexport const list = query({\n\targs: { runId: v.id('approvalRuns') },\n\treturns: v.array(\n\t\tv.object({\n\t\t\tstepKey: v.string(),\n\t\t\tactorRef: v.string(),\n\t\t\tdecision: zodToConvex(approvalDecision),\n\t\t\treason: v.optional(zodToConvex(approvalReason)),\n\t\t\tdecidedAt: v.number(),\n\t\t}),\n\t),\n\thandler: (ctx, input) => listDecisionEvidence(ctx, input.runId),\n})\n\nexport const listForWorkflow = internalQuery({\n\targs: { runId: v.id('approvalRuns'), stepKey: v.string() },\n\treturns: v.array(\n\t\tv.object({\n\t\t\tstepKey: v.string(),\n\t\t\tactorRef: v.string(),\n\t\t\tdecision: zodToConvex(approvalDecision),\n\t\t\treason: v.optional(zodToConvex(approvalReason)),\n\t\t\tdecidedAt: v.number(),\n\t\t}),\n\t),\n\thandler: (ctx, input) => listDecisionEvidenceForStep(ctx, input.runId, input.stepKey),\n})\n\nexport const enter = internalMutation({\n\targs: {\n\t\trunId: v.id('approvalRuns'),\n\t\tstepKey: v.string(),\n\t\tworkflowId: v.string(),\n\t\texpiresAfterMs: v.optional(v.number()),\n\t},\n\treturns: v.object({\n\t\tstate: v.union(\n\t\t\tv.literal(APPROVAL_RUN_STATES[0]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[1]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[2]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[3]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[4]),\n\t\t),\n\t\texpiresAt: v.optional(v.number()),\n\t}),\n\thandler: async (ctx, input) => {\n\t\tconst run = await ctx.db.get(input.runId)\n\t\tif (!run) throw new Error('Approval request not found')\n\t\tif (run.workflowId !== input.workflowId)\n\t\t\tthrow new Error('Approval workflow linkage is incomplete')\n\t\tif (run.state !== 'pending')\n\t\t\treturn {\n\t\t\t\tstate: run.state,\n\t\t\t\texpiresAt: run.expiresAt,\n\t\t\t}\n\t\tconst configuredExpiresAt = Number(run.metadata?.approvalExpiresAt)\n\t\tconst expiresAt = Number.isFinite(configuredExpiresAt)\n\t\t\t? configuredExpiresAt\n\t\t\t: input.expiresAfterMs === undefined\n\t\t\t\t? undefined\n\t\t\t\t: Date.now() + input.expiresAfterMs\n\t\tawait ctx.db.patch(run._id, {\n\t\t\tcurrentStepKey: input.stepKey,\n\t\t\texpiresAt,\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t\tawait logApprovalTransition(ctx, {\n\t\t\taction: 'approval.waiting',\n\t\t\trunId: run._id,\n\t\t\tresourceType: run.resourceType,\n\t\t\tresourceRef: run.resourceRef,\n\t\t\ttransition: `${input.stepKey}:waiting`,\n\t\t})\n\t\tif (expiresAt !== undefined)\n\t\t\tawait ctx.scheduler.runAt(expiresAt, internal.decisions.expire, {\n\t\t\t\trunId: run._id,\n\t\t\t\tstepKey: input.stepKey,\n\t\t\t})\n\t\treturn {\n\t\t\tstate: 'pending' as const,\n\t\t\texpiresAt: expiresAt,\n\t\t}\n\t},\n})\n\nexport const expire = internalMutation({\n\targs: { runId: v.id('approvalRuns'), stepKey: v.string() },\n\treturns: v.object({\n\t\tstate: v.union(\n\t\t\tv.literal('missing'),\n\t\t\tv.literal(APPROVAL_RUN_STATES[0]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[1]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[2]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[3]),\n\t\t\tv.literal(APPROVAL_RUN_STATES[4]),\n\t\t),\n\t}),\n\thandler: async (ctx, input) => {\n\t\tconst run = await ctx.db.get(input.runId)\n\t\tif (!run) return { state: 'missing' as const }\n\t\tif (\n\t\t\trun.state !== 'pending' ||\n\t\t\trun.currentStepKey !== input.stepKey ||\n\t\t\trun.expiresAt === undefined ||\n\t\t\tclassifyPendingRunAt(Date.now(), run.expiresAt) === 'pending'\n\t\t)\n\t\t\treturn { state: run.state }\n\t\treturn claimExpiry(ctx, run, input.stepKey)\n\t},\n})\n\nconst approvalWorkflow = new WorkflowManager(components.workflow)\n\nasync function claimExpiry(\n\tctx: import('./_generated/server').MutationCtx,\n\trun: import('./_generated/dataModel').Doc<'approvalRuns'>,\n\tstepKey: string,\n): Promise<{ state: 'expired' }> {\n\tif (!canTransitionApprovalRun(run.state, 'expired'))\n\t\tthrow new Error(`Approval request cannot transition from ${run.state}`)\n\tconst now = Date.now()\n\tawait ctx.db.patch(run._id, {\n\t\tstate: 'expired',\n\t\texpiresAt: undefined,\n\t\tupdatedAt: now,\n\t\tterminalAt: now,\n\t})\n\tawait logApprovalTransition(ctx, {\n\t\taction: 'approval.expired',\n\t\trunId: run._id,\n\t\tresourceType: run.resourceType,\n\t\tresourceRef: run.resourceRef,\n\t\ttransition: `${stepKey}:expired`,\n\t})\n\tif (!run.workflowId) throw new Error('Approval workflow is not linked')\n\tawait approvalWorkflow.sendEvent(ctx, {\n\t\t// SAFETY: workflowId was persisted from approvalWorkflow.start when this run was created.\n\t\tworkflowId: run.workflowId as WorkflowId,\n\t\tname: approvalDecisionEventName(run._id, stepKey),\n\t\tvalidator: v.object({ outcome: v.literal(APPROVAL_RUN_STATES[3]) }),\n\t\tvalue: { outcome: 'expired' },\n\t})\n\treturn { state: 'expired' }\n}\n\nasync function finishDecision(\n\tctx: import('./_generated/server').MutationCtx,\n\trun: import('./_generated/dataModel').Doc<'approvalRuns'>,\n\tstepKey: string,\n\toutcome: 'approved' | 'rejected',\n\tterminal: boolean,\n\tterminalEvidence: {\n\t\tactorRef: string\n\t\tdecision: 'approved' | 'rejected'\n\t\treason?: string\n\t\tdecidedAt: number\n\t},\n): Promise<void> {\n\tif (terminal) {\n\t\tif (!canTransitionApprovalRun(run.state, outcome))\n\t\t\tthrow new Error(`Approval request cannot transition from ${run.state}`)\n\t\tconst now = Date.now()\n\t\tawait ctx.db.patch(run._id, {\n\t\t\tstate: outcome,\n\t\t\texpiresAt: undefined,\n\t\t\tupdatedAt: now,\n\t\t\tterminalAt: now,\n\t\t})\n\t\tawait logApprovalTransition(ctx, {\n\t\t\taction: `approval.${outcome}`,\n\t\t\trunId: run._id,\n\t\t\tresourceType: run.resourceType,\n\t\t\tresourceRef: run.resourceRef,\n\t\t\ttransition: `${stepKey}:terminal:${outcome}`,\n\t\t})\n\t}\n\tif (!run.workflowId) throw new Error('Approval workflow is not linked')\n\tawait approvalWorkflow.sendEvent(ctx, {\n\t\t// SAFETY: workflowId was persisted from approvalWorkflow.start when this run was created.\n\t\tworkflowId: run.workflowId as WorkflowId,\n\t\tname: approvalDecisionEventName(run._id, stepKey),\n\t\tvalidator: v.object({\n\t\t\toutcome: v.union(v.literal(APPROVAL_DECISIONS[0]), v.literal(APPROVAL_DECISIONS[1])),\n\t\t\tterminalEvidence: v.object({\n\t\t\t\tactorRef: v.string(),\n\t\t\t\tdecision: zodToConvex(approvalDecision),\n\t\t\t\treason: v.optional(zodToConvex(approvalReason)),\n\t\t\t\tdecidedAt: v.number(),\n\t\t\t}),\n\t\t}),\n\t\tvalue: { outcome, terminalEvidence },\n\t})\n}\n\nfunction assertCompatible(actual: string, expected: string): void {\n\tif (actual !== expected)\n\t\tthrow new Error(\n\t\t\t`Approval workflow compatibility mismatch: expected ${expected}, received ${actual}`,\n\t\t)\n}\n\nasync function listDecisionEvidence(\n\tctx: QueryCtx,\n\trunId: import('./_generated/dataModel').Id<'approvalRuns'>,\n) {\n\tconst decisions = await ctx.db\n\t\t.query('approvalDecisions')\n\t\t.withIndex(APPROVAL_DECISION_HISTORY_INDEX, (query) => query.eq('runId', runId))\n\t\t.collect()\n\treturn decisions.map((decision) => ({\n\t\tstepKey: decision.stepKey,\n\t\tactorRef: decision.actor.actorRef,\n\t\tdecision: decision.decision,\n\t\treason: decision.reason,\n\t\tdecidedAt: decision.decidedAt,\n\t}))\n}\n\nasync function listDecisionEvidenceForStep(\n\tctx: QueryCtx,\n\trunId: import('./_generated/dataModel').Id<'approvalRuns'>,\n\tstepKey: string,\n) {\n\tconst decisions = await ctx.db\n\t\t.query('approvalDecisions')\n\t\t.withIndex('by_runId_and_stepKey_and_decidedAt', (query) =>\n\t\t\tquery.eq('runId', runId).eq('stepKey', stepKey),\n\t\t)\n\t\t.collect()\n\treturn decisions.map((decision) => ({\n\t\tstepKey: decision.stepKey,\n\t\tactorRef: decision.actor.actorRef,\n\t\tdecision: decision.decision,\n\t\treason: decision.reason,\n\t\tdecidedAt: decision.decidedAt,\n\t}))\n}\n\nfunction isTerminalDecision(\n\trun: import('./_generated/dataModel').Doc<'approvalRuns'>,\n\tstepKey: string,\n\toutcome: 'approved' | 'rejected',\n): boolean {\n\tif (outcome === 'rejected') return true\n\tconst currentIndex = run.workflow.steps.findIndex(\n\t\t(step) => step.kind === 'decision' && step.key === stepKey,\n\t)\n\treturn !run.workflow.steps.slice(currentIndex + 1).some((step) => step.kind === 'decision')\n}\n"],"mappings":"ixBAuBA,MAAa,EAAS,EAAS,CAC9B,KAAM,CACL,MAAO,EAAE,GAAG,cAAc,EAC1B,SAAU,EAAY,CAAgB,EACtC,OAAQ,EAAE,SAAS,EAAY,CAAc,CAAC,EAC9C,MAAO,EAAY,CAAa,EAChC,iBAAkB,EAAE,OAAO,CAC5B,EACA,QAAS,EAAE,OAAO,CACjB,MAAO,EAAE,MACR,EAAE,QAAQ,EAAoB,EAAE,EAChC,EAAE,QAAQ,EAAoB,EAAE,EAChC,EAAE,QAAQ,EAAoB,EAAE,EAChC,EAAE,QAAQ,EAAoB,EAAE,EAChC,EAAE,QAAQ,EAAoB,EAAE,CACjC,CACD,CAAC,EACD,QAAS,MAAO,EAAK,IAAU,CAC9B,IAAM,EAAM,MAAM,EAAI,GAAG,IAAI,EAAM,KAAK,EACxC,GAAI,CAAC,EAAK,MAAU,MAAM,4BAA4B,EACtD,EAAiB,EAAI,SAAS,iBAAkB,EAAM,gBAAgB,EACtE,IAAM,EAAO,EAAI,SAAS,MAAM,KAC9B,GAAc,EAAU,OAAS,YAAc,EAAU,MAAQ,EAAI,cACvE,EACA,GAAI,CAAC,GAAQ,EAAK,OAAS,WAC1B,MAAU,MAAM,gDAAgD,EAEjE,IAAM,EAAQ,MAAM,EAAI,GACtB,MAAM,mBAAmB,CAAC,CAC1B,UAAU,EAAgC,GAC1C,EACE,GAAG,QAAS,EAAI,GAAG,CAAC,CACpB,GAAG,UAAW,EAAK,GAAG,CAAC,CACvB,GAAG,iBAAkB,EAAM,MAAM,QAAQ,CAC5C,CAAC,CACA,OAAO,EACT,GAAI,EAAO,CACV,GAAI,EAAM,WAAa,EAAM,SAC5B,MAAU,MAAM,kDAAkD,EACnE,MAAO,CACN,MAAO,EAAI,KACZ,CACD,CACA,GAAI,EAAI,QAAU,UAAW,MAAU,MAAM,+BAA+B,EAAI,OAAO,EACvF,GACC,EAAI,YAAc,IAAA,IAClB,EAAqB,KAAK,IAAI,EAAG,EAAI,SAAS,IAAM,UAEpD,OAAO,EAAY,EAAK,EAAK,EAAK,GAAG,EACtC,GAAI,CAAC,EAAK,UAAU,SAAS,EAAM,QAAQ,EAC1C,MAAU,MAAM,+CAA+C,EAChE,GAAI,EAAK,cAAgB,EAAM,MAAM,WAAa,EAAI,UAAU,SAC/D,MAAU,MAAM,4CAA4C,EAE7D,IAAM,EAAY,KAAK,IAAI,EAC3B,MAAM,EAAI,GAAG,OAAO,oBAAqB,CACxC,MAAO,EAAI,IACX,QAAS,EAAK,IACd,MAAO,EAAM,MACb,SAAU,EAAM,SAChB,OAAQ,EAAM,OACd,WACD,CAAC,EACD,IAAM,EAAW,MAAM,EAAI,GACzB,MAAM,mBAAmB,CAAC,CAC1B,UAAU,qCAAuC,GACjD,EAAM,GAAG,QAAS,EAAI,GAAG,CAAC,CAAC,GAAG,UAAW,EAAK,GAAG,CAClD,CAAC,CACA,QAAQ,EACJ,EAAU,EACf,EAAS,IAAK,IAAU,CACvB,SAAU,EAAK,MAAM,SACrB,SAAU,EAAK,QAChB,EAAE,EACF,EAAK,OAAO,SACb,EASA,GARA,MAAM,EAAsB,EAAK,CAChC,OAAQ,qBAAqB,EAAM,WACnC,SAAU,EAAM,MAAM,SACtB,MAAO,EAAI,IACX,aAAc,EAAI,aAClB,YAAa,EAAI,YACjB,WAAY,GAAG,EAAK,IAAI,GAAG,EAAM,MAAM,SAAS,GAAG,EAAM,UAC1D,CAAC,EACG,CAAC,EAAS,MAAO,CAAE,MAAO,SAAmB,EACjD,IAAM,EAAW,EAAmB,EAAK,EAAK,IAAK,CAAO,EAO1D,OANA,MAAM,EAAe,EAAK,EAAK,EAAK,IAAK,EAAS,EAAU,CAC3D,SAAU,EAAM,MAAM,SACtB,SAAU,EAAM,SAChB,OAAQ,EAAM,OACd,WACD,CAAC,EACM,EAAW,CAAE,MAAO,CAAQ,EAAI,CAAE,MAAO,SAAmB,CACpE,CACD,CAAC,EAEY,EAAO,EAAM,CACzB,KAAM,CAAE,MAAO,EAAE,GAAG,cAAc,CAAE,EACpC,QAAS,EAAE,MACV,EAAE,OAAO,CACR,QAAS,EAAE,OAAO,EAClB,SAAU,EAAE,OAAO,EACnB,SAAU,EAAY,CAAgB,EACtC,OAAQ,EAAE,SAAS,EAAY,CAAc,CAAC,EAC9C,UAAW,EAAE,OAAO,CACrB,CAAC,CACF,EACA,SAAU,EAAK,IAAU,EAAqB,EAAK,EAAM,KAAK,CAC/D,CAAC,EAEY,EAAkB,EAAc,CAC5C,KAAM,CAAE,MAAO,EAAE,GAAG,cAAc,EAAG,QAAS,EAAE,OAAO,CAAE,EACzD,QAAS,EAAE,MACV,EAAE,OAAO,CACR,QAAS,EAAE,OAAO,EAClB,SAAU,EAAE,OAAO,EACnB,SAAU,EAAY,CAAgB,EACtC,OAAQ,EAAE,SAAS,EAAY,CAAc,CAAC,EAC9C,UAAW,EAAE,OAAO,CACrB,CAAC,CACF,EACA,SAAU,EAAK,IAAU,EAA4B,EAAK,EAAM,MAAO,EAAM,OAAO,CACrF,CAAC,EAEY,EAAQ,EAAiB,CACrC,KAAM,CACL,MAAO,EAAE,GAAG,cAAc,EAC1B,QAAS,EAAE,OAAO,EAClB,WAAY,EAAE,OAAO,EACrB,eAAgB,EAAE,SAAS,EAAE,OAAO,CAAC,CACtC,EACA,QAAS,EAAE,OAAO,CACjB,MAAO,EAAE,MACR,EAAE,QAAQ,EAAoB,EAAE,EAChC,EAAE,QAAQ,EAAoB,EAAE,EAChC,EAAE,QAAQ,EAAoB,EAAE,EAChC,EAAE,QAAQ,EAAoB,EAAE,EAChC,EAAE,QAAQ,EAAoB,EAAE,CACjC,EACA,UAAW,EAAE,SAAS,EAAE,OAAO,CAAC,CACjC,CAAC,EACD,QAAS,MAAO,EAAK,IAAU,CAC9B,IAAM,EAAM,MAAM,EAAI,GAAG,IAAI,EAAM,KAAK,EACxC,GAAI,CAAC,EAAK,MAAU,MAAM,4BAA4B,EACtD,GAAI,EAAI,aAAe,EAAM,WAC5B,MAAU,MAAM,yCAAyC,EAC1D,GAAI,EAAI,QAAU,UACjB,MAAO,CACN,MAAO,EAAI,MACX,UAAW,EAAI,SAChB,EACD,IAAM,EAAsB,OAAO,EAAI,UAAU,iBAAiB,EAC5D,EAAY,OAAO,SAAS,CAAmB,EAClD,EACA,EAAM,iBAAmB,IAAA,GACxB,IAAA,GACA,KAAK,IAAI,EAAI,EAAM,eAkBvB,OAjBA,MAAM,EAAI,GAAG,MAAM,EAAI,IAAK,CAC3B,eAAgB,EAAM,QACtB,YACA,UAAW,KAAK,IAAI,CACrB,CAAC,EACD,MAAM,EAAsB,EAAK,CAChC,OAAQ,mBACR,MAAO,EAAI,IACX,aAAc,EAAI,aAClB,YAAa,EAAI,YACjB,WAAY,GAAG,EAAM,QAAQ,SAC9B,CAAC,EACG,IAAc,IAAA,IACjB,MAAM,EAAI,UAAU,MAAM,EAAW,EAAS,UAAU,OAAQ,CAC/D,MAAO,EAAI,IACX,QAAS,EAAM,OAChB,CAAC,EACK,CACN,MAAO,UACI,WACZ,CACD,CACD,CAAC,EAEY,EAAS,EAAiB,CACtC,KAAM,CAAE,MAAO,EAAE,GAAG,cAAc,EAAG,QAAS,EAAE,OAAO,CAAE,EACzD,QAAS,EAAE,OAAO,CACjB,MAAO,EAAE,MACR,EAAE,QAAQ,SAAS,EACnB,EAAE,QAAQ,EAAoB,EAAE,EAChC,EAAE,QAAQ,EAAoB,EAAE,EAChC,EAAE,QAAQ,EAAoB,EAAE,EAChC,EAAE,QAAQ,EAAoB,EAAE,EAChC,EAAE,QAAQ,EAAoB,EAAE,CACjC,CACD,CAAC,EACD,QAAS,MAAO,EAAK,IAAU,CAC9B,IAAM,EAAM,MAAM,EAAI,GAAG,IAAI,EAAM,KAAK,EASxC,OARK,EAEJ,EAAI,QAAU,WACd,EAAI,iBAAmB,EAAM,SAC7B,EAAI,YAAc,IAAA,IAClB,EAAqB,KAAK,IAAI,EAAG,EAAI,SAAS,IAAM,UAE7C,CAAE,MAAO,EAAI,KAAM,EACpB,EAAY,EAAK,EAAK,EAAM,OAAO,EARzB,CAAE,MAAO,SAAmB,CAS9C,CACD,CAAC,EAEK,EAAmB,IAAI,EAAgB,EAAW,QAAQ,EAEhE,eAAe,EACd,EACA,EACA,EACgC,CAChC,GAAI,CAAC,EAAyB,EAAI,MAAO,SAAS,EACjD,MAAU,MAAM,2CAA2C,EAAI,OAAO,EACvE,IAAM,EAAM,KAAK,IAAI,EAcrB,GAbA,MAAM,EAAI,GAAG,MAAM,EAAI,IAAK,CAC3B,MAAO,UACP,UAAW,IAAA,GACX,UAAW,EACX,WAAY,CACb,CAAC,EACD,MAAM,EAAsB,EAAK,CAChC,OAAQ,mBACR,MAAO,EAAI,IACX,aAAc,EAAI,aAClB,YAAa,EAAI,YACjB,WAAY,GAAG,EAAQ,SACxB,CAAC,EACG,CAAC,EAAI,WAAY,MAAU,MAAM,iCAAiC,EAQtE,OAPA,MAAM,EAAiB,UAAU,EAAK,CAErC,WAAY,EAAI,WAChB,KAAM,EAA0B,EAAI,IAAK,CAAO,EAChD,UAAW,EAAE,OAAO,CAAE,QAAS,EAAE,QAAQ,EAAoB,EAAE,CAAE,CAAC,EAClE,MAAO,CAAE,QAAS,SAAU,CAC7B,CAAC,EACM,CAAE,MAAO,SAAU,CAC3B,CAEA,eAAe,EACd,EACA,EACA,EACA,EACA,EACA,EAMgB,CAChB,GAAI,EAAU,CACb,GAAI,CAAC,EAAyB,EAAI,MAAO,CAAO,EAC/C,MAAU,MAAM,2CAA2C,EAAI,OAAO,EACvE,IAAM,EAAM,KAAK,IAAI,EACrB,MAAM,EAAI,GAAG,MAAM,EAAI,IAAK,CAC3B,MAAO,EACP,UAAW,IAAA,GACX,UAAW,EACX,WAAY,CACb,CAAC,EACD,MAAM,EAAsB,EAAK,CAChC,OAAQ,YAAY,IACpB,MAAO,EAAI,IACX,aAAc,EAAI,aAClB,YAAa,EAAI,YACjB,WAAY,GAAG,EAAQ,YAAY,GACpC,CAAC,CACF,CACA,GAAI,CAAC,EAAI,WAAY,MAAU,MAAM,iCAAiC,EACtE,MAAM,EAAiB,UAAU,EAAK,CAErC,WAAY,EAAI,WAChB,KAAM,EAA0B,EAAI,IAAK,CAAO,EAChD,UAAW,EAAE,OAAO,CACnB,QAAS,EAAE,MAAM,EAAE,QAAQ,EAAmB,EAAE,EAAG,EAAE,QAAQ,EAAmB,EAAE,CAAC,EACnF,iBAAkB,EAAE,OAAO,CAC1B,SAAU,EAAE,OAAO,EACnB,SAAU,EAAY,CAAgB,EACtC,OAAQ,EAAE,SAAS,EAAY,CAAc,CAAC,EAC9C,UAAW,EAAE,OAAO,CACrB,CAAC,CACF,CAAC,EACD,MAAO,CAAE,UAAS,kBAAiB,CACpC,CAAC,CACF,CAEA,SAAS,EAAiB,EAAgB,EAAwB,CACjE,GAAI,IAAW,EACd,MAAU,MACT,sDAAsD,EAAS,aAAa,GAC7E,CACF,CAEA,eAAe,EACd,EACA,EACC,CAKD,OAAO,MAJiB,EAAI,GAC1B,MAAM,mBAAmB,CAAC,CAC1B,UAAU,EAAkC,GAAU,EAAM,GAAG,QAAS,CAAK,CAAC,CAAC,CAC/E,QAAQ,EAAA,CACO,IAAK,IAAc,CACnC,QAAS,EAAS,QAClB,SAAU,EAAS,MAAM,SACzB,SAAU,EAAS,SACnB,OAAQ,EAAS,OACjB,UAAW,EAAS,SACrB,EAAE,CACH,CAEA,eAAe,EACd,EACA,EACA,EACC,CAOD,OAAO,MANiB,EAAI,GAC1B,MAAM,mBAAmB,CAAC,CAC1B,UAAU,qCAAuC,GACjD,EAAM,GAAG,QAAS,CAAK,CAAC,CAAC,GAAG,UAAW,CAAO,CAC/C,CAAC,CACA,QAAQ,EAAA,CACO,IAAK,IAAc,CACnC,QAAS,EAAS,QAClB,SAAU,EAAS,MAAM,SACzB,SAAU,EAAS,SACnB,OAAQ,EAAS,OACjB,UAAW,EAAS,SACrB,EAAE,CACH,CAEA,SAAS,EACR,EACA,EACA,EACU,CACV,GAAI,IAAY,WAAY,MAAO,GACnC,IAAM,EAAe,EAAI,SAAS,MAAM,UACtC,GAAS,EAAK,OAAS,YAAc,EAAK,MAAQ,CACpD,EACA,MAAO,CAAC,EAAI,SAAS,MAAM,MAAM,EAAe,CAAC,CAAC,CAAC,KAAM,GAAS,EAAK,OAAS,UAAU,CAC3F"}
@@ -1,2 +1,2 @@
1
- function e(e,t){return n[e].includes(t)}function t(e,t){return e>=t?`expired`:`pending`}const n={pending:[`approved`,`rejected`,`expired`,`canceled`],approved:[],rejected:[],expired:[],canceled:[]};export{e as canTransitionApprovalRun,t as classifyPendingRunAt};
1
+ function e(e,t){return n[e].some(e=>e===t)}function t(e,t){return e>=t?`expired`:`pending`}const n={pending:[`approved`,`rejected`,`expired`,`canceled`],approved:[],rejected:[],expired:[],canceled:[]};export{e as canTransitionApprovalRun,t as classifyPendingRunAt};
2
2
  //# sourceMappingURL=functions.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"functions.mjs","names":[],"sources":["../../../src/components/approvals/functions.ts"],"sourcesContent":["import type { ApprovalRunState } from './constants'\n\nexport function canTransitionApprovalRun(\n\tfrom: ApprovalRunState,\n\tto: ApprovalRunState,\n): boolean {\n\treturn (\n\t\tLEGAL_APPROVAL_RUN_TRANSITIONS[from] as readonly ApprovalRunState[]\n\t).includes(to)\n}\n\nexport function classifyPendingRunAt(\n\tnow: number,\n\texpiresAt: number,\n): 'pending' | 'expired' {\n\treturn now >= expiresAt ? 'expired' : 'pending'\n}\n\nconst LEGAL_APPROVAL_RUN_TRANSITIONS = {\n\tpending: ['approved', 'rejected', 'expired', 'canceled'],\n\tapproved: [],\n\trejected: [],\n\texpired: [],\n\tcanceled: [],\n} as const satisfies Readonly<\n\tRecord<ApprovalRunState, readonly ApprovalRunState[]>\n>\n"],"mappings":"AAEA,SAAgB,EACf,EACA,EACU,CACV,OACC,EAA+B,EAAK,CACnC,SAAS,CAAE,CACd,CAEA,SAAgB,EACf,EACA,EACwB,CACxB,OAAO,GAAO,EAAY,UAAY,SACvC,CAEA,MAAM,EAAiC,CACtC,QAAS,CAAC,WAAY,WAAY,UAAW,UAAU,EACvD,SAAU,CAAC,EACX,SAAU,CAAC,EACX,QAAS,CAAC,EACV,SAAU,CAAC,CACZ"}
1
+ {"version":3,"file":"functions.mjs","names":[],"sources":["../../../src/components/approvals/functions.ts"],"sourcesContent":["import type { ApprovalRunState } from './constants'\n\nexport function canTransitionApprovalRun(from: ApprovalRunState, to: ApprovalRunState): boolean {\n\treturn LEGAL_APPROVAL_RUN_TRANSITIONS[from].some((state) => state === to)\n}\n\nexport function classifyPendingRunAt(now: number, expiresAt: number): 'pending' | 'expired' {\n\treturn now >= expiresAt ? 'expired' : 'pending'\n}\n\nconst LEGAL_APPROVAL_RUN_TRANSITIONS = {\n\tpending: ['approved', 'rejected', 'expired', 'canceled'],\n\tapproved: [],\n\trejected: [],\n\texpired: [],\n\tcanceled: [],\n} as const satisfies Readonly<Record<ApprovalRunState, readonly ApprovalRunState[]>>\n"],"mappings":"AAEA,SAAgB,EAAyB,EAAwB,EAA+B,CAC/F,OAAO,EAA+B,EAAK,CAAC,KAAM,GAAU,IAAU,CAAE,CACzE,CAEA,SAAgB,EAAqB,EAAa,EAA0C,CAC3F,OAAO,GAAO,EAAY,UAAY,SACvC,CAEA,MAAM,EAAiC,CACtC,QAAS,CAAC,WAAY,WAAY,UAAW,UAAU,EACvD,SAAU,CAAC,EACX,SAAU,CAAC,EACX,QAAS,CAAC,EACV,SAAU,CAAC,CACZ"}
@@ -1 +1 @@
1
- {"version":3,"file":"health.mjs","names":[],"sources":["../../../src/components/approvals/health.ts"],"sourcesContent":["import { v } from 'convex/values'\n\nimport { query } from './_generated/server'\nimport {\n\tAPPROVAL_COMPONENT_SCHEMA_VERSION,\n\tAPPROVAL_REQUIRED_DECISION_INDEXES,\n} from './constants'\n\n/**\n * Verifies that the package's component schema and required decision indexes\n * were deployed. A missing schema fails with a cvx-kit-specific diagnostic.\n */\nexport const check = query({\n\targs: {},\n\treturns: v.object({\n\t\tstatus: v.literal('ready'),\n\t\tschemaVersion: v.literal(APPROVAL_COMPONENT_SCHEMA_VERSION),\n\t\trequiredIndexes: v.array(v.string()),\n\t}),\n\thandler: async (ctx) => {\n\t\ttry {\n\t\t\tfor (const index of APPROVAL_REQUIRED_DECISION_INDEXES) {\n\t\t\t\tawait ctx.db.query('approvalDecisions').withIndex(index).take(1)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`cvx-kit approvals schema v${APPROVAL_COMPONENT_SCHEMA_VERSION} is not installed with its required indexes. Remove consumer patches, reinstall cvx-kit, and deploy again. Cause: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t)\n\t\t}\n\n\t\treturn {\n\t\t\tstatus: 'ready' as const,\n\t\t\tschemaVersion: APPROVAL_COMPONENT_SCHEMA_VERSION,\n\t\t\trequiredIndexes: [...APPROVAL_REQUIRED_DECISION_INDEXES],\n\t\t}\n\t},\n})\n"],"mappings":"uJAYA,MAAa,EAAQ,EAAM,CAC1B,KAAM,CAAC,EACP,QAAS,EAAE,OAAO,CACjB,OAAQ,EAAE,QAAQ,OAAO,EACzB,cAAe,EAAE,QAAA,CAAyC,EAC1D,gBAAiB,EAAE,MAAM,EAAE,OAAO,CAAC,CACpC,CAAC,EACD,QAAS,KAAO,IAAQ,CACvB,GAAI,CACH,IAAK,IAAM,KAAS,EACnB,MAAM,EAAI,GAAG,MAAM,mBAAmB,CAAC,CAAC,UAAU,CAAK,CAAC,CAAC,KAAK,CAAC,CAEjE,OAAS,EAAO,CACf,MAAU,MACT,gJAAmL,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GACzO,CACD,CAEA,MAAO,CACN,OAAQ,QACR,cAAA,EACA,gBAAiB,CAAC,GAAG,CAAkC,CACxD,CACD,CACD,CAAC"}
1
+ {"version":3,"file":"health.mjs","names":[],"sources":["../../../src/components/approvals/health.ts"],"sourcesContent":["import { v } from 'convex/values'\n\nimport { query } from './_generated/server'\nimport { APPROVAL_COMPONENT_SCHEMA_VERSION, APPROVAL_REQUIRED_DECISION_INDEXES } from './constants'\n\n/**\n * Verifies that the package's component schema and required decision indexes\n * were deployed. A missing schema fails with a cvx-kit-specific diagnostic.\n */\nexport const check = query({\n\targs: {},\n\treturns: v.object({\n\t\tstatus: v.literal('ready'),\n\t\tschemaVersion: v.literal(APPROVAL_COMPONENT_SCHEMA_VERSION),\n\t\trequiredIndexes: v.array(v.string()),\n\t}),\n\thandler: async (ctx) => {\n\t\ttry {\n\t\t\tfor (const index of APPROVAL_REQUIRED_DECISION_INDEXES) {\n\t\t\t\tawait ctx.db.query('approvalDecisions').withIndex(index).take(1)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`cvx-kit approvals schema v${APPROVAL_COMPONENT_SCHEMA_VERSION} is not installed with its required indexes. Remove consumer patches, reinstall cvx-kit, and deploy again. Cause: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t)\n\t\t}\n\n\t\treturn {\n\t\t\tstatus: 'ready' as const,\n\t\t\tschemaVersion: APPROVAL_COMPONENT_SCHEMA_VERSION,\n\t\t\trequiredIndexes: [...APPROVAL_REQUIRED_DECISION_INDEXES],\n\t\t}\n\t},\n})\n"],"mappings":"uJASA,MAAa,EAAQ,EAAM,CAC1B,KAAM,CAAC,EACP,QAAS,EAAE,OAAO,CACjB,OAAQ,EAAE,QAAQ,OAAO,EACzB,cAAe,EAAE,QAAA,CAAyC,EAC1D,gBAAiB,EAAE,MAAM,EAAE,OAAO,CAAC,CACpC,CAAC,EACD,QAAS,KAAO,IAAQ,CACvB,GAAI,CACH,IAAK,IAAM,KAAS,EACnB,MAAM,EAAI,GAAG,MAAM,mBAAmB,CAAC,CAAC,UAAU,CAAK,CAAC,CAAC,KAAK,CAAC,CAEjE,OAAS,EAAO,CACf,MAAU,MACT,gJAAmL,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GACzO,CACD,CAEA,MAAO,CACN,OAAQ,QACR,cAAA,EACA,gBAAiB,CAAC,GAAG,CAAkC,CACxD,CACD,CACD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"requests.mjs","names":["schema"],"sources":["../../../src/components/approvals/requests.ts"],"sourcesContent":["import { WorkflowManager, type WorkflowId } from '@convex-dev/workflow'\nimport { zodToConvex } from 'convex-helpers/server/zod4'\nimport { paginator } from 'convex-helpers/server/pagination'\nimport {\n\tpaginationOptsValidator,\n\tpaginationResultValidator,\n} from 'convex/server'\nimport { v } from 'convex/values'\n\nimport { components, internal } from './_generated/api'\nimport {\n\tinternalMutation,\n\tinternalQuery,\n\tmutation,\n\tquery,\n} from './_generated/server'\nimport { logApprovalTransition } from './audit'\nimport schema from './schema'\nimport { APPROVAL_EXECUTION_STATES, APPROVAL_RUN_STATES } from './constants'\nimport { canTransitionApprovalRun } from './functions'\nimport {\n\tapprovalActor,\n\tapprovalMetadata,\n\tapprovalName,\n\tapprovalReference,\n\tapprovalRunDocument,\n\tapprovalRunState,\n\tapprovalWorkflowDescriptor,\n} from './validators'\n\nexport const start = mutation({\n\targs: {\n\t\tscopeRef: zodToConvex(approvalReference),\n\t\tresourceType: zodToConvex(approvalReference),\n\t\tresourceRef: zodToConvex(approvalReference),\n\t\trequester: zodToConvex(approvalActor),\n\t\tmetadata: v.optional(zodToConvex(approvalMetadata)),\n\t\tworkflow: zodToConvex(approvalWorkflowDescriptor),\n\t},\n\treturns: v.object({ runId: v.id('approvalRuns') }),\n\thandler: async (ctx, input) => {\n\t\tconst now = Date.now()\n\t\tconst runId = await ctx.db.insert('approvalRuns', {\n\t\t\t...input,\n\t\t\tstate: 'pending',\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t})\n\t\tconst workflowId: WorkflowId = await approvalWorkflow.start(\n\t\t\tctx,\n\t\t\tinternal.workflow.run,\n\t\t\t{ runId },\n\t\t\t{ startAsync: true },\n\t\t)\n\t\tawait ctx.db.patch(runId, { workflowId, updatedAt: Date.now() })\n\t\tawait logApprovalTransition(ctx, {\n\t\t\taction: 'approval.requested',\n\t\t\tactorRef: input.requester.actorRef,\n\t\t\trunId,\n\t\t\tresourceType: input.resourceType,\n\t\t\tresourceRef: input.resourceRef,\n\t\t\ttransition: 'requested',\n\t\t})\n\t\treturn { runId }\n\t},\n})\n\nexport const cancel = mutation({\n\targs: {\n\t\trunId: v.id('approvalRuns'),\n\t\tactor: zodToConvex(approvalActor),\n\t\tcompatibilityKey: v.string(),\n\t},\n\treturns: v.object({ state: v.literal(APPROVAL_RUN_STATES[4]) }),\n\thandler: async (ctx, input) => {\n\t\tconst run = await ctx.db.get(input.runId)\n\t\tif (!run) throw new Error('Approval request not found')\n\t\tassertCompatible(run.workflow.compatibilityKey, input.compatibilityKey)\n\t\tif (run.state !== 'pending') {\n\t\t\tif (run.state === 'canceled') return { state: 'canceled' as const }\n\t\t\tthrow new Error(`Approval request is already ${run.state}`)\n\t\t}\n\t\tif (!run.workflowId) throw new Error('Approval workflow is not linked')\n\t\tawait approvalWorkflow.cancel(ctx, run.workflowId as WorkflowId)\n\t\tif (!canTransitionApprovalRun(run.state, 'canceled'))\n\t\t\tthrow new Error(`Approval request cannot transition from ${run.state}`)\n\t\tconst now = Date.now()\n\t\tawait ctx.db.patch(run._id, {\n\t\t\tstate: 'canceled',\n\t\t\texpiresAt: undefined,\n\t\t\tupdatedAt: now,\n\t\t\tterminalAt: now,\n\t\t})\n\t\tawait logApprovalTransition(ctx, {\n\t\t\taction: 'approval.canceled',\n\t\t\tactorRef: input.actor.actorRef,\n\t\t\trunId: run._id,\n\t\t\tresourceType: run.resourceType,\n\t\t\tresourceRef: run.resourceRef,\n\t\t\ttransition: 'canceled',\n\t\t})\n\t\treturn { state: 'canceled' as const }\n\t},\n})\n\nexport const restart = mutation({\n\targs: { runId: v.id('approvalRuns'), compatibilityKey: v.string() },\n\treturns: v.null(),\n\thandler: async (ctx, input) => {\n\t\tconst run = await ctx.db.get(input.runId)\n\t\tif (!run) throw new Error('Approval request not found')\n\t\tassertCompatible(run.workflow.compatibilityKey, input.compatibilityKey)\n\t\tif (!run.workflowId) throw new Error('Approval workflow is not linked')\n\t\tif (!run.executionFailedStepKey)\n\t\t\tthrow new Error('Approval workflow has no failed step to restart')\n\t\tawait approvalWorkflow.restart(ctx, run.workflowId as WorkflowId, {\n\t\t\tfrom: run.executionFailedStepKey,\n\t\t\tstartAsync: true,\n\t\t})\n\t\treturn null\n\t},\n})\n\nexport const status = query({\n\targs: { runId: v.id('approvalRuns'), compatibilityKey: v.string() },\n\treturns: v.union(v.null(), approvalStatusValidator()),\n\thandler: async (ctx, input) => {\n\t\tconst run = await ctx.db.get(input.runId)\n\t\tif (!run) return null\n\t\tassertCompatible(run.workflow.compatibilityKey, input.compatibilityKey)\n\t\tconst workflowStatus = run.workflowId\n\t\t\t? await approvalWorkflow.status(ctx, run.workflowId as WorkflowId)\n\t\t\t: null\n\t\tconst execution = normalizeWorkflowStatus(workflowStatus)\n\t\treturn { run, execution }\n\t},\n})\n\nexport const list = query({\n\targs: {\n\t\tscopeRef: zodToConvex(approvalReference),\n\t\tstate: v.optional(zodToConvex(approvalRunState)),\n\t\tpaginationOpts: paginationOptsValidator,\n\t},\n\treturns: paginationResultValidator(approvalRunValidator()),\n\thandler: (ctx, input) =>\n\t\tpaginator(ctx.db, schema)\n\t\t\t.query('approvalRuns')\n\t\t\t.withIndex('by_scopeRef_and_state', (query) =>\n\t\t\t\tinput.state === undefined\n\t\t\t\t\t? query.eq('scopeRef', input.scopeRef)\n\t\t\t\t\t: query.eq('scopeRef', input.scopeRef).eq('state', input.state),\n\t\t\t)\n\t\t\t.order('desc')\n\t\t\t.paginate(input.paginationOpts),\n})\n\nexport const getForWorkflow = internalQuery({\n\targs: { runId: v.id('approvalRuns') },\n\treturns: v.union(v.null(), approvalRunValidator()),\n\thandler: (ctx, input) => ctx.db.get(input.runId),\n})\n\nexport const recordExecutionTerminal = internalMutation({\n\targs: {\n\t\trunId: v.id('approvalRuns'),\n\t\toutcome: v.union(\n\t\t\tv.literal(APPROVAL_EXECUTION_STATES[1]),\n\t\t\tv.literal(APPROVAL_EXECUTION_STATES[3]),\n\t\t),\n\t\tfailedStepKey: v.optional(zodToConvex(approvalName)),\n\t},\n\treturns: v.null(),\n\thandler: async (ctx, input) => {\n\t\tconst run = await ctx.db.get(input.runId)\n\t\tif (!run) throw new Error('Approval request not found')\n\t\tawait ctx.db.patch(run._id, {\n\t\t\texecutionFailedStepKey:\n\t\t\t\tinput.outcome === 'failed' ? input.failedStepKey : undefined,\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t\tawait logApprovalTransition(ctx, {\n\t\t\taction: `approval.execution.${input.outcome}`,\n\t\t\trunId: run._id,\n\t\t\tresourceType: run.resourceType,\n\t\t\tresourceRef: run.resourceRef,\n\t\t\ttransition: `execution:${input.outcome}`,\n\t\t\tseverity: input.outcome === 'failed' ? 'error' : 'info',\n\t\t})\n\t\treturn null\n\t},\n})\n\nconst approvalWorkflow = new WorkflowManager(components.workflow)\n\nfunction normalizeWorkflowStatus(\n\tstatus: Awaited<ReturnType<typeof approvalWorkflow.status>> | null,\n) {\n\tif (!status) return null\n\tswitch (status.type) {\n\t\tcase 'failed':\n\t\t\treturn { type: 'failed' as const, error: status.error }\n\t\tcase 'inProgress':\n\t\tcase 'completed':\n\t\tcase 'canceled':\n\t\t\treturn { type: status.type }\n\t}\n}\n\nfunction approvalRunValidator() {\n\treturn v.object({\n\t\t_id: v.id('approvalRuns'),\n\t\t_creationTime: v.number(),\n\t\t...zodToConvex(approvalRunDocument).fields,\n\t})\n}\n\nfunction workflowStatusValidator() {\n\treturn v.union(\n\t\tv.object({ type: v.literal(APPROVAL_EXECUTION_STATES[0]) }),\n\t\tv.object({ type: v.literal(APPROVAL_EXECUTION_STATES[1]) }),\n\t\tv.object({ type: v.literal(APPROVAL_EXECUTION_STATES[2]) }),\n\t\tv.object({\n\t\t\ttype: v.literal(APPROVAL_EXECUTION_STATES[3]),\n\t\t\terror: v.string(),\n\t\t}),\n\t)\n}\n\nfunction approvalStatusValidator() {\n\treturn v.object({\n\t\trun: approvalRunValidator(),\n\t\texecution: v.union(v.null(), workflowStatusValidator()),\n\t})\n}\n\nfunction assertCompatible(actual: string, expected: string): void {\n\tif (actual !== expected)\n\t\tthrow new Error(\n\t\t\t`Approval workflow compatibility mismatch: expected ${expected}, received ${actual}`,\n\t\t)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"requests.mjs","names":["schema"],"sources":["../../../src/components/approvals/requests.ts"],"sourcesContent":["import { WorkflowManager, type WorkflowId } from '@convex-dev/workflow'\nimport { zodToConvex } from 'convex-helpers/server/zod4'\nimport { paginator } from 'convex-helpers/server/pagination'\nimport { paginationOptsValidator, paginationResultValidator } from 'convex/server'\nimport { v } from 'convex/values'\n\nimport { components, internal } from './_generated/api'\nimport { internalMutation, internalQuery, mutation, query } from './_generated/server'\nimport { logApprovalTransition } from './audit'\nimport schema from './schema'\nimport { APPROVAL_EXECUTION_STATES, APPROVAL_RUN_STATES } from './constants'\nimport { canTransitionApprovalRun } from './functions'\nimport {\n\tapprovalActor,\n\tapprovalMetadata,\n\tapprovalName,\n\tapprovalReference,\n\tapprovalRunDocument,\n\tapprovalRunState,\n\tapprovalWorkflowDescriptor,\n} from './validators'\n\nexport const start = mutation({\n\targs: {\n\t\tscopeRef: zodToConvex(approvalReference),\n\t\tresourceType: zodToConvex(approvalReference),\n\t\tresourceRef: zodToConvex(approvalReference),\n\t\trequester: zodToConvex(approvalActor),\n\t\tmetadata: v.optional(zodToConvex(approvalMetadata)),\n\t\tworkflow: zodToConvex(approvalWorkflowDescriptor),\n\t},\n\treturns: v.object({ runId: v.id('approvalRuns') }),\n\thandler: async (ctx, input) => {\n\t\tconst now = Date.now()\n\t\tconst runId = await ctx.db.insert('approvalRuns', {\n\t\t\t...input,\n\t\t\tstate: 'pending',\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t})\n\t\tconst workflowId: WorkflowId = await approvalWorkflow.start(\n\t\t\tctx,\n\t\t\tinternal.workflow.run,\n\t\t\t{ runId },\n\t\t\t{ startAsync: true },\n\t\t)\n\t\tawait ctx.db.patch(runId, { workflowId, updatedAt: Date.now() })\n\t\tawait logApprovalTransition(ctx, {\n\t\t\taction: 'approval.requested',\n\t\t\tactorRef: input.requester.actorRef,\n\t\t\trunId,\n\t\t\tresourceType: input.resourceType,\n\t\t\tresourceRef: input.resourceRef,\n\t\t\ttransition: 'requested',\n\t\t})\n\t\treturn { runId }\n\t},\n})\n\nexport const cancel = mutation({\n\targs: {\n\t\trunId: v.id('approvalRuns'),\n\t\tactor: zodToConvex(approvalActor),\n\t\tcompatibilityKey: v.string(),\n\t},\n\treturns: v.object({ state: v.literal(APPROVAL_RUN_STATES[4]) }),\n\thandler: async (ctx, input) => {\n\t\tconst run = await ctx.db.get(input.runId)\n\t\tif (!run) throw new Error('Approval request not found')\n\t\tassertCompatible(run.workflow.compatibilityKey, input.compatibilityKey)\n\t\tif (run.state !== 'pending') {\n\t\t\tif (run.state === 'canceled') return { state: 'canceled' as const }\n\t\t\tthrow new Error(`Approval request is already ${run.state}`)\n\t\t}\n\t\tif (!run.workflowId) throw new Error('Approval workflow is not linked')\n\t\t// SAFETY: this run stores the id returned by approvalWorkflow.start.\n\t\tawait approvalWorkflow.cancel(ctx, run.workflowId as WorkflowId)\n\t\tif (!canTransitionApprovalRun(run.state, 'canceled'))\n\t\t\tthrow new Error(`Approval request cannot transition from ${run.state}`)\n\t\tconst now = Date.now()\n\t\tawait ctx.db.patch(run._id, {\n\t\t\tstate: 'canceled',\n\t\t\texpiresAt: undefined,\n\t\t\tupdatedAt: now,\n\t\t\tterminalAt: now,\n\t\t})\n\t\tawait logApprovalTransition(ctx, {\n\t\t\taction: 'approval.canceled',\n\t\t\tactorRef: input.actor.actorRef,\n\t\t\trunId: run._id,\n\t\t\tresourceType: run.resourceType,\n\t\t\tresourceRef: run.resourceRef,\n\t\t\ttransition: 'canceled',\n\t\t})\n\t\treturn { state: 'canceled' as const }\n\t},\n})\n\nexport const restart = mutation({\n\targs: { runId: v.id('approvalRuns'), compatibilityKey: v.string() },\n\treturns: v.null(),\n\thandler: async (ctx, input) => {\n\t\tconst run = await ctx.db.get(input.runId)\n\t\tif (!run) throw new Error('Approval request not found')\n\t\tassertCompatible(run.workflow.compatibilityKey, input.compatibilityKey)\n\t\tif (!run.workflowId) throw new Error('Approval workflow is not linked')\n\t\tif (!run.executionFailedStepKey)\n\t\t\tthrow new Error('Approval workflow has no failed step to restart')\n\t\t// SAFETY: this run stores the id returned by approvalWorkflow.start.\n\t\tawait approvalWorkflow.restart(ctx, run.workflowId as WorkflowId, {\n\t\t\tfrom: run.executionFailedStepKey,\n\t\t\tstartAsync: true,\n\t\t})\n\t\treturn null\n\t},\n})\n\nexport const status = query({\n\targs: { runId: v.id('approvalRuns'), compatibilityKey: v.string() },\n\treturns: v.union(v.null(), approvalStatusValidator()),\n\thandler: async (ctx, input) => {\n\t\tconst run = await ctx.db.get(input.runId)\n\t\tif (!run) return null\n\t\tassertCompatible(run.workflow.compatibilityKey, input.compatibilityKey)\n\t\t// SAFETY: workflowId is the id persisted from approvalWorkflow.start for this run.\n\t\tconst workflowStatus = run.workflowId\n\t\t\t? await approvalWorkflow.status(ctx, run.workflowId as WorkflowId)\n\t\t\t: null\n\t\tconst execution = normalizeWorkflowStatus(workflowStatus)\n\t\treturn { run, execution }\n\t},\n})\n\nexport const list = query({\n\targs: {\n\t\tscopeRef: zodToConvex(approvalReference),\n\t\tstate: v.optional(zodToConvex(approvalRunState)),\n\t\tpaginationOpts: paginationOptsValidator,\n\t},\n\treturns: paginationResultValidator(approvalRunValidator()),\n\thandler: (ctx, input) =>\n\t\tpaginator(ctx.db, schema)\n\t\t\t.query('approvalRuns')\n\t\t\t.withIndex('by_scopeRef_and_state', (query) =>\n\t\t\t\tinput.state === undefined\n\t\t\t\t\t? query.eq('scopeRef', input.scopeRef)\n\t\t\t\t\t: query.eq('scopeRef', input.scopeRef).eq('state', input.state),\n\t\t\t)\n\t\t\t.order('desc')\n\t\t\t.paginate(input.paginationOpts),\n})\n\nexport const getForWorkflow = internalQuery({\n\targs: { runId: v.id('approvalRuns') },\n\treturns: v.union(v.null(), approvalRunValidator()),\n\thandler: (ctx, input) => ctx.db.get(input.runId),\n})\n\nexport const recordExecutionTerminal = internalMutation({\n\targs: {\n\t\trunId: v.id('approvalRuns'),\n\t\toutcome: v.union(\n\t\t\tv.literal(APPROVAL_EXECUTION_STATES[1]),\n\t\t\tv.literal(APPROVAL_EXECUTION_STATES[3]),\n\t\t),\n\t\tfailedStepKey: v.optional(zodToConvex(approvalName)),\n\t},\n\treturns: v.null(),\n\thandler: async (ctx, input) => {\n\t\tconst run = await ctx.db.get(input.runId)\n\t\tif (!run) throw new Error('Approval request not found')\n\t\tawait ctx.db.patch(run._id, {\n\t\t\texecutionFailedStepKey: input.outcome === 'failed' ? input.failedStepKey : undefined,\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t\tawait logApprovalTransition(ctx, {\n\t\t\taction: `approval.execution.${input.outcome}`,\n\t\t\trunId: run._id,\n\t\t\tresourceType: run.resourceType,\n\t\t\tresourceRef: run.resourceRef,\n\t\t\ttransition: `execution:${input.outcome}`,\n\t\t\tseverity: input.outcome === 'failed' ? 'error' : 'info',\n\t\t})\n\t\treturn null\n\t},\n})\n\nconst approvalWorkflow = new WorkflowManager(components.workflow)\n\nfunction normalizeWorkflowStatus(\n\tstatus: Awaited<ReturnType<typeof approvalWorkflow.status>> | null,\n) {\n\tif (!status) return null\n\tswitch (status.type) {\n\t\tcase 'failed':\n\t\t\treturn { type: 'failed' as const, error: status.error }\n\t\tcase 'inProgress':\n\t\tcase 'completed':\n\t\tcase 'canceled':\n\t\t\treturn { type: status.type }\n\t}\n}\n\nfunction approvalRunValidator() {\n\treturn v.object({\n\t\t_id: v.id('approvalRuns'),\n\t\t_creationTime: v.number(),\n\t\t...zodToConvex(approvalRunDocument).fields,\n\t})\n}\n\nfunction workflowStatusValidator() {\n\treturn v.union(\n\t\tv.object({ type: v.literal(APPROVAL_EXECUTION_STATES[0]) }),\n\t\tv.object({ type: v.literal(APPROVAL_EXECUTION_STATES[1]) }),\n\t\tv.object({ type: v.literal(APPROVAL_EXECUTION_STATES[2]) }),\n\t\tv.object({\n\t\t\ttype: v.literal(APPROVAL_EXECUTION_STATES[3]),\n\t\t\terror: v.string(),\n\t\t}),\n\t)\n}\n\nfunction approvalStatusValidator() {\n\treturn v.object({\n\t\trun: approvalRunValidator(),\n\t\texecution: v.union(v.null(), workflowStatusValidator()),\n\t})\n}\n\nfunction assertCompatible(actual: string, expected: string): void {\n\tif (actual !== expected)\n\t\tthrow new Error(\n\t\t\t`Approval workflow compatibility mismatch: expected ${expected}, received ${actual}`,\n\t\t)\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"validators.mjs","names":[],"sources":["../../../src/components/approvals/validators.ts"],"sourcesContent":["import { zid, zodToConvex } from 'convex-helpers/server/zod4'\nimport { z } from 'zod'\n\nimport {\n\tAPPROVAL_CALLBACK_KINDS,\n\tAPPROVAL_DECISIONS,\n\tAPPROVAL_DESCRIPTOR_SCHEMA_VERSION,\n\tAPPROVAL_QUORUM_KINDS,\n\tAPPROVAL_RUN_STATES,\n\tAPPROVAL_STEP_KINDS,\n\tMAX_APPROVAL_CAPABILITIES,\n\tMAX_APPROVAL_DECISIONS_PER_STEP,\n\tMAX_APPROVAL_METADATA_ENTRIES,\n\tMAX_APPROVAL_METADATA_KEY_LENGTH,\n\tMAX_APPROVAL_METADATA_VALUE_LENGTH,\n\tMAX_APPROVAL_NAME_LENGTH,\n\tMAX_APPROVAL_REASON_LENGTH,\n\tMAX_APPROVAL_REFERENCE_LENGTH,\n\tMAX_APPROVAL_WORKFLOW_STEPS,\n} from './constants'\n\nexport const approvalRunState = z.enum(APPROVAL_RUN_STATES)\nexport const approvalDecision = z.enum(APPROVAL_DECISIONS)\nexport const approvalStepKind = z.enum(APPROVAL_STEP_KINDS)\nexport const approvalCallbackKind = z.enum(APPROVAL_CALLBACK_KINDS)\nexport const approvalQuorumKind = z.enum(APPROVAL_QUORUM_KINDS)\n\nexport const approvalReference = z\n\t.string()\n\t.trim()\n\t.min(1)\n\t.max(MAX_APPROVAL_REFERENCE_LENGTH)\n\nexport const approvalName = z\n\t.string()\n\t.trim()\n\t.min(1)\n\t.max(MAX_APPROVAL_NAME_LENGTH)\n\t.regex(/^[A-Za-z][A-Za-z0-9._-]*$/)\n\nexport const approvalReason = z\n\t.string()\n\t.trim()\n\t.min(1)\n\t.max(MAX_APPROVAL_REASON_LENGTH)\n\nexport const approvalMetadata = z\n\t.record(\n\t\tz\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(MAX_APPROVAL_METADATA_KEY_LENGTH)\n\t\t\t.regex(/^(?![$_])[\\x20-\\x7E]+$/),\n\t\tz.string().max(MAX_APPROVAL_METADATA_VALUE_LENGTH),\n\t)\n\t.refine(\n\t\t(value) => Object.keys(value).length <= MAX_APPROVAL_METADATA_ENTRIES,\n\t\t`Metadata may contain at most ${MAX_APPROVAL_METADATA_ENTRIES} entries`,\n\t)\n\nconst approvalCallbackEvidence = z\n\t.object({\n\t\tstepKey: approvalName,\n\t\tactorRef: approvalReference,\n\t\tdecision: approvalDecision,\n\t\treason: approvalReason.optional(),\n\t\tdecidedAt: z.number(),\n\t})\n\t.strict()\n\nconst approvalCallbackTerminalEvidence = approvalCallbackEvidence.omit({\n\tstepKey: true,\n})\n\nexport const approvalCallbackInput = z\n\t.object({\n\t\trunId: approvalReference,\n\t\tscopeRef: approvalReference,\n\t\tresourceType: approvalName,\n\t\tresourceRef: approvalReference,\n\t\tmetadata: approvalMetadata.optional(),\n\t\tdecision: z\n\t\t\t.object({\n\t\t\t\tstepKey: approvalName,\n\t\t\t\toutcome: z.union([approvalDecision, z.literal('expired')]),\n\t\t\t\tevidence: z.array(approvalCallbackEvidence),\n\t\t\t\tterminalEvidence: approvalCallbackTerminalEvidence.optional(),\n\t\t\t})\n\t\t\t.strict()\n\t\t\t.optional(),\n\t})\n\t.strict()\n\n/** Convex argument validator for approval mutation, action, and notify callbacks. */\nexport const approvalCallbackArgs = zodToConvex(approvalCallbackInput)\n\nexport const approvalActor = z\n\t.object({\n\t\tactorRef: approvalReference,\n\t\tcapabilities: z.array(approvalName).max(MAX_APPROVAL_CAPABILITIES),\n\t\tmetadata: approvalMetadata.optional(),\n\t})\n\t.strict()\n\nexport const approvalMutationCallback = z\n\t.object({\n\t\tkind: z.literal('mutation'),\n\t\thandle: approvalReference,\n\t\tretry: z.boolean(),\n\t})\n\t.strict()\n\nexport const approvalActionCallback = z\n\t.object({\n\t\tkind: z.literal('action'),\n\t\thandle: approvalReference,\n\t\tretry: z.boolean(),\n\t})\n\t.strict()\n\nexport const approvalCallback = z.discriminatedUnion('kind', [\n\tapprovalMutationCallback,\n\tapprovalActionCallback,\n])\n\nexport const approvalQuorum = z\n\t.object({\n\t\tkind: approvalQuorumKind,\n\t\tapprovals: z.number().int().positive().max(MAX_APPROVAL_DECISIONS_PER_STEP),\n\t})\n\t.strict()\n\nexport const approvalCallbackStep = z.discriminatedUnion('kind', [\n\tz\n\t\t.object({\n\t\t\tkind: z.literal('mutation'),\n\t\t\tkey: approvalName,\n\t\t\tcallback: approvalMutationCallback,\n\t\t})\n\t\t.strict(),\n\tz\n\t\t.object({\n\t\t\tkind: z.literal('action'),\n\t\t\tkey: approvalName,\n\t\t\tcallback: approvalActionCallback,\n\t\t})\n\t\t.strict(),\n])\n\nexport const approvalNotifyStep = z\n\t.object({\n\t\tkind: z.literal('notify'),\n\t\tkey: approvalName,\n\t\tcallback: approvalActionCallback,\n\t})\n\t.strict()\n\nexport const approvalDecisionStep = z\n\t.object({\n\t\tkind: z.literal('decision'),\n\t\tkey: approvalName,\n\t\tdecisions: z\n\t\t\t.array(approvalDecision)\n\t\t\t.min(1)\n\t\t\t.max(MAX_APPROVAL_DECISIONS_PER_STEP),\n\t\tquorum: approvalQuorum,\n\t\tmakerChecker: z.boolean(),\n\t\texpiresAfterMs: z.number().int().positive().optional(),\n\t})\n\t.strict()\n\nexport const approvalBranchStep = z\n\t.object({\n\t\tkind: z.literal('branch'),\n\t\tkey: approvalName,\n\t\tapprovedStepKey: approvalName,\n\t\trejectedStepKey: approvalName,\n\t})\n\t.strict()\n\nexport const approvalWorkflowStep = z.discriminatedUnion('kind', [\n\tapprovalCallbackStep,\n\tapprovalNotifyStep,\n\tapprovalDecisionStep,\n\tapprovalBranchStep,\n])\n\nexport const approvalWorkflowDescriptor = z\n\t.object({\n\t\tschemaVersion: z.literal(APPROVAL_DESCRIPTOR_SCHEMA_VERSION),\n\t\tcompatibilityKey: approvalName,\n\t\tname: approvalName,\n\t\tsteps: z\n\t\t\t.array(approvalWorkflowStep)\n\t\t\t.min(1)\n\t\t\t.max(MAX_APPROVAL_WORKFLOW_STEPS),\n\t})\n\t.strict()\n\t.superRefine((descriptor, context) => {\n\t\tconst keys = new Set<string>()\n\t\tconst stepsByKey = new Map(\n\t\t\tdescriptor.steps.map((step) => [step.key, step] as const),\n\t\t)\n\t\tconst branchTargetOwners = new Map<string, number>()\n\t\tfor (const [index, step] of descriptor.steps.entries()) {\n\t\t\tif (keys.has(step.key))\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Duplicate workflow step key: ${step.key}`,\n\t\t\t\t\tpath: ['steps', index, 'key'],\n\t\t\t\t})\n\t\t\tkeys.add(step.key)\n\t\t\tif (\n\t\t\t\tstep.kind === 'decision' &&\n\t\t\t\tnew Set(step.decisions).size !== step.decisions.length\n\t\t\t)\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Duplicate decision value in step: ${step.key}`,\n\t\t\t\t\tpath: ['steps', index, 'decisions'],\n\t\t\t\t})\n\t\t}\n\t\tfor (const [index, step] of descriptor.steps.entries()) {\n\t\t\tif (step.kind !== 'branch') continue\n\t\t\tif (descriptor.steps[index - 1]?.kind !== 'decision')\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Branch must immediately follow a decision: ${step.key}`,\n\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t})\n\t\t\tfor (const target of [step.approvedStepKey, step.rejectedStepKey]) {\n\t\t\t\tconst targetStep = stepsByKey.get(target)\n\t\t\t\tconst targetIndex = descriptor.steps.findIndex(\n\t\t\t\t\t(candidate) => candidate.key === target,\n\t\t\t\t)\n\t\t\t\tif (!targetStep)\n\t\t\t\t\tcontext.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `Unknown branch target: ${target}`,\n\t\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t\t})\n\t\t\t\telse if (\n\t\t\t\t\ttargetStep.kind !== 'mutation' &&\n\t\t\t\t\ttargetStep.kind !== 'action' &&\n\t\t\t\t\ttargetStep.kind !== 'notify'\n\t\t\t\t)\n\t\t\t\t\tcontext.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `Branch target must be a callback step: ${target}`,\n\t\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t\t})\n\t\t\t\telse if (targetIndex <= index)\n\t\t\t\t\tcontext.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `Branch target must be declared after its branch: ${target}`,\n\t\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t\t})\n\t\t\t\tbranchTargetOwners.set(\n\t\t\t\t\ttarget,\n\t\t\t\t\t(branchTargetOwners.get(target) ?? 0) + 1,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tfor (const [target, owners] of branchTargetOwners) {\n\t\t\tif (owners !== 1)\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Branch callback must belong to exactly one branch: ${target}`,\n\t\t\t\t\tpath: ['steps'],\n\t\t\t\t})\n\t\t}\n\t})\n\nexport const approvalRunDocument = z\n\t.object({\n\t\tscopeRef: approvalReference,\n\t\tresourceType: approvalName,\n\t\tresourceRef: approvalReference,\n\t\trequester: approvalActor,\n\t\tmetadata: approvalMetadata.optional(),\n\t\tworkflow: approvalWorkflowDescriptor,\n\t\tstate: approvalRunState,\n\t\tcurrentStepKey: approvalName.optional(),\n\t\texecutionFailedStepKey: approvalName.optional(),\n\t\texpiresAt: z.number().optional(),\n\t\tworkflowId: approvalReference.optional(),\n\t\tcreatedAt: z.number(),\n\t\tupdatedAt: z.number(),\n\t\tterminalAt: z.number().optional(),\n\t})\n\t.strict()\n\nexport const approvalDecisionDocument = z\n\t.object({\n\t\trunId: zid('approvalRuns'),\n\t\tstepKey: approvalName,\n\t\tactor: approvalActor,\n\t\tdecision: approvalDecision,\n\t\treason: approvalReason.optional(),\n\t\tdecidedAt: z.number(),\n\t})\n\t.strict()\n\nexport type ApprovalWorkflowDescriptor = z.infer<\n\ttypeof approvalWorkflowDescriptor\n>\nexport type ApprovalActor = z.infer<typeof approvalActor>\nexport type ApprovalMetadata = z.infer<typeof approvalMetadata>\nexport type ApprovalCallbackInput = Readonly<\n\tz.infer<typeof approvalCallbackInput>\n>\n"],"mappings":"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"}
1
+ {"version":3,"file":"validators.mjs","names":[],"sources":["../../../src/components/approvals/validators.ts"],"sourcesContent":["import { zid, zodToConvex } from 'convex-helpers/server/zod4'\nimport { z } from 'zod'\n\nimport {\n\tAPPROVAL_CALLBACK_KINDS,\n\tAPPROVAL_DECISIONS,\n\tAPPROVAL_DESCRIPTOR_SCHEMA_VERSION,\n\tAPPROVAL_QUORUM_KINDS,\n\tAPPROVAL_RUN_STATES,\n\tAPPROVAL_STEP_KINDS,\n\tMAX_APPROVAL_CAPABILITIES,\n\tMAX_APPROVAL_DECISIONS_PER_STEP,\n\tMAX_APPROVAL_METADATA_ENTRIES,\n\tMAX_APPROVAL_METADATA_KEY_LENGTH,\n\tMAX_APPROVAL_METADATA_VALUE_LENGTH,\n\tMAX_APPROVAL_NAME_LENGTH,\n\tMAX_APPROVAL_REASON_LENGTH,\n\tMAX_APPROVAL_REFERENCE_LENGTH,\n\tMAX_APPROVAL_WORKFLOW_STEPS,\n} from './constants'\n\nexport const approvalRunState = z.enum(APPROVAL_RUN_STATES)\nexport const approvalDecision = z.enum(APPROVAL_DECISIONS)\nexport const approvalStepKind = z.enum(APPROVAL_STEP_KINDS)\nexport const approvalCallbackKind = z.enum(APPROVAL_CALLBACK_KINDS)\nexport const approvalQuorumKind = z.enum(APPROVAL_QUORUM_KINDS)\n\nexport const approvalReference = z.string().trim().min(1).max(MAX_APPROVAL_REFERENCE_LENGTH)\n\nexport const approvalName = z\n\t.string()\n\t.trim()\n\t.min(1)\n\t.max(MAX_APPROVAL_NAME_LENGTH)\n\t.regex(/^[A-Za-z][A-Za-z0-9._-]*$/)\n\nexport const approvalReason = z.string().trim().min(1).max(MAX_APPROVAL_REASON_LENGTH)\n\nexport const approvalMetadata = z\n\t.record(\n\t\tz\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(MAX_APPROVAL_METADATA_KEY_LENGTH)\n\t\t\t.regex(/^(?![$_])[\\x20-\\x7E]+$/),\n\t\tz.string().max(MAX_APPROVAL_METADATA_VALUE_LENGTH),\n\t)\n\t.refine(\n\t\t(value) => Object.keys(value).length <= MAX_APPROVAL_METADATA_ENTRIES,\n\t\t`Metadata may contain at most ${MAX_APPROVAL_METADATA_ENTRIES} entries`,\n\t)\n\nconst approvalCallbackEvidence = z\n\t.object({\n\t\tstepKey: approvalName,\n\t\tactorRef: approvalReference,\n\t\tdecision: approvalDecision,\n\t\treason: approvalReason.optional(),\n\t\tdecidedAt: z.number(),\n\t})\n\t.strict()\n\nconst approvalCallbackTerminalEvidence = approvalCallbackEvidence.omit({\n\tstepKey: true,\n})\n\nexport const approvalCallbackInput = z\n\t.object({\n\t\trunId: approvalReference,\n\t\tscopeRef: approvalReference,\n\t\tresourceType: approvalName,\n\t\tresourceRef: approvalReference,\n\t\tmetadata: approvalMetadata.optional(),\n\t\tdecision: z\n\t\t\t.object({\n\t\t\t\tstepKey: approvalName,\n\t\t\t\toutcome: z.union([approvalDecision, z.literal('expired')]),\n\t\t\t\tevidence: z.array(approvalCallbackEvidence),\n\t\t\t\tterminalEvidence: approvalCallbackTerminalEvidence.optional(),\n\t\t\t})\n\t\t\t.strict()\n\t\t\t.optional(),\n\t})\n\t.strict()\n\n/** Convex argument validator for approval mutation, action, and notify callbacks. */\nexport const approvalCallbackArgs = zodToConvex(approvalCallbackInput)\n\nexport const approvalActor = z\n\t.object({\n\t\tactorRef: approvalReference,\n\t\tcapabilities: z.array(approvalName).max(MAX_APPROVAL_CAPABILITIES),\n\t\tmetadata: approvalMetadata.optional(),\n\t})\n\t.strict()\n\nexport const approvalMutationCallback = z\n\t.object({\n\t\tkind: z.literal('mutation'),\n\t\thandle: approvalReference,\n\t\tretry: z.boolean(),\n\t})\n\t.strict()\n\nexport const approvalActionCallback = z\n\t.object({\n\t\tkind: z.literal('action'),\n\t\thandle: approvalReference,\n\t\tretry: z.boolean(),\n\t})\n\t.strict()\n\nexport const approvalCallback = z.discriminatedUnion('kind', [\n\tapprovalMutationCallback,\n\tapprovalActionCallback,\n])\n\nexport const approvalQuorum = z\n\t.object({\n\t\tkind: approvalQuorumKind,\n\t\tapprovals: z.number().int().positive().max(MAX_APPROVAL_DECISIONS_PER_STEP),\n\t})\n\t.strict()\n\nexport const approvalCallbackStep = z.discriminatedUnion('kind', [\n\tz\n\t\t.object({\n\t\t\tkind: z.literal('mutation'),\n\t\t\tkey: approvalName,\n\t\t\tcallback: approvalMutationCallback,\n\t\t})\n\t\t.strict(),\n\tz\n\t\t.object({\n\t\t\tkind: z.literal('action'),\n\t\t\tkey: approvalName,\n\t\t\tcallback: approvalActionCallback,\n\t\t})\n\t\t.strict(),\n])\n\nexport const approvalNotifyStep = z\n\t.object({\n\t\tkind: z.literal('notify'),\n\t\tkey: approvalName,\n\t\tcallback: approvalActionCallback,\n\t})\n\t.strict()\n\nexport const approvalDecisionStep = z\n\t.object({\n\t\tkind: z.literal('decision'),\n\t\tkey: approvalName,\n\t\tdecisions: z.array(approvalDecision).min(1).max(MAX_APPROVAL_DECISIONS_PER_STEP),\n\t\tquorum: approvalQuorum,\n\t\tmakerChecker: z.boolean(),\n\t\texpiresAfterMs: z.number().int().positive().optional(),\n\t})\n\t.strict()\n\nexport const approvalBranchStep = z\n\t.object({\n\t\tkind: z.literal('branch'),\n\t\tkey: approvalName,\n\t\tapprovedStepKey: approvalName,\n\t\trejectedStepKey: approvalName,\n\t})\n\t.strict()\n\nexport const approvalWorkflowStep = z.discriminatedUnion('kind', [\n\tapprovalCallbackStep,\n\tapprovalNotifyStep,\n\tapprovalDecisionStep,\n\tapprovalBranchStep,\n])\n\nexport const approvalWorkflowDescriptor = z\n\t.object({\n\t\tschemaVersion: z.literal(APPROVAL_DESCRIPTOR_SCHEMA_VERSION),\n\t\tcompatibilityKey: approvalName,\n\t\tname: approvalName,\n\t\tsteps: z.array(approvalWorkflowStep).min(1).max(MAX_APPROVAL_WORKFLOW_STEPS),\n\t})\n\t.strict()\n\t.superRefine((descriptor, context) => {\n\t\tconst keys = new Set<string>()\n\t\tconst stepsByKey = new Map(descriptor.steps.map((step) => [step.key, step] as const))\n\t\tconst branchTargetOwners = new Map<string, number>()\n\t\tfor (const [index, step] of descriptor.steps.entries()) {\n\t\t\tif (keys.has(step.key))\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Duplicate workflow step key: ${step.key}`,\n\t\t\t\t\tpath: ['steps', index, 'key'],\n\t\t\t\t})\n\t\t\tkeys.add(step.key)\n\t\t\tif (step.kind === 'decision' && new Set(step.decisions).size !== step.decisions.length)\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Duplicate decision value in step: ${step.key}`,\n\t\t\t\t\tpath: ['steps', index, 'decisions'],\n\t\t\t\t})\n\t\t}\n\t\tfor (const [index, step] of descriptor.steps.entries()) {\n\t\t\tif (step.kind !== 'branch') continue\n\t\t\tif (descriptor.steps[index - 1]?.kind !== 'decision')\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Branch must immediately follow a decision: ${step.key}`,\n\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t})\n\t\t\tfor (const target of [step.approvedStepKey, step.rejectedStepKey]) {\n\t\t\t\tconst targetStep = stepsByKey.get(target)\n\t\t\t\tconst targetIndex = descriptor.steps.findIndex((candidate) => candidate.key === target)\n\t\t\t\tif (!targetStep)\n\t\t\t\t\tcontext.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `Unknown branch target: ${target}`,\n\t\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t\t})\n\t\t\t\telse if (\n\t\t\t\t\ttargetStep.kind !== 'mutation' &&\n\t\t\t\t\ttargetStep.kind !== 'action' &&\n\t\t\t\t\ttargetStep.kind !== 'notify'\n\t\t\t\t)\n\t\t\t\t\tcontext.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `Branch target must be a callback step: ${target}`,\n\t\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t\t})\n\t\t\t\telse if (targetIndex <= index)\n\t\t\t\t\tcontext.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `Branch target must be declared after its branch: ${target}`,\n\t\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t\t})\n\t\t\t\tbranchTargetOwners.set(target, (branchTargetOwners.get(target) ?? 0) + 1)\n\t\t\t}\n\t\t}\n\t\tfor (const [target, owners] of branchTargetOwners) {\n\t\t\tif (owners !== 1)\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Branch callback must belong to exactly one branch: ${target}`,\n\t\t\t\t\tpath: ['steps'],\n\t\t\t\t})\n\t\t}\n\t})\n\nexport const approvalRunDocument = z\n\t.object({\n\t\tscopeRef: approvalReference,\n\t\tresourceType: approvalName,\n\t\tresourceRef: approvalReference,\n\t\trequester: approvalActor,\n\t\tmetadata: approvalMetadata.optional(),\n\t\tworkflow: approvalWorkflowDescriptor,\n\t\tstate: approvalRunState,\n\t\tcurrentStepKey: approvalName.optional(),\n\t\texecutionFailedStepKey: approvalName.optional(),\n\t\texpiresAt: z.number().optional(),\n\t\tworkflowId: approvalReference.optional(),\n\t\tcreatedAt: z.number(),\n\t\tupdatedAt: z.number(),\n\t\tterminalAt: z.number().optional(),\n\t})\n\t.strict()\n\nexport const approvalDecisionDocument = z\n\t.object({\n\t\trunId: zid('approvalRuns'),\n\t\tstepKey: approvalName,\n\t\tactor: approvalActor,\n\t\tdecision: approvalDecision,\n\t\treason: approvalReason.optional(),\n\t\tdecidedAt: z.number(),\n\t})\n\t.strict()\n\nexport type ApprovalWorkflowDescriptor = z.infer<typeof approvalWorkflowDescriptor>\nexport type ApprovalActor = z.infer<typeof approvalActor>\nexport type ApprovalMetadata = z.infer<typeof approvalMetadata>\nexport type ApprovalCallbackInput = Readonly<z.infer<typeof approvalCallbackInput>>\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{components as e,internal as t}from"./_generated/api.mjs";import{APPROVAL_DECISIONS as n,APPROVAL_RUN_STATES as r}from"./constants.mjs";import{approvalDecisionEventName as i,callbackReference as a,resolveWorkflowStepIndex as o}from"./workflow_steps.mjs";import{v as s}from"convex/values";import{WorkflowManager as c}from"@convex-dev/workflow";const l=new c(e.workflow),u=l.define({args:{runId:s.id(`approvalRuns`)},returns:s.null(),handler:async(e,a)=>{let c;try{let l=await e.runQuery(t.requests.getForWorkflow,{runId:a.runId},{name:`loadLinkedApproval`});if(!l)throw Error(`Approval request not found`);if(l.workflowId!==e.workflowId)throw Error(`Approval workflow linkage is incomplete`);let u=d(l),p=l.workflow.steps.map(e=>e.key),m=new Set(l.workflow.steps.flatMap(e=>e.kind===`branch`?[e.approvedStepKey,e.rejectedStepKey]:[])),h=null,g=null,_,v=0;for(;v<l.workflow.steps.length;){let a=l.workflow.steps[v];if(a.kind===`mutation`||a.kind===`action`||a.kind===`notify`){if(m.has(a.key)){v+=1;continue}c=a.key,await f(e,a,u),c=void 0,v+=1;continue}if(a.kind===`decision`){await e.runMutation(t.decisions.enter,{runId:l._id,stepKey:a.key,workflowId:e.workflowId,...a.expiresAfterMs===void 0?{}:{expiresAfterMs:a.expiresAfterMs}},{name:`enter:${a.key}`});let o=await e.awaitEvent({name:i(l._id,a.key),validator:s.object({outcome:s.union(s.literal(n[0]),s.literal(n[1]),s.literal(r[3])),terminalEvidence:s.optional(s.object({actorRef:s.string(),decision:s.union(s.literal(n[0]),s.literal(n[1])),reason:s.optional(s.string()),decidedAt:s.number()}))})});if(h=o.outcome,_=o.terminalEvidence,g=a.key,h!==`approved`&&l.workflow.steps[v+1]?.kind!==`branch`)break;v+=1;continue}if(!h)throw Error(`Branch requires a preceding decision step`);let d=h===`approved`?a.approvedStepKey:a.rejectedStepKey,y=o(p,d),b=l.workflow.steps[y];if(b.kind!==`mutation`&&b.kind!==`action`&&b.kind!==`notify`)throw Error(`Approval branch target must be a callback step`);let x=await e.runQuery(t.decisions.listForWorkflow,{runId:l._id,stepKey:g??a.key},{name:`evidence:${a.key}`});if(c=b.key,await f(e,b,{...u,decision:{stepKey:g??a.key,outcome:h,evidence:x,..._===void 0?{}:{terminalEvidence:_}}}),c=void 0,h===`rejected`||h===`expired`)break;v+=1}return await e.runMutation(t.requests.recordExecutionTerminal,{runId:l._id,outcome:`completed`},{name:`recordExecutionCompleted`}),null}catch(n){throw await e.runMutation(t.requests.recordExecutionTerminal,{runId:a.runId,outcome:`failed`,...c===void 0?{}:{failedStepKey:c}},{name:`recordExecutionFailed`}),n}}});function d(e){return{runId:e._id,scopeRef:e.scopeRef,resourceType:e.resourceType,resourceRef:e.resourceRef,...e.metadata===void 0?{}:{metadata:e.metadata}}}async function f(e,t,n){if(t.callback.kind===`mutation`){await e.runMutation(a(t.callback.handle),n,{name:t.key});return}await e.runAction(a(t.callback.handle),n,{name:t.key,retry:t.callback.retry})}export{l as approvalWorkflow,u as run};
1
+ import{components as e,internal as t}from"./_generated/api.mjs";import{APPROVAL_DECISIONS as n,APPROVAL_RUN_STATES as r}from"./constants.mjs";import{approvalDecisionEventName as i,callbackReference as a,resolveWorkflowStepIndex as o}from"./workflow_steps.mjs";import{v as s}from"convex/values";import{WorkflowManager as c}from"@convex-dev/workflow";const l=new c(e.workflow),u=l.define({args:{runId:s.id(`approvalRuns`)},returns:s.null(),handler:async(e,a)=>{let c;try{let l=await e.runQuery(t.requests.getForWorkflow,{runId:a.runId},{name:`loadLinkedApproval`});if(!l)throw Error(`Approval request not found`);if(l.workflowId!==e.workflowId)throw Error(`Approval workflow linkage is incomplete`);let u=d(l),p=l.workflow.steps.map(e=>e.key),m=new Set(l.workflow.steps.flatMap(e=>e.kind===`branch`?[e.approvedStepKey,e.rejectedStepKey]:[])),h=null,g=null,_,v=0;for(;v<l.workflow.steps.length;){let a=l.workflow.steps[v];if(a.kind===`mutation`||a.kind===`action`||a.kind===`notify`){if(m.has(a.key)){v+=1;continue}c=a.key,await f(e,a,u),c=void 0,v+=1;continue}if(a.kind===`decision`){await e.runMutation(t.decisions.enter,{runId:l._id,stepKey:a.key,workflowId:e.workflowId,expiresAfterMs:a.expiresAfterMs},{name:`enter:${a.key}`});let o=await e.awaitEvent({name:i(l._id,a.key),validator:s.object({outcome:s.union(s.literal(n[0]),s.literal(n[1]),s.literal(r[3])),terminalEvidence:s.optional(s.object({actorRef:s.string(),decision:s.union(s.literal(n[0]),s.literal(n[1])),reason:s.optional(s.string()),decidedAt:s.number()}))})});if(h=o.outcome,_=o.terminalEvidence,g=a.key,h!==`approved`&&l.workflow.steps[v+1]?.kind!==`branch`)break;v+=1;continue}if(!h)throw Error(`Branch requires a preceding decision step`);let d=h===`approved`?a.approvedStepKey:a.rejectedStepKey,y=o(p,d),b=l.workflow.steps[y];if(b.kind!==`mutation`&&b.kind!==`action`&&b.kind!==`notify`)throw Error(`Approval branch target must be a callback step`);let x=await e.runQuery(t.decisions.listForWorkflow,{runId:l._id,stepKey:g??a.key},{name:`evidence:${a.key}`});if(c=b.key,await f(e,b,{...u,decision:{stepKey:g??a.key,outcome:h,evidence:x,terminalEvidence:_}}),c=void 0,h===`rejected`||h===`expired`)break;v+=1}return await e.runMutation(t.requests.recordExecutionTerminal,{runId:l._id,outcome:`completed`},{name:`recordExecutionCompleted`}),null}catch(n){throw await e.runMutation(t.requests.recordExecutionTerminal,{runId:a.runId,outcome:`failed`,failedStepKey:c},{name:`recordExecutionFailed`}),n}}});function d(e){return{runId:e._id,scopeRef:e.scopeRef,resourceType:e.resourceType,resourceRef:e.resourceRef,metadata:e.metadata}}async function f(e,t,n){if(t.callback.kind===`mutation`){await e.runMutation(a(t.callback.handle),n,{name:t.key});return}await e.runAction(a(t.callback.handle),n,{name:t.key,retry:t.callback.retry})}export{l as approvalWorkflow,u as run};
2
2
  //# sourceMappingURL=workflow.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"workflow.mjs","names":[],"sources":["../../../src/components/approvals/workflow.ts"],"sourcesContent":["import { WorkflowManager, type WorkflowCtx } from '@convex-dev/workflow'\nimport { v } from 'convex/values'\n\nimport { components, internal } from './_generated/api'\nimport type { Doc } from './_generated/dataModel'\nimport { APPROVAL_DECISIONS, APPROVAL_RUN_STATES } from './constants'\nimport {\n\tapprovalDecisionEventName,\n\tcallbackReference,\n\tresolveWorkflowStepIndex,\n\ttype ApprovalCallbackInput,\n} from './workflow_steps'\n\nexport const approvalWorkflow = new WorkflowManager(components.workflow)\n\nexport const run = approvalWorkflow.define({\n\targs: { runId: v.id('approvalRuns') },\n\treturns: v.null(),\n\thandler: async (step, input) => {\n\t\tlet failedStepKey: string | undefined\n\t\ttry {\n\t\t\tconst run: Doc<'approvalRuns'> | null = await step.runQuery(\n\t\t\t\tinternal.requests.getForWorkflow,\n\t\t\t\t{ runId: input.runId },\n\t\t\t\t{ name: 'loadLinkedApproval' },\n\t\t\t)\n\t\t\tif (!run) throw new Error('Approval request not found')\n\t\t\tif (run.workflowId !== step.workflowId)\n\t\t\t\tthrow new Error('Approval workflow linkage is incomplete')\n\n\t\t\tconst callbackInput = buildCallbackInput(run)\n\t\t\tconst stepKeys = run.workflow.steps.map(\n\t\t\t\t(workflowStep) => workflowStep.key,\n\t\t\t)\n\t\t\tconst branchTargets = new Set(\n\t\t\t\trun.workflow.steps.flatMap((workflowStep) =>\n\t\t\t\t\tworkflowStep.kind === 'branch'\n\t\t\t\t\t\t? [workflowStep.approvedStepKey, workflowStep.rejectedStepKey]\n\t\t\t\t\t\t: [],\n\t\t\t\t),\n\t\t\t)\n\t\t\tlet lastDecision: 'approved' | 'rejected' | 'expired' | null = null\n\t\t\tlet lastDecisionStepKey: string | null = null\n\t\t\tlet terminalEvidence:\n\t\t\t\t| {\n\t\t\t\t\t\tactorRef: string\n\t\t\t\t\t\tdecision: 'approved' | 'rejected'\n\t\t\t\t\t\treason?: string\n\t\t\t\t\t\tdecidedAt: number\n\t\t\t\t }\n\t\t\t\t| undefined\n\t\t\tlet index = 0\n\t\t\twhile (index < run.workflow.steps.length) {\n\t\t\t\tconst workflowStep = run.workflow.steps[index]\n\t\t\t\tif (\n\t\t\t\t\tworkflowStep.kind === 'mutation' ||\n\t\t\t\t\tworkflowStep.kind === 'action' ||\n\t\t\t\t\tworkflowStep.kind === 'notify'\n\t\t\t\t) {\n\t\t\t\t\tif (branchTargets.has(workflowStep.key)) {\n\t\t\t\t\t\tindex += 1\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tfailedStepKey = workflowStep.key\n\t\t\t\t\tawait executeCallback(step, workflowStep, callbackInput)\n\t\t\t\t\tfailedStepKey = undefined\n\t\t\t\t\tindex += 1\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (workflowStep.kind === 'decision') {\n\t\t\t\t\tawait step.runMutation(\n\t\t\t\t\t\tinternal.decisions.enter,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\trunId: run._id,\n\t\t\t\t\t\t\tstepKey: workflowStep.key,\n\t\t\t\t\t\t\tworkflowId: step.workflowId,\n\t\t\t\t\t\t\t...(workflowStep.expiresAfterMs === undefined\n\t\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t\t: { expiresAfterMs: workflowStep.expiresAfterMs }),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{ name: `enter:${workflowStep.key}` },\n\t\t\t\t\t)\n\t\t\t\t\tconst event = await step.awaitEvent({\n\t\t\t\t\t\tname: approvalDecisionEventName(run._id, workflowStep.key),\n\t\t\t\t\t\tvalidator: v.object({\n\t\t\t\t\t\t\toutcome: v.union(\n\t\t\t\t\t\t\t\tv.literal(APPROVAL_DECISIONS[0]),\n\t\t\t\t\t\t\t\tv.literal(APPROVAL_DECISIONS[1]),\n\t\t\t\t\t\t\t\tv.literal(APPROVAL_RUN_STATES[3]),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tterminalEvidence: v.optional(\n\t\t\t\t\t\t\t\tv.object({\n\t\t\t\t\t\t\t\t\tactorRef: v.string(),\n\t\t\t\t\t\t\t\t\tdecision: v.union(\n\t\t\t\t\t\t\t\t\t\tv.literal(APPROVAL_DECISIONS[0]),\n\t\t\t\t\t\t\t\t\t\tv.literal(APPROVAL_DECISIONS[1]),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\treason: v.optional(v.string()),\n\t\t\t\t\t\t\t\t\tdecidedAt: v.number(),\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t}),\n\t\t\t\t\t})\n\t\t\t\t\tlastDecision = event.outcome\n\t\t\t\t\tterminalEvidence = event.terminalEvidence\n\t\t\t\t\tlastDecisionStepKey = workflowStep.key\n\t\t\t\t\tif (\n\t\t\t\t\t\tlastDecision !== 'approved' &&\n\t\t\t\t\t\trun.workflow.steps[index + 1]?.kind !== 'branch'\n\t\t\t\t\t)\n\t\t\t\t\t\tbreak\n\t\t\t\t\tindex += 1\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (!lastDecision)\n\t\t\t\t\tthrow new Error('Branch requires a preceding decision step')\n\t\t\t\tconst targetKey =\n\t\t\t\t\tlastDecision === 'approved'\n\t\t\t\t\t\t? workflowStep.approvedStepKey\n\t\t\t\t\t\t: workflowStep.rejectedStepKey\n\t\t\t\tconst targetIndex = resolveWorkflowStepIndex(stepKeys, targetKey)\n\t\t\t\tconst target = run.workflow.steps[targetIndex]\n\t\t\t\tif (\n\t\t\t\t\ttarget.kind !== 'mutation' &&\n\t\t\t\t\ttarget.kind !== 'action' &&\n\t\t\t\t\ttarget.kind !== 'notify'\n\t\t\t\t)\n\t\t\t\t\tthrow new Error('Approval branch target must be a callback step')\n\t\t\t\tconst evidence = await step.runQuery(\n\t\t\t\t\tinternal.decisions.listForWorkflow,\n\t\t\t\t\t{ runId: run._id, stepKey: lastDecisionStepKey ?? workflowStep.key },\n\t\t\t\t\t{ name: `evidence:${workflowStep.key}` },\n\t\t\t\t)\n\t\t\t\tfailedStepKey = target.key\n\t\t\t\tawait executeCallback(step, target, {\n\t\t\t\t\t...callbackInput,\n\t\t\t\t\tdecision: {\n\t\t\t\t\t\tstepKey: lastDecisionStepKey ?? workflowStep.key,\n\t\t\t\t\t\toutcome: lastDecision,\n\t\t\t\t\t\tevidence,\n\t\t\t\t\t\t...(terminalEvidence === undefined ? {} : { terminalEvidence }),\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tfailedStepKey = undefined\n\t\t\t\tif (lastDecision === 'rejected' || lastDecision === 'expired') break\n\t\t\t\tindex += 1\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tawait step.runMutation(\n\t\t\t\tinternal.requests.recordExecutionTerminal,\n\t\t\t\t{ runId: run._id, outcome: 'completed' },\n\t\t\t\t{ name: 'recordExecutionCompleted' },\n\t\t\t)\n\t\t\treturn null\n\t\t} catch (error) {\n\t\t\tawait step.runMutation(\n\t\t\t\tinternal.requests.recordExecutionTerminal,\n\t\t\t\t{\n\t\t\t\t\trunId: input.runId,\n\t\t\t\t\toutcome: 'failed',\n\t\t\t\t\t...(failedStepKey === undefined ? {} : { failedStepKey }),\n\t\t\t\t},\n\t\t\t\t{ name: 'recordExecutionFailed' },\n\t\t\t)\n\t\t\tthrow error\n\t\t}\n\t},\n})\n\ntype CallbackStep = Extract<\n\tDoc<'approvalRuns'>['workflow']['steps'][number],\n\t{ kind: 'mutation' | 'action' | 'notify' }\n>\n\nfunction buildCallbackInput(run: Doc<'approvalRuns'>): ApprovalCallbackInput {\n\treturn {\n\t\trunId: run._id,\n\t\tscopeRef: run.scopeRef,\n\t\tresourceType: run.resourceType,\n\t\tresourceRef: run.resourceRef,\n\t\t...(run.metadata === undefined ? {} : { metadata: run.metadata }),\n\t}\n}\n\nasync function executeCallback(\n\tstep: WorkflowCtx,\n\tworkflowStep: CallbackStep,\n\tinput: ApprovalCallbackInput,\n): Promise<void> {\n\tif (workflowStep.callback.kind === 'mutation') {\n\t\tawait step.runMutation(\n\t\t\tcallbackReference<'mutation'>(workflowStep.callback.handle),\n\t\t\tinput,\n\t\t\t{ name: workflowStep.key },\n\t\t)\n\t\treturn\n\t}\n\tawait step.runAction(\n\t\tcallbackReference<'action'>(workflowStep.callback.handle),\n\t\tinput,\n\t\t{ name: workflowStep.key, retry: workflowStep.callback.retry },\n\t)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"workflow.mjs","names":[],"sources":["../../../src/components/approvals/workflow.ts"],"sourcesContent":["import type { ApprovalCallbackInput } from './validators'\nimport { WorkflowManager, type WorkflowCtx } from '@convex-dev/workflow'\nimport { v } from 'convex/values'\n\nimport { components, internal } from './_generated/api'\nimport type { Doc } from './_generated/dataModel'\nimport { APPROVAL_DECISIONS, APPROVAL_RUN_STATES } from './constants'\nimport {\n\tapprovalDecisionEventName,\n\tcallbackReference,\n\tresolveWorkflowStepIndex,\n} from './workflow_steps'\n\nexport const approvalWorkflow = new WorkflowManager(components.workflow)\n\nexport const run = approvalWorkflow.define({\n\targs: { runId: v.id('approvalRuns') },\n\treturns: v.null(),\n\thandler: async (step, input) => {\n\t\tlet failedStepKey: string | undefined\n\t\ttry {\n\t\t\tconst run: Doc<'approvalRuns'> | null = await step.runQuery(\n\t\t\t\tinternal.requests.getForWorkflow,\n\t\t\t\t{ runId: input.runId },\n\t\t\t\t{ name: 'loadLinkedApproval' },\n\t\t\t)\n\t\t\tif (!run) throw new Error('Approval request not found')\n\t\t\tif (run.workflowId !== step.workflowId)\n\t\t\t\tthrow new Error('Approval workflow linkage is incomplete')\n\n\t\t\tconst callbackInput = buildCallbackInput(run)\n\t\t\tconst stepKeys = run.workflow.steps.map((workflowStep) => workflowStep.key)\n\t\t\tconst branchTargets = new Set(\n\t\t\t\trun.workflow.steps.flatMap((workflowStep) =>\n\t\t\t\t\tworkflowStep.kind === 'branch'\n\t\t\t\t\t\t? [workflowStep.approvedStepKey, workflowStep.rejectedStepKey]\n\t\t\t\t\t\t: [],\n\t\t\t\t),\n\t\t\t)\n\t\t\tlet lastDecision: 'approved' | 'rejected' | 'expired' | null = null\n\t\t\tlet lastDecisionStepKey: string | null = null\n\t\t\tlet terminalEvidence:\n\t\t\t\t| {\n\t\t\t\t\t\tactorRef: string\n\t\t\t\t\t\tdecision: 'approved' | 'rejected'\n\t\t\t\t\t\treason?: string\n\t\t\t\t\t\tdecidedAt: number\n\t\t\t\t }\n\t\t\t\t| undefined\n\t\t\tlet index = 0\n\t\t\twhile (index < run.workflow.steps.length) {\n\t\t\t\tconst workflowStep = run.workflow.steps[index]\n\t\t\t\tif (\n\t\t\t\t\tworkflowStep.kind === 'mutation' ||\n\t\t\t\t\tworkflowStep.kind === 'action' ||\n\t\t\t\t\tworkflowStep.kind === 'notify'\n\t\t\t\t) {\n\t\t\t\t\tif (branchTargets.has(workflowStep.key)) {\n\t\t\t\t\t\tindex += 1\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tfailedStepKey = workflowStep.key\n\t\t\t\t\tawait executeCallback(step, workflowStep, callbackInput)\n\t\t\t\t\tfailedStepKey = undefined\n\t\t\t\t\tindex += 1\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (workflowStep.kind === 'decision') {\n\t\t\t\t\tawait step.runMutation(\n\t\t\t\t\t\tinternal.decisions.enter,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\trunId: run._id,\n\t\t\t\t\t\t\tstepKey: workflowStep.key,\n\t\t\t\t\t\t\tworkflowId: step.workflowId,\n\t\t\t\t\t\t\texpiresAfterMs: workflowStep.expiresAfterMs,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{ name: `enter:${workflowStep.key}` },\n\t\t\t\t\t)\n\t\t\t\t\tconst event = await step.awaitEvent({\n\t\t\t\t\t\tname: approvalDecisionEventName(run._id, workflowStep.key),\n\t\t\t\t\t\tvalidator: v.object({\n\t\t\t\t\t\t\toutcome: v.union(\n\t\t\t\t\t\t\t\tv.literal(APPROVAL_DECISIONS[0]),\n\t\t\t\t\t\t\t\tv.literal(APPROVAL_DECISIONS[1]),\n\t\t\t\t\t\t\t\tv.literal(APPROVAL_RUN_STATES[3]),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tterminalEvidence: v.optional(\n\t\t\t\t\t\t\t\tv.object({\n\t\t\t\t\t\t\t\t\tactorRef: v.string(),\n\t\t\t\t\t\t\t\t\tdecision: v.union(\n\t\t\t\t\t\t\t\t\t\tv.literal(APPROVAL_DECISIONS[0]),\n\t\t\t\t\t\t\t\t\t\tv.literal(APPROVAL_DECISIONS[1]),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\treason: v.optional(v.string()),\n\t\t\t\t\t\t\t\t\tdecidedAt: v.number(),\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t}),\n\t\t\t\t\t})\n\t\t\t\t\tlastDecision = event.outcome\n\t\t\t\t\tterminalEvidence = event.terminalEvidence\n\t\t\t\t\tlastDecisionStepKey = workflowStep.key\n\t\t\t\t\tif (lastDecision !== 'approved' && run.workflow.steps[index + 1]?.kind !== 'branch') break\n\t\t\t\t\tindex += 1\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (!lastDecision) throw new Error('Branch requires a preceding decision step')\n\t\t\t\tconst targetKey =\n\t\t\t\t\tlastDecision === 'approved' ? workflowStep.approvedStepKey : workflowStep.rejectedStepKey\n\t\t\t\tconst targetIndex = resolveWorkflowStepIndex(stepKeys, targetKey)\n\t\t\t\tconst target = run.workflow.steps[targetIndex]\n\t\t\t\tif (target.kind !== 'mutation' && target.kind !== 'action' && target.kind !== 'notify')\n\t\t\t\t\tthrow new Error('Approval branch target must be a callback step')\n\t\t\t\tconst evidence = await step.runQuery(\n\t\t\t\t\tinternal.decisions.listForWorkflow,\n\t\t\t\t\t{ runId: run._id, stepKey: lastDecisionStepKey ?? workflowStep.key },\n\t\t\t\t\t{ name: `evidence:${workflowStep.key}` },\n\t\t\t\t)\n\t\t\t\tfailedStepKey = target.key\n\t\t\t\tawait executeCallback(step, target, {\n\t\t\t\t\t...callbackInput,\n\t\t\t\t\tdecision: {\n\t\t\t\t\t\tstepKey: lastDecisionStepKey ?? workflowStep.key,\n\t\t\t\t\t\toutcome: lastDecision,\n\t\t\t\t\t\tevidence,\n\t\t\t\t\t\tterminalEvidence: terminalEvidence,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tfailedStepKey = undefined\n\t\t\t\tif (lastDecision === 'rejected' || lastDecision === 'expired') break\n\t\t\t\tindex += 1\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tawait step.runMutation(\n\t\t\t\tinternal.requests.recordExecutionTerminal,\n\t\t\t\t{ runId: run._id, outcome: 'completed' },\n\t\t\t\t{ name: 'recordExecutionCompleted' },\n\t\t\t)\n\t\t\treturn null\n\t\t} catch (error) {\n\t\t\tawait step.runMutation(\n\t\t\t\tinternal.requests.recordExecutionTerminal,\n\t\t\t\t{\n\t\t\t\t\trunId: input.runId,\n\t\t\t\t\toutcome: 'failed',\n\t\t\t\t\tfailedStepKey: failedStepKey,\n\t\t\t\t},\n\t\t\t\t{ name: 'recordExecutionFailed' },\n\t\t\t)\n\t\t\tthrow error\n\t\t}\n\t},\n})\n\ntype CallbackStep = Extract<\n\tDoc<'approvalRuns'>['workflow']['steps'][number],\n\t{ kind: 'mutation' | 'action' | 'notify' }\n>\n\nfunction buildCallbackInput(run: Doc<'approvalRuns'>): ApprovalCallbackInput {\n\treturn {\n\t\trunId: run._id,\n\t\tscopeRef: run.scopeRef,\n\t\tresourceType: run.resourceType,\n\t\tresourceRef: run.resourceRef,\n\t\tmetadata: run.metadata,\n\t}\n}\n\nasync function executeCallback(\n\tstep: WorkflowCtx,\n\tworkflowStep: CallbackStep,\n\tinput: ApprovalCallbackInput,\n): Promise<void> {\n\tif (workflowStep.callback.kind === 'mutation') {\n\t\tawait step.runMutation(callbackReference<'mutation'>(workflowStep.callback.handle), input, {\n\t\t\tname: workflowStep.key,\n\t\t})\n\t\treturn\n\t}\n\tawait step.runAction(callbackReference<'action'>(workflowStep.callback.handle), input, {\n\t\tname: workflowStep.key,\n\t\tretry: workflowStep.callback.retry,\n\t})\n}\n"],"mappings":"6VAaA,MAAa,EAAmB,IAAI,EAAgB,EAAW,QAAQ,EAE1D,EAAM,EAAiB,OAAO,CAC1C,KAAM,CAAE,MAAO,EAAE,GAAG,cAAc,CAAE,EACpC,QAAS,EAAE,KAAK,EAChB,QAAS,MAAO,EAAM,IAAU,CAC/B,IAAI,EACJ,GAAI,CACH,IAAM,EAAkC,MAAM,EAAK,SAClD,EAAS,SAAS,eAClB,CAAE,MAAO,EAAM,KAAM,EACrB,CAAE,KAAM,oBAAqB,CAC9B,EACA,GAAI,CAAC,EAAK,MAAU,MAAM,4BAA4B,EACtD,GAAI,EAAI,aAAe,EAAK,WAC3B,MAAU,MAAM,yCAAyC,EAE1D,IAAM,EAAgB,EAAmB,CAAG,EACtC,EAAW,EAAI,SAAS,MAAM,IAAK,GAAiB,EAAa,GAAG,EACpE,EAAgB,IAAI,IACzB,EAAI,SAAS,MAAM,QAAS,GAC3B,EAAa,OAAS,SACnB,CAAC,EAAa,gBAAiB,EAAa,eAAe,EAC3D,CAAC,CACL,CACD,EACI,EAA2D,KAC3D,EAAqC,KACrC,EAQA,EAAQ,EACZ,KAAO,EAAQ,EAAI,SAAS,MAAM,QAAQ,CACzC,IAAM,EAAe,EAAI,SAAS,MAAM,GACxC,GACC,EAAa,OAAS,YACtB,EAAa,OAAS,UACtB,EAAa,OAAS,SACrB,CACD,GAAI,EAAc,IAAI,EAAa,GAAG,EAAG,CACxC,GAAS,EACT,QACD,CACA,EAAgB,EAAa,IAC7B,MAAM,EAAgB,EAAM,EAAc,CAAa,EACvD,EAAgB,IAAA,GAChB,GAAS,EACT,QACD,CACA,GAAI,EAAa,OAAS,WAAY,CACrC,MAAM,EAAK,YACV,EAAS,UAAU,MACnB,CACC,MAAO,EAAI,IACX,QAAS,EAAa,IACtB,WAAY,EAAK,WACjB,eAAgB,EAAa,cAC9B,EACA,CAAE,KAAM,SAAS,EAAa,KAAM,CACrC,EACA,IAAM,EAAQ,MAAM,EAAK,WAAW,CACnC,KAAM,EAA0B,EAAI,IAAK,EAAa,GAAG,EACzD,UAAW,EAAE,OAAO,CACnB,QAAS,EAAE,MACV,EAAE,QAAQ,EAAmB,EAAE,EAC/B,EAAE,QAAQ,EAAmB,EAAE,EAC/B,EAAE,QAAQ,EAAoB,EAAE,CACjC,EACA,iBAAkB,EAAE,SACnB,EAAE,OAAO,CACR,SAAU,EAAE,OAAO,EACnB,SAAU,EAAE,MACX,EAAE,QAAQ,EAAmB,EAAE,EAC/B,EAAE,QAAQ,EAAmB,EAAE,CAChC,EACA,OAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,EAC7B,UAAW,EAAE,OAAO,CACrB,CAAC,CACF,CACD,CAAC,CACF,CAAC,EAID,GAHA,EAAe,EAAM,QACrB,EAAmB,EAAM,iBACzB,EAAsB,EAAa,IAC/B,IAAiB,YAAc,EAAI,SAAS,MAAM,EAAQ,EAAE,EAAE,OAAS,SAAU,MACrF,GAAS,EACT,QACD,CACA,GAAI,CAAC,EAAc,MAAU,MAAM,2CAA2C,EAC9E,IAAM,EACL,IAAiB,WAAa,EAAa,gBAAkB,EAAa,gBACrE,EAAc,EAAyB,EAAU,CAAS,EAC1D,EAAS,EAAI,SAAS,MAAM,GAClC,GAAI,EAAO,OAAS,YAAc,EAAO,OAAS,UAAY,EAAO,OAAS,SAC7E,MAAU,MAAM,gDAAgD,EACjE,IAAM,EAAW,MAAM,EAAK,SAC3B,EAAS,UAAU,gBACnB,CAAE,MAAO,EAAI,IAAK,QAAS,GAAuB,EAAa,GAAI,EACnE,CAAE,KAAM,YAAY,EAAa,KAAM,CACxC,EAYA,GAXA,EAAgB,EAAO,IACvB,MAAM,EAAgB,EAAM,EAAQ,CACnC,GAAG,EACH,SAAU,CACT,QAAS,GAAuB,EAAa,IAC7C,QAAS,EACT,WACkB,kBACnB,CACD,CAAC,EACD,EAAgB,IAAA,GACZ,IAAiB,YAAc,IAAiB,UAAW,MAC/D,GAAS,CAEV,CAOA,OALA,MAAM,EAAK,YACV,EAAS,SAAS,wBAClB,CAAE,MAAO,EAAI,IAAK,QAAS,WAAY,EACvC,CAAE,KAAM,0BAA2B,CACpC,EACO,IACR,OAAS,EAAO,CAUf,MATA,MAAM,EAAK,YACV,EAAS,SAAS,wBAClB,CACC,MAAO,EAAM,MACb,QAAS,SACM,eAChB,EACA,CAAE,KAAM,uBAAwB,CACjC,EACM,CACP,CACD,CACD,CAAC,EAOD,SAAS,EAAmB,EAAiD,CAC5E,MAAO,CACN,MAAO,EAAI,IACX,SAAU,EAAI,SACd,aAAc,EAAI,aAClB,YAAa,EAAI,YACjB,SAAU,EAAI,QACf,CACD,CAEA,eAAe,EACd,EACA,EACA,EACgB,CAChB,GAAI,EAAa,SAAS,OAAS,WAAY,CAC9C,MAAM,EAAK,YAAY,EAA8B,EAAa,SAAS,MAAM,EAAG,EAAO,CAC1F,KAAM,EAAa,GACpB,CAAC,EACD,MACD,CACA,MAAM,EAAK,UAAU,EAA4B,EAAa,SAAS,MAAM,EAAG,EAAO,CACtF,KAAM,EAAa,IACnB,MAAO,EAAa,SAAS,KAC9B,CAAC,CACF"}
@@ -1,8 +1,8 @@
1
- import { ApprovalDecision } from "./constants.mjs";
2
1
  import { ApprovalCallbackInput, ApprovalWorkflowDescriptor } from "./validators.mjs";
2
+ import { ApprovalDecision } from "./constants.mjs";
3
3
  import { FunctionReference, FunctionType } from "convex/server";
4
4
  declare namespace workflow_steps_d_exports {
5
- export { ApprovalActionStep, ApprovalBranchStep, ApprovalCallbackInput, ApprovalDecisionEvidence, ApprovalDecisionStep, ApprovalDefinition, ApprovalMutationStep, ApprovalNotifyStep, ApprovalWorkflowStep, CreateApprovalHandle, approvalDecisionEventName, callbackReference, compileApprovalDescriptor, evaluateDecisionOutcome, resolveWorkflowStepIndex };
5
+ export { ApprovalActionStep, ApprovalBranchStep, ApprovalDecisionEvidence, ApprovalDecisionStep, ApprovalDefinition, ApprovalMutationStep, ApprovalNotifyStep, ApprovalWorkflowStep, CreateApprovalHandle, approvalDecisionEventName, callbackReference, compileApprovalDescriptor, evaluateDecisionOutcome, resolveWorkflowStepIndex };
6
6
  }
7
7
  type ApprovalMutationStep = Readonly<{
8
8
  kind: 'mutation';
@@ -56,5 +56,5 @@ declare function resolveWorkflowStepIndex(stepKeys: readonly string[], targetKey
56
56
  declare function callbackReference<Type extends FunctionType>(handle: string): FunctionReference<Type, 'internal', ApprovalCallbackInput, unknown>;
57
57
  declare function compileApprovalDescriptor(definition: ApprovalDefinition, createHandle?: CreateApprovalHandle): Promise<Readonly<ApprovalWorkflowDescriptor>>;
58
58
  //#endregion
59
- export { ApprovalActionStep, ApprovalBranchStep, type ApprovalCallbackInput, ApprovalDecisionEvidence, ApprovalDecisionStep, ApprovalDefinition, ApprovalMutationStep, ApprovalNotifyStep, ApprovalWorkflowStep, CreateApprovalHandle, approvalDecisionEventName, callbackReference, compileApprovalDescriptor, evaluateDecisionOutcome, resolveWorkflowStepIndex, workflow_steps_d_exports };
59
+ export { ApprovalActionStep, ApprovalBranchStep, ApprovalDecisionEvidence, ApprovalDecisionStep, ApprovalDefinition, ApprovalMutationStep, ApprovalNotifyStep, ApprovalWorkflowStep, CreateApprovalHandle, approvalDecisionEventName, callbackReference, compileApprovalDescriptor, evaluateDecisionOutcome, resolveWorkflowStepIndex, workflow_steps_d_exports };
60
60
  //# sourceMappingURL=workflow_steps.d.mts.map
@@ -1,2 +1,2 @@
1
- import{approvalWorkflowDescriptor as e}from"./validators.mjs";import{createFunctionHandle as t}from"convex/server";function n(e,t){return`approval:${e}:${t}`}function r(e,t){return e.some(e=>e.decision===`rejected`)?`rejected`:e.filter(e=>e.decision===`approved`).length>=t?`approved`:null}function i(e,t){let n=e.indexOf(t);if(n===-1)throw Error(`Unknown approval workflow step: ${t}`);return n}function a(e){return e}async function o(n,r=t){let i=await Promise.all(n.steps.map(async e=>e.kind===`mutation`||e.kind===`action`?{kind:e.kind,key:e.key,callback:{kind:e.kind,handle:await r(e.handler),retry:e.retry}}:e.kind===`notify`?{kind:e.kind,key:e.key,callback:{kind:`action`,handle:await r(e.handler),retry:e.retry}}:e.kind===`decision`?{...e,decisions:[...e.decisions],quorum:{...e.quorum}}:{...e}));return s(e.parse({schemaVersion:1,compatibilityKey:n.compatibilityKey,name:n.name,steps:i}))}function s(e){if(e&&typeof e==`object`){Object.freeze(e);for(let t of Object.values(e))s(t)}return e}export{n as approvalDecisionEventName,a as callbackReference,o as compileApprovalDescriptor,r as evaluateDecisionOutcome,i as resolveWorkflowStepIndex};
1
+ import{approvalWorkflowDescriptor as e}from"./validators.mjs";import{createFunctionHandle as t}from"convex/server";function n(e,t){return`approval:${e}:${t}`}function r(e,t){return e.some(e=>e.decision===`rejected`)?`rejected`:e.filter(e=>e.decision===`approved`).length>=t?`approved`:null}function i(e,t){let n=e.indexOf(t);if(n===-1)throw Error(`Unknown approval workflow step: ${t}`);return n}function a(e){return e}async function o(n,r=t){let i=await Promise.all(n.steps.map(async e=>e.kind===`mutation`||e.kind===`action`?{kind:e.kind,key:e.key,callback:{kind:e.kind,handle:await r(e.handler),retry:e.retry}}:e.kind===`notify`?{kind:e.kind,key:e.key,callback:{kind:`action`,handle:await r(e.handler),retry:e.retry}}:e.kind===`decision`?{...e,decisions:[...e.decisions],quorum:{...e.quorum}}:{...e}));return s(e.parse({schemaVersion:1,compatibilityKey:n.compatibilityKey,name:n.name,steps:i}))}function s(e){for(let t of e.steps)t.kind===`decision`?(Object.freeze(t.decisions),Object.freeze(t.quorum)):`callback`in t&&Object.freeze(t.callback),Object.freeze(t);return Object.freeze(e.steps),Object.freeze(e)}export{n as approvalDecisionEventName,a as callbackReference,o as compileApprovalDescriptor,r as evaluateDecisionOutcome,i as resolveWorkflowStepIndex};
2
2
  //# sourceMappingURL=workflow_steps.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"workflow_steps.mjs","names":[],"sources":["../../../src/components/approvals/workflow_steps.ts"],"sourcesContent":["import {\n\tcreateFunctionHandle,\n\ttype FunctionReference,\n\ttype FunctionType,\n} from 'convex/server'\n\nimport type { ApprovalDecision } from './constants'\nimport {\n\tapprovalWorkflowDescriptor,\n\ttype ApprovalCallbackInput,\n\ttype ApprovalWorkflowDescriptor,\n} from './validators'\n\nexport type { ApprovalCallbackInput } from './validators'\n\nexport type ApprovalMutationStep = Readonly<{\n\tkind: 'mutation'\n\tkey: string\n\thandler: FunctionReference<'mutation', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalActionStep = Readonly<{\n\tkind: 'action'\n\tkey: string\n\thandler: FunctionReference<'action', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalNotifyStep = Readonly<{\n\tkind: 'notify'\n\tkey: string\n\thandler: FunctionReference<'action', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalDecisionStep = Readonly<{\n\tkind: 'decision'\n\tkey: string\n\tdecisions: readonly ApprovalDecision[]\n\tquorum: Readonly<{ kind: 'count'; approvals: number }>\n\tmakerChecker: boolean\n\texpiresAfterMs?: number\n}>\n\nexport type ApprovalBranchStep = Readonly<{\n\tkind: 'branch'\n\tkey: string\n\tapprovedStepKey: string\n\trejectedStepKey: string\n}>\n\nexport type ApprovalWorkflowStep =\n\t| ApprovalMutationStep\n\t| ApprovalActionStep\n\t| ApprovalNotifyStep\n\t| ApprovalDecisionStep\n\t| ApprovalBranchStep\n\nexport type ApprovalDefinition = Readonly<{\n\tname: string\n\tcompatibilityKey: string\n\tsteps: readonly ApprovalWorkflowStep[]\n}>\n\nexport type CreateApprovalHandle = (\n\treference: FunctionReference<'mutation' | 'action', 'internal'>,\n) => Promise<string>\n\nexport type ApprovalDecisionEvidence = Readonly<{\n\tactorRef: string\n\tdecision: ApprovalDecision\n}>\n\nexport function approvalDecisionEventName(\n\trunId: string,\n\tstepKey: string,\n): string {\n\treturn `approval:${runId}:${stepKey}`\n}\n\nexport function evaluateDecisionOutcome(\n\tdecisions: readonly ApprovalDecisionEvidence[],\n\tapprovalsRequired: number,\n): ApprovalDecision | null {\n\tif (decisions.some((decision) => decision.decision === 'rejected'))\n\t\treturn 'rejected'\n\treturn decisions.filter((decision) => decision.decision === 'approved')\n\t\t.length >= approvalsRequired\n\t\t? 'approved'\n\t\t: null\n}\n\nexport function resolveWorkflowStepIndex(\n\tstepKeys: readonly string[],\n\ttargetKey: string,\n): number {\n\tconst index = stepKeys.indexOf(targetKey)\n\tif (index === -1)\n\t\tthrow new Error(`Unknown approval workflow step: ${targetKey}`)\n\treturn index\n}\n\nexport function callbackReference<Type extends FunctionType>(\n\thandle: string,\n): FunctionReference<Type, 'internal', ApprovalCallbackInput, unknown> {\n\treturn handle as unknown as FunctionReference<\n\t\tType,\n\t\t'internal',\n\t\tApprovalCallbackInput,\n\t\tunknown\n\t>\n}\n\nexport async function compileApprovalDescriptor(\n\tdefinition: ApprovalDefinition,\n\tcreateHandle: CreateApprovalHandle = createFunctionHandle,\n): Promise<Readonly<ApprovalWorkflowDescriptor>> {\n\tconst steps = await Promise.all(\n\t\tdefinition.steps.map(async (step) => {\n\t\t\tif (step.kind === 'mutation' || step.kind === 'action') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: step.kind,\n\t\t\t\t\tkey: step.key,\n\t\t\t\t\tcallback: {\n\t\t\t\t\t\tkind: step.kind,\n\t\t\t\t\t\thandle: await createHandle(step.handler),\n\t\t\t\t\t\tretry: step.retry,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (step.kind === 'notify') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: step.kind,\n\t\t\t\t\tkey: step.key,\n\t\t\t\t\tcallback: {\n\t\t\t\t\t\tkind: 'action' as const,\n\t\t\t\t\t\thandle: await createHandle(step.handler),\n\t\t\t\t\t\tretry: step.retry,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (step.kind === 'decision') {\n\t\t\t\treturn {\n\t\t\t\t\t...step,\n\t\t\t\t\tdecisions: [...step.decisions],\n\t\t\t\t\tquorum: { ...step.quorum },\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { ...step }\n\t\t}),\n\t)\n\tconst descriptor = approvalWorkflowDescriptor.parse({\n\t\tschemaVersion: 1,\n\t\tcompatibilityKey: definition.compatibilityKey,\n\t\tname: definition.name,\n\t\tsteps,\n\t})\n\treturn deepFreeze(descriptor)\n}\n\nfunction deepFreeze<Value>(value: Value): Readonly<Value> {\n\tif (value && typeof value === 'object') {\n\t\tObject.freeze(value)\n\t\tfor (const nested of Object.values(value)) deepFreeze(nested)\n\t}\n\treturn value\n}\n"],"mappings":"mHA0EA,SAAgB,EACf,EACA,EACS,CACT,MAAO,YAAY,EAAM,GAAG,GAC7B,CAEA,SAAgB,EACf,EACA,EAC0B,CAG1B,OAFI,EAAU,KAAM,GAAa,EAAS,WAAa,UAAU,EACzD,WACD,EAAU,OAAQ,GAAa,EAAS,WAAa,UAAU,CAAC,CACrE,QAAU,EACT,WACA,IACJ,CAEA,SAAgB,EACf,EACA,EACS,CACT,IAAM,EAAQ,EAAS,QAAQ,CAAS,EACxC,GAAI,IAAU,GACb,MAAU,MAAM,mCAAmC,GAAW,EAC/D,OAAO,CACR,CAEA,SAAgB,EACf,EACsE,CACtE,OAAO,CAMR,CAEA,eAAsB,EACrB,EACA,EAAqC,EACW,CAChD,IAAM,EAAQ,MAAM,QAAQ,IAC3B,EAAW,MAAM,IAAI,KAAO,IACvB,EAAK,OAAS,YAAc,EAAK,OAAS,SACtC,CACN,KAAM,EAAK,KACX,IAAK,EAAK,IACV,SAAU,CACT,KAAM,EAAK,KACX,OAAQ,MAAM,EAAa,EAAK,OAAO,EACvC,MAAO,EAAK,KACb,CACD,EAEG,EAAK,OAAS,SACV,CACN,KAAM,EAAK,KACX,IAAK,EAAK,IACV,SAAU,CACT,KAAM,SACN,OAAQ,MAAM,EAAa,EAAK,OAAO,EACvC,MAAO,EAAK,KACb,CACD,EAEG,EAAK,OAAS,WACV,CACN,GAAG,EACH,UAAW,CAAC,GAAG,EAAK,SAAS,EAC7B,OAAQ,CAAE,GAAG,EAAK,MAAO,CAC1B,EAEM,CAAE,GAAG,CAAK,CACjB,CACF,EAOA,OAAO,EANY,EAA2B,MAAM,CACnD,cAAe,EACf,iBAAkB,EAAW,iBAC7B,KAAM,EAAW,KACjB,OACD,CAC2B,CAAC,CAC7B,CAEA,SAAS,EAAkB,EAA+B,CACzD,GAAI,GAAS,OAAO,GAAU,SAAU,CACvC,OAAO,OAAO,CAAK,EACnB,IAAK,IAAM,KAAU,OAAO,OAAO,CAAK,EAAG,EAAW,CAAM,CAC7D,CACA,OAAO,CACR"}
1
+ {"version":3,"file":"workflow_steps.mjs","names":[],"sources":["../../../src/components/approvals/workflow_steps.ts"],"sourcesContent":["import {\n\tcreateFunctionHandle,\n\ttype FunctionReference,\n\ttype FunctionType,\n\ttype FunctionHandle,\n} from 'convex/server'\n\nimport type { ApprovalDecision } from './constants'\nimport {\n\tapprovalWorkflowDescriptor,\n\ttype ApprovalCallbackInput,\n\ttype ApprovalWorkflowDescriptor,\n} from './validators'\n\nexport type ApprovalMutationStep = Readonly<{\n\tkind: 'mutation'\n\tkey: string\n\thandler: FunctionReference<'mutation', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalActionStep = Readonly<{\n\tkind: 'action'\n\tkey: string\n\thandler: FunctionReference<'action', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalNotifyStep = Readonly<{\n\tkind: 'notify'\n\tkey: string\n\thandler: FunctionReference<'action', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalDecisionStep = Readonly<{\n\tkind: 'decision'\n\tkey: string\n\tdecisions: readonly ApprovalDecision[]\n\tquorum: Readonly<{ kind: 'count'; approvals: number }>\n\tmakerChecker: boolean\n\texpiresAfterMs?: number\n}>\n\nexport type ApprovalBranchStep = Readonly<{\n\tkind: 'branch'\n\tkey: string\n\tapprovedStepKey: string\n\trejectedStepKey: string\n}>\n\nexport type ApprovalWorkflowStep =\n\t| ApprovalMutationStep\n\t| ApprovalActionStep\n\t| ApprovalNotifyStep\n\t| ApprovalDecisionStep\n\t| ApprovalBranchStep\n\nexport type ApprovalDefinition = Readonly<{\n\tname: string\n\tcompatibilityKey: string\n\tsteps: readonly ApprovalWorkflowStep[]\n}>\n\nexport type CreateApprovalHandle = (\n\treference: FunctionReference<'mutation' | 'action', 'internal'>,\n) => Promise<string>\n\nexport type ApprovalDecisionEvidence = Readonly<{\n\tactorRef: string\n\tdecision: ApprovalDecision\n}>\n\nexport function approvalDecisionEventName(runId: string, stepKey: string): string {\n\treturn `approval:${runId}:${stepKey}`\n}\n\nexport function evaluateDecisionOutcome(\n\tdecisions: readonly ApprovalDecisionEvidence[],\n\tapprovalsRequired: number,\n): ApprovalDecision | null {\n\tif (decisions.some((decision) => decision.decision === 'rejected')) return 'rejected'\n\treturn decisions.filter((decision) => decision.decision === 'approved').length >=\n\t\tapprovalsRequired\n\t\t? 'approved'\n\t\t: null\n}\n\nexport function resolveWorkflowStepIndex(stepKeys: readonly string[], targetKey: string): number {\n\tconst index = stepKeys.indexOf(targetKey)\n\tif (index === -1) throw new Error(`Unknown approval workflow step: ${targetKey}`)\n\treturn index\n}\n\nexport function callbackReference<Type extends FunctionType>(\n\thandle: string,\n): FunctionReference<Type, 'internal', ApprovalCallbackInput, unknown> {\n\t// SAFETY: descriptors persist handles produced by createFunctionHandle; Convex resolves them on invocation.\n\treturn handle as FunctionHandle<Type, ApprovalCallbackInput, unknown>\n}\n\nexport async function compileApprovalDescriptor(\n\tdefinition: ApprovalDefinition,\n\tcreateHandle: CreateApprovalHandle = createFunctionHandle,\n): Promise<Readonly<ApprovalWorkflowDescriptor>> {\n\tconst steps = await Promise.all(\n\t\tdefinition.steps.map(async (step) => {\n\t\t\tif (step.kind === 'mutation' || step.kind === 'action') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: step.kind,\n\t\t\t\t\tkey: step.key,\n\t\t\t\t\tcallback: {\n\t\t\t\t\t\tkind: step.kind,\n\t\t\t\t\t\thandle: await createHandle(step.handler),\n\t\t\t\t\t\tretry: step.retry,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (step.kind === 'notify') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: step.kind,\n\t\t\t\t\tkey: step.key,\n\t\t\t\t\tcallback: {\n\t\t\t\t\t\tkind: 'action' as const,\n\t\t\t\t\t\thandle: await createHandle(step.handler),\n\t\t\t\t\t\tretry: step.retry,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (step.kind === 'decision') {\n\t\t\t\treturn {\n\t\t\t\t\t...step,\n\t\t\t\t\tdecisions: [...step.decisions],\n\t\t\t\t\tquorum: { ...step.quorum },\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { ...step }\n\t\t}),\n\t)\n\tconst descriptor = approvalWorkflowDescriptor.parse({\n\t\tschemaVersion: 1,\n\t\tcompatibilityKey: definition.compatibilityKey,\n\t\tname: definition.name,\n\t\tsteps,\n\t})\n\treturn deepFreeze(descriptor)\n}\n\nfunction deepFreeze(descriptor: ApprovalWorkflowDescriptor): Readonly<ApprovalWorkflowDescriptor> {\n\tfor (const step of descriptor.steps) {\n\t\tif (step.kind === 'decision') {\n\t\t\tObject.freeze(step.decisions)\n\t\t\tObject.freeze(step.quorum)\n\t\t} else if ('callback' in step) {\n\t\t\tObject.freeze(step.callback)\n\t\t}\n\t\tObject.freeze(step)\n\t}\n\tObject.freeze(descriptor.steps)\n\treturn Object.freeze(descriptor)\n}\n"],"mappings":"mHAyEA,SAAgB,EAA0B,EAAe,EAAyB,CACjF,MAAO,YAAY,EAAM,GAAG,GAC7B,CAEA,SAAgB,EACf,EACA,EAC0B,CAE1B,OADI,EAAU,KAAM,GAAa,EAAS,WAAa,UAAU,EAAU,WACpE,EAAU,OAAQ,GAAa,EAAS,WAAa,UAAU,CAAC,CAAC,QACvE,EACE,WACA,IACJ,CAEA,SAAgB,EAAyB,EAA6B,EAA2B,CAChG,IAAM,EAAQ,EAAS,QAAQ,CAAS,EACxC,GAAI,IAAU,GAAI,MAAU,MAAM,mCAAmC,GAAW,EAChF,OAAO,CACR,CAEA,SAAgB,EACf,EACsE,CAEtE,OAAO,CACR,CAEA,eAAsB,EACrB,EACA,EAAqC,EACW,CAChD,IAAM,EAAQ,MAAM,QAAQ,IAC3B,EAAW,MAAM,IAAI,KAAO,IACvB,EAAK,OAAS,YAAc,EAAK,OAAS,SACtC,CACN,KAAM,EAAK,KACX,IAAK,EAAK,IACV,SAAU,CACT,KAAM,EAAK,KACX,OAAQ,MAAM,EAAa,EAAK,OAAO,EACvC,MAAO,EAAK,KACb,CACD,EAEG,EAAK,OAAS,SACV,CACN,KAAM,EAAK,KACX,IAAK,EAAK,IACV,SAAU,CACT,KAAM,SACN,OAAQ,MAAM,EAAa,EAAK,OAAO,EACvC,MAAO,EAAK,KACb,CACD,EAEG,EAAK,OAAS,WACV,CACN,GAAG,EACH,UAAW,CAAC,GAAG,EAAK,SAAS,EAC7B,OAAQ,CAAE,GAAG,EAAK,MAAO,CAC1B,EAEM,CAAE,GAAG,CAAK,CACjB,CACF,EAOA,OAAO,EANY,EAA2B,MAAM,CACnD,cAAe,EACf,iBAAkB,EAAW,iBAC7B,KAAM,EAAW,KACjB,OACD,CAC2B,CAAC,CAC7B,CAEA,SAAS,EAAW,EAA8E,CACjG,IAAK,IAAM,KAAQ,EAAW,MACzB,EAAK,OAAS,YACjB,OAAO,OAAO,EAAK,SAAS,EAC5B,OAAO,OAAO,EAAK,MAAM,GACf,aAAc,GACxB,OAAO,OAAO,EAAK,QAAQ,EAE5B,OAAO,OAAO,CAAI,EAGnB,OADA,OAAO,OAAO,EAAW,KAAK,EACvB,OAAO,OAAO,CAAU,CAChC"}
@@ -4,6 +4,7 @@ import { AnyQueryMiddleware, Query, QueryExecution, QueryMiddleware } from "./mo
4
4
  import { Result, ResultBoundary, TransactionMetricsContext, executeResultBoundary, projectResult } from "./result.mjs";
5
5
  import { emitSemanticEvent } from "./telemetry.mjs";
6
6
  import { CommandPreparation, OperationExtension, OperationHostContext, TypedOperation, operationFactory } from "./operation.mjs";
7
+ import { Value } from "convex/values";
7
8
  declare namespace client_d_exports {
8
9
  export { AnyCommandMiddleware, AnyQueryMiddleware, ApplicationCommand, AuditEntryInput, AuditWriter, AuditedOperation, AuditedRegistry, CommandArgument, CommandConstructor, CommandDefaults, CommandExecution, CommandHandlerResult, CommandInput, CommandMiddleware, CommandObservation, CommandPreparation, CommandRegistry, CommandResult, Foundation, FoundationOptions, ObservabilityOptions, Parseable, PermissionChecker, QueryExecution, QueryMiddleware, Result, ResultBoundary, TransactionMetricsContext, TypedOperation };
9
10
  }
@@ -15,19 +16,15 @@ type AuditEntryInput = {
15
16
  type: string;
16
17
  id: string;
17
18
  };
18
- metadata?: Record<string, unknown>;
19
+ metadata?: Record<string, Value>;
19
20
  classification: string;
20
21
  };
21
- type AuditWriter = (context: never, entry: AuditEntryInput) => Promise<unknown> | unknown;
22
+ type AuditWriter<Result = unknown> = (context: never, entry: AuditEntryInput) => MaybePromise<Result>;
22
23
  type MaybePromise<Value> = Value | Promise<Value>;
23
24
  type OperationKey<Operations extends CommandRegistry> = Extract<keyof Operations, string>;
24
25
  type AuditedOperation = Readonly<{
25
- command: Readonly<{
26
- parse: (value: unknown) => unknown;
27
- }>;
28
- result: Readonly<{
29
- parse: (value: unknown) => unknown;
30
- }>;
26
+ command: Parseable<unknown>;
27
+ result: Parseable<unknown>;
31
28
  classification: string;
32
29
  /**
33
30
  * Permission slug required to execute this operation. Checked through the
@@ -36,9 +33,7 @@ type AuditedOperation = Readonly<{
36
33
  */
37
34
  permission?: string;
38
35
  prepare?: (context: never, command: never) => MaybePromise<CommandPreparation<never>>;
39
- replayResult?: Readonly<{
40
- parse: (value: unknown) => unknown;
41
- }>;
36
+ replayResult?: Parseable<unknown>;
42
37
  /**
43
38
  * Per-operation precondition, after the permission check and the registry
44
39
  * default guard, before the handler. Throw to deny — nothing has run yet.
@@ -65,25 +60,25 @@ type AuditedOperation = Readonly<{
65
60
  * transform results, but can never skip authorization, return an invalid
66
61
  * result, or desynchronize audit from effects.
67
62
  */
68
- type CommandMiddleware<Context = never, Extension extends Record<string, unknown> = Record<string, unknown>> = (input: {
63
+ type CommandMiddleware<Context = never, Extension extends object = object, Input = unknown, Result = unknown> = (input: {
69
64
  operation: string;
70
- command: unknown;
65
+ command: Input;
71
66
  context: Context;
72
67
  next: (options?: {
73
68
  /** Merged into the context handed to inner middleware, guards, handler. */
74
69
  context?: Extension;
75
- }) => Promise<unknown>;
76
- }) => Promise<unknown>;
70
+ }) => Promise<Result>;
71
+ }) => Promise<Result>;
77
72
  /** Any-context middleware — what registries and operations accept. */
78
- type AnyCommandMiddleware = (input: {
73
+ type AnyCommandMiddleware<Input = never, Result = unknown, NextResult = never> = (input: {
79
74
  operation: string;
80
75
  definition?: AuditedOperation;
81
- command: unknown;
76
+ command: Input;
82
77
  context: never;
83
78
  next: (options?: {
84
- context?: unknown;
85
- }) => Promise<unknown>;
86
- }) => Promise<unknown>;
79
+ context?: object;
80
+ }) => Promise<NextResult>;
81
+ }) => Promise<Result>;
87
82
  /** Registry-wide defaults applied to every operation of one Command. */
88
83
  type CommandDefaults = Readonly<{
89
84
  /** Runs before every operation's own guard. Throw to deny. */
@@ -133,7 +128,7 @@ declare class BoundCommand<Context, const Operations extends AuditedRegistry> {
133
128
  * Identity helper that types a middleware against its context and the
134
129
  * context extension it passes downstream via next({ context }).
135
130
  */
136
- static middleware<Context = never, const Extension extends Record<string, unknown> = Record<string, unknown>>(middleware: CommandMiddleware<Context, Extension>): CommandMiddleware<Context, Extension>;
131
+ static middleware<Context = never, const Extension extends object = object, Input = unknown, Result = unknown>(middleware: CommandMiddleware<Context, Extension, Input, Result>): CommandMiddleware<Context, Extension, Input, Result>;
137
132
  constructor(operations: Operations, deps: {
138
133
  observability: Observability;
139
134
  writeAudit: AuditWriter;
@@ -143,9 +138,7 @@ declare class BoundCommand<Context, const Operations extends AuditedRegistry> {
143
138
  exec<const Key extends OperationKey<Operations>>(executor: {
144
139
  operation: Key;
145
140
  handler: (context: Context & OperationExtension<Operations[Key]>, command: CommandInput<Operations, Key>) => MaybePromise<CommandHandlerResult<Operations, Key>>;
146
- }): (context: Context, command: SchemaInput<Operations[Key]["command"]>) => Promise<Operations[Key]["result"] extends (infer T) ? T extends Operations[Key]["result"] ? T extends Readonly<{
147
- parse: (value: unknown) => infer Output;
148
- }> ? Output : never : never : never>;
141
+ }): (context: Context, command: SchemaInput<Operations[Key]["command"]>) => Promise<ReturnType<Operations[Key]["result"]["parse"]>>;
149
142
  }
150
143
  type ApplicationCommand<Context, Operations extends AuditedRegistry> = BoundCommand<Context, Operations>;
151
144
  /**