@terreno/api 0.13.3 → 0.14.1
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.
- package/dist/__tests__/versionCheckPlugin.test.js +136 -3
- package/dist/api.arrayOperations.test.js +1 -0
- package/dist/api.d.ts +15 -4
- package/dist/api.errors.test.js +1 -0
- package/dist/api.hooks.test.js +1 -0
- package/dist/api.js +153 -104
- package/dist/api.query.test.js +1 -0
- package/dist/api.test.js +174 -0
- package/dist/auth.d.ts +10 -5
- package/dist/auth.js +163 -90
- package/dist/auth.test.js +159 -0
- package/dist/betterAuthApp.test.js +1 -0
- package/dist/betterAuthSetup.d.ts +5 -6
- package/dist/betterAuthSetup.js +30 -17
- package/dist/betterAuthSetup.test.js +1 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.js +257 -0
- package/dist/config.test.d.ts +1 -0
- package/dist/config.test.js +328 -0
- package/dist/configuration.test.js +1 -0
- package/dist/configurationApp.d.ts +1 -1
- package/dist/configurationApp.js +17 -13
- package/dist/configurationPlugin.test.js +1 -0
- package/dist/consentApp.test.js +1 -0
- package/dist/envConfigurationPlugin.d.ts +2 -0
- package/dist/envConfigurationPlugin.js +173 -0
- package/dist/envConfigurationPlugin.test.d.ts +1 -0
- package/dist/envConfigurationPlugin.test.js +322 -0
- package/dist/errors.d.ts +18 -7
- package/dist/errors.js +111 -12
- package/dist/errors.test.js +16 -1
- package/dist/example.js +19 -7
- package/dist/expressServer.d.ts +10 -9
- package/dist/expressServer.js +62 -53
- package/dist/expressServer.test.js +165 -2
- package/dist/githubAuth.d.ts +2 -1
- package/dist/githubAuth.js +41 -26
- package/dist/githubAuth.test.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/logger.d.ts +1 -1
- package/dist/logger.js +42 -20
- package/dist/models/versionConfig.d.ts +2 -0
- package/dist/models/versionConfig.js +8 -0
- package/dist/notifiers/googleChatNotifier.js +14 -16
- package/dist/notifiers/googleChatNotifier.test.js +1 -0
- package/dist/notifiers/slackNotifier.js +16 -14
- package/dist/notifiers/slackNotifier.test.js +41 -3
- package/dist/notifiers/zoomNotifier.js +7 -10
- package/dist/notifiers/zoomNotifier.test.js +1 -0
- package/dist/openApi.d.ts +1 -1
- package/dist/openApi.test.js +1 -0
- package/dist/openApiBuilder.d.ts +39 -6
- package/dist/openApiBuilder.js +1 -31
- package/dist/openApiBuilder.test.js +1 -0
- package/dist/openApiValidator.js +1 -0
- package/dist/openApiValidator.test.js +1 -0
- package/dist/permissions.d.ts +4 -4
- package/dist/permissions.js +67 -65
- package/dist/permissions.middleware.test.js +1 -0
- package/dist/permissions.test.js +1 -0
- package/dist/plugins.d.ts +5 -5
- package/dist/plugins.js +18 -9
- package/dist/plugins.test.js +1 -1
- package/dist/populate.d.ts +15 -8
- package/dist/populate.js +23 -24
- package/dist/populate.test.js +1 -0
- package/dist/realtime/changeStreamWatcher.d.ts +73 -0
- package/dist/realtime/changeStreamWatcher.js +724 -0
- package/dist/realtime/index.d.ts +6 -0
- package/dist/realtime/index.js +27 -0
- package/dist/realtime/queryMatcher.d.ts +14 -0
- package/dist/realtime/queryMatcher.js +250 -0
- package/dist/realtime/queryStore.d.ts +37 -0
- package/dist/realtime/queryStore.js +195 -0
- package/dist/realtime/realtime.test.d.ts +10 -0
- package/dist/realtime/realtime.test.js +3066 -0
- package/dist/realtime/realtimeApp.d.ts +93 -0
- package/dist/realtime/realtimeApp.js +560 -0
- package/dist/realtime/registry.d.ts +40 -0
- package/dist/realtime/registry.js +38 -0
- package/dist/realtime/socketUser.d.ts +10 -0
- package/dist/realtime/socketUser.js +17 -0
- package/dist/realtime/types.d.ts +100 -0
- package/dist/realtime/types.js +2 -0
- package/dist/requestContext.d.ts +37 -0
- package/dist/requestContext.js +344 -0
- package/dist/requestContext.test.d.ts +1 -0
- package/dist/requestContext.test.js +384 -0
- package/dist/terrenoApp.d.ts +8 -0
- package/dist/terrenoApp.js +50 -13
- package/dist/terrenoApp.test.js +194 -21
- package/dist/terrenoPlugin.d.ts +11 -0
- package/dist/tests/bunSetup.js +1 -0
- package/dist/tests.js +1 -1
- package/dist/transformers.d.ts +2 -2
- package/dist/transformers.js +5 -3
- package/dist/transformers.test.js +90 -0
- package/dist/types/consentResponse.d.ts +6 -3
- package/dist/versionCheckPlugin.d.ts +2 -0
- package/dist/versionCheckPlugin.js +18 -12
- package/package.json +4 -2
- package/src/__tests__/versionCheckPlugin.test.ts +94 -3
- package/src/api.arrayOperations.test.ts +1 -0
- package/src/api.errors.test.ts +1 -0
- package/src/api.hooks.test.ts +1 -0
- package/src/api.query.test.ts +1 -0
- package/src/api.test.ts +132 -0
- package/src/api.ts +199 -84
- package/src/auth.test.ts +160 -0
- package/src/auth.ts +120 -50
- package/src/betterAuthApp.test.ts +1 -0
- package/src/betterAuthSetup.test.ts +1 -0
- package/src/betterAuthSetup.ts +59 -22
- package/src/config.test.ts +255 -0
- package/src/config.ts +216 -0
- package/src/configuration.test.ts +1 -0
- package/src/configurationApp.ts +59 -24
- package/src/configurationPlugin.test.ts +1 -0
- package/src/consentApp.test.ts +1 -0
- package/src/envConfigurationPlugin.test.ts +143 -0
- package/src/envConfigurationPlugin.ts +100 -0
- package/src/errors.test.ts +19 -1
- package/src/errors.ts +118 -38
- package/src/example.ts +49 -21
- package/src/express.d.ts +18 -1
- package/src/expressServer.test.ts +147 -2
- package/src/expressServer.ts +80 -50
- package/src/githubAuth.test.ts +1 -0
- package/src/githubAuth.ts +59 -38
- package/src/index.ts +4 -0
- package/src/logger.ts +47 -17
- package/src/models/versionConfig.ts +13 -2
- package/src/notifiers/googleChatNotifier.test.ts +1 -0
- package/src/notifiers/googleChatNotifier.ts +7 -9
- package/src/notifiers/slackNotifier.test.ts +29 -3
- package/src/notifiers/slackNotifier.ts +9 -7
- package/src/notifiers/zoomNotifier.test.ts +1 -0
- package/src/notifiers/zoomNotifier.ts +8 -11
- package/src/openApi.test.ts +1 -0
- package/src/openApi.ts +4 -4
- package/src/openApiBuilder.test.ts +1 -0
- package/src/openApiBuilder.ts +14 -11
- package/src/openApiValidator.test.ts +1 -0
- package/src/openApiValidator.ts +3 -2
- package/src/permissions.middleware.test.ts +1 -0
- package/src/permissions.test.ts +1 -0
- package/src/permissions.ts +30 -25
- package/src/plugins.test.ts +1 -1
- package/src/plugins.ts +21 -14
- package/src/populate.test.ts +1 -0
- package/src/populate.ts +44 -36
- package/src/realtime/changeStreamWatcher.ts +572 -0
- package/src/realtime/index.ts +34 -0
- package/src/realtime/queryMatcher.ts +179 -0
- package/src/realtime/queryStore.ts +132 -0
- package/src/realtime/realtime.test.ts +2465 -0
- package/src/realtime/realtimeApp.ts +478 -0
- package/src/realtime/registry.ts +64 -0
- package/src/realtime/socketUser.ts +25 -0
- package/src/realtime/types.ts +112 -0
- package/src/requestContext.test.ts +321 -0
- package/src/requestContext.ts +368 -0
- package/src/terrenoApp.test.ts +137 -11
- package/src/terrenoApp.ts +64 -17
- package/src/terrenoPlugin.ts +12 -0
- package/src/tests/bunSetup.ts +1 -0
- package/src/tests.ts +7 -2
- package/src/transformers.test.ts +70 -2
- package/src/transformers.ts +15 -7
- package/src/types/consentResponse.ts +8 -10
- package/src/versionCheckPlugin.ts +15 -7
|
@@ -99,6 +99,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
99
99
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
100
100
|
};
|
|
101
101
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
102
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
|
|
102
103
|
var bun_test_1 = require("bun:test");
|
|
103
104
|
var Sentry = __importStar(require("@sentry/bun"));
|
|
104
105
|
var axios_1 = __importDefault(require("axios"));
|
|
@@ -112,11 +112,11 @@ var sendToSlack = function (text_1) {
|
|
|
112
112
|
args_1[_i - 1] = arguments[_i];
|
|
113
113
|
}
|
|
114
114
|
return __awaiter(void 0, __spreadArray([text_1], __read(args_1), false), void 0, function (text, _a) {
|
|
115
|
-
var slackWebhookUrl, slackWebhooksString, slackWebhooks, channel, formattedText, error_1,
|
|
116
|
-
var _b
|
|
117
|
-
var
|
|
118
|
-
return __generator(this, function (
|
|
119
|
-
switch (
|
|
115
|
+
var slackWebhookUrl, slackWebhooksString, slackWebhooks, channel, formattedText, error_1, message;
|
|
116
|
+
var _b;
|
|
117
|
+
var _c = _a === void 0 ? {} : _a, slackChannel = _c.slackChannel, _d = _c.shouldThrow, shouldThrow = _d === void 0 ? false : _d, env = _c.env, url = _c.url;
|
|
118
|
+
return __generator(this, function (_e) {
|
|
119
|
+
switch (_e.label) {
|
|
120
120
|
case 0:
|
|
121
121
|
slackWebhookUrl = url;
|
|
122
122
|
if (!slackWebhookUrl) {
|
|
@@ -130,8 +130,10 @@ var sendToSlack = function (text_1) {
|
|
|
130
130
|
channel = slackChannel !== null && slackChannel !== void 0 ? slackChannel : "default";
|
|
131
131
|
slackWebhookUrl = (_b = slackWebhooks[channel]) !== null && _b !== void 0 ? _b : slackWebhooks.default;
|
|
132
132
|
if (!slackWebhookUrl) {
|
|
133
|
-
Sentry.captureException(new
|
|
134
|
-
|
|
133
|
+
Sentry.captureException(new errors_1.APIError({
|
|
134
|
+
status: 500,
|
|
135
|
+
title: "No webhook url set in env for ".concat(channel, ". Slack message not sent"),
|
|
136
|
+
}));
|
|
135
137
|
return [2 /*return*/];
|
|
136
138
|
}
|
|
137
139
|
}
|
|
@@ -139,24 +141,24 @@ var sendToSlack = function (text_1) {
|
|
|
139
141
|
if (env) {
|
|
140
142
|
formattedText = "[".concat(env.toUpperCase(), "] ").concat(text);
|
|
141
143
|
}
|
|
142
|
-
|
|
144
|
+
_e.label = 1;
|
|
143
145
|
case 1:
|
|
144
|
-
|
|
146
|
+
_e.trys.push([1, 3, , 4]);
|
|
145
147
|
return [4 /*yield*/, axios_1.default.post(slackWebhookUrl, {
|
|
146
148
|
text: formattedText,
|
|
147
149
|
})];
|
|
148
150
|
case 2:
|
|
149
|
-
|
|
151
|
+
_e.sent();
|
|
150
152
|
return [3 /*break*/, 4];
|
|
151
153
|
case 3:
|
|
152
|
-
error_1 =
|
|
153
|
-
|
|
154
|
-
logger_1.logger.error("Error posting to slack: ".concat(
|
|
154
|
+
error_1 = _e.sent();
|
|
155
|
+
message = (0, errors_1.errorMessage)(error_1);
|
|
156
|
+
logger_1.logger.error("Error posting to slack: ".concat(message));
|
|
155
157
|
Sentry.captureException(error_1);
|
|
156
158
|
if (shouldThrow) {
|
|
157
159
|
throw new errors_1.APIError({
|
|
158
160
|
status: 500,
|
|
159
|
-
title: "Error posting to slack: ".concat(
|
|
161
|
+
title: "Error posting to slack: ".concat(message),
|
|
160
162
|
});
|
|
161
163
|
}
|
|
162
164
|
return [3 /*break*/, 4];
|
|
@@ -102,6 +102,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
102
102
|
var bun_test_1 = require("bun:test");
|
|
103
103
|
var Sentry = __importStar(require("@sentry/bun"));
|
|
104
104
|
var axios_1 = __importDefault(require("axios"));
|
|
105
|
+
var errors_1 = require("../errors");
|
|
105
106
|
var slackNotifier_1 = require("./slackNotifier");
|
|
106
107
|
(0, bun_test_1.describe)("sendToSlack", function () {
|
|
107
108
|
var mockAxiosPost;
|
|
@@ -213,8 +214,44 @@ var slackNotifier_1 = require("./slackNotifier");
|
|
|
213
214
|
}
|
|
214
215
|
});
|
|
215
216
|
}); });
|
|
217
|
+
(0, bun_test_1.it)("reports to Sentry and returns early when channel has no webhook and no default", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
218
|
+
var captured;
|
|
219
|
+
return __generator(this, function (_a) {
|
|
220
|
+
switch (_a.label) {
|
|
221
|
+
case 0:
|
|
222
|
+
process.env.SLACK_WEBHOOKS = JSON.stringify({ ops: "https://slack.example/ops" });
|
|
223
|
+
return [4 /*yield*/, (0, slackNotifier_1.sendToSlack)("orphan message", { slackChannel: "alerts" })];
|
|
224
|
+
case 1:
|
|
225
|
+
_a.sent();
|
|
226
|
+
(0, bun_test_1.expect)(mockAxiosPost.mock.calls.length).toBe(0);
|
|
227
|
+
(0, bun_test_1.expect)(Sentry.captureException.mock.calls.length).toBe(1);
|
|
228
|
+
captured = Sentry.captureException.mock
|
|
229
|
+
.calls[0][0];
|
|
230
|
+
(0, bun_test_1.expect)(captured).toBeInstanceOf(errors_1.APIError);
|
|
231
|
+
(0, bun_test_1.expect)(captured.title).toContain("alerts");
|
|
232
|
+
return [2 /*return*/];
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}); });
|
|
236
|
+
(0, bun_test_1.it)("posts directly using the url parameter without env lookup", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
237
|
+
var _a, url, payload;
|
|
238
|
+
return __generator(this, function (_b) {
|
|
239
|
+
switch (_b.label) {
|
|
240
|
+
case 0:
|
|
241
|
+
mockAxiosPost.mockResolvedValue({ status: 200 });
|
|
242
|
+
return [4 /*yield*/, (0, slackNotifier_1.sendToSlack)("direct msg", { url: "https://direct.example/hook" })];
|
|
243
|
+
case 1:
|
|
244
|
+
_b.sent();
|
|
245
|
+
(0, bun_test_1.expect)(mockAxiosPost.mock.calls.length).toBe(1);
|
|
246
|
+
_a = __read(mockAxiosPost.mock.calls[0], 2), url = _a[0], payload = _a[1];
|
|
247
|
+
(0, bun_test_1.expect)(url).toBe("https://direct.example/hook");
|
|
248
|
+
(0, bun_test_1.expect)(payload).toEqual({ text: "direct msg" });
|
|
249
|
+
return [2 /*return*/];
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}); });
|
|
216
253
|
(0, bun_test_1.it)("captures error and throws APIError when shouldThrow=true", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
217
|
-
var error_1;
|
|
254
|
+
var error_1, apiError;
|
|
218
255
|
return __generator(this, function (_a) {
|
|
219
256
|
switch (_a.label) {
|
|
220
257
|
case 0:
|
|
@@ -231,8 +268,9 @@ var slackNotifier_1 = require("./slackNotifier");
|
|
|
231
268
|
throw new Error("Expected sendToSlack to throw APIError");
|
|
232
269
|
case 3:
|
|
233
270
|
error_1 = _a.sent();
|
|
234
|
-
|
|
235
|
-
(0, bun_test_1.expect)(
|
|
271
|
+
apiError = error_1;
|
|
272
|
+
(0, bun_test_1.expect)(apiError.name).toBe("APIError");
|
|
273
|
+
(0, bun_test_1.expect)(apiError.title).toMatch(/Error posting to slack/i);
|
|
236
274
|
return [3 /*break*/, 4];
|
|
237
275
|
case 4:
|
|
238
276
|
(0, bun_test_1.expect)(mockAxiosPost.mock.calls.length).toBe(1);
|
|
@@ -103,7 +103,7 @@ var logger_1 = require("../logger");
|
|
|
103
103
|
* Uses Zoom's rich message format (format=full) with structured header and body.
|
|
104
104
|
*/
|
|
105
105
|
var sendToZoom = function (_a, _b) { return __awaiter(void 0, [_a, _b], void 0, function (_c, _d) {
|
|
106
|
-
var zoomWebhooksString, msg, zoomWebhooks, zoomChannel, zoomWebhookUrl, msg, zoomToken, msg, messageBody, error_1,
|
|
106
|
+
var zoomWebhooksString, msg, zoomWebhooks, zoomChannel, zoomWebhookUrl, msg, zoomToken, msg, messageBody, error_1, message;
|
|
107
107
|
var _e, _f, _g, _h, _j, _k;
|
|
108
108
|
var header = _c.header, body = _c.body, subheader = _c.subheader;
|
|
109
109
|
var channel = _d.channel, _l = _d.shouldThrow, shouldThrow = _l === void 0 ? false : _l, env = _d.env;
|
|
@@ -113,8 +113,7 @@ var sendToZoom = function (_a, _b) { return __awaiter(void 0, [_a, _b], void 0,
|
|
|
113
113
|
zoomWebhooksString = process.env.ZOOM_CHAT_WEBHOOKS;
|
|
114
114
|
if (!zoomWebhooksString) {
|
|
115
115
|
msg = "ZOOM_CHAT_WEBHOOKS not set. Zoom message not sent";
|
|
116
|
-
Sentry.captureException(new
|
|
117
|
-
logger_1.logger.error(msg);
|
|
116
|
+
Sentry.captureException(new errors_1.APIError({ status: 500, title: msg }));
|
|
118
117
|
return [2 /*return*/];
|
|
119
118
|
}
|
|
120
119
|
zoomWebhooks = JSON.parse(zoomWebhooksString !== null && zoomWebhooksString !== void 0 ? zoomWebhooksString : "{}");
|
|
@@ -122,15 +121,13 @@ var sendToZoom = function (_a, _b) { return __awaiter(void 0, [_a, _b], void 0,
|
|
|
122
121
|
zoomWebhookUrl = (_f = (_e = zoomWebhooks[zoomChannel]) === null || _e === void 0 ? void 0 : _e.channel) !== null && _f !== void 0 ? _f : (_g = zoomWebhooks.default) === null || _g === void 0 ? void 0 : _g.channel;
|
|
123
122
|
if (!zoomWebhookUrl) {
|
|
124
123
|
msg = "No webhook url set in env for ".concat(zoomChannel, ". Zoom message not sent");
|
|
125
|
-
Sentry.captureException(new
|
|
126
|
-
logger_1.logger.error(msg);
|
|
124
|
+
Sentry.captureException(new errors_1.APIError({ status: 500, title: msg }));
|
|
127
125
|
return [2 /*return*/];
|
|
128
126
|
}
|
|
129
127
|
zoomToken = (_j = (_h = zoomWebhooks[zoomChannel]) === null || _h === void 0 ? void 0 : _h.verificationToken) !== null && _j !== void 0 ? _j : (_k = zoomWebhooks.default) === null || _k === void 0 ? void 0 : _k.verificationToken;
|
|
130
128
|
if (!zoomToken) {
|
|
131
129
|
msg = "No verification token set in env for ".concat(zoomChannel, ". Zoom message not sent");
|
|
132
|
-
Sentry.captureException(new
|
|
133
|
-
logger_1.logger.error(msg);
|
|
130
|
+
Sentry.captureException(new errors_1.APIError({ status: 500, title: msg }));
|
|
134
131
|
return [2 /*return*/];
|
|
135
132
|
}
|
|
136
133
|
messageBody = {
|
|
@@ -163,13 +160,13 @@ var sendToZoom = function (_a, _b) { return __awaiter(void 0, [_a, _b], void 0,
|
|
|
163
160
|
return [3 /*break*/, 4];
|
|
164
161
|
case 3:
|
|
165
162
|
error_1 = _m.sent();
|
|
166
|
-
|
|
167
|
-
logger_1.logger.error("Error posting to Zoom: ".concat(
|
|
163
|
+
message = (0, errors_1.errorMessage)(error_1);
|
|
164
|
+
logger_1.logger.error("Error posting to Zoom: ".concat(message));
|
|
168
165
|
Sentry.captureException(error_1);
|
|
169
166
|
if (shouldThrow) {
|
|
170
167
|
throw new errors_1.APIError({
|
|
171
168
|
status: 500,
|
|
172
|
-
title: "Error posting to Zoom: ".concat(
|
|
169
|
+
title: "Error posting to Zoom: ".concat(message),
|
|
173
170
|
});
|
|
174
171
|
}
|
|
175
172
|
return [3 /*break*/, 4];
|
|
@@ -99,6 +99,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
99
99
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
100
100
|
};
|
|
101
101
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
102
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
|
|
102
103
|
var bun_test_1 = require("bun:test");
|
|
103
104
|
var Sentry = __importStar(require("@sentry/bun"));
|
|
104
105
|
var axios_1 = __importDefault(require("axios"));
|
package/dist/openApi.d.ts
CHANGED
|
@@ -58,4 +58,4 @@ export declare function listOpenApiMiddleware<T>(model: Model<T>, options: Parti
|
|
|
58
58
|
export declare function createOpenApiMiddleware<T>(model: Model<T>, options: Partial<ModelRouterOptions<T>>): express.RequestHandler;
|
|
59
59
|
export declare function patchOpenApiMiddleware<T>(model: Model<T>, options: Partial<ModelRouterOptions<T>>): express.RequestHandler;
|
|
60
60
|
export declare function deleteOpenApiMiddleware<T>(model: Model<T>, options: Partial<ModelRouterOptions<T>>): express.RequestHandler;
|
|
61
|
-
export declare function readOpenApiMiddleware<T>(options: Partial<ModelRouterOptions<T>>, properties:
|
|
61
|
+
export declare function readOpenApiMiddleware<T>(options: Partial<ModelRouterOptions<T>>, properties: Record<string, unknown>, required: string[], queryParameters: Array<Record<string, unknown>>): express.RequestHandler;
|
package/dist/openApi.test.js
CHANGED
|
@@ -66,6 +66,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
66
66
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
67
67
|
};
|
|
68
68
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
69
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
|
|
69
70
|
var bun_test_1 = require("bun:test");
|
|
70
71
|
var supertest_1 = __importDefault(require("supertest"));
|
|
71
72
|
var api_1 = require("./api");
|
package/dist/openApiBuilder.d.ts
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAPI Middleware Builder
|
|
3
|
+
*
|
|
4
|
+
* This module provides a fluent builder pattern for constructing OpenAPI middleware
|
|
5
|
+
* for Express routes that don't directly map to Mongoose models. It allows you to
|
|
6
|
+
* define custom API documentation with full control over request/response schemas.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import {createOpenApiBuilder} from "./openApiBuilder";
|
|
13
|
+
*
|
|
14
|
+
* // Create middleware with custom documentation
|
|
15
|
+
* const middleware = createOpenApiBuilder(options)
|
|
16
|
+
* .withTags(["users"])
|
|
17
|
+
* .withSummary("Get user statistics")
|
|
18
|
+
* .withDescription("Returns aggregated statistics for the current user")
|
|
19
|
+
* .withQueryParameter("period", {type: "string"}, {
|
|
20
|
+
* description: "Time period for statistics",
|
|
21
|
+
* required: false,
|
|
22
|
+
* })
|
|
23
|
+
* .withResponse<{count: number; average: number}>(200, {
|
|
24
|
+
* count: {type: "number", description: "Total count"},
|
|
25
|
+
* average: {type: "number", description: "Average value"},
|
|
26
|
+
* })
|
|
27
|
+
* .build();
|
|
28
|
+
*
|
|
29
|
+
* router.get("/stats", middleware, statsHandler);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
import type express from "express";
|
|
1
33
|
import type { ModelRouterOptions } from "./api";
|
|
2
34
|
/**
|
|
3
35
|
* Defines a property within an OpenAPI schema.
|
|
@@ -68,7 +100,7 @@ export interface OpenApiSchemaProperty {
|
|
|
68
100
|
* };
|
|
69
101
|
* ```
|
|
70
102
|
*/
|
|
71
|
-
export
|
|
103
|
+
export interface OpenApiSchema {
|
|
72
104
|
/** The JSON Schema type (typically "object" or "array") */
|
|
73
105
|
type: string;
|
|
74
106
|
/** Property definitions for object types */
|
|
@@ -79,7 +111,8 @@ export type OpenApiSchema = {
|
|
|
79
111
|
items?: OpenApiSchemaProperty;
|
|
80
112
|
/** Schema for additional properties or boolean to allow/disallow them */
|
|
81
113
|
additionalProperties?: OpenApiSchemaProperty | boolean;
|
|
82
|
-
|
|
114
|
+
[key: string]: unknown;
|
|
115
|
+
}
|
|
83
116
|
/**
|
|
84
117
|
* Defines a parameter in an OpenAPI operation.
|
|
85
118
|
*
|
|
@@ -159,7 +192,7 @@ export interface OpenApiResponse {
|
|
|
159
192
|
*/
|
|
160
193
|
export interface OpenApiBuildResult {
|
|
161
194
|
/** The OpenAPI documentation middleware */
|
|
162
|
-
middleware:
|
|
195
|
+
middleware: express.RequestHandler;
|
|
163
196
|
/** Request body schema if defined */
|
|
164
197
|
bodySchema?: Record<string, OpenApiSchemaProperty>;
|
|
165
198
|
/** Query parameter schemas if defined */
|
|
@@ -275,7 +308,7 @@ export declare class OpenApiMiddlewareBuilder {
|
|
|
275
308
|
* });
|
|
276
309
|
* ```
|
|
277
310
|
*/
|
|
278
|
-
withRequestBody<T extends Record<string,
|
|
311
|
+
withRequestBody<T extends Record<string, unknown>>(schema: {
|
|
279
312
|
[K in keyof T]: OpenApiSchemaProperty;
|
|
280
313
|
}, options?: {
|
|
281
314
|
required?: boolean;
|
|
@@ -306,7 +339,7 @@ export declare class OpenApiMiddlewareBuilder {
|
|
|
306
339
|
* builder.withResponse(204, "No content");
|
|
307
340
|
* ```
|
|
308
341
|
*/
|
|
309
|
-
withResponse<T extends Record<string,
|
|
342
|
+
withResponse<T extends Record<string, unknown>>(statusCode: number, schema: {
|
|
310
343
|
[K in keyof T]: OpenApiSchemaProperty;
|
|
311
344
|
} | string, options?: {
|
|
312
345
|
description?: string;
|
|
@@ -334,7 +367,7 @@ export declare class OpenApiMiddlewareBuilder {
|
|
|
334
367
|
* }, {description: "List of users"});
|
|
335
368
|
* ```
|
|
336
369
|
*/
|
|
337
|
-
withArrayResponse<T extends Record<string,
|
|
370
|
+
withArrayResponse<T extends Record<string, unknown>>(statusCode: number, itemSchema: {
|
|
338
371
|
[K in keyof T]: OpenApiSchemaProperty;
|
|
339
372
|
}, options?: {
|
|
340
373
|
description?: string;
|
package/dist/openApiBuilder.js
CHANGED
|
@@ -31,37 +31,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
31
31
|
};
|
|
32
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
33
|
exports.createOpenApiBuilder = exports.OpenApiMiddlewareBuilder = void 0;
|
|
34
|
-
/**
|
|
35
|
-
* OpenAPI Middleware Builder
|
|
36
|
-
*
|
|
37
|
-
* This module provides a fluent builder pattern for constructing OpenAPI middleware
|
|
38
|
-
* for Express routes that don't directly map to Mongoose models. It allows you to
|
|
39
|
-
* define custom API documentation with full control over request/response schemas.
|
|
40
|
-
*
|
|
41
|
-
* @packageDocumentation
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* ```typescript
|
|
45
|
-
* import {createOpenApiBuilder} from "./openApiBuilder";
|
|
46
|
-
*
|
|
47
|
-
* // Create middleware with custom documentation
|
|
48
|
-
* const middleware = createOpenApiBuilder(options)
|
|
49
|
-
* .withTags(["users"])
|
|
50
|
-
* .withSummary("Get user statistics")
|
|
51
|
-
* .withDescription("Returns aggregated statistics for the current user")
|
|
52
|
-
* .withQueryParameter("period", {type: "string"}, {
|
|
53
|
-
* description: "Time period for statistics",
|
|
54
|
-
* required: false,
|
|
55
|
-
* })
|
|
56
|
-
* .withResponse<{count: number; average: number}>(200, {
|
|
57
|
-
* count: {type: "number", description: "Total count"},
|
|
58
|
-
* average: {type: "number", description: "Average value"},
|
|
59
|
-
* })
|
|
60
|
-
* .build();
|
|
61
|
-
*
|
|
62
|
-
* router.get("/stats", middleware, statsHandler);
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
65
34
|
var merge_1 = __importDefault(require("lodash/merge"));
|
|
66
35
|
var logger_1 = require("./logger");
|
|
67
36
|
var openApi_1 = require("./openApi");
|
|
@@ -469,6 +438,7 @@ var OpenApiMiddlewareBuilder = /** @class */ (function () {
|
|
|
469
438
|
* router.get("/users/:id", middleware, getUserHandler);
|
|
470
439
|
* ```
|
|
471
440
|
*/
|
|
441
|
+
// biome-ignore lint/suspicious/noExplicitAny: returns either a single RequestHandler or an array depending on validation config — callers spread or invoke
|
|
472
442
|
OpenApiMiddlewareBuilder.prototype.build = function () {
|
|
473
443
|
var _c, _d, _e, _f, _g;
|
|
474
444
|
var noop = function (_a, _b, next) { return next(); };
|
|
@@ -50,6 +50,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
50
50
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
51
51
|
};
|
|
52
52
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
|
|
53
54
|
var bun_test_1 = require("bun:test");
|
|
54
55
|
var supertest_1 = __importDefault(require("supertest"));
|
|
55
56
|
var api_1 = require("./api");
|
package/dist/openApiValidator.js
CHANGED
|
@@ -191,6 +191,7 @@ var getAjvInstance = function () {
|
|
|
191
191
|
useDefaults: true,
|
|
192
192
|
validateSchema: false,
|
|
193
193
|
});
|
|
194
|
+
// biome-ignore lint/suspicious/noExplicitAny: ajv-formats has a known type compat issue with AJV instances
|
|
194
195
|
(0, ajv_formats_1.default)(instance);
|
|
195
196
|
ajvCache.set(key, instance);
|
|
196
197
|
}
|
|
@@ -72,6 +72,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
72
72
|
return to.concat(ar || Array.prototype.slice.call(from));
|
|
73
73
|
};
|
|
74
74
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
75
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
|
|
75
76
|
var bun_test_1 = require("bun:test");
|
|
76
77
|
var api_1 = require("./api");
|
|
77
78
|
var auth_1 = require("./auth");
|
package/dist/permissions.d.ts
CHANGED
|
@@ -19,8 +19,8 @@ export declare const Permissions: {
|
|
|
19
19
|
IsAny: () => boolean;
|
|
20
20
|
IsAuthenticated: (_method: RESTMethod, user?: User) => boolean;
|
|
21
21
|
IsAuthenticatedOrReadOnly: (method: RESTMethod, user?: User) => boolean;
|
|
22
|
-
IsOwner: (_method: RESTMethod, user?: User, obj?:
|
|
23
|
-
IsOwnerOrReadOnly: (method: RESTMethod, user?: User, obj?:
|
|
22
|
+
IsOwner: (_method: RESTMethod, user?: User, obj?: unknown) => boolean;
|
|
23
|
+
IsOwnerOrReadOnly: (method: RESTMethod, user?: User, obj?: unknown) => boolean;
|
|
24
24
|
};
|
|
25
|
-
export declare
|
|
26
|
-
export declare
|
|
25
|
+
export declare const checkPermissions: <T>(method: RESTMethod, permissions: PermissionMethod<T>[], user?: User, obj?: T) => Promise<boolean>;
|
|
26
|
+
export declare const permissionMiddleware: <T>(model: Model<T>, options: Pick<ModelRouterOptions<T>, "permissions" | "populatePaths">) => (req: express.Request, _res: express.Response, next: NextFunction) => Promise<void>;
|
package/dist/permissions.js
CHANGED
|
@@ -83,10 +83,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
83
83
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
84
84
|
};
|
|
85
85
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
86
|
-
exports.Permissions = exports.OwnerQueryFilter = void 0;
|
|
87
|
-
exports.checkPermissions = checkPermissions;
|
|
88
|
-
exports.permissionMiddleware = permissionMiddleware;
|
|
89
|
-
// Defaults closed
|
|
86
|
+
exports.permissionMiddleware = exports.checkPermissions = exports.Permissions = exports.OwnerQueryFilter = void 0;
|
|
90
87
|
var Sentry = __importStar(require("@sentry/bun"));
|
|
91
88
|
var mongoose_1 = __importDefault(require("mongoose"));
|
|
92
89
|
var api_1 = require("./api");
|
|
@@ -96,7 +93,6 @@ var OwnerQueryFilter = function (user) {
|
|
|
96
93
|
if (user) {
|
|
97
94
|
return { ownerId: user === null || user === void 0 ? void 0 : user.id };
|
|
98
95
|
}
|
|
99
|
-
// Return a null, so we know to return no results.
|
|
100
96
|
return null;
|
|
101
97
|
};
|
|
102
98
|
exports.OwnerQueryFilter = OwnerQueryFilter;
|
|
@@ -131,8 +127,10 @@ exports.Permissions = {
|
|
|
131
127
|
if (user === null || user === void 0 ? void 0 : user.admin) {
|
|
132
128
|
return true;
|
|
133
129
|
}
|
|
134
|
-
var
|
|
135
|
-
|
|
130
|
+
var withOwner = obj;
|
|
131
|
+
var ownerObj = withOwner.ownerId;
|
|
132
|
+
var ownerId = (_a = ownerObj === null || ownerObj === void 0 ? void 0 : ownerObj._id) !== null && _a !== void 0 ? _a : withOwner.ownerId;
|
|
133
|
+
return Boolean((user === null || user === void 0 ? void 0 : user.id) && ownerId && String(ownerId) === String(user === null || user === void 0 ? void 0 : user.id));
|
|
136
134
|
},
|
|
137
135
|
IsOwnerOrReadOnly: function (method, user, obj) {
|
|
138
136
|
// When checking if we can possibly perform the action, return true.
|
|
@@ -142,61 +140,59 @@ exports.Permissions = {
|
|
|
142
140
|
if (user === null || user === void 0 ? void 0 : user.admin) {
|
|
143
141
|
return true;
|
|
144
142
|
}
|
|
145
|
-
|
|
143
|
+
var withOwner = obj;
|
|
144
|
+
if ((user === null || user === void 0 ? void 0 : user.id) && withOwner.ownerId && String(withOwner.ownerId) === String(user === null || user === void 0 ? void 0 : user.id)) {
|
|
146
145
|
return true;
|
|
147
146
|
}
|
|
148
147
|
return method === "list" || method === "read";
|
|
149
148
|
},
|
|
150
149
|
};
|
|
151
|
-
function
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
case 8: return [2 /*return*/, anyTrue];
|
|
190
|
-
}
|
|
191
|
-
});
|
|
150
|
+
var checkPermissions = function (method, permissions, user, obj) { return __awaiter(void 0, void 0, void 0, function () {
|
|
151
|
+
var anyTrue, permissions_1, permissions_1_1, perm, e_1_1;
|
|
152
|
+
var e_1, _a;
|
|
153
|
+
return __generator(this, function (_b) {
|
|
154
|
+
switch (_b.label) {
|
|
155
|
+
case 0:
|
|
156
|
+
anyTrue = false;
|
|
157
|
+
_b.label = 1;
|
|
158
|
+
case 1:
|
|
159
|
+
_b.trys.push([1, 6, 7, 8]);
|
|
160
|
+
permissions_1 = __values(permissions), permissions_1_1 = permissions_1.next();
|
|
161
|
+
_b.label = 2;
|
|
162
|
+
case 2:
|
|
163
|
+
if (!!permissions_1_1.done) return [3 /*break*/, 5];
|
|
164
|
+
perm = permissions_1_1.value;
|
|
165
|
+
return [4 /*yield*/, perm(method, user, obj)];
|
|
166
|
+
case 3:
|
|
167
|
+
if (!(_b.sent())) {
|
|
168
|
+
return [2 /*return*/, false];
|
|
169
|
+
}
|
|
170
|
+
anyTrue = true;
|
|
171
|
+
_b.label = 4;
|
|
172
|
+
case 4:
|
|
173
|
+
permissions_1_1 = permissions_1.next();
|
|
174
|
+
return [3 /*break*/, 2];
|
|
175
|
+
case 5: return [3 /*break*/, 8];
|
|
176
|
+
case 6:
|
|
177
|
+
e_1_1 = _b.sent();
|
|
178
|
+
e_1 = { error: e_1_1 };
|
|
179
|
+
return [3 /*break*/, 8];
|
|
180
|
+
case 7:
|
|
181
|
+
try {
|
|
182
|
+
if (permissions_1_1 && !permissions_1_1.done && (_a = permissions_1.return)) _a.call(permissions_1);
|
|
183
|
+
}
|
|
184
|
+
finally { if (e_1) throw e_1.error; }
|
|
185
|
+
return [7 /*endfinally*/];
|
|
186
|
+
case 8: return [2 /*return*/, anyTrue];
|
|
187
|
+
}
|
|
192
188
|
});
|
|
193
|
-
}
|
|
189
|
+
}); };
|
|
190
|
+
exports.checkPermissions = checkPermissions;
|
|
194
191
|
// Check the permissions for a given model and method. If the method is a read, update, or delete,
|
|
195
192
|
// finds the relevant object, checks the permissions, and attaches the object to the request as
|
|
196
193
|
// req.obj.
|
|
197
|
-
function
|
|
198
|
-
|
|
199
|
-
return function (req, _res, next) { return __awaiter(_this, void 0, void 0, function () {
|
|
194
|
+
var permissionMiddleware = function (model, options) {
|
|
195
|
+
return function (req, _res, next) { return __awaiter(void 0, void 0, void 0, function () {
|
|
200
196
|
var method, reqMethod, builtQuery, populatedQuery, data, error_1, hiddenDoc, error, reason, error, error_2;
|
|
201
197
|
var _a, _b;
|
|
202
198
|
return __generator(this, function (_c) {
|
|
@@ -233,7 +229,7 @@ function permissionMiddleware(model, options) {
|
|
|
233
229
|
title: "Method ".concat(req.method, " not allowed"),
|
|
234
230
|
});
|
|
235
231
|
}
|
|
236
|
-
return [4 /*yield*/, checkPermissions(method, options.permissions[method], req.user)];
|
|
232
|
+
return [4 /*yield*/, (0, exports.checkPermissions)(method, options.permissions[method], req.user)];
|
|
237
233
|
case 2:
|
|
238
234
|
// All methods check for permissions.
|
|
239
235
|
if (!(_c.sent())) {
|
|
@@ -247,14 +243,16 @@ function permissionMiddleware(model, options) {
|
|
|
247
243
|
return [2 /*return*/, next()];
|
|
248
244
|
}
|
|
249
245
|
builtQuery = model.findById(req.params.id);
|
|
250
|
-
populatedQuery = (0, api_1.addPopulateToQuery)(
|
|
246
|
+
populatedQuery = (0, api_1.addPopulateToQuery)(
|
|
247
|
+
// biome-ignore lint/suspicious/noExplicitAny: Query types vary based on populate paths
|
|
248
|
+
builtQuery, options.populatePaths);
|
|
251
249
|
data = void 0;
|
|
252
250
|
_c.label = 3;
|
|
253
251
|
case 3:
|
|
254
252
|
_c.trys.push([3, 5, , 6]);
|
|
255
253
|
return [4 /*yield*/, populatedQuery.exec()];
|
|
256
254
|
case 4:
|
|
257
|
-
data = _c.sent();
|
|
255
|
+
data = (_c.sent());
|
|
258
256
|
return [3 /*break*/, 6];
|
|
259
257
|
case 5:
|
|
260
258
|
error_1 = _c.sent();
|
|
@@ -279,13 +277,16 @@ function permissionMiddleware(model, options) {
|
|
|
279
277
|
error.meta = undefined;
|
|
280
278
|
throw error;
|
|
281
279
|
}
|
|
282
|
-
reason =
|
|
283
|
-
|
|
284
|
-
:
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
280
|
+
reason = null;
|
|
281
|
+
if (hiddenDoc.deleted) {
|
|
282
|
+
reason = { deleted: "true" };
|
|
283
|
+
}
|
|
284
|
+
else if (hiddenDoc.disabled) {
|
|
285
|
+
reason = { disabled: "true" };
|
|
286
|
+
}
|
|
287
|
+
else if (hiddenDoc.archived) {
|
|
288
|
+
reason = { archived: "true" };
|
|
289
|
+
}
|
|
289
290
|
// If no reason found, treat as not found
|
|
290
291
|
if (!reason) {
|
|
291
292
|
error = new errors_1.APIError({
|
|
@@ -302,7 +303,7 @@ function permissionMiddleware(model, options) {
|
|
|
302
303
|
status: 404,
|
|
303
304
|
title: "Document ".concat(req.params.id, " not found for model ").concat(model.modelName),
|
|
304
305
|
});
|
|
305
|
-
case 8: return [4 /*yield*/, checkPermissions(method, options.permissions[method], req.user, data)];
|
|
306
|
+
case 8: return [4 /*yield*/, (0, exports.checkPermissions)(method, options.permissions[method], req.user, data)];
|
|
306
307
|
case 9:
|
|
307
308
|
if (!(_c.sent())) {
|
|
308
309
|
throw new errors_1.APIError({
|
|
@@ -320,4 +321,5 @@ function permissionMiddleware(model, options) {
|
|
|
320
321
|
}
|
|
321
322
|
});
|
|
322
323
|
}); };
|
|
323
|
-
}
|
|
324
|
+
};
|
|
325
|
+
exports.permissionMiddleware = permissionMiddleware;
|
|
@@ -96,6 +96,7 @@ var __read = (this && this.__read) || function (o, n) {
|
|
|
96
96
|
return ar;
|
|
97
97
|
};
|
|
98
98
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
99
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
|
|
99
100
|
var bun_test_1 = require("bun:test");
|
|
100
101
|
var Sentry = __importStar(require("@sentry/bun"));
|
|
101
102
|
var errors_1 = require("./errors");
|
package/dist/permissions.test.js
CHANGED
|
@@ -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");
|