@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
@@ -39,6 +39,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
39
39
  return (mod && mod.__esModule) ? mod : { "default": mod };
40
40
  };
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
+ // biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
42
43
  var bun_test_1 = require("bun:test");
43
44
  var supertest_1 = __importDefault(require("supertest"));
44
45
  var versionConfig_1 = require("../models/versionConfig");
@@ -86,7 +87,7 @@ var versionCheckPlugin_1 = require("../versionCheckPlugin");
86
87
  case 1:
87
88
  res = _a.sent();
88
89
  (0, bun_test_1.expect)(res.status).toBe(200);
89
- (0, bun_test_1.expect)(res.body).toEqual({ status: "ok" });
90
+ (0, bun_test_1.expect)(res.body).toEqual({ pollingIntervalMs: 86400000, status: "ok" });
90
91
  return [2 /*return*/];
91
92
  }
92
93
  });
@@ -131,7 +132,12 @@ var versionCheckPlugin_1 = require("../versionCheckPlugin");
131
132
  case 2:
132
133
  res = _a.sent();
133
134
  (0, bun_test_1.expect)(res.status).toBe(200);
134
- (0, bun_test_1.expect)(res.body).toEqual({ requiredVersion: 50, status: "ok", warningVersion: 100 });
135
+ (0, bun_test_1.expect)(res.body).toEqual({
136
+ pollingIntervalMs: 86400000,
137
+ requiredVersion: 50,
138
+ status: "ok",
139
+ warningVersion: 100,
140
+ });
135
141
  return [2 /*return*/];
136
142
  }
137
143
  });
@@ -236,7 +242,51 @@ var versionCheckPlugin_1 = require("../versionCheckPlugin");
236
242
  case 2:
237
243
  res = _a.sent();
238
244
  (0, bun_test_1.expect)(res.status).toBe(200);
