@voltro/runtime 0.11.2 → 0.11.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -954,6 +954,14 @@ export declare interface AttachAnalyticsMirrorOptions {
954
954
  readonly run?: (effect: Effect.Effect<void, unknown>) => Promise<unknown>;
955
955
  }
956
956
 
957
+ /** The descriptor shape the audit needs — a `defineQuery` result carries it. */
958
+ export declare interface AuditableQuery {
959
+ readonly name: string;
960
+ readonly output: Schema.Schema.Any;
961
+ /** Declared source table(s) — the tables whose serverOnly columns must not leak. */
962
+ readonly source?: string | ReadonlyArray<string> | undefined;
963
+ }
964
+
957
965
  /**
958
966
  * Resolve once a server returned by {@link startRpcServer} has actually
959
967
  * bound its port (node `'listening'` event). REJECTS on `'error'`
@@ -1519,6 +1527,138 @@ export declare const counter: (name: string, description?: string) => Metric.Met
1519
1527
  */
1520
1528
  export declare const countRunningWorkflows: (store: DataStore) => Promise<number>;
1521
1529
 
1530
+ /**
1531
+ * Secure-default CRUD executor factories. Each takes the table NAME (not the
1532
+ * table value — that would be a server import in a descriptor) and returns an
1533
+ * `(input, ctx) => …` executor for a `*.server.ts` default export.
1534
+ */
1535
+ export declare const crud: {
1536
+ /** Tenant-scoped list, redacted — with optional filter / sort / pagination from
1537
+ * the request input, so a real list view doesn't have to be hand-written. */
1538
+ list: (table: string, options?: CrudListOptions) => (input: unknown, ctx: AppContext) => Promise<ReadonlyArray<Row>>;
1539
+ /** One row by id, or `null` when absent — never throws. Redacted, with optional
1540
+ * eager-loaded relations (`include`). */
1541
+ getById: (table: string, options?: CrudReadOptions & {
1542
+ readonly include?: CrudInclude;
1543
+ }) => (input: {
1544
+ readonly id: string;
1545
+ }, ctx: AppContext) => Promise<Row | null>;
1546
+ /** Insert the input as a new row (id / tenant / audit auto-stamped). The echoed
1547
+ * row is redacted. Guard the DESCRIPTOR — this does not gate. */
1548
+ create: (table: string, options?: CrudWriteOptions) => (input: Row, ctx: AppContext) => Promise<Row>;
1549
+ /** Patch a row by id (`{ id, ...patch }`); returns the updated row or `null`.
1550
+ * Redacted. Guard the DESCRIPTOR. */
1551
+ update: (table: string, options?: CrudWriteOptions) => (input: {
1552
+ readonly id: string;
1553
+ } & Record<string, unknown>, ctx: AppContext) => Promise<Row | null>;
1554
+ /**
1555
+ * Tenant-scoped COUNT of the matching rows — the total a page-based UI needs to
1556
+ * render "page 3 of 12". Takes the SAME `filter` as `list` (share the option
1557
+ * object so the two can't disagree about which rows they mean) and ignores
1558
+ * paging: it counts the whole filtered set, not the current page. A real
1559
+ * `COUNT(*)` aggregate, not a fetch-and-length.
1560
+ *
1561
+ * export default crud.count('absenceRequests', { filter: (i) => ({ status: i.status }) })
1562
+ */
1563
+ count: (table: string, options?: Pick<CrudListOptions, "filter">) => (input: unknown, ctx: AppContext) => Promise<number>;
1564
+ /** Delete a row by id; returns `{ deleted }`. Guard the DESCRIPTOR. */
1565
+ remove: (table: string) => (input: {
1566
+ readonly id: string;
1567
+ }, ctx: AppContext) => Promise<{
1568
+ readonly deleted: boolean;
1569
+ }>;
1570
+ };
1571
+
1572
+ /** The eager-load spec `.with()` accepts — nested relations, each branch taking
1573
+ * its own `where` / `orderBy` / `limit` (nested filtering + sort). Reused as-is
1574
+ * so `crud.list`'s `include` is exactly what a hand-written `.with(...)` takes. */
1575
+ export declare type CrudInclude = Parameters<SelectBuilder['with']>[0];
1576
+
1577
+ /**
1578
+ * Options for `crud.list` — the read ergonomics every real list view needs, so a
1579
+ * generated list isn't limited to "all rows". All optional and additive: a bare
1580
+ * `crud.list('t')` still returns every (tenant-scoped, redacted) row.
1581
+ */
1582
+ export declare interface CrudListOptions extends CrudReadOptions {
1583
+ /**
1584
+ * Build a WHERE from the request input — return a column→value map. Only
1585
+ * entries whose value is not `undefined` are applied, so an absent filter field
1586
+ * is simply ignored (`{ employeeId: input.employeeId, status: input.status }`).
1587
+ * The descriptor's `input` schema declares those fields; this maps them to a
1588
+ * scoped `.where(column, value)` on the store query.
1589
+ */
1590
+ readonly filter?: (input: Readonly<Record<string, unknown>>) => Readonly<Record<string, unknown>>;
1591
+ /**
1592
+ * Page the result from the request. BOTH styles are accepted, so a caller uses
1593
+ * whichever its UI thinks in:
1594
+ *
1595
+ * - **page-based** — `input.page` (1-based) + `input.pageSize` (default 100).
1596
+ * A table UI showing "page 3 of 12" sends `?page=3&pageSize=20`.
1597
+ * - **offset-based** — `input.limit` / `input.offset` (defaults 100 / 0).
1598
+ *
1599
+ * `page` wins when both are present. Pair with `crud.count` for the total a
1600
+ * page-based UI needs to render the last-page number.
1601
+ */
1602
+ readonly paginate?: boolean;
1603
+ /**
1604
+ * Upper bound on the rows ONE request may ask for (default 1000). The page
1605
+ * size is caller-controlled, so without a cap `?limit=1000000` on a
1606
+ * `publicApi` list is a one-request resource-exhaustion lever for anyone who
1607
+ * can reach the endpoint. Raise it deliberately for an export-style endpoint;
1608
+ * a `limit`/`pageSize` above it is clamped, not rejected.
1609
+ */
1610
+ readonly maxPageSize?: number;
1611
+ /** Multi-column sort, applied in order (`[{ column: 'createdAt', direction:
1612
+ * 'desc' }, …]`). */
1613
+ readonly sort?: ReadonlyArray<CrudSort>;
1614
+ /**
1615
+ * Eager-load related rows via `.with(...)` — the SAME spec a hand-written query
1616
+ * takes, so nested relations, and per-branch `where` / `orderBy` / `limit`
1617
+ * (nested filtering + sort) all work:
1618
+ *
1619
+ * include: { employee: { with: { team: true } }, tags: { orderBy: 'name' } }
1620
+ */
1621
+ readonly include?: CrudInclude;
1622
+ /**
1623
+ * SQL column projection — narrow the `SELECT` so wide columns are never READ,
1624
+ * not merely dropped at the wire boundary. The output schema already strips
1625
+ * undeclared columns on encode (so nothing extra ships either way); this is the
1626
+ * PERFORMANCE half: a table with a large `json()` blob or a long text body that
1627
+ * a list view never shows shouldn't cost the read, the transfer from the DB, or
1628
+ * the decode.
1629
+ *
1630
+ * columns: ['id', 'title', 'createdAt'] // the big `body` is never read
1631
+ *
1632
+ * `.serverOnly()` columns are removed from the projection automatically — they
1633
+ * are stripped from the response anyway, so reading them is pure waste.
1634
+ *
1635
+ * TRAP with `include`: an eager branch joins on a foreign key, so a projection
1636
+ * that omits that FK column breaks the relation. Keep the FK in `columns` when
1637
+ * you also pass `include`.
1638
+ */
1639
+ readonly columns?: ReadonlyArray<string>;
1640
+ }
1641
+
1642
+ /** Options common to a generated READ. */
1643
+ export declare interface CrudReadOptions {
1644
+ /** Columns stripped from every returned row — a secret/credential a generated
1645
+ * read must never ship (`bankIban`, `tokenHash`, `salary`). The wire schema on
1646
+ * the descriptor should omit them too, so they never reach the client at all;
1647
+ * this is the runtime half that guarantees it regardless. */
1648
+ readonly redact?: ReadonlyArray<string>;
1649
+ }
1650
+
1651
+ /** A single sort term for `crud.list`. */
1652
+ export declare interface CrudSort {
1653
+ readonly column: string;
1654
+ readonly direction?: 'asc' | 'desc';
1655
+ }
1656
+
1657
+ /** Options for a generated WRITE — `redact` applies to the row the write echoes. */
1658
+ export declare interface CrudWriteOptions {
1659
+ readonly redact?: ReadonlyArray<string>;
1660
+ }
1661
+
1522
1662
  /** Read the active context, if any. Tests use this to validate the
1523
1663
  * propagation; production code uses it only inside this module. */
