@geonosis/oxlint-plugin-biological-architecture 2.13.0 → 3.0.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.
Files changed (226) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/RULES.md +1 -2
  3. package/dist/discover-atoms.d.ts +5 -6
  4. package/dist/discover-atoms.js +33 -24
  5. package/dist/index.d.ts +7 -54
  6. package/dist/index.js +180 -7973
  7. package/dist/presets.d.ts +6 -8
  8. package/dist/presets.js +137 -6
  9. package/dist/rules/atom-no-deps.d.ts +21 -0
  10. package/dist/rules/atom-no-deps.js +29 -0
  11. package/dist/rules/boundary-time-is-primitive.d.ts +56 -0
  12. package/dist/rules/boundary-time-is-primitive.js +134 -0
  13. package/dist/rules/cell-must-be-stateful.d.ts +22 -0
  14. package/dist/rules/cell-must-be-stateful.js +75 -0
  15. package/dist/rules/cell-must-not-compose-cell.d.ts +21 -0
  16. package/dist/rules/cell-must-not-compose-cell.js +29 -0
  17. package/dist/rules/cell-no-cross-feature-organelles.d.ts +21 -0
  18. package/dist/rules/cell-no-cross-feature-organelles.js +37 -0
  19. package/dist/rules/cell-no-tissues.d.ts +21 -0
  20. package/dist/rules/cell-no-tissues.js +23 -0
  21. package/dist/rules/cells-folder-index-is-barrel.d.ts +35 -0
  22. package/dist/rules/cells-folder-index-is-barrel.js +66 -0
  23. package/dist/rules/compound-must-be-stateless.d.ts +39 -0
  24. package/dist/rules/compound-must-be-stateless.js +86 -0
  25. package/dist/rules/compound-no-stores.d.ts +21 -0
  26. package/dist/rules/compound-no-stores.js +25 -0
  27. package/dist/rules/constants-in-constants-file.d.ts +41 -0
  28. package/dist/rules/constants-in-constants-file.js +41 -0
  29. package/dist/rules/dialect-through-the-seam.d.ts +63 -0
  30. package/dist/rules/dialect-through-the-seam.js +102 -0
  31. package/dist/rules/durable-body-reads-through-steps.d.ts +78 -0
  32. package/dist/rules/durable-body-reads-through-steps.js +122 -0
  33. package/dist/rules/effect-hook-naming.d.ts +48 -0
  34. package/dist/rules/effect-hook-naming.js +82 -0
  35. package/dist/rules/emit-declares-attempts.d.ts +62 -0
  36. package/dist/rules/emit-declares-attempts.js +102 -0
  37. package/dist/rules/font-roles-only.d.ts +78 -0
  38. package/dist/rules/font-roles-only.js +88 -0
  39. package/dist/rules/layer-walls.d.ts +97 -0
  40. package/dist/rules/layer-walls.js +138 -0
  41. package/dist/rules/lib/cells-barrel.d.ts +2 -0
  42. package/dist/rules/lib/cells-barrel.js +6 -0
  43. package/dist/rules/lib/colors.d.ts +24 -0
  44. package/dist/rules/lib/colors.js +261 -0
  45. package/dist/rules/lib/context.d.ts +26 -0
  46. package/dist/rules/lib/context.js +0 -0
  47. package/dist/rules/lib/direction-rule.d.ts +65 -0
  48. package/dist/rules/lib/direction-rule.js +125 -0
  49. package/dist/rules/lib/doors.d.ts +26 -0
  50. package/dist/rules/lib/doors.js +89 -0
  51. package/dist/rules/lib/hooks.d.ts +20 -0
  52. package/dist/rules/lib/hooks.js +21 -0
  53. package/dist/rules/lib/imports.d.ts +52 -0
  54. package/dist/rules/lib/imports.js +25 -0
  55. package/dist/rules/lib/landing.d.ts +50 -0
  56. package/dist/rules/lib/landing.js +111 -0
  57. package/dist/rules/lib/literal-call.d.ts +11 -0
  58. package/dist/rules/lib/literal-call.js +10 -0
  59. package/dist/rules/lib/module-text.d.ts +43 -0
  60. package/dist/{chunk-H7UQNDFE.js → rules/lib/module-text.js} +44 -30
  61. package/dist/rules/lib/mutations.d.ts +19 -0
  62. package/dist/rules/lib/mutations.js +27 -0
  63. package/dist/rules/lib/nodes.d.ts +25 -0
  64. package/dist/rules/lib/nodes.js +71 -0
  65. package/dist/rules/lib/objects.d.ts +17 -0
  66. package/dist/rules/lib/objects.js +35 -0
  67. package/dist/rules/lib/options.d.ts +74 -0
  68. package/dist/rules/lib/options.js +64 -0
  69. package/dist/rules/lib/package-imports.d.ts +6 -0
  70. package/dist/rules/lib/package-imports.js +43 -0
  71. package/dist/rules/lib/paths.d.ts +47 -0
  72. package/dist/rules/lib/paths.js +28 -0
  73. package/dist/rules/lib/pattern-match.d.ts +16 -0
  74. package/dist/rules/lib/pattern-match.js +29 -0
  75. package/dist/rules/lib/probe.d.ts +55 -0
  76. package/dist/rules/lib/probe.js +39 -0
  77. package/dist/rules/lib/reexports.d.ts +20 -0
  78. package/dist/rules/lib/reexports.js +40 -0
  79. package/dist/rules/lib/render-tracking.d.ts +54 -0
  80. package/dist/rules/lib/render-tracking.js +27 -0
  81. package/dist/rules/lib/schema.d.ts +14 -0
  82. package/dist/rules/lib/schema.js +11 -0
  83. package/dist/rules/lib/shared-features.d.ts +24 -0
  84. package/dist/rules/lib/shared-features.js +16 -0
  85. package/dist/rules/lib/steps.d.ts +19 -0
  86. package/dist/rules/lib/steps.js +37 -0
  87. package/dist/rules/lib/styling.d.ts +65 -0
  88. package/dist/rules/lib/styling.js +90 -0
  89. package/dist/rules/lib/tiers.d.ts +31 -0
  90. package/dist/rules/lib/tiers.js +32 -0
  91. package/dist/rules/lib/tissue-entrypoint.d.ts +5 -0
  92. package/dist/rules/lib/tissue-entrypoint.js +15 -0
  93. package/dist/rules/lib/tsconfig-paths.d.ts +5 -0
  94. package/dist/rules/lib/tsconfig-paths.js +109 -0
  95. package/dist/rules/lib/visitors.d.ts +31 -0
  96. package/dist/rules/lib/visitors.js +38 -0
  97. package/dist/rules/list-take-without-order.d.ts +36 -0
  98. package/dist/rules/list-take-without-order.js +84 -0
  99. package/dist/rules/max-comment-density.d.ts +49 -0
  100. package/dist/rules/max-comment-density.js +72 -0
  101. package/dist/rules/molecule-atoms-only.d.ts +21 -0
  102. package/dist/rules/molecule-atoms-only.js +28 -0
  103. package/dist/rules/molecule-must-compose.d.ts +25 -0
  104. package/dist/rules/molecule-must-compose.js +73 -0
  105. package/dist/rules/mutating-step-declares-retry.d.ts +57 -0
  106. package/dist/rules/mutating-step-declares-retry.js +92 -0
  107. package/dist/rules/named-when-block.d.ts +37 -0
  108. package/dist/rules/named-when-block.js +59 -0
  109. package/dist/rules/next-route-segment-is-thin-delegate.d.ts +56 -0
  110. package/dist/rules/next-route-segment-is-thin-delegate.js +194 -0
  111. package/dist/rules/no-brand-names.d.ts +64 -0
  112. package/dist/rules/no-brand-names.js +80 -0
  113. package/dist/rules/no-card-shaped-div.d.ts +67 -0
  114. package/dist/rules/no-card-shaped-div.js +73 -0
  115. package/dist/rules/no-cross-feature-stores.d.ts +21 -0
  116. package/dist/rules/no-cross-feature-stores.js +31 -0
  117. package/dist/rules/no-d1-transaction.d.ts +69 -0
  118. package/dist/rules/no-d1-transaction.js +90 -0
  119. package/dist/rules/no-date-string-compare.d.ts +28 -0
  120. package/dist/rules/no-date-string-compare.js +100 -0
  121. package/dist/rules/no-duplicate-jsx-patterns.d.ts +55 -0
  122. package/dist/rules/no-duplicate-jsx-patterns.js +103 -0
  123. package/dist/rules/no-generic-utility-package.d.ts +38 -0
  124. package/dist/rules/no-generic-utility-package.js +86 -0
  125. package/dist/rules/no-hook-in-component-disguise.d.ts +96 -0
  126. package/dist/rules/no-hook-in-component-disguise.js +229 -0
  127. package/dist/rules/no-index-count-as-exact.d.ts +42 -0
  128. package/dist/rules/no-index-count-as-exact.js +65 -0
  129. package/dist/rules/no-inert-hidden-jsx.d.ts +40 -0
  130. package/dist/rules/no-inert-hidden-jsx.js +75 -0
  131. package/dist/rules/no-inline-data-in-jsx.d.ts +24 -0
  132. package/dist/rules/no-inline-data-in-jsx.js +71 -0
  133. package/dist/rules/no-invalid-feature-folders.d.ts +36 -0
  134. package/dist/rules/no-invalid-feature-folders.js +88 -0
  135. package/dist/rules/no-loader-side-effect-in-index-file.d.ts +55 -0
  136. package/dist/rules/no-loader-side-effect-in-index-file.js +77 -0
  137. package/dist/rules/no-logic-in-component-files.d.ts +49 -0
  138. package/dist/rules/no-logic-in-component-files.js +105 -0
  139. package/dist/rules/no-mock-db-in-integration.d.ts +88 -0
  140. package/dist/rules/no-mock-db-in-integration.js +105 -0
  141. package/dist/rules/no-orm-outside-db.d.ts +21 -0
  142. package/dist/rules/no-orm-outside-db.js +37 -0
  143. package/dist/rules/no-raw-html-atoms.d.ts +82 -0
  144. package/dist/rules/no-raw-html-atoms.js +91 -0
  145. package/dist/rules/no-raw-sql-outside-allowed.d.ts +61 -0
  146. package/dist/rules/no-raw-sql-outside-allowed.js +57 -0
  147. package/dist/rules/no-react-namespace.d.ts +39 -0
  148. package/dist/rules/no-react-namespace.js +46 -0
  149. package/dist/rules/no-renamed-html-props.d.ts +38 -0
  150. package/dist/rules/no-renamed-html-props.js +80 -0
  151. package/dist/rules/no-render-prop-reader.d.ts +67 -0
  152. package/dist/rules/no-render-prop-reader.js +59 -0
  153. package/dist/rules/no-runtime-dead-affordance.d.ts +69 -0
  154. package/dist/rules/no-runtime-dead-affordance.js +86 -0
  155. package/dist/rules/no-swallowed-catch.d.ts +36 -0
  156. package/dist/rules/no-swallowed-catch.js +92 -0
  157. package/dist/rules/no-trivial-wrapper-component.d.ts +61 -0
  158. package/dist/rules/no-trivial-wrapper-component.js +147 -0
  159. package/dist/rules/no-ts-in-bio-folders.d.ts +52 -0
  160. package/dist/rules/no-ts-in-bio-folders.js +58 -0
  161. package/dist/rules/no-type-definitions-in-components.d.ts +33 -0
  162. package/dist/rules/no-type-definitions-in-components.js +48 -0
  163. package/dist/rules/no-unregistered-plugin-dir.d.ts +116 -0
  164. package/dist/rules/no-unregistered-plugin-dir.js +138 -0
  165. package/dist/rules/no-void-port.d.ts +65 -0
  166. package/dist/rules/no-void-port.js +53 -0
  167. package/dist/rules/organelle-dependency.d.ts +21 -0
  168. package/dist/rules/organelle-dependency.js +37 -0
  169. package/dist/rules/organelle-single-source.d.ts +19 -0
  170. package/dist/rules/organelle-single-source.js +97 -0
  171. package/dist/rules/plugin-route-namespaced.d.ts +79 -0
  172. package/dist/rules/plugin-route-namespaced.js +137 -0
  173. package/dist/rules/queries-require-org-scope.d.ts +88 -0
  174. package/dist/rules/queries-require-org-scope.js +83 -0
  175. package/dist/rules/queue-loop-is-the-library.d.ts +28 -0
  176. package/dist/rules/queue-loop-is-the-library.js +35 -0
  177. package/dist/rules/route-no-inline-mutation.d.ts +81 -0
  178. package/dist/rules/route-no-inline-mutation.js +125 -0
  179. package/dist/rules/router-schema-parity.d.ts +60 -0
  180. package/dist/rules/router-schema-parity.js +95 -0
  181. package/dist/rules/service-name-not-reserved.d.ts +47 -0
  182. package/dist/rules/service-name-not-reserved.js +112 -0
  183. package/dist/rules/ssot-no-inline-facts.d.ts +68 -0
  184. package/dist/rules/ssot-no-inline-facts.js +221 -0
  185. package/dist/rules/ssot-no-process-env.d.ts +55 -0
  186. package/dist/rules/ssot-no-process-env.js +51 -0
  187. package/dist/rules/step-declares-compensation-or-none.d.ts +34 -0
  188. package/dist/rules/step-declares-compensation-or-none.js +52 -0
  189. package/dist/rules/step-opens-its-own-cell.d.ts +82 -0
  190. package/dist/rules/step-opens-its-own-cell.js +163 -0
  191. package/dist/rules/store-route-scopes-tenant-data.d.ts +52 -0
  192. package/dist/rules/store-route-scopes-tenant-data.js +150 -0
  193. package/dist/rules/subpath-over-barrel.d.ts +67 -0
  194. package/dist/rules/subpath-over-barrel.js +153 -0
  195. package/dist/rules/subscriber-declares-id.d.ts +39 -0
  196. package/dist/rules/subscriber-declares-id.js +79 -0
  197. package/dist/rules/tables-declare-their-plane.d.ts +44 -0
  198. package/dist/rules/tables-declare-their-plane.js +64 -0
  199. package/dist/rules/tenant-tables-carry-org-id.d.ts +66 -0
  200. package/dist/rules/tenant-tables-carry-org-id.js +64 -0
  201. package/dist/rules/test-no-clock-polling-for-promise-flips.d.ts +42 -0
  202. package/dist/rules/test-no-clock-polling-for-promise-flips.js +130 -0
  203. package/dist/rules/time-through-the-door.d.ts +86 -0
  204. package/dist/rules/time-through-the-door.js +138 -0
  205. package/dist/rules/tissue-must-compose.d.ts +41 -0
  206. package/dist/rules/tissue-must-compose.js +113 -0
  207. package/dist/rules/tissue-no-data-props.d.ts +27 -0
  208. package/dist/rules/tissue-no-data-props.js +148 -0
  209. package/dist/rules/tissue-no-hooks.d.ts +43 -0
  210. package/dist/rules/tissue-no-hooks.js +72 -0
  211. package/dist/rules/tissue-no-organelles.d.ts +21 -0
  212. package/dist/rules/tissue-no-organelles.js +17 -0
  213. package/dist/rules/tissue-no-stores.d.ts +21 -0
  214. package/dist/rules/tissue-no-stores.js +17 -0
  215. package/dist/rules/todo-names-its-plan.d.ts +75 -0
  216. package/dist/rules/todo-names-its-plan.js +68 -0
  217. package/dist/rules/tokens-only-colors.d.ts +100 -0
  218. package/dist/rules/tokens-only-colors.js +129 -0
  219. package/dist/rules/unique-query-step-name.d.ts +50 -0
  220. package/dist/rules/unique-query-step-name.js +130 -0
  221. package/dist/rules/worker-handles-are-scoped.d.ts +65 -0
  222. package/dist/rules/worker-handles-are-scoped.js +97 -0
  223. package/dist/rules/zustand-v5-best-practices.d.ts +65 -0
  224. package/dist/rules/zustand-v5-best-practices.js +107 -0
  225. package/package.json +4 -4
  226. package/dist/chunk-ZY3XIRRE.js +0 -145
