@terreno/api 0.13.2 → 0.14.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 (175) hide show
  1. package/dist/__tests__/versionCheckPlugin.test.js +53 -3
  2. package/dist/api.arrayOperations.test.js +1 -0
  3. package/dist/api.asyncHandler.test.d.ts +1 -0
  4. package/dist/api.asyncHandler.test.js +236 -0
  5. package/dist/api.d.ts +15 -4
  6. package/dist/api.errors.test.js +1 -0
  7. package/dist/api.hooks.test.js +1 -0
  8. package/dist/api.js +153 -104
  9. package/dist/api.query.test.js +1 -0
  10. package/dist/api.test.js +174 -0
  11. package/dist/auth.d.ts +10 -5
  12. package/dist/auth.js +163 -90
  13. package/dist/auth.test.js +159 -0
  14. package/dist/betterAuthApp.test.js +1 -0
  15. package/dist/betterAuthSetup.d.ts +5 -6
  16. package/dist/betterAuthSetup.js +17 -14
  17. package/dist/betterAuthSetup.test.js +1 -0
  18. package/dist/config.d.ts +48 -0
  19. package/dist/config.js +248 -0
  20. package/dist/config.test.d.ts +1 -0
  21. package/dist/config.test.js +328 -0
  22. package/dist/configuration.test.js +1 -0
  23. package/dist/configurationApp.d.ts +1 -1
  24. package/dist/configurationApp.js +17 -13
  25. package/dist/configurationPlugin.test.js +1 -0
  26. package/dist/consentApp.test.js +1 -0
  27. package/dist/envConfigurationPlugin.d.ts +2 -0
  28. package/dist/envConfigurationPlugin.js +173 -0
  29. package/dist/envConfigurationPlugin.test.d.ts +1 -0
  30. package/dist/envConfigurationPlugin.test.js +322 -0
  31. package/dist/errors.d.ts +18 -7
  32. package/dist/errors.js +106 -10
  33. package/dist/errors.test.js +16 -1
  34. package/dist/example.js +16 -7
  35. package/dist/expressServer.d.ts +10 -9
  36. package/dist/expressServer.js +62 -53
  37. package/dist/expressServer.test.js +53 -2
  38. package/dist/githubAuth.d.ts +2 -1
  39. package/dist/githubAuth.js +41 -26
  40. package/dist/githubAuth.test.js +1 -0
  41. package/dist/index.d.ts +4 -0
  42. package/dist/index.js +4 -0
  43. package/dist/logger.d.ts +1 -1
  44. package/dist/logger.js +42 -20
  45. package/dist/models/versionConfig.d.ts +2 -0
  46. package/dist/models/versionConfig.js +8 -0
  47. package/dist/notifiers/googleChatNotifier.js +14 -16
  48. package/dist/notifiers/googleChatNotifier.test.js +1 -0
  49. package/dist/notifiers/slackNotifier.js +16 -14
  50. package/dist/notifiers/slackNotifier.test.js +41 -3
  51. package/dist/notifiers/zoomNotifier.js +7 -10
  52. package/dist/notifiers/zoomNotifier.test.js +1 -0
  53. package/dist/openApi.d.ts +1 -1
  54. package/dist/openApi.test.js +1 -0
  55. package/dist/openApiBuilder.d.ts +39 -6
  56. package/dist/openApiBuilder.js +1 -31
  57. package/dist/openApiBuilder.test.js +1 -0
  58. package/dist/openApiValidator.js +1 -0
  59. package/dist/openApiValidator.test.js +65 -0
  60. package/dist/permissions.d.ts +4 -4
  61. package/dist/permissions.js +67 -65
  62. package/dist/permissions.middleware.test.js +1 -0
  63. package/dist/permissions.test.js +1 -0
  64. package/dist/plugins.d.ts +5 -5
  65. package/dist/plugins.js +18 -9
  66. package/dist/plugins.test.js +1 -1
  67. package/dist/populate.d.ts +15 -8
  68. package/dist/populate.js +23 -24
  69. package/dist/populate.test.js +1 -0
  70. package/dist/realtime/changeStreamWatcher.d.ts +73 -0
  71. package/dist/realtime/changeStreamWatcher.js +720 -0
  72. package/dist/realtime/index.d.ts +6 -0
  73. package/dist/realtime/index.js +27 -0
  74. package/dist/realtime/queryMatcher.d.ts +14 -0
  75. package/dist/realtime/queryMatcher.js +250 -0
  76. package/dist/realtime/queryStore.d.ts +37 -0
  77. package/dist/realtime/queryStore.js +195 -0
  78. package/dist/realtime/realtime.test.d.ts +10 -0
  79. package/dist/realtime/realtime.test.js +2158 -0
  80. package/dist/realtime/realtimeApp.d.ts +93 -0
  81. package/dist/realtime/realtimeApp.js +560 -0
  82. package/dist/realtime/registry.d.ts +40 -0
  83. package/dist/realtime/registry.js +38 -0
  84. package/dist/realtime/socketUser.d.ts +10 -0
  85. package/dist/realtime/socketUser.js +17 -0
  86. package/dist/realtime/types.d.ts +100 -0
  87. package/dist/realtime/types.js +2 -0
  88. package/dist/requestContext.d.ts +37 -0
  89. package/dist/requestContext.js +344 -0
  90. package/dist/requestContext.test.d.ts +1 -0
  91. package/dist/requestContext.test.js +241 -0
  92. package/dist/terrenoApp.d.ts +8 -0
  93. package/dist/terrenoApp.js +50 -13
  94. package/dist/terrenoApp.test.js +194 -21
  95. package/dist/terrenoPlugin.d.ts +11 -0
  96. package/dist/tests/bunSetup.js +1 -0
  97. package/dist/tests.js +1 -1
  98. package/dist/transformers.d.ts +2 -2
  99. package/dist/transformers.js +5 -3
  100. package/dist/transformers.test.js +90 -0
  101. package/dist/types/consentResponse.d.ts +6 -3
  102. package/dist/versionCheckPlugin.d.ts +2 -0
  103. package/dist/versionCheckPlugin.js +18 -12
  104. package/package.json +4 -2
  105. package/src/__tests__/versionCheckPlugin.test.ts +37 -3
  106. package/src/api.arrayOperations.test.ts +1 -0
  107. package/src/api.asyncHandler.test.ts +177 -0
  108. package/src/api.errors.test.ts +1 -0
  109. package/src/api.hooks.test.ts +1 -0
  110. package/src/api.query.test.ts +1 -0
  111. package/src/api.test.ts +132 -0
  112. package/src/api.ts +199 -84
  113. package/src/auth.test.ts +160 -0
  114. package/src/auth.ts +120 -50
  115. package/src/betterAuthApp.test.ts +1 -0
  116. package/src/betterAuthSetup.test.ts +1 -0
  117. package/src/betterAuthSetup.ts +46 -19
  118. package/src/config.test.ts +255 -0
  119. package/src/config.ts +206 -0
  120. package/src/configuration.test.ts +1 -0
  121. package/src/configurationApp.ts +59 -24
  122. package/src/configurationPlugin.test.ts +1 -0
  123. package/src/consentApp.test.ts +1 -0
  124. package/src/envConfigurationPlugin.test.ts +143 -0
  125. package/src/envConfigurationPlugin.ts +100 -0
  126. package/src/errors.test.ts +19 -1
  127. package/src/errors.ts +94 -20
  128. package/src/example.ts +46 -21
  129. package/src/express.d.ts +18 -1
  130. package/src/expressServer.test.ts +50 -2
  131. package/src/expressServer.ts +80 -50
  132. package/src/githubAuth.test.ts +1 -0
  133. package/src/githubAuth.ts +59 -38
  134. package/src/index.ts +4 -0
  135. package/src/logger.ts +47 -17
  136. package/src/models/versionConfig.ts +13 -2
  137. package/src/notifiers/googleChatNotifier.test.ts +1 -0
  138. package/src/notifiers/googleChatNotifier.ts +7 -9
  139. package/src/notifiers/slackNotifier.test.ts +29 -3
  140. package/src/notifiers/slackNotifier.ts +9 -7
  141. package/src/notifiers/zoomNotifier.test.ts +1 -0
  142. package/src/notifiers/zoomNotifier.ts +8 -11
  143. package/src/openApi.test.ts +1 -0
  144. package/src/openApi.ts +4 -4
  145. package/src/openApiBuilder.test.ts +1 -0
  146. package/src/openApiBuilder.ts +14 -11
  147. package/src/openApiValidator.test.ts +59 -0
  148. package/src/openApiValidator.ts +3 -2
  149. package/src/permissions.middleware.test.ts +1 -0
  150. package/src/permissions.test.ts +1 -0
  151. package/src/permissions.ts +30 -25
  152. package/src/plugins.test.ts +1 -1
  153. package/src/plugins.ts +21 -14
  154. package/src/populate.test.ts +1 -0
  155. package/src/populate.ts +44 -36
  156. package/src/realtime/changeStreamWatcher.ts +568 -0
  157. package/src/realtime/index.ts +34 -0
  158. package/src/realtime/queryMatcher.ts +179 -0
  159. package/src/realtime/queryStore.ts +132 -0
  160. package/src/realtime/realtime.test.ts +1755 -0
  161. package/src/realtime/realtimeApp.ts +478 -0
  162. package/src/realtime/registry.ts +64 -0
  163. package/src/realtime/socketUser.ts +25 -0
  164. package/src/realtime/types.ts +112 -0
  165. package/src/requestContext.test.ts +196 -0
  166. package/src/requestContext.ts +368 -0
  167. package/src/terrenoApp.test.ts +137 -11
  168. package/src/terrenoApp.ts +64 -17
  169. package/src/terrenoPlugin.ts +12 -0
  170. package/src/tests/bunSetup.ts +1 -0
  171. package/src/tests.ts +7 -2
  172. package/src/transformers.test.ts +70 -2
  173. package/src/transformers.ts +15 -7
  174. package/src/types/consentResponse.ts +8 -10
  175. package/src/versionCheckPlugin.ts +15 -7