1524
1664
  export declare const currentRoutingContext: () => RoutingContext | undefined;
@@ -1974,6 +2114,16 @@ export declare interface FieldChange {
1974
2114
  export declare const fingerprintFor: (table: string, predicate: Predicate, indexHint?: string) => string;
1975
2115
 
1976
2116
  export declare interface FluentStore extends Omit<MutationStore, 'update' | 'delete' | 'query'> {
2117
+ /**
2118
+ * Diff-based many-to-many link writer for a junction table. `anchor` names the
2119
+ * source column and its id (`{ postId: 'p1' }`); the target column is the
2120
+ * junction's other reference column, auto-detected. Only the difference is
2121
+ * written, so reactive consumers see one change per changed row, not a
2122
+ * drop+reinsert of the whole set.
2123
+ *
2124
+ * await ctx.store.links('post_tags', { postId: post.id }).set(tagIds)
2125
+ */
2126
+ links(junctionTable: string, anchor: Readonly<Record<string, string>>): JunctionLinks;
1977
2127
  /**
1978
2128
  * Execute a query descriptor and return the matching rows — TYPED.
1979
2129
  *
@@ -2051,6 +2201,9 @@ export declare interface FluentStoreBackend {
2051
2201
  /** Forget the calling subject's credential. Returns whether a row was removed. */
2052
2202
  export declare const forgetCredential: (store: VaultStore, connectionId: string, subjectId: string) => Promise<boolean>;
2053
2203
 
2204
+ /** A boot-ready message for a set of leaks (empty → `undefined`, i.e. clean). */
2205
+ export declare const formatServerOnlyLeaks: (leaks: ReadonlyArray<ServerOnlyLeak>) => string | undefined;
2206
+
2054
2207
  /** Serialise a span's context as a `traceparent` header value. */
2055
2208
  export declare const formatTraceparent: (ctx: TraceContext) => string;
2056
2209
 
@@ -2454,6 +2607,50 @@ export declare type IvmState = ReadonlyMap<string, GroupState>;
2454
2607
  /** Project the public aggregate value from a group's accumulator. */
2455
2608
  export declare const ivmValue: (shape: AggregateShape, g: GroupState) => number | null;
2456
2609
 
2610
+ /**
2611
+ * Diff-based writer for a many-to-many JUNCTION table (A2). Reconciles the set
2612
+ * of links from ONE anchor row (`{ [sourceColumn]: id }`) against a target-id
2613
+ * list by writing only the DIFFERENCE — the added rows are inserted, the removed
2614
+ * rows are deleted, and rows already correct are left untouched. That is the
2615
+ * whole point over a drop-all-then-reinsert `setLinks`: a reactive subscription
2616
+ * on the junction sees a change event only for the rows that actually changed
2617
+ * (no flicker, no lost data if two writers overlap), and an unchanged link never
2618
+ * churns. The TARGET column is the junction's OTHER reference column (the one the
2619
+ * anchor doesn't name); a junction with anything but exactly two reference
2620
+ * columns is rejected with a message naming what it found.
2621
+ */
2622
+ export declare interface JunctionLinks {
2623
+ /** The current target ids linked to the anchor. */
2624
+ list(): Promise<ReadonlyArray<string>>;
2625
+ /** Reconcile the links to EXACTLY `targetIds` — insert the missing, delete the
2626
+ * surplus, leave the rest. Returns what changed. */
2627
+ set(targetIds: ReadonlyArray<string>): Promise<{
2628
+ readonly added: ReadonlyArray<string>;
2629
+ readonly removed: ReadonlyArray<string>;
2630
+ }>;
2631
+ /** Link `targetIds` that aren't linked yet (idempotent — existing links are
2632
+ * not re-inserted, so they emit no event). Returns the ids actually added. */
2633
+ add(targetIds: ReadonlyArray<string>): Promise<ReadonlyArray<string>>;
2634
+ /** Unlink `targetIds` that are currently linked. Returns the ids actually removed. */
2635
+ remove(targetIds: ReadonlyArray<string>): Promise<ReadonlyArray<string>>;
2636
+ /**
2637
+ * Reconcile links that carry PER-ROW PAYLOAD — a junction with business columns
2638
+ * (a membership `role`, a `capacity` value). Each row is
2639
+ * `{ [targetColumn]: id, …payload }`; the diff is on the (source, target) pair:
2640
+ * an added row is inserted with its payload, a removed row deleted, and a
2641
+ * SURVIVING row whose payload actually changed is UPDATED — one whose payload
2642
+ * is unchanged is left untouched, so a reactive consumer sees a change only
2643
+ * where the payload differs (the `set(targetIds)` form can't express payload;
2644
+ * this is the drop+reinsert replacement for a junction that carries data).
2645
+ * Payload is compared by strict equality per column (scalars — capacity, role).
2646
+ */
2647
+ setRows(rows: ReadonlyArray<Readonly<Record<string, unknown>>>): Promise<{
2648
+ readonly added: ReadonlyArray<string>;
2649
+ readonly removed: ReadonlyArray<string>;
2650
+ readonly updated: ReadonlyArray<string>;
2651
+ }>;
2652
+ }
2653
+
2457
2654
  export declare interface KvFacade {
2458
2655
  readonly kv: AsyncKv;
2459
2656
  /** The resolved Effect-native `Kv` service instance. Its methods close over
@@ -2714,6 +2911,24 @@ export declare const makeListConnectionsExecutor: (deps: ConnectionBuiltinDeps)
2714
2911
  */
2715
2912
  export declare const makeMutationRunner: (deps: MutationRunnerDeps) => (mutation: MutationLike, input: unknown, requestContext: ServeRequestContext) => Promise<unknown>;
2716
2913
 
2914
+ /**
2915
+ * Run a QUERY once and resolve its value — the read counterpart to
2916
+ * `makeMutationRunner` / `makeActionRunner`, for callers that have no
2917
+ * subscription: a descriptor projected to a public REST endpoint.
2918
+ *
2919
+ * It is deliberately built ON TOP of `makeQueryDescriptorProducer` rather than
2920
+ * beside it. That producer is where the declarative `guards:` gate, the
2921
+ * per-request row-filter resolution and the tenant-scoping `finalize` live, so
2922
+ * reusing it makes the one-shot path enforce EXACTLY what the WS path enforces.
2923
+ * A second implementation here would be an authorization bypass waiting to
2924
+ * happen — the REST projection of a guarded query must fail the same way the
2925
+ * socket does, and it does because it runs the same code.
2926
+ *
2927
+ * Both handler shapes resolve: a descriptor-returning (reactive) query is
2928
+ * finalized and executed to rows; a computed query yields its value.
2929
+ */
2930
+ export declare const makeOneShotQueryRunner: <D>(deps: OneShotQueryRunnerDeps<D>) => (query: MutationLike, input: unknown, requestContext: ServeRequestContext) => Promise<unknown>;
2931
+
2717
2932
  export declare const makeOutboxFacade: (deps: OutboxFacadeDeps) => OutboxFacade;
2718
2933
 
2719
2934
  export declare const makePostCommitWorkflowFacade: (base: WorkflowsAppContext, afterCommit: (work: () => Promise<unknown>) => void) => WorkflowsAppContext;
@@ -2757,6 +2972,25 @@ export declare const makeQueryDescriptorProducer: <D>(deps: QueryProducerDeps<D>
2757
2972
  */
2758
2973
  export declare const makeQueryReauthorizer: (query: MutationLike, input: unknown) => (subject: Subject) => () => Promise<unknown>;
2759
2974
 
2975
+ /**
2976
+ * Subscribe a QUERY and stream its events to a non-rpc consumer — the SSE
2977
+ * projection of a `publicApi` query. Like `makeOneShotQueryRunner`, it runs the
2978
+ * shared `makeQueryDescriptorProducer`, so the declarative `guards:`, the row
2979
+ * filter and tenant scoping are the same code the socket path runs: an SSE
2980
+ * endpoint is exactly as gated as the WebSocket one.
2981
+ *
2982
+ * Returns an unsubscribe SYNCHRONOUSLY (the HTTP layer needs one immediately)
2983
+ * while the subscription opens in the background; unsubscribing before it
2984
+ * finishes tears it down as soon as it exists, so a client that disconnects
2985
+ * mid-setup cannot leak a subscription. A setup failure — including a guard
2986
+ * denial — is emitted as one `error` event rather than thrown, because by then
2987
+ * the response headers are already on the wire.
2988
+ */
2989
+ export declare const makeQuerySubscriber: <D>(deps: QuerySubscriberDeps<D>) => (query: MutationLike, input: unknown, requestContext: ServeRequestContext, emit: (event: {
2990
+ readonly _tag: string;
2991
+ readonly [k: string]: unknown;
2992
+ }) => void) => (() => void);
2993
+
2760
2994
  export declare const makeRouterActivity: () => RouterActivity;
2761
2995
 
2762
2996
  /**
@@ -3145,6 +3379,15 @@ export declare interface OAuth2ConnectionDefinition extends ConnectionDefinition
3145
3379
  */
3146
3380
  export declare const onBindConnectionSubject: (listener: (clientId: number, subject: Subject) => void) => () => void;
3147
3381
 
3382
+ export declare interface OneShotQueryRunnerDeps<D> extends QueryProducerDeps<D> {
3383
+ /**
3384
+ * Execute a FINALIZED (tenant-scoped) descriptor → rows. On the WS path the
3385
+ * dispatcher owns this step; a ONE-SHOT read (a `publicApi` REST GET, where
3386
+ * there is no subscription to drive) needs it inline.
3387
+ */
3388
+ readonly queryRows: (descriptor: D) => Promise<ReadonlyArray<unknown>>;
3389
+ }
3390
+
3148
3391
  /** Thrown by `.expectVersion(n).set(...)` when the optimistic-lock guard
3149
3392
  * matched no row (the row was concurrently updated or deleted). */
3150
3393
  export declare class OptimisticLockError extends OptimisticLockError_base {
@@ -3449,6 +3692,13 @@ export declare interface QueryProducerDeps<D> {
3449
3692
  readonly interceptor?: ServeRpcInterceptor;
3450
3693
  }
3451
3694
 
3695
+ export declare interface QuerySubscriberDeps<D> extends QueryProducerDeps<D> {
3696
+ /** Open a dispatcher subscription for a finalized descriptor. */
3697
+ readonly subscribeDescriptor: (descriptor: D, emit: (event: unknown) => void, context: ServeRequestContext) => Promise<() => void>;
3698
+ /** Open a dispatcher subscription for a COMPUTED query (re-runs on source change). */
3699
+ readonly subscribeComputed: (computed: ComputedQuery, emit: (event: unknown) => void, context: ServeRequestContext) => Promise<() => void>;
3700
+ }
3701
+
3452
3702
  /** What a reaction does when it fires — run an agent or start a workflow. Both
3453
3703
  * identified by name; the serve layer resolves + runs them as `agentActor`. */
3454
3704
  export declare type ReactionAct = {
@@ -3611,6 +3861,13 @@ export declare const recordTimelineEvent: (change: CdcChange & {
3611
3861
  readonly tenantId?: string | null;
3612
3862
  }) => void;
3613
3863
 
3864
+ /**
3865
+ * Strip `redact` columns from a set of rows. Exposed on its own so a hand-written
3866
+ * handler that isn't a plain CRUD read can still redact declaratively and be
3867
+ * audited the same way. Pure — no store, no context.
3868
+ */
3869
+ export declare const redactColumns: <R extends Row>(rows: ReadonlyArray<R>, redact: ReadonlyArray<string>) => ReadonlyArray<R>;
3870
+
3614
3871
  /** Blank sensitive-looking columns. Returns a new object; null passes through. */
3615
3872
  export declare const redactRow: (row: Row_2 | null | undefined) => Row_2 | null;
3616
3873
 
@@ -4759,6 +5016,14 @@ export declare interface SchedulerHandle {
4759
5016
  export declare interface SchedulerLogger {
4760
5017
  info: (msg: string, fields?: Record<string, unknown>) => void;
4761
5018
  warn: (msg: string, fields?: Record<string, unknown>) => void;
5019
+ /**
5020
+ * Failures. This channel did not exist, which is why a schedule whose handler
5021
+ * failed on EVERY firing was only ever a `warn` — invisible to `voltro logs
5022
+ * --level error`, and a schedule fires unattended, so that log line is the
5023
+ * whole discovery channel. Optional so an embedder passing a two-method logger
5024
+ * still compiles; it falls back to `warn` at the call site.
5025
+ */
5026
+ error?: (msg: string, fields?: Record<string, unknown>) => void;
4762
5027
  /** Optional debug channel for high-frequency expected events
4763
5028
  * (lost coordination claims on sub-minute schedules etc.). */
4764
5029
  debug?: (msg: string, fields?: Record<string, unknown>) => void;
@@ -4827,6 +5092,13 @@ export declare interface SchemaInfo {
4827
5092
  readonly idScheme?: IdScheme;
4828
5093
  }
4829
5094
 
5095
+ /**
5096
+ * Every property NAME that appears anywhere in a schema's shape. Recursive over
5097
+ * the common composition nodes so a column nested under `{ refs: [{ … }] }` or
5098
+ * behind a `NullOr` is still seen. Cycle-guarded via a seen-set on Suspend.
5099
+ */
5100
+ export declare const schemaPropertyNames: (schema: Schema.Schema.Any) => ReadonlySet<string>;
5101
+
4830
5102
  export declare interface SchemaRegistry {
4831
5103
  readonly tables: ReadonlyMap<string, SchemaInfo>;
4832
5104
  /** True iff the table was composed with the named mixin id. */
@@ -4974,6 +5246,20 @@ export declare interface ServeRequestContext {
4974
5246
  readonly rowFilter?: RowFilterScope;
4975
5247
  }
4976
5248
 
5249
+ /** One leak: a wire query that declares a serverOnly column in its output. */
5250
+ export declare interface ServerOnlyLeak {
5251
+ readonly query: string;
5252
+ readonly table: string;
5253
+ readonly column: string;
5254
+ }
5255
+
5256
+ /**
5257
+ * Every serverOnly column a query's output declares. `serverOnlyByTable` maps a
5258
+ * table name to its `.serverOnly()` column names. A query with no `source`, or
5259
+ * whose source has no serverOnly columns, yields nothing.
5260
+ */
5261
+ export declare const serverOnlyLeaks: (query: AuditableQuery, serverOnlyByTable: ReadonlyMap<string, ReadonlyArray<string>>) => ReadonlyArray<ServerOnlyLeak>;
5262
+
4977
5263
  /** A plugin interceptor — wraps the base run Effect (Effect-native chain). */
4978
5264
  export declare type ServeRpcInterceptor = (base: Effect.Effect<unknown, unknown, never>, meta: {
4979
5265
  readonly tag: string;
@@ -6106,6 +6392,32 @@ export declare interface WorkflowLayerOptions<Context> {
6106
6392
  readonly resolveStartContext?: (workflowName: string, executionId: string) => WorkflowCallerContext | undefined | Promise<WorkflowCallerContext | undefined>;
6107
6393
  }
6108
6394
 
6395
+ /**
6396
+ * A start whose PAYLOAD does not match the workflow's schema.
6397
+ *
6398
+ * Distinct from a workflow that ran and failed, and the distinction is the whole
6399
+ * point: `ctx.workflows.start(name, payload)` is typed `(string, unknown)` — the
6400
+ * name is not checked against the registry and the payload is not checked against
6401
+ * anything — so a caller that drifts from the workflow's schema produces a failure
6402
+ * DEEP inside the engine, where it reads like the workflow itself misbehaved.
6403
+ *
6404
+ * A cron firing such a start hit that every single time and looked like a flaky
6405
+ * job. Naming the workflow, the missing fields, and the fact that the payload
6406
+ * (not the workflow) is what's wrong turns it into a one-read fix.
6407
+ */
6408
+ export declare class WorkflowPayloadError extends Error {
6409
+ readonly workflowName: string;
6410
+ /** Top-level property names the schema requires and the payload omitted.
6411
+ * Empty when the mismatch is a type error rather than a missing key. */
6412
+ readonly missingFields: ReadonlyArray<string>;
6413
+ readonly detail: string;
6414
+ readonly _tag = "WorkflowPayloadError";
6415
+ constructor(workflowName: string,
6416
+ /** Top-level property names the schema requires and the payload omitted.
6417
+ * Empty when the mismatch is a type error rather than a missing key. */
6418
+ missingFields: ReadonlyArray<string>, detail: string);
6419
+ }
6420
+
6109
6421
  /** Options for {@link WorkflowsAppContext.retry}. */
6110
6422
  export declare interface WorkflowRetryOptions {
6111
6423
  /** Re-run the workflow against this payload instead of the original