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