@rpcbase/db 0.121.0 → 0.123.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.
@@ -0,0 +1,7 @@
1
+ import { BillingStorage } from '@rpcbase/billing';
2
+ import { LoadModelCtx } from './modelsApi';
3
+ export declare const createTenantBillingStorage: (params: {
4
+ ctx: LoadModelCtx;
5
+ tenantId: string;
6
+ }) => BillingStorage;
7
+ //# sourceMappingURL=billingStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billingStorage.d.ts","sourceRoot":"","sources":["../src/billingStorage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EAKf,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AA8D/C,eAAO,MAAM,0BAA0B,GAAI,QAAQ;IACjD,GAAG,EAAE,YAAY,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;CACjB,KAAG,cAsEH,CAAA"}
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export * from './search';
6
6
  export * from './zod';
7
7
  export * from './modelTypes';
8
8
  export * from './createModels';
9
+ export * from './billingStorage';
9
10
  export * from './modelsApi';
10
11
  export * from './tenantFilesystemDb';
11
12
  export * from './transactions';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAA;AACrB,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,OAAO,CAAA;AAErB,cAAc,cAAc,CAAA;AAC5B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,sBAAsB,CAAA;AACpC,cAAc,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAA;AACrB,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,OAAO,CAAA;AAErB,cAAc,cAAc,CAAA;AAC5B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,sBAAsB,CAAA;AACpC,cAAc,gBAAgB,CAAA"}
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import { b, a, c, g, d, e, f } from "./can-B4pD_pnR.js";
3
3
  import mongoose, { Schema as Schema$1, Types } from "mongoose";
4
4
  import { default as default2 } from "mongoose";
5
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";
6
7
  import { timingSafeEqual, createHmac } from "node:crypto";
7
8
  import { w as withLocalizedStringFallback } from "./index-DrIoUXc2.js";
8
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";
@@ -166,16 +167,22 @@ const RBTenantSchema = new Schema$1({
166
167
  type: String
167
168
  }
168
169
  });
169
- const ZRBTenantSubscriptionStatus = z.enum(["trialing", "active", "past_due", "paused", "canceled", "expired"]);
170
- const ZRBTenantSubscriptionIntervalUnit = z.enum(["month", "year"]);
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);
171
175
  const ZRBTenantSubscriptionEventSource = z.enum(["admin", "system", "webhook", "user"]);