@@ -0,0 +1,23 @@
1
+ // src/rules/cell-no-tissues.ts
2
+ import { directionRule } from "./lib/direction-rule.js";
3
+ var HIGHER_LEVEL_PATTERNS = [
4
+ /(?:^|\/)(?:features\/[^/]+\/)?tissues\//,
5
+ /(?:^|\/)(?:features\/[^/]+\/)?organs\//,
6
+ /(?:^|\/)layouts\//,
7
+ /(?:^|\/)app\//
8
+ ];
9
+ var cellNoTissues = directionRule({
10
+ description: "Cells cannot import tissues, organs, or app route files. Direction is wrong: tissues compose cells (not the reverse), and organs compose tissues and cells.",
11
+ fixShape: `A cell cannot import a tissue, an organ, a layout or an app route — that is the direction
12
+ inverted. If a cell needs a layout around it, take the layout as \`children\` and let a tissue arrange
13
+ both. Type-only imports are exempt; \`export … from\` is not.`,
14
+ forbidden: (target) => HIGHER_LEVEL_PATTERNS.some((pattern) => pattern.test(target.path)),
15
+ messages: {
16
+ forbidden: 'Cell "{{file}}" cannot import higher-level component "{{source}}". Direction is wrong: tissues/organs compose cells, not the reverse. If this cell needs to render a layout-like wrapper, the wrapper should be a compound, or this cell should be promoted.'
17
+ },
18
+ scope: "cells"
19
+ });
20
+ var cell_no_tissues_default = cellNoTissues;
21
+ export {
22
+ cell_no_tissues_default as default
23
+ };
@@ -0,0 +1,35 @@
1
+ import { type RuleContext } from './lib/context.js';
2
+ interface ProgramNode {
3
+ body: Statement[];
4
+ loc?: unknown;
5
+ type: 'Program';
6
+ }
7
+ interface Statement {
8
+ declaration?: unknown;
9
+ importKind?: string;
10
+ loc?: unknown;
11
+ source?: {
12
+ value: string;
13
+ } | null;
14
+ specifiers?: unknown[];
15
+ type: string;
16
+ }
17
+ declare const cellsFolderIndexIsBarrel: {
18
+ create(context: RuleContext): {
19
+ Program?: undefined;
20
+ } | {
21
+ Program(node: ProgramNode): void;
22
+ };
23
+ fixShape: string;
24
+ meta: {
25
+ docs: {
26
+ description: string;
27
+ };
28
+ messages: {
29
+ notBarrel: string;
30
+ };
31
+ schema: never[];
32
+ type: 'problem';
33
+ };
34
+ };
35
+ export default cellsFolderIndexIsBarrel;
@@ -0,0 +1,66 @@
1
+ // src/rules/cells-folder-index-is-barrel.ts
2
+ import { isCellsBarrel } from "./lib/cells-barrel.js";
3
+ import { normalise } from "./lib/paths.js";
4
+ function describeStatement(stmt) {
5
+ switch (stmt.type) {
6
+ case "ClassDeclaration":
7
+ return "a class declaration";
8
+ case "ExportDefaultDeclaration":
9
+ return "a default export declaration";
10
+ case "ExportNamedDeclaration":
11
+ return "a named export declaration (not a re-export)";
12
+ case "ExpressionStatement":
13
+ return "a top-level expression statement";
14
+ case "FunctionDeclaration":
15
+ return "a function declaration";
16
+ case "ImportDeclaration":
17
+ return "a side-effect import";
18
+ case "VariableDeclaration":
19
+ return "a variable declaration";
20
+ default:
21
+ return `a ${stmt.type} statement`;
22
+ }
23
+ }
24
+ var cellsFolderIndexIsBarrel = {
25
+ create(context) {
26
+ const filename = context.filename;
27
+ const normalized = normalise(filename);
28
+ if (!isCellsBarrel(normalized))
29
+ return {};
30
+ const shortName = normalized.split("/").pop() || normalized;
31
+ return {
32
+ Program(node) {
33
+ for (const stmt of node.body) {
34
+ if (stmt.type === "ExportAllDeclaration")
35
+ continue;
36
+ if (stmt.type === "ExportNamedDeclaration" && stmt.source)
37
+ continue;
38
+ if (stmt.type === "ImportDeclaration" && stmt.importKind === "type")
39
+ continue;
40
+ context.report({
41
+ data: { file: shortName, kind: describeStatement(stmt) },
42
+ messageId: "notBarrel",
43
+ node: stmt
44
+ });
45
+ }
46
+ }
47
+ };
48
+ },
49
+ fixShape: `\`cells/index.ts\` and \`cells/dynamic.ts\` are barrels: \`export … from\` lines and nothing else. No
50
+ function, class or variable declaration, no side-effect import. Logic that wants to live in the
51
+ barrel belongs in a cell file the barrel re-exports.`,
52
+ meta: {
53
+ docs: {
54
+ description: "Barrel files under cells/ (index.ts, dynamic.ts, dynamic.tsx) must contain ONLY re-export statements. Component definitions belong in their own file."
55
+ },
56
+ messages: {
57
+ notBarrel: 'Barrel file "{{file}}" contains {{kind}}. Barrel files under cells/ must be pure re-export manifests (only `export * from` or `export { X } from`). Component definitions belong in their own file.'
58
+ },
59
+ schema: [],
60
+ type: "problem"
61
+ }
62
+ };
63
+ var cells_folder_index_is_barrel_default = cellsFolderIndexIsBarrel;
64
+ export {
65
+ cells_folder_index_is_barrel_default as default
66
+ };
@@ -0,0 +1,39 @@
1
+ import { type RuleContext } from './lib/context.js';
2
+ import type { AnyNode } from './lib/nodes.js';
3
+ interface ImportNode {
4
+ importKind?: string;
5
+ source: {
6
+ value: string;
7
+ };
8
+ specifiers?: ImportSpecifier[];
9
+ }
10
+ interface ImportSpecifier {
11
+ imported?: {
12
+ name: string;
13
+ type: string;
14
+ };
15
+ type: string;
16
+ }
17
+ declare const compoundMustBeStateless: {
18
+ create(context: RuleContext): {
19
+ CallExpression?: undefined;
20
+ ImportDeclaration?: undefined;
21
+ } | {
22
+ CallExpression(node: AnyNode): void;
23
+ ImportDeclaration(node: ImportNode): void;
24
+ };
25
+ fixShape: string;
26
+ meta: {
27
+ docs: {
28
+ description: string;
29
+ };
30
+ messages: {
31
+ forbiddenContextImport: string;
32
+ forbiddenHookCall: string;
33
+ forbiddenReactHook: string;
34
+ };
35
+ schema: never[];
36
+ type: 'problem';
37
+ };
38
+ };
39
+ export default compoundMustBeStateless;
@@ -0,0 +1,86 @@
1
+ // src/rules/compound-must-be-stateless.ts
2
+ import { hookCalledBy } from "./lib/hooks.js";
3
+ import { normalise } from "./lib/paths.js";
4
+ var reactForbiddenHooks = new Set([
5
+ "useCallback",
6
+ "useEffect",
7
+ "useImperativeHandle",
8
+ "useLayoutEffect",
9
+ "useMemo",
10
+ "useReducer",
11
+ "useRef",
12
+ "useState"
13
+ ]);
14
+ var CONTEXT_MODULE = /(?:^|\/)(?:providers|contexts)\//;
15
+ var COMPOUND_FILE_PATTERN = /(?:^|\/)(?:features\/[^/]+\/)?compounds\//;
16
+ var compoundMustBeStateless = {
17
+ create(context) {
18
+ const filename = context.filename;
19
+ const normalized = normalise(filename);
20
+ const isCompound = COMPOUND_FILE_PATTERN.test(normalized);
21
+ if (!isCompound)
22
+ return {};
23
+ const shortName = normalized.split("/").pop() || normalized;
24
+ return {
25
+ CallExpression(node) {
26
+ const name = hookCalledBy(node);
27
+ if (name === null)
28
+ return;
29
+ context.report({
30
+ data: { file: shortName, hook: name },
31
+ messageId: "forbiddenHookCall",
32
+ node
33
+ });
34
+ },
35
+ ImportDeclaration(node) {
36
+ if (node.importKind === "type")
37
+ return;
38
+ const source = node.source.value;
39
+ if (source === "react") {
40
+ const specifiers = node.specifiers ?? [];
41
+ for (const spec of specifiers) {
42
+ if (spec.type !== "ImportSpecifier")
43
+ continue;
44
+ const importedName = spec.imported?.name;
45
+ if (!importedName)
46
+ continue;
47
+ if (reactForbiddenHooks.has(importedName)) {
48
+ context.report({
49
+ data: { file: shortName, hook: importedName },
50
+ messageId: "forbiddenReactHook",
51
+ node
52
+ });
53
+ }
54
+ }
55
+ return;
56
+ }
57
+ if (CONTEXT_MODULE.test(source)) {
58
+ context.report({
59
+ data: { file: shortName, source },
60
+ messageId: "forbiddenContextImport",
61
+ node
62
+ });
63
+ }
64
+ }
65
+ };
66
+ },
67
+ fixShape: `A compound is inert: no \`useState\`, no \`useEffect\`, no \`useMemo\`, no store read, no provider import —
68
+ no hook call at all. It takes what it renders as props. If it needs state, the caller is a cell or an
69
+ organelle and holds it; if the file itself needs state, it is not a compound.`,
70
+ meta: {
71
+ docs: {
72
+ description: "Compounds are pure stateless props-in/JSX-out components. They cannot: (1) import React hooks from react, (2) call ANY hook (use* functions), (3) import from providers/ or contexts/. If you need state, this is a cell or organelle."
73
+ },
74
+ messages: {
75
+ forbiddenContextImport: 'Compound "{{file}}" imports from "{{source}}". Compounds must not use context/providers — receive data via props instead.',
76
+ forbiddenHookCall: 'Compound "{{file}}" calls hook "{{hook}}". Compounds are stateless — no hooks allowed. Move to cells/ or organelles/.',
77
+ forbiddenReactHook: 'Compound "{{file}}" imports "{{hook}}" from react. Compounds are stateless — move to cells/ or organelles/.'
78
+ },
79
+ schema: [],
80
+ type: "problem"
81
+ }
82
+ };
83
+ var compound_must_be_stateless_default = compoundMustBeStateless;
84
+ export {
85
+ compound_must_be_stateless_default as default
86
+ };
@@ -0,0 +1,21 @@
1
+ declare const compoundNoStores: {
2
+ create(context: {
3
+ filename: string;
4
+ options?: readonly unknown[];
5
+ report: (descriptor: {
6
+ data: Record<string, string>;
7
+ messageId: string;
8
+ node: unknown;
9
+ }) => void;
10
+ }): import('./lib/visitors.js').Visitor;
11
+ fixShape: string;
12
+ meta: {
13
+ docs: {
14
+ description: string;
15
+ };
16
+ messages: Record<string, string>;
17
+ schema: unknown[];
18
+ type: 'problem';
19
+ };
20
+ };
21
+ export default compoundNoStores;
@@ -0,0 +1,25 @@
1
+ // src/rules/compound-no-stores.ts
2
+ import { directionRule } from "./lib/direction-rule.js";
3
+ var FORBIDDEN_PATTERNS = [
4
+ /(?:^|\/)stores\//,
5
+ /(?:^|\/)organelles\//,
6
+ /(?:^|\/)cells\//,
7
+ /(?:^|\/)tissues\//,
8
+ /(?:^|\/)layouts\//,
9
+ /\/app\//
10
+ ];
11
+ var compoundNoStores = directionRule({
12
+ description: "Compounds may only import atoms, molecules, and other compounds. They cannot import organelles, cells, tissues, stores, or app routes — direction rule: a parent may only compose children at levels <= its own.",
13
+ fixShape: `A compound may import atoms, molecules and other compounds. Stores, organelles, cells, tissues,
14
+ layouts and app routes are all above it. Take the data as a prop from the cell that reads the store.
15
+ Type-only imports are exempt; \`export … from\` is not.`,
16
+ forbidden: (target) => FORBIDDEN_PATTERNS.some((pattern) => pattern.test(target.path)),
17
+ messages: {
18
+ forbidden: "Compounds cannot import from {{source}}. Direction violation — compounds are level 3 and can only depend on atoms, molecules, other compounds, and shared libs/domains/types."
19
+ },
20
+ scope: (importer) => importer.tier === "compounds" && !/__tests__\//.test(importer.path)
21
+ });
22
+ var compound_no_stores_default = compoundNoStores;
23
+ export {
24
+ compound_no_stores_default as default
25
+ };
@@ -0,0 +1,41 @@
1
+ type Declarator = {
2
+ type: string;
3
+ id: {
4
+ type: string;
5
+ name?: string;
6
+ };
7
+ };
8
+ type ExportNamedDeclaration = {
9
+ type: 'ExportNamedDeclaration';
10
+ declaration?: {
11
+ type: string;
12
+ kind?: string;
13
+ declarations?: Declarator[];
14
+ } | null;
15
+ };
16
+ declare const constantsInConstantsFile: {
17
+ create(context: {
18
+ filename: string;
19
+ report: (d: {
20
+ messageId: string;
21
+ node: unknown;
22
+ data: Record<string, string>;
23
+ }) => void;
24
+ }): {
25
+ ExportNamedDeclaration?: undefined;
26
+ } | {
27
+ ExportNamedDeclaration(node: ExportNamedDeclaration): void;
28
+ };
29
+ fixShape: string;
30
+ meta: {
31
+ docs: {
32
+ description: string;
33
+ };
34
+ messages: {
35
+ outsideConstantsFile: string;
36
+ };
37
+ schema: never[];
38
+ type: 'problem';
39
+ };
40
+ };
41
+ export default constantsInConstantsFile;
@@ -0,0 +1,41 @@
1
+ // src/rules/constants-in-constants-file.ts
2
+ import { normalise } from "./lib/paths.js";
3
+ var exemptFiles = [/\/constants\.ts$/, /\/__tests__\//, /\/scripts\//, /\/testing\//, /\.d\.ts$/];
4
+ var screamingSnake = /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/;
5
+ var constantsInConstantsFile = {
6
+ create(context) {
7
+ const normalized = normalise(context.filename);
8
+ if (exemptFiles.some((file) => file.test(normalized)))
9
+ return {};
10
+ return {
11
+ ExportNamedDeclaration(node) {
12
+ const declaration = node.declaration;
13
+ if (!declaration || declaration.type !== "VariableDeclaration")
14
+ return;
15
+ for (const declarator of declaration.declarations ?? []) {
16
+ const name = declarator.id.type === "Identifier" ? declarator.id.name : undefined;
17
+ if (name !== undefined && name.length >= 3 && screamingSnake.test(name)) {
18
+ context.report({ messageId: "outsideConstantsFile", node, data: { name } });
19
+ }
20
+ }
21
+ }
22
+ };
23
+ },
24
+ fixShape: `A \`SCREAMING_SNAKE\` export is a fact, and a fact lives in a \`constants.ts\` beside its owner — not
25
+ scattered across the module that happens to use it first. Move the declaration, import it back.
26
+ Non-exported module locals are unaffected.`,
27
+ meta: {
28
+ docs: {
29
+ description: "A SCREAMING_SNAKE export is a constant, and constants live in the constants.ts beside their owner."
30
+ },
31
+ messages: {
32
+ outsideConstantsFile: "{{name}} is a constant declared outside constants.ts. Move it to the constants.ts of its domain, feature lib or package."
33
+ },
34
+ schema: [],
35
+ type: "problem"
36
+ }
37
+ };
38
+ var constants_in_constants_file_default = constantsInConstantsFile;
39
+ export {
40
+ constants_in_constants_file_default as default
41
+ };
@@ -0,0 +1,63 @@
1
+ import { type RuleContextWithOptions } from './lib/context.js';
2
+ import { type Probe } from './lib/probe.js';
3
+ interface Options {
4
+ /**
5
+ * Regex sources over the normalised filename: the files the Postgres-word check leaves alone —
6
+ * test helpers, fixtures. Naming any REPLACES the test-path default.
7
+ */
8
+ exempt?: string | string[];
9
+ /**
10
+ * Regex sources over the normalised filename: the only files that may name a dialect or driver
11
+ * module — the schema, the dialect module, the client, the drivers, drizzle-kit's configs.
12
+ */
13
+ seam?: string | string[];
14
+ }
15
+ declare const dialectThroughTheSeam: {
16
+ create(context: RuleContextWithOptions<Options>): import('./lib/visitors.js').Visitor;
17
+ fixShape: string;
18
+ meta: {
19
+ docs: {
20
+ description: string;
21
+ };
22
+ messages: {
23
+ dialectOutsideSeam: string;
24
+ postgresOutsideDialect: string;
25
+ };
26
+ schema: {
27
+ additionalProperties: boolean;
28
+ properties: {
29
+ exempt: {
30
+ anyOf: ({
31
+ type: 'string';
32
+ items?: undefined;
33
+ } | {
34
+ items: {
35
+ type: 'string';
36
+ };
37
+ type: 'array';
38
+ })[];
39
+ };
40
+ seam: {
41
+ anyOf: ({
42
+ type: 'string';
43
+ items?: undefined;
44
+ } | {
45
+ items: {
46
+ type: 'string';
47
+ };
48
+ type: 'array';
49
+ })[];
50
+ };
51
+ };
52
+ type: 'object';
53
+ }[];
54
+ type: 'problem';
55
+ };
56
+ /**
57
+ * A file OUTSIDE the seam importing the dialect — the inverse of a probe placed where its option
58
+ * points. Tried against the repo's own `seam`: a seam broad enough to swallow every candidate has
59
+ * configured a rule that can never fire, and the probe says so rather than calling it green.
60
+ */
61
+ probe(options: readonly unknown[]): Probe;
62
+ };
63
+ export default dialectThroughTheSeam;
@@ -0,0 +1,102 @@
1
+ // src/rules/dialect-through-the-seam.ts
2
+ import { DEFAULT_TEST_EXEMPT, PATTERNS, patternsOf, requireOption } from "./lib/options.js";
3
+ import { anyOf, normalise } from "./lib/paths.js";
4
+ import { bagOf } from "./lib/probe.js";
5
+ import { optionsBag } from "./lib/schema.js";
6
+ import { mergeVisitors, onImportSource } from "./lib/visitors.js";
7
+ var RULE = "dialect-through-the-seam";
8
+ var ACCEPTS = "{ seam: string | string[] (regex sources over the filename: the files that may name the dialect), exempt: string | string[] (regex sources: files the Postgres-word check leaves alone, default the test paths: __tests__/, .test., .spec.) }";
9
+ var dialectModules = [
10
+ "drizzle-orm/pg-core",
11
+ "drizzle-orm/sqlite-core",
12
+ "drizzle-orm/d1",
13
+ "drizzle-orm/postgres-js",
14
+ "drizzle-orm/node-postgres",
15
+ "drizzle-orm/pglite",
16
+ "drizzle-orm/neon-http",
17
+ "drizzle-orm/neon-serverless",
18
+ "postgres",
19
+ "pg"
20
+ ];
21
+ var postgresWords = /\b(to_tsquery|ts_rank|set_config|current_setting|to_char|to_timestamp|regexp_replace|explain|information_schema|set local)\b/i;
22
+ var isDialectModule = (source) => typeof source === "string" && dialectModules.some((module) => source === module || source.startsWith(`${module}/`));
23
+ var dialectThroughTheSeam = {
24
+ create(context) {
25
+ const bag = context.options?.[0] ?? {};
26
+ const seam = requireOption(RULE, patternsOf(bag.seam), "seam", ACCEPTS);
27
+ const exempt = bag.exempt === undefined ? DEFAULT_TEST_EXEMPT : patternsOf(bag.exempt);
28
+ const normalized = normalise(context.filename);
29
+ const atTheSeam = anyOf(seam, normalized);
30
+ const inTests = anyOf(exempt, normalized);
31
+ const checkImport = (source, node) => {
32
+ if (atTheSeam || !isDialectModule(source))
33
+ return;
34
+ context.report({ data: { source }, messageId: "dialectOutsideSeam", node });
35
+ };
36
+ const checkText = (text, node) => {
37
+ if (atTheSeam || inTests)
38
+ return;
39
+ const word = postgresWords.exec(text)?.[1];
40
+ if (word === undefined)
41
+ return;
42
+ context.report({ data: { word }, messageId: "postgresOutsideDialect", node });
43
+ };
44
+ return mergeVisitors(onImportSource(checkImport, { typeOnly: "visit" }), {
45
+ TaggedTemplateExpression(node) {
46
+ if (node.tag?.type !== "Identifier" || node.tag.name !== "sql")
47
+ return;
48
+ checkText((node.quasi?.quasis ?? []).map((part) => part.value?.raw ?? "").join(" "), node);
49
+ },
50
+ CallExpression(node) {
51
+ const callee = node.callee;
52
+ if (callee?.type !== "MemberExpression")
53
+ return;
54
+ if (callee.object?.name !== "sql" || callee.property?.name !== "raw")
55
+ return;
56
+ const [first] = node.arguments ?? [];
57
+ if (first?.type !== "Literal" || typeof first.value !== "string")
58
+ return;
59
+ checkText(first.value, node);
60
+ }
61
+ });
62
+ },
63
+ fixShape: `Dialect-typed names (\`drizzle-orm/pg-core\`, \`drizzle-orm/sqlite-core\`, \`drizzle-orm/d1\`, …) are
64
+ imported only at the seam the \`seam\` option names (regex sources over the filename): the schema, the
65
+ dialect module, the client, the drivers, and drizzle-kit's configs. Everywhere else, use the
66
+ dialect-free aliases the seam exports (\`Database\`, \`AnyColumn\`, \`rowsOf\`) so the same query compiles
67
+ against either engine. A Postgres function spelled outside the seam is named in the dialect module and
68
+ called from there; \`exempt\` (default: the test paths) is where that check stays quiet. Enabled without
69
+ \`seam\` the rule reports that once a run and judges nothing.`,
70
+ meta: {
71
+ docs: {
72
+ description: "The dialect and the driver are named at one seam — the files the `seam` option names (the schema, dialect.ts, client.ts, the drivers, the drizzle-kit configs); nothing else imports a dialect or driver module, and nothing outside the seam spells a Postgres function. Enabled without `seam` the rule reports that once a run."
73
+ },
74
+ messages: {
75
+ dialectOutsideSeam: 'This file imports "{{source}}". A dialect or driver module is named only at the seam (the schema, dialect.ts, client.ts, the drivers) — use the neutral types and the cell port the seam exports instead.',
76
+ postgresOutsideDialect: '"{{word}}" is Postgres, not SQL. Name the fragment in the dialect module and call it from here, so the query reads as the question it asks.'
77
+ },
78
+ schema: optionsBag({
79
+ exempt: PATTERNS,
80
+ seam: PATTERNS
81
+ }),
82
+ type: "problem"
83
+ },
84
+ probe(options) {
85
+ const seam = patternsOf(bagOf(options).seam);
86
+ const outside = ["geonosis-probe-dialect.ts", "src/geonosis-probe-dialect.ts"].find((path) => !anyOf(seam, path));
87
+ if (outside === undefined) {
88
+ throw new Error(`biological-architecture/${RULE}: every path a probe could be written at is inside \`seam\` (${seam.join(", ")}), so there is nowhere left for this rule to speak.`);
89
+ }
90
+ return {
91
+ path: outside,
92
+ source: `import { pgTable } from 'drizzle-orm/pg-core'
93
+
94
+ export const geonosisProbe = pgTable
95
+ `
96
+ };
97
+ }
98
+ };
99
+ var dialect_through_the_seam_default = dialectThroughTheSeam;
100
+ export {
101
+ dialect_through_the_seam_default as default
102
+ };
@@ -0,0 +1,78 @@
1
+ import { type RuleContextWithOptions } from './lib/context.js';
2
+ import { type AnyNode } from './lib/nodes.js';
3
+ import { type Probe } from './lib/probe.js';
4
+ interface Options {
5
+ /** The config key that marks a workflow durable. Default: the engine's `execution`. */
6
+ durableKey?: string;
7
+ /** What that key must say. Default: the engine's `'durable'`. */
8
+ durableValue?: boolean | string;
9
+ /** The identifiers a body reaches the outside world through — this repo's ports. */
10
+ ports?: string[];
11
+ /**
12
+ * The receivers whose calls the ENGINE has already made steps of — `stepsOf`' proxy over a module
13
+ * of bound queries. A call through one is journalled under its own name and answered from the
14
+ * record on a replay, so it is exactly what this rule asks for rather than what it forbids.
15
+ */
16
+ stepProxies?: string[];
17
+ /** The call names that define a step. Default: `createStep`. */
18
+ stepFactories?: string[];
19
+ /** The call names that define a workflow. Default: `createWorkflow`. */
20
+ workflowFactories?: string[];
21
+ }
22
+ declare const durableBodyReadsThroughSteps: {
23
+ create(context: RuleContextWithOptions<Options>): {
24
+ CallExpression(node: AnyNode): void;
25
+ };
26
+ fixShape: string;
27
+ meta: {
28
+ docs: {
29
+ description: string;
30
+ };
31
+ messages: {
32
+ freeRead: string;
33
+ };
34
+ schema: {
35
+ additionalProperties: boolean;
36
+ properties: {
37
+ durableKey: {
38
+ type: 'string';
39
+ };
40
+ durableValue: {
41
+ type: readonly ["boolean", "string"];
42
+ };
43
+ ports: {
44
+ items: {
45
+ type: 'string';
46
+ };
47
+ type: 'array';
48
+ };
49
+ stepFactories: {
50
+ items: {
51
+ type: 'string';
52
+ };
53
+ type: 'array';
54
+ };
55
+ stepProxies: {
56
+ items: {
57
+ type: 'string';
58
+ };
59
+ type: 'array';
60
+ };
61
+ workflowFactories: {
62
+ items: {
63
+ type: 'string';
64
+ };
65
+ type: 'array';
66
+ };
67
+ };
68
+ type: 'object';
69
+ }[];
70
+ type: 'problem';
71
+ };
72
+ /**
73
+ * A durable body with one free read and no step at all: a step would be the remedy, and a probe
74
+ * that carried one would be claiming a finding from the shape the rule accepts.
75
+ */
76
+ probe(options: readonly unknown[]): Probe;
77
+ };
78
+ export default durableBodyReadsThroughSteps;