@nodii/telemetry 0.10.0 → 0.12.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.
@@ -0,0 +1,89 @@
1
+ import type { ActorType, NodiiContext } from "../types.js";
2
+ /**
3
+ * The minimal verified-claims surface the adapters consume. This is a
4
+ * STRUCTURAL contract — both `@nodii/auth-sdk`'s `NodiiClaims`
5
+ * (`sub`/`principalKind`/`roles`/`actor`) and the gRPC
6
+ * `UserActorEnvelope` map onto it. Every field is optional so the
7
+ * adapter degrades gracefully when a claim is absent (e.g. an S2S-only
8
+ * call with no user actor).
9
+ */
10
+ export interface AdapterClaims {
11
+ /** Tenant the principal is acting within. NodiiContext.tenant_id. */
12
+ tenantId?: string | null;
13
+ /**
14
+ * Principal id. Becomes NodiiContext.user_id ONLY when the resolved
15
+ * actor_type is 'user' (the type invariant: user_id is null otherwise).
16
+ */
17
+ subjectId?: string | null;
18
+ /**
19
+ * Subject kind from the token. Maps to actor_type. Accepts the
20
+ * auth-sdk `principalKind` vocabulary ('user' | 'customer' | 'agent' |
21
+ * 'service') plus 'system'; anything unrecognized falls back to
22
+ * 'service' (the safe non-user, non-agent default).
23
+ */
24
+ subjectKind?: string | null;
25
+ /**
26
+ * Tenant-defined role names the principal holds. Collapsed to a single
27
+ * NodiiContext.role via highest-privilege selection.
28
+ */
29
+ roles?: readonly string[] | null;
30
+ /**
31
+ * Delegation / on-behalf-of claim. The id of the principal this
32
+ * actor is acting on behalf of (act/obo). Maps to on_behalf_of.
33
+ */
34
+ onBehalfOf?: string | null;
35
+ /** Intent id if one is already active on the inbound call. */
36
+ intentId?: string | null;
37
+ }
38
+ /**
39
+ * Default role-privilege ordering, highest first. Used to collapse a
40
+ * `roles[]` claim down to the single `NodiiContext.role`. There is no
41
+ * canonical RBAC role ordering elsewhere in nodii-libs (the DB `nodii_*`
42
+ * roles in db-rls are Postgres roles, not RBAC role names), and the
43
+ * UserActorEnvelope doc cites "owner" > "admin" > "member" as the
44
+ * representative ladder — this extends that ladder with the common
45
+ * management / read-only tiers. Callers with a service-specific ladder
46
+ * pass `rolePrivilegeOrder` to override it.
47
+ *
48
+ * Selection rule: the role appearing EARLIEST in this list wins. A role
49
+ * not in the list ranks BELOW every listed role (least privilege), so an
50
+ * unknown custom role never silently outranks a known privileged one.
51
+ */
52
+ export declare const ROLE_PRIVILEGE_ORDER: readonly string[];
53
+ /**
54
+ * Collapse a `roles[]` claim to the single highest-privilege role.
55
+ * Returns `fallback` ('member' by default) when the array is empty/absent.
56
+ */
57
+ export declare function collapseRolesToHighest(roles: readonly string[] | null | undefined, order?: readonly string[], fallback?: string): string;
58
+ /**
59
+ * Map a token subject kind to the locked `ActorType` enum. `customer`
60
+ * (an auth-sdk principalKind) is a user-facing principal → 'user'.
61
+ * Unrecognized kinds fall back to 'service' (safe non-user default).
62
+ */
63
+ export declare function deriveActorType(subjectKind?: string | null): ActorType;
64
+ export interface MapClaimsOptions {
65
+ /**
66
+ * The inbound `x-request-id` value (header or gRPC metadata). When
67
+ * absent/empty a fresh uuidv7 is minted — request_id is NEVER empty in
68
+ * a NodiiContext.
69
+ */
70
+ requestId?: string | null;
71
+ /** Override the default role-privilege ladder. */
72
+ rolePrivilegeOrder?: readonly string[];
73
+ /** Override the fallback role when no roles claim is present. */
74
+ roleFallback?: string;
75
+ /**
76
+ * Fallback tenant_id when the claims carry none. NodiiContext.tenant_id
77
+ * is non-nullable; if neither claim nor fallback supplies it the adapter
78
+ * throws (a context with no tenant is not safe to set — RLS keys off it).
79
+ */
80
+ tenantFallback?: string;
81
+ }
82
+ /**
83
+ * Build a `NodiiContext` from verified claims + the inbound request_id.
84
+ * Pure + deterministic given a fixed request_id; the adapters call it
85
+ * after extracting the request_id (so the generated-uuid path is
86
+ * observable/testable).
87
+ */
88
+ export declare function mapClaimsToContext(claims: AdapterClaims, opts?: MapClaimsOptions): NodiiContext;
89
+ //# sourceMappingURL=claims.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claims.d.ts","sourceRoot":"","sources":["../../src/context-adapter/claims.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3D;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IACjC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EAOxC,CAAC;AAEX;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,EAC3C,KAAK,GAAE,SAAS,MAAM,EAAyB,EAC/C,QAAQ,SAAW,GAClB,MAAM,CAmBR;AASD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAQtE;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,kDAAkD;IAClD,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,aAAa,EACrB,IAAI,GAAE,gBAAqB,GAC1B,YAAY,CA+Bd"}
@@ -0,0 +1,113 @@
1
+ // Shared NodiiContext mapping for the entry-seam adapters (D44 § 6.5,
2
+ // D405 context adoption). The Hono middleware (`./hono.ts`) and the gRPC
3
+ // server interceptor (`./grpc.ts`) both funnel through `mapClaimsToContext`
4
+ // so the field mappings are defined exactly once.
5
+ //
6
+ // Operator-LOCKED field mappings (req b3b03fc5, clarification c9906a51,
7
+ // approved 2026-06-25):
8
+ // - actor_type — derived from the token's subject kind.
9
+ // - roles[] → role — NodiiContext.role is singular; collapse roles[] to
10
+ // the HIGHEST-privilege role (see ROLE_PRIVILEGE_ORDER).
11
+ // - on_behalf_of — sourced from the delegation (act/obo) claim.
12
+ // - request_id — read from x-request-id; GENERATED (uuidv7) if absent.
13
+ // tenant_id / intent_id / user_id come from the claims; trace activation
14
+ // is handled separately by the adapter (it activates the inbound
15
+ // trace_parent onto the OTel context, never folded into NodiiContext).
16
+ import { uuidv7 } from "../uuid.js";
17
+ /**
18
+ * Default role-privilege ordering, highest first. Used to collapse a
19
+ * `roles[]` claim down to the single `NodiiContext.role`. There is no
20
+ * canonical RBAC role ordering elsewhere in nodii-libs (the DB `nodii_*`
21
+ * roles in db-rls are Postgres roles, not RBAC role names), and the
22
+ * UserActorEnvelope doc cites "owner" > "admin" > "member" as the
23
+ * representative ladder — this extends that ladder with the common
24
+ * management / read-only tiers. Callers with a service-specific ladder
25
+ * pass `rolePrivilegeOrder` to override it.
26
+ *
27
+ * Selection rule: the role appearing EARLIEST in this list wins. A role
28
+ * not in the list ranks BELOW every listed role (least privilege), so an
29
+ * unknown custom role never silently outranks a known privileged one.
30
+ */
31
+ export const ROLE_PRIVILEGE_ORDER = [
32
+ "owner",
33
+ "admin",
34
+ "manager",
35
+ "member",
36
+ "viewer",
37
+ "guest",
38
+ ];
39
+ /**
40
+ * Collapse a `roles[]` claim to the single highest-privilege role.
41
+ * Returns `fallback` ('member' by default) when the array is empty/absent.
42
+ */
43
+ export function collapseRolesToHighest(roles, order = ROLE_PRIVILEGE_ORDER, fallback = "member") {
44
+ if (!roles || roles.length === 0)
45
+ return fallback;
46
+ const rank = (r) => {
47
+ const i = order.indexOf(r);
48
+ // Unknown roles rank below every known role (largest index).
49
+ return i === -1 ? order.length : i;
50
+ };
51
+ // Pick the role with the smallest rank index; ties broken by input order.
52
+ let best = roles[0];
53
+ let bestRank = rank(best);
54
+ for (let i = 1; i < roles.length; i++) {
55
+ const candidate = roles[i];
56
+ const candidateRank = rank(candidate);
57
+ if (candidateRank < bestRank) {
58
+ best = candidate;
59
+ bestRank = candidateRank;
60
+ }
61
+ }
62
+ return best;
63
+ }
64
+ const ACTOR_TYPES = [
65
+ "user",
66
+ "agent",
67
+ "system",
68
+ "service",
69
+ ];
70
+ /**
71
+ * Map a token subject kind to the locked `ActorType` enum. `customer`
72
+ * (an auth-sdk principalKind) is a user-facing principal → 'user'.
73
+ * Unrecognized kinds fall back to 'service' (safe non-user default).
74
+ */
75
+ export function deriveActorType(subjectKind) {
76
+ if (!subjectKind)
77
+ return "service";
78
+ const normalized = subjectKind.toLowerCase();
79
+ if (normalized === "customer")
80
+ return "user";
81
+ if (ACTOR_TYPES.includes(normalized)) {
82
+ return normalized;
83
+ }
84
+ return "service";
85
+ }
86
+ /**
87
+ * Build a `NodiiContext` from verified claims + the inbound request_id.
88
+ * Pure + deterministic given a fixed request_id; the adapters call it
89
+ * after extracting the request_id (so the generated-uuid path is
90
+ * observable/testable).
91
+ */
92
+ export function mapClaimsToContext(claims, opts = {}) {
93
+ const actor_type = deriveActorType(claims.subjectKind);
94
+ const tenant_id = claims.tenantId ?? opts.tenantFallback;
95
+ if (!tenant_id) {
96
+ throw new Error("Cannot set NodiiContext: no tenant_id in claims and no tenantFallback. " +
97
+ "RLS + every signal key off tenant_id, so a tenant-less context is unsafe.");
98
+ }
99
+ // request_id: read x-request-id, generate a uuidv7 when absent.
100
+ const request_id = opts.requestId && opts.requestId.length > 0 ? opts.requestId : uuidv7();
101
+ const role = collapseRolesToHighest(claims.roles, opts.rolePrivilegeOrder, opts.roleFallback);
102
+ return {
103
+ tenant_id,
104
+ // user_id is null unless actor_type === 'user' (type invariant).
105
+ user_id: actor_type === "user" ? (claims.subjectId ?? null) : null,
106
+ actor_type,
107
+ on_behalf_of: claims.onBehalfOf ?? null,
108
+ role,
109
+ request_id,
110
+ intent_id: claims.intentId ?? null,
111
+ };
112
+ }
113
+ //# sourceMappingURL=claims.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claims.js","sourceRoot":"","sources":["../../src/context-adapter/claims.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import { type AdapterClaims, type MapClaimsOptions } from "./claims.js";
2
+ /** Canonical inbound request-id metadata key. */
3
+ export declare const REQUEST_ID_METADATA = "x-request-id";
4
+ /**
5
+ * Minimal structural shape of a gRPC unary call this adapter reads. A
6
+ * real `@grpc/grpc-js` `ServerUnaryCall` (and the `@nodii/grpc-auth`
7
+ * `withAuthUnary` augmentation) is assignable to it; so is a synthetic
8
+ * test fixture. Telemetry is a foundation lib, so it declares this
9
+ * locally instead of importing `@nodii/grpc-interceptors` (no cycle).
10
+ */
11
+ export interface GrpcUnaryCall {
12
+ metadata?: {
13
+ get(key: string): readonly unknown[] | undefined;
14
+ };
15
+ /** Attached by the upstream auth interceptor (`@nodii/grpc-auth`). */
16
+ auth?: {
17
+ serviceActor?: {
18
+ service_id?: string;
19
+ serviceId?: string;
20
+ tenant_id?: string;
21
+ } | null;
22
+ userActor?: {
23
+ user_id?: string;
24
+ tenant_id?: string;
25
+ user_role?: string;
26
+ } | null;
27
+ requestContext?: {
28
+ tenant_id?: string;
29
+ intent_id?: string;
30
+ actor_type?: string;
31
+ } | null;
32
+ } | null;
33
+ request?: unknown;
34
+ }
35
+ export type GrpcUnaryHandler = (call: GrpcUnaryCall) => Promise<unknown>;
36
+ export type GrpcUnaryInterceptor = (methodName: string, handler: GrpcUnaryHandler) => GrpcUnaryHandler;
37
+ export interface NodiiContextInterceptorOptions extends Pick<MapClaimsOptions, "rolePrivilegeOrder" | "roleFallback" | "tenantFallback"> {
38
+ /**
39
+ * Read the verified claims off the call. Defaults to reading the
40
+ * `call.auth` envelope (userActor / serviceActor / requestContext) the
41
+ * `@nodii/grpc-auth` interceptor attaches, plus the `x-user-roles`
42
+ * metadata when present. Override to read a custom envelope.
43
+ */
44
+ extractClaims?: (call: GrpcUnaryCall) => AdapterClaims | null;
45
+ /** Metadata key for the inbound request id. Defaults to `x-request-id`. */
46
+ requestIdMetadata?: string;
47
+ /**
48
+ * Metadata key carrying a comma-separated roles list. Defaults to
49
+ * `x-user-roles`. Lets the gRPC seam receive `roles[]` (the
50
+ * `UserActorEnvelope` only carries a single `user_role`).
51
+ */
52
+ rolesMetadata?: string;
53
+ }
54
+ /**
55
+ * gRPC server interceptor that SETS the active `NodiiContext` for the
56
+ * handler scope. Composes as a `(methodName, handler) => handler` wrapper
57
+ * (the `@nodii/grpc-interceptors` convention) and runs the wrapped
58
+ * handler inside `withContext` so every read of `getContext()` inside the
59
+ * handler — and any async work it awaits — sees the mapped context.
60
+ */
61
+ export declare function nodiiContextInterceptor(options?: NodiiContextInterceptorOptions): GrpcUnaryInterceptor;
62
+ //# sourceMappingURL=grpc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grpc.d.ts","sourceRoot":"","sources":["../../src/context-adapter/grpc.ts"],"names":[],"mappings":"AAgBA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,gBAAgB,EAEtB,MAAM,aAAa,CAAC;AAErB,iDAAiD;AACjD,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE;QACT,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC;KAClD,CAAC;IACF,sEAAsE;IACtE,IAAI,CAAC,EAAE;QACL,YAAY,CAAC,EAAE;YACb,UAAU,CAAC,EAAE,MAAM,CAAC;YACpB,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,SAAS,CAAC,EAAE,MAAM,CAAC;SACpB,GAAG,IAAI,CAAC;QACT,SAAS,CAAC,EAAE;YACV,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,SAAS,CAAC,EAAE,MAAM,CAAC;SACpB,GAAG,IAAI,CAAC;QACT,cAAc,CAAC,EAAE;YACf,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,UAAU,CAAC,EAAE,MAAM,CAAC;SACrB,GAAG,IAAI,CAAC;KACV,GAAG,IAAI,CAAC;IACT,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AACzE,MAAM,MAAM,oBAAoB,GAAG,CACjC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,gBAAgB,KACtB,gBAAgB,CAAC;AAEtB,MAAM,WAAW,8BACf,SAAQ,IAAI,CACV,gBAAgB,EAChB,oBAAoB,GAAG,cAAc,GAAG,gBAAgB,CACzD;IACD;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,aAAa,GAAG,IAAI,CAAC;IAC9D,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AA4ED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,8BAAmC,GAC3C,oBAAoB,CAsBtB"}
@@ -0,0 +1,113 @@
1
+ // `@nodii/telemetry/context` — gRPC server entry-seam adapter (D44 § 6.5,
2
+ // D405).
3
+ //
4
+ // A handler-wrapping unary interceptor `(methodName, handler) => handler`
5
+ // (the `@nodii/grpc-auth` / `@nodii/grpc-interceptors` server-interceptor
6
+ // convention — `@grpc/grpc-js` has no native server-interceptor API, so
7
+ // services compose handler wrappers). It reads inbound metadata + the
8
+ // `call.auth` envelope attached by the auth interceptor and runs the
9
+ // handler inside a `withContext` scope so `getContext()` resolves the
10
+ // per-request `NodiiContext` for the whole handler.
11
+ //
12
+ // It is an INBOUND seam: it SETS context for the handler scope and makes
13
+ // no outbound peer calls.
14
+ import { withContext } from "../context.js";
15
+ import { mapClaimsToContext, } from "./claims.js";
16
+ /** Canonical inbound request-id metadata key. */
17
+ export const REQUEST_ID_METADATA = "x-request-id";
18
+ /** Read the first value of a metadata key as a UTF-8 string. */
19
+ function readMetadata(call, key) {
20
+ const values = call.metadata?.get(key);
21
+ if (!values || values.length === 0)
22
+ return undefined;
23
+ const first = values[0];
24
+ if (typeof first === "string")
25
+ return first;
26
+ if (first instanceof Uint8Array || Buffer.isBuffer?.(first)) {
27
+ try {
28
+ return Buffer.from(first).toString("utf8");
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ }
34
+ return undefined;
35
+ }
36
+ /**
37
+ * Default claim extractor for the gRPC seam. Prefers the user actor
38
+ * (interactive principal) over the service actor (S2S caller). Reads
39
+ * `roles[]` from `x-user-roles` metadata when present, else falls back to
40
+ * the single `userActor.user_role`.
41
+ */
42
+ function defaultExtractClaims(call, rolesMetadata) {
43
+ const auth = call.auth ?? null;
44
+ const userActor = auth?.userActor ?? null;
45
+ const serviceActor = auth?.serviceActor ?? null;
46
+ const requestContext = auth?.requestContext ?? null;
47
+ if (!userActor && !serviceActor && !requestContext)
48
+ return null;
49
+ // roles[]: prefer explicit metadata, else lift the singular user_role.
50
+ const rolesRaw = readMetadata(call, rolesMetadata);
51
+ const roles = rolesRaw
52
+ ? rolesRaw
53
+ .split(",")
54
+ .map((r) => r.trim())
55
+ .filter((r) => r.length > 0)
56
+ : userActor?.user_role
57
+ ? [userActor.user_role]
58
+ : null;
59
+ // Delegation (on_behalf_of / act / obo): the gRPC seam carries it in the
60
+ // `x-on-behalf-of` metadata header (symmetric with the Python + Go gRPC
61
+ // adapters). Honoured for BOTH branches — agent-delegation must NOT be
62
+ // silently dropped on the gRPC seam the way it is honoured on Hono.
63
+ const onBehalfOf = readMetadata(call, "x-on-behalf-of") ?? null;
64
+ if (userActor) {
65
+ return {
66
+ tenantId: userActor.tenant_id ?? requestContext?.tenant_id ?? null,
67
+ subjectId: userActor.user_id ?? null,
68
+ // Honour the auth interceptor's actor_type when it stamped one
69
+ // (e.g. 'agent' for a delegated principal); default to 'user' for
70
+ // a plain user-actor envelope. deriveActorType maps the value.
71
+ subjectKind: requestContext?.actor_type ?? "user",
72
+ roles,
73
+ onBehalfOf,
74
+ intentId: requestContext?.intent_id ?? null,
75
+ };
76
+ }
77
+ // S2S-only call: a service principal acting with no user actor.
78
+ return {
79
+ tenantId: serviceActor?.tenant_id ?? requestContext?.tenant_id ?? null,
80
+ subjectId: serviceActor?.serviceId ?? serviceActor?.service_id ?? null,
81
+ subjectKind: requestContext?.actor_type ?? "service",
82
+ roles,
83
+ onBehalfOf,
84
+ intentId: requestContext?.intent_id ?? null,
85
+ };
86
+ }
87
+ /**
88
+ * gRPC server interceptor that SETS the active `NodiiContext` for the
89
+ * handler scope. Composes as a `(methodName, handler) => handler` wrapper
90
+ * (the `@nodii/grpc-interceptors` convention) and runs the wrapped
91
+ * handler inside `withContext` so every read of `getContext()` inside the
92
+ * handler — and any async work it awaits — sees the mapped context.
93
+ */
94
+ export function nodiiContextInterceptor(options = {}) {
95
+ const requestIdMetadata = options.requestIdMetadata ?? REQUEST_ID_METADATA;
96
+ const rolesMetadata = options.rolesMetadata ?? "x-user-roles";
97
+ const extract = options.extractClaims ??
98
+ ((call) => defaultExtractClaims(call, rolesMetadata));
99
+ return (_methodName, handler) => {
100
+ return (call) => {
101
+ const claims = extract(call) ?? {};
102
+ const requestId = readMetadata(call, requestIdMetadata) ?? null;
103
+ const ctx = mapClaimsToContext(claims, {
104
+ requestId,
105
+ rolePrivilegeOrder: options.rolePrivilegeOrder,
106
+ roleFallback: options.roleFallback,
107
+ tenantFallback: options.tenantFallback,
108
+ });
109
+ return withContext(ctx, () => handler(call));
110
+ };
111
+ };
112
+ }
113
+ //# sourceMappingURL=grpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grpc.js","sourceRoot":"","sources":["../../src/context-adapter/grpc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { Context, MiddlewareHandler } from "hono";
2
+ import { type AdapterClaims, type MapClaimsOptions } from "./claims.js";
3
+ /** Canonical inbound request-id header (lower-case; Hono headers are case-insensitive). */
4
+ export declare const REQUEST_ID_HEADER = "x-request-id";
5
+ export interface NodiiContextMiddlewareOptions extends Pick<MapClaimsOptions, "rolePrivilegeOrder" | "roleFallback" | "tenantFallback"> {
6
+ /**
7
+ * Where to read the verified claims from the Hono context. Defaults to
8
+ * reading `c.get("nodiiClaims")` (the `@nodii/auth-sdk/hono` convention)
9
+ * and adapting it. Override when claims live under a different key or in
10
+ * a different shape.
11
+ */
12
+ extractClaims?: (c: Context) => AdapterClaims | null;
13
+ /**
14
+ * Header to read the inbound request id from. Defaults to
15
+ * `x-request-id`. When the header is absent a uuidv7 is generated.
16
+ */
17
+ requestIdHeader?: string;
18
+ }
19
+ /**
20
+ * Hono middleware that SETS the active `NodiiContext` for the downstream
21
+ * request scope. Reads verified claims (default: `c.get("nodiiClaims")`)
22
+ * + the `x-request-id` header, applies the locked field mappings, and
23
+ * runs the rest of the request inside a `withContext` scope.
24
+ *
25
+ * The context is SCOPED to the single request via `withContext` (which
26
+ * unwinds the AsyncLocalStorage frame when `next()` settles) — NOT a bare
27
+ * `setContextForCurrentScope`. A bare `enterWith` mutates the current ALS
28
+ * frame and never unwinds, so if the middleware is mounted at the app root
29
+ * (no per-request ALS frame opened first) request N's context would bleed
30
+ * into request N+1. `withContext` is symmetric with the gRPC interceptor
31
+ * and guarantees `getContext()` is unset again once the request settles.
32
+ *
33
+ * A tenant-less request (no `tenant_id` claim + no `tenantFallback`) is
34
+ * rejected with `400 missing_tenant_context` — a context with no tenant is
35
+ * unsafe to run a handler under (RLS + every signal key off `tenant_id`).
36
+ * Symmetric with the Go HTTP middleware (`400`) and the Python adapter.
37
+ */
38
+ export declare function nodiiContextMiddleware(options?: NodiiContextMiddlewareOptions): MiddlewareHandler;
39
+ //# sourceMappingURL=hono.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/context-adapter/hono.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAGvD,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,gBAAgB,EAEtB,MAAM,aAAa,CAAC;AAErB,2FAA2F;AAC3F,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAEhD,MAAM,WAAW,6BACf,SAAQ,IAAI,CACV,gBAAgB,EAChB,oBAAoB,GAAG,cAAc,GAAG,gBAAgB,CACzD;IACD;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,aAAa,GAAG,IAAI,CAAC;IACrD;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AA6CD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,6BAAkC,GAC1C,iBAAiB,CA6BnB"}
@@ -0,0 +1,86 @@
1
+ // `@nodii/telemetry/context` — Hono entry-seam adapter (D44 § 6.5, D405).
2
+ //
3
+ // The Hono middleware that SETS the per-request `NodiiContext` on entry.
4
+ // Place it AFTER auth (so `c.get("nodiiClaims")` is populated) and before
5
+ // any handler that reads `getContext()`:
6
+ //
7
+ // import { requireAuth } from "@nodii/auth-sdk/hono";
8
+ // import { nodiiContextMiddleware } from "@nodii/telemetry/context";
9
+ //
10
+ // app.use("/api/*", requireAuth({ verifier }));
11
+ // app.use("/api/*", nodiiContextMiddleware());
12
+ // app.get("/api/me", (c) => c.json(getContext()));
13
+ //
14
+ // It is an INBOUND seam: it sets context for the downstream scope and
15
+ // makes no outbound peer calls.
16
+ import { withContext } from "../context.js";
17
+ import { mapClaimsToContext, } from "./claims.js";
18
+ /** Canonical inbound request-id header (lower-case; Hono headers are case-insensitive). */
19
+ export const REQUEST_ID_HEADER = "x-request-id";
20
+ /**
21
+ * Default claim extractor: reads `c.get("nodiiClaims")` and maps the
22
+ * auth-sdk NodiiClaims surface onto `AdapterClaims`. Returns null when no
23
+ * claims are present (unauthenticated route) — the middleware then sets a
24
+ * minimal context from headers + the tenantFallback so downstream code
25
+ * still has a request_id-bearing context.
26
+ */
27
+ function defaultExtractClaims(c) {
28
+ // `c.get` is typed against the app Env; cast through unknown so this
29
+ // adapter works regardless of the consumer's declared Variables.
30
+ const raw = c.get("nodiiClaims");
31
+ if (!raw)
32
+ return null;
33
+ return {
34
+ tenantId: raw.tenantId ?? null,
35
+ subjectId: raw.sub ?? null,
36
+ subjectKind: raw.principalKind ?? null,
37
+ roles: raw.roles ?? null,
38
+ onBehalfOf: raw.onBehalfOf ?? raw.impersonator?.sub ?? null,
39
+ intentId: raw.intentId ?? null,
40
+ };
41
+ }
42
+ /**
43
+ * Hono middleware that SETS the active `NodiiContext` for the downstream
44
+ * request scope. Reads verified claims (default: `c.get("nodiiClaims")`)
45
+ * + the `x-request-id` header, applies the locked field mappings, and
46
+ * runs the rest of the request inside a `withContext` scope.
47
+ *
48
+ * The context is SCOPED to the single request via `withContext` (which
49
+ * unwinds the AsyncLocalStorage frame when `next()` settles) — NOT a bare
50
+ * `setContextForCurrentScope`. A bare `enterWith` mutates the current ALS
51
+ * frame and never unwinds, so if the middleware is mounted at the app root
52
+ * (no per-request ALS frame opened first) request N's context would bleed
53
+ * into request N+1. `withContext` is symmetric with the gRPC interceptor
54
+ * and guarantees `getContext()` is unset again once the request settles.
55
+ *
56
+ * A tenant-less request (no `tenant_id` claim + no `tenantFallback`) is
57
+ * rejected with `400 missing_tenant_context` — a context with no tenant is
58
+ * unsafe to run a handler under (RLS + every signal key off `tenant_id`).
59
+ * Symmetric with the Go HTTP middleware (`400`) and the Python adapter.
60
+ */
61
+ export function nodiiContextMiddleware(options = {}) {
62
+ const extract = options.extractClaims ?? defaultExtractClaims;
63
+ const requestIdHeader = options.requestIdHeader ?? REQUEST_ID_HEADER;
64
+ return async (c, next) => {
65
+ const claims = extract(c) ?? {};
66
+ const requestId = c.req.header(requestIdHeader) ?? null;
67
+ let ctx;
68
+ try {
69
+ ctx = mapClaimsToContext(claims, {
70
+ requestId,
71
+ rolePrivilegeOrder: options.rolePrivilegeOrder,
72
+ roleFallback: options.roleFallback,
73
+ tenantFallback: options.tenantFallback,
74
+ });
75
+ }
76
+ catch {
77
+ // The only throw from mapClaimsToContext is the tenant-less guard.
78
+ // Fail closed with a 400 (do NOT run the handler with no context).
79
+ return c.json({ error: true, code: "missing_tenant_context" }, 400);
80
+ }
81
+ // Scope the context to this request: established for the downstream
82
+ // scope, removed after `next()` settles.
83
+ return withContext(ctx, () => next());
84
+ };
85
+ }
86
+ //# sourceMappingURL=hono.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/context-adapter/hono.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,EAAE;AACF,yEAAyE;AACzE,0EAA0E;AAC1E,yCAAyC;AACzC,EAAE;AACF,wDAAwD;AACxD,uEAAuE;AACvE,EAAE;AACF,kDAAkD;AAClD,iDAAiD;AACjD,qDAAqD;AACrD,EAAE;AACF,sEAAsE;AACtE,gCAAgC;AAGhC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAGL,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,2FAA2F;AAC3F,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAwChD;;;;;;GAMG;AACH,SAAS,oBAAoB,CAAC,CAAU;IACtC,qEAAqE;IACrE,iEAAiE;IACjE,MAAM,GAAG,GAAI,CAA4C,CAAC,GAAG,CAC3D,aAAa,CACiB,CAAC;IACjC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO;QACL,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,IAAI;QAC9B,SAAS,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI;QAC1B,WAAW,EAAE,GAAG,CAAC,aAAa,IAAI,IAAI;QACtC,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,IAAI;QACxB,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,YAAY,EAAE,GAAG,IAAI,IAAI;QAC3D,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,IAAI;KAC/B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,sBAAsB,CACpC,UAAyC,EAAE;IAE3C,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,IAAI,oBAAoB,CAAC;IAC9D,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,iBAAiB,CAAC;IAErE,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAChC,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC;QAExD,IAAI,GAAiB,CAAC;QACtB,IAAI,CAAC;YACH,GAAG,GAAG,kBAAkB,CAAC,MAAM,EAAE;gBAC/B,SAAS;gBACT,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;gBAC9C,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,cAAc,EAAE,OAAO,CAAC,cAAc;aACvC,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,mEAAmE;YACnE,mEAAmE;YACnE,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,KAAK,EAAE,IAAa,EAAE,IAAI,EAAE,wBAAwB,EAAE,EACxD,GAAG,CACJ,CAAC;QACJ,CAAC;QAED,oEAAoE;QACpE,yCAAyC;QACzC,OAAO,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { type AdapterClaims, type MapClaimsOptions, collapseRolesToHighest, deriveActorType, mapClaimsToContext, ROLE_PRIVILEGE_ORDER, } from "./claims.js";
2
+ export { type NodiiContextMiddlewareOptions, nodiiContextMiddleware, REQUEST_ID_HEADER, } from "./hono.js";
3
+ export { type GrpcUnaryCall, type GrpcUnaryHandler, type GrpcUnaryInterceptor, type NodiiContextInterceptorOptions, nodiiContextInterceptor, REQUEST_ID_METADATA, } from "./grpc.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/context-adapter/index.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,eAAe,EACf,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,6BAA6B,EAClC,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,8BAA8B,EACnC,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,WAAW,CAAC"}
@@ -0,0 +1,17 @@
1
+ // `@nodii/telemetry/context` — entry-seam context adapters (D44 § 6.5,
2
+ // D405 context adoption; req b3b03fc5 / clarification c9906a51).
3
+ //
4
+ // These adapters SET the per-request `NodiiContext` at the entry seam:
5
+ // - `nodiiContextMiddleware` — Hono middleware (HTTP entry).
6
+ // - `nodiiContextInterceptor` — gRPC unary server interceptor.
7
+ //
8
+ // Both apply the operator-LOCKED field mappings (see `claims.ts`):
9
+ // actor_type ← subject kind; role ← highest-privilege of roles[];
10
+ // on_behalf_of ← delegation claim; request_id ← x-request-id || uuidv7().
11
+ //
12
+ // Lives behind a subpath so consumers who don't run an HTTP/gRPC entry
13
+ // seam (cron / sweeper flows) don't pull `hono`.
14
+ export { collapseRolesToHighest, deriveActorType, mapClaimsToContext, ROLE_PRIVILEGE_ORDER, } from "./claims.js";
15
+ export { nodiiContextMiddleware, REQUEST_ID_HEADER, } from "./hono.js";
16
+ export { nodiiContextInterceptor, REQUEST_ID_METADATA, } from "./grpc.js";
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/context-adapter/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,iEAAiE;AACjE,EAAE;AACF,uEAAuE;AACvE,+DAA+D;AAC/D,iEAAiE;AACjE,EAAE;AACF,mEAAmE;AACnE,oEAAoE;AACpE,4EAA4E;AAC5E,EAAE;AACF,uEAAuE;AACvE,iDAAiD;AAEjD,OAAO,EAGL,sBAAsB,EACtB,eAAe,EACf,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAEL,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EAKL,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,WAAW,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export { initTelemetry } from "./init.js";
2
2
  export { forceFlush, getOtlpAdapterHandle, installOtlpAdapter, shutdownOtlpAdapter, } from "./otlp-adapter.js";
