@dudousxd/nestjs-durable 0.51.0 → 0.52.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/dist/index.cjs +156 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +63 -5
- package/dist/index.d.ts +63 -5
- package/dist/index.js +62 -14
- 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, RunRequest, RunReply, 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, 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, DurableTopology, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, GroupHealth, RunWaiting, EngineEvent, RunRequest, RunReply, 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
|
|
@@ -1091,13 +1111,29 @@ declare class ProxyRunGateway implements RunGateway {
|
|
|
1091
1111
|
private readonly tenant;
|
|
1092
1112
|
private readonly timeoutMs;
|
|
1093
1113
|
private readonly capabilityTtlMs;
|
|
1114
|
+
/**
|
|
1115
|
+
* The tenant's signed token (see `signTenantToken`), presented VERBATIM in the `tenant` field of
|
|
1116
|
+
* every {@link RunRequest} so an operator with `verifyTenant` can authenticate it and derive the
|
|
1117
|
+
* tenant from it. A function is called before every request (rotation / short-lived tokens).
|
|
1118
|
+
* Omit to send the raw `tenant` (prefix/network isolation only). The live-tail channel always
|
|
1119
|
+
* stays keyed by `tenant`, never the token: the operator republishes by the run's namespace.
|
|
1120
|
+
*/
|
|
1121
|
+
private readonly tenantToken?;
|
|
1094
1122
|
private readonly pending;
|
|
1095
1123
|
/** Verb → when to ask the control plane for it again, after it answered that it does not know it.
|
|
1096
1124
|
* During a rolling upgrade the control plane is several pods of mixed versions behind one queue,
|
|
1097
1125
|
* and a reply does not say which pod sent it, so the whole control plane is assumed old until the
|
|
1098
1126
|
* entry expires. */
|
|
1099
1127
|
private readonly unsupportedUntil;
|
|
1100
|
-
constructor(transport: Transport, tenant: string, timeoutMs?: number, capabilityTtlMs?: number
|
|
1128
|
+
constructor(transport: Transport, tenant: string, timeoutMs?: number, capabilityTtlMs?: number,
|
|
1129
|
+
/**
|
|
1130
|
+
* The tenant's signed token (see `signTenantToken`), presented VERBATIM in the `tenant` field of
|
|
1131
|
+
* every {@link RunRequest} so an operator with `verifyTenant` can authenticate it and derive the
|
|
1132
|
+
* tenant from it. A function is called before every request (rotation / short-lived tokens).
|
|
1133
|
+
* Omit to send the raw `tenant` (prefix/network isolation only). The live-tail channel always
|
|
1134
|
+
* stays keyed by `tenant`, never the token: the operator republishes by the run's namespace.
|
|
1135
|
+
*/
|
|
1136
|
+
tenantToken?: TenantTokenSource | undefined);
|
|
1101
1137
|
private handleReply;
|
|
1102
1138
|
private request;
|
|
1103
1139
|
topology(): DurableTopology;
|
|
@@ -1165,6 +1201,22 @@ interface RunRequestTransport {
|
|
|
1165
1201
|
onRunRequest(handler: (msg: RunRequest) => Promise<void>): void;
|
|
1166
1202
|
publishRunReply(reply: RunReply): Promise<void>;
|
|
1167
1203
|
}
|
|
1204
|
+
/** Options for {@link RunRequestResponder}. */
|
|
1205
|
+
interface RunRequestResponderOptions {
|
|
1206
|
+
/**
|
|
1207
|
+
* Verifies the signed token a tenant presents in the wire `tenant` field and DERIVES the real
|
|
1208
|
+
* tenant from it (see `hmacTenantVerifier`). Present: an invalid, tampered or absent token is
|
|
1209
|
+
* rejected with an `unauthorized` error reply and the wire claim is discarded in favour of the
|
|
1210
|
+
* verified tenant. Absent: the wire `tenant` is trusted verbatim (prefix/network isolation only).
|
|
1211
|
+
*/
|
|
1212
|
+
verifyTenant?: TenantVerifier | undefined;
|
|
1213
|
+
/**
|
|
1214
|
+
* Warn at construction when no {@link verifyTenant} is set. Default `true`. `DurableModule` passes
|
|
1215
|
+
* `true` only for an explicit `topology: { role: 'control-plane' }`, so a plain operator app that
|
|
1216
|
+
* never serves tenants boots silently.
|
|
1217
|
+
*/
|
|
1218
|
+
warnWithoutVerifier?: boolean | undefined;
|
|
1219
|
+
}
|
|
1168
1220
|
/**
|
|
1169
1221
|
* Operator-side consumer of a tenant's {@link RunRequest}s: answers each one against a
|
|
1170
1222
|
* `RunGateway`, enforcing the tenant boundary before touching the run. For every runId-bearing
|
|
@@ -1173,11 +1225,17 @@ interface RunRequestTransport {
|
|
|
1173
1225
|
* verb, so a tenant can never read or act on another tenant's run. `listRuns` is scoped by
|
|
1174
1226
|
* overwriting the query's `namespace` with the requester's tenant, ignoring whatever the client
|
|
1175
1227
|
* sent. This is the security boundary of the tenant run gateway — do not weaken it.
|
|
1228
|
+
*
|
|
1229
|
+
* With {@link RunRequestResponderOptions.verifyTenant}, every request is authenticated FIRST: the
|
|
1230
|
+
* wire `tenant` is treated as a signed token, the real tenant is derived from it, and an invalid
|
|
1231
|
+
* token short-circuits into an `unauthorized` error reply before any run is touched. A verifier
|
|
1232
|
+
* that throws becomes an error reply too.
|
|
1176
1233
|
*/
|
|
1177
1234
|
declare class RunRequestResponder {
|
|
1178
1235
|
private readonly transport;
|
|
1179
1236
|
private readonly gateway;
|
|
1180
|
-
|
|
1237
|
+
private readonly verifyTenant;
|
|
1238
|
+
constructor(transport: RunRequestTransport, gateway: RunGateway, options?: RunRequestResponderOptions);
|
|
1181
1239
|
/** Register the consumer on the transport. Each request is answered independently; a handler
|
|
1182
1240
|
* failure never throws back into the transport (errors are captured into an error reply). */
|
|
1183
1241
|
start(): void;
|
|
@@ -1375,4 +1433,4 @@ declare class WorkflowService {
|
|
|
1375
1433
|
}): Promise<number>;
|
|
1376
1434
|
}
|
|
1377
1435
|
|
|
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 };
|
|
1436
|
+
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 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, RunRequest, RunReply, 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, 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, DurableTopology, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, GroupHealth, RunWaiting, EngineEvent, RunRequest, RunReply, 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
|
|
@@ -1091,13 +1111,29 @@ declare class ProxyRunGateway implements RunGateway {
|
|
|
1091
1111
|
private readonly tenant;
|
|
1092
1112
|
private readonly timeoutMs;
|
|
1093
1113
|
private readonly capabilityTtlMs;
|
|
1114
|
+
/**
|
|
1115
|
+
* The tenant's signed token (see `signTenantToken`), presented VERBATIM in the `tenant` field of
|
|
1116
|
+
* every {@link RunRequest} so an operator with `verifyTenant` can authenticate it and derive the
|
|
1117
|
+
* tenant from it. A function is called before every request (rotation / short-lived tokens).
|
|
1118
|
+
* Omit to send the raw `tenant` (prefix/network isolation only). The live-tail channel always
|
|
1119
|
+
* stays keyed by `tenant`, never the token: the operator republishes by the run's namespace.
|
|
1120
|
+
*/
|
|
1121
|
+
private readonly tenantToken?;
|
|
1094
1122
|
private readonly pending;
|
|
1095
1123
|
/** Verb → when to ask the control plane for it again, after it answered that it does not know it.
|
|
1096
1124
|
* During a rolling upgrade the control plane is several pods of mixed versions behind one queue,
|
|
1097
1125
|
* and a reply does not say which pod sent it, so the whole control plane is assumed old until the
|
|
1098
1126
|
* entry expires. */
|
|
1099
1127
|
private readonly unsupportedUntil;
|
|
1100
|
-
constructor(transport: Transport, tenant: string, timeoutMs?: number, capabilityTtlMs?: number
|
|
1128
|
+
constructor(transport: Transport, tenant: string, timeoutMs?: number, capabilityTtlMs?: number,
|
|
1129
|
+
/**
|
|
1130
|
+
* The tenant's signed token (see `signTenantToken`), presented VERBATIM in the `tenant` field of
|
|
1131
|
+
* every {@link RunRequest} so an operator with `verifyTenant` can authenticate it and derive the
|
|
1132
|
+
* tenant from it. A function is called before every request (rotation / short-lived tokens).
|
|
1133
|
+
* Omit to send the raw `tenant` (prefix/network isolation only). The live-tail channel always
|
|
1134
|
+
* stays keyed by `tenant`, never the token: the operator republishes by the run's namespace.
|
|
1135
|
+
*/
|
|
1136
|
+
tenantToken?: TenantTokenSource | undefined);
|
|
1101
1137
|
private handleReply;
|
|
1102
1138
|
private request;
|
|
1103
1139
|
topology(): DurableTopology;
|
|
@@ -1165,6 +1201,22 @@ interface RunRequestTransport {
|
|
|
1165
1201
|
onRunRequest(handler: (msg: RunRequest) => Promise<void>): void;
|
|
1166
1202
|
publishRunReply(reply: RunReply): Promise<void>;
|
|
1167
1203
|
}
|
|
1204
|
+
/** Options for {@link RunRequestResponder}. */
|
|
1205
|
+
interface RunRequestResponderOptions {
|
|
1206
|
+
/**
|
|
1207
|
+
* Verifies the signed token a tenant presents in the wire `tenant` field and DERIVES the real
|
|
1208
|
+
* tenant from it (see `hmacTenantVerifier`). Present: an invalid, tampered or absent token is
|
|
1209
|
+
* rejected with an `unauthorized` error reply and the wire claim is discarded in favour of the
|
|
1210
|
+
* verified tenant. Absent: the wire `tenant` is trusted verbatim (prefix/network isolation only).
|
|
1211
|
+
*/
|
|
1212
|
+
verifyTenant?: TenantVerifier | undefined;
|
|
1213
|
+
/**
|
|
1214
|
+
* Warn at construction when no {@link verifyTenant} is set. Default `true`. `DurableModule` passes
|
|
1215
|
+
* `true` only for an explicit `topology: { role: 'control-plane' }`, so a plain operator app that
|
|
1216
|
+
* never serves tenants boots silently.
|
|
1217
|
+
*/
|
|
1218
|
+
warnWithoutVerifier?: boolean | undefined;
|
|
1219
|
+
}
|
|
1168
1220
|
/**
|
|
1169
1221
|
* Operator-side consumer of a tenant's {@link RunRequest}s: answers each one against a
|
|
1170
1222
|
* `RunGateway`, enforcing the tenant boundary before touching the run. For every runId-bearing
|
|
@@ -1173,11 +1225,17 @@ interface RunRequestTransport {
|
|
|
1173
1225
|
* verb, so a tenant can never read or act on another tenant's run. `listRuns` is scoped by
|
|
1174
1226
|
* overwriting the query's `namespace` with the requester's tenant, ignoring whatever the client
|
|
1175
1227
|
* sent. This is the security boundary of the tenant run gateway — do not weaken it.
|
|
1228
|
+
*
|
|
1229
|
+
* With {@link RunRequestResponderOptions.verifyTenant}, every request is authenticated FIRST: the
|
|
1230
|
+
* wire `tenant` is treated as a signed token, the real tenant is derived from it, and an invalid
|
|
1231
|
+
* token short-circuits into an `unauthorized` error reply before any run is touched. A verifier
|
|
1232
|
+
* that throws becomes an error reply too.
|
|
1176
1233
|
*/
|
|
1177
1234
|
declare class RunRequestResponder {
|
|
1178
1235
|
private readonly transport;
|
|
1179
1236
|
private readonly gateway;
|
|
1180
|
-
|
|
1237
|
+
private readonly verifyTenant;
|
|
1238
|
+
constructor(transport: RunRequestTransport, gateway: RunGateway, options?: RunRequestResponderOptions);
|
|
1181
1239
|
/** Register the consumer on the transport. Each request is answered independently; a handler
|
|
1182
1240
|
* failure never throws back into the transport (errors are captured into an error reply). */
|
|
1183
1241
|
start(): void;
|
|
@@ -1375,4 +1433,4 @@ declare class WorkflowService {
|
|
|
1375
1433
|
}): Promise<number>;
|
|
1376
1434
|
}
|
|
1377
1435
|
|
|
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 };
|
|
1436
|
+
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 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 {
|
|
@@ -1402,17 +1427,19 @@ var ProxyRunGateway = class {
|
|
|
1402
1427
|
tenant;
|
|
1403
1428
|
timeoutMs;
|
|
1404
1429
|
capabilityTtlMs;
|
|
1430
|
+
tenantToken;
|
|
1405
1431
|
pending = /* @__PURE__ */ new Map();
|
|
1406
1432
|
/** Verb → when to ask the control plane for it again, after it answered that it does not know it.
|
|
1407
1433
|
* During a rolling upgrade the control plane is several pods of mixed versions behind one queue,
|
|
1408
1434
|
* and a reply does not say which pod sent it, so the whole control plane is assumed old until the
|
|
1409
1435
|
* entry expires. */
|
|
1410
1436
|
unsupportedUntil = /* @__PURE__ */ new Map();
|
|
1411
|
-
constructor(transport, tenant, timeoutMs = 1e4, capabilityTtlMs = 6e4) {
|
|
1437
|
+
constructor(transport, tenant, timeoutMs = 1e4, capabilityTtlMs = 6e4, tenantToken) {
|
|
1412
1438
|
this.transport = transport;
|
|
1413
1439
|
this.tenant = tenant;
|
|
1414
1440
|
this.timeoutMs = timeoutMs;
|
|
1415
1441
|
this.capabilityTtlMs = capabilityTtlMs;
|
|
1442
|
+
this.tenantToken = tenantToken;
|
|
1416
1443
|
this.transport.onRunReply?.((reply) => this.handleReply(reply));
|
|
1417
1444
|
}
|
|
1418
1445
|
handleReply(reply) {
|
|
@@ -1425,7 +1452,10 @@ var ProxyRunGateway = class {
|
|
|
1425
1452
|
} else if (reply.result.error.code === UNSUPPORTED_RUN_REQUEST) {
|
|
1426
1453
|
pending.reject(new UnsupportedRunRequestError(reply.result.error.message));
|
|
1427
1454
|
} else {
|
|
1428
|
-
|
|
1455
|
+
const { message, code } = reply.result.error;
|
|
1456
|
+
pending.reject(code !== void 0 ? Object.assign(new Error(message), {
|
|
1457
|
+
code
|
|
1458
|
+
}) : new Error(message));
|
|
1429
1459
|
}
|
|
1430
1460
|
}
|
|
1431
1461
|
request(body) {
|
|
@@ -1440,11 +1470,17 @@ var ProxyRunGateway = class {
|
|
|
1440
1470
|
reject,
|
|
1441
1471
|
timer
|
|
1442
1472
|
});
|
|
1443
|
-
this.
|
|
1473
|
+
const source = this.tenantToken;
|
|
1474
|
+
const dispatched = typeof source === "function" ? resolveTenantToken2(source).then((tenant) => this.transport.dispatchRunRequest?.({
|
|
1444
1475
|
requestId,
|
|
1445
|
-
tenant
|
|
1476
|
+
tenant,
|
|
1446
1477
|
body
|
|
1447
|
-
})
|
|
1478
|
+
})) : this.transport.dispatchRunRequest?.({
|
|
1479
|
+
requestId,
|
|
1480
|
+
tenant: source ?? this.tenant,
|
|
1481
|
+
body
|
|
1482
|
+
});
|
|
1483
|
+
dispatched?.catch((error) => {
|
|
1448
1484
|
const stillPending = this.pending.get(requestId);
|
|
1449
1485
|
if (!stillPending) return;
|
|
1450
1486
|
clearTimeout(stillPending.timer);
|
|
@@ -1602,7 +1638,8 @@ ProxyRunGateway = _ts_decorate6([
|
|
|
1602
1638
|
typeof Transport === "undefined" ? Object : Transport,
|
|
1603
1639
|
String,
|
|
1604
1640
|
void 0,
|
|
1605
|
-
void 0
|
|
1641
|
+
void 0,
|
|
1642
|
+
typeof TenantTokenSource === "undefined" ? Object : TenantTokenSource
|
|
1606
1643
|
])
|
|
1607
1644
|
], ProxyRunGateway);
|
|
1608
1645
|
|
|
@@ -2727,7 +2764,15 @@ var RunGatewayBootstrap = class RunGatewayBootstrap2 {
|
|
|
2727
2764
|
onRunRequest: onRunRequest.bind(this.transport),
|
|
2728
2765
|
publishRunReply: publishRunReply.bind(this.transport)
|
|
2729
2766
|
};
|
|
2730
|
-
new RunRequestResponder(runRequestTransport, this.gateway
|
|
2767
|
+
new RunRequestResponder(runRequestTransport, this.gateway, {
|
|
2768
|
+
verifyTenant: this.options.verifyTenant,
|
|
2769
|
+
// Mirrors Agora's rule (`warnWithoutVerifier: role === 'control-plane'`): only a deployment
|
|
2770
|
+
// that EXPLICITLY names itself the control plane is told it runs without tenant auth. A plain
|
|
2771
|
+
// operator (role inferred from `store`) is Agora's `standalone`: anyone with broker access
|
|
2772
|
+
// can already enqueue steps and results there, so the warning would be noise on every boot
|
|
2773
|
+
// of an app that never serves tenants.
|
|
2774
|
+
warnWithoutVerifier: this.options.topology?.role === "control-plane"
|
|
2775
|
+
}).start();
|
|
2731
2776
|
}
|
|
2732
2777
|
if (typeof publishTenantEvent === "function") {
|
|
2733
2778
|
const republisher = new TenantEventRepublisher(store, publishTenantEvent.bind(this.transport));
|
|
@@ -2879,6 +2924,7 @@ var DurableModule = class _DurableModule {
|
|
|
2879
2924
|
context,
|
|
2880
2925
|
rehydrate: rehydrate || void 0,
|
|
2881
2926
|
compensationRetries: options.compensationRetries,
|
|
2927
|
+
verifyTenant: options.verifyTenant,
|
|
2882
2928
|
// A non-driving (dashboard/API) operator must not run workflows: enqueue-only, leaving
|
|
2883
2929
|
// each `pending` run in the store for a DRIVING instance's poll. A driving operator gets
|
|
2884
2930
|
// the engine's default in-process dispatcher: for a body registered inline, it runs
|
|
@@ -2919,7 +2965,7 @@ var DurableModule = class _DurableModule {
|
|
|
2919
2965
|
}
|
|
2920
2966
|
return new StoreRunGateway(store, engine);
|
|
2921
2967
|
}
|
|
2922
|
-
return options.transport ? new ProxyRunGateway(options.transport, options.partition ?? "default", options.runGatewayTimeoutMs) : unavailableRunGateway();
|
|
2968
|
+
return options.transport ? new ProxyRunGateway(options.transport, options.partition ?? "default", options.runGatewayTimeoutMs, void 0, options.tenantToken) : unavailableRunGateway();
|
|
2923
2969
|
}, "useFactory"),
|
|
2924
2970
|
inject: [
|
|
2925
2971
|
DURABLE_OPTIONS_CANONICAL7,
|
|
@@ -2954,7 +3000,7 @@ DurableModule = _ts_decorate12([
|
|
|
2954
3000
|
], DurableModule);
|
|
2955
3001
|
|
|
2956
3002
|
// 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";
|
|
3003
|
+
import { hmacTenantVerifier, prevCronFireMs, readSearchAttributes as readSearchAttributes2, RunGateway as RunGateway3, runSchedules as runSchedules2, scheduledRunId, scheduleRunIdAt, signTenantToken, WorkflowEngine as WorkflowEngine7 } from "@dudousxd/nestjs-durable-core";
|
|
2958
3004
|
export {
|
|
2959
3005
|
CONTEXT_ACCESSOR,
|
|
2960
3006
|
DEAD_LETTER_METADATA,
|
|
@@ -3004,6 +3050,7 @@ export {
|
|
|
3004
3050
|
getEntityMeta,
|
|
3005
3051
|
getOnEvents,
|
|
3006
3052
|
getWorkflowMeta,
|
|
3053
|
+
hmacTenantVerifier,
|
|
3007
3054
|
inAppWorkerProviders,
|
|
3008
3055
|
isDeadLetterHandler,
|
|
3009
3056
|
isDrivingOperator,
|
|
@@ -3015,6 +3062,7 @@ export {
|
|
|
3015
3062
|
runSchedules2 as runSchedules,
|
|
3016
3063
|
scheduleRunIdAt,
|
|
3017
3064
|
scheduledRunId,
|
|
3065
|
+
signTenantToken,
|
|
3018
3066
|
thinWorkerProviders,
|
|
3019
3067
|
unavailableRunGateway
|
|
3020
3068
|
};
|