@yasainet/eslint 0.0.50 → 0.0.52

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
@@ -25,9 +25,9 @@ Each entry point enforces a feature-based architecture with the following conven
25
25
  ```text
26
26
  {featureRoot}/
27
27
  ├── {feature}/
28
- │ ├── actions/ # *.action.ts — entry points (handleXxx)
28
+ │ ├── interactors/ # *.interactor.ts — entry points
29
29
  │ ├── services/ # *.service.ts — business logic
30
- │ ├── repositories/ # *.repo.ts — data access
30
+ │ ├── queries/ # *.query.ts — data access
31
31
  │ ├── types/ # *.type.ts
32
32
  │ ├── schemas/ # *.schema.ts
33
33
  │ ├── utils/ # *.util.ts
@@ -35,7 +35,7 @@ Each entry point enforces a feature-based architecture with the following conven
35
35
  ├── shared/ # Cross-feature shared modules
36
36
  ├── ...
37
37
  {libRoot}/ # *.lib.ts — library wrappers (e.g., supabase.lib.ts)
38
- {utilsRoot}/ # *.util.ts — top-level utilities
38
+ {utilsRoot}/ # *.util.ts — top-level utilities (e.g., font.util.ts)
39
39
  ```
40
40
 
41
41
  ## Setup
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yasainet/eslint",
3
- "version": "0.0.50",
3
+ "version": "0.0.52",
4
4
  "description": "ESLint",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,46 +1,46 @@