3
3
  export type { OtlpAdapterHandle, OtlpAdapterConfig } from "./otlp-adapter.js";
4
- export { logger } from "./logger.js";
4
+ export { logger, createLogger } from "./logger.js";
5
+ export type { NodiiLogger, LoggerOptions, LogSink, ErrSerializer, } from "./logger.js";
5
6
  export { tracer, withSpanAttributes, getSagaTracer } from "./tracer.js";
6
7
  export { getActiveSpan } from "./active-span.js";
7
8
  export type { ActiveSpanRef } from "./active-span.js";
@@ -17,7 +18,8 @@ export type { ConfigureTelemetryOptions } from "./configure.js";
17
18
  export { intent, IntentTypeNotRegistered } from "./intent.js";
18
19
  export { agentInvocable, readAgentRegistry, AGENT_INVOCABLE_METADATA, } from "./agent-invocable.js";
19
20
  export { getContext, requireContext, withContext, } from "./context.js";
20
- export { isPiiFieldName, redactString, redactFields, PII_FIELD_NAMES, } from "./redaction.js";
21
+ export { isPiiFieldName, redactString, redactFields, isLuhnValid, scrubPanString, PII_FIELD_NAMES, } from "./redaction.js";
22
+ export type { RedactionConfig } from "./redaction.js";
21
23
  export { uuidv7, isUuidv7 } from "./uuid.js";
