@terreno/api 0.20.2 → 0.21.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 (65) 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/dist/__tests__/versionCheckPlugin.test.js +29 -7
  5. package/dist/actions.openApi.test.js +13 -11
  6. package/dist/api.js +98 -11
  7. package/dist/api.query.test.js +31 -1
  8. package/dist/api.test.js +211 -0
  9. package/dist/auth.test.js +10 -10
  10. package/dist/betterAuth.d.ts +1 -1
  11. package/dist/consentApp.test.js +1 -0
  12. package/dist/example.js +4 -4
  13. package/dist/expressServer.d.ts +0 -22
  14. package/dist/expressServer.js +1 -125
  15. package/dist/expressServer.test.js +90 -91
  16. package/dist/githubAuth.test.js +22 -22
  17. package/dist/logger.d.ts +154 -0
  18. package/dist/logger.js +445 -26
  19. package/dist/logger.test.js +435 -0
  20. package/dist/middleware.d.ts +7 -0
  21. package/dist/middleware.js +58 -1
  22. package/dist/middleware.test.js +159 -0
  23. package/dist/openApi.test.js +10 -17
  24. package/dist/openApiBuilder.test.js +18 -10
  25. package/dist/realtime/changeStreamWatcher.d.ts +4 -4
  26. package/dist/realtime/changeStreamWatcher.js +2 -4
  27. package/dist/realtime/queryMatcher.d.ts +1 -1
  28. package/dist/realtime/queryMatcher.js +39 -14
  29. package/dist/realtime/types.d.ts +3 -3
  30. package/dist/requestContext.d.ts +61 -0
  31. package/dist/requestContext.js +74 -0
  32. package/dist/secretProviders.test.js +335 -0
  33. package/dist/terrenoApp.d.ts +27 -15
  34. package/dist/terrenoApp.js +24 -14
  35. package/dist/terrenoApp.test.js +52 -0
  36. package/dist/tests/bunSetup.js +61 -7
  37. package/dist/tests.js +27 -4
  38. package/package.json +1 -1
  39. package/src/__tests__/versionCheckPlugin.test.ts +43 -15
  40. package/src/actions.openApi.test.ts +12 -10
  41. package/src/api.query.test.ts +24 -1
  42. package/src/api.test.ts +169 -0
  43. package/src/api.ts +71 -0
  44. package/src/auth.test.ts +10 -10
  45. package/src/betterAuth.ts +1 -1
  46. package/src/consentApp.test.ts +1 -0
  47. package/src/example.ts +4 -4
  48. package/src/expressServer.test.ts +82 -85
  49. package/src/expressServer.ts +1 -213
  50. package/src/githubAuth.test.ts +22 -22
  51. package/src/logger.test.ts +466 -1
  52. package/src/logger.ts +477 -14
  53. package/src/middleware.test.ts +74 -2
  54. package/src/middleware.ts +57 -0
  55. package/src/openApi.test.ts +10 -17
  56. package/src/openApiBuilder.test.ts +18 -10
  57. package/src/realtime/changeStreamWatcher.ts +15 -10
  58. package/src/realtime/queryMatcher.ts +54 -27
  59. package/src/realtime/types.ts +4 -4
  60. package/src/requestContext.ts +86 -0
  61. package/src/secretProviders.test.ts +219 -1
  62. package/src/terrenoApp.test.ts +38 -0
  63. package/src/terrenoApp.ts +37 -15
  64. package/src/tests/bunSetup.ts +16 -3
  65. package/src/tests.ts +17 -4
@@ -18,8 +18,10 @@ var bun_test_1 = require("bun:test");
18
18
  var node_fs_1 = __importDefault(require("node:fs"));
19
19
  var node_os_1 = __importDefault(require("node:os"));
20
20
  var node_path_1 = __importDefault(require("node:path"));
21
+ var node_stream_1 = require("node:stream");
21
22
  var winston_1 = __importDefault(require("winston"));
22
23
  var logger_1 = require("./logger");
24
+ var requestContext_1 = require("./requestContext");
23
25
  (0, bun_test_1.describe)("logger", function () {
24
26
  var OLD_ENV = process.env;
25
27
  (0, bun_test_1.beforeEach)(function () {
@@ -51,6 +53,402 @@ var logger_1 = require("./logger");
51
53
  (0, bun_test_1.expect)(function () { return logger_1.logger.catch(new Error("captured")); }).not.toThrow();
52
54
  });
53
55
  });
