@nodii/grpc-interceptors 0.5.3 → 0.6.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.
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EACV,yBAAyB,EAGzB,gBAAgB,EACjB,MAAM,SAAS,CAAC;AAUjB;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,yBAAyB,GAChC,gBAAgB,CA8FlB"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EACV,yBAAyB,EAGzB,gBAAgB,EACjB,MAAM,SAAS,CAAC;AAUjB;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,yBAAyB,GAChC,gBAAgB,CA0HlB"}
package/dist/factory.js CHANGED
@@ -9,6 +9,9 @@
9
9
  // this lib) before passing it to the composed factory.
10
10
  import { idempotencyGuard } from "@nodii/idempotency";
11
11
  import { sagaContext } from "@nodii/saga";
12
+ import { withMfaRequiredUnary } from "./interceptors/mfa-required";
13
+ import { withSagaEnvelopeUnary } from "./interceptors/saga-envelope";
14
+ import { withSagaIdempotencyUnary } from "./interceptors/saga-idempotency";
12
15
  import { signalBinder } from "./interceptors/signal-binder";
13
16
  import { cancellationGuard, deadlineGuard, enrichAuthContext, errorMap, STANDARD_ERROR_CATALOG, tenantContext, withAuditUnary, withLoggingUnary, auditContext, } from "./interceptors-exports";
14
17
  import { GrpcStatusError } from "./types";
@@ -58,13 +61,36 @@ export function createStandardServerStack(config) {
58
61
  withAuditUnary(config.audit ?? {}),
59
62
  passthrough(), // withAuthUnary slot — service-wired
60
63
  enrichAuthContext(config.enrich ?? {}),
64
+ // OPT-IN saga-envelope leg (D423) — parse the 7 `x-saga-*` headers into
65
+ // `aug.sagaEnvelope` BEFORE the saga-context + idempotency cluster so the
66
+ // saga-idempotency leg can read the full envelope. Omitted = passthrough.
67
+ config.sagaEnvelope ? withSagaEnvelopeUnary() : passthrough(),
61
68
  (methodName, handler) => {
62
69
  const inner = builtSagaContext(methodName, handler);
63
70
  return async (call) => inner(call);
64
71
  },
65
72
  signalBinder(),
73
+ // OPT-IN saga-aware idempotency leg (D423) — engages ONLY for calls
74
+ // carrying a saga envelope; keys on `(tenant_id, method, saga_id,
75
+ // step_id)` over the service idempotency store + rejects a caller key.
76
+ // Ordered BEFORE the generic idempotency-guard so a saga call is claimed
77
+ // under the envelope key (the two are mutually exclusive: saga-idem only
78
+ // runs when an envelope is present, the generic guard's caller-header path
79
+ // is for non-saga mutators).
80
+ config.sagaIdempotency
81
+ ? withSagaIdempotencyUnary(typeof config.sagaIdempotency === "object"
82
+ ? config.sagaIdempotency
83
+ : {})
84
+ : passthrough(),
66
85
  (methodName, handler) => {
67
86
  return async (call) => {
87
+ // A saga call (envelope present) is claimed by the saga-idempotency
88
+ // leg (outer) under the canonical `(tenant, method, saga_id, step_id)`
89
+ // key — the generic caller-header guard MUST NOT also fire (it would
90
+ // wrongly reject the missing caller `x-nodii-idempotency-key`).
91
+ if (call.sagaEnvelope) {
92
+ return handler(call);
93
+ }
68
94
  call.__methodName =
69
95
  methodName;
70
96
  const guarded = builtIdemGuard(handler);
@@ -83,7 +109,10 @@ export function createStandardServerStack(config) {
83
109
  }),
84
110
  ]
85
111
  : []),
86
- passthrough(), // mfaRequiredInterceptor slot service-wired
112
+ // OPT-IN MFA leg (D423) step-up MFA enforcement at the locked mfa slot.
113
+ // Runs AFTER auth/enrich/tenant/audit-context (needs the verified token)
114
+ // but BEFORE deadline/cancellation/error-map. Omitted = passthrough.
115
+ config.mfa ? withMfaRequiredUnary(config.mfa) : passthrough(),
87
116
  deadlineGuard(config.deadline ?? {}),
88
117
  cancellationGuard(config.cancellation ?? {}),
