@dudousxd/nestjs-durable 0.51.0 → 0.52.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +166 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -38
- package/dist/index.d.ts +112 -38
- package/dist/index.js +71 -15
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { WorkflowCtx, SearchAttributes, WorkflowClass, StandardSchemaV1, StepError, OnErrorInput, WorkflowRef, SingletonConfig, ConcurrencyConfig, SearchAttributesSchema, StateStore, Transport, NamedTransport, ControlPlane, StalledRunInfo, ScheduledWorkflow, RetentionPolicy, QueueConfig, AdmissionBackend, WorkflowInputOf, StartOptions, RunResult, RunGateway, WorkflowEngine, EntityHandler, DurableTopology, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, GroupHealth, RunWaiting, EngineEvent,
|
|
2
|
-
export { AttributeFilter, DynamicWorkflowCtx, EngineEvent, InferSearchAttributes, OnErrorInput, RunDetail, RunGateway, RunListItem, RunQuery, RunSchedulesOptions, RunStatus, RunWaiting, ScheduledWorkflow, SearchAttributes, SearchAttributesSchema, StepCheckpoint, StepEvent, StepLogger, WorkflowCtx, WorkflowEngine, WorkflowHandler, WorkflowRun, prevCronFireMs, readSearchAttributes, runSchedules, scheduleRunIdAt, scheduledRunId } from '@dudousxd/nestjs-durable-core';
|
|
1
|
+
import { WorkflowCtx, SearchAttributes, WorkflowClass, StandardSchemaV1, StepError, OnErrorInput, WorkflowRef, SingletonConfig, ConcurrencyConfig, SearchAttributesSchema, StateStore, Transport, NamedTransport, ControlPlane, StalledRunInfo, ScheduledWorkflow, RetentionPolicy, QueueConfig, AdmissionBackend, TenantVerifier, TenantTokenSource, WorkflowInputOf, StartOptions, RunResult, RunGateway, WorkflowEngine, EntityHandler, RunRequest, RunReply, DurableTopology, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, GroupHealth, RunWaiting, EngineEvent, StepInvocation, WorkflowRun, TenantEvent } from '@dudousxd/nestjs-durable-core';
|
|
2
|
+
export { AttributeFilter, DynamicWorkflowCtx, EngineEvent, InferSearchAttributes, OnErrorInput, RunDetail, RunGateway, RunListItem, RunQuery, RunSchedulesOptions, RunStatus, RunWaiting, ScheduledWorkflow, SearchAttributes, SearchAttributesSchema, StepCheckpoint, StepEvent, StepLogger, TenantTokenSource, TenantVerifier, VerifiedTenant, WorkflowCtx, WorkflowEngine, WorkflowHandler, WorkflowRun, hmacTenantVerifier, prevCronFireMs, readSearchAttributes, runSchedules, scheduleRunIdAt, scheduledRunId, signTenantToken } from '@dudousxd/nestjs-durable-core';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { ConcurrencyOption, StartRunDeps, RunRedisWorkerOptions, RunningWorker, DurableWorkerRuntime } from '@dudousxd/durable-worker';
|
|
5
5
|
import { DynamicModule, InjectionToken, OnModuleInit, OnApplicationBootstrap, OnApplicationShutdown, Provider } from '@nestjs/common';
|
|
@@ -790,6 +790,26 @@ interface DurableModuleOptions {
|
|
|
790
790
|
/** Timeout for a thin worker's `RunGateway` round-trip over `transport` before it rejects. Defaults
|
|
791
791
|
* to 10_000ms inside `ProxyRunGateway`. Ignored for an operator (bound to `StoreRunGateway`). */
|
|
792
792
|
runGatewayTimeoutMs?: number;
|
|
793
|
+
/**
|
|
794
|
+
* **Operator.** Verifies the signed tenant token a store-less tenant worker presents in the wire
|
|
795
|
+
* `tenant` field of every run request and start-run message, and DERIVES the tenant from it,
|
|
796
|
+
* discarding the wire claim. An invalid, tampered or absent token is rejected: a run request gets
|
|
797
|
+
* an `unauthorized` error reply, a start-run creates nothing. Use `hmacTenantVerifier(secret)`
|
|
798
|
+
* (tokens minted by `signTenantToken`, wire-compatible with Agora) or supply your own.
|
|
799
|
+
*
|
|
800
|
+
* Optional. Absent: the wire `tenant` is trusted verbatim (prefix/network isolation only), exactly
|
|
801
|
+
* as before. An explicit `topology: { role: 'control-plane' }` without a verifier logs a warning
|
|
802
|
+
* at boot; a plain operator (`store` + `transport`, no `topology`) boots silently.
|
|
803
|
+
*/
|
|
804
|
+
verifyTenant?: TenantVerifier;
|
|
805
|
+
/**
|
|
806
|
+
* **Tenant worker.** The signed token (see `signTenantToken`) this worker presents, verbatim, in
|
|
807
|
+
* the `tenant` field of every run request (`ProxyRunGateway`) and start-run (`engine.start`), so
|
|
808
|
+
* an operator with {@link verifyTenant} can authenticate it. A string, or a function called before
|
|
809
|
+
* every request (rotation, short-lived tokens). Optional: absent, the raw {@link partition} is
|
|
810
|
+
* sent, as before. Live-tail stays keyed by the partition, never the token.
|
|
811
|
+
*/
|
|
812
|
+
tenantToken?: TenantTokenSource;
|
|
793
813
|
/**
|
|
794
814
|
* An explicit preset that NAMES the deployment role instead of leaving it to `store`/`connection`
|
|
795
815
|
* inference, and VALIDATES the axes that are otherwise only prose (see the interface doc's role
|
|
@@ -1078,6 +1098,77 @@ declare class InAppWorkerBootstrap implements OnModuleInit, OnApplicationBootstr
|
|
|
1078
1098
|
*/
|
|
1079
1099
|
declare function inAppWorkerProviders(): Provider[];
|
|
1080
1100
|
|
|
1101
|
+
/**
|
|
1102
|
+
* The error `code` a responder replies with for a verb it does not know, so a newer tenant can tell
|
|
1103
|
+
* "this control plane is older than me" from a real failure and fall back at once. Earlier
|
|
1104
|
+
* responders reply `ok: true` with no `data` instead; `ProxyRunGateway` reads both.
|
|
1105
|
+
*/
|
|
1106
|
+
declare const UNSUPPORTED_RUN_REQUEST = "unsupported-request";
|
|
1107
|
+
/** The narrow slice of `Transport` the responder needs — a tenant's read/control request in, a
|
|
1108
|
+
* correlated reply out. Both are OPTIONAL on the full `Transport` interface (only broker
|
|
1109
|
+
* transports carry the run-request/reply protocol); the caller capability-checks before wiring
|
|
1110
|
+
* this up (see `durable.module.ts`). */
|
|
1111
|
+
interface RunRequestTransport {
|
|
1112
|
+
onRunRequest(handler: (msg: RunRequest) => Promise<void>): void;
|
|
1113
|
+
publishRunReply(reply: RunReply): Promise<void>;
|
|
1114
|
+
}
|
|
1115
|
+
/** Options for {@link RunRequestResponder}. */
|
|
1116
|
+
interface RunRequestResponderOptions {
|
|
1117
|
+
/**
|
|
1118
|
+
* Verifies the signed token a tenant presents in the wire `tenant` field and DERIVES the real
|
|
1119
|
+
* tenant from it (see `hmacTenantVerifier`). Present: an invalid, tampered or absent token is
|
|
1120
|
+
* rejected with an `unauthorized` error reply and the wire claim is discarded in favour of the
|
|
1121
|
+
* verified tenant. Absent: the wire `tenant` is trusted verbatim (prefix/network isolation only).
|
|
1122
|
+
*/
|
|
1123
|
+
verifyTenant?: TenantVerifier | undefined;
|
|
1124
|
+
/**
|
|
1125
|
+
* Warn at construction when no {@link verifyTenant} is set. Default `true`. `DurableModule` passes
|
|
1126
|
+
* `true` only for an explicit `topology: { role: 'control-plane' }`, so a plain operator app that
|
|
1127
|
+
* never serves tenants boots silently.
|
|
1128
|
+
*/
|
|
1129
|
+
warnWithoutVerifier?: boolean | undefined;
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Operator-side consumer of a tenant's {@link RunRequest}s: answers each one against a
|
|
1133
|
+
* `RunGateway`, enforcing the tenant boundary before touching the run. For every runId-bearing
|
|
1134
|
+
* verb it loads the run via `getRunDetail` FIRST and compares `run.namespace` to the requesting
|
|
1135
|
+
* `msg.tenant` — a mismatch short-circuits into a `cross-tenant` error reply WITHOUT calling the
|
|
1136
|
+
* verb, so a tenant can never read or act on another tenant's run. `listRuns` is scoped by
|
|
1137
|
+
* overwriting the query's `namespace` with the requester's tenant, ignoring whatever the client
|
|
1138
|
+
* sent. This is the security boundary of the tenant run gateway — do not weaken it.
|
|
1139
|
+
*
|
|
1140
|
+
* With {@link RunRequestResponderOptions.verifyTenant}, every request is authenticated FIRST: the
|
|
1141
|
+
* wire `tenant` is treated as a signed token, the real tenant is derived from it, and an invalid
|
|
1142
|
+
* token short-circuits into an `unauthorized` error reply before any run is touched. A verifier
|
|
1143
|
+
* that throws becomes an error reply too.
|
|
1144
|
+
*/
|
|
1145
|
+
declare class RunRequestResponder {
|
|
1146
|
+
private readonly transport;
|
|
1147
|
+
private readonly gateway;
|
|
1148
|
+
private readonly verifyTenant;
|
|
1149
|
+
constructor(transport: RunRequestTransport, gateway: RunGateway, options?: RunRequestResponderOptions);
|
|
1150
|
+
/** Register the consumer on the transport. Each request is answered independently; a handler
|
|
1151
|
+
* failure never throws back into the transport (errors are captured into an error reply). */
|
|
1152
|
+
start(): void;
|
|
1153
|
+
private handle;
|
|
1154
|
+
private callVerb;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* An error reply from the control plane. `code` carries the reply's machine-readable reason when it
|
|
1159
|
+
* sent one — `unauthorized` (the tenant token was rejected), `cross-tenant` (the run belongs to
|
|
1160
|
+
* another tenant), ... — so a caller can tell an authentication failure from a real one, as with
|
|
1161
|
+
* Agora's proxy.
|
|
1162
|
+
*/
|
|
1163
|
+
/**
|
|
1164
|
+
* The codes the run-request responder replies with. `string & {}` keeps a code from a newer control
|
|
1165
|
+
* plane assignable without losing autocomplete on the known ones.
|
|
1166
|
+
*/
|
|
1167
|
+
type RunGatewayErrorCode = 'unauthorized' | 'cross-tenant' | typeof UNSUPPORTED_RUN_REQUEST | (string & {});
|
|
1168
|
+
declare class RunGatewayError extends Error {
|
|
1169
|
+
readonly code: RunGatewayErrorCode | undefined;
|
|
1170
|
+
constructor(message: string, code?: RunGatewayErrorCode);
|
|
1171
|
+
}
|
|
1081
1172
|
/**
|
|
1082
1173
|
* Tenant-side `RunGateway` — round-trips every verb as a `RunRequest`/`RunReply` pair over the
|
|
1083
1174
|
* transport, correlated by a minted `requestId`, and bridges `subscribe` onto the transport's
|
|
@@ -1091,15 +1182,32 @@ declare class ProxyRunGateway implements RunGateway {
|
|
|
1091
1182
|
private readonly tenant;
|
|
1092
1183
|
private readonly timeoutMs;
|
|
1093
1184
|
private readonly capabilityTtlMs;
|
|
1185
|
+
/**
|
|
1186
|
+
* The tenant's signed token (see `signTenantToken`), presented VERBATIM in the `tenant` field of
|
|
1187
|
+
* every {@link RunRequest} so an operator with `verifyTenant` can authenticate it and derive the
|
|
1188
|
+
* tenant from it. A function is called before every request (rotation / short-lived tokens).
|
|
1189
|
+
* Omit to send the raw `tenant` (prefix/network isolation only). The live-tail channel always
|
|
1190
|
+
* stays keyed by `tenant`, never the token: the operator republishes by the run's namespace.
|
|
1191
|
+
*/
|
|
1192
|
+
private readonly tenantToken?;
|
|
1094
1193
|
private readonly pending;
|
|
1095
1194
|
/** Verb → when to ask the control plane for it again, after it answered that it does not know it.
|
|
1096
1195
|
* During a rolling upgrade the control plane is several pods of mixed versions behind one queue,
|
|
1097
1196
|
* and a reply does not say which pod sent it, so the whole control plane is assumed old until the
|
|
1098
1197
|
* entry expires. */
|
|
1099
1198
|
private readonly unsupportedUntil;
|
|
1100
|
-
constructor(transport: Transport, tenant: string, timeoutMs?: number, capabilityTtlMs?: number
|
|
1199
|
+
constructor(transport: Transport, tenant: string, timeoutMs?: number, capabilityTtlMs?: number,
|
|
1200
|
+
/**
|
|
1201
|
+
* The tenant's signed token (see `signTenantToken`), presented VERBATIM in the `tenant` field of
|
|
1202
|
+
* every {@link RunRequest} so an operator with `verifyTenant` can authenticate it and derive the
|
|
1203
|
+
* tenant from it. A function is called before every request (rotation / short-lived tokens).
|
|
1204
|
+
* Omit to send the raw `tenant` (prefix/network isolation only). The live-tail channel always
|
|
1205
|
+
* stays keyed by `tenant`, never the token: the operator republishes by the run's namespace.
|
|
1206
|
+
*/
|
|
1207
|
+
tenantToken?: TenantTokenSource | undefined);
|
|
1101
1208
|
private handleReply;
|
|
1102
1209
|
private request;
|
|
1210
|
+
private send;
|
|
1103
1211
|
topology(): DurableTopology;
|
|
1104
1212
|
/**
|
|
1105
1213
|
* A control plane that predates {@link RunDetailOptions} ignores them and sends the full detail;
|
|
@@ -1151,40 +1259,6 @@ declare function isOperatorRole(options: DurableModuleOptions): boolean;
|
|
|
1151
1259
|
*/
|
|
1152
1260
|
declare function isDrivingOperator(options: DurableModuleOptions): boolean;
|
|
1153
1261
|
|
|
1154
|
-
/**
|
|
1155
|
-
* The error `code` a responder replies with for a verb it does not know, so a newer tenant can tell
|
|
1156
|
-
* "this control plane is older than me" from a real failure and fall back at once. Earlier
|
|
1157
|
-
* responders reply `ok: true` with no `data` instead; `ProxyRunGateway` reads both.
|
|
1158
|
-
*/
|
|
1159
|
-
declare const UNSUPPORTED_RUN_REQUEST = "unsupported-request";
|
|
1160
|
-
/** The narrow slice of `Transport` the responder needs — a tenant's read/control request in, a
|
|
1161
|
-
* correlated reply out. Both are OPTIONAL on the full `Transport` interface (only broker
|
|
1162
|
-
* transports carry the run-request/reply protocol); the caller capability-checks before wiring
|
|
1163
|
-
* this up (see `durable.module.ts`). */
|
|
1164
|
-
interface RunRequestTransport {
|
|
1165
|
-
onRunRequest(handler: (msg: RunRequest) => Promise<void>): void;
|
|
1166
|
-
publishRunReply(reply: RunReply): Promise<void>;
|
|
1167
|
-
}
|
|
1168
|
-
/**
|
|
1169
|
-
* Operator-side consumer of a tenant's {@link RunRequest}s: answers each one against a
|
|
1170
|
-
* `RunGateway`, enforcing the tenant boundary before touching the run. For every runId-bearing
|
|
1171
|
-
* verb it loads the run via `getRunDetail` FIRST and compares `run.namespace` to the requesting
|
|
1172
|
-
* `msg.tenant` — a mismatch short-circuits into a `cross-tenant` error reply WITHOUT calling the
|
|
1173
|
-
* verb, so a tenant can never read or act on another tenant's run. `listRuns` is scoped by
|
|
1174
|
-
* overwriting the query's `namespace` with the requester's tenant, ignoring whatever the client
|
|
1175
|
-
* sent. This is the security boundary of the tenant run gateway — do not weaken it.
|
|
1176
|
-
*/
|
|
1177
|
-
declare class RunRequestResponder {
|
|
1178
|
-
private readonly transport;
|
|
1179
|
-
private readonly gateway;
|
|
1180
|
-
constructor(transport: RunRequestTransport, gateway: RunGateway);
|
|
1181
|
-
/** Register the consumer on the transport. Each request is answered independently; a handler
|
|
1182
|
-
* failure never throws back into the transport (errors are captured into an error reply). */
|
|
1183
|
-
start(): void;
|
|
1184
|
-
private handle;
|
|
1185
|
-
private callVerb;
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
1262
|
declare const STEP_INTERCEPTOR_METADATA: unique symbol;
|
|
1189
1263
|
/**
|
|
1190
1264
|
* The shape a `@StepInterceptor()` provider must implement: `intercept(invocation, next)` wraps the
|
|
@@ -1375,4 +1449,4 @@ declare class WorkflowService {
|
|
|
1375
1449
|
}): Promise<number>;
|
|
1376
1450
|
}
|
|
1377
1451
|
|
|
1378
|
-
export { CONTEXT_ACCESSOR, type ContextAccessor, type ContextStore, DEAD_LETTER_METADATA, DURABLE_STEP_METADATA, DURABLE_WORKER_RUNNERS, DURABLE_WORKER_RUNTIME, DeadLetter, type DtoClass, DurableModule, type DurableModuleAsyncOptions, type DurableModuleOptions, type DurableRetentionOptions, DurableStartClient, DurableStep, type DurableStepInterceptor, type DurableStepMeta, ENTITY_METADATA, ENTITY_ON_METADATA, Entity, EntityService, type ErrorClassWithCode, IN_APP_RUN_REDIS_WORKER, IN_APP_WORKER_BINDING, IN_APP_WORKER_RUNNERS, IN_APP_WORKER_RUNTIME, type InAppWorkerBinding, InAppWorkerBootstrap, type IoSchema, ON_ERROR_METADATA, ON_EVENT_METADATA, On, OnDurableEvent, OnError, type OnErrorMethod, OnEvent, ProxyRunGateway, RUN_GATEWAY, RUN_REDIS_WORKER, type RunLookupStore, type RunRedisWorkerFn, RunRequestResponder, type RunRequestTransport, STEP_INTERCEPTOR_METADATA, Step, StepInterceptor, type StepOptions, StoreRunGateway, TenantEventRepublisher, ThinStepRegistrar, ThinWorkerBootstrap, ThinWorkflowRegistrar, UNSUPPORTED_RUN_REQUEST, type UserRef, WORKFLOW_METADATA, Workflow, type WorkflowAttributesOf, type WorkflowMeta, type WorkflowOptions, WorkflowService, attributesOf, entityConfigFor, getDurableStepMeta, getEntityMeta, getOnEvents, getWorkflowMeta, inAppWorkerProviders, isDeadLetterHandler, isDrivingOperator, isOperatorRole, isStepInterceptor, onErrorCodesOf, thinWorkerProviders, unavailableRunGateway };
|
|
1452
|
+
export { CONTEXT_ACCESSOR, type ContextAccessor, type ContextStore, DEAD_LETTER_METADATA, DURABLE_STEP_METADATA, DURABLE_WORKER_RUNNERS, DURABLE_WORKER_RUNTIME, DeadLetter, type DtoClass, DurableModule, type DurableModuleAsyncOptions, type DurableModuleOptions, type DurableRetentionOptions, DurableStartClient, DurableStep, type DurableStepInterceptor, type DurableStepMeta, ENTITY_METADATA, ENTITY_ON_METADATA, Entity, EntityService, type ErrorClassWithCode, IN_APP_RUN_REDIS_WORKER, IN_APP_WORKER_BINDING, IN_APP_WORKER_RUNNERS, IN_APP_WORKER_RUNTIME, type InAppWorkerBinding, InAppWorkerBootstrap, type IoSchema, ON_ERROR_METADATA, ON_EVENT_METADATA, On, OnDurableEvent, OnError, type OnErrorMethod, OnEvent, ProxyRunGateway, RUN_GATEWAY, RUN_REDIS_WORKER, RunGatewayError, type RunGatewayErrorCode, type RunLookupStore, type RunRedisWorkerFn, RunRequestResponder, type RunRequestResponderOptions, type RunRequestTransport, STEP_INTERCEPTOR_METADATA, Step, StepInterceptor, type StepOptions, StoreRunGateway, TenantEventRepublisher, ThinStepRegistrar, ThinWorkerBootstrap, ThinWorkflowRegistrar, UNSUPPORTED_RUN_REQUEST, type UserRef, WORKFLOW_METADATA, Workflow, type WorkflowAttributesOf, type WorkflowMeta, type WorkflowOptions, WorkflowService, attributesOf, entityConfigFor, getDurableStepMeta, getEntityMeta, getOnEvents, getWorkflowMeta, inAppWorkerProviders, isDeadLetterHandler, isDrivingOperator, isOperatorRole, isStepInterceptor, onErrorCodesOf, thinWorkerProviders, unavailableRunGateway };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { WorkflowCtx, SearchAttributes, WorkflowClass, StandardSchemaV1, StepError, OnErrorInput, WorkflowRef, SingletonConfig, ConcurrencyConfig, SearchAttributesSchema, StateStore, Transport, NamedTransport, ControlPlane, StalledRunInfo, ScheduledWorkflow, RetentionPolicy, QueueConfig, AdmissionBackend, WorkflowInputOf, StartOptions, RunResult, RunGateway, WorkflowEngine, EntityHandler, DurableTopology, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, GroupHealth, RunWaiting, EngineEvent,
|
|
2
|
-
export { AttributeFilter, DynamicWorkflowCtx, EngineEvent, InferSearchAttributes, OnErrorInput, RunDetail, RunGateway, RunListItem, RunQuery, RunSchedulesOptions, RunStatus, RunWaiting, ScheduledWorkflow, SearchAttributes, SearchAttributesSchema, StepCheckpoint, StepEvent, StepLogger, WorkflowCtx, WorkflowEngine, WorkflowHandler, WorkflowRun, prevCronFireMs, readSearchAttributes, runSchedules, scheduleRunIdAt, scheduledRunId } from '@dudousxd/nestjs-durable-core';
|
|
1
|
+
import { WorkflowCtx, SearchAttributes, WorkflowClass, StandardSchemaV1, StepError, OnErrorInput, WorkflowRef, SingletonConfig, ConcurrencyConfig, SearchAttributesSchema, StateStore, Transport, NamedTransport, ControlPlane, StalledRunInfo, ScheduledWorkflow, RetentionPolicy, QueueConfig, AdmissionBackend, TenantVerifier, TenantTokenSource, WorkflowInputOf, StartOptions, RunResult, RunGateway, WorkflowEngine, EntityHandler, RunRequest, RunReply, DurableTopology, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, GroupHealth, RunWaiting, EngineEvent, StepInvocation, WorkflowRun, TenantEvent } from '@dudousxd/nestjs-durable-core';
|
|
2
|
+
export { AttributeFilter, DynamicWorkflowCtx, EngineEvent, InferSearchAttributes, OnErrorInput, RunDetail, RunGateway, RunListItem, RunQuery, RunSchedulesOptions, RunStatus, RunWaiting, ScheduledWorkflow, SearchAttributes, SearchAttributesSchema, StepCheckpoint, StepEvent, StepLogger, TenantTokenSource, TenantVerifier, VerifiedTenant, WorkflowCtx, WorkflowEngine, WorkflowHandler, WorkflowRun, hmacTenantVerifier, prevCronFireMs, readSearchAttributes, runSchedules, scheduleRunIdAt, scheduledRunId, signTenantToken } from '@dudousxd/nestjs-durable-core';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { ConcurrencyOption, StartRunDeps, RunRedisWorkerOptions, RunningWorker, DurableWorkerRuntime } from '@dudousxd/durable-worker';
|
|
5
5
|
import { DynamicModule, InjectionToken, OnModuleInit, OnApplicationBootstrap, OnApplicationShutdown, Provider } from '@nestjs/common';
|
|
@@ -790,6 +790,26 @@ interface DurableModuleOptions {
|
|
|
790
790
|
/** Timeout for a thin worker's `RunGateway` round-trip over `transport` before it rejects. Defaults
|
|
791
791
|
* to 10_000ms inside `ProxyRunGateway`. Ignored for an operator (bound to `StoreRunGateway`). */
|
|
792
792
|
runGatewayTimeoutMs?: number;
|
|
793
|
+
/**
|
|
794
|
+
* **Operator.** Verifies the signed tenant token a store-less tenant worker presents in the wire
|
|
795
|
+
* `tenant` field of every run request and start-run message, and DERIVES the tenant from it,
|
|
796
|
+
* discarding the wire claim. An invalid, tampered or absent token is rejected: a run request gets
|
|
797
|
+
* an `unauthorized` error reply, a start-run creates nothing. Use `hmacTenantVerifier(secret)`
|
|
798
|
+
* (tokens minted by `signTenantToken`, wire-compatible with Agora) or supply your own.
|
|
799
|
+
*
|
|
800
|
+
* Optional. Absent: the wire `tenant` is trusted verbatim (prefix/network isolation only), exactly
|
|
801
|
+
* as before. An explicit `topology: { role: 'control-plane' }` without a verifier logs a warning
|
|
802
|
+
* at boot; a plain operator (`store` + `transport`, no `topology`) boots silently.
|
|
803
|
+
*/
|
|
804
|
+
verifyTenant?: TenantVerifier;
|
|
805
|
+
/**
|
|
806
|
+
* **Tenant worker.** The signed token (see `signTenantToken`) this worker presents, verbatim, in
|
|
807
|
+
* the `tenant` field of every run request (`ProxyRunGateway`) and start-run (`engine.start`), so
|
|
808
|
+
* an operator with {@link verifyTenant} can authenticate it. A string, or a function called before
|
|
809
|
+
* every request (rotation, short-lived tokens). Optional: absent, the raw {@link partition} is
|
|
810
|
+
* sent, as before. Live-tail stays keyed by the partition, never the token.
|
|
811
|
+
*/
|
|
812
|
+
tenantToken?: TenantTokenSource;
|
|
793
813
|
/**
|
|
794
814
|
* An explicit preset that NAMES the deployment role instead of leaving it to `store`/`connection`
|
|
795
815
|
* inference, and VALIDATES the axes that are otherwise only prose (see the interface doc's role
|
|
@@ -1078,6 +1098,77 @@ declare class InAppWorkerBootstrap implements OnModuleInit, OnApplicationBootstr
|
|
|
1078
1098
|
*/
|
|
1079
1099
|
declare function inAppWorkerProviders(): Provider[];
|
|
1080
1100
|
|
|
1101
|
+
/**
|
|
1102
|
+
* The error `code` a responder replies with for a verb it does not know, so a newer tenant can tell
|
|
1103
|
+
* "this control plane is older than me" from a real failure and fall back at once. Earlier
|
|
1104
|
+
* responders reply `ok: true` with no `data` instead; `ProxyRunGateway` reads both.
|
|
1105
|
+
*/
|
|
1106
|
+
declare const UNSUPPORTED_RUN_REQUEST = "unsupported-request";
|
|
1107
|
+
/** The narrow slice of `Transport` the responder needs — a tenant's read/control request in, a
|
|
1108
|
+
* correlated reply out. Both are OPTIONAL on the full `Transport` interface (only broker
|
|
1109
|
+
* transports carry the run-request/reply protocol); the caller capability-checks before wiring
|
|
1110
|
+
* this up (see `durable.module.ts`). */
|
|
1111
|
+
interface RunRequestTransport {
|
|
1112
|
+
onRunRequest(handler: (msg: RunRequest) => Promise<void>): void;
|
|
1113
|
+
publishRunReply(reply: RunReply): Promise<void>;
|
|
1114
|
+
}
|
|
1115
|
+
/** Options for {@link RunRequestResponder}. */
|
|
1116
|
+
interface RunRequestResponderOptions {
|
|
1117
|
+
/**
|
|
1118
|
+
* Verifies the signed token a tenant presents in the wire `tenant` field and DERIVES the real
|
|
1119
|
+
* tenant from it (see `hmacTenantVerifier`). Present: an invalid, tampered or absent token is
|
|
1120
|
+
* rejected with an `unauthorized` error reply and the wire claim is discarded in favour of the
|
|
1121
|
+
* verified tenant. Absent: the wire `tenant` is trusted verbatim (prefix/network isolation only).
|
|
1122
|
+
*/
|
|
1123
|
+
verifyTenant?: TenantVerifier | undefined;
|
|
1124
|
+
/**
|
|
1125
|
+
* Warn at construction when no {@link verifyTenant} is set. Default `true`. `DurableModule` passes
|
|
1126
|
+
* `true` only for an explicit `topology: { role: 'control-plane' }`, so a plain operator app that
|
|
1127
|
+
* never serves tenants boots silently.
|
|
1128
|
+
*/
|
|
1129
|
+
warnWithoutVerifier?: boolean | undefined;
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Operator-side consumer of a tenant's {@link RunRequest}s: answers each one against a
|
|
1133
|
+
* `RunGateway`, enforcing the tenant boundary before touching the run. For every runId-bearing
|
|
1134
|
+
* verb it loads the run via `getRunDetail` FIRST and compares `run.namespace` to the requesting
|
|
1135
|
+
* `msg.tenant` — a mismatch short-circuits into a `cross-tenant` error reply WITHOUT calling the
|
|
1136
|
+
* verb, so a tenant can never read or act on another tenant's run. `listRuns` is scoped by
|
|
1137
|
+
* overwriting the query's `namespace` with the requester's tenant, ignoring whatever the client
|
|
1138
|
+
* sent. This is the security boundary of the tenant run gateway — do not weaken it.
|
|
1139
|
+
*
|
|
1140
|
+
* With {@link RunRequestResponderOptions.verifyTenant}, every request is authenticated FIRST: the
|
|
1141
|
+
* wire `tenant` is treated as a signed token, the real tenant is derived from it, and an invalid
|
|
1142
|
+
* token short-circuits into an `unauthorized` error reply before any run is touched. A verifier
|
|
1143
|
+
* that throws becomes an error reply too.
|
|
1144
|
+
*/
|
|
1145
|
+
declare class RunRequestResponder {
|
|
1146
|
+
private readonly transport;
|
|
1147
|
+
private readonly gateway;
|
|
1148
|
+
private readonly verifyTenant;
|
|
1149
|
+
constructor(transport: RunRequestTransport, gateway: RunGateway, options?: RunRequestResponderOptions);
|
|
1150
|
+
/** Register the consumer on the transport. Each request is answered independently; a handler
|
|
1151
|
+
* failure never throws back into the transport (errors are captured into an error reply). */
|
|
1152
|
+
start(): void;
|
|
1153
|
+
private handle;
|
|
1154
|
+
private callVerb;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* An error reply from the control plane. `code` carries the reply's machine-readable reason when it
|
|
1159
|
+
* sent one — `unauthorized` (the tenant token was rejected), `cross-tenant` (the run belongs to
|
|
1160
|
+
* another tenant), ... — so a caller can tell an authentication failure from a real one, as with
|
|
1161
|
+
* Agora's proxy.
|
|
1162
|
+
*/
|
|
1163
|
+
/**
|
|
1164
|
+
* The codes the run-request responder replies with. `string & {}` keeps a code from a newer control
|
|
1165
|
+
* plane assignable without losing autocomplete on the known ones.
|
|
1166
|
+
*/
|
|
1167
|
+
type RunGatewayErrorCode = 'unauthorized' | 'cross-tenant' | typeof UNSUPPORTED_RUN_REQUEST | (string & {});
|
|
1168
|
+
declare class RunGatewayError extends Error {
|
|
1169
|
+
readonly code: RunGatewayErrorCode | undefined;
|
|
1170
|
+
constructor(message: string, code?: RunGatewayErrorCode);
|
|
1171
|
+
}
|
|
1081
1172
|
/**
|
|
1082
1173
|
* Tenant-side `RunGateway` — round-trips every verb as a `RunRequest`/`RunReply` pair over the
|
|
1083
1174
|
* transport, correlated by a minted `requestId`, and bridges `subscribe` onto the transport's
|
|
@@ -1091,15 +1182,32 @@ declare class ProxyRunGateway implements RunGateway {
|
|
|
1091
1182
|
private readonly tenant;
|
|
1092
1183
|
private readonly timeoutMs;
|
|
1093
1184
|
private readonly capabilityTtlMs;
|
|
1185
|
+
/**
|
|
1186
|
+
* The tenant's signed token (see `signTenantToken`), presented VERBATIM in the `tenant` field of
|
|
1187
|
+
* every {@link RunRequest} so an operator with `verifyTenant` can authenticate it and derive the
|
|
1188
|
+
* tenant from it. A function is called before every request (rotation / short-lived tokens).
|
|
1189
|
+
* Omit to send the raw `tenant` (prefix/network isolation only). The live-tail channel always
|
|
1190
|
+
* stays keyed by `tenant`, never the token: the operator republishes by the run's namespace.
|
|
1191
|
+
*/
|
|
1192
|
+
private readonly tenantToken?;
|
|
1094
1193
|
private readonly pending;
|
|
1095
1194
|
/** Verb → when to ask the control plane for it again, after it answered that it does not know it.
|
|
1096
1195
|
* During a rolling upgrade the control plane is several pods of mixed versions behind one queue,
|
|
1097
1196
|
* and a reply does not say which pod sent it, so the whole control plane is assumed old until the
|
|
1098
1197
|
* entry expires. */
|
|
1099
1198
|
private readonly unsupportedUntil;
|
|
1100
|
-
constructor(transport: Transport, tenant: string, timeoutMs?: number, capabilityTtlMs?: number
|
|
1199
|
+
constructor(transport: Transport, tenant: string, timeoutMs?: number, capabilityTtlMs?: number,
|
|
1200
|
+
/**
|
|
1201
|
+
* The tenant's signed token (see `signTenantToken`), presented VERBATIM in the `tenant` field of
|
|
1202
|
+
* every {@link RunRequest} so an operator with `verifyTenant` can authenticate it and derive the
|
|
1203
|
+
* tenant from it. A function is called before every request (rotation / short-lived tokens).
|
|
1204
|
+
* Omit to send the raw `tenant` (prefix/network isolation only). The live-tail channel always
|
|
1205
|
+
* stays keyed by `tenant`, never the token: the operator republishes by the run's namespace.
|
|
1206
|
+
*/
|
|
1207
|
+
tenantToken?: TenantTokenSource | undefined);
|
|
1101
1208
|
private handleReply;
|
|
1102
1209
|
private request;
|
|
1210
|
+
private send;
|
|
1103
1211
|
topology(): DurableTopology;
|
|
1104
1212
|
/**
|
|
1105
1213
|
* A control plane that predates {@link RunDetailOptions} ignores them and sends the full detail;
|
|
@@ -1151,40 +1259,6 @@ declare function isOperatorRole(options: DurableModuleOptions): boolean;
|
|
|
1151
1259
|
*/
|
|
1152
1260
|
declare function isDrivingOperator(options: DurableModuleOptions): boolean;
|
|
1153
1261
|
|
|
1154
|
-
/**
|
|
1155
|
-
* The error `code` a responder replies with for a verb it does not know, so a newer tenant can tell
|
|
1156
|
-
* "this control plane is older than me" from a real failure and fall back at once. Earlier
|
|
1157
|
-
* responders reply `ok: true` with no `data` instead; `ProxyRunGateway` reads both.
|
|
1158
|
-
*/
|
|
1159
|
-
declare const UNSUPPORTED_RUN_REQUEST = "unsupported-request";
|
|
1160
|
-
/** The narrow slice of `Transport` the responder needs — a tenant's read/control request in, a
|
|
1161
|
-
* correlated reply out. Both are OPTIONAL on the full `Transport` interface (only broker
|
|
1162
|
-
* transports carry the run-request/reply protocol); the caller capability-checks before wiring
|
|
1163
|
-
* this up (see `durable.module.ts`). */
|
|
1164
|
-
interface RunRequestTransport {
|
|
1165
|
-
onRunRequest(handler: (msg: RunRequest) => Promise<void>): void;
|
|
1166
|
-
publishRunReply(reply: RunReply): Promise<void>;
|
|
1167
|
-
}
|
|
1168
|
-
/**
|
|
1169
|
-
* Operator-side consumer of a tenant's {@link RunRequest}s: answers each one against a
|
|
1170
|
-
* `RunGateway`, enforcing the tenant boundary before touching the run. For every runId-bearing
|
|
1171
|
-
* verb it loads the run via `getRunDetail` FIRST and compares `run.namespace` to the requesting
|
|
1172
|
-
* `msg.tenant` — a mismatch short-circuits into a `cross-tenant` error reply WITHOUT calling the
|
|
1173
|
-
* verb, so a tenant can never read or act on another tenant's run. `listRuns` is scoped by
|
|
1174
|
-
* overwriting the query's `namespace` with the requester's tenant, ignoring whatever the client
|
|
1175
|
-
* sent. This is the security boundary of the tenant run gateway — do not weaken it.
|
|
1176
|
-
*/
|
|
1177
|
-
declare class RunRequestResponder {
|
|
1178
|
-
private readonly transport;
|
|
1179
|
-
private readonly gateway;
|
|
1180
|
-
constructor(transport: RunRequestTransport, gateway: RunGateway);
|
|
1181
|
-
/** Register the consumer on the transport. Each request is answered independently; a handler
|
|
1182
|
-
* failure never throws back into the transport (errors are captured into an error reply). */
|
|
1183
|
-
start(): void;
|
|
1184
|
-
private handle;
|
|
1185
|
-
private callVerb;
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
1262
|
declare const STEP_INTERCEPTOR_METADATA: unique symbol;
|
|
1189
1263
|
/**
|
|
1190
1264
|
* The shape a `@StepInterceptor()` provider must implement: `intercept(invocation, next)` wraps the
|
|
@@ -1375,4 +1449,4 @@ declare class WorkflowService {
|
|
|
1375
1449
|
}): Promise<number>;
|
|
1376
1450
|
}
|
|
1377
1451
|
|
|
1378
|
-
export { CONTEXT_ACCESSOR, type ContextAccessor, type ContextStore, DEAD_LETTER_METADATA, DURABLE_STEP_METADATA, DURABLE_WORKER_RUNNERS, DURABLE_WORKER_RUNTIME, DeadLetter, type DtoClass, DurableModule, type DurableModuleAsyncOptions, type DurableModuleOptions, type DurableRetentionOptions, DurableStartClient, DurableStep, type DurableStepInterceptor, type DurableStepMeta, ENTITY_METADATA, ENTITY_ON_METADATA, Entity, EntityService, type ErrorClassWithCode, IN_APP_RUN_REDIS_WORKER, IN_APP_WORKER_BINDING, IN_APP_WORKER_RUNNERS, IN_APP_WORKER_RUNTIME, type InAppWorkerBinding, InAppWorkerBootstrap, type IoSchema, ON_ERROR_METADATA, ON_EVENT_METADATA, On, OnDurableEvent, OnError, type OnErrorMethod, OnEvent, ProxyRunGateway, RUN_GATEWAY, RUN_REDIS_WORKER, type RunLookupStore, type RunRedisWorkerFn, RunRequestResponder, type RunRequestTransport, STEP_INTERCEPTOR_METADATA, Step, StepInterceptor, type StepOptions, StoreRunGateway, TenantEventRepublisher, ThinStepRegistrar, ThinWorkerBootstrap, ThinWorkflowRegistrar, UNSUPPORTED_RUN_REQUEST, type UserRef, WORKFLOW_METADATA, Workflow, type WorkflowAttributesOf, type WorkflowMeta, type WorkflowOptions, WorkflowService, attributesOf, entityConfigFor, getDurableStepMeta, getEntityMeta, getOnEvents, getWorkflowMeta, inAppWorkerProviders, isDeadLetterHandler, isDrivingOperator, isOperatorRole, isStepInterceptor, onErrorCodesOf, thinWorkerProviders, unavailableRunGateway };
|
|
1452
|
+
export { CONTEXT_ACCESSOR, type ContextAccessor, type ContextStore, DEAD_LETTER_METADATA, DURABLE_STEP_METADATA, DURABLE_WORKER_RUNNERS, DURABLE_WORKER_RUNTIME, DeadLetter, type DtoClass, DurableModule, type DurableModuleAsyncOptions, type DurableModuleOptions, type DurableRetentionOptions, DurableStartClient, DurableStep, type DurableStepInterceptor, type DurableStepMeta, ENTITY_METADATA, ENTITY_ON_METADATA, Entity, EntityService, type ErrorClassWithCode, IN_APP_RUN_REDIS_WORKER, IN_APP_WORKER_BINDING, IN_APP_WORKER_RUNNERS, IN_APP_WORKER_RUNTIME, type InAppWorkerBinding, InAppWorkerBootstrap, type IoSchema, ON_ERROR_METADATA, ON_EVENT_METADATA, On, OnDurableEvent, OnError, type OnErrorMethod, OnEvent, ProxyRunGateway, RUN_GATEWAY, RUN_REDIS_WORKER, RunGatewayError, type RunGatewayErrorCode, type RunLookupStore, type RunRedisWorkerFn, RunRequestResponder, type RunRequestResponderOptions, type RunRequestTransport, STEP_INTERCEPTOR_METADATA, Step, StepInterceptor, type StepOptions, StoreRunGateway, TenantEventRepublisher, ThinStepRegistrar, ThinWorkerBootstrap, ThinWorkflowRegistrar, UNSUPPORTED_RUN_REQUEST, type UserRef, WORKFLOW_METADATA, Workflow, type WorkflowAttributesOf, type WorkflowMeta, type WorkflowOptions, WorkflowService, attributesOf, entityConfigFor, getDurableStepMeta, getEntityMeta, getOnEvents, getWorkflowMeta, inAppWorkerProviders, isDeadLetterHandler, isDrivingOperator, isOperatorRole, isStepInterceptor, onErrorCodesOf, thinWorkerProviders, unavailableRunGateway };
|
package/dist/index.js
CHANGED
|
@@ -264,7 +264,7 @@ function attributesOf(workflow, run) {
|
|
|
264
264
|
__name(attributesOf, "attributesOf");
|
|
265
265
|
|
|
266
266
|
// src/durable-start-client.ts
|
|
267
|
-
import { workflowName } from "@dudousxd/nestjs-durable-core";
|
|
267
|
+
import { resolveTenantToken, workflowName } from "@dudousxd/nestjs-durable-core";
|
|
268
268
|
import { Injectable } from "@nestjs/common";
|
|
269
269
|
|
|
270
270
|
// src/worker-sdk.ts
|
|
@@ -322,7 +322,9 @@ var DurableStartClient = class {
|
|
|
322
322
|
async start(workflow, input, runId = globalThis.crypto.randomUUID(), opts) {
|
|
323
323
|
const name = workflowName(workflow);
|
|
324
324
|
await startRun(this.options.connection, {
|
|
325
|
-
tenant
|
|
325
|
+
// The signed token (when configured) rides in the `tenant` field, as on every run request; an
|
|
326
|
+
// operator with `verifyTenant` derives the run's namespace from it.
|
|
327
|
+
tenant: this.options.tenantToken !== void 0 ? await resolveTenantToken(this.options.tenantToken) : this.tenant,
|
|
326
328
|
workflow: name,
|
|
327
329
|
input,
|
|
328
330
|
runId,
|
|
@@ -1159,10 +1161,11 @@ function inAppWorkerProviders() {
|
|
|
1159
1161
|
__name(inAppWorkerProviders, "inAppWorkerProviders");
|
|
1160
1162
|
|
|
1161
1163
|
// src/proxy-run-gateway.ts
|
|
1162
|
-
import { SUMMARY_CHILD_LIMIT, childIdsOf, pageFan, summarizeTimeline } from "@dudousxd/nestjs-durable-core";
|
|
1164
|
+
import { SUMMARY_CHILD_LIMIT, childIdsOf, pageFan, resolveTenantToken as resolveTenantToken2, summarizeTimeline } from "@dudousxd/nestjs-durable-core";
|
|
1163
1165
|
import { Injectable as Injectable6 } from "@nestjs/common";
|
|
1164
1166
|
|
|
1165
1167
|
// src/run-request-responder.ts
|
|
1168
|
+
import { resolveWireTenant } from "@dudousxd/nestjs-durable-core";
|
|
1166
1169
|
var UNSUPPORTED_RUN_REQUEST = "unsupported-request";
|
|
1167
1170
|
var KNOWN_KINDS = /* @__PURE__ */ new Set([
|
|
1168
1171
|
"getRunDetail",
|
|
@@ -1184,9 +1187,14 @@ var RunRequestResponder = class {
|
|
|
1184
1187
|
}
|
|
1185
1188
|
transport;
|
|
1186
1189
|
gateway;
|
|
1187
|
-
|
|
1190
|
+
verifyTenant;
|
|
1191
|
+
constructor(transport, gateway, options = {}) {
|
|
1188
1192
|
this.transport = transport;
|
|
1189
1193
|
this.gateway = gateway;
|
|
1194
|
+
this.verifyTenant = options.verifyTenant;
|
|
1195
|
+
if (!this.verifyTenant && options.warnWithoutVerifier !== false) {
|
|
1196
|
+
console.warn("[nestjs-durable] run-request responder started WITHOUT a tenant verifier: the wire `tenant` claim is trusted verbatim (prefix/network isolation only). Any broker client can act as any tenant. Configure `verifyTenant` (e.g. hmacTenantVerifier(secret)) and signed tenant tokens unless your broker is fully isolated per tenant.");
|
|
1197
|
+
}
|
|
1190
1198
|
}
|
|
1191
1199
|
/** Register the consumer on the transport. Each request is answered independently; a handler
|
|
1192
1200
|
* failure never throws back into the transport (errors are captured into an error reply). */
|
|
@@ -1209,7 +1217,24 @@ var RunRequestResponder = class {
|
|
|
1209
1217
|
await this.transport.publishRunReply(reply);
|
|
1210
1218
|
});
|
|
1211
1219
|
}
|
|
1212
|
-
async handle(
|
|
1220
|
+
async handle(wire) {
|
|
1221
|
+
const tenant = await resolveWireTenant(wire.tenant, this.verifyTenant);
|
|
1222
|
+
if (tenant === null) {
|
|
1223
|
+
return {
|
|
1224
|
+
requestId: wire.requestId,
|
|
1225
|
+
result: {
|
|
1226
|
+
ok: false,
|
|
1227
|
+
error: {
|
|
1228
|
+
message: "invalid or unauthorized tenant token",
|
|
1229
|
+
code: "unauthorized"
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
const msg = {
|
|
1235
|
+
...wire,
|
|
1236
|
+
tenant
|
|
1237
|
+
};
|
|
1213
1238
|
const { body } = msg;
|
|
1214
1239
|
if (!KNOWN_KINDS.has(body.kind)) {
|
|
1215
1240
|
return {
|
|
@@ -1385,7 +1410,18 @@ function _ts_metadata6(metadataKey, metadataValue) {
|
|
|
1385
1410
|
}
|
|
1386
1411
|
}
|
|
1387
1412
|
__name(_ts_metadata6, "_ts_metadata");
|
|
1388
|
-
var
|
|
1413
|
+
var RunGatewayError = class extends Error {
|
|
1414
|
+
static {
|
|
1415
|
+
__name(this, "RunGatewayError");
|
|
1416
|
+
}
|
|
1417
|
+
code;
|
|
1418
|
+
constructor(message, code) {
|
|
1419
|
+
super(message);
|
|
1420
|
+
this.name = "RunGatewayError";
|
|
1421
|
+
this.code = code;
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1424
|
+
var UnsupportedRunRequestError = class UnsupportedRunRequestError2 extends RunGatewayError {
|
|
1389
1425
|
static {
|
|
1390
1426
|
__name(this, "UnsupportedRunRequestError");
|
|
1391
1427
|
}
|
|
@@ -1402,17 +1438,19 @@ var ProxyRunGateway = class {
|
|
|
1402
1438
|
tenant;
|
|
1403
1439
|
timeoutMs;
|
|
1404
1440
|
capabilityTtlMs;
|
|
1441
|
+
tenantToken;
|
|
1405
1442
|
pending = /* @__PURE__ */ new Map();
|
|
1406
1443
|
/** Verb → when to ask the control plane for it again, after it answered that it does not know it.
|
|
1407
1444
|
* During a rolling upgrade the control plane is several pods of mixed versions behind one queue,
|
|
1408
1445
|
* and a reply does not say which pod sent it, so the whole control plane is assumed old until the
|
|
1409
1446
|
* entry expires. */
|
|
1410
1447
|
unsupportedUntil = /* @__PURE__ */ new Map();
|
|
1411
|
-
constructor(transport, tenant, timeoutMs = 1e4, capabilityTtlMs = 6e4) {
|
|
1448
|
+
constructor(transport, tenant, timeoutMs = 1e4, capabilityTtlMs = 6e4, tenantToken) {
|
|
1412
1449
|
this.transport = transport;
|
|
1413
1450
|
this.tenant = tenant;
|
|
1414
1451
|
this.timeoutMs = timeoutMs;
|
|
1415
1452
|
this.capabilityTtlMs = capabilityTtlMs;
|
|
1453
|
+
this.tenantToken = tenantToken;
|
|
1416
1454
|
this.transport.onRunReply?.((reply) => this.handleReply(reply));
|
|
1417
1455
|
}
|
|
1418
1456
|
handleReply(reply) {
|
|
@@ -1423,12 +1461,17 @@ var ProxyRunGateway = class {
|
|
|
1423
1461
|
if (reply.result.ok) {
|
|
1424
1462
|
pending.resolve(reply.result.data);
|
|
1425
1463
|
} else if (reply.result.error.code === UNSUPPORTED_RUN_REQUEST) {
|
|
1426
|
-
pending.reject(new UnsupportedRunRequestError(reply.result.error.message));
|
|
1464
|
+
pending.reject(new UnsupportedRunRequestError(reply.result.error.message, UNSUPPORTED_RUN_REQUEST));
|
|
1427
1465
|
} else {
|
|
1428
|
-
pending.reject(new
|
|
1466
|
+
pending.reject(new RunGatewayError(reply.result.error.message, reply.result.error.code));
|
|
1429
1467
|
}
|
|
1430
1468
|
}
|
|
1431
1469
|
request(body) {
|
|
1470
|
+
const source = this.tenantToken;
|
|
1471
|
+
if (typeof source !== "function") return this.send(source ?? this.tenant, body);
|
|
1472
|
+
return resolveTenantToken2(source).then((tenant) => this.send(tenant, body));
|
|
1473
|
+
}
|
|
1474
|
+
send(tenant, body) {
|
|
1432
1475
|
return new Promise((resolve, reject) => {
|
|
1433
1476
|
const requestId = globalThis.crypto.randomUUID();
|
|
1434
1477
|
const timer = setTimeout(() => {
|
|
@@ -1442,9 +1485,9 @@ var ProxyRunGateway = class {
|
|
|
1442
1485
|
});
|
|
1443
1486
|
this.transport.dispatchRunRequest?.({
|
|
1444
1487
|
requestId,
|
|
1445
|
-
tenant
|
|
1488
|
+
tenant,
|
|
1446
1489
|
body
|
|
1447
|
-
})
|
|
1490
|
+
})?.catch((error) => {
|
|
1448
1491
|
const stillPending = this.pending.get(requestId);
|
|
1449
1492
|
if (!stillPending) return;
|
|
1450
1493
|
clearTimeout(stillPending.timer);
|
|
@@ -1602,7 +1645,8 @@ ProxyRunGateway = _ts_decorate6([
|
|
|
1602
1645
|
typeof Transport === "undefined" ? Object : Transport,
|
|
1603
1646
|
String,
|
|
1604
1647
|
void 0,
|
|
1605
|
-
void 0
|
|
1648
|
+
void 0,
|
|
1649
|
+
typeof TenantTokenSource === "undefined" ? Object : TenantTokenSource
|
|
1606
1650
|
])
|
|
1607
1651
|
], ProxyRunGateway);
|
|
1608
1652
|
|
|
@@ -2727,7 +2771,15 @@ var RunGatewayBootstrap = class RunGatewayBootstrap2 {
|
|
|
2727
2771
|
onRunRequest: onRunRequest.bind(this.transport),
|
|
2728
2772
|
publishRunReply: publishRunReply.bind(this.transport)
|
|
2729
2773
|
};
|
|
2730
|
-
new RunRequestResponder(runRequestTransport, this.gateway
|
|
2774
|
+
new RunRequestResponder(runRequestTransport, this.gateway, {
|
|
2775
|
+
verifyTenant: this.options.verifyTenant,
|
|
2776
|
+
// Mirrors Agora's rule (`warnWithoutVerifier: role === 'control-plane'`): only a deployment
|
|
2777
|
+
// that EXPLICITLY names itself the control plane is told it runs without tenant auth. A plain
|
|
2778
|
+
// operator (role inferred from `store`) is Agora's `standalone`: anyone with broker access
|
|
2779
|
+
// can already enqueue steps and results there, so the warning would be noise on every boot
|
|
2780
|
+
// of an app that never serves tenants.
|
|
2781
|
+
warnWithoutVerifier: this.options.topology?.role === "control-plane"
|
|
2782
|
+
}).start();
|
|
2731
2783
|
}
|
|
2732
2784
|
if (typeof publishTenantEvent === "function") {
|
|
2733
2785
|
const republisher = new TenantEventRepublisher(store, publishTenantEvent.bind(this.transport));
|
|
@@ -2879,6 +2931,7 @@ var DurableModule = class _DurableModule {
|
|
|
2879
2931
|
context,
|
|
2880
2932
|
rehydrate: rehydrate || void 0,
|
|
2881
2933
|
compensationRetries: options.compensationRetries,
|
|
2934
|
+
verifyTenant: options.verifyTenant,
|
|
2882
2935
|
// A non-driving (dashboard/API) operator must not run workflows: enqueue-only, leaving
|
|
2883
2936
|
// each `pending` run in the store for a DRIVING instance's poll. A driving operator gets
|
|
2884
2937
|
// the engine's default in-process dispatcher: for a body registered inline, it runs
|
|
@@ -2919,7 +2972,7 @@ var DurableModule = class _DurableModule {
|
|
|
2919
2972
|
}
|
|
2920
2973
|
return new StoreRunGateway(store, engine);
|
|
2921
2974
|
}
|
|
2922
|
-
return options.transport ? new ProxyRunGateway(options.transport, options.partition ?? "default", options.runGatewayTimeoutMs) : unavailableRunGateway();
|
|
2975
|
+
return options.transport ? new ProxyRunGateway(options.transport, options.partition ?? "default", options.runGatewayTimeoutMs, void 0, options.tenantToken) : unavailableRunGateway();
|
|
2923
2976
|
}, "useFactory"),
|
|
2924
2977
|
inject: [
|
|
2925
2978
|
DURABLE_OPTIONS_CANONICAL7,
|
|
@@ -2954,7 +3007,7 @@ DurableModule = _ts_decorate12([
|
|
|
2954
3007
|
], DurableModule);
|
|
2955
3008
|
|
|
2956
3009
|
// src/index.ts
|
|
2957
|
-
import { prevCronFireMs, readSearchAttributes as readSearchAttributes2, RunGateway as RunGateway3, runSchedules as runSchedules2, scheduledRunId, scheduleRunIdAt, WorkflowEngine as WorkflowEngine7 } from "@dudousxd/nestjs-durable-core";
|
|
3010
|
+
import { hmacTenantVerifier, prevCronFireMs, readSearchAttributes as readSearchAttributes2, RunGateway as RunGateway3, runSchedules as runSchedules2, scheduledRunId, scheduleRunIdAt, signTenantToken, WorkflowEngine as WorkflowEngine7 } from "@dudousxd/nestjs-durable-core";
|
|
2958
3011
|
export {
|
|
2959
3012
|
CONTEXT_ACCESSOR,
|
|
2960
3013
|
DEAD_LETTER_METADATA,
|
|
@@ -2984,6 +3037,7 @@ export {
|
|
|
2984
3037
|
RUN_GATEWAY,
|
|
2985
3038
|
RUN_REDIS_WORKER,
|
|
2986
3039
|
RunGateway3 as RunGateway,
|
|
3040
|
+
RunGatewayError,
|
|
2987
3041
|
RunRequestResponder,
|
|
2988
3042
|
STEP_INTERCEPTOR_METADATA,
|
|
2989
3043
|
Step,
|
|
@@ -3004,6 +3058,7 @@ export {
|
|
|
3004
3058
|
getEntityMeta,
|
|
3005
3059
|
getOnEvents,
|
|
3006
3060
|
getWorkflowMeta,
|
|
3061
|
+
hmacTenantVerifier,
|
|
3007
3062
|
inAppWorkerProviders,
|
|
3008
3063
|
isDeadLetterHandler,
|
|
3009
3064
|
isDrivingOperator,
|
|
@@ -3015,6 +3070,7 @@ export {
|
|
|
3015
3070
|
runSchedules2 as runSchedules,
|
|
3016
3071
|
scheduleRunIdAt,
|
|
3017
3072
|
scheduledRunId,
|
|
3073
|
+
signTenantToken,
|
|
3018
3074
|
thinWorkerProviders,
|
|
3019
3075
|
unavailableRunGateway
|
|
3020
3076
|
};
|