@node-tenant/tenant-storage-prisma 0.1.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 ADDED
@@ -0,0 +1,148 @@
1
+ # @node-tenant/tenant-storage-prisma
2
+
3
+ > **Prisma storage adapter** for the `@node-tenant` ecosystem — manages tenant metadata, relationships, and configuration using Prisma ORM.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@node-tenant/tenant-storage-prisma)](https://www.npmjs.com/package/@node-tenant/tenant-storage-prisma)
6
+ [![license](https://img.shields.io/npm/l/@node-tenant/tenant-storage-prisma)](./LICENSE)
7
+
8
+ ---
9
+
10
+ ## Overview
11
+
12
+ `@node-tenant/tenant-storage-prisma` implements the **`TenantStorage`** interface from `@node-tenant/tenant-core`. It acts as the "Control Plane", storing your tenants, their configurations, statuses, and cross-tenant relationships.
13
+
14
+ **Note:** This package does **not** handle tenant data isolation (that's the job of the `DatabaseAdapter` or Mongoose routers). This package only manages the list of tenants themselves.
15
+
16
+ ---
17
+
18
+ ## Table of Contents
19
+
20
+ - [Installation](#installation)
21
+ - [Quick Start](#quick-start)
22
+ - [Prisma Schema](#prisma-schema)
23
+ - [API Reference](#api-reference)
24
+ - [License](#license)
25
+
26
+ ---
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ npm install @node-tenant/tenant-core @node-tenant/tenant-storage-prisma @prisma/client
32
+ ```
33
+
34
+ > **Peer dependency**: `@prisma/client >= 5.0.0` must be installed in your project.
35
+
36
+ ---
37
+
38
+ ## Prisma Schema
39
+
40
+ This package requires a specific set of models in your `schema.prisma` file. A pre-defined schema is included in this package at `node_modules/@node-tenant/tenant-storage-prisma/schema.prisma`.
41
+
42
+ You can manually copy these models into your own `schema.prisma`:
43
+
44
+ ```prisma
45
+ model Tenant {
46
+ id String @id @default(uuid())
47
+ slug String @unique
48
+ name String
49
+ status String // e.g., 'active', 'suspended', 'inactive'
50
+ strategy String // e.g., 'tenantId', 'database', 'schema'
51
+ config Json? @default("{}")
52
+ organizationId String?
53
+ createdAt DateTime @default(now())
54
+ updatedAt DateTime @updatedAt
55
+ }
56
+
57
+ model TenantRelationship {
58
+ id String @id @default(uuid())
59
+ sourceTenantId String
60
+ targetTenantId String
61
+ type String // e.g., 'partner', 'parent_child'
62
+ metadata Json? @default("{}")
63
+ createdAt DateTime @default(now())
64
+ updatedAt DateTime @updatedAt
65
+
66
+ @@unique([sourceTenantId, targetTenantId, type])
67
+ }
68
+
69
+ model TenantShare {
70
+ id String @id @default(uuid())
71
+ resourceType String
72
+ resourceId String
73
+ ownerTenantId String
74
+ targetTenantId String
75
+ permission String // e.g., 'read', 'write', 'admin'
76
+ metadata Json? @default("{}")
77
+ createdAt DateTime @default(now())
78
+ updatedAt DateTime @updatedAt
79
+
80
+ @@unique([resourceType, resourceId, ownerTenantId, targetTenantId])
81
+ }
82
+ ```
83
+
84
+ After adding these to your schema, run:
85
+
86
+ ```bash
87
+ npx prisma generate
88
+ npx prisma db push # Or create a migration
89
+ ```
90
+
91
+ ---
92
+
93
+ ## Quick Start
94
+
95
+ ```ts
96
+ import { PrismaClient } from '@prisma/client';
97
+ import { TenantManager } from '@node-tenant/tenant-core';
98
+ import { createPrismaStorage } from '@node-tenant/tenant-storage-prisma';
99
+
100
+ // 1. Initialize your Prisma Client
101
+ const prisma = new PrismaClient();
102
+
103
+ // 2. Create the Storage Adapter
104
+ const storage = createPrismaStorage(prisma);
105
+
106
+ // 3. Pass to the TenantManager
107
+ const manager = new TenantManager({ storage });
108
+
109
+ // Now you can manage tenants!
110
+ async function setup() {
111
+ const tenant = await manager.createTenant({
112
+ slug: 'acme-corp',
113
+ name: 'Acme Corporation',
114
+ status: 'active',
115
+ strategy: 'tenantId',
116
+ });
117
+
118
+ console.log('Created tenant:', tenant);
119
+ }
120
+ ```
121
+
122
+ ---
123
+
124
+ ## API Reference
125
+
126
+ ### `createPrismaStorage(prismaClient)`
127
+
128
+ Creates a `TenantStorage` compatible adapter.
129
+
130
+ ```ts
131
+ import { createPrismaStorage } from '@node-tenant/tenant-storage-prisma';
132
+ import { PrismaClient } from '@prisma/client';
133
+
134
+ const prisma = new PrismaClient();
135
+ const storage = createPrismaStorage(prisma);
136
+ ```
137
+
138
+ | Parameter | Type | Required | Description |
139
+ |---|---|---|---|
140
+ | `prismaClient` | `PrismaClient` | ✅ | An initialized PrismaClient instance |
141
+
142
+ **Returns:** `TenantStorage` object containing all required methods (`getTenantById`, `getTenantBySlug`, `listTenants`, `createTenant`, `updateTenant`, `deleteTenant`, `createRelationship`, etc.)
143
+
144
+ ---
145
+
146
+ ## License
147
+
148
+ MIT © AdPulseFlow
package/dist/index.cjs ADDED
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ createPrismaStorage: () => createPrismaStorage
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+ function toTenant(raw) {
27
+ return {
28
+ id: raw.id,
29
+ organizationId: raw.organizationId ?? void 0,
30
+ name: raw.name,
31
+ slug: raw.slug,
32
+ strategy: raw.strategy,
33
+ status: raw.status,
34
+ config: raw.config ?? {},
35
+ metadata: raw.metadata ?? void 0,
36
+ createdAt: raw.createdAt,
37
+ updatedAt: raw.updatedAt
38
+ };
39
+ }
40
+ function toOrg(raw) {
41
+ return {
42
+ id: raw.id,
43
+ name: raw.name,
44
+ slug: raw.slug,
45
+ metadata: raw.metadata ?? void 0,
46
+ createdAt: raw.createdAt,
47
+ updatedAt: raw.updatedAt
48
+ };
49
+ }
50
+ function createPrismaStorage(prisma) {
51
+ return {
52
+ // ── Tenants ──────────────────────────────────────────────────────────────
53
+ async createTenant(data) {
54
+ const raw = await prisma.tenant.create({
55
+ data: {
56
+ name: data.name,
57
+ slug: data.slug,
58
+ organizationId: data.organizationId,
59
+ strategy: data.strategy,
60
+ status: data.status,
61
+ config: data.config ?? {},
62
+ metadata: data.metadata ?? void 0
63
+ }
64
+ });
65
+ return toTenant(raw);
66
+ },
67
+ async getTenant(id) {
68
+ const raw = await prisma.tenant.findUnique({ where: { id } });
69
+ return raw ? toTenant(raw) : null;
70
+ },
71
+ async getTenantBySlug(slug) {
72
+ const raw = await prisma.tenant.findUnique({ where: { slug } });
73
+ return raw ? toTenant(raw) : null;
74
+ },
75
+ async updateTenant(id, data) {
76
+ const raw = await prisma.tenant.update({ where: { id }, data });
77
+ return toTenant(raw);
78
+ },
79
+ async deleteTenant(id) {
80
+ await prisma.tenant.delete({ where: { id } });
81
+ },
82
+ async listTenants(filter) {
83
+ const raws = await prisma.tenant.findMany({ where: filter ?? {} });
84
+ return raws.map(toTenant);
85
+ },
86
+ // ── Organizations ─────────────────────────────────────────────────────────
87
+ async createOrganization(data) {
88
+ const raw = await prisma.tenantOrganization.create({ data });
89
+ return toOrg(raw);
90
+ },
91
+ async getOrganization(id) {
92
+ const raw = await prisma.tenantOrganization.findUnique({ where: { id } });
93
+ return raw ? toOrg(raw) : null;
94
+ },
95
+ async updateOrganization(id, data) {
96
+ const raw = await prisma.tenantOrganization.update({ where: { id }, data });
97
+ return toOrg(raw);
98
+ },
99
+ async deleteOrganization(id) {
100
+ await prisma.tenantOrganization.delete({ where: { id } });
101
+ },
102
+ async listOrganizations() {
103
+ const raws = await prisma.tenantOrganization.findMany();
104
+ return raws.map(toOrg);
105
+ },
106
+ // ── Domains ───────────────────────────────────────────────────────────────
107
+ async createDomain(data) {
108
+ return prisma.tenantDomain.create({ data });
109
+ },
110
+ async getDomainByHost(host) {
111
+ return prisma.tenantDomain.findUnique({ where: { domain: host } });
112
+ },
113
+ async deleteDomain(id) {
114
+ await prisma.tenantDomain.delete({ where: { id } });
115
+ },
116
+ // ── Relationships ─────────────────────────────────────────────────────────
117
+ async createRelationship(data) {
118
+ return prisma.tenantRelationship.create({ data });
119
+ },
120
+ async getRelationships(tenantId) {
121
+ return prisma.tenantRelationship.findMany({
122
+ where: {
123
+ OR: [{ sourceTenantId: tenantId }, { targetTenantId: tenantId }]
124
+ }
125
+ });
126
+ },
127
+ async deleteRelationship(id) {
128
+ await prisma.tenantRelationship.delete({ where: { id } });
129
+ },
130
+ // ── Resource shares ───────────────────────────────────────────────────────
131
+ async createResourceShare(data) {
132
+ return prisma.resourceShare.create({ data });
133
+ },
134
+ async getResourceShares(filter) {
135
+ const where = {};
136
+ if (filter.resourceType) where["resourceType"] = filter.resourceType;
137
+ if (filter.resourceId) where["resourceId"] = filter.resourceId;
138
+ if (filter.ownerTenantId) where["ownerTenantId"] = filter.ownerTenantId;
139
+ if (filter.targetTenantId) where["targetTenantId"] = filter.targetTenantId;
140
+ return prisma.resourceShare.findMany({ where });
141
+ },
142
+ async deleteResourceShare(id) {
143
+ await prisma.resourceShare.delete({ where: { id } });
144
+ },
145
+ // ── Audit ─────────────────────────────────────────────────────────────────
146
+ async createAuditLog(data) {
147
+ return prisma.auditLog.create({
148
+ data: { ...data, createdAt: /* @__PURE__ */ new Date() }
149
+ });
150
+ },
151
+ async listAuditLogs(tenantId, limit = 100) {
152
+ return prisma.auditLog.findMany({
153
+ where: { tenantId },
154
+ orderBy: { createdAt: "desc" },
155
+ take: limit
156
+ });
157
+ }
158
+ };
159
+ }
160
+ // Annotate the CommonJS export names for ESM import in node:
161
+ 0 && (module.exports = {
162
+ createPrismaStorage
163
+ });
164
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { PrismaClient } from '@prisma/client';\nimport type {\n StorageAdapter,\n Tenant,\n Organization,\n TenantDomain,\n TenantRelationship,\n ResourceShare,\n AuditLog,\n} from '@node-tenant/tenant-core';\n\nfunction toTenant(raw: any): Tenant {\n return {\n id: raw.id,\n organizationId: raw.organizationId ?? undefined,\n name: raw.name,\n slug: raw.slug,\n strategy: raw.strategy as Tenant['strategy'],\n status: raw.status as Tenant['status'],\n config: (raw.config as Record<string, unknown>) ?? {},\n metadata: (raw.metadata as Record<string, unknown>) ?? undefined,\n createdAt: raw.createdAt,\n updatedAt: raw.updatedAt,\n };\n}\n\nfunction toOrg(raw: any): Organization {\n return {\n id: raw.id,\n name: raw.name,\n slug: raw.slug,\n metadata: (raw.metadata as Record<string, unknown>) ?? undefined,\n createdAt: raw.createdAt,\n updatedAt: raw.updatedAt,\n };\n}\n\n/**\n * Create a Prisma-backed StorageAdapter for the @node-tenant/tenant public control layer.\n *\n * Usage:\n * const storage = createPrismaStorage(prisma);\n * const manager = new TenantManager({ storage });\n */\nexport function createPrismaStorage(prisma: PrismaClient): StorageAdapter {\n return {\n // ── Tenants ──────────────────────────────────────────────────────────────\n async createTenant(data) {\n const raw = await (prisma as any).tenant.create({\n data: {\n name: data.name,\n slug: data.slug,\n organizationId: data.organizationId,\n strategy: data.strategy,\n status: data.status,\n config: data.config ?? {},\n metadata: data.metadata ?? undefined,\n },\n });\n return toTenant(raw);\n },\n\n async getTenant(id) {\n const raw = await (prisma as any).tenant.findUnique({ where: { id } });\n return raw ? toTenant(raw) : null;\n },\n\n async getTenantBySlug(slug) {\n const raw = await (prisma as any).tenant.findUnique({ where: { slug } });\n return raw ? toTenant(raw) : null;\n },\n\n async updateTenant(id, data) {\n const raw = await (prisma as any).tenant.update({ where: { id }, data });\n return toTenant(raw);\n },\n\n async deleteTenant(id) {\n await (prisma as any).tenant.delete({ where: { id } });\n },\n\n async listTenants(filter) {\n const raws = await (prisma as any).tenant.findMany({ where: filter ?? {} });\n return raws.map(toTenant);\n },\n\n // ── Organizations ─────────────────────────────────────────────────────────\n async createOrganization(data) {\n const raw = await (prisma as any).tenantOrganization.create({ data });\n return toOrg(raw);\n },\n\n async getOrganization(id) {\n const raw = await (prisma as any).tenantOrganization.findUnique({ where: { id } });\n return raw ? toOrg(raw) : null;\n },\n\n async updateOrganization(id, data) {\n const raw = await (prisma as any).tenantOrganization.update({ where: { id }, data });\n return toOrg(raw);\n },\n\n async deleteOrganization(id) {\n await (prisma as any).tenantOrganization.delete({ where: { id } });\n },\n\n async listOrganizations() {\n const raws = await (prisma as any).tenantOrganization.findMany();\n return raws.map(toOrg);\n },\n\n // ── Domains ───────────────────────────────────────────────────────────────\n async createDomain(data) {\n return (prisma as any).tenantDomain.create({ data }) as Promise<TenantDomain>;\n },\n\n async getDomainByHost(host) {\n return (prisma as any).tenantDomain.findUnique({ where: { domain: host } }) as Promise<TenantDomain | null>;\n },\n\n async deleteDomain(id) {\n await (prisma as any).tenantDomain.delete({ where: { id } });\n },\n\n // ── Relationships ─────────────────────────────────────────────────────────\n async createRelationship(data) {\n return (prisma as any).tenantRelationship.create({ data }) as Promise<TenantRelationship>;\n },\n\n async getRelationships(tenantId) {\n return (prisma as any).tenantRelationship.findMany({\n where: {\n OR: [{ sourceTenantId: tenantId }, { targetTenantId: tenantId }],\n },\n }) as Promise<TenantRelationship[]>;\n },\n\n async deleteRelationship(id) {\n await (prisma as any).tenantRelationship.delete({ where: { id } });\n },\n\n // ── Resource shares ───────────────────────────────────────────────────────\n async createResourceShare(data) {\n return (prisma as any).resourceShare.create({ data }) as Promise<ResourceShare>;\n },\n\n async getResourceShares(filter) {\n const where: Record<string, unknown> = {};\n if (filter.resourceType) where['resourceType'] = filter.resourceType;\n if (filter.resourceId) where['resourceId'] = filter.resourceId;\n if (filter.ownerTenantId) where['ownerTenantId'] = filter.ownerTenantId;\n if (filter.targetTenantId) where['targetTenantId'] = filter.targetTenantId;\n return (prisma as any).resourceShare.findMany({ where }) as Promise<ResourceShare[]>;\n },\n\n async deleteResourceShare(id) {\n await (prisma as any).resourceShare.delete({ where: { id } });\n },\n\n // ── Audit ─────────────────────────────────────────────────────────────────\n async createAuditLog(data) {\n return (prisma as any).auditLog.create({\n data: { ...data, createdAt: new Date() },\n }) as Promise<AuditLog>;\n },\n\n async listAuditLogs(tenantId, limit = 100) {\n return (prisma as any).auditLog.findMany({\n where: { tenantId },\n orderBy: { createdAt: 'desc' },\n take: limit,\n }) as Promise<AuditLog[]>;\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,13 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+ import { StorageAdapter } from '@node-tenant/tenant-core';
3
+
4
+ /**
5
+ * Create a Prisma-backed StorageAdapter for the @node-tenant/tenant public control layer.
6
+ *
7
+ * Usage:
8
+ * const storage = createPrismaStorage(prisma);
9
+ * const manager = new TenantManager({ storage });
10
+ */
11
+ declare function createPrismaStorage(prisma: PrismaClient): StorageAdapter;
12
+
13
+ export { createPrismaStorage };
@@ -0,0 +1,13 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+ import { StorageAdapter } from '@node-tenant/tenant-core';
3
+
4
+ /**
5
+ * Create a Prisma-backed StorageAdapter for the @node-tenant/tenant public control layer.
6
+ *
7
+ * Usage:
8
+ * const storage = createPrismaStorage(prisma);
9
+ * const manager = new TenantManager({ storage });
10
+ */
11
+ declare function createPrismaStorage(prisma: PrismaClient): StorageAdapter;
12
+
13
+ export { createPrismaStorage };
package/dist/index.js ADDED
@@ -0,0 +1,139 @@
1
+ // src/index.ts
2
+ function toTenant(raw) {
3
+ return {
4
+ id: raw.id,
5
+ organizationId: raw.organizationId ?? void 0,
6
+ name: raw.name,
7
+ slug: raw.slug,
8
+ strategy: raw.strategy,
9
+ status: raw.status,
10
+ config: raw.config ?? {},
11
+ metadata: raw.metadata ?? void 0,
12
+ createdAt: raw.createdAt,
13
+ updatedAt: raw.updatedAt
14
+ };
15
+ }
16
+ function toOrg(raw) {
17
+ return {
18
+ id: raw.id,
19
+ name: raw.name,
20
+ slug: raw.slug,
21
+ metadata: raw.metadata ?? void 0,
22
+ createdAt: raw.createdAt,
23
+ updatedAt: raw.updatedAt
24
+ };
25
+ }
26
+ function createPrismaStorage(prisma) {
27
+ return {
28
+ // ── Tenants ──────────────────────────────────────────────────────────────
29
+ async createTenant(data) {
30
+ const raw = await prisma.tenant.create({
31
+ data: {
32
+ name: data.name,
33
+ slug: data.slug,
34
+ organizationId: data.organizationId,
35
+ strategy: data.strategy,
36
+ status: data.status,
37
+ config: data.config ?? {},
38
+ metadata: data.metadata ?? void 0
39
+ }
40
+ });
41
+ return toTenant(raw);
42
+ },
43
+ async getTenant(id) {
44
+ const raw = await prisma.tenant.findUnique({ where: { id } });
45
+ return raw ? toTenant(raw) : null;
46
+ },
47
+ async getTenantBySlug(slug) {
48
+ const raw = await prisma.tenant.findUnique({ where: { slug } });
49
+ return raw ? toTenant(raw) : null;
50
+ },
51
+ async updateTenant(id, data) {
52
+ const raw = await prisma.tenant.update({ where: { id }, data });
53
+ return toTenant(raw);
54
+ },
55
+ async deleteTenant(id) {
56
+ await prisma.tenant.delete({ where: { id } });
57
+ },
58
+ async listTenants(filter) {
59
+ const raws = await prisma.tenant.findMany({ where: filter ?? {} });
60
+ return raws.map(toTenant);
61
+ },
62
+ // ── Organizations ─────────────────────────────────────────────────────────
63
+ async createOrganization(data) {
64
+ const raw = await prisma.tenantOrganization.create({ data });
65
+ return toOrg(raw);
66
+ },
67
+ async getOrganization(id) {
68
+ const raw = await prisma.tenantOrganization.findUnique({ where: { id } });
69
+ return raw ? toOrg(raw) : null;
70
+ },
71
+ async updateOrganization(id, data) {
72
+ const raw = await prisma.tenantOrganization.update({ where: { id }, data });
73
+ return toOrg(raw);
74
+ },
75
+ async deleteOrganization(id) {
76
+ await prisma.tenantOrganization.delete({ where: { id } });
77
+ },
78
+ async listOrganizations() {
79
+ const raws = await prisma.tenantOrganization.findMany();
80
+ return raws.map(toOrg);
81
+ },
82
+ // ── Domains ───────────────────────────────────────────────────────────────
83
+ async createDomain(data) {
84
+ return prisma.tenantDomain.create({ data });
85
+ },
86
+ async getDomainByHost(host) {
87
+ return prisma.tenantDomain.findUnique({ where: { domain: host } });
88
+ },
89
+ async deleteDomain(id) {
90
+ await prisma.tenantDomain.delete({ where: { id } });
91
+ },
92
+ // ── Relationships ─────────────────────────────────────────────────────────
93
+ async createRelationship(data) {
94
+ return prisma.tenantRelationship.create({ data });
95
+ },
96
+ async getRelationships(tenantId) {
97
+ return prisma.tenantRelationship.findMany({
98
+ where: {
99
+ OR: [{ sourceTenantId: tenantId }, { targetTenantId: tenantId }]
100
+ }
101
+ });
102
+ },
103
+ async deleteRelationship(id) {
104
+ await prisma.tenantRelationship.delete({ where: { id } });
105
+ },
106
+ // ── Resource shares ───────────────────────────────────────────────────────
107
+ async createResourceShare(data) {
108
+ return prisma.resourceShare.create({ data });
109
+ },
110
+ async getResourceShares(filter) {
111
+ const where = {};
112
+ if (filter.resourceType) where["resourceType"] = filter.resourceType;
113
+ if (filter.resourceId) where["resourceId"] = filter.resourceId;
114
+ if (filter.ownerTenantId) where["ownerTenantId"] = filter.ownerTenantId;
115
+ if (filter.targetTenantId) where["targetTenantId"] = filter.targetTenantId;
116
+ return prisma.resourceShare.findMany({ where });
117
+ },
118
+ async deleteResourceShare(id) {
119
+ await prisma.resourceShare.delete({ where: { id } });
120
+ },
121
+ // ── Audit ─────────────────────────────────────────────────────────────────
122
+ async createAuditLog(data) {
123
+ return prisma.auditLog.create({
124
+ data: { ...data, createdAt: /* @__PURE__ */ new Date() }
125
+ });
126
+ },
127
+ async listAuditLogs(tenantId, limit = 100) {
128
+ return prisma.auditLog.findMany({
129
+ where: { tenantId },
130
+ orderBy: { createdAt: "desc" },
131
+ take: limit
132
+ });
133
+ }
134
+ };
135
+ }
136
+ export {
137
+ createPrismaStorage
138
+ };
139
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { PrismaClient } from '@prisma/client';\nimport type {\n StorageAdapter,\n Tenant,\n Organization,\n TenantDomain,\n TenantRelationship,\n ResourceShare,\n AuditLog,\n} from '@node-tenant/tenant-core';\n\nfunction toTenant(raw: any): Tenant {\n return {\n id: raw.id,\n organizationId: raw.organizationId ?? undefined,\n name: raw.name,\n slug: raw.slug,\n strategy: raw.strategy as Tenant['strategy'],\n status: raw.status as Tenant['status'],\n config: (raw.config as Record<string, unknown>) ?? {},\n metadata: (raw.metadata as Record<string, unknown>) ?? undefined,\n createdAt: raw.createdAt,\n updatedAt: raw.updatedAt,\n };\n}\n\nfunction toOrg(raw: any): Organization {\n return {\n id: raw.id,\n name: raw.name,\n slug: raw.slug,\n metadata: (raw.metadata as Record<string, unknown>) ?? undefined,\n createdAt: raw.createdAt,\n updatedAt: raw.updatedAt,\n };\n}\n\n/**\n * Create a Prisma-backed StorageAdapter for the @node-tenant/tenant public control layer.\n *\n * Usage:\n * const storage = createPrismaStorage(prisma);\n * const manager = new TenantManager({ storage });\n */\nexport function createPrismaStorage(prisma: PrismaClient): StorageAdapter {\n return {\n // ── Tenants ──────────────────────────────────────────────────────────────\n async createTenant(data) {\n const raw = await (prisma as any).tenant.create({\n data: {\n name: data.name,\n slug: data.slug,\n organizationId: data.organizationId,\n strategy: data.strategy,\n status: data.status,\n config: data.config ?? {},\n metadata: data.metadata ?? undefined,\n },\n });\n return toTenant(raw);\n },\n\n async getTenant(id) {\n const raw = await (prisma as any).tenant.findUnique({ where: { id } });\n return raw ? toTenant(raw) : null;\n },\n\n async getTenantBySlug(slug) {\n const raw = await (prisma as any).tenant.findUnique({ where: { slug } });\n return raw ? toTenant(raw) : null;\n },\n\n async updateTenant(id, data) {\n const raw = await (prisma as any).tenant.update({ where: { id }, data });\n return toTenant(raw);\n },\n\n async deleteTenant(id) {\n await (prisma as any).tenant.delete({ where: { id } });\n },\n\n async listTenants(filter) {\n const raws = await (prisma as any).tenant.findMany({ where: filter ?? {} });\n return raws.map(toTenant);\n },\n\n // ── Organizations ─────────────────────────────────────────────────────────\n async createOrganization(data) {\n const raw = await (prisma as any).tenantOrganization.create({ data });\n return toOrg(raw);\n },\n\n async getOrganization(id) {\n const raw = await (prisma as any).tenantOrganization.findUnique({ where: { id } });\n return raw ? toOrg(raw) : null;\n },\n\n async updateOrganization(id, data) {\n const raw = await (prisma as any).tenantOrganization.update({ where: { id }, data });\n return toOrg(raw);\n },\n\n async deleteOrganization(id) {\n await (prisma as any).tenantOrganization.delete({ where: { id } });\n },\n\n async listOrganizations() {\n const raws = await (prisma as any).tenantOrganization.findMany();\n return raws.map(toOrg);\n },\n\n // ── Domains ───────────────────────────────────────────────────────────────\n async createDomain(data) {\n return (prisma as any).tenantDomain.create({ data }) as Promise<TenantDomain>;\n },\n\n async getDomainByHost(host) {\n return (prisma as any).tenantDomain.findUnique({ where: { domain: host } }) as Promise<TenantDomain | null>;\n },\n\n async deleteDomain(id) {\n await (prisma as any).tenantDomain.delete({ where: { id } });\n },\n\n // ── Relationships ─────────────────────────────────────────────────────────\n async createRelationship(data) {\n return (prisma as any).tenantRelationship.create({ data }) as Promise<TenantRelationship>;\n },\n\n async getRelationships(tenantId) {\n return (prisma as any).tenantRelationship.findMany({\n where: {\n OR: [{ sourceTenantId: tenantId }, { targetTenantId: tenantId }],\n },\n }) as Promise<TenantRelationship[]>;\n },\n\n async deleteRelationship(id) {\n await (prisma as any).tenantRelationship.delete({ where: { id } });\n },\n\n // ── Resource shares ───────────────────────────────────────────────────────\n async createResourceShare(data) {\n return (prisma as any).resourceShare.create({ data }) as Promise<ResourceShare>;\n },\n\n async getResourceShares(filter) {\n const where: Record<string, unknown> = {};\n if (filter.resourceType) where['resourceType'] = filter.resourceType;\n if (filter.resourceId) where['resourceId'] = filter.resourceId;\n if (filter.ownerTenantId) where['ownerTenantId'] = filter.ownerTenantId;\n if (filter.targetTenantId) where['targetTenantId'] = filter.targetTenantId;\n return (prisma as any).resourceShare.findMany({ where }) as Promise<ResourceShare[]>;\n },\n\n async deleteResourceShare(id) {\n await (prisma as any).resourceShare.delete({ where: { id } });\n },\n\n // ── Audit ─────────────────────────────────────────────────────────────────\n async createAuditLog(data) {\n return (prisma as any).auditLog.create({\n data: { ...data, createdAt: new Date() },\n }) as Promise<AuditLog>;\n },\n\n async listAuditLogs(tenantId, limit = 100) {\n return (prisma as any).auditLog.findMany({\n where: { tenantId },\n orderBy: { createdAt: 'desc' },\n take: limit,\n }) as Promise<AuditLog[]>;\n },\n };\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@node-tenant/tenant-storage-prisma",
3
+ "version": "0.1.0",
4
+ "description": "Prisma storage adapter for @node-tenant/tenant",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".":{
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ },
16
+ "./schema": "./schema.prisma"
17
+ },
18
+ "files": ["dist", "schema.prisma"],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "dev": "tsup --watch",
22
+ "test": "vitest run",
23
+ "clean": "rimraf dist"
24
+ },
25
+ "dependencies": {
26
+ "@node-tenant/tenant-core": "^0.1.0"
27
+ },
28
+ "peerDependencies": {
29
+ "@prisma/client": ">=5.0.0"
30
+ },
31
+ "devDependencies": {
32
+ "@prisma/client": "^5.10.0",
33
+ "prisma": "^5.10.0",
34
+ "rimraf": "^5.0.5",
35
+ "tsup": "^8.0.0",
36
+ "typescript": "^5.4.0",
37
+ "vitest": "^1.4.0"
38
+ }
39
+ }
40
+
package/schema.prisma ADDED
@@ -0,0 +1,103 @@
1
+ // Prisma schema for @node-tenant/tenant public control layer.
2
+ // Copy this into your app's schema.prisma or use prisma merge.
3
+
4
+ generator client {
5
+ provider = "prisma-client-js"
6
+ }
7
+
8
+ datasource db {
9
+ provider = "postgresql"
10
+ url = env("DATABASE_URL")
11
+ }
12
+
13
+ model TenantOrganization {
14
+ id String @id @default(cuid())
15
+ name String
16
+ slug String @unique
17
+ metadata Json?
18
+ createdAt DateTime @default(now())
19
+ updatedAt DateTime @updatedAt
20
+ tenants Tenant[]
21
+
22
+ @@map("tenant_organizations")
23
+ }
24
+
25
+ model Tenant {
26
+ id String @id @default(cuid())
27
+ organizationId String?
28
+ organization TenantOrganization? @relation(fields: [organizationId], references: [id])
29
+ name String
30
+ slug String @unique
31
+ strategy String @default("tenantId")
32
+ status String @default("active")
33
+ config Json @default("{}")
34
+ metadata Json?
35
+ createdAt DateTime @default(now())
36
+ updatedAt DateTime @updatedAt
37
+
38
+ domains TenantDomain[]
39
+ sourceRelationships TenantRelationship[] @relation("SourceTenant")
40
+ targetRelationships TenantRelationship[] @relation("TargetTenant")
41
+ ownedShares ResourceShare[] @relation("OwnerTenant")
42
+ receivedShares ResourceShare[] @relation("TargetTenant")
43
+ auditLogs AuditLog[]
44
+
45
+ @@map("tenants")
46
+ }
47
+
48
+ model TenantDomain {
49
+ id String @id @default(cuid())
50
+ tenantId String
51
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
52
+ domain String @unique
53
+ isPrimary Boolean @default(false)
54
+ createdAt DateTime @default(now())
55
+
56
+ @@map("tenant_domains")
57
+ }
58
+
59
+ model TenantRelationship {
60
+ id String @id @default(cuid())
61
+ sourceTenantId String
62
+ targetTenantId String
63
+ sourceTenant Tenant @relation("SourceTenant", fields: [sourceTenantId], references: [id])
64
+ targetTenant Tenant @relation("TargetTenant", fields: [targetTenantId], references: [id])
65
+ type String
66
+ metadata Json?
67
+ createdAt DateTime @default(now())
68
+
69
+ @@unique([sourceTenantId, targetTenantId, type])
70
+ @@map("tenant_relationships")
71
+ }
72
+
73
+ model ResourceShare {
74
+ id String @id @default(cuid())
75
+ resourceType String
76
+ resourceId String
77
+ ownerTenantId String
78
+ targetTenantId String
79
+ ownerTenant Tenant @relation("OwnerTenant", fields: [ownerTenantId], references: [id])
80
+ targetTenant Tenant @relation("TargetTenant", fields: [targetTenantId], references: [id])
81
+ permission String @default("read")
82
+ expiresAt DateTime?
83
+ metadata Json?
84
+ createdAt DateTime @default(now())
85
+
86
+ @@unique([resourceType, resourceId, ownerTenantId, targetTenantId])
87
+ @@map("resource_shares")
88
+ }
89
+
90
+ model AuditLog {
91
+ id String @id @default(cuid())
92
+ tenantId String
93
+ tenant Tenant @relation(fields: [tenantId], references: [id])
94
+ action String
95
+ actorId String?
96
+ resourceType String?
97
+ resourceId String?
98
+ metadata Json?
99
+ createdAt DateTime @default(now())
100
+
101
+ @@index([tenantId, createdAt])
102
+ @@map("audit_logs")
103
+ }