@synapsestudios/eslint-plugin-data-boundaries 1.3.0 → 1.5.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 +158 -12
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/rules/no-cross-domain-drizzle-access.d.ts +8 -0
- package/dist/rules/no-cross-domain-drizzle-access.d.ts.map +1 -0
- package/dist/rules/no-cross-domain-drizzle-access.js +197 -0
- package/dist/rules/no-cross-domain-drizzle-access.js.map +1 -0
- package/dist/rules/no-cross-domain-prisma-access.d.ts +1 -1
- package/dist/rules/no-cross-file-model-references.d.ts.map +1 -1
- package/dist/rules/no-cross-file-model-references.js +48 -16
- package/dist/rules/no-cross-file-model-references.js.map +1 -1
- package/dist/rules/no-cross-schema-drizzle-references.d.ts +7 -0
- package/dist/rules/no-cross-schema-drizzle-references.d.ts.map +1 -0
- package/dist/rules/no-cross-schema-drizzle-references.js +261 -0
- package/dist/rules/no-cross-schema-drizzle-references.js.map +1 -0
- package/dist/utils/drizzle-parser.d.ts +25 -0
- package/dist/utils/drizzle-parser.d.ts.map +1 -0
- package/dist/utils/drizzle-parser.js +263 -0
- package/dist/utils/drizzle-parser.js.map +1 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @synapsestudios/eslint-plugin-data-boundaries
|
|
2
2
|
|
|
3
|
-
ESLint plugin to enforce data boundary policies in modular monoliths using Prisma ORM and slonik.
|
|
3
|
+
ESLint plugin to enforce data boundary policies in modular monoliths using Prisma ORM, Drizzle ORM, and slonik.
|
|
4
4
|
|
|
5
5
|
## Rules
|
|
6
6
|
|
|
@@ -8,17 +8,21 @@ ESLint plugin to enforce data boundary policies in modular monoliths using Prism
|
|
|
8
8
|
|------|-------------|-------|
|
|
9
9
|
| [`no-cross-file-model-references`](#no-cross-file-model-references) | Prevents Prisma models from referencing models defined in other schema files | Prisma schema files |
|
|
10
10
|
| [`no-cross-domain-prisma-access`](#no-cross-domain-prisma-access) | Prevents modules from accessing Prisma models outside their domain boundaries | TypeScript/JavaScript |
|
|
11
|
+
| [`no-cross-schema-drizzle-references`](#no-cross-schema-drizzle-references) | Prevents Drizzle table definitions from referencing tables in other schema files | Drizzle schema files |
|
|
12
|
+
| [`no-cross-domain-drizzle-access`](#no-cross-domain-drizzle-access) | Prevents modules from accessing Drizzle tables outside their domain boundaries | TypeScript/JavaScript |
|
|
11
13
|
| [`no-cross-schema-slonik-access`](#no-cross-schema-slonik-access) | Prevents modules from accessing database tables outside their schema boundaries via slonik | TypeScript/JavaScript |
|
|
12
14
|
|
|
13
15
|
## Overview
|
|
14
16
|
|
|
15
|
-
When building modular monoliths, maintaining clear boundaries between domains is crucial for long-term maintainability. ORMs like Prisma and query builders like slonik make it easy to accidentally create tight coupling at the data layer by allowing modules to access data that belongs to other domains.
|
|
17
|
+
When building modular monoliths, maintaining clear boundaries between domains is crucial for long-term maintainability. ORMs like Prisma and Drizzle and query builders like slonik make it easy to accidentally create tight coupling at the data layer by allowing modules to access data that belongs to other domains.
|
|
16
18
|
|
|
17
|
-
This ESLint plugin provides
|
|
19
|
+
This ESLint plugin provides five complementary rules to prevent such violations:
|
|
18
20
|
|
|
19
|
-
1. **
|
|
20
|
-
2. **
|
|
21
|
-
3. **
|
|
21
|
+
1. **Prisma schema-level enforcement**: Prevents Prisma schema files from referencing models defined in other schema files
|
|
22
|
+
2. **Prisma application-level enforcement**: Prevents TypeScript code from accessing Prisma models outside their domain boundaries
|
|
23
|
+
3. **Drizzle schema-level enforcement**: Prevents Drizzle table definitions from referencing tables in other schema files
|
|
24
|
+
4. **Drizzle application-level enforcement**: Prevents TypeScript code from accessing Drizzle tables outside their domain boundaries
|
|
25
|
+
5. **SQL-level enforcement**: Prevents slonik SQL queries from accessing tables outside the module's schema
|
|
22
26
|
|
|
23
27
|
## Installation
|
|
24
28
|
|
|
@@ -91,6 +95,79 @@ class AuthService {
|
|
|
91
95
|
}
|
|
92
96
|
```
|
|
93
97
|
|
|
98
|
+
### `no-cross-schema-drizzle-references`
|
|
99
|
+
|
|
100
|
+
Prevents Drizzle table definitions from referencing tables defined in other schema files. This rule ensures that each Drizzle schema file is self-contained within its domain by detecting foreign key references and relations that cross schema file boundaries.
|
|
101
|
+
|
|
102
|
+
**Examples of violations:**
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// In execution.schema.ts
|
|
106
|
+
import { pgTable, text } from 'drizzle-orm/pg-core';
|
|
107
|
+
import { identity_user } from './auth.schema';
|
|
108
|
+
|
|
109
|
+
export const execution = pgTable('execution', {
|
|
110
|
+
id: text('id').primaryKey(),
|
|
111
|
+
userId: text('user_id')
|
|
112
|
+
.notNull()
|
|
113
|
+
.references(() => identity_user.id, { onDelete: 'cascade' }), // ❌ Error: identity_user not defined in this file
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Valid usage:**
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// In auth.schema.ts
|
|
121
|
+
import { pgTable, text } from 'drizzle-orm/pg-core';
|
|
122
|
+
|
|
123
|
+
export const identity_user = pgTable('identity_user', {
|
|
124
|
+
id: text('id').primaryKey(),
|
|
125
|
+
name: text('name').notNull(),
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
export const identity_session = pgTable('identity_session', {
|
|
129
|
+
id: text('id').primaryKey(),
|
|
130
|
+
userId: text('user_id')
|
|
131
|
+
.notNull()
|
|
132
|
+
.references(() => identity_user.id, { onDelete: 'cascade' }), // ✅ Valid: identity_user is defined in same file
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### `no-cross-domain-drizzle-access`
|
|
137
|
+
|
|
138
|
+
Prevents TypeScript/JavaScript modules from accessing Drizzle tables that belong to other domains. This rule analyzes your application code and maps file paths to domains, then ensures modules only access tables from their own domain.
|
|
139
|
+
|
|
140
|
+
**Examples of violations:**
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// In /modules/auth/service.ts
|
|
144
|
+
import { organization } from '@/db/schema';
|
|
145
|
+
|
|
146
|
+
class AuthService {
|
|
147
|
+
async getOrganizations() {
|
|
148
|
+
return db.select().from(organization);
|
|
149
|
+
// ❌ Error: Module 'auth' cannot access 'organization' table (belongs to 'organization' domain)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Valid usage:**
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// In /modules/auth/service.ts
|
|
158
|
+
import { identity_user, identity_session } from '@/db/schema';
|
|
159
|
+
|
|
160
|
+
class AuthService {
|
|
161
|
+
async getUser(id: string) {
|
|
162
|
+
return db.select().from(identity_user).where(eq(identity_user.id, id)); // ✅ Valid: identity_user belongs to auth domain
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async getSessions(userId: string) {
|
|
166
|
+
return db.select().from(identity_session).where(eq(identity_session.userId, userId)); // ✅ Valid: identity_session belongs to auth domain
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
94
171
|
### `no-cross-schema-slonik-access`
|
|
95
172
|
|
|
96
173
|
Prevents TypeScript/JavaScript modules from accessing database tables outside their schema boundaries when using slonik. This rule enforces that all table references must be explicitly qualified with the module's schema name and prevents cross-schema access.
|
|
@@ -185,10 +262,20 @@ module.exports = {
|
|
|
185
262
|
{
|
|
186
263
|
files: ['**/*.ts', '**/*.tsx'],
|
|
187
264
|
rules: {
|
|
265
|
+
// Prisma rules
|
|
188
266
|
'@synapsestudios/data-boundaries/no-cross-domain-prisma-access': ['error', {
|
|
189
267
|
schemaDir: 'prisma/schema',
|
|
190
268
|
modulePath: '/modules/' // Default - change to '/src/' for NestJS projects
|
|
191
269
|
}],
|
|
270
|
+
// Drizzle rules
|
|
271
|
+
'@synapsestudios/data-boundaries/no-cross-schema-drizzle-references': ['error', {
|
|
272
|
+
schemaDir: 'src/db/schema', // Adjust to your Drizzle schema directory
|
|
273
|
+
}],
|
|
274
|
+
'@synapsestudios/data-boundaries/no-cross-domain-drizzle-access': ['error', {
|
|
275
|
+
schemaDir: 'src/db/schema', // Adjust to your Drizzle schema directory
|
|
276
|
+
modulePath: '/modules/' // Default - change to '/src/' for NestJS projects
|
|
277
|
+
}],
|
|
278
|
+
// Slonik rules
|
|
192
279
|
'@synapsestudios/data-boundaries/no-cross-schema-slonik-access': ['error', {
|
|
193
280
|
modulePath: '/modules/' // Default - change to '/src/' for NestJS projects
|
|
194
281
|
}]
|
|
@@ -232,20 +319,36 @@ export default [
|
|
|
232
319
|
// 4. TypeScript files rule config
|
|
233
320
|
{
|
|
234
321
|
files: ['**/*.ts', '**/*.tsx'],
|
|
235
|
-
plugins: {
|
|
236
|
-
'@synapsestudios/data-boundaries': eslintPluginDataBoundaries
|
|
322
|
+
plugins: {
|
|
323
|
+
'@synapsestudios/data-boundaries': eslintPluginDataBoundaries
|
|
237
324
|
},
|
|
238
325
|
rules: {
|
|
326
|
+
// Prisma rules
|
|
239
327
|
'@synapsestudios/data-boundaries/no-cross-domain-prisma-access': [
|
|
240
328
|
'error',
|
|
241
|
-
{
|
|
242
|
-
schemaDir: 'prisma/schema',
|
|
329
|
+
{
|
|
330
|
+
schemaDir: 'prisma/schema',
|
|
331
|
+
modulePath: '/src/' // Use '/src/' for NestJS, '/modules/' for other structures
|
|
332
|
+
}
|
|
333
|
+
],
|
|
334
|
+
// Drizzle rules
|
|
335
|
+
'@synapsestudios/data-boundaries/no-cross-schema-drizzle-references': [
|
|
336
|
+
'error',
|
|
337
|
+
{
|
|
338
|
+
schemaDir: 'src/db/schema', // Adjust to your Drizzle schema directory
|
|
339
|
+
}
|
|
340
|
+
],
|
|
341
|
+
'@synapsestudios/data-boundaries/no-cross-domain-drizzle-access': [
|
|
342
|
+
'error',
|
|
343
|
+
{
|
|
344
|
+
schemaDir: 'src/db/schema', // Adjust to your Drizzle schema directory
|
|
243
345
|
modulePath: '/src/' // Use '/src/' for NestJS, '/modules/' for other structures
|
|
244
346
|
}
|
|
245
347
|
],
|
|
348
|
+
// Slonik rules
|
|
246
349
|
'@synapsestudios/data-boundaries/no-cross-schema-slonik-access': [
|
|
247
350
|
'error',
|
|
248
|
-
{
|
|
351
|
+
{
|
|
249
352
|
modulePath: '/src/' // Use '/src/' for NestJS, '/modules/' for other structures
|
|
250
353
|
}
|
|
251
354
|
],
|
|
@@ -289,6 +392,32 @@ module.exports = {
|
|
|
289
392
|
}
|
|
290
393
|
```
|
|
291
394
|
|
|
395
|
+
#### `no-cross-schema-drizzle-references`
|
|
396
|
+
|
|
397
|
+
- **`schemaDir`** (string): Directory containing Drizzle schema files, relative to project root. Default: `'src/db/schema'`
|
|
398
|
+
|
|
399
|
+
```javascript
|
|
400
|
+
{
|
|
401
|
+
'@synapsestudios/data-boundaries/no-cross-schema-drizzle-references': ['error', {
|
|
402
|
+
schemaDir: 'src/db/schema' // Adjust to your Drizzle schema directory
|
|
403
|
+
}]
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
#### `no-cross-domain-drizzle-access`
|
|
408
|
+
|
|
409
|
+
- **`schemaDir`** (string): Directory containing Drizzle schema files, relative to project root. Default: `'src/db/schema'`
|
|
410
|
+
- **`modulePath`** (string): Path pattern to match module directories. Default: `'/modules/'`. Use `'/src/'` for NestJS projects or other domain-based structures.
|
|
411
|
+
|
|
412
|
+
```javascript
|
|
413
|
+
{
|
|
414
|
+
'@synapsestudios/data-boundaries/no-cross-domain-drizzle-access': ['error', {
|
|
415
|
+
schemaDir: 'src/db/schema', // Adjust to your Drizzle schema directory
|
|
416
|
+
modulePath: '/src/' // For NestJS-style projects
|
|
417
|
+
}]
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
292
421
|
#### `no-cross-schema-slonik-access`
|
|
293
422
|
|
|
294
423
|
- **`modulePath`** (string): Path pattern to match module directories. Default: `'/modules/'`. Use `'/src/'` for NestJS projects or other domain-based structures.
|
|
@@ -334,7 +463,7 @@ src/
|
|
|
334
463
|
|
|
335
464
|
**Note**: For NestJS projects, set `modulePath: '/src/'` in your rule configuration.
|
|
336
465
|
|
|
337
|
-
### Schema Structure
|
|
466
|
+
### Prisma Schema Structure
|
|
338
467
|
```
|
|
339
468
|
prisma/
|
|
340
469
|
schema/
|
|
@@ -343,14 +472,31 @@ prisma/
|
|
|
343
472
|
main.prisma # Contains shared models (AuditLog, Setting)
|
|
344
473
|
```
|
|
345
474
|
|
|
475
|
+
### Drizzle Schema Structure
|
|
476
|
+
```
|
|
477
|
+
src/
|
|
478
|
+
db/
|
|
479
|
+
schema/
|
|
480
|
+
auth.schema.ts # Contains identity_user, identity_session tables
|
|
481
|
+
organization.schema.ts # Contains organization table
|
|
482
|
+
execution.schema.ts # Contains execution, message, llmCall tables
|
|
483
|
+
index.ts # Barrel export for all schemas
|
|
484
|
+
```
|
|
485
|
+
|
|
346
486
|
## Domain Mapping
|
|
347
487
|
|
|
348
488
|
The plugin automatically maps:
|
|
349
489
|
|
|
490
|
+
### Prisma
|
|
350
491
|
- **File paths to domains**: `/modules/auth/` → `auth` domain
|
|
351
492
|
- **Schema files to domains**: `auth.prisma` → `auth` domain
|
|
352
493
|
- **Special cases**: `main.prisma` and `schema.prisma` → `shared` domain
|
|
353
494
|
|
|
495
|
+
### Drizzle
|
|
496
|
+
- **File paths to domains**: `/modules/auth/` → `auth` domain
|
|
497
|
+
- **Schema files to domains**: `auth.schema.ts` → `auth` domain
|
|
498
|
+
- **Table names to domains**: Extracted from schema file exports
|
|
499
|
+
|
|
354
500
|
## Use Cases
|
|
355
501
|
|
|
356
502
|
### Modular Monoliths
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
declare const noCrossFileModelReferences: any;
|
|
2
2
|
declare const noCrossDomainPrismaAccess: any;
|
|
3
3
|
declare const noCrossSchemaSlonikAccess: any;
|
|
4
|
+
declare const noCrossSchemaDrizzleReferences: any;
|
|
5
|
+
declare const noCrossDomainDrizzleAccess: any;
|
|
4
6
|
declare const prismaParser: any;
|
|
5
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,0BAA0B,KAAoD,CAAC;AACrF,QAAA,MAAM,yBAAyB,KAAmD,CAAC;AACnF,QAAA,MAAM,yBAAyB,KAAmD,CAAC;AACnF,QAAA,MAAM,YAAY,KAAqC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,0BAA0B,KAAoD,CAAC;AACrF,QAAA,MAAM,yBAAyB,KAAmD,CAAC;AACnF,QAAA,MAAM,yBAAyB,KAAmD,CAAC;AACnF,QAAA,MAAM,8BAA8B,KAAwD,CAAC;AAC7F,QAAA,MAAM,0BAA0B,KAAoD,CAAC;AACrF,QAAA,MAAM,YAAY,KAAqC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
const noCrossFileModelReferences = require('./rules/no-cross-file-model-references');
|
|
3
3
|
const noCrossDomainPrismaAccess = require('./rules/no-cross-domain-prisma-access');
|
|
4
4
|
const noCrossSchemaSlonikAccess = require('./rules/no-cross-schema-slonik-access');
|
|
5
|
+
const noCrossSchemaDrizzleReferences = require('./rules/no-cross-schema-drizzle-references');
|
|
6
|
+
const noCrossDomainDrizzleAccess = require('./rules/no-cross-domain-drizzle-access');
|
|
5
7
|
const prismaParser = require('./parsers/prisma-parser');
|
|
6
8
|
module.exports = {
|
|
7
9
|
rules: {
|
|
8
10
|
'no-cross-file-model-references': noCrossFileModelReferences,
|
|
9
11
|
'no-cross-domain-prisma-access': noCrossDomainPrismaAccess,
|
|
10
12
|
'no-cross-schema-slonik-access': noCrossSchemaSlonikAccess,
|
|
13
|
+
'no-cross-schema-drizzle-references': noCrossSchemaDrizzleReferences,
|
|
14
|
+
'no-cross-domain-drizzle-access': noCrossDomainDrizzleAccess,
|
|
11
15
|
},
|
|
12
16
|
parsers: {
|
|
13
17
|
prisma: prismaParser,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,MAAM,0BAA0B,GAAG,OAAO,CAAC,wCAAwC,CAAC,CAAC;AACrF,MAAM,yBAAyB,GAAG,OAAO,CAAC,uCAAuC,CAAC,CAAC;AACnF,MAAM,yBAAyB,GAAG,OAAO,CAAC,uCAAuC,CAAC,CAAC;AACnF,MAAM,YAAY,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;AAExD,MAAM,CAAC,OAAO,GAAG;IACf,KAAK,EAAE;QACL,gCAAgC,EAAE,0BAA0B;QAC5D,+BAA+B,EAAE,yBAAyB;QAC1D,+BAA+B,EAAE,yBAAyB;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,MAAM,0BAA0B,GAAG,OAAO,CAAC,wCAAwC,CAAC,CAAC;AACrF,MAAM,yBAAyB,GAAG,OAAO,CAAC,uCAAuC,CAAC,CAAC;AACnF,MAAM,yBAAyB,GAAG,OAAO,CAAC,uCAAuC,CAAC,CAAC;AACnF,MAAM,8BAA8B,GAAG,OAAO,CAAC,4CAA4C,CAAC,CAAC;AAC7F,MAAM,0BAA0B,GAAG,OAAO,CAAC,wCAAwC,CAAC,CAAC;AACrF,MAAM,YAAY,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;AAExD,MAAM,CAAC,OAAO,GAAG;IACf,KAAK,EAAE;QACL,gCAAgC,EAAE,0BAA0B;QAC5D,+BAA+B,EAAE,yBAAyB;QAC1D,+BAA+B,EAAE,yBAAyB;QAC1D,oCAAoC,EAAE,8BAA8B;QACpE,gCAAgC,EAAE,0BAA0B;KAC7D;IACD,OAAO,EAAE;QACP,MAAM,EAAE,YAAY;KACrB;IACD,OAAO,EAAE;QACP,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,iCAAiC,CAAC;YAC5C,SAAS,EAAE;gBACT;oBACE,KAAK,EAAE,CAAC,aAAa,CAAC;oBACtB,MAAM,EAAE,8DAA8D;oBACtE,KAAK,EAAE;wBACL,gEAAgE,EAAE,OAAO;qBAC1E;iBACF;gBACD;oBACE,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;oBAC9B,KAAK,EAAE;wBACL,+DAA+D,EAAE,OAAO;wBACxE,+DAA+D,EAAE,OAAO;qBACzE;iBACF;aACF;SACF;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
interface RuleOptions {
|
|
3
|
+
schemaDir: string;
|
|
4
|
+
modulePath: string;
|
|
5
|
+
}
|
|
6
|
+
declare const rule: ESLintUtils.RuleModule<"crossDomainAccess" | "tableNotFound" | "configError", [RuleOptions], unknown, ESLintUtils.RuleListener>;
|
|
7
|
+
export = rule;
|
|
8
|
+
//# sourceMappingURL=no-cross-domain-drizzle-access.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-cross-domain-drizzle-access.d.ts","sourceRoot":"","sources":["../../src/rules/no-cross-domain-drizzle-access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAY,MAAM,0BAA0B,CAAC;AAMjE,UAAU,WAAW;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AA6CD,QAAA,MAAM,IAAI,iIAsIR,CAAC;AAEH,SAAS,IAAI,CAAC"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
36
|
+
const path = __importStar(require("path"));
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const drizzle_parser_1 = require("../utils/drizzle-parser");
|
|
39
|
+
const schema_parser_1 = require("../utils/schema-parser");
|
|
40
|
+
/**
|
|
41
|
+
* Find project root by looking for package.json
|
|
42
|
+
*/
|
|
43
|
+
function findProjectRoot(filePath) {
|
|
44
|
+
let dir = path.dirname(filePath);
|
|
45
|
+
while (dir !== path.dirname(dir)) {
|
|
46
|
+
if (fs.existsSync(path.join(dir, 'package.json'))) {
|
|
47
|
+
return dir;
|
|
48
|
+
}
|
|
49
|
+
dir = path.dirname(dir);
|
|
50
|
+
}
|
|
51
|
+
throw new Error('Could not find project root');
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if an import is from a Drizzle schema barrel export
|
|
55
|
+
* Looks for patterns like: import { users } from '@/db/schema'
|
|
56
|
+
*/
|
|
57
|
+
function isSchemaImport(node) {
|
|
58
|
+
if (!node.source.value || typeof node.source.value !== 'string') {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const source = node.source.value;
|
|
62
|
+
// Check for various patterns:
|
|
63
|
+
// - '@/db/schema'
|
|
64
|
+
// - '../db/schema'
|
|
65
|
+
// - './schema'
|
|
66
|
+
// - etc.
|
|
67
|
+
return source.includes('/db/schema') || source.includes('/schema') || source.endsWith('/schema');
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* ESLint rule to prevent modules from accessing Drizzle tables
|
|
71
|
+
* that belong to other domains
|
|
72
|
+
*/
|
|
73
|
+
const createRule = utils_1.ESLintUtils.RuleCreator((name) => `https://github.com/synapsestudios/eslint-plugin-data-boundaries#${name}`);
|
|
74
|
+
const rule = createRule({
|
|
75
|
+
name: 'no-cross-domain-drizzle-access',
|
|
76
|
+
meta: {
|
|
77
|
+
type: 'problem',
|
|
78
|
+
docs: {
|
|
79
|
+
description: 'Disallow modules from accessing Drizzle tables defined in other domain schema files',
|
|
80
|
+
},
|
|
81
|
+
fixable: undefined,
|
|
82
|
+
schema: [
|
|
83
|
+
{
|
|
84
|
+
type: 'object',
|
|
85
|
+
properties: {
|
|
86
|
+
schemaDir: {
|
|
87
|
+
type: 'string',
|
|
88
|
+
description: 'Directory containing Drizzle schema files (relative to project root)',
|
|
89
|
+
},
|
|
90
|
+
modulePath: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
description: 'Path pattern to match module directories (e.g., "/modules/", "/src/")',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
additionalProperties: false,
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
messages: {
|
|
99
|
+
crossDomainAccess: "Module '{{currentModule}}' cannot access '{{tableName}}' table (belongs to '{{tableDomain}}' domain). Consider using a shared service or moving the logic to the appropriate domain.",
|
|
100
|
+
tableNotFound: "Table '{{tableName}}' not found in any schema file. Ensure the table exists and schema files are properly configured.",
|
|
101
|
+
configError: "Could not determine schema directory. Please configure the 'schemaDir' option in your ESLint config.",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
defaultOptions: [
|
|
105
|
+
{
|
|
106
|
+
schemaDir: 'src/db/schema',
|
|
107
|
+
modulePath: '/modules/',
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
create(context, [options]) {
|
|
111
|
+
const filename = context.getFilename();
|
|
112
|
+
// Only process TypeScript files in modules
|
|
113
|
+
if (!filename.includes(options.modulePath) || !filename.match(/\.(ts|tsx)$/)) {
|
|
114
|
+
return {};
|
|
115
|
+
}
|
|
116
|
+
// Extract current module from file path
|
|
117
|
+
const currentModule = (0, schema_parser_1.extractModuleFromPath)(filename, options.modulePath);
|
|
118
|
+
if (!currentModule) {
|
|
119
|
+
return {};
|
|
120
|
+
}
|
|
121
|
+
// Build table-to-domain mapping
|
|
122
|
+
let tableToDomain = {};
|
|
123
|
+
try {
|
|
124
|
+
let schemaDir;
|
|
125
|
+
if (path.isAbsolute(options.schemaDir)) {
|
|
126
|
+
// Use absolute path directly (for tests)
|
|
127
|
+
schemaDir = options.schemaDir;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Resolve relative to project root (for real usage)
|
|
131
|
+
const projectRoot = findProjectRoot(filename);
|
|
132
|
+
schemaDir = path.join(projectRoot, options.schemaDir);
|
|
133
|
+
}
|
|
134
|
+
tableToDomain = (0, drizzle_parser_1.buildTableToDomainMapping)(schemaDir);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// Report configuration error only once per file
|
|
138
|
+
let reportedConfigError = false;
|
|
139
|
+
return {
|
|
140
|
+
Program(node) {
|
|
141
|
+
if (!reportedConfigError) {
|
|
142
|
+
context.report({
|
|
143
|
+
node,
|
|
144
|
+
messageId: 'configError',
|
|
145
|
+
});
|
|
146
|
+
reportedConfigError = true;
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// Track imported tables and their locations
|
|
152
|
+
const importedTables = new Map();
|
|
153
|
+
return {
|
|
154
|
+
// Detect imports: import { users, posts } from '@/db/schema'
|
|
155
|
+
ImportDeclaration(node) {
|
|
156
|
+
// Check if this is a schema import
|
|
157
|
+
if (!isSchemaImport(node)) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// Extract imported table names
|
|
161
|
+
for (const specifier of node.specifiers) {
|
|
162
|
+
if (specifier.type === 'ImportSpecifier' && specifier.imported.type === 'Identifier') {
|
|
163
|
+
const tableName = specifier.imported.name;
|
|
164
|
+
importedTables.set(tableName, specifier);
|
|
165
|
+
// Check if this table belongs to a different domain
|
|
166
|
+
const tableDomain = tableToDomain[tableName];
|
|
167
|
+
if (!tableDomain) {
|
|
168
|
+
// Table not found in any schema file
|
|
169
|
+
context.report({
|
|
170
|
+
node: specifier,
|
|
171
|
+
messageId: 'tableNotFound',
|
|
172
|
+
data: { tableName },
|
|
173
|
+
});
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
// Check if current module matches table domain
|
|
177
|
+
// Special case: 'index' files are treated as the domain name itself
|
|
178
|
+
const effectiveCurrentModule = currentModule === 'index' ? path.basename(path.dirname(filename)) : currentModule;
|
|
179
|
+
if (effectiveCurrentModule !== tableDomain) {
|
|
180
|
+
context.report({
|
|
181
|
+
node: specifier,
|
|
182
|
+
messageId: 'crossDomainAccess',
|
|
183
|
+
data: {
|
|
184
|
+
currentModule: effectiveCurrentModule,
|
|
185
|
+
tableName,
|
|
186
|
+
tableDomain,
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
module.exports = rule;
|
|
197
|
+
//# sourceMappingURL=no-cross-domain-drizzle-access.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-cross-domain-drizzle-access.js","sourceRoot":"","sources":["../../src/rules/no-cross-domain-drizzle-access.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,6 @@ interface RuleOptions {
|
|
|
3
3
|
schemaDir: string;
|
|
4
4
|
modulePath: string;
|
|
5
5
|
}
|
|
6
|
-
declare const rule: ESLintUtils.RuleModule<"crossDomainAccess" | "
|
|
6
|
+
declare const rule: ESLintUtils.RuleModule<"crossDomainAccess" | "configError" | "modelNotFound", [RuleOptions], unknown, ESLintUtils.RuleListener>;
|
|
7
7
|
export = rule;
|
|
8
8
|
//# sourceMappingURL=no-cross-domain-prisma-access.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-cross-file-model-references.d.ts","sourceRoot":"","sources":["../../src/rules/no-cross-file-model-references.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"no-cross-file-model-references.d.ts","sourceRoot":"","sources":["../../src/rules/no-cross-file-model-references.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAwD9B,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAyGhB,CAAC;AAEF,SAAS,IAAI,CAAC"}
|
|
@@ -1,15 +1,45 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Helper function to recursively extract the base type name from complex field types
|
|
4
|
+
*/
|
|
5
|
+
function extractBaseType(fieldType) {
|
|
6
|
+
if (typeof fieldType === 'string') {
|
|
7
|
+
return fieldType;
|
|
8
|
+
}
|
|
9
|
+
if (fieldType && fieldType.type) {
|
|
10
|
+
switch (fieldType.type) {
|
|
11
|
+
case 'array':
|
|
12
|
+
return extractBaseType(fieldType.fieldType);
|
|
13
|
+
case 'optional':
|
|
14
|
+
return extractBaseType(fieldType.fieldType);
|
|
15
|
+
default:
|
|
16
|
+
// For other types, try to get the name or fieldType
|
|
17
|
+
return fieldType.name || extractBaseType(fieldType.fieldType) || '';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return '';
|
|
21
|
+
}
|
|
2
22
|
/**
|
|
3
23
|
* Helper function to find the line number of a field in the schema content
|
|
4
|
-
*
|
|
24
|
+
* Looks for the field name followed by a type (space-separated tokens)
|
|
5
25
|
*/
|
|
6
26
|
function getLineNumber(content, fieldName) {
|
|
7
27
|
const lines = content.split('\n');
|
|
8
28
|
for (let i = 0; i < lines.length; i++) {
|
|
9
|
-
|
|
29
|
+
const line = lines[i].trim();
|
|
30
|
+
// Look for lines that start with the field name followed by whitespace and a type
|
|
31
|
+
// This avoids matching field names that appear in comments or other contexts
|
|
32
|
+
const fieldPattern = new RegExp(`^${fieldName}\\s+\\w+`);
|
|
33
|
+
if (fieldPattern.test(line)) {
|
|
10
34
|
return i + 1; // ESLint uses 1-based line numbers
|
|
11
35
|
}
|
|
12
36
|
}
|
|
37
|
+
// Fallback: look for any line containing the field name
|
|
38
|
+
for (let i = 0; i < lines.length; i++) {
|
|
39
|
+
if (lines[i].includes(fieldName)) {
|
|
40
|
+
return i + 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
13
43
|
return 1; // Default to line 1 if not found
|
|
14
44
|
}
|
|
15
45
|
const rule = {
|
|
@@ -42,20 +72,24 @@ const rule = {
|
|
|
42
72
|
return;
|
|
43
73
|
}
|
|
44
74
|
const ast = services.getPrismaAst();
|
|
45
|
-
// Extract all models defined in this file
|
|
46
|
-
const
|
|
75
|
+
// Extract all models and enums defined in this file
|
|
76
|
+
const definedTypes = new Set();
|
|
47
77
|
ast.list.forEach((item) => {
|
|
48
|
-
if (item.type === 'model' && item.name) {
|
|
49
|
-
|
|
78
|
+
if ((item.type === 'model' || item.type === 'enum') && item.name) {
|
|
79
|
+
definedTypes.add(item.name);
|
|
50
80
|
}
|
|
51
81
|
});
|
|
52
82
|
// Check each model for cross-file references
|
|
53
83
|
ast.list.forEach((item) => {
|
|
54
84
|
if (item.type === 'model' && item.properties) {
|
|
55
85
|
item.properties.forEach((property) => {
|
|
56
|
-
if (property && property.fieldType && property.name) {
|
|
57
|
-
//
|
|
58
|
-
const
|
|
86
|
+
if (property && property.type === 'field' && property.fieldType && property.name) {
|
|
87
|
+
// Extract the field type - handle both simple types and complex types
|
|
88
|
+
const referencedTypeName = extractBaseType(property.fieldType);
|
|
89
|
+
if (!referencedTypeName) {
|
|
90
|
+
// Skip if we can't determine the type
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
59
93
|
// Skip primitive types (String, Int, DateTime, etc.)
|
|
60
94
|
const primitiveTypes = [
|
|
61
95
|
'String',
|
|
@@ -66,14 +100,12 @@ const rule = {
|
|
|
66
100
|
'Json',
|
|
67
101
|
'Bytes',
|
|
68
102
|
];
|
|
69
|
-
if (primitiveTypes.includes(
|
|
103
|
+
if (primitiveTypes.includes(referencedTypeName)) {
|
|
70
104
|
return;
|
|
71
105
|
}
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (!definedModels.has(cleanModelName)) {
|
|
76
|
-
// This is a cross-file model reference - report it
|
|
106
|
+
// Check if the referenced type is defined in this file
|
|
107
|
+
if (!definedTypes.has(referencedTypeName)) {
|
|
108
|
+
// This is a cross-file reference - report it
|
|
77
109
|
const sourceCode = context.getSourceCode();
|
|
78
110
|
const schemaContent = sourceCode.getText();
|
|
79
111
|
const fieldLine = getLineNumber(schemaContent, property.name);
|
|
@@ -86,7 +118,7 @@ const rule = {
|
|
|
86
118
|
messageId: 'crossFileReference',
|
|
87
119
|
data: {
|
|
88
120
|
field: property.name,
|
|
89
|
-
model:
|
|
121
|
+
model: referencedTypeName,
|
|
90
122
|
},
|
|
91
123
|
});
|
|
92
124
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-cross-file-model-references.js","sourceRoot":"","sources":["../../src/rules/no-cross-file-model-references.ts"],"names":[],"mappings":";AAOA;;;GAGG;AACH,SAAS,aAAa,CAAC,OAAe,EAAE,SAAiB;IACvD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"no-cross-file-model-references.js","sourceRoot":"","sources":["../../src/rules/no-cross-file-model-references.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
interface RuleOptions {
|
|
3
|
+
schemaDir: string;
|
|
4
|
+
}
|
|
5
|
+
declare const rule: ESLintUtils.RuleModule<"configError" | "crossSchemaReference", [RuleOptions], unknown, ESLintUtils.RuleListener>;
|
|
6
|
+
export = rule;
|
|
7
|
+
//# sourceMappingURL=no-cross-schema-drizzle-references.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-cross-schema-drizzle-references.d.ts","sourceRoot":"","sources":["../../src/rules/no-cross-schema-drizzle-references.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAY,MAAM,0BAA0B,CAAC;AAOjE,UAAU,WAAW;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAsJD,QAAA,MAAM,IAAI,kHAwGR,CAAC;AAEH,SAAS,IAAI,CAAC"}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
36
|
+
const path = __importStar(require("path"));
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const parser = __importStar(require("@typescript-eslint/typescript-estree"));
|
|
39
|
+
const utils_2 = require("@typescript-eslint/utils");
|
|
40
|
+
const drizzle_parser_1 = require("../utils/drizzle-parser");
|
|
41
|
+
/**
|
|
42
|
+
* Find project root by looking for package.json
|
|
43
|
+
*/
|
|
44
|
+
function findProjectRoot(filePath) {
|
|
45
|
+
let dir = path.dirname(filePath);
|
|
46
|
+
while (dir !== path.dirname(dir)) {
|
|
47
|
+
if (fs.existsSync(path.join(dir, 'package.json'))) {
|
|
48
|
+
return dir;
|
|
49
|
+
}
|
|
50
|
+
dir = path.dirname(dir);
|
|
51
|
+
}
|
|
52
|
+
throw new Error('Could not find project root');
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Extract table references from the AST
|
|
56
|
+
* Looks for patterns like:
|
|
57
|
+
* - .references(() => table_name.id)
|
|
58
|
+
* - relations(table_name, ...)
|
|
59
|
+
* - one(table_name, ...)
|
|
60
|
+
* - many(table_name, ...)
|
|
61
|
+
*/
|
|
62
|
+
function extractTableReferences(ast) {
|
|
63
|
+
const references = [];
|
|
64
|
+
function visit(node) {
|
|
65
|
+
if (!node)
|
|
66
|
+
return;
|
|
67
|
+
// Look for .references() in column definitions
|
|
68
|
+
if (node.type === utils_2.AST_NODE_TYPES.CallExpression &&
|
|
69
|
+
node.callee.type === utils_2.AST_NODE_TYPES.MemberExpression &&
|
|
70
|
+
node.callee.property.type === utils_2.AST_NODE_TYPES.Identifier &&
|
|
71
|
+
node.callee.property.name === 'references') {
|
|
72
|
+
// The argument is usually an arrow function: () => users.id
|
|
73
|
+
const arg = node.arguments[0];
|
|
74
|
+
if (arg?.type === utils_2.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
75
|
+
const body = arg.body;
|
|
76
|
+
// Look for users.id pattern
|
|
77
|
+
if (body.type === utils_2.AST_NODE_TYPES.MemberExpression &&
|
|
78
|
+
body.object.type === utils_2.AST_NODE_TYPES.Identifier &&
|
|
79
|
+
body.object.loc) {
|
|
80
|
+
references.push({
|
|
81
|
+
name: body.object.name,
|
|
82
|
+
loc: body.object.loc,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Look for relations() calls
|
|
88
|
+
if (node.type === utils_2.AST_NODE_TYPES.CallExpression &&
|
|
89
|
+
node.callee.type === utils_2.AST_NODE_TYPES.Identifier &&
|
|
90
|
+
node.callee.name === 'relations') {
|
|
91
|
+
// First argument should be the table reference
|
|
92
|
+
const firstArg = node.arguments[0];
|
|
93
|
+
if (firstArg?.type === utils_2.AST_NODE_TYPES.Identifier && firstArg.loc) {
|
|
94
|
+
references.push({
|
|
95
|
+
name: firstArg.name,
|
|
96
|
+
loc: firstArg.loc,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Look for one() and many() calls inside relations
|
|
101
|
+
if (node.type === utils_2.AST_NODE_TYPES.CallExpression &&
|
|
102
|
+
node.callee.type === utils_2.AST_NODE_TYPES.Identifier &&
|
|
103
|
+
(node.callee.name === 'one' || node.callee.name === 'many')) {
|
|
104
|
+
// First argument should be the referenced table
|
|
105
|
+
const firstArg = node.arguments[0];
|
|
106
|
+
if (firstArg?.type === utils_2.AST_NODE_TYPES.Identifier && firstArg.loc) {
|
|
107
|
+
references.push({
|
|
108
|
+
name: firstArg.name,
|
|
109
|
+
loc: firstArg.loc,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Recursively visit child nodes
|
|
114
|
+
for (const key in node) {
|
|
115
|
+
if (key === 'parent' || key === 'loc' || key === 'range')
|
|
116
|
+
continue;
|
|
117
|
+
const child = node[key];
|
|
118
|
+
if (Array.isArray(child)) {
|
|
119
|
+
child.forEach(visit);
|
|
120
|
+
}
|
|
121
|
+
else if (typeof child === 'object') {
|
|
122
|
+
visit(child);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
visit(ast);
|
|
127
|
+
return references;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Build a mapping of all tables to their schema files
|
|
131
|
+
*/
|
|
132
|
+
function buildTableToFileMapping(schemaDir) {
|
|
133
|
+
const tableToFile = new Map();
|
|
134
|
+
try {
|
|
135
|
+
const files = fs.readdirSync(schemaDir);
|
|
136
|
+
const schemaFiles = files.filter((f) => f.endsWith('.schema.ts') || f.endsWith('.ts'));
|
|
137
|
+
for (const file of schemaFiles) {
|
|
138
|
+
const filePath = path.join(schemaDir, file);
|
|
139
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
140
|
+
// Parse TypeScript file
|
|
141
|
+
const ast = parser.parse(content, {
|
|
142
|
+
loc: true,
|
|
143
|
+
range: true,
|
|
144
|
+
jsx: false,
|
|
145
|
+
});
|
|
146
|
+
// Extract tables from this schema file
|
|
147
|
+
const tables = (0, drizzle_parser_1.extractTablesFromAst)(ast);
|
|
148
|
+
for (const table of tables) {
|
|
149
|
+
tableToFile.set(table, file);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
155
|
+
console.warn(`Warning: Could not build table-to-file mapping in ${schemaDir}:`, errorMessage);
|
|
156
|
+
}
|
|
157
|
+
return tableToFile;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* ESLint rule to prevent Drizzle table definitions in one schema file
|
|
161
|
+
* from referencing tables in other schema files
|
|
162
|
+
*/
|
|
163
|
+
const createRule = utils_1.ESLintUtils.RuleCreator((name) => `https://github.com/synapsestudios/eslint-plugin-data-boundaries#${name}`);
|
|
164
|
+
const rule = createRule({
|
|
165
|
+
name: 'no-cross-schema-drizzle-references',
|
|
166
|
+
meta: {
|
|
167
|
+
type: 'problem',
|
|
168
|
+
docs: {
|
|
169
|
+
description: 'Disallow Drizzle table definitions from referencing tables defined in other schema files',
|
|
170
|
+
},
|
|
171
|
+
fixable: undefined,
|
|
172
|
+
schema: [
|
|
173
|
+
{
|
|
174
|
+
type: 'object',
|
|
175
|
+
properties: {
|
|
176
|
+
schemaDir: {
|
|
177
|
+
type: 'string',
|
|
178
|
+
description: 'Directory containing Drizzle schema files (relative to project root)',
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
additionalProperties: false,
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
messages: {
|
|
185
|
+
crossSchemaReference: "Table '{{currentFile}}' references '{{tableName}}' which is defined in '{{referencedFile}}'. Cross-schema table references are not allowed. Consider moving related tables to the same schema file or restructuring your domain boundaries.",
|
|
186
|
+
configError: "Could not determine schema directory. Please configure the 'schemaDir' option in your ESLint config.",
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
defaultOptions: [
|
|
190
|
+
{
|
|
191
|
+
schemaDir: 'src/db/schema',
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
create(context, [options]) {
|
|
195
|
+
const filename = context.getFilename();
|
|
196
|
+
// Only process .schema.ts or .ts files in the schema directory
|
|
197
|
+
if (!filename.includes('.schema.ts') && !filename.endsWith('.ts')) {
|
|
198
|
+
return {};
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
Program(node) {
|
|
202
|
+
try {
|
|
203
|
+
// Determine schema directory
|
|
204
|
+
let schemaDir;
|
|
205
|
+
if (path.isAbsolute(options.schemaDir)) {
|
|
206
|
+
schemaDir = options.schemaDir;
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
const projectRoot = findProjectRoot(filename);
|
|
210
|
+
schemaDir = path.join(projectRoot, options.schemaDir);
|
|
211
|
+
}
|
|
212
|
+
// Check if this file is actually in the schema directory
|
|
213
|
+
const normalizedFilename = path.normalize(filename);
|
|
214
|
+
const normalizedSchemaDir = path.normalize(schemaDir);
|
|
215
|
+
if (!normalizedFilename.startsWith(normalizedSchemaDir)) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
// Build mapping of all tables to their files
|
|
219
|
+
const tableToFile = buildTableToFileMapping(schemaDir);
|
|
220
|
+
// Get tables defined in this file
|
|
221
|
+
const localTables = new Set((0, drizzle_parser_1.extractTablesFromAst)(node));
|
|
222
|
+
// Get all table references in this file
|
|
223
|
+
const references = extractTableReferences(node);
|
|
224
|
+
// Check each reference
|
|
225
|
+
for (const ref of references) {
|
|
226
|
+
// Skip if it's defined locally
|
|
227
|
+
if (localTables.has(ref.name)) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
// Check if it exists in another schema file
|
|
231
|
+
const referencedFile = tableToFile.get(ref.name);
|
|
232
|
+
if (referencedFile) {
|
|
233
|
+
const currentFile = path.basename(filename);
|
|
234
|
+
context.report({
|
|
235
|
+
node,
|
|
236
|
+
loc: ref.loc,
|
|
237
|
+
messageId: 'crossSchemaReference',
|
|
238
|
+
data: {
|
|
239
|
+
currentFile,
|
|
240
|
+
tableName: ref.name,
|
|
241
|
+
referencedFile,
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
249
|
+
if (errorMessage.includes('Could not find project root')) {
|
|
250
|
+
context.report({
|
|
251
|
+
node,
|
|
252
|
+
messageId: 'configError',
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
module.exports = rule;
|
|
261
|
+
//# sourceMappingURL=no-cross-schema-drizzle-references.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-cross-schema-drizzle-references.js","sourceRoot":"","sources":["../../src/rules/no-cross-schema-drizzle-references.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { TSESTree } from '@typescript-eslint/utils';
|
|
2
|
+
export interface TableToDomainMapping {
|
|
3
|
+
[tableName: string]: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Extract table definitions from Drizzle schema file AST
|
|
7
|
+
* Looks for patterns like: export const users = pgTable('users', {...})
|
|
8
|
+
*/
|
|
9
|
+
export declare function extractTablesFromAst(ast: TSESTree.Program): string[];
|
|
10
|
+
/**
|
|
11
|
+
* Parse all Drizzle schema files in a directory and build table-to-domain mapping
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildTableToDomainMapping(schemaDir: string): TableToDomainMapping;
|
|
14
|
+
/**
|
|
15
|
+
* Check if a table reference in a schema file references a table from another file
|
|
16
|
+
*/
|
|
17
|
+
export declare function checkCrossSchemaTableReference(filePath: string, schemaDir: string): {
|
|
18
|
+
isValid: boolean;
|
|
19
|
+
violations: Array<{
|
|
20
|
+
table: string;
|
|
21
|
+
line: number;
|
|
22
|
+
column: number;
|
|
23
|
+
}>;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=drizzle-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drizzle-parser.d.ts","sourceRoot":"","sources":["../../src/utils/drizzle-parser.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkB,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpE,MAAM,WAAW,oBAAoB;IACnC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,GAAG,MAAM,EAAE,CA+CpE;AAyED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,oBAAoB,CAmCjF;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CA+C1F"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.extractTablesFromAst = extractTablesFromAst;
|
|
37
|
+
exports.buildTableToDomainMapping = buildTableToDomainMapping;
|
|
38
|
+
exports.checkCrossSchemaTableReference = checkCrossSchemaTableReference;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const glob_1 = require("glob");
|
|
42
|
+
const parser = __importStar(require("@typescript-eslint/typescript-estree"));
|
|
43
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
44
|
+
/**
|
|
45
|
+
* Extract table definitions from Drizzle schema file AST
|
|
46
|
+
* Looks for patterns like: export const users = pgTable('users', {...})
|
|
47
|
+
*/
|
|
48
|
+
function extractTablesFromAst(ast) {
|
|
49
|
+
const tables = [];
|
|
50
|
+
function visit(node) {
|
|
51
|
+
if (!node)
|
|
52
|
+
return;
|
|
53
|
+
// Look for: export const tableName = pgTable/mysqlTable/sqliteTable(...)
|
|
54
|
+
if (node.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration &&
|
|
55
|
+
node.declaration?.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
56
|
+
const declarations = node.declaration.declarations;
|
|
57
|
+
for (const decl of declarations) {
|
|
58
|
+
if (decl.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
59
|
+
decl.id.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
60
|
+
decl.init?.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
61
|
+
const callee = decl.init.callee;
|
|
62
|
+
// Check if calling pgTable, mysqlTable, or sqliteTable
|
|
63
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
64
|
+
(callee.name === 'pgTable' ||
|
|
65
|
+
callee.name === 'mysqlTable' ||
|
|
66
|
+
callee.name === 'sqliteTable')) {
|
|
67
|
+
// Add the exported constant name (the table reference)
|
|
68
|
+
tables.push(decl.id.name);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Recursively visit child nodes
|
|
74
|
+
for (const key in node) {
|
|
75
|
+
if (key === 'parent' || key === 'loc' || key === 'range')
|
|
76
|
+
continue;
|
|
77
|
+
const child = node[key];
|
|
78
|
+
if (Array.isArray(child)) {
|
|
79
|
+
child.forEach(visit);
|
|
80
|
+
}
|
|
81
|
+
else if (typeof child === 'object') {
|
|
82
|
+
visit(child);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
visit(ast);
|
|
87
|
+
return tables;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Extract table references from relations() calls
|
|
91
|
+
* Looks for patterns like: relations(users, ...) or relations(posts, ...)
|
|
92
|
+
*/
|
|
93
|
+
function extractTableReferencesFromRelations(ast) {
|
|
94
|
+
const references = [];
|
|
95
|
+
function visit(node) {
|
|
96
|
+
if (!node)
|
|
97
|
+
return;
|
|
98
|
+
// Look for: relations(tableName, ({ one, many }) => ({...}))
|
|
99
|
+
if (node.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
100
|
+
const callee = node.callee;
|
|
101
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier && callee.name === 'relations') {
|
|
102
|
+
// First argument should be the table reference
|
|
103
|
+
const firstArg = node.arguments[0];
|
|
104
|
+
if (firstArg?.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
105
|
+
references.push(firstArg.name);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Also look for one() and many() calls inside relations
|
|
109
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
110
|
+
(callee.name === 'one' || callee.name === 'many')) {
|
|
111
|
+
// First argument should be the referenced table
|
|
112
|
+
const firstArg = node.arguments[0];
|
|
113
|
+
if (firstArg?.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
114
|
+
references.push(firstArg.name);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Look for .references() in column definitions
|
|
119
|
+
if (node.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
120
|
+
node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
121
|
+
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
122
|
+
node.callee.property.name === 'references') {
|
|
123
|
+
// The argument is usually an arrow function: () => users.id
|
|
124
|
+
const arg = node.arguments[0];
|
|
125
|
+
if (arg?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
126
|
+
const body = arg.body;
|
|
127
|
+
// Look for users.id pattern
|
|
128
|
+
if (body.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
129
|
+
body.object.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
130
|
+
references.push(body.object.name);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Recursively visit child nodes
|
|
135
|
+
for (const key in node) {
|
|
136
|
+
if (key === 'parent' || key === 'loc' || key === 'range')
|
|
137
|
+
continue;
|
|
138
|
+
const child = node[key];
|
|
139
|
+
if (Array.isArray(child)) {
|
|
140
|
+
child.forEach(visit);
|
|
141
|
+
}
|
|
142
|
+
else if (typeof child === 'object') {
|
|
143
|
+
visit(child);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
visit(ast);
|
|
148
|
+
return references;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Parse all Drizzle schema files in a directory and build table-to-domain mapping
|
|
152
|
+
*/
|
|
153
|
+
function buildTableToDomainMapping(schemaDir) {
|
|
154
|
+
const tableToDomain = {};
|
|
155
|
+
try {
|
|
156
|
+
// Find all .ts files in the schema directory
|
|
157
|
+
const schemaFiles = glob_1.glob.sync(path.join(schemaDir, '**/*.ts'));
|
|
158
|
+
for (const filePath of schemaFiles) {
|
|
159
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
160
|
+
// Parse TypeScript file
|
|
161
|
+
const ast = parser.parse(content, {
|
|
162
|
+
loc: true,
|
|
163
|
+
range: true,
|
|
164
|
+
jsx: false,
|
|
165
|
+
});
|
|
166
|
+
// Extract domain name from filename (e.g., auth.schema.ts -> auth)
|
|
167
|
+
const fileName = path.basename(filePath);
|
|
168
|
+
const match = fileName.match(/^(.+?)(?:\.schema)?\.ts$/);
|
|
169
|
+
const domainName = match ? match[1] : path.basename(filePath, '.ts');
|
|
170
|
+
// Extract tables from this schema file
|
|
171
|
+
const tables = extractTablesFromAst(ast);
|
|
172
|
+
for (const table of tables) {
|
|
173
|
+
tableToDomain[table] = domainName;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
// If we can't parse schemas, return empty mapping
|
|
179
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
180
|
+
console.warn(`Warning: Could not parse Drizzle schema files in ${schemaDir}:`, errorMessage);
|
|
181
|
+
}
|
|
182
|
+
return tableToDomain;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Check if a table reference in a schema file references a table from another file
|
|
186
|
+
*/
|
|
187
|
+
function checkCrossSchemaTableReference(filePath, schemaDir) {
|
|
188
|
+
const violations = [];
|
|
189
|
+
try {
|
|
190
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
191
|
+
const ast = parser.parse(content, {
|
|
192
|
+
loc: true,
|
|
193
|
+
range: true,
|
|
194
|
+
jsx: false,
|
|
195
|
+
});
|
|
196
|
+
// Get tables defined in this file
|
|
197
|
+
const localTables = new Set(extractTablesFromAst(ast));
|
|
198
|
+
// Get all table references (from relations, foreign keys, etc.)
|
|
199
|
+
const referencedTables = extractTableReferencesFromRelations(ast);
|
|
200
|
+
// Build mapping of all tables in schema directory
|
|
201
|
+
const allTables = buildTableToDomainMapping(schemaDir);
|
|
202
|
+
// Check each reference
|
|
203
|
+
for (const refTable of referencedTables) {
|
|
204
|
+
// Skip if it's defined locally
|
|
205
|
+
if (localTables.has(refTable)) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
// Check if it exists in another schema file
|
|
209
|
+
if (allTables[refTable]) {
|
|
210
|
+
// Find the location of this reference
|
|
211
|
+
const location = findTableReferenceLocation(ast, refTable);
|
|
212
|
+
violations.push({
|
|
213
|
+
table: refTable,
|
|
214
|
+
line: location.line,
|
|
215
|
+
column: location.column,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
222
|
+
console.warn(`Warning: Could not check cross-schema references in ${filePath}:`, errorMessage);
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
isValid: violations.length === 0,
|
|
226
|
+
violations,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Find the location of a table reference in the AST
|
|
231
|
+
*/
|
|
232
|
+
function findTableReferenceLocation(ast, tableName) {
|
|
233
|
+
let location = { line: 1, column: 0 };
|
|
234
|
+
function visit(node) {
|
|
235
|
+
if (!node)
|
|
236
|
+
return false;
|
|
237
|
+
// Look for identifier matching the table name
|
|
238
|
+
if (node.type === utils_1.AST_NODE_TYPES.Identifier && node.name === tableName && node.loc) {
|
|
239
|
+
location = { line: node.loc.start.line, column: node.loc.start.column };
|
|
240
|
+
return true; // Found it
|
|
241
|
+
}
|
|
242
|
+
// Recursively visit child nodes
|
|
243
|
+
for (const key in node) {
|
|
244
|
+
if (key === 'parent' || key === 'loc' || key === 'range')
|
|
245
|
+
continue;
|
|
246
|
+
const child = node[key];
|
|
247
|
+
if (Array.isArray(child)) {
|
|
248
|
+
for (const item of child) {
|
|
249
|
+
if (visit(item))
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else if (typeof child === 'object') {
|
|
254
|
+
if (visit(child))
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
visit(ast);
|
|
261
|
+
return location;
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=drizzle-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drizzle-parser.js","sourceRoot":"","sources":["../../src/utils/drizzle-parser.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synapsestudios/eslint-plugin-data-boundaries",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "ESLint plugin to enforce data boundary policies in modular monoliths",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -37,6 +37,8 @@
|
|
|
37
37
|
"eslintplugin",
|
|
38
38
|
"eslint-plugin",
|
|
39
39
|
"prisma",
|
|
40
|
+
"drizzle",
|
|
41
|
+
"drizzle-orm",
|
|
40
42
|
"data-boundaries",
|
|
41
43
|
"modular-monolith",
|
|
42
44
|
"domain-driven-design"
|