@@ -0,0 +1,241 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __generator = (this && this.__generator) || function (thisArg, body) {
45
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
46
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
47
+ function verb(n) { return function (v) { return step([n, v]); }; }
48
+ function step(op) {
49
+ if (f) throw new TypeError("Generator is already executing.");
50
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
51
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
52
+ if (y = 0, t) op = [op[0] & 2, t.value];
53
+ switch (op[0]) {
54
+ case 0: case 1: t = op; break;
55
+ case 4: _.label++; return { value: op[1], done: false };
56
+ case 5: _.label++; y = op[1]; op = [0]; continue;
57
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
58
+ default:
59
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
60
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
61
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
62
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
63
+ if (t[2]) _.ops.pop();
64
+ _.trys.pop(); continue;
65
+ }
66
+ op = body.call(thisArg, _);
67
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
68
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
69
+ }
70
+ };
71
+ var __importDefault = (this && this.__importDefault) || function (mod) {
72
+ return (mod && mod.__esModule) ? mod : { "default": mod };
73
+ };
74
+ Object.defineProperty(exports, "__esModule", { value: true });
75
+ var bun_test_1 = require("bun:test");
76
+ var node_stream_1 = require("node:stream");
77
+ var Sentry = __importStar(require("@sentry/bun"));
78
+ var express_1 = __importDefault(require("express"));
79
+ var supertest_1 = __importDefault(require("supertest"));
80
+ var winston_1 = __importDefault(require("winston"));
81
+ var logger_1 = require("./logger");
82
+ var requestContext_1 = require("./requestContext");
83
+ (0, bun_test_1.describe)("request context job propagation", function () {
84
+ (0, bun_test_1.beforeEach)(function () {
85
+ var scope = Sentry.getCurrentScope();
86
+ scope.setContext.mockClear();
87
+ scope.setTag.mockClear();
88
+ scope.setUser.mockClear();
89
+ });
90
+ (0, bun_test_1.afterEach)(function () {
91
+ (0, logger_1.setupLogging)({ disableFileLogging: true });
92
+ });
93
+ (0, bun_test_1.it)("serializes the current context for downstream jobs", function () {
94
+ (0, requestContext_1.runWithRequestContext)({
95
+ jobId: "job-1",
96
+ requestId: "request-1",
97
+ sessionId: "session-1",
98
+ spanId: "span-1",
99
+ traceId: "trace-1",
100
+ traceSampled: false,
101
+ userId: "user-1",
102
+ }, function () {
103
+ (0, bun_test_1.expect)((0, requestContext_1.getCurrentRequestContextAttributes)()).toEqual({
104
+ "x-job-id": "job-1",
105
+ "x-request-id": "request-1",
106
+ "x-session-id": "session-1",
107
+ "x-span-id": "span-1",
108
+ "x-trace-id": "trace-1",
109
+ "x-trace-sampled": "false",
110
+ "x-user-id": "user-1",
111
+ });
112
+ });
113
+ });
114
+ (0, bun_test_1.it)("allows downstream jobs to replace only the job id", function () {
115
+ (0, requestContext_1.runWithRequestContext)({ jobId: "job-parent", requestId: "request-1", sessionId: "session-1" }, function () {
116
+ (0, bun_test_1.expect)((0, requestContext_1.getCurrentRequestContextAttributes)({ jobId: "job-child" })).toEqual({
117
+ "x-job-id": "job-child",
118
+ "x-request-id": "request-1",
119
+ "x-session-id": "session-1",
120
+ });
121
+ });
122
+ });
123
+ (0, bun_test_1.it)("restores worker context from message attributes", function () {
124
+ var scope = Sentry.getCurrentScope();
125
+ var setContextMock = scope.setContext;
126
+ var setTagMock = scope.setTag;
127
+ var setUserMock = scope.setUser;
128
+ (0, requestContext_1.runWithRequestContextAttributes)({
129
+ traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
130
+ "x-job-id": "job-worker-1",
131
+ "x-request-id": "request-1",
132
+ "x-session-id": "session-1",
133
+ "x-user-id": "user-1",
134
+ }, function () {
135
+ (0, bun_test_1.expect)((0, requestContext_1.getCurrentRequestContext)()).toEqual({
136
+ jobId: "job-worker-1",
137
+ requestId: "request-1",
138
+ sessionId: "session-1",
139
+ spanId: "00f067aa0ba902b7",
140
+ traceId: "4bf92f3577b34da6a3ce929d0e0e4736",
141
+ traceSampled: true,
142
+ userId: "user-1",
143
+ });
144
+ });
145
+ (0, bun_test_1.expect)(setTagMock).toHaveBeenCalledWith("request_id", "request-1");
146
+ (0, bun_test_1.expect)(setTagMock).toHaveBeenCalledWith("session_id", "session-1");
147
+ (0, bun_test_1.expect)(setTagMock).toHaveBeenCalledWith("job_id", "job-worker-1");
148
+ (0, bun_test_1.expect)(setTagMock).toHaveBeenCalledWith("user_id", "user-1");
149
+ (0, bun_test_1.expect)(setTagMock).toHaveBeenCalledWith("trace_id", "4bf92f3577b34da6a3ce929d0e0e4736");
150
+ (0, bun_test_1.expect)(setUserMock).toHaveBeenCalledWith({ id: "user-1" });
151
+ (0, bun_test_1.expect)(setContextMock).toHaveBeenCalledWith("request_context", {
152
+ jobId: "job-worker-1",
153
+ requestId: "request-1",
154
+ sessionId: "session-1",
155
+ spanId: "00f067aa0ba902b7",
156
+ traceId: "4bf92f3577b34da6a3ce929d0e0e4736",
157
+ traceSampled: true,
158
+ userId: "user-1",
159
+ });
160
+ });
161
+ (0, bun_test_1.it)("sends context updates to Sentry for auth session changes", function () {
162
+ var scope = Sentry.getCurrentScope();
163
+ var setTagMock = scope.setTag;
164
+ var setUserMock = scope.setUser;
165
+ (0, requestContext_1.runWithRequestContext)({ requestId: "request-auth-1" }, function () {
166
+ setTagMock.mockClear();
167
+ setUserMock.mockClear();
168
+ (0, requestContext_1.setRequestContext)({ sessionId: "session-auth-1", userId: "user-auth-1" });
169
+ });
170
+ (0, bun_test_1.expect)(setTagMock).toHaveBeenCalledWith("session_id", "session-auth-1");
171
+ (0, bun_test_1.expect)(setTagMock).toHaveBeenCalledWith("user_id", "user-auth-1");
172
+ (0, bun_test_1.expect)(setUserMock).toHaveBeenCalledWith({ id: "user-auth-1" });
173
+ });
174
+ (0, bun_test_1.it)("uses trace id as request id when attributes do not include request id", function () {
175
+ var context = (0, requestContext_1.getRequestContextFromAttributes)({
176
+ traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00",
177
+ "x-job-id": "job-worker-1",
178
+ });
179
+ (0, bun_test_1.expect)(context).toEqual({
180
+ jobId: "job-worker-1",
181
+ requestId: "4bf92f3577b34da6a3ce929d0e0e4736",
182
+ sessionId: undefined,
183
+ spanId: "00f067aa0ba902b7",
184
+ traceId: "4bf92f3577b34da6a3ce929d0e0e4736",
185
+ traceSampled: false,
186
+ userId: undefined,
187
+ });
188
+ });
189
+ (0, bun_test_1.it)("accepts job id from HTTP headers", function () { return __awaiter(void 0, void 0, void 0, function () {
190
+ var app, res;
191
+ return __generator(this, function (_a) {
192
+ switch (_a.label) {
193
+ case 0:
194
+ app = (0, express_1.default)();
195
+ app.use(requestContext_1.requestContextMiddleware);
196
+ app.get("/job", function (req, res) {
197
+ res.json({ context: (0, requestContext_1.getCurrentLogContext)(), jobId: req.jobId });
198
+ });
199
+ return [4 /*yield*/, (0, supertest_1.default)(app)
200
+ .get("/job")
201
+ .set("X-Job-ID", "job-http-1")
202
+ .set("X-Request-ID", "request-http-1")
203
+ .expect(200)];
204
+ case 1:
205
+ res = _a.sent();
206
+ (0, bun_test_1.expect)(res.headers["x-job-id"]).toBe("job-http-1");
207
+ (0, bun_test_1.expect)(res.body).toEqual({
208
+ context: { jobId: "job-http-1", requestId: "request-http-1" },
209
+ jobId: "job-http-1",
210
+ });
211
+ return [2 /*return*/];
212
+ }
213
+ });
214
+ }); });
215
+ (0, bun_test_1.it)("adds job id to logger context", function () {
216
+ var output = "";
217
+ var stream = new node_stream_1.Writable({
218
+ write: function (chunk, _encoding, callback) {
219
+ output += chunk.toString();
220
+ callback();
221
+ },
222
+ });
223
+ (0, logger_1.setupLogging)({
224
+ disableConsoleLogging: true,
225
+ disableFileLogging: true,
226
+ transports: [
227
+ new winston_1.default.transports.Stream({
228
+ format: winston_1.default.format.printf(function (info) {
229
+ return "".concat(info.level, ": ").concat(info.message, " requestId=").concat(info.requestId, " jobId=").concat(info.jobId);
230
+ }),
231
+ stream: stream,
232
+ }),
233
+ ],
234
+ });
235
+ (0, requestContext_1.runWithRequestContext)({ jobId: "job-log-1", requestId: "request-log-1" }, function () {
236
+ logger_1.logger.info("worker handled job");
237
+ });
238
+ (0, bun_test_1.expect)(output).toContain("requestId=request-log-1");
239
+ (0, bun_test_1.expect)(output).toContain("jobId=job-log-1");
240
+ });
241
+ });
@@ -6,6 +6,7 @@ import { type ConfigurationAppOptions } from "./configurationApp";
6
6
  import { type AuthOptions } from "./expressServer";
