@voltro/runtime 0.85.0 → 0.86.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.
Files changed (160) hide show
  1. package/CHANGELOG.md +666 -0
  2. package/THIRD-PARTY-NOTICES.md +5 -33
  3. package/dist/aggregate.js +42 -0
  4. package/dist/aggregateExtrema.js +80 -0
  5. package/dist/aggregateIvm.js +115 -0
  6. package/dist/analyticsMirror.js +228 -0
  7. package/dist/analyticsSink.js +38 -0
  8. package/dist/apiKeyUsage.js +33 -0
  9. package/dist/apiKeys.js +223 -0
  10. package/dist/apiKeysWiring.js +114 -0
  11. package/dist/approvalBuiltins.js +10 -0
  12. package/dist/approvals.js +269 -0
  13. package/dist/authMiddlewareLayer.js +47 -0
  14. package/dist/bindEvent.js +72 -0
  15. package/dist/bootListener.js +76 -0
  16. package/dist/bootRefusal.js +7 -0
  17. package/dist/bootStoreCodec.js +70 -0
  18. package/dist/businessRules.js +67 -0
  19. package/dist/changeClaimsTable.js +12 -0
  20. package/dist/circuitBreaker.js +72 -0
  21. package/dist/columnOnUpdate.js +23 -0
  22. package/dist/composeAnalytics.js +108 -0
  23. package/dist/computedQuery.js +4 -0
  24. package/dist/connectionBuiltins.js +81 -0
  25. package/dist/connectionCredentials.js +66 -0
  26. package/dist/connectionState.js +34 -0
  27. package/dist/connectionVault.js +410 -0
  28. package/dist/connectionsFacade.js +54 -0
  29. package/dist/coordinatedSchedule.js +139 -0
  30. package/dist/crdtHistory.js +138 -0
  31. package/dist/crdtMerge.js +179 -0
  32. package/dist/crdtVersionsTable.js +25 -0
  33. package/dist/credentialBound.js +12 -0
  34. package/dist/crud.js +72 -0
  35. package/dist/dataStore.js +679 -0
  36. package/dist/dataloader.js +61 -0
  37. package/dist/declaredTablesGuard.js +48 -0
  38. package/dist/defineExecutor.js +4 -0
  39. package/dist/dependencyGraph.js +32 -0
  40. package/dist/devices.js +228 -0
  41. package/dist/devicesWiring.js +168 -0
  42. package/dist/dialectReplicationAdapter.js +29 -0
  43. package/dist/dispatcher.js +447 -0
  44. package/dist/effectStore.js +71 -0
  45. package/dist/eventBus.js +260 -0
  46. package/dist/eventPublish.js +49 -0
  47. package/dist/events.js +193 -0
  48. package/dist/expectation.js +52 -0
  49. package/dist/expectationEvaluator.js +308 -0
  50. package/dist/experiment.js +96 -0
  51. package/dist/experimentEvaluator.js +245 -0
  52. package/dist/externalizedTasks.js +25 -0
  53. package/dist/fieldCipher.js +61 -0
  54. package/dist/fingerprint.js +21 -0
  55. package/dist/finops.js +42 -0
  56. package/dist/finopsAccountant.js +216 -0
  57. package/dist/handlerBody.js +8 -0
  58. package/dist/healthProbes.js +55 -0
  59. package/dist/httpClientSsrf.js +49 -0
  60. package/dist/httpClientTracing.js +11 -0
  61. package/dist/httpCompression.js +76 -0
  62. package/dist/httpKeepAlive.js +12 -0
  63. package/dist/httpRouteMounts.js +47 -0
  64. package/dist/idempotencyStore.js +70 -0
  65. package/dist/idleDetector.js +21 -0
  66. package/dist/index.d.ts +88 -69
  67. package/dist/index.js +144 -15292
  68. package/dist/insertRequiredColumns.js +19 -0
  69. package/dist/inspectWorkflow.js +1 -0
  70. package/dist/instanceMembership.js +98 -0
  71. package/dist/ivm.js +125 -0
  72. package/dist/kvDataStore.js +86 -0
  73. package/dist/kvFacade.js +32 -0
  74. package/dist/matcher.js +304 -0
  75. package/dist/memoryColumnDefaults.js +11 -0
  76. package/dist/memoryQueryJoins.js +114 -0
  77. package/dist/metrics.js +161 -0
  78. package/dist/observability.js +123 -0
  79. package/dist/observedGraph.js +52 -0
  80. package/dist/originGuard.js +116 -0
  81. package/dist/outbox.js +352 -0
  82. package/dist/pluginRefRules.js +65 -0
  83. package/dist/policyGuardResolver.js +55 -0
  84. package/dist/preview.js +62 -0
  85. package/dist/projectedRows.js +12 -0
  86. package/dist/provenance.js +63 -0
  87. package/dist/rangeIndex.js +89 -0
  88. package/dist/rawReadScope.js +17 -0
  89. package/dist/reaction.js +85 -0
  90. package/dist/reactiveConfig.js +11 -0
  91. package/dist/readClassification.js +57 -0
  92. package/dist/readRecorder.js +66 -0
  93. package/dist/rebac.js +104 -0
  94. package/dist/rebacInterceptor.js +81 -0
  95. package/dist/registry.js +29 -0
  96. package/dist/relevantFields.js +65 -0
  97. package/dist/replicaHealth.js +42 -0
  98. package/dist/replicaLocality.js +44 -0
  99. package/dist/replicaObservations.js +121 -0
  100. package/dist/replicaSelection.js +32 -0
  101. package/dist/replicatedDataStore.js +239 -0
  102. package/dist/requestLoader.js +7 -0
  103. package/dist/resumeCensus.js +28 -0
  104. package/dist/revalidation.js +22 -0
  105. package/dist/rowFilter.js +65 -0
  106. package/dist/rowStateBytes.js +45 -0
  107. package/dist/rpcBodyCap.js +63 -0
  108. package/dist/rpcConnections.js +83 -0
  109. package/dist/rpcDefectDiagnostics.js +15 -0
  110. package/dist/rpcServer.js +992 -0
  111. package/dist/runAsSystem.js +23 -0
  112. package/dist/rywPositionStore.js +84 -0
  113. package/dist/schedule.js +23 -0
  114. package/dist/scheduleBackfillRequests.js +151 -0
  115. package/dist/scheduleClaimsTable.js +11 -0
  116. package/dist/scheduleRunsTable.js +37 -0
  117. package/dist/scheduler.js +835 -0
  118. package/dist/schemaRegistry.js +65 -0
  119. package/dist/secretsResolver.js +35 -0
  120. package/dist/securityHeaders.js +33 -0
  121. package/dist/servePipeline.js +305 -0
  122. package/dist/serverOnlyAudit.js +66 -0
  123. package/dist/serverTracer.js +15 -0
  124. package/dist/serviceCredentials.js +40 -0
  125. package/dist/singleNodeOrchestrator.js +91 -0
  126. package/dist/sourceResolution.js +42 -0
  127. package/dist/storeMiddleware.js +880 -0
  128. package/dist/storeQuery.js +204 -0
  129. package/dist/storeRead.js +10 -0
  130. package/dist/subscriber.js +7 -0
  131. package/dist/subscriptionOutbox.js +139 -0
  132. package/dist/subscriptionRegistry.js +82 -0
  133. package/dist/subscriptionResume.js +132 -0
  134. package/dist/subscriptionSocketMetrics.js +65 -0
  135. package/dist/systemSubject.js +5 -0
  136. package/dist/tenantScope.js +23 -0
  137. package/dist/tenantSelection.js +27 -0
  138. package/dist/timeline.js +105 -0
  139. package/dist/traceContext.js +5 -0
  140. package/dist/traceSink.js +63 -0
  141. package/dist/traceSpansTable.js +29 -0
  142. package/dist/traceparent.js +34 -0
  143. package/dist/transactionSubject.js +12 -0
  144. package/dist/trustedProxy.js +155 -0
  145. package/dist/undo.js +129 -0
  146. package/dist/undoBuiltins.js +69 -0
  147. package/dist/undoCapture.js +74 -0
  148. package/dist/undoLogTable.js +16 -0
  149. package/dist/upsertBinding.js +20 -0
  150. package/dist/wakeOrchestrator.js +34 -0
  151. package/dist/wakeRegistry.js +71 -0
  152. package/dist/wakeRouter.js +63 -0
  153. package/dist/wakeSupervisor.js +62 -0
  154. package/dist/wakeupsTable.js +12 -0
  155. package/dist/webSocketGatewayRuntime.js +114 -0
  156. package/dist/webhookVerification.js +18 -0
  157. package/dist/wireColumnStrip.js +84 -0
  158. package/dist/workflowReadBuiltins.js +87 -0
  159. package/dist/workflowRuntime.js +708 -0
  160. package/package.json +7 -7
