@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
package/dist/errors.js
CHANGED
|
@@ -58,11 +58,27 @@ var __values = (this && this.__values) || function(o) {
|
|
|
58
58
|
};
|
|
59
59
|
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
60
60
|
};
|
|
61
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
62
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
63
|
+
if (!m) return o;
|
|
64
|
+
var i = m.call(o), r, ar = [], e;
|
|
65
|
+
try {
|
|
66
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
67
|
+
}
|
|
68
|
+
catch (error) { e = { error: error }; }
|
|
69
|
+
finally {
|
|
70
|
+
try {
|
|
71
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
72
|
+
}
|
|
73
|
+
finally { if (e) throw e.error; }
|
|
74
|
+
}
|
|
75
|
+
return ar;
|
|
76
|
+
};
|
|
61
77
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
62
|
-
exports.apiErrorMiddleware = exports.apiUnauthorizedMiddleware = exports.getAPIErrorBody = exports.getDisableExternalErrorTracking = exports.isAPIError = exports.errorsPlugin = exports.APIError = void 0;
|
|
78
|
+
exports.apiFallthroughErrorMiddleware = exports.apiErrorMiddleware = exports.mongooseErrorToAPIError = exports.apiUnauthorizedMiddleware = exports.getAPIErrorBody = exports.getDisableExternalErrorTracking = exports.errorStack = exports.errorMessage = exports.isAPIError = exports.errorsPlugin = exports.APIError = void 0;
|
|
63
79
|
// https://jsonapi.org/format/#errors
|
|
64
80
|
var Sentry = __importStar(require("@sentry/bun"));
|
|
65
|
-
var mongoose_1 = require("mongoose");
|
|
81
|
+
var mongoose_1 = __importStar(require("mongoose"));
|
|
66
82
|
var logger_1 = require("./logger");
|
|
67
83
|
/**
|
|
68
84
|
* APIError is a simple way to throw an error in an API route and control what is shown and the
|
|
@@ -81,9 +97,10 @@ var logger_1 = require("./logger");
|
|
|
81
97
|
var APIError = /** @class */ (function (_super) {
|
|
82
98
|
__extends(APIError, _super);
|
|
83
99
|
function APIError(data) {
|
|
84
|
-
var
|
|
100
|
+
var _this = this;
|
|
101
|
+
var errorStack = data.error instanceof Error && data.error.stack ? "\n".concat(data.error.stack) : "";
|
|
85
102
|
// Include details in when the error is printed to the console or sent to Sentry.
|
|
86
|
-
|
|
103
|
+
_this = _super.call(this, "".concat(data.title).concat(data.detail ? ": ".concat(data.detail) : "").concat(errorStack)) || this;
|
|
87
104
|
_this.name = "APIError";
|
|
88
105
|
var title = data.title, id = data.id, links = data.links, status = data.status, code = data.code, detail = data.detail, source = data.source, meta = data.meta, fields = data.fields, error = data.error;
|
|
89
106
|
if (!status) {
|
|
@@ -106,7 +123,8 @@ var APIError = /** @class */ (function (_super) {
|
|
|
106
123
|
_this.meta.fields = fields;
|
|
107
124
|
}
|
|
108
125
|
_this.error = error;
|
|
109
|
-
var
|
|
126
|
+
var dataErrorStack = data.error instanceof Error && data.error.stack ? "\n".concat(data.error.stack) : "";
|
|
127
|
+
var logMessage = "APIError(".concat(status, "): ").concat(title, " ").concat(detail ? detail : "").concat(dataErrorStack);
|
|
110
128
|
if (data.disableExternalErrorTracking) {
|
|
111
129
|
logger_1.logger.warn(logMessage);
|
|
112
130
|
}
|
|
@@ -132,7 +150,10 @@ var errorsPlugin = function (schema) {
|
|
|
132
150
|
about: { description: "Link to documentation about this error", type: String },
|
|
133
151
|
type: { description: "Link describing the error type", type: String },
|
|
134
152
|
},
|
|
135
|
-
meta: {
|
|
153
|
+
meta: {
|
|
154
|
+
description: "Non-standard meta information about the error",
|
|
155
|
+
type: mongoose_1.Schema.Types.Mixed,
|
|
156
|
+
},
|
|
136
157
|
source: {
|
|
137
158
|
header: { description: "HTTP header that caused the error", type: String },
|
|
138
159
|
parameter: { description: "Query parameter that caused the error", type: String },
|
|
@@ -143,14 +164,30 @@ var errorsPlugin = function (schema) {
|
|
|
143
164
|
},
|
|
144
165
|
status: { description: "HTTP status code for this error", type: Number },
|
|
145
166
|
title: { description: "Short summary of the error", required: true, type: String },
|
|
146
|
-
});
|
|
167
|
+
}, { _id: false, strict: "throw" });
|
|
147
168
|
schema.add({ apiErrors: errorSchema });
|
|
148
169
|
};
|
|
149
170
|
exports.errorsPlugin = errorsPlugin;
|
|
150
171
|
var isAPIError = function (error) {
|
|
151
|
-
return error.name === "APIError";
|
|
172
|
+
return error instanceof Error && error.name === "APIError";
|
|
152
173
|
};
|
|
153
174
|
exports.isAPIError = isAPIError;
|
|
175
|
+
/** Extract a human-readable message from an unknown error. */
|
|
176
|
+
var errorMessage = function (error) {
|
|
177
|
+
if (error instanceof Error) {
|
|
178
|
+
return error.message;
|
|
179
|
+
}
|
|
180
|
+
return String(error);
|
|
181
|
+
};
|
|
182
|
+
exports.errorMessage = errorMessage;
|
|
183
|
+
/** Extract a stack trace string from an unknown error. */
|
|
184
|
+
var errorStack = function (error) {
|
|
185
|
+
if (error instanceof Error && error.stack) {
|
|
186
|
+
return error.stack;
|
|
187
|
+
}
|
|
188
|
+
return String(error);
|
|
189
|
+
};
|
|
190
|
+
exports.errorStack = errorStack;
|
|
154
191
|
/**
|
|
155
192
|
* Safely extracts the disableExternalErrorTracking property from an error.
|
|
156
193
|
* Works with both APIError instances and regular Error objects that may have
|
|
@@ -174,6 +211,7 @@ exports.getDisableExternalErrorTracking = getDisableExternalErrorTracking;
|
|
|
174
211
|
var getAPIErrorBody = function (error) {
|
|
175
212
|
var e_1, _a;
|
|
176
213
|
var errorData = { status: error.status, title: error.title };
|
|
214
|
+
var indexable = error;
|
|
177
215
|
try {
|
|
178
216
|
for (var _b = __values([
|
|
179
217
|
"id",
|
|
@@ -186,8 +224,8 @@ var getAPIErrorBody = function (error) {
|
|
|
186
224
|
"disableExternalErrorTracking",
|
|
187
225
|
]), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
188
226
|
var key = _c.value;
|
|
189
|
-
if (
|
|
190
|
-
errorData[key] =
|
|
227
|
+
if (indexable[key]) {
|
|
228
|
+
errorData[key] = indexable[key];
|
|
191
229
|
}
|
|
192
230
|
}
|
|
193
231
|
}
|
|
@@ -211,15 +249,76 @@ var apiUnauthorizedMiddleware = function (err, _req, res, next) {
|
|
|
211
249
|
}
|
|
212
250
|
};
|
|
213
251
|
exports.apiUnauthorizedMiddleware = apiUnauthorizedMiddleware;
|
|
252
|
+
/**
|
|
253
|
+
* Converts Mongoose validation/cast errors into client-friendly APIErrors.
|
|
254
|
+
*/
|
|
255
|
+
var mongooseErrorToAPIError = function (err) {
|
|
256
|
+
var e_2, _a, _b;
|
|
257
|
+
var _c, _d;
|
|
258
|
+
if (err instanceof mongoose_1.default.Error.ValidationError) {
|
|
259
|
+
var fields = {};
|
|
260
|
+
try {
|
|
261
|
+
for (var _e = __values(Object.entries(err.errors)), _f = _e.next(); !_f.done; _f = _e.next()) {
|
|
262
|
+
var _g = __read(_f.value, 2), path = _g[0], subErr = _g[1];
|
|
263
|
+
fields[path] = subErr.message;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
267
|
+
finally {
|
|
268
|
+
try {
|
|
269
|
+
if (_f && !_f.done && (_a = _e.return)) _a.call(_e);
|
|
270
|
+
}
|
|
271
|
+
finally { if (e_2) throw e_2.error; }
|
|
272
|
+
}
|
|
273
|
+
return new APIError({
|
|
274
|
+
detail: err.message,
|
|
275
|
+
disableExternalErrorTracking: true,
|
|
276
|
+
fields: fields,
|
|
277
|
+
status: 400,
|
|
278
|
+
title: "Validation failed",
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
if (err instanceof mongoose_1.default.Error.CastError) {
|
|
282
|
+
var path = (_c = err.path) !== null && _c !== void 0 ? _c : "field";
|
|
283
|
+
return new APIError({
|
|
284
|
+
detail: "Invalid value for ".concat(path),
|
|
285
|
+
disableExternalErrorTracking: true,
|
|
286
|
+
fields: (_b = {},
|
|
287
|
+
_b[path] = "Expected ".concat((_d = err.kind) !== null && _d !== void 0 ? _d : "a valid value", ", got ").concat(JSON.stringify(err.value)),
|
|
288
|
+
_b),
|
|
289
|
+
status: 400,
|
|
290
|
+
title: "Validation failed",
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return null;
|
|
294
|
+
};
|
|
295
|
+
exports.mongooseErrorToAPIError = mongooseErrorToAPIError;
|
|
214
296
|
var apiErrorMiddleware = function (err, _req, res, next) {
|
|
215
297
|
if ((0, exports.isAPIError)(err)) {
|
|
216
298
|
if (!err.disableExternalErrorTracking) {
|
|
217
299
|
Sentry.captureException(err);
|
|
218
300
|
}
|
|
219
301
|
res.status(err.status).json((0, exports.getAPIErrorBody)(err)).send();
|
|
302
|
+
return;
|
|
220
303
|
}
|
|
221
|
-
|
|
222
|
-
|
|
304
|
+
var mongooseError = (0, exports.mongooseErrorToAPIError)(err);
|
|
305
|
+
if (mongooseError) {
|
|
306
|
+
res.status(mongooseError.status).json((0, exports.getAPIErrorBody)(mongooseError)).send();
|
|
307
|
+
return;
|
|
223
308
|
}
|
|
309
|
+
next(err);
|
|
224
310
|
};
|
|
225
311
|
exports.apiErrorMiddleware = apiErrorMiddleware;
|
|
312
|
+
/**
|
|
313
|
+
* Final Express error handler for unexpected errors. Always returns JSON so
|
|
314
|
+
* clients (e.g. RTK Query) can parse the response.
|
|
315
|
+
*/
|
|
316
|
+
var apiFallthroughErrorMiddleware = function (err, _req, res, _next) {
|
|
317
|
+
logger_1.logger.error("Fallthrough error: ".concat(err).concat(err.stack ? "\n".concat(err.stack) : ""));
|
|
318
|
+
Sentry.captureException(err);
|
|
319
|
+
if (res.headersSent) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
res.status(500).json({ status: 500, title: "Internal server error" }).send();
|
|
323
|
+
};
|
|
324
|
+
exports.apiFallthroughErrorMiddleware = apiFallthroughErrorMiddleware;
|
package/dist/errors.test.js
CHANGED
|
@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
var bun_test_1 = require("bun:test");
|
|
37
37
|
var Sentry = __importStar(require("@sentry/bun"));
|
|
38
|
-
var mongoose_1 = require("mongoose");
|
|
38
|
+
var mongoose_1 = __importStar(require("mongoose"));
|
|
39
39
|
var errors_1 = require("./errors");
|
|
40
40
|
var buildResponse = function () {
|
|
41
41
|
var res = {
|
|
@@ -277,4 +277,19 @@ var buildResponse = function () {
|
|
|
277
277
|
(0, bun_test_1.expect)(next).toHaveBeenCalledWith(err);
|
|
278
278
|
(0, bun_test_1.expect)(res.status).not.toHaveBeenCalled();
|
|
279
279
|
});
|
|
280
|
+
(0, bun_test_1.it)("converts Mongoose CastError to a 400 APIError response", function () {
|
|
281
|
+
var err = new mongoose_1.default.Error.CastError("Number", "not-a-number", "general.maxUploadSizeMb");
|
|
282
|
+
(0, errors_1.apiErrorMiddleware)(err, req, res, next);
|
|
283
|
+
(0, bun_test_1.expect)(res.status).toHaveBeenCalledWith(400);
|
|
284
|
+
(0, bun_test_1.expect)(res.json).toHaveBeenCalledWith(bun_test_1.expect.objectContaining({
|
|
285
|
+
meta: bun_test_1.expect.objectContaining({
|
|
286
|
+
fields: bun_test_1.expect.objectContaining({
|
|
287
|
+
"general.maxUploadSizeMb": bun_test_1.expect.stringContaining("Expected Number"),
|
|
288
|
+
}),
|
|
289
|
+
}),
|
|
290
|
+
status: 400,
|
|
291
|
+
title: "Validation failed",
|
|
292
|
+
}));
|
|
293
|
+
(0, bun_test_1.expect)(next).not.toHaveBeenCalled();
|
|
294
|
+
});
|
|
280
295
|
});
|
package/dist/example.js
CHANGED
|
@@ -67,20 +67,32 @@ mongoose_1.default
|
|
|
67
67
|
var userSchema = new mongoose_1.Schema({
|
|
68
68
|
admin: { default: false, description: "Whether the user has admin privileges", type: Boolean },
|
|
69
69
|
username: { description: "The user's username", type: String },
|
|
70
|
-
});
|
|
70
|
+
}, { strict: "throw", toJSON: { virtuals: true }, toObject: { virtuals: true } });
|
|
71
|
+
// biome-ignore lint/suspicious/noExplicitAny: passport-local-mongoose's plugin type is incompatible with mongoose Schema generics
|
|
71
72
|
userSchema.plugin(passport_local_mongoose_1.default, { usernameField: "email" });
|
|
72
73
|
userSchema.plugin(plugins_1.createdUpdatedPlugin);
|
|
74
|
+
userSchema.plugin(plugins_1.isDeletedPlugin);
|
|
75
|
+
userSchema.plugin(plugins_1.findOneOrNone);
|
|
76
|
+
userSchema.plugin(plugins_1.findExactlyOne);
|
|
73
77
|
userSchema.plugin(plugins_1.baseUserPlugin);
|
|
74
78
|
var UserModel = (0, mongoose_1.model)("User", userSchema);
|
|
75
79
|
var schema = new mongoose_1.Schema({
|
|
76
80
|
calories: { description: "Number of calories in the food", type: Number },
|
|
77
81
|
created: { description: "When this food was created", type: Date },
|
|
78
|
-
hidden: {
|
|
82
|
+
hidden: {
|
|
83
|
+
default: false,
|
|
84
|
+
description: "Whether this food is hidden from listings",
|
|
85
|
+
type: Boolean,
|
|
86
|
+
},
|
|
79
87
|
name: { description: "The name of the food", type: String },
|
|
80
88
|
ownerId: { description: "The user who owns this food entry", ref: "User", type: "ObjectId" },
|
|
81
|
-
});
|
|
89
|
+
}, { strict: "throw", toJSON: { virtuals: true }, toObject: { virtuals: true } });
|
|
90
|
+
schema.plugin(plugins_1.createdUpdatedPlugin);
|
|
91
|
+
schema.plugin(plugins_1.isDeletedPlugin);
|
|
92
|
+
schema.plugin(plugins_1.findOneOrNone);
|
|
93
|
+
schema.plugin(plugins_1.findExactlyOne);
|
|
82
94
|
var FoodModel = (0, mongoose_1.model)("Food", schema);
|
|
83
|
-
function
|
|
95
|
+
var getBaseServer = function () {
|
|
84
96
|
var app = (0, express_1.default)();
|
|
85
97
|
app.use(function (req, res, next) {
|
|
86
98
|
res.header("Access-Control-Allow-Origin", "*");
|
|
@@ -96,7 +108,7 @@ function getBaseServer() {
|
|
|
96
108
|
app.use(express_1.default.json());
|
|
97
109
|
(0, auth_1.setupAuth)(app, UserModel);
|
|
98
110
|
(0, auth_1.addAuthRoutes)(app, UserModel);
|
|
99
|
-
function
|
|
111
|
+
var addRoutes = function (router, options) {
|
|
100
112
|
router.use("/food", (0, api_1.modelRouter)(FoodModel, __assign(__assign({}, options), { openApiOverwrite: {
|
|
101
113
|
get: { responses: { 200: { description: "Get all the food" } } },
|
|
102
114
|
}, permissions: {
|
|
@@ -106,7 +118,7 @@ function getBaseServer() {
|
|
|
106
118
|
read: [permissions_1.Permissions.IsAny],
|
|
107
119
|
update: [permissions_1.Permissions.IsOwner],
|
|
108
120
|
}, queryFields: ["name", "calories", "created", "ownerId", "hidden"] })));
|
|
109
|
-
}
|
|
121
|
+
};
|
|
110
122
|
return (0, expressServer_1.setupServer)({
|
|
111
123
|
addRoutes: addRoutes,
|
|
112
124
|
loggingOptions: {
|
|
@@ -114,5 +126,5 @@ function getBaseServer() {
|
|
|
114
126
|
},
|
|
115
127
|
userModel: UserModel,
|
|
116
128
|
});
|
|
117
|
-
}
|
|
129
|
+
};
|
|
118
130
|
getBaseServer();
|
package/dist/expressServer.d.ts
CHANGED
|
@@ -5,13 +5,13 @@ import type { ModelRouterOptions } from "./api";
|
|
|
5
5
|
import { type UserModel as UserMongooseModel } from "./auth";
|
|
6
6
|
import { type GitHubAuthOptions } from "./githubAuth";
|
|
7
7
|
import { type LoggingOptions } from "./logger";
|
|
8
|
-
export declare
|
|
8
|
+
export declare const setupEnvironment: () => void;
|
|
9
9
|
export type AddRoutes = (router: Router, options?: Partial<ModelRouterOptions<unknown>>) => void;
|
|
10
|
-
export declare
|
|
11
|
-
export declare
|
|
12
|
-
export declare
|
|
10
|
+
export declare const logRequests: (req: any, res: any, next: express.NextFunction) => void;
|
|
11
|
+
export declare const createRouter: (rootPath: string, addRoutes: AddRoutes, middleware?: express.RequestHandler[]) => Array<string | express.RequestHandler | Router>;
|
|
12
|
+
export declare const createRouterWithAuth: (rootPath: string, addRoutes: (router: Router) => void, middleware?: express.RequestHandler[]) => Array<string | express.RequestHandler | Router>;
|
|
13
13
|
export interface AuthOptions {
|
|
14
|
-
generateJWTPayload?: (user: any) => Record<string,
|
|
14
|
+
generateJWTPayload?: (user: any) => Record<string, unknown>;
|
|
15
15
|
generateTokenExpiration?: (user: any) => number | jwt.SignOptions["expiresIn"];
|
|
16
16
|
generateRefreshTokenExpiration?: (user: any) => number | jwt.SignOptions["expiresIn"];
|
|
17
17
|
}
|
|
@@ -19,6 +19,7 @@ export interface SetupServerOptions {
|
|
|
19
19
|
userModel: UserMongooseModel;
|
|
20
20
|
addRoutes: AddRoutes;
|
|
21
21
|
loggingOptions?: LoggingOptions;
|
|
22
|
+
logRequests?: boolean;
|
|
22
23
|
authOptions?: AuthOptions;
|
|
23
24
|
/**
|
|
24
25
|
* GitHub OAuth configuration. When provided, enables GitHub authentication.
|
|
@@ -31,11 +32,11 @@ export interface SetupServerOptions {
|
|
|
31
32
|
ignoreTraces?: string[];
|
|
32
33
|
sentryOptions?: Sentry.BunOptions;
|
|
33
34
|
}
|
|
34
|
-
export declare
|
|
35
|
-
export declare
|
|
35
|
+
export declare const setupServer: (options: SetupServerOptions) => express.Application;
|
|
36
|
+
export declare const cronjob: (name: string, schedule: "hourly" | "minutely" | string, callback: () => void) => void;
|
|
36
37
|
export interface WrapScriptOptions {
|
|
37
|
-
onFinish?: (result?:
|
|
38
|
+
onFinish?: (result?: unknown) => void | Promise<void>;
|
|
38
39
|
terminateTimeout?: number;
|
|
39
40
|
slackChannel?: string;
|
|
40
41
|
}
|
|
41
|
-
export declare
|
|
42
|
+
export declare const wrapScript: (func: () => Promise<unknown>, options?: WrapScriptOptions) => Promise<void>;
|
package/dist/expressServer.js
CHANGED
|
@@ -97,13 +97,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
97
97
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
98
98
|
};
|
|
99
99
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
100
|
-
exports.setupEnvironment =
|
|
101
|
-
exports.logRequests = logRequests;
|
|
102
|
-
exports.createRouter = createRouter;
|
|
103
|
-
exports.createRouterWithAuth = createRouterWithAuth;
|
|
104
|
-
exports.setupServer = setupServer;
|
|
105
|
-
exports.cronjob = cronjob;
|
|
106
|
-
exports.wrapScript = wrapScript;
|
|
100
|
+
exports.wrapScript = exports.cronjob = exports.setupServer = exports.createRouterWithAuth = exports.createRouter = exports.logRequests = exports.setupEnvironment = void 0;
|
|
107
101
|
var Sentry = __importStar(require("@sentry/bun"));
|
|
108
102
|
var cors_1 = __importDefault(require("cors"));
|
|
109
103
|
var cron_1 = __importDefault(require("cron"));
|
|
@@ -119,11 +113,12 @@ var logger_1 = require("./logger");
|
|
|
119
113
|
var notifiers_1 = require("./notifiers");
|
|
120
114
|
var openApiCompat_1 = require("./openApiCompat");
|
|
121
115
|
var openApiEtag_1 = require("./openApiEtag");
|
|
116
|
+
var requestContext_1 = require("./requestContext");
|
|
122
117
|
var index_1 = __importDefault(require("./vendor/wesleytodd-openapi/index"));
|
|
123
118
|
var SLOW_READ_MAX = 200;
|
|
124
119
|
var SLOW_WRITE_MAX = 500;
|
|
125
120
|
var IS_JEST = process.env.JEST_WORKER_ID !== undefined;
|
|
126
|
-
function
|
|
121
|
+
var setupEnvironment = function () {
|
|
127
122
|
if (!process.env.TOKEN_ISSUER) {
|
|
128
123
|
throw new Error("TOKEN_ISSUER must be set in env.");
|
|
129
124
|
}
|
|
@@ -142,12 +137,14 @@ function setupEnvironment() {
|
|
|
142
137
|
if (!process.env.REFRESH_TOKEN_EXPIRES_IN && !IS_JEST) {
|
|
143
138
|
logger_1.logger.warn("REFRESH_TOKEN_EXPIRES_IN not set so using default.");
|
|
144
139
|
}
|
|
145
|
-
}
|
|
140
|
+
};
|
|
141
|
+
exports.setupEnvironment = setupEnvironment;
|
|
142
|
+
// biome-ignore lint/suspicious/noExplicitAny: also called from tests with mock request/response objects
|
|
146
143
|
var logRequestsFinished = function (req, res, startTime) {
|
|
147
|
-
var _a, _b, _c;
|
|
148
|
-
var options = ((_a = res.locals.loggingOptions) !== null &&
|
|
149
|
-
var slowReadMs = (
|
|
150
|
-
var slowWriteMs = (
|
|
144
|
+
var _a, _b, _c, _d;
|
|
145
|
+
var options = ((_b = (_a = res.locals) === null || _a === void 0 ? void 0 : _a.loggingOptions) !== null && _b !== void 0 ? _b : {});
|
|
146
|
+
var slowReadMs = (_c = options.logSlowRequestsReadMs) !== null && _c !== void 0 ? _c : SLOW_READ_MAX;
|
|
147
|
+
var slowWriteMs = (_d = options.logSlowRequestsWriteMs) !== null && _d !== void 0 ? _d : SLOW_WRITE_MAX;
|
|
151
148
|
var diff = process.hrtime.bigint() - startTime;
|
|
152
149
|
var diffInMs = Number(diff) / 1000000;
|
|
153
150
|
var pathName = "unknown";
|
|
@@ -180,7 +177,8 @@ var logRequestsFinished = function (req, res, startTime) {
|
|
|
180
177
|
}
|
|
181
178
|
}
|
|
182
179
|
};
|
|
183
|
-
|
|
180
|
+
// biome-ignore lint/suspicious/noExplicitAny: also called from tests with mock request/response objects
|
|
181
|
+
var logRequests = function (req, res, next) {
|
|
184
182
|
var _a, _b, _c, _d;
|
|
185
183
|
var startTime = process.hrtime.bigint();
|
|
186
184
|
var userString = "";
|
|
@@ -210,35 +208,38 @@ function logRequests(req, res, next) {
|
|
|
210
208
|
}
|
|
211
209
|
(0, on_finished_1.default)(res, function () { return logRequestsFinished(req, res, startTime); });
|
|
212
210
|
next();
|
|
213
|
-
}
|
|
214
|
-
|
|
211
|
+
};
|
|
212
|
+
exports.logRequests = logRequests;
|
|
213
|
+
var createRouter = function (rootPath, addRoutes, middleware) {
|
|
215
214
|
if (middleware === void 0) { middleware = []; }
|
|
216
|
-
function
|
|
215
|
+
var routePathMiddleware = function (req, _res, next) {
|
|
217
216
|
if (!req.routeMount) {
|
|
218
217
|
req.routeMount = [];
|
|
219
218
|
}
|
|
220
219
|
req.routeMount.push(rootPath);
|
|
221
220
|
next();
|
|
222
|
-
}
|
|
221
|
+
};
|
|
223
222
|
var router = express_1.default.Router();
|
|
224
223
|
router.use(routePathMiddleware);
|
|
225
224
|
addRoutes(router);
|
|
226
225
|
return __spreadArray(__spreadArray([rootPath], __read(middleware), false), [router], false);
|
|
227
|
-
}
|
|
228
|
-
|
|
226
|
+
};
|
|
227
|
+
exports.createRouter = createRouter;
|
|
228
|
+
var createRouterWithAuth = function (rootPath, addRoutes, middleware) {
|
|
229
229
|
if (middleware === void 0) { middleware = []; }
|
|
230
|
-
return createRouter(rootPath, addRoutes, __spreadArray([
|
|
230
|
+
return (0, exports.createRouter)(rootPath, addRoutes, __spreadArray([
|
|
231
231
|
passport_1.default.authenticate("firebase-jwt", { session: false })
|
|
232
232
|
], __read(middleware), false));
|
|
233
|
-
}
|
|
234
|
-
|
|
233
|
+
};
|
|
234
|
+
exports.createRouterWithAuth = createRouterWithAuth;
|
|
235
|
+
var initializeRoutes = function (UserModel, addRoutes, options) {
|
|
235
236
|
var _a;
|
|
236
237
|
if (options === void 0) { options = {}; }
|
|
237
238
|
var app = (0, express_1.default)();
|
|
238
239
|
// Record mount paths on layers for Express 5 → OpenAPI compat
|
|
239
240
|
(0, openApiCompat_1.patchAppUse)(app);
|
|
240
|
-
// TODO: Log a warning when we hit the array limit.
|
|
241
241
|
app.set("query parser", function (str) { var _a; return qs_1.default.parse(str, { arrayLimit: (_a = options.arrayLimit) !== null && _a !== void 0 ? _a : 200 }); });
|
|
242
|
+
app.use(requestContext_1.requestContextMiddleware);
|
|
242
243
|
app.use((0, cors_1.default)({
|
|
243
244
|
origin: (_a = options.corsOrigin) !== null && _a !== void 0 ? _a : "*",
|
|
244
245
|
}));
|
|
@@ -249,8 +250,12 @@ function initializeRoutes(UserModel, addRoutes, options) {
|
|
|
249
250
|
// Add login/signup/refresh_token before the JWT/auth middlewares
|
|
250
251
|
(0, auth_1.addAuthRoutes)(app, UserModel, options === null || options === void 0 ? void 0 : options.authOptions);
|
|
251
252
|
(0, auth_1.setupAuth)(app, UserModel);
|
|
253
|
+
app.use(function (req, res, next) {
|
|
254
|
+
(0, requestContext_1.updateRequestContextFromRequest)(req, res);
|
|
255
|
+
next();
|
|
256
|
+
});
|
|
252
257
|
if (options.logRequests !== false) {
|
|
253
|
-
app.use(logRequests);
|
|
258
|
+
app.use(exports.logRequests);
|
|
254
259
|
}
|
|
255
260
|
// Store the logging options on the request so we can access them later.
|
|
256
261
|
app.use(function (_req, res, next) {
|
|
@@ -259,17 +264,21 @@ function initializeRoutes(UserModel, addRoutes, options) {
|
|
|
259
264
|
});
|
|
260
265
|
// Add Sentry scopes for session, transaction, and userId if any are set
|
|
261
266
|
app.use(function (req, _res, next) {
|
|
262
|
-
var _a;
|
|
267
|
+
var _a, _b;
|
|
268
|
+
var context = (0, requestContext_1.getCurrentRequestContext)();
|
|
263
269
|
var transactionId = req.header("X-Transaction-ID");
|
|
264
|
-
var sessionId = req.header("X-Session-ID");
|
|
270
|
+
var sessionId = (_a = context === null || context === void 0 ? void 0 : context.sessionId) !== null && _a !== void 0 ? _a : req.header("X-Session-ID");
|
|
271
|
+
if (context === null || context === void 0 ? void 0 : context.requestId) {
|
|
272
|
+
Sentry.getCurrentScope().setTag("request_id", context.requestId);
|
|
273
|
+
}
|
|
265
274
|
if (transactionId) {
|
|
266
275
|
Sentry.getCurrentScope().setTag("transaction_id", transactionId);
|
|
267
276
|
}
|
|
268
277
|
if (sessionId) {
|
|
269
278
|
Sentry.getCurrentScope().setTag("session_id", sessionId);
|
|
270
279
|
}
|
|
271
|
-
if ((
|
|
272
|
-
Sentry.getCurrentScope().setTag("user", req.user._id);
|
|
280
|
+
if ((_b = req.user) === null || _b === void 0 ? void 0 : _b._id) {
|
|
281
|
+
Sentry.getCurrentScope().setTag("user", String(req.user._id));
|
|
273
282
|
}
|
|
274
283
|
next();
|
|
275
284
|
});
|
|
@@ -299,16 +308,10 @@ function initializeRoutes(UserModel, addRoutes, options) {
|
|
|
299
308
|
// Catch any thrown APIErrors and return them in an OpenAPI compatible format
|
|
300
309
|
app.use(errors_1.apiUnauthorizedMiddleware);
|
|
301
310
|
app.use(errors_1.apiErrorMiddleware);
|
|
302
|
-
app.use(
|
|
303
|
-
logger_1.logger.error("Fallthrough error: ".concat(err).concat((err === null || err === void 0 ? void 0 : err.stack) ? "\n".concat(err.stack) : "", "}"));
|
|
304
|
-
Sentry.captureException(err);
|
|
305
|
-
res.statusCode = 500;
|
|
306
|
-
res.end("".concat(res.sentry, "\n"));
|
|
307
|
-
});
|
|
311
|
+
app.use(errors_1.apiFallthroughErrorMiddleware);
|
|
308
312
|
return app;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function setupServer(options) {
|
|
313
|
+
};
|
|
314
|
+
var setupServer = function (options) {
|
|
312
315
|
var UserModel = options.userModel;
|
|
313
316
|
var addRoutes = options.addRoutes;
|
|
314
317
|
(0, logger_1.setupLogging)(options.loggingOptions);
|
|
@@ -319,10 +322,13 @@ function setupServer(options) {
|
|
|
319
322
|
authOptions: options.authOptions,
|
|
320
323
|
corsOrigin: options.corsOrigin,
|
|
321
324
|
githubAuth: options.githubAuth,
|
|
325
|
+
loggingOptions: options.loggingOptions,
|
|
326
|
+
logRequests: options.logRequests,
|
|
322
327
|
});
|
|
323
328
|
}
|
|
324
329
|
catch (error) {
|
|
325
|
-
|
|
330
|
+
var stack = error instanceof Error && error.stack ? error.stack : String(error);
|
|
331
|
+
logger_1.logger.error("Error initializing routes: ".concat(stack));
|
|
326
332
|
throw error;
|
|
327
333
|
}
|
|
328
334
|
if (!options.skipListen) {
|
|
@@ -333,14 +339,15 @@ function setupServer(options) {
|
|
|
333
339
|
});
|
|
334
340
|
}
|
|
335
341
|
catch (error) {
|
|
336
|
-
|
|
342
|
+
var stack = error instanceof Error ? error.stack : String(error);
|
|
343
|
+
logger_1.logger.error("Error trying to start HTTP server: ".concat(error, "\n").concat(stack));
|
|
337
344
|
process.exit(1);
|
|
338
345
|
}
|
|
339
346
|
}
|
|
340
347
|
return app;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
function
|
|
348
|
+
};
|
|
349
|
+
exports.setupServer = setupServer;
|
|
350
|
+
var cronjob = function (name, schedule, callback) {
|
|
344
351
|
var cronSchedule = schedule === "hourly" ? "0 * * * *" : schedule === "minutely" ? "* * * * *" : schedule;
|
|
345
352
|
logger_1.logger.info("Adding cronjob ".concat(name, ", running at: ").concat(cronSchedule));
|
|
346
353
|
try {
|
|
@@ -349,13 +356,15 @@ function cronjob(name, schedule, callback) {
|
|
|
349
356
|
catch (error) {
|
|
350
357
|
throw new Error("Failed to create cronjob: ".concat(error));
|
|
351
358
|
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
359
|
+
};
|
|
360
|
+
exports.cronjob = cronjob;
|
|
361
|
+
var wrapScript = function (func_1) {
|
|
362
|
+
var args_1 = [];
|
|
363
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
364
|
+
args_1[_i - 1] = arguments[_i];
|
|
365
|
+
}
|
|
366
|
+
return __awaiter(void 0, __spreadArray([func_1], __read(args_1), false), void 0, function (func, options) {
|
|
357
367
|
var name, warnTime_1, closeTime, result, error_1;
|
|
358
|
-
var _this = this;
|
|
359
368
|
var _a, _b, _c;
|
|
360
369
|
if (options === void 0) { options = {}; }
|
|
361
370
|
return __generator(this, function (_d) {
|
|
@@ -371,7 +380,7 @@ function wrapScript(func_1) {
|
|
|
371
380
|
if (options.terminateTimeout !== 0) {
|
|
372
381
|
warnTime_1 = (((_b = options.terminateTimeout) !== null && _b !== void 0 ? _b : 300) / 2) * 1000;
|
|
373
382
|
closeTime = ((_c = options.terminateTimeout) !== null && _c !== void 0 ? _c : 300) * 1000;
|
|
374
|
-
setTimeout(function () { return __awaiter(
|
|
383
|
+
setTimeout(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
375
384
|
var msg;
|
|
376
385
|
return __generator(this, function (_a) {
|
|
377
386
|
switch (_a.label) {
|
|
@@ -385,7 +394,7 @@ function wrapScript(func_1) {
|
|
|
385
394
|
}
|
|
386
395
|
});
|
|
387
396
|
}); }, warnTime_1);
|
|
388
|
-
setTimeout(function () { return __awaiter(
|
|
397
|
+
setTimeout(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
389
398
|
var msg;
|
|
390
399
|
return __generator(this, function (_a) {
|
|
391
400
|
switch (_a.label) {
|
|
@@ -432,10 +441,10 @@ function wrapScript(func_1) {
|
|
|
432
441
|
case 9: return [4 /*yield*/, (0, notifiers_1.sendToSlack)("Success running script ".concat(name, ": ").concat(result))];
|
|
433
442
|
case 10:
|
|
434
443
|
_d.sent();
|
|
435
|
-
// Unclear why we have to exit here to prevent the script for continuing to run.
|
|
436
444
|
process.exit(0);
|
|
437
445
|
return [2 /*return*/];
|
|
438
446
|
}
|
|
439
447
|
});
|
|
440
448
|
});
|
|
441
|
-
}
|
|
449
|
+
};
|
|
450
|
+
exports.wrapScript = wrapScript;
|