@terreno/api 0.20.2 → 0.22.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 (107) hide show
  1. package/.ai/guidelines/core.md +71 -0
  2. package/.ai/skills/mongoose-schema-safety/SKILL.md +143 -0
  3. package/README.md +54 -1
  4. package/bunfig.toml +1 -1
  5. package/dist/__tests__/versionCheckPlugin.test.js +29 -7
  6. package/dist/actions.openApi.test.js +13 -11
  7. package/dist/api.js +98 -11
  8. package/dist/api.query.test.js +31 -1
  9. package/dist/api.test.js +211 -0
  10. package/dist/auth.test.js +418 -43
  11. package/dist/betterAuth.d.ts +1 -1
  12. package/dist/consentApp.test.js +1 -0
  13. package/dist/example.js +4 -4
  14. package/dist/expressServer.d.ts +0 -22
  15. package/dist/expressServer.js +1 -125
  16. package/dist/expressServer.test.js +90 -91
  17. package/dist/githubAuth.test.js +22 -22
  18. package/dist/logger.d.ts +154 -0
  19. package/dist/logger.js +445 -26
  20. package/dist/logger.test.js +435 -0
  21. package/dist/middleware.d.ts +7 -0
  22. package/dist/middleware.js +58 -1
  23. package/dist/middleware.test.js +159 -0
  24. package/dist/models/consentForm.js +2 -1
  25. package/dist/models/consentResponse.js +2 -1
  26. package/dist/models/versionConfig.js +2 -1
  27. package/dist/openApi.test.js +10 -17
  28. package/dist/openApiBuilder.d.ts +18 -0
  29. package/dist/openApiBuilder.js +21 -0
  30. package/dist/openApiBuilder.test.js +34 -10
  31. package/dist/permissions.test.js +10 -43
  32. package/dist/populate.test.js +10 -42
  33. package/dist/realtime/changeStreamWatcher.d.ts +4 -4
  34. package/dist/realtime/changeStreamWatcher.js +2 -4
  35. package/dist/realtime/queryMatcher.d.ts +1 -1
  36. package/dist/realtime/queryMatcher.js +39 -14
  37. package/dist/realtime/types.d.ts +3 -3
  38. package/dist/requestContext.d.ts +61 -0
  39. package/dist/requestContext.js +74 -0
  40. package/dist/secretProviders.test.js +335 -0
  41. package/dist/syncConsents.test.js +2 -2
  42. package/dist/terrenoApp.d.ts +27 -15
  43. package/dist/terrenoApp.js +24 -14
  44. package/dist/terrenoApp.test.js +52 -0
  45. package/dist/tests/bunSetup.js +66 -262
  46. package/dist/tests/createTestData.d.ts +9 -0
  47. package/dist/tests/createTestData.js +272 -0
  48. package/dist/tests/models.d.ts +71 -0
  49. package/dist/tests/models.js +134 -0
  50. package/dist/tests/mongoTestSetup.d.ts +7 -0
  51. package/dist/tests/mongoTestSetup.js +150 -0
  52. package/dist/tests/testEnv.d.ts +0 -0
  53. package/dist/tests/testEnv.js +6 -0
  54. package/dist/tests/testHelper.d.ts +22 -0
  55. package/dist/tests/testHelper.js +115 -0
  56. package/dist/tests/types.d.ts +29 -0
  57. package/dist/tests/types.js +2 -0
  58. package/dist/tests.d.ts +10 -78
  59. package/dist/tests.js +24 -241
  60. package/dist/transformers.test.js +14 -50
  61. package/package.json +18 -4
  62. package/src/__snapshots__/openApiBuilder.test.ts.snap +1 -0
  63. package/src/__tests__/versionCheckPlugin.test.ts +43 -15
  64. package/src/actions.openApi.test.ts +12 -10
  65. package/src/api.query.test.ts +24 -1
  66. package/src/api.test.ts +169 -0
  67. package/src/api.ts +71 -0
  68. package/src/auth.test.ts +287 -39
  69. package/src/betterAuth.ts +1 -1
  70. package/src/consentApp.test.ts +1 -0
  71. package/src/example.ts +4 -4
  72. package/src/expressServer.test.ts +82 -85
  73. package/src/expressServer.ts +1 -213
  74. package/src/githubAuth.test.ts +22 -22
  75. package/src/logger.test.ts +466 -1
  76. package/src/logger.ts +477 -14
  77. package/src/middleware.test.ts +74 -2
  78. package/src/middleware.ts +57 -0
  79. package/src/models/consentForm.ts +3 -4
  80. package/src/models/consentResponse.ts +6 -4
  81. package/src/models/versionConfig.ts +3 -4
  82. package/src/openApi.test.ts +10 -17
  83. package/src/openApiBuilder.test.ts +27 -10
  84. package/src/openApiBuilder.ts +24 -0
  85. package/src/permissions.test.ts +8 -23
  86. package/src/populate.test.ts +7 -22
  87. package/src/realtime/changeStreamWatcher.ts +15 -10
  88. package/src/realtime/queryMatcher.ts +54 -27
  89. package/src/realtime/types.ts +4 -4
  90. package/src/requestContext.ts +86 -0
  91. package/src/secretProviders.test.ts +219 -1
  92. package/src/syncConsents.test.ts +1 -1
  93. package/src/terrenoApp.test.ts +38 -0
  94. package/src/terrenoApp.ts +37 -15
  95. package/src/tests/bunSetup.ts +22 -236
  96. package/src/tests/createTestData.ts +176 -0
  97. package/src/tests/models.ts +164 -0
  98. package/src/tests/mongoTestSetup.ts +69 -0
  99. package/src/tests/testEnv.ts +4 -0
  100. package/src/tests/testHelper.ts +57 -0
  101. package/src/tests/types.ts +35 -0
  102. package/src/tests.ts +40 -231
  103. package/src/transformers.test.ts +11 -30
  104. package/tsconfig.typedoc.json +4 -0
  105. package/dist/tests/index.d.ts +0 -1
  106. package/dist/tests/index.js +0 -17
  107. package/src/tests/index.ts +0 -1
