cvx-kit 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +3 -0
  3. package/dist/components/approvals/_generated/api.d.mts +2 -0
  4. package/dist/components/approvals/_generated/api.mjs.map +1 -1
  5. package/dist/components/approvals/_generated/component.d.mts +7 -0
  6. package/dist/components/approvals/_generated/dataModel.d.mts +1 -1
  7. package/dist/components/approvals/client.d.mts +4 -4
  8. package/dist/components/approvals/client.mjs +1 -1
  9. package/dist/components/approvals/client.mjs.map +1 -1
  10. package/dist/components/approvals/constants.d.mts +8 -2
  11. package/dist/components/approvals/constants.mjs +1 -1
  12. package/dist/components/approvals/constants.mjs.map +1 -1
  13. package/dist/components/approvals/decisions.mjs +1 -1
  14. package/dist/components/approvals/decisions.mjs.map +1 -1
  15. package/dist/components/approvals/health.d.mts +15 -0
  16. package/dist/components/approvals/health.mjs +2 -0
  17. package/dist/components/approvals/health.mjs.map +1 -0
  18. package/dist/components/approvals/requests.mjs +1 -1
  19. package/dist/components/approvals/requests.mjs.map +1 -1
  20. package/dist/components/approvals/schema.js +1 -0
  21. package/dist/components/approvals/validators.d.mts +119 -2
  22. package/dist/components/approvals/validators.mjs +1 -1
  23. package/dist/components/approvals/validators.mjs.map +1 -1
  24. package/dist/components/approvals/workflow_steps.d.mts +2 -25
  25. package/dist/components/approvals/workflow_steps.mjs.map +1 -1
  26. package/dist/components/foundation/_generated/dataModel.d.mts +1 -1
  27. package/dist/components/foundation/{schema.mjs → schema.js} +0 -1
  28. package/docs/approvals.md +50 -2
  29. package/docs/upgrading.md +23 -0
  30. package/package.json +5 -2
  31. package/scripts/convex-config-bridge.mjs +41 -3
  32. package/scripts/smoke-packed-components.mjs +374 -0
  33. package/src/test.ts +2 -2
  34. package/dist/components/approvals/schema.mjs +0 -2
  35. package/dist/components/approvals/schema.mjs.map +0 -1
  36. package/dist/components/foundation/schema.mjs.map +0 -1
  37. /package/dist/components/approvals/{schema.d.mts → schema.d.ts} +0 -0
  38. /package/dist/components/foundation/{schema.d.mts → schema.d.ts} +0 -0
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  declare namespace validators_d_exports {
3
- export { ApprovalActor, ApprovalMetadata, ApprovalWorkflowDescriptor, approvalActionCallback, approvalActor, approvalBranchStep, approvalCallback, approvalCallbackKind, approvalCallbackStep, approvalDecision, approvalDecisionDocument, approvalDecisionStep, approvalMetadata, approvalMutationCallback, approvalName, approvalNotifyStep, approvalQuorum, approvalQuorumKind, approvalReason, approvalReference, approvalRunDocument, approvalRunState, approvalStepKind, approvalWorkflowDescriptor, approvalWorkflowStep };
3
+ export { ApprovalActor, ApprovalCallbackInput, ApprovalMetadata, ApprovalWorkflowDescriptor, approvalActionCallback, approvalActor, approvalBranchStep, approvalCallback, approvalCallbackArgs, approvalCallbackInput, approvalCallbackKind, approvalCallbackStep, approvalDecision, approvalDecisionDocument, approvalDecisionStep, approvalMetadata, approvalMutationCallback, approvalName, approvalNotifyStep, approvalQuorum, approvalQuorumKind, approvalReason, approvalReference, approvalRunDocument, approvalRunState, approvalStepKind, approvalWorkflowDescriptor, approvalWorkflowStep };
4
4
  }
