@saasicat/adapter-prisma 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -23
- package/dist/index.cjs +1014 -176
- package/dist/index.d.cts +297 -60
- package/dist/index.d.ts +297 -60
- package/dist/index.js +1013 -180
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -37,6 +37,8 @@ Options:
|
|
|
37
37
|
`core.superAdminProvisioning` (setup wizard / `user create-super-admin`).
|
|
38
38
|
- `rlsIntegration: true` — declare the `rowLevelSecurity` capability once
|
|
39
39
|
your Prisma middleware really applies the bypass (see below).
|
|
40
|
+
- `schema` — explicit plan identity, delegate and optional-field capabilities
|
|
41
|
+
for schemas that differ from the 0.6 canonical layout.
|
|
40
42
|
|
|
41
43
|
The bundle also ships `planCatalogReadSink` for DB hydration. To use it,
|
|
42
44
|
omit `planCatalog` and pass the identity the database cannot provide:
|
|
@@ -56,27 +58,36 @@ PromoCodesModule.forRoot({
|
|
|
56
58
|
|
|
57
59
|
## Shipped adapters
|
|
58
60
|
|
|
59
|
-
| Class | Implements port | Tables
|
|
60
|
-
| ---------------------------------------- | ---------------------------------- |
|
|
61
|
-
| `PrismaTransactionRunner` | `TransactionRunner` | — (`$transaction`)
|
|
62
|
-
| `PrismaMfaAdapter` | `MfaPort` | `super_admin_mfa`
|
|
63
|
-
| `PrismaAuditAdapter` | `AuditPort` | `audit_logs`
|
|
64
|
-
| `PrismaAuditQueryAdapter` | `AuditQueryPort` | `audit_logs`
|
|
65
|
-
| `PrismaAuditStatsAdapter` | `AuditStatsPort` | `audit_logs`
|
|
66
|
-
| `AsyncLocalRlsBypassAdapter` | `RlsBypassPort` | (no DB access)
|
|
67
|
-
| `PrismaSubscriptionRepository` | `SubscriptionRepository` | `subscriptions`, `plan_versions`
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
61
|
+
| Class | Implements port | Tables |
|
|
62
|
+
| ---------------------------------------- | ---------------------------------- | ------------------------------------------------------------------- |
|
|
63
|
+
| `PrismaTransactionRunner` | `TransactionRunner` | — (`$transaction`) |
|
|
64
|
+
| `PrismaMfaAdapter` | `MfaPort` | `super_admin_mfa` |
|
|
65
|
+
| `PrismaAuditAdapter` | `AuditPort` | `audit_logs` |
|
|
66
|
+
| `PrismaAuditQueryAdapter` | `AuditQueryPort` | `audit_logs` |
|
|
67
|
+
| `PrismaAuditStatsAdapter` | `AuditStatsPort` | `audit_logs` |
|
|
68
|
+
| `AsyncLocalRlsBypassAdapter` | `RlsBypassPort` | (no DB access) |
|
|
69
|
+
| `PrismaSubscriptionRepository` | `SubscriptionRepository` | `subscriptions`, `plan_versions`, optionally `subscription_bundles` |
|
|
70
|
+
| `PrismaSubscriptionBundleRepository` | `SubscriptionBundleRepository` | `subscription_bundles` |
|
|
71
|
+
| `PrismaTenantSubscriptionWriteAdapter` | `TenantSubscriptionWritePort` | `subscriptions`, `plans`, `plan_versions` |
|
|
72
|
+
| `PrismaPlanVersionRepository` | `PlanVersionRepository` | `plan_versions` |
|
|
73
|
+
| `PrismaPromoCodeRepository` | `PromoCodeRepository` | `promo_codes` |
|
|
74
|
+
| `PrismaPromoCodeRedemptionRepository` | `PromoCodeRedemptionRepository` | `promo_code_redemptions` |
|
|
75
|
+
| `PrismaPromoCodeValidationLogRepository` | `PromoCodeValidationLogRepository` | `promo_code_validation_logs` |
|
|
76
|
+
| `PrismaPromoSubscriptionLookup` | `PromoSubscriptionLookup` | `subscriptions` |
|
|
77
|
+
| `ZeroPromoRevenueDeductionAggregator` | `PromoRevenueDeductionAggregator` | — (constant `'0.00'`) |
|
|
78
|
+
| `PrismaSuperAdminBootstrapAdapter` | `SuperAdminProvisioningPort` | `super_admin_users` |
|
|
79
|
+
| `PrismaPlanCatalogReadSink` | `PlanCatalogReadSink` | `plans`, `plan_versions`, `feature_catalog_entries` |
|
|
80
|
+
| `PrismaPlanCatalogImportSink` | `PlanCatalogImportSink` | same |
|
|
81
|
+
| `PrismaPlanRepository` | `PlanRepository` | `plans`, `plan_versions` |
|
|
82
|
+
| `PrismaBundleRepository` | `BundleRepository` | `bundles`, `bundle_versions` |
|
|
83
|
+
| `PrismaCatalogEntryRepository` | `CatalogEntryRepository` | capability, feature and quota catalog tables |
|
|
84
|
+
| `PrismaMarketingProjectionRepository` | `MarketingProjectionRepository` | `marketing_projections` |
|
|
85
|
+
| `PrismaMarketingSettingsRepository` | `MarketingSettingsRepository` | `marketing_settings` |
|
|
86
|
+
| `PrismaPromotionRepository` | `PromotionRepository` | `promotions` |
|
|
87
|
+
| `PrismaSubscriptionContractRepository` | `SubscriptionContractRepository` | `subscription_contracts`, `contract_line_items` |
|
|
88
|
+
|
|
89
|
+
Not shipped (custom adapters stay yours): registration persistence,
|
|
90
|
+
consumer-specific payment/invoice integrations, and `FirstTimeCustomerCheck`.
|
|
80
91
|
Absent optional repository methods degrade fail-closed as documented on the
|
|
81
92
|
ports (e.g. `countByBundleVersionId`).
|
|
82
93
|
|
|
@@ -104,8 +115,121 @@ The canonical schema: copy the models from
|
|
|
104
115
|
`@saasicat/spec/sql/reference-schema.postgres.sql`) **plus**
|
|
105
116
|
`sql/constraints.postgres.sql` — the partial unique indexes and the
|
|
106
117
|
subscription CHECK are part of the contract, `claimSlot`/`findByTenantIdLocked`
|
|
107
|
-
rely on real PostgreSQL semantics.
|
|
108
|
-
|
|
118
|
+
rely on real PostgreSQL semantics. Use the explicit schema options below for
|
|
119
|
+
supported differences; for other shapes, override only the affected adapter —
|
|
120
|
+
the platform ports stay identical.
|
|
121
|
+
|
|
122
|
+
### Plan identity and split PlanVersion delegates
|
|
123
|
+
|
|
124
|
+
The default is the SaaSiCat 0.6 layout: `PlanVersion.planId` stores the
|
|
125
|
+
semantic `planKey`, both catalog and entitlement reads use the `planVersion`
|
|
126
|
+
delegate, and optional validity columns are not queried. There is no schema
|
|
127
|
+
auto-detection.
|
|
128
|
+
|
|
129
|
+
An app with a normalized UUID foreign key opts in explicitly. Port inputs and
|
|
130
|
+
outputs still use the semantic key:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const schema = {
|
|
134
|
+
planBinding: {
|
|
135
|
+
mode: 'normalized-plan-id',
|
|
136
|
+
projectKey: 'vereinsfux',
|
|
137
|
+
},
|
|
138
|
+
planVersionFields: {
|
|
139
|
+
validityWindows: true,
|
|
140
|
+
endsAt: true,
|
|
141
|
+
},
|
|
142
|
+
tenantSubscription: {
|
|
143
|
+
subscriptionBundleDelegate: 'subscriptionBundle',
|
|
144
|
+
synchronizePlanVersion: true,
|
|
145
|
+
atomicOnboardingSelection: true,
|
|
146
|
+
activeVersionSelection: 'validity-window',
|
|
147
|
+
withEndsAt: true,
|
|
148
|
+
},
|
|
149
|
+
} satisfies PrismaSchemaOptions;
|
|
150
|
+
|
|
151
|
+
const persistence = prismaPersistence({ client: PrismaService, schema });
|
|
152
|
+
const plans = new PrismaPlanRepository(prisma, schema);
|
|
153
|
+
const subscriptionWrites = new PrismaTenantSubscriptionWriteAdapter(prisma, schema);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
For direct Nest registration, bind the same object once:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
providers: [
|
|
160
|
+
{ provide: PRISMA_CLIENT_TOKEN, useExisting: PrismaService },
|
|
161
|
+
{ provide: PRISMA_SCHEMA_OPTIONS_TOKEN, useValue: schema },
|
|
162
|
+
PrismaPlanRepository,
|
|
163
|
+
PrismaPlanVersionRepository,
|
|
164
|
+
PrismaTenantSubscriptionWriteAdapter,
|
|
165
|
+
];
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Split schemas can name catalog and entitlement delegates independently. This
|
|
169
|
+
keeps, for example, `catalogPlanVersion` with validity columns separate from a
|
|
170
|
+
legacy billing `planVersion`:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
const schema = {
|
|
174
|
+
delegates: {
|
|
175
|
+
catalogPlanVersion: 'catalogPlanVersion',
|
|
176
|
+
entitlementPlanVersion: 'planVersion',
|
|
177
|
+
},
|
|
178
|
+
planVersionFields: {
|
|
179
|
+
catalog: { validityWindows: true },
|
|
180
|
+
entitlement: { validityWindows: false },
|
|
181
|
+
},
|
|
182
|
+
} satisfies PrismaSchemaOptions;
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Delegate selection is field-level and backwards-compatible; an app whose
|
|
186
|
+
billing version stores fixed quota columns instead of JSON can continue to
|
|
187
|
+
override only the entitlement repository.
|
|
188
|
+
|
|
189
|
+
### Atomic tenant plan binding
|
|
190
|
+
|
|
191
|
+
`PrismaTenantSubscriptionWriteAdapter` exposes the optional
|
|
192
|
+
`applyOnboardingSelection` capability only when
|
|
193
|
+
`tenantSubscription.atomicOnboardingSelection: true`. The default is `false`,
|
|
194
|
+
preserving the 0.6 sequential fallback. When enabled, the subscription update
|
|
195
|
+
and optional promo callback share one Prisma transaction. With
|
|
196
|
+
`tenantSubscription.synchronizePlanVersion: true`, immediate changes and
|
|
197
|
+
onboarding also resolve the target PlanVersion and update `plan`,
|
|
198
|
+
`planVersionId`, cycle and stale pending-version fields atomically. The default
|
|
199
|
+
is `false`, preserving the 0.6 plan-only write until an app opts in.
|
|
200
|
+
|
|
201
|
+
`tenantSubscription.delegate` selects the Prisma model delegate used for all
|
|
202
|
+
subscription ORM operations, including the read that follows a row lock.
|
|
203
|
+
`findByTenantIdLocked` deliberately locks the canonical physical
|
|
204
|
+
`subscriptions` table with raw SQL, so a differently named Prisma model must
|
|
205
|
+
map to that table via `@@map("subscriptions")`.
|
|
206
|
+
|
|
207
|
+
When `tenantSubscription.subscriptionBundleDelegate` names the app's
|
|
208
|
+
SubscriptionBundle delegate, `PrismaSubscriptionRepository` also exposes
|
|
209
|
+
`countByBundleVersionId`. This keeps published-but-future BundleVersion
|
|
210
|
+
editability aligned with real active bookings. Without the option the method
|
|
211
|
+
is absent and the catalog service remains fail-closed.
|
|
212
|
+
|
|
213
|
+
### Bundle validity windows
|
|
214
|
+
|
|
215
|
+
`PrismaBundleRepository` keeps its 0.6-compatible behavior by default and does
|
|
216
|
+
not require `bundle_versions.validFrom` / `validUntil`. After applying the
|
|
217
|
+
additive columns from the current `@saasicat/spec` bundle fragment, enable them
|
|
218
|
+
explicitly:
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
const bundles = new PrismaBundleRepository(prisma, {
|
|
222
|
+
validityWindows: true,
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
The enabled mode persists and returns both dates. Publishing also sets the
|
|
227
|
+
predecessor's `validUntil` to one UTC calendar day before the successor starts,
|
|
228
|
+
and wraps supersede + publish in a transaction when the caller did not already
|
|
229
|
+
provide one. It also exposes the optional
|
|
230
|
+
`BundleRepository.findActiveBundleVersion(bundleId, asOf?)` capability, using
|
|
231
|
+
inclusive UTC-day boundaries and preferring the highest `validFrom`, then
|
|
232
|
+
`version`. In the default legacy mode that optional capability is `undefined`.
|
|
109
233
|
|
|
110
234
|
## RLS bypass
|
|
111
235
|
|