@rpcbase/db 0.86.0 → 0.88.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/index.js CHANGED
@@ -1,1820 +1,2120 @@
1
- import { buildAbility, buildAbilityFromSession, can, getAccessibleByQuery, getRegisteredPolicies, getTenantRolesFromSessionUser, hasRegisteredPolicy, registerPoliciesFromModules, registerPolicy } from "./acl/index.js";
2
- import { a as makeZE164Phone, c as buildLocaleFallbackChain, d as zI18nString, f as zLocalizedString, i as E164_PHONE_REGEX, l as resolveLocalizedString, n as extendZod, o as zE164Phone, r as E164_PHONE_OR_EMPTY_REGEX, s as LANGUAGE_CODE_REGEX, t as z, u as withLocalizedStringFallback } from "./zod-Co_dG17m.js";
3
- import "./model.js";
4
- import { accessibleBy, accessibleRecordsPlugin } from "@casl/mongoose";
5
- import mongoose, { Schema as Schema$1, Types, default as mongoose$1 } from "mongoose";
6
- import { z as z$1 } from "zod";
7
- import { createHmac, timingSafeEqual } from "node:crypto";
1
+ import { r as registerPoliciesFromModules, h as hasRegisteredPolicy } from "./can-B4pD_pnR.js";
2
+ import { b, a, c, g, d, e, f } from "./can-B4pD_pnR.js";
3
+ import mongoose, { Schema as Schema$1, Types } from "mongoose";
4
+ import { default as default2 } from "mongoose";
5
+ import { z } from "zod";
6
+ import { timingSafeEqual, createHmac } from "node:crypto";
7
+ import { w as withLocalizedStringFallback } from "./index-BQeL_LHp.js";
8
+ import { E, a as a2, L, b as b2, e as e2, m, r, z as z2, c as c2, d as d2, f as f2 } from "./index-BQeL_LHp.js";
8
9
  import assert from "assert";
