@starci/hfs 4.7.47 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +4 -9
  3. package/emit/operation-schemas.mjs +3 -0
  4. package/lint/bound-sys.cjs +2 -2
  5. package/package.json +11 -5
  6. package/runtime/engine/admission.mjs +1 -1
  7. package/runtime/engine/config.mjs +2 -2
  8. package/runtime/engine/db/machine.mjs +2 -3
  9. package/runtime/engine/invalid-config.mjs +1 -1
  10. package/runtime/knowledge/grammars/{starci → core}/playbook.yaml +3 -3
  11. package/runtime/knowledge/grammars/index.yaml +14 -23
  12. package/runtime/knowledge/grammars/offset-pop/playbook.yaml +2 -2
  13. package/runtime/knowledge/hfs/canon-pins.yaml +23 -23
  14. package/runtime/knowledge/hfs/fe-stack.yaml +1 -1
  15. package/runtime/knowledge/hfs/rules.yaml +83 -60
  16. package/runtime/modules/kernel/failure-codes.yaml +23 -13
  17. package/runtime/scripts/guards/git-policy.mjs +2 -3
  18. package/runtime/scripts/hfs/rule-catalog.mjs +2 -2
  19. package/runtime/scripts/hfs/rules/pipeline.mjs +5 -5
  20. package/runtime/scripts/lib/example-refs.mjs +10 -3
  21. package/runtime/scripts/lib/grammar-lint.mjs +16 -3
  22. package/runtime/scripts/lib/package-at.mjs +5 -6
  23. package/runtime/scripts/lib/roots.mjs +2 -2
  24. package/runtime/scripts/work/draw/draw-dna-findings.mjs +23 -12
  25. package/runtime/scripts/work/draw/draw-dna.mjs +14 -8
  26. package/scaffold/app.mjs +6 -6
  27. package/src/main.mjs +3 -3
  28. package/sync/hygiene.mjs +1 -1
  29. package/sync/index.mjs +15 -2
  30. package/sync/managed.mjs +3 -2
  31. package/templates/app/codegen-graphql.mjs.partial +4 -4
  32. package/templates/app/quality-config/sonar-project.properties +4 -2
  33. package/runtime/engine/canonical-json.mjs +0 -14
  34. package/runtime/engine/db/ledger-open.mjs +0 -38
  35. package/runtime/engine/db/ledger-paths.mjs +0 -87
  36. package/runtime/engine/db/ledger.mjs +0 -1068
  37. package/runtime/engine/db/provision-authority.mjs +0 -76
  38. package/runtime/engine/db/provision-operations.mjs +0 -146
  39. package/runtime/packages/grammar/scripts/build-stamp.mjs +0 -98
  40. package/runtime/scripts/agent/codex-app-server.mjs +0 -79
  41. package/runtime/scripts/agent/codex-mirror-source.mjs +0 -34
  42. package/runtime/scripts/agent/host-agents.mjs +0 -80
  43. package/runtime/scripts/agent/launch-trust-policy.mjs +0 -53
  44. package/runtime/scripts/agent/trust-launch.mjs +0 -191
  45. package/runtime/scripts/agent/trust.mjs +0 -523
  46. package/runtime/scripts/api/fs/temp-path.mjs +0 -8
  47. package/runtime/scripts/api/git/diff-names.mjs +0 -8
  48. package/runtime/scripts/api/git/head-time.mjs +0 -10
  49. package/runtime/scripts/api/git/index-lock.mjs +0 -78
  50. package/runtime/scripts/api/git/log.mjs +0 -5
  51. package/runtime/scripts/api/git/merge-tree.mjs +0 -5
  52. package/runtime/scripts/api/git/rev-list.mjs +0 -5
  53. package/runtime/scripts/api/git/rev-parse.mjs +0 -13
  54. package/runtime/scripts/api/git/status-query.mjs +0 -5
  55. package/runtime/scripts/api/git/worktree-list-query.mjs +0 -5
  56. package/runtime/scripts/api/node/spawn-node.mjs +0 -6
  57. package/runtime/scripts/api/npm/dist-tag.mjs +0 -8
  58. package/runtime/scripts/api/npm/lib.mjs +0 -32
  59. package/runtime/scripts/api/npm/pack-dry-run.mjs +0 -9
  60. package/runtime/scripts/api/npm/run-npm.mjs +0 -6
  61. package/runtime/scripts/api/npm/view.mjs +0 -30
  62. package/runtime/scripts/api/npm/whoami.mjs +0 -8
  63. package/runtime/scripts/api/process/hide-child-windows.mjs +0 -31
  64. package/runtime/scripts/api/process/kill-tree.mjs +0 -22
  65. package/runtime/scripts/api/process/lib.mjs +0 -125
  66. package/runtime/scripts/api/process/probe-guard-command.mjs +0 -74
  67. package/runtime/scripts/api/process/process-list.mjs +0 -28
  68. package/runtime/scripts/api/process/system-tool.mjs +0 -37
  69. package/runtime/scripts/gates/canon-digest.mjs +0 -81
  70. package/runtime/scripts/gates/canon-pins.mjs +0 -119
  71. package/runtime/scripts/gates/gate-input.mjs +0 -98
  72. package/runtime/scripts/gates/gate.mjs +0 -636
  73. package/runtime/scripts/gates/grammar-dist.mjs +0 -206
  74. package/runtime/scripts/gates/output.mjs +0 -40
  75. package/runtime/scripts/gates/runtime-package-selection.mjs +0 -27
  76. package/runtime/scripts/gates/test-world-run.mjs +0 -252
  77. package/runtime/scripts/gates/type-impact.mjs +0 -277
  78. package/runtime/scripts/gates/type-program.mjs +0 -113
  79. package/runtime/scripts/guards/assigned-command.mjs +0 -12
  80. package/runtime/scripts/guards/call-rights.mjs +0 -47
  81. package/runtime/scripts/guards/command-guard.mjs +0 -468
  82. package/runtime/scripts/guards/command-policy.mjs +0 -342
  83. package/runtime/scripts/guards/critic-reach.mjs +0 -82
  84. package/runtime/scripts/guards/deps-guard.mjs +0 -150
  85. package/runtime/scripts/guards/env-dump-verdict.mjs +0 -89
  86. package/runtime/scripts/guards/guards-root.mjs +0 -27
  87. package/runtime/scripts/guards/hook-install.mjs +0 -343
  88. package/runtime/scripts/guards/hook-verdict.mjs +0 -12
  89. package/runtime/scripts/guards/install-verdict.mjs +0 -41
  90. package/runtime/scripts/guards/kernel-seat.mjs +0 -63
  91. package/runtime/scripts/guards/launch-verdict.mjs +0 -74
  92. package/runtime/scripts/guards/op-starci.mjs +0 -16
  93. package/runtime/scripts/guards/op-write-scope.mjs +0 -27
  94. package/runtime/scripts/guards/orca-self-lifecycle.mjs +0 -39
  95. package/runtime/scripts/guards/process-kill-verdict.mjs +0 -106
  96. package/runtime/scripts/guards/protected-zone.mjs +0 -56
  97. package/runtime/scripts/guards/refusals.mjs +0 -27
  98. package/runtime/scripts/guards/rights.mjs +0 -268
  99. package/runtime/scripts/guards/seat-tools.mjs +0 -47
  100. package/runtime/scripts/guards/shell-commands.mjs +0 -328
  101. package/runtime/scripts/guards/supervisor-seat.mjs +0 -80
  102. package/runtime/scripts/guards/verdict-process.mjs +0 -81
  103. package/runtime/scripts/lib/app-data.mjs +0 -11
  104. package/runtime/scripts/lib/canon-binding-text.mjs +0 -129
  105. package/runtime/scripts/lib/check-format.mjs +0 -7
  106. package/runtime/scripts/lib/failure-code-reads.mjs +0 -100
  107. package/runtime/scripts/lib/failure-code-template-errors.mjs +0 -161
  108. package/runtime/scripts/lib/git-dir.mjs +0 -120
  109. package/runtime/scripts/lib/guard-command.mjs +0 -24
  110. package/runtime/scripts/lib/interrupt-cleanup.mjs +0 -38
  111. package/runtime/scripts/lib/json-formats.mjs +0 -34
  112. package/runtime/scripts/lib/managed-json.mjs +0 -51
  113. package/runtime/scripts/lib/menu-parts.mjs +0 -11
  114. package/runtime/scripts/lib/op-gate.mjs +0 -15
  115. package/runtime/scripts/lib/provision-control-failure.mjs +0 -32
  116. package/runtime/scripts/lib/release-version.mjs +0 -134
  117. package/runtime/scripts/lib/runtime-location.mjs +0 -21
  118. package/runtime/scripts/lib/runtime-package-inventory.mjs +0 -21
  119. package/runtime/scripts/lib/runtime-package-manifests.mjs +0 -84
  120. package/runtime/scripts/lib/runtime-publication-tags.mjs +0 -56
  121. package/runtime/scripts/lib/seat-env.mjs +0 -43
  122. package/runtime/scripts/lib/sonar-new-code.mjs +0 -58
  123. package/runtime/scripts/lib/source-package-imports.mjs +0 -111
  124. package/runtime/scripts/lib/tar-files.mjs +0 -39
  125. package/runtime/scripts/lib/uat-run-folder.mjs +0 -118
  126. package/runtime/scripts/lib/worktree-record.mjs +0 -14
  127. package/runtime/scripts/machine/ask-receipts.mjs +0 -113
  128. package/runtime/scripts/machine/evidence-store.mjs +0 -223
  129. package/runtime/scripts/machine/home.mjs +0 -216
  130. package/runtime/scripts/machine/host-lock.mjs +0 -184
  131. package/runtime/scripts/machine/lock-recovery.mjs +0 -55
  132. package/runtime/scripts/machine/roles-contract.mjs +0 -90
  133. package/runtime/scripts/machine/runtime-change.mjs +0 -20
  134. package/runtime/scripts/machine/workflow-tree.mjs +0 -122
  135. package/runtime/scripts/reconciler/checklist-items.mjs +0 -7
  136. package/runtime/scripts/reconciler/guard-row.mjs +0 -25
  137. package/runtime/scripts/work/brand/brand-colour.mjs +0 -125
  138. package/runtime/scripts/work/brand/brand-tokens.mjs +0 -167
  139. package/runtime/scripts/work/brand/brand-value-source.mjs +0 -106
  140. package/runtime/scripts/work/brand/brand.mjs +0 -892
  141. package/runtime/scripts/work/draw/draw-layer.mjs +0 -306
  142. package/runtime/scripts/work/draw/draw-rationale-page.mjs +0 -237
  143. package/runtime/scripts/work/draw/draw-rationale-ref.mjs +0 -14
  144. package/runtime/scripts/work/draw/draw-rationale.mjs +0 -353
  145. package/runtime/scripts/work/draw/draw-source.mjs +0 -412
  146. package/runtime/scripts/work/draw/draw-taste.mjs +0 -278
  147. package/runtime/scripts/work/draw/draw-typecheck.mjs +0 -72
  148. package/runtime/scripts/work/draw/rationale-findings.mjs +0 -205
  149. package/runtime/scripts/work/draw-grammar.mjs +0 -175
  150. package/runtime/scripts/work/draw-render-page.mjs +0 -188
  151. package/runtime/scripts/work/draw-render.mjs +0 -626
  152. package/runtime/scripts/work/png.mjs +0 -237
  153. package/runtime/scripts/work/render-tools.mjs +0 -64
