@starci/eslint-canon-fe 9.5.23 → 9.6.2
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 +12 -0
- package/docs/button-intent.md +1 -1
- package/package.json +1 -1
- package/runtime/bundle.json +192 -0
- package/runtime/engine/by-code-unit.mjs +6 -6
- package/runtime/engine/model-config.mjs +98 -76
- package/runtime/engine/orca-config.mjs +22 -17
- package/runtime/engine/release-config.mjs +22 -18
- package/runtime/engine/removed-vocabulary.mjs +21 -20
- package/runtime/engine/resources-config.mjs +42 -35
- package/runtime/engine/runtime-root.mjs +11 -13
- package/runtime/engine/secrets.mjs +170 -102
- package/runtime/engine/sonar-config.mjs +16 -13
- package/runtime/engine/temp-root.mjs +3 -9
- package/runtime/knowledge/hfs/canon-pins.yaml +10 -10
- package/runtime/knowledge/hfs/slots.yaml +2 -2
- package/runtime/knowledge/sonar-gate.yaml +1 -1
- package/runtime/packages/hfs/templates/app/package-scripts/package.json +1 -1
- package/runtime/scripts/api/git/cat-file.mjs +0 -1
- package/runtime/scripts/api/git/config-get.mjs +1 -2
- package/runtime/scripts/api/git/diff.mjs +0 -1
- package/runtime/scripts/api/git/lib.mjs +32 -32
- package/runtime/scripts/api/git/ls-files.mjs +0 -1
- package/runtime/scripts/api/git/ls-tree.mjs +0 -1
- package/runtime/scripts/api/git/merge-base.mjs +2 -3
- package/runtime/scripts/api/git/remote.mjs +0 -1
- package/runtime/scripts/api/git/rev-parse-query.mjs +0 -1
- package/runtime/scripts/api/git/show.mjs +0 -1
- package/runtime/scripts/hfs/allows.mjs +37 -38
- package/runtime/scripts/hfs/architecture/ast-walks.mjs +180 -163
- package/runtime/scripts/hfs/architecture/automatic-gates.mjs +80 -73
- package/runtime/scripts/hfs/architecture/backend-app-files.mjs +88 -77
- package/runtime/scripts/hfs/architecture/backend.mjs +378 -357
- package/runtime/scripts/hfs/architecture/background-unowned.mjs +201 -187
- package/runtime/scripts/hfs/architecture/canon-clones.mjs +260 -209
- package/runtime/scripts/hfs/architecture/canon-jsx-clones.mjs +149 -116
- package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +72 -68
- package/runtime/scripts/hfs/architecture/clones.mjs +212 -217
- package/runtime/scripts/hfs/architecture/config-unread.mjs +7 -9
- package/runtime/scripts/hfs/architecture/config.mjs +302 -268
- package/runtime/scripts/hfs/architecture/connection-map.mjs +258 -225
- package/runtime/scripts/hfs/architecture/constructor-deps.mjs +80 -71
- package/runtime/scripts/hfs/architecture/context-coupling.mjs +82 -77
- package/runtime/scripts/hfs/architecture/context-map.mjs +267 -234
- package/runtime/scripts/hfs/architecture/context-owner.mjs +73 -61
- package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +47 -43
- package/runtime/scripts/hfs/architecture/context-transaction.mjs +54 -52
- package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +113 -104
- package/runtime/scripts/hfs/architecture/contracts-readonly.mjs +288 -270
- package/runtime/scripts/hfs/architecture/contracts.mjs +408 -393
- package/runtime/scripts/hfs/architecture/cross-app-duplicate.mjs +50 -51
- package/runtime/scripts/hfs/architecture/dead-exports-bindings.mjs +143 -119
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +286 -228
- package/runtime/scripts/hfs/architecture/default-deny.mjs +129 -110
- package/runtime/scripts/hfs/architecture/doc-language.mjs +24 -19
- package/runtime/scripts/hfs/architecture/entity-registry.mjs +20 -22
- package/runtime/scripts/hfs/architecture/entrypoint.mjs +47 -45
- package/runtime/scripts/hfs/architecture/error-codes.mjs +37 -35
- package/runtime/scripts/hfs/architecture/error-masked.mjs +55 -59
- package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +24 -23
- package/runtime/scripts/hfs/architecture/feature-shape.mjs +40 -38
- package/runtime/scripts/hfs/architecture/framework-pinned.mjs +21 -21
- package/runtime/scripts/hfs/architecture/frontend-block-render.mjs +106 -98
- package/runtime/scripts/hfs/architecture/frontend-grammar.mjs +153 -150
- package/runtime/scripts/hfs/architecture/frontend-routing.mjs +161 -149
- package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +343 -305
- package/runtime/scripts/hfs/architecture/frontend-world.mjs +340 -294
- package/runtime/scripts/hfs/architecture/frontend.mjs +174 -184
- package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +124 -106
- package/runtime/scripts/hfs/architecture/hfs-graph.mjs +52 -48
- package/runtime/scripts/hfs/architecture/hooks-are-hooks.mjs +81 -71
- package/runtime/scripts/hfs/architecture/i18n-keys.mjs +155 -141
- package/runtime/scripts/hfs/architecture/identity.mjs +141 -125
- package/runtime/scripts/hfs/architecture/index.mjs +272 -272
- package/runtime/scripts/hfs/architecture/injection-token-exported.mjs +45 -43
- package/runtime/scripts/hfs/architecture/jwt-ast.mjs +195 -176
- package/runtime/scripts/hfs/architecture/machine-ast.mjs +163 -155
- package/runtime/scripts/hfs/architecture/managed-scripts.mjs +19 -20
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +177 -172
- package/runtime/scripts/hfs/architecture/next-data-contract.mjs +100 -78
- package/runtime/scripts/hfs/architecture/next-data-inference.mjs +170 -154
- package/runtime/scripts/hfs/architecture/next-data-key.mjs +311 -275
- package/runtime/scripts/hfs/architecture/next-data-symbols.mjs +17 -17
- package/runtime/scripts/hfs/architecture/next-data.mjs +351 -334
- package/runtime/scripts/hfs/architecture/operation-contract-roots.mjs +39 -37
- package/runtime/scripts/hfs/architecture/owners.mjs +97 -98
- package/runtime/scripts/hfs/architecture/package-shape.mjs +56 -55
- package/runtime/scripts/hfs/architecture/presentation.mjs +138 -130
- package/runtime/scripts/hfs/architecture/reachability.mjs +258 -241
- package/runtime/scripts/hfs/architecture/register-once.mjs +152 -139
- package/runtime/scripts/hfs/architecture/registration.mjs +301 -267
- package/runtime/scripts/hfs/architecture/required-files.mjs +172 -167
- package/runtime/scripts/hfs/architecture/route-files-thin.mjs +85 -77
- package/runtime/scripts/hfs/architecture/schema-owner.mjs +206 -181
- package/runtime/scripts/hfs/architecture/server-action.mjs +48 -48
- package/runtime/scripts/hfs/architecture/side-boundary.mjs +2 -5
- package/runtime/scripts/hfs/architecture/source-names-shape.mjs +261 -259
- package/runtime/scripts/hfs/architecture/source-names.mjs +304 -278
- package/runtime/scripts/hfs/architecture/sql-keys.mjs +277 -194
- package/runtime/scripts/hfs/architecture/sql-owner.mjs +236 -194
- package/runtime/scripts/hfs/architecture/sql-returning.mjs +46 -45
- package/runtime/scripts/hfs/architecture/sql-token-forms.mjs +86 -74
- package/runtime/scripts/hfs/architecture/sql-tokens.mjs +456 -341
- package/runtime/scripts/hfs/architecture/staged-consumers.mjs +35 -30
- package/runtime/scripts/hfs/architecture/surface.mjs +64 -74
- package/runtime/scripts/hfs/architecture/symbols.mjs +209 -216
- package/runtime/scripts/hfs/architecture/test-world-files.mjs +165 -155
- package/runtime/scripts/hfs/architecture/tiers.mjs +161 -154
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +155 -149
- package/runtime/scripts/hfs/architecture/type-context.mjs +255 -250
- package/runtime/scripts/hfs/architecture/typescript.mjs +358 -318
- package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +110 -92
- package/runtime/scripts/hfs/declaration-shape.mjs +140 -103
- package/runtime/scripts/hfs/declaration-slots.mjs +64 -61
- package/runtime/scripts/hfs/edition-slots.mjs +43 -40
- package/runtime/scripts/hfs/linear-text.mjs +20 -20
- package/runtime/scripts/hfs/param-names.mjs +6 -11
- package/runtime/scripts/hfs/path-findings.mjs +120 -100
- package/runtime/scripts/hfs/repo-identity.mjs +55 -42
- package/runtime/scripts/hfs/rule-catalog.mjs +353 -257
- package/runtime/scripts/hfs/rule-params-shape.mjs +60 -47
- package/runtime/scripts/hfs/rules/database-migrations.mjs +428 -388
- package/runtime/scripts/hfs/rules/fe-no-tests.mjs +37 -36
- package/runtime/scripts/hfs/rules/read.mjs +19 -15
- package/runtime/scripts/hfs/rules/secrets.mjs +1 -1
- package/runtime/scripts/hfs/rules/spec-placement.mjs +12 -13
- package/runtime/scripts/hfs/slot-app-view.mjs +69 -74
- package/runtime/scripts/hfs/slot-classify.mjs +42 -38
- package/runtime/scripts/hfs/slot-errors.mjs +8 -7
- package/runtime/scripts/hfs/slot-imports.mjs +70 -63
- package/runtime/scripts/hfs/slot-manifest-shape.mjs +74 -52
- package/runtime/scripts/hfs/slot-match.mjs +122 -111
- package/runtime/scripts/hfs/slot-path.mjs +0 -2
- package/runtime/scripts/hfs/slot-required.mjs +65 -59
- package/runtime/scripts/hfs/slot-semantic-problems.mjs +138 -112
- package/runtime/scripts/hfs/slot-side-problems.mjs +23 -19
- package/runtime/scripts/hfs/slots.mjs +175 -152
- package/runtime/scripts/hfs/test-world-slot.mjs +0 -1
- package/runtime/scripts/hfs/trailing-slashes.mjs +4 -4
- package/runtime/scripts/hfs/typescript-programs.mjs +66 -69
- package/runtime/scripts/hfs/view.mjs +38 -40
- package/runtime/scripts/lib/dockerfile.mjs +131 -114
- package/runtime/scripts/lib/env.mjs +3 -6
- package/runtime/scripts/lib/event-contract.mjs +73 -78
- package/runtime/scripts/lib/git.mjs +15 -16
- package/runtime/scripts/lib/glob.mjs +23 -14
- package/runtime/scripts/lib/grammar-lint.mjs +33 -37
- package/runtime/scripts/lib/i18n.mjs +32 -35
- package/runtime/scripts/lib/in-order.mjs +57 -38
- package/runtime/scripts/lib/json-clone.mjs +45 -36
- package/runtime/scripts/lib/json.mjs +26 -14
- package/runtime/scripts/lib/language.mjs +133 -129
- package/runtime/scripts/lib/list.mjs +9 -14
- package/runtime/scripts/lib/mutation-fence.mjs +10 -5
- package/runtime/scripts/lib/path-key.mjs +53 -38
- package/runtime/scripts/lib/read-text.mjs +30 -17
- package/runtime/scripts/lib/regex.mjs +1 -2
- package/runtime/scripts/lib/same-text.mjs +0 -1
- package/runtime/scripts/lib/sops-envelope.mjs +148 -120
- package/runtime/scripts/lib/stack-services.mjs +128 -129
- package/runtime/scripts/lib/ts-analysis.mjs +28 -26
- package/runtime/scripts/lib/ts-ast.mjs +36 -42
- package/runtime/scripts/lib/walk.mjs +33 -27
- package/runtime/scripts/lib/yaml-cached.mjs +4 -4
|
@@ -51,7 +51,7 @@ versioning:
|
|
|
51
51
|
# 3.10.0 is a minor (2026-10-29): it ADDS the lite be.platform.database.entities slot, the entity registry (slot fields role and importedBy), so the
|
|
52
52
|
# tier matrix and the public-entry rules admit exactly its <c>Entities imports and its deep importers (persistence.md BE-PERSISTENCE-3).
|
|
53
53
|
# 3.11.0 (ruling:lite-official-postgres-image, 2026-10-29) is, like 3.9.0, a change for lite only: it ADDS the managed app.db.image slot
|
|
54
|
-
# (db/Dockerfile, written by `starci app sync --
|
|
54
|
+
# (db/Dockerfile, written by `starci app sync --execute`), narrows ruleParams.db.extensions to the extensions that image carries, and gives
|
|
55
55
|
# @nestjs/schedule and cron one owner, platform/jobs (R335).
|
|
56
56
|
# 3.13.0 (rulings lite-pgmq-bus-bullmq-jobs and lite-db-image-in-stacks, 2026-10-29) is a change for lite only: ruleParams.db.extensions
|
|
57
57
|
# admits pgmq, the event bus, ruleParams.db.queueOutbox is retired with the pg_cron half of R258, the lite event-bus functions replace
|
|
@@ -940,7 +940,7 @@ slots:
|
|
|
940
940
|
|
|
941
941
|
# ----- app root: the database of a lite app (ruling:lite-self-hosted-postgres-passport-minio): a self-hosted PostgreSQL in the app's
|
|
942
942
|
# stack (the `db` service, built from the managed .starcistacks/images/db/Dockerfile: the official postgres image plus pgvector and pgmq, ruling:lite-db-image-in-stacks), migrated by
|
|
943
|
-
# `starci app db migrate --url-env <NAME>`. A full app keeps its TypeORM migrations under be/ and has no such folder.
|
|
943
|
+
# `starci app db migrate --db-url-env <NAME>`. A full app keeps its TypeORM migrations under be/ and has no such folder.
|
|
944
944
|
- id: app.db
|
|
945
945
|
profiles: [app]
|
|
946
946
|
editions: [lite]
|
|
@@ -41,7 +41,7 @@ newCode:
|
|
|
41
41
|
minReviewedPercent: 100
|
|
42
42
|
unreviewedMax: 0
|
|
43
43
|
# The whole code, not only the new code. Every finding of the HFS canon reaches Sonar through ONE entry, `starci app lint`
|
|
44
|
-
# (packages/hfs, contract changes hfs-sonar-import and hfs-lint-entry). CI runs it per side with --side and --
|
|
44
|
+
# (packages/hfs, contract changes hfs-sonar-import and hfs-lint-entry). CI runs it per side with --side and --out,
|
|
45
45
|
# then merges rules by id and joins issues into ONE file, reports/lint.sonar.json, named by sonar.externalIssuesReportPaths; the gate holds the open-issue count of
|
|
46
46
|
# the whole project at zero. The file carries three engines:
|
|
47
47
|
# starci-hfs the repository findings of `starci app check` (managed files, tree, pins, CI, contracts; the rule id is the finding code)
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"test:integration": "npm run typecheck:tests && cd be && jest --selectProjects integration",
|
|
9
9
|
"test:e2e": "npm run typecheck:tests && cd be && jest --selectProjects e2e",
|
|
10
10
|
"test:contract": "npm run typecheck:tests && cd be && jest --selectProjects contract",
|
|
11
|
-
"test:
|
|
11
|
+
"test:world": "starci app world up",
|
|
12
12
|
{{supabaseScripts}}
|
|
13
13
|
"contract:emit": "starci app emit"
|
|
14
14
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// cat-file.mjs — `git cat-file <args>`: the type, size or content of repository objects (-e, -p, --batch).
|
|
2
2
|
import { runGit } from './lib.mjs';
|
|
3
|
-
|
|
4
3
|
/** The spawn result {status, stdout, stderr, error} of `git cat-file ...args`; options are runGit's (cwd or dir, config, timeout, maxBuffer, input, encoding, env, git). */
|
|
5
4
|
export const catFile = (args = [], options = {}) => runGit(['cat-file', ...args], options);
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
// config-get.mjs — `git config [--local] --get <key>`: one configuration value of a checkout (empty when unset).
|
|
2
2
|
import { gitRunner } from './lib.mjs';
|
|
3
|
-
|
|
4
3
|
/**
|
|
5
4
|
* {ok, stdout, stderr}; `local` reads the repository's own config only; `env` replaces the process env (a caller drops
|
|
6
5
|
* git's repository-local variables). git: the caller's runner or null.
|
|
7
6
|
*/
|
|
8
7
|
export function configGet(cwd, key, { local = false, env = undefined, git = null } = {}) {
|
|
9
|
-
|
|
8
|
+
return gitRunner(git)(['config', ...(local ? ['--local'] : []), '--get', key], env ? { cwd, env } : { cwd });
|
|
10
9
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// diff.mjs — `git diff <args>`: the changes between commits, the index and the working tree.
|
|
2
2
|
import { runGit } from './lib.mjs';
|
|
3
|
-
|
|
4
3
|
/** The spawn result {status, stdout, stderr, error} of `git diff ...args`; options are runGit's (cwd or dir, config, timeout, maxBuffer, input, encoding, env, git). */
|
|
5
4
|
export const diff = (args = [], options = {}) => runGit(['diff', ...args], options);
|
|
@@ -13,7 +13,6 @@ import path from 'node:path';
|
|
|
13
13
|
import { spawnSync } from 'node:child_process';
|
|
14
14
|
import { withoutGitLocalEnv } from '../../lib/git.mjs';
|
|
15
15
|
import { withTempEnv } from '../../../engine/temp-root.mjs';
|
|
16
|
-
|
|
17
16
|
/**
|
|
18
17
|
* Spawn `file` (the git binary) once with `args`: utf8 text, a hidden window, never a shell.
|
|
19
18
|
* Options pass through last, so cwd, timeout, input, env or maxBuffer land as given. encoding:'buffer'
|
|
@@ -21,46 +20,46 @@ import { withTempEnv } from '../../../engine/temp-root.mjs';
|
|
|
21
20
|
* ERR_UNKNOWN_ENCODING, so it reaches spawnSync as encoding:null (Buffer output either way).
|
|
22
21
|
*/
|
|
23
22
|
const gitSpawn = (file, args, options = {}) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
assertMutationFence({ kind: 'git-effect', args });
|
|
24
|
+
const spawn = { encoding: 'utf8', windowsHide: true, ...options };
|
|
25
|
+
if (spawn.encoding === 'buffer')
|
|
26
|
+
spawn.encoding = null;
|
|
27
|
+
// tempChildEnv copies the env (node adds NODE_V8_COVERAGE to the env object it is given: a frozen caller env must not throw under coverage) and points TEMP/TMP/TMPDIR at the temp root
|
|
28
|
+
// reason: encoding:null (the former 'buffer') returns Buffers; callers of that mode read the result as raw bytes
|
|
29
|
+
const spawnOptions = withTempEnv(spawn);
|
|
30
|
+
return spawnSync(file, args, spawnOptions);
|
|
29
31
|
};
|
|
30
|
-
|
|
31
32
|
/**
|
|
32
33
|
* `git args` in `cwd`, or `git -C dir args` when `dir` is given. `git` overrides the binary; `config` ({key: value})
|
|
33
34
|
* prefixes one `-c key=value` per entry (core.quotepath=off, a committer identity). Scoped calls discard ambient
|
|
34
35
|
* hook repository variables; an explicit env remains authoritative, including an intentional temporary index.
|
|
35
36
|
*/
|
|
36
|
-
export const runGit = (args, { cwd = null, dir = null, git = 'git', config = null, ...options } = {}) =>
|
|
37
|
-
gitSpawn(git, [...(dir ? ['-C', dir] : []), ...Object.entries(config ?? {}).flatMap(([key, value]) => ['-c', `${key}=${value}`]), ...args], {
|
|
37
|
+
export const runGit = (args, { cwd = null, dir = null, git = 'git', config = null, ...options } = {}) => gitSpawn(git, [...(dir ? ['-C', dir] : []), ...Object.entries(config ?? {}).flatMap(([key, value]) => ['-c', `${key}=${value}`]), ...args], {
|
|
38
38
|
...(cwd ? { cwd } : {}), ...options,
|
|
39
39
|
...((cwd || dir) && options.env === undefined ? { env: withoutGitLocalEnv(process.env) } : {}),
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
});
|
|
42
41
|
/**
|
|
43
42
|
* A caller's git runner folded to (args, opts) -> {ok, stdout, stderr}. `git`: the caller's runner (args, {cwd}) ->
|
|
44
43
|
* {ok|status, stdout|out, stderr|err} (a spec's fake); null: runGit with a 5-minute timeout and a 64 MB buffer.
|
|
45
44
|
*/
|
|
46
45
|
export const gitRunner = (git = null) => (args, opts = {}) => {
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
const r = git ? git(args, opts) : runGit(args, { timeout: 300_000, maxBuffer: 64 * 1024 * 1024, ...opts });
|
|
47
|
+
return { ok: r?.ok ?? (!r?.error && r?.status === 0), stdout: String(r?.stdout ?? r?.out ?? '').trim(), stderr: String(r?.stderr ?? r?.err ?? (typeof r?.error === 'object' ? r.error?.message : undefined) ?? r?.error ?? '').trim() };
|
|
49
48
|
};
|
|
50
|
-
|
|
51
49
|
let cachedGit;
|
|
52
50
|
const gitPathOf = ({ env, platform, home, exists }) => {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
51
|
+
const p = platform === 'win32' ? path.win32 : path.posix;
|
|
52
|
+
const pathKey = Object.keys(env).find((name) => name.toLowerCase() === 'path');
|
|
53
|
+
const shim = p.join(home, '.starci', 'bin');
|
|
54
|
+
const same = (a, b) => (platform === 'win32' ? p.resolve(a).toLowerCase() === p.resolve(b).toLowerCase() : p.resolve(a) === p.resolve(b));
|
|
55
|
+
const binary = platform === 'win32' ? 'git.exe' : 'git';
|
|
56
|
+
for (const raw of String(pathKey ? env[pathKey] : '').split(platform === 'win32' ? ';' : ':')) {
|
|
57
|
+
const dir = raw.replace(/^"(.*)"$/, '$1');
|
|
58
|
+
const file = dir && p.isAbsolute(dir) && !same(dir, shim) ? p.join(dir, binary) : null;
|
|
59
|
+
if (file && exists(file))
|
|
60
|
+
return file;
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
64
63
|
};
|
|
65
64
|
/**
|
|
66
65
|
* The absolute path of the real git binary, or null: the first PATH directory holding git (git.exe on Windows) that is not
|
|
@@ -69,12 +68,13 @@ const gitPathOf = ({ env, platform, home, exists }) => {
|
|
|
69
68
|
* platform, home, exists) are for a spec and bypass the cache.
|
|
70
69
|
*/
|
|
71
70
|
export const gitExecutable = (options = null) => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
if (!options && cachedGit !== undefined)
|
|
72
|
+
return cachedGit;
|
|
73
|
+
const { env = process.env, platform = process.platform, home = os.homedir(), exists = (file) => fs.statSync(file, { throwIfNoEntry: false })?.isFile() === true } = options ?? {};
|
|
74
|
+
const found = gitPathOf({ env, platform, home, exists });
|
|
75
|
+
if (!options)
|
|
76
|
+
cachedGit = found;
|
|
77
|
+
return found;
|
|
77
78
|
};
|
|
78
|
-
|
|
79
79
|
/** The output ceiling of the whole-diff and `cat-file --batch` calls batch (a release diff is tens of megabytes). */
|
|
80
|
-
const BATCH_MAX_BUFFER = 1024 * 1024 * 1024;
|
|
80
|
+
export const BATCH_MAX_BUFFER = 1024 * 1024 * 1024;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// ls-files.mjs — `git ls-files <args>`: the paths the index tracks (--cached), or the untracked / deleted / ignored ones
|
|
2
2
|
// the flags ask for (-z keeps any file name intact).
|
|
3
3
|
import { runGit } from './lib.mjs';
|
|
4
|
-
|
|
5
4
|
/** The spawn result {status, stdout, stderr, error} of `git ls-files ...args`; options are runGit's (cwd or dir, config, timeout, maxBuffer, input, encoding, env, git). */
|
|
6
5
|
export const lsFiles = (args = [], options = {}) => runGit(['ls-files', ...args], options);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// ls-tree.mjs — `git ls-tree <args>`: the entries of a tree object.
|
|
2
2
|
import { runGit } from './lib.mjs';
|
|
3
|
-
|
|
4
3
|
/** The spawn result {status, stdout, stderr, error} of `git ls-tree ...args`; options are runGit's (cwd or dir, config, timeout, maxBuffer, input, encoding, env, git). */
|
|
5
4
|
export const lsTree = (args = [], options = {}) => runGit(['ls-tree', ...args], options);
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// merge-base.mjs — `git merge-base <a> <b>`: the best common ancestor of two commits. Whether one commit is in another
|
|
2
2
|
// is is-ancestor.mjs.
|
|
3
3
|
import { gitRunner } from './lib.mjs';
|
|
4
|
-
|
|
5
4
|
/** The best common ancestor of commits `a` and `b` (a sha), or null when they share none or a ref does not resolve. */
|
|
6
5
|
export const mergeBase = (cwd, a, b, { git = null } = {}) => {
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const r = gitRunner(git)(['merge-base', a, b], { cwd });
|
|
7
|
+
return r.ok && r.stdout ? r.stdout : null;
|
|
9
8
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// remote.mjs — `git remote <args>`: the configured remotes (get-url <name>).
|
|
2
2
|
import { runGit } from './lib.mjs';
|
|
3
|
-
|
|
4
3
|
/** The spawn result {status, stdout, stderr, error} of `git remote ...args`; options are runGit's (cwd or dir, config, timeout, maxBuffer, input, encoding, env, git). */
|
|
5
4
|
export const remote = (args = [], options = {}) => runGit(['remote', ...args], options);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// rev-parse-query.mjs — `git rev-parse <args>`: one rev-parse query (--show-toplevel, --git-common-dir, --git-path, a ref): the text it prints.
|
|
2
2
|
import { runGit } from './lib.mjs';
|
|
3
|
-
|
|
4
3
|
/** The spawn result {status, stdout, stderr, error} of `git rev-parse ...args`; options are runGit's (cwd or dir, config, timeout, maxBuffer, input, encoding, env, git). */
|
|
5
4
|
export const revParseQuery = (args = [], options = {}) => runGit(['rev-parse', ...args], options);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// show.mjs — `git show <args>`: an object of a repository - a file at a revision (`<rev>:<path>`), or a commit's
|
|
2
2
|
// header and message (-s --format=...).
|
|
3
3
|
import { runGit } from './lib.mjs';
|
|
4
|
-
|
|
5
4
|
/** The spawn result {status, stdout, stderr, error} of `git show ...args`; options are runGit's (cwd or dir, config, timeout, maxBuffer, input, encoding, env, git). */
|
|
6
5
|
export const show = (args = [], options = {}) => runGit(['show', ...args], options);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { braceVariants, globExpression } from '../lib/glob.mjs';
|
|
3
3
|
import { declaredLogicOf } from './manifest-shape.mjs';
|
|
4
|
-
|
|
5
4
|
/**
|
|
6
5
|
* What a slot's `requires`, `allows` and `forbids` entries (knowledge/hfs/slots.yaml) say about a file, read through the
|
|
7
6
|
* resolver of scripts/hfs/slots.mjs. The machine checks that judge "this folder holds only what its slot allows"
|
|
@@ -14,65 +13,65 @@ import { declaredLogicOf } from './manifest-shape.mjs';
|
|
|
14
13
|
*/
|
|
15
14
|
const VAR = /<([A-Za-z][A-Za-z0-9-]*)>/g;
|
|
16
15
|
const SPEC = /\.spec\.ts$/;
|
|
17
|
-
|
|
18
16
|
/**
|
|
19
17
|
* Whether `relative` is the unit spec `<stem>.spec.ts` of a measured file beside it that the slot names: a file the slot declares in
|
|
20
18
|
* `logic`, or a `requires`/`allows` file of a logic role (ruleParams.be.logicRoles), in a slot whose coverage is required and whose tests
|
|
21
19
|
* are unit-beside. The slot admits that spec without listing it, as unit-test-colocated (R47) accepts it.
|
|
22
20
|
*/
|
|
23
21
|
function isMeasuredSpec(resolver, classified, slot, relative, named) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
if (!SPEC.test(relative) || slot.coverage !== 'required' || slot.tests !== 'unit-beside')
|
|
23
|
+
return false;
|
|
24
|
+
const subject = relative.replace(SPEC, '.ts');
|
|
25
|
+
if (declaredLogicOf(slot).includes(subject))
|
|
26
|
+
return true;
|
|
27
|
+
const role = /\.([a-z0-9-]+)\.ts$/.exec(subject)?.[1];
|
|
28
|
+
// An app resolver keeps no rule parameters of its own: a path on a side reads that side's.
|
|
29
|
+
const params = classified.side ? resolver.sides?.[classified.side]?.ruleParams() : resolver.ruleParams?.();
|
|
30
|
+
return (params?.logicRoles ?? []).includes(role) && named.some((matcher) => matcher.expression.test(subject));
|
|
31
31
|
}
|
|
32
|
-
|
|
33
32
|
function matchersOf(entries, bindings) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
return (entries ?? []).flatMap((entry) => {
|
|
34
|
+
const directory = entry.endsWith('/');
|
|
35
|
+
const body = directory ? entry.slice(0, -1) : entry;
|
|
36
|
+
const filled = body.replace(VAR, (_whole, name) => bindings[name] ?? '*');
|
|
37
|
+
return braceVariants(filled).map((variant) => ({ entry, directory, expression: globExpression(directory ? `${variant}/**` : variant) }));
|
|
38
|
+
});
|
|
40
39
|
}
|
|
41
|
-
|
|
42
40
|
/** The path of `file` below the instance root of the slot that owns it. */
|
|
43
41
|
export function relativeToRoot(file, root) {
|
|
44
|
-
|
|
42
|
+
return root && file.startsWith(`${root}/`) ? file.slice(root.length + 1) : path.posix.basename(file);
|
|
45
43
|
}
|
|
46
|
-
|
|
47
44
|
/**
|
|
48
45
|
* Whether the slot that owns `file` names it in `requires` or `allows` (and does not forbid it); null when no enabled slot
|
|
49
46
|
* owns it. Unlike allowsFile it judges a slot that lists only `requires` too, so a folder closed by its slot (an app's
|
|
50
47
|
* src/) is read from the slot and never from a check's own list.
|
|
51
48
|
*/
|
|
52
49
|
export function slotAdmitsFile(resolver, file) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
const classified = resolver.classifyPath(file);
|
|
51
|
+
if (classified.status === 'no-slot' || classified.status === 'ambiguous' || !classified.slot)
|
|
52
|
+
return null;
|
|
53
|
+
const slot = resolver.slot(classified.slot);
|
|
54
|
+
const relative = relativeToRoot(classified.path, classified.root);
|
|
55
|
+
const named = matchersOf([...(slot.requires ?? []), ...(slot.allows ?? [])], classified.bindings);
|
|
56
|
+
const hit = named.some(matcher => matcher.expression.test(relative)) || isMeasuredSpec(resolver, classified, slot, relative, named);
|
|
57
|
+
const forbidden = matchersOf(slot.forbids, classified.bindings).some(matcher => matcher.expression.test(relative) || matcher.expression.test(`${relative}/`));
|
|
58
|
+
return hit && !forbidden;
|
|
61
59
|
}
|
|
62
|
-
|
|
63
60
|
/**
|
|
64
61
|
* {slot, root, relative, allowed, entry?, forbiddenBy?} for a repository-relative file, or null when no enabled slot owns
|
|
65
62
|
* it or the slot names no `allows`. `allowed` is true when the relative path matches a `requires` or `allows` entry.
|
|
66
63
|
*/
|
|
67
64
|
export function allowsFile(resolver, file) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
const classified = resolver.classifyPath(file);
|
|
66
|
+
if (classified.status === 'no-slot' || classified.status === 'ambiguous' || !classified.slot)
|
|
67
|
+
return null;
|
|
68
|
+
const slot = resolver.slot(classified.slot);
|
|
69
|
+
if (!slot?.allows?.length)
|
|
70
|
+
return null;
|
|
71
|
+
const relative = relativeToRoot(classified.path, classified.root);
|
|
72
|
+
const named = matchersOf([...(slot.requires ?? []), ...slot.allows], classified.bindings);
|
|
73
|
+
const hit = named.find(matcher => matcher.expression.test(relative));
|
|
74
|
+
const spec = !hit && isMeasuredSpec(resolver, classified, slot, relative, named);
|
|
75
|
+
const forbidden = matchersOf(slot.forbids, classified.bindings).find(matcher => matcher.expression.test(relative) || matcher.expression.test(`${relative}/`));
|
|
76
|
+
return { slot: slot.id, root: classified.root, relative, allowed: (Boolean(hit) || spec) && !forbidden, entry: hit?.entry, forbiddenBy: forbidden?.entry, allows: slot.allows };
|
|
78
77
|
}
|