@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.
- package/dist/__tests__/versionCheckPlugin.test.js +53 -3
- package/dist/api.arrayOperations.test.js +1 -0
- package/dist/api.asyncHandler.test.d.ts +1 -0
- package/dist/api.asyncHandler.test.js +236 -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 +17 -14
- package/dist/betterAuthSetup.test.js +1 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.js +248 -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 +106 -10
- package/dist/errors.test.js +16 -1
- package/dist/example.js +16 -7
- package/dist/expressServer.d.ts +10 -9
- package/dist/expressServer.js +62 -53
- package/dist/expressServer.test.js +53 -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 +65 -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 +720 -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 +2158 -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 +241 -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 +37 -3
- package/src/api.arrayOperations.test.ts +1 -0
- package/src/api.asyncHandler.test.ts +177 -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 +46 -19
- package/src/config.test.ts +255 -0
- package/src/config.ts +206 -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 +94 -20
- package/src/example.ts +46 -21
- package/src/express.d.ts +18 -1
- package/src/expressServer.test.ts +50 -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 +59 -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 +568 -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 +1755 -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 +196 -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
|
@@ -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");
|
|
@@ -262,6 +263,70 @@ var setupFreshApp = function () { return __awaiter(void 0, void 0, void 0, funct
|
|
|
262
263
|
});
|
|
263
264
|
}); });
|
|
264
265
|
});
|
|
266
|
+
(0, bun_test_1.describe)("per-route validation: object-form options", function () {
|
|
267
|
+
(0, bun_test_1.it)("applies object validation options with per-operation control", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
268
|
+
var removedProps, errorsCaught, freshApp, admin, res;
|
|
269
|
+
return __generator(this, function (_a) {
|
|
270
|
+
switch (_a.label) {
|
|
271
|
+
case 0:
|
|
272
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ removeAdditional: true });
|
|
273
|
+
removedProps = [];
|
|
274
|
+
errorsCaught = [];
|
|
275
|
+
return [4 /*yield*/, setupFreshApp()];
|
|
276
|
+
case 1:
|
|
277
|
+
freshApp = _a.sent();
|
|
278
|
+
freshApp.use("/required", (0, api_1.modelRouter)(tests_1.RequiredModel, __assign(__assign({}, requiredRouterOptions), { validation: {
|
|
279
|
+
excludeFromCreate: ["about"],
|
|
280
|
+
onAdditionalPropertiesRemoved: function (props) {
|
|
281
|
+
removedProps.push.apply(removedProps, __spreadArray([], __read(props), false));
|
|
282
|
+
},
|
|
283
|
+
onError: function (errors) {
|
|
284
|
+
errorsCaught.push(errors);
|
|
285
|
+
},
|
|
286
|
+
validateCreate: true,
|
|
287
|
+
validateQuery: true,
|
|
288
|
+
validateUpdate: true,
|
|
289
|
+
} })));
|
|
290
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(freshApp, "admin")];
|
|
291
|
+
case 2:
|
|
292
|
+
admin = _a.sent();
|
|
293
|
+
return [4 /*yield*/, admin.post("/required").send({ name: "Validated" }).expect(201)];
|
|
294
|
+
case 3:
|
|
295
|
+
res = _a.sent();
|
|
296
|
+
(0, bun_test_1.expect)(res.body.data.name).toBe("Validated");
|
|
297
|
+
return [2 /*return*/];
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}); });
|
|
301
|
+
(0, bun_test_1.it)("disables create validation when validateCreate is false", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
302
|
+
var freshApp, admin, res;
|
|
303
|
+
return __generator(this, function (_a) {
|
|
304
|
+
switch (_a.label) {
|
|
305
|
+
case 0:
|
|
306
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ removeAdditional: true });
|
|
307
|
+
return [4 /*yield*/, setupFreshApp()];
|
|
308
|
+
case 1:
|
|
309
|
+
freshApp = _a.sent();
|
|
310
|
+
freshApp.use("/required", (0, api_1.modelRouter)(tests_1.RequiredModel, __assign(__assign({}, requiredRouterOptions), { validation: {
|
|
311
|
+
excludeFromUpdate: ["about"],
|
|
312
|
+
validateCreate: false,
|
|
313
|
+
validateUpdate: true,
|
|
314
|
+
} })));
|
|
315
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(freshApp, "admin")];
|
|
316
|
+
case 2:
|
|
317
|
+
admin = _a.sent();
|
|
318
|
+
return [4 /*yield*/, admin
|
|
319
|
+
.post("/required")
|
|
320
|
+
.send({ extraField: "ignored", name: "NoCreateValidation" })
|
|
321
|
+
.expect(201)];
|
|
322
|
+
case 3:
|
|
323
|
+
res = _a.sent();
|
|
324
|
+
(0, bun_test_1.expect)(res.body.data.name).toBe("NoCreateValidation");
|
|
325
|
+
return [2 /*return*/];
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}); });
|
|
329
|
+
});
|
|
265
330
|
(0, bun_test_1.describe)("sanitization of non-standard mongoose-to-swagger types", function () {
|
|
266
331
|
(0, bun_test_1.it)("validates models with ObjectId and DateOnly fields after sanitization", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
267
332
|
var freshApp, admin, res;
|
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");
|
package/dist/plugins.d.ts
CHANGED
|
@@ -60,19 +60,19 @@ export declare const findExactlyOne: <T>(schema: Schema<T>) => void;
|
|
|
60
60
|
export declare const upsertPlugin: <T>(schema: Schema<any, any, any, any>) => void;
|
|
61
61
|
/** For models with the upsertPlugin, extend this interface to add the upsert static method. */
|
|
62
62
|
export interface HasUpsert<T> {
|
|
63
|
-
upsert(conditions: Record<string,
|
|
63
|
+
upsert(conditions: Record<string, unknown>, update: Record<string, unknown>): Promise<T>;
|
|
64
64
|
}
|
|
65
65
|
export interface FindOneOrNonePlugin<T> {
|
|
66
|
-
findOneOrNone(query: Record<string,
|
|
66
|
+
findOneOrNone(query: Record<string, unknown>, errorArgs?: Partial<APIErrorConstructor>): Promise<(Document & T) | null>;
|
|
67
67
|
}
|
|
68
68
|
export interface FindExactlyOnePlugin<T> {
|
|
69
|
-
findExactlyOne(query: Record<string,
|
|
69
|
+
findExactlyOne(query: Record<string, unknown>, errorArgs?: Partial<APIErrorConstructor>): Promise<Document & T>;
|
|
70
70
|
}
|
|
71
71
|
export declare class DateOnly extends SchemaType {
|
|
72
72
|
constructor(key: string, options: SchemaTypeOptions<Date>);
|
|
73
|
-
handleSingle(val:
|
|
73
|
+
handleSingle(val: unknown): Date | undefined;
|
|
74
74
|
$conditionalHandlers: any;
|
|
75
|
-
castForQuery($conditional:
|
|
75
|
+
castForQuery($conditional: string | undefined, val: unknown, context: unknown): Date | undefined;
|
|
76
76
|
cast(val: unknown): Date | undefined;
|
|
77
77
|
get(val: unknown): this;
|
|
78
78
|
}
|
package/dist/plugins.js
CHANGED
|
@@ -99,6 +99,7 @@ exports.DateOnly = exports.upsertPlugin = exports.findExactlyOne = exports.findO
|
|
|
99
99
|
var luxon_1 = require("luxon");
|
|
100
100
|
var mongoose_1 = __importStar(require("mongoose"));
|
|
101
101
|
var errors_1 = require("./errors");
|
|
102
|
+
// biome-ignore lint/suspicious/noExplicitAny: Schema generics must be loose to accept arbitrary consumer schemas
|
|
102
103
|
var baseUserPlugin = function (schema) {
|
|
103
104
|
schema.add({
|
|
104
105
|
admin: { default: false, description: "Whether the user has admin privileges", type: Boolean },
|
|
@@ -106,6 +107,7 @@ var baseUserPlugin = function (schema) {
|
|
|
106
107
|
schema.add({ email: { description: "The user's email address", index: true, type: String } });
|
|
107
108
|
};
|
|
108
109
|
exports.baseUserPlugin = baseUserPlugin;
|
|
110
|
+
// biome-ignore lint/suspicious/noExplicitAny: Schema generics must be loose to accept arbitrary consumer schemas
|
|
109
111
|
var isDeletedPlugin = function (schema, defaultValue) {
|
|
110
112
|
if (defaultValue === void 0) { defaultValue = false; }
|
|
111
113
|
schema.add({
|
|
@@ -117,6 +119,7 @@ var isDeletedPlugin = function (schema, defaultValue) {
|
|
|
117
119
|
type: Boolean,
|
|
118
120
|
},
|
|
119
121
|
});
|
|
122
|
+
// biome-ignore lint/suspicious/noExplicitAny: Query<any, any> must be loose to accept arbitrary consumer queries
|
|
120
123
|
var applyDeleteFilter = function (q) {
|
|
121
124
|
var query = q.getQuery();
|
|
122
125
|
if (query && query.deleted === undefined) {
|
|
@@ -131,7 +134,9 @@ var isDeletedPlugin = function (schema, defaultValue) {
|
|
|
131
134
|
});
|
|
132
135
|
};
|
|
133
136
|
exports.isDeletedPlugin = isDeletedPlugin;
|
|
134
|
-
var isDisabledPlugin = function (
|
|
137
|
+
var isDisabledPlugin = function (
|
|
138
|
+
// biome-ignore lint/suspicious/noExplicitAny: Schema generics must be loose to accept arbitrary consumer schemas
|
|
139
|
+
schema, defaultValue) {
|
|
135
140
|
if (defaultValue === void 0) { defaultValue = false; }
|
|
136
141
|
schema.add({
|
|
137
142
|
disabled: {
|
|
@@ -143,6 +148,7 @@ var isDisabledPlugin = function (schema, defaultValue) {
|
|
|
143
148
|
});
|
|
144
149
|
};
|
|
145
150
|
exports.isDisabledPlugin = isDisabledPlugin;
|
|
151
|
+
// biome-ignore lint/suspicious/noExplicitAny: Schema generics must be loose to accept arbitrary consumer schemas
|
|
146
152
|
var createdUpdatedPlugin = function (schema) {
|
|
147
153
|
schema.add({
|
|
148
154
|
updated: { description: "When this document was last updated", index: true, type: Date },
|
|
@@ -268,6 +274,7 @@ exports.findExactlyOne = findExactlyOne;
|
|
|
268
274
|
* match the conditions to prevent ambiguous updates.
|
|
269
275
|
* @param schema Mongoose Schema
|
|
270
276
|
*/
|
|
277
|
+
// biome-ignore lint/suspicious/noExplicitAny: Schema generics with unknown collide with mongoose's loose this-binding on schema.statics
|
|
271
278
|
var upsertPlugin = function (schema) {
|
|
272
279
|
schema.statics.upsert = function (conditions, update) {
|
|
273
280
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -285,14 +292,16 @@ var upsertPlugin = function (schema) {
|
|
|
285
292
|
});
|
|
286
293
|
}
|
|
287
294
|
doc = docs[0];
|
|
288
|
-
if (doc)
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
295
|
+
if (!doc) return [3 /*break*/, 3];
|
|
296
|
+
// If the document exists, update it with the provided update values.
|
|
297
|
+
Object.assign(doc, update);
|
|
298
|
+
return [4 /*yield*/, doc.save()];
|
|
299
|
+
case 2: return [2 /*return*/, (_a.sent())];
|
|
300
|
+
case 3:
|
|
293
301
|
combinedData = __assign(__assign({}, conditions), update);
|
|
294
302
|
newDoc = new this(combinedData);
|
|
295
|
-
return [
|
|
303
|
+
return [4 /*yield*/, newDoc.save()];
|
|
304
|
+
case 4: return [2 /*return*/, (_a.sent())];
|
|
296
305
|
}
|
|
297
306
|
});
|
|
298
307
|
});
|
|
@@ -313,7 +322,7 @@ var DateOnly = /** @class */ (function (_super) {
|
|
|
313
322
|
// When using $gt, $gte, $lt, $lte, etc, we need to cast the value to a Date
|
|
314
323
|
DateOnly.prototype.castForQuery = function ($conditional, val, context) {
|
|
315
324
|
if ($conditional == null) {
|
|
316
|
-
// noExplicitAny: applySetters is an internal Mongoose SchemaType method not in public type definitions
|
|
325
|
+
// biome-ignore lint/suspicious/noExplicitAny: applySetters is an internal Mongoose SchemaType method not in public type definitions
|
|
317
326
|
return this.applySetters(val, context);
|
|
318
327
|
}
|
|
319
328
|
var handler = this.$conditionalHandlers[$conditional];
|
|
@@ -354,5 +363,5 @@ var DateOnly = /** @class */ (function (_super) {
|
|
|
354
363
|
}(mongoose_1.SchemaType));
|
|
355
364
|
exports.DateOnly = DateOnly;
|
|
356
365
|
// Register DateOnly with Mongoose's Schema.Types
|
|
357
|
-
// noExplicitAny: DateOnly is a custom SchemaType not declared in Mongoose's Schema.Types interface
|
|
366
|
+
// biome-ignore lint/suspicious/noExplicitAny: DateOnly is a custom SchemaType not declared in Mongoose's Schema.Types interface
|
|
358
367
|
mongoose_1.default.Schema.Types.DateOnly = DateOnly;
|
package/dist/plugins.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 mongoose_1 = require("mongoose");
|
|
60
61
|
var supertest_1 = __importDefault(require("supertest"));
|
|
@@ -77,7 +78,6 @@ var StuffModel = (0, mongoose_1.model)("Stuff", stuffSchema);
|
|
|
77
78
|
(0, bun_test_1.describe)("baseUserPlugin", function () {
|
|
78
79
|
(0, bun_test_1.it)("adds admin and email fields to the schema", function () {
|
|
79
80
|
var testSchema = new mongoose_1.Schema({});
|
|
80
|
-
// biome-ignore lint/suspicious/noExplicitAny: test schema
|
|
81
81
|
(0, plugins_1.baseUserPlugin)(testSchema);
|
|
82
82
|
var adminPath = testSchema.path("admin");
|
|
83
83
|
(0, bun_test_1.expect)(adminPath).toBeDefined();
|
package/dist/populate.d.ts
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
|
-
import type { Document } from "mongoose";
|
|
2
|
-
|
|
1
|
+
import type { Document, Schema } from "mongoose";
|
|
2
|
+
interface OpenApiSchemaNode {
|
|
3
|
+
description?: string;
|
|
4
|
+
items?: OpenApiSchemaNode;
|
|
5
|
+
properties?: Record<string, OpenApiSchemaNode>;
|
|
6
|
+
type?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface PopulatePath {
|
|
3
9
|
path: string;
|
|
4
|
-
openApiComponent?:
|
|
10
|
+
openApiComponent?: string;
|
|
5
11
|
fields?: string[];
|
|
6
|
-
}
|
|
7
|
-
export declare const fixMixedFields: (schema:
|
|
8
|
-
export declare
|
|
12
|
+
}
|
|
13
|
+
export declare const fixMixedFields: (schema: Schema | null, properties: Record<string, OpenApiSchemaNode> | Record<string, unknown> | null) => void;
|
|
14
|
+
export declare const getOpenApiSpecForModel: (model: any, { populatePaths, extraModelProperties, }?: {
|
|
9
15
|
populatePaths?: PopulatePath[];
|
|
10
16
|
extraModelProperties?: Record<string, unknown>;
|
|
11
|
-
})
|
|
17
|
+
}) => {
|
|
12
18
|
properties: Record<string, unknown>;
|
|
13
19
|
required: string[];
|
|
14
20
|
};
|
|
15
|
-
export declare
|
|
21
|
+
export declare const unpopulate: <T>(doc: Document<T>, path: string) => Document<T>;
|
|
22
|
+
export {};
|
package/dist/populate.js
CHANGED
|
@@ -50,19 +50,14 @@ 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
|
-
exports.fixMixedFields = void 0;
|
|
54
|
-
exports.getOpenApiSpecForModel = getOpenApiSpecForModel;
|
|
55
|
-
exports.unpopulate = unpopulate;
|
|
53
|
+
exports.unpopulate = exports.getOpenApiSpecForModel = exports.fixMixedFields = void 0;
|
|
56
54
|
var isArray_1 = __importDefault(require("lodash/isArray"));
|
|
57
55
|
var mongoose_to_swagger_1 = __importDefault(require("mongoose-to-swagger"));
|
|
58
56
|
var errors_1 = require("./errors");
|
|
59
57
|
var m2sOptions = {
|
|
60
58
|
props: ["readOnly", "required", "enum", "default"],
|
|
61
59
|
};
|
|
62
|
-
//
|
|
63
|
-
// It supports nested keys using dot notation (e.g., 'user.name').
|
|
64
|
-
// If no keys are provided, it returns the original object.
|
|
65
|
-
// The function recursively traverses the object structure to handle nested properties.
|
|
60
|
+
// Keeps only the specified dot-notation keys from an object.
|
|
66
61
|
var filterKeys = function (obj, keysToKeep) {
|
|
67
62
|
if (!keysToKeep) {
|
|
68
63
|
return obj;
|
|
@@ -98,7 +93,7 @@ var filterKeys = function (obj, keysToKeep) {
|
|
|
98
93
|
};
|
|
99
94
|
// Helper function to get the path in the OpenAPI schema, so we can swap out the type for the
|
|
100
95
|
// populated model component or generated type.
|
|
101
|
-
function
|
|
96
|
+
var getPathInSchema = function (schema, path) {
|
|
102
97
|
var _a;
|
|
103
98
|
var keys = path.split(".");
|
|
104
99
|
var currentSchema = schema;
|
|
@@ -119,23 +114,21 @@ function getPathInSchema(schema, path) {
|
|
|
119
114
|
break;
|
|
120
115
|
}
|
|
121
116
|
else {
|
|
122
|
-
throw new
|
|
117
|
+
throw new errors_1.APIError({ status: 500, title: "Path ".concat(path, " not found in schema at key ").concat(key) });
|
|
123
118
|
}
|
|
124
119
|
}
|
|
125
120
|
return fullPath;
|
|
126
|
-
}
|
|
127
|
-
//
|
|
128
|
-
// Recursively walks a Mongoose schema and fixes any Mixed fields in the
|
|
129
|
-
// OpenAPI properties so they use an empty schema (accepts any type) instead
|
|
130
|
-
// of the `{type: "object", properties: {}}` that mongoose-to-swagger emits.
|
|
121
|
+
};
|
|
122
|
+
// Corrects Mixed-type fields in OpenAPI properties so they accept any value.
|
|
131
123
|
var fixMixedFields = function (schema, properties) {
|
|
132
124
|
var e_1, _a;
|
|
133
125
|
var _b, _c, _d;
|
|
134
126
|
if (!properties || !schema) {
|
|
135
127
|
return;
|
|
136
128
|
}
|
|
129
|
+
var props = properties;
|
|
137
130
|
try {
|
|
138
|
-
for (var _e = __values(Object.keys(
|
|
131
|
+
for (var _e = __values(Object.keys(props)), _f = _e.next(); !_f.done; _f = _e.next()) {
|
|
139
132
|
var key = _f.value;
|
|
140
133
|
var schemaPath = schema.path(key);
|
|
141
134
|
if (!schemaPath) {
|
|
@@ -143,14 +136,15 @@ var fixMixedFields = function (schema, properties) {
|
|
|
143
136
|
}
|
|
144
137
|
// Direct Mixed field
|
|
145
138
|
if (schemaPath.instance === "Mixed") {
|
|
146
|
-
|
|
139
|
+
props[key] = { description: (_b = props[key]) === null || _b === void 0 ? void 0 : _b.description };
|
|
147
140
|
continue;
|
|
148
141
|
}
|
|
149
142
|
// Array of sub-documents — check each sub-field for Mixed
|
|
150
|
-
if (schemaPath.instance === "Array" &&
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
143
|
+
if (schemaPath.instance === "Array" && schemaPath.schema) {
|
|
144
|
+
var itemProperties = (_d = (_c = props[key]) === null || _c === void 0 ? void 0 : _c.items) === null || _d === void 0 ? void 0 : _d.properties;
|
|
145
|
+
if (itemProperties) {
|
|
146
|
+
(0, exports.fixMixedFields)(schemaPath.schema, itemProperties);
|
|
147
|
+
}
|
|
154
148
|
}
|
|
155
149
|
}
|
|
156
150
|
}
|
|
@@ -163,7 +157,9 @@ var fixMixedFields = function (schema, properties) {
|
|
|
163
157
|
}
|
|
164
158
|
};
|
|
165
159
|
exports.fixMixedFields = fixMixedFields;
|
|
166
|
-
|
|
160
|
+
var getOpenApiSpecForModel = function (
|
|
161
|
+
// biome-ignore lint/suspicious/noExplicitAny: noExplicitAny: Mongoose Model param uses deep internal APIs (schema.path().options.ref, schema.virtuals, schema.childSchemas, db.model) that are not exposed in public type definitions
|
|
162
|
+
model, _a) {
|
|
167
163
|
var e_2, _b, _c, e_3, _d, e_4, _e, e_5, _f;
|
|
168
164
|
var _g, _h, _j, _k;
|
|
169
165
|
var _l = _a === void 0 ? {} : _a, populatePaths = _l.populatePaths, extraModelProperties = _l.extraModelProperties;
|
|
@@ -296,17 +292,19 @@ function getOpenApiSpecForModel(model, _a) {
|
|
|
296
292
|
properties: __assign(__assign({}, modelSwagger.properties), extraModelProperties),
|
|
297
293
|
required: (_k = modelSwagger.required) !== null && _k !== void 0 ? _k : [],
|
|
298
294
|
};
|
|
299
|
-
}
|
|
295
|
+
};
|
|
296
|
+
exports.getOpenApiSpecForModel = getOpenApiSpecForModel;
|
|
300
297
|
// Helper function to unpopulate a document that has been populated.
|
|
301
298
|
// This is helpful for supporting backwards compatibility. E.g. you use populatePaths
|
|
302
299
|
// to populate a document but if the version header for the request is below the version
|
|
303
300
|
// that the populatePath was added, we remove the population and just return the _id.
|
|
304
|
-
function
|
|
301
|
+
var unpopulate = function (doc, path) {
|
|
305
302
|
if (!path) {
|
|
306
303
|
throw new errors_1.APIError({ status: 500, title: "path is required for unpopulate" });
|
|
307
304
|
}
|
|
308
305
|
var pathParts = path.split(".");
|
|
309
306
|
// Recursive because we need to support nested paths.
|
|
307
|
+
// biome-ignore lint/suspicious/noExplicitAny: noExplicitAny: recursive document traversal uses bracket-notation indexing on arbitrary nested document shapes that Mongoose Document types do not expose
|
|
310
308
|
var recursiveUnpopulate = function (current, parts) {
|
|
311
309
|
var e_6, _a;
|
|
312
310
|
var _b;
|
|
@@ -352,4 +350,5 @@ function unpopulate(doc, path) {
|
|
|
352
350
|
return current;
|
|
353
351
|
};
|
|
354
352
|
return recursiveUnpopulate(doc, pathParts);
|
|
355
|
-
}
|
|
353
|
+
};
|
|
354
|
+
exports.unpopulate = unpopulate;
|
package/dist/populate.test.js
CHANGED
|
@@ -85,6 +85,7 @@ var __read = (this && this.__read) || function (o, n) {
|
|
|
85
85
|
return ar;
|
|
86
86
|
};
|
|
87
87
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
88
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
|
|
88
89
|
var bun_test_1 = require("bun:test");
|
|
89
90
|
var mongoose_1 = __importStar(require("mongoose"));
|
|
90
91
|
var populate_1 = require("./populate");
|