56
+ (0, bun_test_1.describe)("formatLogContextSuffix", function () {
57
+ (0, bun_test_1.it)("includes request ids and sorts terrenoLabels for stable output", function () {
58
+ var suffix = (0, logger_1.formatLogContextSuffix)({
59
+ requestId: "r1",
60
+ terrenoLabels: { alpha: "a", zebra: "z" },
61
+ });
62
+ (0, bun_test_1.expect)(suffix).toContain("requestId=r1");
63
+ (0, bun_test_1.expect)(suffix.indexOf("alpha=a")).toBeLessThan(suffix.indexOf("zebra=z"));
64
+ });
65
+ (0, bun_test_1.it)("returns empty string when no fields are set", function () {
66
+ (0, bun_test_1.expect)((0, logger_1.formatLogContextSuffix)({})).toBe("");
67
+ });
68
+ (0, bun_test_1.it)("includes terrenoLogPrefix in suffix", function () {
69
+ var suffix = (0, logger_1.formatLogContextSuffix)({
70
+ requestId: "r1",
71
+ terrenoLogPrefix: "[Job]",
72
+ });
73
+ (0, bun_test_1.expect)(suffix).toContain("logPrefix=[Job]");
74
+ (0, bun_test_1.expect)(suffix).toContain("requestId=r1");
75
+ });
76
+ });
77
+ (0, bun_test_1.describe)("createScopedLogger", function () {
78
+ (0, bun_test_1.afterEach)(function () {
79
+ (0, logger_1.setupLogging)({ disableFileLogging: true });
80
+ });
81
+ (0, bun_test_1.it)("returns the global logger when prefix and labels are empty", function () {
82
+ (0, bun_test_1.expect)((0, logger_1.createScopedLogger)({})).toBe(logger_1.logger);
83
+ (0, bun_test_1.expect)((0, logger_1.createScopedLogger)({ labels: { skipped: undefined } })).toBe(logger_1.logger);
84
+ });
85
+ (0, bun_test_1.it)("prefixes messages when only prefix is set", function () {
86
+ var lines = [];
87
+ var snapshots = [];
88
+ var captureTransport = new winston_1.default.transports.Stream({
89
+ format: winston_1.default.format.combine(winston_1.default.format(function (info) {
90
+ snapshots.push({
91
+ terrenoLogPrefix: info.terrenoLogPrefix,
92
+ });
93
+ return info;
94
+ })(), winston_1.default.format.printf(function (info) {
95
+ var msg = typeof info.message === "string" ? info.message : String(info.message);
96
+ return "".concat(info.level, ": ").concat(msg);
97
+ })),
98
+ stream: new node_stream_1.Writable({
99
+ write: function (chunk, _encoding, callback) {
100
+ lines.push(chunk.toString().trim());
101
+ callback();
102
+ },
103
+ }),
104
+ });
105
+ logger_1.winstonLogger.add(captureTransport);
106
+ try {
107
+ (0, logger_1.createScopedLogger)({ prefix: "[ScopedTest]" }).info("hello");
108
+ }
109
+ finally {
110
+ logger_1.winstonLogger.remove(captureTransport);
111
+ }
112
+ (0, bun_test_1.expect)(lines.some(function (l) { return l.includes("[ScopedTest]") && l.includes("hello"); })).toBe(true);
113
+ (0, bun_test_1.expect)(snapshots.some(function (s) { return s.terrenoLogPrefix === "[ScopedTest]"; })).toBe(true);
114
+ });
115
+ (0, bun_test_1.it)("attaches terrenoLabels to winston metadata for structured transports", function () {
116
+ var snapshots = [];
117
+ var captureTransport = new winston_1.default.transports.Stream({
118
+ format: winston_1.default.format(function (info) {
119
+ snapshots.push({
120
+ level: info.level,
121
+ message: info.message,
122
+ terrenoLabels: info.terrenoLabels,
123
+ });
124
+ return info;
125
+ })(),
126
+ stream: new node_stream_1.Writable({
127
+ write: function (_chunk, _encoding, callback) {
128
+ callback();
129
+ },
130
+ }),
131
+ });
132
+ logger_1.winstonLogger.add(captureTransport);
133
+ try {
134
+ (0, logger_1.createScopedLogger)({ labels: { billingId: "b1" } }).warn("charged");
135
+ }
136
+ finally {
137
+ logger_1.winstonLogger.remove(captureTransport);
138
+ }
139
+ (0, bun_test_1.expect)(snapshots.some(function (s) { var _a; return ((_a = s.terrenoLabels) === null || _a === void 0 ? void 0 : _a.billingId) === "b1"; })).toBe(true);
140
+ });
141
+ (0, bun_test_1.it)("includes terrenoLogPrefix alongside terrenoLabels in metadata", function () {
142
+ var snapshots = [];
143
+ var captureTransport = new winston_1.default.transports.Stream({
144
+ format: winston_1.default.format(function (info) {
145
+ snapshots.push({
146
+ terrenoLabels: info.terrenoLabels,
147
+ terrenoLogPrefix: info.terrenoLogPrefix,
148
+ });
149
+ return info;
150
+ })(),
151
+ stream: new node_stream_1.Writable({
152
+ write: function (_chunk, _encoding, callback) {
153
+ callback();
154
+ },
155
+ }),
156
+ });
157
+ logger_1.winstonLogger.add(captureTransport);
158
+ try {
159
+ (0, logger_1.createScopedLogger)({ labels: { x: "1" }, prefix: "[Both]" }).info("m");
160
+ }
161
+ finally {
162
+ logger_1.winstonLogger.remove(captureTransport);
163
+ }
164
+ (0, bun_test_1.expect)(snapshots.some(function (s) {
165
+ var _a;
166
+ return ((_a = s.terrenoLabels) === null || _a === void 0 ? void 0 : _a.x) === "1" &&
167
+ s.terrenoLogPrefix === "[Both]";
168
+ })).toBe(true);
169
+ });
170
+ (0, bun_test_1.it)("merges all request context fields including jobId, sessionId, spanId, traceId, traceSampled", function () {
171
+ var snapshots = [];
172
+ var captureTransport = new winston_1.default.transports.Stream({
173
+ format: winston_1.default.format(function (info) {
174
+ snapshots.push({
175
+ jobId: info.jobId,
176
+ requestId: info.requestId,
177
+ sessionId: info.sessionId,
178
+ spanId: info.spanId,
179
+ traceId: info.traceId,
180
+ traceSampled: info.traceSampled,
181
+ userId: info.userId,
182
+ });
183
+ return info;
184
+ })(),
185
+ stream: new node_stream_1.Writable({
186
+ write: function (_chunk, _encoding, callback) {
187
+ callback();
188
+ },
189
+ }),
190
+ });
191
+ logger_1.winstonLogger.add(captureTransport);
192
+ try {
193
+ (0, requestContext_1.runWithRequestContext)({
194
+ jobId: "job-42",
195
+ requestId: "req-full",
196
+ sessionId: "sess-7",
197
+ spanId: "span-abc",
198
+ traceId: "trace-xyz",
199
+ traceSampled: true,
200
+ userId: "user-1",
201
+ }, function () {
202
+ logger_1.logger.info("full context");
203
+ });
204
+ }
205
+ finally {
206
+ logger_1.winstonLogger.remove(captureTransport);
207
+ }
208
+ var entry = snapshots.find(function (s) { return s.requestId === "req-full"; });
209
+ (0, bun_test_1.expect)(entry).toBeDefined();
210
+ (0, bun_test_1.expect)(entry === null || entry === void 0 ? void 0 : entry.jobId).toBe("job-42");
211
+ (0, bun_test_1.expect)(entry === null || entry === void 0 ? void 0 : entry.sessionId).toBe("sess-7");
212
+ (0, bun_test_1.expect)(entry === null || entry === void 0 ? void 0 : entry.spanId).toBe("span-abc");
213
+ (0, bun_test_1.expect)(entry === null || entry === void 0 ? void 0 : entry.traceId).toBe("trace-xyz");
214
+ (0, bun_test_1.expect)(entry === null || entry === void 0 ? void 0 : entry.traceSampled).toBe(true);
215
+ (0, bun_test_1.expect)(entry === null || entry === void 0 ? void 0 : entry.userId).toBe("user-1");
216
+ });
217
+ (0, bun_test_1.it)("exercises all scoped logger methods (debug, error, catch)", function () {
218
+ var lines = [];
219
+ var captureTransport = new winston_1.default.transports.Stream({
220
+ format: winston_1.default.format.printf(function (info) {
221
+ var msg = typeof info.message === "string" ? info.message : String(info.message);
222
+ return "".concat(info.level, ": ").concat(msg);
223
+ }),
224
+ stream: new node_stream_1.Writable({
225
+ write: function (chunk, _encoding, callback) {
226
+ lines.push(chunk.toString().trim());
227
+ callback();
228
+ },
229
+ }),
230
+ });
231
+ logger_1.winstonLogger.add(captureTransport);
232
+ try {
233
+ var scoped = (0, logger_1.createScopedLogger)({ prefix: "[Methods]" });
234
+ scoped.debug("d-msg");
235
+ scoped.error("e-msg");
236
+ scoped.catch(new Error("caught-msg"));
237
+ }
238
+ finally {
239
+ logger_1.winstonLogger.remove(captureTransport);
240
+ }
241
+ (0, bun_test_1.expect)(lines.some(function (l) { return l.includes("[Methods]") && l.includes("d-msg"); })).toBe(true);
242
+ (0, bun_test_1.expect)(lines.some(function (l) { return l.includes("[Methods]") && l.includes("e-msg"); })).toBe(true);
243
+ (0, bun_test_1.expect)(lines.some(function (l) { return l.includes("[Methods]") && l.includes("caught-msg"); })).toBe(true);
244
+ });
245
+ (0, bun_test_1.it)("scoped logger catch with Sentry enabled and Error instance", function () {
246
+ var OLD_ENV = process.env;
247
+ process.env = __assign(__assign({}, OLD_ENV), { USE_SENTRY_LOGGING: "true" });
248
+ try {
249
+ var scoped_1 = (0, logger_1.createScopedLogger)({ prefix: "[Sentry]" });
250
+ (0, bun_test_1.expect)(function () { return scoped_1.catch(new Error("sentry scoped error")); }).not.toThrow();
251
+ }
252
+ finally {
253
+ process.env = OLD_ENV;
254
+ }
255
+ });
256
+ (0, bun_test_1.it)("attaches terrenoRequestLog while request ALS scope is active", function () {
257
+ var _a, _b;
258
+ var snapshots = [];
259
+ var captureTransport = new winston_1.default.transports.Stream({
260
+ format: winston_1.default.format(function (info) {
261
+ snapshots.push({
262
+ terrenoRequestLog: info
263
+ .terrenoRequestLog,
264
+ });
265
+ return info;
266
+ })(),
267
+ stream: new node_stream_1.Writable({
268
+ write: function (_chunk, _encoding, callback) {
269
+ callback();
270
+ },
271
+ }),
272
+ });
273
+ logger_1.winstonLogger.add(captureTransport);
274
+ try {
275
+ (0, requestContext_1.runWithRequestContext)({ requestId: "req-als-1", userId: "user-99" }, function () {
276
+ logger_1.logger.info("in scope");
277
+ });
278
+ (0, requestContext_1.runWithRequestContext)({ requestId: "req-als-2" }, function () {
279
+ logger_1.logger.info("anon");
280
+ });
281
+ }
282
+ finally {
283
+ logger_1.winstonLogger.remove(captureTransport);
284
+ }
285
+ var withUser = snapshots.find(function (s) { var _a; return ((_a = s.terrenoRequestLog) === null || _a === void 0 ? void 0 : _a.requestId) === "req-als-1"; });
286
+ (0, bun_test_1.expect)((_a = withUser === null || withUser === void 0 ? void 0 : withUser.terrenoRequestLog) === null || _a === void 0 ? void 0 : _a.userId).toBe("user-99");
287
+ var anon = snapshots.find(function (s) { var _a; return ((_a = s.terrenoRequestLog) === null || _a === void 0 ? void 0 : _a.requestId) === "req-als-2"; });
288
+ (0, bun_test_1.expect)((_b = anon === null || anon === void 0 ? void 0 : anon.terrenoRequestLog) === null || _b === void 0 ? void 0 : _b.userId).toBeNull();
289
+ });
290
+ });
291
+ (0, bun_test_1.describe)("createFeatureFlaggedLogger", function () {
292
+ (0, bun_test_1.afterEach)(function () {
293
+ (0, logger_1.setupLogging)({ disableFileLogging: true });
294
+ });
295
+ (0, bun_test_1.it)("drops info lines while disabled", function () {
296
+ var hits = 0;
297
+ var captureTransport = new winston_1.default.transports.Stream({
298
+ format: winston_1.default.format.printf(function () {
299
+ hits += 1;
300
+ return "";
301
+ }),
302
+ stream: new node_stream_1.Writable({
303
+ write: function (_chunk, _encoding, callback) {
304
+ callback();
305
+ },
306
+ }),
307
+ });
308
+ logger_1.winstonLogger.add(captureTransport);
309
+ try {
310
+ var log = (0, logger_1.createFeatureFlaggedLogger)({ isEnabled: function () { return false; }, target: logger_1.logger });
311
+ log.info("hidden");
312
+ }
313
+ finally {
314
+ logger_1.winstonLogger.remove(captureTransport);
315
+ }
316
+ (0, bun_test_1.expect)(hits).toBe(0);
317
+ });
318
+ (0, bun_test_1.it)("forwards lines when enabled", function () {
319
+ var hits = 0;
320
+ var captureTransport = new winston_1.default.transports.Stream({
321
+ format: winston_1.default.format.printf(function () {
322
+ hits += 1;
323
+ return "";
324
+ }),
325
+ stream: new node_stream_1.Writable({
326
+ write: function (_chunk, _encoding, callback) {
327
+ callback();
328
+ },
329
+ }),
330
+ });
331
+ logger_1.winstonLogger.add(captureTransport);
332
+ try {
333
+ var log = (0, logger_1.createFeatureFlaggedLogger)({ isEnabled: function () { return true; }, target: logger_1.logger });
334
+ log.info("visible");
335
+ }
336
+ finally {
337
+ logger_1.winstonLogger.remove(captureTransport);
338
+ }
339
+ (0, bun_test_1.expect)(hits).toBeGreaterThan(0);
340
+ });
341
+ (0, bun_test_1.it)("forwards catch while disabled when gateCatch is false", function () {
342
+ var hits = 0;
343
+ var captureTransport = new winston_1.default.transports.Stream({
344
+ format: winston_1.default.format.printf(function () {
345
+ hits += 1;
346
+ return "";
347
+ }),
348
+ stream: new node_stream_1.Writable({
349
+ write: function (_chunk, _encoding, callback) {
350
+ callback();
351
+ },
352
+ }),
353
+ });
354
+ logger_1.winstonLogger.add(captureTransport);
355
+ try {
356
+ var log = (0, logger_1.createFeatureFlaggedLogger)({
357
+ gateCatch: false,
358
+ isEnabled: function () { return false; },
359
+ target: logger_1.logger,
360
+ });
361
+ log.catch(new Error("still-logged"));
362
+ }
363
+ finally {
364
+ logger_1.winstonLogger.remove(captureTransport);
365
+ }
366
+ (0, bun_test_1.expect)(hits).toBeGreaterThan(0);
367
+ });
368
+ (0, bun_test_1.it)("forwards all methods when enabled (debug, warn, error)", function () {
369
+ var levels = [];
370
+ var captureTransport = new winston_1.default.transports.Stream({
371
+ format: winston_1.default.format(function (info) {
372
+ levels.push(info.level);
373
+ return info;
374
+ })(),
375
+ stream: new node_stream_1.Writable({
376
+ write: function (_chunk, _encoding, callback) {
377
+ callback();
378
+ },
379
+ }),
380
+ });
381
+ logger_1.winstonLogger.add(captureTransport);
382
+ try {
383
+ var log = (0, logger_1.createFeatureFlaggedLogger)({ isEnabled: function () { return true; }, target: logger_1.logger });
384
+ log.debug("d");
385
+ log.warn("w");
386
+ log.error("e");
387
+ }
388
+ finally {
389
+ logger_1.winstonLogger.remove(captureTransport);
390
+ }
391
+ (0, bun_test_1.expect)(levels.some(function (l) { return l === "debug"; })).toBe(true);
392
+ (0, bun_test_1.expect)(levels.some(function (l) { return l === "warn"; })).toBe(true);
393
+ (0, bun_test_1.expect)(levels.some(function (l) { return l === "error"; })).toBe(true);
394
+ });
395
+ (0, bun_test_1.it)("drops debug, warn, error lines when disabled", function () {
396
+ var hits = 0;
397
+ var captureTransport = new winston_1.default.transports.Stream({
398
+ format: winston_1.default.format.printf(function () {
399
+ hits += 1;
400
+ return "";
401
+ }),
402
+ stream: new node_stream_1.Writable({
403
+ write: function (_chunk, _encoding, callback) {
404
+ callback();
405
+ },
406
+ }),
407
+ });
408
+ logger_1.winstonLogger.add(captureTransport);
409
+ try {
410
+ var log = (0, logger_1.createFeatureFlaggedLogger)({ isEnabled: function () { return false; }, target: logger_1.logger });
411
+ log.debug("hidden-d");
412
+ log.warn("hidden-w");
413
+ log.error("hidden-e");
414
+ }
415
+ finally {
416
+ logger_1.winstonLogger.remove(captureTransport);
417
+ }
418
+ (0, bun_test_1.expect)(hits).toBe(0);
419
+ });
420
+ (0, bun_test_1.it)("uses the global logger as default target when target is not provided", function () {
421
+ var log = (0, logger_1.createFeatureFlaggedLogger)({ isEnabled: function () { return true; } });
422
+ (0, bun_test_1.expect)(function () { return log.info("default target"); }).not.toThrow();
423
+ });
424
+ (0, bun_test_1.it)("drops catch while disabled when gateCatch is true", function () {
425
+ var hits = 0;
426
+ var captureTransport = new winston_1.default.transports.Stream({
427
+ format: winston_1.default.format.printf(function () {
428
+ hits += 1;
429
+ return "";
430
+ }),
431
+ stream: new node_stream_1.Writable({
432
+ write: function (_chunk, _encoding, callback) {
433
+ callback();
434
+ },
435
+ }),
436
+ });
437
+ logger_1.winstonLogger.add(captureTransport);
438
+ try {
439
+ var log = (0, logger_1.createFeatureFlaggedLogger)({
440
+ gateCatch: true,
441
+ isEnabled: function () { return false; },
442
+ target: logger_1.logger,
443
+ });
444
+ log.catch(new Error("suppressed"));
445
+ }
446
+ finally {
447
+ logger_1.winstonLogger.remove(captureTransport);
448
+ }
449
+ (0, bun_test_1.expect)(hits).toBe(0);
450
+ });
451
+ });
54
452
  (0, bun_test_1.describe)("setupLogging", function () {
55
453
  var tempDir;
56
454
  (0, bun_test_1.beforeEach)(function () {
@@ -140,4 +538,41 @@ var logger_1 = require("./logger");
140
538
  });
141
539
  (0, bun_test_1.expect)(true).toBe(true);
142
540
  });
