@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.cjs
CHANGED
|
@@ -23,7 +23,9 @@ var index_exports = {};
|
|
|
23
23
|
__export(index_exports, {
|
|
24
24
|
AsyncLocalRlsBypassAdapter: () => AsyncLocalRlsBypassAdapter,
|
|
25
25
|
PASSWORD_HASHER_TOKEN: () => PASSWORD_HASHER_TOKEN,
|
|
26
|
+
PRISMA_BUNDLE_REPOSITORY_OPTIONS: () => PRISMA_BUNDLE_REPOSITORY_OPTIONS,
|
|
26
27
|
PRISMA_CLIENT_TOKEN: () => PRISMA_CLIENT_TOKEN,
|
|
28
|
+
PRISMA_SCHEMA_OPTIONS_TOKEN: () => PRISMA_SCHEMA_OPTIONS_TOKEN,
|
|
27
29
|
PrismaAuditAdapter: () => PrismaAuditAdapter,
|
|
28
30
|
PrismaAuditQueryAdapter: () => PrismaAuditQueryAdapter,
|
|
29
31
|
PrismaAuditStatsAdapter: () => PrismaAuditStatsAdapter,
|
|
@@ -49,7 +51,10 @@ __export(index_exports, {
|
|
|
49
51
|
PrismaTransactionRunner: () => PrismaTransactionRunner,
|
|
50
52
|
ZeroPromoRevenueDeductionAggregator: () => ZeroPromoRevenueDeductionAggregator,
|
|
51
53
|
buildActorTag: () => buildActorTag,
|
|
52
|
-
|
|
54
|
+
createPrismaPlanBindingResolver: () => createPrismaPlanBindingResolver,
|
|
55
|
+
getPrismaDelegate: () => getPrismaDelegate,
|
|
56
|
+
prismaPersistence: () => prismaPersistence,
|
|
57
|
+
resolvePrismaSchemaOptions: () => resolvePrismaSchemaOptions
|
|
53
58
|
});
|
|
54
59
|
module.exports = __toCommonJS(index_exports);
|
|
55
60
|
|
|
@@ -138,7 +143,7 @@ PrismaAuditAdapter = _ts_decorate2([
|
|
|
138
143
|
_ts_param(0, (0, import_common2.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
139
144
|
_ts_metadata("design:type", Function),
|
|
140
145
|
_ts_metadata("design:paramtypes", [
|
|
141
|
-
typeof
|
|
146
|
+
typeof Pick === "undefined" ? Object : Pick
|
|
142
147
|
])
|
|
143
148
|
], PrismaAuditAdapter);
|
|
144
149
|
|
|
@@ -201,7 +206,7 @@ PrismaAuditQueryAdapter = _ts_decorate3([
|
|
|
201
206
|
_ts_param2(0, (0, import_common3.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
202
207
|
_ts_metadata2("design:type", Function),
|
|
203
208
|
_ts_metadata2("design:paramtypes", [
|
|
204
|
-
typeof
|
|
209
|
+
typeof Pick === "undefined" ? Object : Pick
|
|
205
210
|
])
|
|
206
211
|
], PrismaAuditQueryAdapter);
|
|
207
212
|
function toActorTagFilter(actorTag) {
|
|
@@ -273,7 +278,7 @@ PrismaAuditStatsAdapter = _ts_decorate4([
|
|
|
273
278
|
_ts_param3(0, (0, import_common4.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
274
279
|
_ts_metadata3("design:type", Function),
|
|
275
280
|
_ts_metadata3("design:paramtypes", [
|
|
276
|
-
typeof
|
|
281
|
+
typeof Pick === "undefined" ? Object : Pick
|
|
277
282
|
])
|
|
278
283
|
], PrismaAuditStatsAdapter);
|
|
279
284
|
|
|
@@ -343,12 +348,149 @@ PrismaMfaAdapter = _ts_decorate5([
|
|
|
343
348
|
_ts_param4(0, (0, import_common5.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
344
349
|
_ts_metadata4("design:type", Function),
|
|
345
350
|
_ts_metadata4("design:paramtypes", [
|
|
346
|
-
typeof
|
|
351
|
+
typeof Pick === "undefined" ? Object : Pick
|
|
347
352
|
])
|
|
348
353
|
], PrismaMfaAdapter);
|
|
349
354
|
|
|
350
355
|
// src/prisma-plan-catalog-import-sink.ts
|
|
351
356
|
var import_common6 = require("@nestjs/common");
|
|
357
|
+
|
|
358
|
+
// src/prisma-plan-binding.ts
|
|
359
|
+
var PRISMA_SCHEMA_OPTIONS_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/adapter-prisma/PrismaSchemaOptions");
|
|
360
|
+
var DEFAULT_SCHEMA_OPTIONS = {
|
|
361
|
+
planBinding: {
|
|
362
|
+
mode: "legacy-plan-key"
|
|
363
|
+
},
|
|
364
|
+
delegates: {
|
|
365
|
+
catalogPlanVersion: "planVersion",
|
|
366
|
+
entitlementPlanVersion: "planVersion"
|
|
367
|
+
},
|
|
368
|
+
planVersionFields: {
|
|
369
|
+
catalog: {
|
|
370
|
+
validityWindows: false,
|
|
371
|
+
endsAt: false
|
|
372
|
+
},
|
|
373
|
+
entitlement: {
|
|
374
|
+
validityWindows: false,
|
|
375
|
+
endsAt: false
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
tenantSubscription: {
|
|
379
|
+
delegate: "subscription",
|
|
380
|
+
subscriptionBundleDelegate: false,
|
|
381
|
+
synchronizePlanVersion: false,
|
|
382
|
+
atomicOnboardingSelection: false,
|
|
383
|
+
activeVersionSelection: "latest-live",
|
|
384
|
+
withEndsAt: false
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
function resolvePrismaSchemaOptions(options) {
|
|
388
|
+
const mode = options?.planBinding?.mode ?? "legacy-plan-key";
|
|
389
|
+
const projectKey = options?.planBinding?.projectKey;
|
|
390
|
+
if (mode === "normalized-plan-id" && !projectKey?.trim()) {
|
|
391
|
+
throw new Error("Prisma plan binding mode 'normalized-plan-id' requires a non-empty projectKey.");
|
|
392
|
+
}
|
|
393
|
+
const sharedFields = options?.planVersionFields;
|
|
394
|
+
const catalogFields = sharedFields?.catalog;
|
|
395
|
+
const entitlementFields = sharedFields?.entitlement;
|
|
396
|
+
return {
|
|
397
|
+
planBinding: {
|
|
398
|
+
mode,
|
|
399
|
+
...projectKey ? {
|
|
400
|
+
projectKey
|
|
401
|
+
} : {}
|
|
402
|
+
},
|
|
403
|
+
delegates: {
|
|
404
|
+
catalogPlanVersion: options?.delegates?.catalogPlanVersion ?? DEFAULT_SCHEMA_OPTIONS.delegates.catalogPlanVersion,
|
|
405
|
+
entitlementPlanVersion: options?.delegates?.entitlementPlanVersion ?? DEFAULT_SCHEMA_OPTIONS.delegates.entitlementPlanVersion
|
|
406
|
+
},
|
|
407
|
+
planVersionFields: {
|
|
408
|
+
catalog: {
|
|
409
|
+
validityWindows: catalogFields?.validityWindows ?? sharedFields?.validityWindows ?? false,
|
|
410
|
+
endsAt: catalogFields?.endsAt ?? sharedFields?.endsAt ?? false
|
|
411
|
+
},
|
|
412
|
+
entitlement: {
|
|
413
|
+
validityWindows: entitlementFields?.validityWindows ?? sharedFields?.validityWindows ?? false,
|
|
414
|
+
endsAt: entitlementFields?.endsAt ?? sharedFields?.endsAt ?? false
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
tenantSubscription: {
|
|
418
|
+
delegate: options?.tenantSubscription?.delegate ?? DEFAULT_SCHEMA_OPTIONS.tenantSubscription.delegate,
|
|
419
|
+
subscriptionBundleDelegate: options?.tenantSubscription?.subscriptionBundleDelegate ?? DEFAULT_SCHEMA_OPTIONS.tenantSubscription.subscriptionBundleDelegate,
|
|
420
|
+
synchronizePlanVersion: options?.tenantSubscription?.synchronizePlanVersion ?? DEFAULT_SCHEMA_OPTIONS.tenantSubscription.synchronizePlanVersion,
|
|
421
|
+
atomicOnboardingSelection: options?.tenantSubscription?.atomicOnboardingSelection ?? DEFAULT_SCHEMA_OPTIONS.tenantSubscription.atomicOnboardingSelection,
|
|
422
|
+
activeVersionSelection: options?.tenantSubscription?.activeVersionSelection ?? DEFAULT_SCHEMA_OPTIONS.tenantSubscription.activeVersionSelection,
|
|
423
|
+
withEndsAt: options?.tenantSubscription?.withEndsAt ?? DEFAULT_SCHEMA_OPTIONS.tenantSubscription.withEndsAt
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
__name(resolvePrismaSchemaOptions, "resolvePrismaSchemaOptions");
|
|
428
|
+
function createPrismaPlanBindingResolver(options) {
|
|
429
|
+
const resolved = resolvePrismaSchemaOptions({
|
|
430
|
+
planBinding: options
|
|
431
|
+
}).planBinding;
|
|
432
|
+
return {
|
|
433
|
+
mode: resolved.mode,
|
|
434
|
+
projectKey: resolved.projectKey,
|
|
435
|
+
async toStoragePlanId(client, planKey, projectKey) {
|
|
436
|
+
if (resolved.mode === "legacy-plan-key") return planKey;
|
|
437
|
+
const scope = resolveProjectKey(resolved.projectKey, projectKey);
|
|
438
|
+
const plan = await asPlanIdentityClient(client).plan.findFirst({
|
|
439
|
+
where: {
|
|
440
|
+
projectKey: scope,
|
|
441
|
+
planKey,
|
|
442
|
+
deletedAt: null
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
if (!plan) {
|
|
446
|
+
throw new Error(`Plan '${planKey}' not found in project '${scope}'.`);
|
|
447
|
+
}
|
|
448
|
+
return plan.id;
|
|
449
|
+
},
|
|
450
|
+
async toPlanKey(client, storedPlanId, projectKey) {
|
|
451
|
+
if (resolved.mode === "legacy-plan-key") return storedPlanId;
|
|
452
|
+
const scope = resolveProjectKey(resolved.projectKey, projectKey);
|
|
453
|
+
const plan = await asPlanIdentityClient(client).plan.findUnique({
|
|
454
|
+
where: {
|
|
455
|
+
id: storedPlanId
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
if (!plan || plan.projectKey !== scope) {
|
|
459
|
+
throw new Error(`Plan id '${storedPlanId}' not found in project '${scope}'.`);
|
|
460
|
+
}
|
|
461
|
+
return plan.planKey;
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
__name(createPrismaPlanBindingResolver, "createPrismaPlanBindingResolver");
|
|
466
|
+
function getPrismaDelegate(client, delegateName) {
|
|
467
|
+
const delegate = client?.[delegateName];
|
|
468
|
+
if (!delegate || typeof delegate !== "object") {
|
|
469
|
+
throw new Error(`Prisma client has no '${delegateName}' delegate.`);
|
|
470
|
+
}
|
|
471
|
+
return delegate;
|
|
472
|
+
}
|
|
473
|
+
__name(getPrismaDelegate, "getPrismaDelegate");
|
|
474
|
+
function resolveProjectKey(configured, requested) {
|
|
475
|
+
const projectKey = requested ?? configured;
|
|
476
|
+
if (!projectKey) {
|
|
477
|
+
throw new Error("Prisma plan binding mode 'normalized-plan-id' requires a projectKey.");
|
|
478
|
+
}
|
|
479
|
+
if (configured && requested && configured !== requested) {
|
|
480
|
+
throw new Error(`Prisma plan binding is configured for project '${configured}', not '${requested}'.`);
|
|
481
|
+
}
|
|
482
|
+
return projectKey;
|
|
483
|
+
}
|
|
484
|
+
__name(resolveProjectKey, "resolveProjectKey");
|
|
485
|
+
function asPlanIdentityClient(client) {
|
|
486
|
+
if (!client || typeof client !== "object" || !("plan" in client)) {
|
|
487
|
+
throw new Error("Prisma client has no 'plan' delegate.");
|
|
488
|
+
}
|
|
489
|
+
return client;
|
|
490
|
+
}
|
|
491
|
+
__name(asPlanIdentityClient, "asPlanIdentityClient");
|
|
492
|
+
|
|
493
|
+
// src/prisma-plan-catalog-import-sink.ts
|
|
352
494
|
function _ts_decorate6(decorators, target, key, desc) {
|
|
353
495
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
354
496
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -371,11 +513,23 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
371
513
|
__name(this, "PrismaPlanCatalogImportSink");
|
|
372
514
|
}
|
|
373
515
|
prisma;
|
|
374
|
-
|
|
516
|
+
binding;
|
|
517
|
+
delegateName;
|
|
518
|
+
constructor(prisma, options) {
|
|
375
519
|
this.prisma = prisma;
|
|
520
|
+
const schema = resolvePrismaSchemaOptions(options);
|
|
521
|
+
this.binding = createPrismaPlanBindingResolver(options?.planBinding);
|
|
522
|
+
this.delegateName = schema.delegates.catalogPlanVersion;
|
|
523
|
+
}
|
|
524
|
+
db() {
|
|
525
|
+
return this.prisma;
|
|
376
526
|
}
|
|
377
527
|
async upsertPlan(input) {
|
|
378
|
-
|
|
528
|
+
if (this.binding.mode === "normalized-plan-id" && this.binding.projectKey !== input.projectKey) {
|
|
529
|
+
throw new Error(`Prisma plan binding is configured for project '${this.binding.projectKey}', not '${input.projectKey}'.`);
|
|
530
|
+
}
|
|
531
|
+
const db = this.db();
|
|
532
|
+
const existing = await db.plan.findFirst({
|
|
379
533
|
where: {
|
|
380
534
|
projectKey: input.projectKey,
|
|
381
535
|
planKey: input.planKey
|
|
@@ -385,7 +539,7 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
385
539
|
created: false,
|
|
386
540
|
skipReason: "exists"
|
|
387
541
|
};
|
|
388
|
-
await
|
|
542
|
+
await db.plan.create({
|
|
389
543
|
data: {
|
|
390
544
|
projectKey: input.projectKey,
|
|
391
545
|
planKey: input.planKey,
|
|
@@ -399,9 +553,11 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
399
553
|
};
|
|
400
554
|
}
|
|
401
555
|
async upsertPlanVersion(input) {
|
|
402
|
-
const
|
|
556
|
+
const planVersion = this.planVersions();
|
|
557
|
+
const storedPlanId = await this.binding.toStoragePlanId(this.prisma, input.planKey);
|
|
558
|
+
const existing = await planVersion.findFirst({
|
|
403
559
|
where: {
|
|
404
|
-
planId:
|
|
560
|
+
planId: storedPlanId,
|
|
405
561
|
version: input.version
|
|
406
562
|
}
|
|
407
563
|
});
|
|
@@ -411,9 +567,9 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
411
567
|
};
|
|
412
568
|
const now = /* @__PURE__ */ new Date();
|
|
413
569
|
if (input.publish) {
|
|
414
|
-
await
|
|
570
|
+
await planVersion.updateMany({
|
|
415
571
|
where: {
|
|
416
|
-
planId:
|
|
572
|
+
planId: storedPlanId,
|
|
417
573
|
publishedAt: {
|
|
418
574
|
not: null
|
|
419
575
|
},
|
|
@@ -427,9 +583,9 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
427
583
|
}
|
|
428
584
|
});
|
|
429
585
|
}
|
|
430
|
-
await
|
|
586
|
+
await planVersion.create({
|
|
431
587
|
data: {
|
|
432
|
-
planId:
|
|
588
|
+
planId: storedPlanId,
|
|
433
589
|
version: input.version,
|
|
434
590
|
features: input.features,
|
|
435
591
|
quotas: input.quotas,
|
|
@@ -444,8 +600,12 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
444
600
|
created: true
|
|
445
601
|
};
|
|
446
602
|
}
|
|
603
|
+
planVersions() {
|
|
604
|
+
return getPrismaDelegate(this.prisma, this.delegateName);
|
|
605
|
+
}
|
|
447
606
|
async upsertFeatureCatalogEntry(input) {
|
|
448
|
-
const
|
|
607
|
+
const db = this.db();
|
|
608
|
+
const existing = await db.featureCatalogEntry.findFirst({
|
|
449
609
|
where: {
|
|
450
610
|
projectKey: input.projectKey,
|
|
451
611
|
featureKey: input.featureKey
|
|
@@ -455,7 +615,7 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
455
615
|
created: false,
|
|
456
616
|
skipReason: "exists"
|
|
457
617
|
};
|
|
458
|
-
await
|
|
618
|
+
await db.featureCatalogEntry.create({
|
|
459
619
|
data: {
|
|
460
620
|
projectKey: input.projectKey,
|
|
461
621
|
featureKey: input.featureKey,
|
|
@@ -474,9 +634,12 @@ var PrismaPlanCatalogImportSink = class {
|
|
|
474
634
|
PrismaPlanCatalogImportSink = _ts_decorate6([
|
|
475
635
|
(0, import_common6.Injectable)(),
|
|
476
636
|
_ts_param5(0, (0, import_common6.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
637
|
+
_ts_param5(1, (0, import_common6.Optional)()),
|
|
638
|
+
_ts_param5(1, (0, import_common6.Inject)(PRISMA_SCHEMA_OPTIONS_TOKEN)),
|
|
477
639
|
_ts_metadata5("design:type", Function),
|
|
478
640
|
_ts_metadata5("design:paramtypes", [
|
|
479
|
-
typeof
|
|
641
|
+
typeof PlanCatalogImportClient === "undefined" ? Object : PlanCatalogImportClient,
|
|
642
|
+
typeof PrismaSchemaOptions === "undefined" ? Object : PrismaSchemaOptions
|
|
480
643
|
])
|
|
481
644
|
], PrismaPlanCatalogImportSink);
|
|
482
645
|
|
|
@@ -523,11 +686,25 @@ var PrismaPlanCatalogReadSink = class {
|
|
|
523
686
|
__name(this, "PrismaPlanCatalogReadSink");
|
|
524
687
|
}
|
|
525
688
|
prisma;
|
|
526
|
-
|
|
689
|
+
binding;
|
|
690
|
+
delegateName;
|
|
691
|
+
fields;
|
|
692
|
+
constructor(prisma, options) {
|
|
527
693
|
this.prisma = prisma;
|
|
694
|
+
const schema = resolvePrismaSchemaOptions(options);
|
|
695
|
+
this.binding = createPrismaPlanBindingResolver(options?.planBinding);
|
|
696
|
+
this.delegateName = schema.delegates.catalogPlanVersion;
|
|
697
|
+
this.fields = schema.planVersionFields.catalog;
|
|
698
|
+
}
|
|
699
|
+
db() {
|
|
700
|
+
return this.prisma;
|
|
528
701
|
}
|
|
529
702
|
async loadSnapshot(projectKey) {
|
|
530
|
-
|
|
703
|
+
if (this.binding.mode === "normalized-plan-id" && this.binding.projectKey !== projectKey) {
|
|
704
|
+
throw new Error(`Prisma plan binding is configured for project '${this.binding.projectKey}', not '${projectKey}'.`);
|
|
705
|
+
}
|
|
706
|
+
const db = this.db();
|
|
707
|
+
const plans = await db.plan.findMany({
|
|
531
708
|
where: {
|
|
532
709
|
projectKey,
|
|
533
710
|
deletedAt: null
|
|
@@ -536,11 +713,17 @@ var PrismaPlanCatalogReadSink = class {
|
|
|
536
713
|
sortOrder: "asc"
|
|
537
714
|
}
|
|
538
715
|
});
|
|
539
|
-
const
|
|
540
|
-
|
|
716
|
+
const planKeysByStoredId = new Map(plans.map((plan) => [
|
|
717
|
+
this.binding.mode === "normalized-plan-id" ? plan.id : plan.planKey,
|
|
718
|
+
plan.planKey
|
|
719
|
+
]));
|
|
720
|
+
const storedPlanIds = [
|
|
721
|
+
...planKeysByStoredId.keys()
|
|
722
|
+
];
|
|
723
|
+
const livePlanVersions = storedPlanIds.length === 0 ? [] : await this.planVersions().findMany({
|
|
541
724
|
where: {
|
|
542
725
|
planId: {
|
|
543
|
-
in:
|
|
726
|
+
in: storedPlanIds
|
|
544
727
|
},
|
|
545
728
|
publishedAt: {
|
|
546
729
|
not: null
|
|
@@ -548,7 +731,7 @@ var PrismaPlanCatalogReadSink = class {
|
|
|
548
731
|
supersededAt: null
|
|
549
732
|
}
|
|
550
733
|
});
|
|
551
|
-
const featureEntries = await
|
|
734
|
+
const featureEntries = await db.featureCatalogEntry.findMany({
|
|
552
735
|
where: {
|
|
553
736
|
projectKey,
|
|
554
737
|
deletedAt: null
|
|
@@ -559,17 +742,29 @@ var PrismaPlanCatalogReadSink = class {
|
|
|
559
742
|
});
|
|
560
743
|
return {
|
|
561
744
|
plans: plans.map(toPlanRow),
|
|
562
|
-
livePlanVersions: livePlanVersions.map(
|
|
745
|
+
livePlanVersions: livePlanVersions.map((row) => {
|
|
746
|
+
const planKey = planKeysByStoredId.get(row.planId);
|
|
747
|
+
if (!planKey) {
|
|
748
|
+
throw new Error(`PlanVersion ${row.id} references plan '${row.planId}' outside project '${projectKey}'.`);
|
|
749
|
+
}
|
|
750
|
+
return toPlanVersionRow(row, planKey, this.fields);
|
|
751
|
+
}),
|
|
563
752
|
featureEntries: featureEntries.map(toFeatureCatalogEntryRow)
|
|
564
753
|
};
|
|
565
754
|
}
|
|
755
|
+
planVersions() {
|
|
756
|
+
return getPrismaDelegate(this.prisma, this.delegateName);
|
|
757
|
+
}
|
|
566
758
|
};
|
|
567
759
|
PrismaPlanCatalogReadSink = _ts_decorate7([
|
|
568
760
|
(0, import_common7.Injectable)(),
|
|
569
761
|
_ts_param6(0, (0, import_common7.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
762
|
+
_ts_param6(1, (0, import_common7.Optional)()),
|
|
763
|
+
_ts_param6(1, (0, import_common7.Inject)(PRISMA_SCHEMA_OPTIONS_TOKEN)),
|
|
570
764
|
_ts_metadata6("design:type", Function),
|
|
571
765
|
_ts_metadata6("design:paramtypes", [
|
|
572
|
-
typeof
|
|
766
|
+
typeof PlanCatalogReadClient === "undefined" ? Object : PlanCatalogReadClient,
|
|
767
|
+
typeof PrismaSchemaOptions === "undefined" ? Object : PrismaSchemaOptions
|
|
573
768
|
])
|
|
574
769
|
], PrismaPlanCatalogReadSink);
|
|
575
770
|
function toPlanRow(row) {
|
|
@@ -587,10 +782,10 @@ function toPlanRow(row) {
|
|
|
587
782
|
};
|
|
588
783
|
}
|
|
589
784
|
__name(toPlanRow, "toPlanRow");
|
|
590
|
-
function toPlanVersionRow(row) {
|
|
591
|
-
|
|
785
|
+
function toPlanVersionRow(row, planKey, fields) {
|
|
786
|
+
const mapped = {
|
|
592
787
|
id: row.id,
|
|
593
|
-
planId:
|
|
788
|
+
planId: planKey,
|
|
594
789
|
version: row.version,
|
|
595
790
|
baseVersionId: row.baseVersionId,
|
|
596
791
|
publishedAt: row.publishedAt ? row.publishedAt.toISOString() : null,
|
|
@@ -598,8 +793,8 @@ function toPlanVersionRow(row) {
|
|
|
598
793
|
publishedChanges: row.publishedChanges ?? null,
|
|
599
794
|
changeNote: row.changeNote,
|
|
600
795
|
nonRegressive: row.nonRegressive,
|
|
601
|
-
validFrom: null,
|
|
602
|
-
validUntil: null,
|
|
796
|
+
validFrom: fields.validityWindows && row.validFrom ? row.validFrom.toISOString() : null,
|
|
797
|
+
validUntil: fields.validityWindows && row.validUntil ? row.validUntil.toISOString() : null,
|
|
603
798
|
createdByUserId: row.createdByUserId,
|
|
604
799
|
publishedByUserId: row.publishedByUserId,
|
|
605
800
|
createdAt: row.createdAt.toISOString(),
|
|
@@ -610,6 +805,10 @@ function toPlanVersionRow(row) {
|
|
|
610
805
|
yearlyNet: String(row.yearlyNet),
|
|
611
806
|
marketed: row.marketed
|
|
612
807
|
};
|
|
808
|
+
if (fields.endsAt) {
|
|
809
|
+
mapped.endsAt = row.endsAt?.toISOString() ?? null;
|
|
810
|
+
}
|
|
811
|
+
return mapped;
|
|
613
812
|
}
|
|
614
813
|
__name(toPlanVersionRow, "toPlanVersionRow");
|
|
615
814
|
function toFeatureCatalogEntryRow(row) {
|
|
@@ -643,6 +842,7 @@ __name(toFeatureCatalogEntryRow, "toFeatureCatalogEntryRow");
|
|
|
643
842
|
|
|
644
843
|
// src/prisma-plan-version.repository.ts
|
|
645
844
|
var import_common8 = require("@nestjs/common");
|
|
845
|
+
var import_types = require("@saasicat/types");
|
|
646
846
|
function _ts_decorate8(decorators, target, key, desc) {
|
|
647
847
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
648
848
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -665,14 +865,29 @@ var PrismaPlanVersionRepository = class {
|
|
|
665
865
|
__name(this, "PrismaPlanVersionRepository");
|
|
666
866
|
}
|
|
667
867
|
prisma;
|
|
668
|
-
|
|
868
|
+
findActive;
|
|
869
|
+
binding;
|
|
870
|
+
delegateName;
|
|
871
|
+
fields;
|
|
872
|
+
constructor(prisma, options) {
|
|
669
873
|
this.prisma = prisma;
|
|
874
|
+
const schema = resolvePrismaSchemaOptions(options);
|
|
875
|
+
this.binding = createPrismaPlanBindingResolver(options?.planBinding);
|
|
876
|
+
this.delegateName = schema.delegates.entitlementPlanVersion;
|
|
877
|
+
this.fields = schema.planVersionFields.entitlement;
|
|
878
|
+
if (this.fields.validityWindows) {
|
|
879
|
+
this.findActive = (planId, asOf = /* @__PURE__ */ new Date(), tx) => this.findActivePlanVersion(planId, asOf, tx);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
db(tx) {
|
|
883
|
+
return tx ?? this.prisma;
|
|
670
884
|
}
|
|
671
885
|
async findLatestLive(planId, tx) {
|
|
672
|
-
const db =
|
|
673
|
-
const
|
|
886
|
+
const db = this.db(tx);
|
|
887
|
+
const storedPlanId = await this.binding.toStoragePlanId(db, planId);
|
|
888
|
+
const row = await this.versions(db).findFirst({
|
|
674
889
|
where: {
|
|
675
|
-
planId,
|
|
890
|
+
planId: storedPlanId,
|
|
676
891
|
publishedAt: {
|
|
677
892
|
not: null
|
|
678
893
|
},
|
|
@@ -683,8 +898,40 @@ var PrismaPlanVersionRepository = class {
|
|
|
683
898
|
}
|
|
684
899
|
});
|
|
685
900
|
if (!row) return null;
|
|
901
|
+
return this.toRecord(db, row);
|
|
902
|
+
}
|
|
903
|
+
async findActivePlanVersion(planId, asOf, tx) {
|
|
904
|
+
const db = this.db(tx);
|
|
905
|
+
const storedPlanId = await this.binding.toStoragePlanId(db, planId);
|
|
906
|
+
const activeWhere = this.fields.endsAt ? (0, import_types.buildActivePlanVersionWhere)(asOf, {
|
|
907
|
+
withEndsAt: true
|
|
908
|
+
}) : (0, import_types.buildActivePlanVersionWhere)(asOf);
|
|
909
|
+
const row = await this.versions(db).findFirst({
|
|
910
|
+
where: {
|
|
911
|
+
planId: storedPlanId,
|
|
912
|
+
...activeWhere
|
|
913
|
+
},
|
|
914
|
+
orderBy: [
|
|
915
|
+
{
|
|
916
|
+
validFrom: {
|
|
917
|
+
sort: "desc",
|
|
918
|
+
nulls: "last"
|
|
919
|
+
}
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
version: "desc"
|
|
923
|
+
}
|
|
924
|
+
]
|
|
925
|
+
});
|
|
926
|
+
if (!row) return null;
|
|
927
|
+
return this.toRecord(db, row);
|
|
928
|
+
}
|
|
929
|
+
versions(client) {
|
|
930
|
+
return getPrismaDelegate(client, this.delegateName);
|
|
931
|
+
}
|
|
932
|
+
async toRecord(client, row) {
|
|
686
933
|
return {
|
|
687
|
-
planId: row.planId,
|
|
934
|
+
planId: await this.binding.toPlanKey(client, row.planId),
|
|
688
935
|
quotas: toQuotaMap(row.quotas),
|
|
689
936
|
features: toStringArray(row.features)
|
|
690
937
|
};
|
|
@@ -693,9 +940,12 @@ var PrismaPlanVersionRepository = class {
|
|
|
693
940
|
PrismaPlanVersionRepository = _ts_decorate8([
|
|
694
941
|
(0, import_common8.Injectable)(),
|
|
695
942
|
_ts_param7(0, (0, import_common8.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
943
|
+
_ts_param7(1, (0, import_common8.Optional)()),
|
|
944
|
+
_ts_param7(1, (0, import_common8.Inject)(PRISMA_SCHEMA_OPTIONS_TOKEN)),
|
|
696
945
|
_ts_metadata7("design:type", Function),
|
|
697
946
|
_ts_metadata7("design:paramtypes", [
|
|
698
|
-
typeof
|
|
947
|
+
typeof PlanVersionRepositoryClient === "undefined" ? Object : PlanVersionRepositoryClient,
|
|
948
|
+
typeof PrismaSchemaOptions === "undefined" ? Object : PrismaSchemaOptions
|
|
699
949
|
])
|
|
700
950
|
], PrismaPlanVersionRepository);
|
|
701
951
|
|
|
@@ -1163,19 +1413,35 @@ var PrismaSubscriptionRepository = class {
|
|
|
1163
1413
|
__name(this, "PrismaSubscriptionRepository");
|
|
1164
1414
|
}
|
|
1165
1415
|
prisma;
|
|
1166
|
-
|
|
1416
|
+
countByBundleVersionId;
|
|
1417
|
+
binding;
|
|
1418
|
+
planVersionDelegateName;
|
|
1419
|
+
subscriptionDelegateName;
|
|
1420
|
+
subscriptionBundleDelegateName;
|
|
1421
|
+
constructor(prisma, options) {
|
|
1167
1422
|
this.prisma = prisma;
|
|
1423
|
+
const schema = resolvePrismaSchemaOptions(options);
|
|
1424
|
+
this.binding = createPrismaPlanBindingResolver(options?.planBinding);
|
|
1425
|
+
this.planVersionDelegateName = schema.delegates.entitlementPlanVersion;
|
|
1426
|
+
this.subscriptionDelegateName = schema.tenantSubscription.delegate;
|
|
1427
|
+
this.subscriptionBundleDelegateName = schema.tenantSubscription.subscriptionBundleDelegate;
|
|
1428
|
+
if (this.subscriptionBundleDelegateName) {
|
|
1429
|
+
this.countByBundleVersionId = (bundleVersionId) => this.countActiveBundleBindings(bundleVersionId);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
db(tx) {
|
|
1433
|
+
return tx ?? this.prisma;
|
|
1168
1434
|
}
|
|
1169
1435
|
async findByTenantId(tenantId) {
|
|
1170
|
-
return this.loadByTenantId(this.
|
|
1436
|
+
return this.loadByTenantId(this.db(), tenantId);
|
|
1171
1437
|
}
|
|
1172
1438
|
async findByTenantIdLocked(tenantId, tx) {
|
|
1173
|
-
const db =
|
|
1439
|
+
const db = this.db(tx);
|
|
1174
1440
|
await db.$queryRaw`SELECT id FROM subscriptions WHERE "tenantId" = ${tenantId} FOR UPDATE`;
|
|
1175
1441
|
return this.loadByTenantId(db, tenantId);
|
|
1176
1442
|
}
|
|
1177
1443
|
async countByPlanVersionId(planVersionId) {
|
|
1178
|
-
return this.
|
|
1444
|
+
return this.subscriptions(this.db()).count({
|
|
1179
1445
|
where: {
|
|
1180
1446
|
OR: [
|
|
1181
1447
|
{
|
|
@@ -1188,22 +1454,72 @@ var PrismaSubscriptionRepository = class {
|
|
|
1188
1454
|
}
|
|
1189
1455
|
});
|
|
1190
1456
|
}
|
|
1191
|
-
async countActiveByPlanKey(
|
|
1192
|
-
const
|
|
1457
|
+
async countActiveByPlanKey(projectKey) {
|
|
1458
|
+
const db = this.db();
|
|
1459
|
+
const subscriptions = await this.subscriptions(db).findMany({
|
|
1193
1460
|
where: {
|
|
1194
1461
|
status: {
|
|
1195
1462
|
in: ACTIVE_STATUSES
|
|
1196
1463
|
}
|
|
1464
|
+
},
|
|
1465
|
+
select: {
|
|
1466
|
+
planVersionId: true
|
|
1197
1467
|
}
|
|
1198
1468
|
});
|
|
1469
|
+
const versionIds = [
|
|
1470
|
+
...new Set(subscriptions.map((subscription) => subscription.planVersionId))
|
|
1471
|
+
];
|
|
1472
|
+
if (versionIds.length === 0) return {};
|
|
1473
|
+
const planVersions = await this.planVersions(db).findMany({
|
|
1474
|
+
where: {
|
|
1475
|
+
id: {
|
|
1476
|
+
in: versionIds
|
|
1477
|
+
}
|
|
1478
|
+
},
|
|
1479
|
+
select: {
|
|
1480
|
+
id: true,
|
|
1481
|
+
planId: true
|
|
1482
|
+
}
|
|
1483
|
+
});
|
|
1484
|
+
const planVersionById = new Map(planVersions.map((planVersion) => [
|
|
1485
|
+
planVersion.id,
|
|
1486
|
+
planVersion
|
|
1487
|
+
]));
|
|
1488
|
+
const storedPlanIds = [
|
|
1489
|
+
...new Set(planVersions.map((planVersion) => planVersion.planId))
|
|
1490
|
+
];
|
|
1491
|
+
const planKeyByStoredId = await this.planKeysForProject(db, storedPlanIds, projectKey);
|
|
1199
1492
|
const counts = {};
|
|
1200
|
-
for (const
|
|
1201
|
-
|
|
1493
|
+
for (const subscription of subscriptions) {
|
|
1494
|
+
const planVersion = planVersionById.get(subscription.planVersionId);
|
|
1495
|
+
const planKey = planVersion ? planKeyByStoredId.get(planVersion.planId) : void 0;
|
|
1496
|
+
if (!planKey) continue;
|
|
1497
|
+
counts[planKey] = (counts[planKey] ?? 0) + 1;
|
|
1202
1498
|
}
|
|
1203
1499
|
return counts;
|
|
1204
1500
|
}
|
|
1501
|
+
async countActiveBundleBindings(bundleVersionId) {
|
|
1502
|
+
if (!this.subscriptionBundleDelegateName) {
|
|
1503
|
+
throw new Error("SubscriptionBundle counting is not configured.");
|
|
1504
|
+
}
|
|
1505
|
+
return getPrismaDelegate(this.prisma, this.subscriptionBundleDelegateName).count({
|
|
1506
|
+
where: {
|
|
1507
|
+
bundleVersionId,
|
|
1508
|
+
OR: [
|
|
1509
|
+
{
|
|
1510
|
+
canceledAt: null
|
|
1511
|
+
},
|
|
1512
|
+
{
|
|
1513
|
+
canceledEffectiveAt: {
|
|
1514
|
+
gt: /* @__PURE__ */ new Date()
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
]
|
|
1518
|
+
}
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1205
1521
|
async loadByTenantId(db, tenantId) {
|
|
1206
|
-
const row = await db.
|
|
1522
|
+
const row = await this.subscriptions(db).findUnique({
|
|
1207
1523
|
where: {
|
|
1208
1524
|
tenantId
|
|
1209
1525
|
}
|
|
@@ -1212,7 +1528,7 @@ var PrismaSubscriptionRepository = class {
|
|
|
1212
1528
|
return this.toRecord(db, row);
|
|
1213
1529
|
}
|
|
1214
1530
|
async toRecord(db, row) {
|
|
1215
|
-
const planVersion = await db.
|
|
1531
|
+
const planVersion = await this.planVersions(db).findUnique({
|
|
1216
1532
|
where: {
|
|
1217
1533
|
id: row.planVersionId
|
|
1218
1534
|
}
|
|
@@ -1220,10 +1536,13 @@ var PrismaSubscriptionRepository = class {
|
|
|
1220
1536
|
if (!planVersion) {
|
|
1221
1537
|
throw new Error(`Subscription ${row.id} references missing PlanVersion ${row.planVersionId}.`);
|
|
1222
1538
|
}
|
|
1539
|
+
const planKey = await this.binding.toPlanKey(db, planVersion.planId);
|
|
1223
1540
|
return {
|
|
1224
1541
|
id: row.id,
|
|
1225
1542
|
tenantId: row.tenantId,
|
|
1226
|
-
|
|
1543
|
+
// The concrete PlanVersion is authoritative. This also normalizes
|
|
1544
|
+
// legacy rows whose denormalized `Subscription.plan` drifted.
|
|
1545
|
+
plan: planKey,
|
|
1227
1546
|
status: row.status,
|
|
1228
1547
|
isPilot: row.isPilot,
|
|
1229
1548
|
trialEntitlementPlan: row.trialEntitlementPlan,
|
|
@@ -1232,25 +1551,84 @@ var PrismaSubscriptionRepository = class {
|
|
|
1232
1551
|
customLimits: row.customLimits ?? null,
|
|
1233
1552
|
planVersionId: row.planVersionId,
|
|
1234
1553
|
planVersion: {
|
|
1235
|
-
planId:
|
|
1554
|
+
planId: planKey,
|
|
1236
1555
|
quotas: toQuotaMap(planVersion.quotas),
|
|
1237
1556
|
features: toStringArray(planVersion.features)
|
|
1238
1557
|
}
|
|
1239
1558
|
};
|
|
1240
1559
|
}
|
|
1560
|
+
planVersions(client) {
|
|
1561
|
+
return getPrismaDelegate(client, this.planVersionDelegateName);
|
|
1562
|
+
}
|
|
1563
|
+
subscriptions(client) {
|
|
1564
|
+
return getPrismaDelegate(client, this.subscriptionDelegateName);
|
|
1565
|
+
}
|
|
1566
|
+
async planKeysForProject(db, storedPlanIds, projectKey) {
|
|
1567
|
+
if (storedPlanIds.length === 0) return /* @__PURE__ */ new Map();
|
|
1568
|
+
if (this.binding.mode === "normalized-plan-id") {
|
|
1569
|
+
if (this.binding.projectKey && this.binding.projectKey !== projectKey) {
|
|
1570
|
+
throw new Error(`Prisma plan binding is configured for project '${this.binding.projectKey}', not '${projectKey}'.`);
|
|
1571
|
+
}
|
|
1572
|
+
const plans2 = await db.plan.findMany({
|
|
1573
|
+
where: {
|
|
1574
|
+
projectKey,
|
|
1575
|
+
id: {
|
|
1576
|
+
in: storedPlanIds
|
|
1577
|
+
}
|
|
1578
|
+
},
|
|
1579
|
+
select: {
|
|
1580
|
+
id: true,
|
|
1581
|
+
planKey: true
|
|
1582
|
+
}
|
|
1583
|
+
});
|
|
1584
|
+
return new Map(plans2.map((plan) => [
|
|
1585
|
+
plan.id,
|
|
1586
|
+
plan.planKey
|
|
1587
|
+
]));
|
|
1588
|
+
}
|
|
1589
|
+
const plans = await db.plan.findMany({
|
|
1590
|
+
where: {
|
|
1591
|
+
planKey: {
|
|
1592
|
+
in: storedPlanIds
|
|
1593
|
+
}
|
|
1594
|
+
},
|
|
1595
|
+
select: {
|
|
1596
|
+
projectKey: true,
|
|
1597
|
+
planKey: true
|
|
1598
|
+
}
|
|
1599
|
+
});
|
|
1600
|
+
const projectsByPlanKey = /* @__PURE__ */ new Map();
|
|
1601
|
+
for (const plan of plans) {
|
|
1602
|
+
const projects = projectsByPlanKey.get(plan.planKey) ?? /* @__PURE__ */ new Set();
|
|
1603
|
+
projects.add(plan.projectKey);
|
|
1604
|
+
projectsByPlanKey.set(plan.planKey, projects);
|
|
1605
|
+
}
|
|
1606
|
+
return new Map(storedPlanIds.flatMap((planKey) => {
|
|
1607
|
+
const projects = projectsByPlanKey.get(planKey);
|
|
1608
|
+
return projects?.size === 1 && projects.has(projectKey) ? [
|
|
1609
|
+
[
|
|
1610
|
+
planKey,
|
|
1611
|
+
planKey
|
|
1612
|
+
]
|
|
1613
|
+
] : [];
|
|
1614
|
+
}));
|
|
1615
|
+
}
|
|
1241
1616
|
};
|
|
1242
1617
|
PrismaSubscriptionRepository = _ts_decorate13([
|
|
1243
1618
|
(0, import_common13.Injectable)(),
|
|
1244
1619
|
_ts_param12(0, (0, import_common13.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
1620
|
+
_ts_param12(1, (0, import_common13.Optional)()),
|
|
1621
|
+
_ts_param12(1, (0, import_common13.Inject)(PRISMA_SCHEMA_OPTIONS_TOKEN)),
|
|
1245
1622
|
_ts_metadata12("design:type", Function),
|
|
1246
1623
|
_ts_metadata12("design:paramtypes", [
|
|
1247
|
-
typeof
|
|
1624
|
+
typeof SubscriptionRepositoryClient === "undefined" ? Object : SubscriptionRepositoryClient,
|
|
1625
|
+
typeof PrismaSchemaOptions === "undefined" ? Object : PrismaSchemaOptions
|
|
1248
1626
|
])
|
|
1249
1627
|
], PrismaSubscriptionRepository);
|
|
1250
1628
|
|
|
1251
1629
|
// src/prisma-super-admin-bootstrap.adapter.ts
|
|
1252
1630
|
var import_common14 = require("@nestjs/common");
|
|
1253
|
-
var
|
|
1631
|
+
var import_types2 = require("@saasicat/types");
|
|
1254
1632
|
function _ts_decorate14(decorators, target, key, desc) {
|
|
1255
1633
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1256
1634
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1295,7 +1673,7 @@ var PrismaSuperAdminBootstrapAdapter = class {
|
|
|
1295
1673
|
}
|
|
1296
1674
|
});
|
|
1297
1675
|
if (existing) {
|
|
1298
|
-
throw new
|
|
1676
|
+
throw new import_types2.PlatformUserExistsError(email, existing.platformRole);
|
|
1299
1677
|
}
|
|
1300
1678
|
const row = await this.prisma.superAdminUser.create({
|
|
1301
1679
|
data: {
|
|
@@ -1356,7 +1734,8 @@ var PrismaTransactionRunner = class {
|
|
|
1356
1734
|
this.prisma = prisma;
|
|
1357
1735
|
}
|
|
1358
1736
|
async run(fn) {
|
|
1359
|
-
|
|
1737
|
+
const transaction = this.prisma.$transaction.bind(this.prisma);
|
|
1738
|
+
return transaction((tx) => fn(tx));
|
|
1360
1739
|
}
|
|
1361
1740
|
};
|
|
1362
1741
|
PrismaTransactionRunner = _ts_decorate15([
|
|
@@ -1364,7 +1743,7 @@ PrismaTransactionRunner = _ts_decorate15([
|
|
|
1364
1743
|
_ts_param14(0, (0, import_common15.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
1365
1744
|
_ts_metadata14("design:type", Function),
|
|
1366
1745
|
_ts_metadata14("design:paramtypes", [
|
|
1367
|
-
typeof
|
|
1746
|
+
typeof Record === "undefined" ? Object : Record
|
|
1368
1747
|
])
|
|
1369
1748
|
], PrismaTransactionRunner);
|
|
1370
1749
|
|
|
@@ -1415,8 +1794,8 @@ function prismaPersistence(options) {
|
|
|
1415
1794
|
superAdminProvisioning: buildProvisioning(client, options.passwordHasher)
|
|
1416
1795
|
},
|
|
1417
1796
|
entitlement: {
|
|
1418
|
-
subscriptionRepository: provide((prisma) => new PrismaSubscriptionRepository(prisma)),
|
|
1419
|
-
planVersionRepository: provide((prisma) => new PrismaPlanVersionRepository(prisma))
|
|
1797
|
+
subscriptionRepository: provide((prisma) => new PrismaSubscriptionRepository(prisma, options.schema)),
|
|
1798
|
+
planVersionRepository: provide((prisma) => new PrismaPlanVersionRepository(prisma, options.schema))
|
|
1420
1799
|
},
|
|
1421
1800
|
promo: {
|
|
1422
1801
|
promoCodeRepository: provide((prisma) => new PrismaPromoCodeRepository(prisma)),
|
|
@@ -1425,8 +1804,8 @@ function prismaPersistence(options) {
|
|
|
1425
1804
|
subscriptionLookup: provide((prisma) => new PrismaPromoSubscriptionLookup(prisma)),
|
|
1426
1805
|
revenueAggregator: new ZeroPromoRevenueDeductionAggregator()
|
|
1427
1806
|
},
|
|
1428
|
-
planCatalogReadSink: provide((prisma) => new PrismaPlanCatalogReadSink(prisma)),
|
|
1429
|
-
planCatalogImportSink: provide((prisma) => new PrismaPlanCatalogImportSink(prisma))
|
|
1807
|
+
planCatalogReadSink: provide((prisma) => new PrismaPlanCatalogReadSink(prisma, options.schema)),
|
|
1808
|
+
planCatalogImportSink: provide((prisma) => new PrismaPlanCatalogImportSink(prisma, options.schema))
|
|
1430
1809
|
};
|
|
1431
1810
|
}
|
|
1432
1811
|
__name(prismaPersistence, "prismaPersistence");
|
|
@@ -1495,7 +1874,7 @@ var PrismaSubscriptionBundleRepository = class {
|
|
|
1495
1874
|
this.prisma = prisma;
|
|
1496
1875
|
}
|
|
1497
1876
|
db(tx) {
|
|
1498
|
-
return
|
|
1877
|
+
return tx ?? this.prisma;
|
|
1499
1878
|
}
|
|
1500
1879
|
async listBySubscription(subscriptionId) {
|
|
1501
1880
|
const rows = await this.db().subscriptionBundle.findMany({
|
|
@@ -1595,7 +1974,7 @@ PrismaSubscriptionBundleRepository = _ts_decorate17([
|
|
|
1595
1974
|
_ts_param15(0, (0, import_common17.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
1596
1975
|
_ts_metadata15("design:type", Function),
|
|
1597
1976
|
_ts_metadata15("design:paramtypes", [
|
|
1598
|
-
typeof
|
|
1977
|
+
typeof SubscriptionBundleClient === "undefined" ? Object : SubscriptionBundleClient
|
|
1599
1978
|
])
|
|
1600
1979
|
], PrismaSubscriptionBundleRepository);
|
|
1601
1980
|
function toRecord3(row) {
|
|
@@ -1615,6 +1994,7 @@ __name(toRecord3, "toRecord");
|
|
|
1615
1994
|
|
|
1616
1995
|
// src/prisma-tenant-subscription-write.adapter.ts
|
|
1617
1996
|
var import_common18 = require("@nestjs/common");
|
|
1997
|
+
var import_types3 = require("@saasicat/types");
|
|
1618
1998
|
function _ts_decorate18(decorators, target, key, desc) {
|
|
1619
1999
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1620
2000
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1637,37 +2017,66 @@ var PrismaTenantSubscriptionWriteAdapter = class {
|
|
|
1637
2017
|
__name(this, "PrismaTenantSubscriptionWriteAdapter");
|
|
1638
2018
|
}
|
|
1639
2019
|
prisma;
|
|
1640
|
-
|
|
2020
|
+
applyOnboardingSelection;
|
|
2021
|
+
schema;
|
|
2022
|
+
planBinding;
|
|
2023
|
+
constructor(prisma, options) {
|
|
1641
2024
|
this.prisma = prisma;
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
2025
|
+
this.schema = resolvePrismaSchemaOptions(options);
|
|
2026
|
+
this.planBinding = createPrismaPlanBindingResolver(options?.planBinding);
|
|
2027
|
+
this.assertConfiguration();
|
|
2028
|
+
if (this.schema.tenantSubscription.atomicOnboardingSelection) {
|
|
2029
|
+
this.applyOnboardingSelection = (tenantId, input, redeemPromo) => this.applyOnboardingSelectionAtomic(tenantId, input, redeemPromo);
|
|
2030
|
+
}
|
|
1645
2031
|
}
|
|
1646
2032
|
async changePlanImmediate(tenantId, input) {
|
|
1647
|
-
|
|
2033
|
+
if (this.schema.tenantSubscription.synchronizePlanVersion) {
|
|
2034
|
+
return this.prisma.$transaction((tx) => this.changePlanImmediateInClient(tx, tenantId, input));
|
|
2035
|
+
}
|
|
2036
|
+
return this.changePlanImmediateInClient(this.prisma, tenantId, input);
|
|
2037
|
+
}
|
|
2038
|
+
async changePlanImmediateInClient(client, tenantId, input) {
|
|
2039
|
+
const subscription = this.subscription(client);
|
|
2040
|
+
const data = {
|
|
2041
|
+
plan: input.planId,
|
|
2042
|
+
billingCycle: input.cycle,
|
|
2043
|
+
pendingPlan: null,
|
|
2044
|
+
pendingBillingCycle: null,
|
|
2045
|
+
pendingEffectiveAt: null,
|
|
2046
|
+
...input.nextStatus ? {
|
|
2047
|
+
status: input.nextStatus
|
|
2048
|
+
} : {},
|
|
2049
|
+
...input.periodStart && input.periodEnd ? {
|
|
2050
|
+
currentPeriodStart: input.periodStart,
|
|
2051
|
+
currentPeriodEnd: input.periodEnd
|
|
2052
|
+
} : {},
|
|
2053
|
+
// #17: the platform changePlan path computes the carried-over
|
|
2054
|
+
// trial end and passes it through; null/undefined leaves the
|
|
2055
|
+
// existing trialEndsAt untouched.
|
|
2056
|
+
...input.trialEndsAt ? {
|
|
2057
|
+
trialEndsAt: input.trialEndsAt
|
|
2058
|
+
} : {}
|
|
2059
|
+
};
|
|
2060
|
+
if (this.schema.tenantSubscription.synchronizePlanVersion) {
|
|
2061
|
+
const current = await subscription.findUnique({
|
|
2062
|
+
where: {
|
|
2063
|
+
tenantId
|
|
2064
|
+
}
|
|
2065
|
+
});
|
|
2066
|
+
if (!current) {
|
|
2067
|
+
throw new Error(`No subscription for tenant ${tenantId}.`);
|
|
2068
|
+
}
|
|
2069
|
+
const storagePlanId = await this.planBinding.toStoragePlanId(client, input.planId);
|
|
2070
|
+
data.planVersionId = await this.findTargetPlanVersionId(client, storagePlanId, input.periodStart ?? /* @__PURE__ */ new Date());
|
|
2071
|
+
if (await this.pendingVersionBelongsToAnotherPlan(client, current.pendingPlanVersionId, storagePlanId)) {
|
|
2072
|
+
Object.assign(data, clearedPendingVersionData());
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
const updated = await subscription.update({
|
|
1648
2076
|
where: {
|
|
1649
2077
|
tenantId
|
|
1650
2078
|
},
|
|
1651
|
-
data
|
|
1652
|
-
plan: input.planId,
|
|
1653
|
-
billingCycle: input.cycle,
|
|
1654
|
-
pendingPlan: null,
|
|
1655
|
-
pendingBillingCycle: null,
|
|
1656
|
-
pendingEffectiveAt: null,
|
|
1657
|
-
...input.nextStatus ? {
|
|
1658
|
-
status: input.nextStatus
|
|
1659
|
-
} : {},
|
|
1660
|
-
...input.periodStart && input.periodEnd ? {
|
|
1661
|
-
currentPeriodStart: input.periodStart,
|
|
1662
|
-
currentPeriodEnd: input.periodEnd
|
|
1663
|
-
} : {},
|
|
1664
|
-
// #17: the platform changePlan path computes the carried-over
|
|
1665
|
-
// trial end and passes it through; null/undefined leaves the
|
|
1666
|
-
// existing trialEndsAt untouched.
|
|
1667
|
-
...input.trialEndsAt ? {
|
|
1668
|
-
trialEndsAt: input.trialEndsAt
|
|
1669
|
-
} : {}
|
|
1670
|
-
}
|
|
2079
|
+
data
|
|
1671
2080
|
});
|
|
1672
2081
|
return {
|
|
1673
2082
|
plan: updated.plan,
|
|
@@ -1675,7 +2084,7 @@ var PrismaTenantSubscriptionWriteAdapter = class {
|
|
|
1675
2084
|
};
|
|
1676
2085
|
}
|
|
1677
2086
|
async schedulePlanChange(tenantId, input) {
|
|
1678
|
-
await this.
|
|
2087
|
+
await this.subscription(this.prisma).update({
|
|
1679
2088
|
where: {
|
|
1680
2089
|
tenantId
|
|
1681
2090
|
},
|
|
@@ -1687,7 +2096,8 @@ var PrismaTenantSubscriptionWriteAdapter = class {
|
|
|
1687
2096
|
});
|
|
1688
2097
|
}
|
|
1689
2098
|
async acceptPendingPlanVersion(tenantId, userId, now) {
|
|
1690
|
-
const
|
|
2099
|
+
const subscription = this.subscription(this.prisma);
|
|
2100
|
+
const sub = await subscription.findUnique({
|
|
1691
2101
|
where: {
|
|
1692
2102
|
tenantId
|
|
1693
2103
|
}
|
|
@@ -1695,17 +2105,15 @@ var PrismaTenantSubscriptionWriteAdapter = class {
|
|
|
1695
2105
|
if (!sub) {
|
|
1696
2106
|
throw new Error(`No subscription for tenant ${tenantId}.`);
|
|
1697
2107
|
}
|
|
1698
|
-
if (sub.
|
|
1699
|
-
|
|
1700
|
-
accepted: true,
|
|
1701
|
-
acceptedAt: sub.pendingPlanVersionAcceptedAt,
|
|
1702
|
-
effectiveAt: sub.pendingPlanVersionEffectiveAt,
|
|
1703
|
-
alreadyAccepted: true
|
|
1704
|
-
};
|
|
2108
|
+
if (!sub.pendingPlanVersionId) {
|
|
2109
|
+
throw new Error(`No pending PlanVersion for tenant ${tenantId}.`);
|
|
1705
2110
|
}
|
|
1706
|
-
const
|
|
2111
|
+
const pendingPlanVersionId = sub.pendingPlanVersionId;
|
|
2112
|
+
const claimed = await subscription.updateMany({
|
|
1707
2113
|
where: {
|
|
1708
|
-
id: sub.id
|
|
2114
|
+
id: sub.id,
|
|
2115
|
+
pendingPlanVersionId,
|
|
2116
|
+
pendingPlanVersionAccepted: false
|
|
1709
2117
|
},
|
|
1710
2118
|
data: {
|
|
1711
2119
|
pendingPlanVersionAccepted: true,
|
|
@@ -1713,15 +2121,66 @@ var PrismaTenantSubscriptionWriteAdapter = class {
|
|
|
1713
2121
|
pendingPlanVersionAcceptedByUserId: userId
|
|
1714
2122
|
}
|
|
1715
2123
|
});
|
|
2124
|
+
const updated = await subscription.findUnique({
|
|
2125
|
+
where: {
|
|
2126
|
+
id: sub.id
|
|
2127
|
+
}
|
|
2128
|
+
});
|
|
2129
|
+
if (!updated) {
|
|
2130
|
+
throw new Error(`No subscription for tenant ${tenantId}.`);
|
|
2131
|
+
}
|
|
2132
|
+
if (claimed.count === 0 && (updated.pendingPlanVersionId !== pendingPlanVersionId || !updated.pendingPlanVersionAccepted)) {
|
|
2133
|
+
throw new Error(`Pending PlanVersion changed while accepting it for tenant ${tenantId}.`);
|
|
2134
|
+
}
|
|
1716
2135
|
return {
|
|
1717
2136
|
accepted: true,
|
|
1718
2137
|
acceptedAt: updated.pendingPlanVersionAcceptedAt,
|
|
1719
2138
|
effectiveAt: updated.pendingPlanVersionEffectiveAt,
|
|
1720
|
-
alreadyAccepted:
|
|
2139
|
+
alreadyAccepted: claimed.count === 0
|
|
1721
2140
|
};
|
|
1722
2141
|
}
|
|
2142
|
+
async applyOnboardingSelectionAtomic(tenantId, input, redeemPromo) {
|
|
2143
|
+
return this.prisma.$transaction(async (tx) => {
|
|
2144
|
+
const data = {
|
|
2145
|
+
plan: input.planId,
|
|
2146
|
+
billingCycle: input.cycle,
|
|
2147
|
+
pendingPlan: null,
|
|
2148
|
+
pendingBillingCycle: null,
|
|
2149
|
+
pendingEffectiveAt: null,
|
|
2150
|
+
...clearedPendingVersionData(),
|
|
2151
|
+
...input.nextStatus ? {
|
|
2152
|
+
status: input.nextStatus
|
|
2153
|
+
} : {},
|
|
2154
|
+
...input.periodStart && input.periodEnd ? {
|
|
2155
|
+
currentPeriodStart: input.periodStart,
|
|
2156
|
+
currentPeriodEnd: input.periodEnd
|
|
2157
|
+
} : {}
|
|
2158
|
+
};
|
|
2159
|
+
if (this.schema.tenantSubscription.synchronizePlanVersion) {
|
|
2160
|
+
const storagePlanId = await this.planBinding.toStoragePlanId(tx, input.planId);
|
|
2161
|
+
data.planVersionId = await this.findTargetPlanVersionId(tx, storagePlanId, input.periodStart ?? /* @__PURE__ */ new Date());
|
|
2162
|
+
}
|
|
2163
|
+
const updated = await this.subscription(tx).update({
|
|
2164
|
+
where: {
|
|
2165
|
+
tenantId
|
|
2166
|
+
},
|
|
2167
|
+
data
|
|
2168
|
+
});
|
|
2169
|
+
let promoRedemption = null;
|
|
2170
|
+
if (redeemPromo) {
|
|
2171
|
+
promoRedemption = await redeemPromo(tx, updated.id);
|
|
2172
|
+
}
|
|
2173
|
+
return {
|
|
2174
|
+
plan: updated.plan,
|
|
2175
|
+
billingCycle: updated.billingCycle,
|
|
2176
|
+
subscriptionId: updated.id,
|
|
2177
|
+
promoRedemption
|
|
2178
|
+
};
|
|
2179
|
+
});
|
|
2180
|
+
}
|
|
1723
2181
|
async cancelSubscription(tenantId, immediate, now) {
|
|
1724
|
-
const
|
|
2182
|
+
const subscription = this.subscription(this.prisma);
|
|
2183
|
+
const sub = await subscription.findUnique({
|
|
1725
2184
|
where: {
|
|
1726
2185
|
tenantId
|
|
1727
2186
|
}
|
|
@@ -1730,7 +2189,7 @@ var PrismaTenantSubscriptionWriteAdapter = class {
|
|
|
1730
2189
|
throw new Error(`No subscription for tenant ${tenantId}.`);
|
|
1731
2190
|
}
|
|
1732
2191
|
const canceledAt = immediate ? now : sub.currentPeriodEnd ?? now;
|
|
1733
|
-
const updated = await
|
|
2192
|
+
const updated = await subscription.update({
|
|
1734
2193
|
where: {
|
|
1735
2194
|
tenantId
|
|
1736
2195
|
},
|
|
@@ -1744,18 +2203,107 @@ var PrismaTenantSubscriptionWriteAdapter = class {
|
|
|
1744
2203
|
status: updated.status
|
|
1745
2204
|
};
|
|
1746
2205
|
}
|
|
2206
|
+
subscription(client) {
|
|
2207
|
+
return getPrismaDelegate(client, this.schema.tenantSubscription.delegate);
|
|
2208
|
+
}
|
|
2209
|
+
planVersions(client) {
|
|
2210
|
+
return getPrismaDelegate(client, this.schema.delegates.entitlementPlanVersion);
|
|
2211
|
+
}
|
|
2212
|
+
async findTargetPlanVersionId(client, storagePlanId, asOf) {
|
|
2213
|
+
const activeWindow = this.schema.tenantSubscription.activeVersionSelection === "validity-window";
|
|
2214
|
+
const activeVersionWhere = this.schema.tenantSubscription.withEndsAt ? (0, import_types3.buildActivePlanVersionWhere)(asOf, {
|
|
2215
|
+
withEndsAt: true
|
|
2216
|
+
}) : (0, import_types3.buildActivePlanVersionWhere)(asOf);
|
|
2217
|
+
const where = activeWindow ? {
|
|
2218
|
+
planId: storagePlanId,
|
|
2219
|
+
...activeVersionWhere
|
|
2220
|
+
} : {
|
|
2221
|
+
planId: storagePlanId,
|
|
2222
|
+
publishedAt: {
|
|
2223
|
+
not: null
|
|
2224
|
+
},
|
|
2225
|
+
supersededAt: null,
|
|
2226
|
+
...this.schema.tenantSubscription.withEndsAt ? {
|
|
2227
|
+
OR: [
|
|
2228
|
+
{
|
|
2229
|
+
endsAt: null
|
|
2230
|
+
},
|
|
2231
|
+
{
|
|
2232
|
+
endsAt: {
|
|
2233
|
+
gt: asOf
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
]
|
|
2237
|
+
} : {}
|
|
2238
|
+
};
|
|
2239
|
+
const target = await this.planVersions(client).findFirst({
|
|
2240
|
+
where,
|
|
2241
|
+
orderBy: activeWindow ? [
|
|
2242
|
+
{
|
|
2243
|
+
validFrom: {
|
|
2244
|
+
sort: "desc",
|
|
2245
|
+
nulls: "last"
|
|
2246
|
+
}
|
|
2247
|
+
},
|
|
2248
|
+
{
|
|
2249
|
+
version: "desc"
|
|
2250
|
+
}
|
|
2251
|
+
] : {
|
|
2252
|
+
version: "desc"
|
|
2253
|
+
}
|
|
2254
|
+
});
|
|
2255
|
+
if (!target) {
|
|
2256
|
+
throw new Error(`No active PlanVersion for plan '${storagePlanId}'.`);
|
|
2257
|
+
}
|
|
2258
|
+
return target.id;
|
|
2259
|
+
}
|
|
2260
|
+
async pendingVersionBelongsToAnotherPlan(client, pendingPlanVersionId, targetStoragePlanId) {
|
|
2261
|
+
if (!pendingPlanVersionId) return false;
|
|
2262
|
+
const pending = await this.planVersions(client).findUnique({
|
|
2263
|
+
where: {
|
|
2264
|
+
id: pendingPlanVersionId
|
|
2265
|
+
}
|
|
2266
|
+
});
|
|
2267
|
+
return !pending || pending.planId !== targetStoragePlanId;
|
|
2268
|
+
}
|
|
2269
|
+
assertConfiguration() {
|
|
2270
|
+
if (!this.schema.tenantSubscription.synchronizePlanVersion) return;
|
|
2271
|
+
const entitlementFields = this.schema.planVersionFields.entitlement;
|
|
2272
|
+
if (this.schema.tenantSubscription.activeVersionSelection === "validity-window" && !entitlementFields.validityWindows) {
|
|
2273
|
+
throw new Error("tenantSubscription.activeVersionSelection='validity-window' requires planVersionFields.entitlement.validityWindows=true.");
|
|
2274
|
+
}
|
|
2275
|
+
if (this.schema.tenantSubscription.withEndsAt && !entitlementFields.endsAt) {
|
|
2276
|
+
throw new Error("tenantSubscription.withEndsAt=true requires planVersionFields.entitlement.endsAt=true.");
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
1747
2279
|
};
|
|
1748
2280
|
PrismaTenantSubscriptionWriteAdapter = _ts_decorate18([
|
|
1749
2281
|
(0, import_common18.Injectable)(),
|
|
1750
2282
|
_ts_param16(0, (0, import_common18.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
2283
|
+
_ts_param16(1, (0, import_common18.Optional)()),
|
|
2284
|
+
_ts_param16(1, (0, import_common18.Inject)(PRISMA_SCHEMA_OPTIONS_TOKEN)),
|
|
1751
2285
|
_ts_metadata16("design:type", Function),
|
|
1752
2286
|
_ts_metadata16("design:paramtypes", [
|
|
1753
|
-
typeof
|
|
2287
|
+
typeof TransactionalPrismaClient === "undefined" ? Object : TransactionalPrismaClient,
|
|
2288
|
+
typeof PrismaSchemaOptions === "undefined" ? Object : PrismaSchemaOptions
|
|
1754
2289
|
])
|
|
1755
2290
|
], PrismaTenantSubscriptionWriteAdapter);
|
|
2291
|
+
function clearedPendingVersionData() {
|
|
2292
|
+
return {
|
|
2293
|
+
pendingPlanVersionId: null,
|
|
2294
|
+
pendingPlanVersionEffectiveAt: null,
|
|
2295
|
+
pendingPlanVersionAccepted: false,
|
|
2296
|
+
pendingPlanVersionAcceptedAt: null,
|
|
2297
|
+
pendingPlanVersionAcceptedByUserId: null,
|
|
2298
|
+
pendingPlanVersionNotifiedAt: null,
|
|
2299
|
+
pendingPlanVersionReminderSentAt: null
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
__name(clearedPendingVersionData, "clearedPendingVersionData");
|
|
1756
2303
|
|
|
1757
2304
|
// src/prisma-plan.repository.ts
|
|
1758
2305
|
var import_common19 = require("@nestjs/common");
|
|
2306
|
+
var import_types4 = require("@saasicat/types");
|
|
1759
2307
|
function _ts_decorate19(decorators, target, key, desc) {
|
|
1760
2308
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1761
2309
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1778,30 +2326,105 @@ var PrismaPlanRepository = class {
|
|
|
1778
2326
|
__name(this, "PrismaPlanRepository");
|
|
1779
2327
|
}
|
|
1780
2328
|
prisma;
|
|
1781
|
-
|
|
2329
|
+
binding;
|
|
2330
|
+
delegateName;
|
|
2331
|
+
fields;
|
|
2332
|
+
constructor(prisma, options) {
|
|
1782
2333
|
this.prisma = prisma;
|
|
2334
|
+
const schema = resolvePrismaSchemaOptions(options);
|
|
2335
|
+
this.binding = createPrismaPlanBindingResolver(options?.planBinding);
|
|
2336
|
+
this.delegateName = schema.delegates.catalogPlanVersion;
|
|
2337
|
+
this.fields = schema.planVersionFields.catalog;
|
|
1783
2338
|
}
|
|
1784
2339
|
db(tx) {
|
|
1785
|
-
return
|
|
2340
|
+
return tx ?? this.prisma;
|
|
2341
|
+
}
|
|
2342
|
+
versions(client) {
|
|
2343
|
+
return getPrismaDelegate(client, this.delegateName);
|
|
1786
2344
|
}
|
|
1787
2345
|
// ─── Stem operations (Pack 1) ───
|
|
1788
2346
|
async list(filter) {
|
|
1789
2347
|
const excludeDeleted = filter.excludeDeleted ?? true;
|
|
2348
|
+
const db = this.db();
|
|
1790
2349
|
let publishedKeys = null;
|
|
1791
2350
|
if (filter.onlyPublished) {
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
2351
|
+
if (this.binding.mode === "legacy-plan-key") {
|
|
2352
|
+
const projectPlans = await db.plan.findMany({
|
|
2353
|
+
where: {
|
|
2354
|
+
projectKey: filter.projectKey,
|
|
2355
|
+
...excludeDeleted ? {
|
|
2356
|
+
deletedAt: null
|
|
2357
|
+
} : {}
|
|
2358
|
+
}
|
|
2359
|
+
});
|
|
2360
|
+
const candidateKeys = [
|
|
2361
|
+
...new Set(projectPlans.map((plan) => plan.planKey))
|
|
2362
|
+
];
|
|
2363
|
+
const allMatchingPlans = candidateKeys.length === 0 ? [] : await db.plan.findMany({
|
|
2364
|
+
where: {
|
|
2365
|
+
planKey: {
|
|
2366
|
+
in: candidateKeys
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
});
|
|
2370
|
+
const projectsByKey = /* @__PURE__ */ new Map();
|
|
2371
|
+
for (const plan of allMatchingPlans) {
|
|
2372
|
+
const projects = projectsByKey.get(plan.planKey) ?? /* @__PURE__ */ new Set();
|
|
2373
|
+
projects.add(plan.projectKey);
|
|
2374
|
+
projectsByKey.set(plan.planKey, projects);
|
|
1798
2375
|
}
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
2376
|
+
const unambiguousKeys = candidateKeys.filter((planKey) => projectsByKey.get(planKey)?.size === 1);
|
|
2377
|
+
const live = await this.versions(db).findMany({
|
|
2378
|
+
where: {
|
|
2379
|
+
planId: {
|
|
2380
|
+
in: unambiguousKeys
|
|
2381
|
+
},
|
|
2382
|
+
publishedAt: {
|
|
2383
|
+
not: null
|
|
2384
|
+
},
|
|
2385
|
+
supersededAt: null
|
|
2386
|
+
}
|
|
2387
|
+
});
|
|
2388
|
+
publishedKeys = [
|
|
2389
|
+
...new Set(live.map((version) => version.planId))
|
|
2390
|
+
];
|
|
2391
|
+
} else {
|
|
2392
|
+
const projectPlans = await db.plan.findMany({
|
|
2393
|
+
where: {
|
|
2394
|
+
projectKey: filter.projectKey,
|
|
2395
|
+
...excludeDeleted ? {
|
|
2396
|
+
deletedAt: null
|
|
2397
|
+
} : {}
|
|
2398
|
+
}
|
|
2399
|
+
});
|
|
2400
|
+
const planKeyById = new Map(projectPlans.map((plan) => [
|
|
2401
|
+
plan.id,
|
|
2402
|
+
plan.planKey
|
|
2403
|
+
]));
|
|
2404
|
+
const live = await this.versions(db).findMany({
|
|
2405
|
+
where: {
|
|
2406
|
+
planId: {
|
|
2407
|
+
in: [
|
|
2408
|
+
...planKeyById.keys()
|
|
2409
|
+
]
|
|
2410
|
+
},
|
|
2411
|
+
publishedAt: {
|
|
2412
|
+
not: null
|
|
2413
|
+
},
|
|
2414
|
+
supersededAt: null
|
|
2415
|
+
}
|
|
2416
|
+
});
|
|
2417
|
+
publishedKeys = [
|
|
2418
|
+
...new Set(live.flatMap((version) => {
|
|
2419
|
+
const planKey = planKeyById.get(version.planId);
|
|
2420
|
+
return planKey ? [
|
|
2421
|
+
planKey
|
|
2422
|
+
] : [];
|
|
2423
|
+
}))
|
|
2424
|
+
];
|
|
2425
|
+
}
|
|
1803
2426
|
}
|
|
1804
|
-
const rows = await
|
|
2427
|
+
const rows = await db.plan.findMany({
|
|
1805
2428
|
where: {
|
|
1806
2429
|
projectKey: filter.projectKey,
|
|
1807
2430
|
...excludeDeleted ? {
|
|
@@ -1896,65 +2519,112 @@ var PrismaPlanRepository = class {
|
|
|
1896
2519
|
}
|
|
1897
2520
|
// ─── Lifecycle operations (Pack 2a) — keyed by planKey ───
|
|
1898
2521
|
async listVersions(planKey) {
|
|
1899
|
-
const
|
|
2522
|
+
const db = this.db();
|
|
2523
|
+
const storedPlanId = await this.binding.toStoragePlanId(db, planKey);
|
|
2524
|
+
const rows = await this.versions(db).findMany({
|
|
1900
2525
|
where: {
|
|
1901
|
-
planId:
|
|
2526
|
+
planId: storedPlanId
|
|
1902
2527
|
},
|
|
1903
2528
|
orderBy: {
|
|
1904
2529
|
version: "asc"
|
|
1905
2530
|
}
|
|
1906
2531
|
});
|
|
1907
|
-
return rows.map(
|
|
2532
|
+
return rows.map((row) => this.toPlanVersionRow(row, planKey));
|
|
1908
2533
|
}
|
|
1909
2534
|
async findVersionById(versionId) {
|
|
1910
|
-
const
|
|
2535
|
+
const db = this.db();
|
|
2536
|
+
const row = await this.versions(db).findUnique({
|
|
1911
2537
|
where: {
|
|
1912
2538
|
id: versionId
|
|
1913
2539
|
}
|
|
1914
2540
|
});
|
|
1915
|
-
return row ?
|
|
2541
|
+
return row ? this.toPlanVersionRow(row, await this.binding.toPlanKey(db, row.planId)) : null;
|
|
1916
2542
|
}
|
|
1917
2543
|
async findCurrentDraft(planKey) {
|
|
1918
|
-
const
|
|
2544
|
+
const db = this.db();
|
|
2545
|
+
const storedPlanId = await this.binding.toStoragePlanId(db, planKey);
|
|
2546
|
+
const row = await this.versions(db).findFirst({
|
|
1919
2547
|
where: {
|
|
1920
|
-
planId:
|
|
2548
|
+
planId: storedPlanId,
|
|
1921
2549
|
publishedAt: null
|
|
1922
2550
|
}
|
|
1923
2551
|
});
|
|
1924
|
-
return row ?
|
|
2552
|
+
return row ? this.toPlanVersionRow(row, planKey) : null;
|
|
1925
2553
|
}
|
|
1926
2554
|
async findLatestLivePlanVersion(planKey, tx) {
|
|
1927
|
-
const
|
|
2555
|
+
const db = this.db(tx);
|
|
2556
|
+
const storedPlanId = await this.binding.toStoragePlanId(db, planKey);
|
|
2557
|
+
const row = await this.versions(db).findFirst({
|
|
1928
2558
|
where: {
|
|
1929
|
-
planId:
|
|
2559
|
+
planId: storedPlanId,
|
|
1930
2560
|
publishedAt: {
|
|
1931
2561
|
not: null
|
|
1932
2562
|
},
|
|
1933
|
-
supersededAt: null
|
|
2563
|
+
supersededAt: null,
|
|
2564
|
+
...this.fields.endsAt ? {
|
|
2565
|
+
OR: [
|
|
2566
|
+
{
|
|
2567
|
+
endsAt: null
|
|
2568
|
+
},
|
|
2569
|
+
{
|
|
2570
|
+
endsAt: {
|
|
2571
|
+
gt: /* @__PURE__ */ new Date()
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
]
|
|
2575
|
+
} : {}
|
|
1934
2576
|
},
|
|
1935
2577
|
orderBy: {
|
|
1936
2578
|
version: "desc"
|
|
1937
2579
|
}
|
|
1938
2580
|
});
|
|
1939
|
-
return row ?
|
|
2581
|
+
return row ? this.toPlanVersionRow(row, planKey) : null;
|
|
1940
2582
|
}
|
|
1941
|
-
async findActivePlanVersion() {
|
|
1942
|
-
|
|
2583
|
+
async findActivePlanVersion(planKey, asOf = /* @__PURE__ */ new Date(), tx) {
|
|
2584
|
+
if (!this.fields.validityWindows) {
|
|
2585
|
+
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.");
|
|
2586
|
+
}
|
|
2587
|
+
const db = this.db(tx);
|
|
2588
|
+
const storedPlanId = await this.binding.toStoragePlanId(db, planKey);
|
|
2589
|
+
const activeWhere = this.fields.endsAt ? (0, import_types4.buildActivePlanVersionWhere)(asOf, {
|
|
2590
|
+
withEndsAt: true
|
|
2591
|
+
}) : (0, import_types4.buildActivePlanVersionWhere)(asOf);
|
|
2592
|
+
const row = await this.versions(db).findFirst({
|
|
2593
|
+
where: {
|
|
2594
|
+
planId: storedPlanId,
|
|
2595
|
+
...activeWhere
|
|
2596
|
+
},
|
|
2597
|
+
orderBy: [
|
|
2598
|
+
{
|
|
2599
|
+
validFrom: {
|
|
2600
|
+
sort: "desc",
|
|
2601
|
+
nulls: "last"
|
|
2602
|
+
}
|
|
2603
|
+
},
|
|
2604
|
+
{
|
|
2605
|
+
version: "desc"
|
|
2606
|
+
}
|
|
2607
|
+
]
|
|
2608
|
+
});
|
|
2609
|
+
return row ? this.toPlanVersionRow(row, planKey) : null;
|
|
1943
2610
|
}
|
|
1944
2611
|
async createPlanVersionDraft(data) {
|
|
1945
2612
|
const planKey = data.planId;
|
|
1946
|
-
const
|
|
2613
|
+
const db = this.db();
|
|
2614
|
+
const planVersion = this.versions(db);
|
|
2615
|
+
const storedPlanId = await this.binding.toStoragePlanId(db, planKey);
|
|
2616
|
+
const latest = await planVersion.findFirst({
|
|
1947
2617
|
where: {
|
|
1948
|
-
planId:
|
|
2618
|
+
planId: storedPlanId
|
|
1949
2619
|
},
|
|
1950
2620
|
orderBy: {
|
|
1951
2621
|
version: "desc"
|
|
1952
2622
|
}
|
|
1953
2623
|
});
|
|
1954
2624
|
const nextVersion = (latest?.version ?? 0) + 1;
|
|
1955
|
-
const created = await
|
|
2625
|
+
const created = await planVersion.create({
|
|
1956
2626
|
data: {
|
|
1957
|
-
planId:
|
|
2627
|
+
planId: storedPlanId,
|
|
1958
2628
|
version: nextVersion,
|
|
1959
2629
|
baseVersionId: data.baseVersionId ?? null,
|
|
1960
2630
|
features: data.features,
|
|
@@ -1963,13 +2633,17 @@ var PrismaPlanRepository = class {
|
|
|
1963
2633
|
yearlyNet: data.yearlyNet,
|
|
1964
2634
|
marketed: data.marketed ?? true,
|
|
1965
2635
|
changeNote: data.changeNote ?? "",
|
|
1966
|
-
createdByUserId: data.createdByUserId ?? null
|
|
2636
|
+
createdByUserId: data.createdByUserId ?? null,
|
|
2637
|
+
...this.fields.validityWindows ? {
|
|
2638
|
+
validFrom: data.validFrom ? new Date(data.validFrom) : null,
|
|
2639
|
+
validUntil: data.validUntil ? new Date(data.validUntil) : null
|
|
2640
|
+
} : {}
|
|
1967
2641
|
}
|
|
1968
2642
|
});
|
|
1969
|
-
return
|
|
2643
|
+
return this.toPlanVersionRow(created, planKey);
|
|
1970
2644
|
}
|
|
1971
2645
|
async updatePlanVersionDraft(versionId, data) {
|
|
1972
|
-
const updated = await this.db().
|
|
2646
|
+
const updated = await this.versions(this.db()).update({
|
|
1973
2647
|
where: {
|
|
1974
2648
|
id: versionId
|
|
1975
2649
|
},
|
|
@@ -1991,13 +2665,21 @@ var PrismaPlanRepository = class {
|
|
|
1991
2665
|
} : {},
|
|
1992
2666
|
...data.changeNote !== void 0 ? {
|
|
1993
2667
|
changeNote: data.changeNote
|
|
2668
|
+
} : {},
|
|
2669
|
+
...this.fields.validityWindows && data.validFrom !== void 0 ? {
|
|
2670
|
+
validFrom: data.validFrom ? new Date(data.validFrom) : null
|
|
2671
|
+
} : {},
|
|
2672
|
+
...this.fields.validityWindows && data.validUntil !== void 0 ? {
|
|
2673
|
+
validUntil: data.validUntil ? new Date(data.validUntil) : null
|
|
1994
2674
|
} : {}
|
|
1995
2675
|
}
|
|
1996
2676
|
});
|
|
1997
|
-
|
|
2677
|
+
const planKey = await this.binding.toPlanKey(this.db(), updated.planId);
|
|
2678
|
+
return this.toPlanVersionRow(updated, planKey);
|
|
1998
2679
|
}
|
|
1999
2680
|
async publishPlanVersionDraft(versionId, publishMeta, tx) {
|
|
2000
|
-
const
|
|
2681
|
+
const operationDb = this.db(tx);
|
|
2682
|
+
const draft = await this.versions(operationDb).findUnique({
|
|
2001
2683
|
where: {
|
|
2002
2684
|
id: versionId
|
|
2003
2685
|
}
|
|
@@ -2005,11 +2687,12 @@ var PrismaPlanRepository = class {
|
|
|
2005
2687
|
if (!draft) {
|
|
2006
2688
|
throw new Error(`PlanVersion ${versionId} not found.`);
|
|
2007
2689
|
}
|
|
2008
|
-
const
|
|
2690
|
+
const storedPlanId = draft.planId;
|
|
2009
2691
|
const publish = /* @__PURE__ */ __name(async (db) => {
|
|
2010
|
-
const
|
|
2692
|
+
const planVersion = this.versions(db);
|
|
2693
|
+
const previous = await planVersion.findFirst({
|
|
2011
2694
|
where: {
|
|
2012
|
-
planId:
|
|
2695
|
+
planId: storedPlanId,
|
|
2013
2696
|
publishedAt: {
|
|
2014
2697
|
not: null
|
|
2015
2698
|
},
|
|
@@ -2024,16 +2707,20 @@ var PrismaPlanRepository = class {
|
|
|
2024
2707
|
});
|
|
2025
2708
|
const now = /* @__PURE__ */ new Date();
|
|
2026
2709
|
if (previous) {
|
|
2027
|
-
|
|
2710
|
+
const predecessorValidUntil = new Date(publishMeta.validFrom.getTime() - 24 * 60 * 60 * 1e3);
|
|
2711
|
+
await planVersion.update({
|
|
2028
2712
|
where: {
|
|
2029
2713
|
id: previous.id
|
|
2030
2714
|
},
|
|
2031
2715
|
data: {
|
|
2032
|
-
supersededAt: now
|
|
2716
|
+
supersededAt: now,
|
|
2717
|
+
...this.fields.validityWindows ? {
|
|
2718
|
+
validUntil: predecessorValidUntil
|
|
2719
|
+
} : {}
|
|
2033
2720
|
}
|
|
2034
2721
|
});
|
|
2035
2722
|
}
|
|
2036
|
-
return
|
|
2723
|
+
return planVersion.update({
|
|
2037
2724
|
where: {
|
|
2038
2725
|
id: versionId
|
|
2039
2726
|
},
|
|
@@ -2041,15 +2728,21 @@ var PrismaPlanRepository = class {
|
|
|
2041
2728
|
publishedAt: now,
|
|
2042
2729
|
publishedChanges: publishMeta.publishedChanges,
|
|
2043
2730
|
nonRegressive: publishMeta.nonRegressive,
|
|
2044
|
-
publishedByUserId: publishMeta.publishedByUserId
|
|
2731
|
+
publishedByUserId: publishMeta.publishedByUserId,
|
|
2732
|
+
...this.fields.validityWindows ? {
|
|
2733
|
+
validFrom: publishMeta.validFrom,
|
|
2734
|
+
validUntil: publishMeta.validUntil
|
|
2735
|
+
} : {}
|
|
2045
2736
|
}
|
|
2046
2737
|
});
|
|
2047
2738
|
}, "publish");
|
|
2048
2739
|
const published = tx ? await publish(this.db(tx)) : await this.prisma.$transaction((txClient) => publish(txClient));
|
|
2049
|
-
|
|
2740
|
+
const planKey = await this.binding.toPlanKey(operationDb, storedPlanId);
|
|
2741
|
+
return this.toPlanVersionRow(published, planKey);
|
|
2050
2742
|
}
|
|
2051
2743
|
async deletePlanVersionDraft(versionId) {
|
|
2052
|
-
const
|
|
2744
|
+
const planVersion = this.versions(this.db());
|
|
2745
|
+
const row = await planVersion.findUnique({
|
|
2053
2746
|
where: {
|
|
2054
2747
|
id: versionId
|
|
2055
2748
|
}
|
|
@@ -2058,23 +2751,42 @@ var PrismaPlanRepository = class {
|
|
|
2058
2751
|
if (row.publishedAt !== null) {
|
|
2059
2752
|
throw new Error(`PlanVersion ${versionId} is already published and cannot be discarded (published versions are immutable \u2014 contract protection P1).`);
|
|
2060
2753
|
}
|
|
2061
|
-
await
|
|
2754
|
+
await planVersion.deleteMany({
|
|
2062
2755
|
where: {
|
|
2063
2756
|
id: versionId,
|
|
2064
2757
|
publishedAt: null
|
|
2065
2758
|
}
|
|
2066
2759
|
});
|
|
2067
2760
|
}
|
|
2068
|
-
async terminate() {
|
|
2069
|
-
|
|
2761
|
+
async terminate(versionId, endsAt) {
|
|
2762
|
+
if (!this.fields.endsAt) {
|
|
2763
|
+
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.");
|
|
2764
|
+
}
|
|
2765
|
+
const db = this.db();
|
|
2766
|
+
const updated = await this.versions(db).update({
|
|
2767
|
+
where: {
|
|
2768
|
+
id: versionId
|
|
2769
|
+
},
|
|
2770
|
+
data: {
|
|
2771
|
+
endsAt
|
|
2772
|
+
}
|
|
2773
|
+
});
|
|
2774
|
+
const planKey = await this.binding.toPlanKey(db, updated.planId);
|
|
2775
|
+
return this.toPlanVersionRow(updated, planKey);
|
|
2776
|
+
}
|
|
2777
|
+
toPlanVersionRow(row, planKey) {
|
|
2778
|
+
return toPlanVersionRow2(row, planKey, this.fields);
|
|
2070
2779
|
}
|
|
2071
2780
|
};
|
|
2072
2781
|
PrismaPlanRepository = _ts_decorate19([
|
|
2073
2782
|
(0, import_common19.Injectable)(),
|
|
2074
2783
|
_ts_param17(0, (0, import_common19.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
2784
|
+
_ts_param17(1, (0, import_common19.Optional)()),
|
|
2785
|
+
_ts_param17(1, (0, import_common19.Inject)(PRISMA_SCHEMA_OPTIONS_TOKEN)),
|
|
2075
2786
|
_ts_metadata17("design:type", Function),
|
|
2076
2787
|
_ts_metadata17("design:paramtypes", [
|
|
2077
|
-
typeof
|
|
2788
|
+
typeof PlanRepositoryClient === "undefined" ? Object : PlanRepositoryClient,
|
|
2789
|
+
typeof PrismaSchemaOptions === "undefined" ? Object : PrismaSchemaOptions
|
|
2078
2790
|
])
|
|
2079
2791
|
], PrismaPlanRepository);
|
|
2080
2792
|
function toPlanRow2(row) {
|
|
@@ -2092,12 +2804,12 @@ function toPlanRow2(row) {
|
|
|
2092
2804
|
};
|
|
2093
2805
|
}
|
|
2094
2806
|
__name(toPlanRow2, "toPlanRow");
|
|
2095
|
-
function toPlanVersionRow2(row) {
|
|
2096
|
-
|
|
2807
|
+
function toPlanVersionRow2(row, planKey, fields) {
|
|
2808
|
+
const mapped = {
|
|
2097
2809
|
id: row.id,
|
|
2098
2810
|
version: row.version,
|
|
2099
2811
|
baseVersionId: row.baseVersionId,
|
|
2100
|
-
planId:
|
|
2812
|
+
planId: planKey,
|
|
2101
2813
|
features: toStringArray(row.features),
|
|
2102
2814
|
quotas: toQuotaMap(row.quotas),
|
|
2103
2815
|
monthlyNet: row.monthlyNet.toString(),
|
|
@@ -2108,19 +2820,23 @@ function toPlanVersionRow2(row) {
|
|
|
2108
2820
|
publishedChanges: Array.isArray(row.publishedChanges) ? row.publishedChanges : null,
|
|
2109
2821
|
changeNote: row.changeNote,
|
|
2110
2822
|
nonRegressive: row.nonRegressive,
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
validUntil: null,
|
|
2823
|
+
validFrom: fields.validityWindows && row.validFrom ? row.validFrom.toISOString() : null,
|
|
2824
|
+
validUntil: fields.validityWindows && row.validUntil ? row.validUntil.toISOString() : null,
|
|
2114
2825
|
createdByUserId: row.createdByUserId,
|
|
2115
2826
|
publishedByUserId: row.publishedByUserId,
|
|
2116
2827
|
createdAt: row.createdAt.toISOString(),
|
|
2117
2828
|
updatedAt: row.updatedAt.toISOString()
|
|
2118
2829
|
};
|
|
2830
|
+
if (fields.endsAt) {
|
|
2831
|
+
mapped.endsAt = row.endsAt?.toISOString() ?? null;
|
|
2832
|
+
}
|
|
2833
|
+
return mapped;
|
|
2119
2834
|
}
|
|
2120
2835
|
__name(toPlanVersionRow2, "toPlanVersionRow");
|
|
2121
2836
|
|
|
2122
2837
|
// src/prisma-bundle.repository.ts
|
|
2123
2838
|
var import_common20 = require("@nestjs/common");
|
|
2839
|
+
var import_types5 = require("@saasicat/types");
|
|
2124
2840
|
function _ts_decorate20(decorators, target, key, desc) {
|
|
2125
2841
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2126
2842
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -2138,16 +2854,32 @@ function _ts_param18(paramIndex, decorator) {
|
|
|
2138
2854
|
};
|
|
2139
2855
|
}
|
|
2140
2856
|
__name(_ts_param18, "_ts_param");
|
|
2857
|
+
var PRISMA_BUNDLE_REPOSITORY_OPTIONS = /* @__PURE__ */ Symbol.for("saasicat/adapter-prisma/PrismaBundleRepositoryOptions");
|
|
2141
2858
|
var PrismaBundleRepository = class {
|
|
2142
2859
|
static {
|
|
2143
2860
|
__name(this, "PrismaBundleRepository");
|
|
2144
2861
|
}
|
|
2145
2862
|
prisma;
|
|
2146
|
-
|
|
2863
|
+
validityWindows;
|
|
2864
|
+
/**
|
|
2865
|
+
* Present only when validity-window mode is enabled. This mirrors the
|
|
2866
|
+
* optional port capability and lets 0.6-schema consumers detect that an
|
|
2867
|
+
* active-at-time lookup is unavailable.
|
|
2868
|
+
*/
|
|
2869
|
+
findActiveBundleVersion;
|
|
2870
|
+
constructor(prisma, options = {}) {
|
|
2147
2871
|
this.prisma = prisma;
|
|
2872
|
+
this.validityWindows = options.validityWindows ?? false;
|
|
2873
|
+
if (this.validityWindows) {
|
|
2874
|
+
this.findActiveBundleVersion = (bundleId, asOf, tx) => this.findActiveBundleVersionWithValidity(bundleId, asOf ?? /* @__PURE__ */ new Date(), tx);
|
|
2875
|
+
}
|
|
2148
2876
|
}
|
|
2149
2877
|
db(tx) {
|
|
2150
|
-
return
|
|
2878
|
+
return tx ?? this.prisma;
|
|
2879
|
+
}
|
|
2880
|
+
transaction(work) {
|
|
2881
|
+
const transaction = this.prisma.$transaction;
|
|
2882
|
+
return transaction.call(this.prisma, (tx) => work(tx));
|
|
2151
2883
|
}
|
|
2152
2884
|
// ─── Stem operations ───
|
|
2153
2885
|
async list(filter) {
|
|
@@ -2253,7 +2985,7 @@ var PrismaBundleRepository = class {
|
|
|
2253
2985
|
version: "asc"
|
|
2254
2986
|
}
|
|
2255
2987
|
});
|
|
2256
|
-
return rows.map((row) => toBundleVersionRow(row, bundle));
|
|
2988
|
+
return rows.map((row) => toBundleVersionRow(row, bundle, this.validityWindows));
|
|
2257
2989
|
}
|
|
2258
2990
|
async findVersionById(versionId) {
|
|
2259
2991
|
const row = await this.db().bundleVersion.findUnique({
|
|
@@ -2267,7 +2999,7 @@ var PrismaBundleRepository = class {
|
|
|
2267
2999
|
id: row.bundleId
|
|
2268
3000
|
}
|
|
2269
3001
|
});
|
|
2270
|
-
return toBundleVersionRow(row, bundle);
|
|
3002
|
+
return toBundleVersionRow(row, bundle, this.validityWindows);
|
|
2271
3003
|
}
|
|
2272
3004
|
async findCurrentDraft(bundleId) {
|
|
2273
3005
|
const row = await this.db().bundleVersion.findFirst({
|
|
@@ -2282,7 +3014,7 @@ var PrismaBundleRepository = class {
|
|
|
2282
3014
|
id: bundleId
|
|
2283
3015
|
}
|
|
2284
3016
|
});
|
|
2285
|
-
return toBundleVersionRow(row, bundle);
|
|
3017
|
+
return toBundleVersionRow(row, bundle, this.validityWindows);
|
|
2286
3018
|
}
|
|
2287
3019
|
async findLatestLive(bundleId, tx) {
|
|
2288
3020
|
const db = this.db(tx);
|
|
@@ -2304,7 +3036,34 @@ var PrismaBundleRepository = class {
|
|
|
2304
3036
|
id: bundleId
|
|
2305
3037
|
}
|
|
2306
3038
|
});
|
|
2307
|
-
return toBundleVersionRow(row, bundle);
|
|
3039
|
+
return toBundleVersionRow(row, bundle, this.validityWindows);
|
|
3040
|
+
}
|
|
3041
|
+
async findActiveBundleVersionWithValidity(bundleId, asOf, tx) {
|
|
3042
|
+
const db = this.db(tx);
|
|
3043
|
+
const row = await db.bundleVersion.findFirst({
|
|
3044
|
+
where: {
|
|
3045
|
+
bundleId,
|
|
3046
|
+
...(0, import_types5.buildActiveVersionWhere)(asOf)
|
|
3047
|
+
},
|
|
3048
|
+
orderBy: [
|
|
3049
|
+
{
|
|
3050
|
+
validFrom: {
|
|
3051
|
+
sort: "desc",
|
|
3052
|
+
nulls: "last"
|
|
3053
|
+
}
|
|
3054
|
+
},
|
|
3055
|
+
{
|
|
3056
|
+
version: "desc"
|
|
3057
|
+
}
|
|
3058
|
+
]
|
|
3059
|
+
});
|
|
3060
|
+
if (!row) return null;
|
|
3061
|
+
const bundle = await db.bundle.findUnique({
|
|
3062
|
+
where: {
|
|
3063
|
+
id: bundleId
|
|
3064
|
+
}
|
|
3065
|
+
});
|
|
3066
|
+
return toBundleVersionRow(row, bundle, true);
|
|
2308
3067
|
}
|
|
2309
3068
|
async createDraft(data) {
|
|
2310
3069
|
const db = this.db();
|
|
@@ -2339,7 +3098,11 @@ var PrismaBundleRepository = class {
|
|
|
2339
3098
|
yearlyNet: data.yearlyNet ?? null,
|
|
2340
3099
|
marketed: data.marketed ?? true,
|
|
2341
3100
|
changeNote: data.changeNote ?? "",
|
|
2342
|
-
createdByUserId: data.createdByUserId ?? null
|
|
3101
|
+
createdByUserId: data.createdByUserId ?? null,
|
|
3102
|
+
...this.validityWindows ? {
|
|
3103
|
+
validFrom: toNullableDate(data.validFrom),
|
|
3104
|
+
validUntil: toNullableDate(data.validUntil)
|
|
3105
|
+
} : {}
|
|
2343
3106
|
}
|
|
2344
3107
|
});
|
|
2345
3108
|
const bundle = await db.bundle.findUnique({
|
|
@@ -2347,7 +3110,7 @@ var PrismaBundleRepository = class {
|
|
|
2347
3110
|
id: data.bundleId
|
|
2348
3111
|
}
|
|
2349
3112
|
});
|
|
2350
|
-
return toBundleVersionRow(created, bundle);
|
|
3113
|
+
return toBundleVersionRow(created, bundle, this.validityWindows);
|
|
2351
3114
|
}
|
|
2352
3115
|
async updateDraft(versionId, data) {
|
|
2353
3116
|
const db = this.db();
|
|
@@ -2379,6 +3142,12 @@ var PrismaBundleRepository = class {
|
|
|
2379
3142
|
} : {},
|
|
2380
3143
|
...data.changeNote !== void 0 ? {
|
|
2381
3144
|
changeNote: data.changeNote
|
|
3145
|
+
} : {},
|
|
3146
|
+
...this.validityWindows && data.validFrom !== void 0 ? {
|
|
3147
|
+
validFrom: toNullableDate(data.validFrom)
|
|
3148
|
+
} : {},
|
|
3149
|
+
...this.validityWindows && data.validUntil !== void 0 ? {
|
|
3150
|
+
validUntil: toNullableDate(data.validUntil)
|
|
2382
3151
|
} : {}
|
|
2383
3152
|
}
|
|
2384
3153
|
});
|
|
@@ -2387,9 +3156,15 @@ var PrismaBundleRepository = class {
|
|
|
2387
3156
|
id: updated.bundleId
|
|
2388
3157
|
}
|
|
2389
3158
|
});
|
|
2390
|
-
return toBundleVersionRow(updated, bundle);
|
|
3159
|
+
return toBundleVersionRow(updated, bundle, this.validityWindows);
|
|
2391
3160
|
}
|
|
2392
3161
|
async publishDraft(versionId, publishMeta, tx) {
|
|
3162
|
+
if (this.validityWindows && tx === void 0) {
|
|
3163
|
+
return this.transaction((transaction) => this.publishDraftWithValidity(transaction, versionId, publishMeta));
|
|
3164
|
+
}
|
|
3165
|
+
if (this.validityWindows) {
|
|
3166
|
+
return this.publishDraftWithValidity(this.db(tx), versionId, publishMeta);
|
|
3167
|
+
}
|
|
2393
3168
|
const db = this.db(tx);
|
|
2394
3169
|
const draft = await db.bundleVersion.findUnique({
|
|
2395
3170
|
where: {
|
|
@@ -2430,7 +3205,53 @@ var PrismaBundleRepository = class {
|
|
|
2430
3205
|
id: published.bundleId
|
|
2431
3206
|
}
|
|
2432
3207
|
});
|
|
2433
|
-
return toBundleVersionRow(published, bundle);
|
|
3208
|
+
return toBundleVersionRow(published, bundle, false);
|
|
3209
|
+
}
|
|
3210
|
+
async publishDraftWithValidity(db, versionId, publishMeta) {
|
|
3211
|
+
const draft = await db.bundleVersion.findUnique({
|
|
3212
|
+
where: {
|
|
3213
|
+
id: versionId
|
|
3214
|
+
}
|
|
3215
|
+
});
|
|
3216
|
+
if (!draft) {
|
|
3217
|
+
throw new Error(`BundleVersion '${versionId}' not found.`);
|
|
3218
|
+
}
|
|
3219
|
+
const now = /* @__PURE__ */ new Date();
|
|
3220
|
+
await db.bundleVersion.updateMany({
|
|
3221
|
+
where: {
|
|
3222
|
+
bundleId: draft.bundleId,
|
|
3223
|
+
publishedAt: {
|
|
3224
|
+
not: null
|
|
3225
|
+
},
|
|
3226
|
+
supersededAt: null,
|
|
3227
|
+
NOT: {
|
|
3228
|
+
id: versionId
|
|
3229
|
+
}
|
|
3230
|
+
},
|
|
3231
|
+
data: {
|
|
3232
|
+
supersededAt: now,
|
|
3233
|
+
validUntil: previousUtcDay(publishMeta.validFrom)
|
|
3234
|
+
}
|
|
3235
|
+
});
|
|
3236
|
+
const published = await db.bundleVersion.update({
|
|
3237
|
+
where: {
|
|
3238
|
+
id: versionId
|
|
3239
|
+
},
|
|
3240
|
+
data: {
|
|
3241
|
+
publishedAt: now,
|
|
3242
|
+
publishedByUserId: publishMeta.publishedByUserId,
|
|
3243
|
+
publishedChanges: publishMeta.publishedChanges,
|
|
3244
|
+
nonRegressive: publishMeta.nonRegressive,
|
|
3245
|
+
validFrom: publishMeta.validFrom,
|
|
3246
|
+
validUntil: publishMeta.validUntil
|
|
3247
|
+
}
|
|
3248
|
+
});
|
|
3249
|
+
const bundle = await db.bundle.findUnique({
|
|
3250
|
+
where: {
|
|
3251
|
+
id: published.bundleId
|
|
3252
|
+
}
|
|
3253
|
+
});
|
|
3254
|
+
return toBundleVersionRow(published, bundle, true);
|
|
2434
3255
|
}
|
|
2435
3256
|
async deleteDraft(versionId) {
|
|
2436
3257
|
const db = this.db();
|
|
@@ -2458,9 +3279,12 @@ var PrismaBundleRepository = class {
|
|
|
2458
3279
|
PrismaBundleRepository = _ts_decorate20([
|
|
2459
3280
|
(0, import_common20.Injectable)(),
|
|
2460
3281
|
_ts_param18(0, (0, import_common20.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
3282
|
+
_ts_param18(1, (0, import_common20.Optional)()),
|
|
3283
|
+
_ts_param18(1, (0, import_common20.Inject)(PRISMA_BUNDLE_REPOSITORY_OPTIONS)),
|
|
2461
3284
|
_ts_metadata18("design:type", Function),
|
|
2462
3285
|
_ts_metadata18("design:paramtypes", [
|
|
2463
|
-
typeof
|
|
3286
|
+
typeof BundlePrismaClient === "undefined" ? Object : BundlePrismaClient,
|
|
3287
|
+
typeof PrismaBundleRepositoryOptions === "undefined" ? Object : PrismaBundleRepositoryOptions
|
|
2464
3288
|
])
|
|
2465
3289
|
], PrismaBundleRepository);
|
|
2466
3290
|
function isPlainObject(value) {
|
|
@@ -2471,6 +3295,16 @@ function toDecimalString(value) {
|
|
|
2471
3295
|
return value == null ? null : value.toString();
|
|
2472
3296
|
}
|
|
2473
3297
|
__name(toDecimalString, "toDecimalString");
|
|
3298
|
+
function toNullableDate(value) {
|
|
3299
|
+
return value ? new Date(value) : null;
|
|
3300
|
+
}
|
|
3301
|
+
__name(toNullableDate, "toNullableDate");
|
|
3302
|
+
function previousUtcDay(value) {
|
|
3303
|
+
const result = new Date(value);
|
|
3304
|
+
result.setUTCDate(result.getUTCDate() - 1);
|
|
3305
|
+
return result;
|
|
3306
|
+
}
|
|
3307
|
+
__name(previousUtcDay, "previousUtcDay");
|
|
2474
3308
|
function toVersionChanges(value) {
|
|
2475
3309
|
return Array.isArray(value) ? value : null;
|
|
2476
3310
|
}
|
|
@@ -2503,7 +3337,7 @@ function toBundleRow(row) {
|
|
|
2503
3337
|
};
|
|
2504
3338
|
}
|
|
2505
3339
|
__name(toBundleRow, "toBundleRow");
|
|
2506
|
-
function toBundleVersionRow(row, bundle) {
|
|
3340
|
+
function toBundleVersionRow(row, bundle, validityWindows) {
|
|
2507
3341
|
return {
|
|
2508
3342
|
id: row.id,
|
|
2509
3343
|
bundleId: row.bundleId,
|
|
@@ -2520,10 +3354,8 @@ function toBundleVersionRow(row, bundle) {
|
|
|
2520
3354
|
marketed: row.marketed,
|
|
2521
3355
|
publishedAt: row.publishedAt?.toISOString() ?? null,
|
|
2522
3356
|
supersededAt: row.supersededAt?.toISOString() ?? null,
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
validFrom: null,
|
|
2526
|
-
validUntil: null,
|
|
3357
|
+
validFrom: validityWindows && row.validFrom instanceof Date ? row.validFrom.toISOString() : null,
|
|
3358
|
+
validUntil: validityWindows && row.validUntil instanceof Date ? row.validUntil.toISOString() : null,
|
|
2527
3359
|
publishedChanges: toVersionChanges(row.publishedChanges),
|
|
2528
3360
|
changeNote: row.changeNote,
|
|
2529
3361
|
nonRegressive: row.nonRegressive,
|
|
@@ -2925,7 +3757,7 @@ PrismaCatalogEntryRepository = _ts_decorate21([
|
|
|
2925
3757
|
_ts_param19(0, (0, import_common21.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
2926
3758
|
_ts_metadata19("design:type", Function),
|
|
2927
3759
|
_ts_metadata19("design:paramtypes", [
|
|
2928
|
-
typeof
|
|
3760
|
+
typeof CatalogEntryRepositoryClient === "undefined" ? Object : CatalogEntryRepositoryClient
|
|
2929
3761
|
])
|
|
2930
3762
|
], PrismaCatalogEntryRepository);
|
|
2931
3763
|
function toI18n2(value) {
|
|
@@ -3166,7 +3998,7 @@ PrismaMarketingProjectionRepository = _ts_decorate22([
|
|
|
3166
3998
|
_ts_param20(0, (0, import_common22.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
3167
3999
|
_ts_metadata20("design:type", Function),
|
|
3168
4000
|
_ts_metadata20("design:paramtypes", [
|
|
3169
|
-
typeof
|
|
4001
|
+
typeof MarketingProjectionRepositoryClient === "undefined" ? Object : MarketingProjectionRepositoryClient
|
|
3170
4002
|
])
|
|
3171
4003
|
], PrismaMarketingProjectionRepository);
|
|
3172
4004
|
function toTopFeatures(value) {
|
|
@@ -3256,7 +4088,7 @@ PrismaMarketingSettingsRepository = _ts_decorate23([
|
|
|
3256
4088
|
_ts_param21(0, (0, import_common23.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
3257
4089
|
_ts_metadata21("design:type", Function),
|
|
3258
4090
|
_ts_metadata21("design:paramtypes", [
|
|
3259
|
-
typeof
|
|
4091
|
+
typeof MarketingSettingsRepositoryClient === "undefined" ? Object : MarketingSettingsRepositoryClient
|
|
3260
4092
|
])
|
|
3261
4093
|
], PrismaMarketingSettingsRepository);
|
|
3262
4094
|
function toRow2(row) {
|
|
@@ -3347,7 +4179,8 @@ var PrismaPromotionRepository = class {
|
|
|
3347
4179
|
}
|
|
3348
4180
|
async update(id, data) {
|
|
3349
4181
|
if (data.onlyLocales === null) {
|
|
3350
|
-
|
|
4182
|
+
const executeRaw = this.prisma.$executeRaw.bind(this.prisma);
|
|
4183
|
+
await executeRaw`
|
|
3351
4184
|
UPDATE promotions SET "onlyLocales" = NULL, "updatedAt" = NOW() WHERE id = ${id}`;
|
|
3352
4185
|
}
|
|
3353
4186
|
const row = await this.db.promotion.update({
|
|
@@ -3414,7 +4247,7 @@ PrismaPromotionRepository = _ts_decorate24([
|
|
|
3414
4247
|
_ts_param22(0, (0, import_common24.Inject)(PRISMA_CLIENT_TOKEN)),
|
|
3415
4248
|
_ts_metadata22("design:type", Function),
|
|
3416
4249
|
_ts_metadata22("design:paramtypes", [
|
|
3417
|
-
typeof
|
|
4250
|
+
typeof PromotionRepositoryClient === "undefined" ? Object : PromotionRepositoryClient
|
|
3418
4251
|
])
|
|
3419
4252
|
], PrismaPromotionRepository);
|
|
3420
4253
|
function toPromotionValue(value) {
|
|
@@ -3723,7 +4556,9 @@ __name(toRecord4, "toRecord");
|
|
|
3723
4556
|
0 && (module.exports = {
|
|
3724
4557
|
AsyncLocalRlsBypassAdapter,
|
|
3725
4558
|
PASSWORD_HASHER_TOKEN,
|
|
4559
|
+
PRISMA_BUNDLE_REPOSITORY_OPTIONS,
|
|
3726
4560
|
PRISMA_CLIENT_TOKEN,
|
|
4561
|
+
PRISMA_SCHEMA_OPTIONS_TOKEN,
|
|
3727
4562
|
PrismaAuditAdapter,
|
|
3728
4563
|
PrismaAuditQueryAdapter,
|
|
3729
4564
|
PrismaAuditStatsAdapter,
|
|
@@ -3749,5 +4584,8 @@ __name(toRecord4, "toRecord");
|
|
|
3749
4584
|
PrismaTransactionRunner,
|
|
3750
4585
|
ZeroPromoRevenueDeductionAggregator,
|
|
3751
4586
|
buildActorTag,
|
|
3752
|
-
|
|
4587
|
+
createPrismaPlanBindingResolver,
|
|
4588
|
+
getPrismaDelegate,
|
|
4589
|
+
prismaPersistence,
|
|
4590
|
+
resolvePrismaSchemaOptions
|
|
3753
4591
|
});
|