7
7
  import { type GitHubAuthOptions } from "./githubAuth";
8
8
  import { type LoggingOptions } from "./logger";
9
+ import type { RealtimeAppOptions } from "./realtime/types";
9
10
  import type { TerrenoPlugin } from "./terrenoPlugin";
10
11
  type CorsOrigin = string | boolean | RegExp | Array<boolean | string | RegExp> | ((requestOrigin: string | undefined, callback: (err: Error | null, origin?: boolean | string | RegExp | Array<boolean | string | RegExp>) => void) => void);
11
12
  /**
@@ -30,6 +31,13 @@ export interface TerrenoAppOptions {
30
31
  arrayLimit?: number;
31
32
  /** Whether to log all incoming requests (default: true) */
32
33
  logRequests?: boolean;
34
+ /**
35
+ * Real-time sync configuration. When provided, Socket.io and MongoDB change streams
36
+ * are set up automatically — no need to register RealtimeApp as a separate plugin.
37
+ *
38
+ * Set to `true` for defaults, or pass a RealtimeAppOptions object for full control.
39
+ */
40
+ realtime?: boolean | RealtimeAppOptions;
33
41
  }
34
42
  /**
35
43
  * Fluent API for building Express applications with Terreno framework.
@@ -59,6 +59,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
59
59
  };
60
60
  Object.defineProperty(exports, "__esModule", { value: true });
61
61
  exports.TerrenoApp = void 0;
62
+ var node_http_1 = require("node:http");
62
63
  var Sentry = __importStar(require("@sentry/bun"));
63
64
  var cors_1 = __importDefault(require("cors"));
64
65
  var express_1 = __importDefault(require("express"));
@@ -71,6 +72,8 @@ var githubAuth_1 = require("./githubAuth");
71
72
  var logger_1 = require("./logger");
72
73
  var openApiCompat_1 = require("./openApiCompat");
73
74
  var openApiEtag_1 = require("./openApiEtag");
75
+ var realtimeApp_1 = require("./realtime/realtimeApp");
76
+ var requestContext_1 = require("./requestContext");
74
77
  var index_1 = __importDefault(require("./vendor/wesleytodd-openapi/index"));
75
78
  /**
76
79
  * Fluent API for building Express applications with Terreno framework.
@@ -211,7 +214,9 @@ var TerrenoApp = /** @class */ (function () {
211
214
  * .start();
212
215
  * ```
213
216
  */
214
- TerrenoApp.prototype.configure = function (model, options) {
217
+ TerrenoApp.prototype.configure = function (
218
+ // biome-ignore lint/suspicious/noExplicitAny: Model<any> required for invariance — consumers pass arbitrary configuration models
219
+ model, options) {
215
220
  this.configurationApp = new configurationApp_1.ConfigurationApp(__assign({ model: model }, options));
216
221
  return this;
217
222
  };
@@ -247,6 +252,7 @@ var TerrenoApp = /** @class */ (function () {
247
252
  // Record mount paths on layers for Express 5 → OpenAPI compat
248
253
  (0, openApiCompat_1.patchAppUse)(app);
249
254
  app.set("query parser", function (str) { var _a; return qs_1.default.parse(str, { arrayLimit: (_a = options.arrayLimit) !== null && _a !== void 0 ? _a : 200 }); });
255
+ app.use(requestContext_1.requestContextMiddleware);
250
256
  app.use((0, cors_1.default)({ credentials: true, origin: (_c = options.corsOrigin) !== null && _c !== void 0 ? _c : "*" }));
251
257
  try {
252
258
  // Apply custom middleware before JSON parsing
@@ -273,6 +279,10 @@ var TerrenoApp = /** @class */ (function () {
273
279
  // Auth routes (login/signup/refresh_token) before JWT middleware
274
280
  (0, auth_1.addAuthRoutes)(app, options.userModel, options.authOptions);
275
281
  (0, auth_1.setupAuth)(app, options.userModel);
282
+ app.use(function (req, res, next) {
283
+ (0, requestContext_1.updateRequestContextFromRequest)(req, res);
284
+ next();
285
+ });
276
286
  if (options.logRequests !== false) {
277
287
  app.use(expressServer_1.logRequests);
278
288
  }
@@ -283,17 +293,21 @@ var TerrenoApp = /** @class */ (function () {
283
293
  });
284
294
  // Sentry scopes
285
295
  app.use(function (req, _res, next) {
286
- var _a;
296
+ var _a, _b;
297
+ var context = (0, requestContext_1.getCurrentRequestContext)();
287
298
  var transactionId = req.header("X-Transaction-ID");
288
- var sessionId = req.header("X-Session-ID");
299
+ var sessionId = (_a = context === null || context === void 0 ? void 0 : context.sessionId) !== null && _a !== void 0 ? _a : req.header("X-Session-ID");
300
+ if (context === null || context === void 0 ? void 0 : context.requestId) {
301
+ Sentry.getCurrentScope().setTag("request_id", context.requestId);
302
+ }
289
303
  if (transactionId) {
290
304
  Sentry.getCurrentScope().setTag("transaction_id", transactionId);
291
305
  }
292
306
  if (sessionId) {
293
307
  Sentry.getCurrentScope().setTag("session_id", sessionId);
294
308
  }
295
- if ((_a = req.user) === null || _a === void 0 ? void 0 : _a._id) {
296
- Sentry.getCurrentScope().setTag("user", req.user._id);
309
+ if ((_b = req.user) === null || _b === void 0 ? void 0 : _b._id) {
310
+ Sentry.getCurrentScope().setTag("user", String(req.user._id));
297
311
  }
298
312
  next();
299
313
  });
@@ -348,12 +362,7 @@ var TerrenoApp = /** @class */ (function () {
348
362
  // Error middleware
349
363
  app.use(errors_1.apiUnauthorizedMiddleware);
350
364
  app.use(errors_1.apiErrorMiddleware);
351
- app.use(function onError(err, _req, res, _next) {
352
- logger_1.logger.error("Fallthrough error: ".concat(err).concat((err === null || err === void 0 ? void 0 : err.stack) ? "\n".concat(err.stack) : "", "}"));
353
- Sentry.captureException(err);
354
- res.statusCode = 500;
355
- res.end("".concat(res.sentry, "\n"));
356
- });
365
+ app.use(errors_1.apiFallthroughErrorMiddleware);
357
366
  return app;
358
367
  };
359
368
  /**
@@ -377,16 +386,44 @@ var TerrenoApp = /** @class */ (function () {
377
386
  * ```
378
387
  */
379
388
  TerrenoApp.prototype.start = function () {
389
+ var e_3, _a;
390
+ var _this = this;
391
+ // If realtime option is set, auto-register the RealtimeApp plugin
392
+ if (this.options.realtime) {
393
+ var hasRealtimePlugin = this.registrations.some(function (r) { return !_this.isModelRouterRegistration(r) && r instanceof realtimeApp_1.RealtimeApp; });
394
+ if (!hasRealtimePlugin) {
395
+ var realtimeConfig = typeof this.options.realtime === "object" ? this.options.realtime : {};
396
+ this.register(new realtimeApp_1.RealtimeApp(realtimeConfig));
397
+ }
398
+ }
380
399
  var app = this.build();
381
400
  if (!this.options.skipListen) {
382
401
  var port_1 = process.env.PORT || "9000";
383
402
  try {
384
- app.listen(port_1, function () {
403
+ var server = (0, node_http_1.createServer)(app);
404
+ try {
405
+ // Notify plugins that need access to the HTTP server (e.g. WebSocket plugins)
406
+ for (var _b = __values(this.registrations), _c = _b.next(); !_c.done; _c = _b.next()) {
407
+ var reg = _c.value;
408
+ if (!this.isModelRouterRegistration(reg) && typeof reg.onServerCreated === "function") {
409
+ reg.onServerCreated(server);
410
+ }
411
+ }
412
+ }
413
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
414
+ finally {
415
+ try {
416
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
417
+ }
418
+ finally { if (e_3) throw e_3.error; }
419
+ }
420
+ server.listen(port_1, function () {
385
421
  logger_1.logger.info("Listening on port ".concat(port_1));
386
422
  });
387
423
  }
388
424
  catch (error) {
389
- logger_1.logger.error("Error trying to start HTTP server: ".concat(error, "\n").concat(error.stack));
425
+ var stack = error instanceof Error ? error.stack : String(error);
426
+ logger_1.logger.error("Error trying to start HTTP server: ".concat(error, "\n").concat(stack));
390
427
  process.exit(1);
391
428
  }
392
429
  }