@rpcbase/server 0.594.0 → 0.595.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/convertHeifToWebp-BT1n72DQ.js +148 -0
  2. package/dist/convertHeifToWebp-BT1n72DQ.js.map +1 -0
  3. package/dist/email-Co1GNjlT.js +7204 -0
  4. package/dist/email-Co1GNjlT.js.map +1 -0
  5. package/dist/handler-BqlcQ9sE.js +245 -0
  6. package/dist/handler-BqlcQ9sE.js.map +1 -0
  7. package/dist/handler-C92_gJkX.js +743 -0
  8. package/dist/handler-C92_gJkX.js.map +1 -0
  9. package/dist/handler-CapJTGzc.js +795 -0
  10. package/dist/handler-CapJTGzc.js.map +1 -0
  11. package/dist/handler-Fk-5fOSm.js +119 -0
  12. package/dist/handler-Fk-5fOSm.js.map +1 -0
  13. package/dist/htmlTemplate.d.ts +16 -0
  14. package/dist/htmlTemplate.d.ts.map +1 -0
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +5140 -5286
  18. package/dist/index.js.map +1 -1
  19. package/dist/notifications.js +273 -382
  20. package/dist/notifications.js.map +1 -1
  21. package/dist/postProcessors-iffHvje4.js +89 -0
  22. package/dist/postProcessors-iffHvje4.js.map +1 -0
  23. package/dist/queryWindow-DP8zD0lb.js +555 -0
  24. package/dist/queryWindow-DP8zD0lb.js.map +1 -0
  25. package/dist/render_resend-B1SSQ4f7.js +7 -0
  26. package/dist/render_resend-B1SSQ4f7.js.map +1 -0
  27. package/dist/rts/index.js +990 -1207
  28. package/dist/rts/index.js.map +1 -1
  29. package/dist/schemas-B2fUvgYo.js +4151 -0
  30. package/dist/schemas-B2fUvgYo.js.map +1 -0
  31. package/dist/shared-DfrVDSp0.js +79 -0
  32. package/dist/shared-DfrVDSp0.js.map +1 -0
  33. package/dist/ssrMiddleware.d.ts +7 -3
  34. package/dist/ssrMiddleware.d.ts.map +1 -1
  35. package/dist/uploads/worker.js +139 -168
  36. package/dist/uploads/worker.js.map +1 -1
  37. package/dist/uploads.js +12 -22
  38. package/dist/uploads.js.map +1 -1
  39. package/package.json +1 -1
  40. package/dist/convertHeifToWebp-C-DGXZ2k.js +0 -169
  41. package/dist/convertHeifToWebp-C-DGXZ2k.js.map +0 -1
  42. package/dist/email-BCf24GmK.js +0 -8071
  43. package/dist/email-BCf24GmK.js.map +0 -1
  44. package/dist/handler-BqoKvylN.js +0 -147
  45. package/dist/handler-BqoKvylN.js.map +0 -1
  46. package/dist/handler-CUOJ51-w.js +0 -903
  47. package/dist/handler-CUOJ51-w.js.map +0 -1
  48. package/dist/handler-D-XdgeG_.js +0 -986
  49. package/dist/handler-D-XdgeG_.js.map +0 -1
  50. package/dist/handler-F0gFTzvh.js +0 -275
  51. package/dist/handler-F0gFTzvh.js.map +0 -1
  52. package/dist/postProcessors-D27fGZP0.js +0 -107
  53. package/dist/postProcessors-D27fGZP0.js.map +0 -1
  54. package/dist/queryWindow-Cdr7K-S1.js +0 -714
  55. package/dist/queryWindow-Cdr7K-S1.js.map +0 -1
  56. package/dist/render_resend_false-MiC__Smr.js +0 -6
  57. package/dist/render_resend_false-MiC__Smr.js.map +0 -1
  58. package/dist/schemas-Drf83ni9.js +0 -4517
  59. package/dist/schemas-Drf83ni9.js.map +0 -1
  60. package/dist/shared-CJrm9Wjp.js +0 -104
  61. package/dist/shared-CJrm9Wjp.js.map +0 -1