541
+ (0, bun_test_1.it)("console format includes timestamps when showConsoleTimestamps is true", function () {
542
+ var lines = [];
543
+ (0, logger_1.setupLogging)({
544
+ disableFileLogging: true,
545
+ showConsoleTimestamps: true,
546
+ });
547
+ var captureTransport = new winston_1.default.transports.Stream({
548
+ format: winston_1.default.format.combine(winston_1.default.format.timestamp(), winston_1.default.format.printf(function (info) {
549
+ if (info.timestamp) {
550
+ return "".concat(info.timestamp, " - ").concat(info.level, ": ").concat(info.message);
551
+ }
552
+ return "".concat(info.level, ": ").concat(info.message);
553
+ })),
554
+ stream: new node_stream_1.Writable({
555
+ write: function (chunk, _encoding, callback) {
556
+ lines.push(chunk.toString().trim());
557
+ callback();
558
+ },
559
+ }),
560
+ });
561
+ logger_1.winstonLogger.add(captureTransport);
562
+ try {
563
+ logger_1.logger.info("timestamp-test");
564
+ }
565
+ finally {
566
+ logger_1.winstonLogger.remove(captureTransport);
567
+ }
568
+ (0, bun_test_1.expect)(lines.some(function (l) { return l.includes("timestamp-test"); })).toBe(true);
569
+ });
570
+ (0, bun_test_1.it)("disableTerrenoDevJsonlLog skips the dev JSONL transport", function () {
571
+ (0, bun_test_1.expect)(function () {
572
+ return (0, logger_1.setupLogging)({
573
+ disableFileLogging: true,
574
+ disableTerrenoDevJsonlLog: true,
575
+ });
576
+ }).not.toThrow();
577
+ });
143
578
  });
