@terreno/api 0.20.2 → 0.22.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/.ai/guidelines/core.md +71 -0
- package/.ai/skills/mongoose-schema-safety/SKILL.md +143 -0
- package/README.md +54 -1
- package/bunfig.toml +1 -1
- package/dist/__tests__/versionCheckPlugin.test.js +29 -7
- package/dist/actions.openApi.test.js +13 -11
- package/dist/api.js +98 -11
- package/dist/api.query.test.js +31 -1
- package/dist/api.test.js +211 -0
- package/dist/auth.test.js +418 -43
- package/dist/betterAuth.d.ts +1 -1
- package/dist/consentApp.test.js +1 -0
- package/dist/example.js +4 -4
- package/dist/expressServer.d.ts +0 -22
- package/dist/expressServer.js +1 -125
- package/dist/expressServer.test.js +90 -91
- package/dist/githubAuth.test.js +22 -22
- package/dist/logger.d.ts +154 -0
- package/dist/logger.js +445 -26
- package/dist/logger.test.js +435 -0
- package/dist/middleware.d.ts +7 -0
- package/dist/middleware.js +58 -1
- package/dist/middleware.test.js +159 -0
- package/dist/models/consentForm.js +2 -1
- package/dist/models/consentResponse.js +2 -1
- package/dist/models/versionConfig.js +2 -1
- package/dist/openApi.test.js +10 -17
- package/dist/openApiBuilder.d.ts +18 -0
- package/dist/openApiBuilder.js +21 -0
- package/dist/openApiBuilder.test.js +34 -10
- package/dist/permissions.test.js +10 -43
- package/dist/populate.test.js +10 -42
- package/dist/realtime/changeStreamWatcher.d.ts +4 -4
- package/dist/realtime/changeStreamWatcher.js +2 -4
- package/dist/realtime/queryMatcher.d.ts +1 -1
- package/dist/realtime/queryMatcher.js +39 -14
- package/dist/realtime/types.d.ts +3 -3
- package/dist/requestContext.d.ts +61 -0
- package/dist/requestContext.js +74 -0
- package/dist/secretProviders.test.js +335 -0
- package/dist/syncConsents.test.js +2 -2
- package/dist/terrenoApp.d.ts +27 -15
- package/dist/terrenoApp.js +24 -14
- package/dist/terrenoApp.test.js +52 -0
- package/dist/tests/bunSetup.js +66 -262
- package/dist/tests/createTestData.d.ts +9 -0
- package/dist/tests/createTestData.js +272 -0
- package/dist/tests/models.d.ts +71 -0
- package/dist/tests/models.js +134 -0
- package/dist/tests/mongoTestSetup.d.ts +7 -0
- package/dist/tests/mongoTestSetup.js +150 -0
- package/dist/tests/testEnv.d.ts +0 -0
- package/dist/tests/testEnv.js +6 -0
- package/dist/tests/testHelper.d.ts +22 -0
- package/dist/tests/testHelper.js +115 -0
- package/dist/tests/types.d.ts +29 -0
- package/dist/tests/types.js +2 -0
- package/dist/tests.d.ts +10 -78
- package/dist/tests.js +24 -241
- package/dist/transformers.test.js +14 -50
- package/package.json +18 -4
- package/src/__snapshots__/openApiBuilder.test.ts.snap +1 -0
- package/src/__tests__/versionCheckPlugin.test.ts +43 -15
- package/src/actions.openApi.test.ts +12 -10
- package/src/api.query.test.ts +24 -1
- package/src/api.test.ts +169 -0
- package/src/api.ts +71 -0
- package/src/auth.test.ts +287 -39
- package/src/betterAuth.ts +1 -1
- package/src/consentApp.test.ts +1 -0
- package/src/example.ts +4 -4
- package/src/expressServer.test.ts +82 -85
- package/src/expressServer.ts +1 -213
- package/src/githubAuth.test.ts +22 -22
- package/src/logger.test.ts +466 -1
- package/src/logger.ts +477 -14
- package/src/middleware.test.ts +74 -2
- package/src/middleware.ts +57 -0
- package/src/models/consentForm.ts +3 -4
- package/src/models/consentResponse.ts +6 -4
- package/src/models/versionConfig.ts +3 -4
- package/src/openApi.test.ts +10 -17
- package/src/openApiBuilder.test.ts +27 -10
- package/src/openApiBuilder.ts +24 -0
- package/src/permissions.test.ts +8 -23
- package/src/populate.test.ts +7 -22
- package/src/realtime/changeStreamWatcher.ts +15 -10
- package/src/realtime/queryMatcher.ts +54 -27
- package/src/realtime/types.ts +4 -4
- package/src/requestContext.ts +86 -0
- package/src/secretProviders.test.ts +219 -1
- package/src/syncConsents.test.ts +1 -1
- package/src/terrenoApp.test.ts +38 -0
- package/src/terrenoApp.ts +37 -15
- package/src/tests/bunSetup.ts +22 -236
- package/src/tests/createTestData.ts +176 -0
- package/src/tests/models.ts +164 -0
- package/src/tests/mongoTestSetup.ts +69 -0
- package/src/tests/testEnv.ts +4 -0
- package/src/tests/testHelper.ts +57 -0
- package/src/tests/types.ts +35 -0
- package/src/tests.ts +40 -231
- package/src/transformers.test.ts +11 -30
- package/tsconfig.typedoc.json +4 -0
- package/dist/tests/index.d.ts +0 -1
- package/dist/tests/index.js +0 -17
- package/src/tests/index.ts +0 -1
package/dist/middleware.test.js
CHANGED
|
@@ -32,10 +32,52 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
45
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
46
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
47
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
48
|
+
function step(op) {
|
|
49
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
50
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
51
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
52
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
53
|
+
switch (op[0]) {
|
|
54
|
+
case 0: case 1: t = op; break;
|
|
55
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
56
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
57
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
58
|
+
default:
|
|
59
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
60
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
61
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
62
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
63
|
+
if (t[2]) _.ops.pop();
|
|
64
|
+
_.trys.pop(); continue;
|
|
65
|
+
}
|
|
66
|
+
op = body.call(thisArg, _);
|
|
67
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
68
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
72
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
73
|
+
};
|
|
35
74
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
75
|
var bun_test_1 = require("bun:test");
|
|
37
76
|
var Sentry = __importStar(require("@sentry/bun"));
|
|
77
|
+
var express_1 = __importDefault(require("express"));
|
|
78
|
+
var supertest_1 = __importDefault(require("supertest"));
|
|
38
79
|
var middleware_1 = require("./middleware");
|
|
80
|
+
var requestContext_1 = require("./requestContext");
|
|
39
81
|
var buildReq = function (headers) {
|
|
40
82
|
return {
|
|
41
83
|
get: function (name) { return headers[name]; },
|
|
@@ -80,3 +122,120 @@ var buildNext = function () { return (0, bun_test_1.mock)(function () { }); };
|
|
|
80
122
|
(0, bun_test_1.expect)(next.mock.calls[0]).toHaveLength(0);
|
|
81
123
|
});
|
|
82
124
|
});
|
|
125
|
+
(0, bun_test_1.describe)("jsonResponseRequestIdMiddleware", function () {
|
|
126
|
+
var buildStackedApp = function () {
|
|
127
|
+
var app = (0, express_1.default)();
|
|
128
|
+
app.use(requestContext_1.requestContextMiddleware);
|
|
129
|
+
app.use(middleware_1.jsonResponseRequestIdMiddleware);
|
|
130
|
+
app.get("/object", function (_req, res) {
|
|
131
|
+
return res.json({ hello: "world" });
|
|
132
|
+
});
|
|
133
|
+
app.get("/array", function (_req, res) {
|
|
134
|
+
return res.json([1, 2]);
|
|
135
|
+
});
|
|
136
|
+
app.get("/openapi.json", function (_req, res) {
|
|
137
|
+
return res.json({ openapi: "3.0.0", paths: {} });
|
|
138
|
+
});
|
|
139
|
+
app.get("/openapi/components/schemas/Food.json", function (_req, res) {
|
|
140
|
+
return res.json({ description: "A food", type: "object" });
|
|
141
|
+
});
|
|
142
|
+
app.get("/openapi/validate", function (_req, res) {
|
|
143
|
+
return res.json({ document: { openapi: "3.0.0" }, valid: true });
|
|
144
|
+
});
|
|
145
|
+
return app;
|
|
146
|
+
};
|
|
147
|
+
(0, bun_test_1.it)("adds requestId to object JSON bodies and matches X-Request-ID header", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
148
|
+
var app, res;
|
|
149
|
+
return __generator(this, function (_a) {
|
|
150
|
+
switch (_a.label) {
|
|
151
|
+
case 0:
|
|
152
|
+
app = buildStackedApp();
|
|
153
|
+
return [4 /*yield*/, (0, supertest_1.default)(app).get("/object").expect(200)];
|
|
154
|
+
case 1:
|
|
155
|
+
res = _a.sent();
|
|
156
|
+
(0, bun_test_1.expect)(res.body.hello).toBe("world");
|
|
157
|
+
(0, bun_test_1.expect)(res.body.requestId).toBeDefined();
|
|
158
|
+
(0, bun_test_1.expect)(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
159
|
+
return [2 /*return*/];
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}); });
|
|
163
|
+
(0, bun_test_1.it)("does not wrap JSON array bodies", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
164
|
+
var app, res;
|
|
165
|
+
return __generator(this, function (_a) {
|
|
166
|
+
switch (_a.label) {
|
|
167
|
+
case 0:
|
|
168
|
+
app = buildStackedApp();
|
|
169
|
+
return [4 /*yield*/, (0, supertest_1.default)(app).get("/array").expect(200)];
|
|
170
|
+
case 1:
|
|
171
|
+
res = _a.sent();
|
|
172
|
+
(0, bun_test_1.expect)(res.body).toEqual([1, 2]);
|
|
173
|
+
(0, bun_test_1.expect)(res.headers["x-request-id"]).toBeDefined();
|
|
174
|
+
return [2 /*return*/];
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}); });
|
|
178
|
+
(0, bun_test_1.it)("does not inject requestId into GET /openapi.json bodies", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
179
|
+
var app, res;
|
|
180
|
+
return __generator(this, function (_a) {
|
|
181
|
+
switch (_a.label) {
|
|
182
|
+
case 0:
|
|
183
|
+
app = buildStackedApp();
|
|
184
|
+
return [4 /*yield*/, (0, supertest_1.default)(app).get("/openapi.json").expect(200)];
|
|
185
|
+
case 1:
|
|
186
|
+
res = _a.sent();
|
|
187
|
+
(0, bun_test_1.expect)(res.body).toEqual({ openapi: "3.0.0", paths: {} });
|
|
188
|
+
(0, bun_test_1.expect)(res.body.requestId).toBeUndefined();
|
|
189
|
+
return [2 /*return*/];
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}); });
|
|
193
|
+
(0, bun_test_1.it)("does not inject requestId into GET /openapi/components/...json bodies", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
194
|
+
var app, res;
|
|
195
|
+
return __generator(this, function (_a) {
|
|
196
|
+
switch (_a.label) {
|
|
197
|
+
case 0:
|
|
198
|
+
app = buildStackedApp();
|
|
199
|
+
return [4 /*yield*/, (0, supertest_1.default)(app).get("/openapi/components/schemas/Food.json").expect(200)];
|
|
200
|
+
case 1:
|
|
201
|
+
res = _a.sent();
|
|
202
|
+
(0, bun_test_1.expect)(res.body).toEqual({ description: "A food", type: "object" });
|
|
203
|
+
(0, bun_test_1.expect)(res.body.requestId).toBeUndefined();
|
|
204
|
+
return [2 /*return*/];
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}); });
|
|
208
|
+
(0, bun_test_1.it)("does not inject requestId into GET /openapi/validate bodies", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
209
|
+
var app, res;
|
|
210
|
+
return __generator(this, function (_a) {
|
|
211
|
+
switch (_a.label) {
|
|
212
|
+
case 0:
|
|
213
|
+
app = buildStackedApp();
|
|
214
|
+
return [4 /*yield*/, (0, supertest_1.default)(app).get("/openapi/validate").expect(200)];
|
|
215
|
+
case 1:
|
|
216
|
+
res = _a.sent();
|
|
217
|
+
(0, bun_test_1.expect)(res.body).toEqual({ document: { openapi: "3.0.0" }, valid: true });
|
|
218
|
+
(0, bun_test_1.expect)(res.body.requestId).toBeUndefined();
|
|
219
|
+
return [2 /*return*/];
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}); });
|
|
223
|
+
(0, bun_test_1.it)("uses incoming X-Request-ID on wrapped object responses", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
224
|
+
var app, res;
|
|
225
|
+
return __generator(this, function (_a) {
|
|
226
|
+
switch (_a.label) {
|
|
227
|
+
case 0:
|
|
228
|
+
app = buildStackedApp();
|
|
229
|
+
return [4 /*yield*/, (0, supertest_1.default)(app)
|
|
230
|
+
.get("/object")
|
|
231
|
+
.set("X-Request-ID", "client-rid-99")
|
|
232
|
+
.expect(200)];
|
|
233
|
+
case 1:
|
|
234
|
+
res = _a.sent();
|
|
235
|
+
(0, bun_test_1.expect)(res.body.requestId).toBe("client-rid-99");
|
|
236
|
+
(0, bun_test_1.expect)(res.headers["x-request-id"]).toBe("client-rid-99");
|
|
237
|
+
return [2 /*return*/];
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}); });
|
|
241
|
+
});
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
|
+
var _a;
|
|
5
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
7
|
exports.ConsentForm = void 0;
|
|
7
8
|
var mongoose_1 = __importDefault(require("mongoose"));
|
|
@@ -120,4 +121,4 @@ consentFormSchema.plugin(plugins_1.createdUpdatedPlugin);
|
|
|
120
121
|
consentFormSchema.plugin(plugins_1.isDeletedPlugin);
|
|
121
122
|
consentFormSchema.plugin(plugins_1.findOneOrNone);
|
|
122
123
|
consentFormSchema.plugin(plugins_1.findExactlyOne);
|
|
123
|
-
exports.ConsentForm = mongoose_1.default.model("ConsentForm", consentFormSchema);
|
|
124
|
+
exports.ConsentForm = (_a = mongoose_1.default.models.ConsentForm) !== null && _a !== void 0 ? _a : mongoose_1.default.model("ConsentForm", consentFormSchema);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
|
+
var _a;
|
|
5
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
7
|
exports.ConsentResponse = void 0;
|
|
7
8
|
var mongoose_1 = __importDefault(require("mongoose"));
|
|
@@ -70,4 +71,4 @@ consentResponseSchema.plugin(plugins_1.createdUpdatedPlugin);
|
|
|
70
71
|
consentResponseSchema.plugin(plugins_1.isDeletedPlugin);
|
|
71
72
|
consentResponseSchema.plugin(plugins_1.findOneOrNone);
|
|
72
73
|
consentResponseSchema.plugin(plugins_1.findExactlyOne);
|
|
73
|
-
exports.ConsentResponse = mongoose_1.default.model("ConsentResponse", consentResponseSchema);
|
|
74
|
+
exports.ConsentResponse = (_a = mongoose_1.default.models.ConsentResponse) !== null && _a !== void 0 ? _a : mongoose_1.default.model("ConsentResponse", consentResponseSchema);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
|
+
var _a;
|
|
5
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
7
|
exports.VersionConfig = void 0;
|
|
7
8
|
var mongoose_1 = __importDefault(require("mongoose"));
|
|
@@ -71,4 +72,4 @@ versionConfigSchema.plugin(plugins_1.createdUpdatedPlugin);
|
|
|
71
72
|
versionConfigSchema.plugin(plugins_1.isDeletedPlugin);
|
|
72
73
|
versionConfigSchema.plugin(plugins_1.findOneOrNone);
|
|
73
74
|
versionConfigSchema.plugin(plugins_1.findExactlyOne);
|
|
74
|
-
exports.VersionConfig = mongoose_1.default.model("VersionConfig", versionConfigSchema);
|
|
75
|
+
exports.VersionConfig = (_a = mongoose_1.default.models.VersionConfig) !== null && _a !== void 0 ? _a : mongoose_1.default.model("VersionConfig", versionConfigSchema);
|
package/dist/openApi.test.js
CHANGED
|
@@ -70,10 +70,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
70
70
|
var bun_test_1 = require("bun:test");
|
|
71
71
|
var supertest_1 = __importDefault(require("supertest"));
|
|
72
72
|
var api_1 = require("./api");
|
|
73
|
-
var auth_1 = require("./auth");
|
|
74
|
-
var expressServer_1 = require("./expressServer");
|
|
75
73
|
var openApi_1 = require("./openApi");
|
|
76
74
|
var permissions_1 = require("./permissions");
|
|
75
|
+
var terrenoApp_1 = require("./terrenoApp");
|
|
77
76
|
var tests_1 = require("./tests");
|
|
78
77
|
function getMessageSummaryOpenApiMiddleware(options) {
|
|
79
78
|
if (!options.openApi) {
|
|
@@ -136,13 +135,11 @@ function addRoutes(router, options) {
|
|
|
136
135
|
return __generator(this, function (_a) {
|
|
137
136
|
process.env.REFRESH_TOKEN_SECRET = "testsecret1234";
|
|
138
137
|
process.env.ENABLE_SWAGGER = "true";
|
|
139
|
-
app =
|
|
140
|
-
|
|
138
|
+
app = new terrenoApp_1.TerrenoApp({
|
|
139
|
+
configureApp: addRoutes,
|
|
141
140
|
skipListen: true,
|
|
142
141
|
userModel: tests_1.UserModel,
|
|
143
|
-
});
|
|
144
|
-
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
145
|
-
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
142
|
+
}).build();
|
|
146
143
|
return [2 /*return*/];
|
|
147
144
|
});
|
|
148
145
|
}); });
|
|
@@ -342,13 +339,11 @@ function addRoutesPopulate(router, options) {
|
|
|
342
339
|
return __generator(this, function (_a) {
|
|
343
340
|
process.env.REFRESH_TOKEN_SECRET = "testsecret1234";
|
|
344
341
|
process.env.ENABLE_SWAGGER = "false";
|
|
345
|
-
app =
|
|
346
|
-
|
|
342
|
+
app = new terrenoApp_1.TerrenoApp({
|
|
343
|
+
configureApp: addRoutes,
|
|
347
344
|
skipListen: true,
|
|
348
345
|
userModel: tests_1.UserModel,
|
|
349
|
-
});
|
|
350
|
-
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
351
|
-
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
346
|
+
}).build();
|
|
352
347
|
return [2 /*return*/];
|
|
353
348
|
});
|
|
354
349
|
}); });
|
|
@@ -371,13 +366,11 @@ function addRoutesPopulate(router, options) {
|
|
|
371
366
|
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
372
367
|
return __generator(this, function (_a) {
|
|
373
368
|
process.env.REFRESH_TOKEN_SECRET = "testsecret1234";
|
|
374
|
-
app =
|
|
375
|
-
|
|
369
|
+
app = new terrenoApp_1.TerrenoApp({
|
|
370
|
+
configureApp: addRoutesPopulate,
|
|
376
371
|
skipListen: true,
|
|
377
372
|
userModel: tests_1.UserModel,
|
|
378
|
-
});
|
|
379
|
-
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
380
|
-
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
373
|
+
}).build();
|
|
381
374
|
return [2 /*return*/];
|
|
382
375
|
});
|
|
383
376
|
}); });
|
package/dist/openApiBuilder.d.ts
CHANGED
|
@@ -272,6 +272,24 @@ export declare class OpenApiMiddlewareBuilder {
|
|
|
272
272
|
* ```
|
|
273
273
|
*/
|
|
274
274
|
withSummary(summary: string): this;
|
|
275
|
+
/**
|
|
276
|
+
* Sets an explicit `operationId` for the OpenAPI operation.
|
|
277
|
+
*
|
|
278
|
+
* The `operationId` is a unique string used to identify an operation. Client and SDK
|
|
279
|
+
* generators (e.g. RTK Query codegen) derive generated function and hook names from it,
|
|
280
|
+
* so setting it keeps generated names stable and readable for routes whose URL path would
|
|
281
|
+
* otherwise produce unwieldy names (e.g. deeply nested routes). It must be unique across
|
|
282
|
+
* the whole OpenAPI document.
|
|
283
|
+
*
|
|
284
|
+
* @param operationId - Unique operation identifier (e.g. "getUserStats")
|
|
285
|
+
* @returns The builder instance for chaining
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```typescript
|
|
289
|
+
* builder.withOperationId("getUserStats");
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
withOperationId(operationId: string): this;
|
|
275
293
|
/**
|
|
276
294
|
* Sets the description for the OpenAPI operation.
|
|
277
295
|
*
|
package/dist/openApiBuilder.js
CHANGED
|
@@ -121,6 +121,27 @@ var OpenApiMiddlewareBuilder = /** @class */ (function () {
|
|
|
121
121
|
this.config.summary = summary;
|
|
122
122
|
return this;
|
|
123
123
|
};
|
|
124
|
+
/**
|
|
125
|
+
* Sets an explicit `operationId` for the OpenAPI operation.
|
|
126
|
+
*
|
|
127
|
+
* The `operationId` is a unique string used to identify an operation. Client and SDK
|
|
128
|
+
* generators (e.g. RTK Query codegen) derive generated function and hook names from it,
|
|
129
|
+
* so setting it keeps generated names stable and readable for routes whose URL path would
|
|
130
|
+
* otherwise produce unwieldy names (e.g. deeply nested routes). It must be unique across
|
|
131
|
+
* the whole OpenAPI document.
|
|
132
|
+
*
|
|
133
|
+
* @param operationId - Unique operation identifier (e.g. "getUserStats")
|
|
134
|
+
* @returns The builder instance for chaining
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* builder.withOperationId("getUserStats");
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
OpenApiMiddlewareBuilder.prototype.withOperationId = function (operationId) {
|
|
142
|
+
this.config.operationId = operationId;
|
|
143
|
+
return this;
|
|
144
|
+
};
|
|
124
145
|
/**
|
|
125
146
|
* Sets the description for the OpenAPI operation.
|
|
126
147
|
*
|
|
@@ -54,10 +54,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
54
54
|
var bun_test_1 = require("bun:test");
|
|
55
55
|
var supertest_1 = __importDefault(require("supertest"));
|
|
56
56
|
var api_1 = require("./api");
|
|
57
|
-
var auth_1 = require("./auth");
|
|
58
|
-
var expressServer_1 = require("./expressServer");
|
|
59
57
|
var openApiBuilder_1 = require("./openApiBuilder");
|
|
60
58
|
var permissions_1 = require("./permissions");
|
|
59
|
+
var terrenoApp_1 = require("./terrenoApp");
|
|
61
60
|
var tests_1 = require("./tests");
|
|
62
61
|
function addRoutesWithBuilder(router, options) {
|
|
63
62
|
var _this = this;
|
|
@@ -65,6 +64,7 @@ function addRoutesWithBuilder(router, options) {
|
|
|
65
64
|
var statsMiddleware = (0, openApiBuilder_1.createOpenApiBuilder)(options !== null && options !== void 0 ? options : {})
|
|
66
65
|
.withTags(["Stats"])
|
|
67
66
|
.withSummary("Get food statistics")
|
|
67
|
+
.withOperationId("getFoodStats")
|
|
68
68
|
.withDescription("Returns aggregated statistics about food items")
|
|
69
69
|
.withQueryParameter("category", { type: "string" }, {
|
|
70
70
|
description: "Filter by food category",
|
|
@@ -168,13 +168,11 @@ function addRoutesWithBuilder(router, options) {
|
|
|
168
168
|
return __generator(this, function (_a) {
|
|
169
169
|
process.env.REFRESH_TOKEN_SECRET = "testsecret1234";
|
|
170
170
|
process.env.ENABLE_SWAGGER = "true";
|
|
171
|
-
app =
|
|
172
|
-
|
|
171
|
+
app = new terrenoApp_1.TerrenoApp({
|
|
172
|
+
configureApp: addRoutesWithBuilder,
|
|
173
173
|
skipListen: true,
|
|
174
174
|
userModel: tests_1.UserModel,
|
|
175
|
-
});
|
|
176
|
-
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
177
|
-
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
175
|
+
}).build();
|
|
178
176
|
return [2 /*return*/];
|
|
179
177
|
});
|
|
180
178
|
}); });
|
|
@@ -223,6 +221,21 @@ function addRoutesWithBuilder(router, options) {
|
|
|
223
221
|
}
|
|
224
222
|
});
|
|
225
223
|
}); });
|
|
224
|
+
(0, bun_test_1.it)("includes the explicit operationId in OpenAPI spec", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
225
|
+
var res, statsPath;
|
|
226
|
+
return __generator(this, function (_a) {
|
|
227
|
+
switch (_a.label) {
|
|
228
|
+
case 0:
|
|
229
|
+
server = (0, supertest_1.default)(app);
|
|
230
|
+
return [4 /*yield*/, server.get("/openapi.json").expect(200)];
|
|
231
|
+
case 1:
|
|
232
|
+
res = _a.sent();
|
|
233
|
+
statsPath = res.body.paths["/food/stats"];
|
|
234
|
+
(0, bun_test_1.expect)(statsPath.get.operationId).toBe("getFoodStats");
|
|
235
|
+
return [2 /*return*/];
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}); });
|
|
226
239
|
(0, bun_test_1.it)("includes request body schema in OpenAPI spec", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
227
240
|
var res, reportsPath, requestBody, schema;
|
|
228
241
|
return __generator(this, function (_a) {
|
|
@@ -367,7 +380,11 @@ function addRoutesWithBuilder(router, options) {
|
|
|
367
380
|
return [4 /*yield*/, server.get("/food/stats").expect(200)];
|
|
368
381
|
case 1:
|
|
369
382
|
res = _a.sent();
|
|
370
|
-
(0, bun_test_1.expect)(res.body).toEqual({
|
|
383
|
+
(0, bun_test_1.expect)(res.body).toEqual({
|
|
384
|
+
avgCalories: 250,
|
|
385
|
+
count: 10,
|
|
386
|
+
requestId: res.headers["x-request-id"],
|
|
387
|
+
});
|
|
371
388
|
return [2 /*return*/];
|
|
372
389
|
}
|
|
373
390
|
});
|
|
@@ -384,7 +401,10 @@ function addRoutesWithBuilder(router, options) {
|
|
|
384
401
|
.expect(201)];
|
|
385
402
|
case 1:
|
|
386
403
|
res = _a.sent();
|
|
387
|
-
(0, bun_test_1.expect)(res.body).toEqual({
|
|
404
|
+
(0, bun_test_1.expect)(res.body).toEqual({
|
|
405
|
+
reportId: "report-123",
|
|
406
|
+
requestId: res.headers["x-request-id"],
|
|
407
|
+
});
|
|
388
408
|
return [2 /*return*/];
|
|
389
409
|
}
|
|
390
410
|
});
|
|
@@ -414,7 +434,11 @@ function addRoutesWithBuilder(router, options) {
|
|
|
414
434
|
return [4 /*yield*/, server.get("/food/categories/cat-123").expect(200)];
|
|
415
435
|
case 1:
|
|
416
436
|
res = _a.sent();
|
|
417
|
-
(0, bun_test_1.expect)(res.body).toEqual({
|
|
437
|
+
(0, bun_test_1.expect)(res.body).toEqual({
|
|
438
|
+
id: "cat-123",
|
|
439
|
+
name: "Fruits",
|
|
440
|
+
requestId: res.headers["x-request-id"],
|
|
441
|
+
});
|
|
418
442
|
return [2 /*return*/];
|
|
419
443
|
}
|
|
420
444
|
});
|
package/dist/permissions.test.js
CHANGED
|
@@ -35,22 +35,6 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
35
35
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
-
var __read = (this && this.__read) || function (o, n) {
|
|
39
|
-
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
40
|
-
if (!m) return o;
|
|
41
|
-
var i = m.call(o), r, ar = [], e;
|
|
42
|
-
try {
|
|
43
|
-
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
44
|
-
}
|
|
45
|
-
catch (error) { e = { error: error }; }
|
|
46
|
-
finally {
|
|
47
|
-
try {
|
|
48
|
-
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
49
|
-
}
|
|
50
|
-
finally { if (e) throw e.error; }
|
|
51
|
-
}
|
|
52
|
-
return ar;
|
|
53
|
-
};
|
|
54
38
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
55
39
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
56
40
|
};
|
|
@@ -66,30 +50,13 @@ var tests_1 = require("./tests");
|
|
|
66
50
|
var server;
|
|
67
51
|
var app;
|
|
68
52
|
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
switch (_b.label) {
|
|
53
|
+
return __generator(this, function (_a) {
|
|
54
|
+
switch (_a.label) {
|
|
72
55
|
case 0:
|
|
73
56
|
process.env.REFRESH_TOKEN_SECRET = "testsecret1234";
|
|
74
|
-
return [4 /*yield*/, (0, tests_1.
|
|
57
|
+
return [4 /*yield*/, (0, tests_1.setupTestData)()];
|
|
75
58
|
case 1:
|
|
76
|
-
_a
|
|
77
|
-
return [4 /*yield*/, Promise.all([
|
|
78
|
-
tests_1.FoodModel.create({
|
|
79
|
-
calories: 1,
|
|
80
|
-
created: new Date(),
|
|
81
|
-
name: "Spinach",
|
|
82
|
-
ownerId: notAdmin._id,
|
|
83
|
-
}),
|
|
84
|
-
tests_1.FoodModel.create({
|
|
85
|
-
calories: 100,
|
|
86
|
-
created: Date.now() - 10,
|
|
87
|
-
name: "Apple",
|
|
88
|
-
ownerId: admin._id,
|
|
89
|
-
}),
|
|
90
|
-
])];
|
|
91
|
-
case 2:
|
|
92
|
-
_b.sent();
|
|
59
|
+
_a.sent();
|
|
93
60
|
app = (0, tests_1.getBaseServer)();
|
|
94
61
|
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
95
62
|
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
@@ -125,7 +92,7 @@ var tests_1 = require("./tests");
|
|
|
125
92
|
case 0: return [4 /*yield*/, server.get("/food").expect(200)];
|
|
126
93
|
case 1:
|
|
127
94
|
res = _a.sent();
|
|
128
|
-
(0, bun_test_1.expect)(res.body.data).toHaveLength(
|
|
95
|
+
(0, bun_test_1.expect)(res.body.data).toHaveLength(4);
|
|
129
96
|
return [2 /*return*/];
|
|
130
97
|
}
|
|
131
98
|
});
|
|
@@ -137,7 +104,7 @@ var tests_1 = require("./tests");
|
|
|
137
104
|
case 0: return [4 /*yield*/, server.get("/food").expect(200)];
|
|
138
105
|
case 1:
|
|
139
106
|
res = _a.sent();
|
|
140
|
-
(0, bun_test_1.expect)(res.body.data).toHaveLength(
|
|
107
|
+
(0, bun_test_1.expect)(res.body.data).toHaveLength(4);
|
|
141
108
|
return [4 /*yield*/, server.get("/food/".concat(res.body.data[0]._id)).expect(200)];
|
|
142
109
|
case 2:
|
|
143
110
|
res2 = _a.sent();
|
|
@@ -213,7 +180,7 @@ var tests_1 = require("./tests");
|
|
|
213
180
|
case 0: return [4 /*yield*/, agent.get("/food").expect(200)];
|
|
214
181
|
case 1:
|
|
215
182
|
res = _a.sent();
|
|
216
|
-
(0, bun_test_1.expect)(res.body.data).toHaveLength(
|
|
183
|
+
(0, bun_test_1.expect)(res.body.data).toHaveLength(4);
|
|
217
184
|
return [2 /*return*/];
|
|
218
185
|
}
|
|
219
186
|
});
|
|
@@ -225,7 +192,7 @@ var tests_1 = require("./tests");
|
|
|
225
192
|
case 0: return [4 /*yield*/, agent.get("/food").expect(200)];
|
|
226
193
|
case 1:
|
|
227
194
|
res = _a.sent();
|
|
228
|
-
(0, bun_test_1.expect)(res.body.data).toHaveLength(
|
|
195
|
+
(0, bun_test_1.expect)(res.body.data).toHaveLength(4);
|
|
229
196
|
return [4 /*yield*/, server.get("/food/".concat(res.body.data[0]._id)).expect(200)];
|
|
230
197
|
case 2:
|
|
231
198
|
res2 = _a.sent();
|
|
@@ -326,7 +293,7 @@ var tests_1 = require("./tests");
|
|
|
326
293
|
case 0: return [4 /*yield*/, agent.get("/food")];
|
|
327
294
|
case 1:
|
|
328
295
|
res = _a.sent();
|
|
329
|
-
(0, bun_test_1.expect)(res.body.data).toHaveLength(
|
|
296
|
+
(0, bun_test_1.expect)(res.body.data).toHaveLength(4);
|
|
330
297
|
return [2 /*return*/];
|
|
331
298
|
}
|
|
332
299
|
});
|
|
@@ -338,7 +305,7 @@ var tests_1 = require("./tests");
|
|
|
338
305
|
case 0: return [4 /*yield*/, agent.get("/food")];
|
|
339
306
|
case 1:
|
|
340
307
|
res = _a.sent();
|
|
341
|
-
(0, bun_test_1.expect)(res.body.data).toHaveLength(
|
|
308
|
+
(0, bun_test_1.expect)(res.body.data).toHaveLength(4);
|
|
342
309
|
return [4 /*yield*/, agent.get("/food/".concat(res.body.data[0]._id))];
|
|
343
310
|
case 2:
|
|
344
311
|
res2 = _a.sent();
|
package/dist/populate.test.js
CHANGED
|
@@ -68,22 +68,6 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
68
68
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
69
69
|
}
|
|
70
70
|
};
|
|
71
|
-
var __read = (this && this.__read) || function (o, n) {
|
|
72
|
-
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
73
|
-
if (!m) return o;
|
|
74
|
-
var i = m.call(o), r, ar = [], e;
|
|
75
|
-
try {
|
|
76
|
-
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
77
|
-
}
|
|
78
|
-
catch (error) { e = { error: error }; }
|
|
79
|
-
finally {
|
|
80
|
-
try {
|
|
81
|
-
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
82
|
-
}
|
|
83
|
-
finally { if (e) throw e.error; }
|
|
84
|
-
}
|
|
85
|
-
return ar;
|
|
86
|
-
};
|
|
87
71
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
88
72
|
// biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
|
|
89
73
|
var bun_test_1 = require("bun:test");
|
|
@@ -96,31 +80,15 @@ var tests_1 = require("./tests");
|
|
|
96
80
|
// noExplicitAny: typing as HydratedDocument<Food> causes cascading errors on populated field access patterns (e.g. populated.ownerId.name)
|
|
97
81
|
var spinach;
|
|
98
82
|
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
99
|
-
var
|
|
100
|
-
return __generator(this, function (
|
|
101
|
-
switch (
|
|
102
|
-
case 0: return [4 /*yield*/, (0, tests_1.
|
|
83
|
+
var testData;
|
|
84
|
+
return __generator(this, function (_a) {
|
|
85
|
+
switch (_a.label) {
|
|
86
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupTestData)()];
|
|
103
87
|
case 1:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
created: new Date("2021-12-03T00:00:20.000Z"),
|
|
109
|
-
eatenBy: [admin._id],
|
|
110
|
-
hidden: false,
|
|
111
|
-
likesIds: [
|
|
112
|
-
{ likes: true, userId: admin._id },
|
|
113
|
-
{ likes: false, userId: notAdmin._id },
|
|
114
|
-
],
|
|
115
|
-
name: "Spinach",
|
|
116
|
-
ownerId: admin._id,
|
|
117
|
-
source: {
|
|
118
|
-
name: "Brand",
|
|
119
|
-
},
|
|
120
|
-
}),
|
|
121
|
-
])];
|
|
122
|
-
case 2:
|
|
123
|
-
_b = __read.apply(void 0, [_c.sent(), 1]), spinach = _b[0];
|
|
88
|
+
testData = _a.sent();
|
|
89
|
+
admin = testData.users.admin;
|
|
90
|
+
notAdmin = testData.users.notAdmin;
|
|
91
|
+
spinach = testData.foods.spinach;
|
|
124
92
|
return [2 /*return*/];
|
|
125
93
|
}
|
|
126
94
|
});
|
|
@@ -139,7 +107,7 @@ var tests_1 = require("./tests");
|
|
|
139
107
|
return [4 /*yield*/, populated.populate("likesIds.userId")];
|
|
140
108
|
case 3:
|
|
141
109
|
populated = _c.sent();
|
|
142
|
-
(0, bun_test_1.expect)(populated.ownerId.name).toBe("Admin");
|
|
110
|
+
(0, bun_test_1.expect)(populated.ownerId.name).toBe("Not Admin");
|
|
143
111
|
(0, bun_test_1.expect)(populated.eatenBy[0].id).toBe(admin.id);
|
|
144
112
|
(0, bun_test_1.expect)(populated.eatenBy[0].name).toBe("Admin");
|
|
145
113
|
(0, bun_test_1.expect)(populated.likesIds[0].userId.id).toBe(admin.id);
|
|
@@ -148,7 +116,7 @@ var tests_1 = require("./tests");
|
|
|
148
116
|
(0, bun_test_1.expect)(populated.likesIds[1].userId.name).toBe("Not Admin");
|
|
149
117
|
unpopulated = (0, populate_1.unpopulate)(populated, "ownerId");
|
|
150
118
|
(0, bun_test_1.expect)(spinach.ownerId.name).toBeUndefined();
|
|
151
|
-
(0, bun_test_1.expect)(unpopulated.ownerId.toString()).toBe(
|
|
119
|
+
(0, bun_test_1.expect)(unpopulated.ownerId.toString()).toBe(notAdmin.id);
|
|
152
120
|
// Ensure nothing else was touched.
|
|
153
121
|
(0, bun_test_1.expect)(populated.likesIds[0].userId.id).toBe(admin.id);
|
|
154
122
|
(0, bun_test_1.expect)(populated.likesIds[0].userId.name).toBe("Admin");
|
|
@@ -21,7 +21,7 @@ export declare const mapOperationType: (operationType: string, change: ChangeStr
|
|
|
21
21
|
* Determine which Socket.io rooms to emit to based on the room strategy.
|
|
22
22
|
* Exported for testing.
|
|
23
23
|
*/
|
|
24
|
-
export declare const resolveRooms: (entry: RealtimeRegistryEntry, doc:
|
|
24
|
+
export declare const resolveRooms: (entry: RealtimeRegistryEntry, doc: Record<string, unknown>, method: string) => string[];
|
|
25
25
|
/**
|
|
26
26
|
* Ensure serialized documents include `id` to match REST API responses.
|
|
27
27
|
* Change stream fullDocument payloads are raw BSON objects with `_id` only.
|
|
@@ -43,8 +43,8 @@ export declare const ensureApiId: (data: unknown) => unknown;
|
|
|
43
43
|
* would risk leaking unsanitized fields (e.g. `hash`/`salt`) that the handler
|
|
44
44
|
* was supposed to strip.
|
|
45
45
|
*/
|
|
46
|
-
export declare const serializeDoc: (entry: RealtimeRegistryEntry, doc:
|
|
47
|
-
export declare const emitToAuthorizedRoom: (io: Server, room: string, event: RealtimeEvent, entry: RealtimeRegistryEntry, fullDocument:
|
|
46
|
+
export declare const serializeDoc: (entry: RealtimeRegistryEntry, doc: Record<string, unknown>, method: "create" | "update" | "delete", user?: User) => Promise<unknown>;
|
|
47
|
+
export declare const emitToAuthorizedRoom: (io: Server, room: string, event: RealtimeEvent, entry: RealtimeRegistryEntry, fullDocument: Record<string, unknown> | undefined, logDebug: (msg: string) => void) => Promise<void>;
|
|
48
48
|
/**
|
|
49
49
|
* Emit a sync event to document-specific and query rooms.
|
|
50
50
|
*
|
|
@@ -61,7 +61,7 @@ export declare const emitToAuthorizedRoom: (io: Server, room: string, event: Rea
|
|
|
61
61
|
*
|
|
62
62
|
* Exported for testing.
|
|
63
63
|
*/
|
|
64
|
-
export declare const emitToDocumentAndQueryRooms: (io: Server, collection: string, event: RealtimeEvent, fullDocument:
|
|
64
|
+
export declare const emitToDocumentAndQueryRooms: (io: Server, collection: string, event: RealtimeEvent, fullDocument: Record<string, unknown> | undefined, logDebug: (msg: string) => void, entry?: RealtimeRegistryEntry) => Promise<void>;
|
|
65
65
|
/**
|
|
66
66
|
* Start watching MongoDB change streams and emitting real-time events.
|
|
67
67
|
*/
|