@saasicat/adapter-prisma 0.26.1 → 0.27.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/LICENSE +90 -201
- package/dist/index.cjs +144 -50
- package/dist/index.d.cts +96 -1
- package/dist/index.d.ts +96 -1
- package/dist/index.js +142 -50
- package/package.json +5 -5
package/LICENSE
CHANGED
|
@@ -1,202 +1,91 @@
|
|
|
1
|
+
Required Notice: Copyright Taci Ülker (https://github.com/uelker70/saasicat)
|
|
1
2
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
-
modifications, and in Source or Object form, provided that You
|
|
93
|
-
meet the following conditions:
|
|
94
|
-
|
|
95
|
-
(a) You must give any other recipients of the Work or
|
|
96
|
-
Derivative Works a copy of this License; and
|
|
97
|
-
|
|
98
|
-
(b) You must cause any modified files to carry prominent notices
|
|
99
|
-
stating that You changed the files; and
|
|
100
|
-
|
|
101
|
-
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
-
that You distribute, all copyright, patent, trademark, and
|
|
103
|
-
attribution notices from the Source form of the Work,
|
|
104
|
-
excluding those notices that do not pertain to any part of
|
|
105
|
-
the Derivative Works; and
|
|
106
|
-
|
|
107
|
-
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
-
distribution, then any Derivative Works that You distribute must
|
|
109
|
-
include a readable copy of the attribution notices contained
|
|
110
|
-
within such NOTICE file, excluding those notices that do not
|
|
111
|
-
pertain to any part of the Derivative Works, in at least one
|
|
112
|
-
of the following places: within a NOTICE text file distributed
|
|
113
|
-
as part of the Derivative Works; within the Source form or
|
|
114
|
-
documentation, if provided along with the Derivative Works; or,
|
|
115
|
-
within a display generated by the Derivative Works, if and
|
|
116
|
-
wherever such third-party notices normally appear. The contents
|
|
117
|
-
of the NOTICE file are for informational purposes only and
|
|
118
|
-
do not modify the License. You may add Your own attribution
|
|
119
|
-
notices within Derivative Works that You distribute, alongside
|
|
120
|
-
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
-
that such additional attribution notices cannot be construed
|
|
122
|
-
as modifying the License.
|
|
123
|
-
|
|
124
|
-
You may add Your own copyright statement to Your modifications and
|
|
125
|
-
may provide additional or different license terms and conditions
|
|
126
|
-
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
-
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
-
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
-
the conditions stated in this License.
|
|
130
|
-
|
|
131
|
-
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
-
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
-
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
-
this License, without any additional terms or conditions.
|
|
135
|
-
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
-
the terms of any separate license agreement you may have executed
|
|
137
|
-
with Licensor regarding such Contributions.
|
|
138
|
-
|
|
139
|
-
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
-
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
-
except as required for reasonable and customary use in describing the
|
|
142
|
-
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
-
|
|
144
|
-
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
-
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
-
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
-
implied, including, without limitation, any warranties or conditions
|
|
149
|
-
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
-
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
-
appropriateness of using or redistributing the Work and assume any
|
|
152
|
-
risks associated with Your exercise of permissions under this License.
|
|
153
|
-
|
|
154
|
-
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
-
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
-
unless required by applicable law (such as deliberate and grossly
|
|
157
|
-
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
-
liable to You for damages, including any direct, indirect, special,
|
|
159
|
-
incidental, or consequential damages of any character arising as a
|
|
160
|
-
result of this License or out of the use or inability to use the
|
|
161
|
-
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
-
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
-
other commercial damages or losses), even if such Contributor
|
|
164
|
-
has been advised of the possibility of such damages.
|
|
165
|
-
|
|
166
|
-
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
-
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
-
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
-
or other liability obligations and/or rights consistent with this
|
|
170
|
-
License. However, in accepting such obligations, You may act only
|
|
171
|
-
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
-
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
-
defend, and hold each Contributor harmless for any liability
|
|
174
|
-
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
-
of your accepting any such warranty or additional liability.
|
|
176
|
-
|
|
177
|
-
END OF TERMS AND CONDITIONS
|
|
178
|
-
|
|
179
|
-
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
-
|
|
181
|
-
To apply the Apache License to your work, attach the following
|
|
182
|
-
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
-
replaced with your own identifying information. (Don't include
|
|
184
|
-
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
-
comment syntax for the file format. We also recommend that a
|
|
186
|
-
file or class name and description of purpose be included on the
|
|
187
|
-
same "printed page" as the copyright notice for easier
|
|
188
|
-
identification within third-party archives.
|
|
189
|
-
|
|
190
|
-
Copyright [yyyy] [name of copyright owner]
|
|
191
|
-
|
|
192
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
-
you may not use this file except in compliance with the License.
|
|
194
|
-
You may obtain a copy of the License at
|
|
195
|
-
|
|
196
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
-
|
|
198
|
-
Unless required by applicable law or agreed to in writing, software
|
|
199
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
-
See the License for the specific language governing permissions and
|
|
202
|
-
limitations under the License.
|
|
3
|
+
# PolyForm Shield License 1.0.0
|
|
4
|
+
|
|
5
|
+
<https://polyformproject.org/licenses/shield/1.0.0>
|
|
6
|
+
|
|
7
|
+
## Acceptance
|
|
8
|
+
|
|
9
|
+
In order to get any license under these terms, you must agree to them as both strict obligations and conditions to all your licenses.
|
|
10
|
+
|
|
11
|
+
## Copyright License
|
|
12
|
+
|
|
13
|
+
The licensor grants you a copyright license for the software to do everything you might do with the software that would otherwise infringe the licensor's copyright in it for any permitted purpose. However, you may only distribute the software according to [Distribution License](#distribution-license) and make changes or new works based on the software according to [Changes and New Works License](#changes-and-new-works-license).
|
|
14
|
+
|
|
15
|
+
## Distribution License
|
|
16
|
+
|
|
17
|
+
The licensor grants you an additional copyright license to distribute copies of the software. Your license to distribute covers distributing the software with changes and new works permitted by [Changes and New Works License](#changes-and-new-works-license).
|
|
18
|
+
|
|
19
|
+
## Notices
|
|
20
|
+
|
|
21
|
+
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms or the URL for them above, as well as copies of any plain-text lines beginning with `Required Notice:` that the licensor provided with the software. For example:
|
|
22
|
+
|
|
23
|
+
> Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
|
|
24
|
+
|
|
25
|
+
## Changes and New Works License
|
|
26
|
+
|
|
27
|
+
The licensor grants you an additional copyright license to make changes and new works based on the software for any permitted purpose.
|
|
28
|
+
|
|
29
|
+
## Patent License
|
|
30
|
+
|
|
31
|
+
The licensor grants you a patent license for the software that covers patent claims the licensor can license, or becomes able to license, that you would infringe by using the software.
|
|
32
|
+
|
|
33
|
+
## Noncompete
|
|
34
|
+
|
|
35
|
+
Any purpose is a permitted purpose, except for providing any product that competes with the software or any product the licensor or any of its affiliates provides using the software.
|
|
36
|
+
|
|
37
|
+
## Competition
|
|
38
|
+
|
|
39
|
+
Goods and services compete even when they provide functionality through different kinds of interfaces or for different technical platforms. Applications can compete with services, libraries with plugins, frameworks with development tools, and so on, even if they're written in different programming languages or for different computer architectures. Goods and services compete even when provided free of charge. If you market a product as a practical substitute for the software or another product, it definitely competes.
|
|
40
|
+
|
|
41
|
+
## New Products
|
|
42
|
+
|
|
43
|
+
If you are using the software to provide a product that does not compete, but the licensor or any of its affiliates brings your product into competition by providing a new version of the software or another product using the software, you may continue using versions of the software available under these terms beforehand to provide your competing product, but not any later versions.
|
|
44
|
+
|
|
45
|
+
## Discontinued Products
|
|
46
|
+
|
|
47
|
+
You may begin using the software to compete with a product or service that the licensor or any of its affiliates has stopped providing, unless the licensor includes a plain-text line beginning with `Licensor Line of Business:` with the software that mentions that line of business. For example:
|
|
48
|
+
|
|
49
|
+
> Licensor Line of Business: YoyodyneCMS Content Management System (http://example.com/cms)
|
|
50
|
+
|
|
51
|
+
## Sales of Business
|
|
52
|
+
|
|
53
|
+
If the licensor or any of its affiliates sells a line of business developing the software or using the software to provide a product, the buyer can also enforce [Noncompete](#noncompete) for that product.
|
|
54
|
+
|
|
55
|
+
## Fair Use
|
|
56
|
+
|
|
57
|
+
You may have "fair use" rights for the software under the law. These terms do not limit them.
|
|
58
|
+
|
|
59
|
+
## No Other Rights
|
|
60
|
+
|
|
61
|
+
These terms do not allow you to sublicense or transfer any of your licenses to anyone else, or prevent the licensor from granting licenses to anyone else. These terms do not imply any other licenses.
|
|
62
|
+
|
|
63
|
+
## Patent Defense
|
|
64
|
+
|
|
65
|
+
If you make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
|
|
66
|
+
|
|
67
|
+
## Violations
|
|
68
|
+
|
|
69
|
+
The first time you are notified in writing that you have violated any of these terms, or done anything with the software not covered by your licenses, your licenses can nonetheless continue if you come into full compliance with these terms, and take practical steps to correct past violations, within 32 days of receiving notice. Otherwise, all your licenses end immediately.
|
|
70
|
+
|
|
71
|
+
## No Liability
|
|
72
|
+
|
|
73
|
+
***As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.***
|
|
74
|
+
|
|
75
|
+
## Definitions
|
|
76
|
+
|
|
77
|
+
The **licensor** is the individual or entity offering these terms, and the **software** is the software the licensor makes available under these terms.
|
|
78
|
+
|
|
79
|
+
A **product** can be a good or service, or a combination of them.
|
|
80
|
+
|
|
81
|
+
**You** refers to the individual or entity agreeing to these terms.
|
|
82
|
+
|
|
83
|
+
**Your company** is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all its affiliates.
|
|
84
|
+
|
|
85
|
+
**Affiliates** means the other organizations than an organization has control over, is under the control of, or is under common control with.
|
|
86
|
+
|
|
87
|
+
**Control** means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
|
|
88
|
+
|
|
89
|
+
**Your licenses** are all the licenses granted to you for the software under these terms.
|
|
90
|
+
|
|
91
|
+
**Use** means anything you do with the software requiring one of your licenses.
|
package/dist/index.cjs
CHANGED
|
@@ -52,10 +52,12 @@ __export(index_exports, {
|
|
|
52
52
|
PrismaTenantSubscriptionWriteAdapter: () => PrismaTenantSubscriptionWriteAdapter,
|
|
53
53
|
PrismaTransactionRunner: () => PrismaTransactionRunner,
|
|
54
54
|
ZeroPromoRevenueDeductionAggregator: () => ZeroPromoRevenueDeductionAggregator,
|
|
55
|
+
assertDelegatesExist: () => assertDelegatesExist,
|
|
55
56
|
buildActorTag: () => buildActorTag,
|
|
56
57
|
createPrismaPlanBindingResolver: () => createPrismaPlanBindingResolver,
|
|
57
58
|
getPrismaDelegate: () => getPrismaDelegate,
|
|
58
59
|
prismaPersistence: () => prismaPersistence,
|
|
60
|
+
resolveAdminResourcesSchema: () => resolveAdminResourcesSchema,
|
|
59
61
|
resolvePrismaSchemaOptions: () => resolvePrismaSchemaOptions
|
|
60
62
|
});
|
|
61
63
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -410,7 +412,7 @@ __name(createPrismaPlanBindingResolver, "createPrismaPlanBindingResolver");
|
|
|
410
412
|
function getPrismaDelegate(client, delegateName) {
|
|
411
413
|
const delegate = client?.[delegateName];
|
|
412
414
|
if (!delegate || typeof delegate !== "object") {
|
|
413
|
-
throw new Error(`Prisma client has no '${delegateName}' delegate.`);
|
|
415
|
+
throw new Error(`Prisma client has no '${delegateName}' delegate. If your model is called something else, map it \u2014 \`adminResources: { delegates: { tenant: "organization" } }\` for the SuperAdmin resources, \`schema\` for the plan binding.`);
|
|
414
416
|
}
|
|
415
417
|
return delegate;
|
|
416
418
|
}
|
|
@@ -434,6 +436,65 @@ function asPlanIdentityClient(client) {
|
|
|
434
436
|
}
|
|
435
437
|
__name(asPlanIdentityClient, "asPlanIdentityClient");
|
|
436
438
|
|
|
439
|
+
// src/admin-resources-schema.ts
|
|
440
|
+
var DEFAULTS = {
|
|
441
|
+
delegates: {
|
|
442
|
+
tenant: "tenant",
|
|
443
|
+
user: "user",
|
|
444
|
+
subscription: "subscription"
|
|
445
|
+
},
|
|
446
|
+
tenant: {
|
|
447
|
+
slug: "slug",
|
|
448
|
+
name: "name",
|
|
449
|
+
isActive: "isActive",
|
|
450
|
+
deletedAt: "deletedAt",
|
|
451
|
+
users: "users",
|
|
452
|
+
subscription: "subscription"
|
|
453
|
+
},
|
|
454
|
+
user: {
|
|
455
|
+
email: "email",
|
|
456
|
+
firstName: "firstName",
|
|
457
|
+
lastName: "lastName",
|
|
458
|
+
isActive: "isActive",
|
|
459
|
+
tenant: "tenant"
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
function resolveAdminResourcesSchema(delegates = {}, fields = {}) {
|
|
463
|
+
return {
|
|
464
|
+
delegates: {
|
|
465
|
+
...DEFAULTS.delegates,
|
|
466
|
+
...delegates
|
|
467
|
+
},
|
|
468
|
+
tenant: {
|
|
469
|
+
...DEFAULTS.tenant,
|
|
470
|
+
...fields.tenant
|
|
471
|
+
},
|
|
472
|
+
user: {
|
|
473
|
+
...DEFAULTS.user,
|
|
474
|
+
...fields.user
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
__name(resolveAdminResourcesSchema, "resolveAdminResourcesSchema");
|
|
479
|
+
function assertDelegatesExist(client, schema) {
|
|
480
|
+
const available = new Set(delegateNamesOf(client));
|
|
481
|
+
const missing = Object.entries(schema.delegates).filter(([, delegateName]) => !available.has(delegateName)).map(([role, delegateName]) => `${role} \u2192 '${delegateName}'`);
|
|
482
|
+
if (missing.length === 0) return;
|
|
483
|
+
throw new Error(`PrismaAdminResourcesAdapter is mapped to delegates the Prisma client does not have: ${missing.join(", ")}. The client offers: ${[
|
|
484
|
+
...available
|
|
485
|
+
].sort().join(", ") || "(none)"}. Map them with \`adminResources: { delegates: { tenant: "organization" } }\`, or implement \`AdminResourcesPort\` yourself if the shape differs rather than the names.`);
|
|
486
|
+
}
|
|
487
|
+
__name(assertDelegatesExist, "assertDelegatesExist");
|
|
488
|
+
function delegateNamesOf(client) {
|
|
489
|
+
if (!client || typeof client !== "object") return [];
|
|
490
|
+
return Object.keys(client).filter((key) => {
|
|
491
|
+
if (key.startsWith("$") || key.startsWith("_")) return false;
|
|
492
|
+
const value = client[key];
|
|
493
|
+
return typeof value === "object" && value !== null && typeof value.findMany === "function";
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
__name(delegateNamesOf, "delegateNamesOf");
|
|
497
|
+
|
|
437
498
|
// src/prisma-admin-resources.adapter.ts
|
|
438
499
|
var DEFAULT_LIST_LIMIT = 200;
|
|
439
500
|
var DEFAULT_AUDIT_LIMIT = 100;
|
|
@@ -445,41 +506,45 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
445
506
|
metrics;
|
|
446
507
|
listLimit;
|
|
447
508
|
audit;
|
|
509
|
+
schema;
|
|
448
510
|
constructor(prisma, options = {}) {
|
|
449
511
|
this.prisma = prisma;
|
|
450
|
-
this.metrics = options.tenantMetrics ?? [
|
|
451
|
-
"users"
|
|
452
|
-
];
|
|
453
512
|
this.listLimit = options.listLimit ?? DEFAULT_LIST_LIMIT;
|
|
454
513
|
this.audit = new PrismaAuditQueryAdapter(prisma);
|
|
514
|
+
this.schema = resolveAdminResourcesSchema(options.delegates, options.fields);
|
|
515
|
+
this.metrics = options.tenantMetrics ?? [
|
|
516
|
+
this.schema.tenant.users
|
|
517
|
+
];
|
|
518
|
+
if (options.delegates) assertDelegatesExist(prisma, this.schema);
|
|
455
519
|
}
|
|
456
520
|
async listTenants(filter) {
|
|
521
|
+
const t = this.schema.tenant;
|
|
457
522
|
const where = {};
|
|
458
523
|
const active = parseActiveFilter(filter.status);
|
|
459
|
-
if (active !== void 0) where.isActive = active;
|
|
524
|
+
if (active !== void 0) where[t.isActive] = active;
|
|
460
525
|
if (filter.search) {
|
|
461
526
|
where.OR = [
|
|
462
527
|
{
|
|
463
|
-
slug: {
|
|
528
|
+
[t.slug]: {
|
|
464
529
|
contains: filter.search,
|
|
465
530
|
mode: "insensitive"
|
|
466
531
|
}
|
|
467
532
|
},
|
|
468
533
|
{
|
|
469
|
-
name: {
|
|
534
|
+
[t.name]: {
|
|
470
535
|
contains: filter.search,
|
|
471
536
|
mode: "insensitive"
|
|
472
537
|
}
|
|
473
538
|
}
|
|
474
539
|
];
|
|
475
540
|
}
|
|
476
|
-
if (filter.plan) where.subscription = {
|
|
541
|
+
if (filter.plan) where[t.subscription] = {
|
|
477
542
|
plan: filter.plan
|
|
478
543
|
};
|
|
479
544
|
const rows = await this.tenants().findMany({
|
|
480
545
|
where,
|
|
481
546
|
include: {
|
|
482
|
-
subscription: {
|
|
547
|
+
[t.subscription]: {
|
|
483
548
|
select: {
|
|
484
549
|
plan: true,
|
|
485
550
|
status: true
|
|
@@ -496,24 +561,25 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
496
561
|
});
|
|
497
562
|
return rows.map((row) => ({
|
|
498
563
|
id: row.id,
|
|
499
|
-
slug: row.slug,
|
|
500
|
-
name: row.name,
|
|
501
|
-
isActive: row.isActive,
|
|
502
|
-
deletedAt: row.deletedAt?.toISOString() ?? null,
|
|
503
|
-
plan: row
|
|
504
|
-
status: row
|
|
564
|
+
slug: this.read(row, t.slug) ?? "",
|
|
565
|
+
name: this.read(row, t.name) ?? "",
|
|
566
|
+
isActive: this.read(row, t.isActive) ?? true,
|
|
567
|
+
deletedAt: this.read(row, t.deletedAt)?.toISOString() ?? null,
|
|
568
|
+
plan: this.subscriptionOf(row)?.plan ?? null,
|
|
569
|
+
status: this.subscriptionOf(row)?.status ?? null,
|
|
505
570
|
createdAt: row.createdAt.toISOString(),
|
|
506
571
|
...row._count ?? {}
|
|
507
572
|
}));
|
|
508
573
|
}
|
|
509
574
|
async getTenantDetail(slug) {
|
|
575
|
+
const t = this.schema.tenant;
|
|
510
576
|
const row = await this.tenants().findUnique({
|
|
511
577
|
where: {
|
|
512
|
-
slug
|
|
578
|
+
[t.slug]: slug
|
|
513
579
|
},
|
|
514
580
|
include: {
|
|
515
|
-
subscription: true,
|
|
516
|
-
users: {
|
|
581
|
+
[t.subscription]: true,
|
|
582
|
+
[t.users]: {
|
|
517
583
|
orderBy: {
|
|
518
584
|
createdAt: "asc"
|
|
519
585
|
}
|
|
@@ -526,31 +592,32 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
526
592
|
if (!row) return null;
|
|
527
593
|
return {
|
|
528
594
|
id: row.id,
|
|
529
|
-
slug: row.slug,
|
|
530
|
-
name: row.name,
|
|
531
|
-
isActive: row.isActive,
|
|
532
|
-
subscription: row
|
|
533
|
-
plan: row.
|
|
534
|
-
status: row.
|
|
535
|
-
billingCycle: row.
|
|
536
|
-
isPilot: row.
|
|
537
|
-
trialEndsAt: row.
|
|
538
|
-
pilotEndsAt: row.
|
|
595
|
+
slug: this.read(row, t.slug) ?? slug,
|
|
596
|
+
name: this.read(row, t.name) ?? "",
|
|
597
|
+
isActive: this.read(row, t.isActive) ?? true,
|
|
598
|
+
subscription: this.subscriptionOf(row) ? {
|
|
599
|
+
plan: this.subscriptionOf(row).plan,
|
|
600
|
+
status: this.subscriptionOf(row).status,
|
|
601
|
+
billingCycle: this.subscriptionOf(row).billingCycle,
|
|
602
|
+
isPilot: this.subscriptionOf(row).isPilot,
|
|
603
|
+
trialEndsAt: this.subscriptionOf(row).trialEndsAt?.toISOString() ?? null,
|
|
604
|
+
pilotEndsAt: this.subscriptionOf(row).pilotEndsAt?.toISOString() ?? null
|
|
539
605
|
} : null,
|
|
540
|
-
users: (row.users ?? []).map((user) => ({
|
|
606
|
+
users: (this.read(row, t.users) ?? []).map((user) => ({
|
|
541
607
|
id: user.id,
|
|
542
|
-
email: user.email,
|
|
543
|
-
firstName: user.firstName ?? void 0,
|
|
544
|
-
lastName: user.lastName ?? void 0,
|
|
608
|
+
email: this.read(user, this.schema.user.email) ?? "",
|
|
609
|
+
firstName: this.read(user, this.schema.user.firstName) ?? void 0,
|
|
610
|
+
lastName: this.read(user, this.schema.user.lastName) ?? void 0,
|
|
545
611
|
createdAt: user.createdAt.toISOString()
|
|
546
612
|
})),
|
|
547
613
|
counts: row._count ?? {}
|
|
548
614
|
};
|
|
549
615
|
}
|
|
550
616
|
async setTenantActive(slug, active, subscriptionStatus) {
|
|
617
|
+
const t = this.schema.tenant;
|
|
551
618
|
const tenant = await this.tenants().findUnique({
|
|
552
619
|
where: {
|
|
553
|
-
slug
|
|
620
|
+
[t.slug]: slug
|
|
554
621
|
}
|
|
555
622
|
});
|
|
556
623
|
if (!tenant) return null;
|
|
@@ -559,7 +626,7 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
559
626
|
id: tenant.id
|
|
560
627
|
},
|
|
561
628
|
data: {
|
|
562
|
-
isActive: active
|
|
629
|
+
[t.isActive]: active
|
|
563
630
|
}
|
|
564
631
|
});
|
|
565
632
|
await this.subscriptions().updateMany({
|
|
@@ -579,20 +646,22 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
579
646
|
};
|
|
580
647
|
}
|
|
581
648
|
async listUsers(filter) {
|
|
649
|
+
const u = this.schema.user;
|
|
650
|
+
const tenantSlug = this.schema.tenant.slug;
|
|
582
651
|
const where = {};
|
|
583
|
-
if (filter.q) where.email = {
|
|
652
|
+
if (filter.q) where[u.email] = {
|
|
584
653
|
contains: filter.q,
|
|
585
654
|
mode: "insensitive"
|
|
586
655
|
};
|
|
587
|
-
if (filter.tenant) where.tenant = {
|
|
588
|
-
|
|
656
|
+
if (filter.tenant) where[u.tenant] = {
|
|
657
|
+
[tenantSlug]: filter.tenant
|
|
589
658
|
};
|
|
590
659
|
const rows = await this.users().findMany({
|
|
591
660
|
where,
|
|
592
661
|
include: {
|
|
593
|
-
tenant: {
|
|
662
|
+
[u.tenant]: {
|
|
594
663
|
select: {
|
|
595
|
-
|
|
664
|
+
[tenantSlug]: true
|
|
596
665
|
}
|
|
597
666
|
}
|
|
598
667
|
},
|
|
@@ -603,12 +672,12 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
603
672
|
});
|
|
604
673
|
return rows.map((row) => ({
|
|
605
674
|
id: row.id,
|
|
606
|
-
email: row.email,
|
|
607
|
-
firstName: row.firstName ?? "",
|
|
608
|
-
lastName: row.lastName ?? "",
|
|
675
|
+
email: this.read(row, u.email) ?? "",
|
|
676
|
+
firstName: this.read(row, u.firstName) ?? "",
|
|
677
|
+
lastName: this.read(row, u.lastName) ?? "",
|
|
609
678
|
role: row.platformRole ?? row.role ?? "MEMBER",
|
|
610
|
-
isActive: row.isActive ?? true,
|
|
611
|
-
tenantSlug: row.tenant
|
|
679
|
+
isActive: this.read(row, u.isActive) ?? true,
|
|
680
|
+
tenantSlug: this.tenantSlugOf(row, u.tenant, tenantSlug),
|
|
612
681
|
lastLoginAt: row.lastLoginAt?.toISOString() ?? null,
|
|
613
682
|
createdAt: row.createdAt.toISOString()
|
|
614
683
|
}));
|
|
@@ -623,12 +692,13 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
623
692
|
});
|
|
624
693
|
}
|
|
625
694
|
async listSubscriptions() {
|
|
695
|
+
const t = this.schema.tenant;
|
|
626
696
|
const rows = await this.subscriptions().findMany({
|
|
627
697
|
include: {
|
|
628
698
|
tenant: {
|
|
629
699
|
select: {
|
|
630
|
-
slug: true,
|
|
631
|
-
name: true
|
|
700
|
+
[t.slug]: true,
|
|
701
|
+
[t.name]: true
|
|
632
702
|
}
|
|
633
703
|
},
|
|
634
704
|
planVersion: {
|
|
@@ -644,7 +714,10 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
644
714
|
});
|
|
645
715
|
return rows.map((row) => ({
|
|
646
716
|
id: row.id,
|
|
647
|
-
tenant:
|
|
717
|
+
tenant: {
|
|
718
|
+
slug: this.read(row.tenant, t.slug) ?? "",
|
|
719
|
+
name: this.read(row.tenant, t.name) ?? ""
|
|
720
|
+
},
|
|
648
721
|
plan: row.plan,
|
|
649
722
|
status: row.status,
|
|
650
723
|
billingCycle: row.billingCycle,
|
|
@@ -653,13 +726,32 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
653
726
|
}));
|
|
654
727
|
}
|
|
655
728
|
tenants() {
|
|
656
|
-
return getPrismaDelegate(this.prisma,
|
|
729
|
+
return getPrismaDelegate(this.prisma, this.schema.delegates.tenant);
|
|
657
730
|
}
|
|
658
731
|
users() {
|
|
659
|
-
return getPrismaDelegate(this.prisma,
|
|
732
|
+
return getPrismaDelegate(this.prisma, this.schema.delegates.user);
|
|
660
733
|
}
|
|
661
734
|
subscriptions() {
|
|
662
|
-
return getPrismaDelegate(this.prisma,
|
|
735
|
+
return getPrismaDelegate(this.prisma, this.schema.delegates.subscription);
|
|
736
|
+
}
|
|
737
|
+
/** The subscription relation of a tenant row, under whatever it is called. */
|
|
738
|
+
subscriptionOf(row) {
|
|
739
|
+
return this.read(row, this.schema.tenant.subscription) ?? null;
|
|
740
|
+
}
|
|
741
|
+
/** The tenant slug on a user row, through both mapped names. */
|
|
742
|
+
tenantSlugOf(row, relation, slugField) {
|
|
743
|
+
const tenant = this.read(row, relation);
|
|
744
|
+
return tenant ? this.read(tenant, slugField) ?? null : null;
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Reads a mapped field off a row.
|
|
748
|
+
*
|
|
749
|
+
* The rows come back keyed by the APP's field names, so every read of a
|
|
750
|
+
* mapped field goes through here — and the row type stays the shape the
|
|
751
|
+
* adapter promises rather than becoming `Record<string, unknown>`.
|
|
752
|
+
*/
|
|
753
|
+
read(row, field) {
|
|
754
|
+
return row[field];
|
|
663
755
|
}
|
|
664
756
|
};
|
|
665
757
|
function metricSelect(metrics) {
|
|
@@ -5016,9 +5108,11 @@ __name(buildProvisioning, "buildProvisioning");
|
|
|
5016
5108
|
PrismaTenantSubscriptionWriteAdapter,
|
|
5017
5109
|
PrismaTransactionRunner,
|
|
5018
5110
|
ZeroPromoRevenueDeductionAggregator,
|
|
5111
|
+
assertDelegatesExist,
|
|
5019
5112
|
buildActorTag,
|
|
5020
5113
|
createPrismaPlanBindingResolver,
|
|
5021
5114
|
getPrismaDelegate,
|
|
5022
5115
|
prismaPersistence,
|
|
5116
|
+
resolveAdminResourcesSchema,
|
|
5023
5117
|
resolvePrismaSchemaOptions
|
|
5024
5118
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -703,6 +703,81 @@ declare function createPrismaPlanBindingResolver(options?: PrismaPlanBindingOpti
|
|
|
703
703
|
/** Resolves a configurable Prisma delegate and fails early on misspellings. */
|
|
704
704
|
declare function getPrismaDelegate<Row>(client: unknown, delegateName: string): PrismaModelDelegateLike<Row>;
|
|
705
705
|
|
|
706
|
+
/** The Prisma delegates the adapter needs, by the name your client uses. */
|
|
707
|
+
interface AdminResourcesDelegates {
|
|
708
|
+
/** Default `tenant`. */
|
|
709
|
+
tenant?: string;
|
|
710
|
+
/** Default `user`. */
|
|
711
|
+
user?: string;
|
|
712
|
+
/** Default `subscription`. */
|
|
713
|
+
subscription?: string;
|
|
714
|
+
}
|
|
715
|
+
/** The scalar and relation fields the adapter reads, by your name for them. */
|
|
716
|
+
interface AdminResourcesFields {
|
|
717
|
+
tenant?: {
|
|
718
|
+
/** Default `slug` — the stable identifier every admin route addresses. */
|
|
719
|
+
slug?: string;
|
|
720
|
+
/** Default `name`. */
|
|
721
|
+
name?: string;
|
|
722
|
+
/** Default `isActive`. */
|
|
723
|
+
isActive?: string;
|
|
724
|
+
/** Default `deletedAt`. */
|
|
725
|
+
deletedAt?: string;
|
|
726
|
+
/** The tenant→users relation. Default `users`. */
|
|
727
|
+
users?: string;
|
|
728
|
+
/**
|
|
729
|
+
* The tenant→subscription relation. Default `subscription`.
|
|
730
|
+
*
|
|
731
|
+
* On YOUR model, so your name for it: Prisma names a relation field
|
|
732
|
+
* after what it points at, and an app whose tenant is `Organization`
|
|
733
|
+
* has usually not renamed this one — but it may have.
|
|
734
|
+
*/
|
|
735
|
+
subscription?: string;
|
|
736
|
+
};
|
|
737
|
+
user?: {
|
|
738
|
+
/** Default `email`. */
|
|
739
|
+
email?: string;
|
|
740
|
+
/** Default `firstName`. */
|
|
741
|
+
firstName?: string;
|
|
742
|
+
/** Default `lastName`. */
|
|
743
|
+
lastName?: string;
|
|
744
|
+
/** Default `isActive`. */
|
|
745
|
+
isActive?: string;
|
|
746
|
+
/**
|
|
747
|
+
* The user→tenant relation. Default `tenant`.
|
|
748
|
+
*
|
|
749
|
+
* The one the documented example gets wrong by omission: an app whose
|
|
750
|
+
* tenant model is `Organization` calls this field `organization`, by
|
|
751
|
+
* the same Prisma convention that named the delegate.
|
|
752
|
+
*/
|
|
753
|
+
tenant?: string;
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
/** The mapping with every default filled in — what the adapter actually reads. */
|
|
757
|
+
interface ResolvedAdminResourcesSchema {
|
|
758
|
+
readonly delegates: Required<AdminResourcesDelegates>;
|
|
759
|
+
readonly tenant: Required<NonNullable<AdminResourcesFields['tenant']>>;
|
|
760
|
+
readonly user: Required<NonNullable<AdminResourcesFields['user']>>;
|
|
761
|
+
}
|
|
762
|
+
declare function resolveAdminResourcesSchema(delegates?: AdminResourcesDelegates, fields?: AdminResourcesFields): ResolvedAdminResourcesSchema;
|
|
763
|
+
/**
|
|
764
|
+
* Refuses a mapping that names a delegate the client does not have.
|
|
765
|
+
*
|
|
766
|
+
* At construction, which is boot: a mapped delegate is resolved lazily, so a
|
|
767
|
+
* typo used to surface as a 500 on the tenants page long after deployment —
|
|
768
|
+
* and only if somebody opened it. The message lists what the client does have,
|
|
769
|
+
* because the answer is almost always a near miss (`organisation` for
|
|
770
|
+
* `organization`, `users` for `user`).
|
|
771
|
+
*
|
|
772
|
+
* Only for an EXPLICIT mapping. `prismaPersistence()` builds this adapter for
|
|
773
|
+
* every bundle, including for applications that never switch `adminResources`
|
|
774
|
+
* on, and several of them have no `tenant` delegate at all — asserting the
|
|
775
|
+
* defaults here would refuse to construct a bundle nobody was going to ask
|
|
776
|
+
* about. Those keep failing where they always did, at the first request, with
|
|
777
|
+
* a message that now names the way out.
|
|
778
|
+
*/
|
|
779
|
+
declare function assertDelegatesExist(client: unknown, schema: ResolvedAdminResourcesSchema): void;
|
|
780
|
+
|
|
706
781
|
interface PrismaAdminResourcesOptions {
|
|
707
782
|
/**
|
|
708
783
|
* Relation counters exposed on tenant rows and tenant details. The names
|
|
@@ -710,6 +785,13 @@ interface PrismaAdminResourcesOptions {
|
|
|
710
785
|
*/
|
|
711
786
|
tenantMetrics?: readonly string[];
|
|
712
787
|
listLimit?: number;
|
|
788
|
+
/**
|
|
789
|
+
* Delegate names, when the app's models are not called `tenant`, `user`
|
|
790
|
+
* and `subscription`. Checked against the client at construction.
|
|
791
|
+
*/
|
|
792
|
+
delegates?: AdminResourcesDelegates;
|
|
793
|
+
/** Field names, when the app's columns differ. See `AdminResourcesFields`. */
|
|
794
|
+
fields?: AdminResourcesFields;
|
|
713
795
|
}
|
|
714
796
|
/**
|
|
715
797
|
* Convention-based adapter for the standard SuperAdmin resource pages.
|
|
@@ -724,6 +806,7 @@ declare class PrismaAdminResourcesAdapter implements AdminResourcesPort {
|
|
|
724
806
|
private readonly metrics;
|
|
725
807
|
private readonly listLimit;
|
|
726
808
|
private readonly audit;
|
|
809
|
+
private readonly schema;
|
|
727
810
|
constructor(prisma: PrismaLike, options?: PrismaAdminResourcesOptions);
|
|
728
811
|
listTenants(filter: AdminTenantListFilter): Promise<AdminTenantListRow[]>;
|
|
729
812
|
getTenantDetail(slug: string): Promise<AdminTenantDetail | null>;
|
|
@@ -734,6 +817,18 @@ declare class PrismaAdminResourcesAdapter implements AdminResourcesPort {
|
|
|
734
817
|
private tenants;
|
|
735
818
|
private users;
|
|
736
819
|
private subscriptions;
|
|
820
|
+
/** The subscription relation of a tenant row, under whatever it is called. */
|
|
821
|
+
private subscriptionOf;
|
|
822
|
+
/** The tenant slug on a user row, through both mapped names. */
|
|
823
|
+
private tenantSlugOf;
|
|
824
|
+
/**
|
|
825
|
+
* Reads a mapped field off a row.
|
|
826
|
+
*
|
|
827
|
+
* The rows come back keyed by the APP's field names, so every read of a
|
|
828
|
+
* mapped field goes through here — and the row type stays the shape the
|
|
829
|
+
* adapter promises rather than becoming `Record<string, unknown>`.
|
|
830
|
+
*/
|
|
831
|
+
private read;
|
|
737
832
|
}
|
|
738
833
|
|
|
739
834
|
/**
|
|
@@ -1461,4 +1556,4 @@ declare class PrismaSubscriptionContractRepository implements SubscriptionContra
|
|
|
1461
1556
|
terminate(contractId: string, data: TerminateSubscriptionContractData): Promise<SubscriptionContractRecord>;
|
|
1462
1557
|
}
|
|
1463
1558
|
|
|
1464
|
-
export { AsyncLocalRlsBypassAdapter, type DecimalLike, PASSWORD_HASHER_TOKEN, PRISMA_BUNDLE_REPOSITORY_OPTIONS, PRISMA_CLIENT_TOKEN, PRISMA_SCHEMA_OPTIONS_TOKEN, PrismaAdminResourcesAdapter, type PrismaAdminResourcesOptions, PrismaAuditAdapter, PrismaAuditQueryAdapter, PrismaAuditStatsAdapter, PrismaBundleRepository, type PrismaBundleRepositoryOptions, PrismaCatalogEntryRepository, type PrismaLike, PrismaMarketingProjectionRepository, PrismaMarketingSettingsRepository, PrismaMfaAdapter, type PrismaModelDelegateLike, type PrismaPersistenceOptions, type PrismaPlanBindingMode, type PrismaPlanBindingOptions, type PrismaPlanBindingResolver, PrismaPlanCatalogImportSink, PrismaPlanCatalogReadSink, type PrismaPlanDelegateOptions, PrismaPlanRepository, type PrismaPlanVersionFieldCapabilities, type PrismaPlanVersionFieldOptions, PrismaPlanVersionRepository, PrismaPromoCodeRedemptionRepository, PrismaPromoCodeRepository, PrismaPromoCodeValidationLogRepository, PrismaPromoSubscriptionLookup, PrismaPromotionRepository, type PrismaSchemaOptions, PrismaSubscriptionBundleRepository, PrismaSubscriptionContractRepository, PrismaSubscriptionRepository, PrismaSubscriptionUsageAdapter, PrismaSuperAdminBootstrapAdapter, type PrismaTenantSubscriptionOptions, PrismaTenantSubscriptionWriteAdapter, PrismaTransactionRunner, type PrismaTxLike, type ResolvedPrismaSchemaOptions, ZeroPromoRevenueDeductionAggregator, buildActorTag, createPrismaPlanBindingResolver, getPrismaDelegate, prismaPersistence, resolvePrismaSchemaOptions };
|
|
1559
|
+
export { type AdminResourcesDelegates, type AdminResourcesFields, AsyncLocalRlsBypassAdapter, type DecimalLike, PASSWORD_HASHER_TOKEN, PRISMA_BUNDLE_REPOSITORY_OPTIONS, PRISMA_CLIENT_TOKEN, PRISMA_SCHEMA_OPTIONS_TOKEN, PrismaAdminResourcesAdapter, type PrismaAdminResourcesOptions, PrismaAuditAdapter, PrismaAuditQueryAdapter, PrismaAuditStatsAdapter, PrismaBundleRepository, type PrismaBundleRepositoryOptions, PrismaCatalogEntryRepository, type PrismaLike, PrismaMarketingProjectionRepository, PrismaMarketingSettingsRepository, PrismaMfaAdapter, type PrismaModelDelegateLike, type PrismaPersistenceOptions, type PrismaPlanBindingMode, type PrismaPlanBindingOptions, type PrismaPlanBindingResolver, PrismaPlanCatalogImportSink, PrismaPlanCatalogReadSink, type PrismaPlanDelegateOptions, PrismaPlanRepository, type PrismaPlanVersionFieldCapabilities, type PrismaPlanVersionFieldOptions, PrismaPlanVersionRepository, PrismaPromoCodeRedemptionRepository, PrismaPromoCodeRepository, PrismaPromoCodeValidationLogRepository, PrismaPromoSubscriptionLookup, PrismaPromotionRepository, type PrismaSchemaOptions, PrismaSubscriptionBundleRepository, PrismaSubscriptionContractRepository, PrismaSubscriptionRepository, PrismaSubscriptionUsageAdapter, PrismaSuperAdminBootstrapAdapter, type PrismaTenantSubscriptionOptions, PrismaTenantSubscriptionWriteAdapter, PrismaTransactionRunner, type PrismaTxLike, type ResolvedAdminResourcesSchema, type ResolvedPrismaSchemaOptions, ZeroPromoRevenueDeductionAggregator, assertDelegatesExist, buildActorTag, createPrismaPlanBindingResolver, getPrismaDelegate, prismaPersistence, resolveAdminResourcesSchema, resolvePrismaSchemaOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -703,6 +703,81 @@ declare function createPrismaPlanBindingResolver(options?: PrismaPlanBindingOpti
|
|
|
703
703
|
/** Resolves a configurable Prisma delegate and fails early on misspellings. */
|
|
704
704
|
declare function getPrismaDelegate<Row>(client: unknown, delegateName: string): PrismaModelDelegateLike<Row>;
|
|
705
705
|
|
|
706
|
+
/** The Prisma delegates the adapter needs, by the name your client uses. */
|
|
707
|
+
interface AdminResourcesDelegates {
|
|
708
|
+
/** Default `tenant`. */
|
|
709
|
+
tenant?: string;
|
|
710
|
+
/** Default `user`. */
|
|
711
|
+
user?: string;
|
|
712
|
+
/** Default `subscription`. */
|
|
713
|
+
subscription?: string;
|
|
714
|
+
}
|
|
715
|
+
/** The scalar and relation fields the adapter reads, by your name for them. */
|
|
716
|
+
interface AdminResourcesFields {
|
|
717
|
+
tenant?: {
|
|
718
|
+
/** Default `slug` — the stable identifier every admin route addresses. */
|
|
719
|
+
slug?: string;
|
|
720
|
+
/** Default `name`. */
|
|
721
|
+
name?: string;
|
|
722
|
+
/** Default `isActive`. */
|
|
723
|
+
isActive?: string;
|
|
724
|
+
/** Default `deletedAt`. */
|
|
725
|
+
deletedAt?: string;
|
|
726
|
+
/** The tenant→users relation. Default `users`. */
|
|
727
|
+
users?: string;
|
|
728
|
+
/**
|
|
729
|
+
* The tenant→subscription relation. Default `subscription`.
|
|
730
|
+
*
|
|
731
|
+
* On YOUR model, so your name for it: Prisma names a relation field
|
|
732
|
+
* after what it points at, and an app whose tenant is `Organization`
|
|
733
|
+
* has usually not renamed this one — but it may have.
|
|
734
|
+
*/
|
|
735
|
+
subscription?: string;
|
|
736
|
+
};
|
|
737
|
+
user?: {
|
|
738
|
+
/** Default `email`. */
|
|
739
|
+
email?: string;
|
|
740
|
+
/** Default `firstName`. */
|
|
741
|
+
firstName?: string;
|
|
742
|
+
/** Default `lastName`. */
|
|
743
|
+
lastName?: string;
|
|
744
|
+
/** Default `isActive`. */
|
|
745
|
+
isActive?: string;
|
|
746
|
+
/**
|
|
747
|
+
* The user→tenant relation. Default `tenant`.
|
|
748
|
+
*
|
|
749
|
+
* The one the documented example gets wrong by omission: an app whose
|
|
750
|
+
* tenant model is `Organization` calls this field `organization`, by
|
|
751
|
+
* the same Prisma convention that named the delegate.
|
|
752
|
+
*/
|
|
753
|
+
tenant?: string;
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
/** The mapping with every default filled in — what the adapter actually reads. */
|
|
757
|
+
interface ResolvedAdminResourcesSchema {
|
|
758
|
+
readonly delegates: Required<AdminResourcesDelegates>;
|
|
759
|
+
readonly tenant: Required<NonNullable<AdminResourcesFields['tenant']>>;
|
|
760
|
+
readonly user: Required<NonNullable<AdminResourcesFields['user']>>;
|
|
761
|
+
}
|
|
762
|
+
declare function resolveAdminResourcesSchema(delegates?: AdminResourcesDelegates, fields?: AdminResourcesFields): ResolvedAdminResourcesSchema;
|
|
763
|
+
/**
|
|
764
|
+
* Refuses a mapping that names a delegate the client does not have.
|
|
765
|
+
*
|
|
766
|
+
* At construction, which is boot: a mapped delegate is resolved lazily, so a
|
|
767
|
+
* typo used to surface as a 500 on the tenants page long after deployment —
|
|
768
|
+
* and only if somebody opened it. The message lists what the client does have,
|
|
769
|
+
* because the answer is almost always a near miss (`organisation` for
|
|
770
|
+
* `organization`, `users` for `user`).
|
|
771
|
+
*
|
|
772
|
+
* Only for an EXPLICIT mapping. `prismaPersistence()` builds this adapter for
|
|
773
|
+
* every bundle, including for applications that never switch `adminResources`
|
|
774
|
+
* on, and several of them have no `tenant` delegate at all — asserting the
|
|
775
|
+
* defaults here would refuse to construct a bundle nobody was going to ask
|
|
776
|
+
* about. Those keep failing where they always did, at the first request, with
|
|
777
|
+
* a message that now names the way out.
|
|
778
|
+
*/
|
|
779
|
+
declare function assertDelegatesExist(client: unknown, schema: ResolvedAdminResourcesSchema): void;
|
|
780
|
+
|
|
706
781
|
interface PrismaAdminResourcesOptions {
|
|
707
782
|
/**
|
|
708
783
|
* Relation counters exposed on tenant rows and tenant details. The names
|
|
@@ -710,6 +785,13 @@ interface PrismaAdminResourcesOptions {
|
|
|
710
785
|
*/
|
|
711
786
|
tenantMetrics?: readonly string[];
|
|
712
787
|
listLimit?: number;
|
|
788
|
+
/**
|
|
789
|
+
* Delegate names, when the app's models are not called `tenant`, `user`
|
|
790
|
+
* and `subscription`. Checked against the client at construction.
|
|
791
|
+
*/
|
|
792
|
+
delegates?: AdminResourcesDelegates;
|
|
793
|
+
/** Field names, when the app's columns differ. See `AdminResourcesFields`. */
|
|
794
|
+
fields?: AdminResourcesFields;
|
|
713
795
|
}
|
|
714
796
|
/**
|
|
715
797
|
* Convention-based adapter for the standard SuperAdmin resource pages.
|
|
@@ -724,6 +806,7 @@ declare class PrismaAdminResourcesAdapter implements AdminResourcesPort {
|
|
|
724
806
|
private readonly metrics;
|
|
725
807
|
private readonly listLimit;
|
|
726
808
|
private readonly audit;
|
|
809
|
+
private readonly schema;
|
|
727
810
|
constructor(prisma: PrismaLike, options?: PrismaAdminResourcesOptions);
|
|
728
811
|
listTenants(filter: AdminTenantListFilter): Promise<AdminTenantListRow[]>;
|
|
729
812
|
getTenantDetail(slug: string): Promise<AdminTenantDetail | null>;
|
|
@@ -734,6 +817,18 @@ declare class PrismaAdminResourcesAdapter implements AdminResourcesPort {
|
|
|
734
817
|
private tenants;
|
|
735
818
|
private users;
|
|
736
819
|
private subscriptions;
|
|
820
|
+
/** The subscription relation of a tenant row, under whatever it is called. */
|
|
821
|
+
private subscriptionOf;
|
|
822
|
+
/** The tenant slug on a user row, through both mapped names. */
|
|
823
|
+
private tenantSlugOf;
|
|
824
|
+
/**
|
|
825
|
+
* Reads a mapped field off a row.
|
|
826
|
+
*
|
|
827
|
+
* The rows come back keyed by the APP's field names, so every read of a
|
|
828
|
+
* mapped field goes through here — and the row type stays the shape the
|
|
829
|
+
* adapter promises rather than becoming `Record<string, unknown>`.
|
|
830
|
+
*/
|
|
831
|
+
private read;
|
|
737
832
|
}
|
|
738
833
|
|
|
739
834
|
/**
|
|
@@ -1461,4 +1556,4 @@ declare class PrismaSubscriptionContractRepository implements SubscriptionContra
|
|
|
1461
1556
|
terminate(contractId: string, data: TerminateSubscriptionContractData): Promise<SubscriptionContractRecord>;
|
|
1462
1557
|
}
|
|
1463
1558
|
|
|
1464
|
-
export { AsyncLocalRlsBypassAdapter, type DecimalLike, PASSWORD_HASHER_TOKEN, PRISMA_BUNDLE_REPOSITORY_OPTIONS, PRISMA_CLIENT_TOKEN, PRISMA_SCHEMA_OPTIONS_TOKEN, PrismaAdminResourcesAdapter, type PrismaAdminResourcesOptions, PrismaAuditAdapter, PrismaAuditQueryAdapter, PrismaAuditStatsAdapter, PrismaBundleRepository, type PrismaBundleRepositoryOptions, PrismaCatalogEntryRepository, type PrismaLike, PrismaMarketingProjectionRepository, PrismaMarketingSettingsRepository, PrismaMfaAdapter, type PrismaModelDelegateLike, type PrismaPersistenceOptions, type PrismaPlanBindingMode, type PrismaPlanBindingOptions, type PrismaPlanBindingResolver, PrismaPlanCatalogImportSink, PrismaPlanCatalogReadSink, type PrismaPlanDelegateOptions, PrismaPlanRepository, type PrismaPlanVersionFieldCapabilities, type PrismaPlanVersionFieldOptions, PrismaPlanVersionRepository, PrismaPromoCodeRedemptionRepository, PrismaPromoCodeRepository, PrismaPromoCodeValidationLogRepository, PrismaPromoSubscriptionLookup, PrismaPromotionRepository, type PrismaSchemaOptions, PrismaSubscriptionBundleRepository, PrismaSubscriptionContractRepository, PrismaSubscriptionRepository, PrismaSubscriptionUsageAdapter, PrismaSuperAdminBootstrapAdapter, type PrismaTenantSubscriptionOptions, PrismaTenantSubscriptionWriteAdapter, PrismaTransactionRunner, type PrismaTxLike, type ResolvedPrismaSchemaOptions, ZeroPromoRevenueDeductionAggregator, buildActorTag, createPrismaPlanBindingResolver, getPrismaDelegate, prismaPersistence, resolvePrismaSchemaOptions };
|
|
1559
|
+
export { type AdminResourcesDelegates, type AdminResourcesFields, AsyncLocalRlsBypassAdapter, type DecimalLike, PASSWORD_HASHER_TOKEN, PRISMA_BUNDLE_REPOSITORY_OPTIONS, PRISMA_CLIENT_TOKEN, PRISMA_SCHEMA_OPTIONS_TOKEN, PrismaAdminResourcesAdapter, type PrismaAdminResourcesOptions, PrismaAuditAdapter, PrismaAuditQueryAdapter, PrismaAuditStatsAdapter, PrismaBundleRepository, type PrismaBundleRepositoryOptions, PrismaCatalogEntryRepository, type PrismaLike, PrismaMarketingProjectionRepository, PrismaMarketingSettingsRepository, PrismaMfaAdapter, type PrismaModelDelegateLike, type PrismaPersistenceOptions, type PrismaPlanBindingMode, type PrismaPlanBindingOptions, type PrismaPlanBindingResolver, PrismaPlanCatalogImportSink, PrismaPlanCatalogReadSink, type PrismaPlanDelegateOptions, PrismaPlanRepository, type PrismaPlanVersionFieldCapabilities, type PrismaPlanVersionFieldOptions, PrismaPlanVersionRepository, PrismaPromoCodeRedemptionRepository, PrismaPromoCodeRepository, PrismaPromoCodeValidationLogRepository, PrismaPromoSubscriptionLookup, PrismaPromotionRepository, type PrismaSchemaOptions, PrismaSubscriptionBundleRepository, PrismaSubscriptionContractRepository, PrismaSubscriptionRepository, PrismaSubscriptionUsageAdapter, PrismaSuperAdminBootstrapAdapter, type PrismaTenantSubscriptionOptions, PrismaTenantSubscriptionWriteAdapter, PrismaTransactionRunner, type PrismaTxLike, type ResolvedAdminResourcesSchema, type ResolvedPrismaSchemaOptions, ZeroPromoRevenueDeductionAggregator, assertDelegatesExist, buildActorTag, createPrismaPlanBindingResolver, getPrismaDelegate, prismaPersistence, resolveAdminResourcesSchema, resolvePrismaSchemaOptions };
|
package/dist/index.js
CHANGED
|
@@ -351,7 +351,7 @@ __name(createPrismaPlanBindingResolver, "createPrismaPlanBindingResolver");
|
|
|
351
351
|
function getPrismaDelegate(client, delegateName) {
|
|
352
352
|
const delegate = client?.[delegateName];
|
|
353
353
|
if (!delegate || typeof delegate !== "object") {
|
|
354
|
-
throw new Error(`Prisma client has no '${delegateName}' delegate.`);
|
|
354
|
+
throw new Error(`Prisma client has no '${delegateName}' delegate. If your model is called something else, map it \u2014 \`adminResources: { delegates: { tenant: "organization" } }\` for the SuperAdmin resources, \`schema\` for the plan binding.`);
|
|
355
355
|
}
|
|
356
356
|
return delegate;
|
|
357
357
|
}
|
|
@@ -375,6 +375,65 @@ function asPlanIdentityClient(client) {
|
|
|
375
375
|
}
|
|
376
376
|
__name(asPlanIdentityClient, "asPlanIdentityClient");
|
|
377
377
|
|
|
378
|
+
// src/admin-resources-schema.ts
|
|
379
|
+
var DEFAULTS = {
|
|
380
|
+
delegates: {
|
|
381
|
+
tenant: "tenant",
|
|
382
|
+
user: "user",
|
|
383
|
+
subscription: "subscription"
|
|
384
|
+
},
|
|
385
|
+
tenant: {
|
|
386
|
+
slug: "slug",
|
|
387
|
+
name: "name",
|
|
388
|
+
isActive: "isActive",
|
|
389
|
+
deletedAt: "deletedAt",
|
|
390
|
+
users: "users",
|
|
391
|
+
subscription: "subscription"
|
|
392
|
+
},
|
|
393
|
+
user: {
|
|
394
|
+
email: "email",
|
|
395
|
+
firstName: "firstName",
|
|
396
|
+
lastName: "lastName",
|
|
397
|
+
isActive: "isActive",
|
|
398
|
+
tenant: "tenant"
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
function resolveAdminResourcesSchema(delegates = {}, fields = {}) {
|
|
402
|
+
return {
|
|
403
|
+
delegates: {
|
|
404
|
+
...DEFAULTS.delegates,
|
|
405
|
+
...delegates
|
|
406
|
+
},
|
|
407
|
+
tenant: {
|
|
408
|
+
...DEFAULTS.tenant,
|
|
409
|
+
...fields.tenant
|
|
410
|
+
},
|
|
411
|
+
user: {
|
|
412
|
+
...DEFAULTS.user,
|
|
413
|
+
...fields.user
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
__name(resolveAdminResourcesSchema, "resolveAdminResourcesSchema");
|
|
418
|
+
function assertDelegatesExist(client, schema) {
|
|
419
|
+
const available = new Set(delegateNamesOf(client));
|
|
420
|
+
const missing = Object.entries(schema.delegates).filter(([, delegateName]) => !available.has(delegateName)).map(([role, delegateName]) => `${role} \u2192 '${delegateName}'`);
|
|
421
|
+
if (missing.length === 0) return;
|
|
422
|
+
throw new Error(`PrismaAdminResourcesAdapter is mapped to delegates the Prisma client does not have: ${missing.join(", ")}. The client offers: ${[
|
|
423
|
+
...available
|
|
424
|
+
].sort().join(", ") || "(none)"}. Map them with \`adminResources: { delegates: { tenant: "organization" } }\`, or implement \`AdminResourcesPort\` yourself if the shape differs rather than the names.`);
|
|
425
|
+
}
|
|
426
|
+
__name(assertDelegatesExist, "assertDelegatesExist");
|
|
427
|
+
function delegateNamesOf(client) {
|
|
428
|
+
if (!client || typeof client !== "object") return [];
|
|
429
|
+
return Object.keys(client).filter((key) => {
|
|
430
|
+
if (key.startsWith("$") || key.startsWith("_")) return false;
|
|
431
|
+
const value = client[key];
|
|
432
|
+
return typeof value === "object" && value !== null && typeof value.findMany === "function";
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
__name(delegateNamesOf, "delegateNamesOf");
|
|
436
|
+
|
|
378
437
|
// src/prisma-admin-resources.adapter.ts
|
|
379
438
|
var DEFAULT_LIST_LIMIT = 200;
|
|
380
439
|
var DEFAULT_AUDIT_LIMIT = 100;
|
|
@@ -386,41 +445,45 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
386
445
|
metrics;
|
|
387
446
|
listLimit;
|
|
388
447
|
audit;
|
|
448
|
+
schema;
|
|
389
449
|
constructor(prisma, options = {}) {
|
|
390
450
|
this.prisma = prisma;
|
|
391
|
-
this.metrics = options.tenantMetrics ?? [
|
|
392
|
-
"users"
|
|
393
|
-
];
|
|
394
451
|
this.listLimit = options.listLimit ?? DEFAULT_LIST_LIMIT;
|
|
395
452
|
this.audit = new PrismaAuditQueryAdapter(prisma);
|
|
453
|
+
this.schema = resolveAdminResourcesSchema(options.delegates, options.fields);
|
|
454
|
+
this.metrics = options.tenantMetrics ?? [
|
|
455
|
+
this.schema.tenant.users
|
|
456
|
+
];
|
|
457
|
+
if (options.delegates) assertDelegatesExist(prisma, this.schema);
|
|
396
458
|
}
|
|
397
459
|
async listTenants(filter) {
|
|
460
|
+
const t = this.schema.tenant;
|
|
398
461
|
const where = {};
|
|
399
462
|
const active = parseActiveFilter(filter.status);
|
|
400
|
-
if (active !== void 0) where.isActive = active;
|
|
463
|
+
if (active !== void 0) where[t.isActive] = active;
|
|
401
464
|
if (filter.search) {
|
|
402
465
|
where.OR = [
|
|
403
466
|
{
|
|
404
|
-
slug: {
|
|
467
|
+
[t.slug]: {
|
|
405
468
|
contains: filter.search,
|
|
406
469
|
mode: "insensitive"
|
|
407
470
|
}
|
|
408
471
|
},
|
|
409
472
|
{
|
|
410
|
-
name: {
|
|
473
|
+
[t.name]: {
|
|
411
474
|
contains: filter.search,
|
|
412
475
|
mode: "insensitive"
|
|
413
476
|
}
|
|
414
477
|
}
|
|
415
478
|
];
|
|
416
479
|
}
|
|
417
|
-
if (filter.plan) where.subscription = {
|
|
480
|
+
if (filter.plan) where[t.subscription] = {
|
|
418
481
|
plan: filter.plan
|
|
419
482
|
};
|
|
420
483
|
const rows = await this.tenants().findMany({
|
|
421
484
|
where,
|
|
422
485
|
include: {
|
|
423
|
-
subscription: {
|
|
486
|
+
[t.subscription]: {
|
|
424
487
|
select: {
|
|
425
488
|
plan: true,
|
|
426
489
|
status: true
|
|
@@ -437,24 +500,25 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
437
500
|
});
|
|
438
501
|
return rows.map((row) => ({
|
|
439
502
|
id: row.id,
|
|
440
|
-
slug: row.slug,
|
|
441
|
-
name: row.name,
|
|
442
|
-
isActive: row.isActive,
|
|
443
|
-
deletedAt: row.deletedAt?.toISOString() ?? null,
|
|
444
|
-
plan: row
|
|
445
|
-
status: row
|
|
503
|
+
slug: this.read(row, t.slug) ?? "",
|
|
504
|
+
name: this.read(row, t.name) ?? "",
|
|
505
|
+
isActive: this.read(row, t.isActive) ?? true,
|
|
506
|
+
deletedAt: this.read(row, t.deletedAt)?.toISOString() ?? null,
|
|
507
|
+
plan: this.subscriptionOf(row)?.plan ?? null,
|
|
508
|
+
status: this.subscriptionOf(row)?.status ?? null,
|
|
446
509
|
createdAt: row.createdAt.toISOString(),
|
|
447
510
|
...row._count ?? {}
|
|
448
511
|
}));
|
|
449
512
|
}
|
|
450
513
|
async getTenantDetail(slug) {
|
|
514
|
+
const t = this.schema.tenant;
|
|
451
515
|
const row = await this.tenants().findUnique({
|
|
452
516
|
where: {
|
|
453
|
-
slug
|
|
517
|
+
[t.slug]: slug
|
|
454
518
|
},
|
|
455
519
|
include: {
|
|
456
|
-
subscription: true,
|
|
457
|
-
users: {
|
|
520
|
+
[t.subscription]: true,
|
|
521
|
+
[t.users]: {
|
|
458
522
|
orderBy: {
|
|
459
523
|
createdAt: "asc"
|
|
460
524
|
}
|
|
@@ -467,31 +531,32 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
467
531
|
if (!row) return null;
|
|
468
532
|
return {
|
|
469
533
|
id: row.id,
|
|
470
|
-
slug: row.slug,
|
|
471
|
-
name: row.name,
|
|
472
|
-
isActive: row.isActive,
|
|
473
|
-
subscription: row
|
|
474
|
-
plan: row.
|
|
475
|
-
status: row.
|
|
476
|
-
billingCycle: row.
|
|
477
|
-
isPilot: row.
|
|
478
|
-
trialEndsAt: row.
|
|
479
|
-
pilotEndsAt: row.
|
|
534
|
+
slug: this.read(row, t.slug) ?? slug,
|
|
535
|
+
name: this.read(row, t.name) ?? "",
|
|
536
|
+
isActive: this.read(row, t.isActive) ?? true,
|
|
537
|
+
subscription: this.subscriptionOf(row) ? {
|
|
538
|
+
plan: this.subscriptionOf(row).plan,
|
|
539
|
+
status: this.subscriptionOf(row).status,
|
|
540
|
+
billingCycle: this.subscriptionOf(row).billingCycle,
|
|
541
|
+
isPilot: this.subscriptionOf(row).isPilot,
|
|
542
|
+
trialEndsAt: this.subscriptionOf(row).trialEndsAt?.toISOString() ?? null,
|
|
543
|
+
pilotEndsAt: this.subscriptionOf(row).pilotEndsAt?.toISOString() ?? null
|
|
480
544
|
} : null,
|
|
481
|
-
users: (row.users ?? []).map((user) => ({
|
|
545
|
+
users: (this.read(row, t.users) ?? []).map((user) => ({
|
|
482
546
|
id: user.id,
|
|
483
|
-
email: user.email,
|
|
484
|
-
firstName: user.firstName ?? void 0,
|
|
485
|
-
lastName: user.lastName ?? void 0,
|
|
547
|
+
email: this.read(user, this.schema.user.email) ?? "",
|
|
548
|
+
firstName: this.read(user, this.schema.user.firstName) ?? void 0,
|
|
549
|
+
lastName: this.read(user, this.schema.user.lastName) ?? void 0,
|
|
486
550
|
createdAt: user.createdAt.toISOString()
|
|
487
551
|
})),
|
|
488
552
|
counts: row._count ?? {}
|
|
489
553
|
};
|
|
490
554
|
}
|
|
491
555
|
async setTenantActive(slug, active, subscriptionStatus) {
|
|
556
|
+
const t = this.schema.tenant;
|
|
492
557
|
const tenant = await this.tenants().findUnique({
|
|
493
558
|
where: {
|
|
494
|
-
slug
|
|
559
|
+
[t.slug]: slug
|
|
495
560
|
}
|
|
496
561
|
});
|
|
497
562
|
if (!tenant) return null;
|
|
@@ -500,7 +565,7 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
500
565
|
id: tenant.id
|
|
501
566
|
},
|
|
502
567
|
data: {
|
|
503
|
-
isActive: active
|
|
568
|
+
[t.isActive]: active
|
|
504
569
|
}
|
|
505
570
|
});
|
|
506
571
|
await this.subscriptions().updateMany({
|
|
@@ -520,20 +585,22 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
520
585
|
};
|
|
521
586
|
}
|
|
522
587
|
async listUsers(filter) {
|
|
588
|
+
const u = this.schema.user;
|
|
589
|
+
const tenantSlug = this.schema.tenant.slug;
|
|
523
590
|
const where = {};
|
|
524
|
-
if (filter.q) where.email = {
|
|
591
|
+
if (filter.q) where[u.email] = {
|
|
525
592
|
contains: filter.q,
|
|
526
593
|
mode: "insensitive"
|
|
527
594
|
};
|
|
528
|
-
if (filter.tenant) where.tenant = {
|
|
529
|
-
|
|
595
|
+
if (filter.tenant) where[u.tenant] = {
|
|
596
|
+
[tenantSlug]: filter.tenant
|
|
530
597
|
};
|
|
531
598
|
const rows = await this.users().findMany({
|
|
532
599
|
where,
|
|
533
600
|
include: {
|
|
534
|
-
tenant: {
|
|
601
|
+
[u.tenant]: {
|
|
535
602
|
select: {
|
|
536
|
-
|
|
603
|
+
[tenantSlug]: true
|
|
537
604
|
}
|
|
538
605
|
}
|
|
539
606
|
},
|
|
@@ -544,12 +611,12 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
544
611
|
});
|
|
545
612
|
return rows.map((row) => ({
|
|
546
613
|
id: row.id,
|
|
547
|
-
email: row.email,
|
|
548
|
-
firstName: row.firstName ?? "",
|
|
549
|
-
lastName: row.lastName ?? "",
|
|
614
|
+
email: this.read(row, u.email) ?? "",
|
|
615
|
+
firstName: this.read(row, u.firstName) ?? "",
|
|
616
|
+
lastName: this.read(row, u.lastName) ?? "",
|
|
550
617
|
role: row.platformRole ?? row.role ?? "MEMBER",
|
|
551
|
-
isActive: row.isActive ?? true,
|
|
552
|
-
tenantSlug: row.tenant
|
|
618
|
+
isActive: this.read(row, u.isActive) ?? true,
|
|
619
|
+
tenantSlug: this.tenantSlugOf(row, u.tenant, tenantSlug),
|
|
553
620
|
lastLoginAt: row.lastLoginAt?.toISOString() ?? null,
|
|
554
621
|
createdAt: row.createdAt.toISOString()
|
|
555
622
|
}));
|
|
@@ -564,12 +631,13 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
564
631
|
});
|
|
565
632
|
}
|
|
566
633
|
async listSubscriptions() {
|
|
634
|
+
const t = this.schema.tenant;
|
|
567
635
|
const rows = await this.subscriptions().findMany({
|
|
568
636
|
include: {
|
|
569
637
|
tenant: {
|
|
570
638
|
select: {
|
|
571
|
-
slug: true,
|
|
572
|
-
name: true
|
|
639
|
+
[t.slug]: true,
|
|
640
|
+
[t.name]: true
|
|
573
641
|
}
|
|
574
642
|
},
|
|
575
643
|
planVersion: {
|
|
@@ -585,7 +653,10 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
585
653
|
});
|
|
586
654
|
return rows.map((row) => ({
|
|
587
655
|
id: row.id,
|
|
588
|
-
tenant:
|
|
656
|
+
tenant: {
|
|
657
|
+
slug: this.read(row.tenant, t.slug) ?? "",
|
|
658
|
+
name: this.read(row.tenant, t.name) ?? ""
|
|
659
|
+
},
|
|
589
660
|
plan: row.plan,
|
|
590
661
|
status: row.status,
|
|
591
662
|
billingCycle: row.billingCycle,
|
|
@@ -594,13 +665,32 @@ var PrismaAdminResourcesAdapter = class {
|
|
|
594
665
|
}));
|
|
595
666
|
}
|
|
596
667
|
tenants() {
|
|
597
|
-
return getPrismaDelegate(this.prisma,
|
|
668
|
+
return getPrismaDelegate(this.prisma, this.schema.delegates.tenant);
|
|
598
669
|
}
|
|
599
670
|
users() {
|
|
600
|
-
return getPrismaDelegate(this.prisma,
|
|
671
|
+
return getPrismaDelegate(this.prisma, this.schema.delegates.user);
|
|
601
672
|
}
|
|
602
673
|
subscriptions() {
|
|
603
|
-
return getPrismaDelegate(this.prisma,
|
|
674
|
+
return getPrismaDelegate(this.prisma, this.schema.delegates.subscription);
|
|
675
|
+
}
|
|
676
|
+
/** The subscription relation of a tenant row, under whatever it is called. */
|
|
677
|
+
subscriptionOf(row) {
|
|
678
|
+
return this.read(row, this.schema.tenant.subscription) ?? null;
|
|
679
|
+
}
|
|
680
|
+
/** The tenant slug on a user row, through both mapped names. */
|
|
681
|
+
tenantSlugOf(row, relation, slugField) {
|
|
682
|
+
const tenant = this.read(row, relation);
|
|
683
|
+
return tenant ? this.read(tenant, slugField) ?? null : null;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Reads a mapped field off a row.
|
|
687
|
+
*
|
|
688
|
+
* The rows come back keyed by the APP's field names, so every read of a
|
|
689
|
+
* mapped field goes through here — and the row type stays the shape the
|
|
690
|
+
* adapter promises rather than becoming `Record<string, unknown>`.
|
|
691
|
+
*/
|
|
692
|
+
read(row, field) {
|
|
693
|
+
return row[field];
|
|
604
694
|
}
|
|
605
695
|
};
|
|
606
696
|
function metricSelect(metrics) {
|
|
@@ -4956,9 +5046,11 @@ export {
|
|
|
4956
5046
|
PrismaTenantSubscriptionWriteAdapter,
|
|
4957
5047
|
PrismaTransactionRunner,
|
|
4958
5048
|
ZeroPromoRevenueDeductionAggregator,
|
|
5049
|
+
assertDelegatesExist,
|
|
4959
5050
|
buildActorTag,
|
|
4960
5051
|
createPrismaPlanBindingResolver,
|
|
4961
5052
|
getPrismaDelegate,
|
|
4962
5053
|
prismaPersistence,
|
|
5054
|
+
resolveAdminResourcesSchema,
|
|
4963
5055
|
resolvePrismaSchemaOptions
|
|
4964
5056
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saasicat/adapter-prisma",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Prisma + PostgreSQL persistence for SaaSiCat: one standard bundle for core, catalog, entitlement, tenant billing, Admin resources and promo, plus individual adapters for custom compositions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dist"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@saasicat/types": "^0.
|
|
25
|
+
"@saasicat/types": "^0.27.0"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"@nestjs/common": "^11.0.0"
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"@prisma/client": "^6.19.0",
|
|
35
35
|
"tsup": "^8.0.0",
|
|
36
36
|
"typescript": "^6.0.0",
|
|
37
|
-
"@saasicat/persistence-testing": "^0.
|
|
38
|
-
"@saasicat/spec": "^0.
|
|
37
|
+
"@saasicat/persistence-testing": "^0.27.0",
|
|
38
|
+
"@saasicat/spec": "^0.27.0"
|
|
39
39
|
},
|
|
40
|
-
"license": "
|
|
40
|
+
"license": "PolyForm-Shield-1.0.0",
|
|
41
41
|
"author": "Taci Uelker",
|
|
42
42
|
"homepage": "https://github.com/uelker70/saasicat",
|
|
43
43
|
"repository": {
|