@nodii/grpc-interceptors 0.0.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compose.d.ts +7 -0
- package/dist/compose.d.ts.map +1 -0
- package/dist/compose.js +20 -0
- package/dist/compose.js.map +1 -0
- package/dist/configure.d.ts +66 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/configure.js +169 -0
- package/dist/configure.js.map +1 -0
- package/dist/factory.d.ts +7 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +100 -0
- package/dist/factory.js.map +1 -0
- package/dist/index.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -4
- package/dist/index.js.map +1 -1
- package/dist/interceptors/audit-context.d.ts +3 -0
- package/dist/interceptors/audit-context.d.ts.map +1 -0
- package/dist/interceptors/audit-context.js +59 -0
- package/dist/interceptors/audit-context.js.map +1 -0
- package/dist/interceptors/audit.d.ts +7 -0
- package/dist/interceptors/audit.d.ts.map +1 -0
- package/dist/interceptors/audit.js +111 -0
- package/dist/interceptors/audit.js.map +1 -0
- package/dist/interceptors/cancellation-guard.d.ts +3 -0
- package/dist/interceptors/cancellation-guard.d.ts.map +1 -0
- package/dist/interceptors/cancellation-guard.js +54 -0
- package/dist/interceptors/cancellation-guard.js.map +1 -0
- package/dist/interceptors/deadline-guard.d.ts +3 -0
- package/dist/interceptors/deadline-guard.d.ts.map +1 -0
- package/dist/interceptors/deadline-guard.js +90 -0
- package/dist/interceptors/deadline-guard.js.map +1 -0
- package/dist/interceptors/enrich-auth.d.ts +3 -0
- package/dist/interceptors/enrich-auth.d.ts.map +1 -0
- package/dist/interceptors/enrich-auth.js +85 -0
- package/dist/interceptors/enrich-auth.js.map +1 -0
- package/dist/interceptors/error-map.d.ts +4 -0
- package/dist/interceptors/error-map.d.ts.map +1 -0
- package/dist/interceptors/error-map.js +94 -0
- package/dist/interceptors/error-map.js.map +1 -0
- package/dist/interceptors/logging.d.ts +7 -0
- package/dist/interceptors/logging.d.ts.map +1 -0
- package/dist/interceptors/logging.js +66 -0
- package/dist/interceptors/logging.js.map +1 -0
- package/dist/interceptors/saga-headers.d.ts +23 -0
- package/dist/interceptors/saga-headers.d.ts.map +1 -0
- package/dist/interceptors/saga-headers.js +40 -0
- package/dist/interceptors/saga-headers.js.map +1 -0
- package/dist/interceptors/signal-binder.d.ts +3 -0
- package/dist/interceptors/signal-binder.d.ts.map +1 -0
- package/dist/interceptors/signal-binder.js +83 -0
- package/dist/interceptors/signal-binder.js.map +1 -0
- package/dist/interceptors/tenant-context.d.ts +3 -0
- package/dist/interceptors/tenant-context.d.ts.map +1 -0
- package/dist/interceptors/tenant-context.js +40 -0
- package/dist/interceptors/tenant-context.js.map +1 -0
- package/dist/interceptors-exports.d.ts +9 -0
- package/dist/interceptors-exports.d.ts.map +1 -0
- package/dist/interceptors-exports.js +12 -0
- package/dist/interceptors-exports.js.map +1 -0
- package/dist/reexports.d.ts +10 -0
- package/dist/reexports.d.ts.map +1 -0
- package/dist/reexports.js +27 -0
- package/dist/reexports.js.map +1 -0
- package/dist/telemetry-shim.d.ts +12 -0
- package/dist/telemetry-shim.d.ts.map +1 -0
- package/dist/telemetry-shim.js +94 -0
- package/dist/telemetry-shim.js.map +1 -0
- package/dist/types.d.ts +228 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +36 -0
- package/dist/types.js.map +1 -0
- package/package.json +14 -7
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { UnaryInterceptor } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Fold `interceptors` outer→inner into a single factory. The first entry
|
|
4
|
+
* becomes the outermost wrapper, the last is closest to the handler.
|
|
5
|
+
*/
|
|
6
|
+
export declare function composeUnaryInterceptors(interceptors: readonly UnaryInterceptor[]): UnaryInterceptor;
|
|
7
|
+
//# sourceMappingURL=compose.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAgB,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE9D;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,YAAY,EAAE,SAAS,gBAAgB,EAAE,GACxC,gBAAgB,CASlB"}
|
package/dist/compose.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// composeUnaryInterceptors — local copy of the @nodii/grpc-auth helper,
|
|
2
|
+
// kept in this package so it can be the source-of-truth for the locked
|
|
3
|
+
// composition order. Bytewise identical to the grpc-auth helper per spec
|
|
4
|
+
// § 5.1 — outer→inner fold.
|
|
5
|
+
/**
|
|
6
|
+
* Fold `interceptors` outer→inner into a single factory. The first entry
|
|
7
|
+
* becomes the outermost wrapper, the last is closest to the handler.
|
|
8
|
+
*/
|
|
9
|
+
export function composeUnaryInterceptors(interceptors) {
|
|
10
|
+
return (methodName, handler) => {
|
|
11
|
+
let wrapped = handler;
|
|
12
|
+
for (let i = interceptors.length - 1; i >= 0; i--) {
|
|
13
|
+
const factory = interceptors[i];
|
|
14
|
+
if (factory)
|
|
15
|
+
wrapped = factory(methodName, wrapped);
|
|
16
|
+
}
|
|
17
|
+
return wrapped;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=compose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,uEAAuE;AACvE,yEAAyE;AACzE,4BAA4B;AAI5B;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CACtC,YAAyC;IAEzC,OAAO,CAAC,UAAkB,EAAE,OAAqB,EAAgB,EAAE;QACjE,IAAI,OAAO,GAAG,OAAO,CAAC;QACtB,KAAK,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAClD,MAAM,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;YAChC,IAAI,OAAO;gBAAE,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { AuditConfig, CancellationConfig, DbConnection, DeadlineConfig, EnrichAuthConfig, ErrorMapEntry, InterceptorLogger, LoggingConfig, UnaryInterceptor } from "./types";
|
|
2
|
+
export interface ConfigureGrpcInterceptorsOptions {
|
|
3
|
+
/** Service short-name baked into the `<short>.audit_ctx` GUC. Required. */
|
|
4
|
+
serviceShortName: string;
|
|
5
|
+
/** Postgres connection abstraction — required for tenant/audit context. */
|
|
6
|
+
db?: DbConnection;
|
|
7
|
+
/**
|
|
8
|
+
* gRPC methods that REQUIRE `x-nodii-idempotency-key` per spec § 5.7
|
|
9
|
+
* (idempotencyGuard re-export). Empty set = guard is a no-op for every
|
|
10
|
+
* method (back-compat).
|
|
11
|
+
*/
|
|
12
|
+
mutatorMethods?: ReadonlySet<string>;
|
|
13
|
+
/** gRPC methods that opt out of `SET app.tenant_id` (system context). */
|
|
14
|
+
systemContextMethods?: ReadonlySet<string>;
|
|
15
|
+
/** Methods that bypass the entire stack (e.g. health probes). */
|
|
16
|
+
skipForMethods?: ReadonlySet<string>;
|
|
17
|
+
/** Service-owned error catalog merged over `STANDARD_ERROR_CATALOG`. */
|
|
18
|
+
errorCatalog?: Record<string, ErrorMapEntry>;
|
|
19
|
+
logging?: LoggingConfig;
|
|
20
|
+
audit?: AuditConfig;
|
|
21
|
+
enrich?: EnrichAuthConfig;
|
|
22
|
+
deadline?: DeadlineConfig;
|
|
23
|
+
cancellation?: CancellationConfig;
|
|
24
|
+
/** Inject an alternate logger across all owned interceptors. */
|
|
25
|
+
logger?: InterceptorLogger;
|
|
26
|
+
/**
|
|
27
|
+
* Methods tagged `require_saga_context: true` per spec § 5.7 (sagaContext
|
|
28
|
+
* re-export from @nodii/saga). Missing = no methods enforced.
|
|
29
|
+
*/
|
|
30
|
+
requireSagaContextMethods?: ReadonlySet<string>;
|
|
31
|
+
/** sagaContext enforcement mode — warn | reject. Default 'warn'. */
|
|
32
|
+
sagaContextEnforce?: "warn" | "reject";
|
|
33
|
+
}
|
|
34
|
+
export interface ConfiguredGrpcInterceptors {
|
|
35
|
+
/**
|
|
36
|
+
* Returns the composed UnaryInterceptor wired with the locked-order
|
|
37
|
+
* stack (spec § 5.4). Adopters wrap their service handlers with it:
|
|
38
|
+
*
|
|
39
|
+
* const composed = configured.standardStack();
|
|
40
|
+
* const wrapped = composed("/billing.X/CancelInvoice", handlerFn);
|
|
41
|
+
*/
|
|
42
|
+
standardStack(): UnaryInterceptor;
|
|
43
|
+
/** Snapshot of the resolved config (useful for diagnostics + tests). */
|
|
44
|
+
readonly resolved: ResolvedGrpcInterceptorsConfig;
|
|
45
|
+
}
|
|
46
|
+
export interface ResolvedGrpcInterceptorsConfig {
|
|
47
|
+
readonly serviceShortName: string;
|
|
48
|
+
readonly db: DbConnection | null;
|
|
49
|
+
readonly mutatorMethods: ReadonlySet<string>;
|
|
50
|
+
readonly systemContextMethods: ReadonlySet<string>;
|
|
51
|
+
readonly skipForMethods: ReadonlySet<string>;
|
|
52
|
+
readonly errorCatalog: Record<string, ErrorMapEntry>;
|
|
53
|
+
readonly requireSagaContextMethods: ReadonlySet<string>;
|
|
54
|
+
readonly sagaContextEnforce: "warn" | "reject";
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Bootstrap the gRPC interceptor stack. Idempotent (re-calls warn + are
|
|
58
|
+
* ignored). Returns a handle exposing `standardStack()` which adopters
|
|
59
|
+
* call once per RPC server set-up.
|
|
60
|
+
*/
|
|
61
|
+
export declare function configureGrpcInterceptors(options: ConfigureGrpcInterceptorsOptions): ConfiguredGrpcInterceptors;
|
|
62
|
+
/** Diagnostics — returns null when configureGrpcInterceptors not yet called. */
|
|
63
|
+
export declare function getConfiguredOrNull(): ConfiguredGrpcInterceptors | null;
|
|
64
|
+
/** Test-only — drop the resolved registry so the next call re-bootstraps. */
|
|
65
|
+
export declare function _resetConfigureForTests(): void;
|
|
66
|
+
//# sourceMappingURL=configure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AA0CA,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,aAAa,EAGb,gBAAgB,EACjB,MAAM,SAAS,CAAC;AAGjB,MAAM,WAAW,gCAAgC;IAC/C,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,EAAE,CAAC,EAAE,YAAY,CAAC;IAClB;;;;OAIG;IACH,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,yEAAyE;IACzE,oBAAoB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3C,iEAAiE;IACjE,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,gEAAgE;IAChE,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;;OAGG;IACH,yBAAyB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChD,oEAAoE;IACpE,kBAAkB,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;CACxC;AAED,MAAM,WAAW,0BAA0B;IACzC;;;;;;OAMG;IACH,aAAa,IAAI,gBAAgB,CAAC;IAClC,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,8BAA8B,CAAC;CACnD;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnD,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACrD,QAAQ,CAAC,yBAAyB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACxD,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,QAAQ,CAAC;CAChD;AAID;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,gCAAgC,GACxC,0BAA0B,CAuF5B;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,IAAI,0BAA0B,GAAG,IAAI,CAEvE;AAED,6EAA6E;AAC7E,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// configureGrpcInterceptors — spec § 5.2 + § 5.5.
|
|
2
|
+
//
|
|
3
|
+
// Boot helper that stores the resolved stack config in process-local
|
|
4
|
+
// state and produces the composed standard server stack on demand.
|
|
5
|
+
// Services call this once at process start (after `initSaga` +
|
|
6
|
+
// `initIdempotency` + auth bootstrap have run); subsequent calls to
|
|
7
|
+
// `createStandardServerStack()` reach into the registry for defaults.
|
|
8
|
+
//
|
|
9
|
+
// Surface:
|
|
10
|
+
// configureGrpcInterceptors({
|
|
11
|
+
// serviceShortName: "billing",
|
|
12
|
+
// db,
|
|
13
|
+
// redis, // optional — used by idempotencyGuard slot
|
|
14
|
+
// errorCatalog?,
|
|
15
|
+
// logging?,
|
|
16
|
+
// audit?,
|
|
17
|
+
// enrich?,
|
|
18
|
+
// deadline?,
|
|
19
|
+
// cancellation?,
|
|
20
|
+
// mutatorMethods?, // gRPC methods that REQUIRE the idempotency header
|
|
21
|
+
// systemContextMethods?, // gRPC methods that opt out of tenant scoping
|
|
22
|
+
// });
|
|
23
|
+
//
|
|
24
|
+
// Returns the composed `UnaryInterceptor` factory wired with the full
|
|
25
|
+
// locked stack (logging → audit → auth → enrich → saga → signal-binder →
|
|
26
|
+
// idempotency-guard → tenant → audit-ctx → mfa → deadline → cancellation
|
|
27
|
+
// → error-map → handler).
|
|
28
|
+
import { idempotencyGuard } from "@nodii/idempotency";
|
|
29
|
+
import { sagaContext } from "@nodii/saga";
|
|
30
|
+
import { signalBinder } from "./interceptors/signal-binder";
|
|
31
|
+
import { cancellationGuard, deadlineGuard, enrichAuthContext, errorMap, STANDARD_ERROR_CATALOG, tenantContext, withAuditUnary, withLoggingUnary, } from "./interceptors-exports";
|
|
32
|
+
import { auditContext } from "./interceptors/audit-context";
|
|
33
|
+
import { GrpcStatusError } from "./types";
|
|
34
|
+
let resolved = null;
|
|
35
|
+
/**
|
|
36
|
+
* Bootstrap the gRPC interceptor stack. Idempotent (re-calls warn + are
|
|
37
|
+
* ignored). Returns a handle exposing `standardStack()` which adopters
|
|
38
|
+
* call once per RPC server set-up.
|
|
39
|
+
*/
|
|
40
|
+
export function configureGrpcInterceptors(options) {
|
|
41
|
+
if (!options) {
|
|
42
|
+
throw new GrpcStatusError("INTERNAL", "interceptor_config_required");
|
|
43
|
+
}
|
|
44
|
+
if (!options.serviceShortName) {
|
|
45
|
+
throw new GrpcStatusError("INTERNAL", "service_short_name_required");
|
|
46
|
+
}
|
|
47
|
+
if (resolved) {
|
|
48
|
+
// eslint-disable-next-line no-console
|
|
49
|
+
console.warn("[@nodii/grpc-interceptors] configureGrpcInterceptors() called more than once; ignoring subsequent call.");
|
|
50
|
+
return resolved;
|
|
51
|
+
}
|
|
52
|
+
const resolvedConfig = {
|
|
53
|
+
serviceShortName: options.serviceShortName,
|
|
54
|
+
db: options.db ?? null,
|
|
55
|
+
mutatorMethods: options.mutatorMethods ?? new Set(),
|
|
56
|
+
systemContextMethods: options.systemContextMethods ?? new Set(),
|
|
57
|
+
skipForMethods: options.skipForMethods ?? new Set(),
|
|
58
|
+
errorCatalog: {
|
|
59
|
+
...STANDARD_ERROR_CATALOG,
|
|
60
|
+
...(options.errorCatalog ?? {}),
|
|
61
|
+
},
|
|
62
|
+
requireSagaContextMethods: options.requireSagaContextMethods ?? new Set(),
|
|
63
|
+
sagaContextEnforce: options.sagaContextEnforce ?? "warn",
|
|
64
|
+
};
|
|
65
|
+
const interceptors = [
|
|
66
|
+
withLoggingUnary({
|
|
67
|
+
...(options.logging ?? {}),
|
|
68
|
+
logger: options.logger ?? options.logging?.logger,
|
|
69
|
+
}),
|
|
70
|
+
withAuditUnary(options.audit ?? {}),
|
|
71
|
+
// withAuthUnary slot: services supply their own configured wrapper
|
|
72
|
+
// via the re-exported `withAuthUnary` (it requires JWKS / token
|
|
73
|
+
// manager wiring that varies per service). This factory composes
|
|
74
|
+
// around a passthrough; services that need auth wrap THEIR handler
|
|
75
|
+
// with the auth re-export before passing to standardStack().
|
|
76
|
+
passthroughSlot(),
|
|
77
|
+
enrichAuthContext(options.enrich ?? {}),
|
|
78
|
+
// Real saga sagaContext re-export. Requires saga library to be
|
|
79
|
+
// initialized; if not initialized, sagaContext throws — that's
|
|
80
|
+
// intentional (the service should init saga before this boot helper).
|
|
81
|
+
sagaContextSlot(resolvedConfig.requireSagaContextMethods, resolvedConfig.sagaContextEnforce),
|
|
82
|
+
// Real signalBinder.
|
|
83
|
+
signalBinder(),
|
|
84
|
+
// Real idempotencyGuard re-export.
|
|
85
|
+
idempotencyGuardSlot(resolvedConfig.mutatorMethods),
|
|
86
|
+
...(options.db
|
|
87
|
+
? [
|
|
88
|
+
tenantContext({
|
|
89
|
+
db: options.db,
|
|
90
|
+
systemContextMethods: resolvedConfig.systemContextMethods,
|
|
91
|
+
}),
|
|
92
|
+
auditContext({
|
|
93
|
+
db: options.db,
|
|
94
|
+
serviceShortName: options.serviceShortName,
|
|
95
|
+
}),
|
|
96
|
+
]
|
|
97
|
+
: []),
|
|
98
|
+
passthroughSlot(), // mfaRequiredInterceptor slot — service-wired
|
|
99
|
+
deadlineGuard(options.deadline ?? {}),
|
|
100
|
+
cancellationGuard(options.cancellation ?? {}),
|
|
101
|
+
errorMap(resolvedConfig.errorCatalog),
|
|
102
|
+
];
|
|
103
|
+
resolved = {
|
|
104
|
+
standardStack() {
|
|
105
|
+
return (methodName, handler) => {
|
|
106
|
+
if (resolvedConfig.skipForMethods.has(methodName))
|
|
107
|
+
return handler;
|
|
108
|
+
let wrapped = handler;
|
|
109
|
+
for (let i = interceptors.length - 1; i >= 0; i--) {
|
|
110
|
+
const factory = interceptors[i];
|
|
111
|
+
if (factory)
|
|
112
|
+
wrapped = factory(methodName, wrapped);
|
|
113
|
+
}
|
|
114
|
+
return wrapped;
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
resolved: resolvedConfig,
|
|
118
|
+
};
|
|
119
|
+
return resolved;
|
|
120
|
+
}
|
|
121
|
+
/** Diagnostics — returns null when configureGrpcInterceptors not yet called. */
|
|
122
|
+
export function getConfiguredOrNull() {
|
|
123
|
+
return resolved;
|
|
124
|
+
}
|
|
125
|
+
/** Test-only — drop the resolved registry so the next call re-bootstraps. */
|
|
126
|
+
export function _resetConfigureForTests() {
|
|
127
|
+
resolved = null;
|
|
128
|
+
}
|
|
129
|
+
// ── Internal slot helpers ────────────────────────────────────────────────────
|
|
130
|
+
function passthroughSlot() {
|
|
131
|
+
return (_method, handler) => async (call) => handler(call);
|
|
132
|
+
}
|
|
133
|
+
function sagaContextSlot(requireMethods, enforce) {
|
|
134
|
+
// sagaContext from @nodii/saga is a method-name-keyed enforcer; build it
|
|
135
|
+
// here so we can swap the requireSagaContextMethods set lazily.
|
|
136
|
+
const built = sagaContext({
|
|
137
|
+
requireSagaContextMethods: requireMethods,
|
|
138
|
+
enforce,
|
|
139
|
+
});
|
|
140
|
+
return (methodName, handler) => {
|
|
141
|
+
const inner = built(methodName, handler);
|
|
142
|
+
return async (call) => inner(call);
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function idempotencyGuardSlot(mutatorMethods) {
|
|
146
|
+
// idempotencyGuard from @nodii/idempotency returns a handler-wrapper, not
|
|
147
|
+
// a method-keyed factory. We adapt it to the UnaryInterceptor contract by
|
|
148
|
+
// wrapping per call and supplying method extraction via the call.
|
|
149
|
+
const built = idempotencyGuard({
|
|
150
|
+
mutatorMethods: new Set(mutatorMethods),
|
|
151
|
+
getMethodName: (call) => {
|
|
152
|
+
const direct = call
|
|
153
|
+
.__methodName;
|
|
154
|
+
if (typeof direct === "string")
|
|
155
|
+
return direct;
|
|
156
|
+
return call.call?.handler?.path ?? "<unknown-method>";
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
return (methodName, handler) => {
|
|
160
|
+
return async (call) => {
|
|
161
|
+
// Stamp the method name onto the call so the guard's getMethodName
|
|
162
|
+
// can pick it up without a real @grpc/grpc-js call envelope.
|
|
163
|
+
call.__methodName = methodName;
|
|
164
|
+
const guarded = built(handler);
|
|
165
|
+
return guarded(call);
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=configure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configure.js","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { StandardServerStackConfig, UnaryInterceptor } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Build the standard composed stack with the locked outer→inner order
|
|
4
|
+
* per spec § 5.4. Accepts a single config; rejects custom-order args.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createStandardServerStack(config: StandardServerStackConfig): UnaryInterceptor;
|
|
7
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +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,CAmFlB"}
|
package/dist/factory.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// createStandardServerStack — spec § 5.3 + § 5.4.
|
|
2
|
+
//
|
|
3
|
+
// Produces the locked 13-interceptor outer→inner composition. v0.1.1: the
|
|
4
|
+
// saga/idempotency slots are REAL re-exports from @nodii/saga and
|
|
5
|
+
// @nodii/idempotency (sibling libs on main). The withAuthUnary + mfa
|
|
6
|
+
// slots remain passthrough at this layer because they require
|
|
7
|
+
// service-specific JWKS / token-manager wiring; services wrap their
|
|
8
|
+
// handler with `withAuthUnary` from `@nodii/grpc-auth` (re-exported from
|
|
9
|
+
// this lib) before passing it to the composed factory.
|
|
10
|
+
import { idempotencyGuard } from "@nodii/idempotency";
|
|
11
|
+
import { sagaContext } from "@nodii/saga";
|
|
12
|
+
import { signalBinder } from "./interceptors/signal-binder";
|
|
13
|
+
import { cancellationGuard, deadlineGuard, enrichAuthContext, errorMap, STANDARD_ERROR_CATALOG, tenantContext, withAuditUnary, withLoggingUnary, auditContext, } from "./interceptors-exports";
|
|
14
|
+
import { GrpcStatusError } from "./types";
|
|
15
|
+
/** Pure passthrough — used for slots services wire themselves. */
|
|
16
|
+
function passthrough() {
|
|
17
|
+
return (_methodName, handler) => async (call) => handler(call);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Build the standard composed stack with the locked outer→inner order
|
|
21
|
+
* per spec § 5.4. Accepts a single config; rejects custom-order args.
|
|
22
|
+
*/
|
|
23
|
+
export function createStandardServerStack(config) {
|
|
24
|
+
if (!config) {
|
|
25
|
+
throw new GrpcStatusError("INTERNAL", "interceptor_config_required");
|
|
26
|
+
}
|
|
27
|
+
if ("interceptors" in config ||
|
|
28
|
+
"order" in config) {
|
|
29
|
+
// Spec § 5.4 — custom-order args rejected at v0.1.0.
|
|
30
|
+
throw new GrpcStatusError("INTERNAL", "composition_order_locked");
|
|
31
|
+
}
|
|
32
|
+
const skipFor = config.skipForMethods ?? new Set();
|
|
33
|
+
const mutatorMethods = config.mutatorMethods ?? new Set();
|
|
34
|
+
const requireSagaCtxMethods = config.requireSagaContextMethods ?? new Set();
|
|
35
|
+
const sagaEnforce = config.sagaContextEnforce ?? "warn";
|
|
36
|
+
const builtSagaContext = sagaContext({
|
|
37
|
+
requireSagaContextMethods: requireSagaCtxMethods,
|
|
38
|
+
enforce: sagaEnforce,
|
|
39
|
+
});
|
|
40
|
+
const builtIdemGuard = idempotencyGuard({
|
|
41
|
+
mutatorMethods: new Set(mutatorMethods),
|
|
42
|
+
getMethodName: (call) => {
|
|
43
|
+
const direct = call
|
|
44
|
+
.__methodName;
|
|
45
|
+
if (typeof direct === "string")
|
|
46
|
+
return direct;
|
|
47
|
+
return call.call?.handler?.path ?? "<unknown-method>";
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
const stack = [
|
|
51
|
+
withLoggingUnary({ ...(config.logging ?? {}), logger: config.logger }),
|
|
52
|
+
withAuditUnary(config.audit ?? {}),
|
|
53
|
+
passthrough(), // withAuthUnary slot — service-wired
|
|
54
|
+
enrichAuthContext(config.enrich ?? {}),
|
|
55
|
+
(methodName, handler) => {
|
|
56
|
+
const inner = builtSagaContext(methodName, handler);
|
|
57
|
+
return async (call) => inner(call);
|
|
58
|
+
},
|
|
59
|
+
signalBinder(),
|
|
60
|
+
(methodName, handler) => {
|
|
61
|
+
return async (call) => {
|
|
62
|
+
call.__methodName =
|
|
63
|
+
methodName;
|
|
64
|
+
const guarded = builtIdemGuard(handler);
|
|
65
|
+
return guarded(call);
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
...(config.db
|
|
69
|
+
? [
|
|
70
|
+
tenantContext({
|
|
71
|
+
db: config.db,
|
|
72
|
+
systemContextMethods: config.tenant?.systemContextMethods,
|
|
73
|
+
}),
|
|
74
|
+
auditContext({
|
|
75
|
+
db: config.db,
|
|
76
|
+
serviceShortName: config.serviceShortName,
|
|
77
|
+
}),
|
|
78
|
+
]
|
|
79
|
+
: []),
|
|
80
|
+
passthrough(), // mfaRequiredInterceptor slot — service-wired
|
|
81
|
+
deadlineGuard(config.deadline ?? {}),
|
|
82
|
+
cancellationGuard(config.cancellation ?? {}),
|
|
83
|
+
errorMap({
|
|
84
|
+
...STANDARD_ERROR_CATALOG,
|
|
85
|
+
...(config.errorCatalog ?? {}),
|
|
86
|
+
}),
|
|
87
|
+
];
|
|
88
|
+
return (methodName, handler) => {
|
|
89
|
+
if (skipFor.has(methodName))
|
|
90
|
+
return handler;
|
|
91
|
+
let wrapped = handler;
|
|
92
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
93
|
+
const factory = stack[i];
|
|
94
|
+
if (factory)
|
|
95
|
+
wrapped = factory(methodName, wrapped);
|
|
96
|
+
}
|
|
97
|
+
return wrapped;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
1
|
export declare const LIB_NAME = "grpc-interceptors";
|
|
2
|
-
export declare const VERSION = "0.
|
|
2
|
+
export declare const VERSION = "0.1.1";
|
|
3
|
+
export { withLoggingUnary } from "./interceptors/logging";
|
|
4
|
+
export { withAuditUnary } from "./interceptors/audit";
|
|
5
|
+
export { enrichAuthContext } from "./interceptors/enrich-auth";
|
|
6
|
+
export { tenantContext } from "./interceptors/tenant-context";
|
|
7
|
+
export { auditContext } from "./interceptors/audit-context";
|
|
8
|
+
export { deadlineGuard } from "./interceptors/deadline-guard";
|
|
9
|
+
export { cancellationGuard } from "./interceptors/cancellation-guard";
|
|
10
|
+
export { errorMap, STANDARD_ERROR_CATALOG, } from "./interceptors/error-map";
|
|
11
|
+
export { createStandardServerStack } from "./factory";
|
|
12
|
+
export { composeUnaryInterceptors } from "./compose";
|
|
13
|
+
export { configureGrpcInterceptors, getConfiguredOrNull, _resetConfigureForTests, } from "./configure";
|
|
14
|
+
export type { ConfigureGrpcInterceptorsOptions, ConfiguredGrpcInterceptors, ResolvedGrpcInterceptorsConfig, } from "./configure";
|
|
15
|
+
export { _resetTelemetryShimForTests, _setTelemetryShimForTests, } from "./telemetry-shim";
|
|
16
|
+
export * from "./reexports";
|
|
17
|
+
export type { SagaHeaderInjector } from "./interceptors/saga-headers";
|
|
18
|
+
export type { AuditConfig, AuditContextConfig, AuditEmitter, AuthOnCall, CancellationConfig, DbConnection, DeadlineConfig, EnrichAuthConfig, ErrorMapEntry, GrpcStatusCode, InterceptorLogger, LoggingConfig, StandardServerStackConfig, TelemetryShim, TenantContextConfig, UnaryCall, UnaryHandler, UnaryInterceptor, } from "./types";
|
|
19
|
+
export { GrpcStatusError, NotImplementedError, RpcCancelledError, } from "./types";
|
|
3
20
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
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,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"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,32 @@
|
|
|
1
|
-
// @nodii/grpc-interceptors —
|
|
2
|
-
// Implementation lands at v0.1.0 per the locked feature_doc.
|
|
1
|
+
// @nodii/grpc-interceptors — v0.1.1 public barrel.
|
|
3
2
|
//
|
|
4
|
-
// Spec:
|
|
3
|
+
// Spec: planning hub feature_doc serviceId=nodii-libs docKey=grpc-interceptors.
|
|
4
|
+
//
|
|
5
|
+
// Single-import surface: 8 owned cross-cutting interceptors + real
|
|
6
|
+
// re-exports of every gRPC interceptor + `composeUnaryInterceptors`
|
|
7
|
+
// helper from the peer libraries (`@nodii/grpc-auth`, `@nodii/saga`,
|
|
8
|
+
// `@nodii/idempotency`) + `configureGrpcInterceptors` boot helper +
|
|
9
|
+
// `createStandardServerStack` factory locked-order composer.
|
|
5
10
|
export const LIB_NAME = "grpc-interceptors";
|
|
6
|
-
export const VERSION = "0.
|
|
11
|
+
export const VERSION = "0.1.1";
|
|
12
|
+
// Owned interceptors.
|
|
13
|
+
export { withLoggingUnary } from "./interceptors/logging";
|
|
14
|
+
export { withAuditUnary } from "./interceptors/audit";
|
|
15
|
+
export { enrichAuthContext } from "./interceptors/enrich-auth";
|
|
16
|
+
export { tenantContext } from "./interceptors/tenant-context";
|
|
17
|
+
export { auditContext } from "./interceptors/audit-context";
|
|
18
|
+
export { deadlineGuard } from "./interceptors/deadline-guard";
|
|
19
|
+
export { cancellationGuard } from "./interceptors/cancellation-guard";
|
|
20
|
+
export { errorMap, STANDARD_ERROR_CATALOG, } from "./interceptors/error-map";
|
|
21
|
+
// Composed-stack factory + composer.
|
|
22
|
+
export { createStandardServerStack } from "./factory";
|
|
23
|
+
export { composeUnaryInterceptors } from "./compose";
|
|
24
|
+
// Boot helper (spec § 5.2 + § 5.5).
|
|
25
|
+
export { configureGrpcInterceptors, getConfiguredOrNull, _resetConfigureForTests, } from "./configure";
|
|
26
|
+
// Test doubles + helpers.
|
|
27
|
+
export { _resetTelemetryShimForTests, _setTelemetryShimForTests, } from "./telemetry-shim";
|
|
28
|
+
// Re-export façade (real grpc-auth + real saga + real idempotency + owned
|
|
29
|
+
// signalBinder + withSagaHeaders).
|
|
30
|
+
export * from "./reexports";
|
|
31
|
+
export { GrpcStatusError, NotImplementedError, RpcCancelledError, } from "./types";
|
|
7
32
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
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,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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-context.d.ts","sourceRoot":"","sources":["../../src/interceptors/audit-context.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,kBAAkB,EAGlB,gBAAgB,EACjB,MAAM,UAAU,CAAC;AAGlB,wBAAgB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,gBAAgB,CA6BzE"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// auditContext — spec § 5.9.
|
|
2
|
+
//
|
|
3
|
+
// `SELECT set_config('<service>.audit_ctx', $1, true)` on the same ALS-bound
|
|
4
|
+
// connection used by `tenantContext`. Read by service-side audit triggers
|
|
5
|
+
// via `current_setting('<service>.audit_ctx', true)::jsonb`. Best-effort:
|
|
6
|
+
// `set_config` failures log + the call proceeds (audit-as-best-effort).
|
|
7
|
+
import { resolveTelemetry } from "../telemetry-shim";
|
|
8
|
+
import { GrpcStatusError } from "../types";
|
|
9
|
+
export function auditContext(config) {
|
|
10
|
+
if (!config.db) {
|
|
11
|
+
throw new GrpcStatusError("INTERNAL", "audit_context_db_unconfigured");
|
|
12
|
+
}
|
|
13
|
+
if (!config.serviceShortName) {
|
|
14
|
+
throw new GrpcStatusError("INTERNAL", "audit_context_service_short_name_required");
|
|
15
|
+
}
|
|
16
|
+
const gucName = `${config.serviceShortName}.audit_ctx`;
|
|
17
|
+
return (_methodName, handler) => {
|
|
18
|
+
return async (call) => {
|
|
19
|
+
const payload = buildPayload(call);
|
|
20
|
+
try {
|
|
21
|
+
await config.db.exec("SELECT set_config($1, $2, true)", [
|
|
22
|
+
gucName,
|
|
23
|
+
JSON.stringify(payload),
|
|
24
|
+
]);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
resolveTelemetry().logger.error("audit_context.set_config_failed", {
|
|
28
|
+
error: err instanceof Error ? err.message : String(err),
|
|
29
|
+
guc: gucName,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return handler(call);
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function buildPayload(call) {
|
|
37
|
+
const service = call.auth?.serviceActor;
|
|
38
|
+
const user = call.auth?.userActor;
|
|
39
|
+
const reqCtx = call.auth?.requestContext;
|
|
40
|
+
return {
|
|
41
|
+
service_actor: service
|
|
42
|
+
? {
|
|
43
|
+
service_id: service.service_id ?? service.serviceId,
|
|
44
|
+
service_name: service.service_name ?? service.serviceName,
|
|
45
|
+
}
|
|
46
|
+
: null,
|
|
47
|
+
user_actor: user
|
|
48
|
+
? {
|
|
49
|
+
user_id: user.user_id,
|
|
50
|
+
tenant_id: user.tenant_id,
|
|
51
|
+
user_role: user.user_role,
|
|
52
|
+
}
|
|
53
|
+
: null,
|
|
54
|
+
correlation_id: reqCtx?.correlationId ?? null,
|
|
55
|
+
tenant_id: reqCtx?.tenant_id ?? null,
|
|
56
|
+
intent_id: reqCtx?.intent_id ?? null,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=audit-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-context.js","sourceRoot":"","sources":["../../src/interceptors/audit-context.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,EAAE;AACF,6EAA6E;AAC7E,0EAA0E;AAC1E,0EAA0E;AAC1E,wEAAwE;AAExE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAOrD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,UAAU,YAAY,CAAC,MAA0B;IACrD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,eAAe,CAAC,UAAU,EAAE,+BAA+B,CAAC,CAAC;IACzE,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAC7B,MAAM,IAAI,eAAe,CACvB,UAAU,EACV,2CAA2C,CAC5C,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,MAAM,CAAC,gBAAgB,YAAY,CAAC;IAEvD,OAAO,CAAC,WAAmB,EAAE,OAAqB,EAAgB,EAAE;QAClE,OAAO,KAAK,EAAE,IAAe,EAAoB,EAAE;YACjD,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,iCAAiC,EAAE;oBACtD,OAAO;oBACP,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;iBACxB,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,gBAAgB,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,iCAAiC,EAAE;oBACjE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;oBACvD,GAAG,EAAE,OAAO;iBACb,CAAC,CAAC;YACL,CAAC;YACD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,IAAe;IACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC;IACxC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC;IAClC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC;IACzC,OAAO;QACL,aAAa,EAAE,OAAO;YACpB,CAAC,CAAC;gBACE,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,SAAS;gBACnD,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,WAAW;aAC1D;YACH,CAAC,CAAC,IAAI;QACR,UAAU,EAAE,IAAI;YACd,CAAC,CAAC;gBACE,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,SAAS,EAAE,IAAI,CAAC,SAAS;aAC1B;YACH,CAAC,CAAC,IAAI;QACR,cAAc,EAAE,MAAM,EAAE,aAAa,IAAI,IAAI;QAC7C,SAAS,EAAE,MAAM,EAAE,SAAS,IAAI,IAAI;QACpC,SAAS,EAAE,MAAM,EAAE,SAAS,IAAI,IAAI;KACrC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AuditConfig, UnaryInterceptor } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Build the audit interceptor. Sits between logging and auth in the
|
|
4
|
+
* locked composition.
|
|
5
|
+
*/
|
|
6
|
+
export declare function withAuditUnary(config?: AuditConfig): UnaryInterceptor;
|
|
7
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/interceptors/audit.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,WAAW,EAIX,gBAAgB,EACjB,MAAM,UAAU,CAAC;AA+BlB;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,GAAE,WAAgB,GAAG,gBAAgB,CAoDzE"}
|