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.
- package/CHANGELOG.md +28 -0
- package/README.md +3 -0
- package/dist/components/approvals/_generated/api.d.mts +2 -0
- package/dist/components/approvals/_generated/api.mjs.map +1 -1
- package/dist/components/approvals/_generated/component.d.mts +7 -0
- package/dist/components/approvals/_generated/dataModel.d.mts +1 -1
- package/dist/components/approvals/client.d.mts +4 -4
- package/dist/components/approvals/client.mjs +1 -1
- package/dist/components/approvals/client.mjs.map +1 -1
- package/dist/components/approvals/constants.d.mts +8 -2
- package/dist/components/approvals/constants.mjs +1 -1
- package/dist/components/approvals/constants.mjs.map +1 -1
- package/dist/components/approvals/decisions.mjs +1 -1
- package/dist/components/approvals/decisions.mjs.map +1 -1
- package/dist/components/approvals/health.d.mts +15 -0
- package/dist/components/approvals/health.mjs +2 -0
- package/dist/components/approvals/health.mjs.map +1 -0
- package/dist/components/approvals/requests.mjs +1 -1
- package/dist/components/approvals/requests.mjs.map +1 -1
- package/dist/components/approvals/schema.js +1 -0
- package/dist/components/approvals/validators.d.mts +119 -2
- package/dist/components/approvals/validators.mjs +1 -1
- package/dist/components/approvals/validators.mjs.map +1 -1
- package/dist/components/approvals/workflow_steps.d.mts +2 -25
- package/dist/components/approvals/workflow_steps.mjs.map +1 -1
- package/dist/components/foundation/_generated/dataModel.d.mts +1 -1
- package/dist/components/foundation/{schema.mjs → schema.js} +0 -1
- package/docs/approvals.md +50 -2
- package/docs/upgrading.md +23 -0
- package/package.json +5 -2
- package/scripts/convex-config-bridge.mjs +41 -3
- package/scripts/smoke-packed-components.mjs +374 -0
- package/src/test.ts +2 -2
- package/dist/components/approvals/schema.mjs +0 -2
- package/dist/components/approvals/schema.mjs.map +0 -1
- package/dist/components/foundation/schema.mjs.map +0 -1
- /package/dist/components/approvals/{schema.d.mts → schema.d.ts} +0 -0
- /package/dist/components/foundation/{schema.d.mts → schema.d.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,34 @@ export-name API audit) — until then, the six 0.1.0 helper surfaces may still
|
|
|
10
10
|
be renamed in a minor. Every release tag gets a GitHub Release whose notes
|
|
11
11
|
are this file's matching section.
|
|
12
12
|
|
|
13
|
+
## [Unreleased]
|
|
14
|
+
|
|
15
|
+
## [0.1.1] - 2026-08-17
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Package Convex component schemas exclusively as discoverable `schema.js` and
|
|
20
|
+
`schema.d.ts` files. This restores the approvals tables and the
|
|
21
|
+
`approvalDecisions.by_runId_and_decidedAt` and
|
|
22
|
+
`approvalDecisions.by_runId_and_stepKey_and_actor_actorRef` indexes without
|
|
23
|
+
consumer `patchedDependencies` workarounds or canonicalization conflicts.
|
|
24
|
+
- Keep compiled component imports aligned with the discoverable `schema.js`
|
|
25
|
+
filename when runtime functions use the schema directly.
|
|
26
|
+
- Include `stepKey` in the public approval callback evidence contract and
|
|
27
|
+
export canonical Zod and Convex callback validators for strict consumers.
|
|
28
|
+
- Use component-safe cursor pagination for approval request lists instead of
|
|
29
|
+
Convex's app-only built-in `.paginate()` implementation.
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- Packed-tarball content checks and clean anonymous-deployment smoke tests for
|
|
34
|
+
every bundled component under both Bun and npm installation.
|
|
35
|
+
- `components.approvals.health.check`, which verifies the approvals schema
|
|
36
|
+
version and required decision indexes with an actionable failure diagnostic.
|
|
37
|
+
- Supported Convex, Zod, Bun, Node.js, and npm versions plus an approvals
|
|
38
|
+
packaging migration note.
|
|
39
|
+
- Packed callback and paginated-list smoke coverage through real host wrappers.
|
|
40
|
+
|
|
13
41
|
## [0.1.0] - 2026-08-14
|
|
14
42
|
|
|
15
43
|
The big one: the full helper surface, in one release.
|
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ import { workflow_steps_d_exports } from "../workflow_steps.mjs";
|
|
|
5
5
|
import { client_d_exports } from "../client.mjs";
|
|
6
6
|
import { decisions_d_exports } from "../decisions.mjs";
|
|
7
7
|
import { functions_d_exports } from "../functions.mjs";
|
|
8
|
+
import { health_d_exports } from "../health.mjs";
|
|
8
9
|
import { requests_d_exports } from "../requests.mjs";
|
|
9
10
|
import { workflow_d_exports } from "../workflow.mjs";
|
|
10
11
|
import { ApiFromModules, FilterApi, FunctionReference } from "convex/server";
|
|
@@ -15,6 +16,7 @@ declare const fullApi: ApiFromModules<{
|
|
|
15
16
|
constants: typeof constants_d_exports;
|
|
16
17
|
decisions: typeof decisions_d_exports;
|
|
17
18
|
functions: typeof functions_d_exports;
|
|
19
|
+
health: typeof health_d_exports;
|
|
18
20
|
requests: typeof requests_d_exports;
|
|
19
21
|
validators: typeof validators_d_exports;
|
|
20
22
|
workflow: typeof workflow_d_exports;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.mjs","names":[],"sources":["../../../../src/components/approvals/_generated/api.ts"],"sourcesContent":["/* eslint-disable */\n/**\n * Generated `api` utility.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport type * as audit from \"../audit.js\";\nimport type * as client from \"../client.js\";\nimport type * as constants from \"../constants.js\";\nimport type * as decisions from \"../decisions.js\";\nimport type * as functions from \"../functions.js\";\nimport type * as requests from \"../requests.js\";\nimport type * as validators from \"../validators.js\";\nimport type * as workflow from \"../workflow.js\";\nimport type * as workflow_steps from \"../workflow_steps.js\";\n\nimport type {\n ApiFromModules,\n FilterApi,\n FunctionReference,\n} from \"convex/server\";\nimport { anyApi, componentsGeneric } from \"convex/server\";\n\nconst fullApi: ApiFromModules<{\n audit: typeof audit;\n client: typeof client;\n constants: typeof constants;\n decisions: typeof decisions;\n functions: typeof functions;\n requests: typeof requests;\n validators: typeof validators;\n workflow: typeof workflow;\n workflow_steps: typeof workflow_steps;\n}> = anyApi as any;\n\n/**\n * A utility for referencing Convex functions in your app's public API.\n *\n * Usage:\n * ```js\n * const myFunctionReference = api.myModule.myFunction;\n * ```\n */\nexport const api: FilterApi<\n typeof fullApi,\n FunctionReference<any, \"public\">\n> = anyApi as any;\n\n/**\n * A utility for referencing Convex functions in your app's internal API.\n *\n * Usage:\n * ```js\n * const myFunctionReference = internal.myModule.myFunction;\n * ```\n */\nexport const internal: FilterApi<\n typeof fullApi,\n FunctionReference<any, \"internal\">\n> = anyApi as any;\n\nexport const components = componentsGeneric() as unknown as {\n workflow: import(\"@convex-dev/workflow/_generated/component.js\").ComponentApi<\"workflow\">;\n auditLog: import(\"convex-audit-log/_generated/component.js\").ComponentApi<\"auditLog\">;\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"api.mjs","names":[],"sources":["../../../../src/components/approvals/_generated/api.ts"],"sourcesContent":["/* eslint-disable */\n/**\n * Generated `api` utility.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport type * as audit from \"../audit.js\";\nimport type * as client from \"../client.js\";\nimport type * as constants from \"../constants.js\";\nimport type * as decisions from \"../decisions.js\";\nimport type * as functions from \"../functions.js\";\nimport type * as health from \"../health.js\";\nimport type * as requests from \"../requests.js\";\nimport type * as validators from \"../validators.js\";\nimport type * as workflow from \"../workflow.js\";\nimport type * as workflow_steps from \"../workflow_steps.js\";\n\nimport type {\n ApiFromModules,\n FilterApi,\n FunctionReference,\n} from \"convex/server\";\nimport { anyApi, componentsGeneric } from \"convex/server\";\n\nconst fullApi: ApiFromModules<{\n audit: typeof audit;\n client: typeof client;\n constants: typeof constants;\n decisions: typeof decisions;\n functions: typeof functions;\n health: typeof health;\n requests: typeof requests;\n validators: typeof validators;\n workflow: typeof workflow;\n workflow_steps: typeof workflow_steps;\n}> = anyApi as any;\n\n/**\n * A utility for referencing Convex functions in your app's public API.\n *\n * Usage:\n * ```js\n * const myFunctionReference = api.myModule.myFunction;\n * ```\n */\nexport const api: FilterApi<\n typeof fullApi,\n FunctionReference<any, \"public\">\n> = anyApi as any;\n\n/**\n * A utility for referencing Convex functions in your app's internal API.\n *\n * Usage:\n * ```js\n * const myFunctionReference = internal.myModule.myFunction;\n * ```\n */\nexport const internal: FilterApi<\n typeof fullApi,\n FunctionReference<any, \"internal\">\n> = anyApi as any;\n\nexport const components = componentsGeneric() as unknown as {\n workflow: import(\"@convex-dev/workflow/_generated/component.js\").ComponentApi<\"workflow\">;\n auditLog: import(\"convex-audit-log/_generated/component.js\").ComponentApi<\"auditLog\">;\n};\n"],"mappings":"8DAiDA,MAAa,EAGT,EAUS,EAGT,EAES,EAAa,EAAkB"}
|
|
@@ -52,6 +52,13 @@ type ComponentApi<Name extends string | undefined = string | undefined> = {
|
|
|
52
52
|
stepKey: string;
|
|
53
53
|
}>, Name>;
|
|
54
54
|
};
|
|
55
|
+
health: {
|
|
56
|
+
check: FunctionReference<"query", "internal", Record<string, never>, {
|
|
57
|
+
requiredIndexes: Array<string>;
|
|
58
|
+
schemaVersion: 1;
|
|
59
|
+
status: "ready";
|
|
60
|
+
}, Name>;
|
|
61
|
+
};
|
|
55
62
|
requests: {
|
|
56
63
|
cancel: FunctionReference<"mutation", "internal", {
|
|
57
64
|
actor: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import _default from "../schema.
|
|
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/approvals/_generated/dataModel.d.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ComponentApi } from "./_generated/component.mjs";
|
|
2
2
|
import { ApprovalDecision } from "./constants.mjs";
|
|
3
|
-
import { ApprovalActor, ApprovalMetadata, ApprovalWorkflowDescriptor } from "./validators.mjs";
|
|
4
|
-
import { ApprovalActionStep, ApprovalBranchStep,
|
|
3
|
+
import { ApprovalActor, ApprovalCallbackInput, ApprovalMetadata, ApprovalWorkflowDescriptor, approvalCallbackArgs, approvalCallbackInput } from "./validators.mjs";
|
|
4
|
+
import { ApprovalActionStep, ApprovalBranchStep, ApprovalDecisionStep, ApprovalDefinition, ApprovalMutationStep, ApprovalNotifyStep, ApprovalWorkflowStep, CreateApprovalHandle } from "./workflow_steps.mjs";
|
|
5
5
|
import { FunctionArgs, FunctionReference, FunctionReturnType, GenericDataModel, GenericMutationCtx, GenericQueryCtx } from "convex/server";
|
|
6
6
|
declare namespace client_d_exports {
|
|
7
|
-
export { ApprovalAuditCleanupInput, ApprovalAuditHistoryInput, ApprovalDecisionInput, ApprovalEvidenceItem, ApprovalStartInput, ApprovalStatus, Approvals, ApprovalsOptions, ConfiguredApprovalWorkflow };
|
|
7
|
+
export { ApprovalAuditCleanupInput, ApprovalAuditHistoryInput, ApprovalCallbackInput, ApprovalDecisionInput, ApprovalEvidenceItem, ApprovalStartInput, ApprovalStatus, Approvals, ApprovalsOptions, ConfiguredApprovalWorkflow, approvalCallbackArgs, approvalCallbackInput };
|
|
8
8
|
}
|
|
9
9
|
type ApprovalsOptions = Readonly<{
|
|
10
10
|
createHandle?: CreateApprovalHandle;
|
|
@@ -93,5 +93,5 @@ declare class ConfiguredApprovalWorkflow<Component extends ComponentApi = Compon
|
|
|
93
93
|
restart<DataModel extends GenericDataModel>(ctx: GenericMutationCtx<DataModel>, runId: string): Promise<FunctionReturnType<Component['requests']['restart']>>;
|
|
94
94
|
}
|
|
95
95
|
//#endregion
|
|
96
|
-
export { ApprovalAuditCleanupInput, ApprovalAuditHistoryInput, ApprovalDecisionInput, ApprovalEvidenceItem, ApprovalStartInput, ApprovalStatus, Approvals, ApprovalsOptions, ConfiguredApprovalWorkflow, client_d_exports };
|
|
96
|
+
export { ApprovalAuditCleanupInput, ApprovalAuditHistoryInput, type ApprovalCallbackInput, ApprovalDecisionInput, ApprovalEvidenceItem, ApprovalStartInput, ApprovalStatus, Approvals, ApprovalsOptions, ConfiguredApprovalWorkflow, approvalCallbackArgs, approvalCallbackInput, client_d_exports };
|
|
97
97
|
//# sourceMappingURL=client.d.mts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{approvalCallbackArgs as e,approvalCallbackInput as t}from"./validators.mjs";import{compileApprovalDescriptor as n}from"./workflow_steps.mjs";var r=class{component;createHandle;constructor(e,t={}){this.component=e,this.createHandle=t.createHandle}cleanupAudit(e,t){return e.runMutation(this.component.audit.cleanup,t)}mutation(e,t){return Object.freeze({kind:`mutation`,key:e,handler:t.handler,retry:t.retry??!1})}action(e,t){return Object.freeze({kind:`action`,key:e,handler:t.handler,retry:t.retry??!1})}notify(e,t){return Object.freeze({kind:`notify`,key:e,handler:t.handler,retry:t.retry??!1})}decision(e,t){return Object.freeze({kind:`decision`,key:e,decisions:Object.freeze([...t.decisions]),quorum:Object.freeze({...t.quorum}),makerChecker:t.makerChecker??!1,...t.expiresAfterMs===void 0?{}:{expiresAfterMs:t.expiresAfterMs}})}branch(e,t){return Object.freeze({kind:`branch`,key:e,...t})}define(e){let t=a({name:e.name,compatibilityKey:e.compatibilityKey??e.name,steps:e.steps});return new i(this.component,t,this.createHandle)}},i=class{name;compatibilityKey;steps;component;definition;createHandle;constructor(e,t,n){this.component=e,this.definition=t,this.name=t.name,this.compatibilityKey=t.compatibilityKey,this.steps=t.steps,this.createHandle=n,Object.freeze(this)}descriptor(){return n(this.definition,this.createHandle)}async start(e,t){return e.runMutation(this.component.requests.start,{...t,workflow:await this.descriptor()})}decide(e,t,n){return e.runMutation(this.component.decisions.decide,{...t,actor:n,compatibilityKey:this.compatibilityKey})}evidence(e,t){return e.runQuery(this.component.decisions.list,{runId:t})}auditHistory(e,t){return e.runQuery(this.component.audit.history,t)}cancel(e,t,n){return e.runMutation(this.component.requests.cancel,{runId:t,actor:n,compatibilityKey:this.compatibilityKey})}status(e,t){return e.runQuery(this.component.requests.status,{runId:t,compatibilityKey:this.compatibilityKey})}list(e,t){return e.runQuery(this.component.requests.list,t)}restart(e,t){return e.runMutation(this.component.requests.restart,{runId:t,compatibilityKey:this.compatibilityKey})}};function a(e){return Object.freeze({...e,steps:Object.freeze(e.steps.map(e=>e.kind===`decision`?Object.freeze({...e,decisions:Object.freeze([...e.decisions]),quorum:Object.freeze({...e.quorum})}):Object.freeze({...e})))})}export{r as Approvals,i as ConfiguredApprovalWorkflow,e as approvalCallbackArgs,t as approvalCallbackInput};
|
|
2
2
|
//# sourceMappingURL=client.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.mjs","names":[],"sources":["../../../src/components/approvals/client.ts"],"sourcesContent":["import type {\n\tFunctionArgs,\n\tFunctionReference,\n\tFunctionReturnType,\n\tGenericDataModel,\n\tGenericMutationCtx,\n\tGenericQueryCtx,\n} from 'convex/server'\n\nimport type { ComponentApi } from './_generated/component'\nimport type { ApprovalDecision } from './constants'\nimport type {\n\tApprovalActor,\n\tApprovalMetadata,\n\tApprovalWorkflowDescriptor,\n} from './validators'\nimport {\n\tcompileApprovalDescriptor,\n\ttype ApprovalActionStep,\n\ttype ApprovalBranchStep,\n\ttype ApprovalDecisionStep,\n\ttype ApprovalDefinition,\n\ttype ApprovalMutationStep,\n\ttype ApprovalNotifyStep,\n\ttype ApprovalWorkflowStep,\n\ttype ApprovalCallbackInput,\n\ttype CreateApprovalHandle,\n} from './workflow_steps'\n\nexport type ApprovalsOptions = Readonly<{\n\tcreateHandle?: CreateApprovalHandle\n}>\n\nexport type ApprovalStartInput = Readonly<{\n\tscopeRef: string\n\tresourceType: string\n\tresourceRef: string\n\trequester: ApprovalActor\n\tmetadata?: ApprovalMetadata\n}>\n\nexport type ApprovalDecisionInput = Readonly<{\n\trunId: string\n\tdecision: ApprovalDecision\n\treason?: string\n}>\n\nexport type ApprovalEvidenceItem = Readonly<{\n\tstepKey: string\n\tactorRef: string\n\tdecision: ApprovalDecision\n\treason?: string\n\tdecidedAt: number\n}>\n\nexport type ApprovalAuditHistoryInput = Readonly<{\n\tresourceType: string\n\tresourceRef: string\n\tlimit?: number\n}>\n\nexport type ApprovalAuditCleanupInput = Readonly<{\n\tolderThanDays: number\n\tbatchSize: number\n}>\n\nexport type ApprovalStatus = FunctionReturnType<\n\tComponentApi['requests']['status']\n>\n\nexport class Approvals<Component extends ComponentApi = ComponentApi> {\n\tprivate readonly component: Component\n\tprivate readonly createHandle: CreateApprovalHandle | undefined\n\n\tconstructor(component: Component, options: ApprovalsOptions = {}) {\n\t\tthis.component = component\n\t\tthis.createHandle = options.createHandle\n\t}\n\n\tcleanupAudit<DataModel extends GenericDataModel>(\n\t\tctx: GenericMutationCtx<DataModel>,\n\t\tinput: ApprovalAuditCleanupInput,\n\t): Promise<FunctionReturnType<Component['audit']['cleanup']>> {\n\t\treturn ctx.runMutation(this.component.audit.cleanup, input)\n\t}\n\n\tmutation(\n\t\tkey: string,\n\t\toptions: Readonly<{\n\t\t\thandler: FunctionReference<\n\t\t\t\t'mutation',\n\t\t\t\t'internal',\n\t\t\t\tApprovalCallbackInput,\n\t\t\t\tunknown\n\t\t\t>\n\t\t\tretry?: boolean\n\t\t}>,\n\t): ApprovalMutationStep {\n\t\treturn Object.freeze({\n\t\t\tkind: 'mutation',\n\t\t\tkey,\n\t\t\thandler: options.handler,\n\t\t\tretry: options.retry ?? false,\n\t\t})\n\t}\n\n\taction(\n\t\tkey: string,\n\t\toptions: Readonly<{\n\t\t\thandler: FunctionReference<\n\t\t\t\t'action',\n\t\t\t\t'internal',\n\t\t\t\tApprovalCallbackInput,\n\t\t\t\tunknown\n\t\t\t>\n\t\t\tretry?: boolean\n\t\t}>,\n\t): ApprovalActionStep {\n\t\treturn Object.freeze({\n\t\t\tkind: 'action',\n\t\t\tkey,\n\t\t\thandler: options.handler,\n\t\t\tretry: options.retry ?? false,\n\t\t})\n\t}\n\n\tnotify(\n\t\tkey: string,\n\t\toptions: Readonly<{\n\t\t\thandler: FunctionReference<\n\t\t\t\t'action',\n\t\t\t\t'internal',\n\t\t\t\tApprovalCallbackInput,\n\t\t\t\tunknown\n\t\t\t>\n\t\t\tretry?: boolean\n\t\t}>,\n\t): ApprovalNotifyStep {\n\t\treturn Object.freeze({\n\t\t\tkind: 'notify',\n\t\t\tkey,\n\t\t\thandler: options.handler,\n\t\t\tretry: options.retry ?? false,\n\t\t})\n\t}\n\n\tdecision(\n\t\tkey: string,\n\t\toptions: Readonly<{\n\t\t\tdecisions: readonly ApprovalDecision[]\n\t\t\tquorum: Readonly<{ kind: 'count'; approvals: number }>\n\t\t\tmakerChecker?: boolean\n\t\t\texpiresAfterMs?: number\n\t\t}>,\n\t): ApprovalDecisionStep {\n\t\treturn Object.freeze({\n\t\t\tkind: 'decision',\n\t\t\tkey,\n\t\t\tdecisions: Object.freeze([...options.decisions]),\n\t\t\tquorum: Object.freeze({ ...options.quorum }),\n\t\t\tmakerChecker: options.makerChecker ?? false,\n\t\t\t...(options.expiresAfterMs === undefined\n\t\t\t\t? {}\n\t\t\t\t: { expiresAfterMs: options.expiresAfterMs }),\n\t\t})\n\t}\n\n\tbranch(\n\t\tkey: string,\n\t\toptions: Readonly<{\n\t\t\tapprovedStepKey: string\n\t\t\trejectedStepKey: string\n\t\t}>,\n\t): ApprovalBranchStep {\n\t\treturn Object.freeze({ kind: 'branch', key, ...options })\n\t}\n\n\tdefine(\n\t\tinput: Readonly<{\n\t\t\tname: string\n\t\t\tcompatibilityKey?: string\n\t\t\tsteps: readonly ApprovalWorkflowStep[]\n\t\t}>,\n\t): ConfiguredApprovalWorkflow<Component> {\n\t\tconst definition = freezeDefinition({\n\t\t\tname: input.name,\n\t\t\tcompatibilityKey: input.compatibilityKey ?? input.name,\n\t\t\tsteps: input.steps,\n\t\t})\n\t\treturn new ConfiguredApprovalWorkflow(\n\t\t\tthis.component,\n\t\t\tdefinition,\n\t\t\tthis.createHandle,\n\t\t)\n\t}\n}\n\nexport class ConfiguredApprovalWorkflow<\n\tComponent extends ComponentApi = ComponentApi,\n> {\n\treadonly name: string\n\treadonly compatibilityKey: string\n\treadonly steps: readonly ApprovalWorkflowStep[]\n\n\tprivate readonly component: Component\n\tprivate readonly definition: ApprovalDefinition\n\tprivate readonly createHandle: CreateApprovalHandle | undefined\n\n\tconstructor(\n\t\tcomponent: Component,\n\t\tdefinition: ApprovalDefinition,\n\t\tcreateHandle?: CreateApprovalHandle,\n\t) {\n\t\tthis.component = component\n\t\tthis.definition = definition\n\t\tthis.name = definition.name\n\t\tthis.compatibilityKey = definition.compatibilityKey\n\t\tthis.steps = definition.steps\n\t\tthis.createHandle = createHandle\n\t\tObject.freeze(this)\n\t}\n\n\tdescriptor(): Promise<Readonly<ApprovalWorkflowDescriptor>> {\n\t\treturn compileApprovalDescriptor(this.definition, this.createHandle)\n\t}\n\n\tasync start<DataModel extends GenericDataModel>(\n\t\tctx: GenericMutationCtx<DataModel>,\n\t\tinput: ApprovalStartInput,\n\t): Promise<FunctionReturnType<Component['requests']['start']>> {\n\t\treturn ctx.runMutation(this.component.requests.start, {\n\t\t\t...input,\n\t\t\tworkflow: await this.descriptor(),\n\t\t})\n\t}\n\n\tdecide<DataModel extends GenericDataModel>(\n\t\tctx: GenericMutationCtx<DataModel>,\n\t\tinput: ApprovalDecisionInput,\n\t\tactor: ApprovalActor,\n\t): Promise<FunctionReturnType<Component['decisions']['decide']>> {\n\t\treturn ctx.runMutation(this.component.decisions.decide, {\n\t\t\t...input,\n\t\t\tactor,\n\t\t\tcompatibilityKey: this.compatibilityKey,\n\t\t})\n\t}\n\n\tevidence<DataModel extends GenericDataModel>(\n\t\tctx: GenericQueryCtx<DataModel>,\n\t\trunId: string,\n\t): Promise<FunctionReturnType<Component['decisions']['list']>> {\n\t\treturn ctx.runQuery(this.component.decisions.list, { runId })\n\t}\n\n\tauditHistory<DataModel extends GenericDataModel>(\n\t\tctx: GenericQueryCtx<DataModel>,\n\t\tinput: ApprovalAuditHistoryInput,\n\t): Promise<FunctionReturnType<Component['audit']['history']>> {\n\t\treturn ctx.runQuery(this.component.audit.history, input)\n\t}\n\n\tcancel<DataModel extends GenericDataModel>(\n\t\tctx: GenericMutationCtx<DataModel>,\n\t\trunId: string,\n\t\tactor: ApprovalActor,\n\t): Promise<FunctionReturnType<Component['requests']['cancel']>> {\n\t\treturn ctx.runMutation(this.component.requests.cancel, {\n\t\t\trunId,\n\t\t\tactor,\n\t\t\tcompatibilityKey: this.compatibilityKey,\n\t\t})\n\t}\n\n\tstatus<DataModel extends GenericDataModel>(\n\t\tctx: GenericQueryCtx<DataModel>,\n\t\trunId: string,\n\t): Promise<ApprovalStatus | null> {\n\t\treturn ctx.runQuery(this.component.requests.status, {\n\t\t\trunId,\n\t\t\tcompatibilityKey: this.compatibilityKey,\n\t\t})\n\t}\n\n\tlist<DataModel extends GenericDataModel>(\n\t\tctx: GenericQueryCtx<DataModel>,\n\t\tinput: FunctionArgs<Component['requests']['list']>,\n\t): Promise<FunctionReturnType<Component['requests']['list']>> {\n\t\treturn ctx.runQuery(this.component.requests.list, input)\n\t}\n\n\trestart<DataModel extends GenericDataModel>(\n\t\tctx: GenericMutationCtx<DataModel>,\n\t\trunId: string,\n\t): Promise<FunctionReturnType<Component['requests']['restart']>> {\n\t\treturn ctx.runMutation(this.component.requests.restart, {\n\t\t\trunId,\n\t\t\tcompatibilityKey: this.compatibilityKey,\n\t\t})\n\t}\n}\n\nfunction freezeDefinition(definition: ApprovalDefinition): ApprovalDefinition {\n\treturn Object.freeze({\n\t\t...definition,\n\t\tsteps: Object.freeze(\n\t\t\tdefinition.steps.map((step) => {\n\t\t\t\tif (step.kind === 'decision')\n\t\t\t\t\treturn Object.freeze({\n\t\t\t\t\t\t...step,\n\t\t\t\t\t\tdecisions: Object.freeze([...step.decisions]),\n\t\t\t\t\t\tquorum: Object.freeze({ ...step.quorum }),\n\t\t\t\t\t})\n\t\t\t\treturn Object.freeze({ ...step })\n\t\t\t}),\n\t\t),\n\t})\n}\n\n// The component definition is deliberately NOT re-exported here: Convex's\n// CLI only discovers a component whose resolved file is convex.config.js,\n// so hosts must mount via the dedicated subpath:\n// import approvals from 'cvx-kit/components/approvals/convex.config'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../../../src/components/approvals/client.ts"],"sourcesContent":["import type {\n\tFunctionArgs,\n\tFunctionReference,\n\tFunctionReturnType,\n\tGenericDataModel,\n\tGenericMutationCtx,\n\tGenericQueryCtx,\n} from 'convex/server'\n\nimport type { ComponentApi } from './_generated/component'\nimport type { ApprovalDecision } from './constants'\nimport type {\n\tApprovalActor,\n\tApprovalMetadata,\n\tApprovalWorkflowDescriptor,\n} from './validators'\nimport {\n\tcompileApprovalDescriptor,\n\ttype ApprovalActionStep,\n\ttype ApprovalBranchStep,\n\ttype ApprovalDecisionStep,\n\ttype ApprovalDefinition,\n\ttype ApprovalMutationStep,\n\ttype ApprovalNotifyStep,\n\ttype ApprovalWorkflowStep,\n\ttype ApprovalCallbackInput,\n\ttype CreateApprovalHandle,\n} from './workflow_steps'\n\nexport { approvalCallbackArgs, approvalCallbackInput } from './validators'\nexport type { ApprovalCallbackInput } from './validators'\n\nexport type ApprovalsOptions = Readonly<{\n\tcreateHandle?: CreateApprovalHandle\n}>\n\nexport type ApprovalStartInput = Readonly<{\n\tscopeRef: string\n\tresourceType: string\n\tresourceRef: string\n\trequester: ApprovalActor\n\tmetadata?: ApprovalMetadata\n}>\n\nexport type ApprovalDecisionInput = Readonly<{\n\trunId: string\n\tdecision: ApprovalDecision\n\treason?: string\n}>\n\nexport type ApprovalEvidenceItem = Readonly<{\n\tstepKey: string\n\tactorRef: string\n\tdecision: ApprovalDecision\n\treason?: string\n\tdecidedAt: number\n}>\n\nexport type ApprovalAuditHistoryInput = Readonly<{\n\tresourceType: string\n\tresourceRef: string\n\tlimit?: number\n}>\n\nexport type ApprovalAuditCleanupInput = Readonly<{\n\tolderThanDays: number\n\tbatchSize: number\n}>\n\nexport type ApprovalStatus = FunctionReturnType<\n\tComponentApi['requests']['status']\n>\n\nexport class Approvals<Component extends ComponentApi = ComponentApi> {\n\tprivate readonly component: Component\n\tprivate readonly createHandle: CreateApprovalHandle | undefined\n\n\tconstructor(component: Component, options: ApprovalsOptions = {}) {\n\t\tthis.component = component\n\t\tthis.createHandle = options.createHandle\n\t}\n\n\tcleanupAudit<DataModel extends GenericDataModel>(\n\t\tctx: GenericMutationCtx<DataModel>,\n\t\tinput: ApprovalAuditCleanupInput,\n\t): Promise<FunctionReturnType<Component['audit']['cleanup']>> {\n\t\treturn ctx.runMutation(this.component.audit.cleanup, input)\n\t}\n\n\tmutation(\n\t\tkey: string,\n\t\toptions: Readonly<{\n\t\t\thandler: FunctionReference<\n\t\t\t\t'mutation',\n\t\t\t\t'internal',\n\t\t\t\tApprovalCallbackInput,\n\t\t\t\tunknown\n\t\t\t>\n\t\t\tretry?: boolean\n\t\t}>,\n\t): ApprovalMutationStep {\n\t\treturn Object.freeze({\n\t\t\tkind: 'mutation',\n\t\t\tkey,\n\t\t\thandler: options.handler,\n\t\t\tretry: options.retry ?? false,\n\t\t})\n\t}\n\n\taction(\n\t\tkey: string,\n\t\toptions: Readonly<{\n\t\t\thandler: FunctionReference<\n\t\t\t\t'action',\n\t\t\t\t'internal',\n\t\t\t\tApprovalCallbackInput,\n\t\t\t\tunknown\n\t\t\t>\n\t\t\tretry?: boolean\n\t\t}>,\n\t): ApprovalActionStep {\n\t\treturn Object.freeze({\n\t\t\tkind: 'action',\n\t\t\tkey,\n\t\t\thandler: options.handler,\n\t\t\tretry: options.retry ?? false,\n\t\t})\n\t}\n\n\tnotify(\n\t\tkey: string,\n\t\toptions: Readonly<{\n\t\t\thandler: FunctionReference<\n\t\t\t\t'action',\n\t\t\t\t'internal',\n\t\t\t\tApprovalCallbackInput,\n\t\t\t\tunknown\n\t\t\t>\n\t\t\tretry?: boolean\n\t\t}>,\n\t): ApprovalNotifyStep {\n\t\treturn Object.freeze({\n\t\t\tkind: 'notify',\n\t\t\tkey,\n\t\t\thandler: options.handler,\n\t\t\tretry: options.retry ?? false,\n\t\t})\n\t}\n\n\tdecision(\n\t\tkey: string,\n\t\toptions: Readonly<{\n\t\t\tdecisions: readonly ApprovalDecision[]\n\t\t\tquorum: Readonly<{ kind: 'count'; approvals: number }>\n\t\t\tmakerChecker?: boolean\n\t\t\texpiresAfterMs?: number\n\t\t}>,\n\t): ApprovalDecisionStep {\n\t\treturn Object.freeze({\n\t\t\tkind: 'decision',\n\t\t\tkey,\n\t\t\tdecisions: Object.freeze([...options.decisions]),\n\t\t\tquorum: Object.freeze({ ...options.quorum }),\n\t\t\tmakerChecker: options.makerChecker ?? false,\n\t\t\t...(options.expiresAfterMs === undefined\n\t\t\t\t? {}\n\t\t\t\t: { expiresAfterMs: options.expiresAfterMs }),\n\t\t})\n\t}\n\n\tbranch(\n\t\tkey: string,\n\t\toptions: Readonly<{\n\t\t\tapprovedStepKey: string\n\t\t\trejectedStepKey: string\n\t\t}>,\n\t): ApprovalBranchStep {\n\t\treturn Object.freeze({ kind: 'branch', key, ...options })\n\t}\n\n\tdefine(\n\t\tinput: Readonly<{\n\t\t\tname: string\n\t\t\tcompatibilityKey?: string\n\t\t\tsteps: readonly ApprovalWorkflowStep[]\n\t\t}>,\n\t): ConfiguredApprovalWorkflow<Component> {\n\t\tconst definition = freezeDefinition({\n\t\t\tname: input.name,\n\t\t\tcompatibilityKey: input.compatibilityKey ?? input.name,\n\t\t\tsteps: input.steps,\n\t\t})\n\t\treturn new ConfiguredApprovalWorkflow(\n\t\t\tthis.component,\n\t\t\tdefinition,\n\t\t\tthis.createHandle,\n\t\t)\n\t}\n}\n\nexport class ConfiguredApprovalWorkflow<\n\tComponent extends ComponentApi = ComponentApi,\n> {\n\treadonly name: string\n\treadonly compatibilityKey: string\n\treadonly steps: readonly ApprovalWorkflowStep[]\n\n\tprivate readonly component: Component\n\tprivate readonly definition: ApprovalDefinition\n\tprivate readonly createHandle: CreateApprovalHandle | undefined\n\n\tconstructor(\n\t\tcomponent: Component,\n\t\tdefinition: ApprovalDefinition,\n\t\tcreateHandle?: CreateApprovalHandle,\n\t) {\n\t\tthis.component = component\n\t\tthis.definition = definition\n\t\tthis.name = definition.name\n\t\tthis.compatibilityKey = definition.compatibilityKey\n\t\tthis.steps = definition.steps\n\t\tthis.createHandle = createHandle\n\t\tObject.freeze(this)\n\t}\n\n\tdescriptor(): Promise<Readonly<ApprovalWorkflowDescriptor>> {\n\t\treturn compileApprovalDescriptor(this.definition, this.createHandle)\n\t}\n\n\tasync start<DataModel extends GenericDataModel>(\n\t\tctx: GenericMutationCtx<DataModel>,\n\t\tinput: ApprovalStartInput,\n\t): Promise<FunctionReturnType<Component['requests']['start']>> {\n\t\treturn ctx.runMutation(this.component.requests.start, {\n\t\t\t...input,\n\t\t\tworkflow: await this.descriptor(),\n\t\t})\n\t}\n\n\tdecide<DataModel extends GenericDataModel>(\n\t\tctx: GenericMutationCtx<DataModel>,\n\t\tinput: ApprovalDecisionInput,\n\t\tactor: ApprovalActor,\n\t): Promise<FunctionReturnType<Component['decisions']['decide']>> {\n\t\treturn ctx.runMutation(this.component.decisions.decide, {\n\t\t\t...input,\n\t\t\tactor,\n\t\t\tcompatibilityKey: this.compatibilityKey,\n\t\t})\n\t}\n\n\tevidence<DataModel extends GenericDataModel>(\n\t\tctx: GenericQueryCtx<DataModel>,\n\t\trunId: string,\n\t): Promise<FunctionReturnType<Component['decisions']['list']>> {\n\t\treturn ctx.runQuery(this.component.decisions.list, { runId })\n\t}\n\n\tauditHistory<DataModel extends GenericDataModel>(\n\t\tctx: GenericQueryCtx<DataModel>,\n\t\tinput: ApprovalAuditHistoryInput,\n\t): Promise<FunctionReturnType<Component['audit']['history']>> {\n\t\treturn ctx.runQuery(this.component.audit.history, input)\n\t}\n\n\tcancel<DataModel extends GenericDataModel>(\n\t\tctx: GenericMutationCtx<DataModel>,\n\t\trunId: string,\n\t\tactor: ApprovalActor,\n\t): Promise<FunctionReturnType<Component['requests']['cancel']>> {\n\t\treturn ctx.runMutation(this.component.requests.cancel, {\n\t\t\trunId,\n\t\t\tactor,\n\t\t\tcompatibilityKey: this.compatibilityKey,\n\t\t})\n\t}\n\n\tstatus<DataModel extends GenericDataModel>(\n\t\tctx: GenericQueryCtx<DataModel>,\n\t\trunId: string,\n\t): Promise<ApprovalStatus | null> {\n\t\treturn ctx.runQuery(this.component.requests.status, {\n\t\t\trunId,\n\t\t\tcompatibilityKey: this.compatibilityKey,\n\t\t})\n\t}\n\n\tlist<DataModel extends GenericDataModel>(\n\t\tctx: GenericQueryCtx<DataModel>,\n\t\tinput: FunctionArgs<Component['requests']['list']>,\n\t): Promise<FunctionReturnType<Component['requests']['list']>> {\n\t\treturn ctx.runQuery(this.component.requests.list, input)\n\t}\n\n\trestart<DataModel extends GenericDataModel>(\n\t\tctx: GenericMutationCtx<DataModel>,\n\t\trunId: string,\n\t): Promise<FunctionReturnType<Component['requests']['restart']>> {\n\t\treturn ctx.runMutation(this.component.requests.restart, {\n\t\t\trunId,\n\t\t\tcompatibilityKey: this.compatibilityKey,\n\t\t})\n\t}\n}\n\nfunction freezeDefinition(definition: ApprovalDefinition): ApprovalDefinition {\n\treturn Object.freeze({\n\t\t...definition,\n\t\tsteps: Object.freeze(\n\t\t\tdefinition.steps.map((step) => {\n\t\t\t\tif (step.kind === 'decision')\n\t\t\t\t\treturn Object.freeze({\n\t\t\t\t\t\t...step,\n\t\t\t\t\t\tdecisions: Object.freeze([...step.decisions]),\n\t\t\t\t\t\tquorum: Object.freeze({ ...step.quorum }),\n\t\t\t\t\t})\n\t\t\t\treturn Object.freeze({ ...step })\n\t\t\t}),\n\t\t),\n\t})\n}\n\n// The component definition is deliberately NOT re-exported here: Convex's\n// CLI only discovers a component whose resolved file is convex.config.js,\n// so hosts must mount via the dedicated subpath:\n// import approvals from 'cvx-kit/components/approvals/convex.config'\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
declare namespace constants_d_exports {
|
|
2
|
-
export { APPROVAL_AUDIT_RETENTION_CATEGORY, APPROVAL_CALLBACK_KINDS, APPROVAL_DECISIONS, APPROVAL_DESCRIPTOR_SCHEMA_VERSION, APPROVAL_EXECUTION_STATES, APPROVAL_QUORUM_KINDS, APPROVAL_RUN_STATES, APPROVAL_STEP_KINDS, ApprovalCallbackKind, ApprovalDecision, ApprovalExecutionState, ApprovalRunState, ApprovalStepKind, MAX_APPROVAL_AUDIT_CLEANUP_BATCH_SIZE, MAX_APPROVAL_AUDIT_RETENTION_DAYS, MAX_APPROVAL_CAPABILITIES, MAX_APPROVAL_DECISIONS_PER_STEP, MAX_APPROVAL_METADATA_ENTRIES, MAX_APPROVAL_METADATA_KEY_LENGTH, MAX_APPROVAL_METADATA_VALUE_LENGTH, MAX_APPROVAL_NAME_LENGTH, MAX_APPROVAL_REASON_LENGTH, MAX_APPROVAL_REFERENCE_LENGTH, MAX_APPROVAL_WORKFLOW_STEPS, MIN_APPROVAL_AUDIT_RETENTION_DAYS };
|
|
2
|
+
export { APPROVAL_ACTOR_DECISION_INDEX, APPROVAL_AUDIT_RETENTION_CATEGORY, APPROVAL_CALLBACK_KINDS, APPROVAL_COMPONENT_SCHEMA_VERSION, APPROVAL_DECISIONS, APPROVAL_DECISION_HISTORY_INDEX, APPROVAL_DESCRIPTOR_SCHEMA_VERSION, APPROVAL_EXECUTION_STATES, APPROVAL_QUORUM_KINDS, APPROVAL_REQUIRED_DECISION_INDEXES, APPROVAL_RUN_STATES, APPROVAL_STEP_KINDS, ApprovalCallbackKind, ApprovalDecision, ApprovalExecutionState, ApprovalRunState, ApprovalStepKind, MAX_APPROVAL_AUDIT_CLEANUP_BATCH_SIZE, MAX_APPROVAL_AUDIT_RETENTION_DAYS, MAX_APPROVAL_CAPABILITIES, MAX_APPROVAL_DECISIONS_PER_STEP, MAX_APPROVAL_METADATA_ENTRIES, MAX_APPROVAL_METADATA_KEY_LENGTH, MAX_APPROVAL_METADATA_VALUE_LENGTH, MAX_APPROVAL_NAME_LENGTH, MAX_APPROVAL_REASON_LENGTH, MAX_APPROVAL_REFERENCE_LENGTH, MAX_APPROVAL_WORKFLOW_STEPS, MIN_APPROVAL_AUDIT_RETENTION_DAYS };
|
|
3
3
|
}
|
|
4
4
|
declare const APPROVAL_RUN_STATES: readonly ['pending', 'approved', 'rejected', 'expired', 'canceled'];
|
|
5
5
|
declare const APPROVAL_DECISIONS: readonly ['approved', 'rejected'];
|
|
@@ -8,6 +8,12 @@ declare const APPROVAL_STEP_KINDS: readonly ['mutation', 'action', 'notify', 'de
|
|
|
8
8
|
declare const APPROVAL_QUORUM_KINDS: readonly ['count'];
|
|
9
9
|
declare const APPROVAL_CALLBACK_KINDS: readonly ['mutation', 'action'];
|
|
10
10
|
declare const APPROVAL_DESCRIPTOR_SCHEMA_VERSION: 1;
|
|
11
|
+
/** Version of the component-owned tables and indexes deployed by cvx-kit. */
|
|
12
|
+
declare const APPROVAL_COMPONENT_SCHEMA_VERSION: 1;
|
|
13
|
+
/** Indexes required by approval evidence reads and decision writes. */
|
|
14
|
+
declare const APPROVAL_DECISION_HISTORY_INDEX: 'by_runId_and_decidedAt';
|
|
15
|
+
declare const APPROVAL_ACTOR_DECISION_INDEX: 'by_runId_and_stepKey_and_actor_actorRef';
|
|
16
|
+
declare const APPROVAL_REQUIRED_DECISION_INDEXES: readonly ["by_runId_and_decidedAt", "by_runId_and_stepKey_and_actor_actorRef"];
|
|
11
17
|
declare const APPROVAL_AUDIT_RETENTION_CATEGORY = "approval-protocol";
|
|
12
18
|
declare const MIN_APPROVAL_AUDIT_RETENTION_DAYS = 1;
|
|
13
19
|
declare const MAX_APPROVAL_AUDIT_RETENTION_DAYS = 3650;
|
|
@@ -27,5 +33,5 @@ type ApprovalExecutionState = (typeof APPROVAL_EXECUTION_STATES)[number];
|
|
|
27
33
|
type ApprovalStepKind = (typeof APPROVAL_STEP_KINDS)[number];
|
|
28
34
|
type ApprovalCallbackKind = (typeof APPROVAL_CALLBACK_KINDS)[number];
|
|
29
35
|
//#endregion
|
|
30
|
-
export { APPROVAL_AUDIT_RETENTION_CATEGORY, APPROVAL_CALLBACK_KINDS, APPROVAL_DECISIONS, APPROVAL_DESCRIPTOR_SCHEMA_VERSION, APPROVAL_EXECUTION_STATES, APPROVAL_QUORUM_KINDS, APPROVAL_RUN_STATES, APPROVAL_STEP_KINDS, ApprovalCallbackKind, ApprovalDecision, ApprovalExecutionState, ApprovalRunState, ApprovalStepKind, MAX_APPROVAL_AUDIT_CLEANUP_BATCH_SIZE, MAX_APPROVAL_AUDIT_RETENTION_DAYS, MAX_APPROVAL_CAPABILITIES, MAX_APPROVAL_DECISIONS_PER_STEP, MAX_APPROVAL_METADATA_ENTRIES, MAX_APPROVAL_METADATA_KEY_LENGTH, MAX_APPROVAL_METADATA_VALUE_LENGTH, MAX_APPROVAL_NAME_LENGTH, MAX_APPROVAL_REASON_LENGTH, MAX_APPROVAL_REFERENCE_LENGTH, MAX_APPROVAL_WORKFLOW_STEPS, MIN_APPROVAL_AUDIT_RETENTION_DAYS, constants_d_exports };
|
|
36
|
+
export { APPROVAL_ACTOR_DECISION_INDEX, APPROVAL_AUDIT_RETENTION_CATEGORY, APPROVAL_CALLBACK_KINDS, APPROVAL_COMPONENT_SCHEMA_VERSION, APPROVAL_DECISIONS, APPROVAL_DECISION_HISTORY_INDEX, APPROVAL_DESCRIPTOR_SCHEMA_VERSION, APPROVAL_EXECUTION_STATES, APPROVAL_QUORUM_KINDS, APPROVAL_REQUIRED_DECISION_INDEXES, APPROVAL_RUN_STATES, APPROVAL_STEP_KINDS, ApprovalCallbackKind, ApprovalDecision, ApprovalExecutionState, ApprovalRunState, ApprovalStepKind, MAX_APPROVAL_AUDIT_CLEANUP_BATCH_SIZE, MAX_APPROVAL_AUDIT_RETENTION_DAYS, MAX_APPROVAL_CAPABILITIES, MAX_APPROVAL_DECISIONS_PER_STEP, MAX_APPROVAL_METADATA_ENTRIES, MAX_APPROVAL_METADATA_KEY_LENGTH, MAX_APPROVAL_METADATA_VALUE_LENGTH, MAX_APPROVAL_NAME_LENGTH, MAX_APPROVAL_REASON_LENGTH, MAX_APPROVAL_REFERENCE_LENGTH, MAX_APPROVAL_WORKFLOW_STEPS, MIN_APPROVAL_AUDIT_RETENTION_DAYS, constants_d_exports };
|
|
31
37
|
//# sourceMappingURL=constants.d.mts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=[`pending`,`approved`,`rejected`,`expired`,`canceled`],t=[`approved`,`rejected`],n=[`inProgress`,`completed`,`canceled`,`failed`],r=[`mutation`,`action`,`notify`,`decision`,`branch`],i=[`count`],a=[`mutation`,`action`],o=1,s=`approval-protocol`,
|
|
1
|
+
const e=[`pending`,`approved`,`rejected`,`expired`,`canceled`],t=[`approved`,`rejected`],n=[`inProgress`,`completed`,`canceled`,`failed`],r=[`mutation`,`action`,`notify`,`decision`,`branch`],i=[`count`],a=[`mutation`,`action`],o=1,s=1,c=`by_runId_and_decidedAt`,l=`by_runId_and_stepKey_and_actor_actorRef`,u=[c,l],d=`approval-protocol`,f=1,p=3650,m=100,h=256,g=96,_=500,v=32,y=32,b=64,x=512,S=32,C=8;export{l as APPROVAL_ACTOR_DECISION_INDEX,d as APPROVAL_AUDIT_RETENTION_CATEGORY,a as APPROVAL_CALLBACK_KINDS,s as APPROVAL_COMPONENT_SCHEMA_VERSION,t as APPROVAL_DECISIONS,c as APPROVAL_DECISION_HISTORY_INDEX,o as APPROVAL_DESCRIPTOR_SCHEMA_VERSION,n as APPROVAL_EXECUTION_STATES,i as APPROVAL_QUORUM_KINDS,u as APPROVAL_REQUIRED_DECISION_INDEXES,e as APPROVAL_RUN_STATES,r as APPROVAL_STEP_KINDS,m as MAX_APPROVAL_AUDIT_CLEANUP_BATCH_SIZE,p as MAX_APPROVAL_AUDIT_RETENTION_DAYS,v as MAX_APPROVAL_CAPABILITIES,C as MAX_APPROVAL_DECISIONS_PER_STEP,y as MAX_APPROVAL_METADATA_ENTRIES,b as MAX_APPROVAL_METADATA_KEY_LENGTH,x as MAX_APPROVAL_METADATA_VALUE_LENGTH,g as MAX_APPROVAL_NAME_LENGTH,_ as MAX_APPROVAL_REASON_LENGTH,h as MAX_APPROVAL_REFERENCE_LENGTH,S as MAX_APPROVAL_WORKFLOW_STEPS,f as MIN_APPROVAL_AUDIT_RETENTION_DAYS};
|
|
2
2
|
//# sourceMappingURL=constants.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.mjs","names":[],"sources":["../../../src/components/approvals/constants.ts"],"sourcesContent":["export const APPROVAL_RUN_STATES = [\n\t'pending',\n\t'approved',\n\t'rejected',\n\t'expired',\n\t'canceled',\n] as const\n\nexport const APPROVAL_DECISIONS = ['approved', 'rejected'] as const\n\nexport const APPROVAL_EXECUTION_STATES = [\n\t'inProgress',\n\t'completed',\n\t'canceled',\n\t'failed',\n] as const\n\nexport const APPROVAL_STEP_KINDS = [\n\t'mutation',\n\t'action',\n\t'notify',\n\t'decision',\n\t'branch',\n] as const\n\nexport const APPROVAL_QUORUM_KINDS = ['count'] as const\n\nexport const APPROVAL_CALLBACK_KINDS = ['mutation', 'action'] as const\n\nexport const APPROVAL_DESCRIPTOR_SCHEMA_VERSION = 1 as const\n\nexport const APPROVAL_AUDIT_RETENTION_CATEGORY = 'approval-protocol'\nexport const MIN_APPROVAL_AUDIT_RETENTION_DAYS = 1\nexport const MAX_APPROVAL_AUDIT_RETENTION_DAYS = 3_650\nexport const MAX_APPROVAL_AUDIT_CLEANUP_BATCH_SIZE = 100\n\nexport const MAX_APPROVAL_REFERENCE_LENGTH = 256\nexport const MAX_APPROVAL_NAME_LENGTH = 96\nexport const MAX_APPROVAL_REASON_LENGTH = 500\nexport const MAX_APPROVAL_CAPABILITIES = 32\nexport const MAX_APPROVAL_METADATA_ENTRIES = 32\nexport const MAX_APPROVAL_METADATA_KEY_LENGTH = 64\nexport const MAX_APPROVAL_METADATA_VALUE_LENGTH = 512\nexport const MAX_APPROVAL_WORKFLOW_STEPS = 32\nexport const MAX_APPROVAL_DECISIONS_PER_STEP = 8\n\nexport type ApprovalRunState = (typeof APPROVAL_RUN_STATES)[number]\nexport type ApprovalDecision = (typeof APPROVAL_DECISIONS)[number]\nexport type ApprovalExecutionState = (typeof APPROVAL_EXECUTION_STATES)[number]\nexport type ApprovalStepKind = (typeof APPROVAL_STEP_KINDS)[number]\nexport type ApprovalCallbackKind = (typeof APPROVAL_CALLBACK_KINDS)[number]\n"],"mappings":"AAAA,MAAa,EAAsB,CAClC,UACA,WACA,WACA,UACA,UACD,EAEa,EAAqB,CAAC,WAAY,UAAU,EAE5C,EAA4B,CACxC,aACA,YACA,WACA,QACD,EAEa,EAAsB,CAClC,WACA,SACA,SACA,WACA,QACD,EAEa,EAAwB,CAAC,OAAO,EAEhC,EAA0B,CAAC,WAAY,QAAQ,EAE/C,EAAqC,
|
|
1
|
+
{"version":3,"file":"constants.mjs","names":[],"sources":["../../../src/components/approvals/constants.ts"],"sourcesContent":["export const APPROVAL_RUN_STATES = [\n\t'pending',\n\t'approved',\n\t'rejected',\n\t'expired',\n\t'canceled',\n] as const\n\nexport const APPROVAL_DECISIONS = ['approved', 'rejected'] as const\n\nexport const APPROVAL_EXECUTION_STATES = [\n\t'inProgress',\n\t'completed',\n\t'canceled',\n\t'failed',\n] as const\n\nexport const APPROVAL_STEP_KINDS = [\n\t'mutation',\n\t'action',\n\t'notify',\n\t'decision',\n\t'branch',\n] as const\n\nexport const APPROVAL_QUORUM_KINDS = ['count'] as const\n\nexport const APPROVAL_CALLBACK_KINDS = ['mutation', 'action'] as const\n\nexport const APPROVAL_DESCRIPTOR_SCHEMA_VERSION = 1 as const\n\n/** Version of the component-owned tables and indexes deployed by cvx-kit. */\nexport const APPROVAL_COMPONENT_SCHEMA_VERSION = 1 as const\n\n/** Indexes required by approval evidence reads and decision writes. */\nexport const APPROVAL_DECISION_HISTORY_INDEX =\n\t'by_runId_and_decidedAt' as const\nexport const APPROVAL_ACTOR_DECISION_INDEX =\n\t'by_runId_and_stepKey_and_actor_actorRef' as const\nexport const APPROVAL_REQUIRED_DECISION_INDEXES = [\n\tAPPROVAL_DECISION_HISTORY_INDEX,\n\tAPPROVAL_ACTOR_DECISION_INDEX,\n] as const\n\nexport const APPROVAL_AUDIT_RETENTION_CATEGORY = 'approval-protocol'\nexport const MIN_APPROVAL_AUDIT_RETENTION_DAYS = 1\nexport const MAX_APPROVAL_AUDIT_RETENTION_DAYS = 3_650\nexport const MAX_APPROVAL_AUDIT_CLEANUP_BATCH_SIZE = 100\n\nexport const MAX_APPROVAL_REFERENCE_LENGTH = 256\nexport const MAX_APPROVAL_NAME_LENGTH = 96\nexport const MAX_APPROVAL_REASON_LENGTH = 500\nexport const MAX_APPROVAL_CAPABILITIES = 32\nexport const MAX_APPROVAL_METADATA_ENTRIES = 32\nexport const MAX_APPROVAL_METADATA_KEY_LENGTH = 64\nexport const MAX_APPROVAL_METADATA_VALUE_LENGTH = 512\nexport const MAX_APPROVAL_WORKFLOW_STEPS = 32\nexport const MAX_APPROVAL_DECISIONS_PER_STEP = 8\n\nexport type ApprovalRunState = (typeof APPROVAL_RUN_STATES)[number]\nexport type ApprovalDecision = (typeof APPROVAL_DECISIONS)[number]\nexport type ApprovalExecutionState = (typeof APPROVAL_EXECUTION_STATES)[number]\nexport type ApprovalStepKind = (typeof APPROVAL_STEP_KINDS)[number]\nexport type ApprovalCallbackKind = (typeof APPROVAL_CALLBACK_KINDS)[number]\n"],"mappings":"AAAA,MAAa,EAAsB,CAClC,UACA,WACA,WACA,UACA,UACD,EAEa,EAAqB,CAAC,WAAY,UAAU,EAE5C,EAA4B,CACxC,aACA,YACA,WACA,QACD,EAEa,EAAsB,CAClC,WACA,SACA,SACA,WACA,QACD,EAEa,EAAwB,CAAC,OAAO,EAEhC,EAA0B,CAAC,WAAY,QAAQ,EAE/C,EAAqC,EAGrC,EAAoC,EAGpC,EACZ,yBACY,EACZ,0CACY,EAAqC,CACjD,EACA,CACD,EAEa,EAAoC,oBACpC,EAAoC,EACpC,EAAoC,KACpC,EAAwC,IAExC,EAAgC,IAChC,EAA2B,GAC3B,EAA6B,IAC7B,EAA4B,GAC5B,EAAgC,GAChC,EAAmC,GACnC,EAAqC,IACrC,EAA8B,GAC9B,EAAkC"}
|
|
@@ -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{
|
|
1
|
+
import{components as e,internal as t}from"./_generated/api.mjs";import{internalMutation as n,internalQuery as r,mutation as i,query as a}from"./_generated/server.mjs";import{APPROVAL_ACTOR_DECISION_INDEX as o,APPROVAL_DECISIONS as s,APPROVAL_DECISION_HISTORY_INDEX as c,APPROVAL_RUN_STATES as l}from"./constants.mjs";import{logApprovalTransition as u}from"./audit.mjs";import{approvalActor as d,approvalDecision as f,approvalReason as p}from"./validators.mjs";import{approvalDecisionEventName as m,evaluateDecisionOutcome as h}from"./workflow_steps.mjs";import{canTransitionApprovalRun as g,classifyPendingRunAt as _}from"./functions.mjs";import{zodToConvex as v}from"convex-helpers/server/zod4";import{v as y}from"convex/values";import{WorkflowManager as b}from"@convex-dev/workflow";const x=i({args:{runId:y.id(`approvalRuns`),decision:v(f),reason:y.optional(v(p)),actor:v(d),compatibilityKey:y.string()},returns:y.object({state:y.union(y.literal(l[0]),y.literal(l[1]),y.literal(l[2]),y.literal(l[3]),y.literal(l[4]))}),handler:async(e,t)=>{let n=await e.db.get(t.runId);if(!n)throw Error(`Approval request not found`);k(n.workflow.compatibilityKey,t.compatibilityKey);let r=n.workflow.steps.find(e=>e.kind===`decision`&&e.key===n.currentStepKey);if(!r||r.kind!==`decision`)throw Error(`Approval request is not waiting for a decision`);let i=await e.db.query(`approvalDecisions`).withIndex(o,e=>e.eq(`runId`,n._id).eq(`stepKey`,r.key).eq(`actor.actorRef`,t.actor.actorRef)).unique();if(i){if(i.decision!==t.decision)throw Error(`Actor already submitted a contradictory decision`);return{state:n.state}}if(n.state!==`pending`)throw Error(`Approval request is already ${n.state}`);if(n.expiresAt!==void 0&&_(Date.now(),n.expiresAt)===`expired`)return D(e,n,r.key);if(!r.decisions.includes(t.decision))throw Error(`Decision is not allowed by this workflow step`);if(r.makerChecker&&t.actor.actorRef===n.requester.actorRef)throw Error(`Requester cannot decide this approval step`);let a=Date.now();await e.db.insert(`approvalDecisions`,{runId:n._id,stepKey:r.key,actor:t.actor,decision:t.decision,...t.reason===void 0?{}:{reason:t.reason},decidedAt:a});let s=await e.db.query(`approvalDecisions`).withIndex(`by_runId_and_stepKey_and_decidedAt`,e=>e.eq(`runId`,n._id).eq(`stepKey`,r.key)).collect(),c=h(s.map(e=>({actorRef:e.actor.actorRef,decision:e.decision})),r.quorum.approvals);if(await u(e,{action:`approval.decision.${t.decision}`,actorRef:t.actor.actorRef,runId:n._id,resourceType:n.resourceType,resourceRef:n.resourceRef,transition:`${r.key}:${t.actor.actorRef}:${t.decision}`}),!c)return{state:`pending`};let l=M(n,r.key,c);return await O(e,n,r.key,c,l,{actorRef:t.actor.actorRef,decision:t.decision,...t.reason===void 0?{}:{reason:t.reason},decidedAt:a}),l?{state:c}:{state:`pending`}}}),S=a({args:{runId:y.id(`approvalRuns`)},returns:y.array(y.object({stepKey:y.string(),actorRef:y.string(),decision:v(f),reason:y.optional(v(p)),decidedAt:y.number()})),handler:(e,t)=>A(e,t.runId)}),C=r({args:{runId:y.id(`approvalRuns`),stepKey:y.string()},returns:y.array(y.object({stepKey:y.string(),actorRef:y.string(),decision:v(f),reason:y.optional(v(p)),decidedAt:y.number()})),handler:(e,t)=>j(e,t.runId,t.stepKey)}),w=n({args:{runId:y.id(`approvalRuns`),stepKey:y.string(),workflowId:y.string(),expiresAfterMs:y.optional(y.number())},returns:y.object({state:y.union(y.literal(l[0]),y.literal(l[1]),y.literal(l[2]),y.literal(l[3]),y.literal(l[4])),expiresAt:y.optional(y.number())}),handler:async(e,n)=>{let r=await e.db.get(n.runId);if(!r)throw Error(`Approval request not found`);if(r.workflowId!==n.workflowId)throw Error(`Approval workflow linkage is incomplete`);if(r.state!==`pending`)return{state:r.state,expiresAt:r.expiresAt};let i=Number(r.metadata?.approvalExpiresAt),a=Number.isFinite(i)?i:n.expiresAfterMs===void 0?void 0:Date.now()+n.expiresAfterMs;return await e.db.patch(r._id,{currentStepKey:n.stepKey,expiresAt:a,updatedAt:Date.now()}),await u(e,{action:`approval.waiting`,runId:r._id,resourceType:r.resourceType,resourceRef:r.resourceRef,transition:`${n.stepKey}:waiting`}),a!==void 0&&await e.scheduler.runAt(a,t.decisions.expire,{runId:r._id,stepKey:n.stepKey}),{state:`pending`,...a===void 0?{}:{expiresAt:a}}}}),T=n({args:{runId:y.id(`approvalRuns`),stepKey:y.string()},returns:y.object({state:y.union(y.literal(`missing`),y.literal(l[0]),y.literal(l[1]),y.literal(l[2]),y.literal(l[3]),y.literal(l[4]))}),handler:async(e,t)=>{let n=await e.db.get(t.runId);return n?n.state!==`pending`||n.currentStepKey!==t.stepKey||n.expiresAt===void 0||_(Date.now(),n.expiresAt)===`pending`?{state:n.state}:D(e,n,t.stepKey):{state:`missing`}}}),E=new b(e.workflow);async function D(e,t,n){if(!g(t.state,`expired`))throw Error(`Approval request cannot transition from ${t.state}`);let r=Date.now();if(await e.db.patch(t._id,{state:`expired`,expiresAt:void 0,updatedAt:r,terminalAt:r}),await u(e,{action:`approval.expired`,runId:t._id,resourceType:t.resourceType,resourceRef:t.resourceRef,transition:`${n}:expired`}),!t.workflowId)throw Error(`Approval workflow is not linked`);return await E.sendEvent(e,{workflowId:t.workflowId,name:m(t._id,n),validator:y.object({outcome:y.literal(l[3])}),value:{outcome:`expired`}}),{state:`expired`}}async function O(e,t,n,r,i,a){if(i){if(!g(t.state,r))throw Error(`Approval request cannot transition from ${t.state}`);let i=Date.now();await e.db.patch(t._id,{state:r,expiresAt:void 0,updatedAt:i,terminalAt:i}),await u(e,{action:`approval.${r}`,runId:t._id,resourceType:t.resourceType,resourceRef:t.resourceRef,transition:`${n}:terminal:${r}`})}if(!t.workflowId)throw Error(`Approval workflow is not linked`);await E.sendEvent(e,{workflowId:t.workflowId,name:m(t._id,n),validator:y.object({outcome:y.union(y.literal(s[0]),y.literal(s[1])),terminalEvidence:y.object({actorRef:y.string(),decision:v(f),reason:y.optional(v(p)),decidedAt:y.number()})}),value:{outcome:r,terminalEvidence:a}})}function k(e,t){if(e!==t)throw Error(`Approval workflow compatibility mismatch: expected ${t}, received ${e}`)}async function A(e,t){return(await e.db.query(`approvalDecisions`).withIndex(c,e=>e.eq(`runId`,t)).collect()).map(e=>({stepKey:e.stepKey,actorRef:e.actor.actorRef,decision:e.decision,...e.reason===void 0?{}:{reason:e.reason},decidedAt:e.decidedAt}))}async function j(e,t,n){return(await e.db.query(`approvalDecisions`).withIndex(`by_runId_and_stepKey_and_decidedAt`,e=>e.eq(`runId`,t).eq(`stepKey`,n)).collect()).map(e=>({stepKey:e.stepKey,actorRef:e.actor.actorRef,decision:e.decision,...e.reason===void 0?{}:{reason:e.reason},decidedAt:e.decidedAt}))}function M(e,t,n){if(n===`rejected`)return!0;let r=e.workflow.steps.findIndex(e=>e.kind===`decision`&&e.key===t);return!e.workflow.steps.slice(r+1).some(e=>e.kind===`decision`)}export{x as decide,w as enter,T as expire,S as list,C as listForWorkflow};
|
|
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_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('by_runId_and_stepKey_and_actor_actorRef', (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('by_runId_and_decidedAt', (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\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\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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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare namespace health_d_exports {
|
|
2
|
+
export { check };
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Verifies that the package's component schema and required decision indexes
|
|
6
|
+
* were deployed. A missing schema fails with a cvx-kit-specific diagnostic.
|
|
7
|
+
*/
|
|
8
|
+
declare const check: import("convex/server").RegisteredQuery<"public", {}, Promise<{
|
|
9
|
+
status: 'ready';
|
|
10
|
+
schemaVersion: 1;
|
|
11
|
+
requiredIndexes: ("by_runId_and_decidedAt" | "by_runId_and_stepKey_and_actor_actorRef")[];
|
|
12
|
+
}>>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { check, health_d_exports };
|
|
15
|
+
//# sourceMappingURL=health.d.mts.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{query as e}from"./_generated/server.mjs";import{APPROVAL_REQUIRED_DECISION_INDEXES as t}from"./constants.mjs";import{v as n}from"convex/values";const r=e({args:{},returns:n.object({status:n.literal(`ready`),schemaVersion:n.literal(1),requiredIndexes:n.array(n.string())}),handler:async e=>{try{for(let n of t)await e.db.query(`approvalDecisions`).withIndex(n).take(1)}catch(e){throw Error(`cvx-kit approvals schema v1 is not installed with its required indexes. Remove consumer patches, reinstall cvx-kit, and deploy again. Cause: ${e instanceof Error?e.message:String(e)}`)}return{status:`ready`,schemaVersion:1,requiredIndexes:[...t]}}});export{r as check};
|
|
2
|
+
//# sourceMappingURL=health.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.mjs","names":[],"sources":["../../../src/components/approvals/health.ts"],"sourcesContent":["import { v } from 'convex/values'\n\nimport { query } from './_generated/server'\nimport {\n\tAPPROVAL_COMPONENT_SCHEMA_VERSION,\n\tAPPROVAL_REQUIRED_DECISION_INDEXES,\n} from './constants'\n\n/**\n * Verifies that the package's component schema and required decision indexes\n * were deployed. A missing schema fails with a cvx-kit-specific diagnostic.\n */\nexport const check = query({\n\targs: {},\n\treturns: v.object({\n\t\tstatus: v.literal('ready'),\n\t\tschemaVersion: v.literal(APPROVAL_COMPONENT_SCHEMA_VERSION),\n\t\trequiredIndexes: v.array(v.string()),\n\t}),\n\thandler: async (ctx) => {\n\t\ttry {\n\t\t\tfor (const index of APPROVAL_REQUIRED_DECISION_INDEXES) {\n\t\t\t\tawait ctx.db.query('approvalDecisions').withIndex(index).take(1)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`cvx-kit approvals schema v${APPROVAL_COMPONENT_SCHEMA_VERSION} is not installed with its required indexes. Remove consumer patches, reinstall cvx-kit, and deploy again. Cause: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t)\n\t\t}\n\n\t\treturn {\n\t\t\tstatus: 'ready' as const,\n\t\t\tschemaVersion: APPROVAL_COMPONENT_SCHEMA_VERSION,\n\t\t\trequiredIndexes: [...APPROVAL_REQUIRED_DECISION_INDEXES],\n\t\t}\n\t},\n})\n"],"mappings":"uJAYA,MAAa,EAAQ,EAAM,CAC1B,KAAM,CAAC,EACP,QAAS,EAAE,OAAO,CACjB,OAAQ,EAAE,QAAQ,OAAO,EACzB,cAAe,EAAE,QAAA,CAAyC,EAC1D,gBAAiB,EAAE,MAAM,EAAE,OAAO,CAAC,CACpC,CAAC,EACD,QAAS,KAAO,IAAQ,CACvB,GAAI,CACH,IAAK,IAAM,KAAS,EACnB,MAAM,EAAI,GAAG,MAAM,mBAAmB,CAAC,CAAC,UAAU,CAAK,CAAC,CAAC,KAAK,CAAC,CAEjE,OAAS,EAAO,CACf,MAAU,MACT,gJAAmL,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GACzO,CACD,CAEA,MAAO,CACN,OAAQ,QACR,cAAA,EACA,gBAAiB,CAAC,GAAG,CAAkC,CACxD,CACD,CACD,CAAC"}
|
|
@@ -1,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_EXECUTION_STATES as o,APPROVAL_RUN_STATES as s}from"./constants.mjs";import{logApprovalTransition as c}from"./audit.mjs";import{approvalActor as l,approvalMetadata as u,approvalName as d,approvalReference as f,approvalRunDocument as p,approvalRunState as m,approvalWorkflowDescriptor as h}from"./validators.mjs";import{canTransitionApprovalRun as g}from"./functions.mjs";import{zodToConvex as
|
|
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_EXECUTION_STATES as o,APPROVAL_RUN_STATES as s}from"./constants.mjs";import{logApprovalTransition as c}from"./audit.mjs";import{approvalActor as l,approvalMetadata as u,approvalName as d,approvalReference as f,approvalRunDocument as p,approvalRunState as m,approvalWorkflowDescriptor as h}from"./validators.mjs";import{canTransitionApprovalRun as g}from"./functions.mjs";import _ from"./schema.js";import{zodToConvex as v}from"convex-helpers/server/zod4";import{paginationOptsValidator as y,paginationResultValidator as b}from"convex/server";import{v as x}from"convex/values";import{WorkflowManager as S}from"@convex-dev/workflow";import{paginator as C}from"convex-helpers/server/pagination";const w=i({args:{scopeRef:v(f),resourceType:v(f),resourceRef:v(f),requester:v(l),metadata:x.optional(v(u)),workflow:v(h)},returns:x.object({runId:x.id(`approvalRuns`)}),handler:async(e,n)=>{let r=Date.now(),i=await e.db.insert(`approvalRuns`,{...n,state:`pending`,createdAt:r,updatedAt:r}),a=await j.start(e,t.workflow.run,{runId:i},{startAsync:!0});return await e.db.patch(i,{workflowId:a,updatedAt:Date.now()}),await c(e,{action:`approval.requested`,actorRef:n.requester.actorRef,runId:i,resourceType:n.resourceType,resourceRef:n.resourceRef,transition:`requested`}),{runId:i}}}),T=i({args:{runId:x.id(`approvalRuns`),actor:v(l),compatibilityKey:x.string()},returns:x.object({state:x.literal(s[4])}),handler:async(e,t)=>{let n=await e.db.get(t.runId);if(!n)throw Error(`Approval request not found`);if(I(n.workflow.compatibilityKey,t.compatibilityKey),n.state!==`pending`){if(n.state===`canceled`)return{state:`canceled`};throw Error(`Approval request is already ${n.state}`)}if(!n.workflowId)throw Error(`Approval workflow is not linked`);if(await j.cancel(e,n.workflowId),!g(n.state,`canceled`))throw Error(`Approval request cannot transition from ${n.state}`);let r=Date.now();return await e.db.patch(n._id,{state:`canceled`,expiresAt:void 0,updatedAt:r,terminalAt:r}),await c(e,{action:`approval.canceled`,actorRef:t.actor.actorRef,runId:n._id,resourceType:n.resourceType,resourceRef:n.resourceRef,transition:`canceled`}),{state:`canceled`}}}),E=i({args:{runId:x.id(`approvalRuns`),compatibilityKey:x.string()},returns:x.null(),handler:async(e,t)=>{let n=await e.db.get(t.runId);if(!n)throw Error(`Approval request not found`);if(I(n.workflow.compatibilityKey,t.compatibilityKey),!n.workflowId)throw Error(`Approval workflow is not linked`);if(!n.executionFailedStepKey)throw Error(`Approval workflow has no failed step to restart`);return await j.restart(e,n.workflowId,{from:n.executionFailedStepKey,startAsync:!0}),null}}),D=a({args:{runId:x.id(`approvalRuns`),compatibilityKey:x.string()},returns:x.union(x.null(),F()),handler:async(e,t)=>{let n=await e.db.get(t.runId);return n?(I(n.workflow.compatibilityKey,t.compatibilityKey),{run:n,execution:M(n.workflowId?await j.status(e,n.workflowId):null)}):null}}),O=a({args:{scopeRef:v(f),state:x.optional(v(m)),paginationOpts:y},returns:b(N()),handler:(e,t)=>C(e.db,_).query(`approvalRuns`).withIndex(`by_scopeRef_and_state`,e=>t.state===void 0?e.eq(`scopeRef`,t.scopeRef):e.eq(`scopeRef`,t.scopeRef).eq(`state`,t.state)).order(`desc`).paginate(t.paginationOpts)}),k=r({args:{runId:x.id(`approvalRuns`)},returns:x.union(x.null(),N()),handler:(e,t)=>e.db.get(t.runId)}),A=n({args:{runId:x.id(`approvalRuns`),outcome:x.union(x.literal(o[1]),x.literal(o[3])),failedStepKey:x.optional(v(d))},returns:x.null(),handler:async(e,t)=>{let n=await e.db.get(t.runId);if(!n)throw Error(`Approval request not found`);return await e.db.patch(n._id,{executionFailedStepKey:t.outcome===`failed`?t.failedStepKey:void 0,updatedAt:Date.now()}),await c(e,{action:`approval.execution.${t.outcome}`,runId:n._id,resourceType:n.resourceType,resourceRef:n.resourceRef,transition:`execution:${t.outcome}`,severity:t.outcome===`failed`?`error`:`info`}),null}}),j=new S(e.workflow);function M(e){if(!e)return null;switch(e.type){case`failed`:return{type:`failed`,error:e.error};case`inProgress`:case`completed`:case`canceled`:return{type:e.type}}}function N(){return x.object({_id:x.id(`approvalRuns`),_creationTime:x.number(),...v(p).fields})}function P(){return x.union(x.object({type:x.literal(o[0])}),x.object({type:x.literal(o[1])}),x.object({type:x.literal(o[2])}),x.object({type:x.literal(o[3]),error:x.string()}))}function F(){return x.object({run:N(),execution:x.union(x.null(),P())})}function I(e,t){if(e!==t)throw Error(`Approval workflow compatibility mismatch: expected ${t}, received ${e}`)}export{T as cancel,k as getForWorkflow,O as list,A as recordExecutionTerminal,E as restart,w as start,D as status};
|
|
2
2
|
//# sourceMappingURL=requests.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requests.mjs","names":[],"sources":["../../../src/components/approvals/requests.ts"],"sourcesContent":["import { WorkflowManager, type WorkflowId } from '@convex-dev/workflow'\nimport { zodToConvex } from 'convex-helpers/server/zod4'\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 { 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\tctx.db\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 {\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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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{APPROVAL_ACTOR_DECISION_INDEX as e,APPROVAL_DECISION_HISTORY_INDEX as t}from"./constants.mjs";import{approvalDecisionDocument as n,approvalRunDocument as r}from"./validators.mjs";import{zodToConvex as i}from"convex-helpers/server/zod4";import{defineSchema as a,defineTable as o}from"convex/server";var s=a({approvalRuns:o(i(r)).index(`by_resourceType_and_resourceRef`,[`resourceType`,`resourceRef`]).index(`by_scopeRef_and_state`,[`scopeRef`,`state`]).index(`by_state_and_expiresAt`,[`state`,`expiresAt`]),approvalDecisions:o(i(n)).index(e,[`runId`,`stepKey`,`actor.actorRef`]).index(`by_runId_and_stepKey_and_decidedAt`,[`runId`,`stepKey`,`decidedAt`]).index(t,[`runId`,`decidedAt`])});export{s as default};
|