@@ -0,0 +1,555 @@
1
+ import assert from "assert";
2
+ import { hkdfSync } from "crypto";
3
+ import { models } from "@rpcbase/db";
4
+ import { createHmac, timingSafeEqual } from "node:crypto";
5
+ import { buildAbilityFromSession, getAccessibleByQuery } from "@rpcbase/db/acl";
6
+ //#region src/getDerivedKey.ts
7
+ var getDerivedKey = (masterKey, info, length = 32, salt = "") => {
8
+ assert(masterKey?.length >= 32, "MASTER_KEY must be 32 chars or longer.");
9
+ return Buffer.from(hkdfSync("sha256", masterKey, Buffer.from(salt), Buffer.from(info), length)).toString("hex");
10
+ };
11
+ //#endregion
12
+ //#region src/proxyAuth.ts
13
+ var AUTHENTICATED_USER_ID_HEADER = "rb-authenticated-user-id";
14
+ var AUTHENTICATED_TENANT_ID_HEADER = "rb-authenticated-tenant-id";
15
+ var PROXY_AUTH_TIMESTAMP_HEADER = "rb-proxy-auth-timestamp";
16
+ var PROXY_AUTH_SIGNATURE_HEADER = "rb-proxy-auth-signature";
17
+ var MAX_PROXY_AUTH_AGE_MS = 300 * 1e3;
18
+ var normalizeString$2 = (value) => {
19
+ if (typeof value !== "string") return null;
20
+ return value.trim() || null;
21
+ };
22
+ var getHeaderValue = (headers, name) => {
23
+ const value = headers?.[name];
24
+ if (Array.isArray(value)) return normalizeString$2(value[0]);
25
+ return normalizeString$2(value);
26
+ };
27
+ var getProxySharedSecret = () => {
28
+ return process.env.RB_PROXY_SHARED_SECRET?.trim() || null;
29
+ };
30
+ var timingSafeEqualText = (left, right) => {
31
+ const leftBuffer = Buffer.from(left);
32
+ const rightBuffer = Buffer.from(right);
33
+ if (leftBuffer.length !== rightBuffer.length) return false;
34
+ return timingSafeEqual(leftBuffer, rightBuffer);
35
+ };
36
+ var buildProxyAuthSignature = ({ userId, tenantId, timestamp, secret }) => {
37
+ return createHmac("sha256", secret).update(`${timestamp}:${userId}:${tenantId}`).digest("hex");
38
+ };
39
+ var getTrustedProxyAuth = (headers) => {
40
+ const userId = getHeaderValue(headers, AUTHENTICATED_USER_ID_HEADER);
41
+ const tenantId = getHeaderValue(headers, AUTHENTICATED_TENANT_ID_HEADER);
42
+ const timestamp = getHeaderValue(headers, PROXY_AUTH_TIMESTAMP_HEADER);
43
+ const signature = getHeaderValue(headers, PROXY_AUTH_SIGNATURE_HEADER);
44
+ if (!userId || !tenantId || !timestamp || !signature) return null;
45
+ const parsedTimestamp = Number(timestamp);
46
+ if (!Number.isInteger(parsedTimestamp)) return null;
47
+ if (Math.abs(Date.now() - parsedTimestamp) > MAX_PROXY_AUTH_AGE_MS) return null;
48
+ const secret = getProxySharedSecret();
49
+ if (!secret) return null;
50
+ if (!timingSafeEqualText(signature, buildProxyAuthSignature({
51
+ userId,
52
+ tenantId,
53
+ timestamp,
54
+ secret
55
+ }))) return null;
56
+ return {
57
+ userId,
58
+ tenantId
59
+ };
60
+ };
61
+ //#endregion
62
+ //#region src/syncAuthenticatedSession.ts
63
+ var normalizeString$1 = (value) => {
64
+ if (typeof value !== "string") return null;
65
+ return value.trim() || null;
66
+ };
67
+ var normalizeStringArray = (value) => {
68
+ if (!Array.isArray(value)) return [];
69
+ return value.map((entry) => normalizeString$1(String(entry))).filter((entry) => Boolean(entry));
70
+ };
71
+ var normalizeRoles = (value) => {
72
+ if (!Array.isArray(value)) return [];
73
+ return value.map((entry) => normalizeString$1(entry)).filter((entry) => Boolean(entry));
74
+ };
75
+ var normalizeTenantRoles = (value) => {
76
+ if (!value || typeof value !== "object") return void 0;
77
+ if (value instanceof Map) {
78
+ const entries = Array.from(value.entries()).map(([tenantId, roles]) => {
79
+ const normalizedTenantId = normalizeString$1(String(tenantId));
80
+ if (!normalizedTenantId) return null;
81
+ return [normalizedTenantId, normalizeRoles(roles)];
82
+ }).filter((entry) => Boolean(entry));
83
+ return entries.length ? Object.fromEntries(entries) : void 0;
84
+ }
85
+ const nextRoles = Object.entries(value).map(([tenantId, roles]) => {
86
+ const normalizedTenantId = normalizeString$1(tenantId);
87
+ if (!normalizedTenantId) return null;
88
+ return [normalizedTenantId, normalizeRoles(roles)];
89
+ }).filter((entry) => Boolean(entry));
90
+ return nextRoles.length ? Object.fromEntries(nextRoles) : void 0;
91
+ };
92
+ var isSessionAuthorizedForTenant = (sessionUser, tenantId) => {
93
+ if (!sessionUser) return false;
94
+ const signedInTenants = normalizeStringArray(sessionUser.signedInTenants);
95
+ if (signedInTenants.length > 0) return signedInTenants.includes(tenantId);
96
+ return normalizeString$1(sessionUser.currentTenantId) === tenantId;
97
+ };
98
+ var loadSessionUser = async (userId, tenantId) => {
99
+ const user = await (await models.getGlobal("RBUser", { req: { session: null } })).findById(userId, {
100
+ tenants: 1,
101
+ tenantRoles: 1
102
+ }).lean();
103
+ if (!user) return null;
104
+ const signedInTenants = normalizeStringArray(user.tenants);
105
+ if (!signedInTenants.includes(tenantId)) return null;
106
+ const tenantRoles = normalizeTenantRoles(user.tenantRoles);
107
+ return {
108
+ id: userId,
109
+ currentTenantId: tenantId,
110
+ signedInTenants,
111
+ isEntryGateAuthorized: true,
112
+ ...tenantRoles ? { tenantRoles } : {}
113
+ };
114
+ };
115
+ var syncAuthenticatedSessionFromRequest = async (req) => {
116
+ const proxyAuth = getTrustedProxyAuth(req.headers);
117
+ if (!proxyAuth) return;
118
+ const { userId, tenantId } = proxyAuth;
119
+ const session = req.session;
120
+ if (!session) return;
121
+ const sessionUser = session.user;
122
+ if (normalizeString$1(sessionUser?.id) === userId && isSessionAuthorizedForTenant(sessionUser, tenantId)) {
123
+ if (normalizeString$1(sessionUser?.currentTenantId) === tenantId) return;
124
+ session.user = {
125
+ ...sessionUser && typeof sessionUser === "object" ? sessionUser : {},
126
+ id: userId,
127
+ currentTenantId: tenantId,
128
+ isEntryGateAuthorized: true
129
+ };
130
+ return;
131
+ }
132
+ const nextSessionUser = await loadSessionUser(userId, tenantId);
133
+ if (!nextSessionUser) {
134
+ if (session.user) delete session.user;
135
+ return;
136
+ }
137
+ session.user = nextSessionUser;
138
+ };
139
+ var syncAuthenticatedSessionMiddleware = (req, _res, next) => {
140
+ syncAuthenticatedSessionFromRequest(req).then(() => {
141
+ next();
142
+ }, next);
143
+ };
144
+ //#endregion
145
+ //#region src/rts/queryExecutor.ts
146
+ var QUERY_MAX_LIMIT = 4096;
147
+ var INTERNAL_MODEL_NAMES = /* @__PURE__ */ new Set(["RBRtsChange", "RBRtsCounter"]);
148
+ var DEFAULT_APPROX_COUNT_SAMPLE_SIZE = 1e3;
149
+ var MAX_APPROX_COUNT_SAMPLE_SIZE = 1e4;
150
+ var UNSUPPORTED_APPROX_COUNT_OPERATORS = /* @__PURE__ */ new Set([
151
+ "$text",
152
+ "$near",
153
+ "$nearSphere",
154
+ "$where"
155
+ ]);
156
+ var paginationCursorSigningSecret = null;
157
+ var getPaginationCursorSigningSecret = () => {
158
+ if (paginationCursorSigningSecret) return paginationCursorSigningSecret;
159
+ const masterKey = process.env.MASTER_KEY?.trim();
160
+ if (!masterKey) throw new Error("MASTER_KEY must be defined to derive pagination cursor signing secret");
161
+ paginationCursorSigningSecret = getDerivedKey(masterKey, "pagination_cursor_signing");
162
+ return paginationCursorSigningSecret;
163
+ };
164
+ var normalizeTenantId = (value) => {
165
+ if (typeof value !== "string") return null;
166
+ const normalized = value.trim();
167
+ return normalized ? normalized : null;
168
+ };
169
+ var getTenantIdFromRequest = (req) => {
170
+ return normalizeTenantId((req.session?.user)?.currentTenantId);
171
+ };
172
+ var resolveRtsRequestTenantId = (req) => {
173
+ return getTenantIdFromRequest(req);
174
+ };
175
+ var isRtsRequestAuthorized = (req, tenantId) => {
176
+ const sessionUser = req.session?.user;
177
+ if (!sessionUser) return false;
178
+ const currentTenantId = normalizeTenantId(sessionUser.currentTenantId);
179
+ if (!currentTenantId) return false;
180
+ return currentTenantId === tenantId;
181
+ };
182
+ var buildRtsAbilityFromRequest = async (req, tenantId) => {
183
+ const sessionUserId = normalizeTenantId((req.session?.user)?.id);
184
+ if (!sessionUserId) {
185
+ const currentTenantId = normalizeTenantId((req.session?.user)?.currentTenantId);
186
+ if (!currentTenantId || currentTenantId !== tenantId) throw new Error("Tenant not authorized for this session");
187
+ return {
188
+ ability: buildAbilityFromSession({
189
+ tenantId,
190
+ session: req.session
191
+ }),
192
+ userId: null
193
+ };
194
+ }
195
+ const currentTenantId = normalizeTenantId((req.session?.user)?.currentTenantId);
196
+ if (!currentTenantId || currentTenantId !== tenantId) throw new Error("Tenant not authorized for this session");
197
+ return {
198
+ ability: buildAbilityFromSession({
199
+ tenantId,
200
+ session: req.session
201
+ }),
202
+ userId: sessionUserId
203
+ };
204
+ };
205
+ var getTenantModel = async (tenantId, modelName, ability) => {
206
+ const ctx = {
207
+ req: { session: { user: { currentTenantId: tenantId } } },
208
+ ability
209
+ };
210
+ return models.get(modelName, ctx);
211
+ };
212
+ var normalizeLimit = (limit) => {
213
+ if (typeof limit !== "number") return QUERY_MAX_LIMIT;
214
+ if (!Number.isFinite(limit)) return QUERY_MAX_LIMIT;
215
+ return Math.min(QUERY_MAX_LIMIT, Math.abs(limit));
216
+ };
217
+ var normalizeNonNegativeInteger = (value) => {
218
+ if (typeof value !== "number") return 0;
219
+ if (!Number.isFinite(value) || value < 0) return 0;
220
+ return Math.floor(value);
221
+ };
222
+ var getApproxCountSampleSize = () => {
223
+ const raw = process.env.RB_RTS_APPROX_COUNT_SAMPLE_SIZE?.trim() ?? "";
224
+ if (!raw) return DEFAULT_APPROX_COUNT_SAMPLE_SIZE;
225
+ const parsed = Number(raw);
226
+ if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_APPROX_COUNT_SAMPLE_SIZE;
227
+ return Math.min(MAX_APPROX_COUNT_SAMPLE_SIZE, Math.floor(parsed));
228
+ };
229
+ var findUnsupportedApproxCountOperator = (value) => {
230
+ if (!value || typeof value !== "object") return null;
231
+ if (Array.isArray(value)) {
232
+ for (const entry of value) {
233
+ const unsupportedOperator = findUnsupportedApproxCountOperator(entry);
234
+ if (unsupportedOperator) return unsupportedOperator;
235
+ }
236
+ return null;
237
+ }
238
+ for (const [key, nestedValue] of Object.entries(value)) {
239
+ if (UNSUPPORTED_APPROX_COUNT_OPERATORS.has(key)) return key;
240
+ const unsupportedOperator = findUnsupportedApproxCountOperator(nestedValue);
241
+ if (unsupportedOperator) return unsupportedOperator;
242
+ }
243
+ return null;
244
+ };
245
+ var castApproxCountQuery = (model, query) => {
246
+ const castedQuery = model.find(query).cast(model);
247
+ if (!castedQuery || typeof castedQuery !== "object" || Array.isArray(castedQuery)) return query;
248
+ return castedQuery;
249
+ };
250
+ var normalizeString = (value) => {
251
+ return typeof value === "string" ? value.trim() : "";
252
+ };
253
+ var normalizeObject = (value) => {
254
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
255
+ return value;
256
+ };
257
+ var normalizePagination = (value) => {
258
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
259
+ return value;
260
+ };
261
+ var prepareCursorProjection = (projection, pagination, includeCursors) => {
262
+ if (!projection || !includeCursors) return {
263
+ projection,
264
+ addedPaths: []
265
+ };
266
+ const inclusionProjection = Object.entries(projection).some(([, value]) => value === 1);
267
+ const nextProjection = { ...projection };
268
+ const addedPaths = [];
269
+ const sortFields = Array.isArray(pagination.sort) ? pagination.sort.map((entry) => entry?.field).filter((field) => typeof field === "string") : [];
270
+ const cursorPaths = Array.from(/* @__PURE__ */ new Set([...sortFields, "_id"]));
271
+ if (!inclusionProjection) {
272
+ for (const path of cursorPaths) {
273
+ const excludedPath = Object.entries(projection).find(([projectedPath, value]) => value === 0 && (projectedPath === path || path.startsWith(`${projectedPath}.`)))?.[0];
274
+ if (!excludedPath) continue;
275
+ if (excludedPath !== path) throw new Error(`Pagination projection cannot exclude cursor path: ${path}`);
276
+ delete nextProjection[path];
277
+ addedPaths.push(path);
278
+ }
279
+ return {
280
+ projection: nextProjection,
281
+ addedPaths
282
+ };
283
+ }
284
+ for (const path of cursorPaths) {
285
+ if (path === "_id" ? projection._id !== 0 : Object.entries(projection).some(([projectedPath, value]) => value === 1 && (projectedPath === path || path.startsWith(`${projectedPath}.`)))) continue;
286
+ nextProjection[path] = 1;
287
+ addedPaths.push(path);
288
+ }
289
+ return {
290
+ projection: nextProjection,
291
+ addedPaths
292
+ };
293
+ };
294
+ var stripPath = (target, path) => {
295
+ const parts = path.split(".");
296
+ let current = target;
297
+ for (let index = 0; index < parts.length - 1; index += 1) {
298
+ const part = parts[index];
299
+ const child = current[part];
300
+ if (!child || typeof child !== "object" || Array.isArray(child)) return;
301
+ const cloned = { ...child };
302
+ current[part] = cloned;
303
+ current = cloned;
304
+ }
305
+ delete current[parts[parts.length - 1]];
306
+ };
307
+ var stripAddedProjectionPaths = (nodes, addedPaths) => {
308
+ if (!addedPaths.length) return nodes;
309
+ return nodes.map((node) => {
310
+ if (!node || typeof node !== "object" || Array.isArray(node)) return node;
311
+ const toObject = node.toObject;
312
+ const raw = typeof toObject === "function" ? toObject.call(node) : node;
313
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return raw;
314
+ const projected = { ...raw };
315
+ for (const path of addedPaths) stripPath(projected, path);
316
+ return projected;
317
+ });
318
+ };
319
+ var normalizePopulateSelect = (value) => {
320
+ if (typeof value === "string") return value.trim() || void 0;
321
+ return normalizeObject(value);
322
+ };
323
+ var normalizePopulateOptions = (value) => {
324
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
325
+ const raw = value;
326
+ const normalized = {};
327
+ if (raw.sort && typeof raw.sort === "object" && !Array.isArray(raw.sort)) normalized.sort = raw.sort;
328
+ if (typeof raw.limit === "number" && Number.isFinite(raw.limit)) normalized.limit = Math.max(0, Math.floor(Math.abs(raw.limit)));
329
+ if (!normalized.sort && normalized.limit === void 0) return void 0;
330
+ return normalized;
331
+ };
332
+ var normalizePopulateObject = (value) => {
333
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
334
+ const raw = value;
335
+ const path = normalizeString(raw.path);
336
+ if (!path) return void 0;
337
+ const normalized = { path };
338
+ const model = normalizeString(raw.model);
339
+ if (model) normalized.model = model;
340
+ const select = normalizePopulateSelect(raw.select);
341
+ if (select !== void 0) normalized.select = select;
342
+ const match = normalizeObject(raw.match);
343
+ if (match) normalized.match = match;
344
+ const nestedPopulate = normalizeRtsPopulateOption(raw.populate);
345
+ if (nestedPopulate !== void 0) normalized.populate = nestedPopulate;
346
+ const options = normalizePopulateOptions(raw.options);
347
+ if (options) normalized.options = options;
348
+ return normalized;
349
+ };
350
+ var normalizeRtsPopulateOption = (value) => {
351
+ if (typeof value === "string") return value.trim() || void 0;
352
+ if (Array.isArray(value)) {
353
+ const normalized = value.map((entry) => {
354
+ if (typeof entry === "string") return entry.trim() || null;
355
+ return normalizePopulateObject(entry) ?? null;
356
+ }).filter((entry) => entry !== null);
357
+ return normalized.length > 0 ? normalized : void 0;
358
+ }
359
+ return normalizePopulateObject(value);
360
+ };
361
+ var normalizeModelName = (value) => {
362
+ if (typeof value !== "string") return null;
363
+ return value.trim() || null;
364
+ };
365
+ var resolvePopulateRefModelName = (model, path, explicitModelName) => {
366
+ if (explicitModelName) return explicitModelName;
367
+ const schema = model.schema;
368
+ const schemaPath = typeof schema.path === "function" ? schema.path(path) : null;
369
+ const directRef = normalizeModelName(schemaPath?.options?.ref);
370
+ if (directRef) return directRef;
371
+ const arrayRef = normalizeModelName(schemaPath?.caster?.options?.ref);
372
+ if (arrayRef) return arrayRef;
373
+ const virtualRef = normalizeModelName((typeof schema.virtualpath === "function" ? schema.virtualpath(path) : null)?.options?.ref);
374
+ if (virtualRef) return virtualRef;
375
+ return null;
376
+ };
377
+ var mergePopulateMatchWithAcl = (populateMatch, aclMatch) => {
378
+ if (!populateMatch || Object.keys(populateMatch).length === 0) return aclMatch;
379
+ return { $and: [populateMatch, aclMatch] };
380
+ };
381
+ var resolvePopulateSpecForModel = async ({ tenantId, model, ability, populate, allowInternalModels, modelCache, dependencyModelNames }) => {
382
+ if (!populate) return void 0;
383
+ const getModelCached = async (targetModelName) => {
384
+ const cached = modelCache.get(targetModelName);
385
+ if (cached) return cached;
386
+ const loaded = await getTenantModel(tenantId, targetModelName, ability);
387
+ modelCache.set(targetModelName, loaded);
388
+ return loaded;
389
+ };
390
+ const resolveOne = async (entry, parentModel) => {
391
+ if (typeof entry === "string") {
392
+ const path = entry.trim();
393
+ if (!path) return null;
394
+ const refModelName = resolvePopulateRefModelName(parentModel, path, null);
395
+ if (!refModelName) return path;
396
+ if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(refModelName)) throw new Error("Model not allowed");
397
+ if (!ability.can("read", refModelName)) throw new Error("forbidden");
398
+ dependencyModelNames.add(refModelName);
399
+ return {
400
+ path,
401
+ match: getAccessibleByQuery(ability, "read", refModelName)
402
+ };
403
+ }
404
+ const path = entry.path.trim();
405
+ if (!path) return null;
406
+ const explicitModelName = normalizeModelName(entry.model);
407
+ const refModelName = resolvePopulateRefModelName(parentModel, path, explicitModelName);
408
+ let nestedModel = parentModel;
409
+ const normalizedEntry = { path };
410
+ if (entry.select !== void 0) normalizedEntry.select = entry.select;
411
+ if (entry.options !== void 0) normalizedEntry.options = entry.options;
412
+ if (explicitModelName) normalizedEntry.model = explicitModelName;
413
+ if (entry.match !== void 0) normalizedEntry.match = entry.match;
414
+ if (refModelName) {
415
+ if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(refModelName)) throw new Error("Model not allowed");
416
+ if (!ability.can("read", refModelName)) throw new Error("forbidden");
417
+ dependencyModelNames.add(refModelName);
418
+ nestedModel = await getModelCached(refModelName);
419
+ const aclMatch = getAccessibleByQuery(ability, "read", refModelName);
420
+ normalizedEntry.match = mergePopulateMatchWithAcl(normalizedEntry.match, aclMatch);
421
+ } else if (entry.populate !== void 0) throw new Error("Populate path must reference a model when nested populate is used");
422
+ const nestedPopulate = await resolvePopulateSpecForModel({
423
+ tenantId,
424
+ model: nestedModel,
425
+ ability,
426
+ populate: entry.populate,
427
+ allowInternalModels,
428
+ modelCache,
429
+ dependencyModelNames
430
+ });
431
+ if (nestedPopulate !== void 0) normalizedEntry.populate = nestedPopulate;
432
+ return normalizedEntry;
433
+ };
434
+ if (Array.isArray(populate)) {
435
+ const filtered = (await Promise.all(populate.map((entry) => resolveOne(entry, model)))).filter((entry) => entry !== null);
436
+ return filtered.length > 0 ? filtered : void 0;
437
+ }
438
+ return await resolveOne(populate, model) ?? void 0;
439
+ };
440
+ var normalizeRtsQueryOptions = (options) => {
441
+ if (!options || typeof options !== "object") return {};
442
+ const normalized = {};
443
+ if (options.projection && typeof options.projection === "object" && !Array.isArray(options.projection)) normalized.projection = options.projection;
444
+ if (options.sort && typeof options.sort === "object" && !Array.isArray(options.sort)) normalized.sort = options.sort;
445
+ normalized.limit = normalizeLimit(options.limit);
446
+ normalized.populate = normalizeRtsPopulateOption(options.populate);
447
+ normalized.pagination = normalizePagination(options.pagination);
448
+ return normalized;
449
+ };
450
+ var resolveRtsQueryDependencyModelNames = async ({ tenantId, ability, modelName, options, allowInternalModels = false }) => {
451
+ const model = await getTenantModel(tenantId, modelName, ability);
452
+ const modelCache = /* @__PURE__ */ new Map();
453
+ modelCache.set(modelName, model);
454
+ const dependencyModelNames = /* @__PURE__ */ new Set();
455
+ await resolvePopulateSpecForModel({
456
+ tenantId,
457
+ model,
458
+ ability,
459
+ populate: options.populate,
460
+ allowInternalModels,
461
+ modelCache,
462
+ dependencyModelNames
463
+ });
464
+ return Array.from(dependencyModelNames);
465
+ };
466
+ var runRtsQuery = async ({ tenantId, ability, modelName, query, options, allowInternalModels = false, paginationMaxLimit, includePaginationCursors = true }) => {
467
+ const { model, finalQuery } = await prepareRtsExecution({
468
+ tenantId,
469
+ ability,
470
+ modelName,
471
+ query,
472
+ allowInternalModels
473
+ });
474
+ const projection = options.projection ?? void 0;
475
+ const sort = options.sort;
476
+ const limit = normalizeLimit(options.limit);
477
+ const modelCache = /* @__PURE__ */ new Map();
478
+ modelCache.set(modelName, model);
479
+ const populate = await resolvePopulateSpecForModel({
480
+ tenantId,
481
+ model,
482
+ ability,
483
+ populate: options.populate,
484
+ allowInternalModels,
485
+ modelCache,
486
+ dependencyModelNames: /* @__PURE__ */ new Set()
487
+ });
488
+ if (options.pagination) {
489
+ const cursorProjection = prepareCursorProjection(projection, options.pagination, includePaginationCursors);
490
+ const paginatedQuery = model.find(finalQuery, cursorProjection.projection);
491
+ if (populate !== void 0) paginatedQuery.populate(populate);
492
+ const paginatedResult = await paginatedQuery.paginate(options.pagination, {
493
+ cursor: {
494
+ signingSecret: getPaginationCursorSigningSecret(),
495
+ ...paginationMaxLimit !== void 0 ? { maxLimit: paginationMaxLimit } : {}
496
+ },
497
+ includeCursors: includePaginationCursors
498
+ });
499
+ const totalCount = typeof paginatedResult.totalCount === "number" && Number.isFinite(paginatedResult.totalCount) && paginatedResult.totalCount >= 0 ? Math.floor(paginatedResult.totalCount) : void 0;
500
+ const approximateTotalCount = totalCount === void 0 ? await runApproximateRtsCount(model, finalQuery).catch(() => void 0) : void 0;
501
+ const resolvedTotalCount = totalCount ?? approximateTotalCount;
502
+ return {
503
+ data: Array.isArray(paginatedResult.nodes) ? stripAddedProjectionPaths(paginatedResult.nodes, cursorProjection.addedPaths) : [],
504
+ pageInfo: paginatedResult.pageInfo,
505
+ ...resolvedTotalCount !== void 0 ? { totalCount: resolvedTotalCount } : {}
506
+ };
507
+ }
508
+ const queryPromise = model.find(finalQuery, projection);
509
+ if (populate !== void 0) queryPromise.populate(populate);
510
+ if (sort && Object.keys(sort).length) queryPromise.sort(sort);
511
+ queryPromise.limit(limit);
512
+ const data = await queryPromise;
513
+ return { data: Array.isArray(data) ? data : [] };
514
+ };
515
+ var prepareRtsExecution = async ({ tenantId, ability, modelName, query, allowInternalModels = false }) => {
516
+ if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(modelName)) throw new Error("Model not allowed");
517
+ if (!ability.can("read", modelName)) throw new Error("forbidden");
518
+ return {
519
+ model: await getTenantModel(tenantId, modelName, ability),
520
+ finalQuery: { $and: [query, getAccessibleByQuery(ability, "read", modelName)] }
521
+ };
522
+ };
523
+ var runApproximateRtsCount = async (model, query) => {
524
+ const unsupportedOperator = findUnsupportedApproxCountOperator(query);
525
+ if (unsupportedOperator) throw new Error(`Approximate RTS count does not support ${unsupportedOperator} queries`);
526
+ const castedQuery = castApproxCountQuery(model, query);
527
+ const estimatedTotal = normalizeNonNegativeInteger(await model.estimatedDocumentCount());
528
+ if (estimatedTotal === 0) return 0;
529
+ const sampleSize = Math.min(getApproxCountSampleSize(), estimatedTotal);
530
+ const sampleMatches = normalizeNonNegativeInteger((await model.aggregate([
531
+ { $sample: { size: sampleSize } },
532
+ { $match: castedQuery },
533
+ { $count: "count" }
534
+ ]))[0]?.count);
535
+ if (sampleSize >= estimatedTotal) return Math.min(sampleMatches, estimatedTotal);
536
+ const estimatedMatches = Math.round(estimatedTotal * sampleMatches / sampleSize);
537
+ return Math.max(0, Math.min(estimatedTotal, estimatedMatches));
538
+ };
539
+ var runRtsCount = async ({ tenantId, ability, modelName, query, allowInternalModels = false }) => {
540
+ const { model, finalQuery } = await prepareRtsExecution({
541
+ tenantId,
542
+ ability,
543
+ modelName,
544
+ query,
545
+ allowInternalModels
546
+ });
547
+ return await runApproximateRtsCount(model, finalQuery);
548
+ };
549
+ //#endregion
550
+ //#region src/rts/queryWindow.ts
551
+ var RTS_QUERY_WINDOW_MAX_COUNT = 4096;
552
+ //#endregion
553
+ export { resolveRtsQueryDependencyModelNames as a, runRtsQuery as c, getDerivedKey as d, normalizeRtsQueryOptions as i, syncAuthenticatedSessionFromRequest as l, buildRtsAbilityFromRequest as n, resolveRtsRequestTenantId as o, isRtsRequestAuthorized as r, runRtsCount as s, RTS_QUERY_WINDOW_MAX_COUNT as t, syncAuthenticatedSessionMiddleware as u };
554
+
555
+ //# sourceMappingURL=queryWindow-DP8zD0lb.js.map