arkgate 3.6.1 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/README.md +4 -4
- package/bin/ark-check.mjs +3 -3
- package/bin/ark-layer-match.mjs +2 -1
- package/bin/lib/analysis-engine.mjs +6 -6
- package/bin/lib/architecture-scan.mjs +66 -13
- package/bin/lib/design-smells.mjs +67 -14
- package/bin/lib/doctor-advisories.mjs +20 -5
- package/bin/lib/doctor-plan.mjs +5 -7
- package/bin/lib/html-report-advisories.mjs +59 -0
- package/bin/lib/parse-health.mjs +74 -0
- package/bin/lib/reshape-decisions.mjs +284 -0
- package/bin/lib/ts-resolve.mjs +3 -2
- package/dist/eslint/index.cjs +2 -2
- package/dist/eslint/index.js +2 -2
- package/dist/index.cjs +7 -7
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -6
- package/docs/agent-guide.md +33 -2
- package/docs/ai-gates.md +3 -2
- package/docs/configuration.md +6 -0
- package/docs/package-surface.md +6 -5
- package/docs/typescript-support.md +9 -0
- package/package.json +2 -1
- package/server.json +2 -2
- package/templates/skills/ark-autopilot.md +12 -0
- package/templates/skills/ark-explore.md +8 -1
- package/templates/skills/ark-fix.md +11 -1
- package/templates/skills/ark-loop.md +14 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,52 @@ All notable changes to ArkGate (`arkgate`; formerly `ark-runtime-kernel`) are do
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 3.7.0 — 2026-07-17
|
|
8
|
+
|
|
9
|
+
Phase Y turns field feedback into explicit decision memory, more honest design/parse advisories,
|
|
10
|
+
safer skill-driven edits, and one confirmed purity-bypass closure. **No breaking** CLI, MCP tool,
|
|
11
|
+
analysis IR, or `ark.config.json` changes. **No product-policy gate weakening. No automatic
|
|
12
|
+
reshape or codemod.**
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Recorded reshape decisions (Y01):** `.ark/reshape-decisions.json` can explicitly accept,
|
|
17
|
+
defer, or reject a physical-cohesion pilot with a required reason and optional `reviewBy`.
|
|
18
|
+
Current deferred/rejected decisions suppress only repeated pilot pressure; the underlying
|
|
19
|
+
facts remain visible. Stale, expired, malformed, duplicate, or oversized records suppress
|
|
20
|
+
nothing, and doctor/HTML surfaces render the lifecycle.
|
|
21
|
+
- **Hollow-persistence advisory (Y02):** the existing `handler-in-persistence` smell now sees
|
|
22
|
+
static framework HTTP imports/re-exports, route definitions, and handler shapes inside
|
|
23
|
+
Persistence-role modules. It stays advisory and judgment-only, with a bounded and explicit
|
|
24
|
+
inspection envelope.
|
|
25
|
+
- **Parse-health honesty (Y03):** doctor JSON/human output and the HTML report expose governed
|
|
26
|
+
files with parser diagnostics through additive `parseHealth` totals and a deterministic,
|
|
27
|
+
overflow-aware file list. The existing AST is reused; the verdict and exit code are unchanged.
|
|
28
|
+
- **Exact `process` module dual (Y08):** `forbiddenGlobals: ["process"]` now owns exact value
|
|
29
|
+
imports of `process` and `node:process` across CLI cold/warm cache, pure IR, atomic preflight,
|
|
30
|
+
AICodeGate/MCP, and ESLint. It emits one `FORBIDDEN_GLOBAL` voice; type-only forms within the
|
|
31
|
+
documented TypeScript envelope, subpaths, and `child_process` remain excluded.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- **Mechanical-edit hygiene (Y04):** `ark-fix`, `ark-autopilot`, and `ark-loop` now require
|
|
36
|
+
injected headers to merge into an existing doc block, typed `defineRoute<…>` calls to retain
|
|
37
|
+
their generics/shape, and convention-only placeholder modules to remain uncreated. A
|
|
38
|
+
deterministic eval guards all three outcomes.
|
|
39
|
+
- **Cycle budgets (Y05):** package and Linux hook/doctor p95 ceilings were re-measured once from
|
|
40
|
+
clean 3.6.1 candidates with evidence-backed headroom, preserving fixed release guardrails.
|
|
41
|
+
- **Confidence budget:** Y08 adds multi-form ESLint regressions while the full-suite branch floor
|
|
42
|
+
is recalibrated from 85% to 84.5%; statement/function/line floors and mutation gates are
|
|
43
|
+
unchanged.
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- **Portable peer isolation:** slice identities are case-normalized, so mixed-case paths do not
|
|
48
|
+
produce a false cross-slice violation on case-insensitive filesystems. Repositories that differ
|
|
49
|
+
only by path case intentionally share the portable identity.
|
|
50
|
+
- **Convergence remediation direction:** an unplanned removed dependency now tells the user to
|
|
51
|
+
restore it; an unplanned added dependency still tells the user to remove it.
|
|
52
|
+
|
|
7
53
|
## 3.6.1 — 2026-07-17
|
|
8
54
|
|
|
9
55
|
Codex project-scoped MCP fix. **No breaking** CLI, MCP tool, or `ark.config.json` changes.
|
package/README.md
CHANGED
|
@@ -16,9 +16,9 @@ and makes sure a “green” check means something real.
|
|
|
16
16
|
|
|
17
17
|
</div>
|
|
18
18
|
|
|
19
|
-
> **ArkGate 3.
|
|
20
|
-
>
|
|
21
|
-
>
|
|
19
|
+
> **ArkGate 3.7.0** is current stable: Phase Y adds explicit reshape decisions, hollow-
|
|
20
|
+
> persistence and parse-health advisories, safer mechanical edits, and exact `process` module-
|
|
21
|
+
> dual enforcement. [Release notes](docs/releases/3.7.0.md).
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
@@ -382,7 +382,7 @@ for real systems. Details: [production-hardening.md](https://github.com/pedrokni
|
|
|
382
382
|
| Security reporting | [SECURITY.md](SECURITY.md) |
|
|
383
383
|
| Demos | [docs/demos/](docs/demos/) |
|
|
384
384
|
| Examples | [examples/](examples/README.md) |
|
|
385
|
-
| Latest release (3.
|
|
385
|
+
| Latest release (3.7.0) | [release notes](docs/releases/3.7.0.md) · [3.0.0 baseline](docs/releases/3.0.0.md) |
|
|
386
386
|
| Roadmap and decisions | [ROADMAP.md](ROADMAP.md) · [ADRs](docs/adr/) · [Changelog](CHANGELOG.md) |
|
|
387
387
|
|
|
388
388
|
---
|
package/bin/ark-check.mjs
CHANGED
|
@@ -1133,7 +1133,7 @@ async function main() {
|
|
|
1133
1133
|
);
|
|
1134
1134
|
}
|
|
1135
1135
|
|
|
1136
|
-
const { violations, warnings, safety } = runArchitectureScan({
|
|
1136
|
+
const { violations, warnings, safety, parseHealth } = runArchitectureScan({
|
|
1137
1137
|
root,
|
|
1138
1138
|
config,
|
|
1139
1139
|
manifest,
|
|
@@ -1148,7 +1148,7 @@ async function main() {
|
|
|
1148
1148
|
configPath: path.isAbsolute(args.config) ? args.config : path.join(root, args.config),
|
|
1149
1149
|
configMissing: !fs.existsSync(path.isAbsolute(args.config) ? args.config : path.join(root, args.config)),
|
|
1150
1150
|
safety,
|
|
1151
|
-
ts,
|
|
1151
|
+
ts, parseHealth,
|
|
1152
1152
|
});
|
|
1153
1153
|
return;
|
|
1154
1154
|
}
|
|
@@ -1320,7 +1320,7 @@ async function main() {
|
|
|
1320
1320
|
originJustCreated: !existingOrigin,
|
|
1321
1321
|
adoption: adoptionForReport,
|
|
1322
1322
|
designDepth,
|
|
1323
|
-
advisories: computeDoctorAdvisories(root, config, coverage, rules, files, ts),
|
|
1323
|
+
advisories: computeDoctorAdvisories(root, config, coverage, rules, files, ts, parseHealth),
|
|
1324
1324
|
};
|
|
1325
1325
|
const html = args.beginner
|
|
1326
1326
|
? renderBeginnerHtmlReport(reportPayload)
|
package/bin/ark-layer-match.mjs
CHANGED
|
@@ -138,6 +138,7 @@ export function layerForRelativePath(relPath, layers) {
|
|
|
138
138
|
/**
|
|
139
139
|
* Extract the slice id under a known folder name.
|
|
140
140
|
* Includes the parent folder so `features/auth` ≠ `modules/auth`.
|
|
141
|
+
* Identity is case-normalized for portable results across filesystems.
|
|
141
142
|
* `src/features/auth/api.ts` + folders `["features"]` → `"features/auth"`.
|
|
142
143
|
*/
|
|
143
144
|
export function sliceIdForPath(relPath, sliceFolders) {
|
|
@@ -149,7 +150,7 @@ export function sliceIdForPath(relPath, sliceFolders) {
|
|
|
149
150
|
const folders = new Set(sliceFolders.map((s) => String(s).toLowerCase()));
|
|
150
151
|
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
151
152
|
if (folders.has(parts[i].toLowerCase())) {
|
|
152
|
-
return `${parts[i]}/${parts[i + 1]}`;
|
|
153
|
+
return `${parts[i].toLowerCase()}/${parts[i + 1].toLowerCase()}`;
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
return undefined;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// GENERATED from src/kernel/analysis.ts by scripts/generate-analysis-engine.mjs.
|
|
2
2
|
// Do not edit this file directly.
|
|
3
|
-
function $(e){let t=2166136261;for(let n=0;n<e.length;n+=1)t^=e.charCodeAt(n),t=Math.imul(t,16777619);return`fnv1a-${(t>>>0).toString(16).padStart(8,"0")}`}function x(e){if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(x).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(n=>`${JSON.stringify(n)}:${x(t[n])}`).join(",")}}`}var
|
|
3
|
+
function $(e){let t=2166136261;for(let n=0;n<e.length;n+=1)t^=e.charCodeAt(n),t=Math.imul(t,16777619);return`fnv1a-${(t>>>0).toString(16).padStart(8,"0")}`}function x(e){if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(x).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(n=>`${JSON.stringify(n)}:${x(t[n])}`).join(",")}}`}var Ce=new Map;function Ie(e){return/[.*+?^${}()|[\]\\]/.test(e)?`\\${e}`:e}function X(e){let t="";for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="\\"&&n+1<e.length){let i=e[n+1];if("*?{}[],".includes(i)||i==="\\"){t+="\\"+i,n+=1;continue}t+="/";continue}t+=r}return t}function Ue(e){let t=0;for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="\\"){n+=1;continue}if(r==="{")t+=1;else if(r==="}"&&(t-=1,t<0))return!1}return t===0}function D(e){let t=Ce.get(e);if(t)return t;let n=X(e),r=Ue(n),i="",a=0;for(let d=0;d<n.length;d+=1){let l=n[d];l==="\\"&&d+1<n.length?(i+=Ie(n[d+1]),d+=1):l==="*"?n[d+1]==="*"?n[d+2]==="/"?(i+="(?:.*/)?",d+=2):(i+=".*",d+=1):i+="[^/]*":l==="?"?i+="[^/]":l==="{"&&r?(i+="(?:",a+=1):l==="}"&&r&&a>0?(i+=")",a-=1):l===","&&r&&a>0?i+="|":i+=Ie(l)}let o=new RegExp(`^${i}$`);return Ce.set(e,o),o}function Q(e){let t=X(String(e)),r=t.split("*")[0].split("/").filter(Boolean).length,i=t.replace(/\*/g,"").length;return r*1e4+i}function _(e,t){let n=String(e).split(/[/\\]/).join("/"),r,i=-1;for(let a of t??[])if(!(a.exclude??[]).some(o=>D(o).test(n))){for(let o of a.patterns??[])if(D(o).test(n)){let d=Q(o);d>i&&(i=d,r=a.name)}}return r}function ke(e,t){if(!t?.length)return;let n=String(e).split(/[/\\]/).filter(Boolean),r=new Set(t.map(i=>String(i).toLowerCase()));for(let i=0;i<n.length-1;i+=1)if(r.has(n[i].toLowerCase()))return`${n[i].toLowerCase()}/${n[i+1].toLowerCase()}`}function Be(e){let t=new Set;for(let n of e??[]){let i=X(String(n)).split("/").filter(Boolean);for(let a=0;a<i.length;a+=1){let o=i[a];if((o==="**"||o==="*")&&a>0){let d=i[a-1];d&&!d.includes("*")&&!d.includes("{")&&!d.includes("}")&&t.add(d)}}}return[...t]}function ze(e,t,n){if(Array.isArray(e.sliceFolders)&&e.sliceFolders.length>0)return e.sliceFolders.filter(i=>typeof i=="string"&&i.length>0);let r=(n??[]).find(i=>i.name===t);return Be(r?.patterns)}function G(e,t,n,r){for(let i of e??[])if(!(i.from!==t||i.to!==n)&&i.allowed===!1){if(i.peerIsolation){let a=r?.fromPath,o=r?.toPath;if(!a||!o)continue;let d=ze(i,t,r?.layers);if(d.length===0)continue;let l=ke(a,d),u=ke(o,d);if(!l||!u)continue;if(l!==u)return i;continue}if(t!==n)return i}}var ee="1.0";var U=class extends Error{issues;source;constructor(t,n){super(`Invalid architecture change map (${t}):
|
|
4
4
|
${n.map(r=>`- ${r.path}: ${r.message}`).join(`
|
|
5
|
-
`)}`),this.name="ArchitectureChangeMapValidationError",this.source=t,this.issues=n}};function te(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function ne(e,t,n,r){for(let i of Object.keys(e))t.includes(i)||r.push({path:`${n}.${i}`,message:"unknown field"})}function P(e,t,n,r){let i=e[t];if(typeof i=="string"&&i.length>0)return i;r.push({path:`${n}.${t}`,message:"must be a non-empty string"})}function Be(e){let t=e.replace(/\\/g,"/");if(!t||t.startsWith("/")||/^[A-Za-z]:\//.test(t)||t.includes("\0"))return;let n=[];for(let i of t.split("/"))if(!(!i||i==="."))if(i===".."){if(n.length===0)return;n.pop()}else n.push(i);let r=n.join("/");return r&&r===t?r:void 0}function ze(e,t,n="architecture change map"){let r=[];if(!te(e))throw new U(n,[{path:"$",message:"must be an object"}]);ne(e,["$schema","schemaVersion","files","dependencies"],"$",r);let i=P(e,"$schema","$",r),a=P(e,"schemaVersion","$",r);a&&a!==ee&&r.push({path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(a)}; expected ${ee}`});let o=new Set(t.layers.map(d=>d.name)),l=[],p=new Set;!Array.isArray(e.files)||e.files.length===0?r.push({path:"$.files",message:"must be a non-empty array"}):e.files.forEach((d,f)=>{let y=`$.files[${f}]`;if(!te(d)){r.push({path:y,message:"must be an object"});return}ne(d,["path","operation","layer"],y,r);let h=P(d,"path",y,r),A=P(d,"operation",y,r),C=P(d,"layer",y,r),k=h?Be(h):void 0;h&&!k&&r.push({path:`${y}.path`,message:"must be a canonical project-relative path"}),A&&!["create","update","delete"].includes(A)&&r.push({path:`${y}.operation`,message:"must be create, update, or delete"}),C&&!o.has(C)&&r.push({path:`${y}.layer`,message:`references unknown layer ${JSON.stringify(C)}`}),k&&p.has(k)&&r.push({path:`${y}.path`,message:`duplicates planned path ${k}`}),k&&p.add(k);let Z=k?_(k,t.layers):void 0;k&&C&&o.has(C)&&Z!==C&&r.push({path:`${y}.layer`,message:Z?`${k} resolves to ${Z}, not ${C}`:`${k} is not assigned to an architecture layer`}),k&&A&&["create","update","delete"].includes(A)&&C&&l.push({path:k,operation:A,layer:C})});let u=[],m=new Map(l.map(d=>[d.path,d.operation])),g=new Set,c=e.dependencies??[];if(Array.isArray(c)?c.forEach((d,f)=>{let y=`$.dependencies[${f}]`;if(!te(d)){r.push({path:y,message:"must be an object"});return}ne(d,["from","to"],y,r);let h=P(d,"from",y,r),A=P(d,"to",y,r);h&&!p.has(h)&&r.push({path:`${y}.from`,message:`must reference a planned file path: ${h}`}),A&&!p.has(A)&&r.push({path:`${y}.to`,message:`must reference a planned file path: ${A}`}),h&&m.get(h)==="delete"&&r.push({path:`${y}.from`,message:`cannot depend from deleted file ${h}`}),A&&m.get(A)==="delete"&&r.push({path:`${y}.to`,message:`cannot depend on deleted file ${A}`}),h&&A&&h===A&&r.push({path:y,message:"must not declare a self dependency"});let C=h&&A?`${h}\0${A}`:void 0;C&&g.has(C)&&r.push({path:y,message:`duplicates dependency ${h} -> ${A}`}),C&&g.add(C),h&&A&&u.push({from:h,to:A})}):r.push({path:"$.dependencies",message:"must be an array"}),r.length>0)throw new U(n,r);let s={$schema:i,schemaVersion:ee,files:l.sort((d,f)=>d.path.localeCompare(f.path)),dependencies:u.sort((d,f)=>d.from.localeCompare(f.from)||d.to.localeCompare(f.to))};return{map:s,hash:$(x(s))}}function w(e){return`${e.from}->${e.to}`}function F(e,t,n,r="dependency"){return{id:`${e}:${r}:${w(t)}`,classification:e,subject:"dependency",from:t.from,to:t.to,message:n,...e==="missing"?{nextAction:`Add the planned dependency ${w(t)} to the candidate, then preflight again.`}:e==="contradictory"?{nextAction:`Replace the reverse dependency with ${w(t)}, then preflight again.`}:e==="unplanned"?{nextAction:`Remove the unplanned dependency ${w(t)} from the candidate, then preflight again.`}:{}}}function re(e){let t=[],n=new Map(e.changeMap.map.files.map(c=>[c.path,c])),r=new Map(e.changes.map(c=>[c.path,c])),i=new Map(e.changeMap.map.dependencies.map(c=>[w(c),c])),a=new Map(e.baseDependencies.map(c=>[w(c),c])),o=new Map(e.candidateDependencies.map(c=>[w(c),c]));for(let c of[...n.values()].sort((s,d)=>s.path.localeCompare(d.path))){let s=r.get(c.path);s?s.operation!==c.operation?t.push({id:`contradictory:file:${c.path}`,classification:"contradictory",subject:"file",path:c.path,expectedOperation:c.operation,actualOperation:s.operation,message:`${c.path} was planned as ${c.operation} but the actual operation is ${s.operation}.`,nextAction:`Change ${c.path} to the planned ${c.operation} operation, then preflight again.`}):t.push({id:`satisfied:file:${c.path}`,classification:"satisfied",subject:"file",path:c.path,expectedOperation:c.operation,actualOperation:s.operation,message:`${c.path} matches the planned ${c.operation} operation.`}):t.push({id:`missing:file:${c.path}`,classification:"missing",subject:"file",path:c.path,expectedOperation:c.operation,message:`${c.path} was planned as ${c.operation} but is absent from the actual change.`,nextAction:`${c.operation[0].toUpperCase()}${c.operation.slice(1)} ${c.path} in the complete change set, then preflight again.`})}for(let c of[...r.values()].sort((s,d)=>s.path.localeCompare(d.path)))n.has(c.path)||t.push({id:`unplanned:file:${c.path}`,classification:"unplanned",subject:"file",path:c.path,actualOperation:c.operation,message:`${c.path} has an unplanned ${c.operation} operation.`,nextAction:`Remove ${c.path} from the change set, then preflight again.`});let l=new Set;for(let c of[...i.values()].sort((s,d)=>w(s).localeCompare(w(d)))){if(o.has(w(c))){t.push(F("satisfied",c,`${c.from} -> ${c.to} exists in the candidate architecture.`));continue}let s={from:c.to,to:c.from};o.has(w(s))?(l.add(w(s)),t.push(F("contradictory",c,`${c.from} -> ${c.to} was planned, but the candidate contains the reverse edge.`))):t.push(F("missing",c,`${c.from} -> ${c.to} is absent from the candidate architecture.`))}let p=new Set([...n.keys(),...r.keys()]);for(let[c,s]of[...o].sort(([d],[f])=>d.localeCompare(f)))a.has(c)||i.has(c)||l.has(c)||!p.has(s.from)&&!p.has(s.to)||t.push({...F("unplanned",s,`${s.from} -> ${s.to} was added without a matching planned dependency.`,"dependency-added"),actualOperation:"added"});let u=new Set(e.changeMap.map.files.filter(c=>c.operation==="delete").map(c=>c.path));for(let[c,s]of[...a].sort(([d],[f])=>d.localeCompare(f)))o.has(c)||u.has(s.from)||u.has(s.to)||!p.has(s.from)&&!p.has(s.to)||t.push({...F("unplanned",s,`${s.from} -> ${s.to} was removed without a planned file deletion.`,"dependency-removed"),actualOperation:"removed"});let m={satisfied:0,missing:1,contradictory:2,unplanned:3};t.sort((c,s)=>m[c.classification]-m[s.classification]||(c.subject===s.subject?0:c.subject==="file"?-1:1)||c.id.localeCompare(s.id));let g={satisfied:t.filter(c=>c.classification==="satisfied").length,missing:t.filter(c=>c.classification==="missing").length,contradictory:t.filter(c=>c.classification==="contradictory").length,unplanned:t.filter(c=>c.classification==="unplanned").length};return{schemaVersion:"1.0",readOnly:!0,changeMapHash:e.changeMap.hash,structurallyConverged:g.missing===0&&g.contradictory===0&&g.unplanned===0,behavioralCompletion:"not-evaluated",summary:g,findings:t}}function M(e,t){return t&&e.isStringLiteralLike(t)?t.text:void 0}function we(e,t){return e.getLineAndCharacterOfPosition(t.getStart(e)).line+1}function Ie(e,t){if(e.isImportDeclaration(t)){let n=t.importClause;if(!n)return!1;if(n.isTypeOnly)return!0;let r=n.namedBindings;return!!(r&&e.isNamedImports(r)&&r.elements.length>0&&r.elements.every(i=>i.isTypeOnly))}if(e.isExportDeclaration(t)){if(t.isTypeOnly)return!0;let n=t.exportClause;return!!(n&&e.isNamedExports(n)&&n.elements.length>0&&n.elements.every(r=>r.isTypeOnly))}return!1}function Se(e,t){let n={noLib:!0,noResolve:!0,target:e.ScriptTarget.Latest},r=e.createCompilerHost(n,!0);return r.getSourceFile=i=>i===t.fileName?t:void 0,r.fileExists=i=>i===t.fileName,r.readFile=i=>i===t.fileName?t.text:void 0,e.createProgram([t.fileName],n,r).getTypeChecker()}function ie(e,t){try{return e.getSymbolAtLocation(t)}catch{return}}function ae(e,t,n,r){let i=r.parent&&e.isShorthandPropertyAssignment(r.parent)&&r.parent.name===r,a;try{a=i?t.getShorthandAssignmentValueSymbol(r.parent):ie(t,r)}catch{a=void 0}return!!a?.declarations?.some(o=>o.getSourceFile().fileName===n.fileName)}function se(e,t){let n,r=[],i=(o,l,p,u=!1)=>r.push({specifier:p,kind:l,line:we(t,o),typeOnly:u,unresolved:p===void 0,node:o}),a=o=>{if(e.isImportDeclaration(o))i(o,"import",M(e,o.moduleSpecifier),Ie(e,o));else if(e.isExportDeclaration(o)&&o.moduleSpecifier)i(o,"export",M(e,o.moduleSpecifier),Ie(e,o));else if(e.isImportEqualsDeclaration(o)&&e.isExternalModuleReference(o.moduleReference))i(o,"require",M(e,o.moduleReference.expression));else if(e.isCallExpression(o)){let l=o.expression.kind===e.SyntaxKind.ImportKeyword,u=e.isIdentifier(o.expression)&&o.expression.text==="require"&&!ae(e,n??(n=Se(e,t)),t,o.expression);(l||u)&&i(o,u?"require":"dynamic-import",M(e,o.arguments[0]))}e.forEachChild(o,a)};return a(t),r}function qe(e,t){let n=[],r=t;for(;e.isPropertyAccessExpression(r)||e.isElementAccessExpression(r);){if(e.isPropertyAccessExpression(r))n.unshift(r.name.text);else{let i=M(e,r.argumentExpression);if(i===void 0)return;n.unshift(i)}r=r.expression}if(e.isIdentifier(r))return n.unshift(r.text),{root:r,segments:n}}function Ke(e,t){let n=t.parent;return e.isPropertyAccessExpression(n)||e.isElementAccessExpression(n)?!1:e.isExpressionNode(t)&&!e.isInTypeQuery(t)||e.isShorthandPropertyAssignment(n)&&n.name===t}function ke(e,t){let n=t[0]==="globalThis"?t.slice(1):t;for(let r=n.length;r>=1;r-=1){let i=n.slice(0,r).join(".");if(e.has(i))return i}}function oe(e,t,n){if(n.length===0)return[];let r=new Set(n),i=Se(e,t),a=new Map,o=new Set;for(let c of t.statements)if(e.isVariableStatement(c))for(let s of c.declarationList.declarations)e.isIdentifier(s.name)&&o.add(s.name.text);let l=c=>{let s=qe(e,c);if(!s)return;let d=ie(i,s.root),f=d?a.get(d):void 0;return f?[...f,...s.segments.slice(1)]:ae(e,i,t,s.root)||o.has(s.root.text)?void 0:s.segments};for(let c of t.statements)if(e.isVariableStatement(c))for(let s of c.declarationList.declarations){if(!s.initializer||!e.isIdentifier(s.name))continue;let d=l(s.initializer),f=ie(i,s.name);!d||!f||a.set(f,d)}let p=[],u=new Set,m=(c,s)=>{let d=we(t,s),f=`${c}:${s.getStart(t)}`;u.has(f)||(u.add(f),p.push({name:c,line:d,node:s}))},g=c=>{let s=c.parent&&(e.isPropertyAccessExpression(c.parent)||e.isElementAccessExpression(c.parent))&&c.parent.expression===c;if((e.isPropertyAccessExpression(c)||e.isElementAccessExpression(c))&&!s){let d=l(c),f=d?ke(r,d):void 0;f&&m(f,c)}else e.isIdentifier(c)&&r.has(c.text)&&Ke(e,c)&&!ae(e,i,t,c)&&m(c.text,c);if(e.isVariableDeclaration(c)&&e.isObjectBindingPattern(c.name)&&c.initializer){let d=l(c.initializer);if(d)for(let f of c.name.elements){if(!e.isIdentifier(f.name))continue;let y=f.propertyName?M(e,f.propertyName)??f.propertyName.text:f.name.text,h=ke(r,[...d,y]);h&&m(h,c.initializer)}}e.forEachChild(c,g)};return g(t),p}var ce={RAW_EVENT_PUBLISH:"Publish through a registered intent creator; raw event objects or intent strings bypass Ark contracts and tooling.",PUBLISH_MISSING_SOURCE:"Strict Ark publish calls must include metadata.source."};function xe(e){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function Ye(e){if(!e.publishCall)return[];let t=[];return(e.rawIntentName!==void 0&&xe(e.rawIntentName)||e.objectHasIntent)&&t.push({ruleId:"RAW_EVENT_PUBLISH",message:ce.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&t.push({ruleId:"PUBLISH_MISSING_SOURCE",message:ce.PUBLISH_MISSING_SOURCE}),t}var pe=Object.freeze(["network","filesystem","clock","randomness","environment","process","persistence"]),B=Object.freeze({fetch:"network",XMLHttpRequest:"network",Date:"clock","Date.now":"clock","Math.random":"randomness","process.env":"environment",process:"process"}),de=Object.freeze(Object.keys(B).sort()),le=Object.freeze({fs:"filesystem","node:fs":"filesystem","fs/promises":"filesystem","node:fs/promises":"filesystem","fs-extra":"filesystem","graceful-fs":"filesystem",memfs:"filesystem",chokidar:"filesystem",http:"network",https:"network",http2:"network",net:"network",tls:"network",dgram:"network",dns:"network","node:http":"network","node:https":"network","node:http2":"network","node:net":"network","node:tls":"network","node:dgram":"network","node:dns":"network",axios:"network",undici:"network","node-fetch":"network",got:"network",ky:"network",superagent:"network",ws:"network",process:"process","node:process":"process",child_process:"process","node:child_process":"process","@prisma/client":"persistence",prisma:"persistence",pg:"persistence",mysql:"persistence",mysql2:"persistence",mongodb:"persistence",mongoose:"persistence",sqlite3:"persistence","better-sqlite3":"persistence",redis:"persistence",ioredis:"persistence",typeorm:"persistence",knex:"persistence","drizzle-orm":"persistence",sequelize:"persistence",kysely:"persistence","@supabase/supabase-js":"persistence"});function j(e){if(!e||e.startsWith(".")||e.startsWith("/"))return null;let t=le[e];if(t)return t;let n=e.indexOf("/");if(n<0)return null;let r=e.slice(0,n),i=le[r];if(i)return i;let a=e.indexOf("/",n+1);return a<0?null:le[e.slice(0,a)]??null}function V(e){let t=e.split(".");for(let n=t.length;n>=1;n-=1){let r=t.slice(0,n).join("."),i=B[r];if(i)return i}return null}function z(e){if(e?.pure===!0)return[...pe].sort();let n=(e?.capabilities?.deny??[]).filter(r=>pe.includes(r));return[...new Set(n)].sort()}function We(e,t){if(t.length===0)return!1;let n=new Set(t),r=e.split(".");for(let i=r.length;i>=1;i-=1)if(n.has(r.slice(0,i).join(".")))return!0;return!1}function q(e){let t=new Set,n=new Set,r=Object.keys(B);for(let i of e?.forbiddenGlobals??[]){let a=r.filter(o=>o===i||o.startsWith(`${i}.`));if(a.length===0)n.add(i);else for(let o of a)t.add(`ambient:${o}`)}for(let i of z(e)){t.add(`import:${i}`);for(let a of r)V(a)===i&&t.add(`ambient:${a}`)}return{atoms:[...t].sort(),rawGlobals:[...n].sort()}}function Je(e){let t=new Set,n=V(e);n&&t.add(n);for(let[r,i]of Object.entries(B))(r===e||r.startsWith(`${e}.`))&&t.add(i);return[...t].sort()}function Ze(e,t){let n=[];for(let r of se(e,t)){if(r.typeOnly||!r.specifier)continue;let i=j(r.specifier);i&&n.push({capability:i,symbol:r.specifier,line:r.line,source:"import-based"})}for(let r of oe(e,t,de)){let i=V(r.name);i&&n.push({capability:i,symbol:r.name,line:r.line,source:"ambient-global"})}return n.sort((r,i)=>r.line-i.line||r.capability.localeCompare(i.capability)||r.symbol.localeCompare(i.symbol))}var ue="https://unpkg.com/arkgate@2/schemas/ark.config.schema.json",Ee=["DomainModel","ApplicationOrchestration","PersistenceAdapters","IntegrationAdapters","WorkflowSagaEngine","BackgroundJobsScheduling","PresentationAdapters","ReportingReadModels","ExtensibilityMetadata","SecurityAuditObservability","Kernel"],Xe=new Set(["PresentationAdapters->ApplicationOrchestration","ApplicationOrchestration->DomainModel","WorkflowSagaEngine->ApplicationOrchestration","WorkflowSagaEngine->DomainModel","BackgroundJobsScheduling->ApplicationOrchestration"]);function Qe(){let e=[];for(let t of Ee)for(let n of Ee)t===n||Xe.has(`${t}->${n}`)||e.push({from:t,to:n,allowed:!1});return e}var Oe=Qe();var O={type:"array",items:{type:"string",minLength:1},uniqueItems:!0},$e={$schema:"https://json-schema.org/draft/2020-12/schema",$id:ue,title:"ArkGate architecture contract",description:"Versioned contract consumed identically by ArkGate CLI, MCP, and ESLint surfaces.",type:"object",additionalProperties:!1,required:["$schema","schemaVersion","include","layers","rules"],properties:{$schema:{type:"string",minLength:1,default:ue,description:"Editor-facing URL or local path for this JSON Schema."},schemaVersion:{type:"string",const:"1.0",default:"1.0"},name:{type:"string",minLength:1},include:{...O,minItems:1,default:["src"]},exclude:{...O,default:[]},excludeGenerated:{type:"boolean",default:!0},frameworkOverlay:{type:"string",minLength:1},layers:{type:"array",default:[],items:{$ref:"#/$defs/layer"}},rules:{type:"array",default:Oe,items:{$ref:"#/$defs/rule"}},cyclePolicy:{type:"string",enum:["strict","soft","framework-soft","off"],default:"strict"},dynamicImportAllowlist:{...O,default:[]},safety:{$ref:"#/$defs/safety",default:{maxTsSuppressions:0,maxAnyCasts:0,allowInMemory:!1,allowDisabledPeerIsolation:!1}}},$defs:{layer:{type:"object",additionalProperties:!1,required:["name","patterns"],properties:{name:{type:"string",minLength:1},patterns:{...O,minItems:1},exclude:O,intentPrefixes:O,description:{type:"string",minLength:1},forbiddenGlobals:O,capabilities:{type:"object",additionalProperties:!1,properties:{deny:{type:"array",uniqueItems:!0,items:{type:"string",enum:["network","filesystem","clock","randomness","environment","process","persistence"]}}}},pure:{type:"boolean"},mayImportInfrastructure:{type:"boolean"},optional:{type:"boolean"}}},rule:{type:"object",additionalProperties:!1,required:["from","to","allowed"],properties:{from:{type:"string",minLength:1},to:{type:"string",minLength:1},allowed:{type:"boolean"},message:{type:"string",minLength:1},peerIsolation:{type:"boolean"},sliceFolders:{...O,minItems:1}}},safety:{type:"object",additionalProperties:!1,properties:{maxTsSuppressions:{type:"integer",minimum:0,default:0},maxAnyCasts:{type:"integer",minimum:0,default:0},allowInMemory:{type:"boolean",default:!1},allowDisabledPeerIsolation:{type:"boolean",default:!1}}}}},T=class extends Error{issues;source;constructor(t,n){super(`Invalid ArkGate config (${t}):
|
|
5
|
+
`)}`),this.name="ArchitectureChangeMapValidationError",this.source=t,this.issues=n}};function te(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function ne(e,t,n,r){for(let i of Object.keys(e))t.includes(i)||r.push({path:`${n}.${i}`,message:"unknown field"})}function R(e,t,n,r){let i=e[t];if(typeof i=="string"&&i.length>0)return i;r.push({path:`${n}.${t}`,message:"must be a non-empty string"})}function qe(e){let t=e.replace(/\\/g,"/");if(!t||t.startsWith("/")||/^[A-Za-z]:\//.test(t)||t.includes("\0"))return;let n=[];for(let i of t.split("/"))if(!(!i||i==="."))if(i===".."){if(n.length===0)return;n.pop()}else n.push(i);let r=n.join("/");return r&&r===t?r:void 0}function Ke(e,t,n="architecture change map"){let r=[];if(!te(e))throw new U(n,[{path:"$",message:"must be an object"}]);ne(e,["$schema","schemaVersion","files","dependencies"],"$",r);let i=R(e,"$schema","$",r),a=R(e,"schemaVersion","$",r);a&&a!==ee&&r.push({path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(a)}; expected ${ee}`});let o=new Set(t.layers.map(p=>p.name)),d=[],l=new Set;!Array.isArray(e.files)||e.files.length===0?r.push({path:"$.files",message:"must be a non-empty array"}):e.files.forEach((p,f)=>{let m=`$.files[${f}]`;if(!te(p)){r.push({path:m,message:"must be an object"});return}ne(p,["path","operation","layer"],m,r);let h=R(p,"path",m,r),A=R(p,"operation",m,r),C=R(p,"layer",m,r),k=h?qe(h):void 0;h&&!k&&r.push({path:`${m}.path`,message:"must be a canonical project-relative path"}),A&&!["create","update","delete"].includes(A)&&r.push({path:`${m}.operation`,message:"must be create, update, or delete"}),C&&!o.has(C)&&r.push({path:`${m}.layer`,message:`references unknown layer ${JSON.stringify(C)}`}),k&&l.has(k)&&r.push({path:`${m}.path`,message:`duplicates planned path ${k}`}),k&&l.add(k);let Z=k?_(k,t.layers):void 0;k&&C&&o.has(C)&&Z!==C&&r.push({path:`${m}.layer`,message:Z?`${k} resolves to ${Z}, not ${C}`:`${k} is not assigned to an architecture layer`}),k&&A&&["create","update","delete"].includes(A)&&C&&d.push({path:k,operation:A,layer:C})});let u=[],y=new Map(d.map(p=>[p.path,p.operation])),g=new Set,c=e.dependencies??[];if(Array.isArray(c)?c.forEach((p,f)=>{let m=`$.dependencies[${f}]`;if(!te(p)){r.push({path:m,message:"must be an object"});return}ne(p,["from","to"],m,r);let h=R(p,"from",m,r),A=R(p,"to",m,r);h&&!l.has(h)&&r.push({path:`${m}.from`,message:`must reference a planned file path: ${h}`}),A&&!l.has(A)&&r.push({path:`${m}.to`,message:`must reference a planned file path: ${A}`}),h&&y.get(h)==="delete"&&r.push({path:`${m}.from`,message:`cannot depend from deleted file ${h}`}),A&&y.get(A)==="delete"&&r.push({path:`${m}.to`,message:`cannot depend on deleted file ${A}`}),h&&A&&h===A&&r.push({path:m,message:"must not declare a self dependency"});let C=h&&A?`${h}\0${A}`:void 0;C&&g.has(C)&&r.push({path:m,message:`duplicates dependency ${h} -> ${A}`}),C&&g.add(C),h&&A&&u.push({from:h,to:A})}):r.push({path:"$.dependencies",message:"must be an array"}),r.length>0)throw new U(n,r);let s={$schema:i,schemaVersion:ee,files:d.sort((p,f)=>p.path.localeCompare(f.path)),dependencies:u.sort((p,f)=>p.from.localeCompare(f.from)||p.to.localeCompare(f.to))};return{map:s,hash:$(x(s))}}function w(e){return`${e.from}->${e.to}`}function F(e,t,n,r="dependency"){return{id:`${e}:${r}:${w(t)}`,classification:e,subject:"dependency",from:t.from,to:t.to,message:n,...e==="missing"?{nextAction:`Add the planned dependency ${w(t)} to the candidate, then preflight again.`}:e==="contradictory"?{nextAction:`Replace the reverse dependency with ${w(t)}, then preflight again.`}:e==="unplanned"?{nextAction:r==="dependency-removed"?`Restore the removed dependency ${w(t)} in the candidate, then preflight again.`:`Remove the unplanned dependency ${w(t)} from the candidate, then preflight again.`}:{}}}function re(e){let t=[],n=new Map(e.changeMap.map.files.map(c=>[c.path,c])),r=new Map(e.changes.map(c=>[c.path,c])),i=new Map(e.changeMap.map.dependencies.map(c=>[w(c),c])),a=new Map(e.baseDependencies.map(c=>[w(c),c])),o=new Map(e.candidateDependencies.map(c=>[w(c),c]));for(let c of[...n.values()].sort((s,p)=>s.path.localeCompare(p.path))){let s=r.get(c.path);s?s.operation!==c.operation?t.push({id:`contradictory:file:${c.path}`,classification:"contradictory",subject:"file",path:c.path,expectedOperation:c.operation,actualOperation:s.operation,message:`${c.path} was planned as ${c.operation} but the actual operation is ${s.operation}.`,nextAction:`Change ${c.path} to the planned ${c.operation} operation, then preflight again.`}):t.push({id:`satisfied:file:${c.path}`,classification:"satisfied",subject:"file",path:c.path,expectedOperation:c.operation,actualOperation:s.operation,message:`${c.path} matches the planned ${c.operation} operation.`}):t.push({id:`missing:file:${c.path}`,classification:"missing",subject:"file",path:c.path,expectedOperation:c.operation,message:`${c.path} was planned as ${c.operation} but is absent from the actual change.`,nextAction:`${c.operation[0].toUpperCase()}${c.operation.slice(1)} ${c.path} in the complete change set, then preflight again.`})}for(let c of[...r.values()].sort((s,p)=>s.path.localeCompare(p.path)))n.has(c.path)||t.push({id:`unplanned:file:${c.path}`,classification:"unplanned",subject:"file",path:c.path,actualOperation:c.operation,message:`${c.path} has an unplanned ${c.operation} operation.`,nextAction:`Remove ${c.path} from the change set, then preflight again.`});let d=new Set;for(let c of[...i.values()].sort((s,p)=>w(s).localeCompare(w(p)))){if(o.has(w(c))){t.push(F("satisfied",c,`${c.from} -> ${c.to} exists in the candidate architecture.`));continue}let s={from:c.to,to:c.from};o.has(w(s))?(d.add(w(s)),t.push(F("contradictory",c,`${c.from} -> ${c.to} was planned, but the candidate contains the reverse edge.`))):t.push(F("missing",c,`${c.from} -> ${c.to} is absent from the candidate architecture.`))}let l=new Set([...n.keys(),...r.keys()]);for(let[c,s]of[...o].sort(([p],[f])=>p.localeCompare(f)))a.has(c)||i.has(c)||d.has(c)||!l.has(s.from)&&!l.has(s.to)||t.push({...F("unplanned",s,`${s.from} -> ${s.to} was added without a matching planned dependency.`,"dependency-added"),actualOperation:"added"});let u=new Set(e.changeMap.map.files.filter(c=>c.operation==="delete").map(c=>c.path));for(let[c,s]of[...a].sort(([p],[f])=>p.localeCompare(f)))o.has(c)||u.has(s.from)||u.has(s.to)||!l.has(s.from)&&!l.has(s.to)||t.push({...F("unplanned",s,`${s.from} -> ${s.to} was removed without a planned file deletion.`,"dependency-removed"),actualOperation:"removed"});let y={satisfied:0,missing:1,contradictory:2,unplanned:3};t.sort((c,s)=>y[c.classification]-y[s.classification]||(c.subject===s.subject?0:c.subject==="file"?-1:1)||c.id.localeCompare(s.id));let g={satisfied:t.filter(c=>c.classification==="satisfied").length,missing:t.filter(c=>c.classification==="missing").length,contradictory:t.filter(c=>c.classification==="contradictory").length,unplanned:t.filter(c=>c.classification==="unplanned").length};return{schemaVersion:"1.0",readOnly:!0,changeMapHash:e.changeMap.hash,structurallyConverged:g.missing===0&&g.contradictory===0&&g.unplanned===0,behavioralCompletion:"not-evaluated",summary:g,findings:t}}function M(e,t){return t&&e.isStringLiteralLike(t)?t.text:void 0}function xe(e,t){return e.getLineAndCharacterOfPosition(t.getStart(e)).line+1}function we(e,t){if(e.isImportDeclaration(t)){let n=t.importClause;if(!n)return!1;if(n.isTypeOnly)return!0;let r=n.namedBindings;return!!(r&&e.isNamedImports(r)&&r.elements.length>0&&r.elements.every(i=>i.isTypeOnly))}if(e.isExportDeclaration(t)){if(t.isTypeOnly)return!0;let n=t.exportClause;return!!(n&&e.isNamedExports(n)&&n.elements.length>0&&n.elements.every(r=>r.isTypeOnly))}return!1}function Ee(e,t){let n={noLib:!0,noResolve:!0,target:e.ScriptTarget.Latest},r=e.createCompilerHost(n,!0);return r.getSourceFile=i=>i===t.fileName?t:void 0,r.fileExists=i=>i===t.fileName,r.readFile=i=>i===t.fileName?t.text:void 0,e.createProgram([t.fileName],n,r).getTypeChecker()}function ie(e,t){try{return e.getSymbolAtLocation(t)}catch{return}}function ae(e,t,n,r){let i=r.parent&&e.isShorthandPropertyAssignment(r.parent)&&r.parent.name===r,a;try{a=i?t.getShorthandAssignmentValueSymbol(r.parent):ie(t,r)}catch{a=void 0}return!!a?.declarations?.some(o=>o.getSourceFile().fileName===n.fileName)}function se(e,t){let n,r=[],i=(o,d,l,u=!1)=>r.push({specifier:l,kind:d,line:xe(t,o),typeOnly:u,unresolved:l===void 0,node:o}),a=o=>{if(e.isImportDeclaration(o))i(o,"import",M(e,o.moduleSpecifier),we(e,o));else if(e.isExportDeclaration(o)&&o.moduleSpecifier)i(o,"export",M(e,o.moduleSpecifier),we(e,o));else if(e.isImportEqualsDeclaration(o)&&e.isExternalModuleReference(o.moduleReference))i(o,"require",M(e,o.moduleReference.expression),o.isTypeOnly===!0);else if(e.isCallExpression(o)){let d=o.expression.kind===e.SyntaxKind.ImportKeyword,u=e.isIdentifier(o.expression)&&o.expression.text==="require"&&!ae(e,n??(n=Ee(e,t)),t,o.expression);(d||u)&&i(o,u?"require":"dynamic-import",M(e,o.arguments[0]))}e.forEachChild(o,a)};return a(t),r}function Ye(e,t){let n=[],r=t;for(;e.isPropertyAccessExpression(r)||e.isElementAccessExpression(r);){if(e.isPropertyAccessExpression(r))n.unshift(r.name.text);else{let i=M(e,r.argumentExpression);if(i===void 0)return;n.unshift(i)}r=r.expression}if(e.isIdentifier(r))return n.unshift(r.text),{root:r,segments:n}}function We(e,t){let n=t.parent;return e.isPropertyAccessExpression(n)||e.isElementAccessExpression(n)?!1:e.isExpressionNode(t)&&!e.isInTypeQuery(t)||e.isShorthandPropertyAssignment(n)&&n.name===t}function Se(e,t){let n=t[0]==="globalThis"?t.slice(1):t;for(let r=n.length;r>=1;r-=1){let i=n.slice(0,r).join(".");if(e.has(i))return i}}function oe(e,t,n){if(n.length===0)return[];let r=new Set(n),i=Ee(e,t),a=new Map,o=new Set;for(let c of t.statements)if(e.isVariableStatement(c))for(let s of c.declarationList.declarations)e.isIdentifier(s.name)&&o.add(s.name.text);let d=c=>{let s=Ye(e,c);if(!s)return;let p=ie(i,s.root),f=p?a.get(p):void 0;return f?[...f,...s.segments.slice(1)]:ae(e,i,t,s.root)||o.has(s.root.text)?void 0:s.segments};for(let c of t.statements)if(e.isVariableStatement(c))for(let s of c.declarationList.declarations){if(!s.initializer||!e.isIdentifier(s.name))continue;let p=d(s.initializer),f=ie(i,s.name);!p||!f||a.set(f,p)}let l=[],u=new Set,y=(c,s)=>{let p=xe(t,s),f=`${c}:${s.getStart(t)}`;u.has(f)||(u.add(f),l.push({name:c,line:p,node:s}))},g=c=>{let s=c.parent&&(e.isPropertyAccessExpression(c.parent)||e.isElementAccessExpression(c.parent))&&c.parent.expression===c;if((e.isPropertyAccessExpression(c)||e.isElementAccessExpression(c))&&!s){let p=d(c),f=p?Se(r,p):void 0;f&&y(f,c)}else e.isIdentifier(c)&&r.has(c.text)&&We(e,c)&&!ae(e,i,t,c)&&y(c.text,c);if(e.isVariableDeclaration(c)&&e.isObjectBindingPattern(c.name)&&c.initializer){let p=d(c.initializer);if(p)for(let f of c.name.elements){if(!e.isIdentifier(f.name))continue;let m=f.propertyName?M(e,f.propertyName)??f.propertyName.text:f.name.text,h=Se(r,[...p,m]);h&&y(h,c.initializer)}}e.forEachChild(c,g)};return g(t),l}var ce={RAW_EVENT_PUBLISH:"Publish through a registered intent creator; raw event objects or intent strings bypass Ark contracts and tooling.",PUBLISH_MISSING_SOURCE:"Strict Ark publish calls must include metadata.source."};function $e(e){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function Je(e){if(!e.publishCall)return[];let t=[];return(e.rawIntentName!==void 0&&$e(e.rawIntentName)||e.objectHasIntent)&&t.push({ruleId:"RAW_EVENT_PUBLISH",message:ce.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&t.push({ruleId:"PUBLISH_MISSING_SOURCE",message:ce.PUBLISH_MISSING_SOURCE}),t}var de=Object.freeze(["network","filesystem","clock","randomness","environment","process","persistence"]),B=Object.freeze({fetch:"network",XMLHttpRequest:"network",Date:"clock","Date.now":"clock","Math.random":"randomness","process.env":"environment",process:"process"}),fe=Object.freeze(Object.keys(B).sort()),le=Object.freeze({fs:"filesystem","node:fs":"filesystem","fs/promises":"filesystem","node:fs/promises":"filesystem","fs-extra":"filesystem","graceful-fs":"filesystem",memfs:"filesystem",chokidar:"filesystem",http:"network",https:"network",http2:"network",net:"network",tls:"network",dgram:"network",dns:"network","node:http":"network","node:https":"network","node:http2":"network","node:net":"network","node:tls":"network","node:dgram":"network","node:dns":"network",axios:"network",undici:"network","node-fetch":"network",got:"network",ky:"network",superagent:"network",ws:"network",process:"process","node:process":"process",child_process:"process","node:child_process":"process","@prisma/client":"persistence",prisma:"persistence",pg:"persistence",mysql:"persistence",mysql2:"persistence",mongodb:"persistence",mongoose:"persistence",sqlite3:"persistence","better-sqlite3":"persistence",redis:"persistence",ioredis:"persistence",typeorm:"persistence",knex:"persistence","drizzle-orm":"persistence",sequelize:"persistence",kysely:"persistence","@supabase/supabase-js":"persistence"}),pe=Object.freeze({process:Object.freeze(["process","node:process"])});function j(e){if(!e||e.startsWith(".")||e.startsWith("/"))return null;let t=le[e];if(t)return t;let n=e.indexOf("/");if(n<0)return null;let r=e.slice(0,n),i=le[r];if(i)return i;let a=e.indexOf("/",n+1);return a<0?null:le[e.slice(0,a)]??null}function ue(e,t){for(let n of t)if(pe[n]?.includes(e))return n;return null}function V(e){let t=e.split(".");for(let n=t.length;n>=1;n-=1){let r=t.slice(0,n).join("."),i=B[r];if(i)return i}return null}function z(e){if(e?.pure===!0)return[...de].sort();let n=(e?.capabilities?.deny??[]).filter(r=>de.includes(r));return[...new Set(n)].sort()}function Ze(e,t){if(t.length===0)return!1;let n=new Set(t),r=e.split(".");for(let i=r.length;i>=1;i-=1)if(n.has(r.slice(0,i).join(".")))return!0;return!1}function q(e){let t=new Set,n=new Set,r=Object.keys(B);for(let i of e?.forbiddenGlobals??[]){let a=r.filter(o=>o===i||o.startsWith(`${i}.`));if(a.length===0)n.add(i);else for(let o of a)t.add(`ambient:${o}`);for(let o of pe[i]??[])t.add(`import-exact:${o}`)}for(let i of z(e)){if(t.add(`import:${i}`),i==="process")for(let a of pe.process)t.add(`import-exact:${a}`);for(let a of r)V(a)===i&&t.add(`ambient:${a}`)}return{atoms:[...t].sort(),rawGlobals:[...n].sort()}}function Xe(e){let t=new Set,n=V(e);n&&t.add(n);for(let[r,i]of Object.entries(B))(r===e||r.startsWith(`${e}.`))&&t.add(i);return[...t].sort()}function Qe(e,t){let n=[];for(let r of se(e,t)){if(r.typeOnly||!r.specifier)continue;let i=j(r.specifier);i&&n.push({capability:i,symbol:r.specifier,line:r.line,source:"import-based"})}for(let r of oe(e,t,fe)){let i=V(r.name);i&&n.push({capability:i,symbol:r.name,line:r.line,source:"ambient-global"})}return n.sort((r,i)=>r.line-i.line||r.capability.localeCompare(i.capability)||r.symbol.localeCompare(i.symbol))}var ye="https://unpkg.com/arkgate@2/schemas/ark.config.schema.json",Oe=["DomainModel","ApplicationOrchestration","PersistenceAdapters","IntegrationAdapters","WorkflowSagaEngine","BackgroundJobsScheduling","PresentationAdapters","ReportingReadModels","ExtensibilityMetadata","SecurityAuditObservability","Kernel"],et=new Set(["PresentationAdapters->ApplicationOrchestration","ApplicationOrchestration->DomainModel","WorkflowSagaEngine->ApplicationOrchestration","WorkflowSagaEngine->DomainModel","BackgroundJobsScheduling->ApplicationOrchestration"]);function tt(){let e=[];for(let t of Oe)for(let n of Oe)t===n||et.has(`${t}->${n}`)||e.push({from:t,to:n,allowed:!1});return e}var Re=tt();var O={type:"array",items:{type:"string",minLength:1},uniqueItems:!0},Le={$schema:"https://json-schema.org/draft/2020-12/schema",$id:ye,title:"ArkGate architecture contract",description:"Versioned contract consumed identically by ArkGate CLI, MCP, and ESLint surfaces.",type:"object",additionalProperties:!1,required:["$schema","schemaVersion","include","layers","rules"],properties:{$schema:{type:"string",minLength:1,default:ye,description:"Editor-facing URL or local path for this JSON Schema."},schemaVersion:{type:"string",const:"1.0",default:"1.0"},name:{type:"string",minLength:1},include:{...O,minItems:1,default:["src"]},exclude:{...O,default:[]},excludeGenerated:{type:"boolean",default:!0},frameworkOverlay:{type:"string",minLength:1},layers:{type:"array",default:[],items:{$ref:"#/$defs/layer"}},rules:{type:"array",default:Re,items:{$ref:"#/$defs/rule"}},cyclePolicy:{type:"string",enum:["strict","soft","framework-soft","off"],default:"strict"},dynamicImportAllowlist:{...O,default:[]},safety:{$ref:"#/$defs/safety",default:{maxTsSuppressions:0,maxAnyCasts:0,allowInMemory:!1,allowDisabledPeerIsolation:!1}}},$defs:{layer:{type:"object",additionalProperties:!1,required:["name","patterns"],properties:{name:{type:"string",minLength:1},patterns:{...O,minItems:1},exclude:O,intentPrefixes:O,description:{type:"string",minLength:1},forbiddenGlobals:O,capabilities:{type:"object",additionalProperties:!1,properties:{deny:{type:"array",uniqueItems:!0,items:{type:"string",enum:["network","filesystem","clock","randomness","environment","process","persistence"]}}}},pure:{type:"boolean"},mayImportInfrastructure:{type:"boolean"},optional:{type:"boolean"}}},rule:{type:"object",additionalProperties:!1,required:["from","to","allowed"],properties:{from:{type:"string",minLength:1},to:{type:"string",minLength:1},allowed:{type:"boolean"},message:{type:"string",minLength:1},peerIsolation:{type:"boolean"},sliceFolders:{...O,minItems:1}}},safety:{type:"object",additionalProperties:!1,properties:{maxTsSuppressions:{type:"integer",minimum:0,default:0},maxAnyCasts:{type:"integer",minimum:0,default:0},allowInMemory:{type:"boolean",default:!1},allowDisabledPeerIsolation:{type:"boolean",default:!1}}}}},T=class extends Error{issues;source;constructor(t,n){super(`Invalid ArkGate config (${t}):
|
|
6
6
|
${n.map(r=>`- ${r.path}: ${r.message}`).join(`
|
|
7
|
-
`)}`),this.name="ArkConfigValidationError",this.source=t,this.issues=n}};function Le(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function fe(e,t){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t)?`${e}.${t}`:`${e}[${JSON.stringify(t)}]`}function N(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function et(e,t){let n="#/$defs/";if(e.startsWith(n))return t.$defs[e.slice(n.length)]}function K(e,t,n,r,i){if(t.$ref){let a=et(t.$ref,r);if(!a){i.push({path:n,message:`schema reference ${t.$ref} cannot be resolved`});return}K(e,a,n,r,i);return}if(t.const!==void 0&&!Object.is(e,t.const)){i.push({path:n,message:`must equal ${JSON.stringify(t.const)}`});return}if(t.enum&&!t.enum.some(a=>Object.is(a,e))){i.push({path:n,message:`must be one of ${t.enum.map(String).join(", ")}`});return}if(t.type==="object"){if(!Le(e)){i.push({path:n,message:`must be an object; received ${N(e)}`});return}let a=t.properties??{};for(let o of t.required??[])e[o]===void 0&&i.push({path:fe(n,o),message:"is required"});if(t.additionalProperties===!1)for(let o of Object.keys(e))o in a||i.push({path:fe(n,o),message:"unknown field"});for(let[o,l]of Object.entries(a))e[o]!==void 0&&K(e[o],l,fe(n,o),r,i);return}if(t.type==="array"){if(!Array.isArray(e)){i.push({path:n,message:`must be an array; received ${N(e)}`});return}if(t.minItems!==void 0&&e.length<t.minItems&&i.push({path:n,message:`must contain at least ${t.minItems} item(s)`}),t.uniqueItems){let a=e.map(o=>JSON.stringify(o));new Set(a).size!==a.length&&i.push({path:n,message:"must not contain duplicate items"})}t.items&&e.forEach((a,o)=>K(a,t.items,`${n}[${o}]`,r,i));return}if(t.type==="string"){if(typeof e!="string"){i.push({path:n,message:`must be a string; received ${N(e)}`});return}t.minLength!==void 0&&e.length<t.minLength&&i.push({path:n,message:`must contain at least ${t.minLength} character(s)`});return}if(t.type==="boolean"){typeof e!="boolean"&&i.push({path:n,message:`must be a boolean; received ${N(e)}`});return}if(t.type==="integer"){if(!Number.isInteger(e)){i.push({path:n,message:`must be an integer; received ${N(e)}`});return}t.minimum!==void 0&&e<t.minimum&&i.push({path:n,message:`must be at least ${t.minimum}`})}}function tt(e){return{...e,$schema:e.$schema===void 0?ue:e.$schema,schemaVersion:e.schemaVersion===void 0?"1.0":e.schemaVersion,include:e.include===void 0?["src"]:e.include,layers:e.layers===void 0?[]:e.layers,rules:e.rules===void 0?Oe.map(t=>({...t})):e.rules}}function nt(e,t="ark.config.json"){if(!Le(e))throw new T(t,[{path:"$",message:`must be an object; received ${N(e)}`}]);let n=e.schemaVersion===void 0?"unversioned":null;if(e.schemaVersion!==void 0&&e.schemaVersion!=="1.0")throw new T(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(e.schemaVersion)}; expected 1.0`}]);return{candidate:tt(e),migratedFrom:n}}function ge(e,t="ark.config.json"){let{candidate:n,migratedFrom:r}=nt(e,t),i=[];if(K(n,$e,"$",$e,i),i.length>0)throw new T(t,i);return{config:n,migratedFrom:r}}function Pe(e,t="ark.config.json"){let n;try{n=JSON.parse(e)}catch(r){throw new T(t,[{path:"$",message:`invalid JSON: ${r instanceof Error?r.message:String(r)}`}])}return ge(n,t)}var Re="1.0";function b(e,t){e.push({id:`${t.classification}:${t.path}:${t.kind}`,path:t.path,classification:t.classification,message:t.message,...t.classification==="weakening"||t.classification==="judgment-required"?{nextAction:`Restore the previous protection at ${t.path}, then run ArkGate again.`}:{},...t.before===void 0?{}:{before:t.before},...t.after===void 0?{}:{after:t.after}})}function S(e){return[...new Set(e??[])].sort()}function R(e,t,n,r,i){let a=S(n),o=S(r),l=new Set(a),p=new Set(o),u=o.filter(g=>!l.has(g)),m=a.filter(g=>!p.has(g));u.length===0&&m.length===0||(u.length>0&&b(e,{kind:"added",path:t,classification:i.added,message:i.addedMessage,before:a,after:o}),m.length>0&&b(e,{kind:"removed",path:t,classification:i.removed,message:i.removedMessage,before:a,after:o}))}function Y(e,t,n,r,i,a,o){if(n===r)return;b(e,{kind:r?"enabled":"disabled",path:t,classification:r?i:i==="strengthening"?"weakening":"strengthening",message:r?a:o,before:n,after:r})}function W(e,t){let n=new Map,r=new Set;for(let i of e){let a=t(i);n.has(a)?r.add(a):n.set(a,i)}return{values:n,duplicates:[...r].sort()}}function rt(e,t,n){let r=W(t,a=>a.name),i=W(n,a=>a.name);(r.duplicates.length>0||i.duplicates.length>0)&&b(e,{kind:"duplicate-layer",path:"$.layers",classification:"judgment-required",message:"Duplicate layer names make policy ownership ambiguous.",before:r.duplicates,after:i.duplicates});for(let a of[...new Set([...r.values.keys(),...i.values.keys()])].sort()){let o=r.values.get(a),l=i.values.get(a),p=`$.layers[${a}]`;if(!o&&l){b(e,{kind:"layer-added",path:p,classification:"judgment-required",message:"A layer was added; verify overlap, ownership, and rule coverage.",after:l});continue}if(o&&!l){b(e,{kind:"layer-removed",path:p,classification:"weakening",message:"Removing a layer can leave its source paths ungoverned.",before:o});continue}if(!o||!l)continue;R(e,`${p}.patterns`,o.patterns,l.patterns,{added:"strengthening",removed:"weakening",addedMessage:"Additional paths are governed by this layer.",removedMessage:"Paths were removed from this layer and may become ungoverned."}),R(e,`${p}.exclude`,o.exclude,l.exclude,{added:"weakening",removed:"strengthening",addedMessage:"Additional paths are excluded from this layer.",removedMessage:"Fewer paths are excluded from this layer."});let u=q(o),m=q(l);R(e,`${p}.forbiddenGlobals`,u.rawGlobals,m.rawGlobals,{added:"strengthening",removed:"weakening",addedMessage:"Additional forbidden globals are enforced in this layer.",removedMessage:"A forbidden-global protection was removed from this layer."}),R(e,`${p}.capabilities`,u.atoms,m.atoms,{added:"strengthening",removed:"weakening",addedMessage:"Additional ambient/import protection is enforced in this layer (coverage atoms).",removedMessage:"An ambient/import protection was lost from this layer (coverage atoms)."}),S(o.intentPrefixes).join("\0")!==S(l.intentPrefixes).join("\0")&&b(e,{kind:"intent-prefixes-changed",path:`${p}.intentPrefixes`,classification:"judgment-required",message:"Intent ownership changed and must be reviewed against publishers and consumers.",before:S(o.intentPrefixes),after:S(l.intentPrefixes)}),Y(e,`${p}.mayImportInfrastructure`,o.mayImportInfrastructure===!0,l.mayImportInfrastructure===!0,"weakening","The layer may now import infrastructure directly.","Direct infrastructure imports are no longer allowed for this layer."),Y(e,`${p}.optional`,o.optional===!0,l.optional===!0,"weakening","The layer is now optional and can be absent without a strict warning.","The layer is now required when its contract is active.")}}function it(e,t,n){let r=o=>`${o.from}->${o.to}`,i=W(t,r),a=W(n,r);(i.duplicates.length>0||a.duplicates.length>0)&&b(e,{kind:"duplicate-rule",path:"$.rules",classification:"judgment-required",message:"Duplicate rule edges make the effective verdict order-dependent.",before:i.duplicates,after:a.duplicates});for(let o of[...new Set([...i.values.keys(),...a.values.keys()])].sort()){let l=i.values.get(o),p=a.values.get(o),u=`$.rules[${o}]`;if(!l&&p){p.allowed===!1&&b(e,{kind:"deny-added",path:u,classification:"strengthening",message:"A denied dependency edge was added.",after:p});continue}if(l&&!p){l.allowed===!1&&b(e,{kind:"deny-removed",path:u,classification:"weakening",message:"A denied dependency edge was removed.",before:l});continue}if(!l||!p)continue;l.allowed!==p.allowed&&b(e,{kind:p.allowed?"deny-disabled":"deny-enabled",path:`${u}.allowed`,classification:p.allowed?"weakening":"strengthening",message:p.allowed?"A previously denied dependency edge is now allowed.":"A dependency edge is now denied.",before:l.allowed,after:p.allowed});let m=l.peerIsolation===!0,g=p.peerIsolation===!0;if(m!==g){let c=l.from===l.to&&p.from===p.to;b(e,{kind:g?"peer-isolation-enabled":"peer-isolation-disabled",path:`${u}.peerIsolation`,classification:c?g?"strengthening":"weakening":"judgment-required",message:c?g?"Cross-slice dependencies inside this layer are now denied.":"Cross-slice dependencies inside this layer are no longer denied.":"Changing peer isolation on a cross-layer edge changes the denial scope.",before:m,after:g})}S(l.sliceFolders).join("\0")!==S(p.sliceFolders).join("\0")&&b(e,{kind:"slice-folders-changed",path:`${u}.sliceFolders`,classification:"judgment-required",message:"Slice ownership folders changed and can reclassify existing dependencies.",before:S(l.sliceFolders),after:S(p.sliceFolders)})}}function at(e,t,n){let r=t.safety??{},i=n.safety??{};for(let a of["maxTsSuppressions","maxAnyCasts"]){let o=r[a]??0,l=i[a]??0;o!==l&&b(e,{kind:l>o?"threshold-raised":"threshold-lowered",path:`$.safety.${a}`,classification:l>o?"weakening":"strengthening",message:l>o?"The safety threshold allows more violations.":"The safety threshold allows fewer violations.",before:o,after:l})}for(let a of["allowInMemory","allowDisabledPeerIsolation"])Y(e,`$.safety.${a}`,r[a]===!0,i[a]===!0,"weakening","A safety exception was enabled.","A safety exception was disabled.")}function st(e){return e.some(t=>t.classification==="weakening")?"weakening":e.some(t=>t.classification==="judgment-required")?"judgment-required":e.some(t=>t.classification==="strengthening")?"strengthening":"neutral"}function ve(e,t){let n=[];R(n,"$.include",e.include,t.include,{added:"strengthening",removed:"weakening",addedMessage:"Additional project roots are governed.",removedMessage:"Project roots were removed from governance."}),R(n,"$.exclude",e.exclude,t.exclude,{added:"weakening",removed:"strengthening",addedMessage:"Additional project paths are excluded from governance.",removedMessage:"Fewer project paths are excluded from governance."}),R(n,"$.dynamicImportAllowlist",e.dynamicImportAllowlist,t.dynamicImportAllowlist,{added:"weakening",removed:"strengthening",addedMessage:"Additional files may use non-literal dynamic imports.",removedMessage:"Fewer files may use non-literal dynamic imports."}),Y(n,"$.excludeGenerated",e.excludeGenerated!==!1,t.excludeGenerated!==!1,"weakening","Generated source is now excluded from governance.","Generated source is now governed.");let r={off:0,soft:1,"framework-soft":1,strict:2},i=e.cyclePolicy??"strict",a=t.cyclePolicy??"strict";if(i!==a){let o=r[a]===r[i]?"judgment-required":r[a]>r[i]?"strengthening":"weakening";b(n,{kind:"cycle-policy-changed",path:"$.cyclePolicy",classification:o,message:"The cycle enforcement level changed.",before:i,after:a})}return(e.frameworkOverlay??null)!==(t.frameworkOverlay??null)&&b(n,{kind:"framework-overlay-changed",path:"$.frameworkOverlay",classification:"judgment-required",message:"The framework overlay changed and may alter effective layer matching.",before:e.frameworkOverlay??null,after:t.frameworkOverlay??null}),rt(n,e.layers,t.layers),it(n,e.rules,t.rules),at(n,e,t),n.sort((o,l)=>o.path.localeCompare(l.path)||o.id.localeCompare(l.id)),{schemaVersion:Re,classification:st(n),findings:n}}function _e(e,t){if(!e||e.schemaVersion!==Re||typeof e.basePolicyHash!="string"||typeof e.candidatePolicyHash!="string"||typeof e.reason!="string"||!Array.isArray(e.findingIds)||e.findingIds.some(i=>typeof i!="string")||e.reason.trim().length===0||e.basePolicyHash!==t.basePolicyHash||e.candidatePolicyHash!==t.candidatePolicyHash)return!1;let n=S(e.findingIds),r=S(t.findingIds);return n.length===r.length&&n.every((i,a)=>i===r[a])}function v(e){let t=[];for(let n of e.replace(/\\/g,"/").split("/"))!n||n==="."||(n===".."&&t.length>0&&t.at(-1)!==".."?t.pop():t.push(n));return t.join("/")}function Me(e){return e!==void 0&&/[A-Za-z0-9_$]/.test(e)}function L(e,t){for(;t<e.length&&/\s/.test(e[t]);)t+=1;return t}function H(e,t){let n=e[t];if(n!=="'"&&n!=='"')return;let r=t,i="";for(t+=1;t<e.length;t+=1){let a=e[t];if(a===n)return{value:i,offset:r,excerpt:e.slice(r,t+1)};a==="\\"&&t+1<e.length?(i+=e[t+1],t+=1):i+=a}}function E(e,t,n){return e.startsWith(t,n)&&!Me(e[n-1])&&!Me(e[n+t.length])}function ot(e,t){if(t=L(e,t+6),e[t]==="(")return H(e,L(e,t+1));let n=!1;if(E(e,"type",t)){let i=L(e,t+4);e[i]!==","&&!E(e,"from",i)&&(n=!0)}let r=Ne(e,t,!0);return r&&n?{...r,typeOnly:!0}:r}function ct(e,t){t=t+6;let n=L(e,t),r=!1;if(E(e,"type",n)){let a=L(e,n+4);(e[a]==="{"||e[a]==="*")&&(r=!0)}let i=Ne(e,t,!1);return i&&r?{...i,typeOnly:!0}:i}function Ne(e,t,n){for(;t<e.length;t+=1){if(e[t]===";")return;if(E(e,"from",t))return H(e,L(e,t+4));if(n&&(e[t]==="'"||e[t]==='"'))return H(e,t);if(t>0&&(E(e,"import",t)||E(e,"export",t)))return}}function lt(e,t){for(t+=1;t<e.length;t+=1){let n=e[t];if(n==="\\")t+=1;else if(n==="`")return t}return e.length}function pt(e,t){let n=t-1;for(;n>=0&&/\s/.test(e[n]);)n-=1;if(e[n]===".")return;let r=L(e,t+7);if(e[r]!=="(")return;r=L(e,r+1);let i=H(e,r);return i?{...i,requireCall:!0}:void 0}function dt(e){let t=[];for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="/"&&e[n+1]==="/"){if(n=e.indexOf(`
|
|
8
|
-
`,n+2),n<0)break;continue}if(r==="/"&&e[n+1]==="*"){let a=e.indexOf("*/",n+2);if(a<0)break;n=a+1;continue}if(r==="`"){n=
|
|
9
|
-
`).length;r.push({file:e.path,symbol:a,capability:u,evidence:{kind:"import",file:e.path,line:
|
|
10
|
-
`).length,l={kind:"import",file:e.path,line:o,excerpt:i.excerpt},p=ft(e.path,a,t);n.push({from:e.path,specifier:a,to:p?.path??null,resolution:p?"resolved":"unresolved",fromLayer:e.layer,toLayer:p?.layer??null,evidence:l})}return{edges:n,capabilityUses:r}}function ft(e,t,n){let r=e.split("/");r.pop();for(let a of t.split("/"))a==="."||a===""||(a===".."?r.pop():r.push(a));let i=r.join("/");for(let a of[i,`${i}.ts`,`${i}.tsx`,`${i}.mts`,`${i}.cts`,`${i}/index.ts`,`${i}/index.tsx`]){let o=n.get(a);if(o)return o}}function De(e,t){let n=[];for(let r of e){if(!r.to||!r.fromLayer||!r.toLayer)continue;let i=G(t.rules,r.fromLayer,r.toLayer,{fromPath:r.from,toPath:r.to,layers:t.layers});i&&n.push({ruleId:`layer-dependency:${i.from}->${i.to}`,message:i.message??`${i.from} must not depend on ${i.to}.`,edge:r,evidence:r.evidence})}return n}function ye(e,t){let n=typeof e=="string"?Pe(e,t):ge(e,t);return{...n,policyHash:$(x(n.config))}}function gt(e){let t=ye(e.baseConfig,e.baseSource??"base ark.config.json"),n=ye(e.candidateConfig,e.candidateSource??"candidate ark.config.json"),r=ve(t.config,n.config),i=r.findings.filter(l=>l.classification==="weakening"||l.classification==="judgment-required").map(l=>l.id).sort(),a=i.length>0,o=a&&_e(e.acknowledgement,{basePolicyHash:t.policyHash,candidatePolicyHash:n.policyHash,findingIds:i});return{schemaVersion:r.schemaVersion,basePolicyHash:t.policyHash,candidatePolicyHash:n.policyHash,classification:r.classification,findings:r.findings,blockingFindingIds:i,requiresAcknowledgement:a,acknowledged:o,valid:!a||o}}function J(e){let t=e.files.map(l=>{let p=v(l.path);return{path:p,content:l.content,contentHash:$(l.content),layer:_(p,e.contract.config.layers)??null}}).sort((l,p)=>l.path.localeCompare(p.path)),n=new Map(t.map(l=>[l.path,l])),r=[],i=[];for(let l of t){let p=Te(l,n);r.push(...p.edges),i.push(...p.capabilityUses)}let a=De(r,e.contract.config),o=new Map(e.contract.config.layers.map(l=>[l.name,new Set(z(l))]));for(let l of i){let p=n.get(l.file)?.layer;!p||!o.get(p)?.has(l.capability)||a.push({ruleId:"CAPABILITY_VIOLATION",message:`${p} denies the ${l.capability} capability; found import of "${l.symbol}".`,capability:l.capability,symbol:l.symbol,evidence:l.evidence})}return{ir:{schemaVersion:"1.0",policyHash:e.contract.policyHash,compilerOptionsHash:$(x(e.compilerOptions??{})),files:t,layers:e.contract.config.layers.map(l=>l.name),edges:r,capabilityUses:i,violations:a}}}function me(e){let t=new Map(e.files.map(n=>[v(n.path),n]));for(let n of e.changes){let r=v(n.path);"delete"in n&&n.delete?t.delete(r):"content"in n&&t.set(r,{path:r,content:n.content})}return J({contract:e.contract,files:[...t.values()],compilerOptions:e.compilerOptions})}function yt(e){let t=`${e.evidence.file}:${e.evidence.line}`;if(!e.edge)return`${e.ruleId} at ${t}: ${e.message}`;let n=e.edge.to??e.edge.specifier;return`${e.ruleId} at ${t}: ${e.edge.from} imports ${n}. ${e.message}`}function Fe(e){let t=0,n=new Map,r=new Map,i=new Set,a=[],o=[],l=p=>{n.set(p,t),r.set(p,t),t+=1,a.push(p),i.add(p);for(let g of[...e.get(p)??[]].sort())e.has(g)&&(n.has(g)?i.has(g)&&r.set(p,Math.min(r.get(p)??0,n.get(g)??0)):(l(g),r.set(p,Math.min(r.get(p)??0,r.get(g)??0))));if(r.get(p)!==n.get(p))return;let u=[],m;do{if(m=a.pop(),m===void 0)break;i.delete(m),u.push(m)}while(m!==p);u.length>1&&o.push(u.sort())};for(let p of[...e.keys()].sort())n.has(p)||l(p);return o.sort((p,u)=>p[0].localeCompare(u[0])).map(p=>({ruleId:"CIRCULAR_DEPENDENCY",file:p[0],line:1,target:p.join(" \u2192 "),message:`Circular dependency among ${p.length} files: ${p.join(" \u2192 ")} \u2192 ${p[0]}.`,cycleKind:"value"}))}function he(e){let t=e.contentViolations.map(a=>({...a})),n=(e.warnings??[]).map(a=>({...a})),r=new Map(e.files.map(a=>[a,new Set]));for(let a of e.edges){if(a.to&&a.to!==a.from&&!a.typeOnly&&r.has(a.from)&&r.get(a.from)?.add(a.to),!a.to||!a.toLayer)continue;let o=G(e.rules,a.fromLayer,a.toLayer,{fromPath:a.from,toPath:a.to,layers:e.config.layers});if(!o)continue;let l=!!o.peerIsolation;t.push({ruleId:"LAYER_IMPORT_VIOLATION",file:a.from,line:a.line,fromLayer:a.fromLayer,toLayer:a.toLayer,target:a.to,...a.typeOnly?{typeOnly:!0}:{},...a.targetTypeOnlyExports?{targetTypeOnlyExports:!0}:{},...a.sourcePureTypeModule?{sourcePureTypeModule:!0}:{},...a.namedBindingsTypeOnly?{namedBindingsTypeOnly:!0}:{},...!l&&a.portProofEligible?{portProofEligible:!0}:{},...a.kind?{edgeKind:a.kind}:{},...l?{peerIsolation:!0}:{},message:o.message??(l?`${a.fromLayer} must not ${a.kind} another slice of ${a.toLayer} (${a.from} \u2192 ${a.to}). Extract shared code or use events/ports across slices.`:`${a.fromLayer} must not ${a.kind} ${a.toLayer}.`)})}let i=String(e.config.cyclePolicy??"strict").toLowerCase();if(i!=="off"){let a=Fe(r);i==="soft"||i==="framework-soft"?n.push(...a.map(o=>({...o,message:`${o.message} (soft cycle policy \u2014 advisory only; set cyclePolicy: "strict" to fail the check)`,failsStrict:!1}))):t.push(...a)}return{violations:t,warnings:n,safety:e.safety}}function je(e){switch(e.ruleId){case"LAYER_IMPORT_VIOLATION":return e.typeOnly||e.targetTypeOnlyExports||e.namedBindingsTypeOnly?"Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.":e.peerIsolation?"Extract the shared dependency to a shared layer, then preflight again.":`Define a port in ${e.fromLayer??"the source layer"}, inject the ${e.toLayer??"outer-layer"} implementation, then preflight again.`;case"FORBIDDEN_GLOBAL":return`Inject ${e.target??"the capability"} through a port, then preflight again.`;case"CAPABILITY_VIOLATION":return`Define a ${String(e.capability??"capability")} port in ${e.fromLayer??"the walled layer"}, bind the implementation outside it, then preflight again.`;case"CIRCULAR_DEPENDENCY":return"Extract the shared dependency into a third module, then preflight again.";case"RAW_EVENT_PUBLISH":return"Publish through a registered intent creator, then run Ark again.";case"PUBLISH_MISSING_SOURCE":return"Add metadata.source to the publish call, then run Ark again.";default:return`Resolve ${typeof e.ruleId=="string"&&e.ruleId.length>0?e.ruleId:"ARK_UNKNOWN"} without weakening ark.config.json, then run Ark again.`}}function Ve(e){return $(x(e.map(({path:t,contentHash:n})=>({path:t,contentHash:n}))))}function mt(e){let t=J(e),n=new Map(t.ir.files.map(s=>[s.path,s])),r=[],i=new Set,a=[];for(let s of e.changes){let d=s.path.replace(/\\/g,"/"),f=v(s.path);if(!f||f===".."||f.startsWith("../")||d.startsWith("/")||/^[A-Za-z]:\//.test(d)||d.includes("\0")){a.push({ruleId:"INVALID_CHANGE_PATH",file:"<change-set>",line:1,message:"Every change requires a safe, non-empty project-relative path."});continue}if(i.has(f)){a.push({ruleId:"DUPLICATE_CHANGE_PATH",file:f,line:1,message:`The atomic change set contains more than one operation for ${f}.`});continue}i.add(f),"delete"in s&&s.delete&&!n.has(f)&&a.push({ruleId:"DELETE_TARGET_MISSING",file:f,line:1,message:`Cannot delete ${f} because it is not present in the supplied base tree.`}),r.push("delete"in s&&s.delete?{path:f,delete:!0}:{path:f,content:"content"in s?s.content:""})}e.changes.length===0&&a.push({ruleId:"CHANGE_SET_EMPTY",file:"<change-set>",line:1,message:"Atomic preflight requires at least one create, update, or delete."});let o=me({...e,changes:r}),l=new Map(o.ir.files.map(s=>[s.path,s])),p=o.ir.violations.filter(s=>s.ruleId==="CAPABILITY_VIOLATION").map(s=>({ruleId:s.ruleId,file:s.evidence.file,line:s.evidence.line,target:s.symbol,capability:s.capability,message:s.message})),u=he({config:e.contract.config,rules:e.contract.config.rules,files:o.ir.files.map(s=>s.path),contentViolations:p,edges:o.ir.edges.filter(s=>!!s.fromLayer).map(s=>({from:s.from,fromLayer:s.fromLayer,...s.to?{to:s.to}:{},...s.toLayer?{toLayer:s.toLayer}:{},line:s.evidence.line,kind:"import"}))}),m=r.map(s=>{let d=v(s.path),f=n.get(d),y=l.get(d);return{path:d,operation:"delete"in s&&s.delete?"delete":f?"update":"create",...f?{beforeContentHash:f.contentHash}:{},...y?{candidateContentHash:y.contentHash}:{}}}).sort((s,d)=>s.path.localeCompare(d.path)),g=[...a,...u.violations].map(s=>({...s,nextAction:je(s)})),c=e.changeMap?re({changeMap:e.changeMap,changes:m,baseDependencies:t.ir.edges.flatMap(s=>s.to?[{from:s.from,to:s.to}]:[]),candidateDependencies:o.ir.edges.flatMap(s=>s.to?[{from:s.from,to:s.to}]:[])}):void 0;return{schemaVersion:"1.0",valid:g.length===0&&(c?.structurallyConverged??!0),readOnly:!0,policyHash:e.contract.policyHash,compilerOptionsHash:o.ir.compilerOptionsHash,baseTreeHash:Ve(t.ir.files),candidateTreeHash:Ve(o.ir.files),...e.changeMap?{changeMapHash:e.changeMap.hash}:{},...c?{convergence:c}:{},changes:m,violations:g,warnings:u.warnings}}function I(e,t,n={}){return{ruleId:e,message:t,...n}}function ht(e){let{config:t,rules:n,files:r,manifest:i}=e,a=[];if(t.dynamicImportAllowlist!==void 0&&(!Array.isArray(t.dynamicImportAllowlist)||t.dynamicImportAllowlist.some(s=>typeof s!="string"))&&a.push(I("CONFIG_INVALID_DYNAMIC_IMPORT_ALLOWLIST","dynamicImportAllowlist must be an array of file globs.")),t.safety!==void 0&&(t.safety===null||typeof t.safety!="object"||Array.isArray(t.safety)))a.push(I("CONFIG_INVALID_SAFETY","safety must be an object."));else if(t.safety)for(let s of["maxTsSuppressions","maxAnyCasts"]){let d=t.safety[s];d!==void 0&&(!Number.isInteger(d)||d<0)&&a.push(I("CONFIG_INVALID_SAFETY_THRESHOLD",`safety.${s} must be a non-negative integer.`))}let o=Array.isArray(t.layers)?t.layers:[],l=Array.isArray(i?.architecture?.layers)?i.architecture.layers:[],p=new Set([...o.map(s=>s.name).filter(Boolean),...l.map(s=>s.name).filter(s=>!!s)]);o.length===0&&a.push(I("CONFIG_NO_LAYERS","No file layers are configured; ark-check cannot classify files for import-boundary enforcement."));let u=new Set,m=new Set;for(let s of o){if(!s.name){a.push(I("CONFIG_LAYER_WITHOUT_NAME","A configured layer is missing a name."));continue}u.has(s.name)&&m.add(s.name),u.add(s.name),s.forbiddenGlobals!==void 0&&(!Array.isArray(s.forbiddenGlobals)||s.forbiddenGlobals.some(f=>typeof f!="string"))&&a.push(I("CONFIG_INVALID_FORBIDDEN_GLOBALS",`Layer "${s.name}" has an invalid forbiddenGlobals value; expected an array of strings (e.g. ["fetch", "Date.now"]). The entry is ignored.`,{layer:s.name}));let d=Array.isArray(s.patterns)?s.patterns:[];if(d.length===0){a.push(I("CONFIG_LAYER_WITHOUT_PATTERNS",`Layer "${s.name}" has no file patterns and will never classify files.`,{layer:s.name}));continue}for(let f of d){let y;try{y=D(f)}catch(h){a.push(I("CONFIG_INVALID_LAYER_PATTERN",`Layer "${s.name}" has an invalid pattern "${f}": ${h instanceof Error?h.message:String(h)}`,{layer:s.name,pattern:f}));continue}!r.some(h=>y.test(h))&&!s.optional&&a.push(I("CONFIG_LAYER_PATTERN_NO_MATCHES",`Layer "${s.name}" pattern "${f}" matched no included files.`,{layer:s.name,pattern:f,failsStrict:!1}))}}for(let s of m)a.push(I("CONFIG_DUPLICATE_LAYER",`Layer "${s}" is configured more than once.`,{layer:s}));if(p.size>0)for(let s of n??[])s.from&&!p.has(s.from)&&a.push(I("CONFIG_RULE_UNKNOWN_FROM_LAYER",`Rule references unknown source layer "${s.from}".`,{fromLayer:s.from,toLayer:s.to})),s.to&&!p.has(s.to)&&a.push(I("CONFIG_RULE_UNKNOWN_TO_LAYER",`Rule references unknown target layer "${s.to}".`,{fromLayer:s.from,toLayer:s.to}));let g=new Set;if(o.length>1)for(let s of r){let d=-1,f=[];for(let y of o)for(let h of y.patterns??[]){if(!D(h).test(s))continue;let A=Q(h);A>d?(d=A,f=[y.name]):A===d&&!f.includes(y.name)&&f.push(y.name)}f.length>1&&g.add([...f].sort().join(" + "))}g.size>0&&a.push(I("CONFIG_AMBIGUOUS_LAYERS",`Some files match multiple layers at equal specificity; classification falls back to declaration order. Disambiguate the overlapping patterns: ${[...g].join(", ")}.`,{pairs:[...g]}));let c=r.filter(s=>!_(s,o));return c.length>0&&a.push(I("CONFIG_UNCLASSIFIED_FILES",`${c.length} included source file(s) are not matched by any configured layer; ark-check will not enforce import rules for those source files.`,{count:c.length,samples:c.slice(0,5)})),a}export{de as AMBIENT_CAPABILITY_ENTRIES,pe as CAPABILITY_IDS,ce as SOURCE_POLICY_MESSAGES,We as ambientCoveredByForbiddenGlobals,re as analyzeArchitectureConvergence,me as analyzeChange,gt as analyzePolicyDelta,J as analyzeProject,V as capabilityForAmbientName,j as capabilityForModuleSpecifier,Ye as classifyPublishFacts,ht as collectAnalysisConfigWarnings,Ze as collectCapabilityUses,oe as collectForbiddenCapabilityUses,Fe as detectArchitectureCycles,z as effectiveCapabilityDeny,he as evaluateArchitectureGraph,yt as explainViolation,se as extractSemanticDependencies,ze as loadArchitectureChangeMap,ye as loadContract,xe as looksLikeArkIntent,Je as lowerForbiddenGlobal,q as loweredLayerCoverage,mt as preflightChange};
|
|
7
|
+
`)}`),this.name="ArkConfigValidationError",this.source=t,this.issues=n}};function Pe(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function ge(e,t){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t)?`${e}.${t}`:`${e}[${JSON.stringify(t)}]`}function N(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function nt(e,t){let n="#/$defs/";if(e.startsWith(n))return t.$defs[e.slice(n.length)]}function K(e,t,n,r,i){if(t.$ref){let a=nt(t.$ref,r);if(!a){i.push({path:n,message:`schema reference ${t.$ref} cannot be resolved`});return}K(e,a,n,r,i);return}if(t.const!==void 0&&!Object.is(e,t.const)){i.push({path:n,message:`must equal ${JSON.stringify(t.const)}`});return}if(t.enum&&!t.enum.some(a=>Object.is(a,e))){i.push({path:n,message:`must be one of ${t.enum.map(String).join(", ")}`});return}if(t.type==="object"){if(!Pe(e)){i.push({path:n,message:`must be an object; received ${N(e)}`});return}let a=t.properties??{};for(let o of t.required??[])e[o]===void 0&&i.push({path:ge(n,o),message:"is required"});if(t.additionalProperties===!1)for(let o of Object.keys(e))o in a||i.push({path:ge(n,o),message:"unknown field"});for(let[o,d]of Object.entries(a))e[o]!==void 0&&K(e[o],d,ge(n,o),r,i);return}if(t.type==="array"){if(!Array.isArray(e)){i.push({path:n,message:`must be an array; received ${N(e)}`});return}if(t.minItems!==void 0&&e.length<t.minItems&&i.push({path:n,message:`must contain at least ${t.minItems} item(s)`}),t.uniqueItems){let a=e.map(o=>JSON.stringify(o));new Set(a).size!==a.length&&i.push({path:n,message:"must not contain duplicate items"})}t.items&&e.forEach((a,o)=>K(a,t.items,`${n}[${o}]`,r,i));return}if(t.type==="string"){if(typeof e!="string"){i.push({path:n,message:`must be a string; received ${N(e)}`});return}t.minLength!==void 0&&e.length<t.minLength&&i.push({path:n,message:`must contain at least ${t.minLength} character(s)`});return}if(t.type==="boolean"){typeof e!="boolean"&&i.push({path:n,message:`must be a boolean; received ${N(e)}`});return}if(t.type==="integer"){if(!Number.isInteger(e)){i.push({path:n,message:`must be an integer; received ${N(e)}`});return}t.minimum!==void 0&&e<t.minimum&&i.push({path:n,message:`must be at least ${t.minimum}`})}}function rt(e){return{...e,$schema:e.$schema===void 0?ye:e.$schema,schemaVersion:e.schemaVersion===void 0?"1.0":e.schemaVersion,include:e.include===void 0?["src"]:e.include,layers:e.layers===void 0?[]:e.layers,rules:e.rules===void 0?Re.map(t=>({...t})):e.rules}}function it(e,t="ark.config.json"){if(!Pe(e))throw new T(t,[{path:"$",message:`must be an object; received ${N(e)}`}]);let n=e.schemaVersion===void 0?"unversioned":null;if(e.schemaVersion!==void 0&&e.schemaVersion!=="1.0")throw new T(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(e.schemaVersion)}; expected 1.0`}]);return{candidate:rt(e),migratedFrom:n}}function me(e,t="ark.config.json"){let{candidate:n,migratedFrom:r}=it(e,t),i=[];if(K(n,Le,"$",Le,i),i.length>0)throw new T(t,i);return{config:n,migratedFrom:r}}function ve(e,t="ark.config.json"){let n;try{n=JSON.parse(e)}catch(r){throw new T(t,[{path:"$",message:`invalid JSON: ${r instanceof Error?r.message:String(r)}`}])}return me(n,t)}var _e="1.0";function b(e,t){e.push({id:`${t.classification}:${t.path}:${t.kind}`,path:t.path,classification:t.classification,message:t.message,...t.classification==="weakening"||t.classification==="judgment-required"?{nextAction:`Restore the previous protection at ${t.path}, then run ArkGate again.`}:{},...t.before===void 0?{}:{before:t.before},...t.after===void 0?{}:{after:t.after}})}function S(e){return[...new Set(e??[])].sort()}function P(e,t,n,r,i){let a=S(n),o=S(r),d=new Set(a),l=new Set(o),u=o.filter(g=>!d.has(g)),y=a.filter(g=>!l.has(g));u.length===0&&y.length===0||(u.length>0&&b(e,{kind:"added",path:t,classification:i.added,message:i.addedMessage,before:a,after:o}),y.length>0&&b(e,{kind:"removed",path:t,classification:i.removed,message:i.removedMessage,before:a,after:o}))}function Y(e,t,n,r,i,a,o){if(n===r)return;b(e,{kind:r?"enabled":"disabled",path:t,classification:r?i:i==="strengthening"?"weakening":"strengthening",message:r?a:o,before:n,after:r})}function W(e,t){let n=new Map,r=new Set;for(let i of e){let a=t(i);n.has(a)?r.add(a):n.set(a,i)}return{values:n,duplicates:[...r].sort()}}function at(e,t,n){let r=W(t,a=>a.name),i=W(n,a=>a.name);(r.duplicates.length>0||i.duplicates.length>0)&&b(e,{kind:"duplicate-layer",path:"$.layers",classification:"judgment-required",message:"Duplicate layer names make policy ownership ambiguous.",before:r.duplicates,after:i.duplicates});for(let a of[...new Set([...r.values.keys(),...i.values.keys()])].sort()){let o=r.values.get(a),d=i.values.get(a),l=`$.layers[${a}]`;if(!o&&d){b(e,{kind:"layer-added",path:l,classification:"judgment-required",message:"A layer was added; verify overlap, ownership, and rule coverage.",after:d});continue}if(o&&!d){b(e,{kind:"layer-removed",path:l,classification:"weakening",message:"Removing a layer can leave its source paths ungoverned.",before:o});continue}if(!o||!d)continue;P(e,`${l}.patterns`,o.patterns,d.patterns,{added:"strengthening",removed:"weakening",addedMessage:"Additional paths are governed by this layer.",removedMessage:"Paths were removed from this layer and may become ungoverned."}),P(e,`${l}.exclude`,o.exclude,d.exclude,{added:"weakening",removed:"strengthening",addedMessage:"Additional paths are excluded from this layer.",removedMessage:"Fewer paths are excluded from this layer."});let u=q(o),y=q(d);P(e,`${l}.forbiddenGlobals`,u.rawGlobals,y.rawGlobals,{added:"strengthening",removed:"weakening",addedMessage:"Additional forbidden globals are enforced in this layer.",removedMessage:"A forbidden-global protection was removed from this layer."}),P(e,`${l}.capabilities`,u.atoms,y.atoms,{added:"strengthening",removed:"weakening",addedMessage:"Additional ambient/import protection is enforced in this layer (coverage atoms).",removedMessage:"An ambient/import protection was lost from this layer (coverage atoms)."}),S(o.intentPrefixes).join("\0")!==S(d.intentPrefixes).join("\0")&&b(e,{kind:"intent-prefixes-changed",path:`${l}.intentPrefixes`,classification:"judgment-required",message:"Intent ownership changed and must be reviewed against publishers and consumers.",before:S(o.intentPrefixes),after:S(d.intentPrefixes)}),Y(e,`${l}.mayImportInfrastructure`,o.mayImportInfrastructure===!0,d.mayImportInfrastructure===!0,"weakening","The layer may now import infrastructure directly.","Direct infrastructure imports are no longer allowed for this layer."),Y(e,`${l}.optional`,o.optional===!0,d.optional===!0,"weakening","The layer is now optional and can be absent without a strict warning.","The layer is now required when its contract is active.")}}function st(e,t,n){let r=o=>`${o.from}->${o.to}`,i=W(t,r),a=W(n,r);(i.duplicates.length>0||a.duplicates.length>0)&&b(e,{kind:"duplicate-rule",path:"$.rules",classification:"judgment-required",message:"Duplicate rule edges make the effective verdict order-dependent.",before:i.duplicates,after:a.duplicates});for(let o of[...new Set([...i.values.keys(),...a.values.keys()])].sort()){let d=i.values.get(o),l=a.values.get(o),u=`$.rules[${o}]`;if(!d&&l){l.allowed===!1&&b(e,{kind:"deny-added",path:u,classification:"strengthening",message:"A denied dependency edge was added.",after:l});continue}if(d&&!l){d.allowed===!1&&b(e,{kind:"deny-removed",path:u,classification:"weakening",message:"A denied dependency edge was removed.",before:d});continue}if(!d||!l)continue;d.allowed!==l.allowed&&b(e,{kind:l.allowed?"deny-disabled":"deny-enabled",path:`${u}.allowed`,classification:l.allowed?"weakening":"strengthening",message:l.allowed?"A previously denied dependency edge is now allowed.":"A dependency edge is now denied.",before:d.allowed,after:l.allowed});let y=d.peerIsolation===!0,g=l.peerIsolation===!0;if(y!==g){let c=d.from===d.to&&l.from===l.to;b(e,{kind:g?"peer-isolation-enabled":"peer-isolation-disabled",path:`${u}.peerIsolation`,classification:c?g?"strengthening":"weakening":"judgment-required",message:c?g?"Cross-slice dependencies inside this layer are now denied.":"Cross-slice dependencies inside this layer are no longer denied.":"Changing peer isolation on a cross-layer edge changes the denial scope.",before:y,after:g})}S(d.sliceFolders).join("\0")!==S(l.sliceFolders).join("\0")&&b(e,{kind:"slice-folders-changed",path:`${u}.sliceFolders`,classification:"judgment-required",message:"Slice ownership folders changed and can reclassify existing dependencies.",before:S(d.sliceFolders),after:S(l.sliceFolders)})}}function ot(e,t,n){let r=t.safety??{},i=n.safety??{};for(let a of["maxTsSuppressions","maxAnyCasts"]){let o=r[a]??0,d=i[a]??0;o!==d&&b(e,{kind:d>o?"threshold-raised":"threshold-lowered",path:`$.safety.${a}`,classification:d>o?"weakening":"strengthening",message:d>o?"The safety threshold allows more violations.":"The safety threshold allows fewer violations.",before:o,after:d})}for(let a of["allowInMemory","allowDisabledPeerIsolation"])Y(e,`$.safety.${a}`,r[a]===!0,i[a]===!0,"weakening","A safety exception was enabled.","A safety exception was disabled.")}function ct(e){return e.some(t=>t.classification==="weakening")?"weakening":e.some(t=>t.classification==="judgment-required")?"judgment-required":e.some(t=>t.classification==="strengthening")?"strengthening":"neutral"}function Me(e,t){let n=[];P(n,"$.include",e.include,t.include,{added:"strengthening",removed:"weakening",addedMessage:"Additional project roots are governed.",removedMessage:"Project roots were removed from governance."}),P(n,"$.exclude",e.exclude,t.exclude,{added:"weakening",removed:"strengthening",addedMessage:"Additional project paths are excluded from governance.",removedMessage:"Fewer project paths are excluded from governance."}),P(n,"$.dynamicImportAllowlist",e.dynamicImportAllowlist,t.dynamicImportAllowlist,{added:"weakening",removed:"strengthening",addedMessage:"Additional files may use non-literal dynamic imports.",removedMessage:"Fewer files may use non-literal dynamic imports."}),Y(n,"$.excludeGenerated",e.excludeGenerated!==!1,t.excludeGenerated!==!1,"weakening","Generated source is now excluded from governance.","Generated source is now governed.");let r={off:0,soft:1,"framework-soft":1,strict:2},i=e.cyclePolicy??"strict",a=t.cyclePolicy??"strict";if(i!==a){let o=r[a]===r[i]?"judgment-required":r[a]>r[i]?"strengthening":"weakening";b(n,{kind:"cycle-policy-changed",path:"$.cyclePolicy",classification:o,message:"The cycle enforcement level changed.",before:i,after:a})}return(e.frameworkOverlay??null)!==(t.frameworkOverlay??null)&&b(n,{kind:"framework-overlay-changed",path:"$.frameworkOverlay",classification:"judgment-required",message:"The framework overlay changed and may alter effective layer matching.",before:e.frameworkOverlay??null,after:t.frameworkOverlay??null}),at(n,e.layers,t.layers),st(n,e.rules,t.rules),ot(n,e,t),n.sort((o,d)=>o.path.localeCompare(d.path)||o.id.localeCompare(d.id)),{schemaVersion:_e,classification:ct(n),findings:n}}function Ne(e,t){if(!e||e.schemaVersion!==_e||typeof e.basePolicyHash!="string"||typeof e.candidatePolicyHash!="string"||typeof e.reason!="string"||!Array.isArray(e.findingIds)||e.findingIds.some(i=>typeof i!="string")||e.reason.trim().length===0||e.basePolicyHash!==t.basePolicyHash||e.candidatePolicyHash!==t.candidatePolicyHash)return!1;let n=S(e.findingIds),r=S(t.findingIds);return n.length===r.length&&n.every((i,a)=>i===r[a])}function v(e){let t=[];for(let n of e.replace(/\\/g,"/").split("/"))!n||n==="."||(n===".."&&t.length>0&&t.at(-1)!==".."?t.pop():t.push(n));return t.join("/")}function Te(e){return e!==void 0&&/[A-Za-z0-9_$]/.test(e)}function L(e,t){for(;t<e.length&&/\s/.test(e[t]);)t+=1;return t}function H(e,t){let n=e[t];if(n!=="'"&&n!=='"')return;let r=t,i="";for(t+=1;t<e.length;t+=1){let a=e[t];if(a===n)return{value:i,offset:r,excerpt:e.slice(r,t+1)};a==="\\"&&t+1<e.length?(i+=e[t+1],t+=1):i+=a}}function E(e,t,n){return e.startsWith(t,n)&&!Te(e[n-1])&&!Te(e[n+t.length])}function lt(e,t){if(t=L(e,t+6),e[t]==="(")return H(e,L(e,t+1));let n=!1;if(E(e,"type",t)){let i=L(e,t+4);e[i]!==","&&!E(e,"from",i)&&(n=!0)}let r=De(e,t,!0);return r&&n?{...r,typeOnly:!0}:r}function dt(e,t){t=t+6;let n=L(e,t),r=!1;if(E(e,"type",n)){let a=L(e,n+4);(e[a]==="{"||e[a]==="*")&&(r=!0)}let i=De(e,t,!1);return i&&r?{...i,typeOnly:!0}:i}function De(e,t,n){for(;t<e.length;t+=1){if(e[t]===";")return;if(E(e,"from",t))return H(e,L(e,t+4));if(n&&(e[t]==="'"||e[t]==='"'))return H(e,t);if(t>0&&(E(e,"import",t)||E(e,"export",t)))return}}function pt(e,t){for(t+=1;t<e.length;t+=1){let n=e[t];if(n==="\\")t+=1;else if(n==="`")return t}return e.length}function ft(e,t){let n=t-1;for(;n>=0&&/\s/.test(e[n]);)n-=1;if(e[n]===".")return;let r=L(e,t+7);if(e[r]!=="(")return;r=L(e,r+1);let i=H(e,r);return i?{...i,requireCall:!0}:void 0}function ut(e){let t=[];for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="/"&&e[n+1]==="/"){if(n=e.indexOf(`
|
|
8
|
+
`,n+2),n<0)break;continue}if(r==="/"&&e[n+1]==="*"){let a=e.indexOf("*/",n+2);if(a<0)break;n=a+1;continue}if(r==="`"){n=pt(e,n);continue}if(r==="'"||r==='"'){let a=H(e,n);a&&(n=a.offset+a.excerpt.length-1);continue}let i=r==="i"&&E(e,"import",n)?lt(e,n):r==="e"&&E(e,"export",n)?dt(e,n):r==="r"&&E(e,"require",n)?ft(e,n):void 0;i&&(t.push(i),n=i.offset+i.excerpt.length-1)}return t}function Fe(e,t){let n=[],r=[];for(let i of ut(e.content)){let a=i.value;if(i.requireCall&&a.startsWith("."))continue;if(!a.startsWith(".")){if(i.typeOnly)continue;let u=j(a);if(!u)continue;let y=e.content.slice(0,i.offset).split(`
|
|
9
|
+
`).length;r.push({file:e.path,symbol:a,capability:u,evidence:{kind:"import",file:e.path,line:y,excerpt:i.excerpt}});continue}let o=e.content.slice(0,i.offset).split(`
|
|
10
|
+
`).length,d={kind:"import",file:e.path,line:o,excerpt:i.excerpt},l=gt(e.path,a,t);n.push({from:e.path,specifier:a,to:l?.path??null,resolution:l?"resolved":"unresolved",fromLayer:e.layer,toLayer:l?.layer??null,evidence:d})}return{edges:n,capabilityUses:r}}function gt(e,t,n){let r=e.split("/");r.pop();for(let a of t.split("/"))a==="."||a===""||(a===".."?r.pop():r.push(a));let i=r.join("/");for(let a of[i,`${i}.ts`,`${i}.tsx`,`${i}.mts`,`${i}.cts`,`${i}/index.ts`,`${i}/index.tsx`]){let o=n.get(a);if(o)return o}}function je(e,t){let n=[];for(let r of e){if(!r.to||!r.fromLayer||!r.toLayer)continue;let i=G(t.rules,r.fromLayer,r.toLayer,{fromPath:r.from,toPath:r.to,layers:t.layers});i&&n.push({ruleId:`layer-dependency:${i.from}->${i.to}`,message:i.message??`${i.from} must not depend on ${i.to}.`,edge:r,evidence:r.evidence})}return n}function he(e,t){let n=typeof e=="string"?ve(e,t):me(e,t);return{...n,policyHash:$(x(n.config))}}function mt(e){let t=he(e.baseConfig,e.baseSource??"base ark.config.json"),n=he(e.candidateConfig,e.candidateSource??"candidate ark.config.json"),r=Me(t.config,n.config),i=r.findings.filter(d=>d.classification==="weakening"||d.classification==="judgment-required").map(d=>d.id).sort(),a=i.length>0,o=a&&Ne(e.acknowledgement,{basePolicyHash:t.policyHash,candidatePolicyHash:n.policyHash,findingIds:i});return{schemaVersion:r.schemaVersion,basePolicyHash:t.policyHash,candidatePolicyHash:n.policyHash,classification:r.classification,findings:r.findings,blockingFindingIds:i,requiresAcknowledgement:a,acknowledged:o,valid:!a||o}}function J(e){let t=e.files.map(l=>{let u=v(l.path);return{path:u,content:l.content,contentHash:$(l.content),layer:_(u,e.contract.config.layers)??null}}).sort((l,u)=>l.path.localeCompare(u.path)),n=new Map(t.map(l=>[l.path,l])),r=[],i=[];for(let l of t){let u=Fe(l,n);r.push(...u.edges),i.push(...u.capabilityUses)}let a=je(r,e.contract.config),o=new Map(e.contract.config.layers.map(l=>[l.name,l])),d=new Map(e.contract.config.layers.map(l=>[l.name,new Set(z(l))]));for(let l of i){let u=n.get(l.file)?.layer;if(!u)continue;let y=o.get(u),g=ue(l.symbol,y?.forbiddenGlobals??[]);if(g){a.push({ruleId:"FORBIDDEN_GLOBAL",message:`${u} must not use module "${l.symbol}" because it is the import form of forbidden global "${g}".`,symbol:l.symbol,evidence:l.evidence});continue}d.get(u)?.has(l.capability)&&a.push({ruleId:"CAPABILITY_VIOLATION",message:`${u} denies the ${l.capability} capability; found import of "${l.symbol}".`,capability:l.capability,symbol:l.symbol,evidence:l.evidence})}return{ir:{schemaVersion:"1.0",policyHash:e.contract.policyHash,compilerOptionsHash:$(x(e.compilerOptions??{})),files:t,layers:e.contract.config.layers.map(l=>l.name),edges:r,capabilityUses:i,violations:a}}}function Ae(e){let t=new Map(e.files.map(n=>[v(n.path),n]));for(let n of e.changes){let r=v(n.path);"delete"in n&&n.delete?t.delete(r):"content"in n&&t.set(r,{path:r,content:n.content})}return J({contract:e.contract,files:[...t.values()],compilerOptions:e.compilerOptions})}function ht(e){let t=`${e.evidence.file}:${e.evidence.line}`;if(!e.edge)return`${e.ruleId} at ${t}: ${e.message}`;let n=e.edge.to??e.edge.specifier;return`${e.ruleId} at ${t}: ${e.edge.from} imports ${n}. ${e.message}`}function Ve(e){let t=0,n=new Map,r=new Map,i=new Set,a=[],o=[],d=l=>{n.set(l,t),r.set(l,t),t+=1,a.push(l),i.add(l);for(let g of[...e.get(l)??[]].sort())e.has(g)&&(n.has(g)?i.has(g)&&r.set(l,Math.min(r.get(l)??0,n.get(g)??0)):(d(g),r.set(l,Math.min(r.get(l)??0,r.get(g)??0))));if(r.get(l)!==n.get(l))return;let u=[],y;do{if(y=a.pop(),y===void 0)break;i.delete(y),u.push(y)}while(y!==l);u.length>1&&o.push(u.sort())};for(let l of[...e.keys()].sort())n.has(l)||d(l);return o.sort((l,u)=>l[0].localeCompare(u[0])).map(l=>({ruleId:"CIRCULAR_DEPENDENCY",file:l[0],line:1,target:l.join(" \u2192 "),message:`Circular dependency among ${l.length} files: ${l.join(" \u2192 ")} \u2192 ${l[0]}.`,cycleKind:"value"}))}function be(e){let t=e.contentViolations.map(a=>({...a})),n=(e.warnings??[]).map(a=>({...a})),r=new Map(e.files.map(a=>[a,new Set]));for(let a of e.edges){if(a.to&&a.to!==a.from&&!a.typeOnly&&r.has(a.from)&&r.get(a.from)?.add(a.to),!a.to||!a.toLayer)continue;let o=G(e.rules,a.fromLayer,a.toLayer,{fromPath:a.from,toPath:a.to,layers:e.config.layers});if(!o)continue;let d=!!o.peerIsolation;t.push({ruleId:"LAYER_IMPORT_VIOLATION",file:a.from,line:a.line,fromLayer:a.fromLayer,toLayer:a.toLayer,target:a.to,...a.typeOnly?{typeOnly:!0}:{},...a.targetTypeOnlyExports?{targetTypeOnlyExports:!0}:{},...a.sourcePureTypeModule?{sourcePureTypeModule:!0}:{},...a.namedBindingsTypeOnly?{namedBindingsTypeOnly:!0}:{},...!d&&a.portProofEligible?{portProofEligible:!0}:{},...a.kind?{edgeKind:a.kind}:{},...d?{peerIsolation:!0}:{},message:o.message??(d?`${a.fromLayer} must not ${a.kind} another slice of ${a.toLayer} (${a.from} \u2192 ${a.to}). Extract shared code or use events/ports across slices.`:`${a.fromLayer} must not ${a.kind} ${a.toLayer}.`)})}let i=String(e.config.cyclePolicy??"strict").toLowerCase();if(i!=="off"){let a=Ve(r);i==="soft"||i==="framework-soft"?n.push(...a.map(o=>({...o,message:`${o.message} (soft cycle policy \u2014 advisory only; set cyclePolicy: "strict" to fail the check)`,failsStrict:!1}))):t.push(...a)}return{violations:t,warnings:n,safety:e.safety}}function He(e){switch(e.ruleId){case"LAYER_IMPORT_VIOLATION":return e.typeOnly||e.targetTypeOnlyExports||e.namedBindingsTypeOnly?"Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.":e.peerIsolation?"Extract the shared dependency to a shared layer, then preflight again.":`Define a port in ${e.fromLayer??"the source layer"}, inject the ${e.toLayer??"outer-layer"} implementation, then preflight again.`;case"FORBIDDEN_GLOBAL":return`Inject ${e.target??"the capability"} through a port, then preflight again.`;case"CAPABILITY_VIOLATION":return`Define a ${String(e.capability??"capability")} port in ${e.fromLayer??"the walled layer"}, bind the implementation outside it, then preflight again.`;case"CIRCULAR_DEPENDENCY":return"Extract the shared dependency into a third module, then preflight again.";case"RAW_EVENT_PUBLISH":return"Publish through a registered intent creator, then run Ark again.";case"PUBLISH_MISSING_SOURCE":return"Add metadata.source to the publish call, then run Ark again.";default:return`Resolve ${typeof e.ruleId=="string"&&e.ruleId.length>0?e.ruleId:"ARK_UNKNOWN"} without weakening ark.config.json, then run Ark again.`}}function Ge(e){return $(x(e.map(({path:t,contentHash:n})=>({path:t,contentHash:n}))))}function At(e){let t=J(e),n=new Map(t.ir.files.map(s=>[s.path,s])),r=[],i=new Set,a=[];for(let s of e.changes){let p=s.path.replace(/\\/g,"/"),f=v(s.path);if(!f||f===".."||f.startsWith("../")||p.startsWith("/")||/^[A-Za-z]:\//.test(p)||p.includes("\0")){a.push({ruleId:"INVALID_CHANGE_PATH",file:"<change-set>",line:1,message:"Every change requires a safe, non-empty project-relative path."});continue}if(i.has(f)){a.push({ruleId:"DUPLICATE_CHANGE_PATH",file:f,line:1,message:`The atomic change set contains more than one operation for ${f}.`});continue}i.add(f),"delete"in s&&s.delete&&!n.has(f)&&a.push({ruleId:"DELETE_TARGET_MISSING",file:f,line:1,message:`Cannot delete ${f} because it is not present in the supplied base tree.`}),r.push("delete"in s&&s.delete?{path:f,delete:!0}:{path:f,content:"content"in s?s.content:""})}e.changes.length===0&&a.push({ruleId:"CHANGE_SET_EMPTY",file:"<change-set>",line:1,message:"Atomic preflight requires at least one create, update, or delete."});let o=Ae({...e,changes:r}),d=new Map(o.ir.files.map(s=>[s.path,s])),l=o.ir.violations.filter(s=>s.ruleId==="CAPABILITY_VIOLATION"||s.ruleId==="FORBIDDEN_GLOBAL").map(s=>{let p=d.get(s.evidence.file)?.layer;return{ruleId:s.ruleId,file:s.evidence.file,line:s.evidence.line,target:s.symbol,...s.capability?{capability:s.capability}:{},...p?{fromLayer:p}:{},edgeKind:"import",message:s.message}}),u=be({config:e.contract.config,rules:e.contract.config.rules,files:o.ir.files.map(s=>s.path),contentViolations:l,edges:o.ir.edges.filter(s=>!!s.fromLayer).map(s=>({from:s.from,fromLayer:s.fromLayer,...s.to?{to:s.to}:{},...s.toLayer?{toLayer:s.toLayer}:{},line:s.evidence.line,kind:"import"}))}),y=r.map(s=>{let p=v(s.path),f=n.get(p),m=d.get(p);return{path:p,operation:"delete"in s&&s.delete?"delete":f?"update":"create",...f?{beforeContentHash:f.contentHash}:{},...m?{candidateContentHash:m.contentHash}:{}}}).sort((s,p)=>s.path.localeCompare(p.path)),g=[...a,...u.violations].map(s=>({...s,nextAction:He(s)})),c=e.changeMap?re({changeMap:e.changeMap,changes:y,baseDependencies:t.ir.edges.flatMap(s=>s.to?[{from:s.from,to:s.to}]:[]),candidateDependencies:o.ir.edges.flatMap(s=>s.to?[{from:s.from,to:s.to}]:[])}):void 0;return{schemaVersion:"1.0",valid:g.length===0&&(c?.structurallyConverged??!0),readOnly:!0,policyHash:e.contract.policyHash,compilerOptionsHash:o.ir.compilerOptionsHash,baseTreeHash:Ge(t.ir.files),candidateTreeHash:Ge(o.ir.files),...e.changeMap?{changeMapHash:e.changeMap.hash}:{},...c?{convergence:c}:{},changes:y,violations:g,warnings:u.warnings}}function I(e,t,n={}){return{ruleId:e,message:t,...n}}function bt(e){let{config:t,rules:n,files:r,manifest:i}=e,a=[];if(t.dynamicImportAllowlist!==void 0&&(!Array.isArray(t.dynamicImportAllowlist)||t.dynamicImportAllowlist.some(s=>typeof s!="string"))&&a.push(I("CONFIG_INVALID_DYNAMIC_IMPORT_ALLOWLIST","dynamicImportAllowlist must be an array of file globs.")),t.safety!==void 0&&(t.safety===null||typeof t.safety!="object"||Array.isArray(t.safety)))a.push(I("CONFIG_INVALID_SAFETY","safety must be an object."));else if(t.safety)for(let s of["maxTsSuppressions","maxAnyCasts"]){let p=t.safety[s];p!==void 0&&(!Number.isInteger(p)||p<0)&&a.push(I("CONFIG_INVALID_SAFETY_THRESHOLD",`safety.${s} must be a non-negative integer.`))}let o=Array.isArray(t.layers)?t.layers:[],d=Array.isArray(i?.architecture?.layers)?i.architecture.layers:[],l=new Set([...o.map(s=>s.name).filter(Boolean),...d.map(s=>s.name).filter(s=>!!s)]);o.length===0&&a.push(I("CONFIG_NO_LAYERS","No file layers are configured; ark-check cannot classify files for import-boundary enforcement."));let u=new Set,y=new Set;for(let s of o){if(!s.name){a.push(I("CONFIG_LAYER_WITHOUT_NAME","A configured layer is missing a name."));continue}u.has(s.name)&&y.add(s.name),u.add(s.name),s.forbiddenGlobals!==void 0&&(!Array.isArray(s.forbiddenGlobals)||s.forbiddenGlobals.some(f=>typeof f!="string"))&&a.push(I("CONFIG_INVALID_FORBIDDEN_GLOBALS",`Layer "${s.name}" has an invalid forbiddenGlobals value; expected an array of strings (e.g. ["fetch", "Date.now"]). The entry is ignored.`,{layer:s.name}));let p=Array.isArray(s.patterns)?s.patterns:[];if(p.length===0){a.push(I("CONFIG_LAYER_WITHOUT_PATTERNS",`Layer "${s.name}" has no file patterns and will never classify files.`,{layer:s.name}));continue}for(let f of p){let m;try{m=D(f)}catch(h){a.push(I("CONFIG_INVALID_LAYER_PATTERN",`Layer "${s.name}" has an invalid pattern "${f}": ${h instanceof Error?h.message:String(h)}`,{layer:s.name,pattern:f}));continue}!r.some(h=>m.test(h))&&!s.optional&&a.push(I("CONFIG_LAYER_PATTERN_NO_MATCHES",`Layer "${s.name}" pattern "${f}" matched no included files.`,{layer:s.name,pattern:f,failsStrict:!1}))}}for(let s of y)a.push(I("CONFIG_DUPLICATE_LAYER",`Layer "${s}" is configured more than once.`,{layer:s}));if(l.size>0)for(let s of n??[])s.from&&!l.has(s.from)&&a.push(I("CONFIG_RULE_UNKNOWN_FROM_LAYER",`Rule references unknown source layer "${s.from}".`,{fromLayer:s.from,toLayer:s.to})),s.to&&!l.has(s.to)&&a.push(I("CONFIG_RULE_UNKNOWN_TO_LAYER",`Rule references unknown target layer "${s.to}".`,{fromLayer:s.from,toLayer:s.to}));let g=new Set;if(o.length>1)for(let s of r){let p=-1,f=[];for(let m of o)for(let h of m.patterns??[]){if(!D(h).test(s))continue;let A=Q(h);A>p?(p=A,f=[m.name]):A===p&&!f.includes(m.name)&&f.push(m.name)}f.length>1&&g.add([...f].sort().join(" + "))}g.size>0&&a.push(I("CONFIG_AMBIGUOUS_LAYERS",`Some files match multiple layers at equal specificity; classification falls back to declaration order. Disambiguate the overlapping patterns: ${[...g].join(", ")}.`,{pairs:[...g]}));let c=r.filter(s=>!_(s,o));return c.length>0&&a.push(I("CONFIG_UNCLASSIFIED_FILES",`${c.length} included source file(s) are not matched by any configured layer; ark-check will not enforce import rules for those source files.`,{count:c.length,samples:c.slice(0,5)})),a}export{fe as AMBIENT_CAPABILITY_ENTRIES,de as CAPABILITY_IDS,ce as SOURCE_POLICY_MESSAGES,Ze as ambientCoveredByForbiddenGlobals,re as analyzeArchitectureConvergence,Ae as analyzeChange,mt as analyzePolicyDelta,J as analyzeProject,V as capabilityForAmbientName,j as capabilityForModuleSpecifier,Je as classifyPublishFacts,bt as collectAnalysisConfigWarnings,Qe as collectCapabilityUses,oe as collectForbiddenCapabilityUses,Ve as detectArchitectureCycles,z as effectiveCapabilityDeny,be as evaluateArchitectureGraph,ht as explainViolation,se as extractSemanticDependencies,ue as forbiddenGlobalForModuleSpecifier,Ke as loadArchitectureChangeMap,he as loadContract,$e as looksLikeArkIntent,Xe as lowerForbiddenGlobal,q as loweredLayerCoverage,At as preflightChange};
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
typeOnlyExportNames,
|
|
23
23
|
} from './ast-scan.mjs';
|
|
24
24
|
import { provePortProofInject } from './port-proof.mjs';
|
|
25
|
+
import { summarizeParseHealth } from './parse-health.mjs';
|
|
25
26
|
import {
|
|
26
27
|
intentLayersFromManifest,
|
|
27
28
|
layerForIntent,
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
effectiveCapabilityDeny,
|
|
36
37
|
evaluateArchitectureGraph,
|
|
37
38
|
extractSemanticDependencies,
|
|
39
|
+
forbiddenGlobalForModuleSpecifier,
|
|
38
40
|
} from './analysis-engine.mjs';
|
|
39
41
|
import { normalize } from './scan-files.mjs';
|
|
40
42
|
import { collectSafetyDiagnostics } from './safety-diagnostics.mjs';
|
|
@@ -80,8 +82,10 @@ export function scanSourceFile(ts, root, config, rules, manifestIntentLayers, fi
|
|
|
80
82
|
for (const use of collectCapabilityUses(ts, sourceFile)) {
|
|
81
83
|
if (!capabilityDeny.has(use.capability)) continue;
|
|
82
84
|
if (
|
|
83
|
-
use.source === 'ambient-global' &&
|
|
84
|
-
|
|
85
|
+
(use.source === 'ambient-global' &&
|
|
86
|
+
ambientCoveredByForbiddenGlobals(use.symbol, forbiddenGlobals)) ||
|
|
87
|
+
(use.source === 'import-based' &&
|
|
88
|
+
forbiddenGlobalForModuleSpecifier(use.symbol, forbiddenGlobals))
|
|
85
89
|
) {
|
|
86
90
|
continue;
|
|
87
91
|
}
|
|
@@ -192,6 +196,7 @@ export function scanSourceFile(ts, root, config, rules, manifestIntentLayers, fi
|
|
|
192
196
|
return {
|
|
193
197
|
contentViolations: violations,
|
|
194
198
|
edges,
|
|
199
|
+
parseDiagnosticCount: sourceFile.parseDiagnostics?.length ?? 0,
|
|
195
200
|
exportsOnlyTypes: sourceFileExportsOnlyTypes(ts, sourceFile),
|
|
196
201
|
typeOnlyExportNames: typeOnlyExportNames(ts, sourceFile),
|
|
197
202
|
hasTopLevelSideEffects: sourceFileHasTopLevelSideEffects(ts, sourceFile),
|
|
@@ -210,7 +215,7 @@ export function runArchitectureScan({ root, config, manifest, rules, files, ts,
|
|
|
210
215
|
const warnings = collectConfigWarnings(root, config, files, rules, manifest);
|
|
211
216
|
const safety = collectSafetyDiagnostics(ts, root, config, files);
|
|
212
217
|
warnings.push(...safety.warnings);
|
|
213
|
-
const cacheKey = args.noCache ? undefined : scanCacheKey(root, args);
|
|
218
|
+
const cacheKey = args.noCache ? undefined : scanCacheKey(root, args, ts.version);
|
|
214
219
|
const cachedFiles = cacheKey ? loadScanCache(root, cacheKey) : undefined;
|
|
215
220
|
const nextCacheFiles = {};
|
|
216
221
|
|
|
@@ -223,7 +228,10 @@ export function runArchitectureScan({ root, config, manifest, rules, files, ts,
|
|
|
223
228
|
const fileKey = `${stat.mtimeMs}:${stat.size}`;
|
|
224
229
|
const cached = cachedFiles?.[relFile];
|
|
225
230
|
const entry =
|
|
226
|
-
cached &&
|
|
231
|
+
cached &&
|
|
232
|
+
cached.fileKey === fileKey &&
|
|
233
|
+
Number.isSafeInteger(cached.parseDiagnosticCount) &&
|
|
234
|
+
cached.parseDiagnosticCount >= 0
|
|
227
235
|
? cached
|
|
228
236
|
: {
|
|
229
237
|
fileKey,
|
|
@@ -242,8 +250,36 @@ export function runArchitectureScan({ root, config, manifest, rules, files, ts,
|
|
|
242
250
|
}
|
|
243
251
|
|
|
244
252
|
const engineEdges = [];
|
|
253
|
+
// Y08 derives exact forbidden-global module duals from dependency facts
|
|
254
|
+
// already stored in every current cache entry. This stays downstream of the
|
|
255
|
+
// cache: no second AST scan, no cache-shape/version bump, and warm pre-Y08
|
|
256
|
+
// entries gain the verdict immediately.
|
|
257
|
+
const forbiddenModuleDualViolations = [];
|
|
258
|
+
const forbiddenModuleDualKeys = new Set();
|
|
245
259
|
for (const { file, sourceLayer, relFile, entry } of scanned) {
|
|
260
|
+
const layerConfig = config.layers.find((layer) => layer.name === sourceLayer);
|
|
261
|
+
const forbiddenGlobals = Array.isArray(layerConfig?.forbiddenGlobals)
|
|
262
|
+
? layerConfig.forbiddenGlobals.filter((item) => typeof item === 'string')
|
|
263
|
+
: [];
|
|
246
264
|
for (const edge of entry.edges) {
|
|
265
|
+
if (!edge.typeOnly) {
|
|
266
|
+
const forbiddenGlobal = forbiddenGlobalForModuleSpecifier(
|
|
267
|
+
edge.specifier,
|
|
268
|
+
forbiddenGlobals
|
|
269
|
+
);
|
|
270
|
+
if (forbiddenGlobal) {
|
|
271
|
+
forbiddenModuleDualKeys.add(`${relFile}\0${edge.specifier}`);
|
|
272
|
+
forbiddenModuleDualViolations.push({
|
|
273
|
+
ruleId: 'FORBIDDEN_GLOBAL',
|
|
274
|
+
file: relFile,
|
|
275
|
+
line: edge.line,
|
|
276
|
+
fromLayer: sourceLayer,
|
|
277
|
+
target: edge.specifier,
|
|
278
|
+
edgeKind: edge.kind,
|
|
279
|
+
message: `${sourceLayer} must not use module "${edge.specifier}" because it is the import form of forbidden global "${forbiddenGlobal}".`,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
247
283
|
const target = resolveImport(
|
|
248
284
|
ts,
|
|
249
285
|
edge.specifier,
|
|
@@ -309,13 +345,30 @@ export function runArchitectureScan({ root, config, manifest, rules, files, ts,
|
|
|
309
345
|
|
|
310
346
|
if (cacheKey) saveScanCache(root, cacheKey, nextCacheFiles);
|
|
311
347
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
348
|
+
// A cache written before Y08 can retain the overlapping capability-wall
|
|
349
|
+
// finding for this import. Filter by file+specifier (not line: D7 explicitly
|
|
350
|
+
// does not depend on cross-engine line anchors), then let FORBIDDEN_GLOBAL be
|
|
351
|
+
// the one declared-policy voice.
|
|
352
|
+
const contentViolations = scanned
|
|
353
|
+
.flatMap(({ entry }) => entry.contentViolations)
|
|
354
|
+
.filter(
|
|
355
|
+
(violation) =>
|
|
356
|
+
!(
|
|
357
|
+
violation.ruleId === 'CAPABILITY_VIOLATION' &&
|
|
358
|
+
forbiddenModuleDualKeys.has(`${violation.file}\0${violation.target}`)
|
|
359
|
+
)
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
return {
|
|
363
|
+
...evaluateArchitectureGraph({
|
|
364
|
+
config,
|
|
365
|
+
rules,
|
|
366
|
+
files: scanned.map(({ relFile }) => relFile),
|
|
367
|
+
contentViolations: [...contentViolations, ...forbiddenModuleDualViolations],
|
|
368
|
+
edges: engineEdges,
|
|
369
|
+
warnings,
|
|
370
|
+
safety: safety.report,
|
|
371
|
+
}),
|
|
372
|
+
parseHealth: summarizeParseHealth(scanned),
|
|
373
|
+
};
|
|
321
374
|
}
|