22
24
  export { TelemetryNotInitialized } from "./types.js";
23
25
  export type { ActorType, AgentInvocableAuthTier, AgentInvocableConfig, AgentInvocableIdempotency, AgentInvocableMode, AgentInvocableRateLimit, AgentInvocableSideEffect, AutoInstrumentationConfig, ErrorHandlingConfig, ExportFailureReason, InitTelemetryConfig, IntentTypeDefinition, IntentTypeRegistry, LogEnvelope, LogLevel, MetricFamily, MetricVocabularyEntry, MetricVocabularyRegistry, NodiiContext, OtlpAuthConfig, OtlpTokenProvider, ResourceAttributes, SamplingConfig, } from "./types.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EACV,oBAAoB,EACpB,IAAI,EACJ,QAAQ,EACR,WAAW,EACX,UAAU,EACV,UAAU,EACV,UAAU,GACX,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5E,YAAY,EACV,aAAa,EACb,WAAW,EACX,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,8BAA8B,EAC9B,KAAK,EACL,iBAAiB,EACjB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,SAAS,EACT,UAAU,EACV,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EACX,0BAA0B,GAC3B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,YAAY,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,UAAU,EACV,cAAc,EACd,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACrD,YAAY,EACV,SAAS,EACT,sBAAsB,EACtB,oBAAoB,EACpB,yBAAyB,EACzB,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,qBAAqB,EACrB,wBAAwB,EACxB,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,uBAAuB,EACvB,eAAe,EACf,4BAA4B,EAC5B,2BAA2B,GAC5B,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -2,12 +2,18 @@