5
5
  declare const approvalRunState: z.ZodEnum<{
6
6
  approved: "approved";
@@ -31,6 +31,122 @@ declare const approvalReference: z.ZodString;
31
31
  declare const approvalName: z.ZodString;
32
32
  declare const approvalReason: z.ZodString;
33
33
  declare const approvalMetadata: z.ZodRecord<z.ZodString, z.ZodString>;
34
+ declare const approvalCallbackInput: z.ZodObject<{
35
+ runId: z.ZodString;
36
+ scopeRef: z.ZodString;
37
+ resourceType: z.ZodString;
38
+ resourceRef: z.ZodString;
39
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
40
+ decision: z.ZodOptional<z.ZodObject<{
41
+ stepKey: z.ZodString;
42
+ outcome: z.ZodUnion<readonly [z.ZodEnum<{
43
+ approved: "approved";
44
+ rejected: "rejected";
45
+ }>, z.ZodLiteral<"expired">]>;
46
+ evidence: z.ZodArray<z.ZodObject<{
47
+ stepKey: z.ZodString;
48
+ actorRef: z.ZodString;
49
+ decision: z.ZodEnum<{
50
+ approved: "approved";
51
+ rejected: "rejected";
52
+ }>;
53
+ reason: z.ZodOptional<z.ZodString>;
54
+ decidedAt: z.ZodNumber;
55
+ }, z.core.$strict>>;
56
+ terminalEvidence: z.ZodOptional<z.ZodObject<{
57
+ actorRef: z.ZodString;
58
+ decision: z.ZodEnum<{
59
+ approved: "approved";
60
+ rejected: "rejected";
61
+ }>;
62
+ reason: z.ZodOptional<z.ZodString>;
63
+ decidedAt: z.ZodNumber;
64
+ }, z.core.$strict>>;
65
+ }, z.core.$strict>>;
66
+ }, z.core.$strict>;
67
+ /** Convex argument validator for approval mutation, action, and notify callbacks. */
68
+ declare const approvalCallbackArgs: import("convex/values").VObject<{
69
+ runId: string;
70
+ scopeRef: string;
71
+ resourceType: string;
72
+ resourceRef: string;
73
+ metadata?: {
74
+ [x: string]: string;
75
+ } | undefined;
76
+ decision?: {
77
+ stepKey: string;
78
+ outcome: "approved" | "expired" | "rejected";
79
+ evidence: {
80
+ stepKey: string;
81
+ actorRef: string;
82
+ decision: "approved" | "rejected";
83
+ reason?: string | undefined;
84
+ decidedAt: number;
85
+ }[];
86
+ terminalEvidence?: {
87
+ actorRef: string;
88
+ decision: "approved" | "rejected";
89
+ reason?: string | undefined;
90
+ decidedAt: number;
91
+ } | undefined;
92
+ } | undefined;
93
+ }, {
94
+ runId: import("convex/values").VString<string, "required">;
95
+ scopeRef: import("convex/values").VString<string, "required">;
96
+ resourceType: import("convex/values").VString<string, "required">;
97
+ resourceRef: import("convex/values").VString<string, "required">;
98
+ metadata: import("convex/values").VRecord<Record<string, string> | undefined, import("convex/values").VString<string, "required">, import("convex/values").VString<string, "required">, "optional", string>;
99
+ decision: import("convex/values").VObject<{
100
+ stepKey: string;
101
+ outcome: "approved" | "expired" | "rejected";
102
+ evidence: {
103
+ stepKey: string;
104
+ actorRef: string;
105
+ decision: "approved" | "rejected";
106
+ reason?: string | undefined;
107
+ decidedAt: number;
108
+ }[];
109
+ terminalEvidence?: {
110
+ actorRef: string;
111
+ decision: "approved" | "rejected";
112
+ reason?: string | undefined;
113
+ decidedAt: number;
114
+ } | undefined;
115
+ } | undefined, {
116
+ stepKey: import("convex/values").VString<string, "required">;
117
+ outcome: import("convex/values").VUnion<"approved" | "expired" | "rejected", [import("convex/values").VUnion<"approved" | "rejected", (import("convex/values").VLiteral<"approved", "required"> | import("convex/values").VLiteral<"rejected", "required">)[], "required", never>, import("convex/values").VLiteral<"expired", "required">], "required", never>;
118
+ evidence: import("convex/values").VArray<{
119
+ stepKey: string;
120
+ actorRef: string;
121
+ decision: "approved" | "rejected";
122
+ reason?: string | undefined;
123
+ decidedAt: number;
124
+ }[], import("convex/values").VObject<{
125
+ stepKey: string;
126
+ actorRef: string;
127
+ decision: "approved" | "rejected";
128
+ reason?: string | undefined;
129
+ decidedAt: number;
130
+ }, {
131
+ stepKey: import("convex/values").VString<string, "required">;
132
+ actorRef: import("convex/values").VString<string, "required">;
133
+ decision: import("convex/values").VUnion<"approved" | "rejected", (import("convex/values").VLiteral<"approved", "required"> | import("convex/values").VLiteral<"rejected", "required">)[], "required", never>;
134
+ reason: import("convex/values").VString<string | undefined, "optional">;
135
+ decidedAt: import("convex/values").VFloat64<number, "required">;
136
+ }, "required", "actorRef" | "decidedAt" | "decision" | "reason" | "stepKey">, "required">;
137
+ terminalEvidence: import("convex/values").VObject<{
138
+ actorRef: string;
139
+ decision: "approved" | "rejected";
140
+ reason?: string | undefined;
141
+ decidedAt: number;
142
+ } | undefined, {
143
+ actorRef: import("convex/values").VString<string, "required">;
144
+ decision: import("convex/values").VUnion<"approved" | "rejected", (import("convex/values").VLiteral<"approved", "required"> | import("convex/values").VLiteral<"rejected", "required">)[], "required", never>;
145
+ reason: import("convex/values").VString<string | undefined, "optional">;
146
+ decidedAt: import("convex/values").VFloat64<number, "required">;
147
+ }, "optional", "actorRef" | "decidedAt" | "decision" | "reason">;
148
+ }, "optional", "evidence" | "outcome" | "stepKey" | "terminalEvidence" | "terminalEvidence.actorRef" | "terminalEvidence.decidedAt" | "terminalEvidence.decision" | "terminalEvidence.reason">;
149
+ }, "required", "decision" | "decision.evidence" | "decision.outcome" | "decision.stepKey" | "decision.terminalEvidence" | "decision.terminalEvidence.actorRef" | "decision.terminalEvidence.decidedAt" | "decision.terminalEvidence.decision" | "decision.terminalEvidence.reason" | "metadata" | "resourceRef" | "resourceType" | "runId" | "scopeRef" | `metadata.${string}`>;
34
150
  declare const approvalActor: z.ZodObject<{
35
151
  actorRef: z.ZodString;
36
152
  capabilities: z.ZodArray<z.ZodString>;
@@ -297,6 +413,7 @@ declare const approvalDecisionDocument: z.ZodObject<{
297
413
  type ApprovalWorkflowDescriptor = z.infer<typeof approvalWorkflowDescriptor>;
298
414
  type ApprovalActor = z.infer<typeof approvalActor>;
299
415
  type ApprovalMetadata = z.infer<typeof approvalMetadata>;
416
+ type ApprovalCallbackInput = Readonly<z.infer<typeof approvalCallbackInput>>;
300
417
  //#endregion
301
- export { ApprovalActor, ApprovalMetadata, ApprovalWorkflowDescriptor, approvalActionCallback, approvalActor, approvalBranchStep, approvalCallback, approvalCallbackKind, approvalCallbackStep, approvalDecision, approvalDecisionDocument, approvalDecisionStep, approvalMetadata, approvalMutationCallback, approvalName, approvalNotifyStep, approvalQuorum, approvalQuorumKind, approvalReason, approvalReference, approvalRunDocument, approvalRunState, approvalStepKind, approvalWorkflowDescriptor, approvalWorkflowStep, validators_d_exports };
418
+ export { ApprovalActor, ApprovalCallbackInput, ApprovalMetadata, ApprovalWorkflowDescriptor, approvalActionCallback, approvalActor, approvalBranchStep, approvalCallback, approvalCallbackArgs, approvalCallbackInput, approvalCallbackKind, approvalCallbackStep, approvalDecision, approvalDecisionDocument, approvalDecisionStep, approvalMetadata, approvalMutationCallback, approvalName, approvalNotifyStep, approvalQuorum, approvalQuorumKind, approvalReason, approvalReference, approvalRunDocument, approvalRunState, approvalStepKind, approvalWorkflowDescriptor, approvalWorkflowStep, validators_d_exports };
302
419
  //# sourceMappingURL=validators.d.mts.map
@@ -1,2 +1,2 @@
1
- import{APPROVAL_CALLBACK_KINDS as e,APPROVAL_DECISIONS as t,APPROVAL_QUORUM_KINDS as n,APPROVAL_RUN_STATES as r,APPROVAL_STEP_KINDS as i}from"./constants.mjs";import{zid as a}from"convex-helpers/server/zod4";import{z as o}from"zod";const s=o.enum(r),c=o.enum(t),l=o.enum(i),u=o.enum(e),d=o.enum(n),f=o.string().trim().min(1).max(256),p=o.string().trim().min(1).max(96).regex(/^[A-Za-z][A-Za-z0-9._-]*$/),m=o.string().trim().min(1).max(500),h=o.record(o.string().min(1).max(64).regex(/^(?![$_])[\x20-\x7E]+$/),o.string().max(512)).refine(e=>Object.keys(e).length<=32,`Metadata may contain at most 32 entries`),g=o.object({actorRef:f,capabilities:o.array(p).max(32),metadata:h.optional()}).strict(),_=o.object({kind:o.literal(`mutation`),handle:f,retry:o.boolean()}).strict(),v=o.object({kind:o.literal(`action`),handle:f,retry:o.boolean()}).strict(),y=o.discriminatedUnion(`kind`,[_,v]),b=o.object({kind:d,approvals:o.number().int().positive().max(8)}).strict(),x=o.discriminatedUnion(`kind`,[o.object({kind:o.literal(`mutation`),key:p,callback:_}).strict(),o.object({kind:o.literal(`action`),key:p,callback:v}).strict()]),S=o.object({kind:o.literal(`notify`),key:p,callback:v}).strict(),C=o.object({kind:o.literal(`decision`),key:p,decisions:o.array(c).min(1).max(8),quorum:b,makerChecker:o.boolean(),expiresAfterMs:o.number().int().positive().optional()}).strict(),w=o.object({kind:o.literal(`branch`),key:p,approvedStepKey:p,rejectedStepKey:p}).strict(),T=o.discriminatedUnion(`kind`,[x,S,C,w]),E=o.object({schemaVersion:o.literal(1),compatibilityKey:p,name:p,steps:o.array(T).min(1).max(32)}).strict().superRefine((e,t)=>{let n=new Set,r=new Map(e.steps.map(e=>[e.key,e])),i=new Map;for(let[r,i]of e.steps.entries())n.has(i.key)&&t.addIssue({code:`custom`,message:`Duplicate workflow step key: ${i.key}`,path:[`steps`,r,`key`]}),n.add(i.key),i.kind===`decision`&&new Set(i.decisions).size!==i.decisions.length&&t.addIssue({code:`custom`,message:`Duplicate decision value in step: ${i.key}`,path:[`steps`,r,`decisions`]});for(let[n,a]of e.steps.entries())if(a.kind===`branch`){e.steps[n-1]?.kind!==`decision`&&t.addIssue({code:`custom`,message:`Branch must immediately follow a decision: ${a.key}`,path:[`steps`,n]});for(let o of[a.approvedStepKey,a.rejectedStepKey]){let a=r.get(o),s=e.steps.findIndex(e=>e.key===o);a?a.kind!==`mutation`&&a.kind!==`action`&&a.kind!==`notify`?t.addIssue({code:`custom`,message:`Branch target must be a callback step: ${o}`,path:[`steps`,n]}):s<=n&&t.addIssue({code:`custom`,message:`Branch target must be declared after its branch: ${o}`,path:[`steps`,n]}):t.addIssue({code:`custom`,message:`Unknown branch target: ${o}`,path:[`steps`,n]}),i.set(o,(i.get(o)??0)+1)}}for(let[e,n]of i)n!==1&&t.addIssue({code:`custom`,message:`Branch callback must belong to exactly one branch: ${e}`,path:[`steps`]})}),D=o.object({scopeRef:f,resourceType:p,resourceRef:f,requester:g,metadata:h.optional(),workflow:E,state:s,currentStepKey:p.optional(),executionFailedStepKey:p.optional(),expiresAt:o.number().optional(),workflowId:f.optional(),createdAt:o.number(),updatedAt:o.number(),terminalAt:o.number().optional()}).strict(),O=o.object({runId:a(`approvalRuns`),stepKey:p,actor:g,decision:c,reason:m.optional(),decidedAt:o.number()}).strict();export{v as approvalActionCallback,g as approvalActor,w as approvalBranchStep,y as approvalCallback,u as approvalCallbackKind,x as approvalCallbackStep,c as approvalDecision,O as approvalDecisionDocument,C as approvalDecisionStep,h as approvalMetadata,_ as approvalMutationCallback,p as approvalName,S as approvalNotifyStep,b as approvalQuorum,d as approvalQuorumKind,m as approvalReason,f as approvalReference,D as approvalRunDocument,s as approvalRunState,l as approvalStepKind,E as approvalWorkflowDescriptor,T as approvalWorkflowStep};
1
+ import{APPROVAL_CALLBACK_KINDS as e,APPROVAL_DECISIONS as t,APPROVAL_QUORUM_KINDS as n,APPROVAL_RUN_STATES as r,APPROVAL_STEP_KINDS as i}from"./constants.mjs";import{zid as a,zodToConvex as o}from"convex-helpers/server/zod4";import{z as s}from"zod";const c=s.enum(r),l=s.enum(t),u=s.enum(i),d=s.enum(e),f=s.enum(n),p=s.string().trim().min(1).max(256),m=s.string().trim().min(1).max(96).regex(/^[A-Za-z][A-Za-z0-9._-]*$/),h=s.string().trim().min(1).max(500),g=s.record(s.string().min(1).max(64).regex(/^(?![$_])[\x20-\x7E]+$/),s.string().max(512)).refine(e=>Object.keys(e).length<=32,`Metadata may contain at most 32 entries`),_=s.object({stepKey:m,actorRef:p,decision:l,reason:h.optional(),decidedAt:s.number()}).strict(),v=_.omit({stepKey:!0}),y=s.object({runId:p,scopeRef:p,resourceType:m,resourceRef:p,metadata:g.optional(),decision:s.object({stepKey:m,outcome:s.union([l,s.literal(`expired`)]),evidence:s.array(_),terminalEvidence:v.optional()}).strict().optional()}).strict(),b=o(y),x=s.object({actorRef:p,capabilities:s.array(m).max(32),metadata:g.optional()}).strict(),S=s.object({kind:s.literal(`mutation`),handle:p,retry:s.boolean()}).strict(),C=s.object({kind:s.literal(`action`),handle:p,retry:s.boolean()}).strict(),w=s.discriminatedUnion(`kind`,[S,C]),T=s.object({kind:f,approvals:s.number().int().positive().max(8)}).strict(),E=s.discriminatedUnion(`kind`,[s.object({kind:s.literal(`mutation`),key:m,callback:S}).strict(),s.object({kind:s.literal(`action`),key:m,callback:C}).strict()]),D=s.object({kind:s.literal(`notify`),key:m,callback:C}).strict(),O=s.object({kind:s.literal(`decision`),key:m,decisions:s.array(l).min(1).max(8),quorum:T,makerChecker:s.boolean(),expiresAfterMs:s.number().int().positive().optional()}).strict(),k=s.object({kind:s.literal(`branch`),key:m,approvedStepKey:m,rejectedStepKey:m}).strict(),A=s.discriminatedUnion(`kind`,[E,D,O,k]),j=s.object({schemaVersion:s.literal(1),compatibilityKey:m,name:m,steps:s.array(A).min(1).max(32)}).strict().superRefine((e,t)=>{let n=new Set,r=new Map(e.steps.map(e=>[e.key,e])),i=new Map;for(let[r,i]of e.steps.entries())n.has(i.key)&&t.addIssue({code:`custom`,message:`Duplicate workflow step key: ${i.key}`,path:[`steps`,r,`key`]}),n.add(i.key),i.kind===`decision`&&new Set(i.decisions).size!==i.decisions.length&&t.addIssue({code:`custom`,message:`Duplicate decision value in step: ${i.key}`,path:[`steps`,r,`decisions`]});for(let[n,a]of e.steps.entries())if(a.kind===`branch`){e.steps[n-1]?.kind!==`decision`&&t.addIssue({code:`custom`,message:`Branch must immediately follow a decision: ${a.key}`,path:[`steps`,n]});for(let o of[a.approvedStepKey,a.rejectedStepKey]){let a=r.get(o),s=e.steps.findIndex(e=>e.key===o);a?a.kind!==`mutation`&&a.kind!==`action`&&a.kind!==`notify`?t.addIssue({code:`custom`,message:`Branch target must be a callback step: ${o}`,path:[`steps`,n]}):s<=n&&t.addIssue({code:`custom`,message:`Branch target must be declared after its branch: ${o}`,path:[`steps`,n]}):t.addIssue({code:`custom`,message:`Unknown branch target: ${o}`,path:[`steps`,n]}),i.set(o,(i.get(o)??0)+1)}}for(let[e,n]of i)n!==1&&t.addIssue({code:`custom`,message:`Branch callback must belong to exactly one branch: ${e}`,path:[`steps`]})}),M=s.object({scopeRef:p,resourceType:m,resourceRef:p,requester:x,metadata:g.optional(),workflow:j,state:c,currentStepKey:m.optional(),executionFailedStepKey:m.optional(),expiresAt:s.number().optional(),workflowId:p.optional(),createdAt:s.number(),updatedAt:s.number(),terminalAt:s.number().optional()}).strict(),N=s.object({runId:a(`approvalRuns`),stepKey:m,actor:x,decision:l,reason:h.optional(),decidedAt:s.number()}).strict();export{C as approvalActionCallback,x as approvalActor,k as approvalBranchStep,w as approvalCallback,b as approvalCallbackArgs,y as approvalCallbackInput,d as approvalCallbackKind,E as approvalCallbackStep,l as approvalDecision,N as approvalDecisionDocument,O as approvalDecisionStep,g as approvalMetadata,S as approvalMutationCallback,m as approvalName,D as approvalNotifyStep,T as approvalQuorum,f as approvalQuorumKind,h as approvalReason,p as approvalReference,M as approvalRunDocument,c as approvalRunState,u as approvalStepKind,j as approvalWorkflowDescriptor,A as approvalWorkflowStep};
2
2
  //# sourceMappingURL=validators.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"validators.mjs","names":[],"sources":["../../../src/components/approvals/validators.ts"],"sourcesContent":["import { zid } 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\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>\n"],"mappings":"wOAqBA,MAAa,EAAmB,EAAE,KAAK,CAAmB,EAC7C,EAAmB,EAAE,KAAK,CAAkB,EAC5C,EAAmB,EAAE,KAAK,CAAmB,EAC7C,EAAuB,EAAE,KAAK,CAAuB,EACrD,EAAqB,EAAE,KAAK,CAAqB,EAEjD,EAAoB,EAC/B,OAAO,CAAC,CACR,KAAK,CAAC,CACN,IAAI,CAAC,CAAC,CACN,IAAA,GAAiC,EAEtB,EAAe,EAC1B,OAAO,CAAC,CACR,KAAK,CAAC,CACN,IAAI,CAAC,CAAC,CACN,IAAA,EAA4B,CAAC,CAC7B,MAAM,2BAA2B,EAEtB,EAAiB,EAC5B,OAAO,CAAC,CACR,KAAK,CAAC,CACN,IAAI,CAAC,CAAC,CACN,IAAA,GAA8B,EAEnB,EAAmB,EAC9B,OACA,EACE,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,IAAA,EAAoC,CAAC,CACrC,MAAM,wBAAwB,EAChC,EAAE,OAAO,CAAC,CAAC,IAAA,GAAsC,CAClD,CAAC,CACA,OACC,GAAU,OAAO,KAAK,CAAK,CAAC,CAAC,QAAA,GAC9B,yCACD,EAEY,EAAgB,EAC3B,OAAO,CACP,SAAU,EACV,aAAc,EAAE,MAAM,CAAY,CAAC,CAAC,IAAA,EAA6B,EACjE,SAAU,EAAiB,SAAS,CACrC,CAAC,CAAC,CACD,OAAO,EAEI,EAA2B,EACtC,OAAO,CACP,KAAM,EAAE,QAAQ,UAAU,EAC1B,OAAQ,EACR,MAAO,EAAE,QAAQ,CAClB,CAAC,CAAC,CACD,OAAO,EAEI,EAAyB,EACpC,OAAO,CACP,KAAM,EAAE,QAAQ,QAAQ,EACxB,OAAQ,EACR,MAAO,EAAE,QAAQ,CAClB,CAAC,CAAC,CACD,OAAO,EAEI,EAAmB,EAAE,mBAAmB,OAAQ,CAC5D,EACA,CACD,CAAC,EAEY,EAAiB,EAC5B,OAAO,CACP,KAAM,EACN,UAAW,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,IAAA,CAAmC,CAC3E,CAAC,CAAC,CACD,OAAO,EAEI,EAAuB,EAAE,mBAAmB,OAAQ,CAChE,EACE,OAAO,CACP,KAAM,EAAE,QAAQ,UAAU,EAC1B,IAAK,EACL,SAAU,CACX,CAAC,CAAC,CACD,OAAO,EACT,EACE,OAAO,CACP,KAAM,EAAE,QAAQ,QAAQ,EACxB,IAAK,EACL,SAAU,CACX,CAAC,CAAC,CACD,OAAO,CACV,CAAC,EAEY,EAAqB,EAChC,OAAO,CACP,KAAM,EAAE,QAAQ,QAAQ,EACxB,IAAK,EACL,SAAU,CACX,CAAC,CAAC,CACD,OAAO,EAEI,EAAuB,EAClC,OAAO,CACP,KAAM,EAAE,QAAQ,UAAU,EAC1B,IAAK,EACL,UAAW,EACT,MAAM,CAAgB,CAAC,CACvB,IAAI,CAAC,CAAC,CACN,IAAA,CAAmC,EACrC,OAAQ,EACR,aAAc,EAAE,QAAQ,EACxB,eAAgB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,CACtD,CAAC,CAAC,CACD,OAAO,EAEI,EAAqB,EAChC,OAAO,CACP,KAAM,EAAE,QAAQ,QAAQ,EACxB,IAAK,EACL,gBAAiB,EACjB,gBAAiB,CAClB,CAAC,CAAC,CACD,OAAO,EAEI,EAAuB,EAAE,mBAAmB,OAAQ,CAChE,EACA,EACA,EACA,CACD,CAAC,EAEY,EAA6B,EACxC,OAAO,CACP,cAAe,EAAE,QAAA,CAA0C,EAC3D,iBAAkB,EAClB,KAAM,EACN,MAAO,EACL,MAAM,CAAoB,CAAC,CAC3B,IAAI,CAAC,CAAC,CACN,IAAA,EAA+B,CAClC,CAAC,CAAC,CACD,OAAO,CAAC,CACR,aAAa,EAAY,IAAY,CACrC,IAAM,EAAO,IAAI,IACX,EAAa,IAAI,IACtB,EAAW,MAAM,IAAK,GAAS,CAAC,EAAK,IAAK,CAAI,CAAU,CACzD,EACM,EAAqB,IAAI,IAC/B,IAAK,GAAM,CAAC,EAAO,KAAS,EAAW,MAAM,QAAQ,EAChD,EAAK,IAAI,EAAK,GAAG,GACpB,EAAQ,SAAS,CAChB,KAAM,SACN,QAAS,gCAAgC,EAAK,MAC9C,KAAM,CAAC,QAAS,EAAO,KAAK,CAC7B,CAAC,EACF,EAAK,IAAI,EAAK,GAAG,EAEhB,EAAK,OAAS,YACd,IAAI,IAAI,EAAK,SAAS,CAAC,CAAC,OAAS,EAAK,UAAU,QAEhD,EAAQ,SAAS,CAChB,KAAM,SACN,QAAS,qCAAqC,EAAK,MACnD,KAAM,CAAC,QAAS,EAAO,WAAW,CACnC,CAAC,EAEH,IAAK,GAAM,CAAC,EAAO,KAAS,EAAW,MAAM,QAAQ,EAChD,KAAK,OAAS,SAClB,CAAI,EAAW,MAAM,EAAQ,EAAE,EAAE,OAAS,YACzC,EAAQ,SAAS,CAChB,KAAM,SACN,QAAS,8CAA8C,EAAK,MAC5D,KAAM,CAAC,QAAS,CAAK,CACtB,CAAC,EACF,IAAK,IAAM,IAAU,CAAC,EAAK,gBAAiB,EAAK,eAAe,EAAG,CAClE,IAAM,EAAa,EAAW,IAAI,CAAM,EAClC,EAAc,EAAW,MAAM,UACnC,GAAc,EAAU,MAAQ,CAClC,EACK,EAOJ,EAAW,OAAS,YACpB,EAAW,OAAS,UACpB,EAAW,OAAS,SAEpB,EAAQ,SAAS,CAChB,KAAM,SACN,QAAS,0CAA0C,IACnD,KAAM,CAAC,QAAS,CAAK,CACtB,CAAC,EACO,GAAe,GACvB,EAAQ,SAAS,CAChB,KAAM,SACN,QAAS,oDAAoD,IAC7D,KAAM,CAAC,QAAS,CAAK,CACtB,CAAC,EApBD,EAAQ,SAAS,CAChB,KAAM,SACN,QAAS,0BAA0B,IACnC,KAAM,CAAC,QAAS,CAAK,CACtB,CAAC,EAiBF,EAAmB,IAClB,GACC,EAAmB,IAAI,CAAM,GAAK,GAAK,CACzC,CACD,CAhCE,CAkCH,IAAK,GAAM,CAAC,EAAQ,KAAW,EAC1B,IAAW,GACd,EAAQ,SAAS,CAChB,KAAM,SACN,QAAS,sDAAsD,IAC/D,KAAM,CAAC,OAAO,CACf,CAAC,CAEJ,CAAC,EAEW,EAAsB,EACjC,OAAO,CACP,SAAU,EACV,aAAc,EACd,YAAa,EACb,UAAW,EACX,SAAU,EAAiB,SAAS,EACpC,SAAU,EACV,MAAO,EACP,eAAgB,EAAa,SAAS,EACtC,uBAAwB,EAAa,SAAS,EAC9C,UAAW,EAAE,OAAO,CAAC,CAAC,SAAS,EAC/B,WAAY,EAAkB,SAAS,EACvC,UAAW,EAAE,OAAO,EACpB,UAAW,EAAE,OAAO,EACpB,WAAY,EAAE,OAAO,CAAC,CAAC,SAAS,CACjC,CAAC,CAAC,CACD,OAAO,EAEI,EAA2B,EACtC,OAAO,CACP,MAAO,EAAI,cAAc,EACzB,QAAS,EACT,MAAO,EACP,SAAU,EACV,OAAQ,EAAe,SAAS,EAChC,UAAW,EAAE,OAAO,CACrB,CAAC,CAAC,CACD,OAAO"}
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,5 +1,5 @@
1
1
  import { ApprovalDecision } from "./constants.mjs";
2
- import { ApprovalWorkflowDescriptor } from "./validators.mjs";
2
+ import { ApprovalCallbackInput, ApprovalWorkflowDescriptor } from "./validators.mjs";
3
3
  import { FunctionReference, FunctionType } from "convex/server";
4
4
  declare namespace workflow_steps_d_exports {
5
5
  export { ApprovalActionStep, ApprovalBranchStep, ApprovalCallbackInput, ApprovalDecisionEvidence, ApprovalDecisionStep, ApprovalDefinition, ApprovalMutationStep, ApprovalNotifyStep, ApprovalWorkflowStep, CreateApprovalHandle, approvalDecisionEventName, callbackReference, compileApprovalDescriptor, evaluateDecisionOutcome, resolveWorkflowStepIndex };
@@ -50,34 +50,11 @@ type ApprovalDecisionEvidence = Readonly<{
50
50
  actorRef: string;
51
51
  decision: ApprovalDecision;
52
52
  }>;
53
- type ApprovalCallbackInput = Readonly<{
54
- runId: string;
55
- scopeRef: string;
56
- resourceType: string;
57
- resourceRef: string;
58
- metadata?: Readonly<Record<string, string>>;
59
- decision?: Readonly<{
60
- stepKey: string;
61
- outcome: ApprovalDecision | 'expired';
62
- evidence: readonly Readonly<{
63
- actorRef: string;
64
- decision: ApprovalDecision;
65
- reason?: string;
66
- decidedAt: number;
67
- }>[];
68
- terminalEvidence?: Readonly<{
69
- actorRef: string;
70
- decision: ApprovalDecision;
71
- reason?: string;
72
- decidedAt: number;
73
- }>;
74
- }>;
75
- }>;
76
53
  declare function approvalDecisionEventName(runId: string, stepKey: string): string;
77
54
  declare function evaluateDecisionOutcome(decisions: readonly ApprovalDecisionEvidence[], approvalsRequired: number): ApprovalDecision | null;
78
55
  declare function resolveWorkflowStepIndex(stepKeys: readonly string[], targetKey: string): number;
79
56
  declare function callbackReference<Type extends FunctionType>(handle: string): FunctionReference<Type, 'internal', ApprovalCallbackInput, unknown>;
80
57
  declare function compileApprovalDescriptor(definition: ApprovalDefinition, createHandle?: CreateApprovalHandle): Promise<Readonly<ApprovalWorkflowDescriptor>>;
81
58
  //#endregion
82
- export { ApprovalActionStep, ApprovalBranchStep, ApprovalCallbackInput, ApprovalDecisionEvidence, ApprovalDecisionStep, ApprovalDefinition, ApprovalMutationStep, ApprovalNotifyStep, ApprovalWorkflowStep, CreateApprovalHandle, approvalDecisionEventName, callbackReference, compileApprovalDescriptor, evaluateDecisionOutcome, resolveWorkflowStepIndex, workflow_steps_d_exports };
59
+ export { ApprovalActionStep, ApprovalBranchStep, type ApprovalCallbackInput, ApprovalDecisionEvidence, ApprovalDecisionStep, ApprovalDefinition, ApprovalMutationStep, ApprovalNotifyStep, ApprovalWorkflowStep, CreateApprovalHandle, approvalDecisionEventName, callbackReference, compileApprovalDescriptor, evaluateDecisionOutcome, resolveWorkflowStepIndex, workflow_steps_d_exports };
83
60
  //# sourceMappingURL=workflow_steps.d.mts.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 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 type ApprovalCallbackInput = Readonly<{\n\trunId: string\n\tscopeRef: string\n\tresourceType: string\n\tresourceRef: string\n\tmetadata?: Readonly<Record<string, string>>\n\tdecision?: Readonly<{\n\t\tstepKey: string\n\t\toutcome: ApprovalDecision | 'expired'\n\t\tevidence: readonly Readonly<{\n\t\t\tactorRef: string\n\t\t\tdecision: ApprovalDecision\n\t\t\treason?: string\n\t\t\tdecidedAt: number\n\t\t}>[]\n\t\tterminalEvidence?: Readonly<{\n\t\t\tactorRef: string\n\t\t\tdecision: ApprovalDecision\n\t\t\treason?: string\n\t\t\tdecidedAt: number\n\t\t}>\n\t}>\n}>\n\nexport function approvalDecisionEventName(\n\trunId: string,\n\tstepKey: string,\n): string {\n\treturn `approval:${runId}:${stepKey}`\n}\n\nexport function evaluateDecisionOutcome(\n\tdecisions: readonly ApprovalDecisionEvidence[],\n\tapprovalsRequired: number,\n): ApprovalDecision | null {\n\tif (decisions.some((decision) => decision.decision === 'rejected'))\n\t\treturn 'rejected'\n\treturn decisions.filter((decision) => decision.decision === 'approved')\n\t\t.length >= approvalsRequired\n\t\t? 'approved'\n\t\t: null\n}\n\nexport function resolveWorkflowStepIndex(\n\tstepKeys: readonly string[],\n\ttargetKey: string,\n): number {\n\tconst index = stepKeys.indexOf(targetKey)\n\tif (index === -1)\n\t\tthrow new Error(`Unknown approval workflow step: ${targetKey}`)\n\treturn index\n}\n\nexport function callbackReference<Type extends FunctionType>(\n\thandle: string,\n): FunctionReference<Type, 'internal', ApprovalCallbackInput, unknown> {\n\treturn handle as unknown as FunctionReference<\n\t\tType,\n\t\t'internal',\n\t\tApprovalCallbackInput,\n\t\tunknown\n\t>\n}\n\nexport async function compileApprovalDescriptor(\n\tdefinition: ApprovalDefinition,\n\tcreateHandle: CreateApprovalHandle = createFunctionHandle,\n): Promise<Readonly<ApprovalWorkflowDescriptor>> {\n\tconst steps = await Promise.all(\n\t\tdefinition.steps.map(async (step) => {\n\t\t\tif (step.kind === 'mutation' || step.kind === 'action') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: step.kind,\n\t\t\t\t\tkey: step.key,\n\t\t\t\t\tcallback: {\n\t\t\t\t\t\tkind: step.kind,\n\t\t\t\t\t\thandle: await createHandle(step.handler),\n\t\t\t\t\t\tretry: step.retry,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (step.kind === 'notify') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: step.kind,\n\t\t\t\t\tkey: step.key,\n\t\t\t\t\tcallback: {\n\t\t\t\t\t\tkind: 'action' as const,\n\t\t\t\t\t\thandle: await createHandle(step.handler),\n\t\t\t\t\t\tretry: step.retry,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (step.kind === 'decision') {\n\t\t\t\treturn {\n\t\t\t\t\t...step,\n\t\t\t\t\tdecisions: [...step.decisions],\n\t\t\t\t\tquorum: { ...step.quorum },\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { ...step }\n\t\t}),\n\t)\n\tconst descriptor = approvalWorkflowDescriptor.parse({\n\t\tschemaVersion: 1,\n\t\tcompatibilityKey: definition.compatibilityKey,\n\t\tname: definition.name,\n\t\tsteps,\n\t})\n\treturn deepFreeze(descriptor)\n}\n\nfunction deepFreeze<Value>(value: Value): Readonly<Value> {\n\tif (value && typeof value === 'object') {\n\t\tObject.freeze(value)\n\t\tfor (const nested of Object.values(value)) deepFreeze(nested)\n\t}\n\treturn value\n}\n"],"mappings":"mHA+FA,SAAgB,EACf,EACA,EACS,CACT,MAAO,YAAY,EAAM,GAAG,GAC7B,CAEA,SAAgB,EACf,EACA,EAC0B,CAG1B,OAFI,EAAU,KAAM,GAAa,EAAS,WAAa,UAAU,EACzD,WACD,EAAU,OAAQ,GAAa,EAAS,WAAa,UAAU,CAAC,CACrE,QAAU,EACT,WACA,IACJ,CAEA,SAAgB,EACf,EACA,EACS,CACT,IAAM,EAAQ,EAAS,QAAQ,CAAS,EACxC,GAAI,IAAU,GACb,MAAU,MAAM,mCAAmC,GAAW,EAC/D,OAAO,CACR,CAEA,SAAgB,EACf,EACsE,CACtE,OAAO,CAMR,CAEA,eAAsB,EACrB,EACA,EAAqC,EACW,CAChD,IAAM,EAAQ,MAAM,QAAQ,IAC3B,EAAW,MAAM,IAAI,KAAO,IACvB,EAAK,OAAS,YAAc,EAAK,OAAS,SACtC,CACN,KAAM,EAAK,KACX,IAAK,EAAK,IACV,SAAU,CACT,KAAM,EAAK,KACX,OAAQ,MAAM,EAAa,EAAK,OAAO,EACvC,MAAO,EAAK,KACb,CACD,EAEG,EAAK,OAAS,SACV,CACN,KAAM,EAAK,KACX,IAAK,EAAK,IACV,SAAU,CACT,KAAM,SACN,OAAQ,MAAM,EAAa,EAAK,OAAO,EACvC,MAAO,EAAK,KACb,CACD,EAEG,EAAK,OAAS,WACV,CACN,GAAG,EACH,UAAW,CAAC,GAAG,EAAK,SAAS,EAC7B,OAAQ,CAAE,GAAG,EAAK,MAAO,CAC1B,EAEM,CAAE,GAAG,CAAK,CACjB,CACF,EAOA,OAAO,EANY,EAA2B,MAAM,CACnD,cAAe,EACf,iBAAkB,EAAW,iBAC7B,KAAM,EAAW,KACjB,OACD,CAC2B,CAAC,CAC7B,CAEA,SAAS,EAAkB,EAA+B,CACzD,GAAI,GAAS,OAAO,GAAU,SAAU,CACvC,OAAO,OAAO,CAAK,EACnB,IAAK,IAAM,KAAU,OAAO,OAAO,CAAK,EAAG,EAAW,CAAM,CAC7D,CACA,OAAO,CACR"}
1
+ {"version":3,"file":"workflow_steps.mjs","names":[],"sources":["../../../src/components/approvals/workflow_steps.ts"],"sourcesContent":["import {\n\tcreateFunctionHandle,\n\ttype FunctionReference,\n\ttype FunctionType,\n} from 'convex/server'\n\nimport type { ApprovalDecision } from './constants'\nimport {\n\tapprovalWorkflowDescriptor,\n\ttype ApprovalCallbackInput,\n\ttype ApprovalWorkflowDescriptor,\n} from './validators'\n\nexport type { ApprovalCallbackInput } from './validators'\n\nexport type ApprovalMutationStep = Readonly<{\n\tkind: 'mutation'\n\tkey: string\n\thandler: FunctionReference<'mutation', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalActionStep = Readonly<{\n\tkind: 'action'\n\tkey: string\n\thandler: FunctionReference<'action', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalNotifyStep = Readonly<{\n\tkind: 'notify'\n\tkey: string\n\thandler: FunctionReference<'action', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalDecisionStep = Readonly<{\n\tkind: 'decision'\n\tkey: string\n\tdecisions: readonly ApprovalDecision[]\n\tquorum: Readonly<{ kind: 'count'; approvals: number }>\n\tmakerChecker: boolean\n\texpiresAfterMs?: number\n}>\n\nexport type ApprovalBranchStep = Readonly<{\n\tkind: 'branch'\n\tkey: string\n\tapprovedStepKey: string\n\trejectedStepKey: string\n}>\n\nexport type ApprovalWorkflowStep =\n\t| ApprovalMutationStep\n\t| ApprovalActionStep\n\t| ApprovalNotifyStep\n\t| ApprovalDecisionStep\n\t| ApprovalBranchStep\n\nexport type ApprovalDefinition = Readonly<{\n\tname: string\n\tcompatibilityKey: string\n\tsteps: readonly ApprovalWorkflowStep[]\n}>\n\nexport type CreateApprovalHandle = (\n\treference: FunctionReference<'mutation' | 'action', 'internal'>,\n) => Promise<string>\n\nexport type ApprovalDecisionEvidence = Readonly<{\n\tactorRef: string\n\tdecision: ApprovalDecision\n}>\n\nexport function approvalDecisionEventName(\n\trunId: string,\n\tstepKey: string,\n): string {\n\treturn `approval:${runId}:${stepKey}`\n}\n\nexport function evaluateDecisionOutcome(\n\tdecisions: readonly ApprovalDecisionEvidence[],\n\tapprovalsRequired: number,\n): ApprovalDecision | null {\n\tif (decisions.some((decision) => decision.decision === 'rejected'))\n\t\treturn 'rejected'\n\treturn decisions.filter((decision) => decision.decision === 'approved')\n\t\t.length >= approvalsRequired\n\t\t? 'approved'\n\t\t: null\n}\n\nexport function resolveWorkflowStepIndex(\n\tstepKeys: readonly string[],\n\ttargetKey: string,\n): number {\n\tconst index = stepKeys.indexOf(targetKey)\n\tif (index === -1)\n\t\tthrow new Error(`Unknown approval workflow step: ${targetKey}`)\n\treturn index\n}\n\nexport function callbackReference<Type extends FunctionType>(\n\thandle: string,\n): FunctionReference<Type, 'internal', ApprovalCallbackInput, unknown> {\n\treturn handle as unknown as FunctionReference<\n\t\tType,\n\t\t'internal',\n\t\tApprovalCallbackInput,\n\t\tunknown\n\t>\n}\n\nexport async function compileApprovalDescriptor(\n\tdefinition: ApprovalDefinition,\n\tcreateHandle: CreateApprovalHandle = createFunctionHandle,\n): Promise<Readonly<ApprovalWorkflowDescriptor>> {\n\tconst steps = await Promise.all(\n\t\tdefinition.steps.map(async (step) => {\n\t\t\tif (step.kind === 'mutation' || step.kind === 'action') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: step.kind,\n\t\t\t\t\tkey: step.key,\n\t\t\t\t\tcallback: {\n\t\t\t\t\t\tkind: step.kind,\n\t\t\t\t\t\thandle: await createHandle(step.handler),\n\t\t\t\t\t\tretry: step.retry,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (step.kind === 'notify') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: step.kind,\n\t\t\t\t\tkey: step.key,\n\t\t\t\t\tcallback: {\n\t\t\t\t\t\tkind: 'action' as const,\n\t\t\t\t\t\thandle: await createHandle(step.handler),\n\t\t\t\t\t\tretry: step.retry,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (step.kind === 'decision') {\n\t\t\t\treturn {\n\t\t\t\t\t...step,\n\t\t\t\t\tdecisions: [...step.decisions],\n\t\t\t\t\tquorum: { ...step.quorum },\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { ...step }\n\t\t}),\n\t)\n\tconst descriptor = approvalWorkflowDescriptor.parse({\n\t\tschemaVersion: 1,\n\t\tcompatibilityKey: definition.compatibilityKey,\n\t\tname: definition.name,\n\t\tsteps,\n\t})\n\treturn deepFreeze(descriptor)\n}\n\nfunction deepFreeze<Value>(value: Value): Readonly<Value> {\n\tif (value && typeof value === 'object') {\n\t\tObject.freeze(value)\n\t\tfor (const nested of Object.values(value)) deepFreeze(nested)\n\t}\n\treturn value\n}\n"],"mappings":"mHA0EA,SAAgB,EACf,EACA,EACS,CACT,MAAO,YAAY,EAAM,GAAG,GAC7B,CAEA,SAAgB,EACf,EACA,EAC0B,CAG1B,OAFI,EAAU,KAAM,GAAa,EAAS,WAAa,UAAU,EACzD,WACD,EAAU,OAAQ,GAAa,EAAS,WAAa,UAAU,CAAC,CACrE,QAAU,EACT,WACA,IACJ,CAEA,SAAgB,EACf,EACA,EACS,CACT,IAAM,EAAQ,EAAS,QAAQ,CAAS,EACxC,GAAI,IAAU,GACb,MAAU,MAAM,mCAAmC,GAAW,EAC/D,OAAO,CACR,CAEA,SAAgB,EACf,EACsE,CACtE,OAAO,CAMR,CAEA,eAAsB,EACrB,EACA,EAAqC,EACW,CAChD,IAAM,EAAQ,MAAM,QAAQ,IAC3B,EAAW,MAAM,IAAI,KAAO,IACvB,EAAK,OAAS,YAAc,EAAK,OAAS,SACtC,CACN,KAAM,EAAK,KACX,IAAK,EAAK,IACV,SAAU,CACT,KAAM,EAAK,KACX,OAAQ,MAAM,EAAa,EAAK,OAAO,EACvC,MAAO,EAAK,KACb,CACD,EAEG,EAAK,OAAS,SACV,CACN,KAAM,EAAK,KACX,IAAK,EAAK,IACV,SAAU,CACT,KAAM,SACN,OAAQ,MAAM,EAAa,EAAK,OAAO,EACvC,MAAO,EAAK,KACb,CACD,EAEG,EAAK,OAAS,WACV,CACN,GAAG,EACH,UAAW,CAAC,GAAG,EAAK,SAAS,EAC7B,OAAQ,CAAE,GAAG,EAAK,MAAO,CAC1B,EAEM,CAAE,GAAG,CAAK,CACjB,CACF,EAOA,OAAO,EANY,EAA2B,MAAM,CACnD,cAAe,EACf,iBAAkB,EAAW,iBAC7B,KAAM,EAAW,KACjB,OACD,CAC2B,CAAC,CAC7B,CAEA,SAAS,EAAkB,EAA+B,CACzD,GAAI,GAAS,OAAO,GAAU,SAAU,CACvC,OAAO,OAAO,CAAK,EACnB,IAAK,IAAM,KAAU,OAAO,OAAO,CAAK,EAAG,EAAW,CAAM,CAC7D,CACA,OAAO,CACR"}
@@ -1,4 +1,4 @@
1
- import _default from "../schema.mjs";
1
+ import _default from "../schema.js";
2
2
  import { DataModelFromSchemaDefinition, DocumentByName, SystemTableNames, TableNamesInDataModel } from "convex/server";
3
3
  import { GenericId } from "convex/values";
4
4
  //#region src/components/foundation/_generated/dataModel.d.ts
@@ -1,2 +1 @@
1
1
  import{defineSchema as e}from"convex/server";var t=e({});export{t as default};
2
- //# sourceMappingURL=schema.mjs.map
package/docs/approvals.md CHANGED
@@ -16,6 +16,20 @@ import approvals from 'cvx-kit/components/approvals/convex.config'
16
16
  app.use(approvals)
17
17
  ```
18
18
 
19
+ After installation or upgrade, deploy once and run the component health query
20
+ from a host wrapper:
21
+
22
+ ```ts
23
+ return ctx.runQuery(components.approvals.health.check, {})
24
+ ```
25
+
26
+ It returns the installed approvals schema version and required index names. It
27
+ also executes reads through
28
+ `approvalDecisions.by_runId_and_decidedAt` and
29
+ `approvalDecisions.by_runId_and_stepKey_and_actor_actorRef`, failing with an
30
+ installation-specific diagnostic if the packaged component schema was not
31
+ deployed.
32
+
19
33
  The component mounts its own private `workflow` and `auditLog` children. Do
20
34
  **not** re-register those children at the app root under the same identity,
21
35
  and never reach into `components.approvals.workflow` / `.auditLog` from host
@@ -99,10 +113,14 @@ const { runId } = await publishApproval.start(ctx, {
99
113
  // decide — a manager approves or rejects
100
114
  await publishApproval.decide(ctx, { runId, decision: 'approved', reason }, ctx.actor)
101
115
 
102
- // observe
116
+ // observe from a host query
103
117
  await publishApproval.status(ctx, runId) // pending | approved | rejected | expired | canceled
104
118
  await publishApproval.evidence(ctx, runId) // per-step decision evidence
105
- await publishApproval.list(ctx, { scopeRef, ... })
119
+ await publishApproval.list(ctx, {
120
+ scopeRef,
121
+ state: 'pending',
122
+ paginationOpts: { cursor: null, numItems: 20 },
123
+ })
106
124
 
107
125
  // manage
108
126
  await publishApproval.cancel(ctx, runId, ctx.actor)
@@ -119,6 +137,8 @@ Because the component cannot know your tables, the handler must re-establish
119
137
  trust:
120
138
 
121
139
  ```ts
140
+ import { approvalCallbackArgs } from 'cvx-kit/components/approvals'
141
+
122
142
  export const applyDecision = systemMutation({
123
143
  args: approvalCallbackArgs,
124
144
  handler: async (ctx, input) => {
@@ -136,6 +156,34 @@ export const applyDecision = systemMutation({
136
156
  That `approvalRunId` check is the idempotency/staleness guard from the origin
137
157
  app — copy it.
138
158
 
159
+ For branch callbacks, `decision.evidence` contains
160
+ `{ stepKey, actorRef, decision, reason?, decidedAt }` items. Use the exported
161
+ `approvalCallbackArgs` validator instead of duplicating that protocol boundary.
162
+
163
+ ### Paginated inboxes
164
+
165
+ Expose the configured workflow through a host query; component pagination uses
166
+ `convex-helpers` internally because Convex's built-in `.paginate()` is not
167
+ available inside components:
168
+
169
+ ```ts
170
+ export const approvalInbox = authQuery({
171
+ args: {
172
+ state: z.enum(['pending', 'approved', 'rejected', 'expired', 'canceled']).optional(),
173
+ paginationOpts: convexToZod(paginationOptsValidator),
174
+ },
175
+ handler: (ctx, args) => publishApproval.list(ctx, {
176
+ scopeRef: ctx.org.organizationId,
177
+ ...args,
178
+ }),
179
+ })
180
+ ```
181
+
182
+ The result keeps Convex's standard pagination shape, so React consumers can
183
+ call `usePaginatedQuery(api.approvals.approvalInbox, { state: 'pending' },
184
+ { initialNumItems: 20 })`. Pass each returned cursor only back to the same
185
+ scope/state query.
186
+
139
187
  ## Rules and footguns
140
188
 
141
189
  1. Root facade (`convex/approvals.ts`) instantiates the client; workflow
package/docs/upgrading.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Upgrading to 0.1.0 — the new way of things
2
2
 
3
+ ## Approvals packaging repair (0.1.x patch)
4
+
5
+ `cvx-kit@0.1.0` published component schemas as `schema.mjs`, which Convex does
6
+ not discover for packaged components. Upgrade to the patch containing this
7
+ repair, remove any `patchedDependencies` entry that copied or renamed the
8
+ approvals schema, reinstall from a clean lockfile state, and run one normal
9
+ deployment:
10
+
11
+ ```bash
12
+ bun install
13
+ bunx convex dev --once
14
+ ```
15
+
16
+ The repaired package publishes only `schema.js` and `schema.d.ts` for each
17
+ component. Do not retain both `.mjs` and `.js`; Convex canonicalizes them to the
18
+ same module and rejects the deployment with `CanonicalizationConflict`.
19
+
20
+ This upgrade does not change table fields or index definitions, so a second
21
+ deployment is not required for staged index backfills. Existing deployments
22
+ that never received the component schema will create the tables and indexes on
23
+ the first repaired deployment. Confirm readiness through
24
+ `components.approvals.health.check` before resuming approval traffic.
25
+
3
26
  The migration file for consumers on 0.0.x. Each section: the old way you were
4
27
  hand-writing, the 0.1.0 way, and what stays unchanged. Nothing existing
5
28
  breaks in this release except one boundary-mask improvement (see the last
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cvx-kit",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Reusable Convex application kit: foundation kernels, approvals component, zod table boundaries, WorkOS-backed auth function constructors, and audited command protocol.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -90,7 +90,10 @@
90
90
  "scripts": {
91
91
  "build": "vp pack && node scripts/convex-config-bridge.mjs && publint",
92
92
  "typecheck": "tsc --noEmit",
93
- "test": "vp test run",
93
+ "test": "vp test run --exclude test/package-contents.test.mjs",
94
+ "test:package": "bun run build && vp test run test/package-contents.test.mjs",
95
+ "test:package:smoke:bun": "bun scripts/smoke-packed-components.mjs bun",
96
+ "test:package:smoke:npm": "node scripts/smoke-packed-components.mjs npm",
94
97
  "docs:sync": "rm -f docs/*.md && cp src/docs/*.md docs/",
95
98
  "prepublishOnly": "bun run build && bun run typecheck && bun run test && bun run docs:sync",
96
99
  "build:codegen": "convex codegen --component-dir ./src/components/foundation && convex codegen --component-dir ./src/components/approvals && vp pack",
@@ -2,7 +2,13 @@
2
2
  // is literally named `convex.config.js` (see DEFINITION_FILENAME_JS in the
3
3
  // convex CLI). vp pack emits .mjs, so this postbuild step materializes the
4
4
  // compiled definition as convex.config.js in each component's dist directory.
5
- import { readFileSync, writeFileSync } from 'node:fs'
5
+ import {
6
+ readFileSync,
7
+ readdirSync,
8
+ renameSync,
9
+ rmSync,
10
+ writeFileSync,
11
+ } from 'node:fs'
6
12
  import { dirname, join } from 'node:path'
7
13
  import { fileURLToPath } from 'node:url'
8
14
 
@@ -10,8 +16,40 @@ const root = join(dirname(fileURLToPath(import.meta.url)), '..')
10
16
 
11
17
  for (const component of ['foundation', 'approvals']) {
12
18
  const directory = join(root, 'dist', 'components', component)
13
- const compiled = readFileSync(join(directory, 'convex.config.mjs'), 'utf8')
19
+ const config = readFileSync(join(directory, 'convex.config.mjs'), 'utf8')
14
20
  .replace(/\/\/# sourceMappingURL=.*\n?/, '')
15
- writeFileSync(join(directory, 'convex.config.js'), compiled)
21
+ writeFileSync(join(directory, 'convex.config.js'), config)
22
+
23
+ // Component implementations are discovered from schema.ts/schema.js only.
24
+ // Keeping schema.mjs beside schema.js is also invalid because Convex
25
+ // canonicalizes both extensions to the same module path.
26
+ const schema = readFileSync(join(directory, 'schema.mjs'), 'utf8').replace(
27
+ /\/\/# sourceMappingURL=.*\n?/,
28
+ '',
29
+ )
30
+ writeFileSync(join(directory, 'schema.js'), schema)
31
+ for (const filename of readdirSync(directory)) {
32
+ if (!filename.endsWith('.mjs')) continue
33
+ const path = join(directory, filename)
34
+ const source = readFileSync(path, 'utf8')
35
+ const bridged = source.replaceAll('./schema.mjs', './schema.js')
36
+ if (bridged !== source) writeFileSync(path, bridged)
37
+ }
38
+ renameSync(join(directory, 'schema.d.mts'), join(directory, 'schema.d.ts'))
39
+ const dataModelDeclaration = join(
40
+ directory,
41
+ '_generated',
42
+ 'dataModel.d.mts',
43
+ )
44
+ writeFileSync(
45
+ dataModelDeclaration,
46
+ readFileSync(dataModelDeclaration, 'utf8').replace(
47
+ '../schema.mjs',
48
+ '../schema.js',
49
+ ),
50
+ )
51
+ rmSync(join(directory, 'schema.mjs'))
52
+ rmSync(join(directory, 'schema.mjs.map'))
16
53
  console.log(`bridged dist/components/${component}/convex.config.js`)
54
+ console.log(`bridged dist/components/${component}/schema.js`)
17
55
  }