@nodii/telemetry 0.0.2 → 0.3.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.
Files changed (82) hide show
  1. package/README.md +223 -1
  2. package/dist/active-span.d.ts +12 -0
  3. package/dist/active-span.d.ts.map +1 -0
  4. package/dist/active-span.js +20 -0
  5. package/dist/active-span.js.map +1 -0
  6. package/dist/agent-invocable.d.ts +24 -0
  7. package/dist/agent-invocable.d.ts.map +1 -0
  8. package/dist/agent-invocable.js +64 -0
  9. package/dist/agent-invocable.js.map +1 -0
  10. package/dist/audit-types.d.ts +15 -0
  11. package/dist/audit-types.d.ts.map +1 -0
  12. package/dist/audit-types.js +39 -0
  13. package/dist/audit-types.js.map +1 -0
  14. package/dist/audit.d.ts +226 -0
  15. package/dist/audit.d.ts.map +1 -0
  16. package/dist/audit.js +255 -0
  17. package/dist/audit.js.map +1 -0
  18. package/dist/configure.d.ts +34 -0
  19. package/dist/configure.d.ts.map +1 -0
  20. package/dist/configure.js +23 -0
  21. package/dist/configure.js.map +1 -0
  22. package/dist/context.d.ts +40 -0
  23. package/dist/context.d.ts.map +1 -0
  24. package/dist/context.js +70 -0
  25. package/dist/context.js.map +1 -0
  26. package/dist/drizzle/audit-schema.d.ts +884 -0
  27. package/dist/drizzle/audit-schema.d.ts.map +1 -0
  28. package/dist/drizzle/audit-schema.js +268 -0
  29. package/dist/drizzle/audit-schema.js.map +1 -0
  30. package/dist/drizzle/index.d.ts +3 -0
  31. package/dist/drizzle/index.d.ts.map +1 -0
  32. package/dist/drizzle/index.js +5 -0
  33. package/dist/drizzle/index.js.map +1 -0
  34. package/dist/index.d.ts +21 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +31 -5
  37. package/dist/index.js.map +1 -1
  38. package/dist/init.d.ts +39 -0
  39. package/dist/init.d.ts.map +1 -0
  40. package/dist/init.js +114 -0
  41. package/dist/init.js.map +1 -0
  42. package/dist/intent.d.ts +21 -0
  43. package/dist/intent.d.ts.map +1 -0
  44. package/dist/intent.js +123 -0
  45. package/dist/intent.js.map +1 -0
  46. package/dist/logger.d.ts +19 -0
  47. package/dist/logger.d.ts.map +1 -0
  48. package/dist/logger.js +99 -0
  49. package/dist/logger.js.map +1 -0
  50. package/dist/metrics.d.ts +53 -0
  51. package/dist/metrics.d.ts.map +1 -0
  52. package/dist/metrics.js +152 -0
  53. package/dist/metrics.js.map +1 -0
  54. package/dist/otlp-adapter.d.ts +44 -0
  55. package/dist/otlp-adapter.d.ts.map +1 -0
  56. package/dist/otlp-adapter.js +351 -0
  57. package/dist/otlp-adapter.js.map +1 -0
  58. package/dist/outbox-emit.d.ts +34 -0
  59. package/dist/outbox-emit.d.ts.map +1 -0
  60. package/dist/outbox-emit.js +58 -0
  61. package/dist/outbox-emit.js.map +1 -0
  62. package/dist/redaction.d.ts +51 -0
  63. package/dist/redaction.d.ts.map +1 -0
  64. package/dist/redaction.js +173 -0
  65. package/dist/redaction.js.map +1 -0
  66. package/dist/sdk-counters.d.ts +14 -0
  67. package/dist/sdk-counters.d.ts.map +1 -0
  68. package/dist/sdk-counters.js +42 -0
  69. package/dist/sdk-counters.js.map +1 -0
  70. package/dist/tracer.d.ts +63 -0
  71. package/dist/tracer.d.ts.map +1 -0
  72. package/dist/tracer.js +214 -0
  73. package/dist/tracer.js.map +1 -0
  74. package/dist/types.d.ts +230 -0
  75. package/dist/types.d.ts.map +1 -0
  76. package/dist/types.js +66 -0
  77. package/dist/types.js.map +1 -0
  78. package/dist/uuid.d.ts +5 -0
  79. package/dist/uuid.d.ts.map +1 -0
  80. package/dist/uuid.js +34 -0
  81. package/dist/uuid.js.map +1 -0
  82. package/package.json +44 -7