2
2
  //
3
3
  // Spec: planning hub feature_doc serviceId=nodii-libs docKey=telemetry v3
4
4
  //
5
- // Use subpath imports for tree-shaking:
6
- // - @nodii/telemetry/grpc gRPC interceptor + client wrapper
7
- // - @nodii/telemetry/bullmq BullMQ producer + consumer wrappers
8
- // - @nodii/telemetry/outbox — Outbox emit + drainer instrumentation
9
- // - @nodii/telemetry/system-process System-process adapter
10
- // - @nodii/telemetry/lambda Lambda lifecycle wrapper
5
+ // Subpath imports (the entry-seam adapters per D44 § 6.5 / D405). Each is
6
+ // a real, built, exported subpath verified by the subpath-import test:
7
+ // - @nodii/telemetry/context Hono middleware + gRPC server interceptor
8
+ // that SET NodiiContext at the entry seam.
9
+ // - @nodii/telemetry/outbox Outbox emit + propagation-column adapter.
10
+ // - @nodii/telemetry/drizzle Drizzle audit_event schema fragments.
11
+ //
12
+ // Not yet shipped (D44 § 6.5 lists them; no implementation exists yet —
13
+ // they are tracked as follow-on requests, NOT advertised as vapor):
14
+ // - bullmq (BullMQ producer/consumer context envelope, D42)
15
+ // - system-process (cron/sweeper synthetic context, § 6.6)
16
+ // - saga (saga-step context coupling)
11
17
  //
