cvx-kit 0.1.2 → 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.
- package/CHANGELOG.md +38 -0
- package/README.md +69 -24
- package/dist/agent-tools.d.mts +28 -12
- package/dist/agent-tools.mjs +1 -1
- package/dist/agent-tools.mjs.map +1 -1
- package/dist/auth.d.mts +4 -2
- package/dist/auth.mjs +1 -1
- package/dist/auth.mjs.map +1 -1
- package/dist/components/approvals/_generated/api.d.mts +1 -1
- package/dist/components/approvals/audit.mjs +1 -1
- package/dist/components/approvals/audit.mjs.map +1 -1
- package/dist/components/approvals/client.d.mts +1 -1
- package/dist/components/approvals/client.mjs +1 -1
- package/dist/components/approvals/client.mjs.map +1 -1
- package/dist/components/approvals/constants.mjs.map +1 -1
- package/dist/components/approvals/decisions.d.mts +6 -7
- package/dist/components/approvals/decisions.mjs +1 -1
- package/dist/components/approvals/decisions.mjs.map +1 -1
- package/dist/components/approvals/functions.mjs +1 -1
- package/dist/components/approvals/functions.mjs.map +1 -1
- package/dist/components/approvals/health.mjs.map +1 -1
- package/dist/components/approvals/requests.mjs.map +1 -1
- package/dist/components/approvals/validators.mjs.map +1 -1
- package/dist/components/approvals/workflow.d.mts +1 -1
- package/dist/components/approvals/workflow.mjs +1 -1
- package/dist/components/approvals/workflow.mjs.map +1 -1
- package/dist/components/approvals/workflow_steps.d.mts +3 -3
- package/dist/components/approvals/workflow_steps.mjs +1 -1
- package/dist/components/approvals/workflow_steps.mjs.map +1 -1
- package/dist/components/foundation/client.d.mts +27 -28
- package/dist/components/foundation/client.mjs +1 -1
- package/dist/components/foundation/client.mjs.map +1 -1
- package/dist/components/foundation/modules/command/command.d.mts +17 -8
- package/dist/components/foundation/modules/command/command.mjs +1 -1
- package/dist/components/foundation/modules/command/command.mjs.map +1 -1
- package/dist/components/foundation/modules/observability/observability.d.mts +1 -1
- package/dist/components/foundation/modules/observability/observability.mjs +1 -1
- package/dist/components/foundation/modules/observability/observability.mjs.map +1 -1
- package/dist/components/foundation/modules/query/query.d.mts +8 -8
- package/dist/components/foundation/modules/query/query.mjs +1 -1
- package/dist/components/foundation/modules/query/query.mjs.map +1 -1
- package/dist/components/foundation/operation.d.mts +74 -0
- package/dist/components/foundation/operation.mjs +2 -0
- package/dist/components/foundation/operation.mjs.map +1 -0
- package/dist/components/foundation/result.d.mts +1 -1
- package/dist/components/foundation/result.mjs.map +1 -1
- package/dist/components/foundation/telemetry.mjs +1 -1
- package/dist/components/foundation/telemetry.mjs.map +1 -1
- package/dist/crud.d.mts +177 -16
- package/dist/crud.mjs +1 -1
- package/dist/crud.mjs.map +1 -1
- package/dist/middleware.d.mts +7 -7
- package/dist/middleware.mjs +1 -1
- package/dist/middleware.mjs.map +1 -1
- package/dist/oxlint.d.mts +9 -0
- package/dist/oxlint.mjs +2 -0
- package/dist/oxlint.mjs.map +1 -0
- package/dist/tenancy.mjs +1 -1
- package/dist/tenancy.mjs.map +1 -1
- package/dist/triggers.mjs +1 -1
- package/dist/triggers.mjs.map +1 -1
- package/dist/webhooks.d.mts +17 -106
- package/dist/webhooks.mjs.map +1 -1
- package/dist/zod-table.d.mts +47 -91
- package/dist/zod-table.mjs +1 -1
- package/dist/zod-table.mjs.map +1 -1
- package/docs/approvals.md +57 -56
- package/docs/architecture.md +1 -1
- package/docs/auth.md +57 -33
- package/docs/commands.md +159 -44
- package/docs/conventions.md +40 -40
- package/docs/crud.md +5 -5
- package/docs/llms.md +117 -71
- package/docs/maintainability.md +4 -4
- package/docs/migration.md +33 -33
- package/docs/tenancy.md +5 -8
- package/docs/triggers.md +4 -2
- package/docs/upgrading.md +74 -7
- package/docs/zod-table.md +70 -45
- package/package.json +41 -28
- package/scripts/convex-config-bridge.mjs +7 -18
- package/scripts/extract-changelog.mjs +1 -1
- package/scripts/smoke-packed-components.mjs +35 -75
- package/scripts/test-packed-ids.mjs +149 -0
- package/scripts/test-packed-oxlint.mjs +121 -0
- package/src/test.ts +9 -17
- package/docs/plans/2026-08-14-001-feat-v0.1.0-feature-set-plan.md +0 -461
- package/docs/plans/2026-08-21-0930-feat-resolve-organization-hook-plan.md +0 -178
|
@@ -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:
|
|
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
|
|
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
|
|
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:
|
|
44
|
+
state: "approved" | "canceled" | "expired" | "rejected";
|
|
46
45
|
expiresAt: number | undefined;
|
|
47
46
|
} | {
|
|
48
47
|
state: 'pending';
|
|
49
|
-
expiresAt
|
|
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:
|
|
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
|
|
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":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e,t){return n[e].
|
|
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(
|
|
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 {
|
|
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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"}
|
|
@@ -5,7 +5,7 @@ declare namespace workflow_d_exports {
|
|
|
5
5
|
declare const approvalWorkflow: WorkflowManager;
|
|
6
6
|
declare const run: import("convex/server").RegisteredMutation<"internal", import("@convex-dev/workflow").WorkflowArgs<{
|
|
7
7
|
runId: import("convex/values").VId<import("convex/values").GenericId<"approvalRuns">, "required">;
|
|
8
|
-
}>, import("@convex-dev/workflow").
|
|
8
|
+
}>, import("@convex-dev/workflow").WorkflowMutationResult<null>>;
|
|
9
9
|
//#endregion
|
|
10
10
|
export { approvalWorkflow, run, workflow_d_exports };
|
|
11
11
|
//# sourceMappingURL=workflow.d.mts.map
|
|
@@ -1,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
|
|
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":"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"}
|
|
@@ -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,
|
|
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,
|
|
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){
|
|
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
|
|
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"}
|