@saasicat/adapter-prisma 0.6.0 → 0.7.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/README.md +147 -23
- package/dist/index.cjs +1014 -176
- package/dist/index.d.cts +297 -60
- package/dist/index.d.ts +297 -60
- package/dist/index.js +1013 -180
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -86,7 +86,7 @@ PrismaAuditAdapter = _ts_decorate2([
|
|
|
86
86
|
_ts_param(0, Inject(PRISMA_CLIENT_TOKEN)),
|
|
87
87
|
_ts_metadata("design:type", Function),
|
|
88
88
|
_ts_metadata("design:paramtypes", [
|
|
89
|
-
typeof
|
|
89
|
+
typeof Pick === "undefined" ? Object : Pick
|
|
90
90
|
])
|
|
91
91
|
], PrismaAuditAdapter);
|
|
92
92
|
|
|
@@ -149,7 +149,7 @@ PrismaAuditQueryAdapter = _ts_decorate3([
|
|
|
149
149
|
_ts_param2(0, Inject2(PRISMA_CLIENT_TOKEN)),
|
|
150
150
|
_ts_metadata2("design:type", Function),
|
|
151
151
|
_ts_metadata2("design:paramtypes", [
|
|
152
|
-
typeof
|
|
152
|
+
typeof Pick === "undefined" ? Object : Pick
|
|
153
153
|
])
|
|
154
154
|
], PrismaAuditQueryAdapter);
|
|
155
155
|
function toActorTagFilter(actorTag) {
|
|
@@ -221,7 +221,7 @@ PrismaAuditStatsAdapter = _ts_decorate4([
|
|
|
221
221
|
_ts_param3(0, Inject3(PRISMA_CLIENT_TOKEN)),
|
|
222
222
|
_ts_metadata3("design:type", Function),
|
|
223
223
|
_ts_metadata3("design:paramtypes", [
|
|
224
|
-
typeof
|
|
224
|
+
typeof Pick === "undefined" ? Object : Pick
|
|
225
225
|
])
|
|
226
226
|
], PrismaAuditStatsAdapter);
|
|
227
227
|
|
|
@@ -291,12 +291,149 @@ PrismaMfaAdapter = _ts_decorate5([
|
|
|
291
291
|
_ts_param4(0, Inject4(PRISMA_CLIENT_TOKEN)),
|
|
292
292
|
_ts_metadata4("design:type", Function),
|
|
293
293
|
_ts_metadata4("design:paramtypes", [
|
|
294
|
-
typeof
|
|
294
|
+
typeof Pick === "undefined" ? Object : Pick
|
|
295
295
|
])
|
|
296
296
|
], PrismaMfaAdapter);
|
|
297
297
|
|
|
298
298
|
// src/prisma-plan-catalog-import-sink.ts
|
|
299
|
-
import { Inject as Inject5, Injectable as Injectable6 } from "@nestjs/common";
|
|
299
|
+
import { Inject as Inject5, Injectable as Injectable6, Optional } from "@nestjs/common";
|
|
300
|
+
|
|
301
|
+
// src/prisma-plan-binding.ts
|
|
302
|
+
var PRISMA_SCHEMA_OPTIONS_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/adapter-prisma/PrismaSchemaOptions");
|
|
303
|
+
var DEFAULT_SCHEMA_OPTIONS = {
|
|
304
|
+
planBinding: {
|
|
305
|
+
mode: "legacy-plan-key"
|
|
306
|
+
},
|
|
307
|
+
delegates: {
|
|
308
|
+
catalogPlanVersion: "planVersion",
|
|
309
|
+
entitlementPlanVersion: "planVersion"
|
|
310
|
+
},
|
|
311
|
+
planVersionFields: {
|
|
312
|
+
catalog: {
|
|
313
|
+
validityWindows: false,
|
|
314
|
+
endsAt: false
|
|
315
|
+
},
|
|
316
|
+
entitlement: {
|
|
317
|
+
validityWindows: false,
|
|
318
|
+
endsAt: false
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
tenantSubscription: {
|
|
322
|
+
delegate: "subscription",
|
|
323
|
+
subscriptionBundleDelegate: false,
|
|
324
|
+
synchronizePlanVersion: false,
|
|
325
|
+
atomicOnboardingSelection: false,
|
|
326
|
+
activeVersionSelection: "latest-live",
|
|
327
|
+
withEndsAt: false
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
function resolvePrismaSchemaOptions(options) {
|
|
331
|
+
const mode = options?.planBinding?.mode ?? "legacy-plan-key";
|
|
332
|
+
const projectKey = options?.planBinding?.projectKey;
|
|
333
|
+
if (mode === "normalized-plan-id" && !projectKey?.trim()) {
|
|
334
|
+
throw new Error("Prisma plan binding mode 'normalized-plan-id' requires a non-empty projectKey.");
|
|
335
|
+
}
|
|
336
|
+
const sharedFields = options?.planVersionFields;
|
|
337
|
+
const catalogFields = sharedFields?.catalog;
|
|
338
|
+
const entitlementFields = sharedFields?.entitlement;
|
|
339
|
+
return {
|
|
340
|
+
planBinding: {
|
|
341
|
+
mode,
|
|
342
|
+
...projectKey ? {
|
|
343
|
+
projectKey
|
|
344
|
+
} : {}
|
|
345
|
+
},
|
|
346
|
+
delegates: {
|
|
347
|
+
catalogPlanVersion: options?.delegates?.catalogPlanVersion ?? DEFAULT_SCHEMA_OPTIONS.delegates.catalogPlanVersion,
|
|
348
|
+
entitlementPlanVersion: options?.delegates?.entitlementPlanVersion ?? DEFAULT_SCHEMA_OPTIONS.delegates.entitlementPlanVersion
|
|
349
|
+
},
|
|
350
|
+
planVersionFields: {
|
|
351
|
+
catalog: {
|
|
352
|
+
validityWindows: catalogFields?.validityWindows ?? sharedFields?.validityWindows ?? false,
|
|
353
|
+
endsAt: catalogFields?.endsAt ?? sharedFields?.endsAt ?? false
|
|
354
|
+
},
|
|
355
|
+
entitlement: {
|
|
356
|
+
validityWindows: entitlementFields?.validityWindows ?? sharedFields?.validityWindows ?? false,
|
|
357
|
+
endsAt: entitlementFields?.endsAt ?? sharedFields?.endsAt ?? false
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
tenantSubscription: {
|
|
361
|
+
delegate: options?.tenantSubscription?.delegate ?? DEFAULT_SCHEMA_OPTIONS.tenantSubscription.delegate,
|
|
362
|
+
subscriptionBundleDelegate: options?.tenantSubscription?.subscriptionBundleDelegate ?? DEFAULT_SCHEMA_OPTIONS.tenantSubscription.subscriptionBundleDelegate,
|
|
363
|
+
synchronizePlanVersion: options?.tenantSubscription?.synchronizePlanVersion ?? DEFAULT_SCHEMA_OPTIONS.tenantSubscription.synchronizePlanVersion,
|
|
364
|
+
atomicOnboardingSelection: options?.tenantSubscription?.atomicOnboardingSelection ?? DEFAULT_SCHEMA_OPTIONS.tenantSubscription.atomicOnboardingSelection,
|
|
365
|
+
activeVersionSelection: options?.tenantSubscription?.activeVersionSelection ?? DEFAULT_SCHEMA_OPTIONS.tenantSubscription.activeVersionSelection,
|
|
366
|
+
withEndsAt: options?.tenantSubscription?.withEndsAt ?? DEFAULT_SCHEMA_OPTIONS.tenantSubscription.withEndsAt
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
__name(resolvePrismaSchemaOptions, "resolvePrismaSchemaOptions");
|
|
371
|
+
function createPrismaPlanBindingResolver(options) {
|
|
372
|
+
const resolved = resolvePrismaSchemaOptions({
|
|
373
|
+
planBinding: options
|
|
374
|
+
}).planBinding;
|
|
375
|
+
return {
|
|
376
|
+
mode: resolved.mode,
|
|
377
|
+
projectKey: resolved.projectKey,
|
|
378
|
+
async toStoragePlanId(client, planKey, projectKey) {
|
|
379
|
+
if (resolved.mode === "legacy-plan-key") return planKey;
|
|
380
|
+
const scope = resolveProjectKey(resolved.projectKey, projectKey);
|
|
381
|
+
const plan = await asPlanIdentityClient(client).plan.findFirst({
|
|
382
|
+
where: {
|
|
383
|
+
projectKey: scope,
|
|
384
|
+
planKey,
|
|
385
|
+
deletedAt: null
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
if (!plan) {
|
|
389
|
+
throw new Error(`Plan '${planKey}' not found in project '${scope}'.`);
|
|
390
|
+
}
|
|
391
|
+
return plan.id;
|
|
392
|
+
},
|
|
393
|
+
async toPlanKey(client, storedPlanId, projectKey) {
|
|
394
|
+
if (resolved.mode === "legacy-plan-key") return storedPlanId;
|
|
395
|
+
const scope = resolveProjectKey(resolved.projectKey, projectKey);
|
|
396
|
+
const plan = await asPlanIdentityClient(client).plan.findUnique({
|
|
397
|
+
where: {
|
|
398
|
+
id: storedPlanId
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
if (!plan || plan.projectKey !== scope) {
|
|
402
|
+
throw new Error(`Plan id '${storedPlanId}' not found in project '${scope}'.`);
|
|
403
|
+
}
|
|
404
|
+
return plan.planKey;
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
__name(createPrismaPlanBindingResolver, "createPrismaPlanBindingResolver");
|
|
409
|
+
function getPrismaDelegate(client, delegateName) {
|
|
410
|
+
const delegate = client?.[delegateName];
|
|
411
|
+
if (!delegate || typeof delegate !== "object") {
|
|
412
|
+
throw new Error(`Prisma client has no '${delegateName}' delegate.`);
|
|
413
|
+
}
|
|
414
|
+
return delegate;
|
|
415
|
+
}
|
|
416
|
+
__name(getPrismaDelegate, "getPrismaDelegate");
|
|
417
|
+
function resolveProjectKey(configured, requested) {
|
|
418
|
+
const projectKey = requested ?? configured;
|
|
419
|
+
if (!projectKey) {
|
|
420
|
+
throw new Error("Prisma plan binding mode 'normalized-plan-id' requires a projectKey.");
|
|
421
|
+
}
|
|
422
|
+
if (configured && requested && configured !== requested) {
|
|
423
|
+
throw new Error(`Prisma plan binding is configured for project '${configured}', not '${requested}'.`);
|
|
424
|
+
}
|
|
425
|
+
return projectKey;
|
|
426
|
+
}
|
|
427
|
+
__name(resolveProjectKey, "resolveProjectKey");
|
|
428
|
+
function asPlanIdentityClient(client) {
|
|
429
|
+
if (!client || typeof client !== "object" || !("plan" in client)) {
|
|
430
|
+
throw new Error("Prisma client has no 'plan' delegate.");
|
|
431
|
+
}
|
|
432
|
+
return client;
|
|
433
|
+
}
|
|
434
|
+
__name(asPlanIdentityClient, "asPlanIdentityClient");
|
|
435
|
+
|
|
436
|
+
// src/prisma-plan-catalog-import-sink.ts
|
|
300
437
|
function _ts_decorate6(decorators, target, key, desc) {
|
|
301
438
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
302
439
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -319,11 +456,23 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
319
456
|
__name(this, "PrismaPlanCatalogImportSink");
|
|
320
457
|
}
|
|
321
458
|
prisma;
|
|
322
|
-
|
|
459
|
+
binding;
|
|
460
|
+
delegateName;
|
|
461
|
+
constructor(prisma, options) {
|
|
323
462
|
this.prisma = prisma;
|
|
463
|
+
const schema = resolvePrismaSchemaOptions(options);
|
|
464
|
+
this.binding = createPrismaPlanBindingResolver(options?.planBinding);
|
|
465
|
+
this.delegateName = schema.delegates.catalogPlanVersion;
|
|
466
|
+
}
|
|
467
|
+
db() {
|
|
468
|
+
return this.prisma;
|
|
324
469
|
}
|
|
325
470
|
async upsertPlan(input) {
|
|
326
|
-
|
|
471
|
+
if (this.binding.mode === "normalized-plan-id" && this.binding.projectKey !== input.projectKey) {
|
|
472
|
+
throw new Error(`Prisma plan binding is configured for project '${this.binding.projectKey}', not '${input.projectKey}'.`);
|
|
473
|
+
}
|
|
474
|
+
const db = this.db();
|
|
475
|
+
const existing = await db.plan.findFirst({
|
|
327
476
|
where: {
|
|
328
477
|
projectKey: input.projectKey,
|
|
329
478
|
planKey: input.planKey
|
|
@@ -333,7 +482,7 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
333
482
|
created: false,
|
|
334
483
|
skipReason: "exists"
|
|
335
484
|
};
|
|
336
|
-
await
|
|
485
|
+
await db.plan.create({
|
|
337
486
|
data: {
|
|
338
487
|
projectKey: input.projectKey,
|
|
339
488
|
planKey: input.planKey,
|
|
@@ -347,9 +496,11 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
347
496
|
};
|
|
348
497
|
}
|
|
349
498
|
async upsertPlanVersion(input) {
|
|
350
|
-
const
|
|
499
|
+
const planVersion = this.planVersions();
|
|
500
|
+
const storedPlanId = await this.binding.toStoragePlanId(this.prisma, input.planKey);
|
|
501
|
+
const existing = await planVersion.findFirst({
|
|
351
502
|
where: {
|
|
352
|
-
planId:
|
|
503
|
+
planId: storedPlanId,
|
|
353
504
|
version: input.version
|
|
354
505
|
}
|
|
355
506
|
});
|
|
@@ -359,9 +510,9 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
359
510
|
};
|
|
360
511
|
const now = /* @__PURE__ */ new Date();
|
|
361
512
|
if (input.publish) {
|
|
362
|
-
await
|
|
513
|
+
await planVersion.updateMany({
|
|
363
514
|
where: {
|
|
364
|
-
planId:
|
|
515
|
+
planId: storedPlanId,
|
|
365
516
|
publishedAt: {
|
|
366
517
|
not: null
|
|
367
518
|
},
|
|
@@ -375,9 +526,9 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
375
526
|
}
|
|
376
527
|
});
|
|
377
528
|
}
|
|
378
|
-
await
|
|
529
|
+
await planVersion.create({
|
|
379
530
|
data: {
|
|
380
|
-
planId:
|
|
531
|
+
planId: storedPlanId,
|
|
381
532
|
version: input.version,
|
|
382
533
|
features: input.features,
|
|
383
534
|
quotas: input.quotas,
|
|
@@ -392,8 +543,12 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
392
543
|
created: true
|
|
393
544
|
};
|
|
394
545
|
}
|
|
546
|
+
planVersions() {
|
|
547
|
+
return getPrismaDelegate(this.prisma, this.delegateName);
|
|
548
|
+
}
|
|
395
549
|
async upsertFeatureCatalogEntry(input) {
|
|
396
|
-
const
|
|
550
|
+
const db = this.db();
|
|
551
|
+
const existing = await db.featureCatalogEntry.findFirst({
|
|
397
552
|
where: {
|
|
398
553
|
projectKey: input.projectKey,
|
|
399
554
|
featureKey: input.featureKey
|
|
@@ -403,7 +558,7 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
403
558
|
created: false,
|
|
404
559
|
skipReason: "exists"
|
|
405
560
|
};
|
|
406
|
-
await
|
|
561
|
+
await db.featureCatalogEntry.create({
|
|
407
562
|
data: {
|
|
408
563
|
projectKey: input.projectKey,
|
|
409
564
|
featureKey: input.featureKey,
|
|
@@ -422,14 +577,17 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
422
577
|
PrismaPlanCatalogImportSink = _ts_decorate6([
|
|
423
578
|
Injectable6(),
|
|
424
579
|
_ts_param5(0, Inject5(PRISMA_CLIENT_TOKEN)),
|
|
580
|
+
_ts_param5(1, Optional()),
|
|
581
|
+
_ts_param5(1, Inject5(PRISMA_SCHEMA_OPTIONS_TOKEN)),
|
|
425
582
|
_ts_metadata5("design:type", Function),
|
|
426
583
|
_ts_metadata5("design:paramtypes", [
|
|
427
|
-
typeof
|
|
584
|
+
typeof PlanCatalogImportClient === "undefined" ? Object : PlanCatalogImportClient,
|
|
585
|
+
typeof PrismaSchemaOptions === "undefined" ? Object : PrismaSchemaOptions
|
|
428
586
|
])
|
|
429
587
|
], PrismaPlanCatalogImportSink);
|
|
430
588
|
|
|
431
589
|
// src/prisma-plan-catalog-read-sink.ts
|
|
432
|
-
import { Inject as Inject6, Injectable as Injectable7 } from "@nestjs/common";
|
|
590
|
+
import { Inject as Inject6, Injectable as Injectable7, Optional as Optional2 } from "@nestjs/common";
|
|
433
591
|
|
|
434
592
|
// src/tx.ts
|
|
435
593
|
function resolveClient(client, tx) {
|
|
@@ -471,11 +629,25 @@ var PrismaPlanCatalogReadSink = class {
|
|
|
471
629
|
__name(this, "PrismaPlanCatalogReadSink");
|
|
472
630
|
}
|
|
473
631
|
prisma;
|
|
474
|
-
|
|
632
|
+
binding;
|
|
633
|
+
delegateName;
|
|
634
|
+
fields;
|
|
635
|
+
constructor(prisma, options) {
|
|
475
636
|
this.prisma = prisma;
|
|
637
|
+
const schema = resolvePrismaSchemaOptions(options);
|
|
638
|
+
this.binding = createPrismaPlanBindingResolver(options?.planBinding);
|
|
639
|
+
this.delegateName = schema.delegates.catalogPlanVersion;
|
|
640
|
+
this.fields = schema.planVersionFields.catalog;
|
|
641
|
+
}
|
|
642
|
+
db() {
|
|
643
|
+
return this.prisma;
|
|
476
644
|
}
|
|
477
645
|
async loadSnapshot(projectKey) {
|
|
478
|
-
|
|
646
|
+
if (this.binding.mode === "normalized-plan-id" && this.binding.projectKey !== projectKey) {
|
|
647
|
+
throw new Error(`Prisma plan binding is configured for project '${this.binding.projectKey}', not '${projectKey}'.`);
|
|
648
|
+
}
|
|
649
|
+
const db = this.db();
|
|
650
|
+
const plans = await db.plan.findMany({
|
|
479
651
|
where: {
|
|
480
652
|
projectKey,
|
|
481
653
|
deletedAt: null
|
|
@@ -484,11 +656,17 @@ var PrismaPlanCatalogReadSink = class {
|
|
|
484
656
|
sortOrder: "asc"
|
|
485
657
|
}
|
|
486
658
|
});
|
|
487
|
-
const
|
|
488
|
-
|
|
659
|
+
const planKeysByStoredId = new Map(plans.map((plan) => [
|
|
660
|
+
this.binding.mode === "normalized-plan-id" ? plan.id : plan.planKey,
|
|
661
|
+
plan.planKey
|
|
662
|
+
]));
|
|
663
|
+
const storedPlanIds = [
|
|
664
|
+
...planKeysByStoredId.keys()
|
|
665
|
+
];
|
|
666
|
+
const livePlanVersions = storedPlanIds.length === 0 ? [] : await this.planVersions().findMany({
|
|
489
667
|
where: {
|
|
490
668
|
planId: {
|
|
491
|
-
in:
|
|
669
|
+
in: storedPlanIds
|
|
492
670
|
},
|
|
493
671
|
publishedAt: {
|
|
494
672
|
not: null
|
|
@@ -496,7 +674,7 @@ var PrismaPlanCatalogReadSink = class {
|
|
|
496
674
|
supersededAt: null
|
|
497
675
|
}
|
|
498
676
|
});
|
|
499
|
-
const featureEntries = await
|
|
677
|
+
const featureEntries = await db.featureCatalogEntry.findMany({
|
|
500
678
|
where: {
|
|
501
679
|
projectKey,
|
|
502
680
|
deletedAt: null
|
|
@@ -507,17 +685,29 @@ var PrismaPlanCatalogReadSink = class {
|
|
|
507
685
|
});
|
|
508
686
|
return {
|
|
509
687
|
plans: plans.map(toPlanRow),
|
|
510
|
-
livePlanVersions: livePlanVersions.map(
|
|
688
|
+
livePlanVersions: livePlanVersions.map((row) => {
|
|
689
|
+
const planKey = planKeysByStoredId.get(row.planId);
|
|
690
|
+
if (!planKey) {
|
|
691
|
+
throw new Error(`PlanVersion ${row.id} references plan '${row.planId}' outside project '${projectKey}'.`);
|
|
692
|
+
}
|
|
693
|
+
return toPlanVersionRow(row, planKey, this.fields);
|
|
694
|
+
}),
|
|
511
695
|
featureEntries: featureEntries.map(toFeatureCatalogEntryRow)
|
|
512
696
|
};
|
|
513
697
|
}
|
|
698
|
+
planVersions() {
|
|
699
|
+
return getPrismaDelegate(this.prisma, this.delegateName);
|
|
700
|
+
}
|
|
514
701
|
};
|
|
515
702
|
PrismaPlanCatalogReadSink = _ts_decorate7([
|
|
516
703
|
Injectable7(),
|
|
517
704
|
_ts_param6(0, Inject6(PRISMA_CLIENT_TOKEN)),
|
|
705
|
+
_ts_param6(1, Optional2()),
|
|
706
|
+
_ts_param6(1, Inject6(PRISMA_SCHEMA_OPTIONS_TOKEN)),
|
|
518
707
|
_ts_metadata6("design:type", Function),
|
|
519
708
|
_ts_metadata6("design:paramtypes", [
|
|
520
|
-
typeof
|
|
709
|
+
typeof PlanCatalogReadClient === "undefined" ? Object : PlanCatalogReadClient,
|
|
710
|
+
typeof PrismaSchemaOptions === "undefined" ? Object : PrismaSchemaOptions
|
|
521
711
|
])
|
|
522
712
|
], PrismaPlanCatalogReadSink);
|
|
523
713
|
function toPlanRow(row) {
|
|
@@ -535,10 +725,10 @@ function toPlanRow(row) {
|
|
|
535
725
|
};
|
|
536
726
|
}
|
|
537
727
|
__name(toPlanRow, "toPlanRow");
|
|
538
|
-
function toPlanVersionRow(row) {
|
|
539
|
-
|
|
728
|
+
function toPlanVersionRow(row, planKey, fields) {
|
|
729
|
+
const mapped = {
|
|
540
730
|
id: row.id,
|
|
541
|
-
planId:
|
|
731
|
+
planId: planKey,
|
|
542
732
|
version: row.version,
|
|
543
733
|
baseVersionId: row.baseVersionId,
|
|
544
734
|
publishedAt: row.publishedAt ? row.publishedAt.toISOString() : null,
|
|
@@ -546,8 +736,8 @@ function toPlanVersionRow(row) {
|
|
|
546
736
|
publishedChanges: row.publishedChanges ?? null,
|
|
547
737
|
changeNote: row.changeNote,
|
|
548
738
|
nonRegressive: row.nonRegressive,
|
|
549
|
-
validFrom: null,
|
|
550
|
-
validUntil: null,
|
|
739
|
+
validFrom: fields.validityWindows && row.validFrom ? row.validFrom.toISOString() : null,
|
|
740
|
+
validUntil: fields.validityWindows && row.validUntil ? row.validUntil.toISOString() : null,
|
|
551
741
|
createdByUserId: row.createdByUserId,
|
|
552
742
|
publishedByUserId: row.publishedByUserId,
|
|
553
743
|
createdAt: row.createdAt.toISOString(),
|
|
@@ -558,6 +748,10 @@ function toPlanVersionRow(row) {
|
|
|
558
748
|
yearlyNet: String(row.yearlyNet),
|
|
559
749
|
marketed: row.marketed
|
|
560
750
|
};
|
|
751
|
+
if (fields.endsAt) {
|
|
752
|
+
mapped.endsAt = row.endsAt?.toISOString() ?? null;
|
|
753
|
+
}
|
|
754
|
+
return mapped;
|
|
561
755
|
}
|
|
562
756
|
__name(toPlanVersionRow, "toPlanVersionRow");
|
|
563
757
|
function toFeatureCatalogEntryRow(row) {
|
|
@@ -590,7 +784,8 @@ function toFeatureCatalogEntryRow(row) {
|
|
|
590
784
|
__name(toFeatureCatalogEntryRow, "toFeatureCatalogEntryRow");
|
|
591
785
|
|
|
592
786
|
// src/prisma-plan-version.repository.ts
|
|
593
|
-
import { Inject as Inject7, Injectable as Injectable8 } from "@nestjs/common";
|
|
787
|
+
import { Inject as Inject7, Injectable as Injectable8, Optional as Optional3 } from "@nestjs/common";
|
|
788
|
+
import { buildActivePlanVersionWhere } from "@saasicat/types";
|
|
594
789
|
function _ts_decorate8(decorators, target, key, desc) {
|
|
595
790
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
596
791
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -613,14 +808,29 @@ var PrismaPlanVersionRepository = class {
|
|
|
613
808
|
__name(this, "PrismaPlanVersionRepository");
|
|
614
809
|
}
|
|
615
810
|
prisma;
|
|
616
|
-
|
|
811
|
+
findActive;
|
|
812
|
+
binding;
|
|
813
|
+
delegateName;
|
|
814
|
+
fields;
|
|
815
|
+
constructor(prisma, options) {
|
|
617
816
|
this.prisma = prisma;
|
|
817
|
+
const schema = resolvePrismaSchemaOptions(options);
|
|
818
|
+
this.binding = createPrismaPlanBindingResolver(options?.planBinding);
|
|
819
|
+
this.delegateName = schema.delegates.entitlementPlanVersion;
|
|
820
|
+
this.fields = schema.planVersionFields.entitlement;
|
|
821
|
+
if (this.fields.validityWindows) {
|
|
822
|
+
this.findActive = (planId, asOf = /* @__PURE__ */ new Date(), tx) => this.findActivePlanVersion(planId, asOf, tx);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
db(tx) {
|
|
826
|
+
return tx ?? this.prisma;
|
|
618
827
|
}
|
|
619
828
|
async findLatestLive(planId, tx) {
|
|
620
|
-
const db =
|
|
621
|
-
const
|
|
829
|
+
const db = this.db(tx);
|
|
830
|
+
const storedPlanId = await this.binding.toStoragePlanId(db, planId);
|
|
831
|
+
const row = await this.versions(db).findFirst({
|
|
622
832
|
where: {
|
|
623
|
-
planId,
|
|
833
|
+
planId: storedPlanId,
|
|
624
834
|
publishedAt: {
|
|
625
835
|
not: null
|
|
626
836
|
},
|
|
@@ -631,8 +841,40 @@ var PrismaPlanVersionRepository = class {
|
|
|
631
841
|
}
|
|
632
842
|
});
|
|
633
843
|
if (!row) return null;
|
|
844
|
+
return this.toRecord(db, row);
|
|
845
|
+
}
|
|
846
|
+
async findActivePlanVersion(planId, asOf, tx) {
|
|
847
|
+
const db = this.db(tx);
|
|
848
|
+
const storedPlanId = await this.binding.toStoragePlanId(db, planId);
|
|
849
|
+
const activeWhere = this.fields.endsAt ? buildActivePlanVersionWhere(asOf, {
|
|
850
|
+
withEndsAt: true
|
|
851
|
+
}) : buildActivePlanVersionWhere(asOf);
|
|
852
|
+
const row = await this.versions(db).findFirst({
|
|
853
|
+
where: {
|
|
854
|
+
planId: storedPlanId,
|
|
855
|
+
...activeWhere
|
|
856
|
+
},
|
|
857
|
+
orderBy: [
|
|
858
|
+
{
|
|
859
|
+
validFrom: {
|
|
860
|
+
sort: "desc",
|
|
861
|
+
nulls: "last"
|
|
862
|
+
}
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
version: "desc"
|
|
866
|
+
}
|
|
867
|
+
]
|
|
868
|
+
});
|
|
869
|
+
if (!row) return null;
|
|
870
|
+
return this.toRecord(db, row);
|
|
871
|
+
}
|
|
872
|
+
versions(client) {
|
|
873
|
+
return getPrismaDelegate(client, this.delegateName);
|
|
874
|
+
}
|
|
875
|
+
async toRecord(client, row) {
|
|
634
876
|
return {
|
|
635
|
-
planId: row.planId,
|
|
877
|
+
planId: await this.binding.toPlanKey(client, row.planId),
|
|
636
878
|
quotas: toQuotaMap(row.quotas),
|
|
637
879
|
features: toStringArray(row.features)
|
|
638
880
|
};
|
|
@@ -641,9 +883,12 @@ var PrismaPlanVersionRepository = class {
|
|
|
641
883
|
PrismaPlanVersionRepository = _ts_decorate8([
|
|
642
884
|
Injectable8(),
|
|
643
885
|
_ts_param7(0, Inject7(PRISMA_CLIENT_TOKEN)),
|
|
886
|
+
_ts_param7(1, Optional3()),
|
|
887
|
+
_ts_param7(1, Inject7(PRISMA_SCHEMA_OPTIONS_TOKEN)),
|
|
644
888
|
_ts_metadata7("design:type", Function),
|
|
645
889
|
_ts_metadata7("design:paramtypes", [
|
|
646
|
-
typeof
|
|
890
|
+
typeof PlanVersionRepositoryClient === "undefined" ? Object : PlanVersionRepositoryClient,
|
|
891
|
+
typeof PrismaSchemaOptions === "undefined" ? Object : PrismaSchemaOptions
|
|
647
892
|
])
|
|
648
893
|
], PrismaPlanVersionRepository);
|
|
649
894
|
|
|
@@ -1084,7 +1329,7 @@ PrismaPromoSubscriptionLookup = _ts_decorate12([
|
|
|
1084
1329
|
], PrismaPromoSubscriptionLookup);
|
|
1085
1330
|
|
|
1086
1331
|
// src/prisma-subscription.repository.ts
|
|
1087
|
-
import { Inject as Inject12, Injectable as Injectable13 } from "@nestjs/common";
|
|
1332
|
+
import { Inject as Inject12, Injectable as Injectable13, Optional as Optional4 } from "@nestjs/common";
|
|
1088
1333
|
function _ts_decorate13(decorators, target, key, desc) {
|
|
1089
1334
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1090
1335
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1111,19 +1356,35 @@ var PrismaSubscriptionRepository = class {
|
|
|
1111
1356
|
__name(this, "PrismaSubscriptionRepository");
|
|
1112
1357
|
}
|
|
1113
1358
|
prisma;
|
|
1114
|
-
|
|
1359
|
+
countByBundleVersionId;
|
|
1360
|
+
binding;
|
|
1361
|
+
planVersionDelegateName;
|
|
1362
|
+
subscriptionDelegateName;
|
|
1363
|
+
subscriptionBundleDelegateName;
|
|
1364
|
+
constructor(prisma, options) {
|
|
1115
1365
|
this.prisma = prisma;
|
|
1366
|
+
const schema = resolvePrismaSchemaOptions(options);
|
|
1367
|
+
this.binding = createPrismaPlanBindingResolver(options?.planBinding);
|
|
1368
|
+
this.planVersionDelegateName = schema.delegates.entitlementPlanVersion;
|
|
1369
|
+
this.subscriptionDelegateName = schema.tenantSubscription.delegate;
|
|
1370
|
+
this.subscriptionBundleDelegateName = schema.tenantSubscription.subscriptionBundleDelegate;
|
|
1371
|
+
if (this.subscriptionBundleDelegateName) {
|
|
1372
|
+
this.countByBundleVersionId = (bundleVersionId) => this.countActiveBundleBindings(bundleVersionId);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
db(tx) {
|
|
1376
|
+
return tx ?? this.prisma;
|
|
1116
1377
|
}
|
|
1117
1378
|
async findByTenantId(tenantId) {
|
|
1118
|
-
return this.loadByTenantId(this.
|
|
1379
|
+
return this.loadByTenantId(this.db(), tenantId);
|
|
1119
1380
|
}
|
|
1120
1381
|
async findByTenantIdLocked(tenantId, tx) {
|
|
1121
|
-
const db =
|
|
1382
|
+
const db = this.db(tx);
|
|
1122
1383
|
await db.$queryRaw`SELECT id FROM subscriptions WHERE "tenantId" = ${tenantId} FOR UPDATE`;
|
|
1123
1384
|
return this.loadByTenantId(db, tenantId);
|
|
1124
1385
|
}
|
|
1125
1386
|
async countByPlanVersionId(planVersionId) {
|
|
1126
|
-
return this.
|
|
1387
|
+
return this.subscriptions(this.db()).count({
|
|
1127
1388
|
where: {
|
|
1128
1389
|
OR: [
|
|
1129
1390
|
{
|
|
@@ -1136,22 +1397,72 @@ var PrismaSubscriptionRepository = class {
|
|
|
1136
1397
|
}
|
|
1137
1398
|
});
|
|
1138
1399
|
}
|
|
1139
|
-
async countActiveByPlanKey(
|
|
1140
|
-
const
|
|
1400
|
+
async countActiveByPlanKey(projectKey) {
|
|
1401
|
+
const db = this.db();
|
|
1402
|
+
const subscriptions = await this.subscriptions(db).findMany({
|
|
1141
1403
|
where: {
|
|
1142
1404
|
status: {
|
|
1143
1405
|
in: ACTIVE_STATUSES
|
|
1144
1406
|
}
|
|
1407
|
+
},
|
|
1408
|
+
select: {
|
|
1409
|
+
planVersionId: true
|
|
1410
|
+
}
|
|
1411
|
+
});
|
|
1412
|
+
const versionIds = [
|
|
1413
|
+
...new Set(subscriptions.map((subscription) => subscription.planVersionId))
|
|
1414
|
+
];
|
|
1415
|
+
if (versionIds.length === 0) return {};
|
|
1416
|
+
const planVersions = await this.planVersions(db).findMany({
|
|
1417
|
+
where: {
|
|
1418
|
+
id: {
|
|
1419
|
+
in: versionIds
|
|
1420
|
+
}
|
|
1421
|
+
},
|
|
1422
|
+
select: {
|
|
1423
|
+
id: true,
|
|
1424
|
+
planId: true
|
|
1145
1425
|
}
|
|
1146
1426
|
});
|
|
1427
|
+
const planVersionById = new Map(planVersions.map((planVersion) => [
|
|
1428
|
+
planVersion.id,
|
|
1429
|
+
planVersion
|
|
1430
|
+
]));
|
|
1431
|
+
const storedPlanIds = [
|
|
1432
|
+
...new Set(planVersions.map((planVersion) => planVersion.planId))
|
|
1433
|
+
];
|
|
1434
|
+
const planKeyByStoredId = await this.planKeysForProject(db, storedPlanIds, projectKey);
|
|
1147
1435
|
const counts = {};
|
|
1148
|
-
for (const
|
|
1149
|
-
|
|
1436
|
+
for (const subscription of subscriptions) {
|
|
1437
|
+
const planVersion = planVersionById.get(subscription.planVersionId);
|
|
1438
|
+
const planKey = planVersion ? planKeyByStoredId.get(planVersion.planId) : void 0;
|
|
1439
|
+
if (!planKey) continue;
|
|
1440
|
+
counts[planKey] = (counts[planKey] ?? 0) + 1;
|
|
1150
1441
|
}
|
|
1151
1442
|
return counts;
|
|
1152
1443
|
}
|
|
1444
|
+
async countActiveBundleBindings(bundleVersionId) {
|
|
1445
|
+
if (!this.subscriptionBundleDelegateName) {
|
|
1446
|
+
throw new Error("SubscriptionBundle counting is not configured.");
|
|
1447
|
+
}
|
|
1448
|
+
return getPrismaDelegate(this.prisma, this.subscriptionBundleDelegateName).count({
|
|
1449
|
+
where: {
|
|
1450
|
+
bundleVersionId,
|
|
1451
|
+
OR: [
|
|
1452
|
+
{
|
|
1453
|
+
canceledAt: null
|
|
1454
|
+
},
|
|
1455
|
+
{
|
|
1456
|
+
canceledEffectiveAt: {
|
|
1457
|
+
gt: /* @__PURE__ */ new Date()
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
]
|
|
1461
|
+
}
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1153
1464
|
async loadByTenantId(db, tenantId) {
|
|
1154
|
-
const row = await db.
|
|
1465
|
+
const row = await this.subscriptions(db).findUnique({
|
|
1155
1466
|
where: {
|
|
1156
1467
|
tenantId
|
|
1157
1468
|
}
|
|
@@ -1160,7 +1471,7 @@ var PrismaSubscriptionRepository = class {
|
|
|
1160
1471
|
return this.toRecord(db, row);
|
|
1161
1472
|
}
|
|
1162
1473
|
async toRecord(db, row) {
|
|
1163
|
-
const planVersion = await db.
|
|
1474
|
+
const planVersion = await this.planVersions(db).findUnique({
|
|
1164
1475
|
where: {
|
|
1165
1476
|
id: row.planVersionId
|
|
1166
1477
|
}
|
|
@@ -1168,10 +1479,13 @@ var PrismaSubscriptionRepository = class {
|
|
|
1168
1479
|
if (!planVersion) {
|
|
1169
1480
|
throw new Error(`Subscription ${row.id} references missing PlanVersion ${row.planVersionId}.`);
|
|
1170
1481
|
}
|
|
1482
|
+
const planKey = await this.binding.toPlanKey(db, planVersion.planId);
|
|
1171
1483
|
return {
|
|
1172
1484
|
id: row.id,
|
|
1173
1485
|
tenantId: row.tenantId,
|
|
1174
|
-
|
|
1486
|
+
// The concrete PlanVersion is authoritative. This also normalizes
|
|
1487
|
+
// legacy rows whose denormalized `Subscription.plan` drifted.
|
|
1488
|
+
plan: planKey,
|
|
1175
1489
|
status: row.status,
|
|
1176
1490
|
isPilot: row.isPilot,
|
|
1177
1491
|
trialEntitlementPlan: row.trialEntitlementPlan,
|
|
@@ -1180,19 +1494,78 @@ var PrismaSubscriptionRepository = class {
|
|
|
1180
1494
|
customLimits: row.customLimits ?? null,
|
|
1181
1495
|
planVersionId: row.planVersionId,
|
|
1182
1496
|
planVersion: {
|
|
1183
|
-
planId:
|
|
1497
|
+
planId: planKey,
|
|
1184
1498
|
quotas: toQuotaMap(planVersion.quotas),
|
|
1185
1499
|
features: toStringArray(planVersion.features)
|
|
1186
1500
|
}
|
|
1187
1501
|
};
|
|
1188
1502
|
}
|
|
1503
|
+
planVersions(client) {
|
|
1504
|
+
return getPrismaDelegate(client, this.planVersionDelegateName);
|
|
1505
|
+
}
|
|
1506
|
+
subscriptions(client) {
|
|
1507
|
+
return getPrismaDelegate(client, this.subscriptionDelegateName);
|
|
1508
|
+
}
|
|
1509
|
+
async planKeysForProject(db, storedPlanIds, projectKey) {
|
|
1510
|
+
if (storedPlanIds.length === 0) return /* @__PURE__ */ new Map();
|
|
1511
|
+
if (this.binding.mode === "normalized-plan-id") {
|
|
1512
|
+
if (this.binding.projectKey && this.binding.projectKey !== projectKey) {
|
|
1513
|
+
throw new Error(`Prisma plan binding is configured for project '${this.binding.projectKey}', not '${projectKey}'.`);
|
|
1514
|
+
}
|
|
1515
|
+
const plans2 = await db.plan.findMany({
|
|
1516
|
+
where: {
|
|
1517
|
+
projectKey,
|
|
1518
|
+
id: {
|
|
1519
|
+
in: storedPlanIds
|
|
1520
|
+
}
|
|
1521
|
+
},
|
|
1522
|
+
select: {
|
|
1523
|
+
id: true,
|
|
1524
|
+
planKey: true
|
|
1525
|
+
}
|
|
1526
|
+
});
|
|
1527
|
+
return new Map(plans2.map((plan) => [
|
|
1528
|
+
plan.id,
|
|
1529
|
+
plan.planKey
|
|
1530
|
+
]));
|
|
1531
|
+
}
|
|
1532
|
+
const plans = await db.plan.findMany({
|
|
1533
|
+
where: {
|
|
1534
|
+
planKey: {
|
|
1535
|
+
in: storedPlanIds
|
|
1536
|
+
}
|
|
1537
|
+
},
|
|
1538
|
+
select: {
|
|
1539
|
+
projectKey: true,
|
|
1540
|
+
planKey: true
|
|
1541
|
+
}
|
|
1542
|
+
});
|
|
1543
|
+
const projectsByPlanKey = /* @__PURE__ */ new Map();
|
|
1544
|
+
for (const plan of plans) {
|
|
1545
|
+
const projects = projectsByPlanKey.get(plan.planKey) ?? /* @__PURE__ */ new Set();
|
|
1546
|
+
projects.add(plan.projectKey);
|
|
1547
|
+
projectsByPlanKey.set(plan.planKey, projects);
|
|
1548
|
+
}
|
|
1549
|
+
return new Map(storedPlanIds.flatMap((planKey) => {
|
|
1550
|
+
const projects = projectsByPlanKey.get(planKey);
|
|
1551
|
+
return projects?.size === 1 && projects.has(projectKey) ? [
|
|
1552
|
+
[
|
|
1553
|
+
planKey,
|
|
1554
|
+
planKey
|
|
1555
|
+
]
|
|
1556
|
+
] : [];
|
|
1557
|
+
}));
|
|
1558
|
+
}
|
|
1189
1559
|
};
|
|
1190
1560
|
PrismaSubscriptionRepository = _ts_decorate13([
|
|
1191
1561
|
Injectable13(),
|
|
1192
1562
|
_ts_param12(0, Inject12(PRISMA_CLIENT_TOKEN)),
|
|
1563
|
+
_ts_param12(1, Optional4()),
|
|
1564
|
+
_ts_param12(1, Inject12(PRISMA_SCHEMA_OPTIONS_TOKEN)),
|
|
1193
1565
|
_ts_metadata12("design:type", Function),
|
|
1194
1566
|
_ts_metadata12("design:paramtypes", [
|
|
1195
|
-
typeof
|
|
1567
|
+
typeof SubscriptionRepositoryClient === "undefined" ? Object : SubscriptionRepositoryClient,
|
|
1568
|
+
typeof PrismaSchemaOptions === "undefined" ? Object : PrismaSchemaOptions
|
|
1196
1569
|
])
|
|
1197
1570
|
], PrismaSubscriptionRepository);
|
|
1198
1571
|
|
|
@@ -1304,7 +1677,8 @@ var PrismaTransactionRunner = class {
|
|
|
1304
1677
|
this.prisma = prisma;
|
|
1305
1678
|
}
|
|
1306
1679
|
async run(fn) {
|
|
1307
|
-
|
|
1680
|
+
const transaction = this.prisma.$transaction.bind(this.prisma);
|
|
1681
|
+
return transaction((tx) => fn(tx));
|
|
1308
1682
|
}
|
|
1309
1683
|
};
|
|
1310
1684
|
PrismaTransactionRunner = _ts_decorate15([
|
|
@@ -1312,7 +1686,7 @@ PrismaTransactionRunner = _ts_decorate15([
|
|
|
1312
1686
|
_ts_param14(0, Inject14(PRISMA_CLIENT_TOKEN)),
|
|
1313
1687
|
_ts_metadata14("design:type", Function),
|
|
1314
1688
|
_ts_metadata14("design:paramtypes", [
|
|
1315
|
-
typeof
|
|
1689
|
+
typeof Record === "undefined" ? Object : Record
|
|
1316
1690
|
])
|
|
1317
1691
|
], PrismaTransactionRunner);
|
|
1318
1692
|
|
|
@@ -1363,8 +1737,8 @@ function prismaPersistence(options) {
|
|
|
1363
1737
|
superAdminProvisioning: buildProvisioning(client, options.passwordHasher)
|
|
1364
1738
|
},
|
|
1365
1739
|
entitlement: {
|
|
1366
|
-
subscriptionRepository: provide((prisma) => new PrismaSubscriptionRepository(prisma)),
|
|
1367
|
-
planVersionRepository: provide((prisma) => new PrismaPlanVersionRepository(prisma))
|
|
1740
|
+
subscriptionRepository: provide((prisma) => new PrismaSubscriptionRepository(prisma, options.schema)),
|
|
1741
|
+
planVersionRepository: provide((prisma) => new PrismaPlanVersionRepository(prisma, options.schema))
|
|
1368
1742
|
},
|
|
1369
1743
|
promo: {
|
|
1370
1744
|
promoCodeRepository: provide((prisma) => new PrismaPromoCodeRepository(prisma)),
|
|
@@ -1373,8 +1747,8 @@ function prismaPersistence(options) {
|
|
|
1373
1747
|
subscriptionLookup: provide((prisma) => new PrismaPromoSubscriptionLookup(prisma)),
|
|
1374
1748
|
revenueAggregator: new ZeroPromoRevenueDeductionAggregator()
|
|
1375
1749
|
},
|
|
1376
|
-
planCatalogReadSink: provide((prisma) => new PrismaPlanCatalogReadSink(prisma)),
|
|
1377
|
-
planCatalogImportSink: provide((prisma) => new PrismaPlanCatalogImportSink(prisma))
|
|
1750
|
+
planCatalogReadSink: provide((prisma) => new PrismaPlanCatalogReadSink(prisma, options.schema)),
|
|
1751
|
+
planCatalogImportSink: provide((prisma) => new PrismaPlanCatalogImportSink(prisma, options.schema))
|
|
1378
1752
|
};
|
|
1379
1753
|
}
|
|
1380
1754
|
__name(prismaPersistence, "prismaPersistence");
|
|
@@ -1443,7 +1817,7 @@ var PrismaSubscriptionBundleRepository = class {
|
|
|
1443
1817
|
this.prisma = prisma;
|
|
1444
1818
|
}
|
|
1445
1819
|
db(tx) {
|
|
1446
|
-
return
|
|
1820
|
+
return tx ?? this.prisma;
|
|
1447
1821
|
}
|
|
1448
1822
|
async listBySubscription(subscriptionId) {
|
|
1449
1823
|
const rows = await this.db().subscriptionBundle.findMany({
|
|
@@ -1543,7 +1917,7 @@ PrismaSubscriptionBundleRepository = _ts_decorate17([
|
|
|
1543
1917
|
_ts_param15(0, Inject15(PRISMA_CLIENT_TOKEN)),
|
|
1544
1918
|
_ts_metadata15("design:type", Function),
|
|
1545
1919
|
_ts_metadata15("design:paramtypes", [
|
|
1546
|
-
typeof
|
|
1920
|
+
typeof SubscriptionBundleClient === "undefined" ? Object : SubscriptionBundleClient
|
|
1547
1921
|
])
|
|
1548
1922
|
], PrismaSubscriptionBundleRepository);
|
|
1549
1923
|
function toRecord3(row) {
|
|
@@ -1562,7 +1936,8 @@ function toRecord3(row) {
|
|
|
1562
1936
|
__name(toRecord3, "toRecord");
|
|
1563
1937
|
|
|
1564
1938
|
// src/prisma-tenant-subscription-write.adapter.ts
|
|
1565
|
-
import { Inject as Inject16, Injectable as Injectable18 } from "@nestjs/common";
|
|
1939
|
+
import { Inject as Inject16, Injectable as Injectable18, Optional as Optional5 } from "@nestjs/common";
|
|
1940
|
+
import { buildActivePlanVersionWhere as buildActivePlanVersionWhere2 } from "@saasicat/types";
|
|
1566
1941
|
function _ts_decorate18(decorators, target, key, desc) {
|
|
1567
1942
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1568
1943
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1585,37 +1960,66 @@ var PrismaTenantSubscriptionWriteAdapter = class {
|
|
|
1585
1960
|
__name(this, "PrismaTenantSubscriptionWriteAdapter");
|
|
1586
1961
|
}
|
|
1587
1962
|
prisma;
|
|
1588
|
-
|
|
1963
|
+
applyOnboardingSelection;
|
|
1964
|
+
schema;
|
|
1965
|
+
planBinding;
|
|
1966
|
+
constructor(prisma, options) {
|
|
1589
1967
|
this.prisma = prisma;
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1968
|
+
this.schema = resolvePrismaSchemaOptions(options);
|
|
1969
|
+
this.planBinding = createPrismaPlanBindingResolver(options?.planBinding);
|
|
1970
|
+
this.assertConfiguration();
|
|
1971
|
+
if (this.schema.tenantSubscription.atomicOnboardingSelection) {
|
|
1972
|
+
this.applyOnboardingSelection = (tenantId, input, redeemPromo) => this.applyOnboardingSelectionAtomic(tenantId, input, redeemPromo);
|
|
1973
|
+
}
|
|
1593
1974
|
}
|
|
1594
1975
|
async changePlanImmediate(tenantId, input) {
|
|
1595
|
-
|
|
1976
|
+
if (this.schema.tenantSubscription.synchronizePlanVersion) {
|
|
1977
|
+
return this.prisma.$transaction((tx) => this.changePlanImmediateInClient(tx, tenantId, input));
|
|
1978
|
+
}
|
|
1979
|
+
return this.changePlanImmediateInClient(this.prisma, tenantId, input);
|
|
1980
|
+
}
|
|
1981
|
+
async changePlanImmediateInClient(client, tenantId, input) {
|
|
1982
|
+
const subscription = this.subscription(client);
|
|
1983
|
+
const data = {
|
|
1984
|
+
plan: input.planId,
|
|
1985
|
+
billingCycle: input.cycle,
|
|
1986
|
+
pendingPlan: null,
|
|
1987
|
+
pendingBillingCycle: null,
|
|
1988
|
+
pendingEffectiveAt: null,
|
|
1989
|
+
...input.nextStatus ? {
|
|
1990
|
+
status: input.nextStatus
|
|
1991
|
+
} : {},
|
|
1992
|
+
...input.periodStart && input.periodEnd ? {
|
|
1993
|
+
currentPeriodStart: input.periodStart,
|
|
1994
|
+
currentPeriodEnd: input.periodEnd
|
|
1995
|
+
} : {},
|
|
1996
|
+
// #17: the platform changePlan path computes the carried-over
|
|
1997
|
+
// trial end and passes it through; null/undefined leaves the
|
|
1998
|
+
// existing trialEndsAt untouched.
|
|
1999
|
+
...input.trialEndsAt ? {
|
|
2000
|
+
trialEndsAt: input.trialEndsAt
|
|
2001
|
+
} : {}
|
|
2002
|
+
};
|
|
2003
|
+
if (this.schema.tenantSubscription.synchronizePlanVersion) {
|
|
2004
|
+
const current = await subscription.findUnique({
|
|
2005
|
+
where: {
|
|
2006
|
+
tenantId
|
|
2007
|
+
}
|
|
2008
|
+
});
|
|
2009
|
+
if (!current) {
|
|
2010
|
+
throw new Error(`No subscription for tenant ${tenantId}.`);
|
|
2011
|
+
}
|
|
2012
|
+
const storagePlanId = await this.planBinding.toStoragePlanId(client, input.planId);
|
|
2013
|
+
data.planVersionId = await this.findTargetPlanVersionId(client, storagePlanId, input.periodStart ?? /* @__PURE__ */ new Date());
|
|
2014
|
+
if (await this.pendingVersionBelongsToAnotherPlan(client, current.pendingPlanVersionId, storagePlanId)) {
|
|
2015
|
+
Object.assign(data, clearedPendingVersionData());
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
const updated = await subscription.update({
|
|
1596
2019
|
where: {
|
|
1597
2020
|
tenantId
|
|
1598
2021
|
},
|
|
1599
|
-
data
|
|
1600
|
-
plan: input.planId,
|
|
1601
|
-
billingCycle: input.cycle,
|
|
1602
|
-
pendingPlan: null,
|
|
1603
|
-
pendingBillingCycle: null,
|
|
1604
|
-
pendingEffectiveAt: null,
|
|
1605
|
-
...input.nextStatus ? {
|
|
1606
|
-
status: input.nextStatus
|
|
1607
|
-
} : {},
|
|
1608
|
-
...input.periodStart && input.periodEnd ? {
|
|
1609
|
-
currentPeriodStart: input.periodStart,
|
|
1610
|
-
currentPeriodEnd: input.periodEnd
|
|
1611
|
-
} : {},
|
|
1612
|
-
// #17: the platform changePlan path computes the carried-over
|
|
1613
|
-
// trial end and passes it through; null/undefined leaves the
|
|
1614
|
-
// existing trialEndsAt untouched.
|
|
1615
|
-
...input.trialEndsAt ? {
|
|
1616
|
-
trialEndsAt: input.trialEndsAt
|
|
1617
|
-
} : {}
|
|
1618
|
-
}
|
|
2022
|
+
data
|
|
1619
2023
|
});
|
|
1620
2024
|
return {
|
|
1621
2025
|
plan: updated.plan,
|
|
@@ -1623,7 +2027,7 @@ var PrismaTenantSubscriptionWriteAdapter = class {
|
|
|
1623
2027
|
};
|
|
1624
2028
|
}
|
|
1625
2029
|
async schedulePlanChange(tenantId, input) {
|
|
1626
|
-
await this.
|
|
2030
|
+
await this.subscription(this.prisma).update({
|
|
1627
2031
|
where: {
|
|
1628
2032
|
tenantId
|
|
1629
2033
|
},
|
|
@@ -1635,7 +2039,8 @@ var PrismaTenantSubscriptionWriteAdapter = class {
|
|
|
1635
2039
|
});
|
|
1636
2040
|
}
|
|
1637
2041
|
async acceptPendingPlanVersion(tenantId, userId, now) {
|
|
1638
|
-
const
|
|
2042
|
+
const subscription = this.subscription(this.prisma);
|
|
2043
|
+
const sub = await subscription.findUnique({
|
|
1639
2044
|
where: {
|
|
1640
2045
|
tenantId
|
|
1641
2046
|
}
|
|
@@ -1643,17 +2048,15 @@ var PrismaTenantSubscriptionWriteAdapter = class {
|
|
|
1643
2048
|
if (!sub) {
|
|
1644
2049
|
throw new Error(`No subscription for tenant ${tenantId}.`);
|
|
1645
2050
|
}
|
|
1646
|
-
if (sub.
|
|
1647
|
-
|
|
1648
|
-
accepted: true,
|
|
1649
|
-
acceptedAt: sub.pendingPlanVersionAcceptedAt,
|
|
1650
|
-
effectiveAt: sub.pendingPlanVersionEffectiveAt,
|
|
1651
|
-
alreadyAccepted: true
|
|
1652
|
-
};
|
|
2051
|
+
if (!sub.pendingPlanVersionId) {
|
|
2052
|
+
throw new Error(`No pending PlanVersion for tenant ${tenantId}.`);
|
|
1653
2053
|
}
|
|
1654
|
-
const
|
|
2054
|
+
const pendingPlanVersionId = sub.pendingPlanVersionId;
|
|
2055
|
+
const claimed = await subscription.updateMany({
|
|
1655
2056
|
where: {
|
|
1656
|
-
id: sub.id
|
|
2057
|
+
id: sub.id,
|
|
2058
|
+
pendingPlanVersionId,
|
|
2059
|
+
pendingPlanVersionAccepted: false
|
|
1657
2060
|
},
|
|
1658
2061
|
data: {
|
|
1659
2062
|
pendingPlanVersionAccepted: true,
|
|
@@ -1661,15 +2064,66 @@ var PrismaTenantSubscriptionWriteAdapter = class {
|
|
|
1661
2064
|
pendingPlanVersionAcceptedByUserId: userId
|
|
1662
2065
|
}
|
|
1663
2066
|
});
|
|
2067
|
+
const updated = await subscription.findUnique({
|
|
2068
|
+
where: {
|
|
2069
|
+
id: sub.id
|
|
2070
|
+
}
|
|
2071
|
+
});
|
|
2072
|
+
if (!updated) {
|
|
2073
|
+
throw new Error(`No subscription for tenant ${tenantId}.`);
|
|
2074
|
+
}
|
|
2075
|
+
if (claimed.count === 0 && (updated.pendingPlanVersionId !== pendingPlanVersionId || !updated.pendingPlanVersionAccepted)) {
|
|
2076
|
+
throw new Error(`Pending PlanVersion changed while accepting it for tenant ${tenantId}.`);
|
|
2077
|
+
}
|
|
1664
2078
|
return {
|
|
1665
2079
|
accepted: true,
|
|
1666
2080
|
acceptedAt: updated.pendingPlanVersionAcceptedAt,
|
|
1667
2081
|
effectiveAt: updated.pendingPlanVersionEffectiveAt,
|
|
1668
|
-
alreadyAccepted:
|
|
2082
|
+
alreadyAccepted: claimed.count === 0
|
|
1669
2083
|
};
|
|
1670
2084
|
}
|
|
2085
|
+
async applyOnboardingSelectionAtomic(tenantId, input, redeemPromo) {
|
|
2086
|
+
return this.prisma.$transaction(async (tx) => {
|
|
2087
|
+
const data = {
|
|
2088
|
+
plan: input.planId,
|
|
2089
|
+
billingCycle: input.cycle,
|
|
2090
|
+
pendingPlan: null,
|
|
2091
|
+
pendingBillingCycle: null,
|
|
2092
|
+
pendingEffectiveAt: null,
|
|
2093
|
+
...clearedPendingVersionData(),
|
|
2094
|
+
...input.nextStatus ? {
|
|
2095
|
+
status: input.nextStatus
|
|
2096
|
+
} : {},
|
|
2097
|
+
...input.periodStart && input.periodEnd ? {
|
|
2098
|
+
currentPeriodStart: input.periodStart,
|
|
2099
|
+
currentPeriodEnd: input.periodEnd
|
|
2100
|
+
} : {}
|
|
2101
|
+
};
|
|
2102
|
+
if (this.schema.tenantSubscription.synchronizePlanVersion) {
|
|
2103
|
+
const storagePlanId = await this.planBinding.toStoragePlanId(tx, input.planId);
|
|
2104
|
+
data.planVersionId = await this.findTargetPlanVersionId(tx, storagePlanId, input.periodStart ?? /* @__PURE__ */ new Date());
|
|
2105
|
+
}
|
|
2106
|
+
const updated = await this.subscription(tx).update({
|
|
2107
|
+
where: {
|
|
2108
|
+
tenantId
|
|
2109
|
+
},
|
|
2110
|
+
data
|
|
2111
|
+
});
|
|
2112
|
+
let promoRedemption = null;
|
|
2113
|
+
if (redeemPromo) {
|
|
2114
|
+
promoRedemption = await redeemPromo(tx, updated.id);
|
|
2115
|
+
}
|
|
2116
|
+
return {
|
|
2117
|
+
plan: updated.plan,
|
|
2118
|
+
billingCycle: updated.billingCycle,
|
|
2119
|
+
subscriptionId: updated.id,
|
|
2120
|
+
promoRedemption
|
|
2121
|
+
};
|
|
2122
|
+
});
|
|
2123
|
+
}
|
|
1671
2124
|
async cancelSubscription(tenantId, immediate, now) {
|
|
1672
|
-
const
|
|
2125
|
+
const subscription = this.subscription(this.prisma);
|
|
2126
|
+
const sub = await subscription.findUnique({
|
|
1673
2127
|
where: {
|
|
1674
2128
|
tenantId
|
|
1675
2129
|
}
|
|
@@ -1678,7 +2132,7 @@ var PrismaTenantSubscriptionWriteAdapter = class {
|
|
|
1678
2132
|
throw new Error(`No subscription for tenant ${tenantId}.`);
|
|
1679
2133
|
}
|
|
1680
2134
|
const canceledAt = immediate ? now : sub.currentPeriodEnd ?? now;
|
|
1681
|
-
const updated = await
|
|
2135
|
+
const updated = await subscription.update({
|
|
1682
2136
|
where: {
|
|
1683
2137
|
tenantId
|
|
1684
2138
|
},
|
|
@@ -1692,18 +2146,107 @@ var PrismaTenantSubscriptionWriteAdapter = class {
|
|
|
1692
2146
|
status: updated.status
|
|
1693
2147
|
};
|
|
1694
2148
|
}
|
|
2149
|
+
subscription(client) {
|
|
2150
|
+
return getPrismaDelegate(client, this.schema.tenantSubscription.delegate);
|
|
2151
|
+
}
|
|
2152
|
+
planVersions(client) {
|
|
2153
|
+
return getPrismaDelegate(client, this.schema.delegates.entitlementPlanVersion);
|
|
2154
|
+
}
|
|
2155
|
+
async findTargetPlanVersionId(client, storagePlanId, asOf) {
|
|
2156
|
+
const activeWindow = this.schema.tenantSubscription.activeVersionSelection === "validity-window";
|
|
2157
|
+
const activeVersionWhere = this.schema.tenantSubscription.withEndsAt ? buildActivePlanVersionWhere2(asOf, {
|
|
2158
|
+
withEndsAt: true
|
|
2159
|
+
}) : buildActivePlanVersionWhere2(asOf);
|
|
2160
|
+
const where = activeWindow ? {
|
|
2161
|
+
planId: storagePlanId,
|
|
2162
|
+
...activeVersionWhere
|
|
2163
|
+
} : {
|
|
2164
|
+
planId: storagePlanId,
|
|
2165
|
+
publishedAt: {
|
|
2166
|
+
not: null
|
|
2167
|
+
},
|
|
2168
|
+
supersededAt: null,
|
|
2169
|
+
...this.schema.tenantSubscription.withEndsAt ? {
|
|
2170
|
+
OR: [
|
|
2171
|
+
{
|
|
2172
|
+
endsAt: null
|
|
2173
|
+
},
|
|
2174
|
+
{
|
|
2175
|
+
endsAt: {
|
|
2176
|
+
gt: asOf
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
]
|
|
2180
|
+
} : {}
|
|
2181
|
+
};
|
|
2182
|
+
const target = await this.planVersions(client).findFirst({
|
|
2183
|
+
where,
|
|
2184
|
+
orderBy: activeWindow ? [
|
|
2185
|
+
{
|
|
2186
|
+
validFrom: {
|
|
2187
|
+
sort: "desc",
|
|
2188
|
+
nulls: "last"
|
|
2189
|
+
}
|
|
2190
|
+
},
|
|
2191
|
+
{
|
|
2192
|
+
version: "desc"
|
|
2193
|
+
}
|
|
2194
|
+
] : {
|
|
2195
|
+
version: "desc"
|
|
2196
|
+
}
|
|
2197
|
+
});
|
|
2198
|
+
if (!target) {
|
|
2199
|
+
throw new Error(`No active PlanVersion for plan '${storagePlanId}'.`);
|
|
2200
|
+
}
|
|
2201
|
+
return target.id;
|
|
2202
|
+
}
|
|
2203
|
+
async pendingVersionBelongsToAnotherPlan(client, pendingPlanVersionId, targetStoragePlanId) {
|
|
2204
|
+
if (!pendingPlanVersionId) return false;
|
|
2205
|
+
const pending = await this.planVersions(client).findUnique({
|
|
2206
|
+
where: {
|
|
2207
|
+
id: pendingPlanVersionId
|
|
2208
|
+
}
|
|
2209
|
+
});
|
|
2210
|
+
return !pending || pending.planId !== targetStoragePlanId;
|
|
2211
|
+
}
|
|
2212
|
+
assertConfiguration() {
|
|
2213
|
+
if (!this.schema.tenantSubscription.synchronizePlanVersion) return;
|
|
2214
|
+
const entitlementFields = this.schema.planVersionFields.entitlement;
|
|
2215
|
+
if (this.schema.tenantSubscription.activeVersionSelection === "validity-window" && !entitlementFields.validityWindows) {
|
|
2216
|
+
throw new Error("tenantSubscription.activeVersionSelection='validity-window' requires planVersionFields.entitlement.validityWindows=true.");
|
|
2217
|
+
}
|
|
2218
|
+
if (this.schema.tenantSubscription.withEndsAt && !entitlementFields.endsAt) {
|
|
2219
|
+
throw new Error("tenantSubscription.withEndsAt=true requires planVersionFields.entitlement.endsAt=true.");
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
1695
2222
|
};
|
|
1696
2223
|
PrismaTenantSubscriptionWriteAdapter = _ts_decorate18([
|
|
1697
2224
|
Injectable18(),
|
|
1698
2225
|
_ts_param16(0, Inject16(PRISMA_CLIENT_TOKEN)),
|
|
2226
|
+
_ts_param16(1, Optional5()),
|
|
2227
|
+
_ts_param16(1, Inject16(PRISMA_SCHEMA_OPTIONS_TOKEN)),
|
|
1699
2228
|
_ts_metadata16("design:type", Function),
|
|
1700
2229
|
_ts_metadata16("design:paramtypes", [
|
|
1701
|
-
typeof
|
|
2230
|
+
typeof TransactionalPrismaClient === "undefined" ? Object : TransactionalPrismaClient,
|
|
2231
|
+
typeof PrismaSchemaOptions === "undefined" ? Object : PrismaSchemaOptions
|
|
1702
2232
|
])
|
|
1703
2233
|
], PrismaTenantSubscriptionWriteAdapter);
|
|
2234
|
+
function clearedPendingVersionData() {
|
|
2235
|
+
return {
|
|
2236
|
+
pendingPlanVersionId: null,
|
|
2237
|
+
pendingPlanVersionEffectiveAt: null,
|
|
2238
|
+
pendingPlanVersionAccepted: false,
|
|
2239
|
+
pendingPlanVersionAcceptedAt: null,
|
|
2240
|
+
pendingPlanVersionAcceptedByUserId: null,
|
|
2241
|
+
pendingPlanVersionNotifiedAt: null,
|
|
2242
|
+
pendingPlanVersionReminderSentAt: null
|
|
2243
|
+
};
|
|
2244
|
+
}
|
|
2245
|
+
__name(clearedPendingVersionData, "clearedPendingVersionData");
|
|
1704
2246
|
|
|
1705
2247
|
// src/prisma-plan.repository.ts
|
|
1706
|
-
import { Inject as Inject17, Injectable as Injectable19 } from "@nestjs/common";
|
|
2248
|
+
import { Inject as Inject17, Injectable as Injectable19, Optional as Optional6 } from "@nestjs/common";
|
|
2249
|
+
import { buildActivePlanVersionWhere as buildActivePlanVersionWhere3 } from "@saasicat/types";
|
|
1707
2250
|
function _ts_decorate19(decorators, target, key, desc) {
|
|
1708
2251
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1709
2252
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1726,30 +2269,105 @@ var PrismaPlanRepository = class {
|
|
|
1726
2269
|
__name(this, "PrismaPlanRepository");
|
|
1727
2270
|
}
|
|
1728
2271
|
prisma;
|
|
1729
|
-
|
|
2272
|
+
binding;
|
|
2273
|
+
delegateName;
|
|
2274
|
+
fields;
|
|
2275
|
+
constructor(prisma, options) {
|
|
1730
2276
|
this.prisma = prisma;
|
|
2277
|
+
const schema = resolvePrismaSchemaOptions(options);
|
|
2278
|
+
this.binding = createPrismaPlanBindingResolver(options?.planBinding);
|
|
2279
|
+
this.delegateName = schema.delegates.catalogPlanVersion;
|
|
2280
|
+
this.fields = schema.planVersionFields.catalog;
|
|
1731
2281
|
}
|
|
1732
2282
|
db(tx) {
|
|
1733
|
-
return
|
|
2283
|
+
return tx ?? this.prisma;
|
|
2284
|
+
}
|
|
2285
|
+
versions(client) {
|
|
2286
|
+
return getPrismaDelegate(client, this.delegateName);
|
|
1734
2287
|
}
|
|
1735
2288
|
// ─── Stem operations (Pack 1) ───
|
|
1736
2289
|
async list(filter) {
|
|
1737
2290
|
const excludeDeleted = filter.excludeDeleted ?? true;
|
|
2291
|
+
const db = this.db();
|
|
1738
2292
|
let publishedKeys = null;
|
|
1739
2293
|
if (filter.onlyPublished) {
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
2294
|
+
if (this.binding.mode === "legacy-plan-key") {
|
|
2295
|
+
const projectPlans = await db.plan.findMany({
|
|
2296
|
+
where: {
|
|
2297
|
+
projectKey: filter.projectKey,
|
|
2298
|
+
...excludeDeleted ? {
|
|
2299
|
+
deletedAt: null
|
|
2300
|
+
} : {}
|
|
2301
|
+
}
|
|
2302
|
+
});
|
|
2303
|
+
const candidateKeys = [
|
|
2304
|
+
...new Set(projectPlans.map((plan) => plan.planKey))
|
|
2305
|
+
];
|
|
2306
|
+
const allMatchingPlans = candidateKeys.length === 0 ? [] : await db.plan.findMany({
|
|
2307
|
+
where: {
|
|
2308
|
+
planKey: {
|
|
2309
|
+
in: candidateKeys
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
});
|
|
2313
|
+
const projectsByKey = /* @__PURE__ */ new Map();
|
|
2314
|
+
for (const plan of allMatchingPlans) {
|
|
2315
|
+
const projects = projectsByKey.get(plan.planKey) ?? /* @__PURE__ */ new Set();
|
|
2316
|
+
projects.add(plan.projectKey);
|
|
2317
|
+
projectsByKey.set(plan.planKey, projects);
|
|
1746
2318
|
}
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
2319
|
+
const unambiguousKeys = candidateKeys.filter((planKey) => projectsByKey.get(planKey)?.size === 1);
|
|
2320
|
+
const live = await this.versions(db).findMany({
|
|
2321
|
+
where: {
|
|
2322
|
+
planId: {
|
|
2323
|
+
in: unambiguousKeys
|
|
2324
|
+
},
|
|
2325
|
+
publishedAt: {
|
|
2326
|
+
not: null
|
|
2327
|
+
},
|
|
2328
|
+
supersededAt: null
|
|
2329
|
+
}
|
|
2330
|
+
});
|
|
2331
|
+
publishedKeys = [
|
|
2332
|
+
...new Set(live.map((version) => version.planId))
|
|
2333
|
+
];
|
|
2334
|
+
} else {
|
|
2335
|
+
const projectPlans = await db.plan.findMany({
|
|
2336
|
+
where: {
|
|
2337
|
+
projectKey: filter.projectKey,
|
|
2338
|
+
...excludeDeleted ? {
|
|
2339
|
+
deletedAt: null
|
|
2340
|
+
} : {}
|
|
2341
|
+
}
|
|
2342
|
+
});
|
|
2343
|
+
const planKeyById = new Map(projectPlans.map((plan) => [
|
|
2344
|
+
plan.id,
|
|
2345
|
+
plan.planKey
|
|
2346
|
+
]));
|
|
2347
|
+
const live = await this.versions(db).findMany({
|
|
2348
|
+
where: {
|
|
2349
|
+
planId: {
|
|
2350
|
+
in: [
|
|
2351
|
+
...planKeyById.keys()
|
|
2352
|
+
]
|
|
2353
|
+
},
|
|
2354
|
+
publishedAt: {
|
|
2355
|
+
not: null
|
|
2356
|
+
},
|
|
2357
|
+
supersededAt: null
|
|
2358
|
+
}
|
|
2359
|
+
});
|
|
2360
|
+
publishedKeys = [
|
|
2361
|
+
...new Set(live.flatMap((version) => {
|
|
2362
|
+
const planKey = planKeyById.get(version.planId);
|
|
2363
|
+
return planKey ? [
|
|
2364
|
+
planKey
|
|
2365
|
+
] : [];
|
|
2366
|
+
}))
|
|
2367
|
+
];
|
|
2368
|
+
}
|
|
1751
2369
|
}
|
|
1752
|
-
const rows = await
|
|
2370
|
+
const rows = await db.plan.findMany({
|
|
1753
2371
|
where: {
|
|
1754
2372
|
projectKey: filter.projectKey,
|
|
1755
2373
|
...excludeDeleted ? {
|
|
@@ -1844,65 +2462,112 @@ var PrismaPlanRepository = class {
|
|
|
1844
2462
|
}
|
|
1845
2463
|
// ─── Lifecycle operations (Pack 2a) — keyed by planKey ───
|
|
1846
2464
|
async listVersions(planKey) {
|
|
1847
|
-
const
|
|
2465
|
+
const db = this.db();
|
|
2466
|
+
const storedPlanId = await this.binding.toStoragePlanId(db, planKey);
|
|
2467
|
+
const rows = await this.versions(db).findMany({
|
|
1848
2468
|
where: {
|
|
1849
|
-
planId:
|
|
2469
|
+
planId: storedPlanId
|
|
1850
2470
|
},
|
|
1851
2471
|
orderBy: {
|
|
1852
2472
|
version: "asc"
|
|
1853
2473
|
}
|
|
1854
2474
|
});
|
|
1855
|
-
return rows.map(
|
|
2475
|
+
return rows.map((row) => this.toPlanVersionRow(row, planKey));
|
|
1856
2476
|
}
|
|
1857
2477
|
async findVersionById(versionId) {
|
|
1858
|
-
const
|
|
2478
|
+
const db = this.db();
|
|
2479
|
+
const row = await this.versions(db).findUnique({
|
|
1859
2480
|
where: {
|
|
1860
2481
|
id: versionId
|
|
1861
2482
|
}
|
|
1862
2483
|
});
|
|
1863
|
-
return row ?
|
|
2484
|
+
return row ? this.toPlanVersionRow(row, await this.binding.toPlanKey(db, row.planId)) : null;
|
|
1864
2485
|
}
|
|
1865
2486
|
async findCurrentDraft(planKey) {
|
|
1866
|
-
const
|
|
2487
|
+
const db = this.db();
|
|
2488
|
+
const storedPlanId = await this.binding.toStoragePlanId(db, planKey);
|
|
2489
|
+
const row = await this.versions(db).findFirst({
|
|
1867
2490
|
where: {
|
|
1868
|
-
planId:
|
|
2491
|
+
planId: storedPlanId,
|
|
1869
2492
|
publishedAt: null
|
|
1870
2493
|
}
|
|
1871
2494
|
});
|
|
1872
|
-
return row ?
|
|
2495
|
+
return row ? this.toPlanVersionRow(row, planKey) : null;
|
|
1873
2496
|
}
|
|
1874
2497
|
async findLatestLivePlanVersion(planKey, tx) {
|
|
1875
|
-
const
|
|
2498
|
+
const db = this.db(tx);
|
|
2499
|
+
const storedPlanId = await this.binding.toStoragePlanId(db, planKey);
|
|
2500
|
+
const row = await this.versions(db).findFirst({
|
|
1876
2501
|
where: {
|
|
1877
|
-
planId:
|
|
2502
|
+
planId: storedPlanId,
|
|
1878
2503
|
publishedAt: {
|
|
1879
2504
|
not: null
|
|
1880
2505
|
},
|
|
1881
|
-
supersededAt: null
|
|
2506
|
+
supersededAt: null,
|
|
2507
|
+
...this.fields.endsAt ? {
|
|
2508
|
+
OR: [
|
|
2509
|
+
{
|
|
2510
|
+
endsAt: null
|
|
2511
|
+
},
|
|
2512
|
+
{
|
|
2513
|
+
endsAt: {
|
|
2514
|
+
gt: /* @__PURE__ */ new Date()
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
]
|
|
2518
|
+
} : {}
|
|
1882
2519
|
},
|
|
1883
2520
|
orderBy: {
|
|
1884
2521
|
version: "desc"
|
|
1885
2522
|
}
|
|
1886
2523
|
});
|
|
1887
|
-
return row ?
|
|
2524
|
+
return row ? this.toPlanVersionRow(row, planKey) : null;
|
|
1888
2525
|
}
|
|
1889
|
-
async findActivePlanVersion() {
|
|
1890
|
-
|
|
2526
|
+
async findActivePlanVersion(planKey, asOf = /* @__PURE__ */ new Date(), tx) {
|
|
2527
|
+
if (!this.fields.validityWindows) {
|
|
2528
|
+
throw new Error("findActivePlanVersion requires schema.planVersionFields.catalog.validityWindows=true and the current @saasicat/spec PlanVersion validity columns. Apply the additive schema first, or use findLatestLivePlanVersion for a 0.6-compatible newest-live lookup.");
|
|
2529
|
+
}
|
|
2530
|
+
const db = this.db(tx);
|
|
2531
|
+
const storedPlanId = await this.binding.toStoragePlanId(db, planKey);
|
|
2532
|
+
const activeWhere = this.fields.endsAt ? buildActivePlanVersionWhere3(asOf, {
|
|
2533
|
+
withEndsAt: true
|
|
2534
|
+
}) : buildActivePlanVersionWhere3(asOf);
|
|
2535
|
+
const row = await this.versions(db).findFirst({
|
|
2536
|
+
where: {
|
|
2537
|
+
planId: storedPlanId,
|
|
2538
|
+
...activeWhere
|
|
2539
|
+
},
|
|
2540
|
+
orderBy: [
|
|
2541
|
+
{
|
|
2542
|
+
validFrom: {
|
|
2543
|
+
sort: "desc",
|
|
2544
|
+
nulls: "last"
|
|
2545
|
+
}
|
|
2546
|
+
},
|
|
2547
|
+
{
|
|
2548
|
+
version: "desc"
|
|
2549
|
+
}
|
|
2550
|
+
]
|
|
2551
|
+
});
|
|
2552
|
+
return row ? this.toPlanVersionRow(row, planKey) : null;
|
|
1891
2553
|
}
|
|
1892
2554
|
async createPlanVersionDraft(data) {
|
|
1893
2555
|
const planKey = data.planId;
|
|
1894
|
-
const
|
|
2556
|
+
const db = this.db();
|
|
2557
|
+
const planVersion = this.versions(db);
|
|
2558
|
+
const storedPlanId = await this.binding.toStoragePlanId(db, planKey);
|
|
2559
|
+
const latest = await planVersion.findFirst({
|
|
1895
2560
|
where: {
|
|
1896
|
-
planId:
|
|
2561
|
+
planId: storedPlanId
|
|
1897
2562
|
},
|
|
1898
2563
|
orderBy: {
|
|
1899
2564
|
version: "desc"
|
|
1900
2565
|
}
|
|
1901
2566
|
});
|
|
1902
2567
|
const nextVersion = (latest?.version ?? 0) + 1;
|
|
1903
|
-
const created = await
|
|
2568
|
+
const created = await planVersion.create({
|
|
1904
2569
|
data: {
|
|
1905
|
-
planId:
|
|
2570
|
+
planId: storedPlanId,
|
|
1906
2571
|
version: nextVersion,
|
|
1907
2572
|
baseVersionId: data.baseVersionId ?? null,
|
|
1908
2573
|
features: data.features,
|
|
@@ -1911,13 +2576,17 @@ var PrismaPlanRepository = class {
|
|
|
1911
2576
|
yearlyNet: data.yearlyNet,
|
|
1912
2577
|
marketed: data.marketed ?? true,
|
|
1913
2578
|
changeNote: data.changeNote ?? "",
|
|
1914
|
-
createdByUserId: data.createdByUserId ?? null
|
|
2579
|
+
createdByUserId: data.createdByUserId ?? null,
|
|
2580
|
+
...this.fields.validityWindows ? {
|
|
2581
|
+
validFrom: data.validFrom ? new Date(data.validFrom) : null,
|
|
2582
|
+
validUntil: data.validUntil ? new Date(data.validUntil) : null
|
|
2583
|
+
} : {}
|
|
1915
2584
|
}
|
|
1916
2585
|
});
|
|
1917
|
-
return
|
|
2586
|
+
return this.toPlanVersionRow(created, planKey);
|
|
1918
2587
|
}
|
|
1919
2588
|
async updatePlanVersionDraft(versionId, data) {
|
|
1920
|
-
const updated = await this.db().
|
|
2589
|
+
const updated = await this.versions(this.db()).update({
|
|
1921
2590
|
where: {
|
|
1922
2591
|
id: versionId
|
|
1923
2592
|
},
|
|
@@ -1939,13 +2608,21 @@ var PrismaPlanRepository = class {
|
|
|
1939
2608
|
} : {},
|
|
1940
2609
|
...data.changeNote !== void 0 ? {
|
|
1941
2610
|
changeNote: data.changeNote
|
|
2611
|
+
} : {},
|
|
2612
|
+
...this.fields.validityWindows && data.validFrom !== void 0 ? {
|
|
2613
|
+
validFrom: data.validFrom ? new Date(data.validFrom) : null
|
|
2614
|
+
} : {},
|
|
2615
|
+
...this.fields.validityWindows && data.validUntil !== void 0 ? {
|
|
2616
|
+
validUntil: data.validUntil ? new Date(data.validUntil) : null
|
|
1942
2617
|
} : {}
|
|
1943
2618
|
}
|
|
1944
2619
|
});
|
|
1945
|
-
|
|
2620
|
+
const planKey = await this.binding.toPlanKey(this.db(), updated.planId);
|
|
2621
|
+
return this.toPlanVersionRow(updated, planKey);
|
|
1946
2622
|
}
|
|
1947
2623
|
async publishPlanVersionDraft(versionId, publishMeta, tx) {
|
|
1948
|
-
const
|
|
2624
|
+
const operationDb = this.db(tx);
|
|
2625
|
+
const draft = await this.versions(operationDb).findUnique({
|
|
1949
2626
|
where: {
|
|
1950
2627
|
id: versionId
|
|
1951
2628
|
}
|
|
@@ -1953,11 +2630,12 @@ var PrismaPlanRepository = class {
|
|
|
1953
2630
|
if (!draft) {
|
|
1954
2631
|
throw new Error(`PlanVersion ${versionId} not found.`);
|
|
1955
2632
|
}
|
|
1956
|
-
const
|
|
2633
|
+
const storedPlanId = draft.planId;
|
|
1957
2634
|
const publish = /* @__PURE__ */ __name(async (db) => {
|
|
1958
|
-
const
|
|
2635
|
+
const planVersion = this.versions(db);
|
|
2636
|
+
const previous = await planVersion.findFirst({
|
|
1959
2637
|
where: {
|
|
1960
|
-
planId:
|
|
2638
|
+
planId: storedPlanId,
|
|
1961
2639
|
publishedAt: {
|
|
1962
2640
|
not: null
|
|
1963
2641
|
},
|
|
@@ -1972,16 +2650,20 @@ var PrismaPlanRepository = class {
|
|
|
1972
2650
|
});
|
|
1973
2651
|
const now = /* @__PURE__ */ new Date();
|
|
1974
2652
|
if (previous) {
|
|
1975
|
-
|
|
2653
|
+
const predecessorValidUntil = new Date(publishMeta.validFrom.getTime() - 24 * 60 * 60 * 1e3);
|
|
2654
|
+
await planVersion.update({
|
|
1976
2655
|
where: {
|
|
1977
2656
|
id: previous.id
|
|
1978
2657
|
},
|
|
1979
2658
|
data: {
|
|
1980
|
-
supersededAt: now
|
|
2659
|
+
supersededAt: now,
|
|
2660
|
+
...this.fields.validityWindows ? {
|
|
2661
|
+
validUntil: predecessorValidUntil
|
|
2662
|
+
} : {}
|
|
1981
2663
|
}
|
|
1982
2664
|
});
|
|
1983
2665
|
}
|
|
1984
|
-
return
|
|
2666
|
+
return planVersion.update({
|
|
1985
2667
|
where: {
|
|
1986
2668
|
id: versionId
|
|
1987
2669
|
},
|
|
@@ -1989,15 +2671,21 @@ var PrismaPlanRepository = class {
|
|
|
1989
2671
|
publishedAt: now,
|
|
1990
2672
|
publishedChanges: publishMeta.publishedChanges,
|
|
1991
2673
|
nonRegressive: publishMeta.nonRegressive,
|
|
1992
|
-
publishedByUserId: publishMeta.publishedByUserId
|
|
2674
|
+
publishedByUserId: publishMeta.publishedByUserId,
|
|
2675
|
+
...this.fields.validityWindows ? {
|
|
2676
|
+
validFrom: publishMeta.validFrom,
|
|
2677
|
+
validUntil: publishMeta.validUntil
|
|
2678
|
+
} : {}
|
|
1993
2679
|
}
|
|
1994
2680
|
});
|
|
1995
2681
|
}, "publish");
|
|
1996
2682
|
const published = tx ? await publish(this.db(tx)) : await this.prisma.$transaction((txClient) => publish(txClient));
|
|
1997
|
-
|
|
2683
|
+
const planKey = await this.binding.toPlanKey(operationDb, storedPlanId);
|
|
2684
|
+
return this.toPlanVersionRow(published, planKey);
|
|
1998
2685
|
}
|
|
1999
2686
|
async deletePlanVersionDraft(versionId) {
|
|
2000
|
-
const
|
|
2687
|
+
const planVersion = this.versions(this.db());
|
|
2688
|
+
const row = await planVersion.findUnique({
|
|
2001
2689
|
where: {
|
|
2002
2690
|
id: versionId
|
|
2003
2691
|
}
|
|
@@ -2006,23 +2694,42 @@ var PrismaPlanRepository = class {
|
|
|
2006
2694
|
if (row.publishedAt !== null) {
|
|
2007
2695
|
throw new Error(`PlanVersion ${versionId} is already published and cannot be discarded (published versions are immutable \u2014 contract protection P1).`);
|
|
2008
2696
|
}
|
|
2009
|
-
await
|
|
2697
|
+
await planVersion.deleteMany({
|
|
2010
2698
|
where: {
|
|
2011
2699
|
id: versionId,
|
|
2012
2700
|
publishedAt: null
|
|
2013
2701
|
}
|
|
2014
2702
|
});
|
|
2015
2703
|
}
|
|
2016
|
-
async terminate() {
|
|
2017
|
-
|
|
2704
|
+
async terminate(versionId, endsAt) {
|
|
2705
|
+
if (!this.fields.endsAt) {
|
|
2706
|
+
throw new Error("terminate requires schema.planVersionFields.catalog.endsAt=true and the current @saasicat/spec PlanVersion.endsAt column. Apply the additive schema before enabling SuperAdmin-initiated plan-version termination.");
|
|
2707
|
+
}
|
|
2708
|
+
const db = this.db();
|
|
2709
|
+
const updated = await this.versions(db).update({
|
|
2710
|
+
where: {
|
|
2711
|
+
id: versionId
|
|
2712
|
+
},
|
|
2713
|
+
data: {
|
|
2714
|
+
endsAt
|
|
2715
|
+
}
|
|
2716
|
+
});
|
|
2717
|
+
const planKey = await this.binding.toPlanKey(db, updated.planId);
|
|
2718
|
+
return this.toPlanVersionRow(updated, planKey);
|
|
2719
|
+
}
|
|
2720
|
+
toPlanVersionRow(row, planKey) {
|
|
2721
|
+
return toPlanVersionRow2(row, planKey, this.fields);
|
|
2018
2722
|
}
|
|
2019
2723
|
};
|
|
2020
2724
|
PrismaPlanRepository = _ts_decorate19([
|
|
2021
2725
|
Injectable19(),
|
|
2022
2726
|
_ts_param17(0, Inject17(PRISMA_CLIENT_TOKEN)),
|
|
2727
|
+
_ts_param17(1, Optional6()),
|
|
2728
|
+
_ts_param17(1, Inject17(PRISMA_SCHEMA_OPTIONS_TOKEN)),
|
|
2023
2729
|
_ts_metadata17("design:type", Function),
|
|
2024
2730
|
_ts_metadata17("design:paramtypes", [
|
|
2025
|
-
typeof
|
|
2731
|
+
typeof PlanRepositoryClient === "undefined" ? Object : PlanRepositoryClient,
|
|
2732
|
+
typeof PrismaSchemaOptions === "undefined" ? Object : PrismaSchemaOptions
|
|
2026
2733
|
])
|
|
2027
2734
|
], PrismaPlanRepository);
|
|
2028
2735
|
function toPlanRow2(row) {
|
|
@@ -2040,12 +2747,12 @@ function toPlanRow2(row) {
|
|
|
2040
2747
|
};
|
|
2041
2748
|
}
|
|
2042
2749
|
__name(toPlanRow2, "toPlanRow");
|
|
2043
|
-
function toPlanVersionRow2(row) {
|
|
2044
|
-
|
|
2750
|
+
function toPlanVersionRow2(row, planKey, fields) {
|
|
2751
|
+
const mapped = {
|
|
2045
2752
|
id: row.id,
|
|
2046
2753
|
version: row.version,
|
|
2047
2754
|
baseVersionId: row.baseVersionId,
|
|
2048
|
-
planId:
|
|
2755
|
+
planId: planKey,
|
|
2049
2756
|
features: toStringArray(row.features),
|
|
2050
2757
|
quotas: toQuotaMap(row.quotas),
|
|
2051
2758
|
monthlyNet: row.monthlyNet.toString(),
|
|
@@ -2056,19 +2763,23 @@ function toPlanVersionRow2(row) {
|
|
|
2056
2763
|
publishedChanges: Array.isArray(row.publishedChanges) ? row.publishedChanges : null,
|
|
2057
2764
|
changeNote: row.changeNote,
|
|
2058
2765
|
nonRegressive: row.nonRegressive,
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
validUntil: null,
|
|
2766
|
+
validFrom: fields.validityWindows && row.validFrom ? row.validFrom.toISOString() : null,
|
|
2767
|
+
validUntil: fields.validityWindows && row.validUntil ? row.validUntil.toISOString() : null,
|
|
2062
2768
|
createdByUserId: row.createdByUserId,
|
|
2063
2769
|
publishedByUserId: row.publishedByUserId,
|
|
2064
2770
|
createdAt: row.createdAt.toISOString(),
|
|
2065
2771
|
updatedAt: row.updatedAt.toISOString()
|
|
2066
2772
|
};
|
|
2773
|
+
if (fields.endsAt) {
|
|
2774
|
+
mapped.endsAt = row.endsAt?.toISOString() ?? null;
|
|
2775
|
+
}
|
|
2776
|
+
return mapped;
|
|
2067
2777
|
}
|
|
2068
2778
|
__name(toPlanVersionRow2, "toPlanVersionRow");
|
|
2069
2779
|
|
|
2070
2780
|
// src/prisma-bundle.repository.ts
|
|
2071
|
-
import { Inject as Inject18, Injectable as Injectable20 } from "@nestjs/common";
|
|
2781
|
+
import { Inject as Inject18, Injectable as Injectable20, Optional as Optional7 } from "@nestjs/common";
|
|
2782
|
+
import { buildActiveVersionWhere } from "@saasicat/types";
|
|
2072
2783
|
function _ts_decorate20(decorators, target, key, desc) {
|
|
2073
2784
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2074
2785
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -2086,16 +2797,32 @@ function _ts_param18(paramIndex, decorator) {
|
|
|
2086
2797
|
};
|
|
2087
2798
|
}
|
|
2088
2799
|
__name(_ts_param18, "_ts_param");
|
|
2800
|
+
var PRISMA_BUNDLE_REPOSITORY_OPTIONS = /* @__PURE__ */ Symbol.for("saasicat/adapter-prisma/PrismaBundleRepositoryOptions");
|
|
2089
2801
|
var PrismaBundleRepository = class {
|
|
2090
2802
|
static {
|
|
2091
2803
|
__name(this, "PrismaBundleRepository");
|
|
2092
2804
|
}
|
|
2093
2805
|
prisma;
|
|
2094
|
-
|
|
2806
|
+
validityWindows;
|
|
2807
|
+
/**
|
|
2808
|
+
* Present only when validity-window mode is enabled. This mirrors the
|
|
2809
|
+
* optional port capability and lets 0.6-schema consumers detect that an
|
|
2810
|
+
* active-at-time lookup is unavailable.
|
|
2811
|
+
*/
|
|
2812
|
+
findActiveBundleVersion;
|
|
2813
|
+
constructor(prisma, options = {}) {
|
|
2095
2814
|
this.prisma = prisma;
|
|
2815
|
+
this.validityWindows = options.validityWindows ?? false;
|
|
2816
|
+
if (this.validityWindows) {
|
|
2817
|
+
this.findActiveBundleVersion = (bundleId, asOf, tx) => this.findActiveBundleVersionWithValidity(bundleId, asOf ?? /* @__PURE__ */ new Date(), tx);
|
|
2818
|
+
}
|
|
2096
2819
|
}
|
|
2097
2820
|
db(tx) {
|
|
2098
|
-
return
|
|
2821
|
+
return tx ?? this.prisma;
|
|
2822
|
+
}
|
|
2823
|
+
transaction(work) {
|
|
2824
|
+
const transaction = this.prisma.$transaction;
|
|
2825
|
+
return transaction.call(this.prisma, (tx) => work(tx));
|
|
2099
2826
|
}
|
|
2100
2827
|
// ─── Stem operations ───
|
|
2101
2828
|
async list(filter) {
|
|
@@ -2201,7 +2928,7 @@ var PrismaBundleRepository = class {
|
|
|
2201
2928
|
version: "asc"
|
|
2202
2929
|
}
|
|
2203
2930
|
});
|
|
2204
|
-
return rows.map((row) => toBundleVersionRow(row, bundle));
|
|
2931
|
+
return rows.map((row) => toBundleVersionRow(row, bundle, this.validityWindows));
|
|
2205
2932
|
}
|
|
2206
2933
|
async findVersionById(versionId) {
|
|
2207
2934
|
const row = await this.db().bundleVersion.findUnique({
|
|
@@ -2215,7 +2942,7 @@ var PrismaBundleRepository = class {
|
|
|
2215
2942
|
id: row.bundleId
|
|
2216
2943
|
}
|
|
2217
2944
|
});
|
|
2218
|
-
return toBundleVersionRow(row, bundle);
|
|
2945
|
+
return toBundleVersionRow(row, bundle, this.validityWindows);
|
|
2219
2946
|
}
|
|
2220
2947
|
async findCurrentDraft(bundleId) {
|
|
2221
2948
|
const row = await this.db().bundleVersion.findFirst({
|
|
@@ -2230,7 +2957,7 @@ var PrismaBundleRepository = class {
|
|
|
2230
2957
|
id: bundleId
|
|
2231
2958
|
}
|
|
2232
2959
|
});
|
|
2233
|
-
return toBundleVersionRow(row, bundle);
|
|
2960
|
+
return toBundleVersionRow(row, bundle, this.validityWindows);
|
|
2234
2961
|
}
|
|
2235
2962
|
async findLatestLive(bundleId, tx) {
|
|
2236
2963
|
const db = this.db(tx);
|
|
@@ -2252,7 +2979,34 @@ var PrismaBundleRepository = class {
|
|
|
2252
2979
|
id: bundleId
|
|
2253
2980
|
}
|
|
2254
2981
|
});
|
|
2255
|
-
return toBundleVersionRow(row, bundle);
|
|
2982
|
+
return toBundleVersionRow(row, bundle, this.validityWindows);
|
|
2983
|
+
}
|
|
2984
|
+
async findActiveBundleVersionWithValidity(bundleId, asOf, tx) {
|
|
2985
|
+
const db = this.db(tx);
|
|
2986
|
+
const row = await db.bundleVersion.findFirst({
|
|
2987
|
+
where: {
|
|
2988
|
+
bundleId,
|
|
2989
|
+
...buildActiveVersionWhere(asOf)
|
|
2990
|
+
},
|
|
2991
|
+
orderBy: [
|
|
2992
|
+
{
|
|
2993
|
+
validFrom: {
|
|
2994
|
+
sort: "desc",
|
|
2995
|
+
nulls: "last"
|
|
2996
|
+
}
|
|
2997
|
+
},
|
|
2998
|
+
{
|
|
2999
|
+
version: "desc"
|
|
3000
|
+
}
|
|
3001
|
+
]
|
|
3002
|
+
});
|
|
3003
|
+
if (!row) return null;
|
|
3004
|
+
const bundle = await db.bundle.findUnique({
|
|
3005
|
+
where: {
|
|
3006
|
+
id: bundleId
|
|
3007
|
+
}
|
|
3008
|
+
});
|
|
3009
|
+
return toBundleVersionRow(row, bundle, true);
|
|
2256
3010
|
}
|
|
2257
3011
|
async createDraft(data) {
|
|
2258
3012
|
const db = this.db();
|
|
@@ -2287,7 +3041,11 @@ var PrismaBundleRepository = class {
|
|
|
2287
3041
|
yearlyNet: data.yearlyNet ?? null,
|
|
2288
3042
|
marketed: data.marketed ?? true,
|
|
2289
3043
|
changeNote: data.changeNote ?? "",
|
|
2290
|
-
createdByUserId: data.createdByUserId ?? null
|
|
3044
|
+
createdByUserId: data.createdByUserId ?? null,
|
|
3045
|
+
...this.validityWindows ? {
|
|
3046
|
+
validFrom: toNullableDate(data.validFrom),
|
|
3047
|
+
validUntil: toNullableDate(data.validUntil)
|
|
3048
|
+
} : {}
|
|
2291
3049
|
}
|
|
2292
3050
|
});
|
|
2293
3051
|
const bundle = await db.bundle.findUnique({
|
|
@@ -2295,7 +3053,7 @@ var PrismaBundleRepository = class {
|
|
|
2295
3053
|
id: data.bundleId
|
|
2296
3054
|
}
|
|
2297
3055
|
});
|
|
2298
|
-
return toBundleVersionRow(created, bundle);
|
|
3056
|
+
return toBundleVersionRow(created, bundle, this.validityWindows);
|
|
2299
3057
|
}
|
|
2300
3058
|
async updateDraft(versionId, data) {
|
|
2301
3059
|
const db = this.db();
|
|
@@ -2327,6 +3085,12 @@ var PrismaBundleRepository = class {
|
|
|
2327
3085
|
} : {},
|
|
2328
3086
|
...data.changeNote !== void 0 ? {
|
|
2329
3087
|
changeNote: data.changeNote
|
|
3088
|
+
} : {},
|
|
3089
|
+
...this.validityWindows && data.validFrom !== void 0 ? {
|
|
3090
|
+
validFrom: toNullableDate(data.validFrom)
|
|
3091
|
+
} : {},
|
|
3092
|
+
...this.validityWindows && data.validUntil !== void 0 ? {
|
|
3093
|
+
validUntil: toNullableDate(data.validUntil)
|
|
2330
3094
|
} : {}
|
|
2331
3095
|
}
|
|
2332
3096
|
});
|
|
@@ -2335,9 +3099,15 @@ var PrismaBundleRepository = class {
|
|
|
2335
3099
|
id: updated.bundleId
|
|
2336
3100
|
}
|
|
2337
3101
|
});
|
|
2338
|
-
return toBundleVersionRow(updated, bundle);
|
|
3102
|
+
return toBundleVersionRow(updated, bundle, this.validityWindows);
|
|
2339
3103
|
}
|
|
2340
3104
|
async publishDraft(versionId, publishMeta, tx) {
|
|
3105
|
+
if (this.validityWindows && tx === void 0) {
|
|
3106
|
+
return this.transaction((transaction) => this.publishDraftWithValidity(transaction, versionId, publishMeta));
|
|
3107
|
+
}
|
|
3108
|
+
if (this.validityWindows) {
|
|
3109
|
+
return this.publishDraftWithValidity(this.db(tx), versionId, publishMeta);
|
|
3110
|
+
}
|
|
2341
3111
|
const db = this.db(tx);
|
|
2342
3112
|
const draft = await db.bundleVersion.findUnique({
|
|
2343
3113
|
where: {
|
|
@@ -2378,7 +3148,53 @@ var PrismaBundleRepository = class {
|
|
|
2378
3148
|
id: published.bundleId
|
|
2379
3149
|
}
|
|
2380
3150
|
});
|
|
2381
|
-
return toBundleVersionRow(published, bundle);
|
|
3151
|
+
return toBundleVersionRow(published, bundle, false);
|
|
3152
|
+
}
|
|
3153
|
+
async publishDraftWithValidity(db, versionId, publishMeta) {
|
|
3154
|
+
const draft = await db.bundleVersion.findUnique({
|
|
3155
|
+
where: {
|
|
3156
|
+
id: versionId
|
|
3157
|
+
}
|
|
3158
|
+
});
|
|
3159
|
+
if (!draft) {
|
|
3160
|
+
throw new Error(`BundleVersion '${versionId}' not found.`);
|
|
3161
|
+
}
|
|
3162
|
+
const now = /* @__PURE__ */ new Date();
|
|
3163
|
+
await db.bundleVersion.updateMany({
|
|
3164
|
+
where: {
|
|
3165
|
+
bundleId: draft.bundleId,
|
|
3166
|
+
publishedAt: {
|
|
3167
|
+
not: null
|
|
3168
|
+
},
|
|
3169
|
+
supersededAt: null,
|
|
3170
|
+
NOT: {
|
|
3171
|
+
id: versionId
|
|
3172
|
+
}
|
|
3173
|
+
},
|
|
3174
|
+
data: {
|
|
3175
|
+
supersededAt: now,
|
|
3176
|
+
validUntil: previousUtcDay(publishMeta.validFrom)
|
|
3177
|
+
}
|
|
3178
|
+
});
|
|
3179
|
+
const published = await db.bundleVersion.update({
|
|
3180
|
+
where: {
|
|
3181
|
+
id: versionId
|
|
3182
|
+
},
|
|
3183
|
+
data: {
|
|
3184
|
+
publishedAt: now,
|
|
3185
|
+
publishedByUserId: publishMeta.publishedByUserId,
|
|
3186
|
+
publishedChanges: publishMeta.publishedChanges,
|
|
3187
|
+
nonRegressive: publishMeta.nonRegressive,
|
|
3188
|
+
validFrom: publishMeta.validFrom,
|
|
3189
|
+
validUntil: publishMeta.validUntil
|
|
3190
|
+
}
|
|
3191
|
+
});
|
|
3192
|
+
const bundle = await db.bundle.findUnique({
|
|
3193
|
+
where: {
|
|
3194
|
+
id: published.bundleId
|
|
3195
|
+
}
|
|
3196
|
+
});
|
|
3197
|
+
return toBundleVersionRow(published, bundle, true);
|
|
2382
3198
|
}
|
|
2383
3199
|
async deleteDraft(versionId) {
|
|
2384
3200
|
const db = this.db();
|
|
@@ -2406,9 +3222,12 @@ var PrismaBundleRepository = class {
|
|
|
2406
3222
|
PrismaBundleRepository = _ts_decorate20([
|
|
2407
3223
|
Injectable20(),
|
|
2408
3224
|
_ts_param18(0, Inject18(PRISMA_CLIENT_TOKEN)),
|
|
3225
|
+
_ts_param18(1, Optional7()),
|
|
3226
|
+
_ts_param18(1, Inject18(PRISMA_BUNDLE_REPOSITORY_OPTIONS)),
|
|
2409
3227
|
_ts_metadata18("design:type", Function),
|
|
2410
3228
|
_ts_metadata18("design:paramtypes", [
|
|
2411
|
-
typeof
|
|
3229
|
+
typeof BundlePrismaClient === "undefined" ? Object : BundlePrismaClient,
|
|
3230
|
+
typeof PrismaBundleRepositoryOptions === "undefined" ? Object : PrismaBundleRepositoryOptions
|
|
2412
3231
|
])
|
|
2413
3232
|
], PrismaBundleRepository);
|
|
2414
3233
|
function isPlainObject(value) {
|
|
@@ -2419,6 +3238,16 @@ function toDecimalString(value) {
|
|
|
2419
3238
|
return value == null ? null : value.toString();
|
|
2420
3239
|
}
|
|
2421
3240
|
__name(toDecimalString, "toDecimalString");
|
|
3241
|
+
function toNullableDate(value) {
|
|
3242
|
+
return value ? new Date(value) : null;
|
|
3243
|
+
}
|
|
3244
|
+
__name(toNullableDate, "toNullableDate");
|
|
3245
|
+
function previousUtcDay(value) {
|
|
3246
|
+
const result = new Date(value);
|
|
3247
|
+
result.setUTCDate(result.getUTCDate() - 1);
|
|
3248
|
+
return result;
|
|
3249
|
+
}
|
|
3250
|
+
__name(previousUtcDay, "previousUtcDay");
|
|
2422
3251
|
function toVersionChanges(value) {
|
|
2423
3252
|
return Array.isArray(value) ? value : null;
|
|
2424
3253
|
}
|
|
@@ -2451,7 +3280,7 @@ function toBundleRow(row) {
|
|
|
2451
3280
|
};
|
|
2452
3281
|
}
|
|
2453
3282
|
__name(toBundleRow, "toBundleRow");
|
|
2454
|
-
function toBundleVersionRow(row, bundle) {
|
|
3283
|
+
function toBundleVersionRow(row, bundle, validityWindows) {
|
|
2455
3284
|
return {
|
|
2456
3285
|
id: row.id,
|
|
2457
3286
|
bundleId: row.bundleId,
|
|
@@ -2468,10 +3297,8 @@ function toBundleVersionRow(row, bundle) {
|
|
|
2468
3297
|
marketed: row.marketed,
|
|
2469
3298
|
publishedAt: row.publishedAt?.toISOString() ?? null,
|
|
2470
3299
|
supersededAt: row.supersededAt?.toISOString() ?? null,
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
validFrom: null,
|
|
2474
|
-
validUntil: null,
|
|
3300
|
+
validFrom: validityWindows && row.validFrom instanceof Date ? row.validFrom.toISOString() : null,
|
|
3301
|
+
validUntil: validityWindows && row.validUntil instanceof Date ? row.validUntil.toISOString() : null,
|
|
2475
3302
|
publishedChanges: toVersionChanges(row.publishedChanges),
|
|
2476
3303
|
changeNote: row.changeNote,
|
|
2477
3304
|
nonRegressive: row.nonRegressive,
|
|
@@ -2873,7 +3700,7 @@ PrismaCatalogEntryRepository = _ts_decorate21([
|
|
|
2873
3700
|
_ts_param19(0, Inject19(PRISMA_CLIENT_TOKEN)),
|
|
2874
3701
|
_ts_metadata19("design:type", Function),
|
|
2875
3702
|
_ts_metadata19("design:paramtypes", [
|
|
2876
|
-
typeof
|
|
3703
|
+
typeof CatalogEntryRepositoryClient === "undefined" ? Object : CatalogEntryRepositoryClient
|
|
2877
3704
|
])
|
|
2878
3705
|
], PrismaCatalogEntryRepository);
|
|
2879
3706
|
function toI18n2(value) {
|
|
@@ -3114,7 +3941,7 @@ PrismaMarketingProjectionRepository = _ts_decorate22([
|
|
|
3114
3941
|
_ts_param20(0, Inject20(PRISMA_CLIENT_TOKEN)),
|
|
3115
3942
|
_ts_metadata20("design:type", Function),
|
|
3116
3943
|
_ts_metadata20("design:paramtypes", [
|
|
3117
|
-
typeof
|
|
3944
|
+
typeof MarketingProjectionRepositoryClient === "undefined" ? Object : MarketingProjectionRepositoryClient
|
|
3118
3945
|
])
|
|
3119
3946
|
], PrismaMarketingProjectionRepository);
|
|
3120
3947
|
function toTopFeatures(value) {
|
|
@@ -3204,7 +4031,7 @@ PrismaMarketingSettingsRepository = _ts_decorate23([
|
|
|
3204
4031
|
_ts_param21(0, Inject21(PRISMA_CLIENT_TOKEN)),
|
|
3205
4032
|
_ts_metadata21("design:type", Function),
|
|
3206
4033
|
_ts_metadata21("design:paramtypes", [
|
|
3207
|
-
typeof
|
|
4034
|
+
typeof MarketingSettingsRepositoryClient === "undefined" ? Object : MarketingSettingsRepositoryClient
|
|
3208
4035
|
])
|
|
3209
4036
|
], PrismaMarketingSettingsRepository);
|
|
3210
4037
|
function toRow2(row) {
|
|
@@ -3295,7 +4122,8 @@ var PrismaPromotionRepository = class {
|
|
|
3295
4122
|
}
|
|
3296
4123
|
async update(id, data) {
|
|
3297
4124
|
if (data.onlyLocales === null) {
|
|
3298
|
-
|
|
4125
|
+
const executeRaw = this.prisma.$executeRaw.bind(this.prisma);
|
|
4126
|
+
await executeRaw`
|
|
3299
4127
|
UPDATE promotions SET "onlyLocales" = NULL, "updatedAt" = NOW() WHERE id = ${id}`;
|
|
3300
4128
|
}
|
|
3301
4129
|
const row = await this.db.promotion.update({
|
|
@@ -3362,7 +4190,7 @@ PrismaPromotionRepository = _ts_decorate24([
|
|
|
3362
4190
|
_ts_param22(0, Inject22(PRISMA_CLIENT_TOKEN)),
|
|
3363
4191
|
_ts_metadata22("design:type", Function),
|
|
3364
4192
|
_ts_metadata22("design:paramtypes", [
|
|
3365
|
-
typeof
|
|
4193
|
+
typeof PromotionRepositoryClient === "undefined" ? Object : PromotionRepositoryClient
|
|
3366
4194
|
])
|
|
3367
4195
|
], PrismaPromotionRepository);
|
|
3368
4196
|
function toPromotionValue(value) {
|
|
@@ -3670,7 +4498,9 @@ __name(toRecord4, "toRecord");
|
|
|
3670
4498
|
export {
|
|
3671
4499
|
AsyncLocalRlsBypassAdapter,
|
|
3672
4500
|
PASSWORD_HASHER_TOKEN,
|
|
4501
|
+
PRISMA_BUNDLE_REPOSITORY_OPTIONS,
|
|
3673
4502
|
PRISMA_CLIENT_TOKEN,
|
|
4503
|
+
PRISMA_SCHEMA_OPTIONS_TOKEN,
|
|
3674
4504
|
PrismaAuditAdapter,
|
|
3675
4505
|
PrismaAuditQueryAdapter,
|
|
3676
4506
|
PrismaAuditStatsAdapter,
|
|
@@ -3696,5 +4526,8 @@ export {
|
|
|
3696
4526
|
PrismaTransactionRunner,
|
|
3697
4527
|
ZeroPromoRevenueDeductionAggregator,
|
|
3698
4528
|
buildActorTag,
|
|
3699
|
-
|
|
4529
|
+
createPrismaPlanBindingResolver,
|
|
4530
|
+
getPrismaDelegate,
|
|
4531
|
+
prismaPersistence,
|
|
4532
|
+
resolvePrismaSchemaOptions
|
|
3700
4533
|
};
|