@@ -0,0 +1,21 @@
1
+ import { nextWakeup as e } from "./wakeRegistry.js";
2
+ //#region src/idleDetector.ts
3
+ var t = async (t) => {
4
+ if (t.signals.connectedClients() > 0 || t.signals.inFlightRequests() > 0 || t.signals.activeWorkflowExecutions() > 0) return !1;
5
+ let n = (t.now?.() ?? /* @__PURE__ */ new Date()).getTime() + (t.wakeLookaheadMs ?? 3e4), r = await e(t.store);
6
+ return !(r !== null && r.wakeAt.getTime() <= n);
7
+ }, n = (e, t = Date.now) => {
8
+ let n;
9
+ return {
10
+ update: (r) => {
11
+ if (!r) return n = void 0, !1;
12
+ let i = t();
13
+ return n === void 0 && (n = i), i - n >= e;
14
+ },
15
+ reset: () => {
16
+ n = void 0;
17
+ }
18
+ };
19
+ };
20
+ //#endregion
21
+ export { t as isIdleNow, n as makeIdleGate };
package/dist/index.d.ts CHANGED
@@ -30,6 +30,7 @@ import { DialectReplicationAdapter } from '@voltro/database';
30
30
  import { Duplex } from 'node:stream';
31
31
  import { Duration } from 'effect';
