@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.
- package/dist/convertHeifToWebp-BT1n72DQ.js +148 -0
- package/dist/convertHeifToWebp-BT1n72DQ.js.map +1 -0
- package/dist/email-Co1GNjlT.js +7204 -0
- package/dist/email-Co1GNjlT.js.map +1 -0
- package/dist/handler-BqlcQ9sE.js +245 -0
- package/dist/handler-BqlcQ9sE.js.map +1 -0
- package/dist/handler-C92_gJkX.js +743 -0
- package/dist/handler-C92_gJkX.js.map +1 -0
- package/dist/handler-CapJTGzc.js +795 -0
- package/dist/handler-CapJTGzc.js.map +1 -0
- package/dist/handler-Fk-5fOSm.js +119 -0
- package/dist/handler-Fk-5fOSm.js.map +1 -0
- package/dist/htmlTemplate.d.ts +16 -0
- package/dist/htmlTemplate.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5140 -5286
- package/dist/index.js.map +1 -1
- package/dist/notifications.js +273 -382
- package/dist/notifications.js.map +1 -1
- package/dist/postProcessors-iffHvje4.js +89 -0
- package/dist/postProcessors-iffHvje4.js.map +1 -0
- package/dist/queryWindow-DP8zD0lb.js +555 -0
- package/dist/queryWindow-DP8zD0lb.js.map +1 -0
- package/dist/render_resend-B1SSQ4f7.js +7 -0
- package/dist/render_resend-B1SSQ4f7.js.map +1 -0
- package/dist/rts/index.js +990 -1207
- package/dist/rts/index.js.map +1 -1
- package/dist/schemas-B2fUvgYo.js +4151 -0
- package/dist/schemas-B2fUvgYo.js.map +1 -0
- package/dist/shared-DfrVDSp0.js +79 -0
- package/dist/shared-DfrVDSp0.js.map +1 -0
- package/dist/ssrMiddleware.d.ts +7 -3
- package/dist/ssrMiddleware.d.ts.map +1 -1
- package/dist/uploads/worker.js +139 -168
- package/dist/uploads/worker.js.map +1 -1
- package/dist/uploads.js +12 -22
- package/dist/uploads.js.map +1 -1
- package/package.json +1 -1
- package/dist/convertHeifToWebp-C-DGXZ2k.js +0 -169
- package/dist/convertHeifToWebp-C-DGXZ2k.js.map +0 -1
- package/dist/email-BCf24GmK.js +0 -8071
- package/dist/email-BCf24GmK.js.map +0 -1
- package/dist/handler-BqoKvylN.js +0 -147
- package/dist/handler-BqoKvylN.js.map +0 -1
- package/dist/handler-CUOJ51-w.js +0 -903
- package/dist/handler-CUOJ51-w.js.map +0 -1
- package/dist/handler-D-XdgeG_.js +0 -986
- package/dist/handler-D-XdgeG_.js.map +0 -1
- package/dist/handler-F0gFTzvh.js +0 -275
- package/dist/handler-F0gFTzvh.js.map +0 -1
- package/dist/postProcessors-D27fGZP0.js +0 -107
- package/dist/postProcessors-D27fGZP0.js.map +0 -1
- package/dist/queryWindow-Cdr7K-S1.js +0 -714
- package/dist/queryWindow-Cdr7K-S1.js.map +0 -1
- package/dist/render_resend_false-MiC__Smr.js +0 -6
- package/dist/render_resend_false-MiC__Smr.js.map +0 -1
- package/dist/schemas-Drf83ni9.js +0 -4517
- package/dist/schemas-Drf83ni9.js.map +0 -1
- package/dist/shared-CJrm9Wjp.js +0 -104
- 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
|