12
18
  // Top-level barrel re-exports the public surface so the common-case
13
19
  // import works:
@@ -17,7 +23,7 @@
17
23
  // type NodiiContext } from '@nodii/telemetry';
18
24
  export { initTelemetry } from "./init.js";
19
25
  export { forceFlush, getOtlpAdapterHandle, installOtlpAdapter, shutdownOtlpAdapter, } from "./otlp-adapter.js";
20
- export { logger } from "./logger.js";
26
+ export { logger, createLogger } from "./logger.js";
21
27
  export { tracer, withSpanAttributes, getSagaTracer } from "./tracer.js";
22
28
  export { getActiveSpan } from "./active-span.js";
23
29
  export { forensicParentValue, parseTraceparent, resolveSpanParent, } from "./traceparent.js";
@@ -28,7 +34,7 @@ export { configureTelemetry } from "./configure.js";
28
34
  export { intent, IntentTypeNotRegistered } from "./intent.js";
29
35
  export { agentInvocable, readAgentRegistry, AGENT_INVOCABLE_METADATA, } from "./agent-invocable.js";
30
36
  export { getContext, requireContext, withContext, } from "./context.js";
31
- export { isPiiFieldName, redactString, redactFields, PII_FIELD_NAMES, } from "./redaction.js";
37
+ export { isPiiFieldName, redactString, redactFields, isLuhnValid, scrubPanString, PII_FIELD_NAMES, } from "./redaction.js";
32
38
  export { uuidv7, isUuidv7 } from "./uuid.js";
33
39
  export { TelemetryNotInitialized } from "./types.js";
34
40
  export { DEFAULT_SAMPLING_CONFIG, METRIC_FAMILIES, AGENT_INVOCABLE_SIDE_EFFECTS, REPLICA_CONSUMER_VOCABULARY, } from "./types.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,EAAE;AACF,0EAA0E;AAC1E,EAAE;AACF,wCAAwC;AACxC,2EAA2E;AAC3E,6EAA6E;AAC7E,+EAA+E;AAC/E,gEAAgE;AAChE,kEAAkE;AAClE,EAAE;AACF,oEAAoE;AACpE,gBAAgB;AAChB,EAAE;AACF,oEAAoE;AACpE,gEAAgE;AAChE,0DAA0D;AAE1D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAWjD,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAS5E,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,8BAA8B,EAC9B,KAAK,EACL,iBAAiB,EACjB,cAAc,GACf,MAAM,YAAY,CAAC;AAYpB,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,OAAO,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,UAAU,EACV,cAAc,EACd,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AA0BrD,OAAO,EACL,uBAAuB,EACvB,eAAe,EACf,4BAA4B,EAC5B,2BAA2B,GAC5B,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,EAAE;AACF,0EAA0E;AAC1E,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,4EAA4E;AAC5E,2EAA2E;AAC3E,4EAA4E;AAC5E,wEAAwE;AACxE,EAAE;AACF,wEAAwE;AACxE,oEAAoE;AACpE,uEAAuE;AACvE,8DAA8D;AAC9D,mDAAmD;AACnD,EAAE;AACF,oEAAoE;AACpE,gBAAgB;AAChB,EAAE;AACF,oEAAoE;AACpE,gEAAgE;AAChE,0DAA0D;AAE1D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAOnD,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAWjD,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAS5E,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,8BAA8B,EAC9B,KAAK,EACL,iBAAiB,EACjB,cAAc,GACf,MAAM,YAAY,CAAC;AAYpB,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,OAAO,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,UAAU,EACV,cAAc,EACd,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,cAAc,EACd,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AA0BrD,OAAO,EACL,uBAAuB,EACvB,eAAe,EACf,4BAA4B,EAC5B,2BAA2B,GAC5B,MAAM,YAAY,CAAC"}
package/dist/logger.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type RedactionConfig } from "./redaction.js";
1
2
  import type { LogEnvelope } from "./types.js";
2
3
  /** A sink that consumes formed log envelopes. Default = JSON to stdio. */
3
4
  export type LogSink = (envelope: LogEnvelope, fields: Record<string, unknown>) => void;