172
- const ZRBTenantSubscriptionChangeDirection = z.enum(["upgrade", "downgrade", "lateral"]);
173
- const ZRBTenantSubscriptionEvent = z.object({
174
- tenantId: z.string(),
175
- subscriptionId: z.string(),
176
- type: z.string(),
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),
177
180
  occurredAt: z.date(),
178
- effectiveAt: z.date().optional(),
181
+ effectiveAt: z.date(),
182
+ subscriptionType: ZRBTenantSubscriptionType,
183
+ parentSubscriptionId: z.string().optional(),
184
+ scope: ZRBTenantSubscriptionScope,
185
+ scopeId: z.string().optional(),
179
186
  fromPlanKey: z.string().optional(),
180
187
  toPlanKey: z.string().optional(),
181
188
  fromStatus: ZRBTenantSubscriptionStatus.optional(),
@@ -186,6 +193,11 @@ const ZRBTenantSubscriptionEvent = z.object({
186
193
  toIntervalUnit: ZRBTenantSubscriptionIntervalUnit.optional(),
187
194
  fromIntervalCount: z.number().int().min(1).optional(),
188
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),
189
201
  direction: ZRBTenantSubscriptionChangeDirection.optional(),
190
202
  actorUserId: z.string().optional(),
191
203
  source: ZRBTenantSubscriptionEventSource.optional(),
@@ -195,6 +207,65 @@ const ZRBTenantSubscriptionEvent = z.object({
195
207
  providerPayload: z.unknown().optional(),
196
208
  metadata: z.record(z.string(), z.unknown()).optional()
197
209
  });
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()
218
+ });
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)
229
+ });
230
+ const subscriptionStatusChangedEventSchema = subscriptionEventBaseSchema.extend({
231
+ type: z.literal("status_changed"),
232
+ fromStatus: ZRBTenantSubscriptionStatus,
233
+ toStatus: ZRBTenantSubscriptionStatus
234
+ });
235
+ const subscriptionResumedEventSchema = subscriptionEventBaseSchema.extend({
236
+ type: z.literal("resumed"),
237
+ fromStatus: ZRBTenantSubscriptionStatus,
238
+ toStatus: z.literal("active")
239
+ });
240
+ const subscriptionCanceledEventSchema = subscriptionEventBaseSchema.extend({
241
+ type: z.literal("canceled"),
242
+ fromStatus: ZRBTenantSubscriptionStatus,
243
+ toStatus: z.literal("canceled")
244
+ });
245
+ const subscriptionRenewedEventSchema = subscriptionEventBaseSchema.extend({
246
+ type: z.literal("renewed"),
247
+ fromStatus: ZRBTenantSubscriptionStatus,
248
+ toStatus: ZRBTenantSubscriptionStatus
249
+ });
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
+ }
265
+ });
266
+ const requiredForEventTypes = (...types) => function requiredForEventType() {
267
+ return Boolean(this.type && types.includes(this.type));
268
+ };
198
269
  const RBTenantSubscriptionEventSchema = new Schema$1({
199
270
  tenantId: {
200
271
  type: String,
@@ -208,7 +279,8 @@ const RBTenantSubscriptionEventSchema = new Schema$1({
208
279
  },
209
280
  type: {
210
281
  type: String,
211
- required: true
282
+ required: true,
283
+ enum: ZRBTenantSubscriptionEventType.options
212
284
  },
213
285
  occurredAt: {
214
286
  type: Date,
@@ -216,46 +288,96 @@ const RBTenantSubscriptionEventSchema = new Schema$1({
216
288
  default: Date.now
217
289
  },
218
290
  effectiveAt: {
219
- type: Date
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
+ }
220
315
  },
221
316
  fromPlanKey: {
222
- type: String
317
+ type: String,
318
+ required: requiredForEventTypes("plan_changed")
223
319
  },
224
320
  toPlanKey: {
225
- type: String
321
+ type: String,
322
+ required: requiredForEventTypes("created", "plan_changed")
226
323
  },
227
324
  fromStatus: {
228
325
  type: String,
326
+ required: requiredForEventTypes("status_changed", "resumed", "canceled", "renewed"),
229
327
  enum: ZRBTenantSubscriptionStatus.options
230
328
  },
231
329
  toStatus: {
232
330
  type: String,
331
+ required: requiredForEventTypes("created", "status_changed", "resumed", "canceled", "renewed"),
233
332
  enum: ZRBTenantSubscriptionStatus.options
234
333
  },
235
334
  fromModules: {
236
335
  type: [String],
336
+ required: requiredForEventTypes("plan_changed"),
237
337
  default: void 0
238
338
  },
239
339
  toModules: {
240
340
  type: [String],
341
+ required: requiredForEventTypes("created", "plan_changed"),
241
342
  default: void 0
242
343
  },
243
344
  fromIntervalUnit: {
244
345
  type: String,
346
+ required: requiredForEventTypes("plan_changed"),
245
347
  enum: ZRBTenantSubscriptionIntervalUnit.options
246
348
  },
247
349
  toIntervalUnit: {
248
350
  type: String,
351
+ required: requiredForEventTypes("created", "plan_changed"),
249
352
  enum: ZRBTenantSubscriptionIntervalUnit.options
250
353
  },
251
354
  fromIntervalCount: {
252
355
  type: Number,
356
+ required: requiredForEventTypes("plan_changed"),
253
357
  min: 1
254
358
  },
255
359
  toIntervalCount: {
256
360
  type: Number,
361
+ required: requiredForEventTypes("created", "plan_changed"),
257
362
  min: 1
258
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
+ },
259
381
  direction: {
260
382
  type: String,
261
383
  enum: ZRBTenantSubscriptionChangeDirection.options
@@ -286,6 +408,7 @@ const RBTenantSubscriptionEventSchema = new Schema$1({
286
408
  RBTenantSubscriptionEventSchema.index({
287
409
  tenantId: 1,
288
410
  subscriptionId: 1,
411
+ effectiveAt: 1,
289
412
  occurredAt: 1
290
413
  });
291
414
  RBTenantSubscriptionEventSchema.index({
@@ -293,7 +416,155 @@ RBTenantSubscriptionEventSchema.index({
293
416
  providerEventId: 1
294
417
  }, {
295
418
  unique: true,
296
- sparse: true
419
+ partialFilterExpression: {
420
+ provider: {
421
+ $type: "string"
422
+ },
423
+ providerEventId: {
424
+ $type: "string"
425
+ }
426
+ }
427
+ });
428
+ RBTenantSubscriptionEventSchema.index({
429
+ tenantId: 1,
430
+ idempotencyKey: 1
431
+ }, {
432
+ unique: true
433
+ });
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
+ }
557
+ });
558
+ RBTenantSubscriptionProjectionSchema.index({
559
+ tenantId: 1,
560
+ subscriptionId: 1
561
+ }, {
562
+ unique: true
563
+ });
564
+ RBTenantSubscriptionProjectionSchema.index({
565
+ tenantId: 1,
566
+ scope: 1,
567
+ scopeId: 1
297
568
  });
298
569
  const ZRBRtsCounter = z.object({
299
570
  _id: z.string(),
@@ -889,6 +1160,7 @@ const frameworkSchemas = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de
889
1160
  RBRtsCounterSchema,
890
1161
  RBTenantSchema,
891
1162
  RBTenantSubscriptionEventSchema,
1163
+ RBTenantSubscriptionProjectionSchema,
892
1164
  RBUploadChunkSchema,
893
1165
  RBUploadSessionPolicy,
894
1166
  RBUploadSessionSchema,
@@ -907,8 +1179,12 @@ const frameworkSchemas = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de
907
1179
  ZRBTenantSubscriptionChangeDirection,
908
1180
  ZRBTenantSubscriptionEvent,
909
1181
  ZRBTenantSubscriptionEventSource,
1182
+ ZRBTenantSubscriptionEventType,
910
1183
  ZRBTenantSubscriptionIntervalUnit,
1184
+ ZRBTenantSubscriptionProjection,
1185
+ ZRBTenantSubscriptionScope,
911
1186
  ZRBTenantSubscriptionStatus,
1187
+ ZRBTenantSubscriptionType,
912
1188
  ZRBUploadChunk,
913
1189
  ZRBUploadSession,
914
1190
  ZRBUploadSessionStatus,
@@ -2045,6 +2321,123 @@ async function withTransaction(scope, fn, options) {
2045
2321
  await session.endSession();
2046
2322
  }
2047
2323
  }
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
2339
+ });
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
2366
+ });
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
+ };
2048
2441
  export {
2049
2442
  E as E164_PHONE_OR_EMPTY_REGEX,
2050
2443
  a2 as E164_PHONE_REGEX,
@@ -2061,6 +2454,7 @@ export {
2061
2454
  RBRtsCounterSchema,
2062
2455
  RBTenantSchema,
2063
2456
  RBTenantSubscriptionEventSchema,
2457
+ RBTenantSubscriptionProjectionSchema,
2064
2458
  RBUploadChunkSchema,
2065
2459
  RBUploadSessionPolicy,
2066
2460
  RBUploadSessionSchema,
@@ -2080,8 +2474,12 @@ export {
2080
2474
  ZRBTenantSubscriptionChangeDirection,
2081
2475
  ZRBTenantSubscriptionEvent,
2082
2476
  ZRBTenantSubscriptionEventSource,
2477
+ ZRBTenantSubscriptionEventType,
2083
2478
  ZRBTenantSubscriptionIntervalUnit,
2479
+ ZRBTenantSubscriptionProjection,
2480
+ ZRBTenantSubscriptionScope,
2084
2481
  ZRBTenantSubscriptionStatus,
2482
+ ZRBTenantSubscriptionType,
2085
2483
  ZRBUploadChunk,
2086
2484
  ZRBUploadSession,
2087
2485
  ZRBUploadSessionStatus,
@@ -2092,6 +2490,7 @@ export {
2092
2490
  buildSearchTextStage,
2093
2491
  c as can,
2094
2492
  createModels,
2493
+ createTenantBillingStorage,
2095
2494
  ensureSearchIndex,
2096
2495
  extendMongooseSchema,
2097
2496
  e2 as extendZod,