32
32
  import { Effect } from 'effect';
33
+ import { EnqueueOptions } from '@voltro/protocol';
33
34
  import { EventDeliverySemantics } from '@voltro/protocol';
34
35
  import { EventDescriptor } from '@voltro/protocol';
35
36
  import { EventKeyInvalid } from '@voltro/protocol';
@@ -71,6 +72,9 @@ import { MetricReader } from '@opentelemetry/sdk-metrics';
71
72
  import { MutationIdempotencyScope } from '@voltro/protocol';
72
73
  import { NoRowFound } from '@voltro/protocol';
73
74
  import { NoSuchResource } from '@voltro/protocol';
75
+ import { OutboxEnqueueFacade } from '@voltro/protocol';
76
+ import { OutboxHandlerContext } from '@voltro/protocol';
77
+ import { OutboxHandlerDefinition } from '@voltro/protocol';
74
78
  import { PendingApproval } from '@voltro/protocol';
75
79
  import { PluginHttpRoute } from '@voltro/protocol';
76
80
  import { PluginHttpRouteByteStream } from '@voltro/protocol';
@@ -82,6 +86,7 @@ import { Query } from '@voltro/database';
82
86
  import { QueryDescriptor } from '@voltro/database';
83
87
  import { Redis } from 'ioredis';
84
88
  import { registerRetention } from '@voltro/database';
89
+ import { RequestedTenantRefused } from '@voltro/protocol';
85
90
  import { ResourceMissing } from '@voltro/protocol';
86
91
  import { RestRouteDescriptor } from '@voltro/protocol/rest';
87
92
  import { RetentionConflict } from '@voltro/database';
@@ -167,6 +172,7 @@ declare const AccessDenied_base: Schema.Class<AccessDenied, Schema.TaggedStruct<
167
172
  export declare const actingUserId: (subject: StoreMiddlewareContext["subject"]) => string | null;
168
173
 
