cvx-kit 0.1.7 → 0.1.8
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/README.md +59 -43
- package/dist/components/foundation/client.d.mts +8 -0
- package/dist/components/foundation/client.mjs.map +1 -1
- package/dist/modules/effect/api-runtime.d.mts +14 -4
- package/dist/modules/effect/api-runtime.mjs +1 -1
- package/dist/modules/effect/api-runtime.mjs.map +1 -1
- package/dist/modules/effect/api-zod.d.mts +5 -5
- package/dist/modules/effect/api-zod.mjs.map +1 -1
- package/dist/modules/effect/api.d.mts +6 -6
- package/dist/modules/effect/api.mjs.map +1 -1
- package/dist/modules/effect/command.d.mts +6 -2
- package/dist/modules/effect/command.mjs +1 -1
- package/dist/modules/effect/command.mjs.map +1 -1
- package/dist/modules/effect/query.d.mts +5 -1
- package/dist/modules/effect/query.mjs +1 -1
- package/dist/modules/effect/query.mjs.map +1 -1
- package/docs/architecture.md +3 -3
- package/docs/auth.md +10 -4
- package/docs/command-context.md +150 -0
- package/docs/commands-legacy.md +511 -0
- package/docs/commands.md +113 -451
- package/docs/conventions.md +15 -5
- package/docs/crud.md +4 -0
- package/docs/domain-operations.md +1 -1
- package/docs/effect.md +14 -3
- package/docs/llms.md +69 -45
- package/docs/migration.md +1 -1
- package/docs/upgrading.md +9 -0
- package/package.json +1 -1
- package/scripts/test-packed-effect.mjs +3 -0
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ authored in [`src/docs/`](./src/docs/):
|
|
|
35
35
|
- [`conventions.md`](./src/docs/conventions.md) — the doctrine: folder structure, file anatomy, naming grammar, import rules
|
|
36
36
|
- [`zod-table.md`](./src/docs/zod-table.md) — one zod shape per entity, masked into every boundary
|
|
37
37
|
- [`auth.md`](./src/docs/auth.md) — auth-aware function constructors from injected policy
|
|
38
|
-
- [`commands.md`](./src/docs/commands.md) —
|
|
38
|
+
- [`commands.md`](./src/docs/commands.md) — Effect command declarations, lifecycle, and migration
|
|
39
39
|
- [`effect.md`](./src/docs/effect.md) — opt-in Effect v4 registries, injected services, and shared API builders
|
|
40
40
|
- [`domain-operations.md`](./src/docs/domain-operations.md) — server domain/API examples, contracts, errors, receipts, workflow steps, and selected tools
|
|
41
41
|
- [`triggers.md`](./src/docs/triggers.md) — trigger registry, timestamps/append-only/no-delete
|
|
@@ -128,57 +128,67 @@ registry structurally; `include()` bounds every read (default 100 rows).
|
|
|
128
128
|
`resolveOrganization` derives org and role from your own tables, enabling
|
|
129
129
|
identity providers whose tokens carry no org claims.
|
|
130
130
|
|
|
131
|
-
### `cvx-kit/
|
|
131
|
+
### `cvx-kit/effect` — commands and queries
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
Use the Effect API for new commands. Install Effect `>=4.0.1 <5`, bind host
|
|
134
|
+
policies once with `createEffectFoundation`, and declare each handler inside
|
|
135
|
+
`command({...})` beside its schemas and audit policy.
|
|
136
|
+
|
|
137
|
+
<!-- packed-effect-example -->
|
|
136
138
|
|
|
137
139
|
```ts
|
|
138
|
-
|
|
139
|
-
import
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
},
|
|
140
|
+
import { createEffectFoundation } from 'cvx-kit/effect'
|
|
141
|
+
import type { AuditEntryInput } from 'cvx-kit/components/foundation'
|
|
142
|
+
import { z } from 'zod'
|
|
143
|
+
|
|
144
|
+
type Host = {
|
|
145
|
+
actorId: string
|
|
146
|
+
rename: (id: string, title: string) => Promise<void>
|
|
147
|
+
appendAudit: (entry: AuditEntryInput) => Promise<void>
|
|
148
|
+
}
|
|
149
|
+
const { Command } = createEffectFoundation({
|
|
150
|
+
observability: {
|
|
151
|
+
enabled: false,
|
|
152
|
+
classifyError: () => ({ outcome: 'failed', errorCode: 'RENAME_FAILED' }),
|
|
152
153
|
},
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
154
|
+
writeAudit: (host: Host, entry) => host.appendAudit(entry),
|
|
155
|
+
})
|
|
156
|
+
const renameInput = z.object({ id: z.string(), title: z.string() }).strict()
|
|
157
|
+
export const commands = Command({
|
|
158
|
+
context: (host: Host) => host,
|
|
159
|
+
operations: ({ command }) => ({
|
|
160
|
+
'documents.rename': command({
|
|
161
|
+
input: renameInput,
|
|
162
|
+
result: z.object({ ok: z.literal(true) }).strict(),
|
|
163
|
+
classification: 'business',
|
|
164
|
+
handler: async (input, context) => {
|
|
165
|
+
await context.rename(input.id, input.title)
|
|
166
|
+
return { ok: true as const }
|
|
167
|
+
},
|
|
168
|
+
audit: ({ command }, context) => ({
|
|
169
|
+
operation: 'documents.rename',
|
|
170
|
+
actorId: context.actorId,
|
|
171
|
+
aggregate: { type: 'document', id: command.id },
|
|
172
|
+
}),
|
|
173
|
+
}),
|
|
164
174
|
}),
|
|
165
175
|
})
|
|
166
|
-
export const executeRename =
|
|
176
|
+
export const executeRename = (input: z.input<typeof renameInput>, host: Host) =>
|
|
177
|
+
commands.exec('documents.rename', input, host)
|
|
167
178
|
```
|
|
168
179
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
180
|
+
`exec` returns a lazy Effect even for ordinary async handlers. Shared
|
|
181
|
+
`effectApiBuilder` / `effectZodApiBuilder` constructors provide invocation-local
|
|
182
|
+
services and execute it; domain code can compose `Effect.gen` and `Effect.fn`
|
|
183
|
+
without starting another runner. Commands validate inputs and results, enforce
|
|
184
|
+
permissions and guards, and write audit entries in the mutation transaction.
|
|
185
|
+
See [commands](./src/docs/commands.md), [complete Effect wiring](./src/docs/effect.md),
|
|
186
|
+
and [authenticated builders](./src/docs/auth.md).
|
|
175
187
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
the Effect. Existing Foundation declarations and Promise executors remain
|
|
181
|
-
supported, so domains can migrate independently.
|
|
188
|
+
The old `Foundation.Command` Promise facade is **deprecated** and retained for
|
|
189
|
+
existing consumers. Its separate executor handler options are covered only in
|
|
190
|
+
the [compatibility reference](./src/docs/commands-legacy.md). The Foundation
|
|
191
|
+
component and its transaction result helpers remain available.
|
|
182
192
|
|
|
183
193
|
### `cvx-kit/components/approvals` (component)
|
|
184
194
|
|
|
@@ -297,3 +307,9 @@ One-time setup (already-published package required first):
|
|
|
297
307
|
## Roadmap
|
|
298
308
|
|
|
299
309
|
See [ROADMAP.md](./ROADMAP.md) — milestones from 0.1.0 through 1.0.0.
|
|
310
|
+
|
|
311
|
+
Optional request context can be added through the existing Effect API adapter's
|
|
312
|
+
`context` callback. Bind selected registries with `commands.withContext(value)`
|
|
313
|
+
and consume `ctx.commands.exec(name, input)` through the application's existing
|
|
314
|
+
function helper names. Keep dependencies local to each feature; explicit execution
|
|
315
|
+
and input-only command handlers remain supported. See [optional context injection](./src/docs/command-context.md).
|
|
@@ -136,18 +136,25 @@ declare class BoundCommand<Context, const Operations extends AuditedRegistry> {
|
|
|
136
136
|
checkPermission?: PermissionChecker;
|
|
137
137
|
defaults?: CommandDefaults;
|
|
138
138
|
});
|
|
139
|
+
/**
|
|
140
|
+
* @deprecated Declare handlers inside command({...}) using createEffectFoundation
|
|
141
|
+
* from cvx-kit/effect, then call exec(operation, input, host).
|
|
142
|
+
*/
|
|
139
143
|
exec<const Key extends OperationKey<Operations>>(executor: {
|
|
140
144
|
operation: Key;
|
|
141
145
|
handler: (context: Context & OperationExtension<Operations[Key]>, command: CommandInput<Operations, Key>) => MaybePromise<CommandHandlerResult<Operations, Key>>;
|
|
142
146
|
}): (context: Context, command: SchemaInput<Operations[Key]["command"]>) => Promise<SchemaOutput<Operations[Key]["result"]>>;
|
|
143
147
|
}
|
|
148
|
+
/** @deprecated Use the registry returned by createEffectFoundation(...).Command from cvx-kit/effect. */
|
|
144
149
|
type ApplicationCommand<Context, Operations extends AuditedRegistry> = BoundCommand<Context, Operations>;
|
|
145
150
|
/**
|
|
146
151
|
* The constructor shape a Foundation instance exposes as `Command`.
|
|
147
152
|
* Exported as a TYPE ONLY so factories (e.g. cvx-kit/crud) can accept the
|
|
148
153
|
* destructured facade class as input without importing any runtime kernel.
|
|
154
|
+
* @deprecated Use createEffectFoundation from cvx-kit/effect and declare handlers inside command({...}).
|
|
149
155
|
*/
|
|
150
156
|
type CommandConstructor = {
|
|
157
|
+
/** @deprecated Use createEffectFoundation(...).Command({ context, operations }) from cvx-kit/effect. */
|
|
151
158
|
new <Context, const Operations extends AuditedRegistry>(operations: Operations & { readonly [Key in keyof Operations]: OperationHostContext<Context>; }, defaults?: CommandDefaults): BoundCommand<Context, Operations>;
|
|
152
159
|
operation: (typeof BoundCommand)['operation'];
|
|
153
160
|
withContext: (typeof BoundCommand)['withContext'];
|
|
@@ -160,6 +167,7 @@ type CommandConstructor = {
|
|
|
160
167
|
*/
|
|
161
168
|
declare class Foundation<Component extends FoundationComponentApi = FoundationComponentApi> {
|
|
162
169
|
readonly status: Component['functions']['status'];
|
|
170
|
+
/** @deprecated Use createEffectFoundation(...).Command from cvx-kit/effect. */
|
|
163
171
|
readonly Command: CommandConstructor;
|
|
164
172
|
readonly Query: typeof Query;
|
|
165
173
|
readonly observability: Observability;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.mjs","names":["#kernel","#observability","#writeAudit","#checkPermission","#defaults","CommandKernelClass","#execute"],"sources":["../../../src/components/foundation/client.ts"],"sourcesContent":["import type { Value } from 'convex/values'\nimport {\n\tCommand as CommandKernelClass,\n\ttype CommandExecution,\n\ttype CommandHandlerResult,\n\ttype CommandInput,\n\ttype CommandRegistry,\n\ttype CommandResult,\n\ttype CommandSchema,\n\tdecodeCommandSchema,\n\tparseCommandSchema,\n} from './modules/command/command'\nimport { Observability, type ObservabilityOptions } from './modules/observability/observability'\nimport { Query } from './modules/query/query'\nimport {\n\texecuteCommandLifecycle,\n\tpromiseLifecycle,\n\tCommandPermissionError,\n\ttype PromiseExecution,\n} from './modules/command/lifecycle'\nimport { executeResultBoundary, projectResult } from './result'\nimport { emitSemanticEvent } from './telemetry'\nimport {\n\toperationFactory,\n\ttype CommandPreparation,\n\ttype OperationExtension,\n\ttype OperationHostContext,\n} from './operation'\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, Value>\n\tclassification: string\n}\n\nexport type AuditWriter<Result = unknown> = (\n\tcontext: never,\n\tentry: AuditEntryInput,\n) => MaybePromise<Result>\n\ntype MaybePromise<Value> = Value | Promise<Value>\ntype OperationKey<Operations extends CommandRegistry> = Extract<keyof Operations, string>\n\nexport type AuditedOperation = Readonly<{\n\tcommand: CommandSchema\n\tresult: CommandSchema\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\tprepare?: (context: never, command: never) => MaybePromise<CommandPreparation<never>>\n\treplayResult?: CommandSchema\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 object = object,\n\tInput = unknown,\n\tResult = unknown,\n> = (input: {\n\toperation: string\n\tcommand: Input\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<Result>\n}) => Promise<Result>\n\n/** Any-context middleware — what registries and operations accept. */\n// Contravariant seam: the supertype every typed CommandMiddleware<C, E>\n// assigns to. Context, command, and next are contravariant storage slots;\n// dispatch restores the selected operation's types before invocation.\nexport type AnyCommandMiddleware<Input = never, Result = unknown, NextResult = never> = (input: {\n\toperation: string\n\tdefinition?: AuditedOperation\n\tcommand: Input\n\tcontext: never\n\tnext: (options?: { context?: object }) => Promise<NextResult>\n}) => Promise<Result>\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\tstatic withContext = operationFactory\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>(definition: Definition): 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 object = object,\n\t\tInput = unknown,\n\t\tResult = unknown,\n\t>(\n\t\tmiddleware: CommandMiddleware<Context, Extension, Input, Result>,\n\t): CommandMiddleware<Context, Extension, Input, Result> {\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\t// SAFETY: this map preserves every operations key and selects its aggregates property.\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 & OperationExtension<Operations[Key]>,\n\t\t\tcommand: CommandInput<Operations, Key>,\n\t\t) => MaybePromise<CommandHandlerResult<Operations, Key>>\n\t}) {\n\t\treturn this.#kernel.exec({\n\t\t\toperation: executor.operation,\n\t\t\thandler: (context, command) =>\n\t\t\t\texecutor.handler(\n\t\t\t\t\t// SAFETY: operationFactory requires enrichment middleware before this handler.\n\t\t\t\t\tcontext as Context & OperationExtension<Operations[Key]>,\n\t\t\t\t\tcommand,\n\t\t\t\t),\n\t\t})\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\tconst definition = execution.definition\n\t\treturn this.#observability.observe(\n\t\t\t{ operation: execution.operation, classification: definition.classification },\n\t\t\t() =>\n\t\t\t\texecuteCommandLifecycle<\n\t\t\t\t\tPromiseExecution,\n\t\t\t\t\tContext,\n\t\t\t\t\tCommandInput<Operations, Key>,\n\t\t\t\t\tCommandHandlerResult<Operations, Key>,\n\t\t\t\t\tCommandResult<Operations, Key>\n\t\t\t\t>(promiseLifecycle, {\n\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\tclassification: definition.classification,\n\t\t\t\t\tcontext: execution.context,\n\t\t\t\t\tcommand: execution.command,\n\t\t\t\t\tpermission:\n\t\t\t\t\t\tdefinition.permission === undefined\n\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t: async () => {\n\t\t\t\t\t\t\t\t\tif (!this.#checkPermission) throw new CommandPermissionError(execution.operation)\n\t\t\t\t\t\t\t\t\t// SAFETY: injected policy consumes this invocation's original host context.\n\t\t\t\t\t\t\t\t\tawait this.#checkPermission(execution.context as never, {\n\t\t\t\t\t\t\t\t\t\tpermission: definition.permission!,\n\t\t\t\t\t\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\tprepare: async () => {\n\t\t\t\t\t\t// SAFETY: selected definition and parsed input are paired by the kernel.\n\t\t\t\t\t\tconst preparation = await definition.prepare?.(\n\t\t\t\t\t\t\texecution.context as never,\n\t\t\t\t\t\t\texecution.command as never,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (preparation?.kind !== 'execute') return preparation\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tkind: 'execute' as const,\n\t\t\t\t\t\t\tcomplete: preparation.complete\n\t\t\t\t\t\t\t\t? async (result: CommandResult<Operations, Key>) => {\n\t\t\t\t\t\t\t\t\t\t// SAFETY: completion belongs to this operation's validated final result.\n\t\t\t\t\t\t\t\t\t\tawait preparation.complete!(result as never)\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tparseReplay: (value) => {\n\t\t\t\t\t\tif (!definition.replayResult) return execution.parseResult(value)\n\t\t\t\t\t\t// SAFETY: selected replay schema validates this operation's stored output.\n\t\t\t\t\t\treturn parseCommandSchema(definition.replayResult, value) as CommandResult<\n\t\t\t\t\t\t\tOperations,\n\t\t\t\t\t\t\tKey\n\t\t\t\t\t\t>\n\t\t\t\t\t},\n\t\t\t\t\tdecodeReplay: async (value) => {\n\t\t\t\t\t\tif (!definition.replayResult) {\n\t\t\t\t\t\t\treturn execution.decodeResult\n\t\t\t\t\t\t\t\t? execution.decodeResult(value)\n\t\t\t\t\t\t\t\t: execution.parseResult(value)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst replay = definition.replayResult\n\t\t\t\t\t\t// SAFETY: selected replay decoder owns the final output representation.\n\t\t\t\t\t\treturn (await decodeCommandSchema(replay, value)) as CommandResult<Operations, Key>\n\t\t\t\t\t},\n\t\t\t\t\tmiddleware: [...(this.#defaults.middleware ?? []), ...(definition.middleware ?? [])].map(\n\t\t\t\t\t\t(layer) => async (input) => {\n\t\t\t\t\t\t\t// SAFETY: selected definition owns this heterogeneous middleware's pre-validation output.\n\t\t\t\t\t\t\treturn layer({\n\t\t\t\t\t\t\t\t...input,\n\t\t\t\t\t\t\t\tdefinition,\n\t\t\t\t\t\t\t\t// SAFETY: selected command schema owns this heterogeneous storage value.\n\t\t\t\t\t\t\t\tcommand: input.command as never,\n\t\t\t\t\t\t\t\t// SAFETY: only heterogeneous storage erases the selected middleware context.\n\t\t\t\t\t\t\t\tcontext: input.context as never,\n\t\t\t\t\t\t\t\t// SAFETY: selected operation pairs downstream output with its final schema.\n\t\t\t\t\t\t\t\tnext: input.next as (options?: { context?: object }) => Promise<never>,\n\t\t\t\t\t\t\t}) as Promise<CommandHandlerResult<Operations, Key>>\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t\t\tdefaultGuard: async (context) => {\n\t\t\t\t\t\t// SAFETY: default guards consume the downstream middleware context.\n\t\t\t\t\t\tawait this.#defaults.guard?.(context as never)\n\t\t\t\t\t},\n\t\t\t\t\tguard: async (context) => {\n\t\t\t\t\t\t// SAFETY: selected guard consumes its parsed input and enriched context.\n\t\t\t\t\t\tawait definition.guard?.(context as never, execution.command as never)\n\t\t\t\t\t},\n\t\t\t\t\trun: (context) => execution.runUnparsed(context),\n\t\t\t\t\tparseResult: execution.parseResult,\n\t\t\t\t\tdecodeResult: execution.decodeResult,\n\t\t\t\t\taudit: async (result) => {\n\t\t\t\t\t\t// SAFETY: audit receives this selected parsed command/result and original context.\n\t\t\t\t\t\treturn definition.audit(\n\t\t\t\t\t\t\t{ command: execution.command, result } as never,\n\t\t\t\t\t\t\texecution.context as never,\n\t\t\t\t\t\t)\n\t\t\t\t\t},\n\t\t\t\t\taggregates: definition.aggregates,\n\t\t\t\t\twriteAudit: async (entry) => {\n\t\t\t\t\t\t// SAFETY: host supplied writer for this invocation's original context.\n\t\t\t\t\t\treturn this.#writeAudit(execution.context as never, entry)\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t)\n\t}\n}\n\nexport type ApplicationCommand<Context, Operations extends AuditedRegistry> = BoundCommand<\n\tContext,\n\tOperations\n>\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\t\treadonly [Key in keyof Operations]: OperationHostContext<Context>\n\t\t},\n\t\tdefaults?: CommandDefaults,\n\t): BoundCommand<Context, Operations>\n\toperation: (typeof BoundCommand)['operation']\n\twithContext: (typeof BoundCommand)['withContext']\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<Component extends FoundationComponentApi = FoundationComponentApi> {\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\tclass HostCommand<Context, const Operations extends AuditedRegistry> extends BoundCommand<\n\t\t\tContext,\n\t\t\tOperations\n\t\t> {\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\tthis.Command = HostCommand\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 { CommandPreparation, TypedOperation } from './operation'\nexport type {\n\tCommandArgument,\n\tCommandExecution,\n\tCommandHandlerResult,\n\tCommandInput,\n\tCommandRegistry,\n\tCommandResult,\n\tSchemaOutput,\n\tParseable,\n} from './modules/command/command'\nexport type {\n\tCommandObservation,\n\tObservabilityOptions,\n} from './modules/observability/observability'\nexport type { AnyQueryMiddleware, QueryExecution, QueryMiddleware } from './modules/query/query'\nexport type { Result, ResultBoundary, TransactionMetricsContext } 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 type { Value } from 'convex/values'\nimport {\n\tCommand as CommandKernelClass,\n\ttype CommandExecution,\n\ttype CommandHandlerResult,\n\ttype CommandInput,\n\ttype CommandRegistry,\n\ttype CommandResult,\n\ttype CommandSchema,\n\tdecodeCommandSchema,\n\tparseCommandSchema,\n} from './modules/command/command'\nimport { Observability, type ObservabilityOptions } from './modules/observability/observability'\nimport { Query } from './modules/query/query'\nimport {\n\texecuteCommandLifecycle,\n\tpromiseLifecycle,\n\tCommandPermissionError,\n\ttype PromiseExecution,\n} from './modules/command/lifecycle'\nimport { executeResultBoundary, projectResult } from './result'\nimport { emitSemanticEvent } from './telemetry'\nimport {\n\toperationFactory,\n\ttype CommandPreparation,\n\ttype OperationExtension,\n\ttype OperationHostContext,\n} from './operation'\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, Value>\n\tclassification: string\n}\n\nexport type AuditWriter<Result = unknown> = (\n\tcontext: never,\n\tentry: AuditEntryInput,\n) => MaybePromise<Result>\n\ntype MaybePromise<Value> = Value | Promise<Value>\ntype OperationKey<Operations extends CommandRegistry> = Extract<keyof Operations, string>\n\nexport type AuditedOperation = Readonly<{\n\tcommand: CommandSchema\n\tresult: CommandSchema\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\tprepare?: (context: never, command: never) => MaybePromise<CommandPreparation<never>>\n\treplayResult?: CommandSchema\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 object = object,\n\tInput = unknown,\n\tResult = unknown,\n> = (input: {\n\toperation: string\n\tcommand: Input\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<Result>\n}) => Promise<Result>\n\n/** Any-context middleware — what registries and operations accept. */\n// Contravariant seam: the supertype every typed CommandMiddleware<C, E>\n// assigns to. Context, command, and next are contravariant storage slots;\n// dispatch restores the selected operation's types before invocation.\nexport type AnyCommandMiddleware<Input = never, Result = unknown, NextResult = never> = (input: {\n\toperation: string\n\tdefinition?: AuditedOperation\n\tcommand: Input\n\tcontext: never\n\tnext: (options?: { context?: object }) => Promise<NextResult>\n}) => Promise<Result>\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\tstatic withContext = operationFactory\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>(definition: Definition): 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 object = object,\n\t\tInput = unknown,\n\t\tResult = unknown,\n\t>(\n\t\tmiddleware: CommandMiddleware<Context, Extension, Input, Result>,\n\t): CommandMiddleware<Context, Extension, Input, Result> {\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\t// SAFETY: this map preserves every operations key and selects its aggregates property.\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\t/**\n\t * @deprecated Declare handlers inside command({...}) using createEffectFoundation\n\t * from cvx-kit/effect, then call exec(operation, input, host).\n\t */\n\texec<const Key extends OperationKey<Operations>>(executor: {\n\t\toperation: Key\n\t\thandler: (\n\t\t\tcontext: Context & OperationExtension<Operations[Key]>,\n\t\t\tcommand: CommandInput<Operations, Key>,\n\t\t) => MaybePromise<CommandHandlerResult<Operations, Key>>\n\t}) {\n\t\treturn this.#kernel.exec({\n\t\t\toperation: executor.operation,\n\t\t\thandler: (context, command) =>\n\t\t\t\texecutor.handler(\n\t\t\t\t\t// SAFETY: operationFactory requires enrichment middleware before this handler.\n\t\t\t\t\tcontext as Context & OperationExtension<Operations[Key]>,\n\t\t\t\t\tcommand,\n\t\t\t\t),\n\t\t})\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\tconst definition = execution.definition\n\t\treturn this.#observability.observe(\n\t\t\t{ operation: execution.operation, classification: definition.classification },\n\t\t\t() =>\n\t\t\t\texecuteCommandLifecycle<\n\t\t\t\t\tPromiseExecution,\n\t\t\t\t\tContext,\n\t\t\t\t\tCommandInput<Operations, Key>,\n\t\t\t\t\tCommandHandlerResult<Operations, Key>,\n\t\t\t\t\tCommandResult<Operations, Key>\n\t\t\t\t>(promiseLifecycle, {\n\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\tclassification: definition.classification,\n\t\t\t\t\tcontext: execution.context,\n\t\t\t\t\tcommand: execution.command,\n\t\t\t\t\tpermission:\n\t\t\t\t\t\tdefinition.permission === undefined\n\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t: async () => {\n\t\t\t\t\t\t\t\t\tif (!this.#checkPermission) throw new CommandPermissionError(execution.operation)\n\t\t\t\t\t\t\t\t\t// SAFETY: injected policy consumes this invocation's original host context.\n\t\t\t\t\t\t\t\t\tawait this.#checkPermission(execution.context as never, {\n\t\t\t\t\t\t\t\t\t\tpermission: definition.permission!,\n\t\t\t\t\t\t\t\t\t\toperation: execution.operation,\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\tprepare: async () => {\n\t\t\t\t\t\t// SAFETY: selected definition and parsed input are paired by the kernel.\n\t\t\t\t\t\tconst preparation = await definition.prepare?.(\n\t\t\t\t\t\t\texecution.context as never,\n\t\t\t\t\t\t\texecution.command as never,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (preparation?.kind !== 'execute') return preparation\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tkind: 'execute' as const,\n\t\t\t\t\t\t\tcomplete: preparation.complete\n\t\t\t\t\t\t\t\t? async (result: CommandResult<Operations, Key>) => {\n\t\t\t\t\t\t\t\t\t\t// SAFETY: completion belongs to this operation's validated final result.\n\t\t\t\t\t\t\t\t\t\tawait preparation.complete!(result as never)\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tparseReplay: (value) => {\n\t\t\t\t\t\tif (!definition.replayResult) return execution.parseResult(value)\n\t\t\t\t\t\t// SAFETY: selected replay schema validates this operation's stored output.\n\t\t\t\t\t\treturn parseCommandSchema(definition.replayResult, value) as CommandResult<\n\t\t\t\t\t\t\tOperations,\n\t\t\t\t\t\t\tKey\n\t\t\t\t\t\t>\n\t\t\t\t\t},\n\t\t\t\t\tdecodeReplay: async (value) => {\n\t\t\t\t\t\tif (!definition.replayResult) {\n\t\t\t\t\t\t\treturn execution.decodeResult\n\t\t\t\t\t\t\t\t? execution.decodeResult(value)\n\t\t\t\t\t\t\t\t: execution.parseResult(value)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst replay = definition.replayResult\n\t\t\t\t\t\t// SAFETY: selected replay decoder owns the final output representation.\n\t\t\t\t\t\treturn (await decodeCommandSchema(replay, value)) as CommandResult<Operations, Key>\n\t\t\t\t\t},\n\t\t\t\t\tmiddleware: [...(this.#defaults.middleware ?? []), ...(definition.middleware ?? [])].map(\n\t\t\t\t\t\t(layer) => async (input) => {\n\t\t\t\t\t\t\t// SAFETY: selected definition owns this heterogeneous middleware's pre-validation output.\n\t\t\t\t\t\t\treturn layer({\n\t\t\t\t\t\t\t\t...input,\n\t\t\t\t\t\t\t\tdefinition,\n\t\t\t\t\t\t\t\t// SAFETY: selected command schema owns this heterogeneous storage value.\n\t\t\t\t\t\t\t\tcommand: input.command as never,\n\t\t\t\t\t\t\t\t// SAFETY: only heterogeneous storage erases the selected middleware context.\n\t\t\t\t\t\t\t\tcontext: input.context as never,\n\t\t\t\t\t\t\t\t// SAFETY: selected operation pairs downstream output with its final schema.\n\t\t\t\t\t\t\t\tnext: input.next as (options?: { context?: object }) => Promise<never>,\n\t\t\t\t\t\t\t}) as Promise<CommandHandlerResult<Operations, Key>>\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t\t\tdefaultGuard: async (context) => {\n\t\t\t\t\t\t// SAFETY: default guards consume the downstream middleware context.\n\t\t\t\t\t\tawait this.#defaults.guard?.(context as never)\n\t\t\t\t\t},\n\t\t\t\t\tguard: async (context) => {\n\t\t\t\t\t\t// SAFETY: selected guard consumes its parsed input and enriched context.\n\t\t\t\t\t\tawait definition.guard?.(context as never, execution.command as never)\n\t\t\t\t\t},\n\t\t\t\t\trun: (context) => execution.runUnparsed(context),\n\t\t\t\t\tparseResult: execution.parseResult,\n\t\t\t\t\tdecodeResult: execution.decodeResult,\n\t\t\t\t\taudit: async (result) => {\n\t\t\t\t\t\t// SAFETY: audit receives this selected parsed command/result and original context.\n\t\t\t\t\t\treturn definition.audit(\n\t\t\t\t\t\t\t{ command: execution.command, result } as never,\n\t\t\t\t\t\t\texecution.context as never,\n\t\t\t\t\t\t)\n\t\t\t\t\t},\n\t\t\t\t\taggregates: definition.aggregates,\n\t\t\t\t\twriteAudit: async (entry) => {\n\t\t\t\t\t\t// SAFETY: host supplied writer for this invocation's original context.\n\t\t\t\t\t\treturn this.#writeAudit(execution.context as never, entry)\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t)\n\t}\n}\n\n/** @deprecated Use the registry returned by createEffectFoundation(...).Command from cvx-kit/effect. */\nexport type ApplicationCommand<Context, Operations extends AuditedRegistry> = BoundCommand<\n\tContext,\n\tOperations\n>\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 * @deprecated Use createEffectFoundation from cvx-kit/effect and declare handlers inside command({...}).\n */\nexport type CommandConstructor = {\n\t/** @deprecated Use createEffectFoundation(...).Command({ context, operations }) from cvx-kit/effect. */\n\tnew <Context, const Operations extends AuditedRegistry>(\n\t\toperations: Operations & {\n\t\t\treadonly [Key in keyof Operations]: OperationHostContext<Context>\n\t\t},\n\t\tdefaults?: CommandDefaults,\n\t): BoundCommand<Context, Operations>\n\toperation: (typeof BoundCommand)['operation']\n\twithContext: (typeof BoundCommand)['withContext']\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<Component extends FoundationComponentApi = FoundationComponentApi> {\n\treadonly status: Component['functions']['status']\n\t/** @deprecated Use createEffectFoundation(...).Command from cvx-kit/effect. */\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\tclass HostCommand<Context, const Operations extends AuditedRegistry> extends BoundCommand<\n\t\t\tContext,\n\t\t\tOperations\n\t\t> {\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\tthis.Command = HostCommand\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 { CommandPreparation, TypedOperation } from './operation'\nexport type {\n\tCommandArgument,\n\tCommandExecution,\n\tCommandHandlerResult,\n\tCommandInput,\n\tCommandRegistry,\n\tCommandResult,\n\tSchemaOutput,\n\tParseable,\n} from './modules/command/command'\nexport type {\n\tCommandObservation,\n\tObservabilityOptions,\n} from './modules/observability/observability'\nexport type { AnyQueryMiddleware, QueryExecution, QueryMiddleware } from './modules/query/query'\nexport type { Result, ResultBoundary, TransactionMetricsContext } 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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"}
|
|
@@ -7,9 +7,18 @@ type EffectApiErrorContract = Pick<ErrorContract<ErrorDefinitions>, 'project'>;
|
|
|
7
7
|
type EffectApiHandlerValue<Value> = Value | PromiseLike<Value> | Effect.Effect<Value, unknown, unknown>;
|
|
8
8
|
type EffectApiServices = Context.Context<never> | PromiseLike<Context.Context<never>> | Effect.Effect<Context.Context<never>, unknown, Scope.Scope>;
|
|
9
9
|
type ProvidedServices<Provider> = EffectValue<Provider> extends Context.Context<infer Services> ? Services : never;
|
|
10
|
-
type
|
|
10
|
+
type EffectApiContext = EffectApiHandlerValue<Record<string, unknown>>;
|
|
11
|
+
type KeysOfUnion<Value> = Value extends object ? keyof Value : never;
|
|
12
|
+
type CheckedContext<Ctx, Added> = Extract<KeysOfUnion<EffectValue<Added>>, keyof Ctx> extends never ? unknown : never;
|
|
13
|
+
type EffectApiOptions<Ctx, Provider extends EffectApiServices, Added = {}> = ([ProvidedServices<Provider>] extends [never] ? {
|
|
14
|
+
services?: (ctx: Ctx) => Provider;
|
|
15
|
+
} : {
|
|
11
16
|
services: (ctx: Ctx) => Provider;
|
|
12
|
-
} & ({
|
|
17
|
+
}) & ([KeysOfUnion<EffectValue<Added>>] extends [never] ? {
|
|
18
|
+
context?: (ctx: Ctx) => Added & CheckedContext<Ctx, Added>;
|
|
19
|
+
} : {
|
|
20
|
+
context: (ctx: Ctx) => Added & CheckedContext<Ctx, Added>;
|
|
21
|
+
}) & ({
|
|
13
22
|
errors: EffectApiErrorContract;
|
|
14
23
|
mapError?: never;
|
|
15
24
|
} | {
|
|
@@ -19,10 +28,11 @@ type EffectApiOptions<Ctx, Provider extends EffectApiServices> = {
|
|
|
19
28
|
type CheckedApiRequirements<Returned, Provider> = [Exclude<CallbackRequirements<NoInfer<Returned>>, ProvidedServices<Provider> | Scope.Scope>] extends [never] ? unknown : never;
|
|
20
29
|
/** Internal erasure is limited to the registration boundary; public adapters check A/E/R. */
|
|
21
30
|
declare function wrapEffectBuilder(builder: Function, options: {
|
|
22
|
-
services
|
|
31
|
+
services?: Function;
|
|
32
|
+
context?: Function;
|
|
23
33
|
mapError?: EffectApiErrorProjection;
|
|
24
34
|
errors?: EffectApiErrorContract;
|
|
25
35
|
}): Function;
|
|
26
36
|
//#endregion
|
|
27
|
-
export { CheckedApiRequirements, EffectApiErrorContract, EffectApiErrorProjection, EffectApiHandlerValue, EffectApiOptions, EffectApiServices, ProvidedServices, wrapEffectBuilder };
|
|
37
|
+
export { CheckedApiRequirements, CheckedContext, EffectApiContext, EffectApiErrorContract, EffectApiErrorProjection, EffectApiHandlerValue, EffectApiOptions, EffectApiServices, ProvidedServices, wrapEffectBuilder };
|
|
28
38
|
//# sourceMappingURL=api-runtime.d.mts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{normalizeEffect as e}from"./normalize.mjs";import{projectErrorCause as t}from"./errors.mjs";import{ConvexError as n}from"convex/values";import{Cause as r,
|
|
1
|
+
import{normalizeEffect as e}from"./normalize.mjs";import{projectErrorCause as t}from"./errors.mjs";import{ConvexError as n}from"convex/values";import{Cause as r,Context as i,Effect as a,Exit as o}from"effect";function s(s,c){return l=>{let u=typeof l==`function`?l:l.handler,d=async(s,...l)=>{let d=e(()=>c.services?c.services(s):i.empty()).pipe(a.flatMap(t=>e(()=>c.context?c.context(s):{}).pipe(a.flatMap(e=>a.sync(()=>{if(typeof e!=`object`||!e)throw Error(`Effect API context must be an object`);let t=Object.getPrototypeOf(e);if(t!==Object.prototype&&t!==null)throw Error(`Effect API context additions must be a plain record`);let n=s;for(let t of Reflect.ownKeys(e))if(t in n)throw Error(`Effect API context cannot replace ${String(t)}`);return c.context?Object.create(Object.getPrototypeOf(n),{...Object.getOwnPropertyDescriptors(n),...Object.getOwnPropertyDescriptors(e)}):s})),a.flatMap(t=>e(()=>u(t,...l))),a.provideContext(t)))),f=await a.runPromiseExit(a.scoped(d));if(o.isSuccess(f))return f.value;if(c.errors)throw new n(t(c.errors,f.cause));let p=f.cause.reasons.length===1?f.cause.reasons[0]:void 0;throw p&&r.isFailReason(p)?c.mapError?c.mapError(p.error):p.error:p&&r.isDieReason(p)?p.defect:Error(`Effect API execution failed`,{cause:f.cause})};return s(typeof l==`function`?d:{...l,handler:d})}}export{s as wrapEffectBuilder};
|
|
2
2
|
//# sourceMappingURL=api-runtime.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-runtime.mjs","names":[],"sources":["../../../src/modules/effect/api-runtime.ts"],"sourcesContent":["import { Cause, Effect, Exit } from 'effect'\nimport type {
|
|
1
|
+
{"version":3,"file":"api-runtime.mjs","names":[],"sources":["../../../src/modules/effect/api-runtime.ts"],"sourcesContent":["import { Cause, Context, Effect, Exit } from 'effect'\nimport type { Scope } from 'effect'\nimport { ConvexError, type Value } from 'convex/values'\nimport type { ErrorContract, ErrorDefinitions } from '../contracts/errors'\nimport { projectErrorCause } from './errors'\nimport { normalizeEffect } from './normalize'\nimport type { CallbackRequirements, EffectValue } from './operation'\n\n// oxlint-disable-next-line anti-slop/no-unknown-parameters, anti-slop/no-unknown-returns -- Projection receives any expected failure and its result is always thrown, never returned as public data.\nexport type EffectApiErrorProjection = (error: unknown) => unknown\nexport type EffectApiErrorContract = Pick<ErrorContract<ErrorDefinitions>, 'project'>\n\nexport type EffectApiHandlerValue<Value> =\n\t| Value\n\t| PromiseLike<Value>\n\t| Effect.Effect<Value, unknown, unknown>\n\nexport type EffectApiServices =\n\t| Context.Context<never>\n\t| PromiseLike<Context.Context<never>>\n\t| Effect.Effect<Context.Context<never>, unknown, Scope.Scope>\nexport type ProvidedServices<Provider> =\n\tEffectValue<Provider> extends Context.Context<infer Services> ? Services : never\n// oxlint-disable-next-line anti-slop/no-unsafe-dictionary-type -- This inference constraint requires record-shaped additions; each adapter preserves its concrete inferred fields and runtime checks the record boundary.\nexport type EffectApiContext = EffectApiHandlerValue<Record<string, unknown>>\ntype KeysOfUnion<Value> = Value extends object ? keyof Value : never\nexport type CheckedContext<Ctx, Added> =\n\tExtract<KeysOfUnion<EffectValue<Added>>, keyof Ctx> extends never ? unknown : never\nexport type EffectApiOptions<Ctx, Provider extends EffectApiServices, Added = {}> = (\n\t[ProvidedServices<Provider>] extends [never]\n\t\t? { services?: (ctx: Ctx) => Provider }\n\t\t: { services: (ctx: Ctx) => Provider }\n) & (\n\t[KeysOfUnion<EffectValue<Added>>] extends [never]\n\t\t? { context?: (ctx: Ctx) => Added & CheckedContext<Ctx, Added> }\n\t\t: { context: (ctx: Ctx) => Added & CheckedContext<Ctx, Added> }\n) & (\n\t| { errors: EffectApiErrorContract; mapError?: never }\n\t| { errors?: never; mapError?: EffectApiErrorProjection }\n)\nexport type CheckedApiRequirements<Returned, Provider> = [\n\tExclude<CallbackRequirements<NoInfer<Returned>>, ProvidedServices<Provider> | Scope.Scope>,\n] extends [never]\n\t? unknown\n\t: never\n\n/** Internal erasure is limited to the registration boundary; public adapters check A/E/R. */\nexport function wrapEffectBuilder(\n\tbuilder: Function,\n\toptions: {\n\t\tservices?: Function\n\t\tcontext?: Function\n\t\tmapError?: EffectApiErrorProjection\n\t\terrors?: EffectApiErrorContract\n\t},\n): Function {\n\treturn (definition: Function | { handler: Function }) => {\n\t\t// oxlint-disable-next-line anti-slop/no-runtime-typeof -- Native registration explicitly accepts a callable or an object with a handler.\n\t\tconst original = typeof definition === 'function' ? definition : definition.handler\n\t\t// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Private erased callback receives the context already constructed by the checked native/custom builder.\n\t\tconst handler = async (ctx: unknown, ...args: unknown[]) => {\n\t\t\tconst program = normalizeEffect(() =>\n\t\t\t\toptions.services ? options.services(ctx) : Context.empty(),\n\t\t\t).pipe(\n\t\t\t\tEffect.flatMap((services) =>\n\t\t\t\t\tnormalizeEffect(() => (options.context ? options.context(ctx) : {})).pipe(\n\t\t\t\t\t\tEffect.flatMap((added) =>\n\t\t\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\t\t\t// oxlint-disable-next-line anti-slop/no-runtime-typeof -- Validate erased context additions before enumerating or merging their fields.\n\t\t\t\t\t\t\t\tif (added === null || typeof added !== 'object')\n\t\t\t\t\t\t\t\t\tthrow Error('Effect API context must be an object')\n\t\t\t\t\t\t\t\tconst additionsPrototype = Object.getPrototypeOf(added)\n\t\t\t\t\t\t\t\tif (additionsPrototype !== Object.prototype && additionsPrototype !== null)\n\t\t\t\t\t\t\t\t\tthrow Error('Effect API context additions must be a plain record')\n\t\t\t\t\t\t\t\t// SAFETY: native and custom builders construct an object context before invoking this private handler.\n\t\t\t\t\t\t\t\tconst originalContext = ctx as object\n\t\t\t\t\t\t\t\tfor (const key of Reflect.ownKeys(added)) {\n\t\t\t\t\t\t\t\t\tif (key in originalContext)\n\t\t\t\t\t\t\t\t\t\tthrow Error(`Effect API context cannot replace ${String(key)}`)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn options.context\n\t\t\t\t\t\t\t\t\t? Object.create(Object.getPrototypeOf(originalContext), {\n\t\t\t\t\t\t\t\t\t\t\t...Object.getOwnPropertyDescriptors(originalContext),\n\t\t\t\t\t\t\t\t\t\t\t...Object.getOwnPropertyDescriptors(added),\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t: ctx\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tEffect.flatMap((context) => normalizeEffect(() => original(context, ...args))),\n\t\t\t\t\t\tEffect.provideContext(services),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t)\n\t\t\t// SAFETY: checked public signatures prohibit unprovided handler requirements and\n\t\t\t// permit only Scope in providers; services are constructed within this same scope.\n\t\t\tconst exit = await Effect.runPromiseExit(\n\t\t\t\tEffect.scoped(program) as Effect.Effect<unknown, unknown>,\n\t\t\t)\n\t\t\tif (Exit.isSuccess(exit)) return exit.value\n\t\t\tif (options.errors) {\n\t\t\t\t// SAFETY: declared projection copies validated JSON-safe fields; all other Causes have a fixed safe envelope.\n\t\t\t\tthrow new ConvexError(projectErrorCause(options.errors, exit.cause) as Value)\n\t\t\t}\n\t\t\tconst reason = exit.cause.reasons.length === 1 ? exit.cause.reasons[0] : undefined\n\t\t\tif (reason && Cause.isFailReason(reason)) {\n\t\t\t\tthrow options.mapError ? options.mapError(reason.error) : reason.error\n\t\t\t}\n\t\t\tif (reason && Cause.isDieReason(reason)) throw reason.defect\n\t\t\tthrow new Error('Effect API execution failed', { cause: exit.cause })\n\t\t}\n\t\t// oxlint-disable-next-line anti-slop/no-runtime-typeof -- Preserve the original native callable or object registration form.\n\t\treturn builder(typeof definition === 'function' ? handler : { ...definition, handler })\n\t}\n}\n"],"mappings":"iNA+CA,SAAgB,EACf,EACA,EAMW,CACX,MAAQ,IAAiD,CAExD,IAAM,EAAW,OAAO,GAAe,WAAa,EAAa,EAAW,QAEtE,EAAU,MAAO,EAAc,GAAG,IAAoB,CAC3D,IAAM,EAAU,MACf,EAAQ,SAAW,EAAQ,SAAS,CAAG,EAAI,EAAQ,MAAM,CAC1D,CAAC,CAAC,KACD,EAAO,QAAS,GACf,MAAuB,EAAQ,QAAU,EAAQ,QAAQ,CAAG,EAAI,CAAC,CAAE,CAAC,CAAC,KACpE,EAAO,QAAS,GACf,EAAO,SAAW,CAEjB,GAAsB,OAAO,GAAU,WAAnC,EACH,MAAM,MAAM,sCAAsC,EACnD,IAAM,EAAqB,OAAO,eAAe,CAAK,EACtD,GAAI,IAAuB,OAAO,WAAa,IAAuB,KACrE,MAAM,MAAM,qDAAqD,EAElE,IAAM,EAAkB,EACxB,IAAK,IAAM,KAAO,QAAQ,QAAQ,CAAK,EACtC,GAAI,KAAO,EACV,MAAM,MAAM,qCAAqC,OAAO,CAAG,GAAG,EAEhE,OAAO,EAAQ,QACZ,OAAO,OAAO,OAAO,eAAe,CAAe,EAAG,CACtD,GAAG,OAAO,0BAA0B,CAAe,EACnD,GAAG,OAAO,0BAA0B,CAAK,CAC1C,CAAC,EACA,CACJ,CAAC,CACF,EACA,EAAO,QAAS,GAAY,MAAsB,EAAS,EAAS,GAAG,CAAI,CAAC,CAAC,EAC7E,EAAO,eAAe,CAAQ,CAC/B,CACD,CACD,EAGM,EAAO,MAAM,EAAO,eACzB,EAAO,OAAO,CAAO,CACtB,EACA,GAAI,EAAK,UAAU,CAAI,EAAG,OAAO,EAAK,MACtC,GAAI,EAAQ,OAEX,MAAM,IAAI,EAAY,EAAkB,EAAQ,OAAQ,EAAK,KAAK,CAAU,EAE7E,IAAM,EAAS,EAAK,MAAM,QAAQ,SAAW,EAAI,EAAK,MAAM,QAAQ,GAAK,IAAA,GAKzE,MAJI,GAAU,EAAM,aAAa,CAAM,EAChC,EAAQ,SAAW,EAAQ,SAAS,EAAO,KAAK,EAAI,EAAO,MAE9D,GAAU,EAAM,YAAY,CAAM,EAAS,EAAO,OAC5C,MAAM,8BAA+B,CAAE,MAAO,EAAK,KAAM,CAAC,CACrE,EAEA,OAAO,EAAQ,OAAO,GAAe,WAAa,EAAU,CAAE,GAAG,EAAY,SAAQ,CAAC,CACvF,CACD"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CallbackRequirements, EffectValue } from "./operation.mjs";
|
|
2
|
-
import { EffectApiHandlerValue, EffectApiOptions, EffectApiServices, ProvidedServices } from "./api-runtime.mjs";
|
|
2
|
+
import { EffectApiContext, EffectApiHandlerValue, EffectApiOptions, EffectApiServices, ProvidedServices } from "./api-runtime.mjs";
|
|
3
3
|
import { CustomBuilder } from "convex-helpers/server/zod4";
|
|
4
4
|
import { ArgsArrayToObject, DefaultFunctionArgs, FunctionVisibility } from "convex/server";
|
|
5
5
|
import { ObjectType, PropertyValidators } from "convex/values";
|
|
@@ -20,16 +20,16 @@ type ResultInput<Validator extends ResultValidator> = [Validator] extends [zCore
|
|
|
20
20
|
type ResultOutput<Validator extends ResultValidator, Returned> = [Validator] extends [void] ? EffectValue<Returned> : [Validator] extends [zCore.$ZodType] ? zCore.output<Validator> : [Validator] extends [Fields] ? zCore.output<zCore.$ZodObject<Validator, zCore.$strict>> : never;
|
|
21
21
|
type CheckedRequirements<Returned, Services> = [Exclude<CallbackRequirements<NoInfer<Returned>>, Services | Scope.Scope>] extends [never] ? unknown : never;
|
|
22
22
|
/** Retains the installed Zod custom builder's raw/parsed boundaries and customization options. */
|
|
23
|
-
type EffectZodApiBuilder<Kind extends 'query' | 'mutation' | 'action', CustomArgs extends PropertyValidators, CustomCtx extends Record<string, unknown>, CustomMadeArgs extends Record<string, unknown>, InputCtx, Visibility extends FunctionVisibility, ExtraArgs extends Record<string, unknown>, Services> = {
|
|
23
|
+
type EffectZodApiBuilder<Kind extends 'query' | 'mutation' | 'action', CustomArgs extends PropertyValidators, CustomCtx extends Record<string, unknown>, CustomMadeArgs extends Record<string, unknown>, InputCtx, Visibility extends FunctionVisibility, ExtraArgs extends Record<string, unknown>, Services, Added = {}, Initialized = {}> = {
|
|
24
24
|
<Args extends ArgumentsValidator, Returns extends ResultValidator = void, Returned extends EffectApiHandlerValue<ResultInput<Returns> | (null extends ResultInput<Returns> ? undefined | void : never)> = EffectApiHandlerValue<ResultInput<Returns> | (null extends ResultInput<Returns> ? undefined | void : never)>>(definition: (({
|
|
25
25
|
args?: Args;
|
|
26
26
|
returns?: Returns;
|
|
27
27
|
skipConvexValidation?: boolean;
|
|
28
|
-
handler: (ctx: Overwrite<InputCtx, CustomCtx
|
|
29
|
-
} & Omit<ExtraArgs, 'args' | 'returns' | 'handler' | 'skipConvexValidation'>) | ((ctx: Overwrite<InputCtx, CustomCtx
|
|
28
|
+
handler: (ctx: Overwrite<InputCtx, CustomCtx> & Added, ...args: HandlerArgs<Args, CustomMadeArgs>) => Returned;
|
|
29
|
+
} & Omit<ExtraArgs, 'args' | 'returns' | 'handler' | 'skipConvexValidation'>) | ((ctx: Overwrite<InputCtx, CustomCtx> & Added, ...args: HandlerArgs<Args, CustomMadeArgs>) => Returned)) & CheckedRequirements<Returned, Services> & CheckedRequirements<Initialized, Services>): Registration<Kind, Visibility, RawArgs<Args, CustomArgs>, ResultOutput<Returns, Returned>>;
|
|
30
30
|
};
|
|
31
31
|
/** Build request services after the original custom builder resolves its trusted context. */
|
|
32
|
-
declare function effectZodApiBuilder<Kind extends 'query' | 'mutation' | 'action', CustomArgs extends PropertyValidators, CustomCtx extends Record<string, unknown>, CustomMadeArgs extends Record<string, unknown>, InputCtx, Visibility extends FunctionVisibility, ExtraArgs extends Record<string, unknown>, Provider extends EffectApiServices>(builder: CustomBuilder<Kind, CustomArgs, CustomCtx, CustomMadeArgs, InputCtx, Visibility, ExtraArgs>, options: EffectApiOptions<Overwrite<InputCtx, CustomCtx>, Provider>): EffectZodApiBuilder<Kind, CustomArgs, CustomCtx, CustomMadeArgs, InputCtx, Visibility, ExtraArgs, ProvidedServices<Provider
|
|
32
|
+
declare function effectZodApiBuilder<Kind extends 'query' | 'mutation' | 'action', CustomArgs extends PropertyValidators, CustomCtx extends Record<string, unknown>, CustomMadeArgs extends Record<string, unknown>, InputCtx, Visibility extends FunctionVisibility, ExtraArgs extends Record<string, unknown>, Provider extends EffectApiServices = import('effect').Context.Context<never>, Added extends EffectApiContext = {}>(builder: CustomBuilder<Kind, CustomArgs, CustomCtx, CustomMadeArgs, InputCtx, Visibility, ExtraArgs>, options: EffectApiOptions<Overwrite<InputCtx, CustomCtx>, Provider, Added>): EffectZodApiBuilder<Kind, CustomArgs, CustomCtx, CustomMadeArgs, InputCtx, Visibility, ExtraArgs, ProvidedServices<Provider>, EffectValue<Added>, Added>;
|
|
33
33
|
//#endregion
|
|
34
34
|
export { EffectZodApiBuilder, effectZodApiBuilder };
|
|
35
35
|
//# sourceMappingURL=api-zod.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-zod.mjs","names":[],"sources":["../../../src/modules/effect/api-zod.ts"],"sourcesContent":["import type { ArgsArrayToObject, DefaultFunctionArgs, FunctionVisibility } from 'convex/server'\nimport type { ObjectType, PropertyValidators } from 'convex/values'\nimport type { Registration } from 'convex-helpers/server/customFunctions'\nimport type { CustomBuilder } from 'convex-helpers/server/zod4'\nimport type { Scope } from 'effect'\nimport type * as zCore from 'zod/v4/core'\nimport {\n\twrapEffectBuilder,\n\ttype EffectApiOptions,\n\ttype ProvidedServices,\n\ttype EffectApiServices,\n\ttype EffectApiHandlerValue,\n} from './api-runtime'\nimport type { CallbackRequirements, EffectValue } from './operation'\n\ntype Fields = Record<string, zCore.$ZodType>\ntype ArgumentsValidator = Fields | zCore.$ZodObject<Fields> | void\ntype ResultValidator = zCore.$ZodType | Fields | void\ntype Expand<Value> = { [Key in keyof Value]: Value[Key] }\ntype Overwrite<Input, Custom> = Omit<Input, keyof Custom> & Custom\ntype ArgsInput<Validator extends ArgumentsValidator> = [Validator] extends [\n\tzCore.$ZodObject<Fields>,\n]\n\t? [zCore.input<Validator>]\n\t: Validator extends Record<string, never>\n\t\t? [Record<never, never>]\n\t\t: [Validator] extends [Fields]\n\t\t\t? [zCore.input<zCore.$ZodObject<Validator, zCore.$strict>>]\n\t\t\t: [DefaultFunctionArgs]\ntype ArgsOutput<Validator extends ArgumentsValidator> = [Validator] extends [\n\tzCore.$ZodObject<Fields>,\n]\n\t? [zCore.output<Validator>]\n\t: [Validator] extends [Fields]\n\t\t? [zCore.output<zCore.$ZodObject<Validator, zCore.$strict>>]\n\t\t: [DefaultFunctionArgs]\ntype HandlerArgs<Validator extends ArgumentsValidator, Made> =\n\tMade extends Record<string, never>\n\t\t? ArgsOutput<Validator>\n\t\t: [Expand<ArgsOutput<Validator>[0] & Made>]\ntype RawArgs<\n\tValidator extends ArgumentsValidator,\n\tCustom extends PropertyValidators,\n> = ArgsArrayToObject<\n\tCustom extends Record<string, never>\n\t\t? ArgsInput<Validator>\n\t\t: [Expand<ArgsInput<Validator>[0] & ObjectType<Custom>>]\n>\ntype ResultInput<Validator extends ResultValidator> = [Validator] extends [zCore.$ZodType]\n\t? zCore.input<Validator>\n\t: [Validator] extends [Fields]\n\t\t? zCore.input<zCore.$ZodObject<Validator>>\n\t\t: unknown\ntype ResultOutput<Validator extends ResultValidator, Returned> = [Validator] extends [void]\n\t? EffectValue<Returned>\n\t: [Validator] extends [zCore.$ZodType]\n\t\t? zCore.output<Validator>\n\t\t: [Validator] extends [Fields]\n\t\t\t? zCore.output<zCore.$ZodObject<Validator, zCore.$strict>>\n\t\t\t: never\ntype CheckedRequirements<Returned, Services> = [\n\tExclude<CallbackRequirements<NoInfer<Returned>>, Services | Scope.Scope>,\n] extends [never]\n\t? unknown\n\t: never\n\n/** Retains the installed Zod custom builder's raw/parsed boundaries and customization options. */\nexport type EffectZodApiBuilder<\n\tKind extends 'query' | 'mutation' | 'action',\n\tCustomArgs extends PropertyValidators,\n\tCustomCtx extends Record<string, unknown>,\n\tCustomMadeArgs extends Record<string, unknown>,\n\tInputCtx,\n\tVisibility extends FunctionVisibility,\n\tExtraArgs extends Record<string, unknown>,\n\tServices,\n> = {\n\t<\n\t\tArgs extends ArgumentsValidator,\n\t\tReturns extends ResultValidator = void,\n\t\tReturned extends EffectApiHandlerValue<\n\t\t\tResultInput<Returns> | (null extends ResultInput<Returns> ? undefined | void : never)\n\t\t> = EffectApiHandlerValue<\n\t\t\tResultInput<Returns> | (null extends ResultInput<Returns> ? undefined | void : never)\n\t\t>,\n\t>(\n\t\tdefinition: (\n\t\t\t| ({\n\t\t\t\t\targs?: Args\n\t\t\t\t\treturns?: Returns\n\t\t\t\t\tskipConvexValidation?: boolean\n\t\t\t\t\thandler: (\n\t\t\t\t\t\tctx: Overwrite<InputCtx, CustomCtx
|
|
1
|
+
{"version":3,"file":"api-zod.mjs","names":[],"sources":["../../../src/modules/effect/api-zod.ts"],"sourcesContent":["import type { ArgsArrayToObject, DefaultFunctionArgs, FunctionVisibility } from 'convex/server'\nimport type { ObjectType, PropertyValidators } from 'convex/values'\nimport type { Registration } from 'convex-helpers/server/customFunctions'\nimport type { CustomBuilder } from 'convex-helpers/server/zod4'\nimport type { Scope } from 'effect'\nimport type * as zCore from 'zod/v4/core'\nimport {\n\twrapEffectBuilder,\n\ttype EffectApiOptions,\n\ttype ProvidedServices,\n\ttype EffectApiServices,\n\ttype EffectApiHandlerValue,\n\ttype EffectApiContext,\n} from './api-runtime'\nimport type { CallbackRequirements, EffectValue } from './operation'\n\ntype Fields = Record<string, zCore.$ZodType>\ntype ArgumentsValidator = Fields | zCore.$ZodObject<Fields> | void\ntype ResultValidator = zCore.$ZodType | Fields | void\ntype Expand<Value> = { [Key in keyof Value]: Value[Key] }\ntype Overwrite<Input, Custom> = Omit<Input, keyof Custom> & Custom\ntype ArgsInput<Validator extends ArgumentsValidator> = [Validator] extends [\n\tzCore.$ZodObject<Fields>,\n]\n\t? [zCore.input<Validator>]\n\t: Validator extends Record<string, never>\n\t\t? [Record<never, never>]\n\t\t: [Validator] extends [Fields]\n\t\t\t? [zCore.input<zCore.$ZodObject<Validator, zCore.$strict>>]\n\t\t\t: [DefaultFunctionArgs]\ntype ArgsOutput<Validator extends ArgumentsValidator> = [Validator] extends [\n\tzCore.$ZodObject<Fields>,\n]\n\t? [zCore.output<Validator>]\n\t: [Validator] extends [Fields]\n\t\t? [zCore.output<zCore.$ZodObject<Validator, zCore.$strict>>]\n\t\t: [DefaultFunctionArgs]\ntype HandlerArgs<Validator extends ArgumentsValidator, Made> =\n\tMade extends Record<string, never>\n\t\t? ArgsOutput<Validator>\n\t\t: [Expand<ArgsOutput<Validator>[0] & Made>]\ntype RawArgs<\n\tValidator extends ArgumentsValidator,\n\tCustom extends PropertyValidators,\n> = ArgsArrayToObject<\n\tCustom extends Record<string, never>\n\t\t? ArgsInput<Validator>\n\t\t: [Expand<ArgsInput<Validator>[0] & ObjectType<Custom>>]\n>\ntype ResultInput<Validator extends ResultValidator> = [Validator] extends [zCore.$ZodType]\n\t? zCore.input<Validator>\n\t: [Validator] extends [Fields]\n\t\t? zCore.input<zCore.$ZodObject<Validator>>\n\t\t: unknown\ntype ResultOutput<Validator extends ResultValidator, Returned> = [Validator] extends [void]\n\t? EffectValue<Returned>\n\t: [Validator] extends [zCore.$ZodType]\n\t\t? zCore.output<Validator>\n\t\t: [Validator] extends [Fields]\n\t\t\t? zCore.output<zCore.$ZodObject<Validator, zCore.$strict>>\n\t\t\t: never\ntype CheckedRequirements<Returned, Services> = [\n\tExclude<CallbackRequirements<NoInfer<Returned>>, Services | Scope.Scope>,\n] extends [never]\n\t? unknown\n\t: never\n\n/** Retains the installed Zod custom builder's raw/parsed boundaries and customization options. */\nexport type EffectZodApiBuilder<\n\tKind extends 'query' | 'mutation' | 'action',\n\tCustomArgs extends PropertyValidators,\n\tCustomCtx extends Record<string, unknown>,\n\tCustomMadeArgs extends Record<string, unknown>,\n\tInputCtx,\n\tVisibility extends FunctionVisibility,\n\tExtraArgs extends Record<string, unknown>,\n\tServices,\n\tAdded = {},\n\tInitialized = {},\n> = {\n\t<\n\t\tArgs extends ArgumentsValidator,\n\t\tReturns extends ResultValidator = void,\n\t\tReturned extends EffectApiHandlerValue<\n\t\t\tResultInput<Returns> | (null extends ResultInput<Returns> ? undefined | void : never)\n\t\t> = EffectApiHandlerValue<\n\t\t\tResultInput<Returns> | (null extends ResultInput<Returns> ? undefined | void : never)\n\t\t>,\n\t>(\n\t\tdefinition: (\n\t\t\t| ({\n\t\t\t\t\targs?: Args\n\t\t\t\t\treturns?: Returns\n\t\t\t\t\tskipConvexValidation?: boolean\n\t\t\t\t\thandler: (\n\t\t\t\t\t\tctx: Overwrite<InputCtx, CustomCtx> & Added,\n\t\t\t\t\t\t...args: HandlerArgs<Args, CustomMadeArgs>\n\t\t\t\t\t) => Returned\n\t\t\t } & Omit<ExtraArgs, 'args' | 'returns' | 'handler' | 'skipConvexValidation'>)\n\t\t\t| ((\n\t\t\t\t\tctx: Overwrite<InputCtx, CustomCtx> & Added,\n\t\t\t\t\t...args: HandlerArgs<Args, CustomMadeArgs>\n\t\t\t ) => Returned)\n\t\t) &\n\t\t\tCheckedRequirements<Returned, Services> &\n\t\t\tCheckedRequirements<Initialized, Services>,\n\t): Registration<Kind, Visibility, RawArgs<Args, CustomArgs>, ResultOutput<Returns, Returned>>\n}\n\n/** Build request services after the original custom builder resolves its trusted context. */\nexport function effectZodApiBuilder<\n\tKind extends 'query' | 'mutation' | 'action',\n\tCustomArgs extends PropertyValidators,\n\tCustomCtx extends Record<string, unknown>,\n\tCustomMadeArgs extends Record<string, unknown>,\n\tInputCtx,\n\tVisibility extends FunctionVisibility,\n\tExtraArgs extends Record<string, unknown>,\n\tProvider extends EffectApiServices = import('effect').Context.Context<never>,\n\tAdded extends EffectApiContext = {},\n>(\n\tbuilder: CustomBuilder<\n\t\tKind,\n\t\tCustomArgs,\n\t\tCustomCtx,\n\t\tCustomMadeArgs,\n\t\tInputCtx,\n\t\tVisibility,\n\t\tExtraArgs\n\t>,\n\toptions: EffectApiOptions<Overwrite<InputCtx, CustomCtx>, Provider, Added>,\n): EffectZodApiBuilder<\n\tKind,\n\tCustomArgs,\n\tCustomCtx,\n\tCustomMadeArgs,\n\tInputCtx,\n\tVisibility,\n\tExtraArgs,\n\tProvidedServices<Provider>,\n\tEffectValue<Added>,\n\tAdded\n> {\n\t// SAFETY: Runtime substitution changes only the handler. The original builder retains validation,\n\t// customization, registration kind and visibility; the public call signature above checks Effects.\n\treturn wrapEffectBuilder(builder, options) as EffectZodApiBuilder<\n\t\tKind,\n\t\tCustomArgs,\n\t\tCustomCtx,\n\t\tCustomMadeArgs,\n\t\tInputCtx,\n\t\tVisibility,\n\t\tExtraArgs,\n\t\tProvidedServices<Provider>,\n\t\tEffectValue<Added>,\n\t\tAdded\n\t>\n}\n"],"mappings":"sDA8GA,SAAgB,EAWf,EASA,EAYC,CAGD,OAAO,EAAkB,EAAS,CAAO,CAY1C"}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { EffectValue } from "./operation.mjs";
|
|
2
|
-
import { CheckedApiRequirements, EffectApiHandlerValue, EffectApiOptions, EffectApiServices } from "./api-runtime.mjs";
|
|
2
|
+
import { CheckedApiRequirements, EffectApiContext, EffectApiHandlerValue, EffectApiOptions, EffectApiServices } from "./api-runtime.mjs";
|
|
3
3
|
import { ActionBuilder, ArgsArrayForOptionalValidator, ArgsArrayToObject, DefaultArgsForOptionalValidator, FunctionVisibility, GenericActionCtx, GenericDataModel, GenericMutationCtx, GenericQueryCtx, MutationBuilder, QueryBuilder, RegisteredAction, RegisteredMutation, RegisteredQuery, ReturnValueForOptionalValidator } from "convex/server";
|
|
4
4
|
import { PropertyValidators, Validator } from "convex/values";
|
|
5
5
|
//#region src/modules/effect/api.d.ts
|
|
6
6
|
type ApiValidator = PropertyValidators | Validator<unknown, 'required', string> | void;
|
|
7
7
|
type ApiOutput<Returns extends ApiValidator> = [Returns] extends [void] ? unknown : ReturnValueForOptionalValidator<Returns>;
|
|
8
|
-
type EffectApiBuilder<Ctx, Visibility extends FunctionVisibility, Kind extends 'query' | 'mutation' | 'action', Provider> = {
|
|
8
|
+
type EffectApiBuilder<Ctx, Visibility extends FunctionVisibility, Kind extends 'query' | 'mutation' | 'action', Provider, Initialized = {}> = {
|
|
9
9
|
<Args extends ApiValidator, Returns extends ApiValidator = void, Returned extends EffectApiHandlerValue<ApiOutput<Returns>> = EffectApiHandlerValue<ApiOutput<Returns>>, OneOrZeroArgs extends ArgsArrayForOptionalValidator<Args> = DefaultArgsForOptionalValidator<Args>>(definition: ({
|
|
10
10
|
args?: Args;
|
|
11
11
|
returns?: Returns;
|
|
12
12
|
handler: (ctx: Ctx, ...args: OneOrZeroArgs) => Returned;
|
|
13
|
-
} | ((ctx: Ctx, ...args: OneOrZeroArgs) => Returned)) & CheckedApiRequirements<Returned, Provider>): Kind extends 'query' ? RegisteredQuery<Visibility, ArgsArrayToObject<OneOrZeroArgs>, Promise<EffectValue<Returned>>> : Kind extends 'mutation' ? RegisteredMutation<Visibility, ArgsArrayToObject<OneOrZeroArgs>, Promise<EffectValue<Returned>>> : RegisteredAction<Visibility, ArgsArrayToObject<OneOrZeroArgs>, Promise<EffectValue<Returned>>>;
|
|
13
|
+
} | ((ctx: Ctx, ...args: OneOrZeroArgs) => Returned)) & CheckedApiRequirements<Returned, Provider> & CheckedApiRequirements<Initialized, Provider>): Kind extends 'query' ? RegisteredQuery<Visibility, ArgsArrayToObject<OneOrZeroArgs>, Promise<EffectValue<Returned>>> : Kind extends 'mutation' ? RegisteredMutation<Visibility, ArgsArrayToObject<OneOrZeroArgs>, Promise<EffectValue<Returned>>> : RegisteredAction<Visibility, ArgsArrayToObject<OneOrZeroArgs>, Promise<EffectValue<Returned>>>;
|
|
14
14
|
};
|
|
15
15
|
/** Provision request services after the supplied builder has constructed its trusted context. */
|
|
16
|
-
declare function effectApiBuilder<DM extends GenericDataModel, V extends FunctionVisibility, Provider extends EffectApiServices>(builder: QueryBuilder<DM, V>, options: EffectApiOptions<GenericQueryCtx<DM>, Provider>): EffectApiBuilder<GenericQueryCtx<DM>, V, 'query', Provider>;
|
|
17
|
-
declare function effectApiBuilder<DM extends GenericDataModel, V extends FunctionVisibility, Provider extends EffectApiServices>(builder: MutationBuilder<DM, V>, options: EffectApiOptions<GenericMutationCtx<DM>, Provider>): EffectApiBuilder<GenericMutationCtx<DM>, V, 'mutation', Provider>;
|
|
18
|
-
declare function effectApiBuilder<DM extends GenericDataModel, V extends FunctionVisibility, Provider extends EffectApiServices>(builder: ActionBuilder<DM, V>, options: EffectApiOptions<GenericActionCtx<DM>, Provider>): EffectApiBuilder<GenericActionCtx<DM>, V, 'action', Provider>;
|
|
16
|
+
declare function effectApiBuilder<DM extends GenericDataModel, V extends FunctionVisibility, Provider extends EffectApiServices = import('effect').Context.Context<never>, Added extends EffectApiContext = {}>(builder: QueryBuilder<DM, V>, options: EffectApiOptions<GenericQueryCtx<DM>, Provider, Added>): EffectApiBuilder<GenericQueryCtx<DM> & EffectValue<Added>, V, 'query', Provider, Added>;
|
|
17
|
+
declare function effectApiBuilder<DM extends GenericDataModel, V extends FunctionVisibility, Provider extends EffectApiServices = import('effect').Context.Context<never>, Added extends EffectApiContext = {}>(builder: MutationBuilder<DM, V>, options: EffectApiOptions<GenericMutationCtx<DM>, Provider, Added>): EffectApiBuilder<GenericMutationCtx<DM> & EffectValue<Added>, V, 'mutation', Provider, Added>;
|
|
18
|
+
declare function effectApiBuilder<DM extends GenericDataModel, V extends FunctionVisibility, Provider extends EffectApiServices = import('effect').Context.Context<never>, Added extends EffectApiContext = {}>(builder: ActionBuilder<DM, V>, options: EffectApiOptions<GenericActionCtx<DM>, Provider, Added>): EffectApiBuilder<GenericActionCtx<DM> & EffectValue<Added>, V, 'action', Provider, Added>;
|
|
19
19
|
//#endregion
|
|
20
20
|
export { EffectApiBuilder, effectApiBuilder };
|
|
21
21
|
//# sourceMappingURL=api.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.mjs","names":[],"sources":["../../../src/modules/effect/api.ts"],"sourcesContent":["import type {\n\tActionBuilder,\n\tArgsArrayForOptionalValidator,\n\tArgsArrayToObject,\n\tDefaultArgsForOptionalValidator,\n\tFunctionVisibility,\n\tGenericActionCtx,\n\tGenericDataModel,\n\tGenericMutationCtx,\n\tGenericQueryCtx,\n\tMutationBuilder,\n\tQueryBuilder,\n\tRegisteredAction,\n\tRegisteredMutation,\n\tRegisteredQuery,\n\tReturnValueForOptionalValidator,\n} from 'convex/server'\nimport type { PropertyValidators, Validator } from 'convex/values'\nimport type { EffectValue } from './operation'\nimport {\n\twrapEffectBuilder,\n\ttype CheckedApiRequirements,\n\ttype EffectApiOptions,\n\ttype EffectApiErrorProjection,\n\ttype EffectApiErrorContract,\n\ttype EffectApiServices,\n\ttype EffectApiHandlerValue,\n} from './api-runtime'\n\n// Convex's optional-validator grammar, restricted to unknown instead of public any.\ntype ApiValidator = PropertyValidators | Validator<unknown, 'required', string> | void\ntype ApiOutput<Returns extends ApiValidator> = [Returns] extends [void]\n\t? unknown\n\t: ReturnValueForOptionalValidator<Returns>\nexport type EffectApiBuilder<\n\tCtx,\n\tVisibility extends FunctionVisibility,\n\tKind extends 'query' | 'mutation' | 'action',\n\tProvider,\n> = {\n\t<\n\t\tArgs extends ApiValidator,\n\t\tReturns extends ApiValidator = void,\n\t\tReturned extends EffectApiHandlerValue<ApiOutput<Returns>> = EffectApiHandlerValue<\n\t\t\tApiOutput<Returns>\n\t\t>,\n\t\tOneOrZeroArgs extends ArgsArrayForOptionalValidator<Args> =\n\t\t\tDefaultArgsForOptionalValidator<Args>,\n\t>(\n\t\tdefinition: (\n\t\t\t| { args?: Args; returns?: Returns; handler: (ctx: Ctx, ...args: OneOrZeroArgs) => Returned }\n\t\t\t| ((ctx: Ctx, ...args: OneOrZeroArgs) => Returned)\n\t\t) &\n\t\t\tCheckedApiRequirements<Returned, Provider>,\n\t): Kind extends 'query'\n\t\t? RegisteredQuery<Visibility, ArgsArrayToObject<OneOrZeroArgs>, Promise<EffectValue<Returned>>>\n\t\t: Kind extends 'mutation'\n\t\t\t? RegisteredMutation<\n\t\t\t\t\tVisibility,\n\t\t\t\t\tArgsArrayToObject<OneOrZeroArgs>,\n\t\t\t\t\tPromise<EffectValue<Returned>>\n\t\t\t\t>\n\t\t\t: RegisteredAction<\n\t\t\t\t\tVisibility,\n\t\t\t\t\tArgsArrayToObject<OneOrZeroArgs>,\n\t\t\t\t\tPromise<EffectValue<Returned>>\n\t\t\t\t>\n}\n\n/** Provision request services after the supplied builder has constructed its trusted context. */\nexport function effectApiBuilder<\n\tDM extends GenericDataModel,\n\tV extends FunctionVisibility,\n\tProvider extends EffectApiServices,\n>(\n\tbuilder: QueryBuilder<DM, V>,\n\toptions: EffectApiOptions<GenericQueryCtx<DM>, Provider>,\n): EffectApiBuilder<GenericQueryCtx<DM>, V, 'query', Provider>\nexport function effectApiBuilder<\n\tDM extends GenericDataModel,\n\tV extends FunctionVisibility,\n\tProvider extends EffectApiServices,\n>(\n\tbuilder: MutationBuilder<DM, V>,\n\toptions: EffectApiOptions<GenericMutationCtx<DM>, Provider>,\n): EffectApiBuilder<GenericMutationCtx<DM>, V, 'mutation', Provider>\nexport function effectApiBuilder<\n\tDM extends GenericDataModel,\n\tV extends FunctionVisibility,\n\tProvider extends EffectApiServices,\n>(\n\tbuilder: ActionBuilder<DM, V>,\n\toptions: EffectApiOptions<GenericActionCtx<DM>, Provider>,\n): EffectApiBuilder<GenericActionCtx<DM>, V, 'action', Provider>\nexport function effectApiBuilder(\n\tbuilder: Function,\n\toptions: {\n\t\tservices
|
|
1
|
+
{"version":3,"file":"api.mjs","names":[],"sources":["../../../src/modules/effect/api.ts"],"sourcesContent":["import type {\n\tActionBuilder,\n\tArgsArrayForOptionalValidator,\n\tArgsArrayToObject,\n\tDefaultArgsForOptionalValidator,\n\tFunctionVisibility,\n\tGenericActionCtx,\n\tGenericDataModel,\n\tGenericMutationCtx,\n\tGenericQueryCtx,\n\tMutationBuilder,\n\tQueryBuilder,\n\tRegisteredAction,\n\tRegisteredMutation,\n\tRegisteredQuery,\n\tReturnValueForOptionalValidator,\n} from 'convex/server'\nimport type { PropertyValidators, Validator } from 'convex/values'\nimport type { EffectValue } from './operation'\nimport {\n\twrapEffectBuilder,\n\ttype CheckedApiRequirements,\n\ttype EffectApiOptions,\n\ttype EffectApiErrorProjection,\n\ttype EffectApiErrorContract,\n\ttype EffectApiServices,\n\ttype EffectApiHandlerValue,\n\ttype EffectApiContext,\n} from './api-runtime'\n\n// Convex's optional-validator grammar, restricted to unknown instead of public any.\ntype ApiValidator = PropertyValidators | Validator<unknown, 'required', string> | void\ntype ApiOutput<Returns extends ApiValidator> = [Returns] extends [void]\n\t? unknown\n\t: ReturnValueForOptionalValidator<Returns>\nexport type EffectApiBuilder<\n\tCtx,\n\tVisibility extends FunctionVisibility,\n\tKind extends 'query' | 'mutation' | 'action',\n\tProvider,\n\tInitialized = {},\n> = {\n\t<\n\t\tArgs extends ApiValidator,\n\t\tReturns extends ApiValidator = void,\n\t\tReturned extends EffectApiHandlerValue<ApiOutput<Returns>> = EffectApiHandlerValue<\n\t\t\tApiOutput<Returns>\n\t\t>,\n\t\tOneOrZeroArgs extends ArgsArrayForOptionalValidator<Args> =\n\t\t\tDefaultArgsForOptionalValidator<Args>,\n\t>(\n\t\tdefinition: (\n\t\t\t| { args?: Args; returns?: Returns; handler: (ctx: Ctx, ...args: OneOrZeroArgs) => Returned }\n\t\t\t| ((ctx: Ctx, ...args: OneOrZeroArgs) => Returned)\n\t\t) &\n\t\t\tCheckedApiRequirements<Returned, Provider> &\n\t\t\tCheckedApiRequirements<Initialized, Provider>,\n\t): Kind extends 'query'\n\t\t? RegisteredQuery<Visibility, ArgsArrayToObject<OneOrZeroArgs>, Promise<EffectValue<Returned>>>\n\t\t: Kind extends 'mutation'\n\t\t\t? RegisteredMutation<\n\t\t\t\t\tVisibility,\n\t\t\t\t\tArgsArrayToObject<OneOrZeroArgs>,\n\t\t\t\t\tPromise<EffectValue<Returned>>\n\t\t\t\t>\n\t\t\t: RegisteredAction<\n\t\t\t\t\tVisibility,\n\t\t\t\t\tArgsArrayToObject<OneOrZeroArgs>,\n\t\t\t\t\tPromise<EffectValue<Returned>>\n\t\t\t\t>\n}\n\n/** Provision request services after the supplied builder has constructed its trusted context. */\nexport function effectApiBuilder<\n\tDM extends GenericDataModel,\n\tV extends FunctionVisibility,\n\tProvider extends EffectApiServices = import('effect').Context.Context<never>,\n\tAdded extends EffectApiContext = {},\n>(\n\tbuilder: QueryBuilder<DM, V>,\n\toptions: EffectApiOptions<GenericQueryCtx<DM>, Provider, Added>,\n): EffectApiBuilder<GenericQueryCtx<DM> & EffectValue<Added>, V, 'query', Provider, Added>\nexport function effectApiBuilder<\n\tDM extends GenericDataModel,\n\tV extends FunctionVisibility,\n\tProvider extends EffectApiServices = import('effect').Context.Context<never>,\n\tAdded extends EffectApiContext = {},\n>(\n\tbuilder: MutationBuilder<DM, V>,\n\toptions: EffectApiOptions<GenericMutationCtx<DM>, Provider, Added>,\n): EffectApiBuilder<GenericMutationCtx<DM> & EffectValue<Added>, V, 'mutation', Provider, Added>\nexport function effectApiBuilder<\n\tDM extends GenericDataModel,\n\tV extends FunctionVisibility,\n\tProvider extends EffectApiServices = import('effect').Context.Context<never>,\n\tAdded extends EffectApiContext = {},\n>(\n\tbuilder: ActionBuilder<DM, V>,\n\toptions: EffectApiOptions<GenericActionCtx<DM>, Provider, Added>,\n): EffectApiBuilder<GenericActionCtx<DM> & EffectValue<Added>, V, 'action', Provider, Added>\nexport function effectApiBuilder(\n\tbuilder: Function,\n\toptions: {\n\t\tservices?: Function\n\t\tcontext?: Function\n\t\tmapError?: EffectApiErrorProjection\n\t\terrors?: EffectApiErrorContract\n\t},\n): Function {\n\treturn wrapEffectBuilder(builder, options)\n}\n"],"mappings":"sDAoGA,SAAgB,EACf,EACA,EAMW,CACX,OAAO,EAAkB,EAAS,CAAO,CAC1C"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ContractSchema } from "../contracts/contract.mjs";
|
|
1
|
+
import { ContractOutput, ContractSchema } from "../contracts/contract.mjs";
|
|
2
2
|
import { OperationPublicResult, SelectedOperation } from "../contracts/exposure.mjs";
|
|
3
3
|
import { Observability } from "../../components/foundation/modules/observability/observability.mjs";
|
|
4
4
|
import { AuditEntryInput } from "../../components/foundation/client.mjs";
|
|
@@ -49,8 +49,12 @@ type ErrorOf<Definition, Resolve, Dependencies, Guard> = EffectOperationError<De
|
|
|
49
49
|
type RequirementsOf<Definition, Resolve, Dependencies, Guard> = EffectOperationRequirements<Definition> | CallbackRequirements<Resolve | Dependencies[keyof Dependencies]> | UnwrappedDefaultRequirements<Definition, Guard>;
|
|
50
50
|
/** Internal registry constructor: the public Foundation binds host policies once. */
|
|
51
51
|
declare function createEffectCommand<Resolve extends Resolver, const Operations extends Registry<DomainOf<Resolve>>, const Dependencies extends EffectCommandDependencies, Guard = never>(configuration: EffectCommandConfiguration<Resolve, Operations, Guard> & Dependencies & HostCompatible<Resolve, Dependencies>): {
|
|
52
|
+
/** Bind invocation context without resolving it or running an operation. */
|
|
53
|
+
withContext(context: HostOf<Resolve>): {
|
|
54
|
+
exec: <Key extends Extract<keyof Operations, string>>(operation: Key, input: EffectOperationArgument<Operations[Key]>) => Effect.Effect<ContractOutput<Operations[Key]["result"]>, ErrorOf<Operations[Key], Resolve, Dependencies, Guard>, RequirementsOf<Operations[Key], Resolve, Dependencies, Guard>>;
|
|
55
|
+
};
|
|
52
56
|
expose<const Owner extends symbol, const Key extends Extract<keyof Operations, string>>(owner: Owner, operation: Key): SelectedOperation<Owner, Key, Operations[Key]['input'], OperationPublicResult<Operations[Key]>>;
|
|
53
|
-
exec<Key extends Extract<keyof Operations, string>>(operation: Key, input: EffectOperationArgument<Operations[Key]>, host: HostOf<Resolve>)
|
|
57
|
+
exec: <Key extends Extract<keyof Operations, string>>(operation: Key, input: EffectOperationArgument<Operations[Key]>, host: HostOf<Resolve>) => Effect.Effect<EffectOperationResult<Operations[Key]>, ErrorOf<Operations[Key], Resolve, Dependencies, Guard>, RequirementsOf<Operations[Key], Resolve, Dependencies, Guard>>;
|
|
54
58
|
};
|
|
55
59
|
/** Bind trusted application policies without exposing a loose public command kernel. */
|
|
56
60
|
declare function bindEffectCommand<const Dependencies extends EffectCommandDependencies>(dependencies: Dependencies): <Resolve extends Resolver, const Operations extends Registry<DomainOf<Resolve>>, Guard = never>(configuration: EffectCommandConfiguration<Resolve, Operations, Guard> & HostCompatible<Resolve, Dependencies>) => ReturnType<typeof createEffectCommand<Resolve, Operations, Dependencies, Guard>>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{selectOperation as e}from"../contracts/exposure.mjs";import{effectOperationFactory as t}from"./operation.mjs";import{decodeEffectContract as n}from"./schema.mjs";import{CommandPermissionError as r,executeCommandLifecycle as i}from"../../components/foundation/modules/command/lifecycle.mjs";import{normalizeEffect as a,observationFailure as o}from"./normalize.mjs";import{Effect as s,Exit as c}from"effect";const l={succeed:s.succeed,suspend:s.suspend,flatMap:s.flatMap};function u(u){let d=u.operations(t())
|
|
1
|
+
import{selectOperation as e}from"../contracts/exposure.mjs";import{effectOperationFactory as t}from"./operation.mjs";import{decodeEffectContract as n}from"./schema.mjs";import{CommandPermissionError as r,executeCommandLifecycle as i}from"../../components/foundation/modules/command/lifecycle.mjs";import{normalizeEffect as a,observationFailure as o}from"./normalize.mjs";import{Effect as s,Exit as c}from"effect";const l={succeed:s.succeed,suspend:s.suspend,flatMap:s.flatMap};function u(u){let d=u.operations(t()),p=(e,t,p)=>s.suspend(()=>{if(!Object.prototype.hasOwnProperty.call(d,e))throw Error(`The selected command operation is not configured`);let m=d[e];return s.flatMap(n(m.input,t),t=>{let d=t,h=u.observability.start({operation:e,classification:m.classification}),g=u.context;return a(()=>g(p)).pipe(s.flatMap(t=>i(l,{operation:e,classification:m.classification,context:t,command:d,permission:m.permission===void 0?void 0:()=>a(()=>{if(!u.checkPermission)throw new r(e);return u.checkPermission(p,{permission:m.permission,operation:e})}).pipe(s.asVoid),prepare:()=>a(()=>m.prepare?.(t,d)).pipe(s.map(e=>e?.kind===`execute`?{kind:`execute`,complete:e.complete?t=>a(()=>e.complete(t)).pipe(s.asVoid):void 0}:e)),decodeReplay:e=>n(m.replayResult??m.result,e),middleware:m.middleware?[e=>f(()=>m.middleware({...e,input:d}))]:[],defaultGuard:e=>a(()=>u.defaults?.guard?.(e)).pipe(s.asVoid),guard:e=>a(()=>m.guard?.(e,d)).pipe(s.asVoid),run:e=>f(()=>m.handler(d,e)),decodeResult:e=>n(m.result,e),audit:e=>a(()=>m.audit({command:d,result:e},t)),writeAudit:e=>a(()=>u.writeAudit(p,e)),aggregates:m.aggregates})),s.onExit(e=>s.sync(()=>{c.isSuccess(e)?h.completed():h.failed(o(e.cause))})))})});return{withContext(e){return{exec:(t,n)=>p(t,n,e)}},expose(t,n){if(!Object.hasOwn(d,n))throw Error(`The selected command operation is not configured`);return e(t,`mutation`,n,d[n])},exec:p}}function d(e){return function(t){return u({...t,...e})}}function f(e){return a(e)}export{d as bindEffectCommand,u as createEffectCommand};
|
|
2
2
|
//# sourceMappingURL=command.mjs.map
|