@@ -8,3 +8,10 @@ import type { NextFunction, Request, Response } from "express";
8
8
  * Expected header: `App-Version`
9
9
  */
10
10
  export declare const sentryAppVersionMiddleware: (req: Request, _res: Response, next: NextFunction) => void;
11
+ /**
12
+ * TerrenoApp middleware: augments `res.json` so plain-object payloads include
13
+ * `requestId` for client correlation. Skips OpenAPI tooling GET JSON routes
14
+ * (`/openapi.json`, `/openapi/components/...json`, `/openapi/validate`) so
15
+ * machine-consumed payloads stay valid. Does not wrap arrays or primitives.
16
+ */
17
+ export declare const jsonResponseRequestIdMiddleware: (req: Request, res: Response, next: NextFunction) => void;
@@ -1,4 +1,15 @@
1
1
  "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
2
13
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
14
  if (k2 === undefined) k2 = k;
4
15
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -33,8 +44,9 @@ var __importStar = (this && this.__importStar) || (function () {
33
44
  };
34
45
  })();
35
46
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.sentryAppVersionMiddleware = void 0;
47
+ exports.jsonResponseRequestIdMiddleware = exports.sentryAppVersionMiddleware = void 0;
37
48
  var Sentry = __importStar(require("@sentry/bun"));