package/dist/audit.js ADDED
@@ -0,0 +1,255 @@
1
+ // Audit signal class — the canonical adapter per `02-audit-doctrine.md`
2
+ // § 3 (row shape) + § 6.0 (Patterns A/B/C tx-bound) + § 13 (opt-in chain).
3
+ //
4
+ // "If a tenant could legitimately need to see this row, it's audit, not
5
+ // log." — D17 cultural rule, doctrine § 1.
6
+ //
7
+ // The library ships the adapter (the write path); the audit emission
8
+ // policy (D33 three paths: method-level interceptor, saga lifecycle,
9
+ // agent-layer double-emission) and the `<service>_audit_event` schema
10
+ // live in `02-audit-doctrine.md`, not here. The Drizzle schema fragment
11
+ // for the table ships from `@nodii/telemetry/drizzle`.
12
+ //
13
+ // Three emission patterns per § 6.0:
14
+ // - Pattern A — explicit tx pass: `audit.emit(tx, args)`
15
+ // - Pattern B — Hono `c.set("txn", tx)` middleware: also `audit.emit(tx, args)`
16
+ // - Pattern C — out-of-band: `audit.emit(args)` (writer owns its tx)
17
+ import { AUDIT_ACTOR_KINDS, AUDIT_OUTCOMES, } from "./audit-types";
18
+ import { getContext } from "./context";
19
+ import { requireTelemetry } from "./init";
20
+ import { uuidv7 } from "./uuid";
21
+ export { AUDIT_ACTOR_KINDS, AUDIT_OUTCOMES };
22
+ /**
23
+ * Thrown by `audit.emit` when no `AuditWriter` has been wired. v0.1.2
24
+ * replaces the v0.1.0–v0.1.1 silent-Noop failure mode (R1 violation,
25
+ * drift `4cc80f62`). Services MUST wire a writer via `_setAuditWriter(...)`
26
+ * before the first `audit.emit` fires.
27
+ */
28
+ export class TelemetryAuditWriterMissing extends Error {
29
+ constructor() {
30
+ super("@nodii/telemetry: audit.emit called before an AuditWriter was wired. " +
31
+ "Drift 4cc80f62 (R1, breaking) removed the v0.1.0–v0.1.1 Noop default " +
32
+ "because it silently dropped audit rows. Wire a real writer via " +
33
+ "`_setAuditWriter(...)` (typically from `@nodii/db-rls`'s adapter " +
34
+ "writing to <serviceName>_audit_event per 02-audit-doctrine.md § 6.0).");
35
+ this.name = "TelemetryAuditWriterMissing";
36
+ }
37
+ }
38
+ let writer = null;
39
+ let chain = null;
40
+ /**
41
+ * SDK-internal: services or the `@nodii/db-rls` adapter sets the writer.
42
+ * Public callers MUST NOT call this; the override exists for the adapter
43
+ * layer.
44
+ */
45
+ export function _setAuditWriter(w) {
46
+ writer = w;
47
+ }
48
+ /** Test-only reset. Clears the wired writer; the next `audit.emit` will throw. */
49
+ export function _resetAuditWriterForTests() {
50
+ writer = null;
51
+ }
52
+ /**
53
+ * SDK-internal: `configureTelemetry({audit: {chain}})` calls this to wire
54
+ * the opt-in chain adapter. Setting `null` removes it.
55
+ */
56
+ export function _setAuditChain(adapter) {
57
+ chain = adapter;
58
+ }
59
+ /** Test-only reset. Removes the wired chain adapter. */
60
+ export function _resetAuditChainForTests() {
61
+ chain = null;
62
+ }
63
+ /** SDK-internal accessor for tests + chain-layer integration probes. */
64
+ export function _getAuditChain() {
65
+ return chain;
66
+ }
67
+ /**
68
+ * Type guard distinguishing the legacy `AuditEvent` from the rich `AuditArgs`.
69
+ * The discriminator is `target_kind`: present on the legacy shape, absent on
70
+ * the rich shape (which uses `resourceType`). Both shapes carry `action`.
71
+ */
72
+ function isLegacyEvent(arg) {
73
+ return ("target_kind" in arg &&
74
+ typeof arg.target_kind === "string" &&
75
+ !("resourceType" in arg));
76
+ }
77
+ /**
78
+ * Map the legacy shape onto the rich `AuditArgs` so the rest of the
79
+ * pipeline only handles one input form. Mapping per doctrine § 3:
80
+ * target_kind → resource_type
81
+ * target_id → resource_id
82
+ * payload → metadata
83
+ * Other rich fields are left undefined for `resolveRow` to fill from ctx.
84
+ */
85
+ function legacyToRich(event) {
86
+ return {
87
+ action: event.action,
88
+ resourceType: event.target_kind,
89
+ resourceId: event.target_id,
90
+ metadata: event.payload ?? {},
91
+ };
92
+ }
93
+ /**
94
+ * Derive the canonical `actor_kind` from a `NodiiContext`. Falls back to
95
+ * `'unknown'` per doctrine § 4.2 — the SDK MUST emit `unknown` when no
96
+ * actor stamp can be derived; this is a security-event marker, not a
97
+ * silent default.
98
+ *
99
+ * Mapping rules per § 4 + auth-sdk § 5.6:
100
+ * ctx.actor_type='user' → 'tenant_user' (callers override to 'employee_user' when platform-admin)
101
+ * ctx.actor_type='agent' → 'agent'
102
+ * ctx.actor_type='service' → 'service'
103
+ * ctx.actor_type='system' → 'system'
104
+ * ctx absent → 'unknown' (security marker)
105
+ */
106
+ function deriveActorKind(ctx) {
107
+ if (!ctx)
108
+ return "unknown";
109
+ switch (ctx.actor_type) {
110
+ case "user":
111
+ return "tenant_user";
112
+ case "agent":
113
+ return "agent";
114
+ case "service":
115
+ return "service";
116
+ case "system":
117
+ return "system";
118
+ default: {
119
+ // Exhaustiveness check + future-proofing if ActorType grows.
120
+ const _exhaustive = ctx.actor_type;
121
+ void _exhaustive;
122
+ return "unknown";
123
+ }
124
+ }
125
+ }
126
+ /**
127
+ * Derive the canonical `actor_id` from `NodiiContext`. For `actor_type='user'`
128
+ * use `user_id`; for `actor_type='agent'` use `on_behalf_of` (the human who
129
+ * authorised the dispatch — per doctrine § 4.1 agent extension); for service
130
+ * + system contexts no `actor_id` is derivable and the caller must supply
131
+ * one if needed.
132
+ */
133
+ function deriveActorId(ctx) {
134
+ if (!ctx)
135
+ return null;
136
+ switch (ctx.actor_type) {
137
+ case "user":
138
+ return ctx.user_id;
139
+ case "agent":
140
+ return ctx.on_behalf_of;
141
+ case "service":
142
+ case "system":
143
+ return null;
144
+ default:
145
+ return null;
146
+ }
147
+ }
148
+ /**
149
+ * Build the canonical `AuditEventRow` from `AuditArgs` + the active
150
+ * `NodiiContext`. Resolution rules (in order):
151
+ * 1. `tenant_id`: args.tenantId → ctx.tenant_id → throw
152
+ * 2. `actor_kind`: args.actorKind → derive from ctx → 'unknown'
153
+ * 3. `actor_id`: args.actorId → derive from ctx → null
154
+ * 4. `request_id`, `intent_id`: args → ctx → null
155
+ * 5. Other fields: pass through args (null when undefined)
156
+ *
157
+ * Throws `Error` when neither args nor ctx carry a `tenant_id` — the
158
+ * audit row cannot be RLS-bound without one, so silently dropping it
159
+ * would be a doctrine § 3 + R1 violation.
160
+ */
161
+ function resolveRow(args) {
162
+ const ctx = getContext();
163
+ const tenantId = args.tenantId ?? ctx?.tenant_id ?? null;
164
+ if (!tenantId) {
165
+ throw new Error("audit.emit: tenant_id is required — pass `tenantId` in args or " +
166
+ "invoke inside a `withContext({tenant_id: ...})` scope. The audit " +
167
+ "row cannot be RLS-bound without it (02-audit-doctrine.md § 3).");
168
+ }
169
+ const actorKind = args.actorKind ?? deriveActorKind(ctx);
170
+ const actorId = args.actorId ?? deriveActorId(ctx);
171
+ return {
172
+ id: uuidv7(),
173
+ tenant_id: tenantId,
174
+ at: new Date(),
175
+ actor_kind: actorKind,
176
+ actor_id: actorId,
177
+ actor_external_id: args.actorExternalId ?? null,
178
+ actor_display: args.actorDisplay ?? null,
179
+ actor_ip: args.actorIp ?? null,
180
+ actor_user_agent: args.actorUserAgent ?? null,
181
+ actor: args.actor ?? null,
182
+ action: args.action,
183
+ resource_type: args.resourceType,
184
+ resource_id: args.resourceId ?? null,
185
+ resource_label: args.resourceLabel ?? null,
186
+ secondary_resource_type: args.secondaryResourceType ?? null,
187
+ secondary_resource_id: args.secondaryResourceId ?? null,
188
+ outcome: args.outcome ?? "success",
189
+ http_status: args.httpStatus ?? null,
190
+ grpc_code: args.grpcCode ?? null,
191
+ error_code: args.errorCode ?? null,
192
+ error_message: args.errorMessage ?? null,
193
+ error_details: args.errorDetails ?? null,
194
+ before: args.before ?? null,
195
+ after: args.after ?? null,
196
+ patch: args.patch ?? null,
197
+ changed_fields: args.changedFields ?? null,
198
+ reason_code: args.reasonCode ?? null,
199
+ reason_text: args.reasonText ?? null,
200
+ request_id: args.requestId ?? ctx?.request_id ?? null,
201
+ correlation_id: args.correlationId ?? null,
202
+ trace_id: args.traceId ?? null,
203
+ span_id: args.spanId ?? null,
204
+ intent_id: args.intentId ?? ctx?.intent_id ?? null,
205
+ saga_id: args.sagaId ?? null,
206
+ step_id: args.stepId ?? null,
207
+ idempotency_key: args.idempotencyKey ?? null,
208
+ queue_name: args.queueName ?? null,
209
+ job_id: args.jobId ?? null,
210
+ parent_audit_event_id: args.parentAuditEventId ?? null,
211
+ read_count: args.readCount ?? null,
212
+ first_read_at: args.firstReadAt ?? null,
213
+ last_read_at: args.lastReadAt ?? null,
214
+ // Chain columns left null unless an adapter is wired (see emitImpl).
215
+ prev_hash: null,
216
+ row_hash: null,
217
+ tenant_audit_seq: null,
218
+ metadata: args.metadata ?? {},
219
+ };
220
+ }
221
+ /**
222
+ * Shared write path. Resolves the row, optionally runs the chain adapter,
223
+ * and hands the row + tx to the writer. Throws `TelemetryAuditWriterMissing`
224
+ * when no writer is wired (drift `4cc80f62` enforcement).
225
+ */
226
+ async function emitImpl(args, tx) {
227
+ requireTelemetry();
228
+ if (writer === null) {
229
+ throw new TelemetryAuditWriterMissing();
230
+ }
231
+ const row = resolveRow(args);
232
+ if (chain !== null) {
233
+ const augmented = await chain.augment(row, tx);
234
+ row.prev_hash = augmented.prev_hash;
235
+ row.row_hash = augmented.row_hash;
236
+ row.tenant_audit_seq = augmented.tenant_audit_seq;
237
+ }
238
+ await writer(row, tx);
239
+ }
240
+ async function emit(a, b) {
241
+ if (b !== undefined) {
242
+ // emit(tx, args) — Pattern A / B.
243
+ return emitImpl(b, a);
244
+ }
245
+ const arg = a;
246
+ if (isLegacyEvent(arg)) {
247
+ return emitImpl(legacyToRich(arg));
248
+ }
249
+ // emit(args) — Pattern C.
250
+ return emitImpl(arg);
251
+ }
252
+ export const audit = {
253
+ emit,
254
+ };
255
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { type AuditChainAdapter } from "./audit";
2
+ /**
3
+ * Arguments to `configureTelemetry`. Currently only `audit.chain` is
4
+ * supported; the shape exists so additional knobs (e.g. log redaction
5
+ * overrides, custom exporters) can be added without an API break.
6
+ */
7
+ export interface ConfigureTelemetryOptions {
8
+ audit?: {
9
+ /**
10
+ * Opt-in tamper-evident chain layer per `02-audit-doctrine.md § 13` +
11
+ * § 15.3. When wired, every `audit.emit` row receives `prev_hash`,
12
+ * `row_hash`, and `tenant_audit_seq` from the adapter before the row
13
+ * lands in the writer.
14
+ *
15
+ * Pass `null` to remove a previously-wired adapter (useful for tests
16
+ * + services that toggle chain coverage via feature flag).
17
+ *
18
+ * The library does NOT inspect the adapter beyond calling `augment`.
19
+ * Implementations (typically `@nodii/audit-chain`) own the hashing,
20
+ * the per-tenant monotonic sequence (via
21
+ * `pg_advisory_xact_lock(hash(tenant_id || 'audit'))`), and any
22
+ * verifier-worker reconciliation.
23
+ */
24
+ chain?: AuditChainAdapter | null;
25
+ };
26
+ }
27
+ /**
28
+ * Wire optional telemetry adapters. Currently the only knob is
29
+ * `audit.chain`. Idempotent: calling repeatedly with `audit.chain` set
30
+ * replaces the previously-wired adapter; calling with `audit` undefined
31
+ * leaves the existing wiring intact.
32
+ */
33
+ export declare function configureTelemetry(opts: ConfigureTelemetryOptions): void;
34
+ //# sourceMappingURL=configure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AAWA,OAAO,EAAkB,KAAK,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjE;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,KAAK,CAAC,EAAE;QACN;;;;;;;;;;;;;;WAcG;QACH,KAAK,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;KAClC,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,yBAAyB,GAAG,IAAI,CAIxE"}
@@ -0,0 +1,23 @@
1
+ // Top-level telemetry configuration knobs that aren't part of
2
+ // `initTelemetry()`'s argument shape — typically opt-in adapter layers
3
+ // (audit chain, future: tracing exporters, metric reducers).
4
+ //
5
+ // `configureTelemetry(...)` is idempotent and order-independent w.r.t.
6
+ // `initTelemetry()`: a service can call it before `initTelemetry()` (set
7
+ // up the chain adapter at module load) or after (wire it during boot
8
+ // once secrets/clients are resolved). The library reads the wired
9
+ // adapter inside `audit.emit` at write time — there is no init-time
10
+ // snapshot.
11
+ import { _setAuditChain } from "./audit";
12
+ /**
13
+ * Wire optional telemetry adapters. Currently the only knob is
14
+ * `audit.chain`. Idempotent: calling repeatedly with `audit.chain` set
15
+ * replaces the previously-wired adapter; calling with `audit` undefined
16
+ * leaves the existing wiring intact.
17
+ */
18
+ export function configureTelemetry(opts) {
19
+ if (opts.audit !== undefined && "chain" in opts.audit) {
20
+ _setAuditChain(opts.audit.chain ?? null);
21
+ }
22
+ }
23
+ //# sourceMappingURL=configure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.js","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,uEAAuE;AACvE,6DAA6D;AAC7D,EAAE;AACF,uEAAuE;AACvE,yEAAyE;AACzE,qEAAqE;AACrE,kEAAkE;AAClE,oEAAoE;AACpE,YAAY;AAEZ,OAAO,EAAE,cAAc,EAA0B,MAAM,SAAS,CAAC;AA4BjE;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAA+B;IAChE,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACtD,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC"}
@@ -0,0 +1,40 @@
1
+ import type { NodiiContext } from "./types";
2
+ /**
3
+ * Returns the active `NodiiContext`, or null if no middleware adapter
4
+ * has set one for this async scope (cron-flow before the
5
+ * `system-process` adapter runs; raw scripts; tests without harness).
6
+ */
7
+ export declare function getContext(): NodiiContext | null;
8
+ /**
9
+ * Like `getContext()` but throws if no context is active. Use this in
10
+ * code that is allowed to assume an adapter ran — typically inside a
11
+ * request handler.
12
+ */
13
+ export declare function requireContext(): NodiiContext;
14
+ /**
15
+ * Run `fn` inside a child scope where `ctx` is the active context. Per
16
+ * D41: never mutates the parent scope — child finishes and the parent
17
+ * scope's context is unaffected.
18
+ */
19
+ export declare function withContext<T>(ctx: NodiiContext, fn: () => T): T;
20
+ /**
21
+ * SDK-internal use only. The middleware adapters (§ 5.9) call this on
22
+ * request entry to set the active context. Service code MUST NOT call
23
+ * this directly — use `withContext()` for child scopes only. Not part
24
+ * of the public barrel; subpath adapters import it via the package
25
+ * internal path.
26
+ */
27
+ export declare function setContextForCurrentScope(ctx: NodiiContext): void;
28
+ /**
29
+ * Update the active context's `intent_id` only. Used by the intent
30
+ * lifecycle (§ 5.6) when `intent.begin()` / `intent.complete()` /
31
+ * `intent.abandon()` activate or deactivate an intent. Returns the new
32
+ * context so callers can verify; throws if no context is active (intent
33
+ * lifecycle must run inside an established `NodiiContext`).
34
+ *
35
+ * This is the ONLY mutation API per D41. It exists because intent
36
+ * activation is a per-scope assignment that the intent lifecycle owns,
37
+ * not a service-code mutation.
38
+ */
39
+ export declare function _setIntentIdInCurrentScope(intentId: string | null): NodiiContext;
40
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI5C;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,YAAY,GAAG,IAAI,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,YAAY,CAQ7C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAEhE;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAEjE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,GAAG,IAAI,GACtB,YAAY,CAUd"}
@@ -0,0 +1,70 @@
1
+ // Context propagation per D40 / D41.
2
+ //
3
+ // SDK middleware (§ 5.9 of the feature_doc) is the ONLY writer of
4
+ // `NodiiContext`. Service code reads via `getContext()`; child scopes
5
+ // only via `withContext({...}, fn)`. There is no public mutate-in-place
6
+ // API — that's an invariant of D41.
7
+ //
8
+ // Implementation: Node's `AsyncLocalStorage`, which works under both
9
+ // Node and Bun. The store holds a `NodiiContext` directly.
10
+ import { AsyncLocalStorage } from "node:async_hooks";
11
+ const als = new AsyncLocalStorage();
12
+ /**
13
+ * Returns the active `NodiiContext`, or null if no middleware adapter
14
+ * has set one for this async scope (cron-flow before the
15
+ * `system-process` adapter runs; raw scripts; tests without harness).
16
+ */
17
+ export function getContext() {
18
+ return als.getStore() ?? null;
19
+ }
20
+ /**
21
+ * Like `getContext()` but throws if no context is active. Use this in
22
+ * code that is allowed to assume an adapter ran — typically inside a
23
+ * request handler.
24
+ */
25
+ export function requireContext() {
26
+ const ctx = als.getStore();
27
+ if (!ctx) {
28
+ throw new Error("No NodiiContext active. SDK middleware (or `withContext`) must run before this code path.");
29
+ }
30
+ return ctx;
31
+ }
32
+ /**
33
+ * Run `fn` inside a child scope where `ctx` is the active context. Per
34
+ * D41: never mutates the parent scope — child finishes and the parent
35
+ * scope's context is unaffected.
36
+ */
37
+ export function withContext(ctx, fn) {
38
+ return als.run(ctx, fn);
39
+ }
40
+ /**
41
+ * SDK-internal use only. The middleware adapters (§ 5.9) call this on
42
+ * request entry to set the active context. Service code MUST NOT call
43
+ * this directly — use `withContext()` for child scopes only. Not part
44
+ * of the public barrel; subpath adapters import it via the package
45
+ * internal path.
46
+ */
47
+ export function setContextForCurrentScope(ctx) {
48
+ als.enterWith(ctx);
49
+ }
50
+ /**
51
+ * Update the active context's `intent_id` only. Used by the intent
52
+ * lifecycle (§ 5.6) when `intent.begin()` / `intent.complete()` /
53
+ * `intent.abandon()` activate or deactivate an intent. Returns the new
54
+ * context so callers can verify; throws if no context is active (intent
55
+ * lifecycle must run inside an established `NodiiContext`).
56
+ *
57
+ * This is the ONLY mutation API per D41. It exists because intent
58
+ * activation is a per-scope assignment that the intent lifecycle owns,
59
+ * not a service-code mutation.
60
+ */
61
+ export function _setIntentIdInCurrentScope(intentId) {
62
+ const cur = als.getStore();
63
+ if (!cur) {
64
+ throw new Error("Cannot set intent_id: no NodiiContext active. SDK middleware must run before `intent.begin()`.");
65
+ }
66
+ const next = { ...cur, intent_id: intentId };
67
+ als.enterWith(next);
68
+ return next;
69
+ }
70
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,EAAE;AACF,kEAAkE;AAClE,sEAAsE;AACtE,wEAAwE;AACxE,oCAAoC;AACpC,EAAE;AACF,qEAAqE;AACrE,2DAA2D;AAE3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,MAAM,GAAG,GAAG,IAAI,iBAAiB,EAAgB,CAAC;AAElD;;;;GAIG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,GAAG,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC;AAChC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC3B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,2FAA2F,CAC5F,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAI,GAAiB,EAAE,EAAW;IAC3D,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAiB;IACzD,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AACrB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,0BAA0B,CACxC,QAAuB;IAEvB,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC3B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,gGAAgG,CACjG,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAiB,EAAE,GAAG,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;IAC3D,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACpB,OAAO,IAAI,CAAC;AACd,CAAC"}