@starci/hfs 4.0.10 → 4.1.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 +1 -1
- package/emit/operations.mjs +1 -1
- package/emit/type-schema.mjs +2 -1
- package/lint/run.mjs +3 -2
- package/package.json +1 -1
- package/report/order.mjs +2 -0
- package/runtime/config.example.yaml +187 -0
- package/runtime/engine/by-code-unit.mjs +12 -0
- package/runtime/engine/config.mjs +221 -239
- package/runtime/engine/invalid-config.mjs +5 -5
- package/runtime/engine/model-config.mjs +90 -0
- package/runtime/engine/orca-config.mjs +3 -1
- package/runtime/engine/removed-vocabulary.mjs +28 -0
- package/runtime/engine/resources-config.mjs +52 -0
- package/runtime/engine/runtime-root.mjs +17 -0
- package/runtime/engine/secrets.mjs +35 -31
- package/runtime/engine/sonar-config.mjs +21 -0
- package/runtime/engine/temp-root.mjs +32 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +14 -14
- package/runtime/knowledge/hfs/peer-integrations.yaml +2 -3
- package/runtime/knowledge/hfs/rules.yaml +98 -65
- package/runtime/knowledge/hfs/slots.yaml +11 -14
- package/runtime/knowledge/patterns/be/api.yaml +7 -24
- package/runtime/knowledge/patterns/be/cli.yaml +3 -12
- package/runtime/knowledge/patterns/be/realtime.yaml +1 -14
- package/runtime/knowledge/patterns/be/webhooks.yaml +0 -19
- package/runtime/modules/kernel/failure-codes.yaml +1 -41
- package/runtime/modules/kernel/removed-vocabulary.yaml +168 -0
- package/runtime/modules/models/registry.yaml +25 -359
- package/runtime/modules/models/runtimes.yaml +92 -256
- package/runtime/modules/models/tiers.yaml +65 -0
- package/runtime/scripts/api/fs/claim-file.mjs +30 -0
- package/runtime/scripts/api/fs/ensure-temp-root.mjs +27 -0
- package/runtime/scripts/api/fs/forbidden-root.mjs +2 -1
- package/runtime/scripts/api/fs/make-temp-dir.mjs +11 -0
- package/runtime/scripts/api/fs/safe-remove.mjs +52 -35
- package/runtime/scripts/api/git/lib.mjs +34 -1
- package/runtime/scripts/api/process/resolve-real-tool.mjs +14 -8
- package/runtime/scripts/api/process/run-program.mjs +2 -1
- package/runtime/scripts/api/sops/lib.mjs +198 -120
- package/runtime/scripts/hfs/allows.mjs +3 -3
- package/runtime/scripts/hfs/architecture/ast-walks.mjs +63 -50
- package/runtime/scripts/hfs/architecture/automatic-gates.mjs +96 -0
- package/runtime/scripts/hfs/architecture/backend.mjs +261 -175
- package/runtime/scripts/hfs/architecture/background-unowned.mjs +69 -34
- package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +55 -54
- package/runtime/scripts/hfs/architecture/clones.mjs +179 -106
- package/runtime/scripts/hfs/architecture/config-unread.mjs +5 -22
- package/runtime/scripts/hfs/architecture/config.mjs +95 -80
- package/runtime/scripts/hfs/architecture/connection-map.mjs +233 -141
- package/runtime/scripts/hfs/architecture/constructor-deps.mjs +15 -9
- package/runtime/scripts/hfs/architecture/context-coupling.mjs +76 -52
- package/runtime/scripts/hfs/architecture/context-map.mjs +215 -141
- package/runtime/scripts/hfs/architecture/context-owner.mjs +39 -30
- package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +31 -17
- package/runtime/scripts/hfs/architecture/context-transaction.mjs +38 -25
- package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +86 -40
- package/runtime/scripts/hfs/architecture/contracts-readonly.mjs +342 -0
- package/runtime/scripts/hfs/architecture/contracts.mjs +263 -455
- package/runtime/scripts/hfs/architecture/cross-app-duplicate.mjs +43 -34
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +201 -154
- package/runtime/scripts/hfs/architecture/default-deny.mjs +118 -93
- package/runtime/scripts/hfs/architecture/doc-language.mjs +2 -1
- package/runtime/scripts/hfs/architecture/entrypoint.mjs +45 -30
- package/runtime/scripts/hfs/architecture/error-codes.mjs +19 -12
- package/runtime/scripts/hfs/architecture/error-masked.mjs +39 -26
- package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +2 -1
- package/runtime/scripts/hfs/architecture/feature-shape.mjs +26 -20
- package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
- package/runtime/scripts/hfs/architecture/frontend-grammar.mjs +190 -0
- package/runtime/scripts/hfs/architecture/frontend-routing.mjs +186 -0
- package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +311 -0
- package/runtime/scripts/hfs/architecture/frontend-world.mjs +328 -0
- package/runtime/scripts/hfs/architecture/frontend.mjs +109 -829
- package/runtime/scripts/hfs/architecture/hfs-graph.mjs +14 -2
- package/runtime/scripts/hfs/architecture/hfs.mjs +120 -288
- package/runtime/scripts/hfs/architecture/hooks-are-hooks.mjs +46 -26
- package/runtime/scripts/hfs/architecture/i18n-keys.mjs +114 -76
- package/runtime/scripts/hfs/architecture/index.mjs +158 -102
- package/runtime/scripts/hfs/architecture/injection-token-exported.mjs +33 -28
- package/runtime/scripts/hfs/architecture/machine-ast.mjs +180 -144
- package/runtime/scripts/hfs/architecture/managed-scripts.mjs +8 -2
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +150 -101
- package/runtime/scripts/hfs/architecture/next-data-contract.mjs +51 -43
- package/runtime/scripts/hfs/architecture/next-data-key.mjs +338 -0
- package/runtime/scripts/hfs/architecture/next-data.mjs +179 -427
- package/runtime/scripts/hfs/architecture/owners.mjs +27 -13
- package/runtime/scripts/hfs/architecture/package-shape.mjs +51 -23
- package/runtime/scripts/hfs/architecture/presentation.mjs +165 -0
- package/runtime/scripts/hfs/architecture/reachability.mjs +127 -61
- package/runtime/scripts/hfs/architecture/register-once.mjs +112 -63
- package/runtime/scripts/hfs/architecture/registration.mjs +187 -110
- package/runtime/scripts/hfs/architecture/required-files.mjs +100 -68
- package/runtime/scripts/hfs/architecture/route-files-thin.mjs +83 -58
- package/runtime/scripts/hfs/architecture/schema-owner.mjs +182 -108
- package/runtime/scripts/hfs/architecture/source-names-shape.mjs +318 -0
- package/runtime/scripts/hfs/architecture/source-names.mjs +83 -248
- package/runtime/scripts/hfs/architecture/sql-owner.mjs +175 -118
- package/runtime/scripts/hfs/architecture/sql-returning.mjs +39 -28
- package/runtime/scripts/hfs/architecture/sql-tokens.mjs +302 -172
- package/runtime/scripts/hfs/architecture/supabase-ast.mjs +1 -1
- package/runtime/scripts/hfs/architecture/supabase-be.mjs +55 -45
- package/runtime/scripts/hfs/architecture/supabase-results.mjs +182 -0
- package/runtime/scripts/hfs/architecture/supabase-tables.mjs +20 -12
- package/runtime/scripts/hfs/architecture/supabase.mjs +191 -263
- package/runtime/scripts/hfs/architecture/symbols.mjs +175 -99
- package/runtime/scripts/hfs/architecture/test-world-files.mjs +82 -41
- package/runtime/scripts/hfs/architecture/tiers.mjs +120 -69
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +84 -57
- package/runtime/scripts/hfs/architecture/type-context.mjs +282 -0
- package/runtime/scripts/hfs/architecture/typescript.mjs +116 -276
- package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +84 -62
- package/runtime/scripts/hfs/architecture.mjs +3 -4
- package/runtime/scripts/hfs/check.mjs +61 -70
- package/runtime/scripts/hfs/coverage-scope.mjs +84 -38
- package/runtime/scripts/hfs/declaration-shape.mjs +77 -36
- package/runtime/scripts/hfs/declaration-slots.mjs +1 -1
- package/runtime/scripts/hfs/edition-slots.mjs +5 -3
- package/runtime/scripts/hfs/linear-text.mjs +30 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +164 -63
- package/runtime/scripts/hfs/path-findings.mjs +58 -38
- package/runtime/scripts/hfs/pin-findings.mjs +31 -0
- package/runtime/scripts/hfs/repo-identity.mjs +5 -2
- package/runtime/scripts/hfs/rule-catalog.mjs +194 -0
- package/runtime/scripts/hfs/rule-params-shape.mjs +33 -27
- package/runtime/scripts/hfs/rules/cli.mjs +5 -1
- package/runtime/scripts/hfs/rules/contract-compat.mjs +40 -22
- package/runtime/scripts/hfs/rules/contract.mjs +35 -27
- package/runtime/scripts/hfs/rules/database-config.mjs +57 -32
- package/runtime/scripts/hfs/rules/database-migrations.mjs +52 -31
- package/runtime/scripts/hfs/rules/database-plpgsql.mjs +106 -91
- package/runtime/scripts/hfs/rules/database-sql.mjs +254 -173
- package/runtime/scripts/hfs/rules/database.mjs +36 -23
- package/runtime/scripts/hfs/rules/deps.mjs +64 -32
- package/runtime/scripts/hfs/rules/docker.mjs +113 -65
- package/runtime/scripts/hfs/rules/edition.mjs +118 -111
- package/runtime/scripts/hfs/rules/event-bus.mjs +91 -45
- package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +17 -16
- package/runtime/scripts/hfs/rules/fe-no-tests.mjs +36 -16
- package/runtime/scripts/hfs/rules/frontend-tree.mjs +4 -3
- package/runtime/scripts/hfs/rules/integration-specs.mjs +75 -41
- package/runtime/scripts/hfs/rules/kinds.mjs +59 -25
- package/runtime/scripts/hfs/rules/lint-suppression.mjs +9 -3
- package/runtime/scripts/hfs/rules/monorepo.mjs +82 -45
- package/runtime/scripts/hfs/rules/peer-integrations.mjs +24 -3
- package/runtime/scripts/hfs/rules/pipeline.mjs +58 -9
- package/runtime/scripts/hfs/rules/proof-commands.mjs +14 -8
- package/runtime/scripts/hfs/rules/repo-local-checks.mjs +11 -6
- package/runtime/scripts/hfs/rules/saga.mjs +128 -61
- package/runtime/scripts/hfs/rules/secrets.mjs +4 -3
- package/runtime/scripts/hfs/rules/services.mjs +34 -16
- package/runtime/scripts/hfs/rules/stacks.mjs +21 -14
- package/runtime/scripts/hfs/rules/supabase-secrets.mjs +32 -23
- package/runtime/scripts/hfs/rules/test-topology.mjs +25 -11
- package/runtime/scripts/hfs/secret.mjs +59 -48
- package/runtime/scripts/hfs/slot-app-view.mjs +102 -0
- package/runtime/scripts/hfs/slot-classify.mjs +65 -0
- package/runtime/scripts/hfs/slot-errors.mjs +11 -0
- package/runtime/scripts/hfs/slot-imports.mjs +76 -0
- package/runtime/scripts/hfs/slot-manifest-shape.mjs +73 -0
- package/runtime/scripts/hfs/slot-match.mjs +153 -0
- package/runtime/scripts/hfs/slot-path.mjs +8 -0
- package/runtime/scripts/hfs/slot-required.mjs +76 -0
- package/runtime/scripts/hfs/slot-semantic-problems.mjs +148 -0
- package/runtime/scripts/hfs/slot-side-problems.mjs +28 -0
- package/runtime/scripts/hfs/slots.mjs +72 -693
- package/runtime/scripts/hfs/sql/pg-parse.mjs +5 -2
- package/runtime/scripts/hfs/trailing-slashes.mjs +8 -0
- package/runtime/scripts/hfs/tree.mjs +19 -14
- package/runtime/scripts/hfs/typescript-programs.mjs +6 -4
- package/runtime/scripts/hfs/view.mjs +1 -1
- package/runtime/scripts/lib/dockerfile.mjs +56 -33
- package/runtime/scripts/lib/event-contract.mjs +17 -13
- package/runtime/scripts/lib/git.mjs +2 -2
- package/runtime/scripts/lib/graphql-contract.mjs +158 -322
- package/runtime/scripts/lib/graphql-sdl.mjs +262 -0
- package/runtime/scripts/lib/i18n.mjs +3 -2
- package/runtime/scripts/lib/in-order.mjs +71 -0
- package/runtime/scripts/lib/language.mjs +3 -3
- package/runtime/scripts/lib/list.mjs +8 -1
- package/runtime/scripts/lib/path-key.mjs +38 -5
- package/runtime/scripts/lib/pid-alive.mjs +7 -0
- package/runtime/scripts/lib/regex.mjs +1 -1
- package/runtime/scripts/lib/same-text.mjs +1 -1
- package/runtime/scripts/lib/secret-patterns.mjs +6 -2
- package/runtime/scripts/lib/sleep-sync.mjs +2 -2
- package/runtime/scripts/lib/sops-envelope.mjs +95 -48
- package/runtime/scripts/lib/stack-services.mjs +1 -2
- package/runtime/scripts/lib/ts-ast.mjs +9 -0
- package/runtime/scripts/lib/walk.mjs +6 -1
- package/scaffold/add-cli-lite.mjs +2 -1
- package/scaffold/add-table.mjs +2 -2
- package/scaffold/add.mjs +3 -2
- package/scaffold/app.mjs +4 -2
- package/scaffold/edition-gate.mjs +25 -25
- package/scaffold/lite-exports.mjs +2 -1
- package/scaffold/service.mjs +5 -4
- package/sync/hygiene.mjs +15 -9
- package/sync/index.mjs +7 -3
- package/templates/app/ci-workflows/github/workflows/ci.yml +4 -4
- package/templates/app/ci-workflows/github/workflows/e2e.yml +3 -3
- package/templates/app/ci-workflows/github/workflows/images.yml +3 -3
- package/templates/app/ci-workflows/sonar-steps.yml +2 -2
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +6 -6
- package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +3 -3
- package/templates/app/ci-workflows-lite/github/workflows/images.yml +3 -3
- package/templates/app/starciwork.gitignore +1 -1
- package/templates/be/image/api/Dockerfile +1 -1
- package/templates/be/image/cli/Dockerfile +1 -1
- package/templates/be/image/worker/Dockerfile +1 -1
- package/templates/be/patterns/cli/group.cli.spec.ts.tpl +1 -1
- package/templates/be/patterns/cli/group.cli.ts.tpl +2 -2
- package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +13 -0
- package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +7 -2
- package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +4 -1
- package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +2 -1
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +24 -9
- package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +3 -2
- package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +8 -5
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +1 -1
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +2 -2
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +1 -1
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +2 -2
- package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +3 -2
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +5 -4
- package/templates/be/skeleton/src/modules/platform/primitives/index.ts +1 -0
- package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.spec.ts +47 -0
- package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.ts +16 -0
- package/templates/be/skeleton-lite/apps/api/Dockerfile +1 -0
- package/templates/be/skeleton-lite/src/modules/platform/primitives/index.ts +2 -0
- package/templates/fe/image/next/Dockerfile +2 -1
- package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +2 -2
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +2 -2
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +1 -2
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +1 -2
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +2 -2
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +7 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +1 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +10 -2
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +10 -3
- package/upgrade/index.mjs +9 -2
- package/runtime/engine/admission.mjs +0 -308
- package/runtime/engine/canonical-json.mjs +0 -10
- package/runtime/engine/db/blob.mjs +0 -315
- package/runtime/engine/db/ledger-paths.mjs +0 -83
- package/runtime/engine/db/ledger.mjs +0 -1118
- package/runtime/engine/db/machine-connection.mjs +0 -135
- package/runtime/engine/db/machine-schema.mjs +0 -84
- package/runtime/engine/db/machine.mjs +0 -1367
- package/runtime/engine/db/migrations/machine/0001-init.sql +0 -924
- package/runtime/engine/db/migrations/runtime/0001-init.sql +0 -1108
- package/runtime/engine/db/provider-reservations.mjs +0 -101
- package/runtime/engine/digest.mjs +0 -16
- package/runtime/engine/refuse.mjs +0 -11
- package/runtime/modules/ops/_labels.yaml +0 -53
- package/runtime/scripts/api/node/lib.mjs +0 -14
- package/runtime/scripts/api/node/spawn-node.mjs +0 -6
- package/runtime/scripts/api/process/lib.mjs +0 -111
- package/runtime/scripts/api/process/owned-process.mjs +0 -78
- package/runtime/scripts/api/process/stop-owned-process.mjs +0 -9
- package/runtime/scripts/connectors/lib.mjs +0 -488
- package/runtime/scripts/lib/clip.mjs +0 -19
- package/runtime/scripts/lib/display-names.mjs +0 -259
- package/runtime/scripts/lib/example-refs.mjs +0 -158
- package/runtime/scripts/lib/json-schema.mjs +0 -52
- package/runtime/scripts/lib/process-identity.mjs +0 -6
- package/runtime/scripts/lib/read-yaml.mjs +0 -18
- package/runtime/scripts/lib/redact.mjs +0 -161
- package/runtime/scripts/lib/sleep.mjs +0 -7
- package/runtime/scripts/lib/source-phrases.mjs +0 -34
- package/runtime/scripts/lib/sqlite.mjs +0 -21
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
// ast-walks.mjs - AST walks the architecture checkers share (moved out of typescript.mjs, which keeps the program loading and module resolution).
|
|
2
2
|
import { canonical, isInside } from './config.mjs';
|
|
3
|
-
import { sourceLocation } from '../../lib/ts-ast.mjs';
|
|
3
|
+
import { hasAnyFlag, isConstVariable, sourceLocation } from '../../lib/ts-ast.mjs';
|
|
4
4
|
import { UNPROVEN_FRAMEWORK, isUnshadowedCommonJsRequire, relativePath, unwrapExpression } from './typescript.mjs';
|
|
5
5
|
|
|
6
6
|
/** `expression` unwrapped by the caller's own predicate set (a predicate absent on an older ts reads as never). */
|
|
7
7
|
export function unwrapEach(ts, expression, predicates) {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
let current = expression;
|
|
9
|
+
while (current && predicates.some((predicate) => predicate?.(current))) current = current.expression;
|
|
10
|
+
return current;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
/** `value` with every TS alias hop resolved (a re-exported import reads as its target symbol). */
|
|
13
|
-
export function normalizedSymbolValue(ts, checker, value) {
|
|
14
|
-
let symbol = value
|
|
14
|
+
export function normalizedSymbolValue(ts, checker, value = null) {
|
|
15
|
+
let symbol = value;
|
|
15
16
|
const seen = new Set();
|
|
16
|
-
while (symbol && (symbol.flags
|
|
17
|
+
while (symbol && hasAnyFlag(symbol.flags, ts.SymbolFlags.Alias) && !seen.has(symbol)) {
|
|
17
18
|
seen.add(symbol);
|
|
18
19
|
const target = checker.getAliasedSymbol(symbol);
|
|
19
20
|
if (!target || target === symbol) break;
|
|
@@ -48,7 +49,7 @@ export function valueSymbol(ts, checker, node, seen = new Set(), { unselected =
|
|
|
48
49
|
seen.add(symbol);
|
|
49
50
|
const declarations = symbol.getDeclarations?.() ?? [];
|
|
50
51
|
if (declarations.length === 1 && ts.isVariableDeclaration(declarations[0]) && declarations[0].initializer
|
|
51
|
-
&& (ts
|
|
52
|
+
&& isConstVariable(ts, declarations[0])) {
|
|
52
53
|
return valueSymbol(ts, checker, declarations[0].initializer, seen, { unselected });
|
|
53
54
|
}
|
|
54
55
|
return symbol;
|
|
@@ -102,50 +103,59 @@ export function anyDescendant(ts, node, match) {
|
|
|
102
103
|
* selected node's symbols to a kind (default `targets.get(symbol)`); `shorthandOf` (when set) also resolves
|
|
103
104
|
* shorthand-property declarations and descends property assignments; `newExpression` descends `new X()` callees.
|
|
104
105
|
*/
|
|
106
|
+
const addKinds = (target, source) => { for (const kind of source) target.add(kind); };
|
|
107
|
+
|
|
108
|
+
const kindsFromReturns = (ts, checker, declaration, targets, options) => {
|
|
109
|
+
const kinds = new Set();
|
|
110
|
+
for (const returned of returnedExpressions(ts, declaration)) addKinds(kinds, tracedFrameworkKinds(ts, checker, returned, targets, options));
|
|
111
|
+
return kinds;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const kindsFromDeclaration = (ts, checker, declaration, targets, options, shorthandOf) => {
|
|
115
|
+
const kinds = new Set();
|
|
116
|
+
if (ts.isVariableDeclaration(declaration) && declaration.initializer)
|
|
117
|
+
addKinds(kinds, tracedFrameworkKinds(ts, checker, declaration.initializer, targets, options));
|
|
118
|
+
if (shorthandOf && ts.isShorthandPropertyAssignment(declaration)) {
|
|
119
|
+
const target = normalizedSymbolValue(ts, checker, checker.getShorthandAssignmentValueSymbol?.(declaration));
|
|
120
|
+
const kind = shorthandOf(ts, checker, target, targets);
|
|
121
|
+
if (kind) kinds.add(kind);
|
|
122
|
+
}
|
|
123
|
+
if (shorthandOf && ts.isPropertyAssignment(declaration))
|
|
124
|
+
addKinds(kinds, tracedFrameworkKinds(ts, checker, declaration.initializer, targets, options));
|
|
125
|
+
addKinds(kinds, kindsFromReturns(ts, checker, declaration, targets, options));
|
|
126
|
+
return kinds;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const kindsFromSymbol = (ts, checker, symbol, targets, seen, options) => {
|
|
130
|
+
const nextSeen = new Set(seen).add(symbol);
|
|
131
|
+
const declarationOptions = { ...options, seen: nextSeen };
|
|
132
|
+
const kinds = new Set();
|
|
133
|
+
for (const declaration of symbol.getDeclarations?.() ?? [])
|
|
134
|
+
addKinds(kinds, kindsFromDeclaration(ts, checker, declaration, targets, declarationOptions, options.shorthandOf));
|
|
135
|
+
return kinds;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const defaultDirectOf = (ts, checker, { symbol, targets }) => targets.get(symbol) ?? null;
|
|
139
|
+
|
|
105
140
|
export function tracedFrameworkKinds(ts, checker, expression, targets,
|
|
106
141
|
{ seen = new Set(), depth = 0, directOf = null, shorthandOf = null, newExpression = false } = {}) {
|
|
107
142
|
if (!expression) return new Set();
|
|
108
143
|
if (depth > 10) return new Set([UNPROVEN_FRAMEWORK]);
|
|
109
|
-
|
|
110
|
-
const selected = selectedNode(ts,
|
|
144
|
+
const unwrapped = unwrapExpression(ts, expression);
|
|
145
|
+
const selected = selectedNode(ts, unwrapped);
|
|
111
146
|
const symbol = selected ? normalizedSymbol(ts, checker, selected) : null;
|
|
112
|
-
const direct = symbol ? (directOf ??
|
|
147
|
+
const direct = symbol ? (directOf ?? defaultDirectOf)(ts, checker, { symbol, selected, targets }) : null;
|
|
113
148
|
if (direct) return new Set([direct]);
|
|
114
|
-
const
|
|
115
|
-
if (ts.isArrowFunction(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
for (const kind of tracedFrameworkKinds(ts, checker, returned, targets, { ...opts, seen })) kinds.add(kind);
|
|
119
|
-
}
|
|
120
|
-
return kinds;
|
|
121
|
-
}
|
|
122
|
-
if (ts.isCallExpression(expression)) {
|
|
123
|
-
const kinds = tracedFrameworkKinds(ts, checker, expression.expression, targets, { ...opts, seen });
|
|
149
|
+
const options = { depth: depth + 1, directOf, shorthandOf, newExpression };
|
|
150
|
+
if (ts.isArrowFunction(unwrapped) || ts.isFunctionExpression(unwrapped)) return kindsFromReturns(ts, checker, unwrapped, targets, { ...options, seen });
|
|
151
|
+
if (ts.isCallExpression(unwrapped)) {
|
|
152
|
+
const kinds = tracedFrameworkKinds(ts, checker, unwrapped.expression, targets, { ...options, seen });
|
|
124
153
|
if (kinds.size) return kinds;
|
|
125
154
|
}
|
|
126
|
-
if (newExpression && ts.isNewExpression(
|
|
127
|
-
return tracedFrameworkKinds(ts, checker,
|
|
128
|
-
}
|
|
155
|
+
if (newExpression && ts.isNewExpression(unwrapped))
|
|
156
|
+
return tracedFrameworkKinds(ts, checker, unwrapped.expression, targets, { ...options, seen });
|
|
129
157
|
if (!symbol || seen.has(symbol)) return new Set();
|
|
130
|
-
|
|
131
|
-
const kinds = new Set();
|
|
132
|
-
for (const declaration of symbol.getDeclarations?.() ?? []) {
|
|
133
|
-
if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
134
|
-
for (const kind of tracedFrameworkKinds(ts, checker, declaration.initializer, targets, { ...opts, seen: nextSeen })) kinds.add(kind);
|
|
135
|
-
}
|
|
136
|
-
if (shorthandOf && ts.isShorthandPropertyAssignment(declaration)) {
|
|
137
|
-
const target = normalizedSymbolValue(ts, checker, checker.getShorthandAssignmentValueSymbol?.(declaration));
|
|
138
|
-
const kind = shorthandOf(ts, checker, target, targets);
|
|
139
|
-
if (kind) kinds.add(kind);
|
|
140
|
-
}
|
|
141
|
-
if (shorthandOf && ts.isPropertyAssignment(declaration)) {
|
|
142
|
-
for (const kind of tracedFrameworkKinds(ts, checker, declaration.initializer, targets, { ...opts, seen: nextSeen })) kinds.add(kind);
|
|
143
|
-
}
|
|
144
|
-
for (const returned of returnedExpressions(ts, declaration)) {
|
|
145
|
-
for (const kind of tracedFrameworkKinds(ts, checker, returned, targets, { ...opts, seen: nextSeen })) kinds.add(kind);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return kinds;
|
|
158
|
+
return kindsFromSymbol(ts, checker, symbol, targets, seen, options);
|
|
149
159
|
}
|
|
150
160
|
|
|
151
161
|
/** 'unproven framework' | 'multiple framework' | the one kind | null, for a decorator whose expression constructs. */
|
|
@@ -153,7 +163,8 @@ export function constructedDecoratorKind(ts, checker, decorator, targets, traceO
|
|
|
153
163
|
const callee = ts.isCallExpression(decorator.expression) ? decorator.expression.expression : decorator.expression;
|
|
154
164
|
const kinds = tracedFrameworkKinds(ts, checker, callee, targets, traceOpts);
|
|
155
165
|
if (kinds.has(UNPROVEN_FRAMEWORK)) return 'unproven framework';
|
|
156
|
-
|
|
166
|
+
if (kinds.size === 1) return [...kinds][0];
|
|
167
|
+
return kinds.size ? 'multiple framework' : null;
|
|
157
168
|
}
|
|
158
169
|
|
|
159
170
|
/** The target a decorator resolves to through a `let`/`var` alias's single initializer, or null. */
|
|
@@ -163,7 +174,7 @@ export function mutableDecoratorKind(ts, checker, decorator, targets) {
|
|
|
163
174
|
const symbol = selected ? normalizedSymbol(ts, checker, selected) : null;
|
|
164
175
|
const declarations = symbol?.getDeclarations?.() ?? [];
|
|
165
176
|
if (declarations.length !== 1 || !ts.isVariableDeclaration(declarations[0]) || !declarations[0].initializer
|
|
166
|
-
|| (ts
|
|
177
|
+
|| isConstVariable(ts, declarations[0])) return null;
|
|
167
178
|
return targets.get(valueSymbol(ts, checker, declarations[0].initializer)) ?? null;
|
|
168
179
|
}
|
|
169
180
|
|
|
@@ -178,11 +189,12 @@ export function decoratorCallee(ts, decorator) {
|
|
|
178
189
|
* not resolve. Null when the statement names no static specifier.
|
|
179
190
|
*/
|
|
180
191
|
export function moduleExportsOf(ts, checker, statement) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
192
|
+
let moduleSpecifier = null;
|
|
193
|
+
if ((ts.isImportDeclaration(statement) || ts.isExportDeclaration(statement))
|
|
194
|
+
&& statement.moduleSpecifier && ts.isStringLiteralLike(statement.moduleSpecifier)) moduleSpecifier = statement.moduleSpecifier;
|
|
195
|
+
let importEquals = null;
|
|
196
|
+
if (ts.isImportEqualsDeclaration(statement) && ts.isExternalModuleReference(statement.moduleReference)
|
|
197
|
+
&& statement.moduleReference.expression && ts.isStringLiteralLike(statement.moduleReference.expression)) importEquals = statement.moduleReference.expression;
|
|
186
198
|
const specifierNode = moduleSpecifier ?? importEquals;
|
|
187
199
|
const specifier = specifierNode?.text;
|
|
188
200
|
if (specifier == null) return null;
|
|
@@ -208,7 +220,8 @@ export function programSourcesOf(context, checker, localFiles, { root = null } =
|
|
|
208
220
|
* The CommonJS require() caveat every framework scan reports: each `require('<specifier>')` `accepted` pushes
|
|
209
221
|
* `${filePath} uses a CommonJS <label ?? specifier> <detail>` (the import-shape reasoning cannot prove that binding).
|
|
210
222
|
*/
|
|
211
|
-
export function commonJsRequireReasons(ts, checker, sourceFile, accepted, reasons, filePath, detail
|
|
223
|
+
export function commonJsRequireReasons(ts, checker, sourceFile, accepted, reasons, filePath, detail) {
|
|
224
|
+
const label = arguments[7] ?? null;
|
|
212
225
|
const visit = node => {
|
|
213
226
|
if (ts.isCallExpression(node) && isUnshadowedCommonJsRequire(ts, checker, node.expression)
|
|
214
227
|
&& node.arguments.length === 1 && ts.isStringLiteralLike(node.arguments[0]) && accepted(node.arguments[0].text)) {
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { readTextFile } from '../../lib/read-text.mjs';
|
|
2
|
+
|
|
3
|
+
// Owner ruling 2026-09-29 (layout 2026-09-30): integration, e2e and contract run MANUALLY only. No hook, default typecheck,
|
|
4
|
+
// coverage run or automatic CI trigger may include those trees or run those projects. Linting the e2e files is not running them: ESLint reads them
|
|
5
|
+
// as syntax in the one repository-wide lint run (the factory's e2e block), so no `lint:e2e` command exists to judge.
|
|
6
|
+
const E2E_COMMAND = new RegExp([
|
|
7
|
+
String.raw`\btest:(?:e2e|integration|contract)\b|`,
|
|
8
|
+
String.raw`\btypecheck:tests\b|`,
|
|
9
|
+
String.raw`--selectProjects\s+(?:e2e|integration|contract)\b|`,
|
|
10
|
+
String.raw`src\/tests\/(?:world|integration|e2e|contract)\b|`,
|
|
11
|
+
String.raw`jest[^\n|&;]*(?:e2e|integration|contract)`,
|
|
12
|
+
].join(''), 'u');
|
|
13
|
+
const UNIT_RUN_SCRIPTS = ['test', 'test:unit', 'test:ci', 'test:affected', 'test:coverage', 'test:cov'];
|
|
14
|
+
|
|
15
|
+
const runsE2e = text => E2E_COMMAND.test(String(text).replace(/--ignore-pattern[= ]+(?:"[^"]*"|'[^']*'|\S+)/gu, ''));
|
|
16
|
+
const withoutComments = text => text.split('\n').filter(line => !/^\s*#/u.test(line)).join('\n');
|
|
17
|
+
|
|
18
|
+
function inspectHooks(state) {
|
|
19
|
+
for (const hook of ['.husky/pre-commit', '.husky/pre-push']) {
|
|
20
|
+
const text = readTextFile(state.root, hook);
|
|
21
|
+
if (text === null) continue;
|
|
22
|
+
const body = withoutComments(text);
|
|
23
|
+
if (runsE2e(body)) state.finding(state.rule, hook, `${hook} runs integration, e2e or contract. They are manual only; hooks run unit, lint and typecheck.`);
|
|
24
|
+
for (const match of body.matchAll(/npm\s+run\s+([\w:.-]+)/gu)) state.pending.push(match[1]);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function inspectLintStaged(state) {
|
|
29
|
+
const lintStaged = state.pkg?.['lint-staged'];
|
|
30
|
+
if (lintStaged && runsE2e(Object.values(lintStaged).flat().join('\n')))
|
|
31
|
+
state.finding(state.rule, 'package.json', 'lint-staged runs an integration, e2e or contract command. They are manual only.');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function inspectHookScripts(state) {
|
|
35
|
+
for (const name of state.pending) {
|
|
36
|
+
if (state.called.has(name)) continue;
|
|
37
|
+
state.called.add(name);
|
|
38
|
+
const command = state.scripts[name];
|
|
39
|
+
if (typeof command !== 'string') continue;
|
|
40
|
+
if (runsE2e(command)) state.finding(state.rule, 'package.json', `Script ${name} is run by a husky hook and touches integration, e2e or contract. They are manual only.`);
|
|
41
|
+
for (const match of command.matchAll(/npm\s+run\s+([\w:.-]+)/gu)) state.pending.push(match[1]);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function inspectUnitGates(state) {
|
|
46
|
+
if (!state.tree.hasFile(state.jestConfig)) return;
|
|
47
|
+
for (const name of UNIT_RUN_SCRIPTS) {
|
|
48
|
+
const command = state.scripts[name];
|
|
49
|
+
if (typeof command === 'string' && /\bjest\b/u.test(command) && !/--selectProjects\s+unit\b/u.test(command))
|
|
50
|
+
state.finding(state.rule, 'package.json', `Script ${name} runs jest without --selectProjects unit and would run the integration, e2e or contract project.`);
|
|
51
|
+
}
|
|
52
|
+
const jestText = readTextFile(state.root, state.jestConfig) ?? '';
|
|
53
|
+
if (/collectCoverageFrom/u.test(jestText) && !/!src\/tests\/(?:\*\*|e2e)/u.test(jestText))
|
|
54
|
+
state.finding(state.rule, state.jestConfig, 'collectCoverageFrom must exclude src/tests/** so no integration, e2e or contract file counts toward coverage.');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function inspectWorkflows(state) {
|
|
58
|
+
for (const file of state.tree.files().filter(entry => /^\.github\/workflows\/[^/]+\.ya?ml$/u.test(entry))) {
|
|
59
|
+
const text = readTextFile(state.root, file);
|
|
60
|
+
if (text === null) continue;
|
|
61
|
+
const trigger = /^on:.*(?:\n(?:[ \t].*)?$)*/mu.exec(text)?.[0] ?? '';
|
|
62
|
+
if (!/\b(?:push|pull_request)\b/u.test(trigger)) continue;
|
|
63
|
+
if (runsE2e(withoutComments(text)))
|
|
64
|
+
state.finding(state.rule, file, `${file} runs e2e on push or pull_request. Move the e2e job to its own workflow with on: workflow_dispatch only.`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Check hooks, transitive hook scripts, unit scripts, coverage and automatic workflows. */
|
|
69
|
+
export function e2eInAutomaticGate({ root, tree, jestConfig, finding }) {
|
|
70
|
+
const rule = 'HFS_E2E_IN_AUTOMATIC_GATE';
|
|
71
|
+
let pkg = null;
|
|
72
|
+
try { pkg = JSON.parse(readTextFile(root, 'package.json') ?? ''); } catch { /* the package checks own invalid JSON */ }
|
|
73
|
+
const state = { root, tree, jestConfig, finding, rule, pkg, scripts: pkg?.scripts ?? {}, pending: [], called: new Set() };
|
|
74
|
+
inspectHooks(state);
|
|
75
|
+
inspectLintStaged(state);
|
|
76
|
+
inspectHookScripts(state);
|
|
77
|
+
inspectUnitGates(state);
|
|
78
|
+
inspectWorkflows(state);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The be side's default tsconfig excludes the world, integration, e2e and contract trees (they are checked by src/tests/tsconfig.json). */
|
|
82
|
+
export function testTreesOutOfDefaultProgram({ root, tree, finding }) {
|
|
83
|
+
const rule = 'HFS_E2E_IN_AUTOMATIC_GATE';
|
|
84
|
+
const tsconfigText = readTextFile(root, 'tsconfig.json');
|
|
85
|
+
let tsconfig = null;
|
|
86
|
+
try { tsconfig = tsconfigText === null ? null : JSON.parse(tsconfigText); } catch { /* the typecheck itself owns parsing */ }
|
|
87
|
+
if (tsconfig) {
|
|
88
|
+
const excludedText = JSON.stringify(tsconfig.exclude ?? []);
|
|
89
|
+
const excludesTestTrees = ['world', 'integration', 'e2e', 'contract'].every(tree => excludedText.includes(`src/tests/${tree}`));
|
|
90
|
+
const defaultAll = tsconfig.include === undefined && tsconfig.files === undefined;
|
|
91
|
+
const files = tree.files();
|
|
92
|
+
if (files.some(file => /^src\/tests\/(?:world|integration|e2e|contract)\/.+\.[cm]?tsx?$/u.test(file)) && !excludesTestTrees &&
|
|
93
|
+
(defaultAll || JSON.stringify(tsconfig.include ?? []).includes('src')))
|
|
94
|
+
finding(rule, 'tsconfig.json', 'The default tsconfig includes src/tests/{world,integration,e2e,contract}/**. Exclude those trees and check them with src/tests/tsconfig.json (typecheck:tests).');
|
|
95
|
+
}
|
|
96
|
+
}
|