cvx-kit 0.0.8 → 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 +132 -0
- package/README.md +14 -10
- package/ROADMAP.md +126 -0
- package/dist/agent-tools.d.mts +45 -0
- package/dist/agent-tools.mjs +2 -0
- package/dist/agent-tools.mjs.map +1 -0
- package/dist/auth.d.mts +9 -1
- package/dist/auth.mjs +1 -1
- package/dist/auth.mjs.map +1 -1
- 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/client.d.mts +26 -9
- package/dist/components/foundation/client.mjs.map +1 -1
- package/dist/components/foundation/modules/query/query.d.mts +19 -8
- package/dist/components/foundation/modules/query/query.mjs.map +1 -1
- package/dist/components/foundation/{schema.mjs → schema.js} +0 -1
- package/dist/crud.d.mts +79 -0
- package/dist/crud.mjs +2 -0
- package/dist/crud.mjs.map +1 -0
- package/dist/index.d.mts +9 -4
- package/dist/index.mjs +1 -1
- package/dist/middleware.d.mts +46 -0
- package/dist/middleware.mjs +2 -0
- package/dist/middleware.mjs.map +1 -0
- package/dist/state-machine.d.mts +18 -0
- package/dist/state-machine.mjs +2 -0
- package/dist/state-machine.mjs.map +1 -0
- package/dist/tenancy.d.mts +1 -1
- package/dist/webhooks.d.mts +164 -0
- package/dist/webhooks.mjs +2 -0
- package/dist/webhooks.mjs.map +1 -0
- package/dist/zod-table.d.mts +34 -3
- package/dist/zod-table.mjs +1 -1
- package/dist/zod-table.mjs.map +1 -1
- package/docs/agent-tools.md +41 -0
- package/docs/approvals.md +50 -2
- package/docs/crud.md +62 -0
- package/docs/llms.md +8 -1
- package/docs/plans/2026-08-14-001-feat-v0.1.0-feature-set-plan.md +461 -0
- package/docs/upgrading.md +175 -0
- package/docs/webhooks.md +58 -0
- package/package.json +25 -2
- package/scripts/convex-config-bridge.mjs +41 -3
- package/scripts/extract-changelog.mjs +29 -0
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validators.mjs","names":[],"sources":["../../../src/components/approvals/validators.ts"],"sourcesContent":["import { zid } from 'convex-helpers/server/zod4'\nimport { z } from 'zod'\n\nimport {\n\tAPPROVAL_CALLBACK_KINDS,\n\tAPPROVAL_DECISIONS,\n\tAPPROVAL_DESCRIPTOR_SCHEMA_VERSION,\n\tAPPROVAL_QUORUM_KINDS,\n\tAPPROVAL_RUN_STATES,\n\tAPPROVAL_STEP_KINDS,\n\tMAX_APPROVAL_CAPABILITIES,\n\tMAX_APPROVAL_DECISIONS_PER_STEP,\n\tMAX_APPROVAL_METADATA_ENTRIES,\n\tMAX_APPROVAL_METADATA_KEY_LENGTH,\n\tMAX_APPROVAL_METADATA_VALUE_LENGTH,\n\tMAX_APPROVAL_NAME_LENGTH,\n\tMAX_APPROVAL_REASON_LENGTH,\n\tMAX_APPROVAL_REFERENCE_LENGTH,\n\tMAX_APPROVAL_WORKFLOW_STEPS,\n} from './constants'\n\nexport const approvalRunState = z.enum(APPROVAL_RUN_STATES)\nexport const approvalDecision = z.enum(APPROVAL_DECISIONS)\nexport const approvalStepKind = z.enum(APPROVAL_STEP_KINDS)\nexport const approvalCallbackKind = z.enum(APPROVAL_CALLBACK_KINDS)\nexport const approvalQuorumKind = z.enum(APPROVAL_QUORUM_KINDS)\n\nexport const approvalReference = z\n\t.string()\n\t.trim()\n\t.min(1)\n\t.max(MAX_APPROVAL_REFERENCE_LENGTH)\n\nexport const approvalName = z\n\t.string()\n\t.trim()\n\t.min(1)\n\t.max(MAX_APPROVAL_NAME_LENGTH)\n\t.regex(/^[A-Za-z][A-Za-z0-9._-]*$/)\n\nexport const approvalReason = z\n\t.string()\n\t.trim()\n\t.min(1)\n\t.max(MAX_APPROVAL_REASON_LENGTH)\n\nexport const approvalMetadata = z\n\t.record(\n\t\tz\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(MAX_APPROVAL_METADATA_KEY_LENGTH)\n\t\t\t.regex(/^(?![$_])[\\x20-\\x7E]+$/),\n\t\tz.string().max(MAX_APPROVAL_METADATA_VALUE_LENGTH),\n\t)\n\t.refine(\n\t\t(value) => Object.keys(value).length <= MAX_APPROVAL_METADATA_ENTRIES,\n\t\t`Metadata may contain at most ${MAX_APPROVAL_METADATA_ENTRIES} entries`,\n\t)\n\nexport const approvalActor = z\n\t.object({\n\t\tactorRef: approvalReference,\n\t\tcapabilities: z.array(approvalName).max(MAX_APPROVAL_CAPABILITIES),\n\t\tmetadata: approvalMetadata.optional(),\n\t})\n\t.strict()\n\nexport const approvalMutationCallback = z\n\t.object({\n\t\tkind: z.literal('mutation'),\n\t\thandle: approvalReference,\n\t\tretry: z.boolean(),\n\t})\n\t.strict()\n\nexport const approvalActionCallback = z\n\t.object({\n\t\tkind: z.literal('action'),\n\t\thandle: approvalReference,\n\t\tretry: z.boolean(),\n\t})\n\t.strict()\n\nexport const approvalCallback = z.discriminatedUnion('kind', [\n\tapprovalMutationCallback,\n\tapprovalActionCallback,\n])\n\nexport const approvalQuorum = z\n\t.object({\n\t\tkind: approvalQuorumKind,\n\t\tapprovals: z.number().int().positive().max(MAX_APPROVAL_DECISIONS_PER_STEP),\n\t})\n\t.strict()\n\nexport const approvalCallbackStep = z.discriminatedUnion('kind', [\n\tz\n\t\t.object({\n\t\t\tkind: z.literal('mutation'),\n\t\t\tkey: approvalName,\n\t\t\tcallback: approvalMutationCallback,\n\t\t})\n\t\t.strict(),\n\tz\n\t\t.object({\n\t\t\tkind: z.literal('action'),\n\t\t\tkey: approvalName,\n\t\t\tcallback: approvalActionCallback,\n\t\t})\n\t\t.strict(),\n])\n\nexport const approvalNotifyStep = z\n\t.object({\n\t\tkind: z.literal('notify'),\n\t\tkey: approvalName,\n\t\tcallback: approvalActionCallback,\n\t})\n\t.strict()\n\nexport const approvalDecisionStep = z\n\t.object({\n\t\tkind: z.literal('decision'),\n\t\tkey: approvalName,\n\t\tdecisions: z\n\t\t\t.array(approvalDecision)\n\t\t\t.min(1)\n\t\t\t.max(MAX_APPROVAL_DECISIONS_PER_STEP),\n\t\tquorum: approvalQuorum,\n\t\tmakerChecker: z.boolean(),\n\t\texpiresAfterMs: z.number().int().positive().optional(),\n\t})\n\t.strict()\n\nexport const approvalBranchStep = z\n\t.object({\n\t\tkind: z.literal('branch'),\n\t\tkey: approvalName,\n\t\tapprovedStepKey: approvalName,\n\t\trejectedStepKey: approvalName,\n\t})\n\t.strict()\n\nexport const approvalWorkflowStep = z.discriminatedUnion('kind', [\n\tapprovalCallbackStep,\n\tapprovalNotifyStep,\n\tapprovalDecisionStep,\n\tapprovalBranchStep,\n])\n\nexport const approvalWorkflowDescriptor = z\n\t.object({\n\t\tschemaVersion: z.literal(APPROVAL_DESCRIPTOR_SCHEMA_VERSION),\n\t\tcompatibilityKey: approvalName,\n\t\tname: approvalName,\n\t\tsteps: z\n\t\t\t.array(approvalWorkflowStep)\n\t\t\t.min(1)\n\t\t\t.max(MAX_APPROVAL_WORKFLOW_STEPS),\n\t})\n\t.strict()\n\t.superRefine((descriptor, context) => {\n\t\tconst keys = new Set<string>()\n\t\tconst stepsByKey = new Map(\n\t\t\tdescriptor.steps.map((step) => [step.key, step] as const),\n\t\t)\n\t\tconst branchTargetOwners = new Map<string, number>()\n\t\tfor (const [index, step] of descriptor.steps.entries()) {\n\t\t\tif (keys.has(step.key))\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Duplicate workflow step key: ${step.key}`,\n\t\t\t\t\tpath: ['steps', index, 'key'],\n\t\t\t\t})\n\t\t\tkeys.add(step.key)\n\t\t\tif (\n\t\t\t\tstep.kind === 'decision' &&\n\t\t\t\tnew Set(step.decisions).size !== step.decisions.length\n\t\t\t)\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Duplicate decision value in step: ${step.key}`,\n\t\t\t\t\tpath: ['steps', index, 'decisions'],\n\t\t\t\t})\n\t\t}\n\t\tfor (const [index, step] of descriptor.steps.entries()) {\n\t\t\tif (step.kind !== 'branch') continue\n\t\t\tif (descriptor.steps[index - 1]?.kind !== 'decision')\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Branch must immediately follow a decision: ${step.key}`,\n\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t})\n\t\t\tfor (const target of [step.approvedStepKey, step.rejectedStepKey]) {\n\t\t\t\tconst targetStep = stepsByKey.get(target)\n\t\t\t\tconst targetIndex = descriptor.steps.findIndex(\n\t\t\t\t\t(candidate) => candidate.key === target,\n\t\t\t\t)\n\t\t\t\tif (!targetStep)\n\t\t\t\t\tcontext.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `Unknown branch target: ${target}`,\n\t\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t\t})\n\t\t\t\telse if (\n\t\t\t\t\ttargetStep.kind !== 'mutation' &&\n\t\t\t\t\ttargetStep.kind !== 'action' &&\n\t\t\t\t\ttargetStep.kind !== 'notify'\n\t\t\t\t)\n\t\t\t\t\tcontext.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `Branch target must be a callback step: ${target}`,\n\t\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t\t})\n\t\t\t\telse if (targetIndex <= index)\n\t\t\t\t\tcontext.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `Branch target must be declared after its branch: ${target}`,\n\t\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t\t})\n\t\t\t\tbranchTargetOwners.set(\n\t\t\t\t\ttarget,\n\t\t\t\t\t(branchTargetOwners.get(target) ?? 0) + 1,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tfor (const [target, owners] of branchTargetOwners) {\n\t\t\tif (owners !== 1)\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Branch callback must belong to exactly one branch: ${target}`,\n\t\t\t\t\tpath: ['steps'],\n\t\t\t\t})\n\t\t}\n\t})\n\nexport const approvalRunDocument = z\n\t.object({\n\t\tscopeRef: approvalReference,\n\t\tresourceType: approvalName,\n\t\tresourceRef: approvalReference,\n\t\trequester: approvalActor,\n\t\tmetadata: approvalMetadata.optional(),\n\t\tworkflow: approvalWorkflowDescriptor,\n\t\tstate: approvalRunState,\n\t\tcurrentStepKey: approvalName.optional(),\n\t\texecutionFailedStepKey: approvalName.optional(),\n\t\texpiresAt: z.number().optional(),\n\t\tworkflowId: approvalReference.optional(),\n\t\tcreatedAt: z.number(),\n\t\tupdatedAt: z.number(),\n\t\tterminalAt: z.number().optional(),\n\t})\n\t.strict()\n\nexport const approvalDecisionDocument = z\n\t.object({\n\t\trunId: zid('approvalRuns'),\n\t\tstepKey: approvalName,\n\t\tactor: approvalActor,\n\t\tdecision: approvalDecision,\n\t\treason: approvalReason.optional(),\n\t\tdecidedAt: z.number(),\n\t})\n\t.strict()\n\nexport type ApprovalWorkflowDescriptor = z.infer<\n\ttypeof approvalWorkflowDescriptor\n>\nexport type ApprovalActor = z.infer<typeof approvalActor>\nexport type ApprovalMetadata = z.infer<typeof approvalMetadata>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"validators.mjs","names":[],"sources":["../../../src/components/approvals/validators.ts"],"sourcesContent":["import { zid, zodToConvex } from 'convex-helpers/server/zod4'\nimport { z } from 'zod'\n\nimport {\n\tAPPROVAL_CALLBACK_KINDS,\n\tAPPROVAL_DECISIONS,\n\tAPPROVAL_DESCRIPTOR_SCHEMA_VERSION,\n\tAPPROVAL_QUORUM_KINDS,\n\tAPPROVAL_RUN_STATES,\n\tAPPROVAL_STEP_KINDS,\n\tMAX_APPROVAL_CAPABILITIES,\n\tMAX_APPROVAL_DECISIONS_PER_STEP,\n\tMAX_APPROVAL_METADATA_ENTRIES,\n\tMAX_APPROVAL_METADATA_KEY_LENGTH,\n\tMAX_APPROVAL_METADATA_VALUE_LENGTH,\n\tMAX_APPROVAL_NAME_LENGTH,\n\tMAX_APPROVAL_REASON_LENGTH,\n\tMAX_APPROVAL_REFERENCE_LENGTH,\n\tMAX_APPROVAL_WORKFLOW_STEPS,\n} from './constants'\n\nexport const approvalRunState = z.enum(APPROVAL_RUN_STATES)\nexport const approvalDecision = z.enum(APPROVAL_DECISIONS)\nexport const approvalStepKind = z.enum(APPROVAL_STEP_KINDS)\nexport const approvalCallbackKind = z.enum(APPROVAL_CALLBACK_KINDS)\nexport const approvalQuorumKind = z.enum(APPROVAL_QUORUM_KINDS)\n\nexport const approvalReference = z\n\t.string()\n\t.trim()\n\t.min(1)\n\t.max(MAX_APPROVAL_REFERENCE_LENGTH)\n\nexport const approvalName = z\n\t.string()\n\t.trim()\n\t.min(1)\n\t.max(MAX_APPROVAL_NAME_LENGTH)\n\t.regex(/^[A-Za-z][A-Za-z0-9._-]*$/)\n\nexport const approvalReason = z\n\t.string()\n\t.trim()\n\t.min(1)\n\t.max(MAX_APPROVAL_REASON_LENGTH)\n\nexport const approvalMetadata = z\n\t.record(\n\t\tz\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(MAX_APPROVAL_METADATA_KEY_LENGTH)\n\t\t\t.regex(/^(?![$_])[\\x20-\\x7E]+$/),\n\t\tz.string().max(MAX_APPROVAL_METADATA_VALUE_LENGTH),\n\t)\n\t.refine(\n\t\t(value) => Object.keys(value).length <= MAX_APPROVAL_METADATA_ENTRIES,\n\t\t`Metadata may contain at most ${MAX_APPROVAL_METADATA_ENTRIES} entries`,\n\t)\n\nconst approvalCallbackEvidence = z\n\t.object({\n\t\tstepKey: approvalName,\n\t\tactorRef: approvalReference,\n\t\tdecision: approvalDecision,\n\t\treason: approvalReason.optional(),\n\t\tdecidedAt: z.number(),\n\t})\n\t.strict()\n\nconst approvalCallbackTerminalEvidence = approvalCallbackEvidence.omit({\n\tstepKey: true,\n})\n\nexport const approvalCallbackInput = z\n\t.object({\n\t\trunId: approvalReference,\n\t\tscopeRef: approvalReference,\n\t\tresourceType: approvalName,\n\t\tresourceRef: approvalReference,\n\t\tmetadata: approvalMetadata.optional(),\n\t\tdecision: z\n\t\t\t.object({\n\t\t\t\tstepKey: approvalName,\n\t\t\t\toutcome: z.union([approvalDecision, z.literal('expired')]),\n\t\t\t\tevidence: z.array(approvalCallbackEvidence),\n\t\t\t\tterminalEvidence: approvalCallbackTerminalEvidence.optional(),\n\t\t\t})\n\t\t\t.strict()\n\t\t\t.optional(),\n\t})\n\t.strict()\n\n/** Convex argument validator for approval mutation, action, and notify callbacks. */\nexport const approvalCallbackArgs = zodToConvex(approvalCallbackInput)\n\nexport const approvalActor = z\n\t.object({\n\t\tactorRef: approvalReference,\n\t\tcapabilities: z.array(approvalName).max(MAX_APPROVAL_CAPABILITIES),\n\t\tmetadata: approvalMetadata.optional(),\n\t})\n\t.strict()\n\nexport const approvalMutationCallback = z\n\t.object({\n\t\tkind: z.literal('mutation'),\n\t\thandle: approvalReference,\n\t\tretry: z.boolean(),\n\t})\n\t.strict()\n\nexport const approvalActionCallback = z\n\t.object({\n\t\tkind: z.literal('action'),\n\t\thandle: approvalReference,\n\t\tretry: z.boolean(),\n\t})\n\t.strict()\n\nexport const approvalCallback = z.discriminatedUnion('kind', [\n\tapprovalMutationCallback,\n\tapprovalActionCallback,\n])\n\nexport const approvalQuorum = z\n\t.object({\n\t\tkind: approvalQuorumKind,\n\t\tapprovals: z.number().int().positive().max(MAX_APPROVAL_DECISIONS_PER_STEP),\n\t})\n\t.strict()\n\nexport const approvalCallbackStep = z.discriminatedUnion('kind', [\n\tz\n\t\t.object({\n\t\t\tkind: z.literal('mutation'),\n\t\t\tkey: approvalName,\n\t\t\tcallback: approvalMutationCallback,\n\t\t})\n\t\t.strict(),\n\tz\n\t\t.object({\n\t\t\tkind: z.literal('action'),\n\t\t\tkey: approvalName,\n\t\t\tcallback: approvalActionCallback,\n\t\t})\n\t\t.strict(),\n])\n\nexport const approvalNotifyStep = z\n\t.object({\n\t\tkind: z.literal('notify'),\n\t\tkey: approvalName,\n\t\tcallback: approvalActionCallback,\n\t})\n\t.strict()\n\nexport const approvalDecisionStep = z\n\t.object({\n\t\tkind: z.literal('decision'),\n\t\tkey: approvalName,\n\t\tdecisions: z\n\t\t\t.array(approvalDecision)\n\t\t\t.min(1)\n\t\t\t.max(MAX_APPROVAL_DECISIONS_PER_STEP),\n\t\tquorum: approvalQuorum,\n\t\tmakerChecker: z.boolean(),\n\t\texpiresAfterMs: z.number().int().positive().optional(),\n\t})\n\t.strict()\n\nexport const approvalBranchStep = z\n\t.object({\n\t\tkind: z.literal('branch'),\n\t\tkey: approvalName,\n\t\tapprovedStepKey: approvalName,\n\t\trejectedStepKey: approvalName,\n\t})\n\t.strict()\n\nexport const approvalWorkflowStep = z.discriminatedUnion('kind', [\n\tapprovalCallbackStep,\n\tapprovalNotifyStep,\n\tapprovalDecisionStep,\n\tapprovalBranchStep,\n])\n\nexport const approvalWorkflowDescriptor = z\n\t.object({\n\t\tschemaVersion: z.literal(APPROVAL_DESCRIPTOR_SCHEMA_VERSION),\n\t\tcompatibilityKey: approvalName,\n\t\tname: approvalName,\n\t\tsteps: z\n\t\t\t.array(approvalWorkflowStep)\n\t\t\t.min(1)\n\t\t\t.max(MAX_APPROVAL_WORKFLOW_STEPS),\n\t})\n\t.strict()\n\t.superRefine((descriptor, context) => {\n\t\tconst keys = new Set<string>()\n\t\tconst stepsByKey = new Map(\n\t\t\tdescriptor.steps.map((step) => [step.key, step] as const),\n\t\t)\n\t\tconst branchTargetOwners = new Map<string, number>()\n\t\tfor (const [index, step] of descriptor.steps.entries()) {\n\t\t\tif (keys.has(step.key))\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Duplicate workflow step key: ${step.key}`,\n\t\t\t\t\tpath: ['steps', index, 'key'],\n\t\t\t\t})\n\t\t\tkeys.add(step.key)\n\t\t\tif (\n\t\t\t\tstep.kind === 'decision' &&\n\t\t\t\tnew Set(step.decisions).size !== step.decisions.length\n\t\t\t)\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Duplicate decision value in step: ${step.key}`,\n\t\t\t\t\tpath: ['steps', index, 'decisions'],\n\t\t\t\t})\n\t\t}\n\t\tfor (const [index, step] of descriptor.steps.entries()) {\n\t\t\tif (step.kind !== 'branch') continue\n\t\t\tif (descriptor.steps[index - 1]?.kind !== 'decision')\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Branch must immediately follow a decision: ${step.key}`,\n\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t})\n\t\t\tfor (const target of [step.approvedStepKey, step.rejectedStepKey]) {\n\t\t\t\tconst targetStep = stepsByKey.get(target)\n\t\t\t\tconst targetIndex = descriptor.steps.findIndex(\n\t\t\t\t\t(candidate) => candidate.key === target,\n\t\t\t\t)\n\t\t\t\tif (!targetStep)\n\t\t\t\t\tcontext.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `Unknown branch target: ${target}`,\n\t\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t\t})\n\t\t\t\telse if (\n\t\t\t\t\ttargetStep.kind !== 'mutation' &&\n\t\t\t\t\ttargetStep.kind !== 'action' &&\n\t\t\t\t\ttargetStep.kind !== 'notify'\n\t\t\t\t)\n\t\t\t\t\tcontext.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `Branch target must be a callback step: ${target}`,\n\t\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t\t})\n\t\t\t\telse if (targetIndex <= index)\n\t\t\t\t\tcontext.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `Branch target must be declared after its branch: ${target}`,\n\t\t\t\t\t\tpath: ['steps', index],\n\t\t\t\t\t})\n\t\t\t\tbranchTargetOwners.set(\n\t\t\t\t\ttarget,\n\t\t\t\t\t(branchTargetOwners.get(target) ?? 0) + 1,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tfor (const [target, owners] of branchTargetOwners) {\n\t\t\tif (owners !== 1)\n\t\t\t\tcontext.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: `Branch callback must belong to exactly one branch: ${target}`,\n\t\t\t\t\tpath: ['steps'],\n\t\t\t\t})\n\t\t}\n\t})\n\nexport const approvalRunDocument = z\n\t.object({\n\t\tscopeRef: approvalReference,\n\t\tresourceType: approvalName,\n\t\tresourceRef: approvalReference,\n\t\trequester: approvalActor,\n\t\tmetadata: approvalMetadata.optional(),\n\t\tworkflow: approvalWorkflowDescriptor,\n\t\tstate: approvalRunState,\n\t\tcurrentStepKey: approvalName.optional(),\n\t\texecutionFailedStepKey: approvalName.optional(),\n\t\texpiresAt: z.number().optional(),\n\t\tworkflowId: approvalReference.optional(),\n\t\tcreatedAt: z.number(),\n\t\tupdatedAt: z.number(),\n\t\tterminalAt: z.number().optional(),\n\t})\n\t.strict()\n\nexport const approvalDecisionDocument = z\n\t.object({\n\t\trunId: zid('approvalRuns'),\n\t\tstepKey: approvalName,\n\t\tactor: approvalActor,\n\t\tdecision: approvalDecision,\n\t\treason: approvalReason.optional(),\n\t\tdecidedAt: z.number(),\n\t})\n\t.strict()\n\nexport type ApprovalWorkflowDescriptor = z.infer<\n\ttypeof approvalWorkflowDescriptor\n>\nexport type ApprovalActor = z.infer<typeof approvalActor>\nexport type ApprovalMetadata = z.infer<typeof approvalMetadata>\nexport type ApprovalCallbackInput = Readonly<\n\tz.infer<typeof approvalCallbackInput>\n>\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApprovalDecision } from "./constants.mjs";
|
|
2
|
-
import { ApprovalWorkflowDescriptor } from "./validators.mjs";
|
|
2
|
+
import { ApprovalCallbackInput, ApprovalWorkflowDescriptor } from "./validators.mjs";
|
|
3
3
|
import { FunctionReference, FunctionType } from "convex/server";
|
|
4
4
|
declare namespace workflow_steps_d_exports {
|
|
5
5
|
export { ApprovalActionStep, ApprovalBranchStep, ApprovalCallbackInput, ApprovalDecisionEvidence, ApprovalDecisionStep, ApprovalDefinition, ApprovalMutationStep, ApprovalNotifyStep, ApprovalWorkflowStep, CreateApprovalHandle, approvalDecisionEventName, callbackReference, compileApprovalDescriptor, evaluateDecisionOutcome, resolveWorkflowStepIndex };
|
|
@@ -50,34 +50,11 @@ type ApprovalDecisionEvidence = Readonly<{
|
|
|
50
50
|
actorRef: string;
|
|
51
51
|
decision: ApprovalDecision;
|
|
52
52
|
}>;
|
|
53
|
-
type ApprovalCallbackInput = Readonly<{
|
|
54
|
-
runId: string;
|
|
55
|
-
scopeRef: string;
|
|
56
|
-
resourceType: string;
|
|
57
|
-
resourceRef: string;
|
|
58
|
-
metadata?: Readonly<Record<string, string>>;
|
|
59
|
-
decision?: Readonly<{
|
|
60
|
-
stepKey: string;
|
|
61
|
-
outcome: ApprovalDecision | 'expired';
|
|
62
|
-
evidence: readonly Readonly<{
|
|
63
|
-
actorRef: string;
|
|
64
|
-
decision: ApprovalDecision;
|
|
65
|
-
reason?: string;
|
|
66
|
-
decidedAt: number;
|
|
67
|
-
}>[];
|
|
68
|
-
terminalEvidence?: Readonly<{
|
|
69
|
-
actorRef: string;
|
|
70
|
-
decision: ApprovalDecision;
|
|
71
|
-
reason?: string;
|
|
72
|
-
decidedAt: number;
|
|
73
|
-
}>;
|
|
74
|
-
}>;
|
|
75
|
-
}>;
|
|
76
53
|
declare function approvalDecisionEventName(runId: string, stepKey: string): string;
|
|
77
54
|
declare function evaluateDecisionOutcome(decisions: readonly ApprovalDecisionEvidence[], approvalsRequired: number): ApprovalDecision | null;
|
|
78
55
|
declare function resolveWorkflowStepIndex(stepKeys: readonly string[], targetKey: string): number;
|
|
79
56
|
declare function callbackReference<Type extends FunctionType>(handle: string): FunctionReference<Type, 'internal', ApprovalCallbackInput, unknown>;
|
|
80
57
|
declare function compileApprovalDescriptor(definition: ApprovalDefinition, createHandle?: CreateApprovalHandle): Promise<Readonly<ApprovalWorkflowDescriptor>>;
|
|
81
58
|
//#endregion
|
|
82
|
-
export { ApprovalActionStep, ApprovalBranchStep, ApprovalCallbackInput, ApprovalDecisionEvidence, ApprovalDecisionStep, ApprovalDefinition, ApprovalMutationStep, ApprovalNotifyStep, ApprovalWorkflowStep, CreateApprovalHandle, approvalDecisionEventName, callbackReference, compileApprovalDescriptor, evaluateDecisionOutcome, resolveWorkflowStepIndex, workflow_steps_d_exports };
|
|
59
|
+
export { ApprovalActionStep, ApprovalBranchStep, type ApprovalCallbackInput, ApprovalDecisionEvidence, ApprovalDecisionStep, ApprovalDefinition, ApprovalMutationStep, ApprovalNotifyStep, ApprovalWorkflowStep, CreateApprovalHandle, approvalDecisionEventName, callbackReference, compileApprovalDescriptor, evaluateDecisionOutcome, resolveWorkflowStepIndex, workflow_steps_d_exports };
|
|
83
60
|
//# sourceMappingURL=workflow_steps.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow_steps.mjs","names":[],"sources":["../../../src/components/approvals/workflow_steps.ts"],"sourcesContent":["import {\n\tcreateFunctionHandle,\n\ttype FunctionReference,\n\ttype FunctionType,\n} from 'convex/server'\n\nimport type { ApprovalDecision } from './constants'\nimport {\n\tapprovalWorkflowDescriptor,\n\ttype ApprovalWorkflowDescriptor,\n} from './validators'\n\nexport type ApprovalMutationStep = Readonly<{\n\tkind: 'mutation'\n\tkey: string\n\thandler: FunctionReference<'mutation', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalActionStep = Readonly<{\n\tkind: 'action'\n\tkey: string\n\thandler: FunctionReference<'action', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalNotifyStep = Readonly<{\n\tkind: 'notify'\n\tkey: string\n\thandler: FunctionReference<'action', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalDecisionStep = Readonly<{\n\tkind: 'decision'\n\tkey: string\n\tdecisions: readonly ApprovalDecision[]\n\tquorum: Readonly<{ kind: 'count'; approvals: number }>\n\tmakerChecker: boolean\n\texpiresAfterMs?: number\n}>\n\nexport type ApprovalBranchStep = Readonly<{\n\tkind: 'branch'\n\tkey: string\n\tapprovedStepKey: string\n\trejectedStepKey: string\n}>\n\nexport type ApprovalWorkflowStep =\n\t| ApprovalMutationStep\n\t| ApprovalActionStep\n\t| ApprovalNotifyStep\n\t| ApprovalDecisionStep\n\t| ApprovalBranchStep\n\nexport type ApprovalDefinition = Readonly<{\n\tname: string\n\tcompatibilityKey: string\n\tsteps: readonly ApprovalWorkflowStep[]\n}>\n\nexport type CreateApprovalHandle = (\n\treference: FunctionReference<'mutation' | 'action', 'internal'>,\n) => Promise<string>\n\nexport type ApprovalDecisionEvidence = Readonly<{\n\tactorRef: string\n\tdecision: ApprovalDecision\n}>\n\nexport
|
|
1
|
+
{"version":3,"file":"workflow_steps.mjs","names":[],"sources":["../../../src/components/approvals/workflow_steps.ts"],"sourcesContent":["import {\n\tcreateFunctionHandle,\n\ttype FunctionReference,\n\ttype FunctionType,\n} from 'convex/server'\n\nimport type { ApprovalDecision } from './constants'\nimport {\n\tapprovalWorkflowDescriptor,\n\ttype ApprovalCallbackInput,\n\ttype ApprovalWorkflowDescriptor,\n} from './validators'\n\nexport type { ApprovalCallbackInput } from './validators'\n\nexport type ApprovalMutationStep = Readonly<{\n\tkind: 'mutation'\n\tkey: string\n\thandler: FunctionReference<'mutation', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalActionStep = Readonly<{\n\tkind: 'action'\n\tkey: string\n\thandler: FunctionReference<'action', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalNotifyStep = Readonly<{\n\tkind: 'notify'\n\tkey: string\n\thandler: FunctionReference<'action', 'internal'>\n\tretry: boolean\n}>\n\nexport type ApprovalDecisionStep = Readonly<{\n\tkind: 'decision'\n\tkey: string\n\tdecisions: readonly ApprovalDecision[]\n\tquorum: Readonly<{ kind: 'count'; approvals: number }>\n\tmakerChecker: boolean\n\texpiresAfterMs?: number\n}>\n\nexport type ApprovalBranchStep = Readonly<{\n\tkind: 'branch'\n\tkey: string\n\tapprovedStepKey: string\n\trejectedStepKey: string\n}>\n\nexport type ApprovalWorkflowStep =\n\t| ApprovalMutationStep\n\t| ApprovalActionStep\n\t| ApprovalNotifyStep\n\t| ApprovalDecisionStep\n\t| ApprovalBranchStep\n\nexport type ApprovalDefinition = Readonly<{\n\tname: string\n\tcompatibilityKey: string\n\tsteps: readonly ApprovalWorkflowStep[]\n}>\n\nexport type CreateApprovalHandle = (\n\treference: FunctionReference<'mutation' | 'action', 'internal'>,\n) => Promise<string>\n\nexport type ApprovalDecisionEvidence = Readonly<{\n\tactorRef: string\n\tdecision: ApprovalDecision\n}>\n\nexport function approvalDecisionEventName(\n\trunId: string,\n\tstepKey: string,\n): string {\n\treturn `approval:${runId}:${stepKey}`\n}\n\nexport function evaluateDecisionOutcome(\n\tdecisions: readonly ApprovalDecisionEvidence[],\n\tapprovalsRequired: number,\n): ApprovalDecision | null {\n\tif (decisions.some((decision) => decision.decision === 'rejected'))\n\t\treturn 'rejected'\n\treturn decisions.filter((decision) => decision.decision === 'approved')\n\t\t.length >= approvalsRequired\n\t\t? 'approved'\n\t\t: null\n}\n\nexport function resolveWorkflowStepIndex(\n\tstepKeys: readonly string[],\n\ttargetKey: string,\n): number {\n\tconst index = stepKeys.indexOf(targetKey)\n\tif (index === -1)\n\t\tthrow new Error(`Unknown approval workflow step: ${targetKey}`)\n\treturn index\n}\n\nexport function callbackReference<Type extends FunctionType>(\n\thandle: string,\n): FunctionReference<Type, 'internal', ApprovalCallbackInput, unknown> {\n\treturn handle as unknown as FunctionReference<\n\t\tType,\n\t\t'internal',\n\t\tApprovalCallbackInput,\n\t\tunknown\n\t>\n}\n\nexport async function compileApprovalDescriptor(\n\tdefinition: ApprovalDefinition,\n\tcreateHandle: CreateApprovalHandle = createFunctionHandle,\n): Promise<Readonly<ApprovalWorkflowDescriptor>> {\n\tconst steps = await Promise.all(\n\t\tdefinition.steps.map(async (step) => {\n\t\t\tif (step.kind === 'mutation' || step.kind === 'action') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: step.kind,\n\t\t\t\t\tkey: step.key,\n\t\t\t\t\tcallback: {\n\t\t\t\t\t\tkind: step.kind,\n\t\t\t\t\t\thandle: await createHandle(step.handler),\n\t\t\t\t\t\tretry: step.retry,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (step.kind === 'notify') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: step.kind,\n\t\t\t\t\tkey: step.key,\n\t\t\t\t\tcallback: {\n\t\t\t\t\t\tkind: 'action' as const,\n\t\t\t\t\t\thandle: await createHandle(step.handler),\n\t\t\t\t\t\tretry: step.retry,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (step.kind === 'decision') {\n\t\t\t\treturn {\n\t\t\t\t\t...step,\n\t\t\t\t\tdecisions: [...step.decisions],\n\t\t\t\t\tquorum: { ...step.quorum },\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { ...step }\n\t\t}),\n\t)\n\tconst descriptor = approvalWorkflowDescriptor.parse({\n\t\tschemaVersion: 1,\n\t\tcompatibilityKey: definition.compatibilityKey,\n\t\tname: definition.name,\n\t\tsteps,\n\t})\n\treturn deepFreeze(descriptor)\n}\n\nfunction deepFreeze<Value>(value: Value): Readonly<Value> {\n\tif (value && typeof value === 'object') {\n\t\tObject.freeze(value)\n\t\tfor (const nested of Object.values(value)) deepFreeze(nested)\n\t}\n\treturn value\n}\n"],"mappings":"mHA0EA,SAAgB,EACf,EACA,EACS,CACT,MAAO,YAAY,EAAM,GAAG,GAC7B,CAEA,SAAgB,EACf,EACA,EAC0B,CAG1B,OAFI,EAAU,KAAM,GAAa,EAAS,WAAa,UAAU,EACzD,WACD,EAAU,OAAQ,GAAa,EAAS,WAAa,UAAU,CAAC,CACrE,QAAU,EACT,WACA,IACJ,CAEA,SAAgB,EACf,EACA,EACS,CACT,IAAM,EAAQ,EAAS,QAAQ,CAAS,EACxC,GAAI,IAAU,GACb,MAAU,MAAM,mCAAmC,GAAW,EAC/D,OAAO,CACR,CAEA,SAAgB,EACf,EACsE,CACtE,OAAO,CAMR,CAEA,eAAsB,EACrB,EACA,EAAqC,EACW,CAChD,IAAM,EAAQ,MAAM,QAAQ,IAC3B,EAAW,MAAM,IAAI,KAAO,IACvB,EAAK,OAAS,YAAc,EAAK,OAAS,SACtC,CACN,KAAM,EAAK,KACX,IAAK,EAAK,IACV,SAAU,CACT,KAAM,EAAK,KACX,OAAQ,MAAM,EAAa,EAAK,OAAO,EACvC,MAAO,EAAK,KACb,CACD,EAEG,EAAK,OAAS,SACV,CACN,KAAM,EAAK,KACX,IAAK,EAAK,IACV,SAAU,CACT,KAAM,SACN,OAAQ,MAAM,EAAa,EAAK,OAAO,EACvC,MAAO,EAAK,KACb,CACD,EAEG,EAAK,OAAS,WACV,CACN,GAAG,EACH,UAAW,CAAC,GAAG,EAAK,SAAS,EAC7B,OAAQ,CAAE,GAAG,EAAK,MAAO,CAC1B,EAEM,CAAE,GAAG,CAAK,CACjB,CACF,EAOA,OAAO,EANY,EAA2B,MAAM,CACnD,cAAe,EACf,iBAAkB,EAAW,iBAC7B,KAAM,EAAW,KACjB,OACD,CAC2B,CAAC,CAC7B,CAEA,SAAS,EAAkB,EAA+B,CACzD,GAAI,GAAS,OAAO,GAAU,SAAU,CACvC,OAAO,OAAO,CAAK,EACnB,IAAK,IAAM,KAAU,OAAO,OAAO,CAAK,EAAG,EAAW,CAAM,CAC7D,CACA,OAAO,CACR"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import _default from "../schema.
|
|
1
|
+
import _default from "../schema.js";
|
|
2
2
|
import { DataModelFromSchemaDefinition, DocumentByName, SystemTableNames, TableNamesInDataModel } from "convex/server";
|
|
3
3
|
import { GenericId } from "convex/values";
|
|
4
4
|
//#region src/components/foundation/_generated/dataModel.d.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { CommandExecution, CommandInput, CommandRegistry, CommandResult, Parseable } from "./modules/command/command.mjs";
|
|
2
2
|
import { CommandObservation, Observability, ObservabilityOptions } from "./modules/observability/observability.mjs";
|
|
3
|
-
import { Query, QueryExecution, QueryMiddleware } from "./modules/query/query.mjs";
|
|
3
|
+
import { AnyQueryMiddleware, Query, QueryExecution, QueryMiddleware } from "./modules/query/query.mjs";
|
|
4
4
|
import { Result, ResultBoundary, TransactionMetricsContext, executeResultBoundary, projectResult } from "./result.mjs";
|
|
5
5
|
import { emitSemanticEvent } from "./telemetry.mjs";
|
|
6
6
|
declare namespace client_d_exports {
|
|
7
|
-
export { ApplicationCommand, AuditEntryInput, AuditWriter, AuditedOperation, AuditedRegistry, CommandDefaults, CommandExecution, CommandInput, CommandMiddleware, CommandObservation, CommandRegistry, CommandResult, Foundation, FoundationOptions, ObservabilityOptions, Parseable, PermissionChecker, QueryExecution, QueryMiddleware, Result, ResultBoundary, TransactionMetricsContext };
|
|
7
|
+
export { AnyCommandMiddleware, AnyQueryMiddleware, ApplicationCommand, AuditEntryInput, AuditWriter, AuditedOperation, AuditedRegistry, CommandConstructor, CommandDefaults, CommandExecution, CommandInput, CommandMiddleware, CommandObservation, CommandRegistry, CommandResult, Foundation, FoundationOptions, ObservabilityOptions, Parseable, PermissionChecker, QueryExecution, QueryMiddleware, Result, ResultBoundary, TransactionMetricsContext };
|
|
8
8
|
}
|
|
9
9
|
/** Shape handed to the injected audit writer; classification comes from the operation. */
|
|
10
10
|
type AuditEntryInput = {
|
|
@@ -46,7 +46,7 @@ type AuditedOperation = Readonly<{
|
|
|
46
46
|
*/
|
|
47
47
|
aggregates?: readonly string[];
|
|
48
48
|
/** Per-operation middleware, inside the registry-wide chain. */
|
|
49
|
-
middleware?: readonly
|
|
49
|
+
middleware?: readonly AnyCommandMiddleware[];
|
|
50
50
|
audit: (resolution: Readonly<{
|
|
51
51
|
command: never;
|
|
52
52
|
result: never;
|
|
@@ -60,13 +60,22 @@ type AuditedOperation = Readonly<{
|
|
|
60
60
|
* transform results, but can never skip authorization, return an invalid
|
|
61
61
|
* result, or desynchronize audit from effects.
|
|
62
62
|
*/
|
|
63
|
-
type CommandMiddleware<Context = never> = (input: {
|
|
63
|
+
type CommandMiddleware<Context = never, Extension extends Record<string, unknown> = Record<string, unknown>> = (input: {
|
|
64
64
|
operation: string;
|
|
65
65
|
command: unknown;
|
|
66
66
|
context: Context;
|
|
67
67
|
next: (options?: {
|
|
68
68
|
/** Merged into the context handed to inner middleware, guards, handler. */
|
|
69
|
-
context?:
|
|
69
|
+
context?: Extension;
|
|
70
|
+
}) => Promise<unknown>;
|
|
71
|
+
}) => Promise<unknown>;
|
|
72
|
+
/** Any-context middleware — what registries and operations accept. */
|
|
73
|
+
type AnyCommandMiddleware = (input: {
|
|
74
|
+
operation: string;
|
|
75
|
+
command: unknown;
|
|
76
|
+
context: never;
|
|
77
|
+
next: (options?: {
|
|
78
|
+
context?: unknown;
|
|
70
79
|
}) => Promise<unknown>;
|
|
71
80
|
}) => Promise<unknown>;
|
|
72
81
|
/** Registry-wide defaults applied to every operation of one Command. */
|
|
@@ -74,7 +83,7 @@ type CommandDefaults = Readonly<{
|
|
|
74
83
|
/** Runs before every operation's own guard. Throw to deny. */
|
|
75
84
|
guard?: (context: never) => MaybePromise<void>;
|
|
76
85
|
/** Outermost middleware, in array order, around every operation. */
|
|
77
|
-
middleware?: readonly
|
|
86
|
+
middleware?: readonly AnyCommandMiddleware[];
|
|
78
87
|
}>;
|
|
79
88
|
/** Injected permission policy: throw to deny. Host semantics, kit ordering. */
|
|
80
89
|
type PermissionChecker = (context: never, input: Readonly<{
|
|
@@ -113,8 +122,11 @@ declare class BoundCommand<Context, const Operations extends AuditedRegistry> {
|
|
|
113
122
|
/** Declared aggregate types per operation — introspectable for tests. */
|
|
114
123
|
readonly aggregates: Readonly<{ [Key in OperationKey<Operations>]: Operations[Key]['aggregates']; }>;
|
|
115
124
|
static operation<const Definition extends AuditedOperation>(definition: Definition): Definition;
|
|
116
|
-
/**
|
|
117
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Identity helper that types a middleware against its context and the
|
|
127
|
+
* context extension it passes downstream via next({ context }).
|
|
128
|
+
*/
|
|
129
|
+
static middleware<Context = never, const Extension extends Record<string, unknown> = Record<string, unknown>>(middleware: CommandMiddleware<Context, Extension>): CommandMiddleware<Context, Extension>;
|
|
118
130
|
constructor(operations: Operations, deps: {
|
|
119
131
|
observability: Observability;
|
|
120
132
|
writeAudit: AuditWriter;
|
|
@@ -131,6 +143,11 @@ declare class BoundCommand<Context, const Operations extends AuditedRegistry> {
|
|
|
131
143
|
}> ? Output : never : never : never>;
|
|
132
144
|
}
|
|
133
145
|
type ApplicationCommand<Context, Operations extends AuditedRegistry> = BoundCommand<Context, Operations>;
|
|
146
|
+
/**
|
|
147
|
+
* The constructor shape a Foundation instance exposes as `Command`.
|
|
148
|
+
* Exported as a TYPE ONLY so factories (e.g. cvx-kit/crud) can accept the
|
|
149
|
+
* destructured facade class as input without importing any runtime kernel.
|
|
150
|
+
*/
|
|
134
151
|
type CommandConstructor = {
|
|
135
152
|
new <Context, const Operations extends AuditedRegistry>(operations: Operations, defaults?: CommandDefaults): BoundCommand<Context, Operations>;
|
|
136
153
|
operation: (typeof BoundCommand)['operation'];
|
|
@@ -153,5 +170,5 @@ declare class Foundation<Component extends FoundationComponentApi = FoundationCo
|
|
|
153
170
|
constructor(component: Component, options: FoundationOptions);
|
|
154
171
|
}
|
|
155
172
|
//#endregion
|
|
156
|
-
export { ApplicationCommand, AuditEntryInput, AuditWriter, AuditedOperation, AuditedRegistry, CommandDefaults, type CommandExecution, type CommandInput, CommandMiddleware, type CommandObservation, type CommandRegistry, type CommandResult, Foundation, FoundationOptions, type ObservabilityOptions, type Parseable, PermissionChecker, type QueryExecution, type QueryMiddleware, type Result, type ResultBoundary, type TransactionMetricsContext, client_d_exports };
|
|
173
|
+
export { AnyCommandMiddleware, type AnyQueryMiddleware, ApplicationCommand, AuditEntryInput, AuditWriter, AuditedOperation, AuditedRegistry, CommandConstructor, CommandDefaults, type CommandExecution, type CommandInput, CommandMiddleware, type CommandObservation, type CommandRegistry, type CommandResult, Foundation, FoundationOptions, type ObservabilityOptions, type Parseable, PermissionChecker, type QueryExecution, type QueryMiddleware, type Result, type ResultBoundary, type TransactionMetricsContext, client_d_exports };
|
|
157
174
|
//# sourceMappingURL=client.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.mjs","names":["#kernel","#observability","#writeAudit","#checkPermission","#defaults","CommandKernelClass","#execute"],"sources":["../../../src/components/foundation/client.ts"],"sourcesContent":["import {\n\tCommand as CommandKernelClass,\n\ttype CommandExecution,\n\ttype CommandInput,\n\ttype CommandRegistry,\n\ttype CommandResult,\n} from './modules/command/command'\nimport {\n\tObservability,\n\ttype ObservabilityOptions,\n} from './modules/observability/observability'\nimport { Query } from './modules/query/query'\nimport { executeResultBoundary, projectResult } from './result'\nimport { emitSemanticEvent } from './telemetry'\n\n/** Shape handed to the injected audit writer; classification comes from the operation. */\nexport type AuditEntryInput = {\n\toperation: string\n\tactorId: string\n\taggregate: { type: string; id: string }\n\tmetadata?: Record<string, unknown>\n\tclassification: string\n}\n\nexport type AuditWriter = (\n\tcontext: never,\n\tentry: AuditEntryInput,\n) => Promise<unknown> | unknown\n\ntype MaybePromise<Value> = Value | Promise<Value>\ntype OperationKey<Operations extends CommandRegistry> = Extract<\n\tkeyof Operations,\n\tstring\n>\n\nexport type AuditedOperation = Readonly<{\n\tcommand: Readonly<{ parse: (value: unknown) => unknown }>\n\tresult: Readonly<{ parse: (value: unknown) => unknown }>\n\tclassification: string\n\t/**\n\t * Permission slug required to execute this operation. Checked through the\n\t * Foundation's injected checkPermission BEFORE guards and the handler;\n\t * declaring a permission without injecting a checker fails closed.\n\t */\n\tpermission?: string\n\t/**\n\t * Per-operation precondition, after the permission check and the registry\n\t * default guard, before the handler. Throw to deny — nothing has run yet.\n\t */\n\tguard?: (context: never, command: never) => MaybePromise<void>\n\t/**\n\t * Allowlist of aggregate types this operation's audit may reference. When\n\t * declared, an audit entry whose aggregate.type is not listed throws —\n\t * the audit vocabulary is enforced, not advisory.\n\t */\n\taggregates?: readonly string[]\n\t/** Per-operation middleware, inside the registry-wide chain. */\n\tmiddleware?: readonly CommandMiddleware[]\n\taudit: (\n\t\tresolution: Readonly<{ command: never; result: never }>,\n\t\tcontext: never,\n\t) => MaybePromise<Omit<AuditEntryInput, 'classification'> | null>\n}>\n\n/**\n * Composable, next()-based middleware wrapping [guards → handler]. Runs\n * INSIDE the pipeline's invariants: after the permission check, before the\n * result-schema parse, aggregate allowlist, and audit — so middleware can\n * time, trace, enrich context (`next({ context })`), short-circuit, or\n * transform results, but can never skip authorization, return an invalid\n * result, or desynchronize audit from effects.\n */\nexport type CommandMiddleware<Context = never> = (input: {\n\toperation: string\n\tcommand: unknown\n\tcontext: Context\n\tnext: (options?: {\n\t\t/** Merged into the context handed to inner middleware, guards, handler. */\n\t\tcontext?: Record<string, unknown>\n\t}) => Promise<unknown>\n}) => Promise<unknown>\n\n/** Registry-wide defaults applied to every operation of one Command. */\nexport type CommandDefaults = Readonly<{\n\t/** Runs before every operation's own guard. Throw to deny. */\n\tguard?: (context: never) => MaybePromise<void>\n\t/** Outermost middleware, in array order, around every operation. */\n\tmiddleware?: readonly CommandMiddleware[]\n}>\n\n/** Injected permission policy: throw to deny. Host semantics, kit ordering. */\nexport type PermissionChecker = (\n\tcontext: never,\n\tinput: Readonly<{ permission: string; operation: string }>,\n) => MaybePromise<void>\n\nexport type AuditedRegistry = Readonly<Record<string, AuditedOperation>>\n\nexport type FoundationOptions = Readonly<{\n\tobservability: ObservabilityOptions &\n\t\tReadonly<{\n\t\t\t/**\n\t\t\t * In-transaction audit writer used by Command. Operations that\n\t\t\t * return an audit record get it written here automatically.\n\t\t\t */\n\t\t\twriteAudit?: AuditWriter\n\t\t}>\n\t/**\n\t * Permission policy for operations that declare a `permission`. Runs\n\t * before guards and the handler; throw to deny. An operation with a\n\t * permission but no injected checker fails closed.\n\t */\n\tcheckPermission?: PermissionChecker\n}>\n\ntype FoundationComponentApi = Readonly<{\n\tfunctions: Readonly<{ status: unknown }>\n}>\n\n/**\n * Application command protocol bound to a Foundation instance:\n * validate, observe, execute, and audit. `classification` and `audit()`\n * are mandatory per operation — auditing is type-enforced, not opt-in.\n * Obtain it by destructuring the Foundation: `const { Command } = new Foundation(...)`.\n */\nclass BoundCommand<Context, const Operations extends AuditedRegistry> {\n\treadonly #kernel: CommandKernelClass<Context, Operations>\n\treadonly #observability: Observability\n\treadonly #writeAudit: AuditWriter\n\treadonly #checkPermission: PermissionChecker | undefined\n\treadonly #defaults: CommandDefaults\n\n\t/** Declared aggregate types per operation — introspectable for tests. */\n\treadonly aggregates: Readonly<{\n\t\t[Key in OperationKey<Operations>]: Operations[Key]['aggregates']\n\t}>\n\n\tstatic operation<const Definition extends AuditedOperation>(\n\t\tdefinition: Definition,\n\t): Definition {\n\t\treturn definition\n\t}\n\n\t/** Identity helper that types a middleware against its context. */\n\tstatic middleware<Context = never>(\n\t\tmiddleware: CommandMiddleware<Context>,\n\t): CommandMiddleware<Context> {\n\t\treturn middleware\n\t}\n\n\tconstructor(\n\t\toperations: Operations,\n\t\tdeps: {\n\t\t\tobservability: Observability\n\t\t\twriteAudit: AuditWriter\n\t\t\tcheckPermission?: PermissionChecker\n\t\t\tdefaults?: CommandDefaults\n\t\t},\n\t) {\n\t\tthis.#observability = deps.observability\n\t\tthis.#writeAudit = deps.writeAudit\n\t\tthis.#checkPermission = deps.checkPermission\n\t\tthis.#defaults = deps.defaults ?? {}\n\t\tthis.aggregates = Object.freeze(\n\t\t\tObject.fromEntries(\n\t\t\t\tObject.entries(operations).map(([operation, definition]) => [\n\t\t\t\t\toperation,\n\t\t\t\t\tdefinition.aggregates,\n\t\t\t\t]),\n\t\t\t),\n\t\t) as this['aggregates']\n\t\tthis.#kernel = new CommandKernelClass<Context, Operations>({\n\t\t\toperations,\n\t\t\texecute: (execution) => this.#execute(execution),\n\t\t})\n\t}\n\n\texec<const Key extends OperationKey<Operations>>(executor: {\n\t\toperation: Key\n\t\thandler: (\n\t\t\tcontext: Context,\n\t\t\tcommand: CommandInput<Operations, Key>,\n\t\t) => MaybePromise<CommandResult<Operations, Key>>\n\t}) {\n\t\treturn this.#kernel.exec(executor)\n\t}\n\n\tasync #execute<Key extends OperationKey<Operations>>(\n\t\texecution: CommandExecution<Context, Operations, Key>,\n\t): Promise<CommandResult<Operations, Key>> {\n\t\treturn this.#observability.observe(\n\t\t\t{\n\t\t\t\toperation: execution.operation,\n\t\t\t\tclassification: execution.definition.classification,\n\t\t\t},\n\t\t\tasync () => {\n\t\t\t\t// Guards run before the handler: permission → default → operation.\n\t\t\t\tconst permission = execution.definition.permission\n\t\t\t\tif (permission !== undefined) {\n\t\t\t\t\tif (!this.#checkPermission) {\n\t\t\t\t\t\tthrow new CommandPermissionError(execution.operation)\n\t\t\t\t\t}\n\t\t\t\t\tawait this.#checkPermission(execution.context as never, {\n\t\t\t\t\t\tpermission,\n\t\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tconst middleware = [\n\t\t\t\t\t...(this.#defaults.middleware ?? []),\n\t\t\t\t\t...(execution.definition.middleware ?? []),\n\t\t\t\t]\n\t\t\t\tconst terminal = async (context: unknown) => {\n\t\t\t\t\tawait this.#defaults.guard?.(context as never)\n\t\t\t\t\tawait execution.definition.guard?.(\n\t\t\t\t\t\tcontext as never,\n\t\t\t\t\t\texecution.command as never,\n\t\t\t\t\t)\n\t\t\t\t\treturn execution.run(context as never)\n\t\t\t\t}\n\t\t\t\tlet deepest = -1\n\t\t\t\tconst dispatch = async (\n\t\t\t\t\tindex: number,\n\t\t\t\t\tcontext: unknown,\n\t\t\t\t): Promise<unknown> => {\n\t\t\t\t\tif (index <= deepest) {\n\t\t\t\t\t\tthrow new CommandMiddlewareError(execution.operation)\n\t\t\t\t\t}\n\t\t\t\t\tdeepest = index\n\t\t\t\t\tconst layer = middleware[index]\n\t\t\t\t\tif (!layer) return terminal(context)\n\t\t\t\t\treturn layer({\n\t\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\t\tcommand: execution.command,\n\t\t\t\t\t\tcontext: context as never,\n\t\t\t\t\t\tnext: (options) =>\n\t\t\t\t\t\t\tdispatch(\n\t\t\t\t\t\t\t\tindex + 1,\n\t\t\t\t\t\t\t\toptions?.context\n\t\t\t\t\t\t\t\t\t? { ...(context as object), ...options.context }\n\t\t\t\t\t\t\t\t\t: context,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// Middleware may transform the return value, so the strict\n\t\t\t\t// result schema re-parses whatever leaves the chain.\n\t\t\t\tconst result = execution.parseResult(\n\t\t\t\t\tawait dispatch(0, execution.context),\n\t\t\t\t)\n\t\t\t\tconst audit = await execution.definition.audit(\n\t\t\t\t\t{ command: execution.command, result } as never,\n\t\t\t\t\texecution.context as never,\n\t\t\t\t)\n\t\t\t\tif (audit) {\n\t\t\t\t\tconst allowed = execution.definition.aggregates\n\t\t\t\t\tif (allowed && !allowed.includes(audit.aggregate.type)) {\n\t\t\t\t\t\tthrow new CommandAggregateError(\n\t\t\t\t\t\t\texecution.operation,\n\t\t\t\t\t\t\taudit.aggregate.type,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tawait this.#writeAudit(execution.context as never, {\n\t\t\t\t\t\t...audit,\n\t\t\t\t\t\tclassification: execution.definition.classification,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\treturn result\n\t\t\t},\n\t\t)\n\t}\n}\n\n/** A middleware called next() more than once. */\nclass CommandMiddlewareError extends Error {\n\treadonly code = 'COMMAND_MIDDLEWARE_NEXT_REUSED'\n\treadonly name = 'CommandMiddlewareError'\n\n\tconstructor(operation: string) {\n\t\tsuper(\n\t\t\t`A middleware for operation \"${operation}\" called next() more than once`,\n\t\t)\n\t}\n}\n\n/** The audit referenced an aggregate type outside the operation's allowlist. */\nclass CommandAggregateError extends Error {\n\treadonly code = 'COMMAND_AGGREGATE_NOT_DECLARED'\n\treadonly name = 'CommandAggregateError'\n\n\tconstructor(operation: string, aggregateType: string) {\n\t\tsuper(\n\t\t\t`Operation \"${operation}\" audited aggregate type \"${aggregateType}\" outside its declared aggregates`,\n\t\t)\n\t}\n}\n\n/** Fails closed: an operation declared a permission but no checker exists. */\nclass CommandPermissionError extends Error {\n\treadonly code = 'COMMAND_PERMISSION_NOT_CONFIGURED'\n\treadonly name = 'CommandPermissionError'\n\n\tconstructor(operation: string) {\n\t\tsuper(\n\t\t\t`Operation \"${operation}\" declares a permission but the Foundation has no checkPermission`,\n\t\t)\n\t}\n}\n\nexport type ApplicationCommand<\n\tContext,\n\tOperations extends AuditedRegistry,\n> = BoundCommand<Context, Operations>\n\ntype CommandConstructor = {\n\tnew <Context, const Operations extends AuditedRegistry>(\n\t\toperations: Operations,\n\t\tdefaults?: CommandDefaults,\n\t): BoundCommand<Context, Operations>\n\toperation: (typeof BoundCommand)['operation']\n\tmiddleware: (typeof BoundCommand)['middleware']\n}\n\n/**\n * Host facade for a component that owns no application execution capability.\n * Declared once; the sole source of the command protocol, query kernel, and\n * observability: `const { Command, Query, observability } = new Foundation(...)`.\n */\nexport class Foundation<\n\tComponent extends FoundationComponentApi = FoundationComponentApi,\n> {\n\treadonly status: Component['functions']['status']\n\treadonly Command: CommandConstructor\n\treadonly Query = Query\n\treadonly observability: Observability\n\t/** Typed-failure boundary — see result.ts. Facade-bound like the kernels. */\n\treadonly executeResultBoundary = executeResultBoundary\n\treadonly projectResult = projectResult\n\treadonly emitSemanticEvent = emitSemanticEvent\n\n\tconstructor(component: Component, options: FoundationOptions) {\n\t\tthis.status = component.functions.status\n\t\tconst observability = new Observability(options.observability)\n\t\tthis.observability = observability\n\t\tconst writeAudit = options.observability.writeAudit ?? (() => undefined)\n\t\tconst checkPermission = options.checkPermission\n\t\tthis.Command = class <\n\t\t\tContext,\n\t\t\tconst Operations extends AuditedRegistry,\n\t\t> extends BoundCommand<Context, Operations> {\n\t\t\tconstructor(operations: Operations, defaults?: CommandDefaults) {\n\t\t\t\tsuper(operations, {\n\t\t\t\t\tobservability,\n\t\t\t\t\twriteAudit,\n\t\t\t\t\tcheckPermission,\n\t\t\t\t\tdefaults,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Kernel CLASSES are deliberately NOT exported: Command, Query, and\n// Observability exist only as capabilities of a Foundation instance —\n// `const { Command, Query, observability } = new Foundation(...)`. A loose\n// import would construct kernels without the injected observability, audit\n// writer, and permission checker. Types stay exported for signatures.\nexport type {\n\tCommandExecution,\n\tCommandInput,\n\tCommandRegistry,\n\tCommandResult,\n\tParseable,\n} from './modules/command/command'\nexport type {\n\tCommandObservation,\n\tObservabilityOptions,\n} from './modules/observability/observability'\nexport type {\n\tQueryExecution,\n\tQueryMiddleware,\n} from './modules/query/query'\nexport type {\n\tResult,\n\tResultBoundary,\n\tTransactionMetricsContext,\n} from './result'\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 foundation from 'cvx-kit/components/foundation/convex.config'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.mjs","names":["#kernel","#observability","#writeAudit","#checkPermission","#defaults","CommandKernelClass","#execute"],"sources":["../../../src/components/foundation/client.ts"],"sourcesContent":["import {\n\tCommand as CommandKernelClass,\n\ttype CommandExecution,\n\ttype CommandInput,\n\ttype CommandRegistry,\n\ttype CommandResult,\n} from './modules/command/command'\nimport {\n\tObservability,\n\ttype ObservabilityOptions,\n} from './modules/observability/observability'\nimport { Query } from './modules/query/query'\nimport { executeResultBoundary, projectResult } from './result'\nimport { emitSemanticEvent } from './telemetry'\n\n/** Shape handed to the injected audit writer; classification comes from the operation. */\nexport type AuditEntryInput = {\n\toperation: string\n\tactorId: string\n\taggregate: { type: string; id: string }\n\tmetadata?: Record<string, unknown>\n\tclassification: string\n}\n\nexport type AuditWriter = (\n\tcontext: never,\n\tentry: AuditEntryInput,\n) => Promise<unknown> | unknown\n\ntype MaybePromise<Value> = Value | Promise<Value>\ntype OperationKey<Operations extends CommandRegistry> = Extract<\n\tkeyof Operations,\n\tstring\n>\n\nexport type AuditedOperation = Readonly<{\n\tcommand: Readonly<{ parse: (value: unknown) => unknown }>\n\tresult: Readonly<{ parse: (value: unknown) => unknown }>\n\tclassification: string\n\t/**\n\t * Permission slug required to execute this operation. Checked through the\n\t * Foundation's injected checkPermission BEFORE guards and the handler;\n\t * declaring a permission without injecting a checker fails closed.\n\t */\n\tpermission?: string\n\t/**\n\t * Per-operation precondition, after the permission check and the registry\n\t * default guard, before the handler. Throw to deny — nothing has run yet.\n\t */\n\tguard?: (context: never, command: never) => MaybePromise<void>\n\t/**\n\t * Allowlist of aggregate types this operation's audit may reference. When\n\t * declared, an audit entry whose aggregate.type is not listed throws —\n\t * the audit vocabulary is enforced, not advisory.\n\t */\n\taggregates?: readonly string[]\n\t/** Per-operation middleware, inside the registry-wide chain. */\n\tmiddleware?: readonly AnyCommandMiddleware[]\n\taudit: (\n\t\tresolution: Readonly<{ command: never; result: never }>,\n\t\tcontext: never,\n\t) => MaybePromise<Omit<AuditEntryInput, 'classification'> | null>\n}>\n\n/**\n * Composable, next()-based middleware wrapping [guards → handler]. Runs\n * INSIDE the pipeline's invariants: after the permission check, before the\n * result-schema parse, aggregate allowlist, and audit — so middleware can\n * time, trace, enrich context (`next({ context })`), short-circuit, or\n * transform results, but can never skip authorization, return an invalid\n * result, or desynchronize audit from effects.\n */\nexport type CommandMiddleware<\n\tContext = never,\n\tExtension extends Record<string, unknown> = Record<string, unknown>,\n> = (input: {\n\toperation: string\n\tcommand: unknown\n\tcontext: Context\n\tnext: (options?: {\n\t\t/** Merged into the context handed to inner middleware, guards, handler. */\n\t\tcontext?: Extension\n\t}) => Promise<unknown>\n}) => Promise<unknown>\n\n/** Any-context middleware — what registries and operations accept. */\n// Contravariant seam: the supertype every typed CommandMiddleware<C, E>\n// assigns to (context contra-narrows to never; next's options widen to\n// unknown). Definition sites stay typed via Command.middleware.\nexport type AnyCommandMiddleware = (input: {\n\toperation: string\n\tcommand: unknown\n\tcontext: never\n\tnext: (options?: { context?: unknown }) => Promise<unknown>\n}) => Promise<unknown>\n\n/** Registry-wide defaults applied to every operation of one Command. */\nexport type CommandDefaults = Readonly<{\n\t/** Runs before every operation's own guard. Throw to deny. */\n\tguard?: (context: never) => MaybePromise<void>\n\t/** Outermost middleware, in array order, around every operation. */\n\tmiddleware?: readonly AnyCommandMiddleware[]\n}>\n\n/** Injected permission policy: throw to deny. Host semantics, kit ordering. */\nexport type PermissionChecker = (\n\tcontext: never,\n\tinput: Readonly<{ permission: string; operation: string }>,\n) => MaybePromise<void>\n\nexport type AuditedRegistry = Readonly<Record<string, AuditedOperation>>\n\nexport type FoundationOptions = Readonly<{\n\tobservability: ObservabilityOptions &\n\t\tReadonly<{\n\t\t\t/**\n\t\t\t * In-transaction audit writer used by Command. Operations that\n\t\t\t * return an audit record get it written here automatically.\n\t\t\t */\n\t\t\twriteAudit?: AuditWriter\n\t\t}>\n\t/**\n\t * Permission policy for operations that declare a `permission`. Runs\n\t * before guards and the handler; throw to deny. An operation with a\n\t * permission but no injected checker fails closed.\n\t */\n\tcheckPermission?: PermissionChecker\n}>\n\ntype FoundationComponentApi = Readonly<{\n\tfunctions: Readonly<{ status: unknown }>\n}>\n\n/**\n * Application command protocol bound to a Foundation instance:\n * validate, observe, execute, and audit. `classification` and `audit()`\n * are mandatory per operation — auditing is type-enforced, not opt-in.\n * Obtain it by destructuring the Foundation: `const { Command } = new Foundation(...)`.\n */\nclass BoundCommand<Context, const Operations extends AuditedRegistry> {\n\treadonly #kernel: CommandKernelClass<Context, Operations>\n\treadonly #observability: Observability\n\treadonly #writeAudit: AuditWriter\n\treadonly #checkPermission: PermissionChecker | undefined\n\treadonly #defaults: CommandDefaults\n\n\t/** Declared aggregate types per operation — introspectable for tests. */\n\treadonly aggregates: Readonly<{\n\t\t[Key in OperationKey<Operations>]: Operations[Key]['aggregates']\n\t}>\n\n\tstatic operation<const Definition extends AuditedOperation>(\n\t\tdefinition: Definition,\n\t): Definition {\n\t\treturn definition\n\t}\n\n\t/**\n\t * Identity helper that types a middleware against its context and the\n\t * context extension it passes downstream via next({ context }).\n\t */\n\tstatic middleware<\n\t\tContext = never,\n\t\tconst Extension extends Record<string, unknown> = Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>,\n\t>(\n\t\tmiddleware: CommandMiddleware<Context, Extension>,\n\t): CommandMiddleware<Context, Extension> {\n\t\treturn middleware\n\t}\n\n\tconstructor(\n\t\toperations: Operations,\n\t\tdeps: {\n\t\t\tobservability: Observability\n\t\t\twriteAudit: AuditWriter\n\t\t\tcheckPermission?: PermissionChecker\n\t\t\tdefaults?: CommandDefaults\n\t\t},\n\t) {\n\t\tthis.#observability = deps.observability\n\t\tthis.#writeAudit = deps.writeAudit\n\t\tthis.#checkPermission = deps.checkPermission\n\t\tthis.#defaults = deps.defaults ?? {}\n\t\tthis.aggregates = Object.freeze(\n\t\t\tObject.fromEntries(\n\t\t\t\tObject.entries(operations).map(([operation, definition]) => [\n\t\t\t\t\toperation,\n\t\t\t\t\tdefinition.aggregates,\n\t\t\t\t]),\n\t\t\t),\n\t\t) as this['aggregates']\n\t\tthis.#kernel = new CommandKernelClass<Context, Operations>({\n\t\t\toperations,\n\t\t\texecute: (execution) => this.#execute(execution),\n\t\t})\n\t}\n\n\texec<const Key extends OperationKey<Operations>>(executor: {\n\t\toperation: Key\n\t\thandler: (\n\t\t\tcontext: Context,\n\t\t\tcommand: CommandInput<Operations, Key>,\n\t\t) => MaybePromise<CommandResult<Operations, Key>>\n\t}) {\n\t\treturn this.#kernel.exec(executor)\n\t}\n\n\tasync #execute<Key extends OperationKey<Operations>>(\n\t\texecution: CommandExecution<Context, Operations, Key>,\n\t): Promise<CommandResult<Operations, Key>> {\n\t\treturn this.#observability.observe(\n\t\t\t{\n\t\t\t\toperation: execution.operation,\n\t\t\t\tclassification: execution.definition.classification,\n\t\t\t},\n\t\t\tasync () => {\n\t\t\t\t// Guards run before the handler: permission → default → operation.\n\t\t\t\tconst permission = execution.definition.permission\n\t\t\t\tif (permission !== undefined) {\n\t\t\t\t\tif (!this.#checkPermission) {\n\t\t\t\t\t\tthrow new CommandPermissionError(execution.operation)\n\t\t\t\t\t}\n\t\t\t\t\tawait this.#checkPermission(execution.context as never, {\n\t\t\t\t\t\tpermission,\n\t\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tconst middleware = [\n\t\t\t\t\t...(this.#defaults.middleware ?? []),\n\t\t\t\t\t...(execution.definition.middleware ?? []),\n\t\t\t\t]\n\t\t\t\tconst terminal = async (context: unknown) => {\n\t\t\t\t\tawait this.#defaults.guard?.(context as never)\n\t\t\t\t\tawait execution.definition.guard?.(\n\t\t\t\t\t\tcontext as never,\n\t\t\t\t\t\texecution.command as never,\n\t\t\t\t\t)\n\t\t\t\t\treturn execution.run(context as never)\n\t\t\t\t}\n\t\t\t\tlet deepest = -1\n\t\t\t\tconst dispatch = async (\n\t\t\t\t\tindex: number,\n\t\t\t\t\tcontext: unknown,\n\t\t\t\t): Promise<unknown> => {\n\t\t\t\t\tif (index <= deepest) {\n\t\t\t\t\t\tthrow new CommandMiddlewareError(execution.operation)\n\t\t\t\t\t}\n\t\t\t\t\tdeepest = index\n\t\t\t\t\tconst layer = middleware[index]\n\t\t\t\t\tif (!layer) return terminal(context)\n\t\t\t\t\treturn layer({\n\t\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\t\tcommand: execution.command,\n\t\t\t\t\t\tcontext: context as never,\n\t\t\t\t\t\tnext: (options) =>\n\t\t\t\t\t\t\tdispatch(\n\t\t\t\t\t\t\t\tindex + 1,\n\t\t\t\t\t\t\t\toptions?.context\n\t\t\t\t\t\t\t\t\t? { ...(context as object), ...(options.context as object) }\n\t\t\t\t\t\t\t\t\t: context,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// Middleware may transform the return value, so the strict\n\t\t\t\t// result schema re-parses whatever leaves the chain.\n\t\t\t\tconst result = execution.parseResult(\n\t\t\t\t\tawait dispatch(0, execution.context),\n\t\t\t\t)\n\t\t\t\tconst audit = await execution.definition.audit(\n\t\t\t\t\t{ command: execution.command, result } as never,\n\t\t\t\t\texecution.context as never,\n\t\t\t\t)\n\t\t\t\tif (audit) {\n\t\t\t\t\tconst allowed = execution.definition.aggregates\n\t\t\t\t\tif (allowed && !allowed.includes(audit.aggregate.type)) {\n\t\t\t\t\t\tthrow new CommandAggregateError(\n\t\t\t\t\t\t\texecution.operation,\n\t\t\t\t\t\t\taudit.aggregate.type,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tawait this.#writeAudit(execution.context as never, {\n\t\t\t\t\t\t...audit,\n\t\t\t\t\t\tclassification: execution.definition.classification,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\treturn result\n\t\t\t},\n\t\t)\n\t}\n}\n\n/** A middleware called next() more than once. */\nclass CommandMiddlewareError extends Error {\n\treadonly code = 'COMMAND_MIDDLEWARE_NEXT_REUSED'\n\treadonly name = 'CommandMiddlewareError'\n\n\tconstructor(operation: string) {\n\t\tsuper(\n\t\t\t`A middleware for operation \"${operation}\" called next() more than once`,\n\t\t)\n\t}\n}\n\n/** The audit referenced an aggregate type outside the operation's allowlist. */\nclass CommandAggregateError extends Error {\n\treadonly code = 'COMMAND_AGGREGATE_NOT_DECLARED'\n\treadonly name = 'CommandAggregateError'\n\n\tconstructor(operation: string, aggregateType: string) {\n\t\tsuper(\n\t\t\t`Operation \"${operation}\" audited aggregate type \"${aggregateType}\" outside its declared aggregates`,\n\t\t)\n\t}\n}\n\n/** Fails closed: an operation declared a permission but no checker exists. */\nclass CommandPermissionError extends Error {\n\treadonly code = 'COMMAND_PERMISSION_NOT_CONFIGURED'\n\treadonly name = 'CommandPermissionError'\n\n\tconstructor(operation: string) {\n\t\tsuper(\n\t\t\t`Operation \"${operation}\" declares a permission but the Foundation has no checkPermission`,\n\t\t)\n\t}\n}\n\nexport type ApplicationCommand<\n\tContext,\n\tOperations extends AuditedRegistry,\n> = BoundCommand<Context, Operations>\n\n/**\n * The constructor shape a Foundation instance exposes as `Command`.\n * Exported as a TYPE ONLY so factories (e.g. cvx-kit/crud) can accept the\n * destructured facade class as input without importing any runtime kernel.\n */\nexport type CommandConstructor = {\n\tnew <Context, const Operations extends AuditedRegistry>(\n\t\toperations: Operations,\n\t\tdefaults?: CommandDefaults,\n\t): BoundCommand<Context, Operations>\n\toperation: (typeof BoundCommand)['operation']\n\tmiddleware: (typeof BoundCommand)['middleware']\n}\n\n/**\n * Host facade for a component that owns no application execution capability.\n * Declared once; the sole source of the command protocol, query kernel, and\n * observability: `const { Command, Query, observability } = new Foundation(...)`.\n */\nexport class Foundation<\n\tComponent extends FoundationComponentApi = FoundationComponentApi,\n> {\n\treadonly status: Component['functions']['status']\n\treadonly Command: CommandConstructor\n\treadonly Query = Query\n\treadonly observability: Observability\n\t/** Typed-failure boundary — see result.ts. Facade-bound like the kernels. */\n\treadonly executeResultBoundary = executeResultBoundary\n\treadonly projectResult = projectResult\n\treadonly emitSemanticEvent = emitSemanticEvent\n\n\tconstructor(component: Component, options: FoundationOptions) {\n\t\tthis.status = component.functions.status\n\t\tconst observability = new Observability(options.observability)\n\t\tthis.observability = observability\n\t\tconst writeAudit = options.observability.writeAudit ?? (() => undefined)\n\t\tconst checkPermission = options.checkPermission\n\t\tthis.Command = class <\n\t\t\tContext,\n\t\t\tconst Operations extends AuditedRegistry,\n\t\t> extends BoundCommand<Context, Operations> {\n\t\t\tconstructor(operations: Operations, defaults?: CommandDefaults) {\n\t\t\t\tsuper(operations, {\n\t\t\t\t\tobservability,\n\t\t\t\t\twriteAudit,\n\t\t\t\t\tcheckPermission,\n\t\t\t\t\tdefaults,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Kernel CLASSES are deliberately NOT exported: Command, Query, and\n// Observability exist only as capabilities of a Foundation instance —\n// `const { Command, Query, observability } = new Foundation(...)`. A loose\n// import would construct kernels without the injected observability, audit\n// writer, and permission checker. Types stay exported for signatures.\nexport type {\n\tCommandExecution,\n\tCommandInput,\n\tCommandRegistry,\n\tCommandResult,\n\tParseable,\n} from './modules/command/command'\nexport type {\n\tCommandObservation,\n\tObservabilityOptions,\n} from './modules/observability/observability'\nexport type {\n\tAnyQueryMiddleware,\n\tQueryExecution,\n\tQueryMiddleware,\n} from './modules/query/query'\nexport type {\n\tResult,\n\tResultBoundary,\n\tTransactionMetricsContext,\n} from './result'\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 foundation from 'cvx-kit/components/foundation/convex.config'\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
declare namespace query_d_exports {
|
|
2
|
-
export { Query, QueryExecution, QueryMiddleware };
|
|
2
|
+
export { AnyQueryMiddleware, Query, QueryExecution, QueryMiddleware };
|
|
3
3
|
}
|
|
4
4
|
type QueryExecution<Context, Metadata, Result> = Readonly<{
|
|
5
5
|
context: Context;
|
|
@@ -12,29 +12,40 @@ type MaybePromise<Value> = Value | Promise<Value>;
|
|
|
12
12
|
* inside the host's injected execute policy: `next({ context })` merges
|
|
13
13
|
* enrichment into the context handed to inner middleware and the handler.
|
|
14
14
|
*/
|
|
15
|
-
type QueryMiddleware<Context = never> = (input: {
|
|
15
|
+
type QueryMiddleware<Context = never, Extension extends Record<string, unknown> = Record<string, unknown>> = (input: {
|
|
16
16
|
metadata: object;
|
|
17
17
|
context: Context;
|
|
18
18
|
next: (options?: {
|
|
19
|
-
context?:
|
|
19
|
+
context?: Extension;
|
|
20
|
+
}) => Promise<unknown>;
|
|
21
|
+
}) => Promise<unknown>;
|
|
22
|
+
/** Any-context middleware — what kernels and executors accept. */
|
|
23
|
+
type AnyQueryMiddleware = (input: {
|
|
24
|
+
metadata: object;
|
|
25
|
+
context: never;
|
|
26
|
+
next: (options?: {
|
|
27
|
+
context?: unknown;
|
|
20
28
|
}) => Promise<unknown>;
|
|
21
29
|
}) => Promise<unknown>;
|
|
22
30
|
/** Generic query kernel. Authorization remains an injected host policy. */
|
|
23
31
|
declare class Query<Context, Defaults extends object> {
|
|
24
32
|
#private;
|
|
25
|
-
/**
|
|
26
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Identity helper that types a middleware against its context and the
|
|
35
|
+
* extension it passes downstream via next({ context }).
|
|
36
|
+
*/
|
|
37
|
+
static middleware<Context = never, const Extension extends Record<string, unknown> = Record<string, unknown>>(middleware: QueryMiddleware<Context, Extension>): QueryMiddleware<Context, Extension>;
|
|
27
38
|
constructor(configuration: {
|
|
28
39
|
readonly defaults: Defaults;
|
|
29
|
-
readonly middleware?: readonly
|
|
40
|
+
readonly middleware?: readonly AnyQueryMiddleware[];
|
|
30
41
|
readonly execute: <Metadata extends object, Result>(execution: QueryExecution<Context, Defaults & Metadata, Result>) => Promise<Result>;
|
|
31
42
|
});
|
|
32
43
|
exec<Arguments extends readonly unknown[], Result, Metadata extends object = object>(executor: {
|
|
33
44
|
readonly metadata?: Metadata;
|
|
34
|
-
readonly middleware?: readonly
|
|
45
|
+
readonly middleware?: readonly AnyQueryMiddleware[];
|
|
35
46
|
readonly handler: (context: Context, ...args: Arguments) => MaybePromise<Result>;
|
|
36
47
|
}): (context: Context, ...args: Arguments) => Promise<Result>;
|
|
37
48
|
}
|
|
38
49
|
//#endregion
|
|
39
|
-
export { Query, QueryExecution, QueryMiddleware, query_d_exports };
|
|
50
|
+
export { AnyQueryMiddleware, Query, QueryExecution, QueryMiddleware, query_d_exports };
|
|
40
51
|
//# sourceMappingURL=query.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.mjs","names":["#defaults","#middleware","#execute"],"sources":["../../../../../src/components/foundation/modules/query/query.ts"],"sourcesContent":["export type QueryExecution<Context, Metadata, Result> = Readonly<{\n\tcontext: Context\n\tmetadata: Metadata\n\trun: () => Promise<Result>\n}>\n\ntype MaybePromise<Value> = Value | Promise<Value>\n\n/**\n * Composable, next()-based middleware around every query handler. Runs\n * inside the host's injected execute policy: `next({ context })` merges\n * enrichment into the context handed to inner middleware and the handler.\n */\nexport type QueryMiddleware
|
|
1
|
+
{"version":3,"file":"query.mjs","names":["#defaults","#middleware","#execute"],"sources":["../../../../../src/components/foundation/modules/query/query.ts"],"sourcesContent":["export type QueryExecution<Context, Metadata, Result> = Readonly<{\n\tcontext: Context\n\tmetadata: Metadata\n\trun: () => Promise<Result>\n}>\n\ntype MaybePromise<Value> = Value | Promise<Value>\n\n/**\n * Composable, next()-based middleware around every query handler. Runs\n * inside the host's injected execute policy: `next({ context })` merges\n * enrichment into the context handed to inner middleware and the handler.\n */\nexport type QueryMiddleware<\n\tContext = never,\n\tExtension extends Record<string, unknown> = Record<string, unknown>,\n> = (input: {\n\tmetadata: object\n\tcontext: Context\n\tnext: (options?: {\n\t\tcontext?: Extension\n\t}) => Promise<unknown>\n}) => Promise<unknown>\n\n/** Any-context middleware — what kernels and executors accept. */\n// Contravariant seam: the supertype every typed QueryMiddleware<C, E>\n// assigns to (context contra-narrows to never; next's options widen to\n// unknown). Definition sites stay typed via Query.middleware.\nexport type AnyQueryMiddleware = (input: {\n\tmetadata: object\n\tcontext: never\n\tnext: (options?: { context?: unknown }) => Promise<unknown>\n}) => Promise<unknown>\n\nclass QueryMiddlewareError extends Error {\n\treadonly code = 'QUERY_MIDDLEWARE_NEXT_REUSED'\n\treadonly name = 'QueryMiddlewareError'\n\n\tconstructor() {\n\t\tsuper('A query middleware called next() more than once')\n\t}\n}\n\n/** Generic query kernel. Authorization remains an injected host policy. */\nexport class Query<Context, Defaults extends object> {\n\treadonly #defaults: Defaults\n\treadonly #middleware: readonly AnyQueryMiddleware[]\n\treadonly #execute: <Metadata extends object, Result>(\n\t\texecution: QueryExecution<Context, Defaults & Metadata, Result>,\n\t) => Promise<Result>\n\n\t/**\n\t * Identity helper that types a middleware against its context and the\n\t * extension it passes downstream via next({ context }).\n\t */\n\tstatic middleware<\n\t\tContext = never,\n\t\tconst Extension extends Record<string, unknown> = Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>,\n\t>(\n\t\tmiddleware: QueryMiddleware<Context, Extension>,\n\t): QueryMiddleware<Context, Extension> {\n\t\treturn middleware\n\t}\n\n\tconstructor(configuration: {\n\t\treadonly defaults: Defaults\n\t\treadonly middleware?: readonly AnyQueryMiddleware[]\n\t\treadonly execute: <Metadata extends object, Result>(\n\t\t\texecution: QueryExecution<Context, Defaults & Metadata, Result>,\n\t\t) => Promise<Result>\n\t}) {\n\t\tthis.#defaults = configuration.defaults\n\t\tthis.#middleware = configuration.middleware ?? []\n\t\tthis.#execute = configuration.execute\n\t}\n\n\texec<\n\t\tArguments extends readonly unknown[],\n\t\tResult,\n\t\tMetadata extends object = object,\n\t>(executor: {\n\t\treadonly metadata?: Metadata\n\t\treadonly middleware?: readonly AnyQueryMiddleware[]\n\t\treadonly handler: (\n\t\t\tcontext: Context,\n\t\t\t...args: Arguments\n\t\t) => MaybePromise<Result>\n\t}): (context: Context, ...args: Arguments) => Promise<Result> {\n\t\treturn (context, ...args) => {\n\t\t\tconst metadata = {\n\t\t\t\t...this.#defaults,\n\t\t\t\t...(executor.metadata ?? ({} as Metadata)),\n\t\t\t}\n\t\t\tconst middleware = [\n\t\t\t\t...this.#middleware,\n\t\t\t\t...(executor.middleware ?? []),\n\t\t\t]\n\t\t\tconst run = async (): Promise<Result> => {\n\t\t\t\tif (middleware.length === 0) {\n\t\t\t\t\treturn executor.handler(context, ...args)\n\t\t\t\t}\n\t\t\t\tlet deepest = -1\n\t\t\t\tconst dispatch = async (\n\t\t\t\t\tindex: number,\n\t\t\t\t\tcurrent: Context,\n\t\t\t\t): Promise<unknown> => {\n\t\t\t\t\tif (index <= deepest) throw new QueryMiddlewareError()\n\t\t\t\t\tdeepest = index\n\t\t\t\t\tconst layer = middleware[index]\n\t\t\t\t\tif (!layer) return executor.handler(current, ...args)\n\t\t\t\t\treturn layer({\n\t\t\t\t\t\tmetadata,\n\t\t\t\t\t\tcontext: current as never,\n\t\t\t\t\t\tnext: (options) =>\n\t\t\t\t\t\t\tdispatch(\n\t\t\t\t\t\t\t\tindex + 1,\n\t\t\t\t\t\t\t\toptions?.context\n\t\t\t\t\t\t\t\t\t? ({\n\t\t\t\t\t\t\t\t\t\t\t...(current as object),\n\t\t\t\t\t\t\t\t\t\t\t...(options.context as object),\n\t\t\t\t\t\t\t\t\t\t} as Context)\n\t\t\t\t\t\t\t\t\t: current,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\treturn (await dispatch(0, context)) as Result\n\t\t\t}\n\t\t\treturn this.#execute({ context, metadata, run })\n\t\t}\n\t}\n}\n"],"mappings":"AAkCA,IAAM,EAAN,cAAmC,KAAM,CACxC,KAAgB,+BAChB,KAAgB,uBAEhB,aAAc,CACb,MAAM,iDAAiD,CACxD,CACD,EAGa,EAAb,KAAqD,CACpD,GACA,GACA,GAQA,OAAO,WAON,EACsC,CACtC,OAAO,CACR,CAEA,YAAY,EAMT,CACF,KAAKA,GAAY,EAAc,SAC/B,KAAKC,GAAc,EAAc,YAAc,CAAC,EAChD,KAAKC,GAAW,EAAc,OAC/B,CAEA,KAIE,EAO4D,CAC7D,OAAQ,EAAS,GAAG,IAAS,CAC5B,IAAM,EAAW,CAChB,GAAG,KAAKF,GACR,GAAI,EAAS,UAAa,CAAC,CAC5B,EACM,EAAa,CAClB,GAAG,KAAKC,GACR,GAAI,EAAS,YAAc,CAAC,CAC7B,EA+BA,OAAO,KAAKC,GAAS,CAAE,UAAS,WAAU,aA9BD,CACxC,GAAI,EAAW,SAAW,EACzB,OAAO,EAAS,QAAQ,EAAS,GAAG,CAAI,EAEzC,IAAI,EAAU,GACR,EAAW,MAChB,EACA,IACsB,CACtB,GAAI,GAAS,EAAS,MAAM,IAAI,EAChC,EAAU,EACV,IAAM,EAAQ,EAAW,GAEzB,OADK,EACE,EAAM,CACZ,WACA,QAAS,EACT,KAAO,GACN,EACC,EAAQ,EACR,GAAS,QACL,CACD,GAAI,EACJ,GAAI,EAAQ,OACb,EACC,CACJ,CACF,CAAC,EAdkB,EAAS,QAAQ,EAAS,GAAG,CAAI,CAerD,EACA,OAAQ,MAAM,EAAS,EAAG,CAAO,CAClC,CAC8C,CAAC,CAChD,CACD,CACD"}
|
package/dist/crud.d.mts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ErrorFactory } from "./errors.mjs";
|
|
2
|
+
import { AnyCommandMiddleware, ApplicationCommand, AuditedRegistry, CommandConstructor } from "./components/foundation/client.mjs";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
//#region src/crud.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The zodTable surface the factory consumes — structural, so any zodTable /
|
|
7
|
+
* tenantTable return satisfies it.
|
|
8
|
+
*/
|
|
9
|
+
type CrudTable = {
|
|
10
|
+
tableName: string;
|
|
11
|
+
commandInput: z.ZodObject<Record<string, z.ZodType>>;
|
|
12
|
+
storage: z.ZodObject<Record<string, z.ZodType>>;
|
|
13
|
+
};
|
|
14
|
+
type CrudContext = {
|
|
15
|
+
db: {
|
|
16
|
+
insert: (table: never, value: never) => Promise<unknown>;
|
|
17
|
+
patch: (id: never, value: never) => Promise<unknown>;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
type MaybePromise<Value> = Value | Promise<Value>;
|
|
21
|
+
type CrudConfig<Context extends CrudContext> = {
|
|
22
|
+
/** The Foundation-bound Command class (destructured from the facade). */
|
|
23
|
+
Command: CommandConstructor;
|
|
24
|
+
table: CrudTable;
|
|
25
|
+
/** Audit aggregate type; enforced by the aggregates allowlist. */
|
|
26
|
+
aggregateType: string;
|
|
27
|
+
/** Observability/audit classification. Default 'business'. */
|
|
28
|
+
classification?: string;
|
|
29
|
+
/** Derives the audited actor id from the ctx (usually ctx.actor.userId). */
|
|
30
|
+
actor: (context: Context) => string;
|
|
31
|
+
/**
|
|
32
|
+
* Stamps server-owned fields onto creates (tenant, denormalized ids...).
|
|
33
|
+
* REQUIRED when the table is a tenantTable — forgetting it would insert
|
|
34
|
+
* untenanted rows; the factory fails fast at construction instead.
|
|
35
|
+
*/
|
|
36
|
+
enrich?: (context: Context, command: Record<string, unknown>) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
37
|
+
/** Per-operation preconditions; run before the handlers. */
|
|
38
|
+
guards?: {
|
|
39
|
+
create?: (context: Context, command: never) => MaybePromise<void>;
|
|
40
|
+
update?: (context: Context, command: never) => MaybePromise<void>;
|
|
41
|
+
archive?: (context: Context, command: never) => MaybePromise<void>;
|
|
42
|
+
};
|
|
43
|
+
/** Per-operation middleware applied to all three generated operations. */
|
|
44
|
+
middleware?: readonly AnyCommandMiddleware[];
|
|
45
|
+
errors?: ErrorFactory;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Generates the standard create/update/archive operations from one zodTable
|
|
49
|
+
* declaration, fully inside the audited command pipeline: strict command
|
|
50
|
+
* inputs from `commandFields`, mandatory audit with the declared aggregate
|
|
51
|
+
* type, soft delete only (archive sets `archivedAt` — no hard-delete
|
|
52
|
+
* operation is generated, per kit doctrine).
|
|
53
|
+
*
|
|
54
|
+
* convex-helpers ships a same-named `crud` helper; it was deliberately not
|
|
55
|
+
* used — it bypasses the command pipeline (no audits, no guards, and hard
|
|
56
|
+
* deletes). See docs/crud.md.
|
|
57
|
+
*/
|
|
58
|
+
type CrudCommands<Context extends CrudContext> = {
|
|
59
|
+
commands: ApplicationCommand<Context, AuditedRegistry>;
|
|
60
|
+
operations: AuditedRegistry;
|
|
61
|
+
executeCreate: (context: Context, command: Record<string, unknown>) => Promise<{
|
|
62
|
+
id: string;
|
|
63
|
+
}>;
|
|
64
|
+
executeUpdate: (context: Context, command: {
|
|
65
|
+
id: string;
|
|
66
|
+
data: Record<string, unknown>;
|
|
67
|
+
}) => Promise<{
|
|
68
|
+
ok: true;
|
|
69
|
+
}>;
|
|
70
|
+
executeArchive: (context: Context, command: {
|
|
71
|
+
id: string;
|
|
72
|
+
}) => Promise<{
|
|
73
|
+
ok: true;
|
|
74
|
+
}>;
|
|
75
|
+
};
|
|
76
|
+
declare function createCrudCommands<Context extends CrudContext>(config: CrudConfig<Context>): CrudCommands<Context>;
|
|
77
|
+
//#endregion
|
|
78
|
+
export { CrudCommands, CrudConfig, createCrudCommands };
|
|
79
|
+
//# sourceMappingURL=crud.d.mts.map
|
package/dist/crud.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{defaultErrors as e}from"./errors.mjs";import{zid as t}from"convex-helpers/server/zod4";import{z as n}from"zod";function r(r){let i=r.errors??e,a=r.table.tableName,o=r.classification??`business`;if(`tenant`in r.table.storage.shape&&!r.enrich)return i.throw({code:`CRUD_ENRICH_REQUIRED`,message:`Table "${a}" is a tenantTable: createCrudCommands requires an enrich callback that stamps ctx.tenant`});let s=t(a),c=n.object({id:s}).strict(),l=n.object({ok:n.literal(!0)}).strict(),u=n.object({id:s,data:r.table.commandInput.partial().strict()}).strict(),d=n.object({id:s}).strict(),f={[`${a}.create`]:{command:r.table.commandInput,result:c,classification:o,aggregates:[r.aggregateType],...r.middleware?{middleware:r.middleware}:{},...r.guards?.create?{guard:r.guards.create}:{},audit:(e,t)=>({operation:`${a}.create`,actorId:r.actor(t),aggregate:{type:r.aggregateType,id:e.result.id}})},[`${a}.update`]:{command:u,result:l,classification:o,aggregates:[r.aggregateType],...r.middleware?{middleware:r.middleware}:{},...r.guards?.update?{guard:r.guards.update}:{},audit:(e,t)=>({operation:`${a}.update`,actorId:r.actor(t),aggregate:{type:r.aggregateType,id:e.command.id}})},[`${a}.archive`]:{command:d,result:l,classification:o,aggregates:[r.aggregateType],...r.middleware?{middleware:r.middleware}:{},...r.guards?.archive?{guard:r.guards.archive}:{},audit:(e,t)=>({operation:`${a}.archive`,actorId:r.actor(t),aggregate:{type:r.aggregateType,id:e.command.id}})}},p=new r.Command(f);return{commands:p,operations:f,executeCreate:p.exec({operation:`${a}.create`,handler:(async(e,t)=>{let n=r.enrich?await r.enrich(e,t):{};return{id:await e.db.insert(a,{...t,...n})}})}),executeUpdate:p.exec({operation:`${a}.update`,handler:(async(e,t)=>(await e.db.patch(t.id,t.data),{ok:!0}))}),executeArchive:p.exec({operation:`${a}.archive`,handler:(async(e,t)=>(await e.db.patch(t.id,{archivedAt:Date.now()}),{ok:!0}))})}}export{r as createCrudCommands};
|
|
2
|
+
//# sourceMappingURL=crud.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crud.mjs","names":[],"sources":["../src/crud.ts"],"sourcesContent":["import { zid } from 'convex-helpers/server/zod4'\nimport { z } from 'zod'\nimport type {\n\tAnyCommandMiddleware,\n\tApplicationCommand,\n\tAuditedRegistry,\n\tCommandConstructor,\n} from './components/foundation/client'\nimport { defaultErrors, type ErrorFactory } from './errors'\n\n/**\n * The zodTable surface the factory consumes — structural, so any zodTable /\n * tenantTable return satisfies it.\n */\ntype CrudTable = {\n\ttableName: string\n\t// biome-ignore format: structural zod surface\n\tcommandInput: z.ZodObject<Record<string, z.ZodType>>\n\tstorage: z.ZodObject<Record<string, z.ZodType>>\n}\n\ntype CrudContext = {\n\tdb: {\n\t\tinsert: (table: never, value: never) => Promise<unknown>\n\t\tpatch: (id: never, value: never) => Promise<unknown>\n\t}\n}\n\ntype MaybePromise<Value> = Value | Promise<Value>\n\nexport type CrudConfig<Context extends CrudContext> = {\n\t/** The Foundation-bound Command class (destructured from the facade). */\n\tCommand: CommandConstructor\n\ttable: CrudTable\n\t/** Audit aggregate type; enforced by the aggregates allowlist. */\n\taggregateType: string\n\t/** Observability/audit classification. Default 'business'. */\n\tclassification?: string\n\t/** Derives the audited actor id from the ctx (usually ctx.actor.userId). */\n\tactor: (context: Context) => string\n\t/**\n\t * Stamps server-owned fields onto creates (tenant, denormalized ids...).\n\t * REQUIRED when the table is a tenantTable — forgetting it would insert\n\t * untenanted rows; the factory fails fast at construction instead.\n\t */\n\tenrich?: (\n\t\tcontext: Context,\n\t\tcommand: Record<string, unknown>,\n\t) => Record<string, unknown> | Promise<Record<string, unknown>>\n\t/** Per-operation preconditions; run before the handlers. */\n\tguards?: {\n\t\tcreate?: (context: Context, command: never) => MaybePromise<void>\n\t\tupdate?: (context: Context, command: never) => MaybePromise<void>\n\t\tarchive?: (context: Context, command: never) => MaybePromise<void>\n\t}\n\t/** Per-operation middleware applied to all three generated operations. */\n\tmiddleware?: readonly AnyCommandMiddleware[]\n\terrors?: ErrorFactory\n}\n\n/**\n * Generates the standard create/update/archive operations from one zodTable\n * declaration, fully inside the audited command pipeline: strict command\n * inputs from `commandFields`, mandatory audit with the declared aggregate\n * type, soft delete only (archive sets `archivedAt` — no hard-delete\n * operation is generated, per kit doctrine).\n *\n * convex-helpers ships a same-named `crud` helper; it was deliberately not\n * used — it bypasses the command pipeline (no audits, no guards, and hard\n * deletes). See docs/crud.md.\n */\nexport type CrudCommands<Context extends CrudContext> = {\n\tcommands: ApplicationCommand<Context, AuditedRegistry>\n\toperations: AuditedRegistry\n\texecuteCreate: (\n\t\tcontext: Context,\n\t\tcommand: Record<string, unknown>,\n\t) => Promise<{ id: string }>\n\texecuteUpdate: (\n\t\tcontext: Context,\n\t\tcommand: { id: string; data: Record<string, unknown> },\n\t) => Promise<{ ok: true }>\n\texecuteArchive: (\n\t\tcontext: Context,\n\t\tcommand: { id: string },\n\t) => Promise<{ ok: true }>\n}\n\nexport function createCrudCommands<Context extends CrudContext>(\n\tconfig: CrudConfig<Context>,\n): CrudCommands<Context> {\n\tconst errors = config.errors ?? defaultErrors\n\tconst name = config.table.tableName\n\tconst classification = config.classification ?? 'business'\n\tconst isTenantTable = 'tenant' in config.table.storage.shape\n\tif (isTenantTable && !config.enrich) {\n\t\treturn errors.throw({\n\t\t\tcode: 'CRUD_ENRICH_REQUIRED',\n\t\t\tmessage: `Table \"${name}\" is a tenantTable: createCrudCommands requires an enrich callback that stamps ctx.tenant`,\n\t\t})\n\t}\n\n\tconst id = zid(name)\n\tconst createResult = z.object({ id }).strict()\n\tconst okResult = z.object({ ok: z.literal(true) }).strict()\n\tconst updateInput = z\n\t\t.object({ id, data: config.table.commandInput.partial().strict() })\n\t\t.strict()\n\tconst archiveInput = z.object({ id }).strict()\n\n\tconst operations = {\n\t\t[`${name}.create`]: {\n\t\t\tcommand: config.table.commandInput,\n\t\t\tresult: createResult,\n\t\t\tclassification,\n\t\t\taggregates: [config.aggregateType],\n\t\t\t...(config.middleware ? { middleware: config.middleware } : {}),\n\t\t\t...(config.guards?.create ? { guard: config.guards.create } : {}),\n\t\t\taudit: (\n\t\t\t\tresolution: { command: unknown; result: { id: string } },\n\t\t\t\tcontext: Context,\n\t\t\t) => ({\n\t\t\t\toperation: `${name}.create`,\n\t\t\t\tactorId: config.actor(context),\n\t\t\t\taggregate: { type: config.aggregateType, id: resolution.result.id },\n\t\t\t}),\n\t\t},\n\t\t[`${name}.update`]: {\n\t\t\tcommand: updateInput,\n\t\t\tresult: okResult,\n\t\t\tclassification,\n\t\t\taggregates: [config.aggregateType],\n\t\t\t...(config.middleware ? { middleware: config.middleware } : {}),\n\t\t\t...(config.guards?.update ? { guard: config.guards.update } : {}),\n\t\t\taudit: (\n\t\t\t\tresolution: { command: { id: string }; result: unknown },\n\t\t\t\tcontext: Context,\n\t\t\t) => ({\n\t\t\t\toperation: `${name}.update`,\n\t\t\t\tactorId: config.actor(context),\n\t\t\t\taggregate: { type: config.aggregateType, id: resolution.command.id },\n\t\t\t}),\n\t\t},\n\t\t[`${name}.archive`]: {\n\t\t\tcommand: archiveInput,\n\t\t\tresult: okResult,\n\t\t\tclassification,\n\t\t\taggregates: [config.aggregateType],\n\t\t\t...(config.middleware ? { middleware: config.middleware } : {}),\n\t\t\t...(config.guards?.archive ? { guard: config.guards.archive } : {}),\n\t\t\taudit: (\n\t\t\t\tresolution: { command: { id: string }; result: unknown },\n\t\t\t\tcontext: Context,\n\t\t\t) => ({\n\t\t\t\toperation: `${name}.archive`,\n\t\t\t\tactorId: config.actor(context),\n\t\t\t\taggregate: { type: config.aggregateType, id: resolution.command.id },\n\t\t\t}),\n\t\t},\n\t} as never\n\n\tconst commands = new config.Command<Context, AuditedRegistry>(operations)\n\n\tconst executeCreate = commands.exec({\n\t\toperation: `${name}.create` as never,\n\t\thandler: (async (context: Context, command: Record<string, unknown>) => {\n\t\t\tconst enrichment = config.enrich\n\t\t\t\t? await config.enrich(context, command)\n\t\t\t\t: {}\n\t\t\tconst inserted = await context.db.insert(\n\t\t\t\tname as never,\n\t\t\t\t{ ...command, ...enrichment } as never,\n\t\t\t)\n\t\t\treturn { id: inserted }\n\t\t}) as never,\n\t})\n\n\tconst executeUpdate = commands.exec({\n\t\toperation: `${name}.update` as never,\n\t\thandler: (async (\n\t\t\tcontext: Context,\n\t\t\tcommand: { id: string; data: Record<string, unknown> },\n\t\t) => {\n\t\t\tawait context.db.patch(command.id as never, command.data as never)\n\t\t\treturn { ok: true }\n\t\t}) as never,\n\t})\n\n\tconst executeArchive = commands.exec({\n\t\toperation: `${name}.archive` as never,\n\t\thandler: (async (context: Context, command: { id: string }) => {\n\t\t\tawait context.db.patch(\n\t\t\t\tcommand.id as never,\n\t\t\t\t{ archivedAt: Date.now() } as never,\n\t\t\t)\n\t\t\treturn { ok: true }\n\t\t}) as never,\n\t})\n\n\treturn {\n\t\tcommands,\n\t\toperations: operations as AuditedRegistry,\n\t\texecuteCreate,\n\t\texecuteUpdate,\n\t\texecuteArchive,\n\t} as unknown as CrudCommands<Context>\n}\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import { AgentToolHandlers, AgentToolRecord, createAgentTools } from "./agent-tools.mjs";
|
|
1
2
|
import { ErrorFactory, KitError, defaultErrors } from "./errors.mjs";
|
|
2
3
|
import { RLSConfig, Rules, TENANT_FIELD, assertTenantOwned, composeRules, createTenantRules, requireTenantReference } from "./tenancy.mjs";
|
|
3
4
|
import { Actor, AuthBundle, AuthFunctionsConfig, DefaultRole, Include, IncludedQuery, SecurityConfig, createAuthFunctions, createInclude, defaultRoleMap } from "./auth.mjs";
|
|
4
|
-
import { QueryMiddleware } from "./components/foundation/modules/query/query.mjs";
|
|
5
|
-
import { ApplicationCommand, AuditEntryInput, AuditWriter, AuditedOperation, AuditedRegistry, CommandDefaults, CommandMiddleware, Foundation, FoundationOptions, PermissionChecker } from "./components/foundation/client.mjs";
|
|
6
|
-
import {
|
|
5
|
+
import { AnyQueryMiddleware, QueryMiddleware } from "./components/foundation/modules/query/query.mjs";
|
|
6
|
+
import { AnyCommandMiddleware, ApplicationCommand, AuditEntryInput, AuditWriter, AuditedOperation, AuditedRegistry, CommandConstructor, CommandDefaults, CommandMiddleware, Foundation, FoundationOptions, PermissionChecker } from "./components/foundation/client.mjs";
|
|
7
|
+
import { CrudConfig, createCrudCommands } from "./crud.mjs";
|
|
8
|
+
import { TIMESTAMP_FIELDS, TableBoundaryOptions, createModule, jsonSafeZid, paginated, tenantTable, zodTable, zodVariantTable } from "./zod-table.mjs";
|
|
9
|
+
import { RateLimiterLike, rateLimit } from "./middleware.mjs";
|
|
10
|
+
import { WebhookBoundaryConfig, createWebhookBoundary, recordWebhookEvent, webhookEventsTable } from "./webhooks.mjs";
|
|
11
|
+
import { createStateMachine } from "./state-machine.mjs";
|
|
7
12
|
import { Change, Trigger, Triggers, appendOnly, createTriggers, noDelete, tenantOwnership, timestamps } from "./triggers.mjs";
|
|
8
|
-
export { type Actor, type ApplicationCommand, type AuditEntryInput, type AuditWriter, type AuditedOperation, type AuditedRegistry, type AuthBundle, type AuthFunctionsConfig, type Change, type CommandDefaults, type CommandMiddleware, type DefaultRole, type ErrorFactory, Foundation, type FoundationOptions, type Include, type IncludedQuery, KitError, type PermissionChecker, type QueryMiddleware, type RLSConfig, type Rules, type SecurityConfig, TENANT_FIELD, TIMESTAMP_FIELDS, type TableBoundaryOptions, type Trigger, Triggers, appendOnly, assertTenantOwned, composeRules, createAuthFunctions, createInclude, createModule, createTenantRules, createTriggers, defaultErrors, defaultRoleMap, jsonSafeZid, noDelete, requireTenantReference, tenantOwnership, tenantTable, timestamps, zodTable, zodVariantTable };
|
|
13
|
+
export { type Actor, type AgentToolHandlers, type AgentToolRecord, type AnyCommandMiddleware, type AnyQueryMiddleware, type ApplicationCommand, type AuditEntryInput, type AuditWriter, type AuditedOperation, type AuditedRegistry, type AuthBundle, type AuthFunctionsConfig, type Change, type CommandConstructor, type CommandDefaults, type CommandMiddleware, type CrudConfig, type DefaultRole, type ErrorFactory, Foundation, type FoundationOptions, type Include, type IncludedQuery, KitError, type PermissionChecker, type QueryMiddleware, type RLSConfig, type RateLimiterLike, type Rules, type SecurityConfig, TENANT_FIELD, TIMESTAMP_FIELDS, type TableBoundaryOptions, type Trigger, Triggers, type WebhookBoundaryConfig, appendOnly, assertTenantOwned, composeRules, createAgentTools, createAuthFunctions, createCrudCommands, createInclude, createModule, createStateMachine, createTenantRules, createTriggers, createWebhookBoundary, defaultErrors, defaultRoleMap, jsonSafeZid, noDelete, paginated, rateLimit, recordWebhookEvent, requireTenantReference, tenantOwnership, tenantTable, timestamps, webhookEventsTable, zodTable, zodVariantTable };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{KitError as
|
|
1
|
+
import{createAgentTools as e}from"./agent-tools.mjs";import{KitError as t,defaultErrors as n}from"./errors.mjs";import{TENANT_FIELD as r,assertTenantOwned as i,composeRules as a,createTenantRules as o,requireTenantReference as s}from"./tenancy.mjs";import{createAuthFunctions as c,createInclude as l,defaultRoleMap as u}from"./auth.mjs";import{createCrudCommands as d}from"./crud.mjs";import{TIMESTAMP_FIELDS as f,createModule as p,jsonSafeZid as m,paginated as h,tenantTable as g,zodTable as _,zodVariantTable as v}from"./zod-table.mjs";import{rateLimit as y}from"./middleware.mjs";import{createWebhookBoundary as b,recordWebhookEvent as x,webhookEventsTable as S}from"./webhooks.mjs";import{createStateMachine as C}from"./state-machine.mjs";import{Triggers as w,appendOnly as T,createTriggers as E,noDelete as D,tenantOwnership as O,timestamps as k}from"./triggers.mjs";import{Foundation as A}from"./components/foundation/client.mjs";export{A as Foundation,t as KitError,r as TENANT_FIELD,f as TIMESTAMP_FIELDS,w as Triggers,T as appendOnly,i as assertTenantOwned,a as composeRules,e as createAgentTools,c as createAuthFunctions,d as createCrudCommands,l as createInclude,p as createModule,C as createStateMachine,o as createTenantRules,E as createTriggers,b as createWebhookBoundary,n as defaultErrors,u as defaultRoleMap,m as jsonSafeZid,D as noDelete,h as paginated,y as rateLimit,x as recordWebhookEvent,s as requireTenantReference,O as tenantOwnership,g as tenantTable,k as timestamps,S as webhookEventsTable,_ as zodTable,v as zodVariantTable};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ErrorFactory } from "./errors.mjs";
|
|
2
|
+
//#region src/middleware.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Structural surface of @convex-dev/rate-limiter's RateLimiter (checked
|
|
5
|
+
* against 0.3.x; re-verify at install). The host mounts the component and
|
|
6
|
+
* constructs the limiter; the kit only ever receives the instance —
|
|
7
|
+
* injection keeps the kit dependency-free.
|
|
8
|
+
*/
|
|
9
|
+
type RateLimiterLike = {
|
|
10
|
+
limit: (ctx: never, name: string, options?: {
|
|
11
|
+
key?: string;
|
|
12
|
+
}) => Promise<{
|
|
13
|
+
ok: boolean;
|
|
14
|
+
retryAfter?: number;
|
|
15
|
+
}>;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* The first packaged middleware: rate limiting on the Command/Query
|
|
19
|
+
* middleware seams. Runs before guards and the handler; a rejected call
|
|
20
|
+
* throws RATE_LIMITED (audit and handler never run). Keyed by ctx.tenant by
|
|
21
|
+
* default — when no key fn is given and ctx.tenant is undefined, this is a
|
|
22
|
+
* configuration error, not a silent shared bucket.
|
|
23
|
+
*
|
|
24
|
+
* The returned middleware is shape-compatible with both AnyCommandMiddleware
|
|
25
|
+
* and AnyQueryMiddleware.
|
|
26
|
+
*/
|
|
27
|
+
declare function rateLimit(config: {
|
|
28
|
+
limiter: RateLimiterLike;
|
|
29
|
+
name: string;
|
|
30
|
+
/** Defaults to ctx.tenant. */
|
|
31
|
+
key?: (context: never) => string;
|
|
32
|
+
/** Observe a rejection before the throw (metrics, headers). */
|
|
33
|
+
onLimit?: (status: {
|
|
34
|
+
ok: boolean;
|
|
35
|
+
retryAfter?: number;
|
|
36
|
+
}, context: never) => void | Promise<void>;
|
|
37
|
+
errors?: ErrorFactory;
|
|
38
|
+
}): (input: {
|
|
39
|
+
context: never;
|
|
40
|
+
next: (options?: {
|
|
41
|
+
context?: unknown;
|
|
42
|
+
}) => Promise<unknown>;
|
|
43
|
+
}) => Promise<unknown>;
|
|
44
|
+
//#endregion
|
|
45
|
+
export { RateLimiterLike, rateLimit };
|
|
46
|
+
//# sourceMappingURL=middleware.d.mts.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{defaultErrors as e}from"./errors.mjs";function t(t){let n=t.errors??e;return async e=>{let r=t.key?t.key(e.context):e.context.tenant;if(r===void 0)return n.throw({code:`RATE_LIMIT_KEY_MISSING`,message:`rateLimit("${t.name}") has no key: ctx.tenant is undefined and no key fn was configured`});let i=await t.limiter.limit(e.context,t.name,{key:r});return i.ok?e.next():(await t.onLimit?.(i,e.context),n.throw({code:`RATE_LIMITED`,message:`Rate limit "${t.name}" exceeded${i.retryAfter===void 0?``:`; retry after ${Math.ceil(i.retryAfter)}ms`}`}))}}export{t as rateLimit};
|
|
2
|
+
//# sourceMappingURL=middleware.mjs.map
|