@@ -9,11 +10,59 @@ export type LogSink = (envelope: LogEnvelope, fields: Record<string, unknown>) =
9
10
  export declare function _setLogSink(s: LogSink): void;
10
11
  /** Test-only restore. */
11
12
  export declare function _resetLogSinkForTests(): void;
12
- /** D13/D14 logger. Envelope fields are SDK-injected; do not supply them. */
13
- export declare const logger: {
14
- readonly debug: (msg: string, fields?: Record<string, unknown>) => void;
15
- readonly info: (msg: string, fields?: Record<string, unknown>) => void;
16
- readonly warn: (msg: string, fields?: Record<string, unknown>) => void;
17
- readonly error: (msg: string, fields?: Record<string, unknown>) => void;
18
- };
13
+ /**
14
+ * Serializes an Error into a plain record for structured logging. The
15
+ * factory accepts a custom one (`errSerializer`) so a service can match
16
+ * its pino-era shape; the default mirrors pino's `{ type, message,
17
+ * stack }`. The serialized object still flows through D15 redaction.
18
+ */
19
+ export type ErrSerializer = (err: Error) => Record<string, unknown>;
20
+ /**
21
+ * B3 logger options (drift f2336404 / req 6e0dc381). A service builds a
22
+ * logger matching its needs via {@link createLogger} instead of
23
+ * self-rolling pino. All fields optional; omitting all of them yields
24
+ * the legacy 4-method singleton behaviour.
25
+ */
26
+ export interface LoggerOptions {
27
+ /** Base bindings merged into every line's fields (e.g. `{ component }`). */
28
+ readonly bindings?: Record<string, unknown>;
29
+ /** Extra redaction (REDACT_PATHS + Luhn PAN scrub). */
30
+ readonly redaction?: RedactionConfig;
31
+ /** Custom destination sink. Default: JSON to stdout/stderr. */
32
+ readonly sink?: LogSink;
33
+ /** Custom Error serializer for the `err` field. */
34
+ readonly errSerializer?: ErrSerializer;
35
+ }
36
+ /** The D13/D14 logger surface, extended with B3 child/redact. */
37
+ export interface NodiiLogger {
38
+ debug(msg: string, fields?: Record<string, unknown>): void;
39
+ info(msg: string, fields?: Record<string, unknown>): void;
40
+ warn(msg: string, fields?: Record<string, unknown>): void;
41
+ error(msg: string, fields?: Record<string, unknown>): void;
42
+ /**
43
+ * Returns a child logger that carries `bindings` on every emitted
44
+ * line, merged under (overridden by) per-call fields. The parent is
45
+ * unaffected. Child bindings stack with the parent's.
46
+ */
47
+ child(bindings: Record<string, unknown>): NodiiLogger;
48
+ /**
49
+ * Returns a logger that additionally masks `paths` (REDACT_PATHS) on
50
+ * top of the D2 PII list. Additive with any existing redaction config.
51
+ */
52
+ redact(paths: readonly string[]): NodiiLogger;
53
+ }
54
+ /**
55
+ * B3 factory (drift f2336404 / req 6e0dc381). Construct a logger with
56
+ * base bindings, a custom destination sink, a custom Error serializer,
57
+ * and extra redaction (REDACT_PATHS + Luhn PAN scrub) — so a service
58
+ * adopts the SDK logger instead of self-rolling pino, WITHOUT losing
59
+ * D15 PII redaction.
60
+ */
61
+ export declare function createLogger(options?: LoggerOptions): NodiiLogger;
62
+ /**
63
+ * D13/D14 logger. Envelope fields are SDK-injected; do not supply them.
64
+ * This is the default instance (no bindings, default redaction + sink);
65
+ * `.child()` / `.redact()` derive configured loggers from it.
66
+ */
67
+ export declare const logger: NodiiLogger;
19
68
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,WAAW,EAAY,MAAM,YAAY,CAAC;AAExD,0EAA0E;AAC1E,MAAM,MAAM,OAAO,GAAG,CACpB,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,IAAI,CAAC;AAIV;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAE5C;AAED,yBAAyB;AACzB,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AA+DD,4EAA4E;AAC5E,eAAO,MAAM,MAAM;0BACN,MAAM,WAAW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI;yBAGhD,MAAM,WAAW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI;yBAG/C,MAAM,WAAW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI;0BAG9C,MAAM,WAAW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI;CAGlD,CAAC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAmBA,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,WAAW,EAAY,MAAM,YAAY,CAAC;AAExD,0EAA0E;AAC1E,MAAM,MAAM,OAAO,GAAG,CACpB,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,IAAI,CAAC;AAIV;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAE5C;AAED,yBAAyB;AACzB,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAgBD;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAMpE;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,uDAAuD;IACvD,QAAQ,CAAC,SAAS,CAAC,EAAE,eAAe,CAAC;IACrC,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,mDAAmD;IACnD,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;CACxC;AAED,iEAAiE;AACjE,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3D,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1D,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1D,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3D;;;;OAIG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,CAAC;IACtD;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,CAAC;CAC/C;AA6GD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,aAAkB,GAAG,WAAW,CAOrE;AAED;;;;GAIG;AACH,eAAO,MAAM,MAAM,EAAE,WAA4B,CAAC"}
package/dist/logger.js CHANGED
@@ -16,7 +16,7 @@
16
16
  import { getActiveSpan } from "./active-span.js";
17
17
  import { getContext } from "./context.js";
18
18
  import { requireTelemetry } from "./init.js";
19
- import { attemptRedactFields, attemptRedactString } from "./redaction.js";
19
+ import { attemptRedactFields, attemptRedactString, } from "./redaction.js";
20
20
  import { incSdkCounter } from "./sdk-counters.js";
21
21
  let sink = defaultJsonSink;
22
22
  /**
@@ -42,20 +42,31 @@ function defaultJsonSink(envelope, fields) {
42
42
  process.stdout.write(`${line}\n`);
43
43
  }
44
44
  }
45
- function emit(level, msg, fields) {
45
+ function defaultErrSerializer(err) {
46
+ return { type: err.name, message: err.message, stack: err.stack };
47
+ }
48
+ function emit(opts, level, msg, callFields) {
46
49
  const cfg = requireTelemetry();
47
50
  const ctx = getContext();
48
- // D15 redaction (fail-closed). Run BOTH passes before forming the
49
- // envelope; if either throws, drop the line + bump the counter.
50
- const msgResult = attemptRedactString(msg);
51
+ // Merge base bindings (child stack) under per-call fields. An `err`
52
+ // value that is an Error is serialized via the (possibly custom)
53
+ // serializer BEFORE redaction so the serialized shape is scrubbed too.
54
+ const merged = { ...opts.bindings, ...callFields };
55
+ if (merged.err instanceof Error) {
56
+ merged.err = opts.errSerializer(merged.err);
57
+ }
58
+ // D15 redaction (fail-closed) + B3 config. Run BOTH passes before
59
+ // forming the envelope; if either throws, drop the line + bump.
60
+ const msgResult = attemptRedactString(msg, opts.redaction);
51
61
  if (!msgResult.ok) {
52
62
  incSdkCounter("telemetry.sdk.redaction_failure_total", {
53
63
  reason: msgResult.reason,
54
64
  });
55
65
  return;
56
66
  }
57
- const fieldsResult = fields
58
- ? attemptRedactFields(fields)
67
+ const hasFields = Object.keys(merged).length > 0;
68
+ const fieldsResult = hasFields
69
+ ? attemptRedactFields(merged, opts.redaction)
59
70
  : { ok: true, redacted: {} };
60
71
  if (!fieldsResult.ok) {
61
72
  incSdkCounter("telemetry.sdk.redaction_failure_total", {
@@ -79,21 +90,65 @@ function emit(level, msg, fields) {
79
90
  span_id: span?.span_id ?? null,
80
91
  intent_id: ctx?.intent_id ?? null,
81
92
  };
82
- sink(envelope, fieldsResult.redacted);
93
+ // Per-logger sink overrides the module-global sink (factory
94
+ // destination); otherwise fall through to the installed global sink
95
+ // (which the OTel adapter swaps in TS-12).
96
+ const target = opts.sink ?? sink;
97
+ target(envelope, fieldsResult.redacted);
98
+ }
99
+ function mergeRedaction(base, extraPaths) {
100
+ return {
101
+ extraPaths: [...(base?.extraPaths ?? []), ...extraPaths],
102
+ scrubPan: base?.scrubPan,
103
+ };
104
+ }
105
+ function makeLogger(opts) {
106
+ return {
107
+ debug(msg, fields) {
108
+ emit(opts, "debug", msg, fields);
109
+ },
110
+ info(msg, fields) {
111
+ emit(opts, "info", msg, fields);
112
+ },
113
+ warn(msg, fields) {
114
+ emit(opts, "warn", msg, fields);
115
+ },
116
+ error(msg, fields) {
117
+ emit(opts, "error", msg, fields);
118
+ },
119
+ child(bindings) {
120
+ return makeLogger({
121
+ ...opts,
122
+ bindings: { ...opts.bindings, ...bindings },
123
+ });
124
+ },
125
+ redact(paths) {
126
+ return makeLogger({
127
+ ...opts,
128
+ redaction: mergeRedaction(opts.redaction, paths),
129
+ });
130
+ },
131
+ };
83
132
  }
84
- /** D13/D14 logger. Envelope fields are SDK-injected; do not supply them. */
85
- export const logger = {
86
- debug(msg, fields) {
87
- emit("debug", msg, fields);
88
- },
89
- info(msg, fields) {
90
- emit("info", msg, fields);
91
- },
92
- warn(msg, fields) {
93
- emit("warn", msg, fields);
94
- },
95
- error(msg, fields) {
96
- emit("error", msg, fields);
97
- },
98
- };
133
+ /**
134
+ * B3 factory (drift f2336404 / req 6e0dc381). Construct a logger with
135
+ * base bindings, a custom destination sink, a custom Error serializer,
136
+ * and extra redaction (REDACT_PATHS + Luhn PAN scrub) — so a service
137
+ * adopts the SDK logger instead of self-rolling pino, WITHOUT losing
138
+ * D15 PII redaction.
139
+ */
140
+ export function createLogger(options = {}) {
141
+ return makeLogger({
142
+ bindings: options.bindings ? { ...options.bindings } : {},
143
+ redaction: options.redaction,
144
+ sink: options.sink,
145
+ errSerializer: options.errSerializer ?? defaultErrSerializer,
146
+ });
147
+ }
148
+ /**
149
+ * D13/D14 logger. Envelope fields are SDK-injected; do not supply them.
150
+ * This is the default instance (no bindings, default redaction + sink);
151
+ * `.child()` / `.redact()` derive configured loggers from it.
152
+ */
153
+ export const logger = createLogger();
99
154
  //# sourceMappingURL=logger.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { type OutboxColumns, type OutboxEvent, type OutboxWriter, _resetOutboxWriterForTests, _setOutboxWriter, emitOutbox, } from "../outbox-emit.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/outbox/index.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,0BAA0B,EAC1B,gBAAgB,EAChB,UAAU,GACX,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,13 @@
