@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
|
@@ -1,714 +0,0 @@
|
|
|
1
|
-
import { models } from "@rpcbase/db";
|
|
2
|
-
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
3
|
-
import { getAccessibleByQuery, buildAbilityFromSession } from "@rpcbase/db/acl";
|
|
4
|
-
import assert from "assert";
|
|
5
|
-
import { hkdfSync } from "crypto";
|
|
6
|
-
const getDerivedKey = (masterKey, info, length = 32, salt = "") => {
|
|
7
|
-
assert(masterKey?.length >= 32, "MASTER_KEY must be 32 chars or longer.");
|
|
8
|
-
return Buffer.from(hkdfSync("sha256", masterKey, Buffer.from(salt), Buffer.from(info), length)).toString("hex");
|
|
9
|
-
};
|
|
10
|
-
const AUTHENTICATED_USER_ID_HEADER = "rb-authenticated-user-id";
|
|
11
|
-
const AUTHENTICATED_TENANT_ID_HEADER = "rb-authenticated-tenant-id";
|
|
12
|
-
const PROXY_AUTH_TIMESTAMP_HEADER = "rb-proxy-auth-timestamp";
|
|
13
|
-
const PROXY_AUTH_SIGNATURE_HEADER = "rb-proxy-auth-signature";
|
|
14
|
-
const MAX_PROXY_AUTH_AGE_MS = 5 * 60 * 1e3;
|
|
15
|
-
const normalizeString$2 = (value) => {
|
|
16
|
-
if (typeof value !== "string") return null;
|
|
17
|
-
const normalized = value.trim();
|
|
18
|
-
return normalized || null;
|
|
19
|
-
};
|
|
20
|
-
const getHeaderValue = (headers, name) => {
|
|
21
|
-
const value = headers?.[name];
|
|
22
|
-
if (Array.isArray(value)) return normalizeString$2(value[0]);
|
|
23
|
-
return normalizeString$2(value);
|
|
24
|
-
};
|
|
25
|
-
const getProxySharedSecret = () => {
|
|
26
|
-
const secret = process.env.RB_PROXY_SHARED_SECRET?.trim();
|
|
27
|
-
return secret || null;
|
|
28
|
-
};
|
|
29
|
-
const timingSafeEqualText = (left, right) => {
|
|
30
|
-
const leftBuffer = Buffer.from(left);
|
|
31
|
-
const rightBuffer = Buffer.from(right);
|
|
32
|
-
if (leftBuffer.length !== rightBuffer.length) return false;
|
|
33
|
-
return timingSafeEqual(leftBuffer, rightBuffer);
|
|
34
|
-
};
|
|
35
|
-
const buildProxyAuthSignature = ({
|
|
36
|
-
userId,
|
|
37
|
-
tenantId,
|
|
38
|
-
timestamp,
|
|
39
|
-
secret
|
|
40
|
-
}) => {
|
|
41
|
-
return createHmac("sha256", secret).update(`${timestamp}:${userId}:${tenantId}`).digest("hex");
|
|
42
|
-
};
|
|
43
|
-
const getTrustedProxyAuth = (headers) => {
|
|
44
|
-
const userId = getHeaderValue(headers, AUTHENTICATED_USER_ID_HEADER);
|
|
45
|
-
const tenantId = getHeaderValue(headers, AUTHENTICATED_TENANT_ID_HEADER);
|
|
46
|
-
const timestamp = getHeaderValue(headers, PROXY_AUTH_TIMESTAMP_HEADER);
|
|
47
|
-
const signature = getHeaderValue(headers, PROXY_AUTH_SIGNATURE_HEADER);
|
|
48
|
-
if (!userId || !tenantId || !timestamp || !signature) return null;
|
|
49
|
-
const parsedTimestamp = Number(timestamp);
|
|
50
|
-
if (!Number.isInteger(parsedTimestamp)) return null;
|
|
51
|
-
const now = Date.now();
|
|
52
|
-
if (Math.abs(now - parsedTimestamp) > MAX_PROXY_AUTH_AGE_MS) return null;
|
|
53
|
-
const secret = getProxySharedSecret();
|
|
54
|
-
if (!secret) return null;
|
|
55
|
-
const expectedSignature = buildProxyAuthSignature({
|
|
56
|
-
userId,
|
|
57
|
-
tenantId,
|
|
58
|
-
timestamp,
|
|
59
|
-
secret
|
|
60
|
-
});
|
|
61
|
-
if (!timingSafeEqualText(signature, expectedSignature)) return null;
|
|
62
|
-
return {
|
|
63
|
-
userId,
|
|
64
|
-
tenantId
|
|
65
|
-
};
|
|
66
|
-
};
|
|
67
|
-
const normalizeString$1 = (value) => {
|
|
68
|
-
if (typeof value !== "string") return null;
|
|
69
|
-
const normalized = value.trim();
|
|
70
|
-
return normalized || null;
|
|
71
|
-
};
|
|
72
|
-
const normalizeStringArray = (value) => {
|
|
73
|
-
if (!Array.isArray(value)) return [];
|
|
74
|
-
return value.map((entry) => normalizeString$1(String(entry))).filter((entry) => Boolean(entry));
|
|
75
|
-
};
|
|
76
|
-
const normalizeRoles = (value) => {
|
|
77
|
-
if (!Array.isArray(value)) return [];
|
|
78
|
-
return value.map((entry) => normalizeString$1(entry)).filter((entry) => Boolean(entry));
|
|
79
|
-
};
|
|
80
|
-
const normalizeTenantRoles = (value) => {
|
|
81
|
-
if (!value || typeof value !== "object") return void 0;
|
|
82
|
-
if (value instanceof Map) {
|
|
83
|
-
const entries = Array.from(value.entries()).map(([tenantId, roles]) => {
|
|
84
|
-
const normalizedTenantId = normalizeString$1(String(tenantId));
|
|
85
|
-
if (!normalizedTenantId) return null;
|
|
86
|
-
return [normalizedTenantId, normalizeRoles(roles)];
|
|
87
|
-
}).filter((entry) => Boolean(entry));
|
|
88
|
-
return entries.length ? Object.fromEntries(entries) : void 0;
|
|
89
|
-
}
|
|
90
|
-
const nextRoles = Object.entries(value).map(([tenantId, roles]) => {
|
|
91
|
-
const normalizedTenantId = normalizeString$1(tenantId);
|
|
92
|
-
if (!normalizedTenantId) return null;
|
|
93
|
-
return [normalizedTenantId, normalizeRoles(roles)];
|
|
94
|
-
}).filter((entry) => Boolean(entry));
|
|
95
|
-
return nextRoles.length ? Object.fromEntries(nextRoles) : void 0;
|
|
96
|
-
};
|
|
97
|
-
const isSessionAuthorizedForTenant = (sessionUser, tenantId) => {
|
|
98
|
-
if (!sessionUser) return false;
|
|
99
|
-
const signedInTenants = normalizeStringArray(sessionUser.signedInTenants);
|
|
100
|
-
if (signedInTenants.length > 0) {
|
|
101
|
-
return signedInTenants.includes(tenantId);
|
|
102
|
-
}
|
|
103
|
-
const currentTenantId = normalizeString$1(sessionUser.currentTenantId);
|
|
104
|
-
return currentTenantId === tenantId;
|
|
105
|
-
};
|
|
106
|
-
const loadSessionUser = async (userId, tenantId) => {
|
|
107
|
-
const ctx = {
|
|
108
|
-
req: {
|
|
109
|
-
session: null
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
const User = await models.getGlobal("RBUser", ctx);
|
|
113
|
-
const user = await User.findById(userId, {
|
|
114
|
-
tenants: 1,
|
|
115
|
-
tenantRoles: 1
|
|
116
|
-
}).lean();
|
|
117
|
-
if (!user) return null;
|
|
118
|
-
const signedInTenants = normalizeStringArray(user.tenants);
|
|
119
|
-
if (!signedInTenants.includes(tenantId)) return null;
|
|
120
|
-
const tenantRoles = normalizeTenantRoles(user.tenantRoles);
|
|
121
|
-
return {
|
|
122
|
-
id: userId,
|
|
123
|
-
currentTenantId: tenantId,
|
|
124
|
-
signedInTenants,
|
|
125
|
-
isEntryGateAuthorized: true,
|
|
126
|
-
...tenantRoles ? {
|
|
127
|
-
tenantRoles
|
|
128
|
-
} : {}
|
|
129
|
-
};
|
|
130
|
-
};
|
|
131
|
-
const syncAuthenticatedSessionFromRequest = async (req) => {
|
|
132
|
-
const proxyAuth = getTrustedProxyAuth(req.headers);
|
|
133
|
-
if (!proxyAuth) return;
|
|
134
|
-
const {
|
|
135
|
-
userId,
|
|
136
|
-
tenantId
|
|
137
|
-
} = proxyAuth;
|
|
138
|
-
const session = req.session;
|
|
139
|
-
if (!session) return;
|
|
140
|
-
const sessionUser = session.user;
|
|
141
|
-
const sessionUserId = normalizeString$1(sessionUser?.id);
|
|
142
|
-
if (sessionUserId === userId && isSessionAuthorizedForTenant(sessionUser, tenantId)) {
|
|
143
|
-
const currentTenantId = normalizeString$1(sessionUser?.currentTenantId);
|
|
144
|
-
if (currentTenantId === tenantId) return;
|
|
145
|
-
const baseUser = sessionUser && typeof sessionUser === "object" ? sessionUser : {};
|
|
146
|
-
session.user = {
|
|
147
|
-
...baseUser,
|
|
148
|
-
id: userId,
|
|
149
|
-
currentTenantId: tenantId,
|
|
150
|
-
isEntryGateAuthorized: true
|
|
151
|
-
};
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
const nextSessionUser = await loadSessionUser(userId, tenantId);
|
|
155
|
-
if (!nextSessionUser) {
|
|
156
|
-
if (session.user) {
|
|
157
|
-
delete session.user;
|
|
158
|
-
}
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
session.user = nextSessionUser;
|
|
162
|
-
};
|
|
163
|
-
const syncAuthenticatedSessionMiddleware = (req, _res, next) => {
|
|
164
|
-
void syncAuthenticatedSessionFromRequest(req).then(() => {
|
|
165
|
-
next();
|
|
166
|
-
}, next);
|
|
167
|
-
};
|
|
168
|
-
const QUERY_MAX_LIMIT = 4096;
|
|
169
|
-
const INTERNAL_MODEL_NAMES = /* @__PURE__ */ new Set(["RBRtsChange", "RBRtsCounter"]);
|
|
170
|
-
const DEFAULT_APPROX_COUNT_SAMPLE_SIZE = 1e3;
|
|
171
|
-
const MAX_APPROX_COUNT_SAMPLE_SIZE = 1e4;
|
|
172
|
-
const UNSUPPORTED_APPROX_COUNT_OPERATORS = /* @__PURE__ */ new Set(["$text", "$near", "$nearSphere", "$where"]);
|
|
173
|
-
let paginationCursorSigningSecret = null;
|
|
174
|
-
const getPaginationCursorSigningSecret = () => {
|
|
175
|
-
if (paginationCursorSigningSecret) return paginationCursorSigningSecret;
|
|
176
|
-
const masterKey = process.env.MASTER_KEY?.trim();
|
|
177
|
-
if (!masterKey) {
|
|
178
|
-
throw new Error("MASTER_KEY must be defined to derive pagination cursor signing secret");
|
|
179
|
-
}
|
|
180
|
-
paginationCursorSigningSecret = getDerivedKey(masterKey, "pagination_cursor_signing");
|
|
181
|
-
return paginationCursorSigningSecret;
|
|
182
|
-
};
|
|
183
|
-
const normalizeTenantId = (value) => {
|
|
184
|
-
if (typeof value !== "string") return null;
|
|
185
|
-
const normalized = value.trim();
|
|
186
|
-
return normalized ? normalized : null;
|
|
187
|
-
};
|
|
188
|
-
const getTenantIdFromRequest = (req) => {
|
|
189
|
-
return normalizeTenantId(req.session?.user?.currentTenantId);
|
|
190
|
-
};
|
|
191
|
-
const resolveRtsRequestTenantId = (req) => {
|
|
192
|
-
return getTenantIdFromRequest(req);
|
|
193
|
-
};
|
|
194
|
-
const isRtsRequestAuthorized = (req, tenantId) => {
|
|
195
|
-
const sessionUser = req.session?.user;
|
|
196
|
-
if (!sessionUser) return false;
|
|
197
|
-
const currentTenantId = normalizeTenantId(sessionUser.currentTenantId);
|
|
198
|
-
if (!currentTenantId) return false;
|
|
199
|
-
return currentTenantId === tenantId;
|
|
200
|
-
};
|
|
201
|
-
const buildRtsAbilityFromRequest = async (req, tenantId) => {
|
|
202
|
-
const sessionUserId = normalizeTenantId(req.session?.user?.id);
|
|
203
|
-
if (!sessionUserId) {
|
|
204
|
-
const currentTenantId2 = normalizeTenantId(req.session?.user?.currentTenantId);
|
|
205
|
-
if (!currentTenantId2 || currentTenantId2 !== tenantId) {
|
|
206
|
-
throw new Error("Tenant not authorized for this session");
|
|
207
|
-
}
|
|
208
|
-
return {
|
|
209
|
-
ability: buildAbilityFromSession({
|
|
210
|
-
tenantId,
|
|
211
|
-
session: req.session
|
|
212
|
-
}),
|
|
213
|
-
userId: null
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
const currentTenantId = normalizeTenantId(req.session?.user?.currentTenantId);
|
|
217
|
-
if (!currentTenantId || currentTenantId !== tenantId) {
|
|
218
|
-
throw new Error("Tenant not authorized for this session");
|
|
219
|
-
}
|
|
220
|
-
return {
|
|
221
|
-
ability: buildAbilityFromSession({
|
|
222
|
-
tenantId,
|
|
223
|
-
session: req.session
|
|
224
|
-
}),
|
|
225
|
-
userId: sessionUserId
|
|
226
|
-
};
|
|
227
|
-
};
|
|
228
|
-
const getTenantModel = async (tenantId, modelName, ability) => {
|
|
229
|
-
const ctx = {
|
|
230
|
-
req: {
|
|
231
|
-
session: {
|
|
232
|
-
user: {
|
|
233
|
-
currentTenantId: tenantId
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
},
|
|
237
|
-
ability
|
|
238
|
-
};
|
|
239
|
-
return models.get(modelName, ctx);
|
|
240
|
-
};
|
|
241
|
-
const normalizeLimit = (limit) => {
|
|
242
|
-
if (typeof limit !== "number") return QUERY_MAX_LIMIT;
|
|
243
|
-
if (!Number.isFinite(limit)) return QUERY_MAX_LIMIT;
|
|
244
|
-
return Math.min(QUERY_MAX_LIMIT, Math.abs(limit));
|
|
245
|
-
};
|
|
246
|
-
const normalizeNonNegativeInteger = (value) => {
|
|
247
|
-
if (typeof value !== "number") return 0;
|
|
248
|
-
if (!Number.isFinite(value) || value < 0) return 0;
|
|
249
|
-
return Math.floor(value);
|
|
250
|
-
};
|
|
251
|
-
const getApproxCountSampleSize = () => {
|
|
252
|
-
const raw = process.env.RB_RTS_APPROX_COUNT_SAMPLE_SIZE?.trim() ?? "";
|
|
253
|
-
if (!raw) return DEFAULT_APPROX_COUNT_SAMPLE_SIZE;
|
|
254
|
-
const parsed = Number(raw);
|
|
255
|
-
if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_APPROX_COUNT_SAMPLE_SIZE;
|
|
256
|
-
return Math.min(MAX_APPROX_COUNT_SAMPLE_SIZE, Math.floor(parsed));
|
|
257
|
-
};
|
|
258
|
-
const findUnsupportedApproxCountOperator = (value) => {
|
|
259
|
-
if (!value || typeof value !== "object") return null;
|
|
260
|
-
if (Array.isArray(value)) {
|
|
261
|
-
for (const entry of value) {
|
|
262
|
-
const unsupportedOperator = findUnsupportedApproxCountOperator(entry);
|
|
263
|
-
if (unsupportedOperator) return unsupportedOperator;
|
|
264
|
-
}
|
|
265
|
-
return null;
|
|
266
|
-
}
|
|
267
|
-
for (const [key, nestedValue] of Object.entries(value)) {
|
|
268
|
-
if (UNSUPPORTED_APPROX_COUNT_OPERATORS.has(key)) {
|
|
269
|
-
return key;
|
|
270
|
-
}
|
|
271
|
-
const unsupportedOperator = findUnsupportedApproxCountOperator(nestedValue);
|
|
272
|
-
if (unsupportedOperator) return unsupportedOperator;
|
|
273
|
-
}
|
|
274
|
-
return null;
|
|
275
|
-
};
|
|
276
|
-
const castApproxCountQuery = (model, query) => {
|
|
277
|
-
const castedQuery = model.find(query).cast(model);
|
|
278
|
-
if (!castedQuery || typeof castedQuery !== "object" || Array.isArray(castedQuery)) {
|
|
279
|
-
return query;
|
|
280
|
-
}
|
|
281
|
-
return castedQuery;
|
|
282
|
-
};
|
|
283
|
-
const normalizeString = (value) => {
|
|
284
|
-
return typeof value === "string" ? value.trim() : "";
|
|
285
|
-
};
|
|
286
|
-
const normalizeObject = (value) => {
|
|
287
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
288
|
-
return value;
|
|
289
|
-
};
|
|
290
|
-
const normalizePagination = (value) => {
|
|
291
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
292
|
-
return value;
|
|
293
|
-
};
|
|
294
|
-
const prepareCursorProjection = (projection, pagination, includeCursors) => {
|
|
295
|
-
if (!projection || !includeCursors) return {
|
|
296
|
-
projection,
|
|
297
|
-
addedPaths: []
|
|
298
|
-
};
|
|
299
|
-
const inclusionProjection = Object.entries(projection).some(([, value]) => value === 1);
|
|
300
|
-
const nextProjection = {
|
|
301
|
-
...projection
|
|
302
|
-
};
|
|
303
|
-
const addedPaths = [];
|
|
304
|
-
const sortFields = Array.isArray(pagination.sort) ? pagination.sort.map((entry) => entry?.field).filter((field) => typeof field === "string") : [];
|
|
305
|
-
const cursorPaths = Array.from(/* @__PURE__ */ new Set([...sortFields, "_id"]));
|
|
306
|
-
if (!inclusionProjection) {
|
|
307
|
-
for (const path of cursorPaths) {
|
|
308
|
-
const excludedPath = Object.entries(projection).find(([projectedPath, value]) => value === 0 && (projectedPath === path || path.startsWith(`${projectedPath}.`)))?.[0];
|
|
309
|
-
if (!excludedPath) continue;
|
|
310
|
-
if (excludedPath !== path) {
|
|
311
|
-
throw new Error(`Pagination projection cannot exclude cursor path: ${path}`);
|
|
312
|
-
}
|
|
313
|
-
delete nextProjection[path];
|
|
314
|
-
addedPaths.push(path);
|
|
315
|
-
}
|
|
316
|
-
return {
|
|
317
|
-
projection: nextProjection,
|
|
318
|
-
addedPaths
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
for (const path of cursorPaths) {
|
|
322
|
-
const isIncluded = path === "_id" ? projection._id !== 0 : Object.entries(projection).some(([projectedPath, value]) => value === 1 && (projectedPath === path || path.startsWith(`${projectedPath}.`)));
|
|
323
|
-
if (isIncluded) continue;
|
|
324
|
-
nextProjection[path] = 1;
|
|
325
|
-
addedPaths.push(path);
|
|
326
|
-
}
|
|
327
|
-
return {
|
|
328
|
-
projection: nextProjection,
|
|
329
|
-
addedPaths
|
|
330
|
-
};
|
|
331
|
-
};
|
|
332
|
-
const stripPath = (target, path) => {
|
|
333
|
-
const parts = path.split(".");
|
|
334
|
-
let current = target;
|
|
335
|
-
for (let index = 0; index < parts.length - 1; index += 1) {
|
|
336
|
-
const part = parts[index];
|
|
337
|
-
const child = current[part];
|
|
338
|
-
if (!child || typeof child !== "object" || Array.isArray(child)) return;
|
|
339
|
-
const cloned = {
|
|
340
|
-
...child
|
|
341
|
-
};
|
|
342
|
-
current[part] = cloned;
|
|
343
|
-
current = cloned;
|
|
344
|
-
}
|
|
345
|
-
delete current[parts[parts.length - 1]];
|
|
346
|
-
};
|
|
347
|
-
const stripAddedProjectionPaths = (nodes, addedPaths) => {
|
|
348
|
-
if (!addedPaths.length) return nodes;
|
|
349
|
-
return nodes.map((node) => {
|
|
350
|
-
if (!node || typeof node !== "object" || Array.isArray(node)) return node;
|
|
351
|
-
const toObject = node.toObject;
|
|
352
|
-
const raw = typeof toObject === "function" ? toObject.call(node) : node;
|
|
353
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
354
|
-
const projected = {
|
|
355
|
-
...raw
|
|
356
|
-
};
|
|
357
|
-
for (const path of addedPaths) stripPath(projected, path);
|
|
358
|
-
return projected;
|
|
359
|
-
});
|
|
360
|
-
};
|
|
361
|
-
const normalizePopulateSelect = (value) => {
|
|
362
|
-
if (typeof value === "string") {
|
|
363
|
-
const normalized = value.trim();
|
|
364
|
-
return normalized || void 0;
|
|
365
|
-
}
|
|
366
|
-
return normalizeObject(value);
|
|
367
|
-
};
|
|
368
|
-
const normalizePopulateOptions = (value) => {
|
|
369
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
370
|
-
const raw = value;
|
|
371
|
-
const normalized = {};
|
|
372
|
-
if (raw.sort && typeof raw.sort === "object" && !Array.isArray(raw.sort)) {
|
|
373
|
-
normalized.sort = raw.sort;
|
|
374
|
-
}
|
|
375
|
-
if (typeof raw.limit === "number" && Number.isFinite(raw.limit)) {
|
|
376
|
-
normalized.limit = Math.max(0, Math.floor(Math.abs(raw.limit)));
|
|
377
|
-
}
|
|
378
|
-
if (!normalized.sort && normalized.limit === void 0) return void 0;
|
|
379
|
-
return normalized;
|
|
380
|
-
};
|
|
381
|
-
const normalizePopulateObject = (value) => {
|
|
382
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
383
|
-
const raw = value;
|
|
384
|
-
const path = normalizeString(raw.path);
|
|
385
|
-
if (!path) return void 0;
|
|
386
|
-
const normalized = {
|
|
387
|
-
path
|
|
388
|
-
};
|
|
389
|
-
const model = normalizeString(raw.model);
|
|
390
|
-
if (model) normalized.model = model;
|
|
391
|
-
const select = normalizePopulateSelect(raw.select);
|
|
392
|
-
if (select !== void 0) normalized.select = select;
|
|
393
|
-
const match = normalizeObject(raw.match);
|
|
394
|
-
if (match) normalized.match = match;
|
|
395
|
-
const nestedPopulate = normalizeRtsPopulateOption(raw.populate);
|
|
396
|
-
if (nestedPopulate !== void 0) normalized.populate = nestedPopulate;
|
|
397
|
-
const options = normalizePopulateOptions(raw.options);
|
|
398
|
-
if (options) normalized.options = options;
|
|
399
|
-
return normalized;
|
|
400
|
-
};
|
|
401
|
-
const normalizeRtsPopulateOption = (value) => {
|
|
402
|
-
if (typeof value === "string") {
|
|
403
|
-
const normalized = value.trim();
|
|
404
|
-
return normalized || void 0;
|
|
405
|
-
}
|
|
406
|
-
if (Array.isArray(value)) {
|
|
407
|
-
const normalized = value.map((entry) => {
|
|
408
|
-
if (typeof entry === "string") {
|
|
409
|
-
const path = entry.trim();
|
|
410
|
-
return path || null;
|
|
411
|
-
}
|
|
412
|
-
return normalizePopulateObject(entry) ?? null;
|
|
413
|
-
}).filter((entry) => entry !== null);
|
|
414
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
415
|
-
}
|
|
416
|
-
return normalizePopulateObject(value);
|
|
417
|
-
};
|
|
418
|
-
const normalizeModelName = (value) => {
|
|
419
|
-
if (typeof value !== "string") return null;
|
|
420
|
-
const normalized = value.trim();
|
|
421
|
-
return normalized || null;
|
|
422
|
-
};
|
|
423
|
-
const resolvePopulateRefModelName = (model, path, explicitModelName) => {
|
|
424
|
-
if (explicitModelName) return explicitModelName;
|
|
425
|
-
const schema = model.schema;
|
|
426
|
-
const schemaPath = typeof schema.path === "function" ? schema.path(path) : null;
|
|
427
|
-
const directRef = normalizeModelName(schemaPath?.options?.ref);
|
|
428
|
-
if (directRef) return directRef;
|
|
429
|
-
const arrayRef = normalizeModelName(schemaPath?.caster?.options?.ref);
|
|
430
|
-
if (arrayRef) return arrayRef;
|
|
431
|
-
const virtualPath = typeof schema.virtualpath === "function" ? schema.virtualpath(path) : null;
|
|
432
|
-
const virtualRef = normalizeModelName(virtualPath?.options?.ref);
|
|
433
|
-
if (virtualRef) return virtualRef;
|
|
434
|
-
return null;
|
|
435
|
-
};
|
|
436
|
-
const mergePopulateMatchWithAcl = (populateMatch, aclMatch) => {
|
|
437
|
-
if (!populateMatch || Object.keys(populateMatch).length === 0) return aclMatch;
|
|
438
|
-
return {
|
|
439
|
-
$and: [populateMatch, aclMatch]
|
|
440
|
-
};
|
|
441
|
-
};
|
|
442
|
-
const resolvePopulateSpecForModel = async ({
|
|
443
|
-
tenantId,
|
|
444
|
-
model,
|
|
445
|
-
ability,
|
|
446
|
-
populate,
|
|
447
|
-
allowInternalModels,
|
|
448
|
-
modelCache,
|
|
449
|
-
dependencyModelNames
|
|
450
|
-
}) => {
|
|
451
|
-
if (!populate) return void 0;
|
|
452
|
-
const getModelCached = async (targetModelName) => {
|
|
453
|
-
const cached = modelCache.get(targetModelName);
|
|
454
|
-
if (cached) return cached;
|
|
455
|
-
const loaded = await getTenantModel(tenantId, targetModelName, ability);
|
|
456
|
-
modelCache.set(targetModelName, loaded);
|
|
457
|
-
return loaded;
|
|
458
|
-
};
|
|
459
|
-
const resolveOne = async (entry, parentModel) => {
|
|
460
|
-
if (typeof entry === "string") {
|
|
461
|
-
const path2 = entry.trim();
|
|
462
|
-
if (!path2) return null;
|
|
463
|
-
const refModelName2 = resolvePopulateRefModelName(parentModel, path2, null);
|
|
464
|
-
if (!refModelName2) return path2;
|
|
465
|
-
if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(refModelName2)) {
|
|
466
|
-
throw new Error("Model not allowed");
|
|
467
|
-
}
|
|
468
|
-
if (!ability.can("read", refModelName2)) {
|
|
469
|
-
throw new Error("forbidden");
|
|
470
|
-
}
|
|
471
|
-
dependencyModelNames.add(refModelName2);
|
|
472
|
-
const aclMatch = getAccessibleByQuery(ability, "read", refModelName2);
|
|
473
|
-
return {
|
|
474
|
-
path: path2,
|
|
475
|
-
match: aclMatch
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
const path = entry.path.trim();
|
|
479
|
-
if (!path) return null;
|
|
480
|
-
const explicitModelName = normalizeModelName(entry.model);
|
|
481
|
-
const refModelName = resolvePopulateRefModelName(parentModel, path, explicitModelName);
|
|
482
|
-
let nestedModel = parentModel;
|
|
483
|
-
const normalizedEntry = {
|
|
484
|
-
path
|
|
485
|
-
};
|
|
486
|
-
if (entry.select !== void 0) normalizedEntry.select = entry.select;
|
|
487
|
-
if (entry.options !== void 0) normalizedEntry.options = entry.options;
|
|
488
|
-
if (explicitModelName) normalizedEntry.model = explicitModelName;
|
|
489
|
-
if (entry.match !== void 0) normalizedEntry.match = entry.match;
|
|
490
|
-
if (refModelName) {
|
|
491
|
-
if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(refModelName)) {
|
|
492
|
-
throw new Error("Model not allowed");
|
|
493
|
-
}
|
|
494
|
-
if (!ability.can("read", refModelName)) {
|
|
495
|
-
throw new Error("forbidden");
|
|
496
|
-
}
|
|
497
|
-
dependencyModelNames.add(refModelName);
|
|
498
|
-
nestedModel = await getModelCached(refModelName);
|
|
499
|
-
const aclMatch = getAccessibleByQuery(ability, "read", refModelName);
|
|
500
|
-
normalizedEntry.match = mergePopulateMatchWithAcl(normalizedEntry.match, aclMatch);
|
|
501
|
-
} else if (entry.populate !== void 0) {
|
|
502
|
-
throw new Error("Populate path must reference a model when nested populate is used");
|
|
503
|
-
}
|
|
504
|
-
const nestedPopulate = await resolvePopulateSpecForModel({
|
|
505
|
-
tenantId,
|
|
506
|
-
model: nestedModel,
|
|
507
|
-
ability,
|
|
508
|
-
populate: entry.populate,
|
|
509
|
-
allowInternalModels,
|
|
510
|
-
modelCache,
|
|
511
|
-
dependencyModelNames
|
|
512
|
-
});
|
|
513
|
-
if (nestedPopulate !== void 0) normalizedEntry.populate = nestedPopulate;
|
|
514
|
-
return normalizedEntry;
|
|
515
|
-
};
|
|
516
|
-
if (Array.isArray(populate)) {
|
|
517
|
-
const resolved2 = await Promise.all(populate.map((entry) => resolveOne(entry, model)));
|
|
518
|
-
const filtered = resolved2.filter((entry) => entry !== null);
|
|
519
|
-
return filtered.length > 0 ? filtered : void 0;
|
|
520
|
-
}
|
|
521
|
-
const resolved = await resolveOne(populate, model);
|
|
522
|
-
return resolved ?? void 0;
|
|
523
|
-
};
|
|
524
|
-
const normalizeRtsQueryOptions = (options) => {
|
|
525
|
-
if (!options || typeof options !== "object") return {};
|
|
526
|
-
const normalized = {};
|
|
527
|
-
if (options.projection && typeof options.projection === "object" && !Array.isArray(options.projection)) {
|
|
528
|
-
normalized.projection = options.projection;
|
|
529
|
-
}
|
|
530
|
-
if (options.sort && typeof options.sort === "object" && !Array.isArray(options.sort)) {
|
|
531
|
-
normalized.sort = options.sort;
|
|
532
|
-
}
|
|
533
|
-
normalized.limit = normalizeLimit(options.limit);
|
|
534
|
-
normalized.populate = normalizeRtsPopulateOption(options.populate);
|
|
535
|
-
normalized.pagination = normalizePagination(options.pagination);
|
|
536
|
-
return normalized;
|
|
537
|
-
};
|
|
538
|
-
const resolveRtsQueryDependencyModelNames = async ({
|
|
539
|
-
tenantId,
|
|
540
|
-
ability,
|
|
541
|
-
modelName,
|
|
542
|
-
options,
|
|
543
|
-
allowInternalModels = false
|
|
544
|
-
}) => {
|
|
545
|
-
const model = await getTenantModel(tenantId, modelName, ability);
|
|
546
|
-
const modelCache = /* @__PURE__ */ new Map();
|
|
547
|
-
modelCache.set(modelName, model);
|
|
548
|
-
const dependencyModelNames = /* @__PURE__ */ new Set();
|
|
549
|
-
await resolvePopulateSpecForModel({
|
|
550
|
-
tenantId,
|
|
551
|
-
model,
|
|
552
|
-
ability,
|
|
553
|
-
populate: options.populate,
|
|
554
|
-
allowInternalModels,
|
|
555
|
-
modelCache,
|
|
556
|
-
dependencyModelNames
|
|
557
|
-
});
|
|
558
|
-
return Array.from(dependencyModelNames);
|
|
559
|
-
};
|
|
560
|
-
const runRtsQuery = async ({
|
|
561
|
-
tenantId,
|
|
562
|
-
ability,
|
|
563
|
-
modelName,
|
|
564
|
-
query,
|
|
565
|
-
options,
|
|
566
|
-
allowInternalModels = false,
|
|
567
|
-
paginationMaxLimit,
|
|
568
|
-
includePaginationCursors = true
|
|
569
|
-
}) => {
|
|
570
|
-
const {
|
|
571
|
-
model,
|
|
572
|
-
finalQuery
|
|
573
|
-
} = await prepareRtsExecution({
|
|
574
|
-
tenantId,
|
|
575
|
-
ability,
|
|
576
|
-
modelName,
|
|
577
|
-
query,
|
|
578
|
-
allowInternalModels
|
|
579
|
-
});
|
|
580
|
-
const projection = options.projection ?? void 0;
|
|
581
|
-
const sort = options.sort;
|
|
582
|
-
const limit = normalizeLimit(options.limit);
|
|
583
|
-
const modelCache = /* @__PURE__ */ new Map();
|
|
584
|
-
modelCache.set(modelName, model);
|
|
585
|
-
const populate = await resolvePopulateSpecForModel({
|
|
586
|
-
tenantId,
|
|
587
|
-
model,
|
|
588
|
-
ability,
|
|
589
|
-
populate: options.populate,
|
|
590
|
-
allowInternalModels,
|
|
591
|
-
modelCache,
|
|
592
|
-
dependencyModelNames: /* @__PURE__ */ new Set()
|
|
593
|
-
});
|
|
594
|
-
if (options.pagination) {
|
|
595
|
-
const cursorProjection = prepareCursorProjection(projection, options.pagination, includePaginationCursors);
|
|
596
|
-
const paginatedQuery = model.find(finalQuery, cursorProjection.projection);
|
|
597
|
-
if (populate !== void 0) {
|
|
598
|
-
paginatedQuery.populate(populate);
|
|
599
|
-
}
|
|
600
|
-
const paginatedResult = await paginatedQuery.paginate(options.pagination, {
|
|
601
|
-
cursor: {
|
|
602
|
-
signingSecret: getPaginationCursorSigningSecret(),
|
|
603
|
-
...paginationMaxLimit !== void 0 ? {
|
|
604
|
-
maxLimit: paginationMaxLimit
|
|
605
|
-
} : {}
|
|
606
|
-
},
|
|
607
|
-
includeCursors: includePaginationCursors
|
|
608
|
-
});
|
|
609
|
-
const totalCount = typeof paginatedResult.totalCount === "number" && Number.isFinite(paginatedResult.totalCount) && paginatedResult.totalCount >= 0 ? Math.floor(paginatedResult.totalCount) : void 0;
|
|
610
|
-
const approximateTotalCount = totalCount === void 0 ? await runApproximateRtsCount(model, finalQuery).catch(() => void 0) : void 0;
|
|
611
|
-
const resolvedTotalCount = totalCount ?? approximateTotalCount;
|
|
612
|
-
return {
|
|
613
|
-
data: Array.isArray(paginatedResult.nodes) ? stripAddedProjectionPaths(paginatedResult.nodes, cursorProjection.addedPaths) : [],
|
|
614
|
-
pageInfo: paginatedResult.pageInfo,
|
|
615
|
-
...resolvedTotalCount !== void 0 ? {
|
|
616
|
-
totalCount: resolvedTotalCount
|
|
617
|
-
} : {}
|
|
618
|
-
};
|
|
619
|
-
}
|
|
620
|
-
const queryPromise = model.find(finalQuery, projection);
|
|
621
|
-
if (populate !== void 0) {
|
|
622
|
-
queryPromise.populate(populate);
|
|
623
|
-
}
|
|
624
|
-
if (sort && Object.keys(sort).length) {
|
|
625
|
-
queryPromise.sort(sort);
|
|
626
|
-
}
|
|
627
|
-
queryPromise.limit(limit);
|
|
628
|
-
const data = await queryPromise;
|
|
629
|
-
return {
|
|
630
|
-
data: Array.isArray(data) ? data : []
|
|
631
|
-
};
|
|
632
|
-
};
|
|
633
|
-
const prepareRtsExecution = async ({
|
|
634
|
-
tenantId,
|
|
635
|
-
ability,
|
|
636
|
-
modelName,
|
|
637
|
-
query,
|
|
638
|
-
allowInternalModels = false
|
|
639
|
-
}) => {
|
|
640
|
-
if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(modelName)) {
|
|
641
|
-
throw new Error("Model not allowed");
|
|
642
|
-
}
|
|
643
|
-
if (!ability.can("read", modelName)) {
|
|
644
|
-
throw new Error("forbidden");
|
|
645
|
-
}
|
|
646
|
-
const model = await getTenantModel(tenantId, modelName, ability);
|
|
647
|
-
const accessQuery = getAccessibleByQuery(ability, "read", modelName);
|
|
648
|
-
const finalQuery = {
|
|
649
|
-
$and: [query, accessQuery]
|
|
650
|
-
};
|
|
651
|
-
return {
|
|
652
|
-
model,
|
|
653
|
-
finalQuery
|
|
654
|
-
};
|
|
655
|
-
};
|
|
656
|
-
const runApproximateRtsCount = async (model, query) => {
|
|
657
|
-
const unsupportedOperator = findUnsupportedApproxCountOperator(query);
|
|
658
|
-
if (unsupportedOperator) {
|
|
659
|
-
throw new Error(`Approximate RTS count does not support ${unsupportedOperator} queries`);
|
|
660
|
-
}
|
|
661
|
-
const castedQuery = castApproxCountQuery(model, query);
|
|
662
|
-
const estimatedTotal = normalizeNonNegativeInteger(await model.estimatedDocumentCount());
|
|
663
|
-
if (estimatedTotal === 0) return 0;
|
|
664
|
-
const sampleSize = Math.min(getApproxCountSampleSize(), estimatedTotal);
|
|
665
|
-
const sampleResult = await model.aggregate([{
|
|
666
|
-
$sample: {
|
|
667
|
-
size: sampleSize
|
|
668
|
-
}
|
|
669
|
-
}, {
|
|
670
|
-
$match: castedQuery
|
|
671
|
-
}, {
|
|
672
|
-
$count: "count"
|
|
673
|
-
}]);
|
|
674
|
-
const sampleMatches = normalizeNonNegativeInteger(sampleResult[0]?.count);
|
|
675
|
-
if (sampleSize >= estimatedTotal) {
|
|
676
|
-
return Math.min(sampleMatches, estimatedTotal);
|
|
677
|
-
}
|
|
678
|
-
const estimatedMatches = Math.round(estimatedTotal * sampleMatches / sampleSize);
|
|
679
|
-
return Math.max(0, Math.min(estimatedTotal, estimatedMatches));
|
|
680
|
-
};
|
|
681
|
-
const runRtsCount = async ({
|
|
682
|
-
tenantId,
|
|
683
|
-
ability,
|
|
684
|
-
modelName,
|
|
685
|
-
query,
|
|
686
|
-
allowInternalModels = false
|
|
687
|
-
}) => {
|
|
688
|
-
const {
|
|
689
|
-
model,
|
|
690
|
-
finalQuery
|
|
691
|
-
} = await prepareRtsExecution({
|
|
692
|
-
tenantId,
|
|
693
|
-
ability,
|
|
694
|
-
modelName,
|
|
695
|
-
query,
|
|
696
|
-
allowInternalModels
|
|
697
|
-
});
|
|
698
|
-
return await runApproximateRtsCount(model, finalQuery);
|
|
699
|
-
};
|
|
700
|
-
const RTS_QUERY_WINDOW_MAX_COUNT = 4096;
|
|
701
|
-
export {
|
|
702
|
-
RTS_QUERY_WINDOW_MAX_COUNT as R,
|
|
703
|
-
runRtsQuery as a,
|
|
704
|
-
buildRtsAbilityFromRequest as b,
|
|
705
|
-
runRtsCount as c,
|
|
706
|
-
syncAuthenticatedSessionFromRequest as d,
|
|
707
|
-
resolveRtsQueryDependencyModelNames as e,
|
|
708
|
-
getDerivedKey as g,
|
|
709
|
-
isRtsRequestAuthorized as i,
|
|
710
|
-
normalizeRtsQueryOptions as n,
|
|
711
|
-
resolveRtsRequestTenantId as r,
|
|
712
|
-
syncAuthenticatedSessionMiddleware as s
|
|
713
|
-
};
|
|
714
|
-
//# sourceMappingURL=queryWindow-Cdr7K-S1.js.map
|