1
1
  const LAYER_PATTERNS = {
2
- repositories: [
2
+ queries: [
3
3
  {
4
4
  group: ["*/services/*", "*/services"],
5
- message: "repositories cannot import services (layer violation)",
5
+ message: "queries cannot import services (layer violation)",
6
6
  },
7
7
  {
8
- group: ["*/actions/*", "*/actions"],
9
- message: "repositories cannot import actions (layer violation)",
8
+ group: ["*/interactors/*", "*/interactors"],
9
+ message: "queries cannot import interactors (layer violation)",
10
10
  },
11
11
  {
12
12
  group: ["*/hooks/*", "*/hooks"],
13
- message: "repositories cannot import hooks (layer violation)",
13
+ message: "queries cannot import hooks (layer violation)",
14
14
  },
15
15
  ],
16
16
  services: [
17
17
  {
18
- group: ["*/actions/*", "*/actions"],
19
- message: "services cannot import actions (layer violation)",
18
+ group: ["*/interactors/*", "*/interactors"],
19
+ message: "services cannot import interactors (layer violation)",
20
20
  },
21
21
  {
22
22
  group: ["*/hooks/*", "*/hooks"],
23
23
  message: "services cannot import hooks (layer violation)",
24
24
  },
25
25
  ],
26
- actions: [
26
+ interactors: [
27
27
  {
28
- group: ["*/repositories/*", "*/repositories"],
29
- message: "actions cannot import repositories (layer violation)",
28
+ group: ["*/queries/*", "*/queries"],
29
+ message: "interactors cannot import queries (layer violation)",
30
30
  },
31
31
  {
32
32
  group: ["*/hooks/*", "*/hooks"],
33
- message: "actions cannot import hooks (layer violation)",
33
+ message: "interactors cannot import hooks (layer violation)",
34
34
  },
35
35
  ],
36
36
  };
37
37
 
38
38
  const LATERAL_PATTERNS = {
39
- repositories: [
39
+ queries: [
40
40
  {
41
- group: ["@/features/*/repositories/*", "@/features/*/repositories"],
41
+ group: ["@/features/*/queries/*", "@/features/*/queries"],
42
42
  message:
43
- "repositories cannot import other feature's repositories (lateral violation)",
43
+ "queries cannot import other feature's queries (lateral violation)",
44
44
  },
45
45
  ],
46
46
  services: [
@@ -50,11 +50,11 @@ const LATERAL_PATTERNS = {
50
50
  "services cannot import other feature's services (lateral violation)",
51
51
  },
52
52
  ],
53
- actions: [
53
+ interactors: [
54
54
  {
55
- group: ["@/features/*/actions/*", "@/features/*/actions"],
55
+ group: ["@/features/*/interactors/*", "@/features/*/interactors"],
56
56
  message:
57
- "actions cannot import other feature's actions (lateral violation)",
57
+ "interactors cannot import other feature's interactors (lateral violation)",
58
58
  },
59
59
  ],
60
60
  };
@@ -64,21 +64,21 @@ const CARDINALITY_PATTERNS = {
64
64
  {
65
65
  group: ["**/services/client.service*", "**/services/admin.service*"],
66
66
  message:
67
- "server.action can only import server.service (cardinality violation)",
67
+ "server.interactor can only import server.service (cardinality violation)",
68
68
  },
69
69
  ],
70
70
  client: [
71
71
  {
72
72
  group: ["**/services/server.service*", "**/services/admin.service*"],
73
73
  message:
74
- "client.action can only import client.service (cardinality violation)",
74
+ "client.interactor can only import client.service (cardinality violation)",
75
75
  },
76
76
  ],
77
77
  admin: [
78
78
  {
79
79
  group: ["**/services/server.service*", "**/services/client.service*"],
80
80
  message:
81
- "admin.action can only import admin.service (cardinality violation)",
81
+ "admin.interactor can only import admin.service (cardinality violation)",
82
82
  },
83
83
  ],
84
84
  };
@@ -90,7 +90,7 @@ function prefixLibPatterns(prefix, mapping) {
90
90
  .filter((p) => p !== prefix)
91
91
  .map((p) => ({
92
92
  group: [`**/lib/${mapping[p]}`, `**/lib/${mapping[p]}/*`],
93
- message: `${prefix}.repo.ts can only import from lib/${allowedLib}. Use the correct repository file for this lib.`,
93
+ message: `${prefix}.query.ts can only import from lib/${allowedLib}. Use the correct query file for this lib.`,
94
94
  }));
95
95
  }
96
96
 
@@ -98,7 +98,7 @@ const LIB_BOUNDARY_PATTERNS = [
98
98
  {
99
99
  group: ["@/lib/*", "@/lib/**"],
100
100
  message:
101
- "lib/* can only be imported from repositories (lib-boundary violation)",
101
+ "lib/* can only be imported from queries (lib-boundary violation)",
102
102
  },
103
103
  ];
104
104
 
@@ -113,40 +113,40 @@ const MAPPING_PATTERNS = [
113
113
 
114
114
  const PAGE_BOUNDARY_PATTERNS = [
115
115
  {
116
- group: ["*/repositories/*", "*/repositories"],
116
+ group: ["*/queries/*", "*/queries"],
117
117
  message:
118
- "page.tsx can only import actions, not repositories (page-boundary violation)",
118
+ "page.tsx can only import interactors, not queries (page-boundary violation)",
119
119
  },
120
120
  {
121
121
  group: ["*/services/*", "*/services"],
122
122
  message:
123
- "page.tsx can only import actions, not services (page-boundary violation)",
123
+ "page.tsx can only import interactors, not services (page-boundary violation)",
124
124
  },
125
125
  ];
126
126
 
127
127
  const HOOKS_BOUNDARY_PATTERNS = [
128
128
  {
129
- group: ["*/repositories/*", "*/repositories"],
129
+ group: ["*/queries/*", "*/queries"],
130
130
  message:
131
- "hooks can only import actions, not repositories (hooks-boundary violation)",
131
+ "hooks can only import interactors, not queries (hooks-boundary violation)",
132
132
  },
133
133
  {
134
134
  group: ["*/services/*", "*/services"],
135
135
  message:
136
- "hooks can only import actions, not services (hooks-boundary violation)",
136
+ "hooks can only import interactors, not services (hooks-boundary violation)",
137
137
  },
138
138
  ];
139
139
 
140
140
  const COMPONENTS_BOUNDARY_PATTERNS = [
141
141
  {
142
- group: ["*/repositories/*", "*/repositories"],
142
+ group: ["*/queries/*", "*/queries"],
143
143
  message:
144
- "components can only import actions or hooks, not repositories (components-boundary violation)",
144
+ "components can only import interactors or hooks, not queries (components-boundary violation)",
145
145
  },
146
146
  {
147
147
  group: ["*/services/*", "*/services"],
148
148
  message:
149
- "components can only import actions or hooks, not services (components-boundary violation)",
149
+ "components can only import interactors or hooks, not services (components-boundary violation)",
150
150
  },
151
151
  ];
152
152
 
@@ -162,7 +162,7 @@ function makeConfig(name, files, ...patternArrays) {
162
162
  };
163
163
  }
164
164
 
165
- /** Next.js-only: restrict page.tsx to only import actions. */
165
+ /** Next.js-only: restrict page.tsx to only import interactors. */
166
166
  export const pageBoundaryConfigs = [
167
167
  {
168
168
  name: "imports/page-boundary",
@@ -173,7 +173,7 @@ export const pageBoundaryConfigs = [
173
173
  },
174
174
  ];
175
175
 
176
- /** Next.js-only: restrict hooks to only import actions (not repositories or services). */
176
+ /** Next.js-only: restrict hooks to only import interactors (not queries or services). */
177
177
  export const hooksBoundaryConfigs = [
178
178
  {
179
179
  name: "imports/hooks-boundary",
@@ -184,7 +184,7 @@ export const hooksBoundaryConfigs = [
184
184
  },
185
185
  ];
186
186
 
187
- /** Next.js-only: restrict components to only import actions or hooks (not repositories or services). */
187
+ /** Next.js-only: restrict components to only import interactors or hooks (not queries or services). */
188
188
  export const componentsBoundaryConfigs = [
189
189
  {
190
190
  name: "imports/components-boundary",
@@ -253,10 +253,10 @@ export function createImportsConfigs(
253
253
 
254
254
  configs.push(
255
255
  makeConfig(
256
- "repositories",
257
- [`${featureRoot}/**/repositories/*.ts`],
258
- LAYER_PATTERNS.repositories,
259
- LATERAL_PATTERNS.repositories,
256
+ "queries",
257
+ [`${featureRoot}/**/queries/*.ts`],
258
+ LAYER_PATTERNS.queries,
259
+ LATERAL_PATTERNS.queries,
260
260
  MAPPING_PATTERNS,
261
261
  ),
262
262
  );
@@ -266,10 +266,10 @@ export function createImportsConfigs(
266
266
  if (patterns.length === 0) continue;
267
267
  configs.push(
268
268
  makeConfig(
269
- `repositories/${prefix}`,
270
- [`${featureRoot}/**/repositories/${prefix}.repo.ts`],
271
- LAYER_PATTERNS.repositories,
272
- LATERAL_PATTERNS.repositories,
269
+ `queries/${prefix}`,
270
+ [`${featureRoot}/**/queries/${prefix}.query.ts`],
271
+ LAYER_PATTERNS.queries,
272
+ LATERAL_PATTERNS.queries,
273
273
  patterns,
274
274
  MAPPING_PATTERNS,
275
275
  ),
@@ -288,10 +288,10 @@ export function createImportsConfigs(
288
288
 
289
289
  configs.push(
290
290
  makeConfig(
291
- "actions",
292
- [`${featureRoot}/**/actions/*.ts`],
293
- LAYER_PATTERNS.actions,
294
- LATERAL_PATTERNS.actions,
291
+ "interactors",
292
+ [`${featureRoot}/**/interactors/*.ts`],
293
+ LAYER_PATTERNS.interactors,
294
+ LATERAL_PATTERNS.interactors,
295
295
  LIB_BOUNDARY_PATTERNS,
296
296
  MAPPING_PATTERNS,
297
297
  ),
@@ -313,8 +313,8 @@ export function createImportsConfigs(
313
313
  files: [`${featureRoot}/**/*.ts`],
314
314
  ignores: [
315
315
  `${featureRoot}/**/services/*.ts`,
316
- `${featureRoot}/**/repositories/*.ts`,
317
- `${featureRoot}/**/actions/*.ts`,
316
+ `${featureRoot}/**/queries/*.ts`,
317
+ `${featureRoot}/**/interactors/*.ts`,
318
318
  `${featureRoot}/**/utils/*.ts`,
319
319
  `${featureRoot}/**/types/*.ts`,
320
320
  ],
@@ -338,10 +338,10 @@ export function createImportsConfigs(
338
338
  for (const prefix of ["server", "client", "admin"]) {
339
339
  configs.push(
340
340
  makeConfig(
341
- `actions/${prefix}`,
342
- [`${featureRoot}/**/actions/${prefix}.action.ts`],
343
- LAYER_PATTERNS.actions,
344
- LATERAL_PATTERNS.actions,
341
+ `interactors/${prefix}`,
342
+ [`${featureRoot}/**/interactors/${prefix}.interactor.ts`],
343
+ LAYER_PATTERNS.interactors,
344
+ LATERAL_PATTERNS.interactors,
345
345
  CARDINALITY_PATTERNS[prefix],
346
346
  LIB_BOUNDARY_PATTERNS,
347
347
  MAPPING_PATTERNS,
@@ -8,7 +8,7 @@ export function createJsdocConfigs(featureRoot) {
8
8
  {
9
9
  name: "jsdoc",
10
10
  files: [
11
- ...featuresGlob(featureRoot, "**/repositories/*.ts"),
11
+ ...featuresGlob(featureRoot, "**/queries/*.ts"),
12
12
  ...featuresGlob(featureRoot, "**/services*/*.ts"),
13
13
  ...featuresGlob(featureRoot, "**/utils*/*.ts"),
14
14
  ],
@@ -4,14 +4,14 @@ import { localPlugin } from "./local-plugins/index.mjs";
4
4
  export function createLayersConfigs(featureRoot) {
5
5
  const loggerSelector = "CallExpression[callee.object.name='logger']";
6
6
  const loggerMessage =
7
- "logger is not allowed outside actions. Logging belongs in actions.";
7
+ "logger is not allowed outside interactors. Logging belongs in interactors.";
8
8
 
9
9
  return [
10
- // Logger/console: all features except actions
10
+ // Logger/console: all features except interactors
11
11
  {
12
12
  name: "layers/logger",
13
13
  files: [`${featureRoot}/**/*.ts`],
14
- ignores: [`${featureRoot}/**/actions/*.ts`],
14
+ ignores: [`${featureRoot}/**/interactors/*.ts`],
15
15
  rules: {
16
16
  "no-console": "error",
17
17
  "no-restricted-syntax": [
@@ -20,64 +20,64 @@ export function createLayersConfigs(featureRoot) {
20
20
  ],
21
21
  },
22
22
  },
23
- // Repositories: try-catch + if + loops + logger
23
+ // Queries: try-catch + if + loops + logger
24
24
  {
25
- name: "layers/repositories",
26
- files: [`${featureRoot}/**/repositories/*.ts`],
25
+ name: "layers/queries",
26
+ files: [`${featureRoot}/**/queries/*.ts`],
27
27
  rules: {
28
28
  "no-restricted-syntax": [
29
29
  "error",
30
30
  {
31
31
  selector: "TryStatement",
32
32
  message:
33
- "try-catch is not allowed in repositories. Error handling belongs in actions.",
33
+ "try-catch is not allowed in queries. Error handling belongs in interactors.",
34
34
  },
35
35
  {
36
36
  selector: "IfStatement",
37
37
  message:
38
- "if statements are not allowed in repositories. Conditional logic belongs in services.",
38
+ "if statements are not allowed in queries. Conditional logic belongs in services.",
39
39
  },
40
40
  {
41
41
  selector: "ForStatement",
42
42
  message:
43
- "Loops are not allowed in repositories. Repositories should be thin CRUD wrappers — iteration belongs in services.",
43
+ "Loops are not allowed in queries. Queries should be thin CRUD wrappers — iteration belongs in services.",
44
44
  },
45
45
  {
46
46
  selector: "ForOfStatement",
47
47
  message:
48
- "Loops are not allowed in repositories. Repositories should be thin CRUD wrappers — iteration belongs in services.",
48
+ "Loops are not allowed in queries. Queries should be thin CRUD wrappers — iteration belongs in services.",
49
49
  },
50
50
  {
51
51
  selector: "ForInStatement",
52
52
  message:
53
- "Loops are not allowed in repositories. Repositories should be thin CRUD wrappers — iteration belongs in services.",
53
+ "Loops are not allowed in queries. Queries should be thin CRUD wrappers — iteration belongs in services.",
54
54
  },
55
55
  {
56
56
  selector: "WhileStatement",
57
57
  message:
58
- "Loops are not allowed in repositories. Repositories should be thin CRUD wrappers — iteration belongs in services.",
58
+ "Loops are not allowed in queries. Queries should be thin CRUD wrappers — iteration belongs in services.",
59
59
  },
60
60
  {
61
61
  selector: "DoWhileStatement",
62
62
  message:
63
- "Loops are not allowed in repositories. Repositories should be thin CRUD wrappers — iteration belongs in services.",
63
+ "Loops are not allowed in queries. Queries should be thin CRUD wrappers — iteration belongs in services.",
64
64
  },
65
65
  {
66
66
  selector: "ThrowStatement",
67
67
  message:
68
- "throw is not allowed in repositories. Repositories must return Supabase's { data, error } shape as-is. Error handling belongs in actions.",
68
+ "throw is not allowed in queries. Queries must return Supabase's { data, error } shape as-is. Error handling belongs in interactors.",
69
69
  },
70
70
  { selector: loggerSelector, message: loggerMessage },
71
71
  ],
72
72
  },
73
73
  },
74
- // Boundary type safety: repositories & services must not leak `any`
74
+ // Boundary type safety: queries & services must not leak `any`
75
75
  // into their public API. Uses type-aware inspection of the inferred
76
76
  // return type so unannotated functions are still checked.
77
77
  {
78
78
  name: "layers/no-any-return",
79
79
  files: [
80
- `${featureRoot}/**/repositories/*.ts`,
80
+ `${featureRoot}/**/queries/*.ts`,
81
81
  `${featureRoot}/**/services/*.ts`,
82
82
  ],
83
83
  plugins: { local: localPlugin },
@@ -95,20 +95,20 @@ export function createLayersConfigs(featureRoot) {
95
95
  {
96
96
  selector: "TryStatement",
97
97
  message:
98
- "try-catch is not allowed in services. Error handling belongs in actions.",
98
+ "try-catch is not allowed in services. Error handling belongs in interactors.",
99
99
  },
100
100
  { selector: loggerSelector, message: loggerMessage },
101
101
  {
102
102
  selector:
103
103
  "LogicalExpression[operator='??'][left.type='ChainExpression'][left.expression.property.name='message'][right.type='Literal']",
104
104
  message:
105
- "Dead fallback for error message. If you reached this branch the error is known — return the error directly. Unhandled exceptions belong in actions.",
105
+ "Dead fallback for error message. If you reached this branch the error is known — return the error directly. Unhandled exceptions belong in interactors.",
106
106
  },
107
107
  {
108
108
  selector:
109
109
  "LogicalExpression[operator='??'][left.type='MemberExpression'][left.property.name='error'][right.type='ObjectExpression']",
110
110
  message:
111
- "Dead fallback for nullable error. Check `if (error)` and return the error directly. Unhandled exceptions belong in actions.",
111
+ "Dead fallback for nullable error. Check `if (error)` and return the error directly. Unhandled exceptions belong in interactors.",
112
112
  },
113
113
  ],
114
114
  },
@@ -1,18 +1,18 @@
1
- import { actionHandleServiceRule } from "./action-handle-service.mjs";
2
1
  import { featureNameRule } from "./feature-name.mjs";
3
2
  import { importPathStyleRule } from "./import-path-style.mjs";
4
3
  import { namespaceImportNameRule } from "./namespace-import-name.mjs";
5
4
  import { noAnyReturnRule } from "./no-any-return.mjs";
5
+ import { queriesExportRule } from "./queries-export.mjs";
6
6
  import { schemaNamingRule } from "./schema-naming.mjs";
7
7
 
8
8
  /** Single plugin object to avoid ESLint "Cannot redefine plugin" errors. */
9
9
  export const localPlugin = {
10
10
  rules: {
11
- "action-handle-service": actionHandleServiceRule,
12
11
  "feature-name": featureNameRule,
13
12
  "import-path-style": importPathStyleRule,
14
13
  "namespace-import-name": namespaceImportNameRule,
15
14
  "no-any-return": noAnyReturnRule,
15
+ "queries-export": queriesExportRule,
16
16
  "schema-naming": schemaNamingRule,
17
17
  },
18
18
  };
@@ -7,10 +7,10 @@
7
7
 
8
8
  /** @type {Record<string, string>} */
9
9
  const LAYER_MAP = {
10
- repo: "Repository",
10
+ query: "Query",
11
11
  service: "Service",
12
12
  domain: "Domain",
13
- action: "Action",
13
+ interactor: "Interactor",
14
14
  util: "Util",
15
15
  type: "Type",
16
16
  schema: "Schema",
@@ -4,7 +4,7 @@ import ts from "typescript";
4
4
  * Exported function の return type が any を含んでいる場合に error:
5
5
  *
6
6
  * - typescript-eslint の type checker を使って inferred type まで見る
7
- * - repositories / services の API 境界を any 汚染から守ることで、domain shape が型で保証される
7
+ * - queries / services の API 境界を any 汚染から守ることで、domain shape が型で保証される
8
8
  * - Promise<any>, Promise<{ data: any }>, Array<any> など nested も展開して検査する
9
9
  */
10
10
  export const noAnyReturnRule = {
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Enforce verb allow list for `queries/*.query.ts` exports.
3
+ *
4
+ * The queries layer is the TS-idiomatic translation of Rails 5 actions
5
+ * (index/show -> get, create, update, destroy -> delete). Auth ceremonies
6
+ * (signUp / signIn / signOut) are admitted as industry-standard exceptions.
7
+ */
8
+
9
+ const QUERIES_ALLOW = /^(get|create|update|delete|signUp|signIn|signOut)([A-Z]|$)/;
10
+
11
+ function isFunctionLike(initNode) {
12
+ if (!initNode) return false;
13
+ const t = initNode.type;
14
+ if (t === "ArrowFunctionExpression" || t === "FunctionExpression") return true;
15
+ if (t === "CallExpression") {
16
+ return initNode.arguments.some(
17
+ (arg) =>
18
+ arg.type === "ArrowFunctionExpression" ||
19
+ arg.type === "FunctionExpression",
20
+ );
21
+ }
22
+ return false;
23
+ }
24
+
25
+ function reportIfInvalid(context, idNode) {
26
+ if (!QUERIES_ALLOW.test(idNode.name)) {
27
+ context.report({
28
+ node: idNode,
29
+ messageId: "invalidName",
30
+ data: { name: idNode.name },
31
+ });
32
+ }
33
+ }
34
+
35
+ export const queriesExportRule = {
36
+ meta: {
37
+ type: "problem",
38
+ messages: {
39
+ invalidName:
40
+ "queries export '{{ name }}' must start with one of: get, create, update, delete, signUp, signIn, signOut. (Rails 5 actions translated to TS idiom)",
41
+ },
42
+ schema: [],
43
+ },
44
+ create(context) {
45
+ return {
46
+ ExportNamedDeclaration(node) {
47
+ if (!node.declaration) return;
48
+ const decl = node.declaration;
49
+
50
+ if (decl.type === "FunctionDeclaration" && decl.id) {
51
+ reportIfInvalid(context, decl.id);
52
+ return;
53
+ }
54
+
55
+ if (decl.type === "VariableDeclaration") {
56
+ for (const variator of decl.declarations) {
57
+ if (variator.id.type !== "Identifier") continue;
58
+ if (!isFunctionLike(variator.init)) continue;
59
+ reportIfInvalid(context, variator.id);
60
+ }
61
+ }
62
+ },
63
+ };
64
+ },
65
+ };
@@ -52,10 +52,10 @@ export function createNamingConfigs(featureRoot, prefixLibMapping) {
52
52
  const servicePattern = prefixPattern
53
53
  ? `${prefixPattern}.service`
54
54
  : "*.service";
55
- const repoPattern = prefixPattern ? `${prefixPattern}.repo` : "*.repo";
56
- const actionPattern = prefixPattern
57
- ? `${prefixPattern}.action`
58
- : "*.action";
55
+ const queryPattern = prefixPattern ? `${prefixPattern}.query` : "*.query";
56
+ const interactorPattern = prefixPattern
57
+ ? `${prefixPattern}.interactor`
58
+ : "*.interactor";
59
59
 
60
60
  const configs = [];
61
61
 
@@ -91,17 +91,25 @@ export function createNamingConfigs(featureRoot, prefixLibMapping) {
91
91
  },
92
92
  },
93
93
  {
94
- name: "naming/repositories",
95
- files: featuresGlob(featureRoot, "**/repositories/*.ts"),
96
- ignores: featuresGlob(featureRoot, "shared/repositories/*.ts"),
94
+ name: "naming/queries",
95
+ files: featuresGlob(featureRoot, "**/queries/*.ts"),
96
+ ignores: featuresGlob(featureRoot, "shared/queries/*.ts"),
97
97
  plugins: { "check-file": checkFile },
98
98
  rules: {
99
99
  "check-file/filename-naming-convention": [
100
100
  "error",
101
- { "**/*.ts": repoPattern },
101
+ { "**/*.ts": queryPattern },
102
102
  ],
103
103
  },
104
104
  },
105
+ {
106
+ name: "naming/queries-export",
107
+ files: featuresGlob(featureRoot, "**/queries/*.query.ts"),
108
+ plugins: { local: localPlugin },
109
+ rules: {
110
+ "local/queries-export": "error",
111
+ },
112
+ },
105
113
  );
106
114
 
107
115
  configs.push(
@@ -117,13 +125,13 @@ export function createNamingConfigs(featureRoot, prefixLibMapping) {
117
125
  },
118
126
  },
119
127
  {
120
- name: "naming/repositories-shared",
121
- files: featuresGlob(featureRoot, "shared/repositories/*.ts"),
128
+ name: "naming/queries-shared",
129
+ files: featuresGlob(featureRoot, "shared/queries/*.ts"),
122
130
  plugins: { "check-file": checkFile },
123
131
  rules: {
124
132
  "check-file/filename-naming-convention": [
125
133
  "error",
126
- { "**/*.ts": `${sharedPrefixPattern}.repo` },
134
+ { "**/*.ts": `${sharedPrefixPattern}.query` },
127
135
  ],
128
136
  },
129
137
  },
@@ -237,53 +245,30 @@ export function createNamingConfigs(featureRoot, prefixLibMapping) {
237
245
  );
238
246
 
239
247
  configs.push({
240
- name: "naming/actions",
241
- files: featuresGlob(featureRoot, "**/actions/*.ts"),
242
- ignores: featuresGlob(featureRoot, "shared/actions/*.ts"),
248
+ name: "naming/interactors",
249
+ files: featuresGlob(featureRoot, "**/interactors/*.ts"),
250
+ ignores: featuresGlob(featureRoot, "shared/interactors/*.ts"),
243
251
  plugins: { "check-file": checkFile },
244
252
  rules: {
245
253
  "check-file/filename-naming-convention": [
246
254
  "error",
247
- { "**/*.ts": actionPattern },
255
+ { "**/*.ts": interactorPattern },
248
256
  ],
249
257
  },
250
258
  });
251
259
 
252
260
  configs.push(
253
261
  {
254
- name: "naming/actions-shared",
255
- files: featuresGlob(featureRoot, "shared/actions/*.ts"),
262
+ name: "naming/interactors-shared",
263
+ files: featuresGlob(featureRoot, "shared/interactors/*.ts"),
256
264
  plugins: { "check-file": checkFile },
257
265
  rules: {
258
266
  "check-file/filename-naming-convention": [
259
267
  "error",
260
- { "**/*.ts": `${sharedPrefixPattern}.action` },
261
- ],
262
- },
263
- },
264
- {
265
- name: "naming/actions-export",
266
- files: featuresGlob(featureRoot, "**/actions/*.ts"),
267
- rules: {
268
- "no-restricted-syntax": [
269
- "error",
270
- {
271
- selector:
272
- "ExportNamedDeclaration > FunctionDeclaration[id.name!=/^handle[A-Z]/]",
273
- message:
274
- "Exported functions in actions must start with 'handle' (e.g., handleGetComics).",
275
- },
268
+ { "**/*.ts": `${sharedPrefixPattern}.interactor` },
276
269
  ],
277
270
  },
278
271
  },
279
- {
280
- name: "naming/actions-handle-service",
281
- files: featuresGlob(featureRoot, "**/actions/*.ts"),
282
- plugins: { local: localPlugin },
283
- rules: {
284
- "local/action-handle-service": "error",
285
- },
286
- },
287
272
  {
288
273
  name: "naming/features-ts-only",
289
274
  files: featuresGlob(featureRoot, "**/*.tsx"),
@@ -10,7 +10,7 @@ export const denoImportsConfigs = [
10
10
  files: [`${FUNCTIONS_ROOT}/**/*.ts`],
11
11
  ignores: [
12
12
  `${FUNCTIONS_ROOT}/_lib/**`,
13
- `${FEATURE_ROOT}/**/repositories/**`,
13
+ `${FEATURE_ROOT}/**/queries/**`,
14
14
  `${FEATURE_ROOT}/**/types/**`,
15
15
  ],
16
16
  rules: {
@@ -21,7 +21,7 @@ export const denoImportsConfigs = [
21
21
  {
22
22
  group: ["*/_lib/*", "*/_lib/**"],
23
23
  message:
24
- "_lib/ can only be imported from repositories (lib-boundary violation)",
24
+ "_lib/ can only be imported from queries (lib-boundary violation)",
25
25
  },
26
26
  ],
27
27
  },
@@ -40,17 +40,17 @@ export const denoImportsConfigs = [
40
40
  {
41
41
  group: ["**/services/*", "**/services"],
42
42
  message:
43
- "Entry points must not import services directly. Import from actions instead.",
43
+ "Entry points must not import services directly. Import from interactors instead.",
44
44
  },
45
45
  {
46
- group: ["**/repositories/*", "**/repositories"],
46
+ group: ["**/queries/*", "**/queries"],
47
47
  message:
48
- "Entry points must not import repositories directly. Import from actions instead.",
48
+ "Entry points must not import queries directly. Import from interactors instead.",
49
49
  },
50
50
  {
51
51
  group: ["*/_lib/*", "*/_lib/**"],
52
52
  message:
53
- "Entry points must not import _lib/ directly. Import from actions instead.",
53
+ "Entry points must not import _lib/ directly. Import from interactors instead.",
54
54
  },
55
55
  ],
56
56
  },
@@ -1,43 +1,45 @@
1
1
  /** Enforce "use server" / "use client" directives per file convention. */
2
2
  export const directivesConfigs = [
3
3
  {
4
- name: "directives/server-action",
5
- files: ["src/features/**/actions/server.action.ts"],
4
+ name: "directives/server-interactor",
5
+ files: ["src/features/**/interactors/server.interactor.ts"],
6
6
  rules: {
7
7
  "no-restricted-syntax": [
8
8
  "error",
9
9
  {
10
10
  selector:
11
11
  "Program > :first-child:not(ExpressionStatement[expression.value='use server'])",
12
- message: 'server.action.ts must start with "use server" directive.',
12
+ message:
13
+ 'server.interactor.ts must start with "use server" directive.',
13
14
  },
14
15
  ],
15
16
  },
16
17
  },
17
18
  {
18
- name: "directives/admin-action",
19
- files: ["src/features/**/actions/admin.action.ts"],
19
+ name: "directives/admin-interactor",
20
+ files: ["src/features/**/interactors/admin.interactor.ts"],
20
21
  rules: {
21
22
  "no-restricted-syntax": [
22
23
  "error",
23
24
  {
24
25
  selector:
25
26
  "Program > :first-child:not(ExpressionStatement[expression.value='use server'])",
26
- message: 'admin.action.ts must start with "use server" directive.',
27
+ message:
28
+ 'admin.interactor.ts must start with "use server" directive.',
27
29
  },
28
30
  ],
29
31
  },
30
32
  },
31
33
  {
32
- name: "directives/client-action",
33
- files: ["src/features/**/actions/client.action.ts"],
34
+ name: "directives/client-interactor",
35
+ files: ["src/features/**/interactors/client.interactor.ts"],
34
36
  rules: {
35
37
  "no-restricted-syntax": [
36
38
  "error",
37
39
  {
38
40
  selector: "ExpressionStatement[expression.value='use server']",
39
41
  message:
40
- 'client.action.ts must NOT have "use server" directive. It uses @/lib/supabase/client.',
42
+ 'client.interactor.ts must NOT have "use server" directive. It uses @/lib/supabase/client.',
41
43
  },
42
44
  ],
43
45
  },
@@ -1,58 +0,0 @@
1
- /** In *.action.ts, handleXxx must call the service method *.xxx(). */
2
- export const actionHandleServiceRule = {
3
- meta: {
4
- type: "problem",
5
- messages: {
6
- missingCall:
7
- "handleXxx must call the corresponding service method '*.{{ expected }}()'.",
8
- },
9
- schema: [],
10
- },
11
- create(context) {
12
- return {
13
- "ExportNamedDeclaration > FunctionDeclaration[id.name=/^handle[A-Z]/]"(
14
- node,
15
- ) {
16
- const name = node.id.name;
17
- const suffix = name.slice("handle".length);
18
- const expected = suffix[0].toLowerCase() + suffix.slice(1);
19
-
20
- let found = false;
21
-
22
- function walk(n) {
23
- if (found || !n || typeof n !== "object") return;
24
-
25
- if (
26
- n.type === "CallExpression" &&
27
- n.callee.type === "MemberExpression" &&
28
- !n.callee.computed &&
29
- n.callee.property.name === expected
30
- ) {
31
- found = true;
32
- return;
33
- }
34
-
35
- for (const key of Object.keys(n)) {
36
- if (key === "parent") continue;
37
- const child = n[key];
38
- if (Array.isArray(child)) {
39
- for (const item of child) walk(item);
40
- } else if (child && typeof child.type === "string") {
41
- walk(child);
42
- }
43
- }
44
- }
45
-
46
- walk(node.body);
47
-
48
- if (!found) {
49
- context.report({
50
- node: node.id,
51
- messageId: "missingCall",
52
- data: { expected },
53
- });
54
- }
55
- },
56
- };
57
- },
58
- };