1
+ // `@nodii/telemetry/outbox` — Outbox emit + propagation-column adapter
2
+ // (D43, communication-doctrine § 9.1).
3
+ //
4
+ // `emitOutbox(event)` stages an outbox row deriving the propagation
5
+ // columns (`tenant_id, request_id, intent_id, trace_parent, trace_state`)
6
+ // + `context_envelope` jsonb from the active `NodiiContext` + OTel span.
7
+ // `_setOutboxWriter` lets service init wire the per-service writer that
8
+ // persists the row inside the service's transaction.
9
+ //
10
+ // Lives behind a subpath so consumers who don't emit outbox rows don't
11
+ // load the helper.
12
+ export { _resetOutboxWriterForTests, _setOutboxWriter, emitOutbox, } from "../outbox-emit.js";
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/outbox/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,uCAAuC;AACvC,EAAE;AACF,oEAAoE;AACpE,0EAA0E;AAC1E,yEAAyE;AACzE,wEAAwE;AACxE,qDAAqD;AACrD,EAAE;AACF,uEAAuE;AACvE,mBAAmB;AAEnB,OAAO,EAIL,0BAA0B,EAC1B,gBAAgB,EAChB,UAAU,GACX,MAAM,mBAAmB,CAAC"}
@@ -13,6 +13,42 @@ export declare const PII_FIELD_NAMES: readonly string[];
13
13
  * beyond `toLowerCase()`).
14
14
  */
15
15
  export declare function isPiiFieldName(key: string): boolean;
16
+ /**
17
+ * B3 redaction config (drift f2336404 / req 6e0dc381). Extends the
18
+ * canonical D15 passes with caller-supplied options so a service (e.g.
19
+ * billing) can adopt the SDK logger instead of self-rolling pino:
20
+ *
21
+ * - `extraPaths` — additional field names (REDACT_PATHS) masked to
22
+ * `[REDACTED]` on top of the D2 PII list. Case-insensitive bare-key
23
+ * match, same semantics as `PII_FIELD_NAMES`.
24
+ * - `scrubPan` — when true (default), a Luhn-VALIDATED PAN scrubber
25
+ * masks card-number-like digit runs in strings, but ONLY if they
26
+ * pass the Luhn checksum. This is stricter (and safer against
27
+ * over-redaction) than the loose `credit_card` regex: a 16-digit
28
+ * run that fails Luhn is left intact.
29
+ *
30
+ * The config is purely additive — `undefined` reproduces the legacy
31
+ * v0.1.0 behaviour byte-for-byte.
32
+ */
33
+ export interface RedactionConfig {
34
+ /** Extra field names to mask (REDACT_PATHS), on top of the D2 list. */
35
+ readonly extraPaths?: readonly string[];
36
+ /** Luhn-validated PAN scrub on string values. Default: true. */
37
+ readonly scrubPan?: boolean;
38
+ }
39
+ /**
40
+ * Luhn checksum. Returns true iff the digit string passes the Luhn
41
+ * (mod-10) check used by real card PANs. Used by the PAN scrubber so a
42
+ * non-card digit run (an order id, a phone-less numeric token) is NOT
43
+ * masked — preventing the over-redaction the loose regex causes.
44
+ */
45
+ export declare function isLuhnValid(digits: string): boolean;
46
+ /**
47
+ * Mask Luhn-valid PAN-shaped digit runs in a string. A run is masked to
48
+ * `[REDACTED]` ONLY when its bare digits pass `isLuhnValid`; non-Luhn
49
+ * runs of the same length are left untouched (no over-redaction).
50
+ */
51
+ export declare function scrubPanString(s: string): string;
16
52
  /**
17
53
  * Redacts an arbitrary record by replacing values whose keys match the
18
54
  * D2 PII list with `[REDACTED]`. Nested records are walked recursively.
@@ -23,13 +59,13 @@ export declare function isPiiFieldName(key: string): boolean;
23
59
  * object (e.g. circular reference encountered via JSON.stringify
24
60
  * elsewhere) the caller wraps this in `attemptRedaction()` to catch.
25
61
  */
26
- export declare function redactFields(input: Record<string, unknown>): Record<string, unknown>;
62
+ export declare function redactFields(input: Record<string, unknown>, cfg?: RedactionConfig): Record<string, unknown>;
27
63
  /**
28
64
  * Apply the D15 regex pass to a string. Returns the redacted string.
29
65
  * Each pattern that fires replaces its match with [REDACTED]; multiple
30
66
  * matches per string are all replaced.
31
67
  */
