@saasicat/adapter-drizzle 1.0.0-rc.2 → 1.0.0-rc.21
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 +23 -3
- package/dist/.build-stamp +1 -1
- package/dist/index.cjs +2658 -498
- package/dist/index.d.cts +4458 -776
- package/dist/index.d.ts +4458 -776
- package/dist/index.js +2639 -490
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -8,11 +8,11 @@ var __export = (target, all) => {
|
|
|
8
8
|
for (var name in all)
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
|
-
var __copyProps = (to, from, except,
|
|
11
|
+
var __copyProps = (to, from, except, desc10) => {
|
|
12
12
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
13
|
for (let key of __getOwnPropNames(from))
|
|
14
14
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc10 = __getOwnPropDesc(from, key)) || desc10.enumerable });
|
|
16
16
|
}
|
|
17
17
|
return to;
|
|
18
18
|
};
|
|
@@ -23,19 +23,30 @@ var index_exports = {};
|
|
|
23
23
|
__export(index_exports, {
|
|
24
24
|
AsyncLocalRlsBypassAdapter: () => AsyncLocalRlsBypassAdapter,
|
|
25
25
|
DRIZZLE_DB_TOKEN: () => DRIZZLE_DB_TOKEN,
|
|
26
|
+
DrizzleAppliedSettingsRepository: () => DrizzleAppliedSettingsRepository,
|
|
26
27
|
DrizzleAuditAdapter: () => DrizzleAuditAdapter,
|
|
27
28
|
DrizzleAuditQueryAdapter: () => DrizzleAuditQueryAdapter,
|
|
28
29
|
DrizzleAuditStatsAdapter: () => DrizzleAuditStatsAdapter,
|
|
30
|
+
DrizzleBundleRepository: () => DrizzleBundleRepository,
|
|
29
31
|
DrizzleMfaAdapter: () => DrizzleMfaAdapter,
|
|
32
|
+
DrizzlePaymentEventLog: () => DrizzlePaymentEventLog,
|
|
30
33
|
DrizzlePlanCatalogImportSink: () => DrizzlePlanCatalogImportSink,
|
|
31
34
|
DrizzlePlanCatalogReadSink: () => DrizzlePlanCatalogReadSink,
|
|
35
|
+
DrizzlePlanRepository: () => DrizzlePlanRepository,
|
|
32
36
|
DrizzlePlanVersionRepository: () => DrizzlePlanVersionRepository,
|
|
37
|
+
DrizzlePromoCodeHoldRepository: () => DrizzlePromoCodeHoldRepository,
|
|
33
38
|
DrizzlePromoCodeRedemptionRepository: () => DrizzlePromoCodeRedemptionRepository,
|
|
34
39
|
DrizzlePromoCodeRepository: () => DrizzlePromoCodeRepository,
|
|
35
40
|
DrizzlePromoCodeValidationLogRepository: () => DrizzlePromoCodeValidationLogRepository,
|
|
36
41
|
DrizzlePromoSubscriptionLookup: () => DrizzlePromoSubscriptionLookup,
|
|
42
|
+
DrizzleSubscriberPaymentMethodRepository: () => DrizzleSubscriberPaymentMethodRepository,
|
|
43
|
+
DrizzleSubscriberRepository: () => DrizzleSubscriberRepository,
|
|
44
|
+
DrizzleSubscriptionBundleRepository: () => DrizzleSubscriptionBundleRepository,
|
|
45
|
+
DrizzleSubscriptionContractRepository: () => DrizzleSubscriptionContractRepository,
|
|
37
46
|
DrizzleSubscriptionRepository: () => DrizzleSubscriptionRepository,
|
|
47
|
+
DrizzleSubscriptionUsageAdapter: () => DrizzleSubscriptionUsageAdapter,
|
|
38
48
|
DrizzleSuperAdminBootstrapAdapter: () => DrizzleSuperAdminBootstrapAdapter,
|
|
49
|
+
DrizzleTenantSubscriptionWrite: () => DrizzleTenantSubscriptionWrite,
|
|
39
50
|
DrizzleTransactionRunner: () => DrizzleTransactionRunner,
|
|
40
51
|
PASSWORD_HASHER_TOKEN: () => PASSWORD_HASHER_TOKEN,
|
|
41
52
|
ZeroPromoRevenueDeductionAggregator: () => ZeroPromoRevenueDeductionAggregator,
|
|
@@ -46,16 +57,14 @@ __export(index_exports, {
|
|
|
46
57
|
module.exports = __toCommonJS(index_exports);
|
|
47
58
|
|
|
48
59
|
// src/client.ts
|
|
60
|
+
var import_core = require("@saasicat/core");
|
|
49
61
|
var DRIZZLE_DB_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/adapter-drizzle/DrizzleDb");
|
|
50
62
|
function resolveDb(client, tx) {
|
|
51
63
|
return tx ?? client;
|
|
52
64
|
}
|
|
53
65
|
__name(resolveDb, "resolveDb");
|
|
54
66
|
function toQuotaMap(value) {
|
|
55
|
-
|
|
56
|
-
return value;
|
|
57
|
-
}
|
|
58
|
-
return {};
|
|
67
|
+
return (0, import_core.readQuotaRecord)(value);
|
|
59
68
|
}
|
|
60
69
|
__name(toQuotaMap, "toQuotaMap");
|
|
61
70
|
function toStringArray(value) {
|
|
@@ -70,9 +79,9 @@ __name(escapeLikePattern, "escapeLikePattern");
|
|
|
70
79
|
// src/async-local-rls-bypass.adapter.ts
|
|
71
80
|
var import_common = require("@nestjs/common");
|
|
72
81
|
var import_node_async_hooks = require("node:async_hooks");
|
|
73
|
-
function _ts_decorate(decorators, target, key,
|
|
74
|
-
var c = arguments.length, r = c < 3 ? target :
|
|
75
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key,
|
|
82
|
+
function _ts_decorate(decorators, target, key, desc10) {
|
|
83
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
84
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
76
85
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
77
86
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
78
87
|
}
|
|
@@ -96,20 +105,35 @@ AsyncLocalRlsBypassAdapter = _ts_decorate([
|
|
|
96
105
|
(0, import_common.Injectable)()
|
|
97
106
|
], AsyncLocalRlsBypassAdapter);
|
|
98
107
|
|
|
99
|
-
// src/drizzle-
|
|
108
|
+
// src/drizzle-applied-settings.repository.ts
|
|
100
109
|
var import_node_crypto = require("node:crypto");
|
|
101
110
|
var import_common2 = require("@nestjs/common");
|
|
111
|
+
var import_drizzle_orm = require("drizzle-orm");
|
|
102
112
|
|
|
103
113
|
// src/schema.ts
|
|
104
114
|
var schema_exports = {};
|
|
105
115
|
__export(schema_exports, {
|
|
116
|
+
appliedSettings: () => appliedSettings,
|
|
106
117
|
auditLogs: () => auditLogs,
|
|
118
|
+
bundleVersions: () => bundleVersions,
|
|
119
|
+
bundles: () => bundles,
|
|
120
|
+
contractLineItems: () => contractLineItems,
|
|
107
121
|
featureCatalogEntries: () => featureCatalogEntries,
|
|
122
|
+
paymentEventLog: () => paymentEventLog,
|
|
108
123
|
planVersions: () => planVersions,
|
|
109
124
|
plans: () => plans,
|
|
125
|
+
promoCodeHolds: () => promoCodeHolds,
|
|
110
126
|
promoCodeRedemptions: () => promoCodeRedemptions,
|
|
111
127
|
promoCodeValidationLogs: () => promoCodeValidationLogs,
|
|
112
128
|
promoCodes: () => promoCodes,
|
|
129
|
+
settingsChanges: () => settingsChanges,
|
|
130
|
+
subscriberCorrections: () => subscriberCorrections,
|
|
131
|
+
subscriberPaymentMethodSetups: () => subscriberPaymentMethodSetups,
|
|
132
|
+
subscriberPaymentMethods: () => subscriberPaymentMethods,
|
|
133
|
+
subscriberTenants: () => subscriberTenants,
|
|
134
|
+
subscribers: () => subscribers,
|
|
135
|
+
subscriptionBundles: () => subscriptionBundles,
|
|
136
|
+
subscriptionContracts: () => subscriptionContracts,
|
|
113
137
|
subscriptions: () => subscriptions,
|
|
114
138
|
superAdminMfa: () => superAdminMfa,
|
|
115
139
|
superAdminUsers: () => superAdminUsers
|
|
@@ -133,6 +157,42 @@ var subscriptions = (0, import_pg_core.pgTable)("subscriptions", {
|
|
|
133
157
|
pendingPlanVersionId: (0, import_pg_core.text)("pendingPlanVersionId"),
|
|
134
158
|
isPilot: (0, import_pg_core.boolean)("isPilot").notNull().default(false),
|
|
135
159
|
startedAt: ts("startedAt"),
|
|
160
|
+
// Entitlement resolution ends a subscription by reading these two, so the
|
|
161
|
+
// query map has to describe them: a column this file omits is a column the
|
|
162
|
+
// repository cannot return, and the platform then treats a subscription
|
|
163
|
+
// that ended last January as running.
|
|
164
|
+
canceledAt: ts("canceledAt"),
|
|
165
|
+
canceledEffectiveAt: ts("canceledEffectiveAt"),
|
|
166
|
+
// The billing window, the commitment and the day it is billed on. Omitted
|
|
167
|
+
// until 2026-08-27, and a column this file omits is one the repositories
|
|
168
|
+
// cannot return — which is why the renewal, plan-change and cancellation
|
|
169
|
+
// paths could not be served from this adapter at all.
|
|
170
|
+
trialEndsAt: ts("trialEndsAt"),
|
|
171
|
+
currentPeriodStart: ts("currentPeriodStart"),
|
|
172
|
+
currentPeriodEnd: ts("currentPeriodEnd"),
|
|
173
|
+
minimumTermUntil: ts("minimumTermUntil"),
|
|
174
|
+
billingAnchorDay: (0, import_pg_core.integer)("billingAnchorDay"),
|
|
175
|
+
// A pending version change and how far it has got: announced, reminded
|
|
176
|
+
// about, accepted, and the date it lands.
|
|
177
|
+
pendingPlanVersionEffectiveAt: ts("pendingPlanVersionEffectiveAt"),
|
|
178
|
+
pendingPlanVersionAccepted: (0, import_pg_core.boolean)("pendingPlanVersionAccepted").notNull().default(false),
|
|
179
|
+
pendingPlanVersionAcceptedAt: ts("pendingPlanVersionAcceptedAt"),
|
|
180
|
+
pendingPlanVersionAcceptedByUserId: (0, import_pg_core.text)("pendingPlanVersionAcceptedByUserId"),
|
|
181
|
+
pendingPlanVersionNotifiedAt: ts("pendingPlanVersionNotifiedAt"),
|
|
182
|
+
pendingPlanVersionReminderSentAt: ts("pendingPlanVersionReminderSentAt"),
|
|
183
|
+
postTrialPlan: (0, import_pg_core.text)("postTrialPlan"),
|
|
184
|
+
pendingBillingCycle: (0, import_pg_core.text)("pendingBillingCycle"),
|
|
185
|
+
// A negotiated price and the note that explains it, a pilot window, the
|
|
186
|
+
// offer this subscription came from, and the package as it was sold.
|
|
187
|
+
customMonthlyNet: (0, import_pg_core.numeric)("customMonthlyNet", {
|
|
188
|
+
precision: 10,
|
|
189
|
+
scale: 2
|
|
190
|
+
}),
|
|
191
|
+
customNote: (0, import_pg_core.text)("customNote"),
|
|
192
|
+
pilotEndsAt: ts("pilotEndsAt"),
|
|
193
|
+
pilotNote: (0, import_pg_core.text)("pilotNote"),
|
|
194
|
+
checkoutOfferId: (0, import_pg_core.text)("checkoutOfferId"),
|
|
195
|
+
packageSnapshot: (0, import_pg_core.jsonb)("packageSnapshot"),
|
|
136
196
|
createdAt: ts("createdAt").notNull().defaultNow(),
|
|
137
197
|
updatedAt: ts("updatedAt").notNull()
|
|
138
198
|
});
|
|
@@ -157,6 +217,12 @@ var planVersions = (0, import_pg_core.pgTable)("plan_versions", {
|
|
|
157
217
|
publishedChanges: (0, import_pg_core.jsonb)("publishedChanges"),
|
|
158
218
|
changeNote: (0, import_pg_core.text)("changeNote").notNull(),
|
|
159
219
|
nonRegressive: (0, import_pg_core.boolean)("nonRegressive").notNull().default(true),
|
|
220
|
+
// The booking window for new subscriptions, and the day the plan itself
|
|
221
|
+
// stops. All three nullable, so a schema that predates them stays valid and
|
|
222
|
+
// an adapter that does not maintain them says so rather than writing them.
|
|
223
|
+
validFrom: ts("validFrom"),
|
|
224
|
+
validUntil: ts("validUntil"),
|
|
225
|
+
endsAt: ts("endsAt"),
|
|
160
226
|
createdByUserId: (0, import_pg_core.text)("createdByUserId"),
|
|
161
227
|
publishedByUserId: (0, import_pg_core.text)("publishedByUserId"),
|
|
162
228
|
createdAt: ts("createdAt").notNull().defaultNow(),
|
|
@@ -164,7 +230,6 @@ var planVersions = (0, import_pg_core.pgTable)("plan_versions", {
|
|
|
164
230
|
});
|
|
165
231
|
var plans = (0, import_pg_core.pgTable)("plans", {
|
|
166
232
|
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
167
|
-
projectKey: (0, import_pg_core.text)("projectKey").notNull(),
|
|
168
233
|
planKey: (0, import_pg_core.text)("planKey").notNull(),
|
|
169
234
|
label: (0, import_pg_core.text)("label").notNull(),
|
|
170
235
|
description: (0, import_pg_core.text)("description"),
|
|
@@ -174,9 +239,66 @@ var plans = (0, import_pg_core.pgTable)("plans", {
|
|
|
174
239
|
updatedAt: ts("updatedAt").notNull(),
|
|
175
240
|
deletedAt: ts("deletedAt")
|
|
176
241
|
});
|
|
242
|
+
var subscriptionContracts = (0, import_pg_core.pgTable)("subscription_contracts", {
|
|
243
|
+
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
244
|
+
tenantId: (0, import_pg_core.text)("tenantId").notNull(),
|
|
245
|
+
// Postgres enum, declared as text: parameterized values are coerced and
|
|
246
|
+
// reads come back as strings, which is what the records expect.
|
|
247
|
+
status: (0, import_pg_core.text)("status").notNull().default("active"),
|
|
248
|
+
effectiveFrom: ts("effectiveFrom").notNull(),
|
|
249
|
+
effectiveUntil: ts("effectiveUntil"),
|
|
250
|
+
originalOfferId: (0, import_pg_core.text)("originalOfferId"),
|
|
251
|
+
originalPlanVersionId: (0, import_pg_core.text)("originalPlanVersionId"),
|
|
252
|
+
originalBundleVersionIds: (0, import_pg_core.jsonb)("originalBundleVersionIds").notNull(),
|
|
253
|
+
entitlementSnapshot: (0, import_pg_core.jsonb)("entitlementSnapshot"),
|
|
254
|
+
priceSnapshot: (0, import_pg_core.jsonb)("priceSnapshot").notNull(),
|
|
255
|
+
promotionSnapshots: (0, import_pg_core.jsonb)("promotionSnapshots").notNull(),
|
|
256
|
+
promoCodeSnapshots: (0, import_pg_core.jsonb)("promoCodeSnapshots").notNull(),
|
|
257
|
+
termsSnapshot: (0, import_pg_core.jsonb)("termsSnapshot"),
|
|
258
|
+
// Who the contract is between, copied when it was concluded.
|
|
259
|
+
subscriberId: (0, import_pg_core.text)("subscriberId").notNull(),
|
|
260
|
+
subscriberSnapshot: (0, import_pg_core.jsonb)("subscriberSnapshot").notNull(),
|
|
261
|
+
issuerSnapshot: (0, import_pg_core.jsonb)("issuerSnapshot"),
|
|
262
|
+
partiesMigrated: (0, import_pg_core.boolean)("partiesMigrated").notNull().default(false),
|
|
263
|
+
createdAt: ts("createdAt").notNull().defaultNow(),
|
|
264
|
+
updatedAt: ts("updatedAt").notNull()
|
|
265
|
+
});
|
|
266
|
+
var contractLineItems = (0, import_pg_core.pgTable)("contract_line_items", {
|
|
267
|
+
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
268
|
+
contractId: (0, import_pg_core.text)("contractId").notNull(),
|
|
269
|
+
kind: (0, import_pg_core.text)("kind").notNull(),
|
|
270
|
+
sourceKey: (0, import_pg_core.text)("sourceKey").notNull(),
|
|
271
|
+
sourceVersionId: (0, import_pg_core.text)("sourceVersionId"),
|
|
272
|
+
titleSnapshot: (0, import_pg_core.text)("titleSnapshot").notNull(),
|
|
273
|
+
descriptionSnapshot: (0, import_pg_core.text)("descriptionSnapshot"),
|
|
274
|
+
quantity: (0, import_pg_core.integer)("quantity").notNull().default(1),
|
|
275
|
+
unit: (0, import_pg_core.text)("unit"),
|
|
276
|
+
priceNet: (0, import_pg_core.numeric)("priceNet", {
|
|
277
|
+
precision: 10,
|
|
278
|
+
scale: 2
|
|
279
|
+
}).notNull(),
|
|
280
|
+
priceGross: (0, import_pg_core.numeric)("priceGross", {
|
|
281
|
+
precision: 10,
|
|
282
|
+
scale: 2
|
|
283
|
+
}).notNull(),
|
|
284
|
+
billingCycle: (0, import_pg_core.text)("billingCycle").notNull(),
|
|
285
|
+
currency: (0, import_pg_core.text)("currency").notNull(),
|
|
286
|
+
taxRate: (0, import_pg_core.numeric)("taxRate", {
|
|
287
|
+
precision: 5,
|
|
288
|
+
scale: 2
|
|
289
|
+
}).notNull(),
|
|
290
|
+
taxAmount: (0, import_pg_core.numeric)("taxAmount", {
|
|
291
|
+
precision: 10,
|
|
292
|
+
scale: 2
|
|
293
|
+
}).notNull(),
|
|
294
|
+
minimumTermUntil: ts("minimumTermUntil"),
|
|
295
|
+
featuresSnapshot: (0, import_pg_core.jsonb)("featuresSnapshot").notNull(),
|
|
296
|
+
quotaEffectsSnapshot: (0, import_pg_core.jsonb)("quotaEffectsSnapshot").notNull(),
|
|
297
|
+
metadata: (0, import_pg_core.jsonb)("metadata"),
|
|
298
|
+
createdAt: ts("createdAt").notNull().defaultNow()
|
|
299
|
+
});
|
|
177
300
|
var featureCatalogEntries = (0, import_pg_core.pgTable)("feature_catalog_entries", {
|
|
178
301
|
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
179
|
-
projectKey: (0, import_pg_core.text)("projectKey").notNull(),
|
|
180
302
|
featureKey: (0, import_pg_core.text)("featureKey").notNull(),
|
|
181
303
|
label: (0, import_pg_core.text)("label").notNull(),
|
|
182
304
|
description: (0, import_pg_core.text)("description"),
|
|
@@ -213,6 +335,7 @@ var promoCodes = (0, import_pg_core.pgTable)("promo_codes", {
|
|
|
213
335
|
validUntil: ts("validUntil"),
|
|
214
336
|
maxRedemptions: (0, import_pg_core.integer)("maxRedemptions"),
|
|
215
337
|
redemptionsCount: (0, import_pg_core.integer)("redemptionsCount").notNull().default(0),
|
|
338
|
+
heldCount: (0, import_pg_core.integer)("heldCount").notNull().default(0),
|
|
216
339
|
appliesToPlans: (0, import_pg_core.text)("appliesToPlans").array(),
|
|
217
340
|
appliesToBilling: (0, import_pg_core.text)("appliesToBilling"),
|
|
218
341
|
firstTimeCustomersOnly: (0, import_pg_core.boolean)("firstTimeCustomersOnly").notNull().default(true),
|
|
@@ -248,6 +371,16 @@ var promoCodeRedemptions = (0, import_pg_core.pgTable)("promo_code_redemptions",
|
|
|
248
371
|
redeemedAt: ts("redeemedAt").notNull().defaultNow(),
|
|
249
372
|
reversedAt: ts("reversedAt")
|
|
250
373
|
});
|
|
374
|
+
var promoCodeHolds = (0, import_pg_core.pgTable)("promo_code_holds", {
|
|
375
|
+
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
376
|
+
promoCodeId: (0, import_pg_core.text)("promoCodeId").notNull(),
|
|
377
|
+
checkoutOfferId: (0, import_pg_core.text)("checkoutOfferId").notNull(),
|
|
378
|
+
expiresAt: ts("expiresAt").notNull(),
|
|
379
|
+
handedOverTx: (0, import_pg_core.bigint)("handedOverTx", {
|
|
380
|
+
mode: "bigint"
|
|
381
|
+
}),
|
|
382
|
+
createdAt: ts("createdAt").notNull().defaultNow()
|
|
383
|
+
});
|
|
251
384
|
var promoCodeValidationLogs = (0, import_pg_core.pgTable)("promo_code_validation_logs", {
|
|
252
385
|
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
253
386
|
promoCodeId: (0, import_pg_core.text)("promoCodeId"),
|
|
@@ -289,11 +422,166 @@ var superAdminMfa = (0, import_pg_core.pgTable)("super_admin_mfa", {
|
|
|
289
422
|
enabledAt: ts("enabledAt"),
|
|
290
423
|
updatedAt: ts("updatedAt").notNull()
|
|
291
424
|
});
|
|
425
|
+
var bundles = (0, import_pg_core.pgTable)("bundles", {
|
|
426
|
+
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
427
|
+
bundleKey: (0, import_pg_core.text)("bundleKey").notNull(),
|
|
428
|
+
label: (0, import_pg_core.text)("label").notNull(),
|
|
429
|
+
description: (0, import_pg_core.text)("description"),
|
|
430
|
+
icon: (0, import_pg_core.text)("icon"),
|
|
431
|
+
sortOrder: (0, import_pg_core.integer)("sortOrder").notNull().default(0),
|
|
432
|
+
i18n: (0, import_pg_core.jsonb)("i18n").notNull(),
|
|
433
|
+
createdAt: ts("createdAt").notNull().defaultNow(),
|
|
434
|
+
updatedAt: ts("updatedAt").notNull(),
|
|
435
|
+
deletedAt: ts("deletedAt")
|
|
436
|
+
});
|
|
437
|
+
var bundleVersions = (0, import_pg_core.pgTable)("bundle_versions", {
|
|
438
|
+
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
439
|
+
bundleId: (0, import_pg_core.text)("bundleId").notNull(),
|
|
440
|
+
version: (0, import_pg_core.integer)("version").notNull(),
|
|
441
|
+
baseVersionId: (0, import_pg_core.text)("baseVersionId"),
|
|
442
|
+
features: (0, import_pg_core.jsonb)("features").notNull(),
|
|
443
|
+
quotas: (0, import_pg_core.jsonb)("quotas").notNull(),
|
|
444
|
+
compatibility: (0, import_pg_core.jsonb)("compatibility").notNull(),
|
|
445
|
+
pricingOverrides: (0, import_pg_core.jsonb)("pricingOverrides").notNull(),
|
|
446
|
+
// Nullable, unlike a plan version's: a bundle may carry no base price and
|
|
447
|
+
// resolve one per plan through `pricingOverrides` instead.
|
|
448
|
+
monthlyNet: (0, import_pg_core.numeric)("monthlyNet", {
|
|
449
|
+
precision: 10,
|
|
450
|
+
scale: 2
|
|
451
|
+
}),
|
|
452
|
+
yearlyNet: (0, import_pg_core.numeric)("yearlyNet", {
|
|
453
|
+
precision: 10,
|
|
454
|
+
scale: 2
|
|
455
|
+
}),
|
|
456
|
+
marketed: (0, import_pg_core.boolean)("marketed").notNull().default(true),
|
|
457
|
+
publishedAt: ts("publishedAt"),
|
|
458
|
+
supersededAt: ts("supersededAt"),
|
|
459
|
+
publishedChanges: (0, import_pg_core.jsonb)("publishedChanges"),
|
|
460
|
+
changeNote: (0, import_pg_core.text)("changeNote").notNull(),
|
|
461
|
+
nonRegressive: (0, import_pg_core.boolean)("nonRegressive").notNull().default(true),
|
|
462
|
+
validFrom: ts("validFrom"),
|
|
463
|
+
validUntil: ts("validUntil"),
|
|
464
|
+
createdByUserId: (0, import_pg_core.text)("createdByUserId"),
|
|
465
|
+
publishedByUserId: (0, import_pg_core.text)("publishedByUserId"),
|
|
466
|
+
createdAt: ts("createdAt").notNull().defaultNow(),
|
|
467
|
+
updatedAt: ts("updatedAt").notNull()
|
|
468
|
+
});
|
|
469
|
+
var subscriptionBundles = (0, import_pg_core.pgTable)("subscription_bundles", {
|
|
470
|
+
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
471
|
+
subscriptionId: (0, import_pg_core.text)("subscriptionId").notNull(),
|
|
472
|
+
bundleVersionId: (0, import_pg_core.text)("bundleVersionId").notNull(),
|
|
473
|
+
startedAt: ts("startedAt").notNull(),
|
|
474
|
+
minimumTermEndsAt: ts("minimumTermEndsAt"),
|
|
475
|
+
// The rhythm this booking is billed in and the window it is billed for. A
|
|
476
|
+
// bundle's periods end on the day the plan's do; all three are null on a
|
|
477
|
+
// booking made before these columns existed, and on one whose plan had no
|
|
478
|
+
// period to align to — a trial, or a subscription awaiting sales.
|
|
479
|
+
billingCycle: (0, import_pg_core.text)("billingCycle"),
|
|
480
|
+
currentPeriodStart: ts("currentPeriodStart"),
|
|
481
|
+
currentPeriodEnd: ts("currentPeriodEnd"),
|
|
482
|
+
canceledAt: ts("canceledAt"),
|
|
483
|
+
canceledEffectiveAt: ts("canceledEffectiveAt"),
|
|
484
|
+
createdAt: ts("createdAt").notNull().defaultNow(),
|
|
485
|
+
updatedAt: ts("updatedAt").notNull()
|
|
486
|
+
});
|
|
487
|
+
var appliedSettings = (0, import_pg_core.pgTable)("applied_settings", {
|
|
488
|
+
id: (0, import_pg_core.text)("id").primaryKey().default("installation"),
|
|
489
|
+
fingerprint: (0, import_pg_core.text)("fingerprint").notNull(),
|
|
490
|
+
settings: (0, import_pg_core.jsonb)("settings").notNull(),
|
|
491
|
+
source: (0, import_pg_core.text)("source").notNull(),
|
|
492
|
+
appliedAt: ts("appliedAt").notNull()
|
|
493
|
+
});
|
|
494
|
+
var settingsChanges = (0, import_pg_core.pgTable)("settings_changes", {
|
|
495
|
+
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
496
|
+
// Numbered by the database at the write that recorded the change, and the
|
|
497
|
+
// order the list is read in. Never set by the adapter.
|
|
498
|
+
seq: (0, import_pg_core.serial)("seq").notNull(),
|
|
499
|
+
noticedAt: ts("noticedAt").notNull(),
|
|
500
|
+
source: (0, import_pg_core.text)("source").notNull(),
|
|
501
|
+
previous: (0, import_pg_core.jsonb)("previous").notNull(),
|
|
502
|
+
current: (0, import_pg_core.jsonb)("current").notNull(),
|
|
503
|
+
acknowledgedAt: ts("acknowledgedAt"),
|
|
504
|
+
acknowledgedBy: (0, import_pg_core.text)("acknowledgedBy")
|
|
505
|
+
});
|
|
506
|
+
var subscribers = (0, import_pg_core.pgTable)("subscribers", {
|
|
507
|
+
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
508
|
+
// Counted by the database, from where `constraints.postgres.sql` starts it.
|
|
509
|
+
// Never set by the adapter.
|
|
510
|
+
customerSequence: (0, import_pg_core.serial)("customerSequence").notNull(),
|
|
511
|
+
customerNumberPrefix: (0, import_pg_core.text)("customerNumberPrefix").notNull().default(""),
|
|
512
|
+
legalName: (0, import_pg_core.text)("legalName").notNull(),
|
|
513
|
+
vatId: (0, import_pg_core.text)("vatId"),
|
|
514
|
+
taxNumber: (0, import_pg_core.text)("taxNumber"),
|
|
515
|
+
addressLine1: (0, import_pg_core.text)("addressLine1"),
|
|
516
|
+
addressLine2: (0, import_pg_core.text)("addressLine2"),
|
|
517
|
+
postalCode: (0, import_pg_core.text)("postalCode"),
|
|
518
|
+
city: (0, import_pg_core.text)("city"),
|
|
519
|
+
country: (0, import_pg_core.text)("country"),
|
|
520
|
+
invoiceEmail: (0, import_pg_core.text)("invoiceEmail"),
|
|
521
|
+
migrated: (0, import_pg_core.boolean)("migrated").notNull().default(false),
|
|
522
|
+
createdAt: ts("createdAt").notNull().defaultNow(),
|
|
523
|
+
updatedAt: ts("updatedAt").notNull()
|
|
524
|
+
});
|
|
525
|
+
var subscriberTenants = (0, import_pg_core.pgTable)("subscriber_tenants", {
|
|
526
|
+
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
527
|
+
subscriberId: (0, import_pg_core.text)("subscriberId").notNull(),
|
|
528
|
+
tenantId: (0, import_pg_core.text)("tenantId").notNull(),
|
|
529
|
+
linkedAt: ts("linkedAt").notNull().defaultNow(),
|
|
530
|
+
unlinkedAt: ts("unlinkedAt")
|
|
531
|
+
});
|
|
532
|
+
var subscriberCorrections = (0, import_pg_core.pgTable)("subscriber_corrections", {
|
|
533
|
+
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
534
|
+
subscriberId: (0, import_pg_core.text)("subscriberId").notNull(),
|
|
535
|
+
previous: (0, import_pg_core.jsonb)("previous").notNull(),
|
|
536
|
+
corrected: (0, import_pg_core.jsonb)("corrected").notNull(),
|
|
537
|
+
reason: (0, import_pg_core.text)("reason").notNull(),
|
|
538
|
+
correctedBy: (0, import_pg_core.text)("correctedBy").notNull(),
|
|
539
|
+
correctedAt: ts("correctedAt").notNull()
|
|
540
|
+
});
|
|
541
|
+
var subscriberPaymentMethodSetups = (0, import_pg_core.pgTable)("subscriber_payment_method_setups", {
|
|
542
|
+
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
543
|
+
subscriberId: (0, import_pg_core.text)("subscriberId").notNull(),
|
|
544
|
+
gatewayAccount: (0, import_pg_core.text)("gatewayAccount").notNull(),
|
|
545
|
+
sessionRef: (0, import_pg_core.text)("sessionRef").notNull(),
|
|
546
|
+
customerRef: (0, import_pg_core.text)("customerRef").notNull(),
|
|
547
|
+
startedAt: ts("startedAt").notNull().defaultNow(),
|
|
548
|
+
completedAt: ts("completedAt")
|
|
549
|
+
});
|
|
550
|
+
var paymentEventLog = (0, import_pg_core.pgTable)("PaymentEventLog", {
|
|
551
|
+
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
552
|
+
gatewayAccount: (0, import_pg_core.text)("gatewayAccount").notNull(),
|
|
553
|
+
eventId: (0, import_pg_core.text)("eventId").notNull(),
|
|
554
|
+
provider: (0, import_pg_core.text)("provider").notNull(),
|
|
555
|
+
sessionId: (0, import_pg_core.text)("sessionId"),
|
|
556
|
+
status: (0, import_pg_core.text)("status").notNull(),
|
|
557
|
+
payload: (0, import_pg_core.jsonb)("payload"),
|
|
558
|
+
processedAt: ts("processedAt").notNull().defaultNow()
|
|
559
|
+
});
|
|
560
|
+
var subscriberPaymentMethods = (0, import_pg_core.pgTable)("subscriber_payment_methods", {
|
|
561
|
+
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
562
|
+
subscriberId: (0, import_pg_core.text)("subscriberId").notNull(),
|
|
563
|
+
gatewayAccount: (0, import_pg_core.text)("gatewayAccount").notNull(),
|
|
564
|
+
provider: (0, import_pg_core.text)("provider").notNull(),
|
|
565
|
+
customerRef: (0, import_pg_core.text)("customerRef").notNull(),
|
|
566
|
+
paymentMethodRef: (0, import_pg_core.text)("paymentMethodRef").notNull(),
|
|
567
|
+
type: (0, import_pg_core.text)("type").notNull(),
|
|
568
|
+
brand: (0, import_pg_core.text)("brand"),
|
|
569
|
+
last4: (0, import_pg_core.text)("last4").notNull(),
|
|
570
|
+
expiryMonth: (0, import_pg_core.integer)("expiryMonth"),
|
|
571
|
+
expiryYear: (0, import_pg_core.integer)("expiryYear"),
|
|
572
|
+
country: (0, import_pg_core.text)("country"),
|
|
573
|
+
bankCode: (0, import_pg_core.text)("bankCode"),
|
|
574
|
+
mandateReference: (0, import_pg_core.text)("mandateReference"),
|
|
575
|
+
status: (0, import_pg_core.text)("status").notNull().default("ACTIVE"),
|
|
576
|
+
confirmedAt: ts("confirmedAt").notNull(),
|
|
577
|
+
replacedAt: ts("replacedAt"),
|
|
578
|
+
createdAt: ts("createdAt").notNull().defaultNow()
|
|
579
|
+
});
|
|
292
580
|
|
|
293
|
-
// src/drizzle-
|
|
294
|
-
function _ts_decorate2(decorators, target, key,
|
|
295
|
-
var c = arguments.length, r = c < 3 ? target :
|
|
296
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key,
|
|
581
|
+
// src/drizzle-applied-settings.repository.ts
|
|
582
|
+
function _ts_decorate2(decorators, target, key, desc10) {
|
|
583
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
584
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
297
585
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
298
586
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
299
587
|
}
|
|
@@ -308,6 +596,126 @@ function _ts_param(paramIndex, decorator) {
|
|
|
308
596
|
};
|
|
309
597
|
}
|
|
310
598
|
__name(_ts_param, "_ts_param");
|
|
599
|
+
var INSTALLATION_ROW_ID = "installation";
|
|
600
|
+
var DrizzleAppliedSettingsRepository = class {
|
|
601
|
+
static {
|
|
602
|
+
__name(this, "DrizzleAppliedSettingsRepository");
|
|
603
|
+
}
|
|
604
|
+
db;
|
|
605
|
+
constructor(db) {
|
|
606
|
+
this.db = db;
|
|
607
|
+
}
|
|
608
|
+
async readApplied() {
|
|
609
|
+
const rows = await this.db.select().from(appliedSettings).where((0, import_drizzle_orm.eq)(appliedSettings.id, INSTALLATION_ROW_ID)).limit(1);
|
|
610
|
+
const row = rows[0];
|
|
611
|
+
if (!row) return null;
|
|
612
|
+
return {
|
|
613
|
+
fingerprint: row.fingerprint,
|
|
614
|
+
settings: toValues(row.settings),
|
|
615
|
+
source: row.source,
|
|
616
|
+
appliedAt: row.appliedAt
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
async writeApplied(record, expectedFingerprint) {
|
|
620
|
+
return replaceRecord(this.db, record, expectedFingerprint);
|
|
621
|
+
}
|
|
622
|
+
async recordChange(change, record, expectedFingerprint) {
|
|
623
|
+
return this.db.transaction(async (tx) => {
|
|
624
|
+
const db = tx;
|
|
625
|
+
if (!await replaceRecord(db, record, expectedFingerprint)) return null;
|
|
626
|
+
const row = {
|
|
627
|
+
id: (0, import_node_crypto.randomUUID)(),
|
|
628
|
+
noticedAt: change.noticedAt,
|
|
629
|
+
source: change.source,
|
|
630
|
+
previous: change.previous,
|
|
631
|
+
current: change.current,
|
|
632
|
+
acknowledgedAt: null,
|
|
633
|
+
acknowledgedBy: null
|
|
634
|
+
};
|
|
635
|
+
await db.insert(settingsChanges).values(row);
|
|
636
|
+
return toChangeRecord(row);
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
async listChanges(filter = {}) {
|
|
640
|
+
const acknowledgement = filter.acknowledged === void 0 ? void 0 : filter.acknowledged ? (0, import_drizzle_orm.isNotNull)(settingsChanges.acknowledgedAt) : (0, import_drizzle_orm.isNull)(settingsChanges.acknowledgedAt);
|
|
641
|
+
const query = this.db.select().from(settingsChanges).where(acknowledgement).orderBy((0, import_drizzle_orm.desc)(settingsChanges.seq));
|
|
642
|
+
const rows = filter.limit === void 0 ? await query : await query.limit(filter.limit);
|
|
643
|
+
return rows.map(toChangeRecord);
|
|
644
|
+
}
|
|
645
|
+
async acknowledgeChange(id, acknowledgedBy, acknowledgedAt) {
|
|
646
|
+
await this.db.update(settingsChanges).set({
|
|
647
|
+
acknowledgedAt,
|
|
648
|
+
acknowledgedBy
|
|
649
|
+
}).where((0, import_drizzle_orm.and)((0, import_drizzle_orm.eq)(settingsChanges.id, id), (0, import_drizzle_orm.isNull)(settingsChanges.acknowledgedAt)));
|
|
650
|
+
const rows = await this.db.select().from(settingsChanges).where((0, import_drizzle_orm.eq)(settingsChanges.id, id)).limit(1);
|
|
651
|
+
return rows[0] ? toChangeRecord(rows[0]) : null;
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
DrizzleAppliedSettingsRepository = _ts_decorate2([
|
|
655
|
+
(0, import_common2.Injectable)(),
|
|
656
|
+
_ts_param(0, (0, import_common2.Inject)(DRIZZLE_DB_TOKEN)),
|
|
657
|
+
_ts_metadata("design:type", Function),
|
|
658
|
+
_ts_metadata("design:paramtypes", [
|
|
659
|
+
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
660
|
+
])
|
|
661
|
+
], DrizzleAppliedSettingsRepository);
|
|
662
|
+
async function replaceRecord(db, record, expectedFingerprint) {
|
|
663
|
+
const values = {
|
|
664
|
+
fingerprint: record.fingerprint,
|
|
665
|
+
settings: record.settings,
|
|
666
|
+
source: record.source,
|
|
667
|
+
appliedAt: record.appliedAt
|
|
668
|
+
};
|
|
669
|
+
const written = expectedFingerprint === null ? await db.insert(appliedSettings).values({
|
|
670
|
+
id: INSTALLATION_ROW_ID,
|
|
671
|
+
...values
|
|
672
|
+
}).onConflictDoNothing({
|
|
673
|
+
target: appliedSettings.id
|
|
674
|
+
}).returning({
|
|
675
|
+
id: appliedSettings.id
|
|
676
|
+
}) : await db.update(appliedSettings).set(values).where((0, import_drizzle_orm.and)((0, import_drizzle_orm.eq)(appliedSettings.id, INSTALLATION_ROW_ID), (0, import_drizzle_orm.eq)(appliedSettings.fingerprint, expectedFingerprint))).returning({
|
|
677
|
+
id: appliedSettings.id
|
|
678
|
+
});
|
|
679
|
+
return written.length === 1;
|
|
680
|
+
}
|
|
681
|
+
__name(replaceRecord, "replaceRecord");
|
|
682
|
+
function toChangeRecord(row) {
|
|
683
|
+
return {
|
|
684
|
+
id: row.id,
|
|
685
|
+
noticedAt: row.noticedAt,
|
|
686
|
+
source: row.source,
|
|
687
|
+
previous: toValues(row.previous),
|
|
688
|
+
current: toValues(row.current),
|
|
689
|
+
acknowledgedAt: row.acknowledgedAt,
|
|
690
|
+
acknowledgedBy: row.acknowledgedBy
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
__name(toChangeRecord, "toChangeRecord");
|
|
694
|
+
function toValues(value) {
|
|
695
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
696
|
+
}
|
|
697
|
+
__name(toValues, "toValues");
|
|
698
|
+
|
|
699
|
+
// src/drizzle-audit.adapter.ts
|
|
700
|
+
var import_node_crypto2 = require("node:crypto");
|
|
701
|
+
var import_common3 = require("@nestjs/common");
|
|
702
|
+
function _ts_decorate3(decorators, target, key, desc10) {
|
|
703
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
704
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
705
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
706
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
707
|
+
}
|
|
708
|
+
__name(_ts_decorate3, "_ts_decorate");
|
|
709
|
+
function _ts_metadata2(k, v) {
|
|
710
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
711
|
+
}
|
|
712
|
+
__name(_ts_metadata2, "_ts_metadata");
|
|
713
|
+
function _ts_param2(paramIndex, decorator) {
|
|
714
|
+
return function(target, key) {
|
|
715
|
+
decorator(target, key, paramIndex);
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
__name(_ts_param2, "_ts_param");
|
|
311
719
|
function buildActorTag(actor) {
|
|
312
720
|
return `${actor.source}:${actor.email}:${actor.context}`;
|
|
313
721
|
}
|
|
@@ -322,7 +730,7 @@ var DrizzleAuditAdapter = class {
|
|
|
322
730
|
}
|
|
323
731
|
async write(input) {
|
|
324
732
|
await this.db.insert(auditLogs).values({
|
|
325
|
-
id: (0,
|
|
733
|
+
id: (0, import_node_crypto2.randomUUID)(),
|
|
326
734
|
tenantId: null,
|
|
327
735
|
userId: input.actor.userId,
|
|
328
736
|
entity: input.entity,
|
|
@@ -333,35 +741,35 @@ var DrizzleAuditAdapter = class {
|
|
|
333
741
|
});
|
|
334
742
|
}
|
|
335
743
|
};
|
|
336
|
-
DrizzleAuditAdapter =
|
|
337
|
-
(0,
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
744
|
+
DrizzleAuditAdapter = _ts_decorate3([
|
|
745
|
+
(0, import_common3.Injectable)(),
|
|
746
|
+
_ts_param2(0, (0, import_common3.Inject)(DRIZZLE_DB_TOKEN)),
|
|
747
|
+
_ts_metadata2("design:type", Function),
|
|
748
|
+
_ts_metadata2("design:paramtypes", [
|
|
341
749
|
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
342
750
|
])
|
|
343
751
|
], DrizzleAuditAdapter);
|
|
344
752
|
|
|
345
753
|
// src/drizzle-audit-query.adapter.ts
|
|
346
|
-
var
|
|
347
|
-
var
|
|
348
|
-
function
|
|
349
|
-
var c = arguments.length, r = c < 3 ? target :
|
|
350
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key,
|
|
754
|
+
var import_common4 = require("@nestjs/common");
|
|
755
|
+
var import_drizzle_orm2 = require("drizzle-orm");
|
|
756
|
+
function _ts_decorate4(decorators, target, key, desc10) {
|
|
757
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
758
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
351
759
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
352
760
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
353
761
|
}
|
|
354
|
-
__name(
|
|
355
|
-
function
|
|
762
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
763
|
+
function _ts_metadata3(k, v) {
|
|
356
764
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
357
765
|
}
|
|
358
|
-
__name(
|
|
359
|
-
function
|
|
766
|
+
__name(_ts_metadata3, "_ts_metadata");
|
|
767
|
+
function _ts_param3(paramIndex, decorator) {
|
|
360
768
|
return function(target, key) {
|
|
361
769
|
decorator(target, key, paramIndex);
|
|
362
770
|
};
|
|
363
771
|
}
|
|
364
|
-
__name(
|
|
772
|
+
__name(_ts_param3, "_ts_param");
|
|
365
773
|
var DEFAULT_PAGE_SIZE = 50;
|
|
366
774
|
var MAX_PAGE_SIZE = 200;
|
|
367
775
|
var DrizzleAuditQueryAdapter = class {
|
|
@@ -376,33 +784,33 @@ var DrizzleAuditQueryAdapter = class {
|
|
|
376
784
|
const page = Math.max(filter.page ?? 1, 1);
|
|
377
785
|
const pageSize = Math.min(Math.max(filter.pageSize ?? DEFAULT_PAGE_SIZE, 1), MAX_PAGE_SIZE);
|
|
378
786
|
const conditions = [
|
|
379
|
-
filter.tenantId ? (0,
|
|
380
|
-
filter.userId ? (0,
|
|
381
|
-
filter.entity ? (0,
|
|
382
|
-
filter.entityId ? (0,
|
|
383
|
-
filter.action ? (0,
|
|
787
|
+
filter.tenantId ? (0, import_drizzle_orm2.eq)(auditLogs.tenantId, filter.tenantId) : void 0,
|
|
788
|
+
filter.userId ? (0, import_drizzle_orm2.eq)(auditLogs.userId, filter.userId) : void 0,
|
|
789
|
+
filter.entity ? (0, import_drizzle_orm2.eq)(auditLogs.entity, filter.entity) : void 0,
|
|
790
|
+
filter.entityId ? (0, import_drizzle_orm2.eq)(auditLogs.entityId, filter.entityId) : void 0,
|
|
791
|
+
filter.action ? (0, import_drizzle_orm2.eq)(auditLogs.action, filter.action) : void 0,
|
|
384
792
|
toActorTagCondition(filter.actorTag),
|
|
385
|
-
filter.from ? (0,
|
|
386
|
-
filter.to ? (0,
|
|
793
|
+
filter.from ? (0, import_drizzle_orm2.gte)(auditLogs.createdAt, new Date(filter.from)) : void 0,
|
|
794
|
+
filter.to ? (0, import_drizzle_orm2.lte)(auditLogs.createdAt, new Date(filter.to)) : void 0
|
|
387
795
|
];
|
|
388
|
-
const rows = await this.db.select().from(auditLogs).where((0,
|
|
796
|
+
const rows = await this.db.select().from(auditLogs).where((0, import_drizzle_orm2.and)(...conditions)).orderBy((0, import_drizzle_orm2.desc)(auditLogs.createdAt)).limit(pageSize).offset((page - 1) * pageSize);
|
|
389
797
|
return rows.map(toAuditEntry);
|
|
390
798
|
}
|
|
391
799
|
};
|
|
392
|
-
DrizzleAuditQueryAdapter =
|
|
393
|
-
(0,
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
800
|
+
DrizzleAuditQueryAdapter = _ts_decorate4([
|
|
801
|
+
(0, import_common4.Injectable)(),
|
|
802
|
+
_ts_param3(0, (0, import_common4.Inject)(DRIZZLE_DB_TOKEN)),
|
|
803
|
+
_ts_metadata3("design:type", Function),
|
|
804
|
+
_ts_metadata3("design:paramtypes", [
|
|
397
805
|
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
398
806
|
])
|
|
399
807
|
], DrizzleAuditQueryAdapter);
|
|
400
808
|
function toActorTagCondition(actorTag) {
|
|
401
809
|
if (!actorTag) return void 0;
|
|
402
810
|
if (actorTag.endsWith("*")) {
|
|
403
|
-
return (0,
|
|
811
|
+
return (0, import_drizzle_orm2.like)(auditLogs.actorTag, `${escapeLikePattern(actorTag.slice(0, -1))}%`);
|
|
404
812
|
}
|
|
405
|
-
return (0,
|
|
813
|
+
return (0, import_drizzle_orm2.eq)(auditLogs.actorTag, actorTag);
|
|
406
814
|
}
|
|
407
815
|
__name(toActorTagCondition, "toActorTagCondition");
|
|
408
816
|
function toAuditEntry(row) {
|
|
@@ -425,25 +833,25 @@ function toAuditEntry(row) {
|
|
|
425
833
|
__name(toAuditEntry, "toAuditEntry");
|
|
426
834
|
|
|
427
835
|
// src/drizzle-audit-stats.adapter.ts
|
|
428
|
-
var
|
|
429
|
-
var
|
|
430
|
-
function
|
|
431
|
-
var c = arguments.length, r = c < 3 ? target :
|
|
432
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key,
|
|
836
|
+
var import_common5 = require("@nestjs/common");
|
|
837
|
+
var import_drizzle_orm3 = require("drizzle-orm");
|
|
838
|
+
function _ts_decorate5(decorators, target, key, desc10) {
|
|
839
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
840
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
433
841
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
434
842
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
435
843
|
}
|
|
436
|
-
__name(
|
|
437
|
-
function
|
|
844
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
845
|
+
function _ts_metadata4(k, v) {
|
|
438
846
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
439
847
|
}
|
|
440
|
-
__name(
|
|
441
|
-
function
|
|
848
|
+
__name(_ts_metadata4, "_ts_metadata");
|
|
849
|
+
function _ts_param4(paramIndex, decorator) {
|
|
442
850
|
return function(target, key) {
|
|
443
851
|
decorator(target, key, paramIndex);
|
|
444
852
|
};
|
|
445
853
|
}
|
|
446
|
-
__name(
|
|
854
|
+
__name(_ts_param4, "_ts_param");
|
|
447
855
|
var DrizzleAuditStatsAdapter = class {
|
|
448
856
|
static {
|
|
449
857
|
__name(this, "DrizzleAuditStatsAdapter");
|
|
@@ -454,40 +862,40 @@ var DrizzleAuditStatsAdapter = class {
|
|
|
454
862
|
}
|
|
455
863
|
async countSince(since) {
|
|
456
864
|
const rows = await this.db.select({
|
|
457
|
-
value: (0,
|
|
458
|
-
}).from(auditLogs).where((0,
|
|
865
|
+
value: (0, import_drizzle_orm3.count)()
|
|
866
|
+
}).from(auditLogs).where((0, import_drizzle_orm3.gte)(auditLogs.createdAt, since));
|
|
459
867
|
return rows[0]?.value ?? 0;
|
|
460
868
|
}
|
|
461
869
|
};
|
|
462
|
-
DrizzleAuditStatsAdapter =
|
|
463
|
-
(0,
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
870
|
+
DrizzleAuditStatsAdapter = _ts_decorate5([
|
|
871
|
+
(0, import_common5.Injectable)(),
|
|
872
|
+
_ts_param4(0, (0, import_common5.Inject)(DRIZZLE_DB_TOKEN)),
|
|
873
|
+
_ts_metadata4("design:type", Function),
|
|
874
|
+
_ts_metadata4("design:paramtypes", [
|
|
467
875
|
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
468
876
|
])
|
|
469
877
|
], DrizzleAuditStatsAdapter);
|
|
470
878
|
|
|
471
879
|
// src/drizzle-mfa.adapter.ts
|
|
472
|
-
var
|
|
473
|
-
var
|
|
474
|
-
function
|
|
475
|
-
var c = arguments.length, r = c < 3 ? target :
|
|
476
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key,
|
|
880
|
+
var import_common6 = require("@nestjs/common");
|
|
881
|
+
var import_drizzle_orm4 = require("drizzle-orm");
|
|
882
|
+
function _ts_decorate6(decorators, target, key, desc10) {
|
|
883
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
884
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
477
885
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
478
886
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
479
887
|
}
|
|
480
|
-
__name(
|
|
481
|
-
function
|
|
888
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
889
|
+
function _ts_metadata5(k, v) {
|
|
482
890
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
483
891
|
}
|
|
484
|
-
__name(
|
|
485
|
-
function
|
|
892
|
+
__name(_ts_metadata5, "_ts_metadata");
|
|
893
|
+
function _ts_param5(paramIndex, decorator) {
|
|
486
894
|
return function(target, key) {
|
|
487
895
|
decorator(target, key, paramIndex);
|
|
488
896
|
};
|
|
489
897
|
}
|
|
490
|
-
__name(
|
|
898
|
+
__name(_ts_param5, "_ts_param");
|
|
491
899
|
var DrizzleMfaAdapter = class {
|
|
492
900
|
static {
|
|
493
901
|
__name(this, "DrizzleMfaAdapter");
|
|
@@ -497,7 +905,7 @@ var DrizzleMfaAdapter = class {
|
|
|
497
905
|
this.db = db;
|
|
498
906
|
}
|
|
499
907
|
async getSecret(userId) {
|
|
500
|
-
const rows = await this.db.select().from(superAdminMfa).where((0,
|
|
908
|
+
const rows = await this.db.select().from(superAdminMfa).where((0, import_drizzle_orm4.eq)(superAdminMfa.userId, userId)).limit(1);
|
|
501
909
|
return rows[0]?.secret ?? null;
|
|
502
910
|
}
|
|
503
911
|
async setSecret(userId, secret) {
|
|
@@ -517,41 +925,41 @@ var DrizzleMfaAdapter = class {
|
|
|
517
925
|
});
|
|
518
926
|
}
|
|
519
927
|
async isEnabled(userId) {
|
|
520
|
-
const rows = await this.db.select().from(superAdminMfa).where((0,
|
|
928
|
+
const rows = await this.db.select().from(superAdminMfa).where((0, import_drizzle_orm4.eq)(superAdminMfa.userId, userId)).limit(1);
|
|
521
929
|
const row = rows[0];
|
|
522
930
|
return !!row?.secret && !!row?.enabledAt;
|
|
523
931
|
}
|
|
524
932
|
};
|
|
525
|
-
DrizzleMfaAdapter =
|
|
526
|
-
(0,
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
933
|
+
DrizzleMfaAdapter = _ts_decorate6([
|
|
934
|
+
(0, import_common6.Injectable)(),
|
|
935
|
+
_ts_param5(0, (0, import_common6.Inject)(DRIZZLE_DB_TOKEN)),
|
|
936
|
+
_ts_metadata5("design:type", Function),
|
|
937
|
+
_ts_metadata5("design:paramtypes", [
|
|
530
938
|
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
531
939
|
])
|
|
532
940
|
], DrizzleMfaAdapter);
|
|
533
941
|
|
|
534
942
|
// src/drizzle-plan-catalog-import-sink.adapter.ts
|
|
535
|
-
var
|
|
536
|
-
var
|
|
537
|
-
var
|
|
538
|
-
function
|
|
539
|
-
var c = arguments.length, r = c < 3 ? target :
|
|
540
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key,
|
|
943
|
+
var import_node_crypto3 = require("node:crypto");
|
|
944
|
+
var import_common7 = require("@nestjs/common");
|
|
945
|
+
var import_drizzle_orm5 = require("drizzle-orm");
|
|
946
|
+
function _ts_decorate7(decorators, target, key, desc10) {
|
|
947
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
948
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
541
949
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
542
950
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
543
951
|
}
|
|
544
|
-
__name(
|
|
545
|
-
function
|
|
952
|
+
__name(_ts_decorate7, "_ts_decorate");
|
|
953
|
+
function _ts_metadata6(k, v) {
|
|
546
954
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
547
955
|
}
|
|
548
|
-
__name(
|
|
549
|
-
function
|
|
956
|
+
__name(_ts_metadata6, "_ts_metadata");
|
|
957
|
+
function _ts_param6(paramIndex, decorator) {
|
|
550
958
|
return function(target, key) {
|
|
551
959
|
decorator(target, key, paramIndex);
|
|
552
960
|
};
|
|
553
961
|
}
|
|
554
|
-
__name(
|
|
962
|
+
__name(_ts_param6, "_ts_param");
|
|
555
963
|
var DrizzlePlanCatalogImportSink = class {
|
|
556
964
|
static {
|
|
557
965
|
__name(this, "DrizzlePlanCatalogImportSink");
|
|
@@ -563,14 +971,13 @@ var DrizzlePlanCatalogImportSink = class {
|
|
|
563
971
|
async upsertPlan(input) {
|
|
564
972
|
const existing = await this.db.select({
|
|
565
973
|
id: plans.id
|
|
566
|
-
}).from(plans).where((0,
|
|
974
|
+
}).from(plans).where((0, import_drizzle_orm5.eq)(plans.planKey, input.planKey)).limit(1);
|
|
567
975
|
if (existing[0]) return {
|
|
568
976
|
created: false,
|
|
569
977
|
skipReason: "exists"
|
|
570
978
|
};
|
|
571
979
|
await this.db.insert(plans).values({
|
|
572
|
-
id: (0,
|
|
573
|
-
projectKey: input.projectKey,
|
|
980
|
+
id: (0, import_node_crypto3.randomUUID)(),
|
|
574
981
|
planKey: input.planKey,
|
|
575
982
|
label: input.label,
|
|
576
983
|
description: input.description ?? null,
|
|
@@ -584,7 +991,7 @@ var DrizzlePlanCatalogImportSink = class {
|
|
|
584
991
|
async upsertPlanVersion(input) {
|
|
585
992
|
const existing = await this.db.select({
|
|
586
993
|
id: planVersions.id
|
|
587
|
-
}).from(planVersions).where((0,
|
|
994
|
+
}).from(planVersions).where((0, import_drizzle_orm5.and)((0, import_drizzle_orm5.eq)(planVersions.planId, input.planKey), (0, import_drizzle_orm5.eq)(planVersions.version, input.version))).limit(1);
|
|
588
995
|
if (existing[0]) return {
|
|
589
996
|
created: false,
|
|
590
997
|
skipReason: "exists"
|
|
@@ -594,10 +1001,10 @@ var DrizzlePlanCatalogImportSink = class {
|
|
|
594
1001
|
await this.db.update(planVersions).set({
|
|
595
1002
|
supersededAt: now,
|
|
596
1003
|
updatedAt: now
|
|
597
|
-
}).where((0,
|
|
1004
|
+
}).where((0, import_drizzle_orm5.and)((0, import_drizzle_orm5.eq)(planVersions.planId, input.planKey), (0, import_drizzle_orm5.isNotNull)(planVersions.publishedAt), (0, import_drizzle_orm5.isNull)(planVersions.supersededAt), (0, import_drizzle_orm5.lt)(planVersions.version, input.version)));
|
|
598
1005
|
}
|
|
599
1006
|
await this.db.insert(planVersions).values({
|
|
600
|
-
id: (0,
|
|
1007
|
+
id: (0, import_node_crypto3.randomUUID)(),
|
|
601
1008
|
planId: input.planKey,
|
|
602
1009
|
version: input.version,
|
|
603
1010
|
features: input.features,
|
|
@@ -616,14 +1023,13 @@ var DrizzlePlanCatalogImportSink = class {
|
|
|
616
1023
|
async upsertFeatureCatalogEntry(input) {
|
|
617
1024
|
const existing = await this.db.select({
|
|
618
1025
|
id: featureCatalogEntries.id
|
|
619
|
-
}).from(featureCatalogEntries).where((0,
|
|
1026
|
+
}).from(featureCatalogEntries).where((0, import_drizzle_orm5.and)((0, import_drizzle_orm5.eq)(featureCatalogEntries.featureKey, input.featureKey))).limit(1);
|
|
620
1027
|
if (existing[0]) return {
|
|
621
1028
|
created: false,
|
|
622
1029
|
skipReason: "exists"
|
|
623
1030
|
};
|
|
624
1031
|
await this.db.insert(featureCatalogEntries).values({
|
|
625
|
-
id: (0,
|
|
626
|
-
projectKey: input.projectKey,
|
|
1032
|
+
id: (0, import_node_crypto3.randomUUID)(),
|
|
627
1033
|
featureKey: input.featureKey,
|
|
628
1034
|
label: input.label ?? input.featureKey,
|
|
629
1035
|
icon: input.icon ?? null,
|
|
@@ -637,35 +1043,40 @@ var DrizzlePlanCatalogImportSink = class {
|
|
|
637
1043
|
};
|
|
638
1044
|
}
|
|
639
1045
|
};
|
|
640
|
-
DrizzlePlanCatalogImportSink =
|
|
641
|
-
(0,
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
1046
|
+
DrizzlePlanCatalogImportSink = _ts_decorate7([
|
|
1047
|
+
(0, import_common7.Injectable)(),
|
|
1048
|
+
_ts_param6(0, (0, import_common7.Inject)(DRIZZLE_DB_TOKEN)),
|
|
1049
|
+
_ts_metadata6("design:type", Function),
|
|
1050
|
+
_ts_metadata6("design:paramtypes", [
|
|
645
1051
|
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
646
1052
|
])
|
|
647
1053
|
], DrizzlePlanCatalogImportSink);
|
|
648
1054
|
|
|
649
1055
|
// src/drizzle-plan-catalog-read-sink.adapter.ts
|
|
650
|
-
var
|
|
651
|
-
var
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
1056
|
+
var import_common8 = require("@nestjs/common");
|
|
1057
|
+
var import_drizzle_orm6 = require("drizzle-orm");
|
|
1058
|
+
var import_core2 = require("@saasicat/core");
|
|
1059
|
+
function _ts_decorate8(decorators, target, key, desc10) {
|
|
1060
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
1061
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
655
1062
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
656
1063
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
657
1064
|
}
|
|
658
|
-
__name(
|
|
659
|
-
function
|
|
1065
|
+
__name(_ts_decorate8, "_ts_decorate");
|
|
1066
|
+
function _ts_metadata7(k, v) {
|
|
660
1067
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
661
1068
|
}
|
|
662
|
-
__name(
|
|
663
|
-
function
|
|
1069
|
+
__name(_ts_metadata7, "_ts_metadata");
|
|
1070
|
+
function _ts_param7(paramIndex, decorator) {
|
|
664
1071
|
return function(target, key) {
|
|
665
1072
|
decorator(target, key, paramIndex);
|
|
666
1073
|
};
|
|
667
1074
|
}
|
|
668
|
-
__name(
|
|
1075
|
+
__name(_ts_param7, "_ts_param");
|
|
1076
|
+
var CATALOG_VERSION_FIELDS = {
|
|
1077
|
+
validityWindows: false,
|
|
1078
|
+
endsAt: false
|
|
1079
|
+
};
|
|
669
1080
|
var DrizzlePlanCatalogReadSink = class {
|
|
670
1081
|
static {
|
|
671
1082
|
__name(this, "DrizzlePlanCatalogReadSink");
|
|
@@ -674,70 +1085,29 @@ var DrizzlePlanCatalogReadSink = class {
|
|
|
674
1085
|
constructor(db) {
|
|
675
1086
|
this.db = db;
|
|
676
1087
|
}
|
|
677
|
-
async loadSnapshot(
|
|
678
|
-
const planRows = await this.db.select().from(plans).where((0,
|
|
1088
|
+
async loadSnapshot() {
|
|
1089
|
+
const planRows = await this.db.select().from(plans).where((0, import_drizzle_orm6.isNull)(plans.deletedAt)).orderBy((0, import_drizzle_orm6.asc)(plans.sortOrder));
|
|
679
1090
|
const planKeys = planRows.map((plan) => plan.planKey);
|
|
680
|
-
const liveVersionRows = planKeys.length === 0 ? [] : await this.db.select().from(planVersions).where((0,
|
|
681
|
-
const featureRows = await this.db.select().from(featureCatalogEntries).where((0,
|
|
1091
|
+
const liveVersionRows = planKeys.length === 0 ? [] : await this.db.select().from(planVersions).where((0, import_drizzle_orm6.and)((0, import_drizzle_orm6.inArray)(planVersions.planId, planKeys), (0, import_drizzle_orm6.isNotNull)(planVersions.publishedAt), (0, import_drizzle_orm6.isNull)(planVersions.supersededAt)));
|
|
1092
|
+
const featureRows = await this.db.select().from(featureCatalogEntries).where((0, import_drizzle_orm6.isNull)(featureCatalogEntries.deletedAt)).orderBy((0, import_drizzle_orm6.asc)(featureCatalogEntries.sortOrder));
|
|
682
1093
|
return {
|
|
683
|
-
plans: planRows.map(toPlanRow),
|
|
684
|
-
livePlanVersions: liveVersionRows.map(toPlanVersionRow),
|
|
1094
|
+
plans: planRows.map((row) => (0, import_core2.toPlanRow)(row)),
|
|
1095
|
+
livePlanVersions: liveVersionRows.map((row) => (0, import_core2.toPlanVersionRow)(row, row.planId, CATALOG_VERSION_FIELDS)),
|
|
685
1096
|
featureEntries: featureRows.map(toFeatureCatalogEntryRow)
|
|
686
1097
|
};
|
|
687
1098
|
}
|
|
688
1099
|
};
|
|
689
|
-
DrizzlePlanCatalogReadSink =
|
|
690
|
-
(0,
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
1100
|
+
DrizzlePlanCatalogReadSink = _ts_decorate8([
|
|
1101
|
+
(0, import_common8.Injectable)(),
|
|
1102
|
+
_ts_param7(0, (0, import_common8.Inject)(DRIZZLE_DB_TOKEN)),
|
|
1103
|
+
_ts_metadata7("design:type", Function),
|
|
1104
|
+
_ts_metadata7("design:paramtypes", [
|
|
694
1105
|
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
695
1106
|
])
|
|
696
1107
|
], DrizzlePlanCatalogReadSink);
|
|
697
|
-
function toPlanRow(row) {
|
|
698
|
-
return {
|
|
699
|
-
id: row.id,
|
|
700
|
-
projectKey: row.projectKey,
|
|
701
|
-
planKey: row.planKey,
|
|
702
|
-
label: row.label,
|
|
703
|
-
description: row.description,
|
|
704
|
-
icon: row.icon,
|
|
705
|
-
sortOrder: row.sortOrder,
|
|
706
|
-
createdAt: row.createdAt.toISOString(),
|
|
707
|
-
updatedAt: row.updatedAt.toISOString(),
|
|
708
|
-
deletedAt: row.deletedAt ? row.deletedAt.toISOString() : null
|
|
709
|
-
};
|
|
710
|
-
}
|
|
711
|
-
__name(toPlanRow, "toPlanRow");
|
|
712
|
-
function toPlanVersionRow(row) {
|
|
713
|
-
return {
|
|
714
|
-
id: row.id,
|
|
715
|
-
planId: row.planId,
|
|
716
|
-
version: row.version,
|
|
717
|
-
baseVersionId: row.baseVersionId,
|
|
718
|
-
publishedAt: row.publishedAt ? row.publishedAt.toISOString() : null,
|
|
719
|
-
supersededAt: row.supersededAt ? row.supersededAt.toISOString() : null,
|
|
720
|
-
publishedChanges: row.publishedChanges ?? null,
|
|
721
|
-
changeNote: row.changeNote,
|
|
722
|
-
nonRegressive: row.nonRegressive,
|
|
723
|
-
validFrom: null,
|
|
724
|
-
validUntil: null,
|
|
725
|
-
createdByUserId: row.createdByUserId,
|
|
726
|
-
publishedByUserId: row.publishedByUserId,
|
|
727
|
-
createdAt: row.createdAt.toISOString(),
|
|
728
|
-
updatedAt: row.updatedAt.toISOString(),
|
|
729
|
-
features: toStringArray(row.features),
|
|
730
|
-
quotas: toQuotaMap(row.quotas),
|
|
731
|
-
monthlyNet: String(row.monthlyNet),
|
|
732
|
-
yearlyNet: String(row.yearlyNet),
|
|
733
|
-
marketed: row.marketed
|
|
734
|
-
};
|
|
735
|
-
}
|
|
736
|
-
__name(toPlanVersionRow, "toPlanVersionRow");
|
|
737
1108
|
function toFeatureCatalogEntryRow(row) {
|
|
738
1109
|
return {
|
|
739
1110
|
id: row.id,
|
|
740
|
-
projectKey: row.projectKey,
|
|
741
1111
|
featureKey: row.featureKey,
|
|
742
1112
|
label: row.label,
|
|
743
1113
|
description: row.description,
|
|
@@ -763,26 +1133,84 @@ function toFeatureCatalogEntryRow(row) {
|
|
|
763
1133
|
}
|
|
764
1134
|
__name(toFeatureCatalogEntryRow, "toFeatureCatalogEntryRow");
|
|
765
1135
|
|
|
1136
|
+
// src/drizzle-payment-event-log.adapter.ts
|
|
1137
|
+
var import_node_crypto4 = require("node:crypto");
|
|
1138
|
+
var import_drizzle_orm7 = require("drizzle-orm");
|
|
1139
|
+
var import_common9 = require("@nestjs/common");
|
|
1140
|
+
function _ts_decorate9(decorators, target, key, desc10) {
|
|
1141
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
1142
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
1143
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1144
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1145
|
+
}
|
|
1146
|
+
__name(_ts_decorate9, "_ts_decorate");
|
|
1147
|
+
function _ts_metadata8(k, v) {
|
|
1148
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1149
|
+
}
|
|
1150
|
+
__name(_ts_metadata8, "_ts_metadata");
|
|
1151
|
+
function _ts_param8(paramIndex, decorator) {
|
|
1152
|
+
return function(target, key) {
|
|
1153
|
+
decorator(target, key, paramIndex);
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
__name(_ts_param8, "_ts_param");
|
|
1157
|
+
var DrizzlePaymentEventLog = class {
|
|
1158
|
+
static {
|
|
1159
|
+
__name(this, "DrizzlePaymentEventLog");
|
|
1160
|
+
}
|
|
1161
|
+
db;
|
|
1162
|
+
constructor(db) {
|
|
1163
|
+
this.db = db;
|
|
1164
|
+
}
|
|
1165
|
+
async claim(claim, tx) {
|
|
1166
|
+
const inserted = await resolveDb(this.db, tx).insert(paymentEventLog).values({
|
|
1167
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
1168
|
+
gatewayAccount: claim.gatewayAccount,
|
|
1169
|
+
eventId: claim.eventId,
|
|
1170
|
+
provider: claim.provider,
|
|
1171
|
+
sessionId: claim.sessionId,
|
|
1172
|
+
status: claim.kind,
|
|
1173
|
+
payload: claim.summary
|
|
1174
|
+
}).onConflictDoNothing().returning({
|
|
1175
|
+
id: paymentEventLog.id
|
|
1176
|
+
});
|
|
1177
|
+
return inserted.length === 1;
|
|
1178
|
+
}
|
|
1179
|
+
async releaseSession(gatewayAccount, eventId, tx) {
|
|
1180
|
+
await resolveDb(this.db, tx).update(paymentEventLog).set({
|
|
1181
|
+
sessionId: null
|
|
1182
|
+
}).where((0, import_drizzle_orm7.and)((0, import_drizzle_orm7.eq)(paymentEventLog.gatewayAccount, gatewayAccount), (0, import_drizzle_orm7.eq)(paymentEventLog.eventId, eventId)));
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
DrizzlePaymentEventLog = _ts_decorate9([
|
|
1186
|
+
(0, import_common9.Injectable)(),
|
|
1187
|
+
_ts_param8(0, (0, import_common9.Inject)(DRIZZLE_DB_TOKEN)),
|
|
1188
|
+
_ts_metadata8("design:type", Function),
|
|
1189
|
+
_ts_metadata8("design:paramtypes", [
|
|
1190
|
+
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
1191
|
+
])
|
|
1192
|
+
], DrizzlePaymentEventLog);
|
|
1193
|
+
|
|
766
1194
|
// src/drizzle-plan-version.repository.ts
|
|
767
|
-
var
|
|
768
|
-
var
|
|
769
|
-
function
|
|
770
|
-
var c = arguments.length, r = c < 3 ? target :
|
|
771
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key,
|
|
1195
|
+
var import_common10 = require("@nestjs/common");
|
|
1196
|
+
var import_drizzle_orm8 = require("drizzle-orm");
|
|
1197
|
+
function _ts_decorate10(decorators, target, key, desc10) {
|
|
1198
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
1199
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
772
1200
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
773
1201
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
774
1202
|
}
|
|
775
|
-
__name(
|
|
776
|
-
function
|
|
1203
|
+
__name(_ts_decorate10, "_ts_decorate");
|
|
1204
|
+
function _ts_metadata9(k, v) {
|
|
777
1205
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
778
1206
|
}
|
|
779
|
-
__name(
|
|
780
|
-
function
|
|
1207
|
+
__name(_ts_metadata9, "_ts_metadata");
|
|
1208
|
+
function _ts_param9(paramIndex, decorator) {
|
|
781
1209
|
return function(target, key) {
|
|
782
1210
|
decorator(target, key, paramIndex);
|
|
783
1211
|
};
|
|
784
1212
|
}
|
|
785
|
-
__name(
|
|
1213
|
+
__name(_ts_param9, "_ts_param");
|
|
786
1214
|
var DrizzlePlanVersionRepository = class {
|
|
787
1215
|
static {
|
|
788
1216
|
__name(this, "DrizzlePlanVersionRepository");
|
|
@@ -793,7 +1221,7 @@ var DrizzlePlanVersionRepository = class {
|
|
|
793
1221
|
}
|
|
794
1222
|
async findLatestLive(planId, tx) {
|
|
795
1223
|
const db = resolveDb(this.db, tx);
|
|
796
|
-
const rows = await db.select().from(planVersions).where((0,
|
|
1224
|
+
const rows = await db.select().from(planVersions).where((0, import_drizzle_orm8.and)((0, import_drizzle_orm8.eq)(planVersions.planId, planId), (0, import_drizzle_orm8.isNotNull)(planVersions.publishedAt), (0, import_drizzle_orm8.isNull)(planVersions.supersededAt))).orderBy((0, import_drizzle_orm8.desc)(planVersions.version)).limit(1);
|
|
797
1225
|
const row = rows[0];
|
|
798
1226
|
if (!row) return null;
|
|
799
1227
|
return {
|
|
@@ -803,319 +1231,1992 @@ var DrizzlePlanVersionRepository = class {
|
|
|
803
1231
|
};
|
|
804
1232
|
}
|
|
805
1233
|
};
|
|
806
|
-
DrizzlePlanVersionRepository =
|
|
807
|
-
(0,
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
1234
|
+
DrizzlePlanVersionRepository = _ts_decorate10([
|
|
1235
|
+
(0, import_common10.Injectable)(),
|
|
1236
|
+
_ts_param9(0, (0, import_common10.Inject)(DRIZZLE_DB_TOKEN)),
|
|
1237
|
+
_ts_metadata9("design:type", Function),
|
|
1238
|
+
_ts_metadata9("design:paramtypes", [
|
|
811
1239
|
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
812
1240
|
])
|
|
813
1241
|
], DrizzlePlanVersionRepository);
|
|
814
1242
|
|
|
815
|
-
// src/drizzle-promo-code-
|
|
816
|
-
var
|
|
817
|
-
var
|
|
818
|
-
var
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1243
|
+
// src/drizzle-promo-code-hold.repository.ts
|
|
1244
|
+
var import_node_crypto6 = require("node:crypto");
|
|
1245
|
+
var import_common12 = require("@nestjs/common");
|
|
1246
|
+
var import_drizzle_orm10 = require("drizzle-orm");
|
|
1247
|
+
|
|
1248
|
+
// src/drizzle-promo-code.repository.ts
|
|
1249
|
+
var import_node_crypto5 = require("node:crypto");
|
|
1250
|
+
var import_common11 = require("@nestjs/common");
|
|
1251
|
+
var import_drizzle_orm9 = require("drizzle-orm");
|
|
1252
|
+
function _ts_decorate11(decorators, target, key, desc10) {
|
|
1253
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
1254
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
822
1255
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
823
1256
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
824
1257
|
}
|
|
825
|
-
__name(
|
|
826
|
-
function
|
|
1258
|
+
__name(_ts_decorate11, "_ts_decorate");
|
|
1259
|
+
function _ts_metadata10(k, v) {
|
|
827
1260
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
828
1261
|
}
|
|
829
|
-
__name(
|
|
830
|
-
function
|
|
1262
|
+
__name(_ts_metadata10, "_ts_metadata");
|
|
1263
|
+
function _ts_param10(paramIndex, decorator) {
|
|
831
1264
|
return function(target, key) {
|
|
832
1265
|
decorator(target, key, paramIndex);
|
|
833
1266
|
};
|
|
834
1267
|
}
|
|
835
|
-
__name(
|
|
836
|
-
var
|
|
1268
|
+
__name(_ts_param10, "_ts_param");
|
|
1269
|
+
var DrizzlePromoCodeRepository = class {
|
|
837
1270
|
static {
|
|
838
|
-
__name(this, "
|
|
1271
|
+
__name(this, "DrizzlePromoCodeRepository");
|
|
839
1272
|
}
|
|
840
1273
|
db;
|
|
841
1274
|
constructor(db) {
|
|
842
1275
|
this.db = db;
|
|
843
1276
|
}
|
|
844
|
-
async
|
|
845
|
-
const
|
|
846
|
-
const rows = await db.select().from(promoCodeRedemptions).where((0, import_drizzle_orm7.eq)(promoCodeRedemptions.subscriptionId, subscriptionId)).limit(1);
|
|
1277
|
+
async findById(id) {
|
|
1278
|
+
const rows = await this.db.select().from(promoCodes).where((0, import_drizzle_orm9.eq)(promoCodes.id, id)).limit(1);
|
|
847
1279
|
const row = rows[0];
|
|
848
1280
|
return row ? toRecord(row) : null;
|
|
849
1281
|
}
|
|
850
|
-
async
|
|
1282
|
+
async findByCode(code, tx) {
|
|
851
1283
|
const db = resolveDb(this.db, tx);
|
|
852
|
-
const rows = await db.
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1284
|
+
const rows = await db.select().from(promoCodes).where((0, import_drizzle_orm9.eq)(promoCodes.code, normalizeCode(code))).limit(1);
|
|
1285
|
+
const row = rows[0];
|
|
1286
|
+
if (!row || row.deletedAt) return null;
|
|
1287
|
+
return toRecord(row);
|
|
1288
|
+
}
|
|
1289
|
+
async findMany(filter) {
|
|
1290
|
+
const conditions = [
|
|
1291
|
+
(0, import_drizzle_orm9.isNull)(promoCodes.deletedAt),
|
|
1292
|
+
filter.status ? (0, import_drizzle_orm9.eq)(promoCodes.status, filter.status) : void 0,
|
|
1293
|
+
filter.campaignTag ? (0, import_drizzle_orm9.eq)(promoCodes.campaignTag, filter.campaignTag) : void 0,
|
|
1294
|
+
filter.search ? (0, import_drizzle_orm9.like)(promoCodes.code, `%${escapeLikePattern(normalizeCode(filter.search))}%`) : void 0
|
|
1295
|
+
];
|
|
1296
|
+
const rows = await this.db.select().from(promoCodes).where((0, import_drizzle_orm9.and)(...conditions)).orderBy(import_drizzle_orm9.sql`${promoCodes.createdAt} DESC`);
|
|
1297
|
+
return rows.map(toRecord);
|
|
1298
|
+
}
|
|
1299
|
+
async create(data) {
|
|
1300
|
+
const rows = await this.db.insert(promoCodes).values({
|
|
1301
|
+
id: (0, import_node_crypto5.randomUUID)(),
|
|
1302
|
+
code: normalizeCode(data.code),
|
|
1303
|
+
valueType: data.valueType,
|
|
1304
|
+
value: data.value.toFixed(2),
|
|
1305
|
+
durationType: data.durationType,
|
|
1306
|
+
durationValue: data.durationValue ?? null,
|
|
1307
|
+
validFrom: data.validFrom ?? null,
|
|
1308
|
+
validUntil: data.validUntil ?? null,
|
|
1309
|
+
maxRedemptions: data.maxRedemptions ?? null,
|
|
1310
|
+
appliesToPlans: data.appliesToPlans ?? [],
|
|
1311
|
+
appliesToBilling: data.appliesToBilling ?? null,
|
|
1312
|
+
firstTimeCustomersOnly: data.firstTimeCustomersOnly ?? true,
|
|
1313
|
+
minimumPlanAmountGross: data.minimumPlanAmountGross?.toFixed(2) ?? null,
|
|
1314
|
+
allowZeroInvoice: data.allowZeroInvoice ?? false,
|
|
1315
|
+
description: data.description ?? null,
|
|
1316
|
+
campaignTag: data.campaignTag ?? null,
|
|
1317
|
+
revenueDeductionAccount: data.revenueDeductionAccount ?? null,
|
|
1318
|
+
createdById: data.createdById,
|
|
1319
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
863
1320
|
}).returning();
|
|
864
1321
|
return toRecord(rows[0]);
|
|
865
1322
|
}
|
|
866
|
-
async
|
|
867
|
-
const
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1323
|
+
async update(id, data) {
|
|
1324
|
+
const rows = await this.db.update(promoCodes).set({
|
|
1325
|
+
status: data.status,
|
|
1326
|
+
description: data.description,
|
|
1327
|
+
validUntil: data.validUntil,
|
|
1328
|
+
maxRedemptions: data.maxRedemptions,
|
|
1329
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1330
|
+
}).where((0, import_drizzle_orm9.eq)(promoCodes.id, id)).returning();
|
|
1331
|
+
const row = rows[0];
|
|
1332
|
+
if (!row) {
|
|
1333
|
+
throw new Error(`PromoCode ${id} not found.`);
|
|
1334
|
+
}
|
|
1335
|
+
return toRecord(row);
|
|
1336
|
+
}
|
|
1337
|
+
async softDelete(id) {
|
|
1338
|
+
await this.db.update(promoCodes).set({
|
|
1339
|
+
deletedAt: /* @__PURE__ */ new Date(),
|
|
1340
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1341
|
+
}).where((0, import_drizzle_orm9.eq)(promoCodes.id, id));
|
|
1342
|
+
}
|
|
1343
|
+
async claimSlot(id, tx) {
|
|
1344
|
+
const db = resolveDb(this.db, tx);
|
|
1345
|
+
const claimed = await db.update(promoCodes).set({
|
|
1346
|
+
redemptionsCount: import_drizzle_orm9.sql`${promoCodes.redemptionsCount} + 1`,
|
|
1347
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1348
|
+
}).where(hasFreeSlot(id)).returning({
|
|
1349
|
+
id: promoCodes.id
|
|
1350
|
+
});
|
|
1351
|
+
return claimed.length === 1;
|
|
1352
|
+
}
|
|
1353
|
+
async markExhaustedIfFull(id, tx) {
|
|
1354
|
+
const db = resolveDb(this.db, tx);
|
|
1355
|
+
await db.update(promoCodes).set({
|
|
1356
|
+
status: "EXHAUSTED",
|
|
1357
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1358
|
+
}).where((0, import_drizzle_orm9.and)((0, import_drizzle_orm9.eq)(promoCodes.id, id), (0, import_drizzle_orm9.eq)(promoCodes.status, "ACTIVE"), (0, import_drizzle_orm9.isNotNull)(promoCodes.maxRedemptions), (0, import_drizzle_orm9.gte)(promoCodes.redemptionsCount, promoCodes.maxRedemptions)));
|
|
1359
|
+
}
|
|
1360
|
+
async releaseSlot(id, tx) {
|
|
1361
|
+
const db = resolveDb(this.db, tx);
|
|
1362
|
+
await db.update(promoCodes).set({
|
|
1363
|
+
redemptionsCount: import_drizzle_orm9.sql`GREATEST(${promoCodes.redemptionsCount} - 1, 0)`,
|
|
1364
|
+
status: import_drizzle_orm9.sql`CASE WHEN ${promoCodes.status} = 'EXHAUSTED' THEN 'ACTIVE' ELSE ${promoCodes.status} END`,
|
|
1365
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1366
|
+
}).where((0, import_drizzle_orm9.eq)(promoCodes.id, id));
|
|
1367
|
+
}
|
|
1368
|
+
async expireDueCodes(now) {
|
|
1369
|
+
const expired = await this.db.update(promoCodes).set({
|
|
1370
|
+
status: "EXPIRED",
|
|
1371
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1372
|
+
}).where((0, import_drizzle_orm9.and)((0, import_drizzle_orm9.inArray)(promoCodes.status, [
|
|
1373
|
+
"ACTIVE",
|
|
1374
|
+
"PAUSED"
|
|
1375
|
+
]), (0, import_drizzle_orm9.lt)(promoCodes.validUntil, now))).returning({
|
|
1376
|
+
id: promoCodes.id
|
|
1377
|
+
});
|
|
1378
|
+
return expired.length;
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
DrizzlePromoCodeRepository = _ts_decorate11([
|
|
1382
|
+
(0, import_common11.Injectable)(),
|
|
1383
|
+
_ts_param10(0, (0, import_common11.Inject)(DRIZZLE_DB_TOKEN)),
|
|
1384
|
+
_ts_metadata10("design:type", Function),
|
|
1385
|
+
_ts_metadata10("design:paramtypes", [
|
|
1386
|
+
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
1387
|
+
])
|
|
1388
|
+
], DrizzlePromoCodeRepository);
|
|
1389
|
+
function hasFreeSlot(id) {
|
|
1390
|
+
return (0, import_drizzle_orm9.and)((0, import_drizzle_orm9.eq)(promoCodes.id, id), (0, import_drizzle_orm9.eq)(promoCodes.status, "ACTIVE"), (0, import_drizzle_orm9.isNull)(promoCodes.deletedAt), (0, import_drizzle_orm9.or)((0, import_drizzle_orm9.isNull)(promoCodes.maxRedemptions), import_drizzle_orm9.sql`${promoCodes.redemptionsCount} + ${promoCodes.heldCount} < ${promoCodes.maxRedemptions}`));
|
|
1391
|
+
}
|
|
1392
|
+
__name(hasFreeSlot, "hasFreeSlot");
|
|
1393
|
+
function normalizeCode(code) {
|
|
1394
|
+
return code.trim().toUpperCase();
|
|
1395
|
+
}
|
|
1396
|
+
__name(normalizeCode, "normalizeCode");
|
|
1397
|
+
function toRecord(row) {
|
|
1398
|
+
return {
|
|
1399
|
+
id: row.id,
|
|
1400
|
+
code: row.code,
|
|
1401
|
+
valueType: row.valueType,
|
|
1402
|
+
value: String(row.value),
|
|
1403
|
+
durationType: row.durationType,
|
|
1404
|
+
durationValue: row.durationValue,
|
|
1405
|
+
validFrom: row.validFrom,
|
|
1406
|
+
validUntil: row.validUntil,
|
|
1407
|
+
maxRedemptions: row.maxRedemptions,
|
|
1408
|
+
redemptionsCount: row.redemptionsCount,
|
|
1409
|
+
heldCount: row.heldCount,
|
|
1410
|
+
appliesToPlans: row.appliesToPlans ?? [],
|
|
1411
|
+
appliesToBilling: row.appliesToBilling ?? null,
|
|
1412
|
+
firstTimeCustomersOnly: row.firstTimeCustomersOnly,
|
|
1413
|
+
minimumPlanAmountGross: row.minimumPlanAmountGross === null ? null : String(row.minimumPlanAmountGross),
|
|
1414
|
+
allowZeroInvoice: row.allowZeroInvoice,
|
|
1415
|
+
status: row.status,
|
|
1416
|
+
description: row.description,
|
|
1417
|
+
campaignTag: row.campaignTag,
|
|
1418
|
+
revenueDeductionAccount: row.revenueDeductionAccount,
|
|
1419
|
+
createdById: row.createdById,
|
|
1420
|
+
createdAt: row.createdAt,
|
|
1421
|
+
updatedAt: row.updatedAt,
|
|
1422
|
+
deletedAt: row.deletedAt
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
__name(toRecord, "toRecord");
|
|
1426
|
+
|
|
1427
|
+
// src/drizzle-promo-code-hold.repository.ts
|
|
1428
|
+
function _ts_decorate12(decorators, target, key, desc10) {
|
|
1429
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
1430
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
1431
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1432
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1433
|
+
}
|
|
1434
|
+
__name(_ts_decorate12, "_ts_decorate");
|
|
1435
|
+
function _ts_metadata11(k, v) {
|
|
1436
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1437
|
+
}
|
|
1438
|
+
__name(_ts_metadata11, "_ts_metadata");
|
|
1439
|
+
function _ts_param11(paramIndex, decorator) {
|
|
1440
|
+
return function(target, key) {
|
|
1441
|
+
decorator(target, key, paramIndex);
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
__name(_ts_param11, "_ts_param");
|
|
1445
|
+
var HOLD_FIELDS = {
|
|
1446
|
+
id: promoCodeHolds.id,
|
|
1447
|
+
promoCodeId: promoCodeHolds.promoCodeId,
|
|
1448
|
+
checkoutOfferId: promoCodeHolds.checkoutOfferId,
|
|
1449
|
+
expiresAt: promoCodeHolds.expiresAt,
|
|
1450
|
+
createdAt: promoCodeHolds.createdAt
|
|
1451
|
+
};
|
|
1452
|
+
var CURRENT_TRANSACTION = import_drizzle_orm10.sql`txid_current()`;
|
|
1453
|
+
var atExpiry = /* @__PURE__ */ __name((expiresAt) => import_drizzle_orm10.sql.param(expiresAt, promoCodeHolds.expiresAt), "atExpiry");
|
|
1454
|
+
var DrizzlePromoCodeHoldRepository = class {
|
|
1455
|
+
static {
|
|
1456
|
+
__name(this, "DrizzlePromoCodeHoldRepository");
|
|
1457
|
+
}
|
|
1458
|
+
db;
|
|
1459
|
+
constructor(db) {
|
|
1460
|
+
this.db = db;
|
|
1461
|
+
}
|
|
1462
|
+
async findByCheckoutOffer(checkoutOfferId, tx) {
|
|
1463
|
+
const rows = await resolveDb(this.db, tx).select(HOLD_FIELDS).from(promoCodeHolds).where((0, import_drizzle_orm10.eq)(promoCodeHolds.checkoutOfferId, checkoutOfferId)).limit(1);
|
|
1464
|
+
return rows[0] ?? null;
|
|
1465
|
+
}
|
|
1466
|
+
async take(hold) {
|
|
1467
|
+
return this.onTransaction(void 0, async (db) => {
|
|
1468
|
+
const code = await db.select({
|
|
1469
|
+
id: promoCodes.id
|
|
1470
|
+
}).from(promoCodes).where((0, import_drizzle_orm10.eq)(promoCodes.id, hold.promoCodeId)).for("update");
|
|
1471
|
+
if (code.length === 0) return {
|
|
1472
|
+
outcome: "no-slot"
|
|
1473
|
+
};
|
|
1474
|
+
const held = await db.select({
|
|
1475
|
+
id: promoCodeHolds.id
|
|
1476
|
+
}).from(promoCodeHolds).where((0, import_drizzle_orm10.eq)(promoCodeHolds.checkoutOfferId, hold.checkoutOfferId)).limit(1);
|
|
1477
|
+
if (held.length > 0) return {
|
|
1478
|
+
outcome: "offer-holds-one"
|
|
1479
|
+
};
|
|
1480
|
+
const counted = await db.update(promoCodes).set({
|
|
1481
|
+
heldCount: import_drizzle_orm10.sql`${promoCodes.heldCount} + 1`,
|
|
1482
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1483
|
+
}).where(hasFreeSlot(hold.promoCodeId)).returning({
|
|
1484
|
+
id: promoCodes.id
|
|
1485
|
+
});
|
|
1486
|
+
if (counted.length === 0) return {
|
|
1487
|
+
outcome: "no-slot"
|
|
1488
|
+
};
|
|
1489
|
+
const [created] = await db.insert(promoCodeHolds).values({
|
|
1490
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
1491
|
+
...hold
|
|
1492
|
+
}).returning(HOLD_FIELDS);
|
|
1493
|
+
return {
|
|
1494
|
+
outcome: "taken",
|
|
1495
|
+
hold: created
|
|
1496
|
+
};
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
async extend(checkoutOfferId, promoCodeId, expiresAt) {
|
|
1500
|
+
const extended = await this.db.update(promoCodeHolds).set({
|
|
1501
|
+
expiresAt: import_drizzle_orm10.sql`GREATEST(${promoCodeHolds.expiresAt}, ${atExpiry(expiresAt)})`
|
|
1502
|
+
}).where((0, import_drizzle_orm10.and)((0, import_drizzle_orm10.eq)(promoCodeHolds.checkoutOfferId, checkoutOfferId), (0, import_drizzle_orm10.eq)(promoCodeHolds.promoCodeId, promoCodeId))).returning({
|
|
1503
|
+
id: promoCodeHolds.id
|
|
1504
|
+
});
|
|
1505
|
+
return extended.length === 1;
|
|
1506
|
+
}
|
|
1507
|
+
async release(checkoutOfferId, tx) {
|
|
1508
|
+
return this.releaseWhere((0, import_drizzle_orm10.eq)(promoCodeHolds.checkoutOfferId, checkoutOfferId), tx);
|
|
1509
|
+
}
|
|
1510
|
+
async releaseIfUnmoved(checkoutOfferId, expiresAt) {
|
|
1511
|
+
return this.releaseWhere((0, import_drizzle_orm10.and)((0, import_drizzle_orm10.eq)(promoCodeHolds.checkoutOfferId, checkoutOfferId), (0, import_drizzle_orm10.eq)(promoCodeHolds.expiresAt, expiresAt)));
|
|
1512
|
+
}
|
|
1513
|
+
async handOver(checkoutOfferId, now, tx) {
|
|
1514
|
+
const marked = await resolveDb(this.db, tx).update(promoCodeHolds).set({
|
|
1515
|
+
handedOverTx: CURRENT_TRANSACTION
|
|
1516
|
+
}).where((0, import_drizzle_orm10.and)((0, import_drizzle_orm10.eq)(promoCodeHolds.checkoutOfferId, checkoutOfferId), (0, import_drizzle_orm10.gt)(promoCodeHolds.expiresAt, now))).returning({
|
|
1517
|
+
id: promoCodeHolds.id
|
|
1518
|
+
});
|
|
1519
|
+
return marked.length === 1;
|
|
1520
|
+
}
|
|
1521
|
+
async convertHandedOver(promoCodeId, tx) {
|
|
1522
|
+
const db = resolveDb(this.db, tx);
|
|
1523
|
+
const handedOver = await db.select({
|
|
1524
|
+
id: promoCodeHolds.id
|
|
1525
|
+
}).from(promoCodeHolds).where((0, import_drizzle_orm10.and)((0, import_drizzle_orm10.eq)(promoCodeHolds.promoCodeId, promoCodeId), (0, import_drizzle_orm10.eq)(promoCodeHolds.handedOverTx, CURRENT_TRANSACTION))).limit(1);
|
|
1526
|
+
if (handedOver.length === 0) return false;
|
|
1527
|
+
const gone = await db.delete(promoCodeHolds).where((0, import_drizzle_orm10.eq)(promoCodeHolds.id, handedOver[0].id)).returning({
|
|
1528
|
+
promoCodeId: promoCodeHolds.promoCodeId
|
|
1529
|
+
});
|
|
1530
|
+
await giveBack(db, gone, {
|
|
1531
|
+
redeemed: true
|
|
1532
|
+
});
|
|
1533
|
+
return gone.length === 1;
|
|
1534
|
+
}
|
|
1535
|
+
async expireDue(now, promoCodeId, tx) {
|
|
1536
|
+
return this.onTransaction(tx, async (db) => {
|
|
1537
|
+
const gone = await db.delete(promoCodeHolds).where((0, import_drizzle_orm10.and)((0, import_drizzle_orm10.lte)(promoCodeHolds.expiresAt, now), import_drizzle_orm10.sql`${promoCodeHolds.handedOverTx} IS DISTINCT FROM ${CURRENT_TRANSACTION}`, promoCodeId ? (0, import_drizzle_orm10.eq)(promoCodeHolds.promoCodeId, promoCodeId) : void 0)).returning({
|
|
1538
|
+
promoCodeId: promoCodeHolds.promoCodeId
|
|
1539
|
+
});
|
|
1540
|
+
await giveBack(db, gone, {
|
|
1541
|
+
redeemed: false
|
|
1542
|
+
});
|
|
1543
|
+
return gone.length;
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
/** Deletes the holds `where` matches and gives their slots back. */
|
|
1547
|
+
releaseWhere(where, tx) {
|
|
1548
|
+
return this.onTransaction(tx, async (db) => {
|
|
1549
|
+
const gone = await db.delete(promoCodeHolds).where(where).returning({
|
|
1550
|
+
promoCodeId: promoCodeHolds.promoCodeId
|
|
1551
|
+
});
|
|
1552
|
+
await giveBack(db, gone, {
|
|
1553
|
+
redeemed: false
|
|
1554
|
+
});
|
|
1555
|
+
return gone.length === 1;
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
/** Runs `work` on a transaction of its own, a savepoint where the caller has one. */
|
|
1559
|
+
onTransaction(tx, work) {
|
|
1560
|
+
return resolveDb(this.db, tx).transaction((transaction) => work(transaction));
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
DrizzlePromoCodeHoldRepository = _ts_decorate12([
|
|
1564
|
+
(0, import_common12.Injectable)(),
|
|
1565
|
+
_ts_param11(0, (0, import_common12.Inject)(DRIZZLE_DB_TOKEN)),
|
|
1566
|
+
_ts_metadata11("design:type", Function),
|
|
1567
|
+
_ts_metadata11("design:paramtypes", [
|
|
1568
|
+
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
1569
|
+
])
|
|
1570
|
+
], DrizzlePromoCodeHoldRepository);
|
|
1571
|
+
async function giveBack(db, ended, { redeemed }) {
|
|
1572
|
+
const perCode = /* @__PURE__ */ new Map();
|
|
1573
|
+
for (const { promoCodeId } of ended) {
|
|
1574
|
+
perCode.set(promoCodeId, (perCode.get(promoCodeId) ?? 0) + 1);
|
|
1575
|
+
}
|
|
1576
|
+
for (const promoCodeId of [
|
|
1577
|
+
...perCode.keys()
|
|
1578
|
+
].sort()) {
|
|
1579
|
+
const count4 = perCode.get(promoCodeId) ?? 0;
|
|
1580
|
+
await db.update(promoCodes).set({
|
|
1581
|
+
heldCount: import_drizzle_orm10.sql`GREATEST(${promoCodes.heldCount} - ${count4}, 0)`,
|
|
1582
|
+
...redeemed ? {
|
|
1583
|
+
redemptionsCount: import_drizzle_orm10.sql`${promoCodes.redemptionsCount} + ${count4}`
|
|
1584
|
+
} : {},
|
|
1585
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1586
|
+
}).where((0, import_drizzle_orm10.eq)(promoCodes.id, promoCodeId));
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
__name(giveBack, "giveBack");
|
|
1590
|
+
|
|
1591
|
+
// src/drizzle-promo-code-redemption.repository.ts
|
|
1592
|
+
var import_node_crypto7 = require("node:crypto");
|
|
1593
|
+
var import_common13 = require("@nestjs/common");
|
|
1594
|
+
var import_drizzle_orm11 = require("drizzle-orm");
|
|
1595
|
+
function _ts_decorate13(decorators, target, key, desc10) {
|
|
1596
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
1597
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
1598
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1599
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1600
|
+
}
|
|
1601
|
+
__name(_ts_decorate13, "_ts_decorate");
|
|
1602
|
+
function _ts_metadata12(k, v) {
|
|
1603
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1604
|
+
}
|
|
1605
|
+
__name(_ts_metadata12, "_ts_metadata");
|
|
1606
|
+
function _ts_param12(paramIndex, decorator) {
|
|
1607
|
+
return function(target, key) {
|
|
1608
|
+
decorator(target, key, paramIndex);
|
|
1609
|
+
};
|
|
1610
|
+
}
|
|
1611
|
+
__name(_ts_param12, "_ts_param");
|
|
1612
|
+
var DrizzlePromoCodeRedemptionRepository = class {
|
|
1613
|
+
static {
|
|
1614
|
+
__name(this, "DrizzlePromoCodeRedemptionRepository");
|
|
1615
|
+
}
|
|
1616
|
+
db;
|
|
1617
|
+
constructor(db) {
|
|
1618
|
+
this.db = db;
|
|
1619
|
+
}
|
|
1620
|
+
async findBySubscription(subscriptionId, tx) {
|
|
1621
|
+
const db = resolveDb(this.db, tx);
|
|
1622
|
+
const rows = await db.select().from(promoCodeRedemptions).where((0, import_drizzle_orm11.eq)(promoCodeRedemptions.subscriptionId, subscriptionId)).limit(1);
|
|
1623
|
+
const row = rows[0];
|
|
1624
|
+
return row ? toRecord2(row) : null;
|
|
1625
|
+
}
|
|
1626
|
+
async create(data, tx) {
|
|
1627
|
+
const db = resolveDb(this.db, tx);
|
|
1628
|
+
const rows = await db.insert(promoCodeRedemptions).values({
|
|
1629
|
+
id: (0, import_node_crypto7.randomUUID)(),
|
|
1630
|
+
promoCodeId: data.promoCodeId,
|
|
1631
|
+
subscriptionId: data.subscriptionId,
|
|
1632
|
+
tenantId: data.tenantId,
|
|
1633
|
+
appliedValueType: data.appliedValueType,
|
|
1634
|
+
appliedValue: data.appliedValue,
|
|
1635
|
+
appliedDurationType: data.appliedDurationType,
|
|
1636
|
+
appliedDurationValue: data.appliedDurationValue,
|
|
1637
|
+
startsAt: data.startsAt,
|
|
1638
|
+
endsAt: data.endsAt
|
|
1639
|
+
}).returning();
|
|
1640
|
+
return toRecord2(rows[0]);
|
|
1641
|
+
}
|
|
1642
|
+
async setReversed(id, tx) {
|
|
1643
|
+
const db = resolveDb(this.db, tx);
|
|
1644
|
+
const rows = await db.update(promoCodeRedemptions).set({
|
|
1645
|
+
status: "REVERSED",
|
|
1646
|
+
reversedAt: /* @__PURE__ */ new Date()
|
|
1647
|
+
}).where((0, import_drizzle_orm11.eq)(promoCodeRedemptions.id, id)).returning();
|
|
1648
|
+
const row = rows[0];
|
|
1649
|
+
if (!row) {
|
|
1650
|
+
throw new Error(`PromoCodeRedemption ${id} not found.`);
|
|
1651
|
+
}
|
|
1652
|
+
return toRecord2(row);
|
|
1653
|
+
}
|
|
1654
|
+
async countByPromoCode(promoCodeId, status) {
|
|
1655
|
+
const rows = await this.db.select({
|
|
1656
|
+
id: promoCodeRedemptions.id
|
|
1657
|
+
}).from(promoCodeRedemptions).where((0, import_drizzle_orm11.and)((0, import_drizzle_orm11.eq)(promoCodeRedemptions.promoCodeId, promoCodeId), status ? (0, import_drizzle_orm11.eq)(promoCodeRedemptions.status, status) : void 0));
|
|
1658
|
+
return rows.length;
|
|
1659
|
+
}
|
|
1660
|
+
async listByPromoCode(promoCodeId) {
|
|
1661
|
+
const rows = await this.db.select().from(promoCodeRedemptions).where((0, import_drizzle_orm11.eq)(promoCodeRedemptions.promoCodeId, promoCodeId)).orderBy((0, import_drizzle_orm11.desc)(promoCodeRedemptions.redeemedAt));
|
|
1662
|
+
return rows.map(toRecord2);
|
|
1663
|
+
}
|
|
1664
|
+
async expireDueRedemptions(now) {
|
|
1665
|
+
const expired = await this.db.update(promoCodeRedemptions).set({
|
|
1666
|
+
status: "EXPIRED"
|
|
1667
|
+
}).where((0, import_drizzle_orm11.and)((0, import_drizzle_orm11.eq)(promoCodeRedemptions.status, "ACTIVE"), (0, import_drizzle_orm11.lt)(promoCodeRedemptions.endsAt, now))).returning({
|
|
1668
|
+
id: promoCodeRedemptions.id
|
|
1669
|
+
});
|
|
1670
|
+
return expired.length;
|
|
1671
|
+
}
|
|
1672
|
+
};
|
|
1673
|
+
DrizzlePromoCodeRedemptionRepository = _ts_decorate13([
|
|
1674
|
+
(0, import_common13.Injectable)(),
|
|
1675
|
+
_ts_param12(0, (0, import_common13.Inject)(DRIZZLE_DB_TOKEN)),
|
|
1676
|
+
_ts_metadata12("design:type", Function),
|
|
1677
|
+
_ts_metadata12("design:paramtypes", [
|
|
1678
|
+
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
1679
|
+
])
|
|
1680
|
+
], DrizzlePromoCodeRedemptionRepository);
|
|
1681
|
+
function toRecord2(row) {
|
|
1682
|
+
return {
|
|
1683
|
+
id: row.id,
|
|
1684
|
+
promoCodeId: row.promoCodeId,
|
|
1685
|
+
subscriptionId: row.subscriptionId,
|
|
1686
|
+
tenantId: row.tenantId,
|
|
1687
|
+
appliedValueType: row.appliedValueType,
|
|
1688
|
+
appliedValue: String(row.appliedValue),
|
|
1689
|
+
appliedDurationType: row.appliedDurationType,
|
|
1690
|
+
appliedDurationValue: row.appliedDurationValue,
|
|
1691
|
+
startsAt: row.startsAt,
|
|
1692
|
+
endsAt: row.endsAt,
|
|
1693
|
+
status: row.status,
|
|
1694
|
+
redeemedAt: row.redeemedAt,
|
|
1695
|
+
reversedAt: row.reversedAt
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
__name(toRecord2, "toRecord");
|
|
1699
|
+
|
|
1700
|
+
// src/drizzle-bundle.repository.ts
|
|
1701
|
+
var import_node_crypto8 = require("node:crypto");
|
|
1702
|
+
var import_common14 = require("@nestjs/common");
|
|
1703
|
+
var import_drizzle_orm12 = require("drizzle-orm");
|
|
1704
|
+
var import_core3 = require("@saasicat/core");
|
|
1705
|
+
function _ts_decorate14(decorators, target, key, desc10) {
|
|
1706
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
1707
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
1708
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1709
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1710
|
+
}
|
|
1711
|
+
__name(_ts_decorate14, "_ts_decorate");
|
|
1712
|
+
function _ts_metadata13(k, v) {
|
|
1713
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1714
|
+
}
|
|
1715
|
+
__name(_ts_metadata13, "_ts_metadata");
|
|
1716
|
+
function _ts_param13(paramIndex, decorator) {
|
|
1717
|
+
return function(target, key) {
|
|
1718
|
+
decorator(target, key, paramIndex);
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
__name(_ts_param13, "_ts_param");
|
|
1722
|
+
var DrizzleBundleRepository = class {
|
|
1723
|
+
static {
|
|
1724
|
+
__name(this, "DrizzleBundleRepository");
|
|
1725
|
+
}
|
|
1726
|
+
db;
|
|
1727
|
+
validityWindows;
|
|
1728
|
+
constructor(db, options = {}) {
|
|
1729
|
+
this.db = db;
|
|
1730
|
+
this.validityWindows = options.validityWindows ?? false;
|
|
1731
|
+
if (this.validityWindows) {
|
|
1732
|
+
this.findActiveBundleVersion = (bundleId, asOf, tx) => this.activeVersionAt(bundleId, asOf ?? /* @__PURE__ */ new Date(), tx);
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
findActiveBundleVersion;
|
|
1736
|
+
// ─── Stems ───
|
|
1737
|
+
async list(filter) {
|
|
1738
|
+
const excludeDeleted = filter.excludeDeleted ?? true;
|
|
1739
|
+
const rows = await this.db.select().from(bundles).where(excludeDeleted ? (0, import_drizzle_orm12.isNull)(bundles.deletedAt) : void 0).orderBy((0, import_drizzle_orm12.asc)(bundles.sortOrder), (0, import_drizzle_orm12.asc)(bundles.bundleKey));
|
|
1740
|
+
return rows.map(import_core3.toBundleStemRow);
|
|
1741
|
+
}
|
|
1742
|
+
async findById(bundleId) {
|
|
1743
|
+
const rows = await this.db.select().from(bundles).where((0, import_drizzle_orm12.eq)(bundles.id, bundleId)).limit(1);
|
|
1744
|
+
return rows[0] ? (0, import_core3.toBundleStemRow)(rows[0]) : null;
|
|
1745
|
+
}
|
|
1746
|
+
/**
|
|
1747
|
+
* Whether this project already uses this bundle key — **including retired
|
|
1748
|
+
* bundles**.
|
|
1749
|
+
*
|
|
1750
|
+
* Its one caller is the duplicate check in `createBundle`, and the question
|
|
1751
|
+
* it asks is the database's: `bundles_bundleKey_key` is an
|
|
1752
|
+
* unconditional unique index, so a soft-deleted bundle still occupies its
|
|
1753
|
+
* key. Excluding retired rows here makes the check pass and the insert then
|
|
1754
|
+
* fail on the constraint — a 500 where the service had
|
|
1755
|
+
* `BUNDLE_ALREADY_EXISTS` ready.
|
|
1756
|
+
*
|
|
1757
|
+
* Two review rounds landed on this line from opposite sides, which is what
|
|
1758
|
+
* finally settled it: there is no caller that wants the active-catalogue
|
|
1759
|
+
* reading. `list` is that lookup, and it excludes retired rows by default.
|
|
1760
|
+
*/
|
|
1761
|
+
async findByKey(bundleKey) {
|
|
1762
|
+
const rows = await this.db.select().from(bundles).where((0, import_drizzle_orm12.eq)(bundles.bundleKey, bundleKey)).limit(1);
|
|
1763
|
+
return rows[0] ? (0, import_core3.toBundleStemRow)(rows[0]) : null;
|
|
1764
|
+
}
|
|
1765
|
+
async create(data) {
|
|
1766
|
+
const now = /* @__PURE__ */ new Date();
|
|
1767
|
+
const rows = await this.db.insert(bundles).values({
|
|
1768
|
+
id: (0, import_node_crypto8.randomUUID)(),
|
|
1769
|
+
...(0, import_core3.bundleStemDefaults)(data),
|
|
1770
|
+
updatedAt: now
|
|
1771
|
+
}).returning();
|
|
1772
|
+
return (0, import_core3.toBundleStemRow)(rows[0]);
|
|
1773
|
+
}
|
|
1774
|
+
async update(bundleId, data) {
|
|
1775
|
+
const patch = {
|
|
1776
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1777
|
+
};
|
|
1778
|
+
if (data.label !== void 0) patch.label = data.label;
|
|
1779
|
+
if (data.description !== void 0) patch.description = data.description;
|
|
1780
|
+
if (data.icon !== void 0) patch.icon = data.icon;
|
|
1781
|
+
if (data.sortOrder !== void 0) patch.sortOrder = data.sortOrder;
|
|
1782
|
+
if (data.i18n !== void 0) patch.i18n = data.i18n;
|
|
1783
|
+
const rows = await this.db.update(bundles).set(patch).where((0, import_drizzle_orm12.eq)(bundles.id, bundleId)).returning();
|
|
1784
|
+
if (!rows[0]) throw new Error(`Bundle '${bundleId}' not found.`);
|
|
1785
|
+
return (0, import_core3.toBundleStemRow)(rows[0]);
|
|
1786
|
+
}
|
|
1787
|
+
async softDelete(bundleId) {
|
|
1788
|
+
await this.db.update(bundles).set({
|
|
1789
|
+
deletedAt: /* @__PURE__ */ new Date(),
|
|
1790
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1791
|
+
}).where((0, import_drizzle_orm12.eq)(bundles.id, bundleId));
|
|
1792
|
+
}
|
|
1793
|
+
// ─── Versions ───
|
|
1794
|
+
async listVersions(bundleId) {
|
|
1795
|
+
const rows = await this.db.select().from(bundleVersions).where((0, import_drizzle_orm12.eq)(bundleVersions.bundleId, bundleId)).orderBy((0, import_drizzle_orm12.asc)(bundleVersions.version));
|
|
1796
|
+
const stem = await this.findById(bundleId);
|
|
1797
|
+
return rows.map((row) => this.toVersionRow(row, stem));
|
|
1798
|
+
}
|
|
1799
|
+
async findVersionById(versionId, tx) {
|
|
1800
|
+
const rows = await resolveDb(this.db, tx).select().from(bundleVersions).where((0, import_drizzle_orm12.eq)(bundleVersions.id, versionId)).limit(1);
|
|
1801
|
+
if (!rows[0]) return null;
|
|
1802
|
+
return this.toVersionRow(rows[0], await this.stemOn(resolveDb(this.db, tx), rows[0].bundleId));
|
|
1803
|
+
}
|
|
1804
|
+
async findCurrentDraft(bundleId) {
|
|
1805
|
+
const rows = await this.db.select().from(bundleVersions).where((0, import_drizzle_orm12.and)((0, import_drizzle_orm12.eq)(bundleVersions.bundleId, bundleId), (0, import_drizzle_orm12.isNull)(bundleVersions.publishedAt))).limit(1);
|
|
1806
|
+
if (!rows[0]) return null;
|
|
1807
|
+
return this.toVersionRow(rows[0], await this.findById(bundleId));
|
|
1808
|
+
}
|
|
1809
|
+
async findLatestLive(bundleId, tx) {
|
|
1810
|
+
const rows = await resolveDb(this.db, tx).select().from(bundleVersions).where((0, import_drizzle_orm12.and)((0, import_drizzle_orm12.eq)(bundleVersions.bundleId, bundleId), import_drizzle_orm12.sql`${bundleVersions.publishedAt} IS NOT NULL`, (0, import_drizzle_orm12.isNull)(bundleVersions.supersededAt))).orderBy((0, import_drizzle_orm12.desc)(bundleVersions.version)).limit(1);
|
|
1811
|
+
return this.withStem(rows[0], bundleId, tx);
|
|
1812
|
+
}
|
|
1813
|
+
/**
|
|
1814
|
+
* The version bookable at `asOf`: published, inside its validity window.
|
|
1815
|
+
*
|
|
1816
|
+
* Deliberately not filtered by `supersededAt` — a superseded predecessor is
|
|
1817
|
+
* still the bookable one until its window closes, which is the whole point
|
|
1818
|
+
* of auto-succession. `validUntil` is day-inclusive, so a version is active
|
|
1819
|
+
* throughout its last day.
|
|
1820
|
+
*/
|
|
1821
|
+
async activeVersionAt(bundleId, asOf, tx) {
|
|
1822
|
+
const dayStart = new Date(asOf);
|
|
1823
|
+
dayStart.setUTCHours(0, 0, 0, 0);
|
|
1824
|
+
const rows = await resolveDb(this.db, tx).select().from(bundleVersions).where((0, import_drizzle_orm12.and)((0, import_drizzle_orm12.eq)(bundleVersions.bundleId, bundleId), import_drizzle_orm12.sql`${bundleVersions.publishedAt} IS NOT NULL`, (0, import_drizzle_orm12.or)((0, import_drizzle_orm12.isNull)(bundleVersions.validFrom), (0, import_drizzle_orm12.lte)(bundleVersions.validFrom, asOf)), (0, import_drizzle_orm12.or)((0, import_drizzle_orm12.isNull)(bundleVersions.validUntil), (0, import_drizzle_orm12.gte)(bundleVersions.validUntil, dayStart)))).orderBy(import_drizzle_orm12.sql`"validFrom" DESC NULLS LAST`, (0, import_drizzle_orm12.desc)(bundleVersions.version)).limit(1);
|
|
1825
|
+
return this.withStem(rows[0], bundleId, tx);
|
|
1826
|
+
}
|
|
1827
|
+
async createDraft(data) {
|
|
1828
|
+
const existingDraft = await this.findCurrentDraft(data.bundleId);
|
|
1829
|
+
if (existingDraft) {
|
|
1830
|
+
throw new Error(`Bundle '${data.bundleId}' already has a draft version (v${existingDraft.version}); only one draft per bundle is allowed.`);
|
|
1831
|
+
}
|
|
1832
|
+
const latest = await this.db.select({
|
|
1833
|
+
version: bundleVersions.version
|
|
1834
|
+
}).from(bundleVersions).where((0, import_drizzle_orm12.eq)(bundleVersions.bundleId, data.bundleId)).orderBy((0, import_drizzle_orm12.desc)(bundleVersions.version)).limit(1);
|
|
1835
|
+
const now = /* @__PURE__ */ new Date();
|
|
1836
|
+
const rows = await this.db.insert(bundleVersions).values({
|
|
1837
|
+
id: (0, import_node_crypto8.randomUUID)(),
|
|
1838
|
+
bundleId: data.bundleId,
|
|
1839
|
+
version: latest[0] ? latest[0].version + 1 : 1,
|
|
1840
|
+
...(0, import_core3.bundleDraftDefaults)(data),
|
|
1841
|
+
validFrom: this.validityWindows ? toNullableDate(data.validFrom) : null,
|
|
1842
|
+
validUntil: this.validityWindows ? toNullableDate(data.validUntil) : null,
|
|
1843
|
+
updatedAt: now
|
|
1844
|
+
}).returning();
|
|
1845
|
+
return this.toVersionRow(rows[0], await this.findById(data.bundleId));
|
|
1846
|
+
}
|
|
1847
|
+
async updateDraft(versionId, data) {
|
|
1848
|
+
const patch = {
|
|
1849
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1850
|
+
};
|
|
1851
|
+
if (data.features !== void 0) patch.features = data.features;
|
|
1852
|
+
if (data.quotas !== void 0) patch.quotas = data.quotas;
|
|
1853
|
+
if (data.compatibility !== void 0) patch.compatibility = data.compatibility;
|
|
1854
|
+
if (data.pricingOverrides !== void 0) patch.pricingOverrides = data.pricingOverrides;
|
|
1855
|
+
if (data.monthlyNet !== void 0) patch.monthlyNet = data.monthlyNet;
|
|
1856
|
+
if (data.yearlyNet !== void 0) patch.yearlyNet = data.yearlyNet;
|
|
1857
|
+
if (data.marketed !== void 0) patch.marketed = data.marketed;
|
|
1858
|
+
if (data.changeNote !== void 0) patch.changeNote = data.changeNote;
|
|
1859
|
+
if (this.validityWindows) {
|
|
1860
|
+
if (data.validFrom !== void 0) patch.validFrom = toNullableDate(data.validFrom);
|
|
1861
|
+
if (data.validUntil !== void 0) patch.validUntil = toNullableDate(data.validUntil);
|
|
1862
|
+
}
|
|
1863
|
+
const rows = await this.db.update(bundleVersions).set(patch).where((0, import_drizzle_orm12.eq)(bundleVersions.id, versionId)).returning();
|
|
1864
|
+
if (!rows[0]) throw new Error(`BundleVersion '${versionId}' not found.`);
|
|
1865
|
+
return this.toVersionRow(rows[0], await this.findById(rows[0].bundleId));
|
|
1866
|
+
}
|
|
1867
|
+
async publishDraft(versionId, publishMeta, tx) {
|
|
1868
|
+
if (tx !== void 0) return this.publishWithin(resolveDb(this.db, tx), versionId, publishMeta);
|
|
1869
|
+
return this.db.transaction((transaction) => this.publishWithin(transaction, versionId, publishMeta));
|
|
1870
|
+
}
|
|
1871
|
+
async publishWithin(db, versionId, publishMeta) {
|
|
1872
|
+
const now = /* @__PURE__ */ new Date();
|
|
1873
|
+
const publishedRows = await db.update(bundleVersions).set({
|
|
1874
|
+
publishedAt: now,
|
|
1875
|
+
publishedByUserId: publishMeta.publishedByUserId,
|
|
1876
|
+
publishedChanges: publishMeta.publishedChanges,
|
|
1877
|
+
nonRegressive: publishMeta.nonRegressive,
|
|
1878
|
+
updatedAt: now,
|
|
1879
|
+
...this.validityWindows ? {
|
|
1880
|
+
validFrom: publishMeta.validFrom,
|
|
1881
|
+
validUntil: publishMeta.validUntil
|
|
1882
|
+
} : {}
|
|
1883
|
+
}).where((0, import_drizzle_orm12.and)((0, import_drizzle_orm12.eq)(bundleVersions.id, versionId), (0, import_drizzle_orm12.isNull)(bundleVersions.publishedAt))).returning();
|
|
1884
|
+
if (!publishedRows[0]) {
|
|
1885
|
+
throw new Error(`BundleVersion '${versionId}' not found or already published.`);
|
|
1886
|
+
}
|
|
1887
|
+
const draft = publishedRows[0];
|
|
1888
|
+
await db.update(bundleVersions).set(this.validityWindows ? {
|
|
1889
|
+
supersededAt: now,
|
|
1890
|
+
validUntil: (0, import_core3.previousUtcDay)(publishMeta.validFrom)
|
|
1891
|
+
} : {
|
|
1892
|
+
supersededAt: now
|
|
1893
|
+
}).where((0, import_drizzle_orm12.and)((0, import_drizzle_orm12.eq)(bundleVersions.bundleId, draft.bundleId), import_drizzle_orm12.sql`${bundleVersions.publishedAt} IS NOT NULL`, (0, import_drizzle_orm12.isNull)(bundleVersions.supersededAt), (0, import_drizzle_orm12.ne)(bundleVersions.id, versionId)));
|
|
1894
|
+
const stemRows = await db.select().from(bundles).where((0, import_drizzle_orm12.eq)(bundles.id, draft.bundleId)).limit(1);
|
|
1895
|
+
return this.toVersionRow(publishedRows[0], stemRows[0] ? (0, import_core3.toBundleStemRow)(stemRows[0]) : null);
|
|
1896
|
+
}
|
|
1897
|
+
async deleteDraft(versionId) {
|
|
1898
|
+
const deleted = await this.db.delete(bundleVersions).where((0, import_drizzle_orm12.and)((0, import_drizzle_orm12.eq)(bundleVersions.id, versionId), (0, import_drizzle_orm12.isNull)(bundleVersions.publishedAt))).returning({
|
|
1899
|
+
id: bundleVersions.id
|
|
1900
|
+
});
|
|
1901
|
+
if (deleted.length > 0) return;
|
|
1902
|
+
const remaining = await this.db.select({
|
|
1903
|
+
publishedAt: bundleVersions.publishedAt
|
|
1904
|
+
}).from(bundleVersions).where((0, import_drizzle_orm12.eq)(bundleVersions.id, versionId)).limit(1);
|
|
1905
|
+
if (!remaining[0]) return;
|
|
1906
|
+
throw new Error(`BundleVersion '${versionId}' is already published and cannot be discarded (published versions are immutable \u2014 contract protection P1).`);
|
|
1907
|
+
}
|
|
1908
|
+
/**
|
|
1909
|
+
* One version row plus its stem, or null — the tail every version lookup
|
|
1910
|
+
* ends with, and the reason it is here rather than written out each time:
|
|
1911
|
+
* the stem has to be read on the caller's connection, and a copy that
|
|
1912
|
+
* forgets that is the deadlock this class already had once.
|
|
1913
|
+
*/
|
|
1914
|
+
async withStem(row, bundleId, tx) {
|
|
1915
|
+
if (!row) return null;
|
|
1916
|
+
return this.toVersionRow(row, await this.stemOn(resolveDb(this.db, tx), bundleId));
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* The stem, read on the connection the caller is already holding.
|
|
1920
|
+
*
|
|
1921
|
+
* `this.db` would take a second one from the pool. Inside a transaction —
|
|
1922
|
+
* which is where `enforceLimit()` calls this, holding a row lock — that
|
|
1923
|
+
* second connection can only be released when the transaction ends, and the
|
|
1924
|
+
* transaction cannot end until this query returns. With a small pool, or
|
|
1925
|
+
* enough concurrent checks to occupy it, entitlement resolution stops
|
|
1926
|
+
* dead. A version query that honours `tx` and a stem query beside it that
|
|
1927
|
+
* does not is the shape that hides it: the deadlock needs load to appear.
|
|
1928
|
+
*/
|
|
1929
|
+
async stemOn(db, bundleId) {
|
|
1930
|
+
const rows = await db.select().from(bundles).where((0, import_drizzle_orm12.eq)(bundles.id, bundleId)).limit(1);
|
|
1931
|
+
return rows[0] ? (0, import_core3.toBundleStemRow)(rows[0]) : null;
|
|
1932
|
+
}
|
|
1933
|
+
toVersionRow(row, stem) {
|
|
1934
|
+
return {
|
|
1935
|
+
id: row.id,
|
|
1936
|
+
bundleId: row.bundleId,
|
|
1937
|
+
bundleKey: stem?.bundleKey ?? "",
|
|
1938
|
+
label: stem?.label ?? "",
|
|
1939
|
+
version: row.version,
|
|
1940
|
+
baseVersionId: row.baseVersionId,
|
|
1941
|
+
features: row.features ?? [],
|
|
1942
|
+
quotas: row.quotas ?? {},
|
|
1943
|
+
compatibility: row.compatibility ?? {},
|
|
1944
|
+
pricingOverrides: row.pricingOverrides ?? [],
|
|
1945
|
+
monthlyNet: row.monthlyNet,
|
|
1946
|
+
yearlyNet: row.yearlyNet,
|
|
1947
|
+
marketed: row.marketed,
|
|
1948
|
+
publishedAt: toIso(row.publishedAt),
|
|
1949
|
+
supersededAt: toIso(row.supersededAt),
|
|
1950
|
+
publishedChanges: Array.isArray(row.publishedChanges) ? row.publishedChanges : null,
|
|
1951
|
+
changeNote: row.changeNote,
|
|
1952
|
+
nonRegressive: row.nonRegressive,
|
|
1953
|
+
// Null rather than the stored value where the adapter does not
|
|
1954
|
+
// promise windows: returning a date it does not maintain would
|
|
1955
|
+
// invite a caller to trust it.
|
|
1956
|
+
validFrom: this.validityWindows ? toIso(row.validFrom) : null,
|
|
1957
|
+
validUntil: this.validityWindows ? toIso(row.validUntil) : null,
|
|
1958
|
+
createdByUserId: row.createdByUserId,
|
|
1959
|
+
publishedByUserId: row.publishedByUserId,
|
|
1960
|
+
createdAt: row.createdAt.toISOString(),
|
|
1961
|
+
updatedAt: row.updatedAt.toISOString()
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1964
|
+
};
|
|
1965
|
+
DrizzleBundleRepository = _ts_decorate14([
|
|
1966
|
+
(0, import_common14.Injectable)(),
|
|
1967
|
+
_ts_param13(0, (0, import_common14.Inject)(DRIZZLE_DB_TOKEN)),
|
|
1968
|
+
_ts_param13(1, (0, import_common14.Optional)()),
|
|
1969
|
+
_ts_metadata13("design:type", Function),
|
|
1970
|
+
_ts_metadata13("design:paramtypes", [
|
|
1971
|
+
typeof DrizzleClient === "undefined" ? Object : DrizzleClient,
|
|
1972
|
+
typeof DrizzleBundleRepositoryOptions === "undefined" ? Object : DrizzleBundleRepositoryOptions
|
|
1973
|
+
])
|
|
1974
|
+
], DrizzleBundleRepository);
|
|
1975
|
+
function toIso(value) {
|
|
1976
|
+
return value ? value.toISOString() : null;
|
|
1977
|
+
}
|
|
1978
|
+
__name(toIso, "toIso");
|
|
1979
|
+
function toNullableDate(value) {
|
|
1980
|
+
return value ? new Date(value) : null;
|
|
1981
|
+
}
|
|
1982
|
+
__name(toNullableDate, "toNullableDate");
|
|
1983
|
+
|
|
1984
|
+
// src/drizzle-plan.repository.ts
|
|
1985
|
+
var import_node_crypto9 = require("node:crypto");
|
|
1986
|
+
var import_common15 = require("@nestjs/common");
|
|
1987
|
+
var import_drizzle_orm13 = require("drizzle-orm");
|
|
1988
|
+
var import_core4 = require("@saasicat/core");
|
|
1989
|
+
function _ts_decorate15(decorators, target, key, desc10) {
|
|
1990
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
1991
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
1992
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1993
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1994
|
+
}
|
|
1995
|
+
__name(_ts_decorate15, "_ts_decorate");
|
|
1996
|
+
function _ts_metadata14(k, v) {
|
|
1997
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1998
|
+
}
|
|
1999
|
+
__name(_ts_metadata14, "_ts_metadata");
|
|
2000
|
+
function _ts_param14(paramIndex, decorator) {
|
|
2001
|
+
return function(target, key) {
|
|
2002
|
+
decorator(target, key, paramIndex);
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
__name(_ts_param14, "_ts_param");
|
|
2006
|
+
var DrizzlePlanRepository = class {
|
|
2007
|
+
static {
|
|
2008
|
+
__name(this, "DrizzlePlanRepository");
|
|
2009
|
+
}
|
|
2010
|
+
db;
|
|
2011
|
+
validityWindows;
|
|
2012
|
+
constructor(db, options = {}) {
|
|
2013
|
+
this.db = db;
|
|
2014
|
+
this.validityWindows = options.validityWindows ?? false;
|
|
2015
|
+
if (this.validityWindows) {
|
|
2016
|
+
this.findActivePlanVersion = (planKey, asOf, tx) => this.activeVersionAt(planKey, asOf ?? /* @__PURE__ */ new Date(), tx);
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
findActivePlanVersion;
|
|
2020
|
+
// ─── Stem operations ───
|
|
2021
|
+
async list(filter) {
|
|
2022
|
+
const excludeDeleted = filter.excludeDeleted ?? true;
|
|
2023
|
+
const stems = await this.db.select().from(plans).where((0, import_drizzle_orm13.and)(...excludeDeleted ? [
|
|
2024
|
+
(0, import_drizzle_orm13.isNull)(plans.deletedAt)
|
|
2025
|
+
] : [])).orderBy((0, import_drizzle_orm13.asc)(plans.sortOrder), (0, import_drizzle_orm13.asc)(plans.planKey));
|
|
2026
|
+
if (!filter.onlyPublished) return stems.map(import_core4.toPlanRow);
|
|
2027
|
+
if (stems.length === 0) return [];
|
|
2028
|
+
const candidateKeys = [
|
|
2029
|
+
...new Set(stems.map((stem) => stem.planKey))
|
|
2030
|
+
];
|
|
2031
|
+
const live = await this.db.select({
|
|
2032
|
+
planId: planVersions.planId
|
|
2033
|
+
}).from(planVersions).where((0, import_drizzle_orm13.and)((0, import_drizzle_orm13.inArray)(planVersions.planId, candidateKeys), import_drizzle_orm13.sql`${planVersions.publishedAt} IS NOT NULL`, (0, import_drizzle_orm13.isNull)(planVersions.supersededAt)));
|
|
2034
|
+
const publishedKeys = new Set(live.map((row) => row.planId));
|
|
2035
|
+
return stems.filter((stem) => publishedKeys.has(stem.planKey)).map(import_core4.toPlanRow);
|
|
2036
|
+
}
|
|
2037
|
+
async findById(planId) {
|
|
2038
|
+
const rows = await this.db.select().from(plans).where((0, import_drizzle_orm13.eq)(plans.id, planId)).limit(1);
|
|
2039
|
+
return rows[0] ? (0, import_core4.toPlanRow)(rows[0]) : null;
|
|
2040
|
+
}
|
|
2041
|
+
/**
|
|
2042
|
+
* Whether this installation already uses this plan key — **including
|
|
2043
|
+
* retired plans**.
|
|
2044
|
+
*
|
|
2045
|
+
* `plans_planKey_key` is unconditional, so a soft delete does not free the
|
|
2046
|
+
* key, and the duplicate check in `createPlan` needs the database's answer
|
|
2047
|
+
* rather than the active catalogue's. `list` is that other lookup.
|
|
2048
|
+
*/
|
|
2049
|
+
async findByKey(planKey) {
|
|
2050
|
+
const rows = await this.db.select().from(plans).where((0, import_drizzle_orm13.eq)(plans.planKey, planKey)).limit(1);
|
|
2051
|
+
return rows[0] ? (0, import_core4.toPlanRow)(rows[0]) : null;
|
|
2052
|
+
}
|
|
2053
|
+
async create(data) {
|
|
2054
|
+
const now = /* @__PURE__ */ new Date();
|
|
2055
|
+
const rows = await this.db.insert(plans).values({
|
|
2056
|
+
id: (0, import_node_crypto9.randomUUID)(),
|
|
2057
|
+
planKey: data.planKey,
|
|
2058
|
+
label: data.label,
|
|
2059
|
+
description: data.description ?? null,
|
|
2060
|
+
icon: data.icon ?? null,
|
|
2061
|
+
sortOrder: data.sortOrder ?? 0,
|
|
2062
|
+
createdAt: now,
|
|
2063
|
+
updatedAt: now
|
|
2064
|
+
}).returning();
|
|
2065
|
+
return (0, import_core4.toPlanRow)(rows[0]);
|
|
2066
|
+
}
|
|
2067
|
+
async update(planId, data) {
|
|
2068
|
+
const rows = await this.db.update(plans).set({
|
|
2069
|
+
...data.label !== void 0 ? {
|
|
2070
|
+
label: data.label
|
|
2071
|
+
} : {},
|
|
2072
|
+
...data.description !== void 0 ? {
|
|
2073
|
+
description: data.description
|
|
2074
|
+
} : {},
|
|
2075
|
+
...data.icon !== void 0 ? {
|
|
2076
|
+
icon: data.icon
|
|
2077
|
+
} : {},
|
|
2078
|
+
...data.sortOrder !== void 0 ? {
|
|
2079
|
+
sortOrder: data.sortOrder
|
|
2080
|
+
} : {},
|
|
2081
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2082
|
+
}).where((0, import_drizzle_orm13.eq)(plans.id, planId)).returning();
|
|
2083
|
+
if (!rows[0]) throw new Error(`Plan '${planId}' not found.`);
|
|
2084
|
+
return (0, import_core4.toPlanRow)(rows[0]);
|
|
2085
|
+
}
|
|
2086
|
+
async softDelete(planId) {
|
|
2087
|
+
const now = /* @__PURE__ */ new Date();
|
|
2088
|
+
await this.db.update(plans).set({
|
|
2089
|
+
deletedAt: now,
|
|
2090
|
+
updatedAt: now
|
|
2091
|
+
}).where((0, import_drizzle_orm13.eq)(plans.id, planId));
|
|
2092
|
+
}
|
|
2093
|
+
async hardDelete(planId) {
|
|
2094
|
+
await this.db.delete(plans).where((0, import_drizzle_orm13.eq)(plans.id, planId));
|
|
2095
|
+
}
|
|
2096
|
+
// ─── Version lifecycle — keyed by plan key ───
|
|
2097
|
+
async listVersions(planKey) {
|
|
2098
|
+
const rows = await this.db.select().from(planVersions).where((0, import_drizzle_orm13.eq)(planVersions.planId, planKey)).orderBy((0, import_drizzle_orm13.asc)(planVersions.version));
|
|
2099
|
+
return rows.map((row) => this.versionRow(row));
|
|
2100
|
+
}
|
|
2101
|
+
/**
|
|
2102
|
+
* `tx` is beyond what the port asks for, and is what keeps a caller already
|
|
2103
|
+
* inside a transaction on its own connection: drawing a second one waits
|
|
2104
|
+
* for a connection that transaction is itself holding, which on a
|
|
2105
|
+
* one-connection pool never returns.
|
|
2106
|
+
*/
|
|
2107
|
+
async findVersionById(versionId, tx) {
|
|
2108
|
+
const rows = await resolveDb(this.db, tx).select().from(planVersions).where((0, import_drizzle_orm13.eq)(planVersions.id, versionId)).limit(1);
|
|
2109
|
+
return rows[0] ? this.versionRow(rows[0]) : null;
|
|
2110
|
+
}
|
|
2111
|
+
async findCurrentDraft(planKey) {
|
|
2112
|
+
const rows = await this.db.select().from(planVersions).where((0, import_drizzle_orm13.and)((0, import_drizzle_orm13.eq)(planVersions.planId, planKey), (0, import_drizzle_orm13.isNull)(planVersions.publishedAt))).orderBy((0, import_drizzle_orm13.desc)(planVersions.version)).limit(1);
|
|
2113
|
+
return rows[0] ? this.versionRow(rows[0]) : null;
|
|
2114
|
+
}
|
|
2115
|
+
async findLatestLivePlanVersion(planKey, tx) {
|
|
2116
|
+
const rows = await resolveDb(this.db, tx).select().from(planVersions).where((0, import_drizzle_orm13.and)(
|
|
2117
|
+
(0, import_drizzle_orm13.eq)(planVersions.planId, planKey),
|
|
2118
|
+
import_drizzle_orm13.sql`${planVersions.publishedAt} IS NOT NULL`,
|
|
2119
|
+
(0, import_drizzle_orm13.isNull)(planVersions.supersededAt),
|
|
2120
|
+
// A version the operator terminated is not live, whatever
|
|
2121
|
+
// its supersession says: `terminate` sets `endsAt` without
|
|
2122
|
+
// a successor to supersede it.
|
|
2123
|
+
(0, import_drizzle_orm13.or)((0, import_drizzle_orm13.isNull)(planVersions.endsAt), import_drizzle_orm13.sql`${planVersions.endsAt} > NOW()`)
|
|
2124
|
+
)).orderBy((0, import_drizzle_orm13.desc)(planVersions.version)).limit(1);
|
|
2125
|
+
return rows[0] ? this.versionRow(rows[0]) : null;
|
|
2126
|
+
}
|
|
2127
|
+
/**
|
|
2128
|
+
* The version bookable at `asOf`: published, inside its validity window,
|
|
2129
|
+
* not terminated.
|
|
2130
|
+
*
|
|
2131
|
+
* `validUntil` is day-inclusive — a window that closes on the 28th is still
|
|
2132
|
+
* open at 23:59 on the 28th — which is why the comparison is against
|
|
2133
|
+
* `startOfUtcDay(asOf)` and not against `asOf`. The reading half of the
|
|
2134
|
+
* rule `previousUtcDay` writes in `publishWithin`; both come from
|
|
2135
|
+
* `@saasicat/core` so the two halves cannot drift apart.
|
|
2136
|
+
*/
|
|
2137
|
+
async activeVersionAt(planKey, asOf, tx) {
|
|
2138
|
+
const rows = await resolveDb(this.db, tx).select().from(planVersions).where((0, import_drizzle_orm13.and)((0, import_drizzle_orm13.eq)(planVersions.planId, planKey), import_drizzle_orm13.sql`${planVersions.publishedAt} IS NOT NULL`, (0, import_drizzle_orm13.or)((0, import_drizzle_orm13.isNull)(planVersions.validFrom), (0, import_drizzle_orm13.lte)(planVersions.validFrom, asOf)), (0, import_drizzle_orm13.or)((0, import_drizzle_orm13.isNull)(planVersions.validUntil), (0, import_drizzle_orm13.gte)(planVersions.validUntil, (0, import_core4.startOfUtcDay)(asOf))), (0, import_drizzle_orm13.or)((0, import_drizzle_orm13.isNull)(planVersions.endsAt), import_drizzle_orm13.sql`${planVersions.endsAt} > ${asOf}`))).orderBy(import_drizzle_orm13.sql`"validFrom" DESC NULLS LAST`, (0, import_drizzle_orm13.desc)(planVersions.version)).limit(1);
|
|
2139
|
+
return rows[0] ? this.versionRow(rows[0]) : null;
|
|
2140
|
+
}
|
|
2141
|
+
async createPlanVersionDraft(data) {
|
|
2142
|
+
const now = /* @__PURE__ */ new Date();
|
|
2143
|
+
const latest = await this.db.select({
|
|
2144
|
+
version: planVersions.version
|
|
2145
|
+
}).from(planVersions).where((0, import_drizzle_orm13.eq)(planVersions.planId, data.planId)).orderBy((0, import_drizzle_orm13.desc)(planVersions.version)).limit(1);
|
|
2146
|
+
const rows = await this.db.insert(planVersions).values({
|
|
2147
|
+
id: (0, import_node_crypto9.randomUUID)(),
|
|
2148
|
+
planId: data.planId,
|
|
2149
|
+
version: (latest[0]?.version ?? 0) + 1,
|
|
2150
|
+
baseVersionId: data.baseVersionId ?? null,
|
|
2151
|
+
features: data.features,
|
|
2152
|
+
quotas: data.quotas,
|
|
2153
|
+
monthlyNet: data.monthlyNet,
|
|
2154
|
+
yearlyNet: data.yearlyNet,
|
|
2155
|
+
marketed: data.marketed ?? true,
|
|
2156
|
+
changeNote: data.changeNote ?? "",
|
|
2157
|
+
createdByUserId: data.createdByUserId ?? null,
|
|
2158
|
+
createdAt: now,
|
|
2159
|
+
updatedAt: now,
|
|
2160
|
+
// publishedAt stays null — this is a draft. `bundles` is
|
|
2161
|
+
// app-specific and has no column here, as in adapter-prisma.
|
|
2162
|
+
...this.validityWindows ? {
|
|
2163
|
+
validFrom: toNullableDate2(data.validFrom),
|
|
2164
|
+
validUntil: toNullableDate2(data.validUntil)
|
|
2165
|
+
} : {}
|
|
2166
|
+
}).returning();
|
|
2167
|
+
return this.versionRow(rows[0]);
|
|
2168
|
+
}
|
|
2169
|
+
async updatePlanVersionDraft(versionId, data) {
|
|
2170
|
+
const rows = await this.db.update(planVersions).set({
|
|
2171
|
+
...data.features !== void 0 ? {
|
|
2172
|
+
features: data.features
|
|
2173
|
+
} : {},
|
|
2174
|
+
...data.quotas !== void 0 ? {
|
|
2175
|
+
quotas: data.quotas
|
|
2176
|
+
} : {},
|
|
2177
|
+
...data.monthlyNet !== void 0 ? {
|
|
2178
|
+
monthlyNet: data.monthlyNet
|
|
2179
|
+
} : {},
|
|
2180
|
+
...data.yearlyNet !== void 0 ? {
|
|
2181
|
+
yearlyNet: data.yearlyNet
|
|
2182
|
+
} : {},
|
|
2183
|
+
...data.marketed !== void 0 ? {
|
|
2184
|
+
marketed: data.marketed
|
|
2185
|
+
} : {},
|
|
2186
|
+
...data.changeNote !== void 0 ? {
|
|
2187
|
+
changeNote: data.changeNote
|
|
2188
|
+
} : {},
|
|
2189
|
+
...this.validityWindows && data.validFrom !== void 0 ? {
|
|
2190
|
+
validFrom: toNullableDate2(data.validFrom)
|
|
2191
|
+
} : {},
|
|
2192
|
+
...this.validityWindows && data.validUntil !== void 0 ? {
|
|
2193
|
+
validUntil: toNullableDate2(data.validUntil)
|
|
2194
|
+
} : {},
|
|
2195
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2196
|
+
}).where((0, import_drizzle_orm13.eq)(planVersions.id, versionId)).returning();
|
|
2197
|
+
if (!rows[0]) {
|
|
2198
|
+
throw new Error(`PlanVersion '${versionId}' not found.`);
|
|
2199
|
+
}
|
|
2200
|
+
return this.versionRow(rows[0]);
|
|
2201
|
+
}
|
|
2202
|
+
async publishPlanVersionDraft(versionId, publishMeta, tx) {
|
|
2203
|
+
if (tx !== void 0) return this.publishWithin(resolveDb(this.db, tx), versionId, publishMeta);
|
|
2204
|
+
return this.db.transaction((transaction) => this.publishWithin(transaction, versionId, publishMeta));
|
|
2205
|
+
}
|
|
2206
|
+
async publishWithin(db, versionId, publishMeta) {
|
|
2207
|
+
const now = /* @__PURE__ */ new Date();
|
|
2208
|
+
const publishedRows = await db.update(planVersions).set({
|
|
2209
|
+
publishedAt: now,
|
|
2210
|
+
publishedByUserId: publishMeta.publishedByUserId,
|
|
2211
|
+
publishedChanges: publishMeta.publishedChanges,
|
|
2212
|
+
nonRegressive: publishMeta.nonRegressive,
|
|
2213
|
+
updatedAt: now,
|
|
2214
|
+
...this.validityWindows ? {
|
|
2215
|
+
validFrom: publishMeta.validFrom,
|
|
2216
|
+
validUntil: publishMeta.validUntil
|
|
2217
|
+
} : {}
|
|
2218
|
+
}).where((0, import_drizzle_orm13.and)((0, import_drizzle_orm13.eq)(planVersions.id, versionId), (0, import_drizzle_orm13.isNull)(planVersions.publishedAt))).returning();
|
|
2219
|
+
if (!publishedRows[0]) {
|
|
2220
|
+
throw new Error(`PlanVersion '${versionId}' not found or already published.`);
|
|
2221
|
+
}
|
|
2222
|
+
await db.update(planVersions).set(this.validityWindows ? {
|
|
2223
|
+
supersededAt: now,
|
|
2224
|
+
validUntil: (0, import_core4.previousUtcDay)(publishMeta.validFrom),
|
|
2225
|
+
updatedAt: now
|
|
2226
|
+
} : {
|
|
2227
|
+
supersededAt: now,
|
|
2228
|
+
updatedAt: now
|
|
2229
|
+
}).where((0, import_drizzle_orm13.and)((0, import_drizzle_orm13.eq)(planVersions.planId, publishedRows[0].planId), import_drizzle_orm13.sql`${planVersions.publishedAt} IS NOT NULL`, (0, import_drizzle_orm13.isNull)(planVersions.supersededAt), (0, import_drizzle_orm13.ne)(planVersions.id, versionId)));
|
|
2230
|
+
return this.versionRow(publishedRows[0]);
|
|
2231
|
+
}
|
|
2232
|
+
async deletePlanVersionDraft(versionId) {
|
|
2233
|
+
const deleted = await this.db.delete(planVersions).where((0, import_drizzle_orm13.and)((0, import_drizzle_orm13.eq)(planVersions.id, versionId), (0, import_drizzle_orm13.isNull)(planVersions.publishedAt))).returning({
|
|
2234
|
+
id: planVersions.id
|
|
2235
|
+
});
|
|
2236
|
+
if (deleted.length > 0) return;
|
|
2237
|
+
const existing = await this.db.select({
|
|
2238
|
+
publishedAt: planVersions.publishedAt
|
|
2239
|
+
}).from(planVersions).where((0, import_drizzle_orm13.eq)(planVersions.id, versionId)).limit(1);
|
|
2240
|
+
if (existing[0]) {
|
|
2241
|
+
throw new Error(`PlanVersion '${versionId}' is already published and cannot be discarded (published versions are immutable \u2014 contract protection P1).`);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
async terminate(versionId, endsAt) {
|
|
2245
|
+
const rows = await this.db.update(planVersions).set({
|
|
2246
|
+
endsAt,
|
|
2247
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2248
|
+
}).where((0, import_drizzle_orm13.eq)(planVersions.id, versionId)).returning();
|
|
2249
|
+
if (!rows[0]) throw new Error(`PlanVersion '${versionId}' not found.`);
|
|
2250
|
+
return this.versionRow(rows[0]);
|
|
2251
|
+
}
|
|
2252
|
+
versionRow(row) {
|
|
2253
|
+
return (0, import_core4.toPlanVersionRow)(row, row.planId, {
|
|
2254
|
+
validityWindows: this.validityWindows,
|
|
2255
|
+
// The canonical schema always has the column, so this adapter can
|
|
2256
|
+
// always answer the question — unlike a Prisma consumer whose
|
|
2257
|
+
// schema may predate it.
|
|
2258
|
+
endsAt: true
|
|
2259
|
+
});
|
|
2260
|
+
}
|
|
2261
|
+
};
|
|
2262
|
+
DrizzlePlanRepository = _ts_decorate15([
|
|
2263
|
+
(0, import_common15.Injectable)(),
|
|
2264
|
+
_ts_param14(0, (0, import_common15.Inject)(DRIZZLE_DB_TOKEN)),
|
|
2265
|
+
_ts_param14(1, (0, import_common15.Optional)()),
|
|
2266
|
+
_ts_metadata14("design:type", Function),
|
|
2267
|
+
_ts_metadata14("design:paramtypes", [
|
|
2268
|
+
typeof DrizzleClient === "undefined" ? Object : DrizzleClient,
|
|
2269
|
+
typeof DrizzlePlanRepositoryOptions === "undefined" ? Object : DrizzlePlanRepositoryOptions
|
|
2270
|
+
])
|
|
2271
|
+
], DrizzlePlanRepository);
|
|
2272
|
+
function toNullableDate2(value) {
|
|
2273
|
+
return value ? new Date(value) : null;
|
|
2274
|
+
}
|
|
2275
|
+
__name(toNullableDate2, "toNullableDate");
|
|
2276
|
+
|
|
2277
|
+
// src/drizzle-subscription-contract.repository.ts
|
|
2278
|
+
var import_node_crypto10 = require("node:crypto");
|
|
2279
|
+
var import_common16 = require("@nestjs/common");
|
|
2280
|
+
var import_drizzle_orm14 = require("drizzle-orm");
|
|
2281
|
+
var import_core5 = require("@saasicat/core");
|
|
2282
|
+
function _ts_decorate16(decorators, target, key, desc10) {
|
|
2283
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
2284
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
2285
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2286
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2287
|
+
}
|
|
2288
|
+
__name(_ts_decorate16, "_ts_decorate");
|
|
2289
|
+
function _ts_metadata15(k, v) {
|
|
2290
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2291
|
+
}
|
|
2292
|
+
__name(_ts_metadata15, "_ts_metadata");
|
|
2293
|
+
function _ts_param15(paramIndex, decorator) {
|
|
2294
|
+
return function(target, key) {
|
|
2295
|
+
decorator(target, key, paramIndex);
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
__name(_ts_param15, "_ts_param");
|
|
2299
|
+
var DrizzleSubscriptionContractRepository = class {
|
|
2300
|
+
static {
|
|
2301
|
+
__name(this, "DrizzleSubscriptionContractRepository");
|
|
2302
|
+
}
|
|
2303
|
+
db;
|
|
2304
|
+
constructor(db) {
|
|
2305
|
+
this.db = db;
|
|
2306
|
+
}
|
|
2307
|
+
async list(filter) {
|
|
2308
|
+
const rows = await this.db.select().from(subscriptionContracts).where((0, import_drizzle_orm14.and)(...filter.tenantId ? [
|
|
2309
|
+
(0, import_drizzle_orm14.eq)(subscriptionContracts.tenantId, filter.tenantId)
|
|
2310
|
+
] : [], ...filter.status ? [
|
|
2311
|
+
(0, import_drizzle_orm14.eq)(subscriptionContracts.status, filter.status)
|
|
2312
|
+
] : [], ...filter.asOf ? [
|
|
2313
|
+
inWindowAt(filter.asOf)
|
|
2314
|
+
] : [])).orderBy((0, import_drizzle_orm14.desc)(subscriptionContracts.effectiveFrom), (0, import_drizzle_orm14.desc)(subscriptionContracts.createdAt));
|
|
2315
|
+
return this.withLineItems(this.db, rows);
|
|
2316
|
+
}
|
|
2317
|
+
async findById(contractId) {
|
|
2318
|
+
const rows = await this.db.select().from(subscriptionContracts).where((0, import_drizzle_orm14.eq)(subscriptionContracts.id, contractId)).limit(1);
|
|
2319
|
+
return (await this.withLineItems(this.db, rows))[0] ?? null;
|
|
2320
|
+
}
|
|
2321
|
+
async findActiveByTenantId(tenantId, asOf = /* @__PURE__ */ new Date(), tx) {
|
|
2322
|
+
const db = resolveDb(this.db, tx);
|
|
2323
|
+
const rows = await db.select().from(subscriptionContracts).where((0, import_drizzle_orm14.and)((0, import_drizzle_orm14.eq)(subscriptionContracts.tenantId, tenantId), (0, import_drizzle_orm14.inArray)(subscriptionContracts.status, [
|
|
2324
|
+
...import_core5.ACTIVE_SUBSCRIPTION_CONTRACT_STATUSES
|
|
2325
|
+
]), inWindowAt(asOf))).orderBy((0, import_drizzle_orm14.desc)(subscriptionContracts.effectiveFrom), (0, import_drizzle_orm14.desc)(subscriptionContracts.createdAt)).limit(1);
|
|
2326
|
+
return (await this.withLineItems(db, rows))[0] ?? null;
|
|
2327
|
+
}
|
|
2328
|
+
async findByOriginalOfferId(offerId, tx) {
|
|
2329
|
+
const db = resolveDb(this.db, tx);
|
|
2330
|
+
const rows = await db.select().from(subscriptionContracts).where((0, import_drizzle_orm14.eq)(subscriptionContracts.originalOfferId, offerId)).orderBy((0, import_drizzle_orm14.asc)(subscriptionContracts.createdAt), (0, import_drizzle_orm14.asc)(subscriptionContracts.id)).limit(1);
|
|
2331
|
+
return (await this.withLineItems(db, rows))[0] ?? null;
|
|
2332
|
+
}
|
|
2333
|
+
async create(data, tx) {
|
|
2334
|
+
const now = /* @__PURE__ */ new Date();
|
|
2335
|
+
const contractId = (0, import_node_crypto10.randomUUID)();
|
|
2336
|
+
return resolveDb(this.db, tx).transaction(async (transaction) => {
|
|
2337
|
+
const db = transaction;
|
|
2338
|
+
const rows = await db.insert(subscriptionContracts).values({
|
|
2339
|
+
id: contractId,
|
|
2340
|
+
tenantId: data.tenantId,
|
|
2341
|
+
status: data.status ?? "active",
|
|
2342
|
+
effectiveFrom: data.effectiveFrom,
|
|
2343
|
+
effectiveUntil: data.effectiveUntil ?? null,
|
|
2344
|
+
originalOfferId: data.originalOfferId ?? null,
|
|
2345
|
+
originalPlanVersionId: data.originalPlanVersionId ?? null,
|
|
2346
|
+
originalBundleVersionIds: data.originalBundleVersionIds ?? [],
|
|
2347
|
+
entitlementSnapshot: data.entitlementSnapshot ?? null,
|
|
2348
|
+
priceSnapshot: data.priceSnapshot,
|
|
2349
|
+
promotionSnapshots: data.promotionSnapshots ?? [],
|
|
2350
|
+
promoCodeSnapshots: data.promoCodeSnapshots ?? [],
|
|
2351
|
+
termsSnapshot: data.termsSnapshot ?? null,
|
|
2352
|
+
subscriberId: data.parties.subscriberId,
|
|
2353
|
+
subscriberSnapshot: data.parties.subscriber,
|
|
2354
|
+
issuerSnapshot: data.parties.issuer,
|
|
2355
|
+
createdAt: now,
|
|
2356
|
+
updatedAt: now
|
|
2357
|
+
}).returning();
|
|
2358
|
+
const lineRows = data.lineItems.length === 0 ? [] : await db.insert(contractLineItems).values(data.lineItems.map((item) => toLineItemInsert(item, contractId, now))).returning();
|
|
2359
|
+
return (0, import_core5.toSubscriptionContractRecord)(rows[0], lineRows);
|
|
2360
|
+
});
|
|
2361
|
+
}
|
|
2362
|
+
async listRunningIssuers(limit, asOf = /* @__PURE__ */ new Date()) {
|
|
2363
|
+
const running = (0, import_drizzle_orm14.and)((0, import_drizzle_orm14.inArray)(subscriptionContracts.status, [
|
|
2364
|
+
...import_core5.ACTIVE_SUBSCRIPTION_CONTRACT_STATUSES
|
|
2365
|
+
]), (0, import_drizzle_orm14.or)((0, import_drizzle_orm14.isNull)(subscriptionContracts.effectiveUntil), (0, import_drizzle_orm14.gt)(subscriptionContracts.effectiveUntil, asOf)));
|
|
2366
|
+
const [totals, rows] = await Promise.all([
|
|
2367
|
+
this.db.select({
|
|
2368
|
+
value: (0, import_drizzle_orm14.count)()
|
|
2369
|
+
}).from(subscriptionContracts).where(running),
|
|
2370
|
+
// Oldest first, and `id` last so two contracts that start in the
|
|
2371
|
+
// same moment are still listed in one order — a refusal that names
|
|
2372
|
+
// a different pair on every restart is one an operator cannot act
|
|
2373
|
+
// on.
|
|
2374
|
+
this.db.select({
|
|
2375
|
+
id: subscriptionContracts.id,
|
|
2376
|
+
tenantId: subscriptionContracts.tenantId,
|
|
2377
|
+
issuerSnapshot: subscriptionContracts.issuerSnapshot,
|
|
2378
|
+
effectiveFrom: subscriptionContracts.effectiveFrom
|
|
2379
|
+
}).from(subscriptionContracts).where(running).orderBy((0, import_drizzle_orm14.asc)(subscriptionContracts.effectiveFrom), (0, import_drizzle_orm14.asc)(subscriptionContracts.createdAt), (0, import_drizzle_orm14.asc)(subscriptionContracts.id)).limit(limit)
|
|
2380
|
+
]);
|
|
2381
|
+
return {
|
|
2382
|
+
total: totals[0]?.value ?? 0,
|
|
2383
|
+
contracts: rows.map(import_core5.toRunningContractIssuer)
|
|
2384
|
+
};
|
|
2385
|
+
}
|
|
2386
|
+
async terminate(contractId, data) {
|
|
2387
|
+
const rows = await this.db.update(subscriptionContracts).set({
|
|
2388
|
+
effectiveUntil: data.effectiveUntil,
|
|
2389
|
+
// A null status leaves the column alone: the contract keeps
|
|
2390
|
+
// whatever it had — `active`, usually — and the new
|
|
2391
|
+
// `effectiveUntil` is what takes it out of the active lookup
|
|
2392
|
+
// once that moment arrives.
|
|
2393
|
+
...data.status === null ? {} : {
|
|
2394
|
+
status: data.status
|
|
2395
|
+
},
|
|
2396
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2397
|
+
}).where((0, import_drizzle_orm14.eq)(subscriptionContracts.id, contractId)).returning();
|
|
2398
|
+
if (!rows[0]) throw new Error(`SubscriptionContract '${contractId}' not found.`);
|
|
2399
|
+
return (await this.withLineItems(this.db, rows))[0];
|
|
2400
|
+
}
|
|
2401
|
+
/**
|
|
2402
|
+
* The lines for a batch of contracts in one query rather than one per
|
|
2403
|
+
* contract: `list` is the operator's contract history and would otherwise
|
|
2404
|
+
* be an N+1 over a table that only grows.
|
|
2405
|
+
*/
|
|
2406
|
+
async withLineItems(db, rows) {
|
|
2407
|
+
if (rows.length === 0) return [];
|
|
2408
|
+
const lines = await db.select().from(contractLineItems).where((0, import_drizzle_orm14.inArray)(contractLineItems.contractId, rows.map((row) => row.id)));
|
|
2409
|
+
const byContract = /* @__PURE__ */ new Map();
|
|
2410
|
+
for (const line of lines) {
|
|
2411
|
+
const bucket = byContract.get(line.contractId);
|
|
2412
|
+
if (bucket) bucket.push(line);
|
|
2413
|
+
else byContract.set(line.contractId, [
|
|
2414
|
+
line
|
|
2415
|
+
]);
|
|
2416
|
+
}
|
|
2417
|
+
return rows.map((row) => (0, import_core5.toSubscriptionContractRecord)(row, byContract.get(row.id) ?? []));
|
|
2418
|
+
}
|
|
2419
|
+
};
|
|
2420
|
+
DrizzleSubscriptionContractRepository = _ts_decorate16([
|
|
2421
|
+
(0, import_common16.Injectable)(),
|
|
2422
|
+
_ts_param15(0, (0, import_common16.Inject)(DRIZZLE_DB_TOKEN)),
|
|
2423
|
+
_ts_metadata15("design:type", Function),
|
|
2424
|
+
_ts_metadata15("design:paramtypes", [
|
|
2425
|
+
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
2426
|
+
])
|
|
2427
|
+
], DrizzleSubscriptionContractRepository);
|
|
2428
|
+
function inWindowAt(asOf) {
|
|
2429
|
+
return (0, import_drizzle_orm14.and)((0, import_drizzle_orm14.lte)(subscriptionContracts.effectiveFrom, asOf), (0, import_drizzle_orm14.or)((0, import_drizzle_orm14.isNull)(subscriptionContracts.effectiveUntil), (0, import_drizzle_orm14.gt)(subscriptionContracts.effectiveUntil, asOf)));
|
|
2430
|
+
}
|
|
2431
|
+
__name(inWindowAt, "inWindowAt");
|
|
2432
|
+
function toLineItemInsert(item, contractId, createdAt) {
|
|
2433
|
+
return {
|
|
2434
|
+
id: (0, import_node_crypto10.randomUUID)(),
|
|
2435
|
+
contractId,
|
|
2436
|
+
kind: item.kind,
|
|
2437
|
+
sourceKey: item.sourceKey,
|
|
2438
|
+
sourceVersionId: item.sourceVersionId ?? null,
|
|
2439
|
+
titleSnapshot: item.titleSnapshot,
|
|
2440
|
+
descriptionSnapshot: item.descriptionSnapshot ?? null,
|
|
2441
|
+
quantity: item.quantity,
|
|
2442
|
+
unit: item.unit ?? null,
|
|
2443
|
+
// Money as a decimal string all the way to the column: passing a JS
|
|
2444
|
+
// number here would round it on the way in, before anybody could
|
|
2445
|
+
// notice.
|
|
2446
|
+
priceNet: item.priceNet.toFixed(2),
|
|
2447
|
+
priceGross: item.priceGross.toFixed(2),
|
|
2448
|
+
billingCycle: item.billingCycle,
|
|
2449
|
+
currency: item.currency,
|
|
2450
|
+
taxRate: item.taxRate.toFixed(2),
|
|
2451
|
+
taxAmount: item.taxAmount.toFixed(2),
|
|
2452
|
+
minimumTermUntil: item.minimumTermUntil ?? null,
|
|
2453
|
+
featuresSnapshot: item.featuresSnapshot,
|
|
2454
|
+
quotaEffectsSnapshot: item.quotaEffectsSnapshot,
|
|
2455
|
+
metadata: item.metadata ?? null,
|
|
2456
|
+
createdAt
|
|
2457
|
+
};
|
|
2458
|
+
}
|
|
2459
|
+
__name(toLineItemInsert, "toLineItemInsert");
|
|
2460
|
+
|
|
2461
|
+
// src/drizzle-subscriber-payment-method.repository.ts
|
|
2462
|
+
var import_node_crypto11 = require("node:crypto");
|
|
2463
|
+
var import_common17 = require("@nestjs/common");
|
|
2464
|
+
var import_drizzle_orm15 = require("drizzle-orm");
|
|
2465
|
+
var import_core6 = require("@saasicat/core");
|
|
2466
|
+
function _ts_decorate17(decorators, target, key, desc10) {
|
|
2467
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
2468
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
2469
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2470
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2471
|
+
}
|
|
2472
|
+
__name(_ts_decorate17, "_ts_decorate");
|
|
2473
|
+
function _ts_metadata16(k, v) {
|
|
2474
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2475
|
+
}
|
|
2476
|
+
__name(_ts_metadata16, "_ts_metadata");
|
|
2477
|
+
function _ts_param16(paramIndex, decorator) {
|
|
2478
|
+
return function(target, key) {
|
|
2479
|
+
decorator(target, key, paramIndex);
|
|
2480
|
+
};
|
|
2481
|
+
}
|
|
2482
|
+
__name(_ts_param16, "_ts_param");
|
|
2483
|
+
var DrizzleSubscriberPaymentMethodRepository = class {
|
|
2484
|
+
static {
|
|
2485
|
+
__name(this, "DrizzleSubscriberPaymentMethodRepository");
|
|
2486
|
+
}
|
|
2487
|
+
db;
|
|
2488
|
+
constructor(db) {
|
|
2489
|
+
this.db = db;
|
|
2490
|
+
}
|
|
2491
|
+
async recordConfirmed(data, tx) {
|
|
2492
|
+
return resolveDb(this.db, tx).transaction(async (transaction) => {
|
|
2493
|
+
const db = transaction;
|
|
2494
|
+
const locked = await db.select({
|
|
2495
|
+
id: subscribers.id
|
|
2496
|
+
}).from(subscribers).where((0, import_drizzle_orm15.eq)(subscribers.id, data.subscriberId)).for("update").limit(1);
|
|
2497
|
+
if (locked.length === 0) {
|
|
2498
|
+
throw new Error(`Subscriber '${data.subscriberId}' does not exist; its payment method cannot be recorded.`);
|
|
2499
|
+
}
|
|
2500
|
+
const [recorded] = await db.select().from(subscriberPaymentMethods).where(this.byReference(data.gatewayAccount, data.paymentMethodRef)).limit(1);
|
|
2501
|
+
if (recorded) {
|
|
2502
|
+
(0, import_core6.refuseForeignPaymentMethodReference)(recorded, data.subscriberId);
|
|
2503
|
+
return {
|
|
2504
|
+
method: (0, import_core6.toSubscriberPaymentMethodRecord)(recorded),
|
|
2505
|
+
outcome: "already-recorded"
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
const [active] = await db.select().from(subscriberPaymentMethods).where(this.activeOf(data.subscriberId)).limit(1);
|
|
2509
|
+
const replacedOnArrival = active && active.confirmedAt.getTime() > data.confirmedAt.getTime() ? active.confirmedAt : null;
|
|
2510
|
+
const claimed = await this.claimReference(db, {
|
|
2511
|
+
...(0, import_core6.subscriberPaymentMethodColumns)(data),
|
|
2512
|
+
id: (0, import_node_crypto11.randomUUID)(),
|
|
2513
|
+
status: "REPLACED",
|
|
2514
|
+
replacedAt: replacedOnArrival ?? data.confirmedAt
|
|
2515
|
+
});
|
|
2516
|
+
if (replacedOnArrival) {
|
|
2517
|
+
return {
|
|
2518
|
+
method: (0, import_core6.toSubscriberPaymentMethodRecord)(claimed),
|
|
2519
|
+
outcome: "superseded"
|
|
2520
|
+
};
|
|
2521
|
+
}
|
|
2522
|
+
if (active) {
|
|
2523
|
+
await db.update(subscriberPaymentMethods).set({
|
|
2524
|
+
status: "REPLACED",
|
|
2525
|
+
replacedAt: data.confirmedAt
|
|
2526
|
+
}).where((0, import_drizzle_orm15.eq)(subscriberPaymentMethods.id, active.id));
|
|
2527
|
+
}
|
|
2528
|
+
const [inUse] = await db.update(subscriberPaymentMethods).set({
|
|
2529
|
+
status: "ACTIVE",
|
|
2530
|
+
replacedAt: null
|
|
2531
|
+
}).where((0, import_drizzle_orm15.eq)(subscriberPaymentMethods.id, claimed.id)).returning();
|
|
2532
|
+
return {
|
|
2533
|
+
method: (0, import_core6.toSubscriberPaymentMethodRecord)(inUse),
|
|
2534
|
+
outcome: "activated"
|
|
2535
|
+
};
|
|
2536
|
+
});
|
|
2537
|
+
}
|
|
2538
|
+
/**
|
|
2539
|
+
* Takes the reference for this subscriber, or refuses it to them.
|
|
2540
|
+
*
|
|
2541
|
+
* The first write of the three, and the only one that can be refused — so a
|
|
2542
|
+
* refusal leaves the caller's transaction as it found it, whichever way the
|
|
2543
|
+
* caller opened it. `onConflictDoNothing` on the reference's own columns is
|
|
2544
|
+
* what answers: the row the read above could not see, because the
|
|
2545
|
+
* transaction holding it had not committed, is waited for here rather than
|
|
2546
|
+
* raised as a driver error, which every driver spells differently.
|
|
2547
|
+
*
|
|
2548
|
+
* It is written `REPLACED` because the seat a subscriber's one `ACTIVE`
|
|
2549
|
+
* payment method occupies may still be taken; `recordConfirmed` gives it
|
|
2550
|
+
* the status it keeps once the seat is free. Nothing outside the
|
|
2551
|
+
* transaction sees the difference.
|
|
2552
|
+
*/
|
|
2553
|
+
async claimReference(db, values) {
|
|
2554
|
+
const [claimed] = await db.insert(subscriberPaymentMethods).values(values).onConflictDoNothing({
|
|
2555
|
+
target: [
|
|
2556
|
+
subscriberPaymentMethods.gatewayAccount,
|
|
2557
|
+
subscriberPaymentMethods.paymentMethodRef
|
|
2558
|
+
]
|
|
2559
|
+
}).returning();
|
|
2560
|
+
if (!claimed) {
|
|
2561
|
+
throw new import_core6.ForeignPaymentMethodReferenceError(values.gatewayAccount, values.paymentMethodRef);
|
|
2562
|
+
}
|
|
2563
|
+
return claimed;
|
|
2564
|
+
}
|
|
2565
|
+
async findActive(subscriberId, tx) {
|
|
2566
|
+
const [row] = await resolveDb(this.db, tx).select().from(subscriberPaymentMethods).where(this.activeOf(subscriberId)).limit(1);
|
|
2567
|
+
return row ? (0, import_core6.toSubscriberPaymentMethodRecord)(row) : null;
|
|
2568
|
+
}
|
|
2569
|
+
async findByReference(reference, tx) {
|
|
2570
|
+
const [row] = await resolveDb(this.db, tx).select().from(subscriberPaymentMethods).where((0, import_drizzle_orm15.and)((0, import_drizzle_orm15.eq)(subscriberPaymentMethods.subscriberId, reference.subscriberId), this.byReference(reference.gatewayAccount, reference.paymentMethodRef))).limit(1);
|
|
2571
|
+
return row ? (0, import_core6.toSubscriberPaymentMethodRecord)(row) : null;
|
|
2572
|
+
}
|
|
2573
|
+
async accountsInUse() {
|
|
2574
|
+
const rows = await this.db.selectDistinct({
|
|
2575
|
+
gatewayAccount: subscriberPaymentMethods.gatewayAccount
|
|
2576
|
+
}).from(subscriberPaymentMethods).where((0, import_drizzle_orm15.eq)(subscriberPaymentMethods.status, "ACTIVE")).orderBy((0, import_drizzle_orm15.asc)(subscriberPaymentMethods.gatewayAccount));
|
|
2577
|
+
return rows.map((row) => row.gatewayAccount);
|
|
2578
|
+
}
|
|
2579
|
+
async recordSetup(data, tx) {
|
|
2580
|
+
await resolveDb(this.db, tx).insert(subscriberPaymentMethodSetups).values({
|
|
2581
|
+
id: (0, import_node_crypto11.randomUUID)(),
|
|
2582
|
+
subscriberId: data.subscriberId,
|
|
2583
|
+
gatewayAccount: data.gatewayAccount,
|
|
2584
|
+
sessionRef: data.sessionRef,
|
|
2585
|
+
customerRef: data.customerRef,
|
|
2586
|
+
startedAt: data.startedAt
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2589
|
+
async completeSetup(match, completedAt, tx) {
|
|
2590
|
+
const completed = await resolveDb(this.db, tx).update(subscriberPaymentMethodSetups).set({
|
|
2591
|
+
completedAt
|
|
2592
|
+
}).where((0, import_drizzle_orm15.and)((0, import_drizzle_orm15.eq)(subscriberPaymentMethodSetups.gatewayAccount, match.gatewayAccount), (0, import_drizzle_orm15.eq)(subscriberPaymentMethodSetups.sessionRef, match.sessionRef), (0, import_drizzle_orm15.eq)(subscriberPaymentMethodSetups.subscriberId, match.subscriberId), (0, import_drizzle_orm15.isNull)(subscriberPaymentMethodSetups.completedAt))).returning({
|
|
2593
|
+
id: subscriberPaymentMethodSetups.id
|
|
2594
|
+
});
|
|
2595
|
+
return completed.length === 1;
|
|
2596
|
+
}
|
|
2597
|
+
activeOf(subscriberId) {
|
|
2598
|
+
return (0, import_drizzle_orm15.and)((0, import_drizzle_orm15.eq)(subscriberPaymentMethods.subscriberId, subscriberId), (0, import_drizzle_orm15.eq)(subscriberPaymentMethods.status, "ACTIVE"));
|
|
2599
|
+
}
|
|
2600
|
+
byReference(gatewayAccount, paymentMethodRef) {
|
|
2601
|
+
return (0, import_drizzle_orm15.and)((0, import_drizzle_orm15.eq)(subscriberPaymentMethods.gatewayAccount, gatewayAccount), (0, import_drizzle_orm15.eq)(subscriberPaymentMethods.paymentMethodRef, paymentMethodRef));
|
|
2602
|
+
}
|
|
2603
|
+
};
|
|
2604
|
+
DrizzleSubscriberPaymentMethodRepository = _ts_decorate17([
|
|
2605
|
+
(0, import_common17.Injectable)(),
|
|
2606
|
+
_ts_param16(0, (0, import_common17.Inject)(DRIZZLE_DB_TOKEN)),
|
|
2607
|
+
_ts_metadata16("design:type", Function),
|
|
2608
|
+
_ts_metadata16("design:paramtypes", [
|
|
2609
|
+
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
2610
|
+
])
|
|
2611
|
+
], DrizzleSubscriberPaymentMethodRepository);
|
|
2612
|
+
|
|
2613
|
+
// src/drizzle-subscriber.repository.ts
|
|
2614
|
+
var import_node_crypto12 = require("node:crypto");
|
|
2615
|
+
var import_common18 = require("@nestjs/common");
|
|
2616
|
+
var import_drizzle_orm16 = require("drizzle-orm");
|
|
2617
|
+
var import_core7 = require("@saasicat/core");
|
|
2618
|
+
function _ts_decorate18(decorators, target, key, desc10) {
|
|
2619
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
2620
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
2621
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2622
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2623
|
+
}
|
|
2624
|
+
__name(_ts_decorate18, "_ts_decorate");
|
|
2625
|
+
function _ts_metadata17(k, v) {
|
|
2626
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2627
|
+
}
|
|
2628
|
+
__name(_ts_metadata17, "_ts_metadata");
|
|
2629
|
+
function _ts_param17(paramIndex, decorator) {
|
|
2630
|
+
return function(target, key) {
|
|
2631
|
+
decorator(target, key, paramIndex);
|
|
2632
|
+
};
|
|
2633
|
+
}
|
|
2634
|
+
__name(_ts_param17, "_ts_param");
|
|
2635
|
+
var DrizzleSubscriberRepository = class {
|
|
2636
|
+
static {
|
|
2637
|
+
__name(this, "DrizzleSubscriberRepository");
|
|
2638
|
+
}
|
|
2639
|
+
db;
|
|
2640
|
+
constructor(db) {
|
|
2641
|
+
this.db = db;
|
|
2642
|
+
}
|
|
2643
|
+
async createForTenant(data, tx) {
|
|
2644
|
+
const { tenantId, ...details } = data;
|
|
2645
|
+
const now = /* @__PURE__ */ new Date();
|
|
2646
|
+
const subscriberId = (0, import_node_crypto12.randomUUID)();
|
|
2647
|
+
return resolveDb(this.db, tx).transaction(async (transaction) => {
|
|
2648
|
+
const db = transaction;
|
|
2649
|
+
const [created] = await db.insert(subscribers).values({
|
|
2650
|
+
...details,
|
|
2651
|
+
id: subscriberId,
|
|
2652
|
+
createdAt: now,
|
|
2653
|
+
updatedAt: now
|
|
2654
|
+
}).returning();
|
|
2655
|
+
const linked = await db.insert(subscriberTenants).values({
|
|
2656
|
+
id: (0, import_node_crypto12.randomUUID)(),
|
|
2657
|
+
subscriberId,
|
|
2658
|
+
tenantId,
|
|
2659
|
+
linkedAt: now
|
|
2660
|
+
}).onConflictDoNothing().returning();
|
|
2661
|
+
if (linked.length === 0) {
|
|
2662
|
+
await db.delete(subscribers).where((0, import_drizzle_orm16.eq)(subscribers.id, subscriberId));
|
|
2663
|
+
return null;
|
|
2664
|
+
}
|
|
2665
|
+
return (0, import_core7.toSubscriberRecord)(created, tenantId);
|
|
2666
|
+
});
|
|
2667
|
+
}
|
|
2668
|
+
async findById(subscriberId, tx) {
|
|
2669
|
+
const db = resolveDb(this.db, tx);
|
|
2670
|
+
const rows = await db.select().from(subscribers).where((0, import_drizzle_orm16.eq)(subscribers.id, subscriberId)).limit(1);
|
|
2671
|
+
return rows[0] ? this.withLiveTenant(db, rows[0]) : null;
|
|
2672
|
+
}
|
|
2673
|
+
async findByTenantId(tenantId, tx) {
|
|
2674
|
+
const db = resolveDb(this.db, tx);
|
|
2675
|
+
const rows = await db.select({
|
|
2676
|
+
subscriber: subscribers
|
|
2677
|
+
}).from(subscriberTenants).innerJoin(subscribers, (0, import_drizzle_orm16.eq)(subscribers.id, subscriberTenants.subscriberId)).where((0, import_drizzle_orm16.and)((0, import_drizzle_orm16.eq)(subscriberTenants.tenantId, tenantId), (0, import_drizzle_orm16.isNull)(subscriberTenants.unlinkedAt))).limit(1);
|
|
2678
|
+
return rows[0] ? (0, import_core7.toSubscriberRecord)(rows[0].subscriber, tenantId) : null;
|
|
2679
|
+
}
|
|
2680
|
+
async updateContact(subscriberId, change, tx) {
|
|
2681
|
+
const db = resolveDb(this.db, tx);
|
|
2682
|
+
const rows = await db.update(subscribers).set({
|
|
2683
|
+
...change,
|
|
2684
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2685
|
+
}).where((0, import_drizzle_orm16.eq)(subscribers.id, subscriberId)).returning();
|
|
2686
|
+
return rows[0] ? this.withLiveTenant(db, rows[0]) : null;
|
|
2687
|
+
}
|
|
2688
|
+
async correctIdentity(subscriberId, data, tx) {
|
|
2689
|
+
return resolveDb(this.db, tx).transaction(async (transaction) => {
|
|
2690
|
+
const db = transaction;
|
|
2691
|
+
const [current] = await db.select().from(subscribers).where((0, import_drizzle_orm16.eq)(subscribers.id, subscriberId)).for("update").limit(1);
|
|
2692
|
+
if (!current) return null;
|
|
2693
|
+
const delta = (0, import_core7.identityCorrectionDelta)(current, data.corrected);
|
|
2694
|
+
if (Object.keys(delta.corrected).length === 0) {
|
|
2695
|
+
return {
|
|
2696
|
+
subscriber: await this.withLiveTenant(db, current),
|
|
2697
|
+
correction: null
|
|
2698
|
+
};
|
|
2699
|
+
}
|
|
2700
|
+
const [updated] = await db.update(subscribers).set({
|
|
2701
|
+
...delta.corrected,
|
|
2702
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2703
|
+
}).where((0, import_drizzle_orm16.eq)(subscribers.id, subscriberId)).returning();
|
|
2704
|
+
const [correction] = await db.insert(subscriberCorrections).values({
|
|
2705
|
+
id: (0, import_node_crypto12.randomUUID)(),
|
|
2706
|
+
subscriberId,
|
|
2707
|
+
previous: delta.previous,
|
|
2708
|
+
corrected: delta.corrected,
|
|
2709
|
+
reason: data.reason,
|
|
2710
|
+
correctedBy: data.correctedBy,
|
|
2711
|
+
correctedAt: data.correctedAt
|
|
2712
|
+
}).returning();
|
|
2713
|
+
return {
|
|
2714
|
+
subscriber: await this.withLiveTenant(db, updated),
|
|
2715
|
+
correction: (0, import_core7.toSubscriberCorrectionRecord)(correction)
|
|
2716
|
+
};
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
async listCorrections(subscriberId) {
|
|
2720
|
+
const rows = await this.db.select().from(subscriberCorrections).where((0, import_drizzle_orm16.eq)(subscriberCorrections.subscriberId, subscriberId)).orderBy((0, import_drizzle_orm16.desc)(subscriberCorrections.correctedAt), (0, import_drizzle_orm16.desc)(subscriberCorrections.id));
|
|
2721
|
+
return rows.map(import_core7.toSubscriberCorrectionRecord);
|
|
2722
|
+
}
|
|
2723
|
+
async withLiveTenant(db, row) {
|
|
2724
|
+
const links = await db.select({
|
|
2725
|
+
tenantId: subscriberTenants.tenantId
|
|
2726
|
+
}).from(subscriberTenants).where((0, import_drizzle_orm16.and)((0, import_drizzle_orm16.eq)(subscriberTenants.subscriberId, row.id), (0, import_drizzle_orm16.isNull)(subscriberTenants.unlinkedAt))).limit(1);
|
|
2727
|
+
return (0, import_core7.toSubscriberRecord)(row, links[0]?.tenantId ?? null);
|
|
2728
|
+
}
|
|
2729
|
+
};
|
|
2730
|
+
DrizzleSubscriberRepository = _ts_decorate18([
|
|
2731
|
+
(0, import_common18.Injectable)(),
|
|
2732
|
+
_ts_param17(0, (0, import_common18.Inject)(DRIZZLE_DB_TOKEN)),
|
|
2733
|
+
_ts_metadata17("design:type", Function),
|
|
2734
|
+
_ts_metadata17("design:paramtypes", [
|
|
2735
|
+
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
2736
|
+
])
|
|
2737
|
+
], DrizzleSubscriberRepository);
|
|
2738
|
+
|
|
2739
|
+
// src/drizzle-subscription-usage.adapter.ts
|
|
2740
|
+
var import_common19 = require("@nestjs/common");
|
|
2741
|
+
var import_drizzle_orm17 = require("drizzle-orm");
|
|
2742
|
+
function _ts_decorate19(decorators, target, key, desc10) {
|
|
2743
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
2744
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
2745
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2746
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2747
|
+
}
|
|
2748
|
+
__name(_ts_decorate19, "_ts_decorate");
|
|
2749
|
+
function _ts_metadata18(k, v) {
|
|
2750
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2751
|
+
}
|
|
2752
|
+
__name(_ts_metadata18, "_ts_metadata");
|
|
2753
|
+
function _ts_param18(paramIndex, decorator) {
|
|
2754
|
+
return function(target, key) {
|
|
2755
|
+
decorator(target, key, paramIndex);
|
|
2756
|
+
};
|
|
2757
|
+
}
|
|
2758
|
+
__name(_ts_param18, "_ts_param");
|
|
2759
|
+
var DrizzleSubscriptionUsageAdapter = class {
|
|
2760
|
+
static {
|
|
2761
|
+
__name(this, "DrizzleSubscriptionUsageAdapter");
|
|
2762
|
+
}
|
|
2763
|
+
db;
|
|
2764
|
+
constructor(db) {
|
|
2765
|
+
this.db = db;
|
|
2766
|
+
}
|
|
2767
|
+
async findForTenant(tenantId) {
|
|
2768
|
+
const [subscription] = await this.db.select().from(subscriptions).where((0, import_drizzle_orm17.eq)(subscriptions.tenantId, tenantId)).limit(1);
|
|
2769
|
+
if (!subscription) return null;
|
|
2770
|
+
const [planVersion] = await this.db.select().from(planVersions).where((0, import_drizzle_orm17.eq)(planVersions.id, subscription.planVersionId)).limit(1);
|
|
2771
|
+
if (!planVersion) {
|
|
2772
|
+
throw new Error(`Subscription ${subscription.id} references missing PlanVersion ${subscription.planVersionId}.`);
|
|
2773
|
+
}
|
|
2774
|
+
const [pendingPlanVersion] = subscription.pendingPlanVersionId ? await this.db.select().from(planVersions).where((0, import_drizzle_orm17.eq)(planVersions.id, subscription.pendingPlanVersionId)).limit(1) : [];
|
|
2775
|
+
return {
|
|
2776
|
+
id: subscription.id,
|
|
2777
|
+
plan: planVersion.planId,
|
|
2778
|
+
billingCycle: subscription.billingCycle,
|
|
2779
|
+
status: subscription.status,
|
|
2780
|
+
isPilot: subscription.isPilot,
|
|
2781
|
+
pilotEndsAt: subscription.pilotEndsAt,
|
|
2782
|
+
trialEndsAt: subscription.trialEndsAt,
|
|
2783
|
+
startedAt: subscription.startedAt,
|
|
2784
|
+
currentPeriodStart: subscription.currentPeriodStart,
|
|
2785
|
+
currentPeriodEnd: subscription.currentPeriodEnd,
|
|
2786
|
+
minimumTermUntil: subscription.minimumTermUntil,
|
|
2787
|
+
canceledAt: subscription.canceledAt,
|
|
2788
|
+
canceledEffectiveAt: subscription.canceledEffectiveAt,
|
|
2789
|
+
billingAnchorDay: subscription.billingAnchorDay,
|
|
2790
|
+
pendingPlan: subscription.pendingPlan,
|
|
2791
|
+
pendingBillingCycle: subscription.pendingBillingCycle,
|
|
2792
|
+
pendingEffectiveAt: subscription.pendingEffectiveAt,
|
|
2793
|
+
planVersion: toUsagePlanVersion(planVersion),
|
|
2794
|
+
pendingPlanVersion: pendingPlanVersion ? {
|
|
2795
|
+
...toUsagePlanVersion(pendingPlanVersion),
|
|
2796
|
+
// The two fields a tenant is shown before accepting: does
|
|
2797
|
+
// the change take anything away, and what changed.
|
|
2798
|
+
nonRegressive: pendingPlanVersion.nonRegressive,
|
|
2799
|
+
publishedChanges: pendingPlanVersion.publishedChanges
|
|
2800
|
+
} : null,
|
|
2801
|
+
pendingPlanVersionEffectiveAt: subscription.pendingPlanVersionEffectiveAt,
|
|
2802
|
+
pendingPlanVersionAccepted: subscription.pendingPlanVersionAccepted,
|
|
2803
|
+
pendingPlanVersionAcceptedAt: subscription.pendingPlanVersionAcceptedAt,
|
|
2804
|
+
packageSnapshot: subscription.packageSnapshot,
|
|
2805
|
+
checkoutOfferId: subscription.checkoutOfferId
|
|
2806
|
+
};
|
|
2807
|
+
}
|
|
2808
|
+
};
|
|
2809
|
+
DrizzleSubscriptionUsageAdapter = _ts_decorate19([
|
|
2810
|
+
(0, import_common19.Injectable)(),
|
|
2811
|
+
_ts_param18(0, (0, import_common19.Inject)(DRIZZLE_DB_TOKEN)),
|
|
2812
|
+
_ts_metadata18("design:type", Function),
|
|
2813
|
+
_ts_metadata18("design:paramtypes", [
|
|
2814
|
+
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
2815
|
+
])
|
|
2816
|
+
], DrizzleSubscriptionUsageAdapter);
|
|
2817
|
+
function toUsagePlanVersion(row) {
|
|
2818
|
+
return {
|
|
2819
|
+
id: row.id,
|
|
2820
|
+
// The canonical schema stores the plan key here, which is what the
|
|
2821
|
+
// record's `planId` means too.
|
|
2822
|
+
planId: row.planId,
|
|
2823
|
+
version: row.version,
|
|
2824
|
+
publishedAt: row.publishedAt,
|
|
2825
|
+
supersededAt: row.supersededAt,
|
|
2826
|
+
changeNote: row.changeNote
|
|
2827
|
+
};
|
|
2828
|
+
}
|
|
2829
|
+
__name(toUsagePlanVersion, "toUsagePlanVersion");
|
|
2830
|
+
|
|
2831
|
+
// src/drizzle-tenant-subscription-write.adapter.ts
|
|
2832
|
+
var import_common20 = require("@nestjs/common");
|
|
2833
|
+
var import_drizzle_orm18 = require("drizzle-orm");
|
|
2834
|
+
function _ts_decorate20(decorators, target, key, desc10) {
|
|
2835
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
2836
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
2837
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2838
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2839
|
+
}
|
|
2840
|
+
__name(_ts_decorate20, "_ts_decorate");
|
|
2841
|
+
function _ts_metadata19(k, v) {
|
|
2842
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2843
|
+
}
|
|
2844
|
+
__name(_ts_metadata19, "_ts_metadata");
|
|
2845
|
+
function _ts_param19(paramIndex, decorator) {
|
|
2846
|
+
return function(target, key) {
|
|
2847
|
+
decorator(target, key, paramIndex);
|
|
2848
|
+
};
|
|
2849
|
+
}
|
|
2850
|
+
__name(_ts_param19, "_ts_param");
|
|
2851
|
+
var DrizzleTenantSubscriptionWrite = class {
|
|
2852
|
+
static {
|
|
2853
|
+
__name(this, "DrizzleTenantSubscriptionWrite");
|
|
2854
|
+
}
|
|
2855
|
+
db;
|
|
2856
|
+
/** Both plan-changing writes bind the version they sell; there is no opt-out here. */
|
|
2857
|
+
bindsPlanVersion = true;
|
|
2858
|
+
plans;
|
|
2859
|
+
constructor(db) {
|
|
2860
|
+
this.db = db;
|
|
2861
|
+
this.plans = new DrizzlePlanRepository(this.db, {
|
|
2862
|
+
validityWindows: true
|
|
2863
|
+
});
|
|
2864
|
+
}
|
|
2865
|
+
async changePlanImmediate(tenantId, input) {
|
|
2866
|
+
return this.db.transaction(async (tx) => {
|
|
2867
|
+
const db = tx;
|
|
2868
|
+
const current = await this.requireSubscription(db, tenantId, {
|
|
2869
|
+
lock: true
|
|
2870
|
+
});
|
|
2871
|
+
const planVersionId = await this.activeVersionId(input.planId, input.periodStart ?? /* @__PURE__ */ new Date(), tx);
|
|
2872
|
+
const pendingMovedAway = await this.pendingVersionBelongsToAnotherPlan(tx, current.pendingPlanVersionId, input.planId);
|
|
2873
|
+
const claimed = await this.claim(db, tenantId, input.expectedCanceledAt, {
|
|
2874
|
+
plan: input.planId,
|
|
2875
|
+
billingCycle: input.cycle,
|
|
2876
|
+
planVersionId,
|
|
2877
|
+
pendingPlan: null,
|
|
2878
|
+
pendingBillingCycle: null,
|
|
2879
|
+
pendingEffectiveAt: null,
|
|
2880
|
+
...input.nextStatus ? {
|
|
2881
|
+
status: input.nextStatus
|
|
2882
|
+
} : {},
|
|
2883
|
+
...periodFields(input.periodStart, input.periodEnd),
|
|
2884
|
+
// Null and undefined both mean "leave the trial as it is" — the
|
|
2885
|
+
// platform passes a carried-over end date or nothing at all.
|
|
2886
|
+
...input.trialEndsAt ? {
|
|
2887
|
+
trialEndsAt: input.trialEndsAt
|
|
2888
|
+
} : {},
|
|
2889
|
+
...pendingMovedAway ? clearedPendingVersion() : {}
|
|
2890
|
+
});
|
|
2891
|
+
const after = await this.requireSubscription(db, tenantId);
|
|
2892
|
+
return {
|
|
2893
|
+
plan: after.plan,
|
|
2894
|
+
billingCycle: after.billingCycle,
|
|
2895
|
+
claimed: claimed > 0
|
|
2896
|
+
};
|
|
2897
|
+
});
|
|
2898
|
+
}
|
|
2899
|
+
async schedulePlanChange(tenantId, input) {
|
|
2900
|
+
const claimed = await this.claim(this.db, tenantId, input.expectedCanceledAt, {
|
|
2901
|
+
pendingPlan: input.pendingPlan,
|
|
2902
|
+
pendingBillingCycle: input.pendingBillingCycle,
|
|
2903
|
+
pendingEffectiveAt: input.pendingEffectiveAt
|
|
2904
|
+
});
|
|
2905
|
+
return {
|
|
2906
|
+
claimed: claimed > 0
|
|
2907
|
+
};
|
|
2908
|
+
}
|
|
2909
|
+
async acceptPendingPlanVersion(tenantId, userId, now) {
|
|
2910
|
+
const sub = await this.requireSubscription(this.db, tenantId);
|
|
2911
|
+
if (!sub.pendingPlanVersionId) {
|
|
2912
|
+
throw new Error(`No pending PlanVersion for tenant ${tenantId}.`);
|
|
875
2913
|
}
|
|
876
|
-
|
|
2914
|
+
const pendingPlanVersionId = sub.pendingPlanVersionId;
|
|
2915
|
+
const claimed = await this.db.update(subscriptions).set({
|
|
2916
|
+
pendingPlanVersionAccepted: true,
|
|
2917
|
+
pendingPlanVersionAcceptedAt: now,
|
|
2918
|
+
pendingPlanVersionAcceptedByUserId: userId,
|
|
2919
|
+
updatedAt: now
|
|
2920
|
+
}).where((0, import_drizzle_orm18.and)((0, import_drizzle_orm18.eq)(subscriptions.id, sub.id), (0, import_drizzle_orm18.eq)(subscriptions.pendingPlanVersionId, pendingPlanVersionId), (0, import_drizzle_orm18.eq)(subscriptions.pendingPlanVersionAccepted, false))).returning({
|
|
2921
|
+
id: subscriptions.id
|
|
2922
|
+
});
|
|
2923
|
+
const updated = await this.requireSubscription(this.db, tenantId);
|
|
2924
|
+
if (claimed.length === 0 && (updated.pendingPlanVersionId !== pendingPlanVersionId || !updated.pendingPlanVersionAccepted)) {
|
|
2925
|
+
throw new Error(`Pending PlanVersion changed while accepting it for tenant ${tenantId}.`);
|
|
2926
|
+
}
|
|
2927
|
+
return {
|
|
2928
|
+
accepted: true,
|
|
2929
|
+
acceptedAt: updated.pendingPlanVersionAcceptedAt,
|
|
2930
|
+
effectiveAt: updated.pendingPlanVersionEffectiveAt,
|
|
2931
|
+
alreadyAccepted: claimed.length === 0
|
|
2932
|
+
};
|
|
877
2933
|
}
|
|
878
|
-
async
|
|
879
|
-
const
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
2934
|
+
async cancelSubscription(tenantId, input) {
|
|
2935
|
+
const now = /* @__PURE__ */ new Date();
|
|
2936
|
+
const claimed = await this.db.update(subscriptions).set({
|
|
2937
|
+
canceledAt: input.canceledAt,
|
|
2938
|
+
canceledEffectiveAt: input.effectiveAt,
|
|
2939
|
+
// Absent means unchanged rather than null: an ordinary
|
|
2940
|
+
// cancellation does not touch the commitment it was measured
|
|
2941
|
+
// against, and writing the field on every call would erase a
|
|
2942
|
+
// term that is still running.
|
|
2943
|
+
...input.minimumTermUntil ? {
|
|
2944
|
+
minimumTermUntil: input.minimumTermUntil
|
|
2945
|
+
} : {},
|
|
2946
|
+
// Written only when the cancellation is already effective.
|
|
2947
|
+
// Restating the status this call read would undo whatever
|
|
2948
|
+
// changed it in between — a trial going live between the read
|
|
2949
|
+
// and the write came back as `TRIAL`, entitlements and all.
|
|
2950
|
+
// An ordinary cancellation records dates; it has no opinion
|
|
2951
|
+
// about the status.
|
|
2952
|
+
...input.terminateNow ? {
|
|
2953
|
+
status: "CANCELED"
|
|
2954
|
+
} : {},
|
|
2955
|
+
updatedAt: now
|
|
2956
|
+
}).where((0, import_drizzle_orm18.and)((0, import_drizzle_orm18.eq)(subscriptions.tenantId, tenantId), (0, import_drizzle_orm18.isNull)(subscriptions.canceledAt), (0, import_drizzle_orm18.isNull)(subscriptions.canceledEffectiveAt))).returning({
|
|
2957
|
+
id: subscriptions.id
|
|
2958
|
+
});
|
|
2959
|
+
const current = await this.requireSubscription(this.db, tenantId);
|
|
2960
|
+
return {
|
|
2961
|
+
canceledAt: current.canceledAt ?? null,
|
|
2962
|
+
canceledEffectiveAt: current.canceledEffectiveAt ?? null,
|
|
2963
|
+
status: current.status,
|
|
2964
|
+
alreadyCanceled: claimed.length === 0
|
|
2965
|
+
};
|
|
883
2966
|
}
|
|
884
|
-
async
|
|
885
|
-
|
|
886
|
-
|
|
2967
|
+
async applyOnboardingSelection(tenantId, input, redeemPromo) {
|
|
2968
|
+
return this.db.transaction(async (tx) => {
|
|
2969
|
+
const db = tx;
|
|
2970
|
+
const planVersionId = await this.activeVersionId(input.planId, input.periodStart ?? /* @__PURE__ */ new Date(), tx);
|
|
2971
|
+
const claimed = await this.claim(db, tenantId, input.expectedCanceledAt, {
|
|
2972
|
+
plan: input.planId,
|
|
2973
|
+
billingCycle: input.cycle,
|
|
2974
|
+
planVersionId,
|
|
2975
|
+
pendingPlan: null,
|
|
2976
|
+
pendingBillingCycle: null,
|
|
2977
|
+
pendingEffectiveAt: null,
|
|
2978
|
+
...clearedPendingVersion(),
|
|
2979
|
+
...input.nextStatus ? {
|
|
2980
|
+
status: input.nextStatus
|
|
2981
|
+
} : {},
|
|
2982
|
+
...periodFields(input.periodStart, input.periodEnd)
|
|
2983
|
+
});
|
|
2984
|
+
const updated = await this.requireSubscription(db, tenantId);
|
|
2985
|
+
if (claimed === 0) {
|
|
2986
|
+
return {
|
|
2987
|
+
plan: updated.plan,
|
|
2988
|
+
billingCycle: updated.billingCycle,
|
|
2989
|
+
subscriptionId: updated.id,
|
|
2990
|
+
promoRedemption: null,
|
|
2991
|
+
claimed: false
|
|
2992
|
+
};
|
|
2993
|
+
}
|
|
2994
|
+
let promoRedemption = null;
|
|
2995
|
+
if (redeemPromo) {
|
|
2996
|
+
promoRedemption = await redeemPromo(tx, updated.id);
|
|
2997
|
+
}
|
|
2998
|
+
return {
|
|
2999
|
+
plan: updated.plan,
|
|
3000
|
+
billingCycle: updated.billingCycle,
|
|
3001
|
+
subscriptionId: updated.id,
|
|
3002
|
+
promoRedemption,
|
|
3003
|
+
claimed: true
|
|
3004
|
+
};
|
|
3005
|
+
});
|
|
887
3006
|
}
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
3007
|
+
/**
|
|
3008
|
+
* One `UPDATE` naming the cancellation state the caller decided against,
|
|
3009
|
+
* answering with how many rows it took. Reading first and updating by
|
|
3010
|
+
* tenant alone would leave the window this closes.
|
|
3011
|
+
*/
|
|
3012
|
+
async claim(db, tenantId, expectedCanceledAt, values) {
|
|
3013
|
+
const claimed = await db.update(subscriptions).set({
|
|
3014
|
+
...values,
|
|
3015
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
3016
|
+
}).where((0, import_drizzle_orm18.and)((0, import_drizzle_orm18.eq)(subscriptions.tenantId, tenantId), canceledAtIs(expectedCanceledAt))).returning({
|
|
3017
|
+
id: subscriptions.id
|
|
893
3018
|
});
|
|
894
|
-
return
|
|
3019
|
+
return claimed.length;
|
|
3020
|
+
}
|
|
3021
|
+
/**
|
|
3022
|
+
* `lock: true` takes the row for the rest of the transaction, so a caller
|
|
3023
|
+
* deciding from what it read cannot have the ground move underneath it. It
|
|
3024
|
+
* is only meaningful inside a transaction — outside one the lock is
|
|
3025
|
+
* released with the statement.
|
|
3026
|
+
*/
|
|
3027
|
+
async requireSubscription(db, tenantId, options = {}) {
|
|
3028
|
+
const query = db.select().from(subscriptions).where((0, import_drizzle_orm18.eq)(subscriptions.tenantId, tenantId));
|
|
3029
|
+
const rows = await (options.lock ? query.for("update") : query).limit(1);
|
|
3030
|
+
if (!rows[0]) throw new Error(`No subscription for tenant ${tenantId}.`);
|
|
3031
|
+
return rows[0];
|
|
3032
|
+
}
|
|
3033
|
+
async activeVersionId(planKey, asOf, tx) {
|
|
3034
|
+
const active = await this.plans.findActivePlanVersion?.(planKey, asOf, tx);
|
|
3035
|
+
if (!active) throw new Error(`No active PlanVersion for plan '${planKey}'.`);
|
|
3036
|
+
return active.id;
|
|
3037
|
+
}
|
|
3038
|
+
/**
|
|
3039
|
+
* On the caller's transaction, not beside it. The change already holds a
|
|
3040
|
+
* connection for its whole length; asking for a second one waits for the
|
|
3041
|
+
* connection this transaction is holding, and a one-connection pool then
|
|
3042
|
+
* never gets past here.
|
|
3043
|
+
*/
|
|
3044
|
+
async pendingVersionBelongsToAnotherPlan(tx, pendingPlanVersionId, targetPlanKey) {
|
|
3045
|
+
if (!pendingPlanVersionId) return false;
|
|
3046
|
+
const pending = await this.plans.findVersionById(pendingPlanVersionId, tx);
|
|
3047
|
+
return !pending || pending.planId !== targetPlanKey;
|
|
895
3048
|
}
|
|
896
3049
|
};
|
|
897
|
-
|
|
898
|
-
(0,
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
3050
|
+
DrizzleTenantSubscriptionWrite = _ts_decorate20([
|
|
3051
|
+
(0, import_common20.Injectable)(),
|
|
3052
|
+
_ts_param19(0, (0, import_common20.Inject)(DRIZZLE_DB_TOKEN)),
|
|
3053
|
+
_ts_metadata19("design:type", Function),
|
|
3054
|
+
_ts_metadata19("design:paramtypes", [
|
|
902
3055
|
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
903
3056
|
])
|
|
904
|
-
],
|
|
905
|
-
function
|
|
3057
|
+
], DrizzleTenantSubscriptionWrite);
|
|
3058
|
+
function periodFields(periodStart, periodEnd) {
|
|
3059
|
+
if (!periodStart || !periodEnd) return {};
|
|
906
3060
|
return {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
tenantId: row.tenantId,
|
|
911
|
-
appliedValueType: row.appliedValueType,
|
|
912
|
-
appliedValue: String(row.appliedValue),
|
|
913
|
-
appliedDurationType: row.appliedDurationType,
|
|
914
|
-
appliedDurationValue: row.appliedDurationValue,
|
|
915
|
-
startsAt: row.startsAt,
|
|
916
|
-
endsAt: row.endsAt,
|
|
917
|
-
status: row.status,
|
|
918
|
-
redeemedAt: row.redeemedAt,
|
|
919
|
-
reversedAt: row.reversedAt
|
|
3061
|
+
currentPeriodStart: periodStart,
|
|
3062
|
+
currentPeriodEnd: periodEnd,
|
|
3063
|
+
billingAnchorDay: periodStart.getUTCDate()
|
|
920
3064
|
};
|
|
921
3065
|
}
|
|
922
|
-
__name(
|
|
3066
|
+
__name(periodFields, "periodFields");
|
|
3067
|
+
function clearedPendingVersion() {
|
|
3068
|
+
return {
|
|
3069
|
+
pendingPlanVersionId: null,
|
|
3070
|
+
pendingPlanVersionEffectiveAt: null,
|
|
3071
|
+
pendingPlanVersionAccepted: false,
|
|
3072
|
+
pendingPlanVersionAcceptedAt: null,
|
|
3073
|
+
pendingPlanVersionAcceptedByUserId: null,
|
|
3074
|
+
pendingPlanVersionNotifiedAt: null,
|
|
3075
|
+
pendingPlanVersionReminderSentAt: null
|
|
3076
|
+
};
|
|
3077
|
+
}
|
|
3078
|
+
__name(clearedPendingVersion, "clearedPendingVersion");
|
|
3079
|
+
function canceledAtIs(expected) {
|
|
3080
|
+
return expected === null ? (0, import_drizzle_orm18.isNull)(subscriptions.canceledAt) : (0, import_drizzle_orm18.eq)(subscriptions.canceledAt, expected);
|
|
3081
|
+
}
|
|
3082
|
+
__name(canceledAtIs, "canceledAtIs");
|
|
923
3083
|
|
|
924
|
-
// src/drizzle-
|
|
925
|
-
var
|
|
926
|
-
var
|
|
927
|
-
var
|
|
928
|
-
function
|
|
929
|
-
var c = arguments.length, r = c < 3 ? target :
|
|
930
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key,
|
|
3084
|
+
// src/drizzle-subscription-bundle.repository.ts
|
|
3085
|
+
var import_node_crypto13 = require("node:crypto");
|
|
3086
|
+
var import_common21 = require("@nestjs/common");
|
|
3087
|
+
var import_drizzle_orm19 = require("drizzle-orm");
|
|
3088
|
+
function _ts_decorate21(decorators, target, key, desc10) {
|
|
3089
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
3090
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
931
3091
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
932
3092
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
933
3093
|
}
|
|
934
|
-
__name(
|
|
935
|
-
function
|
|
3094
|
+
__name(_ts_decorate21, "_ts_decorate");
|
|
3095
|
+
function _ts_metadata20(k, v) {
|
|
936
3096
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
937
3097
|
}
|
|
938
|
-
__name(
|
|
939
|
-
function
|
|
3098
|
+
__name(_ts_metadata20, "_ts_metadata");
|
|
3099
|
+
function _ts_param20(paramIndex, decorator) {
|
|
940
3100
|
return function(target, key) {
|
|
941
3101
|
decorator(target, key, paramIndex);
|
|
942
3102
|
};
|
|
943
3103
|
}
|
|
944
|
-
__name(
|
|
945
|
-
var
|
|
3104
|
+
__name(_ts_param20, "_ts_param");
|
|
3105
|
+
var DrizzleSubscriptionBundleRepository = class {
|
|
946
3106
|
static {
|
|
947
|
-
__name(this, "
|
|
3107
|
+
__name(this, "DrizzleSubscriptionBundleRepository");
|
|
948
3108
|
}
|
|
949
3109
|
db;
|
|
950
3110
|
constructor(db) {
|
|
951
3111
|
this.db = db;
|
|
952
3112
|
}
|
|
953
|
-
async
|
|
954
|
-
const rows = await this.db.select().from(
|
|
955
|
-
|
|
956
|
-
return row ? toRecord2(row) : null;
|
|
3113
|
+
async listBySubscription(subscriptionId) {
|
|
3114
|
+
const rows = await this.db.select().from(subscriptionBundles).where((0, import_drizzle_orm19.eq)(subscriptionBundles.subscriptionId, subscriptionId)).orderBy((0, import_drizzle_orm19.desc)(subscriptionBundles.startedAt));
|
|
3115
|
+
return rows.map(toRecord3);
|
|
957
3116
|
}
|
|
958
|
-
async
|
|
959
|
-
const
|
|
960
|
-
|
|
961
|
-
const row = rows[0];
|
|
962
|
-
if (!row || row.deletedAt) return null;
|
|
963
|
-
return toRecord2(row);
|
|
3117
|
+
async findById(subscriptionBundleId) {
|
|
3118
|
+
const rows = await this.db.select().from(subscriptionBundles).where((0, import_drizzle_orm19.eq)(subscriptionBundles.id, subscriptionBundleId)).limit(1);
|
|
3119
|
+
return rows[0] ? toRecord3(rows[0]) : null;
|
|
964
3120
|
}
|
|
965
|
-
async
|
|
966
|
-
const
|
|
967
|
-
|
|
968
|
-
filter.status ? (0, import_drizzle_orm8.eq)(promoCodes.status, filter.status) : void 0,
|
|
969
|
-
filter.campaignTag ? (0, import_drizzle_orm8.eq)(promoCodes.campaignTag, filter.campaignTag) : void 0,
|
|
970
|
-
filter.search ? (0, import_drizzle_orm8.like)(promoCodes.code, `%${escapeLikePattern(normalizeCode(filter.search))}%`) : void 0
|
|
971
|
-
];
|
|
972
|
-
const rows = await this.db.select().from(promoCodes).where((0, import_drizzle_orm8.and)(...conditions)).orderBy(import_drizzle_orm8.sql`${promoCodes.createdAt} DESC`);
|
|
973
|
-
return rows.map(toRecord2);
|
|
3121
|
+
async listActiveBySubscription(subscriptionId, asOf = /* @__PURE__ */ new Date(), tx) {
|
|
3122
|
+
const rows = await resolveDb(this.db, tx).select().from(subscriptionBundles).where((0, import_drizzle_orm19.and)((0, import_drizzle_orm19.eq)(subscriptionBundles.subscriptionId, subscriptionId), stillActive(asOf)));
|
|
3123
|
+
return rows.map(toRecord3);
|
|
974
3124
|
}
|
|
975
|
-
async
|
|
976
|
-
const
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
minimumPlanAmountGross: data.minimumPlanAmountGross?.toFixed(2) ?? null,
|
|
990
|
-
allowZeroInvoice: data.allowZeroInvoice ?? false,
|
|
991
|
-
description: data.description ?? null,
|
|
992
|
-
campaignTag: data.campaignTag ?? null,
|
|
993
|
-
revenueDeductionAccount: data.revenueDeductionAccount ?? null,
|
|
994
|
-
createdById: data.createdById,
|
|
995
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
3125
|
+
async add(data) {
|
|
3126
|
+
const now = /* @__PURE__ */ new Date();
|
|
3127
|
+
const rows = await this.db.insert(subscriptionBundles).values({
|
|
3128
|
+
id: (0, import_node_crypto13.randomUUID)(),
|
|
3129
|
+
subscriptionId: data.subscriptionId,
|
|
3130
|
+
bundleVersionId: data.bundleVersionId,
|
|
3131
|
+
startedAt: data.startedAt ?? now,
|
|
3132
|
+
minimumTermEndsAt: data.minimumTermEndsAt ?? null,
|
|
3133
|
+
billingCycle: data.billingCycle ?? null,
|
|
3134
|
+
currentPeriodStart: data.currentPeriodStart ?? null,
|
|
3135
|
+
currentPeriodEnd: data.currentPeriodEnd ?? null,
|
|
3136
|
+
canceledAt: null,
|
|
3137
|
+
canceledEffectiveAt: null,
|
|
3138
|
+
updatedAt: now
|
|
996
3139
|
}).returning();
|
|
997
|
-
return
|
|
3140
|
+
return toRecord3(rows[0]);
|
|
998
3141
|
}
|
|
999
|
-
async
|
|
1000
|
-
const rows = await this.db.update(
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
validUntil: data.validUntil,
|
|
1004
|
-
maxRedemptions: data.maxRedemptions,
|
|
3142
|
+
async cancel(subscriptionBundleId, data) {
|
|
3143
|
+
const rows = await this.db.update(subscriptionBundles).set({
|
|
3144
|
+
canceledAt: data.canceledAt,
|
|
3145
|
+
canceledEffectiveAt: data.canceledEffectiveAt,
|
|
1005
3146
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1006
|
-
}).where((0,
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
throw new Error(`PromoCode ${id} not found.`);
|
|
3147
|
+
}).where((0, import_drizzle_orm19.and)((0, import_drizzle_orm19.eq)(subscriptionBundles.id, subscriptionBundleId), (0, import_drizzle_orm19.isNull)(subscriptionBundles.canceledAt))).returning();
|
|
3148
|
+
if (!rows[0]) {
|
|
3149
|
+
throw new Error(`SubscriptionBundle '${subscriptionBundleId}' not found or already cancelled`);
|
|
1010
3150
|
}
|
|
1011
|
-
return
|
|
1012
|
-
}
|
|
1013
|
-
async softDelete(id) {
|
|
1014
|
-
await this.db.update(promoCodes).set({
|
|
1015
|
-
deletedAt: /* @__PURE__ */ new Date(),
|
|
1016
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
1017
|
-
}).where((0, import_drizzle_orm8.eq)(promoCodes.id, id));
|
|
1018
|
-
}
|
|
1019
|
-
async claimSlot(id, tx) {
|
|
1020
|
-
const db = resolveDb(this.db, tx);
|
|
1021
|
-
const claimed = await db.update(promoCodes).set({
|
|
1022
|
-
redemptionsCount: import_drizzle_orm8.sql`${promoCodes.redemptionsCount} + 1`,
|
|
1023
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
1024
|
-
}).where((0, import_drizzle_orm8.and)((0, import_drizzle_orm8.eq)(promoCodes.id, id), (0, import_drizzle_orm8.eq)(promoCodes.status, "ACTIVE"), (0, import_drizzle_orm8.isNull)(promoCodes.deletedAt), (0, import_drizzle_orm8.or)((0, import_drizzle_orm8.isNull)(promoCodes.maxRedemptions), (0, import_drizzle_orm8.lt)(promoCodes.redemptionsCount, promoCodes.maxRedemptions)))).returning({
|
|
1025
|
-
id: promoCodes.id
|
|
1026
|
-
});
|
|
1027
|
-
return claimed.length === 1;
|
|
1028
|
-
}
|
|
1029
|
-
async markExhaustedIfFull(id, tx) {
|
|
1030
|
-
const db = resolveDb(this.db, tx);
|
|
1031
|
-
await db.update(promoCodes).set({
|
|
1032
|
-
status: "EXHAUSTED",
|
|
1033
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
1034
|
-
}).where((0, import_drizzle_orm8.and)((0, import_drizzle_orm8.eq)(promoCodes.id, id), (0, import_drizzle_orm8.eq)(promoCodes.status, "ACTIVE"), (0, import_drizzle_orm8.isNotNull)(promoCodes.maxRedemptions), (0, import_drizzle_orm8.gte)(promoCodes.redemptionsCount, promoCodes.maxRedemptions)));
|
|
3151
|
+
return toRecord3(rows[0]);
|
|
1035
3152
|
}
|
|
1036
|
-
async
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
status: import_drizzle_orm8.sql`CASE WHEN ${promoCodes.status} = 'EXHAUSTED' THEN 'ACTIVE' ELSE ${promoCodes.status} END`,
|
|
3153
|
+
async reactivate(subscriptionBundleId) {
|
|
3154
|
+
const rows = await this.db.update(subscriptionBundles).set({
|
|
3155
|
+
canceledAt: null,
|
|
3156
|
+
canceledEffectiveAt: null,
|
|
1041
3157
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1042
|
-
}).where((0,
|
|
3158
|
+
}).where((0, import_drizzle_orm19.eq)(subscriptionBundles.id, subscriptionBundleId)).returning();
|
|
3159
|
+
if (!rows[0]) throw new Error(`SubscriptionBundle '${subscriptionBundleId}' not found`);
|
|
3160
|
+
return toRecord3(rows[0]);
|
|
1043
3161
|
}
|
|
1044
|
-
async
|
|
1045
|
-
const
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
"ACTIVE",
|
|
1050
|
-
"PAUSED"
|
|
1051
|
-
]), (0, import_drizzle_orm8.lt)(promoCodes.validUntil, now))).returning({
|
|
1052
|
-
id: promoCodes.id
|
|
1053
|
-
});
|
|
1054
|
-
return expired.length;
|
|
3162
|
+
async countActiveByBundleVersionId(bundleVersionId, asOf = /* @__PURE__ */ new Date()) {
|
|
3163
|
+
const rows = await this.db.select({
|
|
3164
|
+
id: subscriptionBundles.id
|
|
3165
|
+
}).from(subscriptionBundles).where((0, import_drizzle_orm19.and)((0, import_drizzle_orm19.eq)(subscriptionBundles.bundleVersionId, bundleVersionId), stillActive(asOf)));
|
|
3166
|
+
return rows.length;
|
|
1055
3167
|
}
|
|
1056
3168
|
};
|
|
1057
|
-
|
|
1058
|
-
(0,
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
3169
|
+
DrizzleSubscriptionBundleRepository = _ts_decorate21([
|
|
3170
|
+
(0, import_common21.Injectable)(),
|
|
3171
|
+
_ts_param20(0, (0, import_common21.Inject)(DRIZZLE_DB_TOKEN)),
|
|
3172
|
+
_ts_metadata20("design:type", Function),
|
|
3173
|
+
_ts_metadata20("design:paramtypes", [
|
|
1062
3174
|
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
1063
3175
|
])
|
|
1064
|
-
],
|
|
1065
|
-
function
|
|
1066
|
-
return
|
|
3176
|
+
], DrizzleSubscriptionBundleRepository);
|
|
3177
|
+
function stillActive(asOf) {
|
|
3178
|
+
return (0, import_drizzle_orm19.or)((0, import_drizzle_orm19.isNull)(subscriptionBundles.canceledAt), (0, import_drizzle_orm19.gt)(subscriptionBundles.canceledEffectiveAt, asOf));
|
|
1067
3179
|
}
|
|
1068
|
-
__name(
|
|
1069
|
-
function
|
|
3180
|
+
__name(stillActive, "stillActive");
|
|
3181
|
+
function toRecord3(row) {
|
|
1070
3182
|
return {
|
|
1071
3183
|
id: row.id,
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
appliesToPlans: row.appliesToPlans ?? [],
|
|
1082
|
-
appliesToBilling: row.appliesToBilling ?? null,
|
|
1083
|
-
firstTimeCustomersOnly: row.firstTimeCustomersOnly,
|
|
1084
|
-
minimumPlanAmountGross: row.minimumPlanAmountGross === null ? null : String(row.minimumPlanAmountGross),
|
|
1085
|
-
allowZeroInvoice: row.allowZeroInvoice,
|
|
1086
|
-
status: row.status,
|
|
1087
|
-
description: row.description,
|
|
1088
|
-
campaignTag: row.campaignTag,
|
|
1089
|
-
revenueDeductionAccount: row.revenueDeductionAccount,
|
|
1090
|
-
createdById: row.createdById,
|
|
3184
|
+
subscriptionId: row.subscriptionId,
|
|
3185
|
+
bundleVersionId: row.bundleVersionId,
|
|
3186
|
+
startedAt: row.startedAt,
|
|
3187
|
+
minimumTermEndsAt: row.minimumTermEndsAt,
|
|
3188
|
+
billingCycle: row.billingCycle,
|
|
3189
|
+
currentPeriodStart: row.currentPeriodStart,
|
|
3190
|
+
currentPeriodEnd: row.currentPeriodEnd,
|
|
3191
|
+
canceledAt: row.canceledAt,
|
|
3192
|
+
canceledEffectiveAt: row.canceledEffectiveAt,
|
|
1091
3193
|
createdAt: row.createdAt,
|
|
1092
|
-
updatedAt: row.updatedAt
|
|
1093
|
-
deletedAt: row.deletedAt
|
|
3194
|
+
updatedAt: row.updatedAt
|
|
1094
3195
|
};
|
|
1095
3196
|
}
|
|
1096
|
-
__name(
|
|
3197
|
+
__name(toRecord3, "toRecord");
|
|
1097
3198
|
|
|
1098
3199
|
// src/drizzle-promo-code-validation-log.repository.ts
|
|
1099
|
-
var
|
|
1100
|
-
var
|
|
1101
|
-
var
|
|
1102
|
-
function
|
|
1103
|
-
var c = arguments.length, r = c < 3 ? target :
|
|
1104
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key,
|
|
3200
|
+
var import_node_crypto14 = require("node:crypto");
|
|
3201
|
+
var import_common22 = require("@nestjs/common");
|
|
3202
|
+
var import_drizzle_orm20 = require("drizzle-orm");
|
|
3203
|
+
function _ts_decorate22(decorators, target, key, desc10) {
|
|
3204
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
3205
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
1105
3206
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1106
3207
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1107
3208
|
}
|
|
1108
|
-
__name(
|
|
1109
|
-
function
|
|
3209
|
+
__name(_ts_decorate22, "_ts_decorate");
|
|
3210
|
+
function _ts_metadata21(k, v) {
|
|
1110
3211
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1111
3212
|
}
|
|
1112
|
-
__name(
|
|
1113
|
-
function
|
|
3213
|
+
__name(_ts_metadata21, "_ts_metadata");
|
|
3214
|
+
function _ts_param21(paramIndex, decorator) {
|
|
1114
3215
|
return function(target, key) {
|
|
1115
3216
|
decorator(target, key, paramIndex);
|
|
1116
3217
|
};
|
|
1117
3218
|
}
|
|
1118
|
-
__name(
|
|
3219
|
+
__name(_ts_param21, "_ts_param");
|
|
1119
3220
|
var DrizzlePromoCodeValidationLogRepository = class {
|
|
1120
3221
|
static {
|
|
1121
3222
|
__name(this, "DrizzlePromoCodeValidationLogRepository");
|
|
@@ -1126,7 +3227,7 @@ var DrizzlePromoCodeValidationLogRepository = class {
|
|
|
1126
3227
|
}
|
|
1127
3228
|
async log(args) {
|
|
1128
3229
|
await this.db.insert(promoCodeValidationLogs).values({
|
|
1129
|
-
id: (0,
|
|
3230
|
+
id: (0, import_node_crypto14.randomUUID)(),
|
|
1130
3231
|
promoCodeId: args.promoCodeId,
|
|
1131
3232
|
codeAttempt: args.codeAttempt.trim().toUpperCase(),
|
|
1132
3233
|
result: args.result,
|
|
@@ -1137,39 +3238,39 @@ var DrizzlePromoCodeValidationLogRepository = class {
|
|
|
1137
3238
|
async countValid(promoCodeId) {
|
|
1138
3239
|
const rows = await this.db.select({
|
|
1139
3240
|
id: promoCodeValidationLogs.id
|
|
1140
|
-
}).from(promoCodeValidationLogs).where((0,
|
|
3241
|
+
}).from(promoCodeValidationLogs).where((0, import_drizzle_orm20.and)((0, import_drizzle_orm20.eq)(promoCodeValidationLogs.promoCodeId, promoCodeId), (0, import_drizzle_orm20.eq)(promoCodeValidationLogs.result, "VALID")));
|
|
1141
3242
|
return rows.length;
|
|
1142
3243
|
}
|
|
1143
3244
|
};
|
|
1144
|
-
DrizzlePromoCodeValidationLogRepository =
|
|
1145
|
-
(0,
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
3245
|
+
DrizzlePromoCodeValidationLogRepository = _ts_decorate22([
|
|
3246
|
+
(0, import_common22.Injectable)(),
|
|
3247
|
+
_ts_param21(0, (0, import_common22.Inject)(DRIZZLE_DB_TOKEN)),
|
|
3248
|
+
_ts_metadata21("design:type", Function),
|
|
3249
|
+
_ts_metadata21("design:paramtypes", [
|
|
1149
3250
|
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
1150
3251
|
])
|
|
1151
3252
|
], DrizzlePromoCodeValidationLogRepository);
|
|
1152
3253
|
|
|
1153
3254
|
// src/drizzle-promo-subscription-lookup.adapter.ts
|
|
1154
|
-
var
|
|
1155
|
-
var
|
|
1156
|
-
function
|
|
1157
|
-
var c = arguments.length, r = c < 3 ? target :
|
|
1158
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key,
|
|
3255
|
+
var import_common23 = require("@nestjs/common");
|
|
3256
|
+
var import_drizzle_orm21 = require("drizzle-orm");
|
|
3257
|
+
function _ts_decorate23(decorators, target, key, desc10) {
|
|
3258
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
3259
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
1159
3260
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1160
3261
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1161
3262
|
}
|
|
1162
|
-
__name(
|
|
1163
|
-
function
|
|
3263
|
+
__name(_ts_decorate23, "_ts_decorate");
|
|
3264
|
+
function _ts_metadata22(k, v) {
|
|
1164
3265
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1165
3266
|
}
|
|
1166
|
-
__name(
|
|
1167
|
-
function
|
|
3267
|
+
__name(_ts_metadata22, "_ts_metadata");
|
|
3268
|
+
function _ts_param22(paramIndex, decorator) {
|
|
1168
3269
|
return function(target, key) {
|
|
1169
3270
|
decorator(target, key, paramIndex);
|
|
1170
3271
|
};
|
|
1171
3272
|
}
|
|
1172
|
-
__name(
|
|
3273
|
+
__name(_ts_param22, "_ts_param");
|
|
1173
3274
|
var DrizzlePromoSubscriptionLookup = class {
|
|
1174
3275
|
static {
|
|
1175
3276
|
__name(this, "DrizzlePromoSubscriptionLookup");
|
|
@@ -1186,7 +3287,7 @@ var DrizzlePromoSubscriptionLookup = class {
|
|
|
1186
3287
|
plan: subscriptions.plan,
|
|
1187
3288
|
billingCycle: subscriptions.billingCycle,
|
|
1188
3289
|
startedAt: subscriptions.startedAt
|
|
1189
|
-
}).from(subscriptions).where((0,
|
|
3290
|
+
}).from(subscriptions).where((0, import_drizzle_orm21.eq)(subscriptions.id, subscriptionId)).limit(1);
|
|
1190
3291
|
const row = rows[0];
|
|
1191
3292
|
if (!row) return null;
|
|
1192
3293
|
return {
|
|
@@ -1198,35 +3299,35 @@ var DrizzlePromoSubscriptionLookup = class {
|
|
|
1198
3299
|
};
|
|
1199
3300
|
}
|
|
1200
3301
|
};
|
|
1201
|
-
DrizzlePromoSubscriptionLookup =
|
|
1202
|
-
(0,
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
3302
|
+
DrizzlePromoSubscriptionLookup = _ts_decorate23([
|
|
3303
|
+
(0, import_common23.Injectable)(),
|
|
3304
|
+
_ts_param22(0, (0, import_common23.Inject)(DRIZZLE_DB_TOKEN)),
|
|
3305
|
+
_ts_metadata22("design:type", Function),
|
|
3306
|
+
_ts_metadata22("design:paramtypes", [
|
|
1206
3307
|
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
1207
3308
|
])
|
|
1208
3309
|
], DrizzlePromoSubscriptionLookup);
|
|
1209
3310
|
|
|
1210
3311
|
// src/drizzle-subscription.repository.ts
|
|
1211
|
-
var
|
|
1212
|
-
var
|
|
1213
|
-
function
|
|
1214
|
-
var c = arguments.length, r = c < 3 ? target :
|
|
1215
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key,
|
|
3312
|
+
var import_common24 = require("@nestjs/common");
|
|
3313
|
+
var import_drizzle_orm22 = require("drizzle-orm");
|
|
3314
|
+
function _ts_decorate24(decorators, target, key, desc10) {
|
|
3315
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
3316
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
1216
3317
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1217
3318
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1218
3319
|
}
|
|
1219
|
-
__name(
|
|
1220
|
-
function
|
|
3320
|
+
__name(_ts_decorate24, "_ts_decorate");
|
|
3321
|
+
function _ts_metadata23(k, v) {
|
|
1221
3322
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1222
3323
|
}
|
|
1223
|
-
__name(
|
|
1224
|
-
function
|
|
3324
|
+
__name(_ts_metadata23, "_ts_metadata");
|
|
3325
|
+
function _ts_param23(paramIndex, decorator) {
|
|
1225
3326
|
return function(target, key) {
|
|
1226
3327
|
decorator(target, key, paramIndex);
|
|
1227
3328
|
};
|
|
1228
3329
|
}
|
|
1229
|
-
__name(
|
|
3330
|
+
__name(_ts_param23, "_ts_param");
|
|
1230
3331
|
var ACTIVE_STATUSES = [
|
|
1231
3332
|
"ACTIVE",
|
|
1232
3333
|
"TRIAL"
|
|
@@ -1246,19 +3347,28 @@ var DrizzleSubscriptionRepository = class {
|
|
|
1246
3347
|
const db = resolveDb(this.db, tx);
|
|
1247
3348
|
await db.select({
|
|
1248
3349
|
id: subscriptions.id
|
|
1249
|
-
}).from(subscriptions).where((0,
|
|
3350
|
+
}).from(subscriptions).where((0, import_drizzle_orm22.eq)(subscriptions.tenantId, tenantId)).for("update");
|
|
1250
3351
|
return this.loadByTenantId(db, tenantId);
|
|
1251
3352
|
}
|
|
1252
3353
|
async countByPlanVersionId(planVersionId) {
|
|
1253
3354
|
const rows = await this.db.select({
|
|
1254
3355
|
id: subscriptions.id
|
|
1255
|
-
}).from(subscriptions).where((0,
|
|
3356
|
+
}).from(subscriptions).where((0, import_drizzle_orm22.or)((0, import_drizzle_orm22.eq)(subscriptions.planVersionId, planVersionId), (0, import_drizzle_orm22.eq)(subscriptions.pendingPlanVersionId, planVersionId)));
|
|
1256
3357
|
return rows.length;
|
|
1257
3358
|
}
|
|
1258
|
-
async countActiveByPlanKey(
|
|
3359
|
+
async countActiveByPlanKey() {
|
|
3360
|
+
const now = /* @__PURE__ */ new Date();
|
|
1259
3361
|
const rows = await this.db.select({
|
|
1260
3362
|
plan: subscriptions.plan
|
|
1261
|
-
}).from(subscriptions).where((0,
|
|
3363
|
+
}).from(subscriptions).where((0, import_drizzle_orm22.and)((0, import_drizzle_orm22.inArray)(subscriptions.status, ACTIVE_STATUSES), (0, import_drizzle_orm22.or)(
|
|
3364
|
+
(0, import_drizzle_orm22.isNull)(subscriptions.canceledAt),
|
|
3365
|
+
(0, import_drizzle_orm22.gt)(subscriptions.canceledEffectiveAt, now),
|
|
3366
|
+
// The pre-split row keeps its effective date in the
|
|
3367
|
+
// first column; reading only the second drops that
|
|
3368
|
+
// customer on the day they declare rather than the day
|
|
3369
|
+
// they leave.
|
|
3370
|
+
(0, import_drizzle_orm22.and)((0, import_drizzle_orm22.isNull)(subscriptions.canceledEffectiveAt), (0, import_drizzle_orm22.gt)(subscriptions.canceledAt, now))
|
|
3371
|
+
)));
|
|
1262
3372
|
const counts = {};
|
|
1263
3373
|
for (const row of rows) {
|
|
1264
3374
|
counts[row.plan] = (counts[row.plan] ?? 0) + 1;
|
|
@@ -1266,13 +3376,13 @@ var DrizzleSubscriptionRepository = class {
|
|
|
1266
3376
|
return counts;
|
|
1267
3377
|
}
|
|
1268
3378
|
async loadByTenantId(db, tenantId) {
|
|
1269
|
-
const rows = await db.select().from(subscriptions).where((0,
|
|
3379
|
+
const rows = await db.select().from(subscriptions).where((0, import_drizzle_orm22.eq)(subscriptions.tenantId, tenantId)).limit(1);
|
|
1270
3380
|
const row = rows[0];
|
|
1271
3381
|
if (!row) return null;
|
|
1272
3382
|
return this.toRecord(db, row);
|
|
1273
3383
|
}
|
|
1274
3384
|
async toRecord(db, row) {
|
|
1275
|
-
const versionRows = await db.select().from(planVersions).where((0,
|
|
3385
|
+
const versionRows = await db.select().from(planVersions).where((0, import_drizzle_orm22.eq)(planVersions.id, row.planVersionId)).limit(1);
|
|
1276
3386
|
const planVersion = versionRows[0];
|
|
1277
3387
|
if (!planVersion) {
|
|
1278
3388
|
throw new Error(`Subscription ${row.id} references missing PlanVersion ${row.planVersionId}.`);
|
|
@@ -1287,6 +3397,8 @@ var DrizzleSubscriptionRepository = class {
|
|
|
1287
3397
|
pendingPlan: row.pendingPlan,
|
|
1288
3398
|
pendingEffectiveAt: row.pendingEffectiveAt,
|
|
1289
3399
|
customLimits: row.customLimits ?? null,
|
|
3400
|
+
canceledAt: row.canceledAt ?? null,
|
|
3401
|
+
canceledEffectiveAt: row.canceledEffectiveAt ?? null,
|
|
1290
3402
|
planVersionId: row.planVersionId,
|
|
1291
3403
|
planVersion: {
|
|
1292
3404
|
planId: planVersion.planId,
|
|
@@ -1296,37 +3408,37 @@ var DrizzleSubscriptionRepository = class {
|
|
|
1296
3408
|
};
|
|
1297
3409
|
}
|
|
1298
3410
|
};
|
|
1299
|
-
DrizzleSubscriptionRepository =
|
|
1300
|
-
(0,
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
3411
|
+
DrizzleSubscriptionRepository = _ts_decorate24([
|
|
3412
|
+
(0, import_common24.Injectable)(),
|
|
3413
|
+
_ts_param23(0, (0, import_common24.Inject)(DRIZZLE_DB_TOKEN)),
|
|
3414
|
+
_ts_metadata23("design:type", Function),
|
|
3415
|
+
_ts_metadata23("design:paramtypes", [
|
|
1304
3416
|
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
1305
3417
|
])
|
|
1306
3418
|
], DrizzleSubscriptionRepository);
|
|
1307
3419
|
|
|
1308
3420
|
// src/drizzle-super-admin-bootstrap.adapter.ts
|
|
1309
|
-
var
|
|
1310
|
-
var
|
|
1311
|
-
var
|
|
1312
|
-
var
|
|
1313
|
-
function
|
|
1314
|
-
var c = arguments.length, r = c < 3 ? target :
|
|
1315
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key,
|
|
3421
|
+
var import_node_crypto15 = require("node:crypto");
|
|
3422
|
+
var import_common25 = require("@nestjs/common");
|
|
3423
|
+
var import_drizzle_orm23 = require("drizzle-orm");
|
|
3424
|
+
var import_core8 = require("@saasicat/core");
|
|
3425
|
+
function _ts_decorate25(decorators, target, key, desc10) {
|
|
3426
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
3427
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
1316
3428
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1317
3429
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1318
3430
|
}
|
|
1319
|
-
__name(
|
|
1320
|
-
function
|
|
3431
|
+
__name(_ts_decorate25, "_ts_decorate");
|
|
3432
|
+
function _ts_metadata24(k, v) {
|
|
1321
3433
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1322
3434
|
}
|
|
1323
|
-
__name(
|
|
1324
|
-
function
|
|
3435
|
+
__name(_ts_metadata24, "_ts_metadata");
|
|
3436
|
+
function _ts_param24(paramIndex, decorator) {
|
|
1325
3437
|
return function(target, key) {
|
|
1326
3438
|
decorator(target, key, paramIndex);
|
|
1327
3439
|
};
|
|
1328
3440
|
}
|
|
1329
|
-
__name(
|
|
3441
|
+
__name(_ts_param24, "_ts_param");
|
|
1330
3442
|
var PASSWORD_HASHER_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/adapter-drizzle/PasswordHasher");
|
|
1331
3443
|
var DrizzleSuperAdminBootstrapAdapter = class {
|
|
1332
3444
|
static {
|
|
@@ -1340,18 +3452,18 @@ var DrizzleSuperAdminBootstrapAdapter = class {
|
|
|
1340
3452
|
}
|
|
1341
3453
|
async countSuperAdmins() {
|
|
1342
3454
|
const rows = await this.db.select({
|
|
1343
|
-
value: (0,
|
|
1344
|
-
}).from(superAdminUsers).where((0,
|
|
3455
|
+
value: (0, import_drizzle_orm23.count)()
|
|
3456
|
+
}).from(superAdminUsers).where((0, import_drizzle_orm23.and)((0, import_drizzle_orm23.eq)(superAdminUsers.isActive, true), (0, import_drizzle_orm23.isNull)(superAdminUsers.deletedAt)));
|
|
1345
3457
|
return rows[0]?.value ?? 0;
|
|
1346
3458
|
}
|
|
1347
3459
|
async createSuperAdmin(input) {
|
|
1348
3460
|
const email = input.email.trim().toLowerCase();
|
|
1349
|
-
const existing = await this.db.select().from(superAdminUsers).where((0,
|
|
3461
|
+
const existing = await this.db.select().from(superAdminUsers).where((0, import_drizzle_orm23.eq)(superAdminUsers.email, email)).limit(1);
|
|
1350
3462
|
if (existing[0]) {
|
|
1351
|
-
throw new
|
|
3463
|
+
throw new import_core8.PlatformUserExistsError(email, existing[0].platformRole);
|
|
1352
3464
|
}
|
|
1353
3465
|
const rows = await this.db.insert(superAdminUsers).values({
|
|
1354
|
-
id: (0,
|
|
3466
|
+
id: (0, import_node_crypto15.randomUUID)(),
|
|
1355
3467
|
email,
|
|
1356
3468
|
passwordHash: await this.passwordHasher.hash(input.password),
|
|
1357
3469
|
firstName: input.firstName ?? null,
|
|
@@ -1371,36 +3483,36 @@ var DrizzleSuperAdminBootstrapAdapter = class {
|
|
|
1371
3483
|
};
|
|
1372
3484
|
}
|
|
1373
3485
|
};
|
|
1374
|
-
DrizzleSuperAdminBootstrapAdapter =
|
|
1375
|
-
(0,
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
3486
|
+
DrizzleSuperAdminBootstrapAdapter = _ts_decorate25([
|
|
3487
|
+
(0, import_common25.Injectable)(),
|
|
3488
|
+
_ts_param24(0, (0, import_common25.Inject)(DRIZZLE_DB_TOKEN)),
|
|
3489
|
+
_ts_param24(1, (0, import_common25.Inject)(PASSWORD_HASHER_TOKEN)),
|
|
3490
|
+
_ts_metadata24("design:type", Function),
|
|
3491
|
+
_ts_metadata24("design:paramtypes", [
|
|
1380
3492
|
typeof DrizzleClient === "undefined" ? Object : DrizzleClient,
|
|
1381
3493
|
typeof PasswordHasher === "undefined" ? Object : PasswordHasher
|
|
1382
3494
|
])
|
|
1383
3495
|
], DrizzleSuperAdminBootstrapAdapter);
|
|
1384
3496
|
|
|
1385
3497
|
// src/drizzle-transaction-runner.adapter.ts
|
|
1386
|
-
var
|
|
1387
|
-
function
|
|
1388
|
-
var c = arguments.length, r = c < 3 ? target :
|
|
1389
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key,
|
|
3498
|
+
var import_common26 = require("@nestjs/common");
|
|
3499
|
+
function _ts_decorate26(decorators, target, key, desc10) {
|
|
3500
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
3501
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
1390
3502
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1391
3503
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1392
3504
|
}
|
|
1393
|
-
__name(
|
|
1394
|
-
function
|
|
3505
|
+
__name(_ts_decorate26, "_ts_decorate");
|
|
3506
|
+
function _ts_metadata25(k, v) {
|
|
1395
3507
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1396
3508
|
}
|
|
1397
|
-
__name(
|
|
1398
|
-
function
|
|
3509
|
+
__name(_ts_metadata25, "_ts_metadata");
|
|
3510
|
+
function _ts_param25(paramIndex, decorator) {
|
|
1399
3511
|
return function(target, key) {
|
|
1400
3512
|
decorator(target, key, paramIndex);
|
|
1401
3513
|
};
|
|
1402
3514
|
}
|
|
1403
|
-
__name(
|
|
3515
|
+
__name(_ts_param25, "_ts_param");
|
|
1404
3516
|
var DrizzleTransactionRunner = class {
|
|
1405
3517
|
static {
|
|
1406
3518
|
__name(this, "DrizzleTransactionRunner");
|
|
@@ -1413,24 +3525,24 @@ var DrizzleTransactionRunner = class {
|
|
|
1413
3525
|
return this.db.transaction((tx) => fn(tx));
|
|
1414
3526
|
}
|
|
1415
3527
|
};
|
|
1416
|
-
DrizzleTransactionRunner =
|
|
1417
|
-
(0,
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
3528
|
+
DrizzleTransactionRunner = _ts_decorate26([
|
|
3529
|
+
(0, import_common26.Injectable)(),
|
|
3530
|
+
_ts_param25(0, (0, import_common26.Inject)(DRIZZLE_DB_TOKEN)),
|
|
3531
|
+
_ts_metadata25("design:type", Function),
|
|
3532
|
+
_ts_metadata25("design:paramtypes", [
|
|
1421
3533
|
typeof DrizzleClient === "undefined" ? Object : DrizzleClient
|
|
1422
3534
|
])
|
|
1423
3535
|
], DrizzleTransactionRunner);
|
|
1424
3536
|
|
|
1425
3537
|
// src/zero-promo-revenue-aggregator.adapter.ts
|
|
1426
|
-
var
|
|
1427
|
-
function
|
|
1428
|
-
var c = arguments.length, r = c < 3 ? target :
|
|
1429
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key,
|
|
3538
|
+
var import_common27 = require("@nestjs/common");
|
|
3539
|
+
function _ts_decorate27(decorators, target, key, desc10) {
|
|
3540
|
+
var c = arguments.length, r = c < 3 ? target : desc10 === null ? desc10 = Object.getOwnPropertyDescriptor(target, key) : desc10, d;
|
|
3541
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc10);
|
|
1430
3542
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1431
3543
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1432
3544
|
}
|
|
1433
|
-
__name(
|
|
3545
|
+
__name(_ts_decorate27, "_ts_decorate");
|
|
1434
3546
|
var ZeroPromoRevenueDeductionAggregator = class {
|
|
1435
3547
|
static {
|
|
1436
3548
|
__name(this, "ZeroPromoRevenueDeductionAggregator");
|
|
@@ -1439,8 +3551,8 @@ var ZeroPromoRevenueDeductionAggregator = class {
|
|
|
1439
3551
|
return "0.00";
|
|
1440
3552
|
}
|
|
1441
3553
|
};
|
|
1442
|
-
ZeroPromoRevenueDeductionAggregator =
|
|
1443
|
-
(0,
|
|
3554
|
+
ZeroPromoRevenueDeductionAggregator = _ts_decorate27([
|
|
3555
|
+
(0, import_common27.Injectable)()
|
|
1444
3556
|
], ZeroPromoRevenueDeductionAggregator);
|
|
1445
3557
|
|
|
1446
3558
|
// src/drizzle-persistence.ts
|
|
@@ -1466,18 +3578,55 @@ function drizzlePersistence(options) {
|
|
|
1466
3578
|
transactionRunner: provide((client) => new DrizzleTransactionRunner(client)),
|
|
1467
3579
|
auditQuery: provide((client) => new DrizzleAuditQueryAdapter(client)),
|
|
1468
3580
|
auditStats: provide((client) => new DrizzleAuditStatsAdapter(client)),
|
|
1469
|
-
superAdminProvisioning: buildProvisioning(db, options.passwordHasher)
|
|
3581
|
+
superAdminProvisioning: buildProvisioning(db, options.passwordHasher),
|
|
3582
|
+
appliedSettings: provide((client) => new DrizzleAppliedSettingsRepository(client))
|
|
1470
3583
|
},
|
|
1471
3584
|
entitlement: {
|
|
1472
3585
|
subscriptionRepository: provide((client) => new DrizzleSubscriptionRepository(client)),
|
|
1473
|
-
planVersionRepository: provide((client) => new DrizzlePlanVersionRepository(client))
|
|
3586
|
+
planVersionRepository: provide((client) => new DrizzlePlanVersionRepository(client)),
|
|
3587
|
+
// Entitlement reads the bookings as well as the plan: a tenant's
|
|
3588
|
+
// effective features are the union of the plan and the bundles
|
|
3589
|
+
// booked on top of it, so an adapter that omits this grants less
|
|
3590
|
+
// than the tenant paid for.
|
|
3591
|
+
subscriptionBundleRepository: provide((client) => new DrizzleSubscriptionBundleRepository(client)),
|
|
3592
|
+
// What the tenant actually agreed to, frozen at signing. Entitlement
|
|
3593
|
+
// prefers it over the live catalogue where it exists, which is what
|
|
3594
|
+
// makes a price change stop at the contracts already signed.
|
|
3595
|
+
subscriptionContractRepository: provide((client) => new DrizzleSubscriptionContractRepository(client)),
|
|
3596
|
+
// The party each of those contracts is concluded with.
|
|
3597
|
+
subscriberRepository: provide((client) => new DrizzleSubscriberRepository(client)),
|
|
3598
|
+
// The catalogue behind those bookings: entitlement resolves a
|
|
3599
|
+
// booking's features by reading the pinned version.
|
|
3600
|
+
bundleRepository: provide((client) => new DrizzleBundleRepository(client, options.bundle))
|
|
3601
|
+
},
|
|
3602
|
+
// The editable catalogue: which plans a project sells and in which
|
|
3603
|
+
// versions. Present since 2026-08-27 — before that this adapter could
|
|
3604
|
+
// read a plan version but not author one, so a Drizzle consumer had to
|
|
3605
|
+
// bring their own `CatalogModule` wiring for a slice the adapter was
|
|
3606
|
+
// otherwise complete for.
|
|
3607
|
+
catalog: {
|
|
3608
|
+
planRepository: provide((client) => new DrizzlePlanRepository(client, options.plan)),
|
|
3609
|
+
bundleRepository: provide((client) => new DrizzleBundleRepository(client, options.bundle))
|
|
3610
|
+
},
|
|
3611
|
+
// The tenant's own billing page and the writes behind its buttons.
|
|
3612
|
+
// Both members are required, and until 2026-08-27 neither existed —
|
|
3613
|
+
// which left `DrizzleTenantSubscriptionWrite` unreachable through the
|
|
3614
|
+
// documented `drizzlePersistence({ db })` path however complete it was.
|
|
3615
|
+
tenantBilling: {
|
|
3616
|
+
subscriptionUsagePort: provide((client) => new DrizzleSubscriptionUsageAdapter(client)),
|
|
3617
|
+
subscriptionWritePort: provide((client) => new DrizzleTenantSubscriptionWrite(client))
|
|
1474
3618
|
},
|
|
1475
3619
|
promo: {
|
|
1476
3620
|
promoCodeRepository: provide((client) => new DrizzlePromoCodeRepository(client)),
|
|
1477
3621
|
redemptionRepository: provide((client) => new DrizzlePromoCodeRedemptionRepository(client)),
|
|
1478
3622
|
validationLogRepository: provide((client) => new DrizzlePromoCodeValidationLogRepository(client)),
|
|
1479
3623
|
subscriptionLookup: provide((client) => new DrizzlePromoSubscriptionLookup(client)),
|
|
1480
|
-
revenueAggregator: new ZeroPromoRevenueDeductionAggregator()
|
|
3624
|
+
revenueAggregator: new ZeroPromoRevenueDeductionAggregator(),
|
|
3625
|
+
holdRepository: provide((client) => new DrizzlePromoCodeHoldRepository(client))
|
|
3626
|
+
},
|
|
3627
|
+
payments: {
|
|
3628
|
+
paymentEventLog: provide((client) => new DrizzlePaymentEventLog(client)),
|
|
3629
|
+
subscriberPaymentMethodRepository: provide((client) => new DrizzleSubscriberPaymentMethodRepository(client))
|
|
1481
3630
|
},
|
|
1482
3631
|
planCatalogReadSink: provide((client) => new DrizzlePlanCatalogReadSink(client)),
|
|
1483
3632
|
planCatalogImportSink: provide((client) => new DrizzlePlanCatalogImportSink(client))
|
|
@@ -1524,19 +3673,30 @@ __name(buildProvisioning, "buildProvisioning");
|
|
|
1524
3673
|
0 && (module.exports = {
|
|
1525
3674
|
AsyncLocalRlsBypassAdapter,
|
|
1526
3675
|
DRIZZLE_DB_TOKEN,
|
|
3676
|
+
DrizzleAppliedSettingsRepository,
|
|
1527
3677
|
DrizzleAuditAdapter,
|
|
1528
3678
|
DrizzleAuditQueryAdapter,
|
|
1529
3679
|
DrizzleAuditStatsAdapter,
|
|
3680
|
+
DrizzleBundleRepository,
|
|
1530
3681
|
DrizzleMfaAdapter,
|
|
3682
|
+
DrizzlePaymentEventLog,
|
|
1531
3683
|
DrizzlePlanCatalogImportSink,
|
|
1532
3684
|
DrizzlePlanCatalogReadSink,
|
|
3685
|
+
DrizzlePlanRepository,
|
|
1533
3686
|
DrizzlePlanVersionRepository,
|
|
3687
|
+
DrizzlePromoCodeHoldRepository,
|
|
1534
3688
|
DrizzlePromoCodeRedemptionRepository,
|
|
1535
3689
|
DrizzlePromoCodeRepository,
|
|
1536
3690
|
DrizzlePromoCodeValidationLogRepository,
|
|
1537
3691
|
DrizzlePromoSubscriptionLookup,
|
|
3692
|
+
DrizzleSubscriberPaymentMethodRepository,
|
|
3693
|
+
DrizzleSubscriberRepository,
|
|
3694
|
+
DrizzleSubscriptionBundleRepository,
|
|
3695
|
+
DrizzleSubscriptionContractRepository,
|
|
1538
3696
|
DrizzleSubscriptionRepository,
|
|
3697
|
+
DrizzleSubscriptionUsageAdapter,
|
|
1539
3698
|
DrizzleSuperAdminBootstrapAdapter,
|
|
3699
|
+
DrizzleTenantSubscriptionWrite,
|
|
1540
3700
|
DrizzleTransactionRunner,
|
|
1541
3701
|
PASSWORD_HASHER_TOKEN,
|
|
1542
3702
|
ZeroPromoRevenueDeductionAggregator,
|