169
174
  export declare interface ActionRunnerDeps {
175
+ readonly outcomeOutbox?: MutationRunnerDeps['outcomeOutbox'];
170
176
  /** Build the per-call `AppContext` — NO transactional tx (actions do
171
177
  * external I/O and run outside a txn deliberately). */
172
178
  readonly buildContext: (request: ServeRequestContext) => {
@@ -500,15 +506,9 @@ export declare interface AggregateReadOptions {
500
506
  * - Interval string (`'5m'`, `'1h'`, `'24h'`): the canonical
501
507
  * simple case.
502
508
  * - Cron expression `{ cron: '0 2 * * *', timezone: 'UTC' }`: for
503
- * business-hours / off-peak refresh windows. **Defer to v2** —
504
- * emitted from the spec but not yet honoured by the runner.
509
+ * business-hours / off-peak refresh windows, in the specified timezone.
505
510
  * - On-change debounced `{ interval: '1h', onChange: ['matches'],
506
- * debounceMs: 30_000 }`: near-realtime, source-table-driven. **Defer
507
- * to v2** — same status.
508
- *
509
- * v1 supports interval only. The other shapes are accepted so consumers
510
- * can declare them today + see them honoured when v2 lands without
511
- * touching the spec.
511
+ * debounceMs: 30_000 }`: near-realtime, source-table-driven.
512
512
  */
513
513
  export declare type AggregateRefreshPolicy = string | {
514
514
  readonly interval?: string;
@@ -1112,7 +1112,9 @@ export declare interface AppAccess {
1112
1112
  readonly hasAny: (scopes: ReadonlyArray<string>) => boolean;
1113
1113
  /** Effect guard — fails with a typed `ScopeError` when the caller lacks
1114
1114
  * `scope`. `yield* ctx.access.require('notes:write')` at the top of a
1115
- * handler mirrors a descriptor `guards:` entry for in-body branching. */
1115
+ * handler mirrors a descriptor `guards:` entry for in-body branching.
1116
+ * Reauthenticates bound authority when the Effect executes. Authentication
1117
+ * or authority-source failures abort rather than becoming scope decisions. */
1116
1118
  readonly require: (scope: string) => Effect.Effect<void, ScopeError>;
1117
1119
  /** The caller's effective scope set (live — reflects rbac resolution). */
1118
1120
  readonly scopes: ReadonlyArray<string>;
@@ -1798,7 +1800,7 @@ export declare const bindConnectionCredential: (clientId: number, credential: Co
1798
1800
  * interrupt registry subscriptions use — so one reconnect policy, one devtools
1799
1801
  * view, one connection lifecycle for both primitives.
1800
1802
  */
1801
- export declare const bindEvent: (input: BindEventInput) => Stream.Stream<EventStreamEvent<unknown>, ScopeError | Unauthenticated | NoSuchResource, SubjectService | ConnectionInfo>;
1803
+ export declare const bindEvent: (input: BindEventInput) => Stream.Stream<EventStreamEvent<unknown>, ScopeError | Unauthenticated | RequestedTenantRefused | NoSuchResource, SubjectService | ConnectionInfo>;
1802
1804
 
1803
1805
  export declare interface BindEventInput {
1804
1806
  readonly bus: EventBus;
@@ -2130,6 +2132,8 @@ export declare const canonicalInputKey: (input: unknown) => string;
2130
2132
  * Canonical JSON — object keys sorted recursively, so two structurally equal
2131
2133
  * inputs that differ only in key order digest identically. A retry from a
2132
2134
  * different client build must not mint a second approval.
2135
+ * Preserve own JSON fields such as `__proto__`; a prototype setter must never
2136
+ * discard part of the content that an approval identity binds.
2133
2137
  */
2134
2138
  export declare const canonicalJson: (value: unknown) => string;
2135
2139
 
@@ -4056,6 +4060,9 @@ export declare class Dispatcher {
4056
4060
  * grant comes back.
4057
4061
  */
4058
4062
  private revokeSubscription;
4063
+ /** Remove once, including a pending retry and its active metric. In-flight
4064
+ * async reads check identity again before emitting or scheduling anything. */
4065
+ private removeComputed;
4059
4066
  /** `revokeSubscription` for the computed path. Same reasoning. */
4060
4067
  private revokeComputed;
4061
4068
  /**
@@ -4066,13 +4073,10 @@ export declare class Dispatcher {
4066
4073
  * Returns an `unsubscribe` the caller MUST invoke on teardown.
4067
4074
  */
4068
4075
  subscribeComputed(computed: ComputedQuery, emit: (event: SubscriptionEvent<unknown>) => void, context: RuntimeContext, label?: string): Promise<() => void>;
4069
- /** Re-run a computed subscription's handler; emit a fresh snapshot iff
4070
- * the value changed. A computed value is an arbitrary payload (a scalar,
4071
- * an aggregate object, a non-id-keyed array) — it has no id-keyed row
4072
- * set to diff, so an update ships the full value as a `snapshot`, which
4073
- * the client materialises as a wholesale base replacement. Best-effort:
4074
- * a recompute throw is logged, not propagated, so one failing query
4075
- * can't wedge the change stream. */
4076
+ /** Serialize computed reads. A source change during a pending read asks for
4077
+ * one more read before delivery; an older result cannot overtake newer data.
4078
+ * Ordinary failures are visible in-band and retry independently of changes.
4079
+ * Access withdrawal stays terminal and never enters the retry lane. */
4076
4080
  private recomputeComputed;
4077
4081
  /** Live snapshot of all fingerprinted subscriptions — used by the
4078
4082
  * `/_voltro/inspect/subscriptions` endpoint and DevTools. */
@@ -4235,7 +4239,7 @@ export declare interface DrainResult {
4235
4239
  readonly dead: number;
4236
4240
  readonly skipped: number;
4237
4241
  /**
4238
- * How many rows this pass had to look at. `0` means the queue is EMPTY, which
4242
+ * How many rows this pass had to look at. `0` means the eligible queue is EMPTY, which
4239
4243
  * a caller cannot otherwise tell from "everything delivered" — both leave
4240
4244
  * `delivered` at whatever it was, and only one of them means the poll has
4241
4245
  * nothing left to do.
@@ -4429,14 +4433,7 @@ export declare const enforceApproval: (input: {
4429
4433
  readonly traceId?: string | undefined;
4430
4434
  }) => Promise<void>;
4431
4435
 
4432
- export declare interface EnqueueOptions {
4433
- /** Drop this enqueue if an undelivered row already carries the same key. */
4434
- readonly idempotencyKey?: string;
4435
- /** Override the handler's `maxAttempts` for this one effect. */
4436
- readonly maxAttempts?: number;
4437
- /** Delay the first attempt (ms from now). */
4438
- readonly delayMs?: number;
4439
- }
4436
+ export { EnqueueOptions }
4440
4437
 
4441
4438
  /**
4442
4439
  * Attribute the reads that FOLLOW, in this execution context, to `procedure`.
@@ -6728,9 +6725,9 @@ export declare const makeAsyncKv: ({ store, env }: KvFacadeOptions) => Promise<K
6728
6725
  * (a no-op for a connection that never re-authenticated);
6729
6726
  * 2. run the FULL chain on the result — strategies, session revocation,
6730
6727
  * `resolveScopes`, the scope cache;
6731
- * 3. record what the strategy verified about the credential's lifetime, so
6732
- * `ConnectionInfo.credentialExpiresAt` reports the token's own `exp` rather
6733
- * than a cookie derivation that only ever saw one auth shape.
6728
+ * 3. pass the verified lifetime through call-local Effect context, so parallel
6729
+ * calls and explicitly unbounded credentials cannot inherit another call's
6730
+ * expiry. Compose the group's pair with `withRpcAuthMiddleware`.
6734
6731
  *
6735
6732
  * Step 1 is the only thing a re-authenticated connection gets. It changes WHICH
6736
6733
  * credential is presented and nothing about how it is judged.
@@ -6965,16 +6962,13 @@ export declare const makeQueryFinalizer: (options: QueryFinalizeOptions) => ((de
6965
6962
  * the socket keeps delivering rows the subject may no longer read until
6966
6963
  * the client happens to disconnect.
6967
6964
  *
6968
- * So every delivery re-runs the same `checkGuardsEffect` the subscribe-time
6969
- * gate ran — including the async resource-scope resolver, which is where a
6970
- * per-row/per-team revocation actually shows up. Returns `null` when the
6971
- * subject still passes, or the typed `ScopeError` that denied it.
6972
- *
6973
- * Returns a closure that always resolves `null` when the descriptor carries
6974
- * no guards AND default-deny is off, so the caller needs no branch and an
6975
- * unguarded query pays only a resolved promise. Under `defaultDeny` an
6976
- * UNDECIDED descriptor resolves the same typed refusal on every delivery —
6977
- * fail closed on the re-check too, not only at subscribe.
6965
+ * Each delivery refreshes the original authentication chain, subject guards,
6966
+ * dynamic role grants, and resource guards before emitting data. Configured
6967
+ * authentication/scope caches retain their documented invalidation windows.
6968
+ * A revoked credential or grant ends the subscription with a typed refusal.
6969
+ * The original principal and tenant remain fixed; changing either requires a
6970
+ * new subscription. Even unguarded descriptors refresh authentication, while
6971
+ * default-deny keeps rejecting descriptors without a declared access policy.
6978
6972
  */
6979
6973
  export declare const makeQueryReauthorizer: (query: MutationLike, input: unknown, defaultDeny?: boolean) => (subject: Subject) => () => Promise<unknown>;
6980
6974
 
@@ -7462,6 +7456,10 @@ export declare interface MutationOutputCodec<Output> {
7462
7456
  }
7463
7457
 
7464
7458
  export declare interface MutationRunnerDeps {
7459
+ readonly outcomeOutbox?: (request: ServeRequestContext) => OutboxEnqueueFacade | undefined;
7460
+ /** A failure aborts the business transaction; transient contention replays
7461
+ * executor and hook together. Never perform external I/O here. */
7462
+ readonly beforeMutationCommit?: ServeMutationCommitHook | undefined;
7465
7463
  readonly store: TransactionalStore;
7466
7464
  /** Select and provision the request's physical store before opening its
7467
7465
  * transaction. Resolved once, including across retries and post-commit folds. */
@@ -7484,6 +7482,7 @@ export declare interface MutationRunnerDeps {
7484
7482
  /** Build the per-call `AppContext` bound to the transactional `tx`. */
7485
7483
  readonly buildContext: (request: ServeRequestContext, tx: unknown, lifecycle?: MutationLifecycle) => {
7486
7484
  readonly store: unknown;
7485
+ readonly outbox?: OutboxEnqueueFacade;
7487
7486
  };
7488
7487
  /** Run an Effect-returning executor with the per-request store layer. */
7489
7488
  readonly runEffect: (effect: Effect.Effect<unknown, unknown, never>, store: unknown) => Promise<unknown>;
@@ -8013,14 +8012,14 @@ export declare const outboxAttemptsTable: TableLike;
8013
8012
  * twice because of us or because of them" becomes unanswerable. */
8014
8013
  export declare type OutboxAttemptTrigger = 'automatic' | 'manual';
8015
8014
 
8016
- export declare interface OutboxFacade {
8017
- /**
8018
- * Persist the intent to run `effect` after this transaction commits.
8019
- *
8020
- * Returns the outbox row id, which doubles as the delivery id a client can
8021
- * watch to render external-side-effect progress ("saving… syncing… synced").
8022
- */
8023
- enqueue(effect: string, payload: Record<string, unknown>, options?: EnqueueOptions): Promise<string>;
8015
+ /** Declared-event publication for an outbox attempt, scoped to its queued tenant.
8016
+ * This service provides no request subject or store access. */
8017
+ export declare class OutboxEventPublisher extends OutboxEventPublisher_base {
8018
+ }
8019
+
8020
+ declare const OutboxEventPublisher_base: Context.ServiceClass<OutboxEventPublisher, "@voltro/OutboxEventPublisher", EventPublisher>;
8021
+
8022
+ export declare interface OutboxFacade extends OutboxEnqueueFacade {
8024
8023
  /**
8025
8024
  * Re-arm one entry for delivery NOW, on purpose, attributed to the caller.
8026
8025
  *
@@ -8065,25 +8064,9 @@ export declare interface OutboxFacadeDeps {
8065
8064
  readonly now?: () => Date;
8066
8065
  }
8067
8066
 
8068
- export declare interface OutboxHandlerContext {
8069
- /** Stable identity of this delivery intent, preserved across retries. */
8070
- readonly outboxId: string;
8071
- /** Caller-supplied enqueue key, or null. Unlike outboxId this can be reused
8072
- * for a distinct intent after an earlier intent has been delivered. */
8073
- readonly idempotencyKey: string | null;
8074
- readonly payload: Record<string, unknown>;
8075
- readonly attempt: number;
8076
- readonly subjectId: string | null;
8077
- readonly tenantId: string | null;
8078
- readonly traceId: string | null;
8079
- }
8067
+ export { OutboxHandlerContext }
8080
8068
 
8081
- export declare interface OutboxHandlerDefinition<R = never> {
8082
- readonly effect: string;
8083
- readonly handler: (ctx: OutboxHandlerContext) => Promise<unknown> | Effect.Effect<unknown, unknown, R>;
8084
- /** Give up after this many attempts, then dead-letter. Default 8. */
8085
- readonly maxAttempts?: number;
8086
- }
8069
+ export { OutboxHandlerDefinition }
8087
8070
 
8088
8071
  /** What the pump hands events to — the `emit` of a `Stream.asyncScoped`. */
8089
8072
  export declare interface OutboxSink<T> {
@@ -9720,7 +9703,9 @@ export declare interface ResolvedDevice {
9720
9703
  * Walk a query descriptor's eager-load tree and return the FULL set
9721
9704
  * of tables whose changes could affect the resolved snapshot.
9722
9705
  *
9723
- * - For a query without `eager`, returns just `{ descriptor.table }`.
9706
+ * - Includes explicitly declared invalidation sources (`dependentTables`),
9707
+ * independently of SQL predicates and projections.
9708
+ * - A plain query with no additional sources returns `{ descriptor.table }`.
9724
9709
  * - For each one()/many() relation, adds the target table.
9725
9710
  * - For each manyToMany() relation, adds the through-table AND the
9726
9711
  * target table (writes to either can shift the resolved set).
@@ -11511,6 +11496,16 @@ export declare type SerializedGuard = {
11511
11496
  */
11512
11497
  export declare const serializeGuards: (guards: ReadonlyArray<AnyGuardSpec>, input: unknown) => ReadonlyArray<SerializedGuard>;
11513
11498
 
11499
+ /** Required plugin work executed on the actual transaction connection. */
11500
+ export declare type ServeMutationCommitHook = (context: Parameters<ServeRpcInterceptor>[1] & {
11501
+ readonly kind: 'mutation';
11502
+ readonly store: DataStore;
11503
+ readonly output: unknown;
11504
+ readonly outbox?: OutboxEnqueueFacade;
11505
+ readonly startedAt?: number;
11506
+ readonly durationMs?: number;
11507
+ }) => Effect.Effect<void, unknown>;
11508
+
11514
11509
  export declare interface ServeRequestContext {
11515
11510
  readonly subject: unknown;
11516
11511
  readonly traceId: string;
@@ -11574,6 +11569,7 @@ export declare type ServeRpcInterceptor = (base: Effect.Effect<unknown, unknown,
11574
11569
  readonly input: unknown;
11575
11570
  readonly subject: unknown;
11576
11571
  readonly traceId: string;
11572
+ readonly enqueueOutcome?: OutboxEnqueueFacade['enqueue'];
11577
11573
  readonly spanId?: string;
11578
11574
  /**
11579
11575
  * The declared write-target TABLE names of this mutation/action, derived
@@ -13341,6 +13337,14 @@ export declare type WhereOp = '=' | '!=' | '<>' | '>' | '>=' | '<' | '<=' | 'in'
13341
13337
 
13342
13338
  export declare const withRowFilter: <R extends ServeRequestContext>(request: R) => Promise<R>;
13343
13339
 
13340
+ /**
13341
+ * Add the shared RPC auth pair once to an undecorated group. Authentication
13342
+ * runs before ConnectionInfo captures this call's verified credential lifetime.
13343
+ * Groups already carrying either middleware are refused: Effect preserves Set
13344
+ * insertion order, so appending a duplicate cannot repair an existing order.
13345
+ */
13346
+ export declare const withRpcAuthMiddleware: <R extends Rpc.Any>(group: RpcGroup.RpcGroup<R>) => RpcGroup.RpcGroup<Rpc.AddMiddleware<Rpc.AddMiddleware<R, typeof ConnectionInfoMiddleware>, typeof AuthMiddleware>>;
13347
+
13344
13348
  /**
13345
13349
  * Run `body` against a request that carries a resolved row-filter scope.
13346
13350
  *
@@ -13511,6 +13515,15 @@ export declare interface WorkflowEventTriggerResult {
13511
13515
  readonly reason?: string;
13512
13516
  }
13513
13517
 
13518
+ export declare interface WorkflowExecutionRefusal {
13519
+ readonly workflowName: string;
13520
+ readonly executionId: string;
13521
+ readonly payload: unknown;
13522
+ readonly cause: Cause.Cause<unknown>;
13523
+ /** Restored identity and host-owned placement; no resolved permissions. */
13524
+ readonly callerContext: WorkflowCallerContext;
13525
+ }
13526
+
13514
13527
  export declare interface WorkflowFacadeOptions {
13515
13528
  readonly cancelQueued?: (input: WorkflowQueuedControlTarget & {
13516
13529
  readonly callerContext: WorkflowCallerContext | undefined;
@@ -13652,13 +13665,18 @@ export declare interface WorkflowLayerOptions<Context> {
13652
13665
  * whose business logic said no.
13653
13666
  */
13654
13667
  readonly resolveAuthority?: (identity: unknown) => Promise<unknown>;
13668
+ /** Record a known caller's refusal through the host's ordinary resident run
13669
+ * recorder. Called before application context/body construction. The failed
13670
+ * attempt stays failed, even if this hook completes successfully. Missing
13671
+ * caller identity cannot fabricate a tenant or a first-run record. */
13672
+ readonly onExecutionRefusal?: (refusal: WorkflowExecutionRefusal) => Effect.Effect<unknown, unknown, unknown>;
13655
13673
  /**
13656
13674
  * Told when an execution is refused BEFORE its body — the start context
13657
13675
  * could not be resolved, the identity was missing, the authority or row
13658
13676
  * filter resolver rejected. The engine already fails the attempt; what a
13659
- * discarded start does NOT do is tell anyone, and a run refused here has no
13660
- * row yet, so nothing else can. Measured: an acknowledged start whose
13661
- * resident context was filed under another id died with a debug-level
13677
+ * discarded start does NOT do is tell anyone. A first start may have no
13678
+ * row yet; a resumed run may still have a running row. Measured: an
13679
+ * acknowledged start whose resident context was filed under another id died with a debug-level
13662
13680
  * "Fiber terminated" and nothing else. Hosts wire this to an error log.
13663
13681
  */
13664
13682
  readonly reportExecutionRefusal?: (refusal: {
@@ -14037,6 +14055,7 @@ export declare interface WorkflowStartOptions {
14037
14055
  }
14038
14056
 
14039
14057
  export declare interface WorkflowStartRunnerDeps {
14058
+ readonly outcomeOutbox?: MutationRunnerDeps['outcomeOutbox'];
14040
14059
  /** Composed plugin workflow-interceptor (`interceptWorkflow`). */
14041
14060
  readonly interceptor?: ServeRpcInterceptor;
14042
14061
  readonly recordMetric?: MutationRunnerDeps['recordMetric'];