32
- export declare function redactString(s: string): string;
68
+ export declare function redactString(s: string, cfg?: RedactionConfig): string;
33
69
  /**
34
70
  * Fail-closed wrapper. Returns:
35
71
  * - `{ ok: true, redacted }` when redaction succeeded.
@@ -46,6 +82,6 @@ export type RedactionResult<T> = {
46
82
  ok: false;
47
83
  reason: string;
48
84
  };
49
- export declare function attemptRedactFields(input: Record<string, unknown>): RedactionResult<Record<string, unknown>>;
50
- export declare function attemptRedactString(s: string): RedactionResult<string>;
85
+ export declare function attemptRedactFields(input: Record<string, unknown>, cfg?: RedactionConfig): RedactionResult<Record<string, unknown>>;
86
+ export declare function attemptRedactString(s: string, cfg?: RedactionConfig): RedactionResult<string>;
51
87
  //# sourceMappingURL=redaction.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAaA;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAyCnC,CAAC;AAwCX;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUzB;AAYD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAM9C;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,CAAC,CAAA;CAAE,GACzB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAS1C;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAStE"}
1
+ {"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAaA;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAyCnC,CAAC;AAwCX;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAuBD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAgBnD;AAOD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,GAAG,CAAC,EAAE,eAAe,GACpB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGzB;AA2BD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,eAAe,GAAG,MAAM,CAErE;AAiBD;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,CAAC,CAAA;CAAE,GACzB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,GAAG,CAAC,EAAE,eAAe,GACpB,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAS1C;AAED,wBAAgB,mBAAmB,CACjC,CAAC,EAAE,MAAM,EACT,GAAG,CAAC,EAAE,eAAe,GACpB,eAAe,CAAC,MAAM,CAAC,CASzB"}
package/dist/redaction.js CHANGED
@@ -103,6 +103,56 @@ const REGEX_PATTERNS = [
103
103
  export function isPiiFieldName(key) {
104
104
  return PII_FIELD_SET.has(key.toLowerCase());
105
105
  }
106
+ function resolveConfig(cfg) {
107
+ const extraSet = new Set();
108
+ if (cfg?.extraPaths) {
109
+ for (const p of cfg.extraPaths)
110
+ extraSet.add(p.toLowerCase());
111
+ }
112
+ // PAN scrub defaults ON. It only ever masks Luhn-valid runs, so it is
113
+ // safe to enable by default for the configurable layer.
114
+ const scrubPan = cfg?.scrubPan ?? true;
115
+ return { extraSet, scrubPan };
116
+ }
117
+ /**
118
+ * Luhn checksum. Returns true iff the digit string passes the Luhn
119
+ * (mod-10) check used by real card PANs. Used by the PAN scrubber so a
120
+ * non-card digit run (an order id, a phone-less numeric token) is NOT
121
+ * masked — preventing the over-redaction the loose regex causes.
122
+ */
123
+ export function isLuhnValid(digits) {
124
+ if (!/^\d+$/.test(digits) || digits.length < 12 || digits.length > 19) {
125
+ return false;
126
+ }
127
+ let sum = 0;
128
+ let double = false;
129
+ for (let i = digits.length - 1; i >= 0; i--) {
130
+ let d = digits.charCodeAt(i) - 48;
131
+ if (double) {
132
+ d *= 2;
133
+ if (d > 9)
134
+ d -= 9;
135
+ }
136
+ sum += d;
137
+ double = !double;
138
+ }
139
+ return sum % 10 === 0;
140
+ }
141
+ // PAN candidate: 12–19 digits, optionally grouped by single spaces or
142
+ // dashes (the common `4111 1111 1111 1111` / `4111-1111-...` shapes).
143
+ // We extract the bare digits, Luhn-check them, and only mask on a pass.
144
+ const PAN_CANDIDATE_RE = /\b\d(?:[ -]?\d){11,18}\b/g;
145
+ /**
146
+ * Mask Luhn-valid PAN-shaped digit runs in a string. A run is masked to
147
+ * `[REDACTED]` ONLY when its bare digits pass `isLuhnValid`; non-Luhn
148
+ * runs of the same length are left untouched (no over-redaction).
149
+ */
150
+ export function scrubPanString(s) {
151
+ return s.replace(PAN_CANDIDATE_RE, (match) => {
152
+ const bare = match.replace(/[ -]/g, "");
153
+ return isLuhnValid(bare) ? REDACTED : match;
154
+ });
155
+ }
106
156
  /**
107
157
  * Redacts an arbitrary record by replacing values whose keys match the
108
158
  * D2 PII list with `[REDACTED]`. Nested records are walked recursively.
@@ -113,26 +163,30 @@ export function isPiiFieldName(key) {
113
163
  * object (e.g. circular reference encountered via JSON.stringify
114
164
  * elsewhere) the caller wraps this in `attemptRedaction()` to catch.
115
165
  */
116
- export function redactFields(input) {
166
+ export function redactFields(input, cfg) {
167
+ const resolved = resolveConfig(cfg);
168
+ return redactFieldsResolved(input, resolved);
169
+ }
170
+ function redactFieldsResolved(input, resolved) {
117
171
  const out = {};
118
172
  for (const [k, v] of Object.entries(input)) {
119
- if (isPiiFieldName(k)) {
173
+ if (isPiiFieldName(k) || resolved.extraSet.has(k.toLowerCase())) {
120
174
  out[k] = REDACTED;
121
175
  continue;
122
176
  }
123
- out[k] = redactValue(v);
177
+ out[k] = redactValue(v, resolved);
124
178
  }
125
179
  return out;
126
180
  }
127
- function redactValue(v) {
181
+ function redactValue(v, resolved) {
128
182
  if (v === null || v === undefined)
129
183
  return v;
130
184
  if (typeof v === "string")
131
- return redactString(v);
185
+ return redactStringResolved(v, resolved);
132
186
  if (Array.isArray(v))
133
- return v.map(redactValue);
187
+ return v.map((e) => redactValue(e, resolved));
134
188
  if (typeof v === "object") {
135
- return redactFields(v);
189
+ return redactFieldsResolved(v, resolved);
136
190
  }
137
191
  return v;
138
192
  }
@@ -141,16 +195,26 @@ function redactValue(v) {
141
195
  * Each pattern that fires replaces its match with [REDACTED]; multiple
142
196
  * matches per string are all replaced.
143
197
  */
144
- export function redactString(s) {
198
+ export function redactString(s, cfg) {
199
+ return redactStringResolved(s, resolveConfig(cfg));
200
+ }
201
+ function redactStringResolved(s, resolved) {
145
202
  let out = s;
203
+ // PAN-Luhn scrub runs FIRST: it's stricter than the loose credit_card
204
+ // regex (Luhn-validated), so masking valid PANs here leaves the regex
205
+ // pass to catch the remaining looser shapes without double work on the
206
+ // already-masked bytes.
207
+ if (resolved.scrubPan) {
208
+ out = scrubPanString(out);
209
+ }
146
210
  for (const { re } of REGEX_PATTERNS) {
147
211
  out = out.replace(re, REDACTED);
148
212
  }
149
213
  return out;
150
214
  }
151
- export function attemptRedactFields(input) {
215
+ export function attemptRedactFields(input, cfg) {
152
216
  try {
153
- return { ok: true, redacted: redactFields(input) };
217
+ return { ok: true, redacted: redactFields(input, cfg) };
154
218
  }
155
219
  catch (err) {
156
220
  return {
@@ -159,9 +223,9 @@ export function attemptRedactFields(input) {
159
223
  };
160
224
  }
161
225
  }
162
- export function attemptRedactString(s) {
226
+ export function attemptRedactString(s, cfg) {
163
227
  try {
164
- return { ok: true, redacted: redactString(s) };
228
+ return { ok: true, redacted: redactString(s, cfg) };
165
229
  }
166
230
  catch (err) {
167
231
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"redaction.js","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"redaction.js","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodii/telemetry",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Substrate observability library for the Nodii microservice stack — OTel wrapper, log envelope, intent lifecycle, audit signal, agent registry, context propagation. Spec: planning hub docKey=telemetry.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,6 +14,14 @@
14
14
  "./drizzle": {
15
15
  "types": "./dist/drizzle/index.d.ts",
16
16
  "default": "./dist/drizzle/index.js"
17
+ },
18
+ "./context": {
19
+ "types": "./dist/context-adapter/index.d.ts",
20
+ "default": "./dist/context-adapter/index.js"
21
+ },
22
+ "./outbox": {
23
+ "types": "./dist/outbox/index.d.ts",
24
+ "default": "./dist/outbox/index.js"
17
25
  }
18
26
  },
19
27
  "files": [
@@ -47,10 +55,11 @@
47
55
  "@opentelemetry/semantic-conventions": "^1.28.0"
48
56
  },
49
57
  "peerDependencies": {
50
- "@nodii/audit-chain": "^0.9.1",
58
+ "@nodii/audit-chain": ">=0.9.1 <1.0.0",
51
59
  "@opentelemetry/api": "^1.9.0",
52
60
  "@opentelemetry/api-logs": "^0.57.2",
53
- "drizzle-orm": ">=0.36 <0.50"
61
+ "drizzle-orm": ">=0.36 <0.50",
62
+ "hono": "^4.0.0"
54
63
  },
55
64
  "peerDependenciesMeta": {
56
65
  "@nodii/audit-chain": {
@@ -64,11 +73,15 @@
64
73
  },
65
74
  "drizzle-orm": {
66
75
  "optional": true
76
+ },
77
+ "hono": {
78
+ "optional": true
67
79
  }
68
80
  },
69
81
  "devDependencies": {
70
- "@nodii/audit-chain": "0.9.1",
82
+ "@nodii/audit-chain": "0.10.0",
71
83
  "drizzle-orm": "^0.45.2",
84
+ "hono": "^4.4.0",
72
85
  "postgres": "^3.4.0",
73
86
  "typescript": "^5.9.3"
74
87
  },