9
- //#region \0rolldown/runtime.js
10
- var __defProp = Object.defineProperty;
11
- var __exportAll = (all, no_symbols) => {
12
- let target = {};
13
- for (var name in all) __defProp(target, name, {
14
- get: all[name],
15
- enumerable: true
16
- });
17
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
18
- return target;
19
- };
20
- //#endregion
21
- //#region src/models/RBUser.ts
22
- var ZRBUser = z$1.object({
23
- email: z$1.string().email().optional(),
24
- password: z$1.string(),
25
- name: z$1.string().optional(),
26
- phone: z$1.string().optional(),
27
- tenants: z$1.array(z$1.string()),
28
- tenantRoles: z$1.record(z$1.string(), z$1.array(z$1.string())).optional(),
29
- oauthProviders: z$1.record(z$1.string(), z$1.object({
30
- subject: z$1.string(),
31
- email: z$1.string().email().optional(),
32
- name: z$1.string().optional(),
33
- accessToken: z$1.string().optional(),
34
- refreshToken: z$1.string().optional(),
35
- idToken: z$1.string().optional(),
36
- scope: z$1.string().optional(),
37
- tokenType: z$1.string().optional(),
38
- expiresAt: z$1.date().optional(),
39
- rawUserInfo: z$1.unknown().optional(),
40
- createdAt: z$1.date().optional(),
41
- updatedAt: z$1.date().optional()
42
- })).optional(),
43
- emailVerificationCode: z$1.string().length(6).optional(),
44
- emailVerificationExpiresAt: z$1.date().optional(),
45
- passwordResetCode: z$1.string().length(6).optional(),
46
- passwordResetCodeExpiresAt: z$1.date().optional(),
47
- passwordResetToken: z$1.string().optional(),
48
- passwordResetTokenExpiresAt: z$1.date().optional()
10
+ import { accessibleBy, accessibleRecordsPlugin } from "@casl/mongoose";
11
+ import "@casl/ability";
12
+ const ZRBUser = z.object({
13
+ email: z.string().email().optional(),
14
+ password: z.string(),
15
+ name: z.string().optional(),
16
+ phone: z.string().optional(),
17
+ tenants: z.array(z.string()),
18
+ tenantRoles: z.record(z.string(), z.array(z.string())).optional(),
19
+ oauthProviders: z.record(z.string(), z.object({
20
+ subject: z.string(),
21
+ email: z.string().email().optional(),
22
+ name: z.string().optional(),
23
+ accessToken: z.string().optional(),
24
+ refreshToken: z.string().optional(),
25
+ idToken: z.string().optional(),
26
+ scope: z.string().optional(),
27
+ tokenType: z.string().optional(),
28
+ expiresAt: z.date().optional(),
29
+ rawUserInfo: z.unknown().optional(),
30
+ createdAt: z.date().optional(),
31
+ updatedAt: z.date().optional()
32
+ })).optional(),
33
+ emailVerificationCode: z.string().length(6).optional(),
34
+ emailVerificationExpiresAt: z.date().optional(),
35
+ passwordResetCode: z.string().length(6).optional(),
36
+ passwordResetCodeExpiresAt: z.date().optional(),
37
+ passwordResetToken: z.string().optional(),
38
+ passwordResetTokenExpiresAt: z.date().optional()
49
39
  });
50
- var RBUserSchema = new Schema$1({
51
- email: {
52
- type: String,
53
- unique: true,
54
- sparse: true
55
- },
56
- phone: {
57
- type: String,
58
- unique: true,
59
- sparse: true
60
- },
61
- password: {
62
- type: String,
63
- required: true
64
- },
65
- name: String,
66
- tenants: {
67
- type: [String],
68
- index: true,
69
- required: true
70
- },
71
- tenantRoles: {
72
- type: Map,
73
- of: [String],
74
- required: false,
75
- default: {}
76
- },
77
- oauthProviders: {
78
- type: Map,
79
- of: new Schema$1({
80
- subject: {
81
- type: String,
82
- required: true
83
- },
84
- email: {
85
- type: String,
86
- required: false
87
- },
88
- name: {
89
- type: String,
90
- required: false
91
- },
92
- accessToken: {
93
- type: String,
94
- required: false
95
- },
96
- refreshToken: {
97
- type: String,
98
- required: false
99
- },
100
- idToken: {
101
- type: String,
102
- required: false
103
- },
104
- scope: {
105
- type: String,
106
- required: false
107
- },
108
- tokenType: {
109
- type: String,
110
- required: false
111
- },
112
- expiresAt: {
113
- type: Date,
114
- required: false
115
- },
116
- rawUserInfo: {
117
- type: Schema$1.Types.Mixed,
118
- required: false
119
- },
120
- createdAt: {
121
- type: Date,
122
- required: false
123
- },
124
- updatedAt: {
125
- type: Date,
126
- required: false
127
- }
128
- }, { _id: false }),
129
- default: {},
130
- required: false
131
- },
132
- emailVerificationCode: {
133
- type: String,
134
- required: false
135
- },
136
- emailVerificationExpiresAt: {
137
- type: Date,
138
- required: false
139
- },
140
- passwordResetCode: {
141
- type: String,
142
- required: false
143
- },
144
- passwordResetCodeExpiresAt: {
145
- type: Date,
146
- required: false
147
- },
148
- passwordResetToken: {
149
- type: String,
150
- required: false
151
- },
152
- passwordResetTokenExpiresAt: {
153
- type: Date,
154
- required: false
155
- }
40
+ const RBUserSchema = new Schema$1({
41
+ email: {
42
+ type: String,
43
+ unique: true,
44
+ sparse: true
45
+ },
46
+ phone: {
47
+ type: String,
48
+ unique: true,
49
+ sparse: true
50
+ },
51
+ password: {
52
+ type: String,
53
+ required: true
54
+ },
55
+ name: String,
56
+ tenants: {
57
+ type: [String],
58
+ index: true,
59
+ required: true
60
+ },
61
+ tenantRoles: {
62
+ type: Map,
63
+ of: [String],
64
+ required: false,
65
+ default: {}
66
+ },
67
+ oauthProviders: {
68
+ type: Map,
69
+ of: new Schema$1({
70
+ subject: {
71
+ type: String,
72
+ required: true
73
+ },
74
+ email: {
75
+ type: String,
76
+ required: false
77
+ },
78
+ name: {
79
+ type: String,
80
+ required: false
81
+ },
82
+ accessToken: {
83
+ type: String,
84
+ required: false
85
+ },
86
+ refreshToken: {
87
+ type: String,
88
+ required: false
89
+ },
90
+ idToken: {
91
+ type: String,
92
+ required: false
93
+ },
94
+ scope: {
95
+ type: String,
96
+ required: false
97
+ },
98
+ tokenType: {
99
+ type: String,
100
+ required: false
101
+ },
102
+ expiresAt: {
103
+ type: Date,
104
+ required: false
105
+ },
106
+ rawUserInfo: {
107
+ type: Schema$1.Types.Mixed,
108
+ required: false
109
+ },
110
+ createdAt: {
111
+ type: Date,
112
+ required: false
113
+ },
114
+ updatedAt: {
115
+ type: Date,
116
+ required: false
117
+ }
118
+ }, {
119
+ _id: false
120
+ }),
121
+ default: {},
122
+ required: false
123
+ },
124
+ emailVerificationCode: {
125
+ type: String,
126
+ required: false
127
+ },
128
+ emailVerificationExpiresAt: {
129
+ type: Date,
130
+ required: false
131
+ },
132
+ passwordResetCode: {
133
+ type: String,
134
+ required: false
135
+ },
136
+ passwordResetCodeExpiresAt: {
137
+ type: Date,
138
+ required: false
139
+ },
140
+ passwordResetToken: {
141
+ type: String,
142
+ required: false
143
+ },
144
+ passwordResetTokenExpiresAt: {
145
+ type: Date,
146
+ required: false
147
+ }
156
148
  });
157
- //#endregion
158
- //#region src/models/RBTenant.ts
159
- var ZRBTenant = z$1.object({
160
- tenantId: z$1.string(),
161
- parentTenantId: z$1.string().optional(),
162
- name: z$1.string().optional()
149
+ const ZRBTenant = z.object({
150
+ tenantId: z.string(),
151
+ parentTenantId: z.string().optional(),
152
+ name: z.string().optional()
163
153
  });
164
- var RBTenantSchema = new Schema$1({
165
- tenantId: {
166
- type: String,
167
- required: true,
168
- unique: true,
169
- index: true
170
- },
171
- parentTenantId: { type: String },
172
- name: { type: String }
154
+ const RBTenantSchema = new Schema$1({
155
+ tenantId: {
156
+ type: String,
157
+ required: true,
158
+ unique: true,
159
+ index: true
160
+ },
161
+ parentTenantId: {
162
+ type: String
163
+ },
164
+ name: {
165
+ type: String
166
+ }
173
167
  });
174
- //#endregion
175
- //#region src/models/RBTenantSubscription.ts
176
- var ZRBTenantSubscriptionStatus = z$1.enum([
177
- "trialing",
178
- "active",
179
- "past_due",
180
- "paused",
181
- "canceled",
182
- "expired"
183
- ]);
184
- var ZRBTenantSubscriptionIntervalUnit = z$1.enum(["month", "year"]);
185
- var ZRBTenantSubscriptionType = z$1.enum(["primary", "addon"]);
186
- var ZRBTenantSubscriptionScope = z$1.enum([
187
- "tenant",
188
- "shop",
189
- "custom"
190
- ]);
191
- var ZRBTenantSubscription = z$1.object({
192
- tenantId: z$1.string(),
193
- subscriptionId: z$1.string(),
194
- type: ZRBTenantSubscriptionType.optional(),
195
- parentSubscriptionId: z$1.string().optional(),
196
- scope: ZRBTenantSubscriptionScope.optional(),
197
- scopeId: z$1.string().optional(),
198
- planKey: z$1.string(),
199
- status: ZRBTenantSubscriptionStatus,
200
- intervalUnit: ZRBTenantSubscriptionIntervalUnit,
201
- intervalCount: z$1.number().int().min(1).optional(),
202
- modules: z$1.array(z$1.string()).optional(),
203
- currentPeriodStart: z$1.date().optional(),
204
- currentPeriodEnd: z$1.date().optional(),
205
- trialEndsAt: z$1.date().optional(),
206
- cancelAt: z$1.date().optional(),
207
- cancelAtPeriodEnd: z$1.boolean().optional(),
208
- canceledAt: z$1.date().optional(),
209
- provider: z$1.string().optional(),
210
- providerCustomerId: z$1.string().optional(),
211
- providerSubscriptionId: z$1.string().optional(),
212
- latestEventId: z$1.string().optional(),
213
- latestEventAt: z$1.date().optional(),
214
- metadata: z$1.record(z$1.string(), z$1.unknown()).optional()
168
+ const ZRBTenantSubscriptionStatus = z.enum(["trialing", "active", "past_due", "paused", "canceled", "expired"]);
169
+ const ZRBTenantSubscriptionIntervalUnit = z.enum(["month", "year"]);
170
+ const ZRBTenantSubscriptionType = z.enum(["primary", "addon"]);
171
+ const ZRBTenantSubscriptionScope = z.enum(["tenant", "shop", "custom"]);
172
+ const ZRBTenantSubscription = z.object({
173
+ tenantId: z.string(),
174
+ subscriptionId: z.string(),
175
+ type: ZRBTenantSubscriptionType.optional(),
176
+ parentSubscriptionId: z.string().optional(),
177
+ scope: ZRBTenantSubscriptionScope.optional(),
178
+ scopeId: z.string().optional(),
179
+ planKey: z.string(),
180
+ status: ZRBTenantSubscriptionStatus,
181
+ intervalUnit: ZRBTenantSubscriptionIntervalUnit,
182
+ intervalCount: z.number().int().min(1).optional(),
183
+ modules: z.array(z.string()).optional(),
184
+ currentPeriodStart: z.date().optional(),
185
+ currentPeriodEnd: z.date().optional(),
186
+ trialEndsAt: z.date().optional(),
187
+ cancelAt: z.date().optional(),
188
+ cancelAtPeriodEnd: z.boolean().optional(),
189
+ canceledAt: z.date().optional(),
190
+ provider: z.string().optional(),
191
+ providerCustomerId: z.string().optional(),
192
+ providerSubscriptionId: z.string().optional(),
193
+ latestEventId: z.string().optional(),
194
+ latestEventAt: z.date().optional(),
195
+ metadata: z.record(z.string(), z.unknown()).optional()
215
196
  });
216
- var RBTenantSubscriptionSchema = new Schema$1({
217
- tenantId: {
218
- type: String,
219
- required: true,
220
- index: true
221
- },
222
- subscriptionId: {
223
- type: String,
224
- required: true
225
- },
226
- type: {
227
- type: String,
228
- required: true,
229
- enum: ZRBTenantSubscriptionType.options,
230
- default: "primary"
231
- },
232
- parentSubscriptionId: { type: String },
233
- scope: {
234
- type: String,
235
- enum: ZRBTenantSubscriptionScope.options,
236
- default: "tenant"
237
- },
238
- scopeId: { type: String },
239
- planKey: {
240
- type: String,
241
- required: true
242
- },
243
- status: {
244
- type: String,
245
- required: true,
246
- enum: ZRBTenantSubscriptionStatus.options
247
- },
248
- intervalUnit: {
249
- type: String,
250
- required: true,
251
- enum: ZRBTenantSubscriptionIntervalUnit.options
252
- },
253
- intervalCount: {
254
- type: Number,
255
- min: 1,
256
- default: 1
257
- },
258
- modules: {
259
- type: [String],
260
- default: []
261
- },
262
- currentPeriodStart: { type: Date },
263
- currentPeriodEnd: { type: Date },
264
- trialEndsAt: { type: Date },
265
- cancelAt: { type: Date },
266
- cancelAtPeriodEnd: {
267
- type: Boolean,
268
- default: false
269
- },
270
- canceledAt: { type: Date },
271
- provider: { type: String },
272
- providerCustomerId: { type: String },
273
- providerSubscriptionId: { type: String },
274
- latestEventId: { type: String },
275
- latestEventAt: { type: Date },
276
- metadata: { type: Schema$1.Types.Mixed }
197
+ const RBTenantSubscriptionSchema = new Schema$1({
198
+ tenantId: {
199
+ type: String,
200
+ required: true,
201
+ index: true
202
+ },
203
+ subscriptionId: {
204
+ type: String,
205
+ required: true
206
+ },
207
+ type: {
208
+ type: String,
209
+ required: true,
210
+ enum: ZRBTenantSubscriptionType.options,
211
+ default: "primary"
212
+ },
213
+ parentSubscriptionId: {
214
+ type: String
215
+ },
216
+ scope: {
217
+ type: String,
218
+ enum: ZRBTenantSubscriptionScope.options,
219
+ default: "tenant"
220
+ },
221
+ scopeId: {
222
+ type: String
223
+ },
224
+ planKey: {
225
+ type: String,
226
+ required: true
227
+ },
228
+ status: {
229
+ type: String,
230
+ required: true,
231
+ enum: ZRBTenantSubscriptionStatus.options
232
+ },
233
+ intervalUnit: {
234
+ type: String,
235
+ required: true,
236
+ enum: ZRBTenantSubscriptionIntervalUnit.options
237
+ },
238
+ intervalCount: {
239
+ type: Number,
240
+ min: 1,
241
+ default: 1
242
+ },
243
+ modules: {
244
+ type: [String],
245
+ default: []
246
+ },
247
+ currentPeriodStart: {
248
+ type: Date
249
+ },
250
+ currentPeriodEnd: {
251
+ type: Date
252
+ },
253
+ trialEndsAt: {
254
+ type: Date
255
+ },
256
+ cancelAt: {
257
+ type: Date
258
+ },
259
+ cancelAtPeriodEnd: {
260
+ type: Boolean,
261
+ default: false
262
+ },
263
+ canceledAt: {
264
+ type: Date
265
+ },
266
+ provider: {
267
+ type: String
268
+ },
269
+ providerCustomerId: {
270
+ type: String
271
+ },
272
+ providerSubscriptionId: {
273
+ type: String
274
+ },
275
+ latestEventId: {
276
+ type: String
277
+ },
278
+ latestEventAt: {
279
+ type: Date
280
+ },
281
+ metadata: {
282
+ type: Schema$1.Types.Mixed
283
+ }
277
284
  });
278
285
  RBTenantSubscriptionSchema.index({
279
- tenantId: 1,
280
- subscriptionId: 1
281
- }, { unique: true });
286
+ tenantId: 1,
287
+ subscriptionId: 1
288
+ }, {
289
+ unique: true
290
+ });
282
291
  RBTenantSubscriptionSchema.index({
283
- tenantId: 1,
284
- scope: 1,
285
- scopeId: 1
292
+ tenantId: 1,
293
+ scope: 1,
294
+ scopeId: 1
286
295
  });
287
- //#endregion
288
- //#region src/models/RBTenantSubscriptionEvent.ts
289
- var ZRBTenantSubscriptionEventSource = z$1.enum([
290
- "admin",
291
- "system",
292
- "webhook",
293
- "user"
294
- ]);
295
- var ZRBTenantSubscriptionChangeDirection = z$1.enum([
296
- "upgrade",
297
- "downgrade",
298
- "lateral"
299
- ]);
300
- var ZRBTenantSubscriptionEvent = z$1.object({
301
- tenantId: z$1.string(),
302
- subscriptionId: z$1.string(),
303
- type: z$1.string(),
304
- occurredAt: z$1.date(),
305
- effectiveAt: z$1.date().optional(),
306
- fromPlanKey: z$1.string().optional(),
307
- toPlanKey: z$1.string().optional(),
308
- fromStatus: ZRBTenantSubscriptionStatus.optional(),
309
- toStatus: ZRBTenantSubscriptionStatus.optional(),
310
- fromModules: z$1.array(z$1.string()).optional(),
311
- toModules: z$1.array(z$1.string()).optional(),
312
- fromIntervalUnit: ZRBTenantSubscriptionIntervalUnit.optional(),
313
- toIntervalUnit: ZRBTenantSubscriptionIntervalUnit.optional(),
314
- fromIntervalCount: z$1.number().int().min(1).optional(),
315
- toIntervalCount: z$1.number().int().min(1).optional(),
316
- direction: ZRBTenantSubscriptionChangeDirection.optional(),
317
- actorUserId: z$1.string().optional(),
318
- source: ZRBTenantSubscriptionEventSource.optional(),
319
- reason: z$1.string().optional(),
320
- provider: z$1.string().optional(),
321
- providerEventId: z$1.string().optional(),
322
- providerPayload: z$1.unknown().optional(),
323
- metadata: z$1.record(z$1.string(), z$1.unknown()).optional()
296
+ const ZRBTenantSubscriptionEventSource = z.enum(["admin", "system", "webhook", "user"]);
297
+ const ZRBTenantSubscriptionChangeDirection = z.enum(["upgrade", "downgrade", "lateral"]);
298
+ const ZRBTenantSubscriptionEvent = z.object({
299
+ tenantId: z.string(),
300
+ subscriptionId: z.string(),
301
+ type: z.string(),
302
+ occurredAt: z.date(),
303
+ effectiveAt: z.date().optional(),
304
+ fromPlanKey: z.string().optional(),
305
+ toPlanKey: z.string().optional(),
306
+ fromStatus: ZRBTenantSubscriptionStatus.optional(),
307
+ toStatus: ZRBTenantSubscriptionStatus.optional(),
308
+ fromModules: z.array(z.string()).optional(),
309
+ toModules: z.array(z.string()).optional(),
310
+ fromIntervalUnit: ZRBTenantSubscriptionIntervalUnit.optional(),
311
+ toIntervalUnit: ZRBTenantSubscriptionIntervalUnit.optional(),
312
+ fromIntervalCount: z.number().int().min(1).optional(),
313
+ toIntervalCount: z.number().int().min(1).optional(),
314
+ direction: ZRBTenantSubscriptionChangeDirection.optional(),
315
+ actorUserId: z.string().optional(),
316
+ source: ZRBTenantSubscriptionEventSource.optional(),
317
+ reason: z.string().optional(),
318
+ provider: z.string().optional(),
319
+ providerEventId: z.string().optional(),
320
+ providerPayload: z.unknown().optional(),
321
+ metadata: z.record(z.string(), z.unknown()).optional()
324
322
  });
325
- var RBTenantSubscriptionEventSchema = new Schema$1({
326
- tenantId: {
327
- type: String,
328
- required: true,
329
- index: true
330
- },
331
- subscriptionId: {
332
- type: String,
333
- required: true,
334
- index: true
335
- },
336
- type: {
337
- type: String,
338
- required: true
339
- },
340
- occurredAt: {
341
- type: Date,
342
- required: true,
343
- default: Date.now
344
- },
345
- effectiveAt: { type: Date },
346
- fromPlanKey: { type: String },
347
- toPlanKey: { type: String },
348
- fromStatus: {
349
- type: String,
350
- enum: ZRBTenantSubscriptionStatus.options
351
- },
352
- toStatus: {
353
- type: String,
354
- enum: ZRBTenantSubscriptionStatus.options
355
- },
356
- fromModules: {
357
- type: [String],
358
- default: void 0
359
- },
360
- toModules: {
361
- type: [String],
362
- default: void 0
363
- },
364
- fromIntervalUnit: {
365
- type: String,
366
- enum: ZRBTenantSubscriptionIntervalUnit.options
367
- },
368
- toIntervalUnit: {
369
- type: String,
370
- enum: ZRBTenantSubscriptionIntervalUnit.options
371
- },
372
- fromIntervalCount: {
373
- type: Number,
374
- min: 1
375
- },
376
- toIntervalCount: {
377
- type: Number,
378
- min: 1
379
- },
380
- direction: {
381
- type: String,
382
- enum: ZRBTenantSubscriptionChangeDirection.options
383
- },
384
- actorUserId: { type: String },
385
- source: {
386
- type: String,
387
- enum: ZRBTenantSubscriptionEventSource.options
388
- },
389
- reason: { type: String },
390
- provider: { type: String },
391
- providerEventId: { type: String },
392
- providerPayload: { type: Schema$1.Types.Mixed },
393
- metadata: { type: Schema$1.Types.Mixed }
323
+ const RBTenantSubscriptionEventSchema = new Schema$1({
324
+ tenantId: {
325
+ type: String,
326
+ required: true,
327
+ index: true
328
+ },
329
+ subscriptionId: {
330
+ type: String,
331
+ required: true,
332
+ index: true
333
+ },
334
+ type: {
335
+ type: String,
336
+ required: true
337
+ },
338
+ occurredAt: {
339
+ type: Date,
340
+ required: true,
341
+ default: Date.now
342
+ },
343
+ effectiveAt: {
344
+ type: Date
345
+ },
346
+ fromPlanKey: {
347
+ type: String
348
+ },
349
+ toPlanKey: {
350
+ type: String
351
+ },
352
+ fromStatus: {
353
+ type: String,
354
+ enum: ZRBTenantSubscriptionStatus.options
355
+ },
356
+ toStatus: {
357
+ type: String,
358
+ enum: ZRBTenantSubscriptionStatus.options
359
+ },
360
+ fromModules: {
361
+ type: [String],
362
+ default: void 0
363
+ },
364
+ toModules: {
365
+ type: [String],
366
+ default: void 0
367
+ },
368
+ fromIntervalUnit: {
369
+ type: String,
370
+ enum: ZRBTenantSubscriptionIntervalUnit.options
371
+ },
372
+ toIntervalUnit: {
373
+ type: String,
374
+ enum: ZRBTenantSubscriptionIntervalUnit.options
375
+ },
376
+ fromIntervalCount: {
377
+ type: Number,
378
+ min: 1
379
+ },
380
+ toIntervalCount: {
381
+ type: Number,
382
+ min: 1
383
+ },
384
+ direction: {
385
+ type: String,
386
+ enum: ZRBTenantSubscriptionChangeDirection.options
387
+ },
388
+ actorUserId: {
389
+ type: String
390
+ },
391
+ source: {
392
+ type: String,
393
+ enum: ZRBTenantSubscriptionEventSource.options
394
+ },
395
+ reason: {
396
+ type: String
397
+ },
398
+ provider: {
399
+ type: String
400
+ },
401
+ providerEventId: {
402
+ type: String
403
+ },
404
+ providerPayload: {
405
+ type: Schema$1.Types.Mixed
406
+ },
407
+ metadata: {
408
+ type: Schema$1.Types.Mixed
409
+ }
394
410
  });
395
411
  RBTenantSubscriptionEventSchema.index({
396
- tenantId: 1,
397
- subscriptionId: 1,
398
- occurredAt: 1
412
+ tenantId: 1,
413
+ subscriptionId: 1,
414
+ occurredAt: 1
399
415
  });
400
416
  RBTenantSubscriptionEventSchema.index({
401
- provider: 1,
402
- providerEventId: 1
417
+ provider: 1,
418
+ providerEventId: 1
419
+ }, {
420
+ unique: true,
421
+ sparse: true
422
+ });
423
+ const ZRBRtsCounter = z.object({
424
+ _id: z.string(),
425
+ seq: z.number().int().min(0)
426
+ });
427
+ const RBRtsCounterSchema = new Schema$1({
428
+ _id: {
429
+ type: String,
430
+ required: true
431
+ },
432
+ seq: {
433
+ type: Number,
434
+ required: true,
435
+ default: 0
436
+ }
403
437
  }, {
404
- unique: true,
405
- sparse: true
438
+ versionKey: false
406
439
  });
407
- //#endregion
408
- //#region src/models/RBRtsCounter.ts
409
- var ZRBRtsCounter = z$1.object({
410
- _id: z$1.string(),
411
- seq: z$1.number().int().min(0)
440
+ const ttlSecondsRaw = process.env.RB_RTS_CHANGES_TTL_S ?? "";
441
+ const ttlSeconds = Number.isFinite(Number(ttlSecondsRaw)) ? Math.max(60, Math.floor(Number(ttlSecondsRaw))) : 60 * 60 * 24 * 30;
442
+ const ZRBRtsChangeOp = z.enum(["delete", "reset_model"]);
443
+ const ZRBRtsChange = z.object({
444
+ seq: z.number().int().min(0),
445
+ modelName: z.string(),
446
+ op: ZRBRtsChangeOp,
447
+ docId: z.string().optional(),
448
+ ts: z.date()
412
449
  });
413
- var RBRtsCounterSchema = new Schema$1({
414
- _id: {
415
- type: String,
416
- required: true
417
- },
418
- seq: {
419
- type: Number,
420
- required: true,
421
- default: 0
422
- }
423
- }, { versionKey: false });
424
- //#endregion
425
- //#region src/models/RBRtsChange.ts
426
- var ttlSecondsRaw = process.env.RB_RTS_CHANGES_TTL_S ?? "";
427
- var ttlSeconds = Number.isFinite(Number(ttlSecondsRaw)) ? Math.max(60, Math.floor(Number(ttlSecondsRaw))) : 3600 * 24 * 30;
428
- var ZRBRtsChangeOp = z$1.enum(["delete", "reset_model"]);
429
- var ZRBRtsChange = z$1.object({
430
- seq: z$1.number().int().min(0),
431
- modelName: z$1.string(),
432
- op: ZRBRtsChangeOp,
433
- docId: z$1.string().optional(),
434
- ts: z$1.date()
450
+ const RBRtsChangeSchema = new Schema$1({
451
+ seq: {
452
+ type: Number,
453
+ required: true
454
+ },
455
+ modelName: {
456
+ type: String,
457
+ required: true,
458
+ index: true
459
+ },
460
+ op: {
461
+ type: String,
462
+ required: true,
463
+ enum: ZRBRtsChangeOp.options
464
+ },
465
+ docId: {
466
+ type: String,
467
+ required: false
468
+ },
469
+ ts: {
470
+ type: Date,
471
+ required: true,
472
+ default: Date.now
473
+ }
474
+ }, {
475
+ versionKey: false
476
+ });
477
+ RBRtsChangeSchema.index({
478
+ seq: 1
479
+ }, {
480
+ unique: true
435
481
  });
436
- var RBRtsChangeSchema = new Schema$1({
437
- seq: {
438
- type: Number,
439
- required: true
440
- },
441
- modelName: {
442
- type: String,
443
- required: true,
444
- index: true
445
- },
446
- op: {
447
- type: String,
448
- required: true,
449
- enum: ZRBRtsChangeOp.options
450
- },
451
- docId: {
452
- type: String,
453
- required: false
454
- },
455
- ts: {
456
- type: Date,
457
- required: true,
458
- default: Date.now
459
- }
460
- }, { versionKey: false });
461
- RBRtsChangeSchema.index({ seq: 1 }, { unique: true });
462
- RBRtsChangeSchema.index({ ts: 1 }, { expireAfterSeconds: ttlSeconds });
463
- //#endregion
464
- //#region src/models/RBUploadSession.ts
465
- var ZRBUploadSessionStatus = z$1.enum([
466
- "uploading",
467
- "assembling",
468
- "done",
469
- "error"
470
- ]);
471
- var ZRBUploadSession = z$1.object({
472
- _id: z$1.string(),
473
- userId: z$1.string().optional(),
474
- ownerKeyHash: z$1.string().optional(),
475
- filename: z$1.string(),
476
- mimeType: z$1.string(),
477
- totalSize: z$1.number().int().min(0),
478
- chunkSize: z$1.number().int().min(1),
479
- chunksTotal: z$1.number().int().min(1),
480
- status: ZRBUploadSessionStatus,
481
- createdAt: z$1.date(),
482
- expiresAt: z$1.date(),
483
- fileId: z$1.string().optional(),
484
- isPublic: z$1.boolean().optional(),
485
- error: z$1.string().optional()
482
+ RBRtsChangeSchema.index({
483
+ ts: 1
484
+ }, {
485
+ expireAfterSeconds: ttlSeconds
486
+ });
487
+ const ZRBUploadSessionStatus = z.enum(["uploading", "assembling", "done", "error"]);
488
+ const ZRBUploadSession = z.object({
489
+ _id: z.string(),
490
+ userId: z.string().optional(),
491
+ ownerKeyHash: z.string().optional(),
492
+ filename: z.string(),
493
+ mimeType: z.string(),
494
+ totalSize: z.number().int().min(0),
495
+ chunkSize: z.number().int().min(1),
496
+ chunksTotal: z.number().int().min(1),
497
+ status: ZRBUploadSessionStatus,
498
+ createdAt: z.date(),
499
+ expiresAt: z.date(),
500
+ fileId: z.string().optional(),
501
+ isPublic: z.boolean().optional(),
502
+ error: z.string().optional()
503
+ });
504
+ const RBUploadSessionSchema = new Schema$1({
505
+ _id: {
506
+ type: String,
507
+ required: true
508
+ },
509
+ userId: {
510
+ type: String,
511
+ required: false,
512
+ index: true
513
+ },
514
+ ownerKeyHash: {
515
+ type: String,
516
+ required: false
517
+ },
518
+ filename: {
519
+ type: String,
520
+ required: true
521
+ },
522
+ mimeType: {
523
+ type: String,
524
+ required: true
525
+ },
526
+ totalSize: {
527
+ type: Number,
528
+ required: true
529
+ },
530
+ chunkSize: {
531
+ type: Number,
532
+ required: true
533
+ },
534
+ chunksTotal: {
535
+ type: Number,
536
+ required: true
537
+ },
538
+ status: {
539
+ type: String,
540
+ required: true,
541
+ enum: ZRBUploadSessionStatus.options
542
+ },
543
+ createdAt: {
544
+ type: Date,
545
+ required: true,
546
+ default: Date.now
547
+ },
548
+ expiresAt: {
549
+ type: Date,
550
+ required: true
551
+ },
552
+ fileId: {
553
+ type: String,
554
+ required: false
555
+ },
556
+ isPublic: {
557
+ type: Boolean,
558
+ required: false
559
+ },
560
+ error: {
561
+ type: String,
562
+ required: false
563
+ }
564
+ }, {
565
+ versionKey: false
566
+ });
567
+ RBUploadSessionSchema.index({
568
+ expiresAt: 1
569
+ }, {
570
+ expireAfterSeconds: 0
571
+ });
572
+ const RBUploadSessionPolicy = {
573
+ subject: "RBUploadSession",
574
+ define: (builder, ctx) => {
575
+ builder.can("create", "RBUploadSession");
576
+ if (ctx.userId) {
577
+ builder.can("read", "RBUploadSession", {
578
+ userId: ctx.userId
579
+ });
580
+ builder.can("update", "RBUploadSession", {
581
+ userId: ctx.userId
582
+ });
583
+ builder.can("delete", "RBUploadSession", {
584
+ userId: ctx.userId
585
+ });
586
+ }
587
+ const uploadKeyHash = typeof ctx.claims?.uploadKeyHash === "string" ? ctx.claims.uploadKeyHash.trim() : "";
588
+ if (uploadKeyHash) {
589
+ builder.can("read", "RBUploadSession", {
590
+ ownerKeyHash: uploadKeyHash
591
+ });
592
+ builder.can("update", "RBUploadSession", {
593
+ ownerKeyHash: uploadKeyHash
594
+ });
595
+ builder.can("delete", "RBUploadSession", {
596
+ ownerKeyHash: uploadKeyHash
597
+ });
598
+ }
599
+ }
600
+ };
601
+ const ZRBUploadChunk = z.object({
602
+ uploadId: z.string(),
603
+ index: z.number().int().min(0),
604
+ data: z.unknown(),
605
+ size: z.number().int().min(0),
606
+ sha256: z.string().optional(),
607
+ createdAt: z.date(),
608
+ expiresAt: z.date()
609
+ });
610
+ const RBUploadChunkSchema = new Schema$1({
611
+ uploadId: {
612
+ type: String,
613
+ required: true,
614
+ index: true
615
+ },
616
+ index: {
617
+ type: Number,
618
+ required: true
619
+ },
620
+ data: {
621
+ type: Buffer,
622
+ required: true
623
+ },
624
+ size: {
625
+ type: Number,
626
+ required: true
627
+ },
628
+ sha256: {
629
+ type: String,
630
+ required: false
631
+ },
632
+ createdAt: {
633
+ type: Date,
634
+ required: true,
635
+ default: Date.now
636
+ },
637
+ expiresAt: {
638
+ type: Date,
639
+ required: true
640
+ }
641
+ }, {
642
+ versionKey: false
486
643
  });
487
- var RBUploadSessionSchema = new Schema$1({
488
- _id: {
489
- type: String,
490
- required: true
491
- },
492
- userId: {
493
- type: String,
494
- required: false,
495
- index: true
496
- },
497
- ownerKeyHash: {
498
- type: String,
499
- required: false
500
- },
501
- filename: {
502
- type: String,
503
- required: true
504
- },
505
- mimeType: {
506
- type: String,
507
- required: true
508
- },
509
- totalSize: {
510
- type: Number,
511
- required: true
512
- },
513
- chunkSize: {
514
- type: Number,
515
- required: true
516
- },
517
- chunksTotal: {
518
- type: Number,
519
- required: true
520
- },
521
- status: {
522
- type: String,
523
- required: true,
524
- enum: ZRBUploadSessionStatus.options
525
- },
526
- createdAt: {
527
- type: Date,
528
- required: true,
529
- default: Date.now
530
- },
531
- expiresAt: {
532
- type: Date,
533
- required: true
534
- },
535
- fileId: {
536
- type: String,
537
- required: false
538
- },
539
- isPublic: {
540
- type: Boolean,
541
- required: false
542
- },
543
- error: {
544
- type: String,
545
- required: false
546
- }
547
- }, { versionKey: false });
548
- RBUploadSessionSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
549
- var RBUploadSessionPolicy = {
550
- subject: "RBUploadSession",
551
- define: (builder, ctx) => {
552
- builder.can("create", "RBUploadSession");
553
- if (ctx.userId) {
554
- builder.can("read", "RBUploadSession", { userId: ctx.userId });
555
- builder.can("update", "RBUploadSession", { userId: ctx.userId });
556
- builder.can("delete", "RBUploadSession", { userId: ctx.userId });
557
- }
558
- const uploadKeyHash = typeof ctx.claims?.uploadKeyHash === "string" ? ctx.claims.uploadKeyHash.trim() : "";
559
- if (uploadKeyHash) {
560
- builder.can("read", "RBUploadSession", { ownerKeyHash: uploadKeyHash });
561
- builder.can("update", "RBUploadSession", { ownerKeyHash: uploadKeyHash });
562
- builder.can("delete", "RBUploadSession", { ownerKeyHash: uploadKeyHash });
563
- }
564
- }
565
- };
566
- //#endregion
567
- //#region src/models/RBUploadChunk.ts
568
- var ZRBUploadChunk = z$1.object({
569
- uploadId: z$1.string(),
570
- index: z$1.number().int().min(0),
571
- data: z$1.unknown(),
572
- size: z$1.number().int().min(0),
573
- sha256: z$1.string().optional(),
574
- createdAt: z$1.date(),
575
- expiresAt: z$1.date()
644
+ RBUploadChunkSchema.index({
645
+ uploadId: 1,
646
+ index: 1
647
+ }, {
648
+ unique: true
576
649
  });
577
- var RBUploadChunkSchema = new Schema$1({
578
- uploadId: {
579
- type: String,
580
- required: true,
581
- index: true
582
- },
583
- index: {
584
- type: Number,
585
- required: true
586
- },
587
- data: {
588
- type: Buffer,
589
- required: true
590
- },
591
- size: {
592
- type: Number,
593
- required: true
594
- },
595
- sha256: {
596
- type: String,
597
- required: false
598
- },
599
- createdAt: {
600
- type: Date,
601
- required: true,
602
- default: Date.now
603
- },
604
- expiresAt: {
605
- type: Date,
606
- required: true
607
- }
608
- }, { versionKey: false });
609
650
  RBUploadChunkSchema.index({
610
- uploadId: 1,
611
- index: 1
612
- }, { unique: true });
613
- RBUploadChunkSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
614
- //#endregion
615
- //#region src/models/RBNotification.ts
616
- var ZRBNotification = z$1.object({
617
- userId: z$1.string(),
618
- topic: z$1.string().optional(),
619
- title: z$1.string(),
620
- body: z$1.string().optional(),
621
- url: z$1.string().optional(),
622
- createdAt: z$1.date(),
623
- seenAt: z$1.date().optional(),
624
- readAt: z$1.date().optional(),
625
- archivedAt: z$1.date().optional(),
626
- metadata: z$1.record(z$1.string(), z$1.unknown()).optional()
651
+ expiresAt: 1
652
+ }, {
653
+ expireAfterSeconds: 0
654
+ });
655
+ const ZRBNotification = z.object({
656
+ userId: z.string(),
657
+ topic: z.string().optional(),
658
+ title: z.string(),
659
+ body: z.string().optional(),
660
+ url: z.string().optional(),
661
+ createdAt: z.date(),
662
+ seenAt: z.date().optional(),
663
+ readAt: z.date().optional(),
664
+ archivedAt: z.date().optional(),
665
+ metadata: z.record(z.string(), z.unknown()).optional()
666
+ });
667
+ const TTL_90_DAYS_S = 60 * 60 * 24 * 90;
668
+ const RBNotificationSchema = new Schema$1({
669
+ userId: {
670
+ type: String,
671
+ required: true,
672
+ index: true
673
+ },
674
+ topic: {
675
+ type: String,
676
+ required: false,
677
+ index: true
678
+ },
679
+ title: {
680
+ type: String,
681
+ required: true
682
+ },
683
+ body: {
684
+ type: String,
685
+ required: false
686
+ },
687
+ url: {
688
+ type: String,
689
+ required: false
690
+ },
691
+ createdAt: {
692
+ type: Date,
693
+ required: true,
694
+ default: Date.now,
695
+ index: true
696
+ },
697
+ seenAt: {
698
+ type: Date,
699
+ required: false,
700
+ index: true
701
+ },
702
+ readAt: {
703
+ type: Date,
704
+ required: false,
705
+ index: true
706
+ },
707
+ archivedAt: {
708
+ type: Date,
709
+ required: false
710
+ },
711
+ metadata: {
712
+ type: Schema$1.Types.Mixed,
713
+ required: false
714
+ }
715
+ }, {
716
+ versionKey: false
627
717
  });
628
- var TTL_90_DAYS_S = 3600 * 24 * 90;
629
- var RBNotificationSchema = new Schema$1({
630
- userId: {
631
- type: String,
632
- required: true,
633
- index: true
634
- },
635
- topic: {
636
- type: String,
637
- required: false,
638
- index: true
639
- },
640
- title: {
641
- type: String,
642
- required: true
643
- },
644
- body: {
645
- type: String,
646
- required: false
647
- },
648
- url: {
649
- type: String,
650
- required: false
651
- },
652
- createdAt: {
653
- type: Date,
654
- required: true,
655
- default: Date.now,
656
- index: true
657
- },
658
- seenAt: {
659
- type: Date,
660
- required: false,
661
- index: true
662
- },
663
- readAt: {
664
- type: Date,
665
- required: false,
666
- index: true
667
- },
668
- archivedAt: {
669
- type: Date,
670
- required: false
671
- },
672
- metadata: {
673
- type: Schema$1.Types.Mixed,
674
- required: false
675
- }
676
- }, { versionKey: false });
677
718
  RBNotificationSchema.index({
678
- userId: 1,
679
- archivedAt: 1,
680
- createdAt: -1
719
+ userId: 1,
720
+ archivedAt: 1,
721
+ createdAt: -1
681
722
  });
682
723
  RBNotificationSchema.index({
683
- userId: 1,
684
- seenAt: 1,
685
- archivedAt: 1,
686
- createdAt: -1
724
+ userId: 1,
725
+ seenAt: 1,
726
+ archivedAt: 1,
727
+ createdAt: -1
687
728
  });
688
729
  RBNotificationSchema.index({
689
- userId: 1,
690
- readAt: 1,
691
- archivedAt: 1,
692
- createdAt: -1
730
+ userId: 1,
731
+ readAt: 1,
732
+ archivedAt: 1,
733
+ createdAt: -1
693
734
  });
694
- RBNotificationSchema.index({ archivedAt: 1 }, { expireAfterSeconds: TTL_90_DAYS_S });
695
- var RBNotificationPolicy = {
696
- subject: "RBNotification",
697
- define: (builder, ctx) => {
698
- if (!ctx.userId) return;
699
- builder.can("create", "RBNotification");
700
- builder.can("read", "RBNotification", { userId: ctx.userId });
701
- builder.can("update", "RBNotification", { userId: ctx.userId });
702
- builder.can("delete", "RBNotification", { userId: ctx.userId });
703
- }
704
- };
705
- //#endregion
706
- //#region src/models/RBNotificationSettings.ts
707
- var ZRBNotificationDigestFrequency = z$1.enum([
708
- "off",
709
- "daily",
710
- "weekly"
711
- ]);
712
- var ZRBNotificationTopicPreference = z$1.object({
713
- topic: z$1.string(),
714
- inApp: z$1.boolean(),
715
- emailDigest: z$1.boolean(),
716
- push: z$1.boolean()
735
+ RBNotificationSchema.index({
736
+ archivedAt: 1
737
+ }, {
738
+ expireAfterSeconds: TTL_90_DAYS_S
717
739
  });
718
- var ZRBNotificationSettings = z$1.object({
719
- userId: z$1.string(),
720
- digestFrequency: ZRBNotificationDigestFrequency,
721
- topicPreferences: z$1.array(ZRBNotificationTopicPreference).optional(),
722
- lastDigestSentAt: z$1.date().optional()
740
+ const RBNotificationPolicy = {
741
+ subject: "RBNotification",
742
+ define: (builder, ctx) => {
743
+ if (!ctx.userId) return;
744
+ builder.can("create", "RBNotification");
745
+ builder.can("read", "RBNotification", {
746
+ userId: ctx.userId
747
+ });
748
+ builder.can("update", "RBNotification", {
749
+ userId: ctx.userId
750
+ });
751
+ builder.can("delete", "RBNotification", {
752
+ userId: ctx.userId
753
+ });
754
+ }
755
+ };
756
+ const ZRBNotificationDigestFrequency = z.enum(["off", "daily", "weekly"]);
757
+ const ZRBNotificationTopicPreference = z.object({
758
+ topic: z.string(),
759
+ inApp: z.boolean(),
760
+ emailDigest: z.boolean(),
761
+ push: z.boolean()
723
762
  });
724
- var TopicPreferenceSchema = new Schema$1({
725
- topic: {
726
- type: String,
727
- required: true
728
- },
729
- inApp: {
730
- type: Boolean,
731
- required: true,
732
- default: true
733
- },
734
- emailDigest: {
735
- type: Boolean,
736
- required: true,
737
- default: true
738
- },
739
- push: {
740
- type: Boolean,
741
- required: true,
742
- default: false
743
- }
744
- }, { _id: false });
745
- var RBNotificationSettingsSchema = new Schema$1({
746
- userId: {
747
- type: String,
748
- required: true
749
- },
750
- digestFrequency: {
751
- type: String,
752
- required: true,
753
- enum: ZRBNotificationDigestFrequency.options,
754
- default: "weekly"
755
- },
756
- topicPreferences: {
757
- type: [TopicPreferenceSchema],
758
- default: []
759
- },
760
- lastDigestSentAt: {
761
- type: Date,
762
- required: false
763
- }
763
+ const ZRBNotificationSettings = z.object({
764
+ userId: z.string(),
765
+ digestFrequency: ZRBNotificationDigestFrequency,
766
+ topicPreferences: z.array(ZRBNotificationTopicPreference).optional(),
767
+ lastDigestSentAt: z.date().optional()
768
+ });
769
+ const TopicPreferenceSchema = new Schema$1({
770
+ topic: {
771
+ type: String,
772
+ required: true
773
+ },
774
+ inApp: {
775
+ type: Boolean,
776
+ required: true,
777
+ default: true
778
+ },
779
+ emailDigest: {
780
+ type: Boolean,
781
+ required: true,
782
+ default: true
783
+ },
784
+ push: {
785
+ type: Boolean,
786
+ required: true,
787
+ default: false
788
+ }
764
789
  }, {
765
- versionKey: false,
766
- timestamps: true
790
+ _id: false
791
+ });
792
+ const RBNotificationSettingsSchema = new Schema$1({
793
+ userId: {
794
+ type: String,
795
+ required: true
796
+ },
797
+ digestFrequency: {
798
+ type: String,
799
+ required: true,
800
+ enum: ZRBNotificationDigestFrequency.options,
801
+ default: "weekly"
802
+ },
803
+ topicPreferences: {
804
+ type: [TopicPreferenceSchema],
805
+ default: []
806
+ },
807
+ lastDigestSentAt: {
808
+ type: Date,
809
+ required: false
810
+ }
811
+ }, {
812
+ versionKey: false,
813
+ timestamps: true
767
814
  });
768
- RBNotificationSettingsSchema.index({ userId: 1 }, { unique: true });
769
815
  RBNotificationSettingsSchema.index({
770
- userId: 1,
771
- "topicPreferences.topic": 1
816
+ userId: 1
817
+ }, {
818
+ unique: true
772
819
  });
773
- var RBNotificationSettingsPolicy = {
774
- subject: "RBNotificationSettings",
775
- define: (builder, ctx) => {
776
- if (!ctx.userId) return;
777
- builder.can("create", "RBNotificationSettings");
778
- builder.can("read", "RBNotificationSettings", { userId: ctx.userId });
779
- builder.can("update", "RBNotificationSettings", { userId: ctx.userId });
780
- }
781
- };
782
- //#endregion
783
- //#region src/models/RBOAuthRequest.ts
784
- var ZRBOAuthRequest = z$1.object({
785
- _id: z$1.string(),
786
- providerId: z$1.string(),
787
- codeVerifier: z$1.string(),
788
- returnTo: z$1.string().optional(),
789
- createdAt: z$1.date(),
790
- expiresAt: z$1.date()
820
+ RBNotificationSettingsSchema.index({
821
+ userId: 1,
822
+ "topicPreferences.topic": 1
791
823
  });
792
- var RBOAuthRequestSchema = new Schema$1({
793
- _id: {
794
- type: String,
795
- required: true
796
- },
797
- providerId: {
798
- type: String,
799
- required: true,
800
- index: true
801
- },
802
- codeVerifier: {
803
- type: String,
804
- required: true
805
- },
806
- returnTo: {
807
- type: String,
808
- required: false
809
- },
810
- createdAt: {
811
- type: Date,
812
- required: true,
813
- default: Date.now
814
- },
815
- expiresAt: {
816
- type: Date,
817
- required: true
818
- }
819
- }, { versionKey: false });
820
- RBOAuthRequestSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
821
- //#endregion
822
- //#region src/models/index.ts
823
- var models_exports = /* @__PURE__ */ __exportAll({
824
- RBNotificationPolicy: () => RBNotificationPolicy,
825
- RBNotificationSchema: () => RBNotificationSchema,
826
- RBNotificationSettingsPolicy: () => RBNotificationSettingsPolicy,
827
- RBNotificationSettingsSchema: () => RBNotificationSettingsSchema,
828
- RBOAuthRequestSchema: () => RBOAuthRequestSchema,
829
- RBRtsChangeSchema: () => RBRtsChangeSchema,
830
- RBRtsCounterSchema: () => RBRtsCounterSchema,
831
- RBTenantSchema: () => RBTenantSchema,
832
- RBTenantSubscriptionEventSchema: () => RBTenantSubscriptionEventSchema,
833
- RBTenantSubscriptionSchema: () => RBTenantSubscriptionSchema,
834
- RBUploadChunkSchema: () => RBUploadChunkSchema,
835
- RBUploadSessionPolicy: () => RBUploadSessionPolicy,
836
- RBUploadSessionSchema: () => RBUploadSessionSchema,
837
- RBUserSchema: () => RBUserSchema,
838
- ZRBNotification: () => ZRBNotification,
839
- ZRBNotificationDigestFrequency: () => ZRBNotificationDigestFrequency,
840
- ZRBNotificationSettings: () => ZRBNotificationSettings,
841
- ZRBNotificationTopicPreference: () => ZRBNotificationTopicPreference,
842
- ZRBOAuthRequest: () => ZRBOAuthRequest,
843
- ZRBRtsChange: () => ZRBRtsChange,
844
- ZRBRtsChangeOp: () => ZRBRtsChangeOp,
845
- ZRBRtsCounter: () => ZRBRtsCounter,
846
- ZRBTenant: () => ZRBTenant,
847
- ZRBTenantSubscription: () => ZRBTenantSubscription,
848
- ZRBTenantSubscriptionChangeDirection: () => ZRBTenantSubscriptionChangeDirection,
849
- ZRBTenantSubscriptionEvent: () => ZRBTenantSubscriptionEvent,
850
- ZRBTenantSubscriptionEventSource: () => ZRBTenantSubscriptionEventSource,
851
- ZRBTenantSubscriptionIntervalUnit: () => ZRBTenantSubscriptionIntervalUnit,
852
- ZRBTenantSubscriptionScope: () => ZRBTenantSubscriptionScope,
853
- ZRBTenantSubscriptionStatus: () => ZRBTenantSubscriptionStatus,
854
- ZRBTenantSubscriptionType: () => ZRBTenantSubscriptionType,
855
- ZRBUploadChunk: () => ZRBUploadChunk,
856
- ZRBUploadSession: () => ZRBUploadSession,
857
- ZRBUploadSessionStatus: () => ZRBUploadSessionStatus,
858
- ZRBUser: () => ZRBUser
824
+ const RBNotificationSettingsPolicy = {
825
+ subject: "RBNotificationSettings",
826
+ define: (builder, ctx) => {
827
+ if (!ctx.userId) return;
828
+ builder.can("create", "RBNotificationSettings");
829
+ builder.can("read", "RBNotificationSettings", {
830
+ userId: ctx.userId
831
+ });
832
+ builder.can("update", "RBNotificationSettings", {
833
+ userId: ctx.userId
834
+ });
835
+ }
836
+ };
837
+ const ZRBOAuthRequest = z.object({
838
+ _id: z.string(),
839
+ providerId: z.string(),
840
+ codeVerifier: z.string(),
841
+ returnTo: z.string().optional(),
842
+ createdAt: z.date(),
843
+ expiresAt: z.date()
844
+ });
845
+ const RBOAuthRequestSchema = new Schema$1({
846
+ _id: {
847
+ type: String,
848
+ required: true
849
+ },
850
+ providerId: {
851
+ type: String,
852
+ required: true,
853
+ index: true
854
+ },
855
+ codeVerifier: {
856
+ type: String,
857
+ required: true
858
+ },
859
+ returnTo: {
860
+ type: String,
861
+ required: false
862
+ },
863
+ createdAt: {
864
+ type: Date,
865
+ required: true,
866
+ default: Date.now
867
+ },
868
+ expiresAt: {
869
+ type: Date,
870
+ required: true
871
+ }
872
+ }, {
873
+ versionKey: false
859
874
  });
860
- //#endregion
861
- //#region src/mongoose/extendMongooseSchema.ts
875
+ RBOAuthRequestSchema.index({
876
+ expiresAt: 1
877
+ }, {
878
+ expireAfterSeconds: 0
879
+ });
880
+ const frameworkSchemas = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
881
+ __proto__: null,
882
+ RBNotificationPolicy,
883
+ RBNotificationSchema,
884
+ RBNotificationSettingsPolicy,
885
+ RBNotificationSettingsSchema,
886
+ RBOAuthRequestSchema,
887
+ RBRtsChangeSchema,
888
+ RBRtsCounterSchema,
889
+ RBTenantSchema,
890
+ RBTenantSubscriptionEventSchema,
891
+ RBTenantSubscriptionSchema,
892
+ RBUploadChunkSchema,
893
+ RBUploadSessionPolicy,
894
+ RBUploadSessionSchema,
895
+ RBUserSchema,
896
+ ZRBNotification,
897
+ ZRBNotificationDigestFrequency,
898
+ ZRBNotificationSettings,
899
+ ZRBNotificationTopicPreference,
900
+ ZRBOAuthRequest,
901
+ ZRBRtsChange,
902
+ ZRBRtsChangeOp,
903
+ ZRBRtsCounter,
904
+ ZRBTenant,
905
+ ZRBTenantSubscription,
906
+ ZRBTenantSubscriptionChangeDirection,
907
+ ZRBTenantSubscriptionEvent,
908
+ ZRBTenantSubscriptionEventSource,
909
+ ZRBTenantSubscriptionIntervalUnit,
910
+ ZRBTenantSubscriptionScope,
911
+ ZRBTenantSubscriptionStatus,
912
+ ZRBTenantSubscriptionType,
913
+ ZRBUploadChunk,
914
+ ZRBUploadSession,
915
+ ZRBUploadSessionStatus,
916
+ ZRBUser
917
+ }, Symbol.toStringTag, { value: "Module" }));
862
918
  function extendMongooseSchema(baseSchema, ...extensions) {
863
- const schema = baseSchema.clone();
864
- extensions.forEach((extension) => schema.add(extension));
865
- return schema;
919
+ const schema = baseSchema.clone();
920
+ extensions.forEach((extension) => schema.add(extension));
921
+ return schema;
866
922
  }
867
923
  function omitMongooseSchemaPaths(schema, paths) {
868
- const clone = schema.clone();
869
- paths.forEach((path) => clone.remove(path));
870
- return clone;
924
+ const clone = schema.clone();
925
+ paths.forEach((path) => clone.remove(path));
926
+ return clone;
871
927
  }
872
- //#endregion
873
- //#region src/mongoose/localizedStringField.ts
874
928
  function localizedStringField(options) {
875
- const userGet = options?.get;
876
- return {
877
- ...options,
878
- type: Schema$1.Types.Mixed,
879
- get: (value) => withLocalizedStringFallback(userGet ? userGet(value) : value)
880
- };
929
+ const userGet = options?.get;
930
+ return {
931
+ ...options,
932
+ type: Schema$1.Types.Mixed,
933
+ get: (value) => withLocalizedStringFallback(userGet ? userGet(value) : value)
934
+ };
881
935
  }
882
- //#endregion
883
- //#region src/mongoose/index.ts
884
- var { Schema, model } = mongoose;
885
- //#endregion
886
- //#region src/pagination/errors.ts
887
- var PaginationValidationError = class extends Error {
888
- code = "invalid_pagination";
889
- statusCode = 400;
890
- constructor(message, options) {
891
- super(message, options);
892
- this.name = "PaginationValidationError";
893
- }
894
- };
895
- var isPaginationValidationError = (error) => {
896
- if (!error || typeof error !== "object") return false;
897
- const anyError = error;
898
- return anyError.name === "PaginationValidationError" && anyError.code === "invalid_pagination" && anyError.statusCode === 400;
899
- };
900
- //#endregion
901
- //#region src/pagination/normalizeSpec.ts
902
- var DISALLOWED_MONGO_FIELD_SEGMENTS = new Set([
903
- "__proto__",
904
- "constructor",
905
- "prototype"
906
- ]);
907
- var PAGINATION_LIMIT_MAX = 128;
908
- var normalizePaginationSpec = (spec) => {
909
- if (!spec || typeof spec !== "object") throw new PaginationValidationError("Invalid PaginationSpec");
910
- const limit = normalizeLimit(spec.limit);
911
- const direction = spec.direction ?? "next";
912
- if (direction !== "next" && direction !== "prev") throw new PaginationValidationError("Invalid pagination direction");
913
- if (!Array.isArray(spec.sort) || spec.sort.length === 0) throw new PaginationValidationError("Invalid pagination sort");
914
- const sort = spec.sort.map(({ field, order }) => ({
915
- field: assertSafeMongoFieldPath(field),
916
- order: normalizeOrder(order)
917
- }));
918
- const seenFields = /* @__PURE__ */ new Set();
919
- for (const { field } of sort) {
920
- if (seenFields.has(field)) throw new PaginationValidationError(`Duplicate pagination sort field: ${field}`);
921
- seenFields.add(field);
922
- }
923
- const primaryOrder = sort[0]?.order;
924
- if (!seenFields.has("_id")) sort.push({
925
- field: "_id",
926
- order: primaryOrder
927
- });
928
- return {
929
- ...spec,
930
- limit,
931
- direction,
932
- sort
933
- };
934
- };
935
- var normalizeLimit = (limit) => {
936
- if (typeof limit !== "number" || !Number.isFinite(limit) || !Number.isInteger(limit) || limit <= 0) throw new PaginationValidationError("Invalid pagination limit");
937
- if (limit > PAGINATION_LIMIT_MAX) throw new PaginationValidationError("Invalid pagination limit");
938
- return limit;
939
- };
940
- var normalizeOrder = (order) => {
941
- if (order !== "asc" && order !== "desc") throw new PaginationValidationError("Invalid pagination order");
942
- return order;
943
- };
944
- var assertSafeMongoFieldPath = (field) => {
945
- if (typeof field !== "string" || field.length === 0) throw new PaginationValidationError("Invalid pagination sort field");
946
- if (field.startsWith("$")) throw new PaginationValidationError("Invalid pagination sort field");
947
- const parts = field.split(".");
948
- for (const part of parts) {
949
- if (part.length === 0) throw new PaginationValidationError("Invalid pagination sort field");
950
- if (DISALLOWED_MONGO_FIELD_SEGMENTS.has(part)) throw new PaginationValidationError("Invalid pagination sort field");
951
- if (!/^[a-zA-Z0-9_]+$/.test(part)) throw new PaginationValidationError("Invalid pagination sort field");
952
- }
953
- return field;
954
- };
955
- //#endregion
956
- //#region src/pagination/cursor.ts
957
- var encodePaginationCursor = (spec, node, options) => {
958
- const normalized = normalizePaginationSpec(spec);
959
- const values = Object.create(null);
960
- for (const { field } of normalized.sort) {
961
- const value = readFieldValue(node, field);
962
- if (typeof value === "undefined") throw new Error(`Pagination cursor encode failed (missing field: ${field})`);
963
- values[field] = encodeCursorValue(field, value);
964
- }
965
- const payload = {
966
- v: 1,
967
- values
968
- };
969
- const payloadB64 = Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
970
- return `${payloadB64}.${signCursorPayloadB64(payloadB64, options.signingSecret)}`;
971
- };
972
- var decodePaginationCursor = (spec, cursor, options) => {
973
- const normalized = normalizePaginationSpec(spec);
974
- const [payloadB64, sigB64, ...rest] = cursor.split(".");
975
- if (rest.length > 0) throw new PaginationValidationError("Invalid pagination cursor format");
976
- if (!sigB64) throw new PaginationValidationError("Invalid pagination cursor format");
977
- verifyCursorSignature(payloadB64, sigB64, options.signingSecret);
978
- const payloadRaw = Buffer.from(payloadB64, "base64url").toString("utf8");
979
- let payloadUnknown;
980
- try {
981
- payloadUnknown = JSON.parse(payloadRaw);
982
- } catch {
983
- throw new PaginationValidationError("Invalid pagination cursor payload");
984
- }
985
- if (!payloadUnknown || typeof payloadUnknown !== "object") throw new PaginationValidationError("Invalid pagination cursor payload");
986
- const payload = payloadUnknown;
987
- if (payload.v !== 1) throw new PaginationValidationError("Unsupported pagination cursor version");
988
- if (!payload.values || typeof payload.values !== "object") throw new PaginationValidationError("Invalid pagination cursor payload");
989
- const decoded = Object.create(null);
990
- for (const { field } of normalized.sort) {
991
- if (!Object.prototype.hasOwnProperty.call(payload.values, field)) throw new PaginationValidationError(`Pagination cursor missing field: ${field}`);
992
- const encodedValue = payload.values[field];
993
- decoded[field] = decodeCursorValue(field, encodedValue);
994
- }
995
- return decoded;
996
- };
997
- var signCursorPayloadB64 = (payloadB64, secret) => {
998
- return createHmac("sha256", secret).update(payloadB64, "utf8").digest("base64url");
999
- };
1000
- var verifyCursorSignature = (payloadB64, sigB64, secret) => {
1001
- const expectedSigB64 = signCursorPayloadB64(payloadB64, secret);
1002
- const a = Buffer.from(sigB64, "utf8");
1003
- const b = Buffer.from(expectedSigB64, "utf8");
1004
- if (a.length !== b.length || !timingSafeEqual(a, b)) throw new PaginationValidationError("Invalid pagination cursor signature");
1005
- };
1006
- var unwrapCursorComparableValue = (value) => {
1007
- if (value === null || typeof value !== "object") return value;
1008
- if (value instanceof Date || value instanceof Types.ObjectId) return value;
1009
- let current = value;
1010
- for (let depth = 0; depth < 4; depth += 1) {
1011
- if (!current || typeof current !== "object") return current;
1012
- if (current instanceof Date || current instanceof Types.ObjectId) return current;
1013
- if (!("_id" in current)) return current;
1014
- current = current._id;
1015
- }
1016
- return current;
1017
- };
1018
- var encodeCursorValue = (field, value) => {
1019
- const comparableValue = unwrapCursorComparableValue(value);
1020
- if (comparableValue === null) return null;
1021
- if (field === "_id") {
1022
- if (comparableValue instanceof Types.ObjectId) return { $oid: comparableValue.toHexString() };
1023
- if (typeof comparableValue === "string") return comparableValue;
1024
- throw new Error("Pagination cursor encode failed (_id must be an ObjectId or string)");
1025
- }
1026
- if (comparableValue instanceof Date) return { $date: comparableValue.toISOString() };
1027
- if (typeof comparableValue === "string" || typeof comparableValue === "number" || typeof comparableValue === "boolean") return comparableValue;
1028
- if (comparableValue instanceof Types.ObjectId) return { $oid: comparableValue.toHexString() };
1029
- throw new Error(`Unsupported pagination cursor value type for field: ${field}`);
1030
- };
1031
- var decodeCursorValue = (field, value) => {
1032
- if (value === null) return null;
1033
- if (typeof value === "string") return value;
1034
- if (typeof value === "number" || typeof value === "boolean") return value;
1035
- if (!value || typeof value !== "object") throw new PaginationValidationError(`Invalid pagination cursor value for field: ${field}`);
1036
- if ("$date" in value) {
1037
- if (typeof value.$date !== "string") throw new PaginationValidationError(`Invalid pagination cursor date for field: ${field}`);
1038
- const d = new Date(value.$date);
1039
- if (Number.isNaN(d.getTime())) throw new PaginationValidationError(`Invalid pagination cursor date for field: ${field}`);
1040
- return d;
1041
- }
1042
- if ("$oid" in value) {
1043
- if (typeof value.$oid !== "string" || !Types.ObjectId.isValid(value.$oid)) throw new PaginationValidationError(`Invalid pagination cursor ObjectId for field: ${field}`);
1044
- return new Types.ObjectId(value.$oid);
1045
- }
1046
- throw new PaginationValidationError(`Invalid pagination cursor value for field: ${field}`);
1047
- };
1048
- var readFieldValue = (node, field) => {
1049
- if (!node || typeof node !== "object") return void 0;
1050
- if ("get" in node && typeof node.get === "function") return node.get(field);
1051
- return field.split(".").reduce((acc, key) => {
1052
- if (!acc || typeof acc !== "object") return void 0;
1053
- return acc[key];
1054
- }, node);
1055
- };
1056
- //#endregion
1057
- //#region src/pagination/compileMongoPagination.ts
1058
- var compileMongoPagination = (spec, options) => {
1059
- const normalized = normalizePaginationSpec(spec);
1060
- const mongoLimit = normalized.limit + 1;
1061
- const mongoSort = toMongoSort(normalized);
1062
- if (normalized.cursor == null) return {
1063
- spec: normalized,
1064
- mongoFilterDelta: {},
1065
- mongoSort,
1066
- mongoLimit
1067
- };
1068
- if (typeof normalized.cursor !== "string" || normalized.cursor.length === 0) throw new PaginationValidationError("Invalid pagination cursor");
1069
- return {
1070
- spec: normalized,
1071
- mongoFilterDelta: buildKeysetFilterDelta(normalized, decodePaginationCursor(normalized, normalized.cursor, options.cursor)),
1072
- mongoSort,
1073
- mongoLimit
1074
- };
1075
- };
1076
- var toMongoSort = (spec) => {
1077
- const mongoSort = Object.create(null);
1078
- for (const { field, order } of spec.sort) mongoSort[field] = (spec.direction === "prev" ? invertOrder(order) : order) === "asc" ? 1 : -1;
1079
- return mongoSort;
1080
- };
1081
- var buildKeysetFilterDelta = (spec, cursorValues) => {
1082
- const branches = [];
1083
- for (let i = 0; i < spec.sort.length; i++) {
1084
- const current = spec.sort[i];
1085
- if (!current) continue;
1086
- const and = [];
1087
- for (let j = 0; j < i; j++) {
1088
- const prev = spec.sort[j];
1089
- if (!prev) continue;
1090
- const eq = Object.create(null);
1091
- eq[prev.field] = cursorValues[prev.field];
1092
- and.push(eq);
1093
- }
1094
- const op = getKeysetOp(current.order, spec.direction);
1095
- const cmpOp = Object.create(null);
1096
- cmpOp[op] = cursorValues[current.field];
1097
- const cmp = Object.create(null);
1098
- cmp[current.field] = cmpOp;
1099
- and.push(cmp);
1100
- branches.push(and.length === 1 ? and[0] : { $and: and });
1101
- }
1102
- return { $or: branches };
1103
- };
1104
- var getKeysetOp = (order, direction) => {
1105
- if (direction === "next") return order === "asc" ? "$gt" : "$lt";
1106
- return order === "asc" ? "$lt" : "$gt";
1107
- };
1108
- var invertOrder = (order) => {
1109
- return order === "asc" ? "desc" : "asc";
1110
- };
1111
- //#endregion
1112
- //#region src/pagination/materializePagination.ts
1113
- var materializeMongoPagination = (compiled, fetchedNodes, options) => {
1114
- const limit = compiled.spec.limit;
1115
- const hasMore = fetchedNodes.length > limit;
1116
- const trimmed = fetchedNodes.slice(0, limit);
1117
- const nodes = compiled.spec.direction === "prev" ? trimmed.reverse() : trimmed;
1118
- const hasPrevPage = compiled.spec.direction === "next" ? Boolean(compiled.spec.cursor) : hasMore;
1119
- const hasNextPage = compiled.spec.direction === "next" ? hasMore : Boolean(compiled.spec.cursor);
1120
- const pageInfo = {
1121
- hasNextPage,
1122
- hasPrevPage
1123
- };
1124
- if (nodes.length === 0) return {
1125
- nodes,
1126
- pageInfo
1127
- };
1128
- if (hasPrevPage) pageInfo.prevCursor = encodePaginationCursor(compiled.spec, nodes[0], options.cursor);
1129
- if (hasNextPage) pageInfo.nextCursor = encodePaginationCursor(compiled.spec, nodes[nodes.length - 1], options.cursor);
1130
- return {
1131
- nodes,
1132
- pageInfo
1133
- };
1134
- };
1135
- //#endregion
1136
- //#region src/pagination/mongoAdapter.ts
1137
- var MongoAdapter = { applyPagination: (query, compiled) => {
1138
- query.where(compiled.mongoFilterDelta);
1139
- query.sort(compiled.mongoSort);
1140
- query.limit(compiled.mongoLimit);
1141
- return query;
1142
- } };
1143
- //#endregion
1144
- //#region src/pagination/paginateMongoQuery.ts
1145
- var paginateMongoQuery = async (query, pagination, options) => {
1146
- const compiled = compileMongoPagination(pagination, { cursor: options.cursor });
1147
- MongoAdapter.applyPagination(query, compiled);
1148
- const fetchedNodes = await query.exec();
1149
- if (!Array.isArray(fetchedNodes)) throw new Error("paginateMongoQuery expects query.exec() to return an array");
1150
- return materializeMongoPagination(compiled, fetchedNodes, { cursor: options.cursor });
1151
- };
1152
- //#endregion
1153
- //#region src/pagination/mongoPaginationPlugin.ts
1154
- var getQueryOptions$1 = (query) => {
1155
- if (!query || typeof query !== "object") return void 0;
1156
- if (!("getOptions" in query) || typeof query.getOptions !== "function") return void 0;
1157
- return query.getOptions();
1158
- };
1159
- var getPaginationFromOptions = (query) => {
1160
- return getQueryOptions$1(query)?.pagination;
1161
- };
1162
- var getCursorFromOptions = (query) => {
1163
- return getQueryOptions$1(query)?.paginationCursor;
1164
- };
1165
- var mongoPaginationPlugin = (schema, pluginOptions) => {
1166
- schema.query.paginate = async function(pagination, options) {
1167
- const spec = pagination ?? getPaginationFromOptions(this);
1168
- if (!spec) throw new Error("Missing pagination spec");
1169
- const cursor = options?.cursor ?? getCursorFromOptions(this) ?? pluginOptions?.cursor;
1170
- if (!cursor?.signingSecret) throw new Error("Missing pagination cursor signingSecret");
1171
- return await paginateMongoQuery(this, spec, { cursor });
1172
- };
1173
- };
1174
- //#endregion
1175
- //#region src/search/index.ts
1176
- var buildSearchTextStage = (options) => {
1177
- const index = options.index.trim();
1178
- if (!index) throw new Error("Missing search index name");
1179
- const query = options.query.trim();
1180
- if (!query) throw new Error("Missing search query");
1181
- const stage = { $search: {
1182
- index,
1183
- text: {
1184
- query,
1185
- path: options.path
1186
- }
1187
- } };
1188
- if (options.highlightPath) stage.$search.highlight = { path: options.highlightPath };
1189
- return stage;
1190
- };
1191
- var searchMetaProjection = () => {
1192
- return {
1193
- score: { $meta: "searchScore" },
1194
- highlights: { $meta: "searchHighlights" }
1195
- };
1196
- };
1197
- var listResultHasIndex = (listResult, name) => {
1198
- if (!listResult || typeof listResult !== "object") return false;
1199
- if (!("cursor" in listResult)) return false;
1200
- const cursor = listResult.cursor;
1201
- if (!cursor || typeof cursor !== "object") return false;
1202
- const firstBatch = cursor.firstBatch;
1203
- if (!Array.isArray(firstBatch)) return false;
1204
- return firstBatch.some((idx) => idx && typeof idx === "object" && "name" in idx && idx.name === name);
1205
- };
1206
- var isIndexAlreadyExistsError = (error) => {
1207
- if (!error || typeof error !== "object") return false;
1208
- if (("codeName" in error ? error.codeName : void 0) === "IndexAlreadyExists") return true;
1209
- const message = "message" in error ? String(error.message ?? "") : "";
1210
- return /already exists/i.test(message);
1211
- };
1212
- var ensureSearchIndex = async (params) => {
1213
- const collection = params.collection.trim();
1214
- if (!collection) throw new Error("Missing collection name");
1215
- const name = params.name.trim();
1216
- if (!name) throw new Error("Missing search index name");
1217
- let listResult;
1218
- try {
1219
- listResult = await params.db.command({ listSearchIndexes: collection });
1220
- } catch (error) {
1221
- const message = error instanceof Error ? error.message : String(error);
1222
- throw new Error(`listSearchIndexes failed for "${collection}": ${message}`);
1223
- }
1224
- if (listResultHasIndex(listResult, name)) return { created: false };
1225
- try {
1226
- await params.db.command({
1227
- createSearchIndexes: collection,
1228
- indexes: [{
1229
- name,
1230
- definition: params.definition
1231
- }]
1232
- });
1233
- } catch (error) {
1234
- if (isIndexAlreadyExistsError(error)) return { created: false };
1235
- const message = error instanceof Error ? error.message : String(error);
1236
- throw new Error(`createSearchIndexes failed for "${collection}" (index "${name}"): ${message}`);
1237
- }
1238
- return { created: true };
1239
- };
1240
- //#endregion
1241
- //#region src/dbNames.ts
1242
- var getAppName$1 = (env = process.env) => {
1243
- const appName = env.APP_NAME?.trim();
1244
- if (!appName) throw new Error("Missing APP_NAME");
1245
- return appName;
1246
- };
1247
- var GLOBAL_DB_SUFFIX = "-global-db";
1248
- var getGlobalDbName = (env = process.env) => {
1249
- return `${getAppName$1(env)}${GLOBAL_DB_SUFFIX}`;
1250
- };
1251
- var getTenantDbName = (tenantId, env = process.env) => {
1252
- return `${getAppName$1(env)}-${tenantId.trim()}-db`;
1253
- };
1254
- //#endregion
1255
- //#region src/getMongoUrl.ts
1256
- var getMongoUrl = (env = process.env) => {
1257
- const explicitUrl = env.MONGODB_URL ?? env.MONGO_URL ?? env.MONGODB_URI ?? env.DB_URL;
1258
- if (explicitUrl && explicitUrl.trim()) return explicitUrl.trim();
1259
- const port = env.DB_PORT?.trim();
1260
- if (!port) throw new Error("Missing Mongo connection details (MONGODB_URL/MONGO_URL/MONGODB_URI/DB_URL/DB_PORT)");
1261
- return `mongodb://${env.DB_HOST?.trim() || "localhost"}:${port}`;
1262
- };
1263
- //#endregion
1264
- //#region src/ensureMongooseConnection.ts
1265
- var connections = /* @__PURE__ */ new Map();
1266
- var rootConnection = null;
1267
- var CONNECTION_MAX_LISTENERS = 50;
1268
- var waitForOpen = async (connection) => {
1269
- if (connection.readyState === 1) return;
1270
- if (connection.getMaxListeners() < CONNECTION_MAX_LISTENERS) connection.setMaxListeners(CONNECTION_MAX_LISTENERS);
1271
- await new Promise((resolve, reject) => {
1272
- connection.once("open", resolve);
1273
- connection.once("error", reject);
1274
- });
1275
- };
1276
- var ensureMongooseConnection = async (dbName) => {
1277
- const normalizedDbName = dbName.trim();
1278
- if (!normalizedDbName) throw new Error("Missing dbName");
1279
- const existing = connections.get(normalizedDbName);
1280
- if (existing) {
1281
- await waitForOpen(existing);
1282
- return existing;
1283
- }
1284
- if (!rootConnection) {
1285
- const mongoUrl = getMongoUrl();
1286
- rootConnection = mongoose$1.createConnection(mongoUrl, {
1287
- sanitizeFilter: true,
1288
- dbName: normalizedDbName
1289
- });
1290
- }
1291
- await waitForOpen(rootConnection);
1292
- const connection = rootConnection.name === normalizedDbName ? rootConnection : rootConnection.useDb(normalizedDbName, { useCache: true });
1293
- await waitForOpen(connection);
1294
- connections.set(normalizedDbName, connection);
1295
- return connection;
1296
- };
1297
- //#endregion
1298
- //#region src/rtsChangeLogPlugin.ts
1299
- var RTS_COUNTER_ID = "rts";
1300
- var EXCLUDED_MODEL_NAMES = new Set(["RBRtsChange", "RBRtsCounter"]);
1301
- var maxDeleteIdsRaw = process.env.RB_RTS_DELETE_LOG_MAX_IDS ?? "";
1302
- var maxDeleteIds = Number.isFinite(Number(maxDeleteIdsRaw)) ? Math.max(1, Math.floor(Number(maxDeleteIdsRaw))) : 5e3;
1303
- var deleteMetaByQuery = /* @__PURE__ */ new WeakMap();
1304
- var hasToString = (value) => {
1305
- if (typeof value !== "object" || value === null) return false;
1306
- return typeof value.toString === "function";
1307
- };
1308
- var normalizeId = (id) => {
1309
- if (!id) return null;
1310
- if (typeof id === "string") return id;
1311
- if (hasToString(id)) return id.toString();
1312
- return null;
1313
- };
1314
- var getDbName = (db) => {
1315
- if (!db || typeof db !== "object") return "";
1316
- const maybe = db;
1317
- const raw = maybe.name ?? maybe.db?.databaseName;
1318
- return typeof raw === "string" ? raw : "";
1319
- };
1320
- var isGlobalDb = (db) => getDbName(db).endsWith(GLOBAL_DB_SUFFIX);
1321
- var getQuerySession = (query) => {
1322
- const opts = typeof query.getOptions === "function" ? query.getOptions() : void 0;
1323
- if (!opts || typeof opts !== "object") return void 0;
1324
- const session = opts.session;
1325
- if (!session || typeof session !== "object") return void 0;
1326
- return session;
1327
- };
1328
- var getRtsModels = (db) => {
1329
- return {
1330
- RtsCounter: db.models.RBRtsCounter ?? db.model("RBRtsCounter", RBRtsCounterSchema),
1331
- RtsChange: db.models.RBRtsChange ?? db.model("RBRtsChange", RBRtsChangeSchema)
1332
- };
1333
- };
1334
- var allocateSeqRange = async (db, count, session) => {
1335
- const { RtsCounter } = getRtsModels(db);
1336
- const updated = await RtsCounter.findOneAndUpdate({ _id: RTS_COUNTER_ID }, { $inc: { seq: count } }, {
1337
- upsert: true,
1338
- returnDocument: "after",
1339
- setDefaultsOnInsert: true,
1340
- projection: { seq: 1 },
1341
- session
1342
- }).lean();
1343
- const end = Number(updated?.seq ?? 0);
1344
- return {
1345
- start: end - count + 1,
1346
- end
1347
- };
1348
- };
1349
- var insertChanges = async (db, changes, session) => {
1350
- if (!changes.length) return;
1351
- const { RtsChange } = getRtsModels(db);
1352
- const ts = /* @__PURE__ */ new Date();
1353
- const docs = changes.map((c) => ({
1354
- seq: c.seq,
1355
- modelName: c.modelName,
1356
- op: c.op,
1357
- docId: c.docId ?? void 0,
1358
- ts
1359
- }));
1360
- if (session) {
1361
- await RtsChange.insertMany(docs, { session });
1362
- return;
1363
- }
1364
- await RtsChange.insertMany(docs);
1365
- };
1366
- var recordDeleteChanges = async (db, modelName, ids, session) => {
1367
- const uniqueIds = Array.from(new Set(ids)).filter(Boolean);
1368
- if (!uniqueIds.length) return;
1369
- const { start } = await allocateSeqRange(db, uniqueIds.length, session);
1370
- await insertChanges(db, uniqueIds.map((docId, idx) => ({
1371
- seq: start + idx,
1372
- modelName,
1373
- op: "delete",
1374
- docId
1375
- })), session);
1376
- };
1377
- var recordResetModel = async (db, modelName, session) => {
1378
- const { start } = await allocateSeqRange(db, 1, session);
1379
- await insertChanges(db, [{
1380
- seq: start,
1381
- modelName,
1382
- op: "reset_model"
1383
- }], session);
1384
- };
1385
- var captureDeleteMeta = async (query, mode) => {
1386
- const modelName = String(query?.model?.modelName ?? "");
1387
- if (!modelName || modelName.startsWith("RB") || EXCLUDED_MODEL_NAMES.has(modelName)) return;
1388
- if (isGlobalDb(query?.model?.db)) return;
1389
- const filter = typeof query.getFilter === "function" ? query.getFilter() : query.getQuery?.() ?? {};
1390
- const session = getQuerySession(query);
1391
- const findQuery = query.model.find(filter, { _id: 1 });
1392
- if (session && typeof findQuery.session === "function") findQuery.session(session);
1393
- findQuery.lean();
1394
- if (mode === "one") findQuery.limit(1);
1395
- else findQuery.limit(maxDeleteIds + 1);
1396
- const docs = await findQuery;
1397
- const ids = Array.isArray(docs) ? docs.map((d) => normalizeId(d?._id)).filter((id) => Boolean(id)) : [];
1398
- const reset = mode === "many" && ids.length > maxDeleteIds;
1399
- const meta = {
1400
- modelName,
1401
- ids: reset ? [] : ids,
1402
- reset,
1403
- session
1404
- };
1405
- deleteMetaByQuery.set(query, meta);
1406
- };
1407
- var flushDeleteMeta = async (query) => {
1408
- const meta = deleteMetaByQuery.get(query);
1409
- deleteMetaByQuery.delete(query);
1410
- if (!meta) return;
1411
- const db = query?.model?.db;
1412
- if (!db) return;
1413
- try {
1414
- if (meta.reset) await recordResetModel(db, meta.modelName, meta.session);
1415
- else await recordDeleteChanges(db, meta.modelName, meta.ids, meta.session);
1416
- } catch {
1417
- return;
1418
- }
1419
- };
1420
- var rtsChangeLogPlugin = (schema) => {
1421
- schema.pre("deleteOne", {
1422
- query: true,
1423
- document: false
1424
- }, async function() {
1425
- await captureDeleteMeta(this, "one");
1426
- });
1427
- schema.pre("deleteMany", {
1428
- query: true,
1429
- document: false
1430
- }, async function() {
1431
- await captureDeleteMeta(this, "many");
1432
- });
1433
- schema.post("deleteOne", {
1434
- query: true,
1435
- document: false
1436
- }, async function() {
1437
- await flushDeleteMeta(this);
1438
- });
1439
- schema.post("deleteMany", {
1440
- query: true,
1441
- document: false
1442
- }, async function() {
1443
- await flushDeleteMeta(this);
1444
- });
1445
- schema.post("findOneAndDelete", {
1446
- query: true,
1447
- document: false
1448
- }, async function(doc) {
1449
- const modelName = String(this?.model?.modelName ?? "");
1450
- if (!modelName || modelName.startsWith("RB") || EXCLUDED_MODEL_NAMES.has(modelName)) return;
1451
- const db = this?.model?.db;
1452
- if (!db) return;
1453
- if (isGlobalDb(db)) return;
1454
- const docId = normalizeId(doc?._id);
1455
- if (!docId) return;
1456
- try {
1457
- const session = getQuerySession(this);
1458
- await recordDeleteChanges(db, modelName, [docId], session);
1459
- } catch {
1460
- return;
1461
- }
1462
- });
1463
- };
1464
- //#endregion
1465
- //#region src/acl/mongooseAclPlugin.ts
1466
- var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
1467
- var mergeMongoQuery = (left, right) => {
1468
- const leftQuery = isRecord(left) ? left : {};
1469
- if (Object.keys(leftQuery).length === 0) return right;
1470
- if (Object.keys(right).length === 0) return leftQuery;
1471
- return { $and: [leftQuery, right] };
1472
- };
1473
- var getQueryOptions = (query) => {
1474
- if (!query || typeof query !== "object") return void 0;
1475
- if (!("getOptions" in query) || typeof query.getOptions !== "function") return void 0;
1476
- return query.getOptions();
1477
- };
1478
- var getStoredAclFromQuery = (query) => {
1479
- const raw = getQueryOptions(query)?.rbAcl;
1480
- if (!isRecord(raw)) return null;
1481
- if (!("ability" in raw)) return null;
1482
- return raw;
1483
- };
1484
- var getStoredAclFromAggregate = (aggregate) => {
1485
- if (!aggregate || typeof aggregate !== "object") return null;
1486
- const raw = aggregate.options;
1487
- if (!isRecord(raw)) return null;
1488
- const acl = raw.rbAcl;
1489
- if (!isRecord(acl)) return null;
1490
- if (!("ability" in acl)) return null;
1491
- return acl;
1492
- };
1493
- var addQueryAclFilter = (query, action) => {
1494
- const storedAcl = getStoredAclFromQuery(query);
1495
- if (!storedAcl) return;
1496
- const ability = storedAcl.ability;
1497
- const resolvedAction = storedAcl.action ?? action;
1498
- const modelName = query.model.modelName;
1499
- const accessQuery = accessibleBy(ability, resolvedAction).ofType(modelName);
1500
- query.and([accessQuery]);
1501
- };
1502
- var injectAggregateMatch = (pipeline, match) => {
1503
- if (pipeline.length === 0) {
1504
- pipeline.unshift({ $match: match });
1505
- return;
1506
- }
1507
- const first = pipeline[0];
1508
- if (!isRecord(first)) {
1509
- pipeline.unshift({ $match: match });
1510
- return;
1511
- }
1512
- if ("$geoNear" in first) {
1513
- const geoNear = first.$geoNear;
1514
- if (isRecord(geoNear)) {
1515
- geoNear.query = mergeMongoQuery(geoNear.query, match);
1516
- return;
1517
- }
1518
- }
1519
- if ("$search" in first || "$vectorSearch" in first || "$searchMeta" in first) {
1520
- pipeline.splice(1, 0, { $match: match });
1521
- return;
1522
- }
1523
- pipeline.unshift({ $match: match });
1524
- };
1525
- var addAggregateAclFilter = (aggregate, action) => {
1526
- const storedAcl = getStoredAclFromAggregate(aggregate);
1527
- if (!storedAcl) return;
1528
- const ability = storedAcl.ability;
1529
- const resolvedAction = storedAcl.action ?? action;
1530
- const modelName = aggregate.model().modelName;
1531
- const accessQuery = accessibleBy(ability, resolvedAction).ofType(modelName);
1532
- injectAggregateMatch(aggregate.pipeline(), accessQuery);
1533
- };
1534
- var patchAggregateAcl = () => {
1535
- const globalKey = Symbol.for("@rpcbase/db/acl/mongooseAggregateAclPatched");
1536
- const globalState = globalThis;
1537
- if (globalState[globalKey]) return;
1538
- globalState[globalKey] = true;
1539
- const AggregatePrototype = mongoose$1.Aggregate.prototype;
1540
- if (typeof AggregatePrototype.acl === "function") return;
1541
- AggregatePrototype.acl = function(ability, action = "read") {
1542
- this.option({ rbAcl: {
1543
- ability,
1544
- action
1545
- } });
1546
- return this;
1547
- };
1548
- };
1549
- var createModelAclProxy = (model, ability) => {
1550
- return new Proxy(model, { get(target, prop, receiver) {
1551
- if (prop === "acl") return () => {
1552
- throw new Error(`Model "${target.modelName}" is already ACL-scoped. Do not call .acl(...) again.`);
1553
- };
1554
- const value = Reflect.get(target, prop, receiver);
1555
- if (typeof value !== "function") return value;
1556
- return (...args) => {
1557
- const result = Reflect.apply(value, target, args);
1558
- if (result && typeof result === "object" && "acl" in result && typeof result.acl === "function") return result.acl(ability);
1559
- return result;
1560
- };
1561
- } });
1562
- };
1563
- var mongooseAclPlugin = (schema) => {
1564
- patchAggregateAcl();
1565
- schema.query.acl = function(ability, action) {
1566
- this.setOptions({ rbAcl: {
1567
- ability,
1568
- action
1569
- } });
1570
- return this;
1571
- };
1572
- schema.statics.acl = function(ability) {
1573
- return createModelAclProxy(this, ability);
1574
- };
1575
- schema.pre("aggregate", function() {
1576
- addAggregateAclFilter(this, "read");
1577
- });
1578
- schema.pre("countDocuments", function() {
1579
- addQueryAclFilter(this, "read");
1580
- });
1581
- schema.pre("deleteMany", function() {
1582
- addQueryAclFilter(this, "delete");
1583
- });
1584
- schema.pre("deleteOne", function() {
1585
- addQueryAclFilter(this, "delete");
1586
- });
1587
- schema.pre("distinct", function() {
1588
- addQueryAclFilter(this, "read");
1589
- });
1590
- schema.pre("find", function() {
1591
- addQueryAclFilter(this, "read");
1592
- });
1593
- schema.pre("findOne", function() {
1594
- addQueryAclFilter(this, "read");
1595
- });
1596
- schema.pre("findOneAndDelete", function() {
1597
- addQueryAclFilter(this, "delete");
1598
- });
1599
- schema.pre("findOneAndReplace", function() {
1600
- addQueryAclFilter(this, "update");
1601
- });
1602
- schema.pre("findOneAndUpdate", function() {
1603
- addQueryAclFilter(this, "update");
1604
- });
1605
- schema.pre("replaceOne", function() {
1606
- addQueryAclFilter(this, "update");
1607
- });
1608
- schema.pre("updateMany", function() {
1609
- addQueryAclFilter(this, "update");
1610
- });
1611
- schema.pre("updateOne", function() {
1612
- addQueryAclFilter(this, "update");
1613
- });
1614
- };
1615
- //#endregion
1616
- //#region src/registerModels.ts
1617
- var cachedModels = null;
1618
- var DEFAULT_GLOBAL_RB_MODEL_NAMES_SET = new Set([
1619
- "RBUser",
1620
- "RBTenant",
1621
- "RBOAuthRequest"
1622
- ]);
1623
- var assertSchema = (exportName, value) => {
1624
- if (value instanceof mongoose$1.Schema) return value;
1625
- throw new Error([
1626
- `Expected ${exportName} to be an instance of mongoose.Schema, but it was not.`,
1627
- "rpcbase supports mongoose 9+ only.",
1628
- "Fix: ensure the project is using mongoose 9.x and that all packages resolve the same mongoose instance (try `npm ls mongoose`)."
1629
- ].join(" "));
1630
- };
1631
- var getFrameworkSchemaForModelName = (modelName) => {
1632
- const value = models_exports[`${modelName}Schema`];
1633
- if (!(value instanceof mongoose$1.Schema)) return null;
1634
- return value;
1635
- };
1636
- var applyTenantPlugins = (schema) => {
1637
- schema.plugin(accessibleRecordsPlugin);
1638
- schema.plugin(mongooseAclPlugin);
1639
- schema.plugin(mongoPaginationPlugin);
1640
- schema.plugin(rtsChangeLogPlugin);
1641
- };
1642
- var registerSchema = (target, other, modelName, schema, scope) => {
1643
- if (target[modelName] || other[modelName]) throw new Error(`Duplicate model name "${modelName}" across tenant/global scopes`);
1644
- target[modelName] = schema;
1645
- };
1646
- var buildSchemasFromModules = (modules) => Object.entries(modules).filter(([key]) => key.endsWith("Schema")).map(([key, schemaValue]) => {
1647
- const schema = assertSchema(key, schemaValue);
1648
- return {
1649
- modelName: key.replace(/Schema$/, ""),
1650
- schema
1651
- };
936
+ const {
937
+ Schema,
938
+ model
939
+ } = mongoose;
940
+ class PaginationValidationError extends Error {
941
+ code = "invalid_pagination";
942
+ statusCode = 400;
943
+ constructor(message, options) {
944
+ super(message, options);
945
+ this.name = "PaginationValidationError";
946
+ }
947
+ }
948
+ const isPaginationValidationError = (error) => {
949
+ if (!error || typeof error !== "object") return false;
950
+ const anyError = error;
951
+ return anyError.name === "PaginationValidationError" && anyError.code === "invalid_pagination" && anyError.statusCode === 400;
952
+ };
953
+ const DISALLOWED_MONGO_FIELD_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
954
+ const PAGINATION_LIMIT_MAX = 128;
955
+ const normalizePaginationSpec = (spec) => {
956
+ if (!spec || typeof spec !== "object") throw new PaginationValidationError("Invalid PaginationSpec");
957
+ const limit = normalizeLimit(spec.limit);
958
+ const direction = spec.direction ?? "next";
959
+ if (direction !== "next" && direction !== "prev") throw new PaginationValidationError("Invalid pagination direction");
960
+ if (!Array.isArray(spec.sort) || spec.sort.length === 0) throw new PaginationValidationError("Invalid pagination sort");
961
+ const sort = spec.sort.map(({
962
+ field,
963
+ order
964
+ }) => ({
965
+ field: assertSafeMongoFieldPath(field),
966
+ order: normalizeOrder(order)
967
+ }));
968
+ const seenFields = /* @__PURE__ */ new Set();
969
+ for (const {
970
+ field
971
+ } of sort) {
972
+ if (seenFields.has(field)) throw new PaginationValidationError(`Duplicate pagination sort field: ${field}`);
973
+ seenFields.add(field);
974
+ }
975
+ const primaryOrder = sort[0]?.order;
976
+ if (!seenFields.has("_id")) {
977
+ sort.push({
978
+ field: "_id",
979
+ order: primaryOrder
980
+ });
981
+ }
982
+ return {
983
+ ...spec,
984
+ limit,
985
+ direction,
986
+ sort
987
+ };
988
+ };
989
+ const normalizeLimit = (limit) => {
990
+ if (typeof limit !== "number" || !Number.isFinite(limit) || !Number.isInteger(limit) || limit <= 0) {
991
+ throw new PaginationValidationError("Invalid pagination limit");
992
+ }
993
+ if (limit > PAGINATION_LIMIT_MAX) {
994
+ throw new PaginationValidationError("Invalid pagination limit");
995
+ }
996
+ return limit;
997
+ };
998
+ const normalizeOrder = (order) => {
999
+ if (order !== "asc" && order !== "desc") throw new PaginationValidationError("Invalid pagination order");
1000
+ return order;
1001
+ };
1002
+ const assertSafeMongoFieldPath = (field) => {
1003
+ if (typeof field !== "string" || field.length === 0) throw new PaginationValidationError("Invalid pagination sort field");
1004
+ if (field.startsWith("$")) throw new PaginationValidationError("Invalid pagination sort field");
1005
+ const parts = field.split(".");
1006
+ for (const part of parts) {
1007
+ if (part.length === 0) throw new PaginationValidationError("Invalid pagination sort field");
1008
+ if (DISALLOWED_MONGO_FIELD_SEGMENTS.has(part)) throw new PaginationValidationError("Invalid pagination sort field");
1009
+ if (!/^[a-zA-Z0-9_]+$/.test(part)) throw new PaginationValidationError("Invalid pagination sort field");
1010
+ }
1011
+ return field;
1012
+ };
1013
+ const encodePaginationCursor = (spec, node, options) => {
1014
+ const normalized = normalizePaginationSpec(spec);
1015
+ const values = /* @__PURE__ */ Object.create(null);
1016
+ for (const {
1017
+ field
1018
+ } of normalized.sort) {
1019
+ const value = readFieldValue(node, field);
1020
+ if (typeof value === "undefined") {
1021
+ throw new Error(`Pagination cursor encode failed (missing field: ${field})`);
1022
+ }
1023
+ values[field] = encodeCursorValue(field, value);
1024
+ }
1025
+ const payload = {
1026
+ v: 1,
1027
+ values
1028
+ };
1029
+ const payloadB64 = Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
1030
+ const sigB64 = signCursorPayloadB64(payloadB64, options.signingSecret);
1031
+ return `${payloadB64}.${sigB64}`;
1032
+ };
1033
+ const decodePaginationCursor = (spec, cursor, options) => {
1034
+ const normalized = normalizePaginationSpec(spec);
1035
+ const [payloadB64, sigB64, ...rest] = cursor.split(".");
1036
+ if (rest.length > 0) throw new PaginationValidationError("Invalid pagination cursor format");
1037
+ if (!sigB64) throw new PaginationValidationError("Invalid pagination cursor format");
1038
+ verifyCursorSignature(payloadB64, sigB64, options.signingSecret);
1039
+ const payloadRaw = Buffer.from(payloadB64, "base64url").toString("utf8");
1040
+ let payloadUnknown;
1041
+ try {
1042
+ payloadUnknown = JSON.parse(payloadRaw);
1043
+ } catch {
1044
+ throw new PaginationValidationError("Invalid pagination cursor payload");
1045
+ }
1046
+ if (!payloadUnknown || typeof payloadUnknown !== "object") throw new PaginationValidationError("Invalid pagination cursor payload");
1047
+ const payload = payloadUnknown;
1048
+ if (payload.v !== 1) throw new PaginationValidationError("Unsupported pagination cursor version");
1049
+ if (!payload.values || typeof payload.values !== "object") throw new PaginationValidationError("Invalid pagination cursor payload");
1050
+ const decoded = /* @__PURE__ */ Object.create(null);
1051
+ for (const {
1052
+ field
1053
+ } of normalized.sort) {
1054
+ if (!Object.prototype.hasOwnProperty.call(payload.values, field)) {
1055
+ throw new PaginationValidationError(`Pagination cursor missing field: ${field}`);
1056
+ }
1057
+ const encodedValue = payload.values[field];
1058
+ decoded[field] = decodeCursorValue(field, encodedValue);
1059
+ }
1060
+ return decoded;
1061
+ };
1062
+ const signCursorPayloadB64 = (payloadB64, secret) => {
1063
+ return createHmac("sha256", secret).update(payloadB64, "utf8").digest("base64url");
1064
+ };
1065
+ const verifyCursorSignature = (payloadB64, sigB64, secret) => {
1066
+ const expectedSigB64 = signCursorPayloadB64(payloadB64, secret);
1067
+ const a3 = Buffer.from(sigB64, "utf8");
1068
+ const b3 = Buffer.from(expectedSigB64, "utf8");
1069
+ if (a3.length !== b3.length || !timingSafeEqual(a3, b3)) {
1070
+ throw new PaginationValidationError("Invalid pagination cursor signature");
1071
+ }
1072
+ };
1073
+ const unwrapCursorComparableValue = (value) => {
1074
+ if (value === null || typeof value !== "object") return value;
1075
+ if (value instanceof Date || value instanceof Types.ObjectId) return value;
1076
+ let current = value;
1077
+ for (let depth = 0; depth < 4; depth += 1) {
1078
+ if (!current || typeof current !== "object") return current;
1079
+ if (current instanceof Date || current instanceof Types.ObjectId) return current;
1080
+ if (!("_id" in current)) return current;
1081
+ current = current._id;
1082
+ }
1083
+ return current;
1084
+ };
1085
+ const encodeCursorValue = (field, value) => {
1086
+ const comparableValue = unwrapCursorComparableValue(value);
1087
+ if (comparableValue === null) return null;
1088
+ if (field === "_id") {
1089
+ if (comparableValue instanceof Types.ObjectId) return {
1090
+ $oid: comparableValue.toHexString()
1091
+ };
1092
+ if (typeof comparableValue === "string") return comparableValue;
1093
+ throw new Error("Pagination cursor encode failed (_id must be an ObjectId or string)");
1094
+ }
1095
+ if (comparableValue instanceof Date) return {
1096
+ $date: comparableValue.toISOString()
1097
+ };
1098
+ if (typeof comparableValue === "string" || typeof comparableValue === "number" || typeof comparableValue === "boolean") {
1099
+ return comparableValue;
1100
+ }
1101
+ if (comparableValue instanceof Types.ObjectId) return {
1102
+ $oid: comparableValue.toHexString()
1103
+ };
1104
+ throw new Error(`Unsupported pagination cursor value type for field: ${field}`);
1105
+ };
1106
+ const decodeCursorValue = (field, value) => {
1107
+ if (value === null) return null;
1108
+ if (typeof value === "string") {
1109
+ return value;
1110
+ }
1111
+ if (typeof value === "number" || typeof value === "boolean") return value;
1112
+ if (!value || typeof value !== "object") throw new PaginationValidationError(`Invalid pagination cursor value for field: ${field}`);
1113
+ if ("$date" in value) {
1114
+ if (typeof value.$date !== "string") throw new PaginationValidationError(`Invalid pagination cursor date for field: ${field}`);
1115
+ const d3 = new Date(value.$date);
1116
+ if (Number.isNaN(d3.getTime())) throw new PaginationValidationError(`Invalid pagination cursor date for field: ${field}`);
1117
+ return d3;
1118
+ }
1119
+ if ("$oid" in value) {
1120
+ if (typeof value.$oid !== "string" || !Types.ObjectId.isValid(value.$oid)) {
1121
+ throw new PaginationValidationError(`Invalid pagination cursor ObjectId for field: ${field}`);
1122
+ }
1123
+ return new Types.ObjectId(value.$oid);
1124
+ }
1125
+ throw new PaginationValidationError(`Invalid pagination cursor value for field: ${field}`);
1126
+ };
1127
+ const readFieldValue = (node, field) => {
1128
+ if (!node || typeof node !== "object") return void 0;
1129
+ if ("get" in node && typeof node.get === "function") {
1130
+ return node.get(field);
1131
+ }
1132
+ return field.split(".").reduce((acc, key) => {
1133
+ if (!acc || typeof acc !== "object") return void 0;
1134
+ return acc[key];
1135
+ }, node);
1136
+ };
1137
+ const compileMongoPagination = (spec, options) => {
1138
+ const normalized = normalizePaginationSpec(spec);
1139
+ const mongoLimit = normalized.limit + 1;
1140
+ const mongoSort = toMongoSort(normalized);
1141
+ if (normalized.cursor == null) {
1142
+ return {
1143
+ spec: normalized,
1144
+ mongoFilterDelta: {},
1145
+ mongoSort,
1146
+ mongoLimit
1147
+ };
1148
+ }
1149
+ if (typeof normalized.cursor !== "string" || normalized.cursor.length === 0) {
1150
+ throw new PaginationValidationError("Invalid pagination cursor");
1151
+ }
1152
+ const cursorValues = decodePaginationCursor(normalized, normalized.cursor, options.cursor);
1153
+ const mongoFilterDelta = buildKeysetFilterDelta(normalized, cursorValues);
1154
+ return {
1155
+ spec: normalized,
1156
+ mongoFilterDelta,
1157
+ mongoSort,
1158
+ mongoLimit
1159
+ };
1160
+ };
1161
+ const toMongoSort = (spec) => {
1162
+ const mongoSort = /* @__PURE__ */ Object.create(null);
1163
+ for (const {
1164
+ field,
1165
+ order
1166
+ } of spec.sort) {
1167
+ const forQueryOrder = spec.direction === "prev" ? invertOrder(order) : order;
1168
+ mongoSort[field] = forQueryOrder === "asc" ? 1 : -1;
1169
+ }
1170
+ return mongoSort;
1171
+ };
1172
+ const buildKeysetFilterDelta = (spec, cursorValues) => {
1173
+ const branches = [];
1174
+ for (let i = 0; i < spec.sort.length; i++) {
1175
+ const current = spec.sort[i];
1176
+ if (!current) continue;
1177
+ const and = [];
1178
+ for (let j = 0; j < i; j++) {
1179
+ const prev = spec.sort[j];
1180
+ if (!prev) continue;
1181
+ const eq = /* @__PURE__ */ Object.create(null);
1182
+ eq[prev.field] = cursorValues[prev.field];
1183
+ and.push(eq);
1184
+ }
1185
+ const op = getKeysetOp(current.order, spec.direction);
1186
+ const cmpOp = /* @__PURE__ */ Object.create(null);
1187
+ cmpOp[op] = cursorValues[current.field];
1188
+ const cmp = /* @__PURE__ */ Object.create(null);
1189
+ cmp[current.field] = cmpOp;
1190
+ and.push(cmp);
1191
+ branches.push(and.length === 1 ? and[0] : {
1192
+ $and: and
1193
+ });
1194
+ }
1195
+ return {
1196
+ $or: branches
1197
+ };
1198
+ };
1199
+ const getKeysetOp = (order, direction) => {
1200
+ if (direction === "next") return order === "asc" ? "$gt" : "$lt";
1201
+ return order === "asc" ? "$lt" : "$gt";
1202
+ };
1203
+ const invertOrder = (order) => {
1204
+ return order === "asc" ? "desc" : "asc";
1205
+ };
1206
+ const materializeMongoPagination = (compiled, fetchedNodes, options) => {
1207
+ const limit = compiled.spec.limit;
1208
+ const hasMore = fetchedNodes.length > limit;
1209
+ const trimmed = fetchedNodes.slice(0, limit);
1210
+ const nodes = compiled.spec.direction === "prev" ? trimmed.reverse() : trimmed;
1211
+ const hasPrevPage = compiled.spec.direction === "next" ? Boolean(compiled.spec.cursor) : hasMore;
1212
+ const hasNextPage = compiled.spec.direction === "next" ? hasMore : Boolean(compiled.spec.cursor);
1213
+ const pageInfo = {
1214
+ hasNextPage,
1215
+ hasPrevPage
1216
+ };
1217
+ if (nodes.length === 0) {
1218
+ return {
1219
+ nodes,
1220
+ pageInfo
1221
+ };
1222
+ }
1223
+ if (hasPrevPage) {
1224
+ pageInfo.prevCursor = encodePaginationCursor(compiled.spec, nodes[0], options.cursor);
1225
+ }
1226
+ if (hasNextPage) {
1227
+ pageInfo.nextCursor = encodePaginationCursor(compiled.spec, nodes[nodes.length - 1], options.cursor);
1228
+ }
1229
+ return {
1230
+ nodes,
1231
+ pageInfo
1232
+ };
1233
+ };
1234
+ const MongoAdapter = {
1235
+ applyPagination: (query, compiled) => {
1236
+ query.where(compiled.mongoFilterDelta);
1237
+ query.sort(compiled.mongoSort);
1238
+ query.limit(compiled.mongoLimit);
1239
+ return query;
1240
+ }
1241
+ };
1242
+ const paginateMongoQuery = async (query, pagination, options) => {
1243
+ const compiled = compileMongoPagination(pagination, {
1244
+ cursor: options.cursor
1245
+ });
1246
+ MongoAdapter.applyPagination(query, compiled);
1247
+ const fetchedNodes = await query.exec();
1248
+ if (!Array.isArray(fetchedNodes)) {
1249
+ throw new Error("paginateMongoQuery expects query.exec() to return an array");
1250
+ }
1251
+ return materializeMongoPagination(compiled, fetchedNodes, {
1252
+ cursor: options.cursor
1253
+ });
1254
+ };
1255
+ const getQueryOptions$1 = (query) => {
1256
+ if (!query || typeof query !== "object") return void 0;
1257
+ if (!("getOptions" in query) || typeof query.getOptions !== "function") return void 0;
1258
+ return query.getOptions();
1259
+ };
1260
+ const getPaginationFromOptions = (query) => {
1261
+ const options = getQueryOptions$1(query);
1262
+ return options?.pagination;
1263
+ };
1264
+ const getCursorFromOptions = (query) => {
1265
+ const options = getQueryOptions$1(query);
1266
+ return options?.paginationCursor;
1267
+ };
1268
+ const mongoPaginationPlugin = (schema, pluginOptions) => {
1269
+ schema.query.paginate = async function(pagination, options) {
1270
+ const spec = pagination ?? getPaginationFromOptions(this);
1271
+ if (!spec) throw new Error("Missing pagination spec");
1272
+ const cursor = options?.cursor ?? getCursorFromOptions(this) ?? pluginOptions?.cursor;
1273
+ if (!cursor?.signingSecret) throw new Error("Missing pagination cursor signingSecret");
1274
+ return await paginateMongoQuery(this, spec, {
1275
+ cursor
1276
+ });
1277
+ };
1278
+ };
1279
+ const buildSearchTextStage = (options) => {
1280
+ const index = options.index.trim();
1281
+ if (!index) throw new Error("Missing search index name");
1282
+ const query = options.query.trim();
1283
+ if (!query) throw new Error("Missing search query");
1284
+ const stage = {
1285
+ $search: {
1286
+ index,
1287
+ text: {
1288
+ query,
1289
+ path: options.path
1290
+ }
1291
+ }
1292
+ };
1293
+ if (options.highlightPath) {
1294
+ stage.$search.highlight = {
1295
+ path: options.highlightPath
1296
+ };
1297
+ }
1298
+ return stage;
1299
+ };
1300
+ const searchMetaProjection = () => {
1301
+ return {
1302
+ score: {
1303
+ $meta: "searchScore"
1304
+ },
1305
+ highlights: {
1306
+ $meta: "searchHighlights"
1307
+ }
1308
+ };
1309
+ };
1310
+ const listResultHasIndex = (listResult, name) => {
1311
+ if (!listResult || typeof listResult !== "object") return false;
1312
+ if (!("cursor" in listResult)) return false;
1313
+ const cursor = listResult.cursor;
1314
+ if (!cursor || typeof cursor !== "object") return false;
1315
+ const firstBatch = cursor.firstBatch;
1316
+ if (!Array.isArray(firstBatch)) return false;
1317
+ return firstBatch.some((idx) => idx && typeof idx === "object" && "name" in idx && idx.name === name);
1318
+ };
1319
+ const isIndexAlreadyExistsError = (error) => {
1320
+ if (!error || typeof error !== "object") return false;
1321
+ const codeName = "codeName" in error ? error.codeName : void 0;
1322
+ if (codeName === "IndexAlreadyExists") return true;
1323
+ const message = "message" in error ? String(error.message ?? "") : "";
1324
+ return /already exists/i.test(message);
1325
+ };
1326
+ const ensureSearchIndex = async (params) => {
1327
+ const collection = params.collection.trim();
1328
+ if (!collection) throw new Error("Missing collection name");
1329
+ const name = params.name.trim();
1330
+ if (!name) throw new Error("Missing search index name");
1331
+ let listResult;
1332
+ try {
1333
+ listResult = await params.db.command({
1334
+ listSearchIndexes: collection
1335
+ });
1336
+ } catch (error) {
1337
+ const message = error instanceof Error ? error.message : String(error);
1338
+ throw new Error(`listSearchIndexes failed for "${collection}": ${message}`);
1339
+ }
1340
+ if (listResultHasIndex(listResult, name)) {
1341
+ return {
1342
+ created: false
1343
+ };
1344
+ }
1345
+ try {
1346
+ await params.db.command({
1347
+ createSearchIndexes: collection,
1348
+ indexes: [{
1349
+ name,
1350
+ definition: params.definition
1351
+ }]
1352
+ });
1353
+ } catch (error) {
1354
+ if (isIndexAlreadyExistsError(error)) {
1355
+ return {
1356
+ created: false
1357
+ };
1358
+ }
1359
+ const message = error instanceof Error ? error.message : String(error);
1360
+ throw new Error(`createSearchIndexes failed for "${collection}" (index "${name}"): ${message}`);
1361
+ }
1362
+ return {
1363
+ created: true
1364
+ };
1365
+ };
1366
+ const getAppName$1 = (env = process.env) => {
1367
+ const appName = env.APP_NAME?.trim();
1368
+ if (!appName) {
1369
+ throw new Error("Missing APP_NAME");
1370
+ }
1371
+ return appName;
1372
+ };
1373
+ const GLOBAL_DB_SUFFIX = "-global-db";
1374
+ const getGlobalDbName = (env = process.env) => {
1375
+ return `${getAppName$1(env)}${GLOBAL_DB_SUFFIX}`;
1376
+ };
1377
+ const getTenantDbName = (tenantId, env = process.env) => {
1378
+ return `${getAppName$1(env)}-${tenantId.trim()}-db`;
1379
+ };
1380
+ const getMongoUrl = (env = process.env) => {
1381
+ const explicitUrl = env.MONGODB_URL ?? env.MONGO_URL ?? env.MONGODB_URI ?? env.DB_URL;
1382
+ if (explicitUrl && explicitUrl.trim()) {
1383
+ return explicitUrl.trim();
1384
+ }
1385
+ const port = env.DB_PORT?.trim();
1386
+ if (!port) throw new Error("Missing Mongo connection details (MONGODB_URL/MONGO_URL/MONGODB_URI/DB_URL/DB_PORT)");
1387
+ const host = env.DB_HOST?.trim() || "localhost";
1388
+ return `mongodb://${host}:${port}`;
1389
+ };
1390
+ const connections = /* @__PURE__ */ new Map();
1391
+ let rootConnection = null;
1392
+ const CONNECTION_MAX_LISTENERS = 50;
1393
+ const waitForOpen = async (connection) => {
1394
+ if (connection.readyState === 1) return;
1395
+ if (connection.getMaxListeners() < CONNECTION_MAX_LISTENERS) {
1396
+ connection.setMaxListeners(CONNECTION_MAX_LISTENERS);
1397
+ }
1398
+ await new Promise((resolve, reject) => {
1399
+ connection.once("open", resolve);
1400
+ connection.once("error", reject);
1401
+ });
1402
+ };
1403
+ const ensureMongooseConnection = async (dbName) => {
1404
+ const normalizedDbName = dbName.trim();
1405
+ if (!normalizedDbName) {
1406
+ throw new Error("Missing dbName");
1407
+ }
1408
+ const existing = connections.get(normalizedDbName);
1409
+ if (existing) {
1410
+ await waitForOpen(existing);
1411
+ return existing;
1412
+ }
1413
+ if (!rootConnection) {
1414
+ const mongoUrl = getMongoUrl();
1415
+ rootConnection = mongoose.createConnection(mongoUrl, {
1416
+ sanitizeFilter: true,
1417
+ dbName: normalizedDbName
1418
+ });
1419
+ }
1420
+ await waitForOpen(rootConnection);
1421
+ const connection = rootConnection.name === normalizedDbName ? rootConnection : rootConnection.useDb(normalizedDbName, {
1422
+ useCache: true
1423
+ });
1424
+ await waitForOpen(connection);
1425
+ connections.set(normalizedDbName, connection);
1426
+ return connection;
1427
+ };
1428
+ const RTS_COUNTER_ID = "rts";
1429
+ const EXCLUDED_MODEL_NAMES = /* @__PURE__ */ new Set(["RBRtsChange", "RBRtsCounter"]);
1430
+ const maxDeleteIdsRaw = process.env.RB_RTS_DELETE_LOG_MAX_IDS ?? "";
1431
+ const maxDeleteIds = Number.isFinite(Number(maxDeleteIdsRaw)) ? Math.max(1, Math.floor(Number(maxDeleteIdsRaw))) : 5e3;
1432
+ const deleteMetaByQuery = /* @__PURE__ */ new WeakMap();
1433
+ const hasToString = (value) => {
1434
+ if (typeof value !== "object" || value === null) return false;
1435
+ const maybe = value;
1436
+ return typeof maybe.toString === "function";
1437
+ };
1438
+ const normalizeId = (id) => {
1439
+ if (!id) return null;
1440
+ if (typeof id === "string") return id;
1441
+ if (hasToString(id)) return id.toString();
1442
+ return null;
1443
+ };
1444
+ const getDbName = (db) => {
1445
+ if (!db || typeof db !== "object") return "";
1446
+ const maybe = db;
1447
+ const raw = maybe.name ?? maybe.db?.databaseName;
1448
+ return typeof raw === "string" ? raw : "";
1449
+ };
1450
+ const isGlobalDb = (db) => getDbName(db).endsWith(GLOBAL_DB_SUFFIX);
1451
+ const getQuerySession = (query) => {
1452
+ const opts = typeof query.getOptions === "function" ? query.getOptions() : void 0;
1453
+ if (!opts || typeof opts !== "object") return void 0;
1454
+ const session = opts.session;
1455
+ if (!session || typeof session !== "object") return void 0;
1456
+ return session;
1457
+ };
1458
+ const getRtsModels = (db) => {
1459
+ const RtsCounter = db.models.RBRtsCounter ?? db.model("RBRtsCounter", RBRtsCounterSchema);
1460
+ const RtsChange = db.models.RBRtsChange ?? db.model("RBRtsChange", RBRtsChangeSchema);
1461
+ return {
1462
+ RtsCounter,
1463
+ RtsChange
1464
+ };
1465
+ };
1466
+ const allocateSeqRange = async (db, count, session) => {
1467
+ const {
1468
+ RtsCounter
1469
+ } = getRtsModels(db);
1470
+ const updated = await RtsCounter.findOneAndUpdate({
1471
+ _id: RTS_COUNTER_ID
1472
+ }, {
1473
+ $inc: {
1474
+ seq: count
1475
+ }
1476
+ }, {
1477
+ upsert: true,
1478
+ returnDocument: "after",
1479
+ setDefaultsOnInsert: true,
1480
+ projection: {
1481
+ seq: 1
1482
+ },
1483
+ session
1484
+ }).lean();
1485
+ const end = Number(updated?.seq ?? 0);
1486
+ const start = end - count + 1;
1487
+ return {
1488
+ start,
1489
+ end
1490
+ };
1491
+ };
1492
+ const insertChanges = async (db, changes, session) => {
1493
+ if (!changes.length) return;
1494
+ const {
1495
+ RtsChange
1496
+ } = getRtsModels(db);
1497
+ const ts = /* @__PURE__ */ new Date();
1498
+ const docs = changes.map((c3) => ({
1499
+ seq: c3.seq,
1500
+ modelName: c3.modelName,
1501
+ op: c3.op,
1502
+ docId: c3.docId ?? void 0,
1503
+ ts
1504
+ }));
1505
+ if (session) {
1506
+ await RtsChange.insertMany(docs, {
1507
+ session
1508
+ });
1509
+ return;
1510
+ }
1511
+ await RtsChange.insertMany(docs);
1512
+ };
1513
+ const recordDeleteChanges = async (db, modelName, ids, session) => {
1514
+ const uniqueIds = Array.from(new Set(ids)).filter(Boolean);
1515
+ if (!uniqueIds.length) return;
1516
+ const {
1517
+ start
1518
+ } = await allocateSeqRange(db, uniqueIds.length, session);
1519
+ await insertChanges(db, uniqueIds.map((docId, idx) => ({
1520
+ seq: start + idx,
1521
+ modelName,
1522
+ op: "delete",
1523
+ docId
1524
+ })), session);
1525
+ };
1526
+ const recordResetModel = async (db, modelName, session) => {
1527
+ const {
1528
+ start
1529
+ } = await allocateSeqRange(db, 1, session);
1530
+ await insertChanges(db, [{
1531
+ seq: start,
1532
+ modelName,
1533
+ op: "reset_model"
1534
+ }], session);
1535
+ };
1536
+ const captureDeleteMeta = async (query, mode) => {
1537
+ const modelName = String(query?.model?.modelName ?? "");
1538
+ if (!modelName || modelName.startsWith("RB") || EXCLUDED_MODEL_NAMES.has(modelName)) return;
1539
+ if (isGlobalDb(query?.model?.db)) return;
1540
+ const filter = typeof query.getFilter === "function" ? query.getFilter() : query.getQuery?.() ?? {};
1541
+ const session = getQuerySession(query);
1542
+ const findQuery = query.model.find(filter, {
1543
+ _id: 1
1544
+ });
1545
+ if (session && typeof findQuery.session === "function") {
1546
+ findQuery.session(session);
1547
+ }
1548
+ findQuery.lean();
1549
+ if (mode === "one") {
1550
+ findQuery.limit(1);
1551
+ } else {
1552
+ findQuery.limit(maxDeleteIds + 1);
1553
+ }
1554
+ const docs = await findQuery;
1555
+ const ids = Array.isArray(docs) ? docs.map((d3) => normalizeId(d3?._id)).filter((id) => Boolean(id)) : [];
1556
+ const reset = mode === "many" && ids.length > maxDeleteIds;
1557
+ const trimmedIds = reset ? [] : ids;
1558
+ const meta = {
1559
+ modelName,
1560
+ ids: trimmedIds,
1561
+ reset,
1562
+ session
1563
+ };
1564
+ deleteMetaByQuery.set(query, meta);
1565
+ };
1566
+ const flushDeleteMeta = async (query) => {
1567
+ const meta = deleteMetaByQuery.get(query);
1568
+ deleteMetaByQuery.delete(query);
1569
+ if (!meta) return;
1570
+ const db = query?.model?.db;
1571
+ if (!db) return;
1572
+ try {
1573
+ if (meta.reset) {
1574
+ await recordResetModel(db, meta.modelName, meta.session);
1575
+ } else {
1576
+ await recordDeleteChanges(db, meta.modelName, meta.ids, meta.session);
1577
+ }
1578
+ } catch {
1579
+ return;
1580
+ }
1581
+ };
1582
+ const rtsChangeLogPlugin = (schema) => {
1583
+ schema.pre("deleteOne", {
1584
+ query: true,
1585
+ document: false
1586
+ }, async function() {
1587
+ await captureDeleteMeta(this, "one");
1588
+ });
1589
+ schema.pre("deleteMany", {
1590
+ query: true,
1591
+ document: false
1592
+ }, async function() {
1593
+ await captureDeleteMeta(this, "many");
1594
+ });
1595
+ schema.post("deleteOne", {
1596
+ query: true,
1597
+ document: false
1598
+ }, async function() {
1599
+ await flushDeleteMeta(this);
1600
+ });
1601
+ schema.post("deleteMany", {
1602
+ query: true,
1603
+ document: false
1604
+ }, async function() {
1605
+ await flushDeleteMeta(this);
1606
+ });
1607
+ schema.post("findOneAndDelete", {
1608
+ query: true,
1609
+ document: false
1610
+ }, async function(doc) {
1611
+ const modelName = String(this?.model?.modelName ?? "");
1612
+ if (!modelName || modelName.startsWith("RB") || EXCLUDED_MODEL_NAMES.has(modelName)) return;
1613
+ const db = this?.model?.db;
1614
+ if (!db) return;
1615
+ if (isGlobalDb(db)) return;
1616
+ const docId = normalizeId(doc?._id);
1617
+ if (!docId) return;
1618
+ try {
1619
+ const session = getQuerySession(this);
1620
+ await recordDeleteChanges(db, modelName, [docId], session);
1621
+ } catch {
1622
+ return;
1623
+ }
1624
+ });
1625
+ };
1626
+ const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
1627
+ const mergeMongoQuery = (left, right) => {
1628
+ const leftQuery = isRecord(left) ? left : {};
1629
+ if (Object.keys(leftQuery).length === 0) return right;
1630
+ if (Object.keys(right).length === 0) return leftQuery;
1631
+ return {
1632
+ $and: [leftQuery, right]
1633
+ };
1634
+ };
1635
+ const getQueryOptions = (query) => {
1636
+ if (!query || typeof query !== "object") return void 0;
1637
+ if (!("getOptions" in query) || typeof query.getOptions !== "function") return void 0;
1638
+ return query.getOptions();
1639
+ };
1640
+ const getStoredAclFromQuery = (query) => {
1641
+ const options = getQueryOptions(query);
1642
+ const raw = options?.rbAcl;
1643
+ if (!isRecord(raw)) return null;
1644
+ if (!("ability" in raw)) return null;
1645
+ return raw;
1646
+ };
1647
+ const getStoredAclFromAggregate = (aggregate) => {
1648
+ if (!aggregate || typeof aggregate !== "object") return null;
1649
+ const raw = aggregate.options;
1650
+ if (!isRecord(raw)) return null;
1651
+ const acl = raw.rbAcl;
1652
+ if (!isRecord(acl)) return null;
1653
+ if (!("ability" in acl)) return null;
1654
+ return acl;
1655
+ };
1656
+ const addQueryAclFilter = (query, action) => {
1657
+ const storedAcl = getStoredAclFromQuery(query);
1658
+ if (!storedAcl) return;
1659
+ const ability = storedAcl.ability;
1660
+ const resolvedAction = storedAcl.action ?? action;
1661
+ const modelName = query.model.modelName;
1662
+ const accessQuery = accessibleBy(ability, resolvedAction).ofType(modelName);
1663
+ query.and([accessQuery]);
1664
+ };
1665
+ const injectAggregateMatch = (pipeline, match) => {
1666
+ if (pipeline.length === 0) {
1667
+ pipeline.unshift({
1668
+ $match: match
1669
+ });
1670
+ return;
1671
+ }
1672
+ const first = pipeline[0];
1673
+ if (!isRecord(first)) {
1674
+ pipeline.unshift({
1675
+ $match: match
1676
+ });
1677
+ return;
1678
+ }
1679
+ if ("$geoNear" in first) {
1680
+ const geoNear = first.$geoNear;
1681
+ if (isRecord(geoNear)) {
1682
+ geoNear.query = mergeMongoQuery(geoNear.query, match);
1683
+ return;
1684
+ }
1685
+ }
1686
+ if ("$search" in first || "$vectorSearch" in first || "$searchMeta" in first) {
1687
+ pipeline.splice(1, 0, {
1688
+ $match: match
1689
+ });
1690
+ return;
1691
+ }
1692
+ pipeline.unshift({
1693
+ $match: match
1694
+ });
1695
+ };
1696
+ const addAggregateAclFilter = (aggregate, action) => {
1697
+ const storedAcl = getStoredAclFromAggregate(aggregate);
1698
+ if (!storedAcl) return;
1699
+ const ability = storedAcl.ability;
1700
+ const resolvedAction = storedAcl.action ?? action;
1701
+ const modelName = aggregate.model().modelName;
1702
+ const accessQuery = accessibleBy(ability, resolvedAction).ofType(modelName);
1703
+ injectAggregateMatch(aggregate.pipeline(), accessQuery);
1704
+ };
1705
+ const patchAggregateAcl = () => {
1706
+ const globalKey = /* @__PURE__ */ Symbol.for("@rpcbase/db/acl/mongooseAggregateAclPatched");
1707
+ const globalState = globalThis;
1708
+ if (globalState[globalKey]) return;
1709
+ globalState[globalKey] = true;
1710
+ const AggregatePrototype = mongoose.Aggregate.prototype;
1711
+ if (typeof AggregatePrototype.acl === "function") return;
1712
+ AggregatePrototype.acl = function(ability, action = "read") {
1713
+ this.option({
1714
+ rbAcl: {
1715
+ ability,
1716
+ action
1717
+ }
1718
+ });
1719
+ return this;
1720
+ };
1721
+ };
1722
+ const createModelAclProxy = (model2, ability) => {
1723
+ return new Proxy(model2, {
1724
+ get(target, prop, receiver) {
1725
+ if (prop === "acl") {
1726
+ return () => {
1727
+ throw new Error(`Model "${target.modelName}" is already ACL-scoped. Do not call .acl(...) again.`);
1728
+ };
1729
+ }
1730
+ const value = Reflect.get(target, prop, receiver);
1731
+ if (typeof value !== "function") return value;
1732
+ return (...args) => {
1733
+ const result = Reflect.apply(value, target, args);
1734
+ if (result && typeof result === "object" && "acl" in result && typeof result.acl === "function") {
1735
+ return result.acl(ability);
1736
+ }
1737
+ return result;
1738
+ };
1739
+ }
1740
+ });
1741
+ };
1742
+ const mongooseAclPlugin = (schema) => {
1743
+ patchAggregateAcl();
1744
+ schema.query.acl = function(ability, action) {
1745
+ this.setOptions({
1746
+ rbAcl: {
1747
+ ability,
1748
+ action
1749
+ }
1750
+ });
1751
+ return this;
1752
+ };
1753
+ schema.statics.acl = function(ability) {
1754
+ return createModelAclProxy(this, ability);
1755
+ };
1756
+ schema.pre("aggregate", function() {
1757
+ addAggregateAclFilter(this, "read");
1758
+ });
1759
+ schema.pre("countDocuments", function() {
1760
+ addQueryAclFilter(this, "read");
1761
+ });
1762
+ schema.pre("deleteMany", function() {
1763
+ addQueryAclFilter(this, "delete");
1764
+ });
1765
+ schema.pre("deleteOne", function() {
1766
+ addQueryAclFilter(this, "delete");
1767
+ });
1768
+ schema.pre("distinct", function() {
1769
+ addQueryAclFilter(this, "read");
1770
+ });
1771
+ schema.pre("find", function() {
1772
+ addQueryAclFilter(this, "read");
1773
+ });
1774
+ schema.pre("findOne", function() {
1775
+ addQueryAclFilter(this, "read");
1776
+ });
1777
+ schema.pre("findOneAndDelete", function() {
1778
+ addQueryAclFilter(this, "delete");
1779
+ });
1780
+ schema.pre("findOneAndReplace", function() {
1781
+ addQueryAclFilter(this, "update");
1782
+ });
1783
+ schema.pre("findOneAndUpdate", function() {
1784
+ addQueryAclFilter(this, "update");
1785
+ });
1786
+ schema.pre("replaceOne", function() {
1787
+ addQueryAclFilter(this, "update");
1788
+ });
1789
+ schema.pre("updateMany", function() {
1790
+ addQueryAclFilter(this, "update");
1791
+ });
1792
+ schema.pre("updateOne", function() {
1793
+ addQueryAclFilter(this, "update");
1794
+ });
1795
+ };
1796
+ let cachedModels = null;
1797
+ const DEFAULT_GLOBAL_RB_MODEL_NAMES_SET = /* @__PURE__ */ new Set(["RBUser", "RBTenant", "RBOAuthRequest"]);
1798
+ const assertSchema = (exportName, value) => {
1799
+ if (value instanceof mongoose.Schema) return value;
1800
+ throw new Error([`Expected ${exportName} to be an instance of mongoose.Schema, but it was not.`, "rpcbase supports mongoose 9+ only.", "Fix: ensure the project is using mongoose 9.x and that all packages resolve the same mongoose instance (try `npm ls mongoose`)."].join(" "));
1801
+ };
1802
+ const getFrameworkSchemaForModelName = (modelName) => {
1803
+ const exportName = `${modelName}Schema`;
1804
+ const value = frameworkSchemas[exportName];
1805
+ if (!(value instanceof mongoose.Schema)) return null;
1806
+ return value;
1807
+ };
1808
+ const applyTenantPlugins = (schema) => {
1809
+ schema.plugin(accessibleRecordsPlugin);
1810
+ schema.plugin(mongooseAclPlugin);
1811
+ schema.plugin(mongoPaginationPlugin);
1812
+ schema.plugin(rtsChangeLogPlugin);
1813
+ };
1814
+ const registerSchema = (target, other, modelName, schema, scope) => {
1815
+ if (target[modelName] || other[modelName]) {
1816
+ throw new Error(`Duplicate model name "${modelName}" across tenant/global scopes`);
1817
+ }
1818
+ target[modelName] = schema;
1819
+ };
1820
+ const buildSchemasFromModules = (modules) => Object.entries(modules).filter(([key]) => key.endsWith("Schema")).map(([key, schemaValue]) => {
1821
+ const schema = assertSchema(key, schemaValue);
1822
+ const modelName = key.replace(/Schema$/, "");
1823
+ return {
1824
+ modelName,
1825
+ schema
1826
+ };
1827
+ });
1828
+ const registerModels = ({
1829
+ tenant,
1830
+ global
1831
+ }, options = {}) => {
1832
+ registerPoliciesFromModules(frameworkSchemas);
1833
+ registerPoliciesFromModules(tenant);
1834
+ const tenantSchemas = {};
1835
+ const globalSchemas = {};
1836
+ const allowReservedRbModelNames = options.allowReservedRbModelNames === true;
1837
+ for (const {
1838
+ modelName,
1839
+ schema
1840
+ } of buildSchemasFromModules(frameworkSchemas)) {
1841
+ if (DEFAULT_GLOBAL_RB_MODEL_NAMES_SET.has(modelName)) {
1842
+ const cloned = schema.clone();
1843
+ registerSchema(globalSchemas, tenantSchemas, modelName, cloned);
1844
+ } else {
1845
+ const cloned = schema.clone();
1846
+ applyTenantPlugins(cloned);
1847
+ registerSchema(tenantSchemas, globalSchemas, modelName, cloned);
1848
+ }
1849
+ }
1850
+ for (const {
1851
+ modelName,
1852
+ schema
1853
+ } of buildSchemasFromModules(tenant)) {
1854
+ if (modelName === "RBUser" || modelName === "RBTenant") {
1855
+ throw new Error(`Invalid tenant model name "${modelName}". RBUser/RBTenant are global models.`);
1856
+ }
1857
+ if (modelName.startsWith("RB")) {
1858
+ const frameworkSchema = getFrameworkSchemaForModelName(modelName);
1859
+ if (frameworkSchema && schema === frameworkSchema) continue;
1860
+ if (!allowReservedRbModelNames) {
1861
+ throw new Error(`Invalid tenant model name "${modelName}". RB* models are reserved for rpcbase.`);
1862
+ }
1863
+ }
1864
+ const cloned = schema.clone();
1865
+ applyTenantPlugins(cloned);
1866
+ registerSchema(tenantSchemas, globalSchemas, modelName, cloned);
1867
+ }
1868
+ for (const {
1869
+ modelName,
1870
+ schema
1871
+ } of buildSchemasFromModules(global ?? {})) {
1872
+ if (modelName.startsWith("RB")) {
1873
+ const frameworkSchema = getFrameworkSchemaForModelName(modelName);
1874
+ if (frameworkSchema && schema === frameworkSchema) continue;
1875
+ if (!allowReservedRbModelNames) {
1876
+ throw new Error(`Invalid global model name "${modelName}". RB* models are reserved for rpcbase.`);
1877
+ }
1878
+ }
1879
+ const cloned = schema.clone();
1880
+ registerSchema(globalSchemas, tenantSchemas, modelName, cloned);
1881
+ }
1882
+ const allSchemas = {
1883
+ ...globalSchemas,
1884
+ ...tenantSchemas
1885
+ };
1886
+ for (const [modelName, schema] of Object.entries(allSchemas)) {
1887
+ if (!mongoose.models[modelName]) {
1888
+ mongoose.model(modelName, schema);
1889
+ }
1890
+ }
1891
+ cachedModels = {
1892
+ tenant: {
1893
+ ...cachedModels?.tenant ?? {},
1894
+ ...tenantSchemas
1895
+ },
1896
+ global: {
1897
+ ...cachedModels?.global ?? {},
1898
+ ...globalSchemas
1899
+ }
1900
+ };
1901
+ };
1902
+ const getRegisteredModels = (scope) => {
1903
+ if (!cachedModels) {
1904
+ throw new Error("Models not registered. Call createModels(...) once at startup (or import your models module) before using models.get.");
1905
+ }
1906
+ return cachedModels[scope];
1907
+ };
1908
+ const loadModelFromDb = async (modelName, dbName, scope) => {
1909
+ const schemas = getRegisteredModels(scope);
1910
+ const schema = schemas[modelName];
1911
+ assert(schema, `Model ${modelName} not registered. Available models: ${Object.keys(schemas).join(", ")}`);
1912
+ const modelConnection = await ensureMongooseConnection(dbName);
1913
+ if (!modelConnection.models[modelName]) {
1914
+ modelConnection.model(modelName, schema);
1915
+ }
1916
+ return modelConnection.models[modelName];
1917
+ };
1918
+ const normalizeTenantId$1 = (value) => {
1919
+ if (typeof value !== "string") return null;
1920
+ const tenantId = value.trim();
1921
+ return tenantId || null;
1922
+ };
1923
+ const getTenantIdFromLoadModelCtx = (ctx) => {
1924
+ const tenantId = normalizeTenantId$1(ctx.tenantId) ?? normalizeTenantId$1(ctx.req?.session?.user?.currentTenantId);
1925
+ assert(tenantId, "Tenant ID is missing from ctx (expected ctx.tenantId or ctx.req.session.user.currentTenantId)");
1926
+ return tenantId;
1927
+ };
1928
+ const models = {
1929
+ register: registerModels,
1930
+ getUnsafe: async (modelName, ctx) => {
1931
+ const tenantId = getTenantIdFromLoadModelCtx(ctx);
1932
+ const dbName = getTenantDbName(tenantId);
1933
+ return loadModelFromDb(modelName, dbName, "tenant");
1934
+ },
1935
+ get: async (modelName, ctx) => {
1936
+ const model2 = await models.getUnsafe(modelName, ctx);
1937
+ const resolvedAbility = ctx.ability;
1938
+ const isProtected = hasRegisteredPolicy(modelName);
1939
+ if (!isProtected) {
1940
+ return model2;
1941
+ }
1942
+ if (!resolvedAbility) {
1943
+ throw new Error(`Model "${modelName}" is ACL-protected. Set ctx.ability or use models.getUnsafe(...) explicitly.`);
1944
+ }
1945
+ if (typeof model2.acl !== "function") return model2;
1946
+ return model2.acl(resolvedAbility);
1947
+ },
1948
+ getGlobal: async (modelName, ctx) => {
1949
+ const dbName = getGlobalDbName();
1950
+ return loadModelFromDb(modelName, dbName, "global");
1951
+ }
1952
+ };
1953
+ const createModels = (modules, options) => {
1954
+ registerModels(modules, options);
1955
+ const get = (async (modelNameOrNames, ctx) => {
1956
+ if (Array.isArray(modelNameOrNames)) {
1957
+ return Promise.all(modelNameOrNames.map((modelName) => models.get(modelName, ctx)));
1958
+ }
1959
+ return models.get(modelNameOrNames, ctx);
1960
+ });
1961
+ const getUnsafe = (async (modelNameOrNames, ctx) => {
1962
+ if (Array.isArray(modelNameOrNames)) {
1963
+ return Promise.all(modelNameOrNames.map((modelName) => models.getUnsafe(modelName, ctx)));
1964
+ }
1965
+ return models.getUnsafe(modelNameOrNames, ctx);
1966
+ });
1967
+ const getGlobal = (async (modelNameOrNames, ctx) => {
1968
+ if (Array.isArray(modelNameOrNames)) {
1969
+ return Promise.all(modelNameOrNames.map((modelName) => models.getGlobal(modelName, ctx)));
1970
+ }
1971
+ return models.getGlobal(modelNameOrNames, ctx);
1972
+ });
1973
+ return {
1974
+ register: (nextModules, nextOptions) => registerModels(nextModules, nextOptions),
1975
+ get,
1976
+ getUnsafe,
1977
+ getGlobal
1978
+ };
1979
+ };
1980
+ const getAppName = () => {
1981
+ const appName = process.env.APP_NAME?.trim();
1982
+ assert(appName, "Missing APP_NAME");
1983
+ return appName;
1984
+ };
1985
+ const normalizeTenantId = (tenantId) => {
1986
+ const normalized = tenantId.trim();
1987
+ assert(normalized, "Tenant ID is missing");
1988
+ return normalized;
1989
+ };
1990
+ const getTenantFilesystemDbName = (tenantId) => `${getAppName()}-${normalizeTenantId(tenantId)}-filesystem-db`;
1991
+ const getTenantFilesystemDb = async (tenantId) => ensureMongooseConnection(getTenantFilesystemDbName(tenantId));
1992
+ const getTenantFilesystemDbFromCtx = async (ctx) => {
1993
+ const tenantId = getTenantIdFromLoadModelCtx(ctx);
1994
+ return getTenantFilesystemDb(tenantId);
1995
+ };
1996
+ const buildTenantLoadModelCtx = (tenantId) => ({
1997
+ req: {
1998
+ session: {
1999
+ user: {
2000
+ currentTenantId: tenantId
2001
+ }
2002
+ }
2003
+ }
1652
2004
  });
1653
- var registerModels = ({ tenant, global }, options = {}) => {
1654
- registerPoliciesFromModules(models_exports);
1655
- registerPoliciesFromModules(tenant);
1656
- const tenantSchemas = {};
1657
- const globalSchemas = {};
1658
- const allowReservedRbModelNames = options.allowReservedRbModelNames === true;
1659
- for (const { modelName, schema } of buildSchemasFromModules(models_exports)) if (DEFAULT_GLOBAL_RB_MODEL_NAMES_SET.has(modelName)) registerSchema(globalSchemas, tenantSchemas, modelName, schema.clone(), "global");
1660
- else {
1661
- const cloned = schema.clone();
1662
- applyTenantPlugins(cloned);
1663
- registerSchema(tenantSchemas, globalSchemas, modelName, cloned, "tenant");
1664
- }
1665
- for (const { modelName, schema } of buildSchemasFromModules(tenant)) {
1666
- if (modelName === "RBUser" || modelName === "RBTenant") throw new Error(`Invalid tenant model name "${modelName}". RBUser/RBTenant are global models.`);
1667
- if (modelName.startsWith("RB")) {
1668
- const frameworkSchema = getFrameworkSchemaForModelName(modelName);
1669
- if (frameworkSchema && schema === frameworkSchema) continue;
1670
- if (!allowReservedRbModelNames) throw new Error(`Invalid tenant model name "${modelName}". RB* models are reserved for rpcbase.`);
1671
- }
1672
- const cloned = schema.clone();
1673
- applyTenantPlugins(cloned);
1674
- registerSchema(tenantSchemas, globalSchemas, modelName, cloned, "tenant");
1675
- }
1676
- for (const { modelName, schema } of buildSchemasFromModules(global ?? {})) {
1677
- if (modelName.startsWith("RB")) {
1678
- const frameworkSchema = getFrameworkSchemaForModelName(modelName);
1679
- if (frameworkSchema && schema === frameworkSchema) continue;
1680
- if (!allowReservedRbModelNames) throw new Error(`Invalid global model name "${modelName}". RB* models are reserved for rpcbase.`);
1681
- }
1682
- registerSchema(globalSchemas, tenantSchemas, modelName, schema.clone(), "global");
1683
- }
1684
- const allSchemas = {
1685
- ...globalSchemas,
1686
- ...tenantSchemas
1687
- };
1688
- for (const [modelName, schema] of Object.entries(allSchemas)) if (!mongoose$1.models[modelName]) mongoose$1.model(modelName, schema);
1689
- cachedModels = {
1690
- tenant: {
1691
- ...cachedModels?.tenant ?? {},
1692
- ...tenantSchemas
1693
- },
1694
- global: {
1695
- ...cachedModels?.global ?? {},
1696
- ...globalSchemas
1697
- }
1698
- };
1699
- };
1700
- var getRegisteredModels = (scope) => {
1701
- if (!cachedModels) throw new Error("Models not registered. Call createModels(...) once at startup (or import your models module) before using models.get.");
1702
- return cachedModels[scope];
1703
- };
1704
- //#endregion
1705
- //#region src/modelsApi.ts
1706
- var loadModelFromDb = async (modelName, dbName, scope) => {
1707
- const schemas = getRegisteredModels(scope);
1708
- const schema = schemas[modelName];
1709
- assert(schema, `Model ${modelName} not registered. Available models: ${Object.keys(schemas).join(", ")}`);
1710
- const modelConnection = await ensureMongooseConnection(dbName);
1711
- if (!modelConnection.models[modelName]) modelConnection.model(modelName, schema);
1712
- return modelConnection.models[modelName];
1713
- };
1714
- var normalizeTenantId$1 = (value) => {
1715
- if (typeof value !== "string") return null;
1716
- return value.trim() || null;
1717
- };
1718
- var getTenantIdFromLoadModelCtx = (ctx) => {
1719
- const tenantId = normalizeTenantId$1(ctx.tenantId) ?? normalizeTenantId$1(ctx.req?.session?.user?.currentTenantId);
1720
- assert(tenantId, "Tenant ID is missing from ctx (expected ctx.tenantId or ctx.req.session.user.currentTenantId)");
1721
- return tenantId;
1722
- };
1723
- var models = {
1724
- register: registerModels,
1725
- getUnsafe: async (modelName, ctx) => {
1726
- return loadModelFromDb(modelName, getTenantDbName(getTenantIdFromLoadModelCtx(ctx)), "tenant");
1727
- },
1728
- get: async (modelName, ctx) => {
1729
- const model = await models.getUnsafe(modelName, ctx);
1730
- const resolvedAbility = ctx.ability;
1731
- if (!hasRegisteredPolicy(modelName)) return model;
1732
- if (!resolvedAbility) throw new Error(`Model "${modelName}" is ACL-protected. Set ctx.ability or use models.getUnsafe(...) explicitly.`);
1733
- if (typeof model.acl !== "function") return model;
1734
- return model.acl(resolvedAbility);
1735
- },
1736
- getGlobal: async (modelName, ctx) => {
1737
- return loadModelFromDb(modelName, getGlobalDbName(), "global");
1738
- }
1739
- };
1740
- //#endregion
1741
- //#region src/createModels.ts
1742
- var createModels = (modules, options) => {
1743
- registerModels(modules, options);
1744
- const get = (async (modelNameOrNames, ctx) => {
1745
- if (Array.isArray(modelNameOrNames)) return Promise.all(modelNameOrNames.map((modelName) => models.get(modelName, ctx)));
1746
- return models.get(modelNameOrNames, ctx);
1747
- });
1748
- const getUnsafe = (async (modelNameOrNames, ctx) => {
1749
- if (Array.isArray(modelNameOrNames)) return Promise.all(modelNameOrNames.map((modelName) => models.getUnsafe(modelName, ctx)));
1750
- return models.getUnsafe(modelNameOrNames, ctx);
1751
- });
1752
- const getGlobal = (async (modelNameOrNames, ctx) => {
1753
- if (Array.isArray(modelNameOrNames)) return Promise.all(modelNameOrNames.map((modelName) => models.getGlobal(modelName, ctx)));
1754
- return models.getGlobal(modelNameOrNames, ctx);
1755
- });
1756
- return {
1757
- register: (nextModules, nextOptions) => registerModels(nextModules, nextOptions),
1758
- get,
1759
- getUnsafe,
1760
- getGlobal
1761
- };
1762
- };
1763
- //#endregion
1764
- //#region src/tenantFilesystemDb.ts
1765
- var getAppName = () => {
1766
- const appName = process.env.APP_NAME?.trim();
1767
- assert(appName, "Missing APP_NAME");
1768
- return appName;
1769
- };
1770
- var normalizeTenantId = (tenantId) => {
1771
- const normalized = tenantId.trim();
1772
- assert(normalized, "Tenant ID is missing");
1773
- return normalized;
1774
- };
1775
- var getTenantFilesystemDbName = (tenantId) => `${getAppName()}-${normalizeTenantId(tenantId)}-filesystem-db`;
1776
- var getTenantFilesystemDb = async (tenantId) => ensureMongooseConnection(getTenantFilesystemDbName(tenantId));
1777
- var getTenantFilesystemDbFromCtx = async (ctx) => {
1778
- return getTenantFilesystemDb(getTenantIdFromLoadModelCtx(ctx));
1779
- };
1780
- //#endregion
1781
- //#region src/transactions.ts
1782
- var buildTenantLoadModelCtx = (tenantId) => ({ req: { session: { user: { currentTenantId: tenantId } } } });
1783
2005
  async function withTransaction(scope, fn, options) {
1784
- const normalizedTenantId = (() => {
1785
- if (typeof scope === "string") return scope.trim();
1786
- if ("tenantId" in scope) return scope.tenantId.trim();
1787
- return getTenantIdFromLoadModelCtx(scope.ctx);
1788
- })();
1789
- if (!normalizedTenantId) throw new Error("Tenant ID is missing");
1790
- const tenantDbName = getTenantDbName(normalizedTenantId);
1791
- const globalDbName = getGlobalDbName();
1792
- const filesystemDbName = getTenantFilesystemDbName(normalizedTenantId);
1793
- const tenantDb = await ensureMongooseConnection(tenantDbName);
1794
- const globalDb = await ensureMongooseConnection(globalDbName);
1795
- const filesystemDb = await ensureMongooseConnection(filesystemDbName);
1796
- const session = await tenantDb.startSession();
1797
- const tenantCtx = typeof scope === "object" && "ctx" in scope ? scope.ctx : buildTenantLoadModelCtx(normalizedTenantId);
1798
- const globalCtx = { req: { session: null } };
1799
- try {
1800
- return await session.withTransaction(async () => fn({
1801
- tenantId: normalizedTenantId,
1802
- session,
1803
- ctx: {
1804
- tenant: tenantCtx,
1805
- global: globalCtx
1806
- },
1807
- db: {
1808
- tenant: tenantDb,
1809
- global: globalDb,
1810
- filesystem: filesystemDb
1811
- }
1812
- }), options);
1813
- } finally {
1814
- await session.endSession();
1815
- }
2006
+ const normalizedTenantId = (() => {
2007
+ if (typeof scope === "string") return scope.trim();
2008
+ if ("tenantId" in scope) return scope.tenantId.trim();
2009
+ return getTenantIdFromLoadModelCtx(scope.ctx);
2010
+ })();
2011
+ if (!normalizedTenantId) throw new Error("Tenant ID is missing");
2012
+ const tenantDbName = getTenantDbName(normalizedTenantId);
2013
+ const globalDbName = getGlobalDbName();
2014
+ const filesystemDbName = getTenantFilesystemDbName(normalizedTenantId);
2015
+ const tenantDb = await ensureMongooseConnection(tenantDbName);
2016
+ const globalDb = await ensureMongooseConnection(globalDbName);
2017
+ const filesystemDb = await ensureMongooseConnection(filesystemDbName);
2018
+ const session = await tenantDb.startSession();
2019
+ const tenantCtx = typeof scope === "object" && "ctx" in scope ? scope.ctx : buildTenantLoadModelCtx(normalizedTenantId);
2020
+ const globalCtx = {
2021
+ req: {
2022
+ session: null
2023
+ }
2024
+ };
2025
+ try {
2026
+ return await session.withTransaction(async () => fn({
2027
+ tenantId: normalizedTenantId,
2028
+ session,
2029
+ ctx: {
2030
+ tenant: tenantCtx,
2031
+ global: globalCtx
2032
+ },
2033
+ db: {
2034
+ tenant: tenantDb,
2035
+ global: globalDb,
2036
+ filesystem: filesystemDb
2037
+ }
2038
+ }), options);
2039
+ } finally {
2040
+ await session.endSession();
2041
+ }
1816
2042
  }
1817
- //#endregion
1818
- export { E164_PHONE_OR_EMPTY_REGEX, E164_PHONE_REGEX, LANGUAGE_CODE_REGEX, PaginationValidationError, RBNotificationPolicy, RBNotificationSchema, RBNotificationSettingsPolicy, RBNotificationSettingsSchema, RBOAuthRequestSchema, RBRtsChangeSchema, RBRtsCounterSchema, RBTenantSchema, RBTenantSubscriptionEventSchema, RBTenantSubscriptionSchema, RBUploadChunkSchema, RBUploadSessionPolicy, RBUploadSessionSchema, RBUserSchema, Schema, ZRBNotification, ZRBNotificationDigestFrequency, ZRBNotificationSettings, ZRBNotificationTopicPreference, ZRBOAuthRequest, ZRBRtsChange, ZRBRtsChangeOp, ZRBRtsCounter, ZRBTenant, ZRBTenantSubscription, ZRBTenantSubscriptionChangeDirection, ZRBTenantSubscriptionEvent, ZRBTenantSubscriptionEventSource, ZRBTenantSubscriptionIntervalUnit, ZRBTenantSubscriptionScope, ZRBTenantSubscriptionStatus, ZRBTenantSubscriptionType, ZRBUploadChunk, ZRBUploadSession, ZRBUploadSessionStatus, ZRBUser, buildAbility, buildAbilityFromSession, buildLocaleFallbackChain, buildSearchTextStage, can, createModels, ensureSearchIndex, extendMongooseSchema, extendZod, getAccessibleByQuery, getRegisteredPolicies, getTenantFilesystemDb, getTenantFilesystemDbFromCtx, getTenantFilesystemDbName, getTenantIdFromLoadModelCtx, getTenantRolesFromSessionUser, hasRegisteredPolicy, isPaginationValidationError, localizedStringField, makeZE164Phone, model, models, mongoPaginationPlugin, mongoose, omitMongooseSchemaPaths, registerPoliciesFromModules, registerPolicy, resolveLocalizedString, searchMetaProjection, withLocalizedStringFallback, withTransaction, z, zE164Phone, zI18nString, zLocalizedString };
1819
-
1820
- //# sourceMappingURL=index.js.map
2043
+ export {
2044
+ E as E164_PHONE_OR_EMPTY_REGEX,
2045
+ a2 as E164_PHONE_REGEX,
2046
+ L as LANGUAGE_CODE_REGEX,
2047
+ PaginationValidationError,
2048
+ RBNotificationPolicy,
2049
+ RBNotificationSchema,
2050
+ RBNotificationSettingsPolicy,
2051
+ RBNotificationSettingsSchema,
2052
+ RBOAuthRequestSchema,
2053
+ RBRtsChangeSchema,
2054
+ RBRtsCounterSchema,
2055
+ RBTenantSchema,
2056
+ RBTenantSubscriptionEventSchema,
2057
+ RBTenantSubscriptionSchema,
2058
+ RBUploadChunkSchema,
2059
+ RBUploadSessionPolicy,
2060
+ RBUploadSessionSchema,
2061
+ RBUserSchema,
2062
+ Schema,
2063
+ ZRBNotification,
2064
+ ZRBNotificationDigestFrequency,
2065
+ ZRBNotificationSettings,
2066
+ ZRBNotificationTopicPreference,
2067
+ ZRBOAuthRequest,
2068
+ ZRBRtsChange,
2069
+ ZRBRtsChangeOp,
2070
+ ZRBRtsCounter,
2071
+ ZRBTenant,
2072
+ ZRBTenantSubscription,
2073
+ ZRBTenantSubscriptionChangeDirection,
2074
+ ZRBTenantSubscriptionEvent,
2075
+ ZRBTenantSubscriptionEventSource,
2076
+ ZRBTenantSubscriptionIntervalUnit,
2077
+ ZRBTenantSubscriptionScope,
2078
+ ZRBTenantSubscriptionStatus,
2079
+ ZRBTenantSubscriptionType,
2080
+ ZRBUploadChunk,
2081
+ ZRBUploadSession,
2082
+ ZRBUploadSessionStatus,
2083
+ ZRBUser,
2084
+ b as buildAbility,
2085
+ a as buildAbilityFromSession,
2086
+ b2 as buildLocaleFallbackChain,
2087
+ buildSearchTextStage,
2088
+ c as can,
2089
+ createModels,
2090
+ ensureSearchIndex,
2091
+ extendMongooseSchema,
2092
+ e2 as extendZod,
2093
+ g as getAccessibleByQuery,
2094
+ d as getRegisteredPolicies,
2095
+ getTenantFilesystemDb,
2096
+ getTenantFilesystemDbFromCtx,
2097
+ getTenantFilesystemDbName,
2098
+ getTenantIdFromLoadModelCtx,
2099
+ e as getTenantRolesFromSessionUser,
2100
+ hasRegisteredPolicy,
2101
+ isPaginationValidationError,
2102
+ localizedStringField,
2103
+ m as makeZE164Phone,
2104
+ model,
2105
+ models,
2106
+ mongoPaginationPlugin,
2107
+ default2 as mongoose,
2108
+ omitMongooseSchemaPaths,
2109
+ registerPoliciesFromModules,
2110
+ f as registerPolicy,
2111
+ r as resolveLocalizedString,
2112
+ searchMetaProjection,
2113
+ withLocalizedStringFallback,
2114
+ withTransaction,
2115
+ z2 as z,
2116
+ c2 as zE164Phone,
2117
+ d2 as zI18nString,
2118
+ f2 as zLocalizedString
2119
+ };
2120
+ //# sourceMappingURL=index.js.map