@@ -120,7 +120,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
120
120
  };
121
121
  Object.defineProperty(exports, "__esModule", { value: true });
122
122
  exports.stopChangeStreamWatcher = exports.startChangeStreamWatcher = exports.emitToDocumentAndQueryRooms = exports.emitToAuthorizedRoom = exports.serializeDoc = exports.ensureApiId = exports.resolveRooms = exports.mapOperationType = void 0;
123
- // biome-ignore-all lint/suspicious/noExplicitAny: change stream and socket handlers use dynamic document shapes
124
123
  var Sentry = __importStar(require("@sentry/bun"));
125
124
  var luxon_1 = require("luxon");
126
125
  var mongoose_1 = __importDefault(require("mongoose"));
@@ -208,7 +207,6 @@ var canReadDocument = function (entry, user, doc) { return __awaiter(void 0, voi
208
207
  * Exported for testing.
209
208
  */
210
209
  var resolveRooms = function (entry, doc, method) {
211
- var _a, _b, _c;
212
210
  var roomStrategy = entry.config.roomStrategy;
213
211
  // Use the collection tag (e.g. "todos") for model rooms, matching what the frontend subscribes to
214
212
  var collectionTag = getCollectionTag(entry.routePath);
@@ -219,7 +217,7 @@ var resolveRooms = function (entry, doc, method) {
219
217
  }
220
218
  switch (roomStrategy) {
221
219
  case "owner": {
222
- var ownerId = (_c = (_b = (_a = doc === null || doc === void 0 ? void 0 : doc.ownerId) === null || _a === void 0 ? void 0 : _a.toString) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : doc === null || doc === void 0 ? void 0 : doc.ownerId;
220
+ var ownerId = (doc === null || doc === void 0 ? void 0 : doc.ownerId) != null ? String(doc.ownerId) : undefined;
223
221
  if (ownerId) {
224
222
  return ["user:".concat(ownerId)];
225
223
  }
@@ -625,7 +623,7 @@ var startChangeStreamWatcher = function (io, config, debug) {
625
623
  }
626
624
  }
627
625
  else {
628
- rooms = (0, exports.resolveRooms)(entry, fullDocument, method);
626
+ rooms = (0, exports.resolveRooms)(entry, fullDocument !== null && fullDocument !== void 0 ? fullDocument : {}, method);
629
627
  }
630
628
  collection = getCollectionTag(entry.routePath);
631
629
  event_1 = __assign({ collection: collection, id: docId, method: method, model: entry.modelName, timestamp: luxon_1.DateTime.now().toMillis() }, (change.operationType === "update" && ((_e = change.updateDescription) === null || _e === void 0 ? void 0 : _e.updatedFields)
@@ -11,4 +11,4 @@
11
11
  * @param query - MongoDB-style query object
12
12
  * @returns true if the document matches all query conditions
13
13
  */
14
- export declare const matchesQuery: (doc: any, query: Record<string, any>) => boolean;
14
+ export declare const matchesQuery: (doc: Record<string, unknown>, query: Record<string, unknown>) => boolean;
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- // biome-ignore-all lint/suspicious/noExplicitAny: MongoDB query matcher evaluates dynamic filter shapes
3
2
  /**
4
3
  * Simple in-memory MongoDB query matcher.
5
4
  * Evaluates a MongoDB-style query object against a document without hitting the database.
@@ -63,14 +62,32 @@ var normalize = function (value) {
63
62
  return value;
64
63
  }
65
64
  // Handle ObjectId-like objects with toString
66
- if (typeof value === "object" &&
67
- typeof value.toString === "function" &&
68
- ((_a = value.constructor) === null || _a === void 0 ? void 0 : _a.name) !== "Object" &&
69
- !Array.isArray(value)) {
70
- return value.toString();
65
+ if (typeof value === "object" && !Array.isArray(value)) {
66
+ var obj = value;
67
+ var ctorName = (_a = obj.constructor) === null || _a === void 0 ? void 0 : _a.name;
68
+ if (typeof obj.toString === "function" && ctorName !== "Object") {
69
+ return String(value);
70
+ }
71
71
  }
72
72
  return value;
73
73
  };
74
+ /**
75
+ * JS abstract relational comparison on unknown values.
76
+ * Numeric operands compare numerically; everything else compares as strings.
77
+ * This mirrors the coercion behaviour of `>` / `<` on the `any`-typed values
78
+ * that MongoDB in-memory matching historically received.
79
+ */
80
+ var compareValues = function (a, b) {
81
+ if (typeof a === "number" && typeof b === "number") {
82
+ return a - b;
83
+ }
84
+ if (typeof a === "string" && typeof b === "string") {
85
+ return a < b ? -1 : a > b ? 1 : 0;
86
+ }
87
+ var numA = Number(a);
88
+ var numB = Number(b);
89
+ return numA - numB;
90
+ };
74
91
  var matchesCondition = function (rawValue, condition) {
75
92
  var e_2, _a;
76
93
  var value = normalize(rawValue);
@@ -99,26 +116,34 @@ var matchesCondition = function (rawValue, condition) {
99
116
  return false;
100
117
  }
101
118
  break;
102
- case "$gt":
103
- if (!(value > normOp)) {
119
+ case "$gt": {
120
+ var cmp = compareValues(value, normOp);
121
+ if (Number.isNaN(cmp) || cmp <= 0) {
104
122
  return false;
105
123
  }
106
124
  break;
107
- case "$gte":
108
- if (!(value >= normOp)) {
125
+ }
126
+ case "$gte": {
127
+ var cmp = compareValues(value, normOp);
128
+ if (Number.isNaN(cmp) || cmp < 0) {
109
129
  return false;
110
130
  }
111
131
  break;
112
- case "$lt":
113
- if (!(value < normOp)) {
132
+ }
133
+ case "$lt": {
134
+ var cmp = compareValues(value, normOp);
135
+ if (Number.isNaN(cmp) || cmp >= 0) {
114
136
  return false;
115
137
  }
116
138
  break;
117
- case "$lte":
118
- if (!(value <= normOp)) {
139
+ }
140
+ case "$lte": {
141
+ var cmp = compareValues(value, normOp);
142
+ if (Number.isNaN(cmp) || cmp > 0) {
119
143
  return false;
120
144
  }
121
145
  break;
146
+ }
122
147
  case "$in": {
123
148
  if (!Array.isArray(operand)) {
124
149
  return false;
@@ -13,9 +13,9 @@ export interface RealtimeConfig {
13
13
  * - 'broadcast': emit to all authenticated sockets
14
14
  * - function: custom room resolver returning room name(s)
15
15
  */
16
- roomStrategy: "owner" | "model" | "broadcast" | ((doc: any, method: string, req: express.Request) => string[]);
16
+ roomStrategy: "owner" | "model" | "broadcast" | ((doc: Record<string, unknown>, method: string, req: express.Request) => string[]);
17
17
  /** Custom serializer for real-time events. Falls back to the modelRouter responseHandler. */
18
- realtimeResponseHandler?: (doc: any, method: string) => any;
18
+ realtimeResponseHandler?: (doc: Record<string, unknown>, method: string) => unknown;
19
19
  }
20
20
  /**
21
21
  * A real-time sync event emitted to clients via WebSocket.
@@ -94,7 +94,7 @@ export interface QuerySubscription {
94
94
  /** Collection tag (e.g. "todos") */
95
95
  collection: string;
96
96
  /** MongoDB-style query filter (e.g. {completed: false}) */
97
- query: Record<string, any>;
97
+ query: Record<string, unknown>;
98
98
  /** Client-provided queryId (ignored — server computes a canonical ID) */
99
99
  queryId?: string;
100
100
  }
@@ -1,15 +1,32 @@
1
1
  import type express from "express";
2
2
  import type { JwtPayload } from "jsonwebtoken";
3
+ /**
4
+ * Correlation fields stored in AsyncLocalStorage for the lifetime of a request or job. Every log
5
+ * line emitted inside the scope is enriched with these. `requestId` is the only required field; the
6
+ * rest are populated when headers, trace context, or auth supply them.
7
+ */
3
8
  export interface RequestContext {
9
+ /** Background job identifier (from `x-job-id` or set via {@link runWithRequestContext}). */
4
10
  jobId?: string;
11
+ /** Stable id shared by all log lines for one request/job; echoed to clients as `X-Request-ID`. */
5
12
  requestId: string;
13
+ /** Auth session id, resolved from the JWT/Better Auth session or `x-session-id`. */
6
14
  sessionId?: string;
15
+ /** Distributed-tracing span id, parsed from Cloud Trace or W3C `traceparent`. */
7
16
  spanId?: string;
17
+ /** Distributed-tracing trace id, parsed from Cloud Trace or W3C `traceparent`. */
8
18
  traceId?: string;
19
+ /** Whether the trace is sampled, per the incoming trace headers. */
9
20
  traceSampled?: boolean;
21
+ /** Authenticated user id, populated after auth middleware runs. */
10
22
  userId?: string;
11
23
  }
12
24
  export type RequestContextAttributes = Record<string, string>;
25
+ /**
26
+ * Canonical HTTP header names for each correlation field. Use these to propagate context to
27
+ * downstream services (pair with {@link getCurrentRequestContextAttributes}) or to read it from an
28
+ * incoming request (pair with {@link getRequestContextFromAttributes}).
29
+ */
13
30
  export declare const REQUEST_CONTEXT_ATTRIBUTE_NAMES: {
14
31
  readonly jobId: "x-job-id";
15
32
  readonly requestId: "x-request-id";
@@ -26,12 +43,56 @@ export interface JwtSessionPayload extends JwtPayload {
26
43
  }
27
44
  export declare const getSessionIdFromJwtPayload: (payload?: JwtSessionPayload | null) => string | undefined;
28
45
  export declare const getRequestContextFromAttributes: (attributes?: Record<string, string | undefined>) => RequestContext;
46
+ /**
47
+ * Returns the full {@link RequestContext} for the active AsyncLocalStorage scope, or `undefined`
48
+ * when called outside any request/job scope. The logger uses this to enrich each line.
49
+ */
29
50
  export declare const getCurrentRequestContext: () => RequestContext | undefined;
51
+ /**
52
+ * Returns the active correlation fields as a plain object (empty when outside a scope). This is the
53
+ * shape attached to Sentry log attributes and is handy when you need to log or forward the current
54
+ * context yourself.
55
+ */
30
56
  export declare const getCurrentLogContext: () => Partial<RequestContext>;
31
57
  export declare const applyRequestContextToSentry: (context?: Partial<RequestContext>) => void;
32
58
  export declare const setRequestContext: (updates: Partial<RequestContext>) => void;
59
+ /**
60
+ * Serializes the active correlation context into HTTP header attributes (keyed by
61
+ * {@link REQUEST_CONTEXT_ATTRIBUTE_NAMES}) so it can be propagated on outbound requests to other
62
+ * services, keeping the same `requestId`/`traceId` across service boundaries.
63
+ */
33
64
  export declare const getCurrentRequestContextAttributes: (overrides?: Partial<RequestContext>) => RequestContextAttributes;
65
+ /**
66
+ * Runs `callback` inside a fresh correlation scope so every log line it emits shares the same
67
+ * identifiers — the manual equivalent of {@link requestContextMiddleware} for background jobs,
68
+ * cron tasks, scripts, queue consumers, etc. A `requestId` is generated when not supplied, and the
69
+ * context is mirrored to Sentry.
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * import {createScopedLogger, runWithRequestContext} from "@terreno/api";
74
+ *
75
+ * await runWithRequestContext({jobId: "nightly-sync"}, async () => {
76
+ * const log = createScopedLogger({prefix: "[NightlySync]"});
77
+ * log.info("started"); // includes jobId + a generated requestId on every line
78
+ * await sync();
79
+ * });
80
+ * ```
81
+ */
34
82
  export declare const runWithRequestContext: <T>(context: Partial<RequestContext>, callback: () => T) => T;
83
+ /**
84
+ * Like {@link runWithRequestContext}, but seeds the scope from raw header attributes (for example
85
+ * those received on an incoming message or forwarded by another service). Parses Cloud Trace / W3C
86
+ * `traceparent` into `traceId`/`spanId` via {@link getRequestContextFromAttributes}.
87
+ */
35
88
  export declare const runWithRequestContextAttributes: <T>(attributes: Record<string, string | undefined> | undefined, callback: () => T) => T;
36
89
  export declare const updateRequestContextFromRequest: (req: express.Request, res?: express.Response) => void;
90
+ /**
91
+ * Express middleware that opens a correlation scope for the request. Mounted early by `TerrenoApp` /
92
+ * `setupServer`, it resolves a `requestId` (from request-id/correlation headers, Cloud Trace, or
93
+ * W3C `traceparent`, else a new UUID), captures any `jobId`/`sessionId`/trace fields, echoes
94
+ * `X-Request-ID` back to the client, and runs the remaining middleware inside the scope so all
95
+ * downstream logs are correlated. A later auth-aware pass ({@link updateRequestContextFromRequest})
96
+ * fills in `userId`/`sessionId`.
97
+ */
37
98
  export declare const requestContextMiddleware: (req: express.Request, res: express.Response, next: express.NextFunction) => void;
@@ -72,6 +72,31 @@ var __values = (this && this.__values) || function(o) {
72
72
  };
73
73
  Object.defineProperty(exports, "__esModule", { value: true });
74
74
  exports.requestContextMiddleware = exports.updateRequestContextFromRequest = exports.runWithRequestContextAttributes = exports.runWithRequestContext = exports.getCurrentRequestContextAttributes = exports.setRequestContext = exports.applyRequestContextToSentry = exports.getCurrentLogContext = exports.getCurrentRequestContext = exports.getRequestContextFromAttributes = exports.getSessionIdFromJwtPayload = exports.REQUEST_CONTEXT_ATTRIBUTE_NAMES = void 0;
75
+ /**
76
+ * Request/job correlation for `@terreno/api`.
77
+ *
78
+ * Correlation is how every log line emitted while handling one request (or one background job) can
79
+ * be tied back together. It is built on Node's {@link AsyncLocalStorage}: a {@link RequestContext}
80
+ * (with `requestId`, `userId`, `traceId`, etc.) is stored for the duration of a callback, and the
81
+ * logger's Winston format reads it from there and merges it into each line. Nothing needs to be
82
+ * threaded through function arguments.
83
+ *
84
+ * Two ways a scope is established:
85
+ *
86
+ * - **HTTP**: {@link requestContextMiddleware} runs first in the middleware stack. It derives a
87
+ * `requestId` from incoming headers ({@link REQUEST_CONTEXT_ATTRIBUTE_NAMES}, `x-correlation-id`,
88
+ * Cloud Trace, or W3C `traceparent`) or generates one, echoes it back as `X-Request-ID`, and runs
89
+ * the rest of the request inside the scope.
90
+ * - **Jobs/scripts**: {@link runWithRequestContext} (or {@link runWithRequestContextAttributes})
91
+ * establishes the same scope manually so background work is just as traceable.
92
+ *
93
+ * The active context is also pushed to Sentry tags/context via {@link applyRequestContextToSentry},
94
+ * and is exposed to logging via {@link getCurrentLogContext} / {@link getCurrentRequestContext}.
95
+ *
96
+ * @see {@link runWithRequestContext}
97
+ * @see {@link getCurrentLogContext}
98
+ * @module requestContext
99
+ */
75
100
  var node_async_hooks_1 = require("node:async_hooks");
76
101
  var node_crypto_1 = require("node:crypto");
77
102
  var Sentry = __importStar(require("@sentry/bun"));
@@ -84,6 +109,11 @@ var TRACE_ID_HEADER = "x-trace-id";
84
109
  var TRACE_PARENT_HEADER = "traceparent";
85
110
  var TRACE_SAMPLED_HEADER = "x-trace-sampled";
86
111
  var USER_ID_HEADER = "x-user-id";
112
+ /**
113
+ * Canonical HTTP header names for each correlation field. Use these to propagate context to
114
+ * downstream services (pair with {@link getCurrentRequestContextAttributes}) or to read it from an
115
+ * incoming request (pair with {@link getRequestContextFromAttributes}).
116
+ */
87
117
  exports.REQUEST_CONTEXT_ATTRIBUTE_NAMES = {
88
118
  jobId: JOB_ID_HEADER,
89
119
  requestId: "x-request-id",
@@ -193,10 +223,19 @@ var getRequestContextFromAttributes = function (attributes) {
193
223
  };
194
224
  };
195
225
  exports.getRequestContextFromAttributes = getRequestContextFromAttributes;
226
+ /**
227
+ * Returns the full {@link RequestContext} for the active AsyncLocalStorage scope, or `undefined`
228
+ * when called outside any request/job scope. The logger uses this to enrich each line.
229
+ */
196
230
  var getCurrentRequestContext = function () {
197
231
  return requestContextStorage.getStore();
198
232
  };
199
233
  exports.getCurrentRequestContext = getCurrentRequestContext;
234
+ /**
235
+ * Returns the active correlation fields as a plain object (empty when outside a scope). This is the
236
+ * shape attached to Sentry log attributes and is handy when you need to log or forward the current
237
+ * context yourself.
238
+ */
200
239
  var getCurrentLogContext = function () {
201
240
  var context = (0, exports.getCurrentRequestContext)();
202
241
  if (!context) {
@@ -266,6 +305,11 @@ var setAttribute = function (attributes, name, value) {
266
305
  }
267
306
  attributes[name] = String(value);
268
307
  };
308
+ /**
309
+ * Serializes the active correlation context into HTTP header attributes (keyed by
310
+ * {@link REQUEST_CONTEXT_ATTRIBUTE_NAMES}) so it can be propagated on outbound requests to other
311
+ * services, keeping the same `requestId`/`traceId` across service boundaries.
312
+ */
269
313
  var getCurrentRequestContextAttributes = function (overrides) {
270
314
  if (overrides === void 0) { overrides = {}; }
271
315
  var context = __assign(__assign({}, (0, exports.getCurrentLogContext)()), overrides);
@@ -280,6 +324,23 @@ var getCurrentRequestContextAttributes = function (overrides) {
280
324
  return attributes;
281
325
  };
282
326
  exports.getCurrentRequestContextAttributes = getCurrentRequestContextAttributes;
327
+ /**
328
+ * Runs `callback` inside a fresh correlation scope so every log line it emits shares the same
329
+ * identifiers — the manual equivalent of {@link requestContextMiddleware} for background jobs,
330
+ * cron tasks, scripts, queue consumers, etc. A `requestId` is generated when not supplied, and the
331
+ * context is mirrored to Sentry.
332
+ *
333
+ * @example
334
+ * ```typescript
335
+ * import {createScopedLogger, runWithRequestContext} from "@terreno/api";
336
+ *
337
+ * await runWithRequestContext({jobId: "nightly-sync"}, async () => {
338
+ * const log = createScopedLogger({prefix: "[NightlySync]"});
339
+ * log.info("started"); // includes jobId + a generated requestId on every line
340
+ * await sync();
341
+ * });
342
+ * ```
343
+ */
283
344
  var runWithRequestContext = function (context, callback) {
284
345
  var _a;
285
346
  var nextContext = __assign(__assign({}, context), { requestId: (_a = context.requestId) !== null && _a !== void 0 ? _a : (0, node_crypto_1.randomUUID)() });
@@ -289,6 +350,11 @@ var runWithRequestContext = function (context, callback) {
289
350
  });
290
351
  };
291
352
  exports.runWithRequestContext = runWithRequestContext;
353
+ /**
354
+ * Like {@link runWithRequestContext}, but seeds the scope from raw header attributes (for example
355
+ * those received on an incoming message or forwarded by another service). Parses Cloud Trace / W3C
356
+ * `traceparent` into `traceId`/`spanId` via {@link getRequestContextFromAttributes}.
357
+ */
292
358
  var runWithRequestContextAttributes = function (attributes, callback) {
293
359
  if (attributes === void 0) { attributes = {}; }
294
360
  return (0, exports.runWithRequestContext)((0, exports.getRequestContextFromAttributes)(attributes), callback);
@@ -312,6 +378,14 @@ var updateRequestContextFromRequest = function (req, res) {
312
378
  }
313
379
  };
314
380
  exports.updateRequestContextFromRequest = updateRequestContextFromRequest;
381
+ /**
382
+ * Express middleware that opens a correlation scope for the request. Mounted early by `TerrenoApp` /
383
+ * `setupServer`, it resolves a `requestId` (from request-id/correlation headers, Cloud Trace, or
384
+ * W3C `traceparent`, else a new UUID), captures any `jobId`/`sessionId`/trace fields, echoes
385
+ * `X-Request-ID` back to the client, and runs the remaining middleware inside the scope so all
386
+ * downstream logs are correlated. A later auth-aware pass ({@link updateRequestContextFromRequest})
387
+ * fills in `userId`/`sessionId`.
388
+ */
315
389
  var requestContextMiddleware = function (req, res, next) {
316
390
  var cloudTraceContext = parseGoogleCloudTraceContext(getHeader(req, CLOUD_TRACE_CONTEXT_HEADER));
317
391
  var traceParentContext = parseTraceParent(getHeader(req, TRACE_PARENT_HEADER));