@geonosis/oxlint-plugin-biological-architecture 3.0.0 → 3.2.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/CHANGELOG.md +50 -0
- package/README.md +48 -5
- package/RULES.md +201 -98
- package/corpus/.oxlintrc.json +6 -0
- package/corpus/data/bad-dynamic-orm.ts +5 -0
- package/corpus/data/bad-type-only-orm.ts +3 -0
- package/corpus/e2e/bad-list-response.spec.ts +5 -0
- package/corpus/e2e/bad-timeout.spec.ts +5 -0
- package/corpus/e2e/good-detail-response.spec.ts +5 -0
- package/corpus/e2e/good-state.spec.ts +5 -0
- package/corpus/icon-controls/bad-unnamed-icon-button.tsx +8 -0
- package/corpus/icon-controls/good-named-icon-button.tsx +19 -0
- package/corpus/manifest.json +3 -0
- package/corpus/tissues/bad-awaits-data.tsx +8 -0
- package/corpus/tissues/bad-fetches-data.tsx +7 -0
- package/corpus/tissues/good-takes-products.tsx +5 -0
- package/dist/discover-atoms.d.ts +2 -37
- package/dist/discover-atoms.js +1 -104
- package/dist/index.d.ts +3 -12
- package/dist/index.js +19 -174
- package/dist/presets.d.ts +11 -19
- package/dist/presets.js +89 -3
- package/dist/registry.d.ts +2 -0
- package/dist/registry.js +172 -0
- package/dist/rules/atom-no-deps.d.ts +1 -0
- package/dist/rules/atom-no-deps.js +10 -11
- package/dist/rules/boundary-time-is-primitive.d.ts +0 -7
- package/dist/rules/cell-must-be-stateful.d.ts +2 -2
- package/dist/rules/cell-must-be-stateful.js +24 -23
- package/dist/rules/cell-must-not-compose-cell.d.ts +1 -0
- package/dist/rules/cell-must-not-compose-cell.js +10 -3
- package/dist/rules/cell-no-cross-feature-organelles.d.ts +1 -0
- package/dist/rules/cell-no-cross-feature-organelles.js +7 -0
- package/dist/rules/cell-no-tissues.d.ts +1 -0
- package/dist/rules/cell-no-tissues.js +11 -7
- package/dist/rules/cells-folder-index-is-barrel.d.ts +12 -10
- package/dist/rules/cells-folder-index-is-barrel.js +66 -34
- package/dist/rules/compound-must-be-stateless.d.ts +2 -0
- package/dist/rules/compound-must-be-stateless.js +24 -26
- package/dist/rules/compound-no-stores.d.ts +1 -0
- package/dist/rules/compound-no-stores.js +12 -10
- package/dist/rules/constants-in-constants-file.d.ts +2 -8
- package/dist/rules/constants-in-constants-file.js +5 -6
- package/dist/rules/dialect-through-the-seam.d.ts +0 -13
- package/dist/rules/durable-body-reads-through-steps.d.ts +0 -14
- package/dist/rules/durable-body-reads-through-steps.js +8 -3
- package/dist/rules/effect-hook-naming.d.ts +9 -11
- package/dist/rules/effect-hook-naming.js +33 -31
- package/dist/rules/emit-declares-attempts.d.ts +0 -17
- package/dist/rules/emit-declares-attempts.js +5 -10
- package/dist/rules/font-roles-only.d.ts +0 -18
- package/dist/rules/font-roles-only.js +19 -22
- package/dist/rules/hermetic-e2e-no-wall-clock-waits.d.ts +53 -0
- package/dist/rules/hermetic-e2e-no-wall-clock-waits.js +100 -0
- package/dist/rules/icon-control-has-name.d.ts +53 -0
- package/dist/rules/icon-control-has-name.js +71 -0
- package/dist/rules/layer-walls.d.ts +0 -25
- package/dist/rules/layer-walls.js +7 -13
- package/dist/rules/lib/atom-map.d.ts +24 -0
- package/dist/rules/lib/atom-map.js +16 -0
- package/dist/rules/lib/cells-barrel.d.ts +0 -1
- package/dist/rules/lib/colors.d.ts +0 -19
- package/dist/rules/lib/colors.js +10 -243
- package/dist/rules/lib/comments.d.ts +19 -0
- package/dist/rules/lib/comments.js +5 -0
- package/dist/rules/lib/context.d.ts +0 -10
- package/dist/rules/lib/direction-rule.d.ts +3 -21
- package/dist/rules/lib/direction-rule.js +12 -3
- package/dist/rules/lib/doors.d.ts +2 -18
- package/dist/rules/lib/doors.js +79 -41
- package/dist/rules/lib/hooks.d.ts +2 -17
- package/dist/rules/lib/hooks.js +14 -9
- package/dist/rules/lib/imports.d.ts +2 -23
- package/dist/rules/lib/imports.js +7 -2
- package/dist/rules/lib/jsx.d.ts +31 -0
- package/dist/rules/lib/jsx.js +11 -0
- package/dist/rules/lib/landing.d.ts +0 -40
- package/dist/rules/lib/literal-call.d.ts +1 -0
- package/dist/rules/lib/literal-call.js +2 -0
- package/dist/rules/lib/module-text.d.ts +3 -27
- package/dist/rules/lib/module-text.js +19 -4
- package/dist/rules/lib/mutations.d.ts +0 -14
- package/dist/rules/lib/nodes.d.ts +1 -18
- package/dist/rules/lib/nodes.js +1 -0
- package/dist/rules/lib/objects.d.ts +2 -11
- package/dist/rules/lib/objects.js +21 -0
- package/dist/rules/lib/options.d.ts +2 -52
- package/dist/rules/lib/options.js +44 -18
- package/dist/rules/lib/package-imports.d.ts +0 -5
- package/dist/rules/lib/package-imports.js +4 -10
- package/dist/rules/lib/paths.d.ts +15 -41
- package/dist/rules/lib/paths.js +34 -1
- package/dist/rules/lib/pattern-match.d.ts +2 -12
- package/dist/rules/lib/pattern-match.js +5 -1
- package/dist/rules/lib/probe.d.ts +0 -43
- package/dist/rules/lib/reexports.d.ts +0 -18
- package/dist/rules/lib/render-tracking.d.ts +0 -8
- package/dist/rules/lib/shared-features.d.ts +0 -8
- package/dist/rules/lib/shared-features.js +2 -9
- package/dist/rules/lib/state-sources.d.ts +8 -0
- package/dist/rules/lib/state-sources.js +55 -0
- package/dist/rules/lib/steps.d.ts +3 -5
- package/dist/rules/lib/steps.js +6 -1
- package/dist/rules/lib/styling.d.ts +9 -42
- package/dist/rules/lib/styling.js +36 -10
- package/dist/rules/lib/tiers.d.ts +11 -24
- package/dist/rules/lib/tiers.js +41 -1
- package/dist/rules/lib/tissue-entrypoint.d.ts +0 -1
- package/dist/rules/lib/tissue-entrypoint.js +4 -5
- package/dist/rules/lib/tsconfig-paths.d.ts +0 -4
- package/dist/rules/lib/tsconfig-paths.js +47 -15
- package/dist/rules/lib/type-members.d.ts +6 -0
- package/dist/rules/lib/type-members.js +165 -0
- package/dist/rules/lib/visitors.d.ts +0 -22
- package/dist/rules/lib/workspace-members.d.ts +2 -0
- package/dist/rules/lib/workspace-members.js +115 -0
- package/dist/rules/list-take-without-order.js +2 -8
- package/dist/rules/max-comment-density.d.ts +3 -18
- package/dist/rules/max-comment-density.js +3 -3
- package/dist/rules/molecule-atoms-only.d.ts +1 -0
- package/dist/rules/molecule-atoms-only.js +11 -7
- package/dist/rules/molecule-must-compose.d.ts +3 -1
- package/dist/rules/molecule-must-compose.js +17 -21
- package/dist/rules/mutating-step-declares-retry.d.ts +2 -7
- package/dist/rules/mutating-step-declares-retry.js +29 -11
- package/dist/rules/named-when-block.d.ts +0 -2
- package/dist/rules/named-when-block.js +2 -8
- package/dist/rules/next-route-segment-is-thin-delegate.d.ts +4 -19
- package/dist/rules/next-route-segment-is-thin-delegate.js +47 -71
- package/dist/rules/no-brand-names.d.ts +0 -9
- package/dist/rules/no-brand-names.js +1 -1
- package/dist/rules/no-card-shaped-div.d.ts +4 -42
- package/dist/rules/no-card-shaped-div.js +12 -4
- package/dist/rules/no-cross-feature-stores.d.ts +1 -0
- package/dist/rules/no-cross-feature-stores.js +9 -5
- package/dist/rules/no-d1-transaction.d.ts +2 -5
- package/dist/rules/no-d1-transaction.js +7 -0
- package/dist/rules/no-date-string-compare.d.ts +2 -0
- package/dist/rules/no-date-string-compare.js +14 -8
- package/dist/rules/no-duplicate-jsx-patterns.d.ts +2 -0
- package/dist/rules/no-duplicate-jsx-patterns.js +16 -1
- package/dist/rules/no-generic-utility-package.d.ts +48 -2
- package/dist/rules/no-generic-utility-package.js +48 -9
- package/dist/rules/no-hook-in-component-disguise.d.ts +27 -13
- package/dist/rules/no-hook-in-component-disguise.js +38 -30
- package/dist/rules/no-index-count-as-exact.d.ts +0 -3
- package/dist/rules/no-inert-hidden-jsx.d.ts +4 -21
- package/dist/rules/no-inert-hidden-jsx.js +13 -12
- package/dist/rules/no-inline-data-in-jsx.d.ts +2 -0
- package/dist/rules/no-inline-data-in-jsx.js +17 -2
- package/dist/rules/no-invalid-feature-folders.d.ts +2 -0
- package/dist/rules/no-invalid-feature-folders.js +12 -4
- package/dist/rules/no-loader-side-effect-in-index-file.d.ts +0 -7
- package/dist/rules/no-logic-in-component-files.d.ts +6 -5
- package/dist/rules/no-logic-in-component-files.js +52 -31
- package/dist/rules/no-mock-db-in-integration.d.ts +0 -15
- package/dist/rules/no-mock-db-in-integration.js +3 -4
- package/dist/rules/no-orm-outside-db.d.ts +1 -0
- package/dist/rules/no-orm-outside-db.js +9 -7
- package/dist/rules/no-raw-html-atoms.d.ts +3 -22
- package/dist/rules/no-raw-html-atoms.js +5 -17
- package/dist/rules/no-raw-sql-outside-allowed.d.ts +2 -0
- package/dist/rules/no-raw-sql-outside-allowed.js +10 -15
- package/dist/rules/no-react-namespace.d.ts +2 -0
- package/dist/rules/no-react-namespace.js +13 -0
- package/dist/rules/no-renamed-html-props.d.ts +2 -0
- package/dist/rules/no-renamed-html-props.js +11 -2
- package/dist/rules/no-render-prop-reader.d.ts +4 -20
- package/dist/rules/no-render-prop-reader.js +13 -5
- package/dist/rules/no-runtime-dead-affordance.d.ts +3 -26
- package/dist/rules/no-runtime-dead-affordance.js +7 -20
- package/dist/rules/no-swallowed-catch.js +2 -8
- package/dist/rules/no-trivial-wrapper-component.d.ts +4 -19
- package/dist/rules/no-trivial-wrapper-component.js +24 -33
- package/dist/rules/no-ts-in-bio-folders.d.ts +2 -13
- package/dist/rules/no-ts-in-bio-folders.js +9 -7
- package/dist/rules/no-type-definitions-in-components.d.ts +2 -0
- package/dist/rules/no-type-definitions-in-components.js +12 -1
- package/dist/rules/no-unregistered-plugin-dir.d.ts +0 -41
- package/dist/rules/no-unregistered-plugin-dir.js +1 -9
- package/dist/rules/no-void-port.d.ts +2 -4
- package/dist/rules/no-void-port.js +7 -0
- package/dist/rules/organelle-dependency.d.ts +1 -0
- package/dist/rules/organelle-dependency.js +10 -8
- package/dist/rules/organelle-single-source.d.ts +2 -0
- package/dist/rules/organelle-single-source.js +58 -9
- package/dist/rules/plugin-route-namespaced.d.ts +0 -11
- package/dist/rules/plugin-route-namespaced.js +2 -2
- package/dist/rules/queries-require-org-scope.d.ts +0 -3
- package/dist/rules/queries-require-org-scope.js +3 -10
- package/dist/rules/queue-loop-is-the-library.js +3 -2
- package/dist/rules/route-no-inline-mutation.d.ts +1 -25
- package/dist/rules/route-no-inline-mutation.js +2 -7
- package/dist/rules/router-schema-parity.d.ts +0 -9
- package/dist/rules/router-schema-parity.js +5 -9
- package/dist/rules/service-name-not-reserved.d.ts +0 -3
- package/dist/rules/service-name-not-reserved.js +1 -34
- package/dist/rules/ssot-no-inline-facts.js +2 -5
- package/dist/rules/ssot-no-process-env.js +4 -12
- package/dist/rules/step-declares-compensation-or-none.d.ts +2 -1
- package/dist/rules/step-declares-compensation-or-none.js +17 -1
- package/dist/rules/step-opens-its-own-cell.d.ts +2 -25
- package/dist/rules/step-opens-its-own-cell.js +8 -13
- package/dist/rules/store-route-scopes-tenant-data.js +8 -26
- package/dist/rules/subpath-over-barrel.d.ts +2 -10
- package/dist/rules/subpath-over-barrel.js +48 -9
- package/dist/rules/subscriber-declares-id.d.ts +0 -2
- package/dist/rules/subscriber-declares-id.js +2 -8
- package/dist/rules/tables-declare-their-plane.d.ts +0 -2
- package/dist/rules/tables-declare-their-plane.js +4 -10
- package/dist/rules/tenant-tables-carry-org-id.d.ts +1 -4
- package/dist/rules/tenant-tables-carry-org-id.js +5 -12
- package/dist/rules/test-no-clock-polling-for-promise-flips.d.ts +2 -0
- package/dist/rules/test-no-clock-polling-for-promise-flips.js +18 -1
- package/dist/rules/time-through-the-door.d.ts +0 -24
- package/dist/rules/time-through-the-door.js +5 -5
- package/dist/rules/tissue-must-compose.d.ts +4 -2
- package/dist/rules/tissue-must-compose.js +25 -20
- package/dist/rules/tissue-no-data-props.d.ts +56 -0
- package/dist/rules/tissue-no-data-props.js +114 -32
- package/dist/rules/tissue-no-fetch.d.ts +26 -0
- package/dist/rules/tissue-no-fetch.js +57 -0
- package/dist/rules/tissue-no-hooks.d.ts +20 -22
- package/dist/rules/tissue-no-hooks.js +22 -4
- package/dist/rules/tissue-no-organelles.d.ts +1 -0
- package/dist/rules/tissue-no-organelles.js +9 -1
- package/dist/rules/tissue-no-stores.d.ts +1 -0
- package/dist/rules/tissue-no-stores.js +9 -1
- package/dist/rules/todo-names-its-plan.d.ts +1 -28
- package/dist/rules/todo-names-its-plan.js +2 -2
- package/dist/rules/tokens-only-colors.d.ts +2 -37
- package/dist/rules/tokens-only-colors.js +33 -18
- package/dist/rules/unique-query-step-name.d.ts +0 -7
- package/dist/rules/unique-query-step-name.js +6 -10
- package/dist/rules/worker-handles-are-scoped.d.ts +11 -8
- package/dist/rules/worker-handles-are-scoped.js +12 -5
- package/dist/rules/zustand-v5-best-practices.d.ts +8 -18
- package/dist/rules/zustand-v5-best-practices.js +32 -56
- package/package.json +2 -2
|
@@ -6,40 +6,7 @@ import { bagOf, firstOf } from "./lib/probe.js";
|
|
|
6
6
|
import { optionsBag, STRINGS } from "./lib/schema.js";
|
|
7
7
|
var RULE = "service-name-not-reserved";
|
|
8
8
|
var DEFAULT_MODULE_FACTORIES = ["Module"];
|
|
9
|
-
var MODULES_2_19 =
|
|
10
|
-
"analytics",
|
|
11
|
-
"api_key",
|
|
12
|
-
"auth",
|
|
13
|
-
"cache",
|
|
14
|
-
"caching",
|
|
15
|
-
"cart",
|
|
16
|
-
"currency",
|
|
17
|
-
"customer",
|
|
18
|
-
"event_bus",
|
|
19
|
-
"file",
|
|
20
|
-
"fulfillment",
|
|
21
|
-
"index",
|
|
22
|
-
"inventory",
|
|
23
|
-
"link_modules",
|
|
24
|
-
"locking",
|
|
25
|
-
"notification",
|
|
26
|
-
"order",
|
|
27
|
-
"payment",
|
|
28
|
-
"pricing",
|
|
29
|
-
"product",
|
|
30
|
-
"promotion",
|
|
31
|
-
"rbac",
|
|
32
|
-
"region",
|
|
33
|
-
"sales_channel",
|
|
34
|
-
"search",
|
|
35
|
-
"settings",
|
|
36
|
-
"stock_location",
|
|
37
|
-
"store",
|
|
38
|
-
"tax",
|
|
39
|
-
"translation",
|
|
40
|
-
"user",
|
|
41
|
-
"workflows"
|
|
42
|
-
];
|
|
9
|
+
var MODULES_2_19 = "analytics api_key auth cache caching cart currency customer event_bus file fulfillment index inventory link_modules locking notification order payment pricing product promotion rbac region sales_channel search settings stock_location store tax translation user workflows".split(" ");
|
|
43
10
|
var serviceNameNotReserved = {
|
|
44
11
|
create(context) {
|
|
45
12
|
const factories = new Set(requireOption(RULE, context.options?.[0]?.moduleFactories ?? DEFAULT_MODULE_FACTORIES, "moduleFactories"));
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
// src/rules/ssot-no-inline-facts.ts
|
|
2
2
|
import { PATTERNS, patternsOf } from "./lib/options.js";
|
|
3
|
-
import { anyOf, normalise } from "./lib/paths.js";
|
|
3
|
+
import { anyOf, HARNESS_PATHS, normalise } from "./lib/paths.js";
|
|
4
4
|
import { optionsBag, STRINGS } from "./lib/schema.js";
|
|
5
5
|
var CONVENTIONAL_OWNERS = [
|
|
6
6
|
String.raw`/constants\.ts$`,
|
|
7
7
|
String.raw`/lib/env\.ts$`,
|
|
8
8
|
String.raw`/lib/routes\.ts$`,
|
|
9
|
-
|
|
10
|
-
String.raw`/scripts/`,
|
|
11
|
-
String.raw`/testing/`,
|
|
9
|
+
...HARNESS_PATHS,
|
|
12
10
|
String.raw`/test/`,
|
|
13
|
-
String.raw`/seeds/`,
|
|
14
11
|
String.raw`/migrations/`,
|
|
15
12
|
String.raw`\.config\.(ts|mjs|js)$`
|
|
16
13
|
];
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
// src/rules/ssot-no-process-env.ts
|
|
2
2
|
import { PATTERNS, patternsOf } from "./lib/options.js";
|
|
3
|
-
import { anyOf, normalise } from "./lib/paths.js";
|
|
3
|
+
import { anyOf, HARNESS_PATHS, normalise } from "./lib/paths.js";
|
|
4
|
+
import { optionsBag } from "./lib/schema.js";
|
|
4
5
|
var CONVENTIONAL_DOORS = [
|
|
5
6
|
String.raw`/lib/env\.ts$`,
|
|
6
7
|
String.raw`\.config\.(ts|mjs|js)$`,
|
|
7
|
-
|
|
8
|
-
String.raw`/__tests__/`,
|
|
9
|
-
String.raw`/testing/`,
|
|
8
|
+
...HARNESS_PATHS,
|
|
10
9
|
String.raw`/test/`,
|
|
11
|
-
String.raw`/seeds/`,
|
|
12
10
|
String.raw`\.(test|spec)\.[cm]?[jt]sx?$`
|
|
13
11
|
];
|
|
14
12
|
var isProcessEnv = (node) => node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env";
|
|
@@ -35,13 +33,7 @@ it in \`doors\` (regex sources over the path); tests, scripts, seeds and config
|
|
|
35
33
|
messages: {
|
|
36
34
|
envOutsideDoor: "process.env is read outside the env door. Add the variable to the env module (lib/env.ts, or the `doors` this repo names) and import `env` from there."
|
|
37
35
|
},
|
|
38
|
-
schema:
|
|
39
|
-
{
|
|
40
|
-
additionalProperties: false,
|
|
41
|
-
properties: { doors: PATTERNS },
|
|
42
|
-
type: "object"
|
|
43
|
-
}
|
|
44
|
-
],
|
|
36
|
+
schema: optionsBag({ doors: PATTERNS }),
|
|
45
37
|
type: "problem"
|
|
46
38
|
}
|
|
47
39
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type RuleContextWithOptions } from './lib/context.js';
|
|
2
2
|
import type { AnyNode } from './lib/nodes.js';
|
|
3
|
+
import { type Probe } from './lib/probe.js';
|
|
3
4
|
interface Options {
|
|
4
|
-
/** The call names that define a step. Default: Medusa's `createStep`. */
|
|
5
5
|
stepFactories?: string[];
|
|
6
6
|
}
|
|
7
7
|
declare const stepDeclaresCompensationOrNone: {
|
|
@@ -30,5 +30,6 @@ declare const stepDeclaresCompensationOrNone: {
|
|
|
30
30
|
}[];
|
|
31
31
|
type: 'problem';
|
|
32
32
|
};
|
|
33
|
+
probe(options: readonly unknown[]): Probe;
|
|
33
34
|
};
|
|
34
35
|
export default stepDeclaresCompensationOrNone;
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
// src/rules/step-declares-compensation-or-none.ts
|
|
2
2
|
import { valueOf } from "./lib/objects.js";
|
|
3
|
+
import { bagOf, firstOf } from "./lib/probe.js";
|
|
3
4
|
import { optionsBag, STRINGS } from "./lib/schema.js";
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
argumentsOf,
|
|
7
|
+
configOf,
|
|
8
|
+
DEFAULT_STEP_FACTORIES,
|
|
9
|
+
isStepCall,
|
|
10
|
+
stepFactoriesOf,
|
|
11
|
+
stepNameOf
|
|
12
|
+
} from "./lib/steps.js";
|
|
5
13
|
var RULE = "step-declares-compensation-or-none";
|
|
6
14
|
var NO_COMPENSATION = "noCompensation";
|
|
7
15
|
var WITH_COMPENSATION = 3;
|
|
@@ -44,6 +52,14 @@ variable — passes, since the flag may be in the base. Configure \`stepFactorie
|
|
|
44
52
|
stepFactories: STRINGS
|
|
45
53
|
}),
|
|
46
54
|
type: "problem"
|
|
55
|
+
},
|
|
56
|
+
probe(options) {
|
|
57
|
+
const factory = firstOf(bagOf(options).stepFactories, DEFAULT_STEP_FACTORIES);
|
|
58
|
+
return {
|
|
59
|
+
path: "geonosis-probe-step.ts",
|
|
60
|
+
source: `export const geonosisProbe = ${factory}({ name: 'geonosis-probe' }, async () => 1)
|
|
61
|
+
`
|
|
62
|
+
};
|
|
47
63
|
}
|
|
48
64
|
};
|
|
49
65
|
var step_declares_compensation_or_none_default = stepDeclaresCompensationOrNone;
|
|
@@ -1,37 +1,19 @@
|
|
|
1
1
|
import { type RuleContextWithOptions } from './lib/context.js';
|
|
2
|
+
import { type ModuleNode } from './lib/imports.js';
|
|
2
3
|
import { type Probe } from './lib/probe.js';
|
|
3
4
|
interface Options {
|
|
4
|
-
/** The module specifier the engine's `step` comes from — the OLD spelling, off unless set. */
|
|
5
5
|
engine?: string;
|
|
6
|
-
/** The calls that build a durable host's flow. Default: the engine's own two. */
|
|
7
6
|
hosts?: string[];
|
|
8
|
-
/** The key a run scope is declared under. Default: `perStep`. */
|
|
9
7
|
scopeKey?: string;
|
|
10
|
-
/** Regex source, tested against the normalised filename: the tree whose steps must be durable. */
|
|
11
8
|
within?: string;
|
|
12
|
-
/** Regex source: the one file allowed to reach `engine`, because it opens the handle. */
|
|
13
9
|
wrapper?: string;
|
|
14
10
|
}
|
|
15
|
-
interface ImportNode {
|
|
16
|
-
importKind?: string;
|
|
17
|
-
loc?: unknown;
|
|
18
|
-
source: {
|
|
19
|
-
value: string;
|
|
20
|
-
};
|
|
21
|
-
specifiers?: {
|
|
22
|
-
imported?: {
|
|
23
|
-
name?: string;
|
|
24
|
-
};
|
|
25
|
-
importKind?: string;
|
|
26
|
-
type: string;
|
|
27
|
-
}[];
|
|
28
|
-
}
|
|
29
11
|
declare const stepOpensItsOwnCell: {
|
|
30
12
|
create(context: RuleContextWithOptions<Options>): {
|
|
31
13
|
Program?: undefined;
|
|
32
14
|
ImportDeclaration?: undefined;
|
|
33
15
|
} | {
|
|
34
|
-
ImportDeclaration(node:
|
|
16
|
+
ImportDeclaration(node: ModuleNode): void;
|
|
35
17
|
Program(node: unknown): void;
|
|
36
18
|
};
|
|
37
19
|
fixShape: string;
|
|
@@ -72,11 +54,6 @@ declare const stepOpensItsOwnCell: {
|
|
|
72
54
|
}[];
|
|
73
55
|
type: 'problem';
|
|
74
56
|
};
|
|
75
|
-
/**
|
|
76
|
-
* The probe is built for the spelling the OPTIONS select. A repo that named an `engine` is on the
|
|
77
|
-
* older one and is exercised by a saga reaching past its wrapper — which is also why the path has
|
|
78
|
-
* to land inside `within` and outside `wrapper`, the one file allowed to make that import.
|
|
79
|
-
*/
|
|
80
57
|
probe(options: readonly unknown[]): Probe;
|
|
81
58
|
};
|
|
82
59
|
export default stepOpensItsOwnCell;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// src/rules/step-opens-its-own-cell.ts
|
|
2
|
+
import { sourceOf } from "./lib/imports.js";
|
|
2
3
|
import { calleeNameOf, walkNode } from "./lib/nodes.js";
|
|
3
|
-
import { propertiesOf } from "./lib/objects.js";
|
|
4
|
+
import { keyNameOf, propertiesOf } from "./lib/objects.js";
|
|
4
5
|
import { requireOption } from "./lib/options.js";
|
|
5
|
-
import { normalise } from "./lib/paths.js";
|
|
6
|
+
import { normalise, fileOf } from "./lib/paths.js";
|
|
7
|
+
import { readableSource } from "./lib/pattern-match.js";
|
|
6
8
|
import { bagOf, firstOf, pathMatching } from "./lib/probe.js";
|
|
7
9
|
import { optionsBag, STRING, STRINGS } from "./lib/schema.js";
|
|
8
10
|
var RULE = "step-opens-its-own-cell";
|
|
@@ -10,7 +12,6 @@ var STEP = "step";
|
|
|
10
12
|
var ACCEPTS = "{ within: string (required), hosts: string[], scopeKey: string, engine: string, wrapper: string }";
|
|
11
13
|
var DEFAULT_HOSTS = ["createRuntime", "entrypointFor"];
|
|
12
14
|
var DEFAULT_SCOPE_KEY = "perStep";
|
|
13
|
-
var readable = (pattern) => pattern.replaceAll("\\", "").replace(/\$$/, "");
|
|
14
15
|
var isHostCall = (node, hosts) => {
|
|
15
16
|
const callee = calleeNameOf(node);
|
|
16
17
|
return callee !== null && hosts.has(callee);
|
|
@@ -25,16 +26,10 @@ var opaque = (call, hosts) => {
|
|
|
25
26
|
});
|
|
26
27
|
return unreadable;
|
|
27
28
|
};
|
|
28
|
-
var namesKey = (node, key) => {
|
|
29
|
-
if (node.type !== "Property")
|
|
30
|
-
return false;
|
|
31
|
-
const named = node.key;
|
|
32
|
-
return named?.type === "Identifier" && named.name === key || named?.type === "Literal" && named.value === key;
|
|
33
|
-
};
|
|
34
29
|
var declaresScope = (call, scopeKey) => {
|
|
35
30
|
let declared = false;
|
|
36
31
|
walkNode(call, (inner) => {
|
|
37
|
-
if (
|
|
32
|
+
if (inner.type === "Property" && keyNameOf(inner) === scopeKey)
|
|
38
33
|
declared = true;
|
|
39
34
|
});
|
|
40
35
|
return declared;
|
|
@@ -49,7 +44,7 @@ var stepOpensItsOwnCell = {
|
|
|
49
44
|
const filename = normalise(context.filename);
|
|
50
45
|
if (!new RegExp(within).test(filename))
|
|
51
46
|
return {};
|
|
52
|
-
const file = filename
|
|
47
|
+
const file = fileOf(filename);
|
|
53
48
|
const isWrapper = wrapper !== undefined && new RegExp(wrapper).test(filename);
|
|
54
49
|
return {
|
|
55
50
|
ImportDeclaration(node) {
|
|
@@ -57,7 +52,7 @@ var stepOpensItsOwnCell = {
|
|
|
57
52
|
return;
|
|
58
53
|
if (node.importKind === "type")
|
|
59
54
|
return;
|
|
60
|
-
if (node
|
|
55
|
+
if (sourceOf(node) !== engine)
|
|
61
56
|
return;
|
|
62
57
|
for (const specifier of node.specifiers ?? []) {
|
|
63
58
|
if (specifier.type !== "ImportSpecifier")
|
|
@@ -70,7 +65,7 @@ var stepOpensItsOwnCell = {
|
|
|
70
65
|
data: {
|
|
71
66
|
engine,
|
|
72
67
|
file,
|
|
73
|
-
wrapper: wrapper === undefined ? "the repo's own step wrapper" :
|
|
68
|
+
wrapper: wrapper === undefined ? "the repo's own step wrapper" : readableSource(wrapper)
|
|
74
69
|
},
|
|
75
70
|
messageId: "engineStep",
|
|
76
71
|
node
|
|
@@ -1,38 +1,21 @@
|
|
|
1
1
|
// src/rules/store-route-scopes-tenant-data.ts
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
2
|
import { resolveRelative, sourceOf } from "./lib/imports.js";
|
|
4
|
-
import {
|
|
3
|
+
import { fileAt, readSource } from "./lib/module-text.js";
|
|
5
4
|
import { walkNode } from "./lib/nodes.js";
|
|
5
|
+
import { keyNameOf, stringOf } from "./lib/objects.js";
|
|
6
6
|
import { normalise } from "./lib/paths.js";
|
|
7
|
+
import { escapeForRegex } from "./lib/pattern-match.js";
|
|
7
8
|
import { bagOf, firstOf, pathMatching } from "./lib/probe.js";
|
|
8
9
|
import { optionsBag, STRINGS } from "./lib/schema.js";
|
|
9
10
|
var ENTITIES = ["cart", "claim", "company", "customer", "employee", "order", "quote"];
|
|
10
11
|
var ROUTES = [String.raw`/api/store/.*/route\.tsx?$`];
|
|
11
12
|
var SCOPING_CALLS = ["auth_context", "actor_id", "customer_id", "company_id"];
|
|
12
13
|
var ENTITY_KEY = "entity";
|
|
13
|
-
var SUFFIXES = ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js"];
|
|
14
|
-
var keyNameOf = (node) => {
|
|
15
|
-
const key = node.key;
|
|
16
|
-
if (key?.type === "Identifier" && typeof key.name === "string")
|
|
17
|
-
return key.name;
|
|
18
|
-
return key?.type === "Literal" && typeof key.value === "string" ? key.value : null;
|
|
19
|
-
};
|
|
20
|
-
var stringValueOf = (node) => {
|
|
21
|
-
const value = node.value;
|
|
22
|
-
return value?.type === "Literal" && typeof value.value === "string" ? value.value : null;
|
|
23
|
-
};
|
|
24
14
|
var siblingSourceOf = (importer, source) => {
|
|
25
15
|
if (!source.startsWith("."))
|
|
26
16
|
return null;
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
return stripComments(readFileSync(`${landed}${suffix}`, "utf8"));
|
|
31
|
-
} catch {
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return null;
|
|
17
|
+
const file = fileAt(resolveRelative(importer, source));
|
|
18
|
+
return file === undefined ? null : readSource(file) ?? null;
|
|
36
19
|
};
|
|
37
20
|
var MODULE_NODES = new Set([
|
|
38
21
|
"CallExpression",
|
|
@@ -41,15 +24,14 @@ var MODULE_NODES = new Set([
|
|
|
41
24
|
"ImportDeclaration",
|
|
42
25
|
"ImportExpression"
|
|
43
26
|
]);
|
|
44
|
-
var escaped = (literal) => literal.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
45
27
|
var readIn = (text, entities) => {
|
|
46
28
|
for (const entity of entities) {
|
|
47
|
-
if (new RegExp(`${ENTITY_KEY}\\s*:\\s*['"\`]${
|
|
29
|
+
if (new RegExp(`${ENTITY_KEY}\\s*:\\s*['"\`]${escapeForRegex(entity)}['"\`]`).test(text))
|
|
48
30
|
return entity;
|
|
49
31
|
}
|
|
50
32
|
return null;
|
|
51
33
|
};
|
|
52
|
-
var namesAny = (text, names) => names.some((name) => new RegExp(`(?<![\\w$])${
|
|
34
|
+
var namesAny = (text, names) => names.some((name) => new RegExp(`(?<![\\w$])${escapeForRegex(name)}(?![\\w$])`).test(text));
|
|
53
35
|
var storeRouteScopesTenantData = {
|
|
54
36
|
create(context) {
|
|
55
37
|
const options = bagOf(context.options);
|
|
@@ -67,7 +49,7 @@ var storeRouteScopesTenantData = {
|
|
|
67
49
|
const sources = [];
|
|
68
50
|
walkNode(program, (node) => {
|
|
69
51
|
if (node.type === "Property" && keyNameOf(node) === ENTITY_KEY) {
|
|
70
|
-
const entity =
|
|
52
|
+
const entity = stringOf(node.value);
|
|
71
53
|
if (entity !== null && entities.includes(entity) && read === null) {
|
|
72
54
|
read = { entity, node };
|
|
73
55
|
}
|
|
@@ -6,11 +6,6 @@ type Declared = {
|
|
|
6
6
|
subpaths?: Record<string, string[]>;
|
|
7
7
|
};
|
|
8
8
|
interface Options {
|
|
9
|
-
/**
|
|
10
|
-
* A package to read differently from its exports: with `subpaths`, a hand-written map that
|
|
11
|
-
* REPLACES the one on disk; with a name alone, a package read off its exports even though it is
|
|
12
|
-
* not the repo's own.
|
|
13
|
-
*/
|
|
14
9
|
packages?: Declared[];
|
|
15
10
|
}
|
|
16
11
|
declare const subpathOverBarrel: {
|
|
@@ -24,6 +19,8 @@ declare const subpathOverBarrel: {
|
|
|
24
19
|
description: string;
|
|
25
20
|
};
|
|
26
21
|
messages: {
|
|
22
|
+
nothingResolves: string;
|
|
23
|
+
unresolvedPackage: string;
|
|
27
24
|
rootBarrel: string;
|
|
28
25
|
};
|
|
29
26
|
schema: {
|
|
@@ -55,11 +52,6 @@ declare const subpathOverBarrel: {
|
|
|
55
52
|
}[];
|
|
56
53
|
type: 'problem';
|
|
57
54
|
};
|
|
58
|
-
/**
|
|
59
|
-
* One import of one name, at the root — the smallest shape that must fire. From the hand-written
|
|
60
|
-
* map when there is one; otherwise a private package planted under `node_modules` with one door,
|
|
61
|
-
* which is exactly what the default reads.
|
|
62
|
-
*/
|
|
63
55
|
probe(options: readonly unknown[]): Probe | {
|
|
64
56
|
files: readonly Probe[];
|
|
65
57
|
};
|
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
// src/rules/subpath-over-barrel.ts
|
|
2
|
-
import {
|
|
2
|
+
import { statSync } from "node:fs";
|
|
3
|
+
import { doorsOf, nodeModulesAbove, resolvePackage } from "./lib/doors.js";
|
|
3
4
|
import { bagOf } from "./lib/probe.js";
|
|
4
5
|
import { optionsBag, STRING } from "./lib/schema.js";
|
|
6
|
+
import { hasWorkspaceAbove } from "./lib/workspace-members.js";
|
|
5
7
|
var homesOf = (declared) => new Map(Object.entries(declared.subpaths ?? {}).flatMap(([subpath, names]) => names.map((name) => [name, subpath])));
|
|
6
8
|
var read = new Map;
|
|
7
9
|
var doorsFor = (manifest) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
let stamp = 0;
|
|
11
|
+
try {
|
|
12
|
+
stamp = statSync(manifest).mtimeMs;
|
|
13
|
+
} catch {
|
|
14
|
+
stamp = 0;
|
|
15
|
+
}
|
|
16
|
+
const held = read.get(manifest);
|
|
17
|
+
if (held?.stamp === stamp)
|
|
18
|
+
return held.doors?.homes;
|
|
19
|
+
const doors = doorsOf(manifest);
|
|
20
|
+
read.set(manifest, { doors, stamp });
|
|
21
|
+
return doors?.homes;
|
|
22
|
+
};
|
|
23
|
+
var PACKAGE_ROOT = /^(?:@[^/]+\/)?[^/]+$/;
|
|
24
|
+
var told = new Set;
|
|
25
|
+
var firstTime = (key) => {
|
|
26
|
+
if (told.has(key))
|
|
27
|
+
return false;
|
|
28
|
+
told.add(key);
|
|
29
|
+
return true;
|
|
11
30
|
};
|
|
12
31
|
var namesTakenBy = (node) => {
|
|
13
32
|
if (node.importKind === "type" || node.exportKind === "type")
|
|
@@ -32,16 +51,32 @@ var namesTakenBy = (node) => {
|
|
|
32
51
|
var subpathOverBarrel = {
|
|
33
52
|
create(context) {
|
|
34
53
|
const declared = new Map((context.options?.[0]?.packages ?? []).filter((one) => typeof one.name === "string").map((one) => [one.name, one]));
|
|
35
|
-
const
|
|
54
|
+
const writtenByHand = [...declared.values()].some((one) => one.subpaths !== undefined);
|
|
55
|
+
const tell = (messageId, source, node) => {
|
|
56
|
+
if (firstTime(`${messageId}/${source}`)) {
|
|
57
|
+
context.report({ data: { package: source }, messageId, node });
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const homesFor = (source, node) => {
|
|
36
61
|
const written = declared.get(source);
|
|
37
62
|
if (written?.subpaths !== undefined)
|
|
38
63
|
return homesOf(written);
|
|
39
64
|
const found = resolvePackage(context.filename, source);
|
|
40
|
-
if (found === undefined)
|
|
65
|
+
if (found === undefined) {
|
|
66
|
+
if (written !== undefined)
|
|
67
|
+
tell("unresolvedPackage", source, node);
|
|
68
|
+
else if (!writtenByHand && PACKAGE_ROOT.test(source) && !nodeModulesAbove(context.filename) && !hasWorkspaceAbove(context.filename)) {
|
|
69
|
+
tell("nothingResolves", "", node);
|
|
70
|
+
}
|
|
41
71
|
return;
|
|
72
|
+
}
|
|
42
73
|
if (!found.own && written === undefined)
|
|
43
74
|
return;
|
|
44
|
-
|
|
75
|
+
const doors = doorsFor(found.manifest);
|
|
76
|
+
if ((doors === undefined || doors.size === 0) && written !== undefined) {
|
|
77
|
+
tell("unresolvedPackage", source, node);
|
|
78
|
+
}
|
|
79
|
+
return doors;
|
|
45
80
|
};
|
|
46
81
|
const fromRoot = (node) => {
|
|
47
82
|
const source = node.source?.value;
|
|
@@ -50,7 +85,7 @@ var subpathOverBarrel = {
|
|
|
50
85
|
const names = namesTakenBy(node);
|
|
51
86
|
if (names === null || names.length === 0)
|
|
52
87
|
return;
|
|
53
|
-
const home = homesFor(source);
|
|
88
|
+
const home = homesFor(source, node);
|
|
54
89
|
if (home === undefined)
|
|
55
90
|
return;
|
|
56
91
|
const reached = new Set(names.map((name) => home.get(name)));
|
|
@@ -75,7 +110,9 @@ it to reach one pays for every other — at install, at bundle and at worker sta
|
|
|
75
110
|
show for it. An import whose names span two subpaths is what the root is for and is left alone, as
|
|
76
111
|
is a type-only import, which erases before any of that happens. The map is the package's own
|
|
77
112
|
\`package.json\` \`exports\`, read for the repo's own packages (a workspace link, or
|
|
78
|
-
private
|
|
113
|
+
private, or a member of the workspace the repo declares in pnpm-workspace.yaml or package.json
|
|
114
|
+
\`workspaces\`) with the names behind each door read off its entry file; a door written as a pattern
|
|
115
|
+
(\`./icons/*\`) is expanded by listing the files its target names; nothing to configure. \`packages\`
|
|
79
116
|
overrides it: \`{ name, subpaths: { document: ['Document', …] } }\` replaces the map for a package
|
|
80
117
|
whose exports do not describe its real doors, and \`{ name }\` alone reads a third-party package's
|
|
81
118
|
doors too.`,
|
|
@@ -84,6 +121,8 @@ doors too.`,
|
|
|
84
121
|
description: "A root import whose names all live under one of the package's subpaths names the subpath instead. The root entry is the union of every subpath's module graph, so reaching one through it pays for all of them. Names spanning two subpaths, and type-only imports, are exempt. The map is read off the package's own `exports` for the repo's own packages; `packages` overrides it per package."
|
|
85
122
|
},
|
|
86
123
|
messages: {
|
|
124
|
+
nothingResolves: "No package resolves from this tree: there is no node_modules above this file and no workspace membership (pnpm-workspace.yaml, or package.json workspaces) to read the repo’s own packages from, so subpath-over-barrel reads no door and judges nothing here. Link the workspace, or declare its packages in the workspace manifest.",
|
|
125
|
+
unresolvedPackage: 'The package "{{package}}" is named in `packages` and no door could be read from it: nothing resolves it, or its `exports` name no file a root import could be placed under. subpath-over-barrel judges nothing for it until it resolves.',
|
|
87
126
|
rootBarrel: "Every name imported here from `{{package}}` lives under `{{subpath}}`. Import from the subpath: the root entry re-exports all of them, so this file takes the union of every subpath’s module graph to reach one of them."
|
|
88
127
|
},
|
|
89
128
|
schema: optionsBag({
|
|
@@ -2,7 +2,6 @@ import { type RuleContextWithOptions } from './lib/context.js';
|
|
|
2
2
|
import type { AnyNode } from './lib/nodes.js';
|
|
3
3
|
import { type Probe } from './lib/probe.js';
|
|
4
4
|
interface Options {
|
|
5
|
-
/** Regex sources for the directories a subscriber lives in. Default: Medusa's `subscribers/`. */
|
|
6
5
|
paths?: string[];
|
|
7
6
|
}
|
|
8
7
|
declare const subscriberDeclaresId: {
|
|
@@ -33,7 +32,6 @@ declare const subscriberDeclaresId: {
|
|
|
33
32
|
}[];
|
|
34
33
|
type: 'problem';
|
|
35
34
|
};
|
|
36
|
-
/** A subscriber config with an event and no id, inside the configured subscriber tree. */
|
|
37
35
|
probe(options: readonly unknown[]): Probe;
|
|
38
36
|
};
|
|
39
37
|
export default subscriberDeclaresId;
|
|
@@ -3,7 +3,7 @@ import { propertiesOf, valueOf } from "./lib/objects.js";
|
|
|
3
3
|
import { requireOption } from "./lib/options.js";
|
|
4
4
|
import { fileOf, normalise } from "./lib/paths.js";
|
|
5
5
|
import { bagOf, firstOf, pathMatching } from "./lib/probe.js";
|
|
6
|
-
import { STRINGS } from "./lib/schema.js";
|
|
6
|
+
import { optionsBag, STRINGS } from "./lib/schema.js";
|
|
7
7
|
var RULE = "subscriber-declares-id";
|
|
8
8
|
var DEFAULT_PATHS = ["(?:^|/)subscribers/"];
|
|
9
9
|
var CONFIG = "config";
|
|
@@ -54,13 +54,7 @@ other than \`subscribers/\`.`,
|
|
|
54
54
|
messages: {
|
|
55
55
|
inferred: 'Subscriber "{{file}}" declares no `config.context.subscriberId`, so Medusa infers one from its function or file name. Another subscriber that infers the same id dedupes this one — silently, with no error and no way to tell which handler survived. Write the id down.'
|
|
56
56
|
},
|
|
57
|
-
schema:
|
|
58
|
-
{
|
|
59
|
-
additionalProperties: false,
|
|
60
|
-
properties: { paths: STRINGS },
|
|
61
|
-
type: "object"
|
|
62
|
-
}
|
|
63
|
-
],
|
|
57
|
+
schema: optionsBag({ paths: STRINGS }),
|
|
64
58
|
type: "problem"
|
|
65
59
|
},
|
|
66
60
|
probe(options) {
|
|
@@ -2,7 +2,6 @@ import { type RuleContextWithOptions } from './lib/context.js';
|
|
|
2
2
|
import { type LiteralCall } from './lib/literal-call.js';
|
|
3
3
|
import { type Probe } from './lib/probe.js';
|
|
4
4
|
interface Options {
|
|
5
|
-
/** Regex source(s) over the filename: the schema tree whose tables must pick a plane. */
|
|
6
5
|
schema?: string | string[];
|
|
7
6
|
}
|
|
8
7
|
declare const tablesDeclareTheirPlane: {
|
|
@@ -38,7 +37,6 @@ declare const tablesDeclareTheirPlane: {
|
|
|
38
37
|
}[];
|
|
39
38
|
type: 'problem';
|
|
40
39
|
};
|
|
41
|
-
/** A table loose in the schema tree the options name — outside both planes by construction. */
|
|
42
40
|
probe(options: readonly unknown[]): Probe;
|
|
43
41
|
};
|
|
44
42
|
export default tablesDeclareTheirPlane;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// src/rules/tables-declare-their-plane.ts
|
|
2
|
-
import { literalNamedBy } from "./lib/literal-call.js";
|
|
2
|
+
import { literalNamedBy, TABLE_CALLEES } from "./lib/literal-call.js";
|
|
3
3
|
import { PATTERNS, patternsOf, requireOption } from "./lib/options.js";
|
|
4
4
|
import { normalise } from "./lib/paths.js";
|
|
5
5
|
import { bagOf, firstOf, pathMatching } from "./lib/probe.js";
|
|
6
|
+
import { optionsBag } from "./lib/schema.js";
|
|
6
7
|
var RULE = "tables-declare-their-plane";
|
|
7
8
|
var ACCEPTS = "{ schema: string | string[] (regex sources over the filename: the schema tree) }";
|
|
8
|
-
var tableCallees = ["sqliteTable", "pgTable"];
|
|
9
9
|
var PLANES = /(?:^|\/)(?:control|tenant)\//;
|
|
10
10
|
var tablesDeclareTheirPlane = {
|
|
11
11
|
create(context) {
|
|
@@ -18,7 +18,7 @@ var tablesDeclareTheirPlane = {
|
|
|
18
18
|
return {};
|
|
19
19
|
return {
|
|
20
20
|
CallExpression(node) {
|
|
21
|
-
for (const callee of
|
|
21
|
+
for (const callee of TABLE_CALLEES) {
|
|
22
22
|
const table = literalNamedBy(node, callee);
|
|
23
23
|
if (table !== null)
|
|
24
24
|
context.report({ data: { table }, messageId: "undeclared", node });
|
|
@@ -38,13 +38,7 @@ that once a run and judges nothing.`,
|
|
|
38
38
|
messages: {
|
|
39
39
|
undeclared: '"{{table}}" is declared outside control/ and tenant/ in the schema tree. Move it: control-plane tables are read by user, key or address at the edge; tenant tables carry the tenant key and live in a cell.'
|
|
40
40
|
},
|
|
41
|
-
schema:
|
|
42
|
-
{
|
|
43
|
-
additionalProperties: false,
|
|
44
|
-
properties: { schema: PATTERNS },
|
|
45
|
-
type: "object"
|
|
46
|
-
}
|
|
47
|
-
],
|
|
41
|
+
schema: optionsBag({ schema: PATTERNS }),
|
|
48
42
|
type: "problem"
|
|
49
43
|
},
|
|
50
44
|
probe(options) {
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { type RuleContextWithOptions } from './lib/context.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type LiteralCall } from './lib/literal-call.js';
|
|
3
3
|
import { type Probe } from './lib/probe.js';
|
|
4
4
|
interface Options {
|
|
5
|
-
/** Regex source(s), tested against the normalised filename: the tenant-plane schema tree. */
|
|
6
5
|
tenant?: string | string[];
|
|
7
|
-
/** The column a tenant is scoped by. Default: `orgId`. */
|
|
8
6
|
tenantKey?: string;
|
|
9
7
|
}
|
|
10
8
|
type ObjectExpression = {
|
|
@@ -60,7 +58,6 @@ declare const tenantTablesCarryOrgId: {
|
|
|
60
58
|
}[];
|
|
61
59
|
type: 'problem';
|
|
62
60
|
};
|
|
63
|
-
/** A table in the tenant tree the options name, declared with no `orgId` column. */
|
|
64
61
|
probe(options: readonly unknown[]): Probe;
|
|
65
62
|
};
|
|
66
63
|
export default tenantTablesCarryOrgId;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
// src/rules/tenant-tables-carry-org-id.ts
|
|
2
|
-
import {
|
|
2
|
+
import { TABLE_CALLEES } from "./lib/literal-call.js";
|
|
3
|
+
import { DEFAULT_TENANT_KEY, PATTERNS, patternsOf, requireOption } from "./lib/options.js";
|
|
3
4
|
import { normalise } from "./lib/paths.js";
|
|
4
5
|
import { bagOf, firstOf, pathMatching } from "./lib/probe.js";
|
|
5
|
-
import { STRING } from "./lib/schema.js";
|
|
6
|
-
var DEFAULT_TENANT_KEY = "orgId";
|
|
6
|
+
import { optionsBag, STRING } from "./lib/schema.js";
|
|
7
7
|
var RULE = "tenant-tables-carry-org-id";
|
|
8
8
|
var ACCEPTS = "{ tenant: string (a regex over the filename: the tenant-plane schema tree) }";
|
|
9
|
-
var tableCallees = new Set(["sqliteTable", "pgTable"]);
|
|
10
9
|
var carriesKey = (columns, key) => columns?.type === "ObjectExpression" && (columns.properties ?? []).some((property) => property.type === "Property" && (property.key?.name ?? property.key?.value) === key);
|
|
11
10
|
var tenantTablesCarryOrgId = {
|
|
12
11
|
create(context) {
|
|
@@ -17,7 +16,7 @@ var tenantTablesCarryOrgId = {
|
|
|
17
16
|
return {};
|
|
18
17
|
return {
|
|
19
18
|
CallExpression(node) {
|
|
20
|
-
if (node.callee.type !== "Identifier" || !
|
|
19
|
+
if (node.callee.type !== "Identifier" || !TABLE_CALLEES.includes(node.callee.name ?? ""))
|
|
21
20
|
return;
|
|
22
21
|
const [name, columns] = node.arguments ?? [];
|
|
23
22
|
const table = name && "value" in name && typeof name.value === "string" ? name.value : "this table";
|
|
@@ -38,13 +37,7 @@ enabled without it the rule reports that once a run and judges nothing.`,
|
|
|
38
37
|
messages: {
|
|
39
38
|
noOrgId: '"{{table}}" is a tenant table without a `{{key}}` column. Add one — or move it to the control plane if it is not one tenant\'s.'
|
|
40
39
|
},
|
|
41
|
-
schema:
|
|
42
|
-
{
|
|
43
|
-
additionalProperties: false,
|
|
44
|
-
properties: { tenant: PATTERNS, tenantKey: STRING },
|
|
45
|
-
type: "object"
|
|
46
|
-
}
|
|
47
|
-
],
|
|
40
|
+
schema: optionsBag({ tenant: PATTERNS, tenantKey: STRING }),
|
|
48
41
|
type: "problem"
|
|
49
42
|
},
|
|
50
43
|
probe(options) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type RuleContextWithRawOptions } from './lib/context.js';
|
|
2
2
|
import { type AnyNode } from './lib/nodes.js';
|
|
3
|
+
import { type Probe } from './lib/probe.js';
|
|
3
4
|
declare const testNoClockPollingForPromiseFlips: {
|
|
4
5
|
create(context: RuleContextWithRawOptions): {
|
|
5
6
|
ArrowFunctionExpression?: undefined;
|
|
@@ -38,5 +39,6 @@ declare const testNoClockPollingForPromiseFlips: {
|
|
|
38
39
|
}[];
|
|
39
40
|
type: 'problem';
|
|
40
41
|
};
|
|
42
|
+
probe(options: readonly unknown[]): Probe;
|
|
41
43
|
};
|
|
42
44
|
export default testNoClockPollingForPromiseFlips;
|