@voltro/runtime 0.25.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +541 -0
- package/dist/index.d.ts +192 -14
- package/dist/index.js +1115 -1005
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ import { Metric } from 'effect';
|
|
|
43
43
|
import { MetricBoundaries } from 'effect';
|
|
44
44
|
import { MetricReader } from '@opentelemetry/sdk-metrics';
|
|
45
45
|
import { PluginHttpRoute } from '@voltro/protocol';
|
|
46
|
+
import { PluginRefOrphanPolicy } from '@voltro/database';
|
|
46
47
|
import { Predicate } from '@voltro/database';
|
|
47
48
|
import { Query } from '@voltro/database';
|
|
48
49
|
import { QueryDescriptor } from '@voltro/database';
|
|
@@ -871,10 +872,23 @@ export declare interface AppContext {
|
|
|
871
872
|
* `*.workflow.tsx` files. Starts return a run handle immediately;
|
|
872
873
|
* `wait`/`poll`/`signal`/`cancel` are explicit operations. */
|
|
873
874
|
readonly workflows?: WorkflowsAppContext;
|
|
874
|
-
/**
|
|
875
|
-
*
|
|
876
|
-
*
|
|
877
|
-
|
|
875
|
+
/**
|
|
876
|
+
* `ctx.events.publish(descriptor, key, payload)` — present when the app
|
|
877
|
+
* declares `*.event.ts` files.
|
|
878
|
+
*
|
|
879
|
+
* This was typed as the OLD string-emitter facade (`emit(name, data)`) long
|
|
880
|
+
* after that facade was deleted, so the documented call was a `tsc` error
|
|
881
|
+
* while the runtime carried only `publish` — a consumer measured
|
|
882
|
+
* `eventKeys: ["publish"], emitType: undefined` with a cron probe because the
|
|
883
|
+
* type and the docs disagreed and they could not tell which was lying.
|
|
884
|
+
*
|
|
885
|
+
* What let it drift is worth naming: the builder installed the publisher with
|
|
886
|
+
* `as never`, so the compiler had the answer the whole time and was told not
|
|
887
|
+
* to give it. `appContext.ts` even carried a comment stating `emit` is gone —
|
|
888
|
+
* beside the cast that hid it. A context field is the one place this repo
|
|
889
|
+
* treats such a cast as a defect in its own right.
|
|
890
|
+
*/
|
|
891
|
+
readonly events?: EventPublisher;
|
|
878
892
|
/**
|
|
879
893
|
* Transactional outbox (`ctx.outbox`). Absent when the app declares no
|
|
880
894
|
* `*.outbox.ts` handler — an enqueue with nobody to deliver it would be a
|
|
@@ -936,6 +950,18 @@ export declare const applyInverse: (store: UndoApplyStore, op: InverseOp) => Pro
|
|
|
936
950
|
* `synthesizeInverse`), as the caller's transaction wraps them. */
|
|
937
951
|
export declare const applyInverses: (store: UndoApplyStore, ops: ReadonlyArray<InverseOp>) => Promise<void>;
|
|
938
952
|
|
|
953
|
+
/**
|
|
954
|
+
* Apply every rule matching `change`.
|
|
955
|
+
*
|
|
956
|
+
* Returns what it did, so a caller can log it. Never throws for a rule that
|
|
957
|
+
* finds nothing — an orphan rule firing on a row nobody references is the
|
|
958
|
+
* normal case, not an error.
|
|
959
|
+
*/
|
|
960
|
+
export declare const applyPluginRefRules: (store: PluginRefStore, rules: ReadonlyArray<PluginRefRule>, change: PluginRefChange) => Promise<ReadonlyArray<{
|
|
961
|
+
readonly rule: PluginRefRule;
|
|
962
|
+
readonly affected: number;
|
|
963
|
+
}>>;
|
|
964
|
+
|
|
939
965
|
/** AND-merge the row filter for `table` onto an existing predicate. */
|
|
940
966
|
export declare const applyRowFilter: (scope: RowFilterScope, table: string, predicate: Predicate | undefined) => Predicate | undefined;
|
|
941
967
|
|
|
@@ -1125,15 +1151,6 @@ export declare interface BeginOAuthResult {
|
|
|
1125
1151
|
readonly state: string;
|
|
1126
1152
|
}
|
|
1127
1153
|
|
|
1128
|
-
/**
|
|
1129
|
-
* Override the subject for an active connection. Called from the
|
|
1130
|
-
* `auth.signin` (or any "I just authenticated this caller") handler
|
|
1131
|
-
* after the credential check passes. Future calls on the same
|
|
1132
|
-
* connection see the new subject via the AuthMiddleware fast-path.
|
|
1133
|
-
*
|
|
1134
|
-
* Emits to all `onBind` listeners synchronously so subscription
|
|
1135
|
-
* registries can re-scope active subscriptions.
|
|
1136
|
-
*/
|
|
1137
1154
|
export declare const bindConnectionSubject: (clientId: number, subject: Subject) => void;
|
|
1138
1155
|
|
|
1139
1156
|
/**
|
|
@@ -1178,7 +1195,25 @@ export declare const bindMutation: <Input, Output, E = never>(execute: (input: I
|
|
|
1178
1195
|
* finalizers run → the provider request aborts), reusing the same
|
|
1179
1196
|
* per-connection interrupt registry subscriptions use.
|
|
1180
1197
|
*/
|
|
1181
|
-
export declare const bindStream: <Element, Err = unknown>(buildStream: (context: RuntimeContext) => Stream.Stream<Element, Err, never> | Effect.Effect<Stream.Stream<Element, Err, never>, unknown, never>, spanName?: string
|
|
1198
|
+
export declare const bindStream: <Element, Err = unknown>(buildStream: (context: RuntimeContext) => Stream.Stream<Element, Err, never> | Effect.Effect<Stream.Stream<Element, Err, never>, unknown, never>, spanName?: string,
|
|
1199
|
+
/**
|
|
1200
|
+
* The stream's declared `guards:`, when it has any.
|
|
1201
|
+
*
|
|
1202
|
+
* Checked once at subscribe AND before every element, exactly as a query's
|
|
1203
|
+
* are. A stream was the one realtime primitive that could not express
|
|
1204
|
+
* authorization at all, so whatever protection existed was hand-written in an
|
|
1205
|
+
* executor where nothing could verify it was there.
|
|
1206
|
+
*
|
|
1207
|
+
* A denial ENDS the stream rather than dropping the element: a skipped
|
|
1208
|
+
* element is indistinguishable from "nothing to send", and the client must
|
|
1209
|
+
* learn it lost access instead of inferring it from silence.
|
|
1210
|
+
*/
|
|
1211
|
+
guards?: ReadonlyArray<unknown> | undefined,
|
|
1212
|
+
/** The call's decoded input — the guard INPUT, so a resource-scoped guard
|
|
1213
|
+
* (`{ scope: 'x:read', from: 'id' }`) can see which resource was asked for.
|
|
1214
|
+
* Passed explicitly rather than read off RuntimeContext, which does not
|
|
1215
|
+
* carry it. */
|
|
1216
|
+
guardInput?: unknown) => Stream.Stream<Element, Err, SubjectService | ConnectionInfo>;
|
|
1182
1217
|
|
|
1183
1218
|
/**
|
|
1184
1219
|
* Build a `Stream<SubscriptionEvent<T>>` that an @effect/rpc streaming
|
|
@@ -1473,6 +1508,34 @@ export { clearRetentions }
|
|
|
1473
1508
|
/** Clear the process-wide handle (test teardown). */
|
|
1474
1509
|
export declare const clearSystemStoreHandle: () => void;
|
|
1475
1510
|
|
|
1511
|
+
/**
|
|
1512
|
+
* Collect the declared rules from the discovered tables.
|
|
1513
|
+
*
|
|
1514
|
+
* `tables` is the boot's table set. Each column carrying a `pluginRef` spec
|
|
1515
|
+
* contributes one rule, with the target resolved through the plugin's exported
|
|
1516
|
+
* handle — so a rename carries the rule.
|
|
1517
|
+
*
|
|
1518
|
+
* Throws when a `pluginRef` names a table nothing registered: a declared rule
|
|
1519
|
+
* against an absent plugin would sit there looking enforced and never fire,
|
|
1520
|
+
* which is the exact failure this framework has shipped too often. The message
|
|
1521
|
+
* names both sides so the fix is obvious (install the plugin, or drop the
|
|
1522
|
+
* column).
|
|
1523
|
+
*/
|
|
1524
|
+
export declare const collectPluginRefRules: (tables: ReadonlyArray<{
|
|
1525
|
+
readonly name: string;
|
|
1526
|
+
/** The DECLARED references, carried on the built table. Reading the column
|
|
1527
|
+
* bag instead does not work: `table()` materialises builders into plain
|
|
1528
|
+
* field descriptors, so the spec is gone by the time anyone can ask. That
|
|
1529
|
+
* is how this collector silently produced ZERO rules while its own wiring
|
|
1530
|
+
* test — which only checked that it was CALLED — stayed green. */
|
|
1531
|
+
readonly appliedPluginRefs?: ReadonlyArray<{
|
|
1532
|
+
readonly column: string;
|
|
1533
|
+
readonly target: () => unknown;
|
|
1534
|
+
readonly orphanPolicy: PluginRefOrphanPolicy;
|
|
1535
|
+
readonly onSoftDelete: boolean;
|
|
1536
|
+
}>;
|
|
1537
|
+
}>, registered: ReadonlySet<string>) => ReadonlyArray<PluginRefRule>;
|
|
1538
|
+
|
|
1476
1539
|
/** Parse + return the compiled `Cron` for a definition. Throws the
|
|
1477
1540
|
* same way `defineSchedule` validated — callers that already hold a
|
|
1478
1541
|
* branded definition can trust this won't throw. */
|
|
@@ -2834,6 +2897,9 @@ export declare const formatServerOnlyLeaks: (leaks: ReadonlyArray<ServerOnlyLeak
|
|
|
2834
2897
|
/** Serialise a span's context as a `traceparent` header value. */
|
|
2835
2898
|
export declare const formatTraceparent: (ctx: TraceContext) => string;
|
|
2836
2899
|
|
|
2900
|
+
/** The boot warning for unresolved sources, or `undefined` when there are none. */
|
|
2901
|
+
export declare const formatUnresolvedSources: (found: ReadonlyArray<UnresolvedSource>) => string | undefined;
|
|
2902
|
+
|
|
2837
2903
|
/** One captured row change within a mutation invocation. */
|
|
2838
2904
|
export declare interface ForwardChange {
|
|
2839
2905
|
readonly table: string;
|
|
@@ -2874,6 +2940,8 @@ export declare const getConnectionResolver: () => ConnectionResolver | undefined
|
|
|
2874
2940
|
/** Look up the override for a connection. Returns `undefined` if no override is set. */
|
|
2875
2941
|
export declare const getConnectionSubject: (clientId: number) => Subject | undefined;
|
|
2876
2942
|
|
|
2943
|
+
export declare const getCredentialExpiry: (clientId: number) => number | undefined;
|
|
2944
|
+
|
|
2877
2945
|
/** The registered field cipher, if any. The store middleware reads this. */
|
|
2878
2946
|
export declare const getFieldCipher: () => FieldCipher | undefined;
|
|
2879
2947
|
|
|
@@ -3267,6 +3335,20 @@ export declare const isSafeToApply: (current: Record<string, unknown> | null | u
|
|
|
3267
3335
|
|
|
3268
3336
|
export declare const isScheduleDefinition: (v: unknown) => v is BrandedScheduleDefinition;
|
|
3269
3337
|
|
|
3338
|
+
/**
|
|
3339
|
+
* Did this change soft-delete the row?
|
|
3340
|
+
*
|
|
3341
|
+
* A soft delete is NOT a `delete` event — it is an `update` that sets
|
|
3342
|
+
* `deletedAt`. That distinction is why the first version of `onSoftDelete` was
|
|
3343
|
+
* inert in every case: the matcher only looked at `op === 'delete'`, which a
|
|
3344
|
+
* soft delete never is, so the option existed and could not fire. Exactly the
|
|
3345
|
+
* defect this whole round was about, one level down.
|
|
3346
|
+
*
|
|
3347
|
+
* The transition matters, not the value: a row that was already deleted and is
|
|
3348
|
+
* updated again must not re-fire the rule.
|
|
3349
|
+
*/
|
|
3350
|
+
export declare const isSoftDelete: (change: PluginRefChange) => boolean;
|
|
3351
|
+
|
|
3270
3352
|
/** A freshly issued key — `token` is shown ONCE and never stored in clear. */
|
|
3271
3353
|
export declare interface IssuedApiKey {
|
|
3272
3354
|
readonly id: string;
|
|
@@ -4474,6 +4556,34 @@ export declare const payloadPropertyNames: (schema: Schema.Schema.Any) => {
|
|
|
4474
4556
|
readonly optional: ReadonlyArray<string>;
|
|
4475
4557
|
};
|
|
4476
4558
|
|
|
4559
|
+
/** A change as the post-commit channel reports it. */
|
|
4560
|
+
export declare interface PluginRefChange {
|
|
4561
|
+
readonly table: string;
|
|
4562
|
+
readonly op: 'insert' | 'update' | 'delete';
|
|
4563
|
+
readonly rowId: string;
|
|
4564
|
+
readonly tenantId?: string | null;
|
|
4565
|
+
/** The row before, when there was one — used to spot a soft delete. */
|
|
4566
|
+
readonly old?: Record<string, unknown> | null;
|
|
4567
|
+
/** The row after, when there is one. */
|
|
4568
|
+
readonly new?: Record<string, unknown> | null;
|
|
4569
|
+
}
|
|
4570
|
+
|
|
4571
|
+
/** One resolved rule: "when <targetTable> loses a row, do <policy> to <table.column>". */
|
|
4572
|
+
export declare interface PluginRefRule {
|
|
4573
|
+
readonly table: string;
|
|
4574
|
+
readonly column: string;
|
|
4575
|
+
readonly targetTable: string;
|
|
4576
|
+
readonly policy: PluginRefOrphanPolicy;
|
|
4577
|
+
readonly onSoftDelete: boolean;
|
|
4578
|
+
}
|
|
4579
|
+
|
|
4580
|
+
/** The store surface the enforcement needs. */
|
|
4581
|
+
export declare interface PluginRefStore {
|
|
4582
|
+
query: (descriptor: unknown) => Promise<ReadonlyArray<Record<string, unknown>>>;
|
|
4583
|
+
update: (table: string, id: string, patch: Record<string, unknown>) => Promise<unknown>;
|
|
4584
|
+
delete: (table: string, id: string) => Promise<unknown>;
|
|
4585
|
+
}
|
|
4586
|
+
|
|
4477
4587
|
/** The 3-arg signature a plugin sees on its bind-ctx. */
|
|
4478
4588
|
export declare type PluginScheduleCoordinated = (name: string, intervalMs: number, effect: () => void | Promise<void>) => CoordinatedScheduleHandle;
|
|
4479
4589
|
|
|
@@ -4762,6 +4872,8 @@ export declare interface RebacSubject {
|
|
|
4762
4872
|
readonly scopes?: ReadonlyArray<string>;
|
|
4763
4873
|
}
|
|
4764
4874
|
|
|
4875
|
+
export declare const recordCredentialExpiry: (clientId: number, exp: number | undefined) => void;
|
|
4876
|
+
|
|
4765
4877
|
/** What the framework emit-seams (servePipeline / rpcServer / plugin wrappers)
|
|
4766
4878
|
* hand to the registry. A flat, transport-agnostic sample shape the
|
|
4767
4879
|
* recording callbacks can build without touching Effect. */
|
|
@@ -5653,6 +5765,14 @@ export declare interface RpcServerOptions<Rpcs extends Rpc.Any> {
|
|
|
5653
5765
|
readonly maxRpcBodyBytes?: number;
|
|
5654
5766
|
}
|
|
5655
5767
|
|
|
5768
|
+
/**
|
|
5769
|
+
* Rules that apply to one change, or an empty list.
|
|
5770
|
+
*
|
|
5771
|
+
* Exported separately from the execution so the matching is testable without a
|
|
5772
|
+
* store: which rules fire is the part with the edge cases.
|
|
5773
|
+
*/
|
|
5774
|
+
export declare const rulesFor: (rules: ReadonlyArray<PluginRefRule>, change: PluginRefChange) => ReadonlyArray<PluginRefRule>;
|
|
5775
|
+
|
|
5656
5776
|
/**
|
|
5657
5777
|
* Run `fn` with `SubjectService` bound to a `system` subject and a
|
|
5658
5778
|
* system-scoped fluent `ctx.store`. Returns whatever `fn` returns.
|
|
@@ -7259,6 +7379,27 @@ export declare class UndoStack<E> {
|
|
|
7259
7379
|
};
|
|
7260
7380
|
}
|
|
7261
7381
|
|
|
7382
|
+
/** One `source:` entry that resolves to no declared table. */
|
|
7383
|
+
export declare interface UnresolvedSource {
|
|
7384
|
+
/** The procedure that declares it. */
|
|
7385
|
+
readonly procedure: string;
|
|
7386
|
+
/** The name as written. */
|
|
7387
|
+
readonly source: string;
|
|
7388
|
+
/** A declared table whose name is close — the rename case, usually. */
|
|
7389
|
+
readonly didYouMean: string | undefined;
|
|
7390
|
+
}
|
|
7391
|
+
|
|
7392
|
+
/**
|
|
7393
|
+
* Every `source:` across `procedures` that names no declared table.
|
|
7394
|
+
*
|
|
7395
|
+
* `declared` is the table-name set the boot already built. Procedures with no
|
|
7396
|
+
* `source` are skipped — reactivity is opt-in and its absence is not a defect.
|
|
7397
|
+
*/
|
|
7398
|
+
export declare const unresolvedSources: (procedures: ReadonlyArray<{
|
|
7399
|
+
readonly name: string;
|
|
7400
|
+
readonly source: string | ReadonlyArray<string> | undefined;
|
|
7401
|
+
}>, declared: ReadonlySet<string>) => ReadonlyArray<UnresolvedSource>;
|
|
7402
|
+
|
|
7262
7403
|
export declare class UpdateBuilder {
|
|
7263
7404
|
private readonly backend;
|
|
7264
7405
|
private readonly scope;
|
|
@@ -7609,6 +7750,17 @@ export declare interface WorkflowFacadeOptions {
|
|
|
7609
7750
|
* Inspect HTTP + the `voltro workflows retry` CLI delegate to the same
|
|
7610
7751
|
* implementation. */
|
|
7611
7752
|
readonly retry?: (runId: string, options: WorkflowRetryOptions | undefined) => Promise<WorkflowRetryResult>;
|
|
7753
|
+
/** Backs {@link WorkflowsAppContext.redrive}. Supplied by the CLI's facade
|
|
7754
|
+
* builder, which owns the runs/steps store query + the cluster journal
|
|
7755
|
+
* adapter. Inspect HTTP + `voltro workflows redrive` delegate to the same
|
|
7756
|
+
* implementation. */
|
|
7757
|
+
readonly redrive?: (runId: string) => Promise<WorkflowRedriveResult>;
|
|
7758
|
+
/** Fired (fire-and-forget) right after a workflow is ENQUEUED — a start,
|
|
7759
|
+
* child, or run. The CLI wires this to a cross-replica "wake" broadcast so
|
|
7760
|
+
* the replica that OWNS the new run's shard polls immediately instead of
|
|
7761
|
+
* waiting for its next storage tick. No-op when unset (single replica / no
|
|
7762
|
+
* broker → the poll interval covers it). */
|
|
7763
|
+
readonly onEnqueue?: () => void;
|
|
7612
7764
|
}
|
|
7613
7765
|
|
|
7614
7766
|
export declare interface WorkflowLayerExecutionContext {
|
|
@@ -7649,6 +7801,16 @@ export declare class WorkflowPayloadError extends Error {
|
|
|
7649
7801
|
missingFields: ReadonlyArray<string>, detail: string);
|
|
7650
7802
|
}
|
|
7651
7803
|
|
|
7804
|
+
/** Result of {@link WorkflowsAppContext.redrive} — whether the failed run's
|
|
7805
|
+
* durable journal was re-driven, how many failed step attempts were reset so
|
|
7806
|
+
* they re-execute, and a `reason` when it declined (no journal / still
|
|
7807
|
+
* running / already succeeded). */
|
|
7808
|
+
export declare interface WorkflowRedriveResult {
|
|
7809
|
+
readonly redriven: boolean;
|
|
7810
|
+
readonly activitiesReset: number;
|
|
7811
|
+
readonly reason: string | null;
|
|
7812
|
+
}
|
|
7813
|
+
|
|
7652
7814
|
/** Options for {@link WorkflowsAppContext.retry}. */
|
|
7653
7815
|
export declare interface WorkflowRetryOptions {
|
|
7654
7816
|
/** Re-run the workflow against this payload instead of the original
|
|
@@ -7670,6 +7832,11 @@ export declare interface WorkflowRunListFilter {
|
|
|
7670
7832
|
readonly workflowName?: string;
|
|
7671
7833
|
readonly tag?: string;
|
|
7672
7834
|
readonly status?: WorkflowRunRecordStatus;
|
|
7835
|
+
/** The DEAD-LETTER view: failed runs an operator has NOT yet discarded
|
|
7836
|
+
* (`status = 'failed' AND discardedAt IS NULL`). Since the framework applies no
|
|
7837
|
+
* retry, a `failed` run is terminal — this is the queue of unhandled failures.
|
|
7838
|
+
* Combines with the other filters (e.g. by `workflowName`). */
|
|
7839
|
+
readonly deadLettered?: boolean;
|
|
7673
7840
|
readonly limit?: number;
|
|
7674
7841
|
readonly offset?: number;
|
|
7675
7842
|
}
|
|
@@ -7727,6 +7894,9 @@ export declare interface WorkflowRunSummary<Payload = unknown, Output = unknown>
|
|
|
7727
7894
|
readonly durationMs: number | null;
|
|
7728
7895
|
readonly traceId: string | null;
|
|
7729
7896
|
readonly parentExecutionId: string | null;
|
|
7897
|
+
/** When set, an operator has acknowledged this (failed) run — it is off the
|
|
7898
|
+
* dead-letter view. Null = unacknowledged. See `WorkflowRunListFilter.deadLettered`. */
|
|
7899
|
+
readonly discardedAt: Date | null;
|
|
7730
7900
|
}
|
|
7731
7901
|
|
|
7732
7902
|
export declare interface WorkflowsAppContext {
|
|
@@ -7752,6 +7922,14 @@ export declare interface WorkflowsAppContext {
|
|
|
7752
7922
|
* workflow by tag and re-executes it; pass `payloadOverride` to replay
|
|
7753
7923
|
* against a corrected input. */
|
|
7754
7924
|
retry(runId: string, options?: WorkflowRetryOptions): Promise<WorkflowRetryResult>;
|
|
7925
|
+
/** Re-drive a terminally-`failed` run from the step it died on, reusing
|
|
7926
|
+
* its durable journal — the counterpart to {@link retry} (fresh execution,
|
|
7927
|
+
* empty journal) and to {@link resume} (which only re-drives a *suspended*
|
|
7928
|
+
* run). Addressed by run id (`wfrun_…`). Completed steps replay from the
|
|
7929
|
+
* journal; only the failed steps re-execute. Fix the downstream cause
|
|
7930
|
+
* first, then redrive. Refuses a run that is not a not-yet-discarded
|
|
7931
|
+
* failure. */
|
|
7932
|
+
redrive(runId: string): Promise<WorkflowRedriveResult>;
|
|
7755
7933
|
}
|
|
7756
7934
|
|
|
7757
7935
|
export declare interface WorkflowSignalTarget {
|