89
118
  errorMap({
@@ -1 +1 @@
1
- {"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -8,6 +8,13 @@ export { auditContext } from "./interceptors/audit-context";
8
8
  export { deadlineGuard } from "./interceptors/deadline-guard";
9
9
  export { cancellationGuard } from "./interceptors/cancellation-guard";
10
10
  export { errorMap, STANDARD_ERROR_CATALOG, } from "./interceptors/error-map";
11
+ export { withMfaRequiredUnary, createMfaPolicy, MFA_FRESHNESS_SECONDS, } from "./interceptors/mfa-required";
12
+ export { withSagaEnvelopeUnary } from "./interceptors/saga-envelope";
13
+ export type { CallWithSagaEnvelope } from "./interceptors/saga-envelope";
14
+ export { withSagaIdempotencyUnary, computeSagaIdempotencyDigest, } from "./interceptors/saga-idempotency";
15
+ export type { SagaIdempotencyConfig } from "./interceptors/saga-idempotency";
16
+ export { parseSagaEnvelope, SAGA_ENVELOPE_HEADERS, } from "./saga-envelope";
17
+ export type { SagaEnvelope, SagaEnvelopeHeaders, SagaEnvelopeParseError, } from "./saga-envelope";
11
18
  export { createStandardServerStack } from "./factory";
12
19
  export { composeUnaryInterceptors } from "./compose";
13
20
  export { getAuditFramingTableName, normaliseServiceName, makeAuditFramingTableDDL, auditFramingIndexDDL, AUDIT_ACTOR_KIND_ENUM_DDL, AUDIT_OUTCOME_ENUM_DDL, PostgresFramingWriter, RecordingFramingWriter, } from "./framing";
@@ -17,6 +24,6 @@ export type { ConfigureGrpcInterceptorsOptions, ConfiguredGrpcInterceptors, Reso
17
24
  export { _resetTelemetryShimForTests, _setTelemetryShimForTests, } from "./telemetry-shim";
18
25
  export * from "./reexports";
19
26
  export type { SagaHeaderInjector } from "./interceptors/saga-headers";
20
- export type { AuditConfig, AuditContextConfig, AuditEmitter, AuthOnCall, CancellationConfig, DbConnection, DeadlineConfig, EnrichAuthConfig, ErrorMapEntry, GrpcStatusCode, InterceptorLogger, LoggingConfig, StandardServerStackConfig, TelemetryShim, TenantContextConfig, UnaryCall, UnaryHandler, UnaryInterceptor, } from "./types";
27
+ export type { AuditConfig, AuditContextConfig, AuditEmitter, AuthOnCall, CancellationConfig, DbConnection, DeadlineConfig, EnrichAuthConfig, ErrorMapEntry, GrpcStatusCode, InterceptorLogger, LoggingConfig, MfaPolicy, MfaRequiredConfig, SagaIdempotencyLegConfig, StandardServerStackConfig, TelemetryShim, TenantContextConfig, UnaryCall, UnaryHandler, UnaryInterceptor, } from "./types";
21
28
  export { GrpcStatusError, NotImplementedError, RpcCancelledError, } from "./types";
22
29
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,QAAQ,sBAAsB,CAAC;AAC5C,eAAO,MAAM,OAAO,UAAU,CAAC;AAG/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EACL,QAAQ,EACR,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAGrD,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,yBAAyB,EACzB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,UAAU,EACV,aAAa,EACb,kBAAkB,GACnB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,gCAAgC,EAChC,0BAA0B,EAC1B,8BAA8B,GAC/B,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAI1B,cAAc,aAAa,CAAC;AAC5B,YAAY,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAGtE,YAAY,EACV,WAAW,EACX,kBAAkB,EAClB,YAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,yBAAyB,EACzB,aAAa,EACb,mBAAmB,EACnB,SAAS,EACT,YAAY,EACZ,gBAAgB,GACjB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,QAAQ,sBAAsB,CAAC;AAC5C,eAAO,MAAM,OAAO,UAAU,CAAC;AAG/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EACL,QAAQ,EACR,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAKlC,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,qBAAqB,GACtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,iCAAiC,CAAC;AACzC,YAAY,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EACL,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,YAAY,EACZ,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAGrD,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,yBAAyB,EACzB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,UAAU,EACV,aAAa,EACb,kBAAkB,GACnB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,gCAAgC,EAChC,0BAA0B,EAC1B,8BAA8B,GAC/B,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAI1B,cAAc,aAAa,CAAC;AAC5B,YAAY,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAGtE,YAAY,EACV,WAAW,EACX,kBAAkB,EAClB,YAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,SAAS,EACT,iBAAiB,EACjB,wBAAwB,EACxB,yBAAyB,EACzB,aAAa,EACb,mBAAmB,EACnB,SAAS,EACT,YAAY,EACZ,gBAAgB,GACjB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -18,6 +18,13 @@ export { auditContext } from "./interceptors/audit-context";
18
18
  export { deadlineGuard } from "./interceptors/deadline-guard";
19
19
  export { cancellationGuard } from "./interceptors/cancellation-guard";
20
20
  export { errorMap, STANDARD_ERROR_CATALOG, } from "./interceptors/error-map";
21
+ // Billing/auth-specific interceptors (D423 / request 388ea83b) — the lib
22
+ // equivalents of billing's hand-rolled MFA + saga-envelope + saga-idempotency
23
+ // stack, gating the D399 createStandardServerStack migration.
24
+ export { withMfaRequiredUnary, createMfaPolicy, MFA_FRESHNESS_SECONDS, } from "./interceptors/mfa-required";
25
+ export { withSagaEnvelopeUnary } from "./interceptors/saga-envelope";
26
+ export { withSagaIdempotencyUnary, computeSagaIdempotencyDigest, } from "./interceptors/saga-idempotency";
27
+ export { parseSagaEnvelope, SAGA_ENVELOPE_HEADERS, } from "./saga-envelope";
21
28
  // Composed-stack factory + composer.
22
29
  export { createStandardServerStack } from "./factory";
23
30
  export { composeUnaryInterceptors } from "./compose";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,gFAAgF;AAChF,EAAE;AACF,mEAAmE;AACnE,oEAAoE;AACpE,qEAAqE;AACrE,oEAAoE;AACpE,6DAA6D;AAE7D,MAAM,CAAC,MAAM,QAAQ,GAAG,mBAAmB,CAAC;AAC5C,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,sBAAsB;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EACL,QAAQ,EACR,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAElC,qCAAqC;AACrC,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAErD,sEAAsE;AACtE,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,yBAAyB,EACzB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAOnB,oCAAoC;AACpC,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAOrB,0BAA0B;AAC1B,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAE1B,0EAA0E;AAC1E,mCAAmC;AACnC,cAAc,aAAa,CAAC;AAwB5B,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,gFAAgF;AAChF,EAAE;AACF,mEAAmE;AACnE,oEAAoE;AACpE,qEAAqE;AACrE,oEAAoE;AACpE,6DAA6D;AAE7D,MAAM,CAAC,MAAM,QAAQ,GAAG,mBAAmB,CAAC;AAC5C,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,sBAAsB;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EACL,QAAQ,EACR,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAElC,yEAAyE;AACzE,8EAA8E;AAC9E,8DAA8D;AAC9D,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,qBAAqB,GACtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAOzB,qCAAqC;AACrC,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAErD,sEAAsE;AACtE,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,yBAAyB,EACzB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAOnB,oCAAoC;AACpC,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAOrB,0BAA0B;AAC1B,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAE1B,0EAA0E;AAC1E,mCAAmC;AACnC,cAAc,aAAa,CAAC;AA2B5B,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { MfaPolicy, MfaRequiredConfig, UnaryInterceptor } from "../types";
2
+ /** Default freshness window — 15 minutes (matches billing `mfaRequired.ts`). */
3
+ export declare const MFA_FRESHNESS_SECONDS: number;
4
+ /**
5
+ * Build a configurable MFA method-policy registry. Pass the set of
6
+ * fully-qualified gRPC methods (`/svc/Method` and/or bare `Method`) that
7
+ * REQUIRE proven MFA. A method NOT in the set is treated as non-MFA
8
+ * (default-allow) — the registry is opt-in, mirroring billing's
9
+ * `MFA_REQUIRED_METHODS` registry but service-supplied.
10
+ */
11
+ export declare function createMfaPolicy(mfaRequiredMethods?: Iterable<string>): MfaPolicy;
12
+ /**
13
+ * `withMfaRequiredUnary` — interceptor factory `(methodName, handler) ->
14
+ * handler`. For methods the policy marks MFA-required, rejects when MFA is
15
+ * absent or staler than `freshnessSeconds`; otherwise (and for every
16
+ * non-MFA method) passes through to the handler.
17
+ */
18
+ export declare function withMfaRequiredUnary(config?: MfaRequiredConfig): UnaryInterceptor;
19
+ //# sourceMappingURL=mfa-required.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mfa-required.d.ts","sourceRoot":"","sources":["../../src/interceptors/mfa-required.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EACV,SAAS,EACT,iBAAiB,EAGjB,gBAAgB,EACjB,MAAM,UAAU,CAAC;AAGlB,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,QAAU,CAAC;AAE7C;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,kBAAkB,GAAE,QAAQ,CAAC,MAAM,CAAM,GACxC,SAAS,CASX;AAiBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,GAAE,iBAAsB,GAC7B,gBAAgB,CAwDlB"}
@@ -0,0 +1,100 @@
1
+ // withMfaRequiredUnary — step-up MFA enforcement on high-privilege mutators.
2
+ //
3
+ // Gate (D423 / request 388ea83b): billing + auth decode the jose `mfa` /
4
+ // `mfa_at` claims on the verified S2S access token and reject MFA-required
5
+ // methods (the MUTATOR/MFA subset) when MFA is absent OR staler than
6
+ // `MFA_FRESHNESS_SECONDS`. This is the lib equivalent of billing's hand-rolled
7
+ // `withMfaRequiredUnary`, with the per-method policy made CALLER-CONFIGURABLE
8
+ // (the lib does not hardcode any service's method list).
9
+ //
10
+ // Convention (from billing `mfaRequired.ts`): the S2S-issuing gateway stamps
11
+ // `mfa: true` + `mfa_at: <ISO timestamp>` on the access token when an admin
12
+ // proves MFA. We DECODE (jose `decodeJwt`, no re-verification — verification
13
+ // already happened upstream in `withAuthUnary`) and read the claims directly.
14
+ //
15
+ // Wrapper order (per D399 § 5.4 + the factory's `mfaRequiredInterceptor` slot):
16
+ // MFA runs AFTER `withAuthUnary` (it needs the verified token) + after
17
+ // enrich/tenant/audit context, but BEFORE deadline/cancellation/error-map.
18
+ // The factory wires it at its locked slot; this is the interceptor primitive.
19
+ //
20
+ // Fail-closed: every rejection branch returns PERMISSION_DENIED — no token,
21
+ // decode failure, `mfa !== true`, missing `mfa_at`, or stale `mfa_at`.
22
+ import { decodeJwt } from "jose";
23
+ import { GrpcStatusError } from "../types";
24
+ /** Default freshness window — 15 minutes (matches billing `mfaRequired.ts`). */
25
+ export const MFA_FRESHNESS_SECONDS = 15 * 60;
26
+ /**
27
+ * Build a configurable MFA method-policy registry. Pass the set of
28
+ * fully-qualified gRPC methods (`/svc/Method` and/or bare `Method`) that
29
+ * REQUIRE proven MFA. A method NOT in the set is treated as non-MFA
30
+ * (default-allow) — the registry is opt-in, mirroring billing's
31
+ * `MFA_REQUIRED_METHODS` registry but service-supplied.
32
+ */
33
+ export function createMfaPolicy(mfaRequiredMethods = []) {
34
+ const set = new Set(mfaRequiredMethods);
35
+ return {
36
+ requiresMfa(methodName) {
37
+ if (set.has(methodName))
38
+ return true;
39
+ const bare = methodName.split("/").pop() ?? methodName;
40
+ return set.has(bare);
41
+ },
42
+ };
43
+ }
44
+ /**
45
+ * Read the raw access token off the augmented call. After `withAuthUnary`
46
+ * the verified token is exposed as `call.auth.rawToken` (grpc-auth S2S
47
+ * context). We tolerate both the nested `auth.rawToken` and a flattened
48
+ * `rawToken` for synthetic-call parity.
49
+ */
50
+ function readRawToken(call) {
51
+ const auth = call.auth;
52
+ const direct = call.rawToken;
53
+ const v = auth?.rawToken ?? auth?.raw_token ?? direct;
54
+ return typeof v === "string" && v.length > 0 ? v : undefined;
55
+ }
56
+ /**
57
+ * `withMfaRequiredUnary` — interceptor factory `(methodName, handler) ->
58
+ * handler`. For methods the policy marks MFA-required, rejects when MFA is
59
+ * absent or staler than `freshnessSeconds`; otherwise (and for every
60
+ * non-MFA method) passes through to the handler.
61
+ */
62
+ export function withMfaRequiredUnary(config = {}) {
63
+ const policy = config.policy ?? createMfaPolicy(config.mfaRequiredMethods ?? []);
64
+ const freshnessSeconds = config.freshnessSeconds ?? MFA_FRESHNESS_SECONDS;
65
+ const now = config.now ?? (() => Date.now());
66
+ return (methodName, handler) => {
67
+ // Non-MFA method — pass straight through (default-allow).
68
+ if (!policy.requiresMfa(methodName))
69
+ return handler;
70
+ return async (call) => {
71
+ const token = readRawToken(call);
72
+ if (!token) {
73
+ throw new GrpcStatusError("PERMISSION_DENIED", `mfa_required: '${methodName}' requires MFA but no token claims available`);
74
+ }
75
+ let mfa;
76
+ let mfaAt;
77
+ try {
78
+ const claims = decodeJwt(token);
79
+ mfa = claims["mfa"];
80
+ mfaAt = claims["mfa_at"];
81
+ }
82
+ catch {
83
+ throw new GrpcStatusError("PERMISSION_DENIED", "mfa_required: token decode failed");
84
+ }
85
+ if (mfa !== true) {
86
+ throw new GrpcStatusError("PERMISSION_DENIED", `mfa_required: '${methodName}' requires mfa=true claim`);
87
+ }
88
+ if (typeof mfaAt !== "string") {
89
+ throw new GrpcStatusError("PERMISSION_DENIED", "mfa_required: missing mfa_at claim");
90
+ }
91
+ const ageMs = now() - Date.parse(mfaAt);
92
+ if (!Number.isFinite(ageMs) || ageMs / 1000 > freshnessSeconds) {
93
+ throw new GrpcStatusError("PERMISSION_DENIED", `mfa_stale: mfa_at older than ${freshnessSeconds}s`);
94
+ }
95
+ // Fresh MFA — proceed.
96
+ return handler(call);
97
+ };
98
+ };
99
+ }
100
+ //# sourceMappingURL=mfa-required.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mfa-required.js","sourceRoot":"","sources":["../../src/interceptors/mfa-required.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { type SagaEnvelope } from "../saga-envelope";
2
+ import type { UnaryCall, UnaryInterceptor } from "../types";
3
+ /** Augmented call that carries the parsed full saga envelope. */
4
+ export interface CallWithSagaEnvelope extends UnaryCall {
5
+ sagaEnvelope?: SagaEnvelope;
6
+ }
7
+ /**
8
+ * `withSagaEnvelopeUnary` — interceptor factory. Attaches `aug.sagaEnvelope`
9
+ * when a full envelope is on the wire; leaves it `undefined` for non-saga
10
+ * calls; rejects malformed envelopes with INVALID_ARGUMENT.
11
+ */
12
+ export declare function withSagaEnvelopeUnary(): UnaryInterceptor;
13
+ //# sourceMappingURL=saga-envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saga-envelope.d.ts","sourceRoot":"","sources":["../../src/interceptors/saga-envelope.ts"],"names":[],"mappings":"AAmBA,OAAO,EACL,KAAK,YAAY,EAGlB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,SAAS,EAAgB,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAG1E,iEAAiE;AACjE,MAAM,WAAW,oBAAqB,SAAQ,SAAS;IACrD,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AA+BD;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,gBAAgB,CA0BxD"}
@@ -0,0 +1,76 @@
1
+ // withSagaEnvelopeUnary — parse the 7 `x-saga-*` headers off the inbound gRPC
2
+ // metadata into a full `SagaEnvelope` attached to the call as
3
+ // `aug.sagaEnvelope`.
4
+ //
5
+ // Gate (D423 / request 388ea83b): billing's `withSagaIdempotency` +
6
+ // saga-aware idempotency leg depend on the FULL envelope (sagaId, stepId,
7
+ // sagaKind, orchestrator, issuedAt, timeoutAt, traceId?) — not just the
8
+ // re-exported generic `sagaContext` which only carries the partial
9
+ // `x-saga-id`/`x-step-id` pair. This interceptor is the lib equivalent of
10
+ // billing's hand-rolled `withSagaEnvelopeUnary`.
11
+ //
12
+ // Behavior:
13
+ // - No saga headers on the wire → `aug.sagaEnvelope === undefined`
14
+ // (a non-saga call) — the handler runs unchanged.
15
+ // - All 7 headers present + valid → the full envelope is attached.
16
+ // - A PARTIAL or INVALID envelope (some-but-not-all fields, bad UUID, bad
17
+ // timestamp, timeout_at <= issued_at) → INVALID_ARGUMENT (fail-loud; the
18
+ // orchestrator emitted a malformed envelope).
19
+ import { parseSagaEnvelope, } from "../saga-envelope";
20
+ import { GrpcStatusError } from "../types";
21
+ /** Read a single header string off the loose `UnaryCall` metadata shape. */
22
+ function readMeta(call, key) {
23
+ const list = call.metadata.get(key);
24
+ if (!list || list.length === 0)
25
+ return undefined;
26
+ const v = list[0];
27
+ if (typeof v === "string")
28
+ return v;
29
+ if (v instanceof Uint8Array)
30
+ return Buffer.from(v).toString("utf8");
31
+ if (v != null &&
32
+ typeof v.toString === "function") {
33
+ return v.toString();
34
+ }
35
+ return undefined;
36
+ }
37
+ function parseErrorDetail(err) {
38
+ switch (err.kind) {
39
+ case "missing":
40
+ return `saga_envelope_incomplete: ${err.field} is required when other envelope fields are present`;
41
+ case "invalid_uuid":
42
+ return `saga_envelope_invalid_uuid: ${err.field} = '${err.value}'`;
43
+ case "invalid_timestamp":
44
+ return `saga_envelope_invalid_timestamp: ${err.field} = '${err.value}' (expected ISO 8601 UTC)`;
45
+ case "timeout_before_issued":
46
+ return `saga_envelope_timeout_before_issued: issued_at=${err.issuedAt} timeout_at=${err.timeoutAt}`;
47
+ }
48
+ }
49
+ /**
50
+ * `withSagaEnvelopeUnary` — interceptor factory. Attaches `aug.sagaEnvelope`
51
+ * when a full envelope is on the wire; leaves it `undefined` for non-saga
52
+ * calls; rejects malformed envelopes with INVALID_ARGUMENT.
53
+ */
54
+ export function withSagaEnvelopeUnary() {
55
+ return (_methodName, handler) => async (call) => {
56
+ const parsed = parseSagaEnvelope({
57
+ sagaId: readMeta(call, "x-saga-id"),
58
+ stepId: readMeta(call, "x-step-id"),
59
+ sagaKind: readMeta(call, "x-saga-kind"),
60
+ orchestrator: readMeta(call, "x-saga-orchestrator"),
61
+ issuedAt: readMeta(call, "x-saga-issued-at"),
62
+ timeoutAt: readMeta(call, "x-saga-timeout-at"),
63
+ traceId: readMeta(call, "x-saga-trace-id"),
64
+ });
65
+ // No envelope on the wire — non-saga call, run handler unchanged.
66
+ if (parsed === null) {
67
+ return handler(call);
68
+ }
69
+ if (!parsed.ok) {
70
+ throw new GrpcStatusError("INVALID_ARGUMENT", parseErrorDetail(parsed.error));
71
+ }
72
+ call.sagaEnvelope = parsed.envelope;
73
+ return handler(call);
74
+ };
75
+ }
76
+ //# sourceMappingURL=saga-envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saga-envelope.js","sourceRoot":"","sources":["../../src/interceptors/saga-envelope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { SagaEnvelope } from "../saga-envelope";
2
+ import type { UnaryCall, UnaryInterceptor } from "../types";
3
+ export interface SagaIdempotencyConfig {
4
+ /**
5
+ * Resolve the AUTHORITATIVE tenant for the call. Defaults to the enriched
6
+ * `auth.requestContext.tenant_id` (set by `enrichAuthContext`) falling back
7
+ * to the saga envelope's `x-saga-tenant-id` is NOT used — the envelope does
8
+ * not carry a tenant; the tenant comes from the verified actor. A tenant-less
9
+ * saga call FAILS CLOSED (D404).
10
+ */
11
+ getTenantId?: (call: UnaryCall) => string | undefined;
12
+ /** Header name the caller would (wrongly) use to supply a key. */
13
+ headerName?: string;
14
+ /** TTL (seconds) for the claim entry. Defaults to library config. */
15
+ ttlSeconds?: number;
16
+ }
17
+ /**
18
+ * Build the canonical saga idempotency key from `(tenant_id, method,
19
+ * saga_id, step_id)`. Exported for parity-fence byte-equivalence tests.
20
+ */
21
+ export declare function computeSagaIdempotencyDigest(tenantId: string, method: string, envelope: Pick<SagaEnvelope, "sagaId" | "stepId">): string;
22
+ /**
23
+ * `withSagaIdempotencyUnary` — interceptor factory. Engages only when the call
24
+ * carries a full saga envelope; rejects a caller idempotency-key; otherwise
25
+ * CLAIMs the canonical key over the service idempotency store with
26
+ * replay/in-flight semantics; fail-closed on a store outage.
27
+ */
28
+ export declare function withSagaIdempotencyUnary(config?: SagaIdempotencyConfig): UnaryInterceptor;
29
+ //# sourceMappingURL=saga-idempotency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saga-idempotency.d.ts","sourceRoot":"","sources":["../../src/interceptors/saga-idempotency.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAgB,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAM1E,MAAM,WAAW,qBAAqB;IACpC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,MAAM,GAAG,SAAS,CAAC;IACtD,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAgCD;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,GAAG,QAAQ,CAAC,GAChD,MAAM,CAMR;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,GAAE,qBAA0B,GACjC,gBAAgB,CAqIlB"}
@@ -0,0 +1,184 @@
1
+ // withSagaIdempotencyUnary — saga-aware idempotency keying leg.
2
+ //
3
+ // Gate (D423 / request 388ea83b): a saga-participant step's idempotency key is
4
+ // owned by the SAGA ENVELOPE, not the caller. This leg, for calls carrying a
5
+ // full `aug.sagaEnvelope` (parsed by `withSagaEnvelopeUnary`), builds a
6
+ // CANONICAL key from `(tenant_id, method, saga_id, step_id)` and CLAIMs it over
7
+ // the SERVICE's own idempotency store (the `@nodii/idempotency` CLAIM path /
8
+ // Redis Lua). It REJECTS a caller-supplied idempotency-key header on a saga
9
+ // call — the envelope owns the key.
10
+ //
11
+ // Canonical key (reconciles comm-doctrine § 7.3 D163 + D404 tenant-scoping):
12
+ // digest = sha256(tenant_id + method + (saga_id + "|" + step_id))
13
+ // via computeIdempotencyKey({sagaId: tenant_id,
14
+ // stepName: method, serializedInput: saga_id|step_id})
15
+ // redis key = nodii:<service>:saga:idempotency:saga:<digest>
16
+ // (tenant folded into the digest per D404 — fail-closed if tenant absent)
17
+ //
18
+ // NON-saga calls (no envelope) pass through untouched — the standard
19
+ // `idempotencyGuard` leg (caller header `x-nodii-idempotency-key`) handles
20
+ // non-saga mutators. This leg ONLY engages when `aug.sagaEnvelope` is set.
21
+ //
22
+ // FAULT-INJECTION posture (Pillar 5): the idempotency store (Redis) is an
23
+ // external dependency. A CLAIM-time store outage FAILS CLOSED — the exception
24
+ // propagates (mapped downstream to UNAVAILABLE by errorMap) and the handler is
25
+ // NEVER run without the dedupe guarantee. This matches `@nodii/idempotency`'s
26
+ // own posture: only logical outcomes (completed / in_flight / failed) are
27
+ // return values; an escaped exception is by definition infra → fail closed.
28
+ import { assembleSagaStepKey, computeIdempotencyKey, getIdempotencyOrNull, } from "@nodii/idempotency";
29
+ import { GrpcStatusError } from "../types";
30
+ /** Caller-supplied idempotency-key header name (comm-doctrine § 7.4). */
31
+ const DEFAULT_HEADER = "x-nodii-idempotency-key";
32
+ function defaultTenantId(call) {
33
+ const auth = call.auth;
34
+ const fromCtx = auth?.requestContext?.tenant_id;
35
+ if (typeof fromCtx === "string" && fromCtx.length > 0)
36
+ return fromCtx;
37
+ const fromUser = auth?.userActor?.tenant_id;
38
+ if (typeof fromUser === "string" && fromUser.length > 0)
39
+ return fromUser;
40
+ if (typeof auth?.tenantId === "string" && auth.tenantId.length > 0) {
41
+ return auth.tenantId;
42
+ }
43
+ return undefined;
44
+ }
45
+ function readHeader(call, name) {
46
+ const list = call.metadata.get(name);
47
+ if (!list || list.length === 0)
48
+ return undefined;
49
+ const v = list[0];
50
+ if (typeof v === "string")
51
+ return v.length > 0 ? v : undefined;
52
+ if (v instanceof Uint8Array) {
53
+ const s = Buffer.from(v).toString("utf8");
54
+ return s.length > 0 ? s : undefined;
55
+ }
56
+ return undefined;
57
+ }
58
+ /**
59
+ * Build the canonical saga idempotency key from `(tenant_id, method,
60
+ * saga_id, step_id)`. Exported for parity-fence byte-equivalence tests.
61
+ */
62
+ export function computeSagaIdempotencyDigest(tenantId, method, envelope) {
63
+ return computeIdempotencyKey({
64
+ sagaId: tenantId,
65
+ stepName: method,
66
+ serializedInput: `${envelope.sagaId}|${envelope.stepId}`,
67
+ });
68
+ }
69
+ /**
70
+ * `withSagaIdempotencyUnary` — interceptor factory. Engages only when the call
71
+ * carries a full saga envelope; rejects a caller idempotency-key; otherwise
72
+ * CLAIMs the canonical key over the service idempotency store with
73
+ * replay/in-flight semantics; fail-closed on a store outage.
74
+ */
75
+ export function withSagaIdempotencyUnary(config = {}) {
76
+ const headerName = config.headerName ?? DEFAULT_HEADER;
77
+ const getTenantId = config.getTenantId ?? defaultTenantId;
78
+ return (methodName, handler) => async (call) => {
79
+ const envelope = call.sagaEnvelope;
80
+ // No saga envelope → non-saga path; the standard idempotency leg owns it.
81
+ if (!envelope)
82
+ return handler(call);
83
+ // The saga envelope OWNS the key. A caller idempotency-key on a saga call
84
+ // is a contract violation (two competing key owners).
85
+ const callerKey = readHeader(call, headerName);
86
+ if (callerKey) {
87
+ throw new GrpcStatusError("INVALID_ARGUMENT", "idempotency_key_conflict_with_saga: do not pass an idempotency-key header when a saga envelope is present");
88
+ }
89
+ // Tenant is folded into the digest (D404) — fail closed if absent.
90
+ const tenantId = getTenantId(call);
91
+ if (!tenantId) {
92
+ throw new GrpcStatusError("INVALID_ARGUMENT", "idempotency_tenant_required: saga idempotency key needs a resolved tenant");
93
+ }
94
+ const cfg = getIdempotencyOrNull();
95
+ if (!cfg) {
96
+ // `initIdempotency` must run at boot before the stack handles a saga
97
+ // call. Missing init is a wiring bug — fail loud, never run unguarded.
98
+ throw new GrpcStatusError("INTERNAL", "saga_idempotency_not_initialized: call initIdempotency() at boot");
99
+ }
100
+ const digest = computeSagaIdempotencyDigest(tenantId, methodName, envelope);
101
+ const key = assembleSagaStepKey(cfg.serviceId, digest, "saga");
102
+ const client = cfg.redisClient;
103
+ if (typeof client.claim !== "function") {
104
+ throw new GrpcStatusError("INTERNAL", "saga_idempotency_requires_claim_capable_client");
105
+ }
106
+ const nowMs = Date.now();
107
+ const ttl = config.ttlSeconds ?? cfg.defaultTtlSeconds;
108
+ const inflightPayload = JSON.stringify({
109
+ status: "in_flight",
110
+ created_at: new Date(nowMs).toISOString(),
111
+ created_at_ms: nowMs,
112
+ });
113
+ // CLAIM — a store outage here THROWS and propagates: FAIL CLOSED. The
114
+ // handler is never run without the dedupe guarantee (errorMap → UNAVAILABLE).
115
+ const outcome = await client.claim(key, inflightPayload, ttl); // fault-injection:allow @nodii/idempotency store op — store-down is fault-injection tested (FailingRedisClient.claim raises -> fail-closed, handler never runs); Pillar 5 mis-categorizes the idempotency-store client as grpc (categorization follow-up tracked).
116
+ if (outcome.state === "completed") {
117
+ // Idempotent replay — return the cached result without re-running.
118
+ return outcome.result;
119
+ }
120
+ if (outcome.state === "failed") {
121
+ // Cached application-class failure — replay the same rejection.
122
+ const err = outcome.error;
123
+ throw new GrpcStatusError("ABORTED", typeof err === "object" && err && "message" in err
124
+ ? String(err.message)
125
+ : "saga_idempotency_cached_failure");
126
+ }
127
+ if (outcome.state === "in_flight") {
128
+ throw new GrpcStatusError("ABORTED", "idempotency_in_flight");
129
+ }
130
+ // Acquired — run the handler, then COMPLETE (or release on error).
131
+ let res;
132
+ try {
133
+ res = await handler(call);
134
+ }
135
+ catch (err) {
136
+ // Best-effort release so a retry can re-claim the slot.
137
+ try {
138
+ await client.del(key); // fault-injection:allow @nodii/idempotency store op — store-down is fault-injection tested (FailingRedisClient.claim raises -> fail-closed, handler never runs); Pillar 5 mis-categorizes the idempotency-store client as grpc (categorization follow-up tracked).
139
+ }
140
+ catch {
141
+ // best-effort
142
+ }
143
+ throw err;
144
+ }
145
+ // POST-HANDLER completion persist. The handler ALREADY ran + succeeded;
146
+ // its side-effects are committed. A store fault HERE must NOT convert a
147
+ // successful call into a client-visible failure (and must not pin the
148
+ // slot `in_flight` until TTL, which would make the legitimate retry hit
149
+ // `idempotency_in_flight`). So persisting the completion is BEST-EFFORT:
150
+ // on a store fault we release the in_flight slot (so a retry can re-claim
151
+ // + re-run — at most one redundant execution, the documented dedupe-window
152
+ // tradeoff) and return the successful result. This intentional behavior is
153
+ // fault-injection tested (see mfa-saga.test.ts "FAULT: store-down on the
154
+ // POST-handler completion path").
155
+ const completedPayload = JSON.stringify({
156
+ status: "completed",
157
+ result: res,
158
+ completed_at: new Date().toISOString(),
159
+ created_at_ms: nowMs,
160
+ outcome_class: "application",
161
+ });
162
+ try {
163
+ if (typeof client.complete === "function") {
164
+ await client.complete(key, completedPayload, ttl); // fault-injection:allow @nodii/idempotency store op — store-down on the POST-handler completion path is fault-injection tested (best-effort: handler result preserved, in_flight slot released, no crash); Pillar 5 mis-categorizes the idempotency-store client as grpc (categorization follow-up tracked).
165
+ }
166
+ else {
167
+ await client.del(key); // fault-injection:allow @nodii/idempotency store op — store-down on the POST-handler completion path is fault-injection tested (best-effort: handler result preserved, in_flight slot released, no crash); Pillar 5 mis-categorizes the idempotency-store client as grpc (categorization follow-up tracked).
168
+ await client.setNX(key, completedPayload, ttl); // fault-injection:allow @nodii/idempotency store op — store-down on the POST-handler completion path is fault-injection tested (best-effort: handler result preserved, in_flight slot released, no crash); Pillar 5 mis-categorizes the idempotency-store client as grpc (categorization follow-up tracked).
169
+ }
170
+ }
171
+ catch {
172
+ // Best-effort: the handler succeeded; do NOT fail the call. Release the
173
+ // in_flight slot so a retry can re-claim (re-run is idempotency-safe).
174
+ try {
175
+ await client.del(key); // fault-injection:allow @nodii/idempotency store op — store-down on the POST-handler completion path is fault-injection tested (best-effort: handler result preserved, in_flight slot released, no crash); Pillar 5 mis-categorizes the idempotency-store client as grpc (categorization follow-up tracked).
176
+ }
177
+ catch {
178
+ // best-effort
179
+ }
180
+ }
181
+ return res;
182
+ };
183
+ }
184
+ //# sourceMappingURL=saga-idempotency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saga-idempotency.js","sourceRoot":"","sources":["../../src/interceptors/saga-idempotency.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /** The full saga envelope attached to the call by `withSagaEnvelopeUnary`. */
2
+ export interface SagaEnvelope {
3
+ /** Orchestrator-generated UUID, stable across the whole saga. */
4
+ sagaId: string;
5
+ /** Orchestrator-generated UUID, unique per participant step. */
6
+ stepId: string;
7
+ /** Saga kind from the sagas.json registry. */
8
+ sagaKind: string;
9
+ /** Service id of the orchestrator (`tenant`, `billing`, …). */
10
+ orchestrator: string;
11
+ /** When the orchestrator issued this RPC. */
12
+ issuedAt: Date;
13
+ /**
14
+ * When the orchestrator considers this step timed out. Participants SHOULD
15
+ * short-circuit if past it.
16
+ */
17
+ timeoutAt: Date;
18
+ /** Optional W3C traceparent — pinned to the per-step OTel span. */
19
+ traceId?: string;
20
+ }
21
+ export type SagaEnvelopeParseError = {
22
+ kind: "missing";
23
+ field: string;
24
+ } | {
25
+ kind: "invalid_uuid";
26
+ field: string;
27
+ value: string;
28
+ } | {
29
+ kind: "invalid_timestamp";
30
+ field: string;
31
+ value: string;
32
+ } | {
33
+ kind: "timeout_before_issued";
34
+ issuedAt: string;
35
+ timeoutAt: string;
36
+ };
37
+ /** The 7 `x-saga-*` headers, in canonical order. */
38
+ export declare const SAGA_ENVELOPE_HEADERS: {
39
+ readonly sagaId: "x-saga-id";
40
+ readonly stepId: "x-step-id";
41
+ readonly sagaKind: "x-saga-kind";
42
+ readonly orchestrator: "x-saga-orchestrator";
43
+ readonly issuedAt: "x-saga-issued-at";
44
+ readonly timeoutAt: "x-saga-timeout-at";
45
+ readonly traceId: "x-saga-trace-id";
46
+ };
47
+ export interface SagaEnvelopeHeaders {
48
+ sagaId?: string;
49
+ stepId?: string;
50
+ sagaKind?: string;
51
+ orchestrator?: string;
52
+ issuedAt?: string;
53
+ timeoutAt?: string;
54
+ traceId?: string;
55
+ }
56
+ /**
57
+ * Build a SagaEnvelope from the raw header strings. Returns the envelope on
58
+ * success, or a typed error describing the first validation failure.
59
+ *
60
+ * Returns `null` when NONE of the required envelope fields are present —
61
+ * caller treats that as "no envelope sent" (a non-saga call).
62
+ */
63
+ export declare function parseSagaEnvelope(headers: SagaEnvelopeHeaders): {
64
+ ok: true;
65
+ envelope: SagaEnvelope;
66
+ } | {
67
+ ok: false;
68
+ error: SagaEnvelopeParseError;
69
+ } | null;
70
+ //# sourceMappingURL=saga-envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saga-envelope.d.ts","sourceRoot":"","sources":["../src/saga-envelope.ts"],"names":[],"mappings":"AAyBA,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,YAAY,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,QAAQ,EAAE,IAAI,CAAC;IACf;;;OAGG;IACH,SAAS,EAAE,IAAI,CAAC;IAChB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,uBAAuB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAM3E,oDAAoD;AACpD,eAAO,MAAM,qBAAqB;;;;;;;;CAQxB,CAAC;AAEX,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,mBAAmB,GAE1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAA;CAAE,GACpC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,sBAAsB,CAAA;CAAE,GAC5C,IAAI,CA2FP"}
@@ -0,0 +1,131 @@
1
+ // SagaEnvelope — the doctrine-locked metadata carried on every saga-participant
2
+ // gRPC RPC. Wire format on gRPC metadata is discrete per-field headers (NOT a
3
+ // single JSON blob), to match the existing partial-envelope `x-saga-id` /
4
+ // `x-step-id` headers handled by the saga context interceptor. The full
5
+ // envelope is 7 `x-saga-*` headers:
6
+ //
7
+ // x-saga-id — UUID (orchestrator-stable saga id)
8
+ // x-step-id — UUID (unique per participant step)
9
+ // x-saga-kind — string from the sagas.json registry
10
+ // x-saga-orchestrator — service id of the orchestrator
11
+ // x-saga-issued-at — ISO 8601 UTC
12
+ // x-saga-timeout-at — ISO 8601 UTC
13
+ // x-saga-trace-id — W3C traceparent (OPTIONAL)
14
+ //
15
+ // Per comm-doctrine § 5.1 (`x-nodii-saga-id`, `x-nodii-saga-trace-id`) the
16
+ // auto-injected client headers exist; the FULL per-step envelope (this file)
17
+ // is the participant-side IPC contract billing's `withSagaIdempotency` depends
18
+ // on. Backwards compat: when ONLY `x-saga-id` + `x-step-id` are present, the
19
+ // legacy saga-context interceptor handles them (no envelope built); when all
20
+ // required envelope fields are present, `withSagaEnvelopeUnary` builds the full
21
+ // envelope and attaches it to the call as `aug.sagaEnvelope`.
22
+ //
23
+ // Ported from billing's `src/lib/saga/SagaEnvelope.ts` into the lib so every
24
+ // adopter gets the identical parse from `@nodii/grpc-interceptors`.
25
+ // UUID v1-v8. Matches the other interceptors' regex shape.
26
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
27
+ /** The 7 `x-saga-*` headers, in canonical order. */
28
+ export const SAGA_ENVELOPE_HEADERS = {
29
+ sagaId: "x-saga-id",
30
+ stepId: "x-step-id",
31
+ sagaKind: "x-saga-kind",
32
+ orchestrator: "x-saga-orchestrator",
33
+ issuedAt: "x-saga-issued-at",
34
+ timeoutAt: "x-saga-timeout-at",
35
+ traceId: "x-saga-trace-id",
36
+ };
37
+ /**
38
+ * Build a SagaEnvelope from the raw header strings. Returns the envelope on
39
+ * success, or a typed error describing the first validation failure.
40
+ *
41
+ * Returns `null` when NONE of the required envelope fields are present —
42
+ * caller treats that as "no envelope sent" (a non-saga call).
43
+ */
44
+ export function parseSagaEnvelope(headers) {
45
+ // None of the required fields present → no envelope on the wire.
46
+ const anyPresent = headers.sagaId !== undefined ||
47
+ headers.stepId !== undefined ||
48
+ headers.sagaKind !== undefined ||
49
+ headers.orchestrator !== undefined ||
50
+ headers.issuedAt !== undefined ||
51
+ headers.timeoutAt !== undefined;
52
+ if (!anyPresent)
53
+ return null;
54
+ // When ANY required field is present, ALL of them must be present — a
55
+ // partial envelope is a wire-contract violation.
56
+ for (const [field, value] of [
57
+ ["saga_id", headers.sagaId],
58
+ ["step_id", headers.stepId],
59
+ ["saga_kind", headers.sagaKind],
60
+ ["orchestrator", headers.orchestrator],
61
+ ["issued_at", headers.issuedAt],
62
+ ["timeout_at", headers.timeoutAt],
63
+ ]) {
64
+ if (value === undefined || value.length === 0) {
65
+ return { ok: false, error: { kind: "missing", field } };
66
+ }
67
+ }
68
+ const sagaId = headers.sagaId;
69
+ const stepId = headers.stepId;
70
+ const sagaKind = headers.sagaKind;
71
+ const orchestrator = headers.orchestrator;
72
+ const issuedAtRaw = headers.issuedAt;
73
+ const timeoutAtRaw = headers.timeoutAt;
74
+ if (!UUID_RE.test(sagaId)) {
75
+ return {
76
+ ok: false,
77
+ error: { kind: "invalid_uuid", field: "saga_id", value: sagaId },
78
+ };
79
+ }
80
+ if (!UUID_RE.test(stepId)) {
81
+ return {
82
+ ok: false,
83
+ error: { kind: "invalid_uuid", field: "step_id", value: stepId },
84
+ };
85
+ }
86
+ const issuedAt = new Date(issuedAtRaw);
87
+ if (Number.isNaN(issuedAt.getTime())) {
88
+ return {
89
+ ok: false,
90
+ error: {
91
+ kind: "invalid_timestamp",
92
+ field: "issued_at",
93
+ value: issuedAtRaw,
94
+ },
95
+ };
96
+ }
97
+ const timeoutAt = new Date(timeoutAtRaw);
98
+ if (Number.isNaN(timeoutAt.getTime())) {
99
+ return {
100
+ ok: false,
101
+ error: {
102
+ kind: "invalid_timestamp",
103
+ field: "timeout_at",
104
+ value: timeoutAtRaw,
105
+ },
106
+ };
107
+ }
108
+ if (timeoutAt.getTime() <= issuedAt.getTime()) {
109
+ return {
110
+ ok: false,
111
+ error: {
112
+ kind: "timeout_before_issued",
113
+ issuedAt: issuedAtRaw,
114
+ timeoutAt: timeoutAtRaw,
115
+ },
116
+ };
117
+ }
118
+ return {
119
+ ok: true,
120
+ envelope: {
121
+ sagaId,
122
+ stepId,
123
+ sagaKind,
124
+ orchestrator,
125
+ issuedAt,
126
+ timeoutAt,
127
+ traceId: headers.traceId,
128
+ },
129
+ };
130
+ }
131
+ //# sourceMappingURL=saga-envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saga-envelope.js","sourceRoot":"","sources":["../src/saga-envelope.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,8EAA8E;AAC9E,0EAA0E;AAC1E,wEAAwE;AACxE,oCAAoC;AACpC,EAAE;AACF,8DAA8D;AAC9D,8DAA8D;AAC9D,+DAA+D;AAC/D,0DAA0D;AAC1D,wCAAwC;AACxC,wCAAwC;AACxC,sDAAsD;AACtD,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,+EAA+E;AAC/E,6EAA6E;AAC7E,6EAA6E;AAC7E,gFAAgF;AAChF,8DAA8D;AAC9D,EAAE;AACF,6EAA6E;AAC7E,oEAAoE;AA6BpE,2DAA2D;AAC3D,MAAM,OAAO,GACX,4EAA4E,CAAC;AAE/E,oDAAoD;AACpD,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;IACnB,QAAQ,EAAE,aAAa;IACvB,YAAY,EAAE,qBAAqB;IACnC,QAAQ,EAAE,kBAAkB;IAC5B,SAAS,EAAE,mBAAmB;IAC9B,OAAO,EAAE,iBAAiB;CAClB,CAAC;AAYX;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAA4B;IAK5B,iEAAiE;IACjE,MAAM,UAAU,GACd,OAAO,CAAC,MAAM,KAAK,SAAS;QAC5B,OAAO,CAAC,MAAM,KAAK,SAAS;QAC5B,OAAO,CAAC,QAAQ,KAAK,SAAS;QAC9B,OAAO,CAAC,YAAY,KAAK,SAAS;QAClC,OAAO,CAAC,QAAQ,KAAK,SAAS;QAC9B,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC;IAClC,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAE7B,sEAAsE;IACtE,iDAAiD;IACjD,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI;QAC3B,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC;QAC3B,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC;QAC3B,CAAC,WAAW,EAAE,OAAO,CAAC,QAAQ,CAAC;QAC/B,CAAC,cAAc,EAAE,OAAO,CAAC,YAAY,CAAC;QACtC,CAAC,WAAW,EAAE,OAAO,CAAC,QAAQ,CAAC;QAC/B,CAAC,YAAY,EAAE,OAAO,CAAC,SAAS,CAAC;KACzB,EAAE,CAAC;QACX,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,MAAgB,CAAC;IACxC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAgB,CAAC;IACxC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAkB,CAAC;IAC5C,MAAM,YAAY,GAAG,OAAO,CAAC,YAAsB,CAAC;IACpD,MAAM,WAAW,GAAG,OAAO,CAAC,QAAkB,CAAC;IAC/C,MAAM,YAAY,GAAG,OAAO,CAAC,SAAmB,CAAC;IAEjD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE;SACjE,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE;SACjE,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC;IACvC,IAAI,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QACrC,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE;gBACL,IAAI,EAAE,mBAAmB;gBACzB,KAAK,EAAE,WAAW;gBAClB,KAAK,EAAE,WAAW;aACnB;SACF,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC;IACzC,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QACtC,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE;gBACL,IAAI,EAAE,mBAAmB;gBACzB,KAAK,EAAE,YAAY;gBACnB,KAAK,EAAE,YAAY;aACpB;SACF,CAAC;IACJ,CAAC;IACD,IAAI,SAAS,CAAC,OAAO,EAAE,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;QAC9C,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE;gBACL,IAAI,EAAE,uBAAuB;gBAC7B,QAAQ,EAAE,WAAW;gBACrB,SAAS,EAAE,YAAY;aACxB;SACF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,EAAE,EAAE,IAAI;QACR,QAAQ,EAAE;YACR,MAAM;YACN,MAAM;YACN,QAAQ;YACR,YAAY;YACZ,QAAQ;YACR,SAAS;YACT,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB;KACF,CAAC;AACJ,CAAC"}
package/dist/types.d.ts CHANGED
@@ -38,6 +38,12 @@ export interface UnaryCall {
38
38
  * mutates onto the call.
39
39
  */
40
40
  export interface AuthOnCall {
41
+ /**
42
+ * The verified raw access token (set by `withAuthUnary`). The MFA leg
43
+ * decodes it (jose, no re-verification — verification already happened in
44
+ * `withAuthUnary`) to read the `mfa` / `mfa_at` step-up claims.
45
+ */
46
+ rawToken?: string;
41
47
  /** Service-to-service actor stamp. */
42
48
  serviceActor?: {
43
49
  service_id?: string;
@@ -186,6 +192,35 @@ export interface AuditConfig {
186
192
  /** Methods skipped entirely (e.g. health checks). */
187
193
  skipMethods?: ReadonlySet<string>;
188
194
  }
195
+ /**
196
+ * MFA method-policy — decides which methods require proven step-up MFA.
197
+ * Caller-configurable: the lib hardcodes no service's method list.
198
+ */
199
+ export interface MfaPolicy {
200
+ requiresMfa(methodName: string): boolean;
201
+ }
202
+ /** Config for `withMfaRequiredUnary` — spec D423 / request 388ea83b. */
203
+ export interface MfaRequiredConfig {
204
+ /**
205
+ * Fully-qualified (`/svc/Method`) and/or bare (`Method`) gRPC methods that
206
+ * REQUIRE proven MFA. Used to build the default `MfaPolicy` when `policy`
207
+ * is not supplied. A method absent from the set is non-MFA (default-allow).
208
+ */
209
+ mfaRequiredMethods?: Iterable<string>;
210
+ /** A pre-built policy — takes precedence over `mfaRequiredMethods`. */
211
+ policy?: MfaPolicy;
212
+ /** Freshness window in seconds. Default 900 (15 min). */
213
+ freshnessSeconds?: number;
214
+ /** Clock override for deterministic tests. Default `Date.now`. */
215
+ now?: () => number;
216
+ }
217
+ /** Config for `withSagaIdempotencyUnary` — spec D423 / request 388ea83b. */
218
+ export interface SagaIdempotencyLegConfig {
219
+ /** Header name a caller would (wrongly) use. Default `x-nodii-idempotency-key`. */
220
+ headerName?: string;
221
+ /** TTL (seconds) for the saga claim entry. Defaults to library config. */
222
+ ttlSeconds?: number;
223
+ }
189
224
  /** Deadline guard config — spec § 5.10. */
190
225
  export interface DeadlineConfig {
191
226
  /** Budget reserved at the inbound boundary in ms. Default 50ms per § 5.10. */
@@ -259,6 +294,27 @@ export interface StandardServerStackConfig {
259
294
  requireSagaContextMethods?: ReadonlySet<string>;
260
295
  /** sagaContext enforcement mode: 'warn' (default) | 'reject'. */
261
296
  sagaContextEnforce?: "warn" | "reject";
297
+ /**
298
+ * OPT-IN MFA leg (D423 / request 388ea83b). When set, wires
299
+ * `withMfaRequiredUnary` at the locked MFA slot — methods the policy marks
300
+ * MFA-required are rejected without fresh proven MFA. Omitted = MFA slot is
301
+ * a pure passthrough (back-compat).
302
+ */
303
+ mfa?: MfaRequiredConfig;
304
+ /**
305
+ * OPT-IN saga-envelope leg (D423 / request 388ea83b). When `true`, wires
306
+ * `withSagaEnvelopeUnary` so the full 7-header `x-saga-*` envelope is parsed
307
+ * onto `aug.sagaEnvelope`. Required for the saga-idempotency leg. Omitted =
308
+ * no envelope parsing (back-compat: only the generic `sagaContext` runs).
309
+ */
310
+ sagaEnvelope?: boolean;
311
+ /**
312
+ * OPT-IN saga-aware idempotency leg (D423 / request 388ea83b). When set,
313
+ * wires `withSagaIdempotencyUnary` — a canonical `(tenant_id, method,
314
+ * saga_id, step_id)` key over the service idempotency store for saga calls,
315
+ * rejecting a caller idempotency-key. Requires `sagaEnvelope: true`.
316
+ */
317
+ sagaIdempotency?: SagaIdempotencyLegConfig | boolean;
262
318
  }
263
319
  /** Subset of `@nodii/telemetry` we depend on at runtime. */
264
320
  export interface TelemetryShim {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,gFAAgF;AAChF,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,2EAA2E;AAC3E,wEAAwE;AAgHxE,gFAAgF;AAChF,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAiB;IACrB,MAAM,CAAS;IACxB,YAAY,IAAoB,EAAE,MAAc,EAAE,KAAe;QAC/D,KAAK,CAAC,GAAG,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,KAAK,KAAK,SAAS;YAAG,IAA4B,CAAC,KAAK,GAAG,KAAK,CAAC;IACvE,CAAC;CACF;AAED,8EAA8E;AAC9E,MAAM,OAAO,iBAAkB,SAAQ,eAAe;IACpD,YAAY,MAAM,GAAG,mBAAmB;QACtC,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,4DAA4D;AAC5D,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,gFAAgF;AAChF,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,2EAA2E;AAC3E,wEAAwE;AAsHxE,gFAAgF;AAChF,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAiB;IACrB,MAAM,CAAS;IACxB,YAAY,IAAoB,EAAE,MAAc,EAAE,KAAe;QAC/D,KAAK,CAAC,GAAG,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,KAAK,KAAK,SAAS;YAAG,IAA4B,CAAC,KAAK,GAAG,KAAK,CAAC;IACvE,CAAC;CACF;AAED,8EAA8E;AAC9E,MAAM,OAAO,iBAAkB,SAAQ,eAAe;IACpD,YAAY,MAAM,GAAG,mBAAmB;QACtC,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,4DAA4D;AAC5D,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodii/grpc-interceptors",
3
- "version": "0.5.3",
3
+ "version": "0.6.0",
4
4
  "description": "Substrate gRPC interceptor library for the Nodii microservice stack — 8 cross-cutting interceptors (logging, audit, enrichAuthContext, tenantContext, auditContext, deadlineGuard, cancellationGuard, errorMap) + re-export façade over @nodii/grpc-auth/saga/idempotency + locked-order createStandardServerStack factory. Spec: planning hub docKey=grpc-interceptors.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,15 +23,18 @@
23
23
  "typecheck": "tsc --noEmit",
24
24
  "test": "bun test"
25
25
  },
26
+ "dependencies": {
27
+ "jose": "^5.9.6"
28
+ },
26
29
  "devDependencies": {
27
- "@nodii/audit-chain": "0.9.1",
30
+ "@nodii/audit-chain": "0.10.0",
28
31
  "ioredis": "^5.4.0",
29
32
  "postgres": "^3.4.0",
30
33
  "typescript": "^5.9.3",
31
34
  "@nodii/grpc-auth": "0.9.1",
32
35
  "@nodii/idempotency": "0.6.1",
33
36
  "@nodii/saga": "0.8.0",
34
- "@nodii/telemetry": "0.9.1"
37
+ "@nodii/telemetry": "0.13.0"
35
38
  },
36
39
  "repository": {
37
40
  "type": "git",