@starci/hfs 4.8.0 → 4.9.1
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 +10 -0
- package/emit/operation-schemas.mjs +3 -0
- package/lint/bound-sys.cjs +2 -2
- package/package.json +1 -1
- package/runtime/engine/admission.mjs +1 -1
- package/runtime/engine/config.mjs +2 -2
- package/runtime/engine/db/machine.mjs +2 -3
- package/runtime/engine/invalid-config.mjs +1 -1
- package/runtime/knowledge/hfs/canon-pins.yaml +22 -14
- package/runtime/knowledge/hfs/rules.yaml +23 -0
- package/runtime/modules/kernel/failure-codes.yaml +10 -0
- package/runtime/scripts/guards/git-policy.mjs +2 -3
- package/runtime/scripts/hfs/rules/pipeline.mjs +5 -5
- package/runtime/scripts/lib/package-at.mjs +5 -6
- package/runtime/scripts/lib/roots.mjs +2 -2
- package/runtime/scripts/work/draw/draw-dna-findings.mjs +23 -12
- package/runtime/scripts/work/draw/draw-dna.mjs +14 -8
- package/scaffold/app.mjs +4 -4
- package/src/main.mjs +3 -3
- package/sync/index.mjs +14 -1
- package/sync/managed.mjs +3 -2
- package/templates/app/codegen-graphql.mjs.partial +4 -4
- package/templates/app/quality-config/sonar-project.properties +4 -2
- package/runtime/engine/canonical-json.mjs +0 -14
- package/runtime/engine/db/ledger-open.mjs +0 -38
- package/runtime/engine/db/ledger-paths.mjs +0 -87
- package/runtime/engine/db/ledger.mjs +0 -1068
- package/runtime/engine/db/provision-authority.mjs +0 -76
- package/runtime/engine/db/provision-operations.mjs +0 -146
- package/runtime/packages/grammar/scripts/build-stamp.mjs +0 -98
- package/runtime/scripts/agent/codex-app-server.mjs +0 -79
- package/runtime/scripts/agent/codex-mirror-source.mjs +0 -34
- package/runtime/scripts/agent/host-agents.mjs +0 -80
- package/runtime/scripts/agent/launch-trust-policy.mjs +0 -53
- package/runtime/scripts/agent/trust-launch.mjs +0 -191
- package/runtime/scripts/agent/trust.mjs +0 -523
- package/runtime/scripts/api/fs/temp-path.mjs +0 -8
- package/runtime/scripts/api/git/diff-names.mjs +0 -8
- package/runtime/scripts/api/git/head-time.mjs +0 -10
- package/runtime/scripts/api/git/index-lock.mjs +0 -78
- package/runtime/scripts/api/git/log.mjs +0 -5
- package/runtime/scripts/api/git/merge-tree.mjs +0 -5
- package/runtime/scripts/api/git/rev-list.mjs +0 -5
- package/runtime/scripts/api/git/rev-parse.mjs +0 -13
- package/runtime/scripts/api/git/status-query.mjs +0 -5
- package/runtime/scripts/api/git/worktree-list-query.mjs +0 -5
- package/runtime/scripts/api/node/spawn-node.mjs +0 -6
- package/runtime/scripts/api/npm/dist-tag.mjs +0 -8
- package/runtime/scripts/api/npm/lib.mjs +0 -32
- package/runtime/scripts/api/npm/pack-dry-run.mjs +0 -9
- package/runtime/scripts/api/npm/run-npm.mjs +0 -6
- package/runtime/scripts/api/npm/view.mjs +0 -30
- package/runtime/scripts/api/npm/whoami.mjs +0 -8
- package/runtime/scripts/api/process/hide-child-windows.mjs +0 -31
- package/runtime/scripts/api/process/kill-tree.mjs +0 -22
- package/runtime/scripts/api/process/lib.mjs +0 -125
- package/runtime/scripts/api/process/probe-guard-command.mjs +0 -74
- package/runtime/scripts/api/process/process-list.mjs +0 -28
- package/runtime/scripts/api/process/system-tool.mjs +0 -37
- package/runtime/scripts/gates/canon-digest.mjs +0 -81
- package/runtime/scripts/gates/canon-pins.mjs +0 -119
- package/runtime/scripts/gates/gate-input.mjs +0 -98
- package/runtime/scripts/gates/gate.mjs +0 -636
- package/runtime/scripts/gates/grammar-dist.mjs +0 -206
- package/runtime/scripts/gates/output.mjs +0 -40
- package/runtime/scripts/gates/runtime-package-selection.mjs +0 -27
- package/runtime/scripts/gates/test-world-run.mjs +0 -252
- package/runtime/scripts/gates/type-impact.mjs +0 -277
- package/runtime/scripts/gates/type-program.mjs +0 -113
- package/runtime/scripts/guards/assigned-command.mjs +0 -12
- package/runtime/scripts/guards/call-rights.mjs +0 -47
- package/runtime/scripts/guards/command-guard.mjs +0 -468
- package/runtime/scripts/guards/command-policy.mjs +0 -342
- package/runtime/scripts/guards/critic-reach.mjs +0 -82
- package/runtime/scripts/guards/deps-guard.mjs +0 -150
- package/runtime/scripts/guards/env-dump-verdict.mjs +0 -89
- package/runtime/scripts/guards/guards-root.mjs +0 -27
- package/runtime/scripts/guards/hook-install.mjs +0 -343
- package/runtime/scripts/guards/hook-verdict.mjs +0 -12
- package/runtime/scripts/guards/install-verdict.mjs +0 -41
- package/runtime/scripts/guards/kernel-seat.mjs +0 -63
- package/runtime/scripts/guards/launch-verdict.mjs +0 -74
- package/runtime/scripts/guards/op-starci.mjs +0 -16
- package/runtime/scripts/guards/op-write-scope.mjs +0 -27
- package/runtime/scripts/guards/orca-self-lifecycle.mjs +0 -39
- package/runtime/scripts/guards/process-kill-verdict.mjs +0 -106
- package/runtime/scripts/guards/protected-zone.mjs +0 -56
- package/runtime/scripts/guards/refusals.mjs +0 -27
- package/runtime/scripts/guards/rights.mjs +0 -268
- package/runtime/scripts/guards/seat-tools.mjs +0 -47
- package/runtime/scripts/guards/shell-commands.mjs +0 -328
- package/runtime/scripts/guards/supervisor-seat.mjs +0 -80
- package/runtime/scripts/guards/verdict-process.mjs +0 -81
- package/runtime/scripts/lib/app-data.mjs +0 -11
- package/runtime/scripts/lib/canon-binding-text.mjs +0 -129
- package/runtime/scripts/lib/check-format.mjs +0 -7
- package/runtime/scripts/lib/failure-code-reads.mjs +0 -100
- package/runtime/scripts/lib/failure-code-template-errors.mjs +0 -161
- package/runtime/scripts/lib/git-dir.mjs +0 -120
- package/runtime/scripts/lib/guard-command.mjs +0 -24
- package/runtime/scripts/lib/interrupt-cleanup.mjs +0 -38
- package/runtime/scripts/lib/json-formats.mjs +0 -34
- package/runtime/scripts/lib/managed-json.mjs +0 -51
- package/runtime/scripts/lib/menu-parts.mjs +0 -11
- package/runtime/scripts/lib/op-gate.mjs +0 -15
- package/runtime/scripts/lib/provision-control-failure.mjs +0 -32
- package/runtime/scripts/lib/release-version.mjs +0 -134
- package/runtime/scripts/lib/runtime-location.mjs +0 -21
- package/runtime/scripts/lib/runtime-package-inventory.mjs +0 -21
- package/runtime/scripts/lib/runtime-package-manifests.mjs +0 -84
- package/runtime/scripts/lib/runtime-publication-tags.mjs +0 -56
- package/runtime/scripts/lib/seat-env.mjs +0 -43
- package/runtime/scripts/lib/sonar-new-code.mjs +0 -58
- package/runtime/scripts/lib/source-package-imports.mjs +0 -111
- package/runtime/scripts/lib/tar-files.mjs +0 -39
- package/runtime/scripts/lib/uat-run-folder.mjs +0 -118
- package/runtime/scripts/lib/worktree-record.mjs +0 -14
- package/runtime/scripts/machine/ask-receipts.mjs +0 -113
- package/runtime/scripts/machine/evidence-store.mjs +0 -223
- package/runtime/scripts/machine/home.mjs +0 -216
- package/runtime/scripts/machine/host-lock.mjs +0 -184
- package/runtime/scripts/machine/lock-recovery.mjs +0 -55
- package/runtime/scripts/machine/roles-contract.mjs +0 -90
- package/runtime/scripts/machine/runtime-change.mjs +0 -20
- package/runtime/scripts/machine/workflow-tree.mjs +0 -122
- package/runtime/scripts/reconciler/checklist-items.mjs +0 -7
- package/runtime/scripts/reconciler/guard-row.mjs +0 -25
- package/runtime/scripts/work/brand/brand-colour.mjs +0 -125
- package/runtime/scripts/work/brand/brand-tokens.mjs +0 -167
- package/runtime/scripts/work/brand/brand-value-source.mjs +0 -106
- package/runtime/scripts/work/brand/brand.mjs +0 -892
- package/runtime/scripts/work/draw/draw-layer.mjs +0 -306
- package/runtime/scripts/work/draw/draw-rationale-page.mjs +0 -237
- package/runtime/scripts/work/draw/draw-rationale-ref.mjs +0 -14
- package/runtime/scripts/work/draw/draw-rationale.mjs +0 -353
- package/runtime/scripts/work/draw/draw-source.mjs +0 -412
- package/runtime/scripts/work/draw/draw-taste.mjs +0 -278
- package/runtime/scripts/work/draw/draw-typecheck.mjs +0 -72
- package/runtime/scripts/work/draw/rationale-findings.mjs +0 -205
- package/runtime/scripts/work/draw-grammar.mjs +0 -175
- package/runtime/scripts/work/draw-render-page.mjs +0 -188
- package/runtime/scripts/work/draw-render.mjs +0 -626
- package/runtime/scripts/work/png.mjs +0 -237
- package/runtime/scripts/work/render-tools.mjs +0 -64
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
// canon-pins.mjs - where the canon pins live and how they are read: knowledge/hfs/canon-pins.yaml (shape:
|
|
2
|
-
// modules/schemas/canon-pins.schema.yaml) and the profiles bound to the canons, modules/models/code-patterns.yaml.
|
|
3
|
-
// The gates read the pins here; scripts/checks/check-canon-pins.mjs is the self-check that judges them.
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { createRequire } from 'node:module';
|
|
7
|
-
import { skillRoot } from '../../engine/runtime-root.mjs';
|
|
8
|
-
import { parseYaml } from '../../engine/yaml.mjs';
|
|
9
|
-
import { grammarLintFor } from '../lib/grammar-lint.mjs';
|
|
10
|
-
import { locateDeclaration } from '../hfs/slots.mjs';
|
|
11
|
-
import { pathKey, posixPath } from '../lib/path-key.mjs';
|
|
12
|
-
import { canonContentDigest, installedFiles } from './canon-digest.mjs';
|
|
13
|
-
|
|
14
|
-
export const PINS_FILE = 'knowledge/hfs/canon-pins.yaml';
|
|
15
|
-
export const SCHEMA_FILE = 'modules/schemas/canon-pins.schema.yaml';
|
|
16
|
-
export const PROFILES_FILE = 'modules/models/code-patterns.yaml';
|
|
17
|
-
/** A missing, mismatched or unjudged installed canon remains a failing gate. */
|
|
18
|
-
export const CANON_INSTALL_CODES = Object.freeze({ missing: 'CANON_INSTALL_MISSING', mismatch: 'CANON_INSTALL_MISMATCH', unjudged: 'CANON_INSTALL_UNJUDGED' });
|
|
19
|
-
|
|
20
|
-
/** The parsed canon pins of the runtime at `root`. */
|
|
21
|
-
export function loadPins(root = skillRoot) {
|
|
22
|
-
return parseYaml(fs.readFileSync(path.join(root, PINS_FILE), 'utf8'));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** Every independently published canon binding of a profile, including each declared Grammar family. */
|
|
26
|
-
export function profileCanonBindings(profile) {
|
|
27
|
-
return [
|
|
28
|
-
{ binding: 'canon', canon: profile?.canon },
|
|
29
|
-
...Object.entries(profile?.familyCanons ?? {}).map(([family, canon]) => ({ binding: `familyCanons.${family}`, family, canon })),
|
|
30
|
-
];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** The zero-based source lines that author actual profile canon versions, including named family bindings. */
|
|
34
|
-
export function canonBindingVersionLines(text) {
|
|
35
|
-
const profiles = parseYaml(text)?.profiles ?? {};
|
|
36
|
-
const bindings = new Map(Object.entries(profiles).flatMap(([profile, value]) => profileCanonBindings(value)
|
|
37
|
-
.filter(row => typeof row.canon?.package === 'string' && typeof row.canon?.version === 'string')
|
|
38
|
-
.map(row => [JSON.stringify(['profiles', profile, ...row.binding.split('.')]), row.canon])));
|
|
39
|
-
const leaves = new Map(), stack = [];
|
|
40
|
-
text.split('\n').forEach((line, index) => {
|
|
41
|
-
const separator = line.indexOf(':'), field = line.slice(0, separator), key = field.trim();
|
|
42
|
-
if (separator < 0 || !/^[A-Za-z_][A-Za-z0-9_-]*$/.test(key)) return;
|
|
43
|
-
const indent = field.length - field.trimStart().length, value = line.slice(separator + 1).trimStart();
|
|
44
|
-
while (stack.length && stack.at(-1).indent >= indent) stack.pop();
|
|
45
|
-
const keys = [...stack.map(entry => entry.key), key];
|
|
46
|
-
let binding = bindings.get(JSON.stringify(keys));
|
|
47
|
-
if (key === 'version') binding = bindings.get(JSON.stringify(keys.slice(0, -1)));
|
|
48
|
-
if (binding && (key === 'version' || /^\{.*\bversion\s*:/.test(value))) leaves.set(index, { package: binding.package, version: binding.version });
|
|
49
|
-
stack.push({ key, indent });
|
|
50
|
-
});
|
|
51
|
-
return leaves;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** The universal binding and the explicitly selected family's binding; no absent selection implies a family. */
|
|
55
|
-
export function selectedProfileCanonBindings(profile, family) {
|
|
56
|
-
const bindings = [{ binding: 'canon', canon: profile?.canon }];
|
|
57
|
-
if (!profile?.familyCanons) return bindings;
|
|
58
|
-
const contract = grammarLintFor(family);
|
|
59
|
-
if (contract.package === null) return bindings;
|
|
60
|
-
const canon = profile.familyCanons[contract.family];
|
|
61
|
-
if (!canon || canon.package !== contract.package) {
|
|
62
|
-
throw new Error(`Grammar family ${contract.family} has no matching code-pattern canon binding for ${contract.package}`);
|
|
63
|
-
}
|
|
64
|
-
return [...bindings, { binding: `familyCanons.${contract.family}`, family: contract.family, canon }];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** The package root resolved from a side's npm install, without admitting an install above the app boundary. */
|
|
68
|
-
function installedPackageRoot(directory, name, bound) {
|
|
69
|
-
const top = pathKey(bound);
|
|
70
|
-
const searched = (createRequire(path.join(directory, 'package.json')).resolve.paths(name) ?? []).filter(modules => pathKey(modules).startsWith(`${top}/`));
|
|
71
|
-
for (const modules of searched) {
|
|
72
|
-
const candidate = path.join(modules, ...name.split('/'));
|
|
73
|
-
if (fs.existsSync(path.join(candidate, 'package.json'))) return fs.realpathSync(candidate);
|
|
74
|
-
}
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** Inspect one selected binding's actual installed bytes; errors remain unavailable and mismatches remain findings. */
|
|
79
|
-
function inspectBinding(root, bound, pins, profile, row, out) {
|
|
80
|
-
const { canon, family } = row, side = pins[canon?.package]?.side;
|
|
81
|
-
const label = row.binding === 'canon' ? profile : `${profile}.${row.binding}`;
|
|
82
|
-
if (!canon?.package || typeof side !== 'string') { out.errors.push(`${CANON_INSTALL_CODES.unjudged} profile ${label}: canon.package ${canon?.package} has no pinned side`); return; }
|
|
83
|
-
const installed = installedPackageRoot(path.join(root, side), canon.package, bound);
|
|
84
|
-
if (!installed) { out.errors.push(`${CANON_INSTALL_CODES.missing} profile ${label}: ${canon.package} does not resolve from ${side}/ of the app; install the pinned ${canon.version}`); return; }
|
|
85
|
-
const where = posixPath(path.relative(root, installed));
|
|
86
|
-
let manifest, version, digest;
|
|
87
|
-
try {
|
|
88
|
-
manifest = JSON.parse(fs.readFileSync(path.join(installed, 'package.json'), 'utf8'));
|
|
89
|
-
version = manifest.version ?? null;
|
|
90
|
-
digest = canonContentDigest(installed, canon.contentDigest, installedFiles(installed));
|
|
91
|
-
} catch (error) { out.errors.push(`${CANON_INSTALL_CODES.unjudged} profile ${label}: ${error.code ?? 'ERROR'} ${error.message}`); return; }
|
|
92
|
-
out.checked.push({ profile: label, ...(family ? { family } : {}), package: canon.package, side, path: where, version, digest: digest.value, files: digest.files });
|
|
93
|
-
if (version !== canon.version || digest.value !== canon.contentDigest.value || digest.files !== canon.contentDigest.files) {
|
|
94
|
-
out.findings.push({ engine: 'canon', rule: 'installed-canon-mismatch', path: where, line: null,
|
|
95
|
-
message: `${canon.package} installed for ${side}/ is ${version} with ${digest.files} files digesting ${digest.value}; profile ${label} binds ${canon.version} with ${canon.contentDigest.files} files digesting ${canon.contentDigest.value} (${CANON_INSTALL_CODES.mismatch}: reinstall the pinned version from the registry)` });
|
|
96
|
-
}
|
|
97
|
-
return Object.keys(manifest.dependencies ?? {});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** Judge the universal and explicitly selected family installs against their independent version/content bindings. */
|
|
101
|
-
export function inspectInstalledCanons(root, { pins, profiles, bound = root }) {
|
|
102
|
-
const out = { checked: [], findings: [], errors: [] };
|
|
103
|
-
for (const [profile, value] of Object.entries(profiles)) {
|
|
104
|
-
let bindings;
|
|
105
|
-
try {
|
|
106
|
-
const declaration = value?.familyCanons ? JSON.parse(fs.readFileSync(locateDeclaration(root).file, 'utf8')) : null;
|
|
107
|
-
bindings = selectedProfileCanonBindings(value, declaration?.sides?.fe?.grammar);
|
|
108
|
-
} catch (error) { out.errors.push(`${CANON_INSTALL_CODES.unjudged} profile ${profile}: ${error.message}`); continue; }
|
|
109
|
-
const available = profileCanonBindings(value), checked = new Set();
|
|
110
|
-
for (const row of bindings) {
|
|
111
|
-
if (checked.has(row.canon?.package)) continue;
|
|
112
|
-
checked.add(row.canon?.package);
|
|
113
|
-
const dependencies = inspectBinding(root, bound, pins, profile, row, out) ?? [];
|
|
114
|
-
// A family adapter's digest does not cover its independently published implementation dependency.
|
|
115
|
-
bindings.push(...available.filter(entry => dependencies.includes(entry.canon?.package) && !checked.has(entry.canon.package)));
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return out;
|
|
119
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
// gate-input.mjs - the one base, Git delta and current-input reader of a code gate and its settle observer.
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { byCodeUnit } from '../lib/list.mjs';
|
|
5
|
-
import { revParseQuery } from '../api/git/rev-parse-query.mjs';
|
|
6
|
-
import { diff as gitDiff } from '../api/git/diff.mjs';
|
|
7
|
-
import { lsFiles } from '../api/git/ls-files.mjs';
|
|
8
|
-
import { mergeBase as mergeBaseOf } from '../api/git/merge-base.mjs';
|
|
9
|
-
import { posixPath, underAny } from '../lib/path-key.mjs';
|
|
10
|
-
import { sha256File } from '../../engine/digest.mjs';
|
|
11
|
-
import { gateBaseAt } from '../machine/workflow-tree.mjs';
|
|
12
|
-
|
|
13
|
-
/** Run a gate-owned Git query with its existing text/rename output settings and caller options. */
|
|
14
|
-
const git = (call, root, args, options = {}) => call(args, { cwd: root, config: { 'core.quotepath': 'off' }, maxBuffer: 256 * 1024 * 1024, ...options });
|
|
15
|
-
|
|
16
|
-
/** Return a completed query's text, or null when the native child failed; input readers reject missing text. */
|
|
17
|
-
const gitText = (call, root, args) => { const r = git(call, root, args); return !r.error && r.status === 0 ? r.stdout : null; };
|
|
18
|
-
export { git as gateGit, gitText as gateGitText, gateDelta };
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* The base commit: --base verified as a commit; else, when --root is a workflow worktree (the registry knows it, never a
|
|
22
|
-
* branch-name guess), the workflow's previous checkpoint (workflow-checkpoint.mjs gateBaseAt), so only the op's own new
|
|
23
|
-
* findings block; else the merge-base of HEAD with its upstream, main or master.
|
|
24
|
-
*/
|
|
25
|
-
export function resolveGateBase(root, base = null, { workflowBase = (dir) => gateBaseAt({ env: process.env }, dir) } = {}) {
|
|
26
|
-
if (base) {
|
|
27
|
-
const sha = gitText(revParseQuery, root, ['--verify', '--quiet', `${base}^{commit}`])?.trim();
|
|
28
|
-
if (!sha) throw Object.assign(new Error(`--base ${base} is not a commit of ${root}`), { code: 'GATE_BASE_UNKNOWN' });
|
|
29
|
-
return sha;
|
|
30
|
-
}
|
|
31
|
-
const checkpoint = workflowBase(root);
|
|
32
|
-
if (checkpoint) return checkpoint;
|
|
33
|
-
for (const ref of ['@{upstream}', 'main', 'master', 'origin/HEAD']) {
|
|
34
|
-
const sha = mergeBaseOf(root, 'HEAD', ref);
|
|
35
|
-
if (sha) return sha;
|
|
36
|
-
}
|
|
37
|
-
throw Object.assign(new Error(`no base: ${root} has no upstream, main or master to measure against; pass --base`), { code: 'GATE_BASE_UNKNOWN' });
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* The delta between the base and the working tree, relative to --root: {changed[] (existing files), added Set, deleted Set,
|
|
42
|
-
* renamed Map(new -> old)}. A rename's old path counts as deleted and its new path is measured against the old path's base
|
|
43
|
-
* blob, so the findings a move carries stay preexisting; untracked files are additions.
|
|
44
|
-
*/
|
|
45
|
-
/** Files one non-rename diff status line into the delta sets. */
|
|
46
|
-
const recordStatus = (kind, rel, { added, deleted, changed }) => {
|
|
47
|
-
if (kind === 'D') { deleted.add(rel); return; }
|
|
48
|
-
changed.add(rel);
|
|
49
|
-
if (kind === 'A') added.add(rel);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
function gateDelta(root, base) {
|
|
53
|
-
const diff = gitText(gitDiff, root, ['--name-status', '-z', '--find-renames', '--relative', base]);
|
|
54
|
-
const untracked = gitText(lsFiles, root, ['--others', '--exclude-standard', '-z']);
|
|
55
|
-
if (diff === null || untracked === null) throw new Error(`the actual gate delta of ${root} could not be listed`);
|
|
56
|
-
const status = String(diff).split('\0');
|
|
57
|
-
const added = new Set(), deleted = new Set(), changed = new Set(), renamed = new Map();
|
|
58
|
-
for (let i = 0; i < status.length && status[i]; i += 1) {
|
|
59
|
-
const kind = status[i], file = status[++i];
|
|
60
|
-
if (!file) throw new Error('the gate diff has an incomplete path record');
|
|
61
|
-
const rel = posixPath(file);
|
|
62
|
-
if (kind.startsWith('R')) {
|
|
63
|
-
const to = status[++i];
|
|
64
|
-
if (!to) throw new Error('the gate rename has no destination');
|
|
65
|
-
deleted.add(rel); changed.add(posixPath(to)); renamed.set(posixPath(to), rel);
|
|
66
|
-
}
|
|
67
|
-
else recordStatus(kind, rel, { added, deleted, changed });
|
|
68
|
-
}
|
|
69
|
-
for (const file of String(untracked).split('\0').filter(Boolean)) { const rel = posixPath(file); changed.add(rel); added.add(rel); }
|
|
70
|
-
return { changed: [...changed].filter((file) => fs.existsSync(path.join(root, file))).sort(byCodeUnit), added, deleted, renamed };
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Read the gate's actual base, HEAD and current input bytes without preparing or running a tool. Deleted paths carry null;
|
|
75
|
-
* named files extend the real delta. Settle uses the same reader against persisted placement and ownership, so a report's
|
|
76
|
-
* changed list cannot manufacture an empty slice or hide a later edit.
|
|
77
|
-
*/
|
|
78
|
-
export function gateInputSnapshot(root, base = null, files = [], owned = null) {
|
|
79
|
-
root = path.resolve(root);
|
|
80
|
-
base = resolveGateBase(root, base);
|
|
81
|
-
const head = gitText(revParseQuery, root, ['--verify', 'HEAD'])?.trim();
|
|
82
|
-
if (!head) throw new Error(`the gate HEAD of ${root} could not be resolved`);
|
|
83
|
-
const delta = gateDelta(root, base);
|
|
84
|
-
const named = files.map((file) => {
|
|
85
|
-
const rel = path.relative(root, path.resolve(root, file));
|
|
86
|
-
if (!rel || rel === '..' || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel))
|
|
87
|
-
throw new Error(`gate input ${file} is not a file inside ${root}`);
|
|
88
|
-
return posixPath(rel);
|
|
89
|
-
});
|
|
90
|
-
if (owned && (!Array.isArray(owned) || !owned.length || named.some((file) => !underAny(file, owned, { dot: true }))))
|
|
91
|
-
throw new Error('the named gate inputs are outside the admitted owned scope');
|
|
92
|
-
const inputs = [...new Set([...delta.changed, ...delta.deleted, ...named])]
|
|
93
|
-
.filter((file) => !owned || underAny(file, owned, { dot: true })).sort(byCodeUnit).map((file) => {
|
|
94
|
-
const absolute = path.join(root, file);
|
|
95
|
-
return { path: file, sha256: fs.existsSync(absolute) ? sha256File(absolute) : null };
|
|
96
|
-
});
|
|
97
|
-
return { root: posixPath(root), base, head, changed: inputs.filter((file) => file.sha256 !== null).map((file) => file.path), inputs };
|
|
98
|
-
}
|