@@ -1,122 +0,0 @@
1
- // workflow-tree.mjs - the host registry's view of a workflow's worktree (machine.sqlite worktrees, kind workflow) and the
2
- // base an op's gate measures against there. Read by the Kernel (scripts/kernel/workflow-worktree.mjs,
3
- // workflow-checkpoint.mjs) and by scripts/gates/gate.mjs, which finds its workflow from the directory it runs in.
4
- import fs from 'node:fs';
5
- import path from 'node:path';
6
- import { mergeBase } from '../api/git/merge-base.mjs';
7
- import { diffNames } from '../api/git/diff-names.mjs';
8
- import { log as gitLog } from '../api/git/log.mjs';
9
- import { revParse as gitRevParse } from '../api/git/rev-parse.mjs';
10
- import { withMachine } from '../../engine/db/machine.mjs';
11
- import { isInside } from '../lib/walk.mjs';
12
- import { requireWorktreeRecord } from '../lib/worktree-record.mjs';
13
-
14
- const SHA = /^[0-9a-f]{40,64}$/;
15
- const insidePath = (child, parent) => isInside(path.resolve(parent), path.resolve(child));
16
- const fail = ({ code }, message) => Object.assign(new Error(message), { code });
17
- const revParse = (cwd, ref) => { const sha = gitRevParse(cwd, ref); return sha && SHA.test(sha) ? sha : null; };
18
- const mainOf = (ctx) => ctx?.main ?? 'main';
19
- const envOf = (ctx) => ctx?.env ?? process.env;
20
-
21
- /** A registry row of a workflow worktree as a record: {workflowId, orcaWorktreeId, path, branch, checkpoint, repoRoot, ...}, or null. */
22
- export const workflowRecordOf = (row) => (row ? { workflowId: row.workflow_id, orcaWorktreeId: row.orca_id, path: path.resolve(row.path), branch: row.branch ?? null,
23
- checkpoint: row.checkpoint_sha ?? null, repoRoot: row.repo_root, ledgerId: row.ledger_id ?? null, createdAt: row.created_at,
24
- releasePending: row.release_pending_at != null } : null);
25
-
26
- /** The live workflow worktree of `workflowId`: {workflowId, orcaWorktreeId, path, branch, checkpoint, repoRoot}, or null. */
27
- export function workflowWorktreeOf(ctx, workflowId) {
28
- const env = envOf(ctx);
29
- try {
30
- return workflowRecordOf(withMachine((m) => m.db.prepare("SELECT * FROM worktrees WHERE kind='workflow' AND workflow_id=? AND orca_id IS NOT NULL AND removed_at IS NULL ORDER BY created_at DESC LIMIT 1").get(workflowId), { env }));
31
- } catch (cause) { throw Object.assign(new Error(`the workflow worktree registry is unavailable: ${cause.message}`, { cause }), { code: 'worktree-registry-unavailable' }); }
32
- }
33
-
34
- /**
35
- * The live workflow worktree whose directory is `dir` (or holds it), or null: how a tool run inside a worktree
36
- * (scripts/gates/gate.mjs) finds its workflow through the registry, never by parsing a branch or folder name.
37
- */
38
- export function workflowWorktreeAt(ctx, dir) {
39
- const env = envOf(ctx);
40
- const at = path.resolve(dir);
41
- const key = (p) => { let r = path.resolve(p); try { r = fs.realpathSync.native(r); } catch { /* missing */ } return process.platform === 'win32' ? r.toLowerCase() : r; };
42
- try {
43
- const rows = withMachine((m) => m.db.prepare("SELECT * FROM worktrees WHERE kind='workflow' AND orca_id IS NOT NULL AND removed_at IS NULL ORDER BY created_at DESC").all(), { env });
44
- const row = rows.find((r) => key(r.path) === key(at)) ?? rows.find((r) => insidePath(key(at), key(r.path)));
45
- return workflowRecordOf(row ?? null);
46
- } catch (cause) { throw Object.assign(new Error(`the workflow worktree registry is unavailable: ${cause.message}`, { cause }), { code: 'worktree-registry-unavailable' }); }
47
- }
48
-
49
- /** The registry functions a caller may replace (ctx.worktree in a spec, the functions above in the runtime). */
50
- const registryOf = (ctx) => ({ workflowWorktreeOf, workflowWorktreeAt, ...ctx?.worktree });
51
-
52
- /** The registry record of the workflow's worktree, its directory present: {workflowId, orcaWorktreeId, path, branch, checkpoint}. */
53
- function presentRecordOf(ctx, workflowId) {
54
- return requireWorktreeRecord(registryOf(ctx).workflowWorktreeOf(ctx, workflowId), workflowId);
55
- }
56
-
57
- /**
58
- * The gate base of the workflow worktree that holds `dir` (part A's workflowWorktreeAt over the registry), or null when
59
- * `dir` is no workflow worktree: scripts/gates/gate.mjs measures an op there against its previous checkpoint.
60
- */
61
- export function gateBaseAt(ctx, dir) {
62
- const rec = registryOf(ctx).workflowWorktreeAt(ctx, dir);
63
- return rec ? gateBaseOf(ctx, rec.workflowId) : null;
64
- }
65
-
66
- /** The base an op's gate measures against: the previous checkpoint, else the merge-base of the workflow branch with main. */
67
- /**
68
- * The newest checkpoint commit of the workflow's own chain on the branch (subject `checkpoint <workflowId>: ...`, first-parent line from HEAD), or null. A registry
69
- * record that lost its checkpoint pointer (a tree put back by the custody repair) must not fall back to the merge-base with main: that would put the base of
70
- * every settled op's gate, and the target of a reset, behind the work those ops committed.
71
- */
72
- function chainCheckpointOf(rec) {
73
- const r = gitLog(['--first-parent', '--format=%H%x09%s', '-n', '256', 'HEAD'], { cwd: rec.path, timeout: 30_000 });
74
- if (r.status !== 0) return null;
75
- const prefix = `checkpoint ${rec.workflowId}:`;
76
- for (const line of String(r.stdout ?? '').split(/\r?\n/)) {
77
- const [sha, subject] = line.split('\t');
78
- if (SHA.test(sha ?? '') && subject?.startsWith(prefix)) return sha;
79
- }
80
- return null;
81
- }
82
-
83
- export function gateBaseOf(ctx, workflowId) {
84
- const rec = presentRecordOf(ctx, workflowId);
85
- const chained = rec.checkpoint ? null : chainCheckpointOf(rec);
86
- if (chained) return chained;
87
- if (rec.checkpoint) {
88
- const sha = revParse(rec.path, rec.checkpoint);
89
- if (!sha) throw fail({ code: 'workflow-gate-base-unknown' }, `the checkpoint ${rec.checkpoint} of workflow ${workflowId} is not a commit of ${rec.path}`);
90
- return sha;
91
- }
92
- const base = mergeBase(rec.path, `refs/heads/${mainOf(ctx)}`, 'HEAD') ?? '';
93
- if (!SHA.test(base)) throw fail({ code: 'workflow-gate-base-unknown' }, `workflow ${workflowId} has no checkpoint and ${rec.branch} has no merge-base with ${mainOf(ctx)}`);
94
- return base;
95
- }
96
-
97
- const CHAIN_WALK_MAX = 64;
98
- /**
99
- * The bases an op's gate may have measured against, newest first (per side, WFWT2 2.2): the current checkpoint, then each
100
- * older checkpoint of the chain while the checkpoint stepped over changed none of `owned` (the op's owned paths, the
101
- * leases checkpointOp commits), stopping at the chain's start (the merge-base with main). A be checkpoint committed while
102
- * an fe op ran therefore never forces the fe op to re-run its gate: what differs between the bases is the other side's
103
- * committed, gated work, and the finish gates the whole branch again. An op that owns no path or owns '.' gets the current
104
- * checkpoint only.
105
- */
106
- export function gateBasesOf(ctx, workflowId, { owned = [] } = {}) {
107
- const current = gateBaseOf(ctx, workflowId);
108
- const paths = owned.map(String).filter(Boolean);
109
- if (!paths.length || paths.some((p) => p === '.' || p === './')) return [current];
110
- const rec = presentRecordOf(ctx, workflowId);
111
- const root = mergeBase(rec.path, `refs/heads/${mainOf(ctx)}`, current);
112
- const bases = [current];
113
- for (let at = current; at !== root && bases.length < CHAIN_WALK_MAX;) {
114
- const parent = revParse(rec.path, `${at}^`);
115
- if (!parent) break;
116
- const touched = diffNames(rec.path, parent, at, { paths: paths.map((p) => `:(literal)${p}`) });
117
- if (touched === null || touched.length) break;
118
- bases.push(parent);
119
- at = parent;
120
- }
121
- return bases;
122
- }
@@ -1,7 +0,0 @@
1
- // The checklist row builders of `start` (scripts/reconciler/start.mjs) and of the row modules split out of it.
2
-
3
- /** One checklist row. status green|red|warn; required rows decide the exit code, warn never does; a green row with `idle: true` is an agent seat that is not running because nothing needs it yet. */
4
- const item = (group, id, name, status, detail = '', { fix = null, required = true, idle = false } = {}) => ({ group, id, name, status, required: required && status !== 'warn', detail, ...(fix && status !== 'green' ? { fix } : {}), ...(idle && status === 'green' ? { idle: true } : {}) });
5
- export const green = (group, id, name, detail, opts) => item(group, id, name, 'green', detail, opts);
6
- export const red = (group, id, name, detail, fix, opts) => item(group, id, name, 'red', detail, { fix, ...opts });
7
- export const warn = (group, id, name, detail, fix) => item(group, id, name, 'warn', detail, { fix, required: false });
@@ -1,25 +0,0 @@
1
- // guard-row.mjs — the host checklist row of the command guard hook: the exact hook command launch trust registers is run
2
- // the way a seat's shell runs it (scripts/api/process/probe-guard-command.mjs), because a hook that does not resolve is a
3
- // non-blocking error and leaves every guarded command unguarded. Required: red names each failing shell.
4
- import { probeGuardCommand } from '../api/process/probe-guard-command.mjs';
5
- import { toolGuardCommand } from '../lib/guard-command.mjs';
6
- import { green, red } from './checklist-items.mjs';
7
-
8
- const NAME = 'command guard resolvable';
9
- const FIX = 'starci runtime link, then launch again to refresh the registered Node and linked runtime CLI paths';
10
-
11
- /** The row of one probe of `toolGuardCommand()`; never throws. Seam: `probe`. */
12
- export function guardCommandRow({ home, probe = probeGuardCommand } = {}) {
13
- let command;
14
- let result;
15
- try {
16
- command = toolGuardCommand({ home });
17
- result = probe({ command, home });
18
- }
19
- catch (error) { return red('preflight', 'guard-command', NAME, `${command ?? 'linked runtime guard'}: ${String(error?.message ?? error).slice(0, 200)}`, FIX); }
20
- if (!result.ok) return red('preflight', 'guard-command', NAME, `${command} does not run: ${result.reason}`.slice(0, 400), FIX);
21
- const ran = result.shells.filter((entry) => !entry.skipped).map((entry) => entry.shell).join(', ');
22
- const skipped = result.shells.filter((entry) => entry.skipped).map((entry) => entry.skipped);
23
- const note = skipped.length ? ` (skipped: ${skipped.join('; ')})` : '';
24
- return green('preflight', 'guard-command', NAME, `${command} runs through ${ran}${note}`);
25
- }
@@ -1,125 +0,0 @@
1
- // brand-colour.mjs - the colour mathematics of the brand checks (brand.mjs): sRGB <-> linear <-> OKLab <-> oklch, colour
2
- // parsing and WCAG contrast. Pure: nothing here reads a file, so a brand is never proven by a dependency that may not be installed.
3
-
4
- const COLOUR_REGEX=Object.freeze({percent:new RegExp(['^[-+]?',String.raw`\d*`,String.raw`\.?`,String.raw`\d+%$`].join('')),number:new RegExp(['^[-+]?',String.raw`\d*`,String.raw`\.?`,String.raw`\d+`,String.raw`(?:e[-+]?\d+)?$`].join(''),'i'),important:new RegExp([String.raw`\s*`,'!important$'].join(''),'i'),call:new RegExp(['^(oklch|rgba?)',String.raw`\(`,String.raw`\s*`,'([^)]*)',String.raw`\)$`].join(''),'i')});
5
-
6
- /** A brand token matches its source within half a unit of the x100 OKLab distance: below perception, wide enough for hex/oklch rounding. */
7
- export const TOKEN_TOLERANCE=0.5;
8
-
9
- /** `value` without a trailing `!important`. */
10
- export const stripImportant=value=>value.replace(COLOUR_REGEX.important,'');
11
-
12
- // ---------------------------------------------------------------------------
13
- // Colour mathematics: sRGB <-> linear <-> OKLab <-> oklch, and WCAG contrast.
14
- // ---------------------------------------------------------------------------
15
-
16
- const clamp01=value=>{if(value<0){return 0;}if(value>1){return 1;}return value;};
17
- /** sRGB transfer function and its inverse; the piecewise form, not the 2.2 approximation. */
18
- const srgbToLinear=channel=>channel<=0.04045?channel/12.92:((channel+0.055)/1.055)**2.4;
19
- const linearToSrgb=channel=>channel<=0.0031308?channel*12.92:1.055*channel**(1/2.4)-0.055;
20
-
21
- /** Linear-light sRGB (0..1 each) to OKLab. */
22
- function linearRgbToOklab([red,green,blue]){
23
- const long=Math.cbrt(0.4122214708*red+0.5363325363*green+0.0514459929*blue);
24
- const medium=Math.cbrt(0.2119034982*red+0.6806995451*green+0.1073969566*blue);
25
- const short=Math.cbrt(0.0883024619*red+0.2817188376*green+0.6299787005*blue);
26
- return {L:0.2104542553*long+0.7936177850*medium-0.0040720468*short,
27
- a:1.9779984951*long-2.4285922050*medium+0.4505937099*short,
28
- b:0.0259040371*long+0.7827717662*medium-0.8086757660*short};
29
- }
30
-
31
- /** OKLab to linear-light sRGB (unclamped: a value outside 0..1 is outside the sRGB gamut). */
32
- function oklabToLinearRgb({L,a,b}){
33
- const long=(L+0.3963377774*a+0.2158037573*b)**3;
34
- const medium=(L-0.1055613458*a-0.0638541728*b)**3;
35
- const short=(L-0.0894841775*a-1.2914855480*b)**3;
36
- return [4.0767416621*long-3.3077115913*medium+0.2309699292*short,
37
- -1.2684380046*long+2.6097574011*medium-0.3413193965*short,
38
- -0.0041960863*long-0.7034186147*medium+1.7076147010*short];
39
- }
40
-
41
- export const rgbToOklab=([red,green,blue])=>linearRgbToOklab([srgbToLinear(red/255),srgbToLinear(green/255),srgbToLinear(blue/255)]);
42
- /** Out-of-gamut OKLab clips per channel; `clipped` says so rather than hiding it. */
43
- export function oklabToRgb(lab){
44
- const linear=oklabToLinearRgb(lab);
45
- const clipped=linear.some(channel=>channel<-1e-6||channel>1+1e-6);
46
- return {rgb:linear.map(channel=>Math.round(clamp01(linearToSrgb(clamp01(channel)))*255)),clipped};
47
- }
48
-
49
- export function oklabToOklch({L,a,b}){
50
- const chroma=Math.hypot(a,b);
51
- const hue=chroma<1e-7?0:(Math.atan2(b,a)*180/Math.PI+360)%360;
52
- return {L,C:chroma,h:hue};
53
- }
54
- export const oklchToOklab=({L,C,h})=>({L,a:C*Math.cos(h*Math.PI/180),b:C*Math.sin(h*Math.PI/180)});
55
- export const formatHex=rgb=>`#${rgb.map(channel=>Math.max(0,Math.min(255,Math.round(channel))).toString(16).padStart(2,'0')).join('')}`;
56
-
57
- const number=(text,{percentOf=1}={})=>{
58
- const value=String(text).trim();
59
- if(COLOUR_REGEX.percent.test(value))return Number.parseFloat(value)/100*percentOf;
60
- if(COLOUR_REGEX.number.test(value))return Number.parseFloat(value);
61
- if(/^none$/i.test(value))return 0;
62
- return null;
63
- };
64
-
65
- /**
66
- * Parses the colour notations a brand record and a stylesheet actually use: `#rgb`/`#rgba`/`#rrggbb`/
67
- * `#rrggbbaa`, `oklch(L C H[/a])` and `rgb()/rgba()`. Alpha is read and reported but never compared:
68
- * a token's identity is its colour. Anything else - a named colour, `var()`, `color-mix()`, `calc()` -
69
- * returns null so the caller fails loudly instead of guessing a value.
70
- */
71
- export function parseColor(input){
72
- const value=String(input??'').trim().replace(COLOUR_REGEX.important,'');
73
- if(!value)return null;
74
- const hex=/^#([0-9a-f]{3,8})$/i.exec(value);
75
- return hex?parseHexColor(hex[1],value):parseFunctionColor(value);
76
- }
77
-
78
- function parseHexColor(body,raw){
79
- if(![3,4,6,8].includes(body.length))return null;
80
- const pairs=body.length<=4?[...body].map(char=>char+char):body.match(/../g);
81
- const [red,green,blue,alpha]=pairs.map(pair=>Number.parseInt(pair,16));
82
- return color({notation:'hex',rgb:[red,green,blue],alpha:alpha===undefined?1:alpha/255,raw});
83
- }
84
-
85
- function parseFunctionColor(value){
86
- const call=COLOUR_REGEX.call.exec(value);
87
- if(!call)return null;
88
- const kind=call[1].toLowerCase();
89
- const [head,tail]=call[2].split('/');
90
- const parts=head.trim().split(/[\s,]+/).filter(Boolean);
91
- if(parts.length<3)return null;
92
- const alpha=tail===undefined?1:number(tail,{percentOf:1});
93
- return kind==='oklch'?parseOklchParts(parts,alpha,value):parseRgbParts(parts,alpha,value);
94
- }
95
-
96
- function parseOklchParts(parts,alpha,raw){
97
- const lightness=number(parts[0],{percentOf:1});
98
- const chroma=number(parts[1],{percentOf:0.4});
99
- const hue=number(parts[2]==='none'?'0':String(parts[2]).replace(/deg$/i,''));
100
- if([lightness,chroma,hue].includes(null))return null;
101
- const lab=oklchToOklab({L:lightness,C:chroma,h:hue});
102
- const {rgb,clipped}=oklabToRgb(lab);
103
- return color({notation:'oklch',rgb,alpha:alpha??1,raw,lab,clipped});
104
- }
105
-
106
- function parseRgbParts(parts,alpha,raw){
107
- const channels=parts.slice(0,3).map(part=>number(part,{percentOf:255}));
108
- if(channels.includes(null))return null;
109
- return color({notation:'rgb',rgb:channels.map(channel=>Math.round(channel)),alpha:alpha??1,raw});
110
- }
111
-
112
- function color({notation,rgb,alpha,raw,lab=null,clipped=false}){
113
- const oklab=lab??rgbToOklab(rgb);
114
- return {notation,raw,rgb,alpha,clipped,oklab,oklch:oklabToOklch(oklab),hex:formatHex(rgb)};
115
- }
116
-
117
- /** Euclidean OKLab distance on the x100 scale: black against white is 100. */
118
- export const deltaEOk=(first,second)=>100*Math.hypot(first.oklab.L-second.oklab.L,first.oklab.a-second.oklab.a,first.oklab.b-second.oklab.b);
119
- /** WCAG 2.x relative luminance of a parsed colour. */
120
- const relativeLuminance=({rgb:[red,green,blue]})=>0.2126*srgbToLinear(red/255)+0.7152*srgbToLinear(green/255)+0.0722*srgbToLinear(blue/255);
121
- /** WCAG 2.x contrast ratio: 21 for black against white, 1 for a colour against itself. */
122
- export function contrastRatio(first,second){
123
- const one=relativeLuminance(first),two=relativeLuminance(second);
124
- return (Math.max(one,two)+0.05)/(Math.min(one,two)+0.05);
125
- }
@@ -1,167 +0,0 @@
1
- // brand-tokens.mjs - reading the real tokens out of the real source for the brand checks (brand.mjs): css custom
2
- // properties by scope, JSON/YAML token files, and the lookup that follows a `var(--other)` through the declared sources.
3
- import fs from 'node:fs';
4
- import path from 'node:path';
5
- import {parseYaml} from '../../../engine/yaml.mjs';
6
- import {grammarDistRefusal} from '../../gates/grammar-dist.mjs';
7
- import {slash} from '../../lib/path-key.mjs';
8
- import {stripImportant} from './brand-colour.mjs';
9
-
10
- const SOURCE_BYTES_LIMIT=4*1024*1024;
11
- const REGEX=Object.freeze({declaration:new RegExp(['^(--[A-Za-z0-9_-]+)',String.raw`\s*:\s*`,String.raw`([\s\S]+)$`].join('')),semicolons:new RegExp([';+','$'].join(''))});
12
-
13
- // ---------------------------------------------------------------------------
14
- // Reading the real tokens out of the real source.
15
- // ---------------------------------------------------------------------------
16
-
17
- /**
18
- * Every `--name: value` declaration of a stylesheet, with the selector that carries it. Declarations are
19
- * split by scope because a brand's `color.tokens[].value` is the default (light) value: a dark override is
20
- * reported as context, never accepted as a match for it.
21
- */
22
- export function parseCssCustomProperties(text){
23
- const source=String(text??'').replace(/\/\*[\s\S]*?\*\//g,' ');
24
- const base=new Map(),dark=new Map(),all=[];
25
- const stack=[];
26
- let buffer='';
27
- const flush=()=>{
28
- const declaration=buffer.trim();
29
- buffer='';
30
- const match=REGEX.declaration.exec(declaration);
31
- if(!match)return;
32
- const selector=stack.filter(Boolean).join(' ');
33
- const value=stripImportant(match[2].trim()).replace(REGEX.semicolons,'').trim();
34
- // `:root:not([data-theme="light"])` inside a dark media query is a dark scope: a negated light theme is not a light one.
35
- const asserted=selector.replace(/:not\([^)]*\)/g,' ');
36
- const isDark=/dark/i.test(selector)&&!/data-theme\s*=\s*["']?light/i.test(asserted);
37
- const entry={name:match[1],value,selector,scope:isDark?'dark':'base'};
38
- all.push(entry);
39
- const target=isDark?dark:base;
40
- if(!target.has(entry.name))target.set(entry.name,entry);
41
- };
42
- for(const character of source){
43
- if(character==='{'){stack.push(buffer.trim());buffer='';continue;}
44
- if(character==='}'){flush();stack.pop();continue;}
45
- if(character===';'){flush();continue;}
46
- buffer+=character;
47
- }
48
- flush();
49
- return {base,dark,all};
50
- }
51
-
52
- /**
53
- * A JSON/YAML token file, flattened by key. Three authored shapes are accepted: a `tokens: [{name,value}]`
54
- * list (the shape the grammar DNA uses), flat `--token: value` keys, and a nested object whose key path
55
- * spells the token (`starci.core.primary` is `--starci-core-primary`).
56
- */
57
- export function parseTokenData(data){
58
- const maps={exact:new Map(),derived:new Map()};
59
- walkTokens(data,[],maps);
60
- return maps;
61
- }
62
-
63
- const putToken=(map,name,value)=>{if(typeof name==='string'&&name&&!map.has(name))map.set(name,String(value));};
64
- const isTokenLeaf=value=>typeof value==='string'||typeof value==='number';
65
-
66
- function walkTokens(node,trail,maps){
67
- if(Array.isArray(node)){
68
- for(const item of node){
69
- if(item&&typeof item==='object')walkTokenItem(item,trail,maps);
70
- }
71
- return;
72
- }
73
- if(node&&typeof node==='object')walkTokenEntries(node,trail,maps);
74
- }
75
-
76
- function walkTokenEntries(node,trail,maps){
77
- for(const [key,value] of Object.entries(node)){
78
- const next=[...trail,key];
79
- if(!isTokenLeaf(value)){walkTokens(value,key.startsWith('--')?trail:next,maps);continue;}
80
- if(key.startsWith('--'))putToken(maps.exact,key,value);
81
- else {putToken(maps.derived,key,value);putToken(maps.derived,`--${next.join('-')}`,value);}
82
- }
83
- }
84
-
85
- /** One object of a `tokens: [{name,value}]` list: a named leaf, else a nested structure. */
86
- function walkTokenItem(item,trail,maps){
87
- if(!isTokenLeaf(item.value)){walkTokens(item,trail,maps);return;}
88
- const name=item.name??item.token;
89
- if(typeof name==='string')putToken(name.startsWith('--')?maps.exact:maps.derived,name,item.value);
90
- }
91
-
92
- export const readText=file=>{
93
- const stat=fs.lstatSync(file);
94
- if(stat.isSymbolicLink()||!stat.isFile()||stat.size>SOURCE_BYTES_LIMIT)throw new Error('A brand source must be a bounded real file.');
95
- return fs.readFileSync(file,'utf8');
96
- };
97
-
98
- const PATH_ESCAPES='the declared path escapes its repository root';
99
- /** Why a declared source path cannot be read inside `sourceRoot`, or null. */
100
- function sourcePathError(sourceRoot,declared){
101
- if(!declared||path.isAbsolute(declared)||declared.split('/').includes('..'))return PATH_ESCAPES;
102
- const file=path.resolve(sourceRoot,declared);
103
- if(path.relative(path.resolve(sourceRoot),file).startsWith('..'))return PATH_ESCAPES;
104
- return fs.existsSync(file)?null:'this repository does not carry the declared file';
105
- }
106
-
107
- /** The token lookup of a read source file: css custom properties, or a JSON/YAML token file. */
108
- function parseSourceText(file,source,text,entry){
109
- if(source.kind==='css'||path.extname(file).toLowerCase()==='.css'){
110
- const parsed=parseCssCustomProperties(text);
111
- return {...entry,found:true,declarations:parsed.all.length,lookup:{css:parsed}};
112
- }
113
- try{
114
- const data=path.extname(file).toLowerCase()==='.json'?JSON.parse(text):parseYaml(text);
115
- const parsed=parseTokenData(data);
116
- return {...entry,found:true,declarations:parsed.exact.size+parsed.derived.size,lookup:{tokens:parsed}};
117
- }catch(error){return {...entry,found:true,error:`unreadable token file: ${String(error.message??error)}`};}
118
- }
119
-
120
- /** One declared source file, read into a token lookup. Never throws: an unreadable source is reported. */
121
- export function readSourceTokens(sourceRoot,source){
122
- const declared=slash(source?.path??'');
123
- const entry={repository:source?.repository??null,path:declared,kind:source?.kind??null,found:false,declarations:0,scope:null};
124
- const pathError=sourcePathError(sourceRoot,declared);
125
- if(pathError)return {...entry,error:pathError};
126
- const file=path.resolve(sourceRoot,declared);
127
- // A source inside a built @starci/grammar dist is read only when that dist is the build of its source:
128
- // a stale dist would bind the brand to tokens the package no longer ships.
129
- const refusal=grammarDistRefusal(file);
130
- if(refusal)return {...entry,found:true,error:refusal.message,staleGrammarDist:{state:refusal.state,root:refusal.root,fix:refusal.fix}};
131
- let text;
132
- try{text=readText(file);}catch(error){return {...entry,found:true,error:String(error.message??error)};}
133
- return parseSourceText(file,source,text,entry);
134
- }
135
-
136
- /** The first declaration of this token across the declared sources, preferring the default scope. */
137
- export function lookupToken(sources,token,seen=new Set()){
138
- if(seen.has(token))return null;
139
- const nextSeen=new Set(seen).add(token);
140
- for(const scope of ['base','dark']){
141
- for(const source of sources){
142
- const hit=lookupInSource(sources,source,token,scope,nextSeen);
143
- if(hit)return hit;
144
- }
145
- }
146
- return null;
147
- }
148
-
149
- /** A css declaration of the token in `scope`, its `var(--other)` followed through the declared sources. */
150
- function resolveCssDeclaration(sources,source,found,scope,seen){
151
- const reference=/^var\(\s*(--[A-Za-z0-9_-]+)\s*\)$/.exec(String(found.value).trim())?.[1]??null;
152
- if(reference){
153
- const resolved=lookupToken(sources,reference,seen);
154
- if(resolved)return {...resolved,file:source.path,selector:found.selector,scope,declaredValue:found.value,resolvedFrom:reference};
155
- }
156
- return {value:found.value,file:source.path,selector:found.selector,scope};
157
- }
158
-
159
- /** The token as one source declares it in `scope` (a token file only answers the base scope), or null. */
160
- function lookupInSource(sources,source,token,scope,seen){
161
- const found=source.lookup?.css?.[scope].get(token);
162
- if(found)return resolveCssDeclaration(sources,source,found,scope,seen);
163
- const tokens=scope==='base'?source.lookup?.tokens:null;
164
- if(!tokens)return null;
165
- const value=tokens.exact.get(token)??tokens.derived.get(token)??tokens.derived.get(token.replace(/^--/,''));
166
- return value===undefined?null:{value,file:source.path,selector:null,scope:'base'};
167
- }
@@ -1,106 +0,0 @@
1
- // brand-value-source.mjs - how a colour token the app has not written yet is traced (brand.mjs tokens-match-source). Two forms:
2
- // {path, value, token?, line?, sha256?} the reference render the value was read from: the file is read, never written.
3
- // {ruling, token, value} the answered ask that ruled the value, read from the ledger's recorded answer; the
4
- // declaration the app still has to make is owed to interface.implement.
5
- // Both pass at the decide stage and are refused at the verify stage while the app source lacks the token (planned-source-missing).
6
- import fs from 'node:fs';
7
- import path from 'node:path';
8
- import {sha256 as digest} from '../../../engine/digest.mjs';
9
- import {slash} from '../../lib/path-key.mjs';
10
- import {parseColor,deltaEOk,TOKEN_TOLERANCE} from './brand-colour.mjs';
11
- import {readSourceTokens,lookupToken} from './brand-tokens.mjs';
12
-
13
- const round=(value,places=4)=>Number.parseFloat(Number(value).toFixed(places));
14
- const collapse=value=>String(value??'').trim().replace(/\s+/g,' ').toLowerCase();
15
- const CUSTOM_PROPERTY=/^--[A-Za-z0-9_-]+$/;
16
- const COLOUR_IN_TEXT=/#[0-9a-f]{3,8}\b|(?:oklch|rgba?)\([^)]*\)/gi;
17
- /** The answerers whose recorded answer is a ruling: the owner, the owner's standing autopilot ruling, and the config's auto-accept of the recommended option. */
18
- const RULING_ANSWERERS=Object.freeze(['owner','autopilot','auto-recommended']);
19
- /** The op that writes the app theme, so the one that owes the declaration of a planned token. */
20
- export const OWED_TO='interface.implement';
21
-
22
- const invalid=why=>({status:'value-source-invalid',why});
23
-
24
- /** The first recorded answer of `ruling` given by one of RULING_ANSWERERS, or the first refusal. */
25
- function rulingAnswer({ruling,brandDir,findReceipt}){
26
- if(typeof findReceipt!=='function')return {ok:false,why:'no ledger was given to read the ruling from'};
27
- const attempts=RULING_ANSWERERS.map(answerer=>findReceipt({acceptedBy:ruling,brandDir,answerer}));
28
- return attempts.find(attempt=>attempt.ok)??attempts[0];
29
- }
30
-
31
- /** Whether the answer's own words (the chosen option, the note, the picks) carry `value`, as the same text or as a colour equal to it. */
32
- function answerCarries(answer,value){
33
- const wanted=parseColor(value);
34
- const texts=[answer.option,answer.note,...Object.values(answer.picks??{})].filter(text=>typeof text==='string');
35
- const sameColour=text=>[...text.matchAll(COLOUR_IN_TEXT)].some(found=>{const colour=parseColor(found[0]);return Boolean(colour&&wanted&&deltaEOk(colour,wanted)<=TOKEN_TOLERANCE);});
36
- return texts.some(text=>collapse(text).includes(collapse(value))||sameColour(text));
37
- }
38
-
39
- /** The shape of a ruling reference, or the refusal naming what is wrong with it. */
40
- function rulingShapeProblem(reference){
41
- if(typeof reference.ruling!=='string'||!reference.ruling.trim())return 'valueSource.ruling must be the dispatch id of the answered ask that ruled this value';
42
- if(typeof reference.token!=='string'||!CUSTOM_PROPERTY.test(reference.token))return 'valueSource.token must be the custom property the app has to declare';
43
- if(typeof reference.value!=='string'||!reference.value.trim())return 'valueSource.value must be the exact value the ruling carries';
44
- if(['path','sha256','line'].some(key=>Object.hasOwn(reference,key)))return 'a ruling valueSource is {ruling, token, value}; a file reference names path and has no ruling';
45
- return null;
46
- }
47
-
48
- /** A token traced to the ledger's recorded answer: the answer exists, was given by a ruling answerer, carries the value, and the value is the brand value. */
49
- function checkRulingSource({token,reference,brandDir,findReceipt}){
50
- const problem=rulingShapeProblem(reference);
51
- if(problem)return invalid(problem);
52
- if(reference.token!==token.token)return invalid(`valueSource.token ${reference.token} must be this brand token, ${token.token}: the app declares it under its brand token name`);
53
- const evidence={ruling:reference.ruling,token:reference.token,value:reference.value,owedTo:OWED_TO};
54
- const answer=rulingAnswer({ruling:reference.ruling,brandDir,findReceipt});
55
- if(!answer.ok)return {...evidence,status:'ruling-unrecorded',why:answer.why};
56
- const given={answeredBy:answer.answeredBy,provisional:answer.provisional,answeredAt:answer.at,receipt:answer.file};
57
- if(!answerCarries(answer,reference.value))return {...evidence,...given,status:'ruling-differs',why:`the answer to ${reference.ruling} does not carry ${reference.value}`};
58
- const expected=parseColor(token.value),ruled=parseColor(reference.value);
59
- if(!expected||!ruled)return {...evidence,...given,status:'unparseable-reference-value',why:'the brand value or the ruled value is not a colour this runtime can parse'};
60
- const delta=round(deltaEOk(expected,ruled),3);
61
- if(delta>TOKEN_TOLERANCE)return {...evidence,...given,deltaE:delta,status:'ruling-differs',why:`the brand value ${token.value} is not the ruled value ${reference.value}`};
62
- return {...evidence,...given,deltaE:delta,status:'planned-from-ruling'};
63
- }
64
-
65
- /**
66
- * A token the app has not written yet may name the external render it was read from:
67
- * `valueSource: {path, value, token?, line?, sha256?}`. `path` is relative to the same --source root as
68
- * `sources[]` (for example `acme-fe/src/app/globals.css`) and is only ever read; `token` is the
69
- * name the reference declares (default: the brand token's own name); `value` is the exact value it declares
70
- * there. The reference passes when that declaration exists in the default (light) scope with exactly that
71
- * text, its colour is the brand value, and a declared sha256 still names the file's bytes.
72
- */
73
- function checkFileSource({sourceRoot,token,reference}){
74
- if(typeof reference.path!=='string'||!reference.path.trim())return invalid('valueSource.path must name the reference file, relative to the --source root');
75
- if(typeof reference.value!=='string'||!reference.value.trim())return invalid('valueSource.value must be the exact value the reference declares');
76
- const name=reference.token??token.token;
77
- if(typeof name!=='string'||!CUSTOM_PROPERTY.test(name))return invalid('valueSource.token must be the custom property the reference declares');
78
- const evidence={path:slash(reference.path),token:name,value:reference.value,line:reference.line??null,declaredSha256:reference.sha256??null};
79
- const extension=path.extname(reference.path).toLowerCase();
80
- const read=readSourceTokens(sourceRoot,{path:reference.path,kind:extension==='.css'?'css':'tokens'});
81
- if(read.error)return {...evidence,status:'reference-unreadable',why:read.error};
82
- if(reference.sha256!==undefined&&reference.sha256!==null){
83
- const computed=digest(fs.readFileSync(path.resolve(sourceRoot,reference.path)));
84
- if(String(reference.sha256).toLowerCase()!==computed)
85
- return {...evidence,computedSha256:computed,status:'reference-digest-mismatch',why:'the reference file is no longer the bytes the brand read'};
86
- }
87
- const found=lookupToken([read],name);
88
- if(!found)return {...evidence,status:'reference-absent',why:`the reference does not declare ${name}`};
89
- if(found.scope==='dark')return {...evidence,actual:found.value,selector:found.selector,status:'reference-only-in-dark-scope',why:`the reference declares ${name} only in a dark scope`};
90
- const declared=[found.value,found.declaredValue].filter(value=>value!==undefined).map(collapse);
91
- if(!declared.includes(collapse(reference.value)))
92
- return {...evidence,actual:found.declaredValue??found.value,selector:found.selector,status:'reference-differs',why:`the reference declares ${name}: ${found.declaredValue??found.value}, not ${reference.value}`};
93
- const expected=parseColor(token.value),planned=parseColor(reference.value);
94
- if(!expected||!planned)return {...evidence,status:'unparseable-reference-value',why:'the brand value or the reference value is not a colour this runtime can parse'};
95
- const delta=round(deltaEOk(expected,planned),3);
96
- if(delta>TOKEN_TOLERANCE)return {...evidence,deltaE:delta,status:'reference-differs',why:`the brand value ${token.value} is not the reference value ${reference.value}`};
97
- return {...evidence,selector:found.selector,deltaE:delta,status:'planned-from-reference'};
98
- }
99
-
100
- /** One planned token's trace: its reference file or its ruling. `findReceipt` reads an answered ask from the ledger (brand.mjs findOwnerReceipt). */
101
- export function checkValueSource({sourceRoot,token,brandDir=null,findReceipt=null}){
102
- const reference=token?.valueSource;
103
- if(!reference||typeof reference!=='object'||Array.isArray(reference))return invalid('valueSource must be an object {path, value, token?, line?, sha256?} or {ruling, token, value}');
104
- if(Object.hasOwn(reference,'ruling'))return checkRulingSource({token,reference,brandDir,findReceipt});
105
- return checkFileSource({sourceRoot,token,reference});
106
- }