arkgate 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +142 -0
- package/README.md +7 -5
- package/bin/ark-check-runtime.mjs +244 -25
- package/bin/ark-check.mjs +10 -1
- package/bin/ark-layer-match.mjs +80 -5
- package/bin/ark-shared.mjs +170 -9
- package/bin/ark.mjs +52 -5
- package/bin/lib/adapter-contract.mjs +7 -1
- package/bin/lib/agent-gates.mjs +2 -0
- package/bin/lib/analysis-engine.mjs +6 -6
- package/bin/lib/arkrules-sensors.mjs +63 -22
- package/bin/lib/ci-and-commands.mjs +148 -8
- package/bin/lib/core-ratchet.mjs +9 -4
- package/bin/lib/doctor-advisories.mjs +8 -1
- package/bin/lib/doctor-plan.mjs +277 -59
- package/bin/lib/enforcement-honesty.mjs +351 -26
- package/bin/lib/enforcement-state.mjs +1 -1
- package/bin/lib/field-install.mjs +35 -2
- package/bin/lib/graph-blind.mjs +1 -1
- package/bin/lib/html-report-advisories.mjs +8 -25
- package/bin/lib/html-report-depth.mjs +167 -3
- package/bin/lib/html-report.mjs +12 -5
- package/bin/lib/install-migrate.mjs +109 -6
- package/bin/lib/managed-upgrade.mjs +100 -1
- package/bin/lib/presets.mjs +314 -46
- package/bin/lib/project-root.mjs +268 -0
- package/bin/lib/remediation.mjs +12 -11
- package/bin/lib/rules-inventory.mjs +71 -29
- package/bin/lib/rules-under-contract.mjs +389 -5
- package/bin/lib/start-preview.mjs +48 -14
- package/bin/lib/suggestions.mjs +118 -3
- package/bin/lib/unavailable-analysis.mjs +2 -0
- package/bin/lib/upgrade-command.mjs +325 -14
- package/bin/lib/write-path-capabilities.mjs +38 -9
- package/dist/eslint/index.cjs +2 -2
- package/dist/eslint/index.d.ts +27 -2
- package/dist/eslint/index.js +2 -2
- package/dist/index.cjs +16 -14
- package/dist/index.d.ts +3 -1
- package/dist/index.js +16 -14
- package/docs/README.md +3 -2
- package/docs/ai-gates.md +15 -11
- package/docs/brownfield-adoption.md +38 -0
- package/docs/configuration.md +59 -7
- package/docs/package-surface.md +3 -2
- package/docs/product-voice.md +10 -1
- package/docs/typescript-support.md +9 -5
- package/docs/use.md +7 -5
- package/package.json +3 -1
- package/server.json +3 -3
- package/templates/architecture-playbook.json +3 -0
- package/templates/layers/shared-types.starter.json +29 -0
- package/templates/skills/ark-adopt.md +2 -0
- package/templates/skills/ark-explain.md +23 -5
- package/templates/skills/ark-explore.md +21 -1
- package/templates/skills/ark-fix.md +16 -5
- package/templates/skills/ark-upgrade.md +57 -11
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/** Read-only-first `ark upgrade` orchestration. Managed identity logic lives separately. */
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
3
4
|
import { createRequire } from 'node:module';
|
|
4
5
|
import path from 'node:path';
|
|
5
6
|
|
|
7
|
+
import { arkCommand } from '../ark-shared.mjs';
|
|
6
8
|
import { describePackageVersionDualTruth } from './field-install.mjs';
|
|
9
|
+
import { __packageRoot } from './gate-files.mjs';
|
|
7
10
|
import {
|
|
8
11
|
applyManagedUpgrade,
|
|
9
12
|
managedUpgradeJson,
|
|
@@ -17,6 +20,259 @@ function installedCli(root) {
|
|
|
17
20
|
return path.join(path.dirname(packageJson), 'bin', 'ark.mjs');
|
|
18
21
|
}
|
|
19
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the project's installed arkgate package.json.
|
|
25
|
+
* Shallow `node_modules/arkgate` first, then Node module resolution (hoisted monorepos).
|
|
26
|
+
* @returns {string|null} absolute path to package.json, or null when not installed for this root
|
|
27
|
+
*/
|
|
28
|
+
export function resolveProjectArkgatePackageJson(root) {
|
|
29
|
+
const resolvedRoot = path.resolve(root);
|
|
30
|
+
const shallow = path.join(resolvedRoot, 'node_modules', 'arkgate', 'package.json');
|
|
31
|
+
if (fs.existsSync(shallow)) return shallow;
|
|
32
|
+
try {
|
|
33
|
+
const requireFromProject = createRequire(path.join(resolvedRoot, 'package.json'));
|
|
34
|
+
return requireFromProject.resolve('arkgate/package.json');
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Compare numeric major.minor.patch cores (prerelease / build ignored).
|
|
42
|
+
* @returns {-1|0|1}
|
|
43
|
+
*/
|
|
44
|
+
export function compareSemverCore(a, b) {
|
|
45
|
+
const parse = (value) => {
|
|
46
|
+
const core = String(value ?? '')
|
|
47
|
+
.trim()
|
|
48
|
+
.replace(/^v/i, '')
|
|
49
|
+
.split(/[-+]/)[0];
|
|
50
|
+
const parts = core.split('.').map((part) => {
|
|
51
|
+
const n = Number.parseInt(part, 10);
|
|
52
|
+
return Number.isFinite(n) ? n : 0;
|
|
53
|
+
});
|
|
54
|
+
return [parts[0] || 0, parts[1] || 0, parts[2] || 0];
|
|
55
|
+
};
|
|
56
|
+
const left = parse(a);
|
|
57
|
+
const right = parse(b);
|
|
58
|
+
for (let i = 0; i < 3; i += 1) {
|
|
59
|
+
if (left[i] < right[i]) return -1;
|
|
60
|
+
if (left[i] > right[i]) return 1;
|
|
61
|
+
}
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** True when candidate is the same path as root or a descendant (after resolve). */
|
|
66
|
+
export function isPathInside(candidate, root) {
|
|
67
|
+
const resolvedCandidate = path.resolve(candidate);
|
|
68
|
+
const resolvedRoot = path.resolve(root);
|
|
69
|
+
if (resolvedCandidate === resolvedRoot) return true;
|
|
70
|
+
const relative = path.relative(resolvedRoot, resolvedCandidate);
|
|
71
|
+
return relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function tryRealpath(filePath) {
|
|
75
|
+
try {
|
|
76
|
+
return fs.realpathSync(filePath);
|
|
77
|
+
} catch {
|
|
78
|
+
return path.resolve(filePath);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Recovery guidance for refused upgrades. Prefer package-manager-aware
|
|
84
|
+
* `arkCommand` (works for hoisted monorepos where nested `--root` has no shallow
|
|
85
|
+
* `node_modules/arkgate`). Shallow `node …/ark.mjs` is secondary and install-root only.
|
|
86
|
+
* @param {string} root
|
|
87
|
+
*/
|
|
88
|
+
export function recoveryUseLocal(root) {
|
|
89
|
+
const preferred = arkCommand(root, 'arkgate', 'upgrade …');
|
|
90
|
+
return (
|
|
91
|
+
`Use the project-local CLI (package-manager runner preferred):\n` +
|
|
92
|
+
` ${preferred}\n` +
|
|
93
|
+
`From the workspace install root you may also run:\n` +
|
|
94
|
+
` node node_modules/arkgate/bin/ark.mjs upgrade …\n` +
|
|
95
|
+
`Hoisted monorepos: prefer the package-manager form above — nested packages may lack a shallow node_modules/arkgate path.`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @param {{
|
|
101
|
+
* root?: string,
|
|
102
|
+
* cliVersion: string|null,
|
|
103
|
+
* projectVersion: string|null,
|
|
104
|
+
* cliPackageRoot: string,
|
|
105
|
+
* kind: 'older' | 'unknown-cli' | 'project-unreadable',
|
|
106
|
+
* projectPkgPath?: string,
|
|
107
|
+
* }} detail
|
|
108
|
+
*/
|
|
109
|
+
function staleCliRefuseMessage(detail) {
|
|
110
|
+
const { cliVersion, projectVersion, cliPackageRoot, kind, projectPkgPath } = detail;
|
|
111
|
+
const recovery = recoveryUseLocal(detail.root ?? '.');
|
|
112
|
+
if (kind === 'project-unreadable') {
|
|
113
|
+
const where = projectPkgPath ? ` at ${projectPkgPath}` : '';
|
|
114
|
+
const cliPart = cliVersion
|
|
115
|
+
? `this CLI (v${cliVersion} at ${cliPackageRoot})`
|
|
116
|
+
: `this CLI (unknown version at ${cliPackageRoot})`;
|
|
117
|
+
return (
|
|
118
|
+
`Refusing ark upgrade: cannot read the project's arkgate version${where}; ${cliPart} is outside the install tree.\n` +
|
|
119
|
+
`Use the project-local binary so managed upgrade can resolve a real install.\n` +
|
|
120
|
+
recovery
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const versionLine =
|
|
125
|
+
kind === 'unknown-cli' || !cliVersion
|
|
126
|
+
? `Refusing ark upgrade: this CLI (unknown version at ${cliPackageRoot}) is outside the project's arkgate install ` +
|
|
127
|
+
`(v${projectVersion}).`
|
|
128
|
+
: `Refusing ark upgrade: this CLI (v${cliVersion} at ${cliPackageRoot}) is older than the project's arkgate (v${projectVersion}).`;
|
|
129
|
+
|
|
130
|
+
// Pre-managed era (before content-identity / plan-digest, ~3.8.0): mutative 2.x wording.
|
|
131
|
+
const preManaged = !cliVersion || compareSemverCore(cliVersion, '3.8.0') < 0;
|
|
132
|
+
const second = preManaged
|
|
133
|
+
? 'Global/stale arkgate 2.x mutates skills and is unsafe next to 3.8+/4.0 managed upgrade.'
|
|
134
|
+
: 'Older outside-tree CLI must not manage a newer project install; use the project-local binary.';
|
|
135
|
+
|
|
136
|
+
return `${versionLine}\n${second}\n${recovery}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Fail-closed when this process is a global/stale arkgate CLI older than the
|
|
141
|
+
* project's installed arkgate. Legacy global 2.x mutates skills; a newer
|
|
142
|
+
* project pin must not be managed by an older outside-tree binary.
|
|
143
|
+
*
|
|
144
|
+
* Allow: no local install; CLI package root inside the project install tree
|
|
145
|
+
* (realpath); running CLI version >= project installed version (including newer globals).
|
|
146
|
+
*
|
|
147
|
+
* @param {string} root project root for --root
|
|
148
|
+
* @param {{
|
|
149
|
+
* cliVersion?: string|null,
|
|
150
|
+
* cliPackageRoot?: string|null,
|
|
151
|
+
* projectPackageJsonPath?: string|null,
|
|
152
|
+
* }} [options]
|
|
153
|
+
* @returns {{ refuse: boolean, reason: string, message?: string, cliVersion?: string|null, projectVersion?: string|null, cliPackageRoot?: string|null, projectPackageRoot?: string|null }}
|
|
154
|
+
*/
|
|
155
|
+
export function evaluateStaleUpgradeCli(root, options = {}) {
|
|
156
|
+
const projectPkgPath =
|
|
157
|
+
options.projectPackageJsonPath ?? resolveProjectArkgatePackageJson(root);
|
|
158
|
+
if (!projectPkgPath || !fs.existsSync(projectPkgPath)) {
|
|
159
|
+
return { refuse: false, reason: 'no-local-arkgate' };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const cliPackageRoot = tryRealpath(options.cliPackageRoot ?? __packageRoot);
|
|
163
|
+
const projectPackageRoot = tryRealpath(path.dirname(projectPkgPath));
|
|
164
|
+
|
|
165
|
+
// Project-local CLI (node_modules/arkgate or same realpath via pnpm link) is always trusted.
|
|
166
|
+
if (isPathInside(cliPackageRoot, projectPackageRoot)) {
|
|
167
|
+
let projectVersion = null;
|
|
168
|
+
try {
|
|
169
|
+
const pkg = JSON.parse(fs.readFileSync(projectPkgPath, 'utf8'));
|
|
170
|
+
projectVersion = typeof pkg.version === 'string' ? pkg.version : null;
|
|
171
|
+
} catch {
|
|
172
|
+
/* local path still trusted */
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
refuse: false,
|
|
176
|
+
reason: 'project-local-cli',
|
|
177
|
+
cliVersion: options.cliVersion ?? null,
|
|
178
|
+
projectVersion,
|
|
179
|
+
cliPackageRoot,
|
|
180
|
+
projectPackageRoot,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Outside tree: need a readable project version to compare; fail closed if we cannot prove safety.
|
|
185
|
+
let projectVersion = null;
|
|
186
|
+
try {
|
|
187
|
+
const pkg = JSON.parse(fs.readFileSync(projectPkgPath, 'utf8'));
|
|
188
|
+
projectVersion = typeof pkg.version === 'string' ? pkg.version : null;
|
|
189
|
+
} catch {
|
|
190
|
+
return {
|
|
191
|
+
refuse: true,
|
|
192
|
+
reason: 'project-unreadable',
|
|
193
|
+
message: staleCliRefuseMessage({
|
|
194
|
+
root,
|
|
195
|
+
cliVersion: typeof options.cliVersion === 'string' ? options.cliVersion : null,
|
|
196
|
+
projectVersion: null,
|
|
197
|
+
cliPackageRoot,
|
|
198
|
+
kind: 'project-unreadable',
|
|
199
|
+
projectPkgPath,
|
|
200
|
+
}),
|
|
201
|
+
cliVersion: typeof options.cliVersion === 'string' ? options.cliVersion : null,
|
|
202
|
+
projectVersion: null,
|
|
203
|
+
cliPackageRoot,
|
|
204
|
+
projectPackageRoot,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
if (!projectVersion) {
|
|
208
|
+
return {
|
|
209
|
+
refuse: true,
|
|
210
|
+
reason: 'project-version-missing',
|
|
211
|
+
message: staleCliRefuseMessage({
|
|
212
|
+
root,
|
|
213
|
+
cliVersion: typeof options.cliVersion === 'string' ? options.cliVersion : null,
|
|
214
|
+
projectVersion: null,
|
|
215
|
+
cliPackageRoot,
|
|
216
|
+
kind: 'project-unreadable',
|
|
217
|
+
projectPkgPath,
|
|
218
|
+
}),
|
|
219
|
+
cliVersion: typeof options.cliVersion === 'string' ? options.cliVersion : null,
|
|
220
|
+
projectVersion: null,
|
|
221
|
+
cliPackageRoot,
|
|
222
|
+
projectPackageRoot,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const cliVersion =
|
|
227
|
+
typeof options.cliVersion === 'string' && options.cliVersion
|
|
228
|
+
? options.cliVersion
|
|
229
|
+
: null;
|
|
230
|
+
if (!cliVersion) {
|
|
231
|
+
return {
|
|
232
|
+
refuse: true,
|
|
233
|
+
reason: 'outside-tree-unknown-version',
|
|
234
|
+
message: staleCliRefuseMessage({
|
|
235
|
+
root,
|
|
236
|
+
cliVersion: null,
|
|
237
|
+
projectVersion,
|
|
238
|
+
cliPackageRoot,
|
|
239
|
+
kind: 'unknown-cli',
|
|
240
|
+
}),
|
|
241
|
+
cliVersion,
|
|
242
|
+
projectVersion,
|
|
243
|
+
cliPackageRoot,
|
|
244
|
+
projectPackageRoot,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (compareSemverCore(cliVersion, projectVersion) < 0) {
|
|
249
|
+
return {
|
|
250
|
+
refuse: true,
|
|
251
|
+
reason: 'stale-outside-cli',
|
|
252
|
+
message: staleCliRefuseMessage({
|
|
253
|
+
root,
|
|
254
|
+
cliVersion,
|
|
255
|
+
projectVersion,
|
|
256
|
+
cliPackageRoot,
|
|
257
|
+
kind: 'older',
|
|
258
|
+
}),
|
|
259
|
+
cliVersion,
|
|
260
|
+
projectVersion,
|
|
261
|
+
cliPackageRoot,
|
|
262
|
+
projectPackageRoot,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
refuse: false,
|
|
268
|
+
reason: 'outside-cli-ok',
|
|
269
|
+
cliVersion,
|
|
270
|
+
projectVersion,
|
|
271
|
+
cliPackageRoot,
|
|
272
|
+
projectPackageRoot,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
20
276
|
function previewArgs(args) {
|
|
21
277
|
const next = ['upgrade', '--root', args.root, '--no-install'];
|
|
22
278
|
if (args.tools) next.push('--tools', args.tools);
|
|
@@ -31,15 +287,22 @@ function quote(value) {
|
|
|
31
287
|
return /^[A-Za-z0-9_./:@=-]+$/.test(text) ? text : `'${text.replace(/'/g, `'"'"'`)}'`;
|
|
32
288
|
}
|
|
33
289
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
290
|
+
/**
|
|
291
|
+
* Project-local nextCommand (never bare PATH `ark`) so paste-from-preview cannot re-hit global 2.x.
|
|
292
|
+
* Uses package-manager-aware runner (`npx` / `pnpm exec` / `yarn`) + `arkgate` bin name.
|
|
293
|
+
* @param {{ root: string, install?: boolean, tools?: string, acceptConflicts?: boolean, strict?: boolean, json?: boolean }} args
|
|
294
|
+
* @param {string|null|undefined} planDigest
|
|
295
|
+
*/
|
|
296
|
+
export function buildUpgradeNextCommand(args, planDigest) {
|
|
297
|
+
const flagParts = ['upgrade', '--apply', '--root', args.root];
|
|
298
|
+
if (!args.install) flagParts.push('--no-install');
|
|
299
|
+
if (!args.install && planDigest) flagParts.push('--plan-digest', planDigest);
|
|
300
|
+
if (args.tools) flagParts.push('--tools', args.tools);
|
|
301
|
+
if (args.acceptConflicts) flagParts.push('--accept-conflicts');
|
|
302
|
+
if (!args.strict) flagParts.push('--no-strict');
|
|
303
|
+
if (args.json) flagParts.push('--json');
|
|
304
|
+
const argsStr = flagParts.map(quote).join(' ');
|
|
305
|
+
return arkCommand(args.root, 'arkgate', argsStr);
|
|
43
306
|
}
|
|
44
307
|
|
|
45
308
|
function verify(root, json, arkCheck, runArkCheck) {
|
|
@@ -55,6 +318,44 @@ function verify(root, json, arkCheck, runArkCheck) {
|
|
|
55
318
|
|
|
56
319
|
export function runUpgradeCommand(args, dependencies) {
|
|
57
320
|
const root = args.root;
|
|
321
|
+
// Fail closed before install/plan when PATH resolves a global/stale CLI older
|
|
322
|
+
// than the project's installed arkgate (field footgun: Homebrew 2.x next to 3.8+/4.0).
|
|
323
|
+
const staleGuard =
|
|
324
|
+
typeof dependencies?.evaluateStaleUpgradeCli === 'function'
|
|
325
|
+
? dependencies.evaluateStaleUpgradeCli(root, {
|
|
326
|
+
cliVersion: dependencies.cliVersion,
|
|
327
|
+
cliPackageRoot: dependencies.cliPackageRoot,
|
|
328
|
+
})
|
|
329
|
+
: evaluateStaleUpgradeCli(root, {
|
|
330
|
+
cliVersion: dependencies?.cliVersion,
|
|
331
|
+
cliPackageRoot: dependencies?.cliPackageRoot,
|
|
332
|
+
});
|
|
333
|
+
if (staleGuard?.refuse) {
|
|
334
|
+
const message = staleGuard.message || 'Refusing ark upgrade: stale CLI.';
|
|
335
|
+
if (args.json) {
|
|
336
|
+
// Machine-readable refuse (same exit 2) so agents parsing stdout JSON are not blind.
|
|
337
|
+
console.log(
|
|
338
|
+
JSON.stringify(
|
|
339
|
+
{
|
|
340
|
+
refused: true,
|
|
341
|
+
reason: staleGuard.reason,
|
|
342
|
+
message,
|
|
343
|
+
cliVersion: staleGuard.cliVersion ?? null,
|
|
344
|
+
projectVersion: staleGuard.projectVersion ?? null,
|
|
345
|
+
cliPackageRoot: staleGuard.cliPackageRoot ?? null,
|
|
346
|
+
projectPackageRoot: staleGuard.projectPackageRoot ?? null,
|
|
347
|
+
nextCommand: arkCommand(root, 'arkgate', 'upgrade …'),
|
|
348
|
+
},
|
|
349
|
+
null,
|
|
350
|
+
2
|
|
351
|
+
)
|
|
352
|
+
);
|
|
353
|
+
} else {
|
|
354
|
+
console.error(message);
|
|
355
|
+
}
|
|
356
|
+
return 2;
|
|
357
|
+
}
|
|
358
|
+
|
|
58
359
|
if (args.apply && args.install) {
|
|
59
360
|
const skip =
|
|
60
361
|
typeof dependencies.shouldSkipArkgateInstall === 'function'
|
|
@@ -78,13 +379,23 @@ export function runUpgradeCommand(args, dependencies) {
|
|
|
78
379
|
if (exitCode !== 0) {
|
|
79
380
|
if (args.json && install.stderr) console.error(install.stderr.trim());
|
|
80
381
|
const recovery = `${command} ${commandArgs.join(' ')}`;
|
|
382
|
+
const rePreview = arkCommand(
|
|
383
|
+
root,
|
|
384
|
+
'arkgate',
|
|
385
|
+
[
|
|
386
|
+
'upgrade',
|
|
387
|
+
'--no-install',
|
|
388
|
+
'--root',
|
|
389
|
+
quote(root),
|
|
390
|
+
...(args.tools ? ['--tools', args.tools] : []),
|
|
391
|
+
...(!args.strict ? ['--no-strict'] : []),
|
|
392
|
+
...(args.json ? ['--json'] : []),
|
|
393
|
+
].join(' ')
|
|
394
|
+
);
|
|
81
395
|
console.error(
|
|
82
396
|
`Package update failed (exit ${exitCode}). Fix the install and re-run:\n` +
|
|
83
397
|
` ${recovery}\n` +
|
|
84
|
-
`Then:
|
|
85
|
-
(args.tools ? ` --tools ${args.tools}` : '') +
|
|
86
|
-
(!args.strict ? ' --no-strict' : '') +
|
|
87
|
-
(args.json ? ' --json' : '')
|
|
398
|
+
`Then: ${rePreview}`
|
|
88
399
|
);
|
|
89
400
|
return exitCode;
|
|
90
401
|
}
|
|
@@ -114,7 +425,7 @@ export function runUpgradeCommand(args, dependencies) {
|
|
|
114
425
|
const wouldWrite = plan.summary?.wouldWrite ?? 0;
|
|
115
426
|
const blocked = plan.summary?.blocked ?? 0;
|
|
116
427
|
const needsApply = wouldWrite > 0 || blocked > 0;
|
|
117
|
-
const command =
|
|
428
|
+
const command = buildUpgradeNextCommand(args, plan.planDigest);
|
|
118
429
|
if (args.json) {
|
|
119
430
|
// Always expose nextCommand for digest-bound apply (metadata/manifest optional);
|
|
120
431
|
// nothingToApply flags when content writes are zero so UIs do not urge apply.
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
} from './host-support-matrix.mjs';
|
|
20
20
|
import { detectActiveAgentHost } from './skill-install.mjs';
|
|
21
21
|
import { detectCiEnforcement } from './weakest-link.mjs';
|
|
22
|
-
import { buildEnforcementState } from './enforcement-state.mjs';
|
|
22
|
+
import { buildEnforcementState, packageInstallation } from './enforcement-state.mjs';
|
|
23
23
|
|
|
24
24
|
export const WRITE_CAPABILITY_NAMES = [
|
|
25
25
|
'hard-write',
|
|
@@ -46,29 +46,47 @@ function operationCovered(profile, operation) {
|
|
|
46
46
|
return profile.hookOperations.some((candidate) => candidate.toLowerCase() === normalized);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Local write ladder. Hook assets alone never prove hard without package install.
|
|
51
|
+
* @param {string} activeHost
|
|
52
|
+
* @param {object|null} support
|
|
53
|
+
* @param {Record<string, string[]>} evidence
|
|
54
|
+
* @param {object|undefined} attempt
|
|
55
|
+
* @param {{ packageInstalled?: boolean }} [opts]
|
|
56
|
+
*/
|
|
57
|
+
function buildEnforcementLadder(activeHost, support, evidence, attempt, opts = {}) {
|
|
58
|
+
const hookConfigured = evidence['hard-write'].length > 0;
|
|
59
|
+
const packageInstalled = opts.packageInstalled !== false;
|
|
51
60
|
const observedPreTool = attempt?.boundary === 'pre-tool';
|
|
52
61
|
const covered = observedPreTool && operationCovered(support, attempt.operation);
|
|
62
|
+
// P0B-PIN-ABSENT-WRITEPATH: never hard:true without pin+node_modules (packageInstalled).
|
|
63
|
+
// Hook config / observed coverage can still be active or operationCovered without hard.
|
|
53
64
|
const hard = Boolean(
|
|
54
|
-
support?.capabilities['hard-write'] &&
|
|
65
|
+
support?.capabilities['hard-write'] &&
|
|
66
|
+
packageInstalled &&
|
|
67
|
+
(hookConfigured || observedPreTool) &&
|
|
68
|
+
covered
|
|
55
69
|
);
|
|
56
|
-
|
|
70
|
+
// Configured assets → active "unverified" even when package is absent (hard stays false).
|
|
71
|
+
const inferredActive = (configured) => (configured ? 'unverified' : false);
|
|
57
72
|
return {
|
|
58
73
|
schemaVersion: '1.0',
|
|
59
74
|
activeHost,
|
|
60
75
|
localWrite: boundaryState({
|
|
61
76
|
supported: Boolean(support?.capabilities['hard-write']),
|
|
62
77
|
evidence: evidence['hard-write'],
|
|
63
|
-
active: observedPreTool ? covered : inferredActive(
|
|
78
|
+
active: observedPreTool ? Boolean(covered) : inferredActive(hookConfigured),
|
|
64
79
|
bypassable: !hard,
|
|
65
80
|
hard,
|
|
66
81
|
extra: {
|
|
67
|
-
installed
|
|
82
|
+
// Ladder "installed" here means hook assets; package install lives in enforcementState.
|
|
83
|
+
installed: hookConfigured || observedPreTool,
|
|
84
|
+
packageInstalled,
|
|
85
|
+
// completePatch describes op scope (coverage), not package install.
|
|
68
86
|
completePatch: Boolean(covered && attempt?.completePatch),
|
|
69
87
|
coverage: covered && attempt?.completePatch ? 'complete-patch' : support?.hookSurface ?? null,
|
|
70
88
|
...(observedPreTool
|
|
71
|
-
? { operation: attempt.operation, operationCovered: covered }
|
|
89
|
+
? { operation: attempt.operation, operationCovered: Boolean(covered) }
|
|
72
90
|
: { operationCovered: 'unverified' }),
|
|
73
91
|
},
|
|
74
92
|
}),
|
|
@@ -441,20 +459,31 @@ export function buildWritePathCapabilityModel(root, explicitHost, attempt) {
|
|
|
441
459
|
'merge-gate': [...inventory.evidence['merge-gate']],
|
|
442
460
|
};
|
|
443
461
|
|
|
462
|
+
const pkg = packageInstallation(root);
|
|
444
463
|
const model = {
|
|
445
464
|
activeHost,
|
|
446
465
|
support: getHostSupportProfile(activeHost),
|
|
447
466
|
capabilities: capabilityMap(capabilityEvidence),
|
|
448
467
|
capabilityEvidence,
|
|
468
|
+
packageInstallation: pkg,
|
|
449
469
|
enforcementLadder: buildEnforcementLadder(
|
|
450
470
|
activeHost,
|
|
451
471
|
getHostSupportProfile(activeHost),
|
|
452
472
|
capabilityEvidence,
|
|
453
|
-
attempt
|
|
473
|
+
attempt,
|
|
474
|
+
{ packageInstalled: pkg.installed === true }
|
|
454
475
|
),
|
|
455
476
|
inventory,
|
|
456
477
|
};
|
|
457
478
|
model.enforcementState = buildEnforcementState(root, { ...model, ci });
|
|
458
479
|
model.enforcementLadder.ciMerge.requiredStatus = model.enforcementState.ciMerge.required;
|
|
480
|
+
// Ladder/state agreement stamp for pin-absent consumers (configured ≠ installed).
|
|
481
|
+
if (pkg.installed !== true) {
|
|
482
|
+
model.packagePinHonesty = {
|
|
483
|
+
code: 'PACKAGE_PIN_ABSENT',
|
|
484
|
+
hard: false,
|
|
485
|
+
note: 'arkgate not resolved from project — ladder and enforcementState hard stay false (configured hooks ≠ installed).',
|
|
486
|
+
};
|
|
487
|
+
}
|
|
459
488
|
return model;
|
|
460
489
|
}
|
package/dist/eslint/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var Oe=Object.create;var L=Object.defineProperty;var _e=Object.getOwnPropertyDescriptor;var ve=Object.getOwnPropertyNames;var Pe=Object.getPrototypeOf,$e=Object.prototype.hasOwnProperty;var Te=(e,t)=>{for(var n in t)L(e,n,{get:t[n],enumerable:!0})},Q=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ve(t))!$e.call(e,r)&&r!==n&&L(e,r,{get:()=>t[r],enumerable:!(s=_e(t,r))||s.enumerable});return e};var ee=(e,t,n)=>(n=e!=null?Oe(Pe(e)):{},Q(t||!e||!e.__esModule?L(n,"default",{value:e,enumerable:!0}):n,e)),je=e=>Q(L({},"__esModule",{value:!0}),e);var at={};Te(at,{default:()=>it,findConfigPath:()=>T,globToRegExp:()=>E,isEdgeDenied:()=>F,layerForRelativePath:()=>R,loadArkConfig:()=>j,noDeniedCapabilities:()=>Le,noDomainInfraImports:()=>we,noForbiddenGlobals:()=>Ne,noRawEventPublish:()=>Ee,patternSpecificity:()=>D,plugin:()=>$,readTsconfigPathAliases:()=>he,requirePublishSource:()=>Ce,resolveImportSpecifier:()=>ke,resolveRelativeImport:()=>Ae});module.exports=je(at);var S=ee(require("fs"),1),d=ee(require("path"),1);var te=new Map;function ne(e){return/[.*+?^${}()|[\]\\]/.test(e)?`\\${e}`:e}function O(e){let t="";for(let n=0;n<e.length;n+=1){let s=e[n];if(s==="\\"&&n+1<e.length){let r=e[n+1];if("*?{}[],".includes(r)||r==="\\"){t+="\\"+r,n+=1;continue}t+="/";continue}t+=s}return t}function Me(e){let t=0;for(let n=0;n<e.length;n+=1){let s=e[n];if(s==="\\"){n+=1;continue}if(s==="{")t+=1;else if(s==="}"&&(t-=1,t<0))return!1}return t===0}function E(e){let t=te.get(e);if(t)return t;let n=O(e),s=Me(n),r="",i=0;for(let c=0;c<n.length;c+=1){let g=n[c];g==="\\"&&c+1<n.length?(r+=ne(n[c+1]),c+=1):g==="*"?n[c+1]==="*"?n[c+2]==="/"?(r+="(?:.*/)?",c+=2):(r+=".*",c+=1):r+="[^/]*":g==="?"?r+="[^/]":g==="{"&&s?(r+="(?:",i+=1):g==="}"&&s&&i>0?(r+=")",i-=1):g===","&&s&&i>0?r+="|":r+=ne(g)}let l=new RegExp(`^${r}$`);return te.set(e,l),l}function De(e){return O(String(e)).split("/").filter(Boolean).filter(n=>n!=="**"&&n!=="*"&&!n.includes("*")&&!n.includes("?")&&!n.includes("{")&&!n.includes("["))}function D(e,t){let n=O(String(e)),s=De(n),r=n.replace(/\*/g,"").length,i=s.length*1e4+r;if(t==null||t==="")return i;let l=String(t).split(/[/\\]/).filter(Boolean);if(s.length===0)return r;let c=0,g=-1;for(let f of s){let p=-1;for(let o=c;o<l.length;o+=1)if(l[o]===f){p=o;break}if(p<0)return i;g=p,c=p+1}return(g+1)*1e6+s.length*1e4+r}function R(e,t){let n=String(e).split(/[/\\]/).join("/"),s,r=-1;for(let i of t??[])if(!(i.exclude??[]).some(l=>E(l).test(n))){for(let l of i.patterns??[])if(E(l).test(n)){let c=D(l,n);c>r&&(r=c,s=i.name)}}return s}function re(e,t){if(!t?.length)return;let n=String(e).split(/[/\\]/).filter(Boolean),s=new Set(t.map(r=>String(r).toLowerCase()));for(let r=0;r<n.length-1;r+=1)if(s.has(n[r].toLowerCase()))return`${n[r].toLowerCase()}/${n[r+1].toLowerCase()}`}function Ve(e){let t=new Set;for(let n of e??[]){let r=O(String(n)).split("/").filter(Boolean);for(let i=0;i<r.length;i+=1){let l=r[i];if((l==="**"||l==="*")&&i>0){let c=r[i-1];c&&!c.includes("*")&&!c.includes("{")&&!c.includes("}")&&t.add(c)}}}return[...t]}function Fe(e,t,n){if(Array.isArray(e.sliceFolders)&&e.sliceFolders.length>0)return e.sliceFolders.filter(r=>typeof r=="string"&&r.length>0);let s=(n??[]).find(r=>r.name===t);return Ve(s?.patterns)}function V(e,t,n,s){for(let r of e??[])if(!(r.from!==t||r.to!==n)&&r.allowed===!1){if(r.peerIsolation){let i=s?.fromPath,l=s?.toPath;if(!i||!l)return r;let c=Fe(r,t,s?.layers);if(c.length===0)return r;let g=re(i,c),f=re(l,c);if(!g||!f||g!==f)return r;continue}if(t!==n)return r}}function F(e,t,n,s){return V(e,t,n,s)!==void 0}var He=["**/*.gen.ts","**/*.gen.tsx","**/*.generated.ts","**/*.generated.tsx"];function Ke(e){let t=Array.isArray(e?.exclude)?e.exclude.filter(s=>typeof s=="string"):[];return[...e?.excludeGenerated===!1?[]:He,...t]}function se(e,t){let n=String(e).split(/[/\\]/).join("/");return Ke(t).some(s=>E(s).test(n))}var oe=Object.freeze(["network","filesystem","clock","randomness","environment","process","persistence"]),Ge=Object.freeze({fetch:"network",XMLHttpRequest:"network",Date:"clock","Date.now":"clock","Math.random":"randomness","process.env":"environment",process:"process"}),pt=Object.freeze(Object.keys(Ge).sort()),H=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"}),Be=Object.freeze({process:Object.freeze(["process","node:process"])});function ie(e){if(!e||e.startsWith(".")||e.startsWith("/"))return null;let t=H[e];if(t)return t;let n=e.indexOf("/");if(n<0)return null;let s=e.slice(0,n),r=H[s];if(r)return r;let i=e.indexOf("/",n+1);return i<0?null:H[e.slice(0,i)]??null}function K(e,t){for(let n of t)if(Be[n]?.includes(e))return n;return null}function ae(e){if(e?.pure===!0)return[...oe].sort();let n=(e?.capabilities?.deny??[]).filter(s=>oe.includes(s));return[...new Set(n)].sort()}var G="https://unpkg.com/arkgate@2/schemas/ark.config.schema.json",le=["DomainModel","ApplicationOrchestration","PersistenceAdapters","IntegrationAdapters","WorkflowSagaEngine","BackgroundJobsScheduling","PresentationAdapters","ReportingReadModels","ExtensibilityMetadata","SecurityAuditObservability","Kernel"],Ue=new Set(["PresentationAdapters->ApplicationOrchestration","ApplicationOrchestration->DomainModel","WorkflowSagaEngine->ApplicationOrchestration","WorkflowSagaEngine->DomainModel","BackgroundJobsScheduling->ApplicationOrchestration"]);function qe(){let e=[];for(let t of le)for(let n of le)t===n||Ue.has(`${t}->${n}`)||e.push({from:t,to:n,allowed:!1});return e}var pe=qe(),B=[{from:"unversioned",to:"1.0"},{from:"1.0",to:"1.1"}],I={type:"array",items:{type:"string",minLength:1},uniqueItems:!0},ce={$schema:"https://json-schema.org/draft/2020-12/schema",$id:G,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:G,description:"Editor-facing URL or local path for this JSON Schema."},schemaVersion:{type:"string",const:"1.1",default:"1.1"},name:{type:"string",minLength:1},include:{...I,minItems:1,default:["src"]},exclude:{...I,default:[]},excludeGenerated:{type:"boolean",default:!0},frameworkOverlay:{type:"string",minLength:1},layers:{type:"array",default:[],items:{$ref:"#/$defs/layer"}},rules:{type:"array",default:pe,items:{$ref:"#/$defs/rule"}},cyclePolicy:{type:"string",enum:["strict","soft","framework-soft","off"],default:"strict"},dynamicImportAllowlist:{...I,default:[]},safety:{$ref:"#/$defs/safety",default:{maxTsSuppressions:0,maxAnyCasts:0,allowInMemory:!1,allowDisabledPeerIsolation:!1}},arkRules:{type:"object",additionalProperties:{type:"string",minLength:1},default:{}}},$defs:{layer:{type:"object",additionalProperties:!1,required:["name","patterns"],properties:{name:{type:"string",minLength:1},patterns:{...I,minItems:1},exclude:I,intentPrefixes:I,description:{type:"string",minLength:1},forbiddenGlobals:I,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:{...I,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}}}}},k=class extends Error{issues;source;constructor(t,n){super(`Invalid ArkGate config (${t}):
|
|
2
2
|
${n.map(s=>`- ${s.path}: ${s.message}`).join(`
|
|
3
|
-
`)}`),this.name="ArkConfigValidationError",this.source=t,this.issues=n}};function ae(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function O(e,t){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t)?`${e}.${t}`:`${e}[${JSON.stringify(t)}]`}function S(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Fe(e,t){let n="#/$defs/";if(e.startsWith(n))return t.$defs[e.slice(n.length)]}function E(e,t,n,s,r){if(t.$ref){let a=Fe(t.$ref,s);if(!a){r.push({path:n,message:`schema reference ${t.$ref} cannot be resolved`});return}E(e,a,n,s,r);return}if(t.const!==void 0&&!Object.is(e,t.const)){r.push({path:n,message:`must equal ${JSON.stringify(t.const)}`});return}if(t.enum&&!t.enum.some(a=>Object.is(a,e))){r.push({path:n,message:`must be one of ${t.enum.map(String).join(", ")}`});return}if(t.type==="object"){if(!ae(e)){r.push({path:n,message:`must be an object; received ${S(e)}`});return}let a=t.properties??{};for(let l of t.required??[])e[l]===void 0&&r.push({path:O(n,l),message:"is required"});if(t.additionalProperties===!1)for(let l of Object.keys(e))l in a||r.push({path:O(n,l),message:"unknown field"});else if(t.additionalProperties!==void 0&&t.additionalProperties!==!0&&typeof t.additionalProperties=="object"){let l=t.additionalProperties;for(let c of Object.keys(e))c in a||E(e[c],l,O(n,c),s,r)}for(let[l,c]of Object.entries(a))e[l]!==void 0&&E(e[l],c,O(n,l),s,r);return}if(t.type==="array"){if(!Array.isArray(e)){r.push({path:n,message:`must be an array; received ${S(e)}`});return}if(t.minItems!==void 0&&e.length<t.minItems&&r.push({path:n,message:`must contain at least ${t.minItems} item(s)`}),t.uniqueItems){let a=e.map(l=>JSON.stringify(l));new Set(a).size!==a.length&&r.push({path:n,message:"must not contain duplicate items"})}t.items&&e.forEach((a,l)=>E(a,t.items,`${n}[${l}]`,s,r));return}if(t.type==="string"){if(typeof e!="string"){r.push({path:n,message:`must be a string; received ${S(e)}`});return}t.minLength!==void 0&&e.length<t.minLength&&r.push({path:n,message:`must contain at least ${t.minLength} character(s)`});return}if(t.type==="boolean"){typeof e!="boolean"&&r.push({path:n,message:`must be a boolean; received ${S(e)}`});return}if(t.type==="integer"){if(!Number.isInteger(e)){r.push({path:n,message:`must be an integer; received ${S(e)}`});return}t.minimum!==void 0&&e<t.minimum&&r.push({path:n,message:`must be at least ${t.minimum}`})}}function Ke(e){return{...e,$schema:e.$schema===void 0?H:e.$schema,schemaVersion:e.schemaVersion===void 0?"1.1":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 He(){let e=new Set(["1.1"]);for(let t of G)t.from!=="unversioned"&&e.add(t.from),e.add(t.to);return e}function Ge(e,t="ark.config.json"){if(!ae(e))throw new h(t,[{path:"$",message:`must be an object; received ${S(e)}`}]);let n=He(),s=e.schemaVersion===void 0?"unversioned":typeof e.schemaVersion=="string"?e.schemaVersion:null;if(s===null)throw new h(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(e.schemaVersion)}; expected 1.1`}]);if(s!=="unversioned"&&!n.has(s))throw new h(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(s)}; expected 1.1`}]);let r=s,a={...e},l=0;for(;r!=="1.1"&&l<G.length+1;){l+=1;let g=G.find(m=>m.from===r);if(!g)throw new h(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(r)}; expected 1.1`}]);r=g.to,a.schemaVersion=r}if(r!=="1.1")throw new h(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(s)}; expected 1.1`}]);let c=s==="unversioned"?"unversioned":s==="1.0"?"1.0":null;return{candidate:Ke(a),migratedFrom:c}}function Be(e,t="ark.config.json"){let{candidate:n,migratedFrom:s}=Ge(e,t),r=[];if(E(n,ie,"$",ie,r),r.length>0)throw new h(t,r);return{config:n,migratedFrom:s}}function le(e,t="ark.config.json"){let n;try{n=JSON.parse(e)}catch(s){throw new h(t,[{path:"$",message:`invalid JSON: ${s instanceof Error?s.message:String(s)}`}])}return Be(n,t)}function y(e){return typeof e=="string"&&e.length>0?e:void 0}function ce(e,t){return Number.isInteger(e)&&Number(e)>0?Number(e):t}function Ue(e,t,n){if(e==="LAYER_IMPORT_VIOLATION")return t.typeOnly||n.targetTypeOnlyExports===!0||n.namedBindingsTypeOnly===!0?"Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.":n.peerIsolation===!0?"Extract the shared dependency to a shared layer, then preflight again.":`Define a port in ${t.fromLayer??"the source layer"}, inject the ${t.toLayer??"outer-layer"} implementation, then preflight again.`;if(e==="FORBIDDEN_GLOBAL")return`Inject ${t.target??"the capability"} through a port, then preflight again.`;if(e==="CAPABILITY_VIOLATION")return`Define a ${y(n.capability)??"capability"} port in ${t.fromLayer??"the walled layer"}, bind the implementation outside it, then preflight again.`;if(e==="CIRCULAR_DEPENDENCY")return"Extract the shared dependency into a third module, then preflight again.";if(e==="RAW_EVENT_PUBLISH")return"Publish through a registered intent creator, then run Ark again.";if(e==="PUBLISH_MISSING_SOURCE")return"Add metadata.source to the publish call, then run Ark again.";if(e==="ARKRULE_STRUCTURE"||e==="ARKRULE_INVARIANT"||e==="INVARIANT_UNCOVERED"||e.startsWith("ARKRULE_")){let s=t.arkruleSource??"arkrules/<Layer>.json";return`Fix the structure or invariant for ${t.arkruleId??"the ArkRule"} (declared in ${s}), then preflight again. Do not demote the rule without a hash-bound policy acknowledgement.`}return`Resolve ${e} without weakening ark.config.json, then run Ark again.`}function de(e,t="error"){let n=y(e.ruleId)??y(e.code)??"ARK_UNKNOWN",s=e.severity==="warning"?"warning":t,r={...y(e.target)?{target:y(e.target)}:{},...y(e.fromLayer)?{fromLayer:y(e.fromLayer)}:{},...y(e.toLayer)?{toLayer:y(e.toLayer)}:{},...typeof e.typeOnly=="boolean"?{typeOnly:e.typeOnly}:{},...typeof e.targetTypeOnlyExports=="boolean"?{targetTypeOnlyExports:e.targetTypeOnlyExports}:{},...typeof e.sourcePureTypeModule=="boolean"?{sourcePureTypeModule:e.sourcePureTypeModule}:{},...typeof e.namedBindingsTypeOnly=="boolean"?{namedBindingsTypeOnly:e.namedBindingsTypeOnly}:{},...typeof e.portProofEligible=="boolean"?{portProofEligible:e.portProofEligible}:{},...typeof e.peerIsolation=="boolean"?{peerIsolation:e.peerIsolation}:{},...y(e.capability)?{capability:y(e.capability)}:{},...y(e.edgeKind)?{edgeKind:y(e.edgeKind)}:{},...y(e.arkruleId)?{arkruleId:y(e.arkruleId)}:{},...y(e.arkruleSource)?{arkruleSource:y(e.arkruleSource)}:{}};return{ruleId:n,severity:s,message:y(e.message)??n,location:{file:y(e.file)??"<unknown>",line:ce(e.line,1),column:ce(e.column,1)},evidence:r,nextAction:y(e.nextAction)??Ue(n,r,e)}}var pe={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."},at=Object.freeze([{layer:"DomainModel",prefixes:["Domain."]},{layer:"ApplicationOrchestration",prefixes:["Application."]},{layer:"PersistenceAdapters",prefixes:["Adapter.Persistence.","Adapter.Repository."]},{layer:"IntegrationAdapters",prefixes:["Adapter.Integration.","Adapter.External."]},{layer:"WorkflowSagaEngine",prefixes:["Workflow."]},{layer:"BackgroundJobsScheduling",prefixes:["Job."]},{layer:"PresentationAdapters",prefixes:["Presentation.","Adapter.Presentation.","Adapter.Api."]},{layer:"ReportingReadModels",prefixes:["Reporting."]},{layer:"ExtensibilityMetadata",prefixes:["Metadata."]},{layer:"SecurityAuditObservability",prefixes:["Security.","Audit.","Observability."]},{layer:"Kernel",prefixes:["Kernel."]}]);function qe(e){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function B(e){if(!e.publishCall)return[];let t=[];return(e.rawIntentName!==void 0&&qe(e.rawIntentName)||e.objectHasIntent)&&t.push({ruleId:"RAW_EVENT_PUBLISH",message:pe.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&t.push({ruleId:"PUBLISH_MISSING_SOURCE",message:pe.PUBLISH_MISSING_SOURCE}),t}function C(e){if(typeof e.physicalFilename=="string"&&e.physicalFilename.length>0)return e.physicalFilename;if(typeof e.filename=="string"&&e.filename.length>0)return e.filename;if(typeof e.getFilename=="function")try{let t=e.getFilename();if(typeof t=="string"&&t.length>0)return t}catch{}return""}function R(e,t,n,s,r){let a=de({...s,line:s.line??t.loc?.start?.line,column:s.column??(typeof t.loc?.start?.column=="number"?t.loc.start.column+1:void 0)});return e.report({node:t,messageId:n,...r?{data:r}:{},diagnostic:a}),a}function $(e){if(!e||e==="<input>"||e.startsWith("stdin"))return null;let t=p.default.dirname(p.default.resolve(e));for(;;){let n=p.default.join(t,"ark.config.json");if(I.default.existsSync(n))return n;let s=p.default.dirname(t);if(s===t)return null;t=s}}var ue=new Map;function T(e){if(!I.default.existsSync(e))return null;let t=I.default.readFileSync(e,"utf8"),n=ue.get(e);if(n?.source===t)return n.config;let s=le(t,e).config;return ue.set(e,{source:t,config:s}),s}function q(e,t){return(e.include??[]).some(s=>{let r=String(s).replace(/\\/g,"/").replace(/^\.\//,"").replace(/\/$/,"");return r==="."||t===r||t.startsWith(`${r}/`)})&&!ee(t,e)}function fe(e,t){if(!t.startsWith("."))return null;let n=p.default.resolve(p.default.dirname(e),t),s=[n,`${n}.ts`,`${n}.tsx`,`${n}.mts`,`${n}.cts`,`${n}.js`,`${n}.jsx`,p.default.join(n,"index.ts"),p.default.join(n,"index.tsx"),p.default.join(n,"index.js")];for(let r of s)try{if(I.default.existsSync(r)&&I.default.statSync(r).isFile())return r}catch{}return null}function j(e){return typeof e?.value=="string"?e.value:void 0}function W(e){return e?.name??j(e)}function Y(e){return e.sourceCode??e.getSourceCode?.()}function ge(e,t){let n=Y(e)?.getScope?.(t);for(;n;){let s=n.references?.find(r=>r.identifier===t);if(s)return s;n=n.upper??void 0}}function _(e,t,n){let s=ge(e,t);if(s?.resolved)return(s.resolved.defs?.length??0)>0;let r=Y(e)?.getScope?.(t);for(;r;){let a=r.set?.get(n);if(a)return(a.defs?.length??0)>0;r=r.upper??void 0}return!1}function We(e,t){let n=ge(e,t);return n?n.isValueReference!==!1:t.parent?.type==="VariableDeclarator"&&t.parent.init===t}function me(e){if(e?.type==="Identifier"&&e.name)return{root:e,segments:[e.name]};if(!e||!(e.type==="MemberExpression"||!!(e.object&&e.property))||e.computed===!0)return;let n=me(e.object),s=W(e.property);if(!(!n||!s))return{root:n.root,segments:[...n.segments,s]}}function Ye(e){return W(e.callee?.property)}function ye(e,t){return e?.properties?.find(n=>W(n.key)===t)}function v(e,t){return ye(e,t)!==void 0}function ze(e){let t=ye(e,"metadata")?.value;return v(t,"source")}function be(e){return Ye(e)==="publish"}function U(e){if(e.importKind==="type"||e.exportKind==="type")return!0;let t=e.specifiers??[];return t.length===0?!1:t.every(n=>n.type==="ImportSpecifier")?t.every(n=>n.importKind==="type"):t.every(n=>n.exportKind==="type")}function Je(e){let t=e;for(;t?.parent;)t=t.parent;return t?.type==="Program"?t:void 0}function Ze(e){let t=Je(e)?.body;if(!t)return!1;let n=!1;for(let s of t){if(s.type==="ImportDeclaration"){if(!U(s))return!1;continue}if(!(s.type==="TSInterfaceDeclaration"||s.type==="TSTypeAliasDeclaration")){if(s.type==="ExportNamedDeclaration"){if(s.declaration){if(s.declaration.type!=="TSInterfaceDeclaration"&&s.declaration.type!=="TSTypeAliasDeclaration")return!1}else if(!U(s))return!1;n=!0;continue}return!1}}return n}var he={meta:{type:"problem",docs:{description:"Disallow imports that violate ark.config.json layer rules (same contract as arkgate-check)."},messages:{forbiddenImport:"Architecture: {{fromLayer}} must not import {{toLayer}} (ark.config.json). Specifier: {{specifier}}",forbiddenImportHeuristic:"Domain code must not import infrastructure, adapters, repositories, or database modules."},schema:[]},create(e){let t=C(e),n=$(t),s=n?T(n):null,r=n?p.default.dirname(n):null,a=l=>{let c=j(l.source);if(c&&s&&r&&t){let g=p.default.isAbsolute(t)?t:p.default.resolve(t),m=p.default.relative(r,g).split(p.default.sep).join("/");if(!q(s,m))return;let d=k(m,s.layers);if(!d)return;let i=fe(g,c);if(!i)return;let o=p.default.relative(r,i).split(p.default.sep).join("/");if(o.startsWith(".."))return;let u=k(o,s.layers);if(!u)return;if(V(s.rules,d,u,{fromPath:m,toPath:o,layers:s.layers})){let f=l.type?.startsWith("Export")?"export":"import";R(e,l,"forbiddenImport",{ruleId:"LAYER_IMPORT_VIOLATION",file:m,fromLayer:d,toLayer:u,target:o,edgeKind:f,...U(l)?{typeOnly:!0}:{},...Ze(l)?{sourcePureTypeModule:!0}:{},message:`${d} must not ${f} ${u}.`},{fromLayer:d,toLayer:u,specifier:c})}return}};return{ImportDeclaration:a,ExportNamedDeclaration:a,ExportAllDeclaration:a}}},Ae={meta:{type:"problem",docs:{description:"Require event bus publish calls to use registered intent creators instead of raw event objects or intent strings."},messages:{rawPublish:"Publish through a registered intent creator; raw event objects or intent strings bypass Ark contracts."},schema:[]},create(e){return{CallExpression(t){let n=t.arguments?.[0],s=j(n),r=B({publishCall:be(t),rawIntentName:s,objectHasIntent:v(n,"intent"),arkPublishCandidate:!1,hasSource:!0});if(r.some(a=>a.ruleId==="RAW_EVENT_PUBLISH")){let a=r.find(l=>l.ruleId==="RAW_EVENT_PUBLISH");R(e,t,"rawPublish",{...a,file:C(e)})}}}}},ke={meta:{type:"problem",docs:{description:"Require event bus publish calls to include source metadata."},messages:{missingSource:"Strict Ark publish calls must include metadata.source."},schema:[]},create(e){return{CallExpression(t){let n=t.arguments?.[0],s=t.arguments?.[2],a=B({publishCall:be(t),rawIntentName:j(n),objectHasIntent:v(n,"intent"),arkPublishCandidate:!0,hasSource:ze(n)||v(s,"source")}).find(l=>l.ruleId==="PUBLISH_MISSING_SOURCE");a&&R(e,t,"missingSource",{...a,file:C(e)})}}}},Se={meta:{type:"problem",docs:{description:"Disallow ambient globals from the layer\u2019s forbiddenGlobals in ark.config.json (same purity surface as ark-check). Option `globals` is a standalone fallback when no project config applies."},messages:{forbiddenGlobal:'Ambient global "{{name}}" is forbidden in {{layer}} (ark.config.json); inject the capability through a port instead.',forbiddenGlobalDefault:'Ambient global "{{name}}" is forbidden here; inject the capability through a port instead.',forbiddenModule:'{{layer}} must not use module "{{specifier}}" because it is the import form of forbidden global "{{name}}".'},schema:[{type:"object",properties:{globals:{type:"array",items:{type:"string"}}},additionalProperties:!1}]},create(e){let t=C(e),n=e.options?.[0],s=$(t),r=s?T(s):null,a=s?p.default.dirname(s):null,l=null,c="this layer";if(r&&a&&t){let i=p.default.isAbsolute(t)?t:p.default.resolve(t),o=p.default.relative(a,i).split(p.default.sep).join("/");if(!q(r,o))return{};let u=r.layers?.find(f=>f.name===k(o,r.layers));u?.forbiddenGlobals?.length?(l=new Set(u.forbiddenGlobals),c=u.name):l=null}else n?.globals&&(l=new Set(n.globals));if(!l)return{};let g=typeof Y(e)?.getScope=="function",m=(i,o)=>{let u=p.default.isAbsolute(t)?t:p.default.resolve(t),f=a?p.default.relative(a,u).split(p.default.sep).join("/"):t;R(e,i,r?"forbiddenGlobal":"forbiddenGlobalDefault",{ruleId:"FORBIDDEN_GLOBAL",file:f,fromLayer:c,target:o,message:`${c} must not use the ambient global "${o}".`},{name:o,layer:c})},d=(i,o,u,f)=>{if(u||typeof o!="string")return;let b=K(o,l);if(!b)return;let x=p.default.isAbsolute(t)?t:p.default.resolve(t),N=a?p.default.relative(a,x).split(p.default.sep).join("/"):t;R(e,i,"forbiddenModule",{ruleId:"FORBIDDEN_GLOBAL",file:N,fromLayer:c,target:o,edgeKind:f,message:`${c} must not use module "${o}" because it is the import form of forbidden global "${b}".`},{layer:c,name:b,specifier:o,importKind:f})};return{MemberExpression(i){if(i.parent?.type==="MemberExpression"&&i.parent.object===i)return;let o=me(i);if(!o||_(e,o.root,o.segments[0]))return;let u=o.segments[0]==="globalThis",f=u?o.segments.slice(1):o.segments,b;for(let x=f.length;x>=(u?1:2);x-=1){let N=f.slice(0,x).join(".");if(l.has(N)){b=N;break}}b?m(i,b):!g&&l.has(o.segments[0])&&m(i,o.segments[0])},CallExpression(i){let o=i;if(o.callee?.type==="Identifier"&&o.callee.name==="require"&&o.arguments?.[0]?.type==="Literal"&&!_(e,i,"require")&&d(i,o.arguments[0].value,!1,"require"),g)return;let u=o.callee?.type==="Identifier"?o.callee.name:void 0;u&&l.has(u)&&m(i,u)},ImportDeclaration(i){let o=i,u=(o.specifiers??[]).filter(b=>b.type==="ImportSpecifier"),f=u.length>0&&u.length===(o.specifiers??[]).length&&u.every(b=>b.importKind==="type");d(i,o.source?.value,o.importKind==="type"||f,"import")},ImportExpression(i){let o=i;o.source?.type==="Literal"&&d(i,o.source.value,!1,"dynamic-import")},TSImportEqualsDeclaration(i){let o=i;d(i,o.moduleReference?.expression?.value,o.importKind==="type"||o.isTypeOnly===!0,"require")},ExportNamedDeclaration(i){let o=i;if(!o.source)return;let u=o.specifiers??[],f=u.length>0&&u.every(b=>b.exportKind==="type");d(i,o.source.value,o.exportKind==="type"||f,"export")},ExportAllDeclaration(i){let o=i;d(i,o.source?.value,o.exportKind==="type","export")},NewExpression(i){if(g)return;let o=i.callee?.type==="Identifier"?i.callee.name:void 0;o&&l.has(o)&&m(i,o)},Identifier(i){!g||!i.name||!l.has(i.name)||!We(e,i)||_(e,i,i.name)||m(i,i.name)}}}},Ie={meta:{type:"problem",docs:{description:"Disallow importing modules whose effect capability the layer denies (ark.config.json capabilities.deny / pure \u2014 same wall surface as ark-check). Import dimension only: ambient globals stay with no-forbidden-globals and the CLI/hook symbol path."},messages:{deniedCapability:'{{layer}} denies the {{capability}} capability (ark.config.json); "{{specifier}}" imports it. Define a port and bind the implementation in an adapter layer.'},schema:[]},create(e){let t=C(e),n=$(t),s=n?T(n):null,r=n?p.default.dirname(n):null;if(!s||!r||!t)return{};let a=p.default.isAbsolute(t)?t:p.default.resolve(t),l=p.default.relative(r,a).split(p.default.sep).join("/");if(!q(s,l))return{};let c=s.layers?.find(d=>d.name===k(l,s.layers));if(!c)return{};let g=new Set(re(c));if(g.size===0)return{};let m=(d,i,o,u)=>{if(o||typeof i!="string"||K(i,c.forbiddenGlobals??[]))return;let f=ne(i);!f||!g.has(f)||R(e,d,"deniedCapability",{ruleId:"CAPABILITY_VIOLATION",file:l,fromLayer:c.name,target:i,capability:f,edgeKind:u,message:`${c.name} denies the ${f} capability; found import of "${i}".`},{layer:c.name,capability:f,specifier:i})};return{ImportDeclaration(d){let i=d,o=(i.specifiers??[]).filter(f=>f.type==="ImportSpecifier"),u=o.length>0&&o.length===(i.specifiers??[]).length&&o.every(f=>f.importKind==="type");m(d,i.source?.value,i.importKind==="type"||u,"import")},ImportExpression(d){let i=d;i.source?.type==="Literal"&&m(d,i.source.value,!1,"dynamic-import")},TSImportEqualsDeclaration(d){let i=d;m(d,i.moduleReference?.expression?.value,i.importKind==="type"||i.isTypeOnly===!0,"require")},ExportNamedDeclaration(d){let i=d;if(!i.source)return;let o=i.specifiers??[],u=o.length>0&&o.every(f=>f.exportKind==="type");m(d,i.source.value,i.exportKind==="type"||u,"export")},ExportAllDeclaration(d){let i=d;m(d,i.source?.value,i.exportKind==="type","export")},CallExpression(d){let i=d;i.callee?.type==="Identifier"&&i.callee.name==="require"&&i.arguments?.[0]?.type==="Literal"&&!_(e,d,"require")&&m(d,i.arguments[0].value,!1,"require")}}}},Xe={"no-domain-infra-imports":he,"no-raw-event-publish":Ae,"require-publish-source":ke,"no-forbidden-globals":Se,"no-denied-capabilities":Ie},P={rules:Xe};P.configs={recommended:{plugins:{ark:P},rules:{"ark/no-domain-infra-imports":"error","ark/no-raw-event-publish":"error","ark/require-publish-source":"error","ark/no-forbidden-globals":"error","ark/no-denied-capabilities":"error"}}};var Qe=P;0&&(module.exports={findConfigPath,globToRegExp,isEdgeDenied,layerForRelativePath,loadArkConfig,noDeniedCapabilities,noDomainInfraImports,noForbiddenGlobals,noRawEventPublish,patternSpecificity,plugin,requirePublishSource,resolveRelativeImport});
|
|
3
|
+
`)}`),this.name="ArkConfigValidationError",this.source=t,this.issues=n}};function de(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function _(e,t){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t)?`${e}.${t}`:`${e}[${JSON.stringify(t)}]`}function x(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function We(e,t){let n="#/$defs/";if(e.startsWith(n))return t.$defs[e.slice(n.length)]}function C(e,t,n,s,r){if(t.$ref){let i=We(t.$ref,s);if(!i){r.push({path:n,message:`schema reference ${t.$ref} cannot be resolved`});return}C(e,i,n,s,r);return}if(t.const!==void 0&&!Object.is(e,t.const)){r.push({path:n,message:`must equal ${JSON.stringify(t.const)}`});return}if(t.enum&&!t.enum.some(i=>Object.is(i,e))){r.push({path:n,message:`must be one of ${t.enum.map(String).join(", ")}`});return}if(t.type==="object"){if(!de(e)){r.push({path:n,message:`must be an object; received ${x(e)}`});return}let i=t.properties??{};for(let l of t.required??[])e[l]===void 0&&r.push({path:_(n,l),message:"is required"});if(t.additionalProperties===!1)for(let l of Object.keys(e))l in i||r.push({path:_(n,l),message:"unknown field"});else if(t.additionalProperties!==void 0&&t.additionalProperties!==!0&&typeof t.additionalProperties=="object"){let l=t.additionalProperties;for(let c of Object.keys(e))c in i||C(e[c],l,_(n,c),s,r)}for(let[l,c]of Object.entries(i))e[l]!==void 0&&C(e[l],c,_(n,l),s,r);return}if(t.type==="array"){if(!Array.isArray(e)){r.push({path:n,message:`must be an array; received ${x(e)}`});return}if(t.minItems!==void 0&&e.length<t.minItems&&r.push({path:n,message:`must contain at least ${t.minItems} item(s)`}),t.uniqueItems){let i=e.map(l=>JSON.stringify(l));new Set(i).size!==i.length&&r.push({path:n,message:"must not contain duplicate items"})}t.items&&e.forEach((i,l)=>C(i,t.items,`${n}[${l}]`,s,r));return}if(t.type==="string"){if(typeof e!="string"){r.push({path:n,message:`must be a string; received ${x(e)}`});return}t.minLength!==void 0&&e.length<t.minLength&&r.push({path:n,message:`must contain at least ${t.minLength} character(s)`});return}if(t.type==="boolean"){typeof e!="boolean"&&r.push({path:n,message:`must be a boolean; received ${x(e)}`});return}if(t.type==="integer"){if(!Number.isInteger(e)){r.push({path:n,message:`must be an integer; received ${x(e)}`});return}t.minimum!==void 0&&e<t.minimum&&r.push({path:n,message:`must be at least ${t.minimum}`})}}function Ye(e){return{...e,$schema:e.$schema===void 0?G:e.$schema,schemaVersion:e.schemaVersion===void 0?"1.1":e.schemaVersion,include:e.include===void 0?["src"]:e.include,layers:e.layers===void 0?[]:e.layers,rules:e.rules===void 0?pe.map(t=>({...t})):e.rules}}function Je(){let e=new Set(["1.1"]);for(let t of B)t.from!=="unversioned"&&e.add(t.from),e.add(t.to);return e}function ze(e,t="ark.config.json"){if(!de(e))throw new k(t,[{path:"$",message:`must be an object; received ${x(e)}`}]);let n=Je(),s=e.schemaVersion===void 0?"unversioned":typeof e.schemaVersion=="string"?e.schemaVersion:null;if(s===null)throw new k(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(e.schemaVersion)}; expected 1.1`}]);if(s!=="unversioned"&&!n.has(s))throw new k(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(s)}; expected 1.1`}]);let r=s,i={...e},l=0;for(;r!=="1.1"&&l<B.length+1;){l+=1;let g=B.find(f=>f.from===r);if(!g)throw new k(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(r)}; expected 1.1`}]);r=g.to,i.schemaVersion=r}if(r!=="1.1")throw new k(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(s)}; expected 1.1`}]);let c=s==="unversioned"?"unversioned":s==="1.0"?"1.0":null;return{candidate:Ye(i),migratedFrom:c}}function Ze(e,t="ark.config.json"){let{candidate:n,migratedFrom:s}=ze(e,t),r=[];if(C(n,ce,"$",ce,r),r.length>0)throw new k(t,r);return{config:n,migratedFrom:s}}function ue(e,t="ark.config.json"){let n;try{n=JSON.parse(e)}catch(s){throw new k(t,[{path:"$",message:`invalid JSON: ${s instanceof Error?s.message:String(s)}`}])}return Ze(n,t)}function b(e){return typeof e=="string"&&e.length>0?e:void 0}function fe(e,t){return Number.isInteger(e)&&Number(e)>0?Number(e):t}function Xe(e,t,n){if(e==="LAYER_IMPORT_VIOLATION")return t.typeOnly||n.targetTypeOnlyExports===!0||n.namedBindingsTypeOnly===!0?"Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.":n.peerIsolation===!0?"Extract the shared dependency to a shared layer, then preflight again.":`Define a port in ${t.fromLayer??"the source layer"}, inject the ${t.toLayer??"outer-layer"} implementation, then preflight again.`;if(e==="FORBIDDEN_GLOBAL")return`Inject ${t.target??"the capability"} through a port, then preflight again.`;if(e==="CAPABILITY_VIOLATION")return`Define a ${b(n.capability)??"capability"} port in ${t.fromLayer??"the walled layer"}, bind the implementation outside it, then preflight again.`;if(e==="CIRCULAR_DEPENDENCY")return"Extract the shared dependency into a third module, then preflight again.";if(e==="RAW_EVENT_PUBLISH")return"Publish through a registered intent creator, then run Ark again.";if(e==="PUBLISH_MISSING_SOURCE")return"Add metadata.source to the publish call, then run Ark again.";if(e==="ARKRULE_STRUCTURE"||e==="ARKRULE_INVARIANT"||e==="INVARIANT_UNCOVERED"||e.startsWith("ARKRULE_")){let s=t.arkruleSource??"arkrules/<Layer>.json";return`Fix the structure or invariant for ${t.arkruleId??"the ArkRule"} (declared in ${s}), then preflight again. Do not demote the rule without a hash-bound policy acknowledgement.`}return`Resolve ${e} without weakening ark.config.json, then run Ark again.`}function ge(e,t="error"){let n=b(e.ruleId)??b(e.code)??"ARK_UNKNOWN",s=e.severity==="warning"||e.failsStrict===!1||e.typeOnly===!0&&e.peerIsolation!==!0?"warning":t,r={...b(e.target)?{target:b(e.target)}:{},...b(e.fromLayer)?{fromLayer:b(e.fromLayer)}:{},...b(e.toLayer)?{toLayer:b(e.toLayer)}:{},...typeof e.typeOnly=="boolean"?{typeOnly:e.typeOnly}:{},...typeof e.targetTypeOnlyExports=="boolean"?{targetTypeOnlyExports:e.targetTypeOnlyExports}:{},...typeof e.sourcePureTypeModule=="boolean"?{sourcePureTypeModule:e.sourcePureTypeModule}:{},...typeof e.namedBindingsTypeOnly=="boolean"?{namedBindingsTypeOnly:e.namedBindingsTypeOnly}:{},...typeof e.portProofEligible=="boolean"?{portProofEligible:e.portProofEligible}:{},...typeof e.peerIsolation=="boolean"?{peerIsolation:e.peerIsolation}:{},...b(e.capability)?{capability:b(e.capability)}:{},...b(e.edgeKind)?{edgeKind:b(e.edgeKind)}:{},...b(e.arkruleId)?{arkruleId:b(e.arkruleId)}:{},...b(e.arkruleSource)?{arkruleSource:b(e.arkruleSource)}:{}};return{ruleId:n,severity:s,message:b(e.message)??n,location:{file:b(e.file)??"<unknown>",line:fe(e.line,1),column:fe(e.column,1)},evidence:r,nextAction:b(e.nextAction)??Xe(n,r,e)}}var me={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."},gt=Object.freeze([{layer:"DomainModel",prefixes:["Domain."]},{layer:"ApplicationOrchestration",prefixes:["Application."]},{layer:"PersistenceAdapters",prefixes:["Adapter.Persistence.","Adapter.Repository."]},{layer:"IntegrationAdapters",prefixes:["Adapter.Integration.","Adapter.External."]},{layer:"WorkflowSagaEngine",prefixes:["Workflow."]},{layer:"BackgroundJobsScheduling",prefixes:["Job."]},{layer:"PresentationAdapters",prefixes:["Presentation.","Adapter.Presentation.","Adapter.Api."]},{layer:"ReportingReadModels",prefixes:["Reporting."]},{layer:"ExtensibilityMetadata",prefixes:["Metadata."]},{layer:"SecurityAuditObservability",prefixes:["Security.","Audit.","Observability."]},{layer:"Kernel",prefixes:["Kernel."]}]);function Qe(e){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function U(e){if(!e.publishCall)return[];let t=[];return(e.rawIntentName!==void 0&&Qe(e.rawIntentName)||e.objectHasIntent)&&t.push({ruleId:"RAW_EVENT_PUBLISH",message:me.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&t.push({ruleId:"PUBLISH_MISSING_SOURCE",message:me.PUBLISH_MISSING_SOURCE}),t}function N(e){if(typeof e.physicalFilename=="string"&&e.physicalFilename.length>0)return e.physicalFilename;if(typeof e.filename=="string"&&e.filename.length>0)return e.filename;if(typeof e.getFilename=="function")try{let t=e.getFilename();if(typeof t=="string"&&t.length>0)return t}catch{}return""}function w(e,t,n,s,r){let i=ge({...s,line:s.line??t.loc?.start?.line,column:s.column??(typeof t.loc?.start?.column=="number"?t.loc.start.column+1:void 0)});return e.report({node:t,messageId:n,...r?{data:r}:{},diagnostic:i}),i}function T(e){if(!e||e==="<input>"||e.startsWith("stdin"))return null;let t=d.default.dirname(d.default.resolve(e));for(;;){let n=d.default.join(t,"ark.config.json");if(S.default.existsSync(n))return n;let s=d.default.dirname(t);if(s===t)return null;t=s}}var ye=new Map;function j(e){if(!S.default.existsSync(e))return null;let t=S.default.readFileSync(e,"utf8"),n=ye.get(e);if(n?.source===t)return n.config;let s=ue(t,e).config;return ye.set(e,{source:t,config:s}),s}function W(e,t){return(e.include??[]).some(s=>{let r=String(s).replace(/\\/g,"/").replace(/^\.\//,"").replace(/\/$/,"");return r==="."||t===r||t.startsWith(`${r}/`)})&&!se(t,e)}function be(e){let t=[e,`${e}.ts`,`${e}.tsx`,`${e}.mts`,`${e}.cts`,`${e}.js`,`${e}.jsx`,d.default.join(e,"index.ts"),d.default.join(e,"index.tsx"),d.default.join(e,"index.js")];for(let n of t)try{if(S.default.existsSync(n)&&S.default.statSync(n).isFile())return n}catch{}return null}function he(e){let t=d.default.resolve(e),n=null;for(;;){let f=d.default.join(t,"tsconfig.json");if(S.default.existsSync(f)){n=f;break}let p=d.default.dirname(t);if(p===t)break;t=p}if(!n)return{baseUrl:e,aliases:[]};let s=f=>{try{let p=S.default.readFileSync(f,"utf8");return p=p.replace(/\/\*[\s\S]*?\*\//g,"").replace(/(^|[^:])\/\/.*$/gm,"$1"),JSON.parse(p)}catch{return null}},r=(f,p)=>{if(p>4)return{};let o=s(f);if(!o)return{};let a=o.compilerOptions??{},u=a.baseUrl,m=a.paths,y=o.extends;if(typeof y=="string"&&!y.startsWith("@")){let h=d.default.resolve(d.default.dirname(f),y.endsWith(".json")?y:`${y}.json`);if(S.default.existsSync(h)){let A=r(h,p+1);u=u??A.baseUrl,m={...A.paths??{},...m??{}}}}return{baseUrl:u,paths:m}},i=r(n,0),l=d.default.dirname(n),c=d.default.resolve(l,i.baseUrl||"."),g=[];for(let[f,p]of Object.entries(i.paths||{})){if(!Array.isArray(p)||p.length===0)continue;let o=f.replace(/\*$/,"");o&&g.push({from:o,to:String(p[0]).replace(/\*$/,"")})}return g.sort((f,p)=>p.from.length-f.from.length),{baseUrl:c,aliases:g}}function Ae(e,t){if(!t.startsWith("."))return null;let n=d.default.resolve(d.default.dirname(e),t);return be(n)}function ke(e,t,n){if(!t)return null;if(t.startsWith("."))return Ae(e,t);let s=n||d.default.dirname(e),{baseUrl:r,aliases:i}=he(s),l=i.find(g=>t.startsWith(g.from));if(!l)return null;let c=d.default.resolve(r,`${l.to}${t.slice(l.from.length)}`);return be(c)}function M(e){return typeof e?.value=="string"?e.value:void 0}function Y(e){return e?.name??M(e)}function J(e){return e.sourceCode??e.getSourceCode?.()}function Se(e,t){let n=J(e)?.getScope?.(t);for(;n;){let s=n.references?.find(r=>r.identifier===t);if(s)return s;n=n.upper??void 0}}function v(e,t,n){let s=Se(e,t);if(s?.resolved)return(s.resolved.defs?.length??0)>0;let r=J(e)?.getScope?.(t);for(;r;){let i=r.set?.get(n);if(i)return(i.defs?.length??0)>0;r=r.upper??void 0}return!1}function et(e,t){let n=Se(e,t);return n?n.isValueReference!==!1:t.parent?.type==="VariableDeclarator"&&t.parent.init===t}function Ie(e){if(e?.type==="Identifier"&&e.name)return{root:e,segments:[e.name]};if(!e||!(e.type==="MemberExpression"||!!(e.object&&e.property))||e.computed===!0)return;let n=Ie(e.object),s=Y(e.property);if(!(!n||!s))return{root:n.root,segments:[...n.segments,s]}}function tt(e){return Y(e.callee?.property)}function Re(e,t){return e?.properties?.find(n=>Y(n.key)===t)}function P(e,t){return Re(e,t)!==void 0}function nt(e){let t=Re(e,"metadata")?.value;return P(t,"source")}function xe(e){return tt(e)==="publish"}function q(e){if(e.importKind==="type"||e.exportKind==="type")return!0;let t=e.specifiers??[];return t.length===0?!1:t.every(n=>n.type==="ImportSpecifier")?t.every(n=>n.importKind==="type"):t.every(n=>n.exportKind==="type")}function rt(e){let t=e;for(;t?.parent;)t=t.parent;return t?.type==="Program"?t:void 0}function st(e){let t=rt(e)?.body;if(!t)return!1;let n=!1;for(let s of t){if(s.type==="ImportDeclaration"){if(!q(s))return!1;continue}if(!(s.type==="TSInterfaceDeclaration"||s.type==="TSTypeAliasDeclaration")){if(s.type==="ExportNamedDeclaration"){if(s.declaration){if(s.declaration.type!=="TSInterfaceDeclaration"&&s.declaration.type!=="TSTypeAliasDeclaration")return!1}else if(!q(s))return!1;n=!0;continue}return!1}}return n}var we={meta:{type:"problem",docs:{description:"Disallow imports that violate ark.config.json layer rules (same contract as arkgate-check)."},messages:{forbiddenImport:"Architecture: {{fromLayer}} must not import {{toLayer}} (ark.config.json). Specifier: {{specifier}}",forbiddenImportHeuristic:"Domain code must not import infrastructure, adapters, repositories, or database modules."},schema:[]},create(e){let t=N(e),n=T(t),s=n?j(n):null,r=n?d.default.dirname(n):null,i=l=>{let c=M(l.source);if(c&&s&&r&&t){let g=d.default.isAbsolute(t)?t:d.default.resolve(t),f=d.default.relative(r,g).split(d.default.sep).join("/");if(!W(s,f))return;let p=R(f,s.layers);if(!p)return;let o=ke(g,c,r);if(!o)return;let a=d.default.relative(r,o).split(d.default.sep).join("/");if(a.startsWith(".."))return;let u=R(a,s.layers);if(!u)return;let m={fromPath:f,toPath:a,layers:s.layers},y=V(s.rules,p,u,m);if(y||F(s.rules,p,u,m)){let h=l.type?.startsWith("Export")?"export":"import",A=q(l),z=!!y?.peerIsolation,Z=A&&!z,X=y?.message??`${p} must not ${h} ${u}.`;w(e,l,"forbiddenImport",{ruleId:"LAYER_IMPORT_VIOLATION",file:f,fromLayer:p,toLayer:u,target:a,edgeKind:h,...z?{peerIsolation:!0}:{},...A?{typeOnly:!0}:{},...Z?{severity:"warning"}:{},...st(l)?{sourcePureTypeModule:!0}:{},message:Z?`${X} (type-only \u2014 type placement debt; prefer SharedTypes / owning layer; not runtime coupling)`:X},{fromLayer:p,toLayer:u,specifier:c})}return}};return{ImportDeclaration:i,ExportNamedDeclaration:i,ExportAllDeclaration:i}}},Ee={meta:{type:"problem",docs:{description:"Require event bus publish calls to use registered intent creators instead of raw event objects or intent strings."},messages:{rawPublish:"Publish through a registered intent creator; raw event objects or intent strings bypass Ark contracts."},schema:[]},create(e){return{CallExpression(t){let n=t.arguments?.[0],s=M(n),r=U({publishCall:xe(t),rawIntentName:s,objectHasIntent:P(n,"intent"),arkPublishCandidate:!1,hasSource:!0});if(r.some(i=>i.ruleId==="RAW_EVENT_PUBLISH")){let i=r.find(l=>l.ruleId==="RAW_EVENT_PUBLISH");w(e,t,"rawPublish",{...i,file:N(e)})}}}}},Ce={meta:{type:"problem",docs:{description:"Require event bus publish calls to include source metadata."},messages:{missingSource:"Strict Ark publish calls must include metadata.source."},schema:[]},create(e){return{CallExpression(t){let n=t.arguments?.[0],s=t.arguments?.[2],i=U({publishCall:xe(t),rawIntentName:M(n),objectHasIntent:P(n,"intent"),arkPublishCandidate:!0,hasSource:nt(n)||P(s,"source")}).find(l=>l.ruleId==="PUBLISH_MISSING_SOURCE");i&&w(e,t,"missingSource",{...i,file:N(e)})}}}},Ne={meta:{type:"problem",docs:{description:"Disallow ambient globals from the layer\u2019s forbiddenGlobals in ark.config.json (same purity surface as ark-check). Option `globals` is a standalone fallback when no project config applies."},messages:{forbiddenGlobal:'Ambient global "{{name}}" is forbidden in {{layer}} (ark.config.json); inject the capability through a port instead.',forbiddenGlobalDefault:'Ambient global "{{name}}" is forbidden here; inject the capability through a port instead.',forbiddenModule:'{{layer}} must not use module "{{specifier}}" because it is the import form of forbidden global "{{name}}".'},schema:[{type:"object",properties:{globals:{type:"array",items:{type:"string"}}},additionalProperties:!1}]},create(e){let t=N(e),n=e.options?.[0],s=T(t),r=s?j(s):null,i=s?d.default.dirname(s):null,l=null,c="this layer";if(r&&i&&t){let o=d.default.isAbsolute(t)?t:d.default.resolve(t),a=d.default.relative(i,o).split(d.default.sep).join("/");if(!W(r,a))return{};let u=r.layers?.find(m=>m.name===R(a,r.layers));u?.forbiddenGlobals?.length?(l=new Set(u.forbiddenGlobals),c=u.name):l=null}else n?.globals&&(l=new Set(n.globals));if(!l)return{};let g=typeof J(e)?.getScope=="function",f=(o,a)=>{let u=d.default.isAbsolute(t)?t:d.default.resolve(t),m=i?d.default.relative(i,u).split(d.default.sep).join("/"):t;w(e,o,r?"forbiddenGlobal":"forbiddenGlobalDefault",{ruleId:"FORBIDDEN_GLOBAL",file:m,fromLayer:c,target:a,message:`${c} must not use the ambient global "${a}".`},{name:a,layer:c})},p=(o,a,u,m)=>{if(u||typeof a!="string")return;let y=K(a,l);if(!y)return;let h=d.default.isAbsolute(t)?t:d.default.resolve(t),A=i?d.default.relative(i,h).split(d.default.sep).join("/"):t;w(e,o,"forbiddenModule",{ruleId:"FORBIDDEN_GLOBAL",file:A,fromLayer:c,target:a,edgeKind:m,message:`${c} must not use module "${a}" because it is the import form of forbidden global "${y}".`},{layer:c,name:y,specifier:a,importKind:m})};return{MemberExpression(o){if(o.parent?.type==="MemberExpression"&&o.parent.object===o)return;let a=Ie(o);if(!a||v(e,a.root,a.segments[0]))return;let u=a.segments[0]==="globalThis",m=u?a.segments.slice(1):a.segments,y;for(let h=m.length;h>=(u?1:2);h-=1){let A=m.slice(0,h).join(".");if(l.has(A)){y=A;break}}y?f(o,y):!g&&l.has(a.segments[0])&&f(o,a.segments[0])},CallExpression(o){let a=o;if(a.callee?.type==="Identifier"&&a.callee.name==="require"&&a.arguments?.[0]?.type==="Literal"&&!v(e,o,"require")&&p(o,a.arguments[0].value,!1,"require"),g)return;let u=a.callee?.type==="Identifier"?a.callee.name:void 0;u&&l.has(u)&&f(o,u)},ImportDeclaration(o){let a=o,u=(a.specifiers??[]).filter(y=>y.type==="ImportSpecifier"),m=u.length>0&&u.length===(a.specifiers??[]).length&&u.every(y=>y.importKind==="type");p(o,a.source?.value,a.importKind==="type"||m,"import")},ImportExpression(o){let a=o;a.source?.type==="Literal"&&p(o,a.source.value,!1,"dynamic-import")},TSImportEqualsDeclaration(o){let a=o;p(o,a.moduleReference?.expression?.value,a.importKind==="type"||a.isTypeOnly===!0,"require")},ExportNamedDeclaration(o){let a=o;if(!a.source)return;let u=a.specifiers??[],m=u.length>0&&u.every(y=>y.exportKind==="type");p(o,a.source.value,a.exportKind==="type"||m,"export")},ExportAllDeclaration(o){let a=o;p(o,a.source?.value,a.exportKind==="type","export")},NewExpression(o){if(g)return;let a=o.callee?.type==="Identifier"?o.callee.name:void 0;a&&l.has(a)&&f(o,a)},Identifier(o){!g||!o.name||!l.has(o.name)||!et(e,o)||v(e,o,o.name)||f(o,o.name)}}}},Le={meta:{type:"problem",docs:{description:"Disallow importing modules whose effect capability the layer denies (ark.config.json capabilities.deny / pure \u2014 same wall surface as ark-check). Import dimension only: ambient globals stay with no-forbidden-globals and the CLI/hook symbol path."},messages:{deniedCapability:'{{layer}} denies the {{capability}} capability (ark.config.json); "{{specifier}}" imports it. Define a port and bind the implementation in an adapter layer.'},schema:[]},create(e){let t=N(e),n=T(t),s=n?j(n):null,r=n?d.default.dirname(n):null;if(!s||!r||!t)return{};let i=d.default.isAbsolute(t)?t:d.default.resolve(t),l=d.default.relative(r,i).split(d.default.sep).join("/");if(!W(s,l))return{};let c=s.layers?.find(p=>p.name===R(l,s.layers));if(!c)return{};let g=new Set(ae(c));if(g.size===0)return{};let f=(p,o,a,u)=>{if(a||typeof o!="string"||K(o,c.forbiddenGlobals??[]))return;let m=ie(o);!m||!g.has(m)||w(e,p,"deniedCapability",{ruleId:"CAPABILITY_VIOLATION",file:l,fromLayer:c.name,target:o,capability:m,edgeKind:u,message:`${c.name} denies the ${m} capability; found import of "${o}".`},{layer:c.name,capability:m,specifier:o})};return{ImportDeclaration(p){let o=p,a=(o.specifiers??[]).filter(m=>m.type==="ImportSpecifier"),u=a.length>0&&a.length===(o.specifiers??[]).length&&a.every(m=>m.importKind==="type");f(p,o.source?.value,o.importKind==="type"||u,"import")},ImportExpression(p){let o=p;o.source?.type==="Literal"&&f(p,o.source.value,!1,"dynamic-import")},TSImportEqualsDeclaration(p){let o=p;f(p,o.moduleReference?.expression?.value,o.importKind==="type"||o.isTypeOnly===!0,"require")},ExportNamedDeclaration(p){let o=p;if(!o.source)return;let a=o.specifiers??[],u=a.length>0&&a.every(m=>m.exportKind==="type");f(p,o.source.value,o.exportKind==="type"||u,"export")},ExportAllDeclaration(p){let o=p;f(p,o.source?.value,o.exportKind==="type","export")},CallExpression(p){let o=p;o.callee?.type==="Identifier"&&o.callee.name==="require"&&o.arguments?.[0]?.type==="Literal"&&!v(e,p,"require")&&f(p,o.arguments[0].value,!1,"require")}}}},ot={"no-domain-infra-imports":we,"no-raw-event-publish":Ee,"require-publish-source":Ce,"no-forbidden-globals":Ne,"no-denied-capabilities":Le},$={rules:ot};$.configs={recommended:{plugins:{ark:$},rules:{"ark/no-domain-infra-imports":"error","ark/no-raw-event-publish":"error","ark/require-publish-source":"error","ark/no-forbidden-globals":"error","ark/no-denied-capabilities":"error"}}};var it=$;0&&(module.exports={findConfigPath,globToRegExp,isEdgeDenied,layerForRelativePath,loadArkConfig,noDeniedCapabilities,noDomainInfraImports,noForbiddenGlobals,noRawEventPublish,patternSpecificity,plugin,readTsconfigPathAliases,requirePublishSource,resolveImportSpecifier,resolveRelativeImport});
|