239
- (0, bun_test_1.expect)(res.body).toEqual({ requiredVersion: 50, status: "ok", warningVersion: 100 });
245
+ (0, bun_test_1.expect)(res.body).toEqual({
246
+ pollingIntervalMs: 86400000,
247
+ requiredVersion: 50,
248
+ status: "ok",
249
+ warningVersion: 100,
250
+ });
251
+ return [2 /*return*/];
252
+ }
253
+ });
254
+ }); });
255
+ (0, bun_test_1.it)("returns pollingIntervalMs from config pollingIntervalMinutes", function () { return __awaiter(void 0, void 0, void 0, function () {
256
+ var res;
257
+ return __generator(this, function (_a) {
258
+ switch (_a.label) {
259
+ case 0: return [4 /*yield*/, versionConfig_1.VersionConfig.create({
260
+ pollingIntervalMinutes: 60,
261
+ webRequiredVersion: 0,
262
+ webWarningVersion: 0,
263
+ })];
264
+ case 1:
265
+ _a.sent();
266
+ return [4 /*yield*/, app.get("/version-check").query({ platform: "web", version: 100 })];
267
+ case 2:
268
+ res = _a.sent();
269
+ (0, bun_test_1.expect)(res.status).toBe(200);
270
+ (0, bun_test_1.expect)(res.body.pollingIntervalMs).toBe(3600000);
271
+ return [2 /*return*/];
272
+ }
273
+ });
274
+ }); });
275
+ (0, bun_test_1.it)("returns default pollingIntervalMs (86400000) when pollingIntervalMinutes not set", function () { return __awaiter(void 0, void 0, void 0, function () {
276
+ var res;
277
+ return __generator(this, function (_a) {
278
+ switch (_a.label) {
279
+ case 0: return [4 /*yield*/, versionConfig_1.VersionConfig.create({
280
+ webRequiredVersion: 0,
281
+ webWarningVersion: 0,
282
+ })];
283
+ case 1:
284
+ _a.sent();
285
+ return [4 /*yield*/, app.get("/version-check").query({ platform: "web", version: 100 })];
286
+ case 2:
287
+ res = _a.sent();
288
+ (0, bun_test_1.expect)(res.status).toBe(200);
289
+ (0, bun_test_1.expect)(res.body.pollingIntervalMs).toBe(86400000);
240
290
  return [2 /*return*/];
241
291
  }
242
292
  });
@@ -55,6 +55,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
55
55
  return (mod && mod.__esModule) ? mod : { "default": mod };
56
56
  };
57
57
  Object.defineProperty(exports, "__esModule", { value: true });
58
+ // biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
58
59
  var bun_test_1 = require("bun:test");
59
60
  var supertest_1 = __importDefault(require("supertest"));
60
61
  var api_1 = require("./api");
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,236 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ 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);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ 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;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ var bun_test_1 = require("bun:test");
43
+ var express_1 = __importDefault(require("express"));
44
+ var supertest_1 = __importDefault(require("supertest"));
45
+ var api_1 = require("./api");
46
+ var openApiValidator_1 = require("./openApiValidator");
47
+ (0, bun_test_1.afterEach)(function () {
48
+ (0, openApiValidator_1.resetOpenApiValidatorConfig)();
49
+ });
50
+ var createApp = function () {
51
+ var app = (0, express_1.default)();
52
+ app.use(express_1.default.json());
53
+ return app;
54
+ };
55
+ var errorHandler = function (err, _req, res, _next) {
56
+ res.status(err.status || 500).json({ error: err.title || err.message });
57
+ };
58
+ (0, bun_test_1.describe)("asyncHandler with bodySchema validation", function () {
59
+ (0, bun_test_1.it)("validates and accepts a conforming body", function () { return __awaiter(void 0, void 0, void 0, function () {
60
+ var app, res;
61
+ return __generator(this, function (_a) {
62
+ switch (_a.label) {
63
+ case 0:
64
+ (0, openApiValidator_1.configureOpenApiValidator)({});
65
+ app = createApp();
66
+ app.post("/test", (0, api_1.asyncHandler)(function (_req, res) { return __awaiter(void 0, void 0, void 0, function () {
67
+ return __generator(this, function (_a) {
68
+ res.json({ ok: true });
69
+ return [2 /*return*/];
70
+ });
71
+ }); }, {
72
+ bodySchema: { name: { required: true, type: "string" } },
73
+ validate: true,
74
+ }));
75
+ app.use(errorHandler);
76
+ return [4 /*yield*/, (0, supertest_1.default)(app).post("/test").send({ name: "hello" }).expect(200)];
77
+ case 1:
78
+ res = _a.sent();
79
+ (0, bun_test_1.expect)(res.body.ok).toBe(true);
80
+ return [2 /*return*/];
81
+ }
82
+ });
83
+ }); });
84
+ (0, bun_test_1.it)("rejects a body missing a required field", function () { return __awaiter(void 0, void 0, void 0, function () {
85
+ var app;
86
+ return __generator(this, function (_a) {
87
+ switch (_a.label) {
88
+ case 0:
89
+ (0, openApiValidator_1.configureOpenApiValidator)({});
90
+ app = createApp();
91
+ app.post("/test", (0, api_1.asyncHandler)(function (_req, res) { return __awaiter(void 0, void 0, void 0, function () {
92
+ return __generator(this, function (_a) {
93
+ res.json({ ok: true });
94
+ return [2 /*return*/];
95
+ });
96
+ }); }, {
97
+ bodySchema: { name: { required: true, type: "string" } },
98
+ validate: true,
99
+ }));
100
+ app.use(errorHandler);
101
+ return [4 /*yield*/, (0, supertest_1.default)(app).post("/test").send({}).expect(400)];
102
+ case 1:
103
+ _a.sent();
104
+ return [2 /*return*/];
105
+ }
106
+ });
107
+ }); });
108
+ (0, bun_test_1.it)("skips body validation when validate is false", function () { return __awaiter(void 0, void 0, void 0, function () {
109
+ var app, res;
110
+ return __generator(this, function (_a) {
111
+ switch (_a.label) {
112
+ case 0:
113
+ (0, openApiValidator_1.configureOpenApiValidator)({});
114
+ app = createApp();
115
+ app.post("/test", (0, api_1.asyncHandler)(function (_req, res) { return __awaiter(void 0, void 0, void 0, function () {
116
+ return __generator(this, function (_a) {
117
+ res.json({ ok: true });
118
+ return [2 /*return*/];
119
+ });
120
+ }); }, {
121
+ bodySchema: { name: { required: true, type: "string" } },
122
+ validate: false,
123
+ }));
124
+ app.use(errorHandler);
125
+ return [4 /*yield*/, (0, supertest_1.default)(app).post("/test").send({}).expect(200)];
126
+ case 1:
127
+ res = _a.sent();
128
+ (0, bun_test_1.expect)(res.body.ok).toBe(true);
129
+ return [2 /*return*/];
130
+ }
131
+ });
132
+ }); });
133
+ });
134
+ (0, bun_test_1.describe)("asyncHandler with querySchema validation", function () {
135
+ (0, bun_test_1.it)("validates and accepts conforming query params", function () { return __awaiter(void 0, void 0, void 0, function () {
136
+ var app, res;
137
+ return __generator(this, function (_a) {
138
+ switch (_a.label) {
139
+ case 0:
140
+ (0, openApiValidator_1.configureOpenApiValidator)({});
141
+ app = createApp();
142
+ app.get("/test", (0, api_1.asyncHandler)(function (_req, res) { return __awaiter(void 0, void 0, void 0, function () {
143
+ return __generator(this, function (_a) {
144
+ res.json({ ok: true });
145
+ return [2 /*return*/];
146
+ });
147
+ }); }, {
148
+ querySchema: { page: { type: "integer" } },
149
+ validate: true,
150
+ }));
151
+ app.use(errorHandler);
152
+ return [4 /*yield*/, (0, supertest_1.default)(app).get("/test?page=1").expect(200)];
153
+ case 1:
154
+ res = _a.sent();
155
+ (0, bun_test_1.expect)(res.body.ok).toBe(true);
156
+ return [2 /*return*/];
157
+ }
158
+ });
159
+ }); });
160
+ (0, bun_test_1.it)("rejects invalid query params", function () { return __awaiter(void 0, void 0, void 0, function () {
161
+ var app;
162
+ return __generator(this, function (_a) {
163
+ switch (_a.label) {
164
+ case 0:
165
+ (0, openApiValidator_1.configureOpenApiValidator)({});
166
+ app = createApp();
167
+ app.get("/test", (0, api_1.asyncHandler)(function (_req, res) { return __awaiter(void 0, void 0, void 0, function () {
168
+ return __generator(this, function (_a) {
169
+ res.json({ ok: true });
170
+ return [2 /*return*/];
171
+ });
172
+ }); }, {
173
+ querySchema: { page: { required: true, type: "integer" } },
174
+ validate: true,
175
+ }));
176
+ app.use(errorHandler);
177
+ return [4 /*yield*/, (0, supertest_1.default)(app).get("/test").expect(400)];
178
+ case 1:
179
+ _a.sent();
180
+ return [2 /*return*/];
181
+ }
182
+ });
183
+ }); });
184
+ });
185
+ (0, bun_test_1.describe)("asyncHandler with both schemas", function () {
186
+ (0, bun_test_1.it)("runs both body and query validators sequentially", function () { return __awaiter(void 0, void 0, void 0, function () {
187
+ var app, res;
188
+ return __generator(this, function (_a) {
189
+ switch (_a.label) {
190
+ case 0:
191
+ (0, openApiValidator_1.configureOpenApiValidator)({});
192
+ app = createApp();
193
+ app.post("/test", (0, api_1.asyncHandler)(function (_req, res) { return __awaiter(void 0, void 0, void 0, function () {
194
+ return __generator(this, function (_a) {
195
+ res.json({ ok: true });
196
+ return [2 /*return*/];
197
+ });
198
+ }); }, {
199
+ bodySchema: { name: { required: true, type: "string" } },
200
+ querySchema: { page: { type: "integer" } },
201
+ validate: true,
202
+ }));
203
+ app.use(errorHandler);
204
+ return [4 /*yield*/, (0, supertest_1.default)(app).post("/test?page=1").send({ name: "hi" }).expect(200)];
205
+ case 1:
206
+ res = _a.sent();
207
+ (0, bun_test_1.expect)(res.body.ok).toBe(true);
208
+ return [2 /*return*/];
209
+ }
210
+ });
211
+ }); });
212
+ (0, bun_test_1.it)("forwards handler errors through next", function () { return __awaiter(void 0, void 0, void 0, function () {
213
+ var app, res;
214
+ return __generator(this, function (_a) {
215
+ switch (_a.label) {
216
+ case 0:
217
+ (0, openApiValidator_1.configureOpenApiValidator)({});
218
+ app = createApp();
219
+ app.post("/test", (0, api_1.asyncHandler)(function () { return __awaiter(void 0, void 0, void 0, function () {
220
+ return __generator(this, function (_a) {
221
+ throw new Error("handler boom");
222
+ });
223
+ }); }, {
224
+ bodySchema: { name: { type: "string" } },
225
+ validate: true,
226
+ }));
227
+ app.use(errorHandler);
228
+ return [4 /*yield*/, (0, supertest_1.default)(app).post("/test").send({ name: "ok" }).expect(500)];
229
+ case 1:
230
+ res = _a.sent();
231
+ (0, bun_test_1.expect)(res.body.error).toBe("handler boom");
232
+ return [2 /*return*/];
233
+ }
234
+ });
235
+ }); });
236
+ });
package/dist/api.d.ts CHANGED
@@ -4,6 +4,7 @@ import { type User } from "./auth";
4
4
  import { type ModelRouterValidationOptions } from "./openApiValidator";
5
5
  import { type RESTPermissions } from "./permissions";
6
6
  import type { PopulatePath } from "./populate";
7
+ import type { RealtimeConfig } from "./realtime/types";
7
8
  import { type TerrenoTransformer } from "./transformers";
8
9
  export type JSONPrimitive = string | number | boolean | null;
9
10
  export interface JSONArray extends Array<JSONValue> {
@@ -193,13 +194,13 @@ export interface ModelRouterOptions<T> {
193
194
  * Throw an APIError to return a 400 with an error message.
194
195
  * @deprecated: Use responseHandler instead.
195
196
  */
196
- postList?: (value: (Document<any, any, any> & T)[], request: express.Request) => Promise<(Document<any, any, any> & T)[]>;
197
+ postList?: (value: (Document<unknown, unknown, unknown> & T)[], request: express.Request) => Promise<(Document<unknown, unknown, unknown> & T)[]>;
197
198
  /**
198
199
  * Serialize an object or list of objects before returning to the client.
199
200
  * This is a good spot to remove sensitive information from the object, such as passwords or API
200
201
  * keys. Throw an APIError to return a 400 with an error message.
201
202
  */
202
- responseHandler?: (value: (Document<any, any, any> & T) | (Document<any, any, any> & T)[], method: "list" | "create" | "read" | "update" | "delete", request: express.Request, options: ModelRouterOptions<T>) => Promise<JSONValue>;
203
+ responseHandler?: (value: (Document<unknown, unknown, unknown> & T) | (Document<unknown, unknown, unknown> & T)[], method: "list" | "create" | "read" | "update" | "delete", request: express.Request, options: ModelRouterOptions<T>) => Promise<JSONValue>;
203
204
  /**
204
205
  * The OpenAPI generator for this server. This is used to generate the OpenAPI documentation.
205
206
  */
@@ -235,6 +236,14 @@ export interface ModelRouterOptions<T> {
235
236
  * This option overrides the global setting for this specific router.
236
237
  */
237
238
  validation?: boolean | ModelRouterValidationOptions;
239
+ /**
240
+ * Enable real-time sync for this model via WebSocket events.
241
+ * When configured, CRUD operations will emit events to connected clients
242
+ * through the RealtimeApp plugin's change stream watcher.
243
+ *
244
+ * Requires the RealtimeApp plugin to be registered with TerrenoApp.
245
+ */
246
+ realtime?: RealtimeConfig;
238
247
  }
239
248
  /**
240
249
  * Registration object returned by modelRouter when called with a path.
@@ -253,7 +262,7 @@ export interface ModelRouterRegistration {
253
262
  /** The Express router containing CRUD endpoints */
254
263
  router: express.Router;
255
264
  /** @internal Rebuilds the router with the openApi instance injected into options */
256
- _buildWithOpenApi: (openApi: any) => express.Router;
265
+ _buildWithOpenApi: (openApi: OpenApiMiddleware) => express.Router;
257
266
  }
258
267
  /**
259
268
  * Create a set of CRUD routes given a Mongoose model and configuration options.
@@ -324,6 +333,8 @@ export interface AsyncHandlerOptions {
324
333
  * }));
325
334
  * ```
326
335
  */
327
- export declare const asyncHandler: (fn: any, options?: AsyncHandlerOptions) => (req: Request, res: Response, next: NextFunction) => void;
336
+ type AsyncHandlerFn = (req: any, res: Response, next: NextFunction) => Promise<unknown> | unknown;
337
+ export declare const asyncHandler: (fn: AsyncHandlerFn, options?: AsyncHandlerOptions) => (req: Request, res: Response, next: NextFunction) => void;
328
338
  export declare const gooseRestRouter: typeof modelRouter;
329
339
  export type GooseRESTOptions<T> = ModelRouterOptions<T>;
340
+ export {};
@@ -39,6 +39,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
39
39
  return (mod && mod.__esModule) ? mod : { "default": mod };
40
40
  };
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
+ // biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
42
43
  var bun_test_1 = require("bun:test");
43
44
  var mongoose_1 = __importDefault(require("mongoose"));
44
45
  var errors_1 = require("./errors");
@@ -55,6 +55,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
55
55
  return (mod && mod.__esModule) ? mod : { "default": mod };
56
56
  };
57
57
  Object.defineProperty(exports, "__esModule", { value: true });
58
+ // biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
58
59
  var bun_test_1 = require("bun:test");
59
60
  var supertest_1 = __importDefault(require("supertest"));
60
61
  var api_1 = require("./api");