@synapsestudios/eslint-plugin-data-boundaries 1.2.1 → 1.4.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 CHANGED
@@ -1,15 +1,24 @@
1
1
  # @synapsestudios/eslint-plugin-data-boundaries
2
2
 
3
- ESLint plugin to enforce data boundary policies in modular monoliths using Prisma ORM.
3
+ ESLint plugin to enforce data boundary policies in modular monoliths using Prisma ORM and slonik.
4
+
5
+ ## Rules
6
+
7
+ | Rule | Description | Scope |
8
+ |------|-------------|-------|
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
+ | [`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-slonik-access`](#no-cross-schema-slonik-access) | Prevents modules from accessing database tables outside their schema boundaries via slonik | TypeScript/JavaScript |
4
12
 
5
13
  ## Overview
6
14
 
7
- When building modular monoliths, maintaining clear boundaries between domains is crucial for long-term maintainability. ORMs like Prisma make it easy to accidentally create tight coupling at the data layer by allowing modules to access models that belong to other domains.
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.
8
16
 
9
- This ESLint plugin provides two complementary rules to prevent such violations:
17
+ This ESLint plugin provides three complementary rules to prevent such violations:
10
18
 
11
19
  1. **Schema-level enforcement**: Prevents Prisma schema files from referencing models defined in other schema files
12
- 2. **Application-level enforcement**: Prevents TypeScript code from accessing Prisma models outside their domain boundaries
20
+ 2. **Application-level enforcement**: Prevents TypeScript code from accessing Prisma models outside their domain boundaries
21
+ 3. **SQL-level enforcement**: Prevents slonik SQL queries from accessing tables outside the module's schema
13
22
 
14
23
  ## Installation
15
24
 
@@ -82,6 +91,70 @@ class AuthService {
82
91
  }
83
92
  ```
84
93
 
94
+ ### `no-cross-schema-slonik-access`
95
+
96
+ 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.
97
+
98
+ **Examples of violations:**
99
+
100
+ ```typescript
101
+ // In /modules/auth/service.ts
102
+ import { sql } from 'slonik';
103
+
104
+ class AuthService {
105
+ async getUser(id: string) {
106
+ // ❌ Error: Module 'auth' must use fully qualified table names. Use 'auth.users' instead of 'users'.
107
+ return await this.pool.query(sql`
108
+ SELECT * FROM users WHERE id = ${id}
109
+ `);
110
+ }
111
+
112
+ async getUserOrganizations(userId: string) {
113
+ // ❌ Error: Module 'auth' cannot access table 'memberships' in schema 'organization'.
114
+ return await this.pool.query(sql`
115
+ SELECT * FROM organization.memberships WHERE user_id = ${userId}
116
+ `);
117
+ }
118
+ }
119
+ ```
120
+
121
+ **Valid usage:**
122
+
123
+ ```typescript
124
+ // In /modules/auth/service.ts
125
+ import { sql } from 'slonik';
126
+
127
+ class AuthService {
128
+ async getUser(id: string) {
129
+ // ✅ Valid: Fully qualified table name within module's schema
130
+ return await this.pool.query(sql`
131
+ SELECT * FROM auth.users WHERE id = ${id}
132
+ `);
133
+ }
134
+
135
+ async getUserSessions(userId: string) {
136
+ // ✅ Valid: Both tables are explicitly qualified with auth schema
137
+ return await this.pool.query(sql`
138
+ SELECT s.* FROM auth.sessions s
139
+ JOIN auth.users u ON s.user_id = u.id
140
+ WHERE u.id = ${userId}
141
+ `);
142
+ }
143
+ }
144
+ ```
145
+
146
+ **Configuration:**
147
+
148
+ The rule supports the same `modulePath` configuration as other rules:
149
+
150
+ ```javascript
151
+ {
152
+ '@synapsestudios/data-boundaries/no-cross-schema-slonik-access': ['error', {
153
+ modulePath: '/modules/' // Default - change to '/src/' for NestJS projects
154
+ }]
155
+ }
156
+ ```
157
+
85
158
  ## Configuration
86
159
 
87
160
  ### Basic Setup (Legacy Config)
@@ -115,6 +188,9 @@ module.exports = {
115
188
  '@synapsestudios/data-boundaries/no-cross-domain-prisma-access': ['error', {
116
189
  schemaDir: 'prisma/schema',
117
190
  modulePath: '/modules/' // Default - change to '/src/' for NestJS projects
191
+ }],
192
+ '@synapsestudios/data-boundaries/no-cross-schema-slonik-access': ['error', {
193
+ modulePath: '/modules/' // Default - change to '/src/' for NestJS projects
118
194
  }]
119
195
  }
120
196
  }
@@ -167,6 +243,12 @@ export default [
167
243
  modulePath: '/src/' // Use '/src/' for NestJS, '/modules/' for other structures
168
244
  }
169
245
  ],
246
+ '@synapsestudios/data-boundaries/no-cross-schema-slonik-access': [
247
+ 'error',
248
+ {
249
+ modulePath: '/src/' // Use '/src/' for NestJS, '/modules/' for other structures
250
+ }
251
+ ],
170
252
  },
171
253
  },
172
254
  ];
@@ -207,6 +289,18 @@ module.exports = {
207
289
  }
208
290
  ```
209
291
 
292
+ #### `no-cross-schema-slonik-access`
293
+
294
+ - **`modulePath`** (string): Path pattern to match module directories. Default: `'/modules/'`. Use `'/src/'` for NestJS projects or other domain-based structures.
295
+
296
+ ```javascript
297
+ {
298
+ '@synapsestudios/data-boundaries/no-cross-schema-slonik-access': ['error', {
299
+ modulePath: '/src/' // For NestJS-style projects
300
+ }]
301
+ }
302
+ ```
303
+
210
304
  ## Directory Structure
211
305
 
212
306
  This plugin supports multiple project structures:
@@ -285,12 +379,22 @@ Model field 'user' references 'User' which is not defined in this file.
285
379
  Cross-file model references are not allowed.
286
380
  ```
287
381
 
382
+ ```
383
+ Module 'auth' must use fully qualified table names. Use 'auth.users' instead of 'users'.
384
+ ```
385
+
386
+ ```
387
+ Module 'auth' cannot access table 'memberships' in schema 'organization'.
388
+ SQL queries should only access tables within the module's own schema ('auth').
389
+ ```
390
+
288
391
  ## Migration Strategy
289
392
 
290
393
  1. **Start with schema boundaries**: Add the `no-cross-file-model-references` rule to prevent new violations in schema files
291
394
  2. **Split your schema**: Gradually move models to domain-specific schema files
292
395
  3. **Add application boundaries**: Enable `no-cross-domain-prisma-access` to prevent cross-domain access in application code
293
- 4. **Refactor violations**: Create shared services or move logic to appropriate domains
396
+ 4. **Enforce SQL boundaries**: Enable `no-cross-schema-slonik-access` if using slonik to prevent cross-schema SQL queries
397
+ 5. **Refactor violations**: Create shared services or move logic to appropriate domains
294
398
 
295
399
  ## Troubleshooting
296
400
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  declare const noCrossFileModelReferences: any;
2
2
  declare const noCrossDomainPrismaAccess: any;
3
+ declare const noCrossSchemaSlonikAccess: any;
3
4
  declare const prismaParser: any;
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -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,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,YAAY,KAAqC,CAAC"}
package/dist/index.js CHANGED
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  const noCrossFileModelReferences = require('./rules/no-cross-file-model-references');
3
3
  const noCrossDomainPrismaAccess = require('./rules/no-cross-domain-prisma-access');
4
+ const noCrossSchemaSlonikAccess = require('./rules/no-cross-schema-slonik-access');
4
5
  const prismaParser = require('./parsers/prisma-parser');
5
6
  module.exports = {
6
7
  rules: {
7
8
  'no-cross-file-model-references': noCrossFileModelReferences,
8
9
  'no-cross-domain-prisma-access': noCrossDomainPrismaAccess,
10
+ 'no-cross-schema-slonik-access': noCrossSchemaSlonikAccess,
9
11
  },
10
12
  parsers: {
11
13
  prisma: prismaParser,
@@ -25,6 +27,7 @@ module.exports = {
25
27
  files: ['**/*.ts', '**/*.tsx'],
26
28
  rules: {
27
29
  '@synapsestudios/data-boundaries/no-cross-domain-prisma-access': 'error',
30
+ '@synapsestudios/data-boundaries/no-cross-schema-slonik-access': 'error',
28
31
  },
29
32
  },
30
33
  ],
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,YAAY,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;AAExD,MAAM,CAAC,OAAO,GAAG;IACf,KAAK,EAAE;QACL,gCAAgC,EAAE,0BAA0B;QAC5D,+BAA+B,EAAE,yBAAyB;KAC3D;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;qBACzE;iBACF;aACF;SACF;KACF;CACF,CAAC"}
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;KAC3D;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"}
@@ -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;AAqB9B,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAuGhB,CAAC;AAEF,SAAS,IAAI,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
- * This is a simplified implementation that finds the first occurrence
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
- if (lines[i].trim().startsWith(fieldName)) {
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 definedModels = new Set();
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
- definedModels.add(item.name);
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
- // Check if this field references another model
58
- const referencedModelName = property.fieldType;
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(referencedModelName)) {
103
+ if (primitiveTypes.includes(referencedTypeName)) {
70
104
  return;
71
105
  }
72
- // Skip array types (remove [] suffix)
73
- const cleanModelName = referencedModelName.replace(/\[\]$/, '');
74
- // Check if the referenced model is defined in this file
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: cleanModelName,
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":"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"}
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
+ modulePath: string;
4
+ }
5
+ declare const rule: ESLintUtils.RuleModule<"crossSchemaAccess" | "unqualifiedTable", [RuleOptions], unknown, ESLintUtils.RuleListener>;
6
+ export = rule;
7
+ //# sourceMappingURL=no-cross-schema-slonik-access.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-cross-schema-slonik-access.d.ts","sourceRoot":"","sources":["../../src/rules/no-cross-schema-slonik-access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAY,MAAM,0BAA0B,CAAC;AAGjE,UAAU,WAAW;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAmID,QAAA,MAAM,IAAI,oHA0FR,CAAC;AAEH,SAAS,IAAI,CAAC"}
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ const utils_1 = require("@typescript-eslint/utils");
3
+ const schema_parser_1 = require("../utils/schema-parser");
4
+ /**
5
+ * Extract table names from SQL string using simple regex patterns
6
+ */
7
+ function extractTableNames(sql) {
8
+ const tableNames = [];
9
+ // Remove comments and normalize whitespace
10
+ const cleanSql = sql
11
+ .replace(/--.*$/gm, '') // Remove line comments
12
+ .replace(/\/\*[\s\S]*?\*\//g, '') // Remove block comments
13
+ .replace(/\s+/g, ' ') // Normalize whitespace
14
+ .trim();
15
+ // Patterns to match table references
16
+ const patterns = [
17
+ // FROM clause: FROM schema.table, FROM table
18
+ /FROM\s+(?:(\w+)\.)?(\w+)/gi,
19
+ // JOIN clause: JOIN schema.table, JOIN table
20
+ /JOIN\s+(?:(\w+)\.)?(\w+)/gi,
21
+ // INSERT INTO: INSERT INTO schema.table, INSERT INTO table
22
+ /INSERT\s+INTO\s+(?:(\w+)\.)?(\w+)/gi,
23
+ // UPDATE: UPDATE schema.table, UPDATE table
24
+ /UPDATE\s+(?:(\w+)\.)?(\w+)/gi,
25
+ // DELETE FROM: DELETE FROM schema.table, DELETE FROM table
26
+ /DELETE\s+FROM\s+(?:(\w+)\.)?(\w+)/gi,
27
+ ];
28
+ patterns.forEach((pattern) => {
29
+ let match;
30
+ while ((match = pattern.exec(cleanSql)) !== null) {
31
+ const schema = match[1];
32
+ const table = match[2];
33
+ if (table && table.toLowerCase() !== 'select') {
34
+ // If schema is specified, use schema.table format
35
+ if (schema) {
36
+ tableNames.push(`${schema}.${table}`);
37
+ }
38
+ else {
39
+ // If no schema specified, just use table name
40
+ tableNames.push(table);
41
+ }
42
+ }
43
+ }
44
+ });
45
+ return [...new Set(tableNames)]; // Remove duplicates
46
+ }
47
+ /**
48
+ * Check if a table reference violates schema boundaries
49
+ */
50
+ function isTableAccessViolation(tableName, currentModule) {
51
+ // Check if table name includes schema (schema.table format)
52
+ if (tableName.includes('.')) {
53
+ const [schema, table] = tableName.split('.', 2);
54
+ // If schema is explicitly specified and doesn't match module, it's a violation
55
+ if (schema && schema !== currentModule) {
56
+ return {
57
+ isViolation: true,
58
+ schema,
59
+ table,
60
+ reason: 'crossSchema',
61
+ };
62
+ }
63
+ }
64
+ else {
65
+ // For unqualified table names, this is now a violation - require explicit schema
66
+ return {
67
+ isViolation: true,
68
+ table: tableName,
69
+ reason: 'unqualified',
70
+ };
71
+ }
72
+ return { isViolation: false };
73
+ }
74
+ /**
75
+ * Check if node is a slonik sql tagged template literal
76
+ */
77
+ function isSlonikSqlCall(node) {
78
+ if (node.tag.type === 'Identifier' && node.tag.name === 'sql') {
79
+ return true;
80
+ }
81
+ // Handle member expressions like db.sql or this.sql
82
+ if (node.tag.type === 'MemberExpression' &&
83
+ node.tag.property.type === 'Identifier' &&
84
+ node.tag.property.name === 'sql') {
85
+ return true;
86
+ }
87
+ return false;
88
+ }
89
+ /**
90
+ * Extract SQL string from template literal
91
+ */
92
+ function extractSqlFromTemplate(node) {
93
+ const quasi = node.quasi;
94
+ // Combine all template parts into a single string
95
+ let sql = '';
96
+ quasi.quasis.forEach((element, index) => {
97
+ sql += element.value.raw;
98
+ // Add placeholder for expressions (${...})
99
+ if (index < quasi.expressions.length) {
100
+ sql += ' ? '; // Use placeholder for parameter
101
+ }
102
+ });
103
+ return sql;
104
+ }
105
+ /**
106
+ * ESLint rule to prevent slonik SQL queries from accessing tables
107
+ * outside the current module's schema
108
+ */
109
+ const createRule = utils_1.ESLintUtils.RuleCreator((name) => `https://github.com/synapsestudios/eslint-plugin-data-boundaries#${name}`);
110
+ const rule = createRule({
111
+ name: 'no-cross-schema-slonik-access',
112
+ meta: {
113
+ type: 'problem',
114
+ docs: {
115
+ description: 'Disallow slonik SQL queries from accessing tables in schemas outside the current module',
116
+ },
117
+ fixable: undefined,
118
+ schema: [
119
+ {
120
+ type: 'object',
121
+ properties: {
122
+ modulePath: {
123
+ type: 'string',
124
+ description: 'Path pattern to match module directories (e.g., "/modules/", "/src/")',
125
+ },
126
+ },
127
+ additionalProperties: false,
128
+ },
129
+ ],
130
+ messages: {
131
+ crossSchemaAccess: "Module '{{currentModule}}' cannot access table '{{table}}' in schema '{{schema}}'. SQL queries should only access tables within the module's own schema ('{{currentModule}}').",
132
+ unqualifiedTable: "Module '{{currentModule}}' must use fully qualified table names. Use '{{currentModule}}.{{table}}' instead of '{{table}}'.",
133
+ },
134
+ },
135
+ defaultOptions: [
136
+ {
137
+ modulePath: '/modules/',
138
+ },
139
+ ],
140
+ create(context, [options]) {
141
+ const filename = context.getFilename();
142
+ // Only process TypeScript files in modules
143
+ if (!filename.includes(options.modulePath) || !filename.match(/\.(ts|tsx)$/)) {
144
+ return {};
145
+ }
146
+ // Extract current module from file path
147
+ const currentModule = (0, schema_parser_1.extractModuleFromPath)(filename, options.modulePath);
148
+ if (!currentModule) {
149
+ return {};
150
+ }
151
+ return {
152
+ // Detect slonik sql tagged template literals
153
+ TaggedTemplateExpression(node) {
154
+ if (!isSlonikSqlCall(node)) {
155
+ return;
156
+ }
157
+ // Extract SQL from template literal
158
+ const sqlString = extractSqlFromTemplate(node);
159
+ // Extract table names from SQL
160
+ const tableNames = extractTableNames(sqlString);
161
+ // Check each table for schema boundary violations
162
+ tableNames.forEach((tableName) => {
163
+ const violation = isTableAccessViolation(tableName, currentModule);
164
+ if (violation.isViolation) {
165
+ if (violation.reason === 'crossSchema' && violation.schema && violation.table) {
166
+ context.report({
167
+ node,
168
+ messageId: 'crossSchemaAccess',
169
+ data: {
170
+ currentModule,
171
+ schema: violation.schema,
172
+ table: violation.table,
173
+ },
174
+ });
175
+ }
176
+ else if (violation.reason === 'unqualified' && violation.table) {
177
+ context.report({
178
+ node,
179
+ messageId: 'unqualifiedTable',
180
+ data: {
181
+ currentModule,
182
+ table: violation.table,
183
+ },
184
+ });
185
+ }
186
+ }
187
+ });
188
+ },
189
+ };
190
+ },
191
+ });
192
+ module.exports = rule;
193
+ //# sourceMappingURL=no-cross-schema-slonik-access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-cross-schema-slonik-access.js","sourceRoot":"","sources":["../../src/rules/no-cross-schema-slonik-access.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.2.1",
3
+ "version": "1.4.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",
@@ -60,6 +60,7 @@
60
60
  "eslint": "^8.57.1",
61
61
  "jest": "^29.7.0",
62
62
  "prettier": "^3.6.2",
63
+ "slonik": "^47.3.2",
63
64
  "ts-jest": "^29.4.0",
64
65
  "typescript": "^5.8.3"
65
66
  },