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