49
+ var requestContext_1 = require("./requestContext");
38
50
  /**
39
51
  * Express middleware that captures the app version from the request header
40
52
  * and adds it as a tag to the current Sentry scope.
@@ -51,3 +63,48 @@ var sentryAppVersionMiddleware = function (req, _res, next) {
51
63
  next();
52
64
  };
53
65
  exports.sentryAppVersionMiddleware = sentryAppVersionMiddleware;
66
+ /**
67
+ * OpenAPI vendor routes that must return pristine JSON (no injected requestId).
68
+ * Matches @wesleytodd/openapi: main spec, per-component JSON, and validate payload.
69
+ */
70
+ var isOpenApiToolingJsonRequest = function (req) {
71
+ if (req.method !== "GET") {
72
+ return false;
73
+ }
74
+ var path = req.path;
75
+ if (path === "/openapi.json") {
76
+ return true;
77
+ }
78
+ if (path === "/openapi/validate") {
79
+ return true;
80
+ }
81
+ if (path.startsWith("/openapi/components/") && path.endsWith(".json")) {
82
+ return true;
83
+ }
84
+ return false;
85
+ };
86
+ /**
87
+ * TerrenoApp middleware: augments `res.json` so plain-object payloads include
88
+ * `requestId` for client correlation. Skips OpenAPI tooling GET JSON routes
89
+ * (`/openapi.json`, `/openapi/components/...json`, `/openapi/validate`) so
90
+ * machine-consumed payloads stay valid. Does not wrap arrays or primitives.
91
+ */
92
+ var jsonResponseRequestIdMiddleware = function (req, res, next) {
93
+ var originalJson = res.json.bind(res);
94
+ res.json = function (body) {
95
+ var _a, _b;
96
+ if (isOpenApiToolingJsonRequest(req)) {
97
+ return originalJson(body);
98
+ }
99
+ var requestId = (_a = req.requestId) !== null && _a !== void 0 ? _a : (_b = (0, requestContext_1.getCurrentRequestContext)()) === null || _b === void 0 ? void 0 : _b.requestId;
100
+ if (!requestId) {
101
+ return originalJson(body);
102
+ }
103
+ if (body !== null && body !== undefined && typeof body === "object" && !Array.isArray(body)) {
104
+ return originalJson(__assign(__assign({}, body), { requestId: requestId }));
105
+ }
106
+ return originalJson(body);
107
+ };
108
+ next();
109
+ };
110
+ exports.jsonResponseRequestIdMiddleware = jsonResponseRequestIdMiddleware;