arkgate 3.2.0 → 3.4.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 +77 -0
- package/README.md +4 -3
- package/bin/ark-check.mjs +1 -0
- package/bin/ark-mcp.mjs +10 -0
- package/bin/lib/adapter-contract.mjs +3 -0
- package/bin/lib/ambient-state.mjs +221 -0
- package/bin/lib/analysis-engine.mjs +6 -5
- package/bin/lib/architecture-scan.mjs +31 -0
- package/bin/lib/config-contract.mjs +25 -0
- package/bin/lib/doctor-advisories.mjs +20 -0
- package/bin/lib/doctor-plan.mjs +5 -5
- package/bin/lib/remediation.mjs +14 -0
- package/bin/lib/ts-resolve.mjs +2 -1
- package/bin/lib/violations.mjs +2 -0
- package/dist/configTypes-DAPvBqK6.d.cts +61 -0
- package/dist/configTypes-DAPvBqK6.d.ts +61 -0
- package/dist/eslint/index.cjs +3 -3
- package/dist/eslint/index.d.cts +3 -2
- package/dist/eslint/index.d.ts +3 -2
- package/dist/eslint/index.js +3 -3
- package/dist/index.cjs +6 -5
- package/dist/index.d.cts +344 -82
- package/dist/index.d.ts +344 -82
- package/dist/index.js +6 -5
- package/docs/agent-guide.md +18 -0
- package/docs/configuration.md +3 -0
- package/docs/package-surface.md +5 -1
- package/package.json +4 -3
- package/schemas/ark.config.schema.json +25 -0
- package/server.json +2 -2
- package/dist/configContract-BxSIwVRo.d.cts +0 -259
- package/dist/configContract-BxSIwVRo.d.ts +0 -259
|
@@ -29,7 +29,10 @@ import {
|
|
|
29
29
|
collectConfigWarnings,
|
|
30
30
|
} from './config-warnings.mjs';
|
|
31
31
|
import {
|
|
32
|
+
ambientCoveredByForbiddenGlobals,
|
|
33
|
+
collectCapabilityUses,
|
|
32
34
|
collectForbiddenCapabilityUses,
|
|
35
|
+
effectiveCapabilityDeny,
|
|
33
36
|
evaluateArchitectureGraph,
|
|
34
37
|
extractSemanticDependencies,
|
|
35
38
|
} from './analysis-engine.mjs';
|
|
@@ -69,6 +72,34 @@ export function scanSourceFile(ts, root, config, rules, manifestIntentLayers, fi
|
|
|
69
72
|
});
|
|
70
73
|
}
|
|
71
74
|
|
|
75
|
+
// U04 — opted-in capability walls (ADR 0009). One violation, one voice: an
|
|
76
|
+
// ambient use already covered by this layer's forbiddenGlobals reports only
|
|
77
|
+
// FORBIDDEN_GLOBAL (D7 dedup); absence of the surface adds nothing.
|
|
78
|
+
const capabilityDeny = new Set(effectiveCapabilityDeny(layerConfig ?? {}));
|
|
79
|
+
if (capabilityDeny.size > 0) {
|
|
80
|
+
for (const use of collectCapabilityUses(ts, sourceFile)) {
|
|
81
|
+
if (!capabilityDeny.has(use.capability)) continue;
|
|
82
|
+
if (
|
|
83
|
+
use.source === 'ambient-global' &&
|
|
84
|
+
ambientCoveredByForbiddenGlobals(use.symbol, forbiddenGlobals)
|
|
85
|
+
) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
violations.push({
|
|
89
|
+
ruleId: 'CAPABILITY_VIOLATION',
|
|
90
|
+
file: normalize(path.relative(root, file)),
|
|
91
|
+
line: use.line,
|
|
92
|
+
fromLayer: sourceLayer,
|
|
93
|
+
target: use.symbol,
|
|
94
|
+
capability: use.capability,
|
|
95
|
+
message:
|
|
96
|
+
use.source === 'import-based'
|
|
97
|
+
? `${sourceLayer} denies the ${use.capability} capability; found import of "${use.symbol}".`
|
|
98
|
+
: `${sourceLayer} denies the ${use.capability} capability; found ambient "${use.symbol}".`,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
72
103
|
const checkModuleEdge = (specifier, node, kind, typeOnly = false) => {
|
|
73
104
|
const namedBindings = namedModuleBindings(ts, node);
|
|
74
105
|
edges.push({
|
|
@@ -113,6 +113,31 @@ export const ARK_CONFIG_SCHEMA = {
|
|
|
113
113
|
intentPrefixes: stringArraySchema,
|
|
114
114
|
description: { type: 'string', minLength: 1 },
|
|
115
115
|
forbiddenGlobals: stringArraySchema,
|
|
116
|
+
capabilities: {
|
|
117
|
+
type: 'object',
|
|
118
|
+
additionalProperties: false,
|
|
119
|
+
properties: {
|
|
120
|
+
deny: {
|
|
121
|
+
type: 'array',
|
|
122
|
+
uniqueItems: true,
|
|
123
|
+
items: {
|
|
124
|
+
type: 'string',
|
|
125
|
+
// Parity with src/domain/capabilities.ts CAPABILITY_IDS (guarded by tests;
|
|
126
|
+
// this literal keeps the generated CLI artifact self-contained).
|
|
127
|
+
enum: [
|
|
128
|
+
'network',
|
|
129
|
+
'filesystem',
|
|
130
|
+
'clock',
|
|
131
|
+
'randomness',
|
|
132
|
+
'environment',
|
|
133
|
+
'process',
|
|
134
|
+
'persistence',
|
|
135
|
+
],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
pure: { type: 'boolean' },
|
|
116
141
|
mayImportInfrastructure: { type: 'boolean' },
|
|
117
142
|
optional: { type: 'boolean' },
|
|
118
143
|
},
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor's advisory sensors, aggregated (W01 contract health + U05 ambient
|
|
3
|
+
* state). Advisory only: nothing here feeds a verdict, designFitness, or an
|
|
4
|
+
* exit code. One seam keeps doctor-plan.mjs inside its module budget as new
|
|
5
|
+
* advisory surfaces land.
|
|
6
|
+
*/
|
|
7
|
+
import { computeAmbientState, printAmbientStateSection } from './ambient-state.mjs';
|
|
8
|
+
import { computeContractHealth, printContractHealthSection } from './contract-smells.mjs';
|
|
9
|
+
|
|
10
|
+
export function computeDoctorAdvisories(root, config, cov, rules, files, ts) {
|
|
11
|
+
return {
|
|
12
|
+
contractHealth: computeContractHealth(root, config, cov, rules),
|
|
13
|
+
ambientState: computeAmbientState(ts, root, config, files),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function printDoctorAdvisories(advisories, io) {
|
|
18
|
+
printContractHealthSection(advisories.contractHealth, io);
|
|
19
|
+
printAmbientStateSection(advisories.ambientState, io);
|
|
20
|
+
}
|
package/bin/lib/doctor-plan.mjs
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
} from './post-green-path.mjs';
|
|
41
41
|
import { loadGoldenPattern, summarizeGoldenPattern } from './golden-pattern.mjs';
|
|
42
42
|
import { summarizePilotLoop } from './pilot-loop.mjs';
|
|
43
|
-
import {
|
|
43
|
+
import { computeDoctorAdvisories, printDoctorAdvisories } from './doctor-advisories.mjs';
|
|
44
44
|
|
|
45
45
|
const color = {
|
|
46
46
|
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
@@ -425,8 +425,7 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
425
425
|
patternBets: patternBetsForLoop,
|
|
426
426
|
designSmells,
|
|
427
427
|
});
|
|
428
|
-
|
|
429
|
-
const contractHealth = computeContractHealth(root, config, cov, rules);
|
|
428
|
+
const { contractHealth, ambientState } = computeDoctorAdvisories(root, config, cov, rules, files, options.ts); // W01+U05 advisories — never a verdict
|
|
430
429
|
|
|
431
430
|
if (asJson) {
|
|
432
431
|
console.log(
|
|
@@ -465,6 +464,8 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
465
464
|
pilotLoop,
|
|
466
465
|
// W01: contract-health meta-lint (advisory; verdict unchanged).
|
|
467
466
|
contractHealth,
|
|
467
|
+
// U05: ambient-state sensor (advisory; opt-in; verdict unchanged).
|
|
468
|
+
ambientState,
|
|
468
469
|
governed: cov.governed,
|
|
469
470
|
emptyLayers: cov.emptyLayers,
|
|
470
471
|
layersWithoutRules: cov.layersWithoutRules,
|
|
@@ -646,8 +647,7 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
646
647
|
);
|
|
647
648
|
}
|
|
648
649
|
|
|
649
|
-
|
|
650
|
-
printContractHealthSection(contractHealth, { line, warn, color });
|
|
650
|
+
printDoctorAdvisories({ contractHealth, ambientState }, { line, warn, color }); // advisory sections
|
|
651
651
|
|
|
652
652
|
console.log('');
|
|
653
653
|
console.log(color.bold('Coverage'));
|
package/bin/lib/remediation.mjs
CHANGED
|
@@ -54,6 +54,8 @@ export function deterministicNextAction(violation) {
|
|
|
54
54
|
return `Define a port in ${violation.fromLayer ?? 'the source layer'}, inject the ${violation.toLayer ?? 'outer-layer'} implementation, then preflight again.`;
|
|
55
55
|
case 'FORBIDDEN_GLOBAL':
|
|
56
56
|
return `Inject ${violation.target ?? 'the capability'} through a port, then preflight again.`;
|
|
57
|
+
case 'CAPABILITY_VIOLATION':
|
|
58
|
+
return `Define a ${String(violation.capability ?? 'capability')} port in ${violation.fromLayer ?? 'the walled layer'}, bind the implementation outside it, then preflight again.`;
|
|
57
59
|
case 'CIRCULAR_DEPENDENCY':
|
|
58
60
|
return 'Extract the shared dependency into a third module, then preflight again.';
|
|
59
61
|
case 'RAW_EVENT_PUBLISH':
|
|
@@ -148,6 +150,13 @@ export function classifyRemediation(violation) {
|
|
|
148
150
|
rationale: 'Ambient global in a pure layer: inject the capability through a port (Clock, Config, Http). Introducing the port is a design decision.',
|
|
149
151
|
};
|
|
150
152
|
}
|
|
153
|
+
if (ruleId === 'CAPABILITY_VIOLATION') {
|
|
154
|
+
return {
|
|
155
|
+
class: 'judgment',
|
|
156
|
+
confidence: 0.8,
|
|
157
|
+
rationale: 'A denied effect capability (clock/network/persistence/…) reached a walled layer: define a port and bind the implementation outside it. Never mechanical-safe — the port shape is a design decision.',
|
|
158
|
+
};
|
|
159
|
+
}
|
|
151
160
|
if (ruleId === 'CIRCULAR_DEPENDENCY') {
|
|
152
161
|
return {
|
|
153
162
|
class: 'judgment',
|
|
@@ -201,6 +210,11 @@ export function enrichViolationWithFixClass(violation) {
|
|
|
201
210
|
enriched.effort = 'small';
|
|
202
211
|
enriched.enthusiastHint = `Do not call "${violation.target ?? 'that global'}" here. Pass the capability in through a small interface (for example a Clock, HttpPort, or Config provider).`;
|
|
203
212
|
break;
|
|
213
|
+
case 'CAPABILITY_VIOLATION':
|
|
214
|
+
enriched.fixClass = 'inject-port';
|
|
215
|
+
enriched.effort = 'medium';
|
|
216
|
+
enriched.enthusiastHint = `This layer denies the ${String(violation.capability ?? 'effect')} capability. Define a small port (for example ClockPort, HttpPort, StoragePort) and inject the implementation from an adapter layer instead of using "${violation.target ?? 'the capability'}" directly.`;
|
|
217
|
+
break;
|
|
204
218
|
case 'RAW_EVENT_PUBLISH':
|
|
205
219
|
enriched.fixClass = 'registered-intent';
|
|
206
220
|
enriched.effort = 'small';
|
package/bin/lib/ts-resolve.mjs
CHANGED
|
@@ -129,9 +129,10 @@ export function scanCacheKey(root, args) {
|
|
|
129
129
|
// v3: per-file exportsOnlyTypes. v4: typeOnlyExportNames + namedBindings.
|
|
130
130
|
// v5: hasTopLevelSideEffects. v6: non-exported impure inits + non-export class statics.
|
|
131
131
|
// v7: scope-aware forbidden globals + import-equals dependency edges.
|
|
132
|
+
// v8: opted-in capability walls (U04) — stale caches must not miss wall verdicts.
|
|
132
133
|
return crypto
|
|
133
134
|
.createHash('sha1')
|
|
134
|
-
.update(`ark-check-cache-
|
|
135
|
+
.update(`ark-check-cache-v8\0${read(configPath)}\0${manifestPath ? read(manifestPath) : ''}`)
|
|
135
136
|
.digest('hex');
|
|
136
137
|
}
|
|
137
138
|
|
package/bin/lib/violations.mjs
CHANGED
|
@@ -45,6 +45,8 @@ export const FIX_HINTS = {
|
|
|
45
45
|
'Use a source intent that belongs to the same layer as the publishing file, or move the file.',
|
|
46
46
|
FORBIDDEN_GLOBAL:
|
|
47
47
|
'Inject the capability through a port (e.g. a Clock, IdGenerator, or HttpPort) instead of reaching for the ambient global.',
|
|
48
|
+
CAPABILITY_VIOLATION:
|
|
49
|
+
'This layer denies that effect. Define a small port (ClockPort, HttpPort, StoragePort) and bind the implementation in an adapter layer.',
|
|
48
50
|
CIRCULAR_DEPENDENCY:
|
|
49
51
|
'Break the cycle: extract the shared code into a module both sides import, invert one edge behind a port/interface, or merge the files if they are really one unit.',
|
|
50
52
|
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type vocabulary for the ark.config.json contract (U02 pilot 1).
|
|
3
|
+
*
|
|
4
|
+
* Pure declarations only — no runtime values. The loader/validator logic and the
|
|
5
|
+
* published JSON Schema live in ./configContract.ts, whose generated CLI artifact
|
|
6
|
+
* must stay self-contained: type-only imports/exports are erased on transpile, so
|
|
7
|
+
* this split never reaches bin/lib/config-contract.mjs.
|
|
8
|
+
*/
|
|
9
|
+
type ArkConfigSchemaVersion = '1.0';
|
|
10
|
+
type ArkConfigCyclePolicy = 'strict' | 'soft' | 'framework-soft' | 'off';
|
|
11
|
+
type ArkConfigLayerCapabilities = {
|
|
12
|
+
deny?: string[];
|
|
13
|
+
};
|
|
14
|
+
type ArkConfigLayer = {
|
|
15
|
+
name: string;
|
|
16
|
+
patterns: string[];
|
|
17
|
+
exclude?: string[];
|
|
18
|
+
intentPrefixes?: string[];
|
|
19
|
+
description?: string;
|
|
20
|
+
forbiddenGlobals?: string[];
|
|
21
|
+
/** ADR 0009 D2 — opt-in effect-capability walls; absence changes no verdict. */
|
|
22
|
+
capabilities?: ArkConfigLayerCapabilities;
|
|
23
|
+
/** Dual-depth sugar: `pure: true` denies all seven capabilities. */
|
|
24
|
+
pure?: boolean;
|
|
25
|
+
mayImportInfrastructure?: boolean;
|
|
26
|
+
optional?: boolean;
|
|
27
|
+
};
|
|
28
|
+
type ArkConfigRule = {
|
|
29
|
+
from: string;
|
|
30
|
+
to: string;
|
|
31
|
+
allowed: boolean;
|
|
32
|
+
message?: string;
|
|
33
|
+
peerIsolation?: boolean;
|
|
34
|
+
sliceFolders?: string[];
|
|
35
|
+
};
|
|
36
|
+
type ArkConfigSafety = {
|
|
37
|
+
maxTsSuppressions?: number;
|
|
38
|
+
maxAnyCasts?: number;
|
|
39
|
+
allowInMemory?: boolean;
|
|
40
|
+
allowDisabledPeerIsolation?: boolean;
|
|
41
|
+
};
|
|
42
|
+
type ArkConfig = {
|
|
43
|
+
$schema: string;
|
|
44
|
+
schemaVersion: ArkConfigSchemaVersion;
|
|
45
|
+
name?: string;
|
|
46
|
+
include: string[];
|
|
47
|
+
exclude?: string[];
|
|
48
|
+
excludeGenerated?: boolean;
|
|
49
|
+
frameworkOverlay?: string;
|
|
50
|
+
layers: ArkConfigLayer[];
|
|
51
|
+
rules: ArkConfigRule[];
|
|
52
|
+
cyclePolicy?: ArkConfigCyclePolicy;
|
|
53
|
+
dynamicImportAllowlist?: string[];
|
|
54
|
+
safety?: ArkConfigSafety;
|
|
55
|
+
};
|
|
56
|
+
type ArkConfigLoadResult = {
|
|
57
|
+
config: ArkConfig;
|
|
58
|
+
migratedFrom: 'unversioned' | null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type { ArkConfig as A, ArkConfigRule as a, ArkConfigSchemaVersion as b, ArkConfigLoadResult as c, ArkConfigLayer as d };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type vocabulary for the ark.config.json contract (U02 pilot 1).
|
|
3
|
+
*
|
|
4
|
+
* Pure declarations only — no runtime values. The loader/validator logic and the
|
|
5
|
+
* published JSON Schema live in ./configContract.ts, whose generated CLI artifact
|
|
6
|
+
* must stay self-contained: type-only imports/exports are erased on transpile, so
|
|
7
|
+
* this split never reaches bin/lib/config-contract.mjs.
|
|
8
|
+
*/
|
|
9
|
+
type ArkConfigSchemaVersion = '1.0';
|
|
10
|
+
type ArkConfigCyclePolicy = 'strict' | 'soft' | 'framework-soft' | 'off';
|
|
11
|
+
type ArkConfigLayerCapabilities = {
|
|
12
|
+
deny?: string[];
|
|
13
|
+
};
|
|
14
|
+
type ArkConfigLayer = {
|
|
15
|
+
name: string;
|
|
16
|
+
patterns: string[];
|
|
17
|
+
exclude?: string[];
|
|
18
|
+
intentPrefixes?: string[];
|
|
19
|
+
description?: string;
|
|
20
|
+
forbiddenGlobals?: string[];
|
|
21
|
+
/** ADR 0009 D2 — opt-in effect-capability walls; absence changes no verdict. */
|
|
22
|
+
capabilities?: ArkConfigLayerCapabilities;
|
|
23
|
+
/** Dual-depth sugar: `pure: true` denies all seven capabilities. */
|
|
24
|
+
pure?: boolean;
|
|
25
|
+
mayImportInfrastructure?: boolean;
|
|
26
|
+
optional?: boolean;
|
|
27
|
+
};
|
|
28
|
+
type ArkConfigRule = {
|
|
29
|
+
from: string;
|
|
30
|
+
to: string;
|
|
31
|
+
allowed: boolean;
|
|
32
|
+
message?: string;
|
|
33
|
+
peerIsolation?: boolean;
|
|
34
|
+
sliceFolders?: string[];
|
|
35
|
+
};
|
|
36
|
+
type ArkConfigSafety = {
|
|
37
|
+
maxTsSuppressions?: number;
|
|
38
|
+
maxAnyCasts?: number;
|
|
39
|
+
allowInMemory?: boolean;
|
|
40
|
+
allowDisabledPeerIsolation?: boolean;
|
|
41
|
+
};
|
|
42
|
+
type ArkConfig = {
|
|
43
|
+
$schema: string;
|
|
44
|
+
schemaVersion: ArkConfigSchemaVersion;
|
|
45
|
+
name?: string;
|
|
46
|
+
include: string[];
|
|
47
|
+
exclude?: string[];
|
|
48
|
+
excludeGenerated?: boolean;
|
|
49
|
+
frameworkOverlay?: string;
|
|
50
|
+
layers: ArkConfigLayer[];
|
|
51
|
+
rules: ArkConfigRule[];
|
|
52
|
+
cyclePolicy?: ArkConfigCyclePolicy;
|
|
53
|
+
dynamicImportAllowlist?: string[];
|
|
54
|
+
safety?: ArkConfigSafety;
|
|
55
|
+
};
|
|
56
|
+
type ArkConfigLoadResult = {
|
|
57
|
+
config: ArkConfig;
|
|
58
|
+
migratedFrom: 'unversioned' | null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type { ArkConfig as A, ArkConfigRule as a, ArkConfigSchemaVersion as b, ArkConfigLoadResult as c, ArkConfigLayer as d };
|
package/dist/eslint/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
${
|
|
3
|
-
`)}`),this.name="ArkConfigValidationError",this.source=n,this.issues=t}};function Z(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function j(e,n){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(n)?`${e}.${n}`:`${e}[${JSON.stringify(n)}]`}function b(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Ee(e,n){let t="#/$defs/";if(e.startsWith(t))return n.$defs[e.slice(t.length)]}function R(e,n,t,i,r){if(n.$ref){let o=Ee(n.$ref,i);if(!o){r.push({path:t,message:`schema reference ${n.$ref} cannot be resolved`});return}R(e,o,t,i,r);return}if(n.const!==void 0&&!Object.is(e,n.const)){r.push({path:t,message:`must equal ${JSON.stringify(n.const)}`});return}if(n.enum&&!n.enum.some(o=>Object.is(o,e))){r.push({path:t,message:`must be one of ${n.enum.map(String).join(", ")}`});return}if(n.type==="object"){if(!Z(e)){r.push({path:t,message:`must be an object; received ${b(e)}`});return}let o=n.properties??{};for(let s of n.required??[])e[s]===void 0&&r.push({path:j(t,s),message:"is required"});if(n.additionalProperties===!1)for(let s of Object.keys(e))s in o||r.push({path:j(t,s),message:"unknown field"});for(let[s,a]of Object.entries(o))e[s]!==void 0&&R(e[s],a,j(t,s),i,r);return}if(n.type==="array"){if(!Array.isArray(e)){r.push({path:t,message:`must be an array; received ${b(e)}`});return}if(n.minItems!==void 0&&e.length<n.minItems&&r.push({path:t,message:`must contain at least ${n.minItems} item(s)`}),n.uniqueItems){let o=e.map(s=>JSON.stringify(s));new Set(o).size!==o.length&&r.push({path:t,message:"must not contain duplicate items"})}n.items&&e.forEach((o,s)=>R(o,n.items,`${t}[${s}]`,i,r));return}if(n.type==="string"){if(typeof e!="string"){r.push({path:t,message:`must be a string; received ${b(e)}`});return}n.minLength!==void 0&&e.length<n.minLength&&r.push({path:t,message:`must contain at least ${n.minLength} character(s)`});return}if(n.type==="boolean"){typeof e!="boolean"&&r.push({path:t,message:`must be a boolean; received ${b(e)}`});return}if(n.type==="integer"){if(!Number.isInteger(e)){r.push({path:t,message:`must be an integer; received ${b(e)}`});return}n.minimum!==void 0&&e<n.minimum&&r.push({path:t,message:`must be at least ${n.minimum}`})}}function xe(e){return{...e,$schema:e.$schema===void 0?F: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?z.map(n=>({...n})):e.rules}}function Ne(e,n="ark.config.json"){if(!Z(e))throw new h(n,[{path:"$",message:`must be an object; received ${b(e)}`}]);let t=e.schemaVersion===void 0?"unversioned":null;if(e.schemaVersion!==void 0&&e.schemaVersion!=="1.0")throw new h(n,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(e.schemaVersion)}; expected 1.0`}]);return{candidate:xe(e),migratedFrom:t}}function we(e,n="ark.config.json"){let{candidate:t,migratedFrom:i}=Ne(e,n),r=[];if(R(t,Y,"$",Y,r),r.length>0)throw new h(n,r);return{config:t,migratedFrom:i}}function Q(e,n="ark.config.json"){let t;try{t=JSON.parse(e)}catch(i){throw new h(n,[{path:"$",message:`invalid JSON: ${i instanceof Error?i.message:String(i)}`}])}return we(t,n)}function m(e){return typeof e=="string"&&e.length>0?e:void 0}function X(e,n){return Number.isInteger(e)&&Number(e)>0?Number(e):n}function Le(e,n,t){return e==="LAYER_IMPORT_VIOLATION"?n.typeOnly||t.targetTypeOnlyExports===!0||t.namedBindingsTypeOnly===!0?"Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.":t.peerIsolation===!0?"Extract the shared dependency to a shared layer, then preflight again.":`Define a port in ${n.fromLayer??"the source layer"}, inject the ${n.toLayer??"outer-layer"} implementation, then preflight again.`:e==="FORBIDDEN_GLOBAL"?`Inject ${n.target??"the capability"} through a port, then preflight again.`:e==="CIRCULAR_DEPENDENCY"?"Extract the shared dependency into a third module, then preflight again.":e==="RAW_EVENT_PUBLISH"?"Publish through a registered intent creator, then run Ark again.":e==="PUBLISH_MISSING_SOURCE"?"Add metadata.source to the publish call, then run Ark again.":`Resolve ${e} without weakening ark.config.json, then run Ark again.`}function ee(e,n="error"){let t=m(e.ruleId)??m(e.code)??"ARK_UNKNOWN",i=e.severity==="warning"?"warning":n,r={...m(e.target)?{target:m(e.target)}:{},...m(e.fromLayer)?{fromLayer:m(e.fromLayer)}:{},...m(e.toLayer)?{toLayer:m(e.toLayer)}:{},...typeof e.typeOnly=="boolean"?{typeOnly:e.typeOnly}:{}};return{ruleId:t,severity:i,message:m(e.message)??t,location:{file:m(e.file)??"<unknown>",line:X(e.line,1),column:X(e.column,1)},evidence:r,nextAction:m(e.nextAction)??Le(t,r,e)}}var ne={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 V(e){if(!e.publishCall)return[];let n=[];return(e.rawIntentName!==void 0&&_e(e.rawIntentName)||e.objectHasIntent)&&n.push({ruleId:"RAW_EVENT_PUBLISH",message:ne.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&n.push({ruleId:"PUBLISH_MISSING_SOURCE",message:ne.PUBLISH_MISSING_SOURCE}),n}function x(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 n=e.getFilename();if(typeof n=="string"&&n.length>0)return n}catch{}return""}function N(e,n,t,i,r){let o=ee({...i,line:i.line??n.loc?.start?.line,column:i.column??(typeof n.loc?.start?.column=="number"?n.loc.start.column+1:void 0)});return e.report({node:n,messageId:t,...r?{data:r}:{},diagnostic:o}),o}function G(e){if(!e||e==="<input>"||e.startsWith("stdin"))return null;let n=c.default.dirname(c.default.resolve(e));for(;;){let t=c.default.join(n,"ark.config.json");if(A.default.existsSync(t))return t;let i=c.default.dirname(n);if(i===n)return null;n=i}}var M=new Map;function D(e){if(M.has(e))return M.get(e)??null;if(!A.default.existsSync(e))return null;let n=Q(A.default.readFileSync(e,"utf8"),e).config;return M.set(e,n),n}function re(e,n){if(!n.startsWith("."))return null;let t=c.default.resolve(c.default.dirname(e),n),i=[t,`${t}.ts`,`${t}.tsx`,`${t}.mts`,`${t}.cts`,`${t}.js`,`${t}.jsx`,c.default.join(t,"index.ts"),c.default.join(t,"index.tsx"),c.default.join(t,"index.js")];for(let r of i)try{if(A.default.existsSync(r)&&A.default.statSync(r).isFile())return r}catch{}return`${t}.ts`}function w(e){return typeof e?.value=="string"?e.value:void 0}function T(e){return e?.name??w(e)}function H(e){return e.sourceCode??e.getSourceCode?.()}function ie(e,n){let t=H(e)?.getScope?.(n);for(;t;){let i=t.references?.find(r=>r.identifier===n);if(i)return i;t=t.upper??void 0}}function te(e,n,t){let i=ie(e,n);if(i?.resolved)return(i.resolved.defs?.length??0)>0;let r=H(e)?.getScope?.(n);for(;r;){let o=r.set?.get(t);if(o)return(o.defs?.length??0)>0;r=r.upper??void 0}return!1}function Oe(e,n){let t=ie(e,n);return t?t.isValueReference!==!1:n.parent?.type==="VariableDeclarator"&&n.parent.init===n}function oe(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 t=oe(e.object),i=T(e.property);if(!(!t||!i))return{root:t.root,segments:[...t.segments,i]}}function $e(e){return T(e.callee?.property)}function se(e,n){return e?.properties?.find(t=>T(t.key)===n)}function C(e,n){return se(e,n)!==void 0}function Pe(e){let n=se(e,"metadata")?.value;return C(n,"source")}function ae(e){return $e(e)==="publish"}var le={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 n=x(e),t=G(n),i=t?D(t):null,r=t?c.default.dirname(t):null,o=s=>{let a=w(s.source);if(a&&i&&r&&n){let d=c.default.isAbsolute(n)?n:c.default.resolve(n),p=c.default.relative(r,d).split(c.default.sep).join("/"),l=S(p,i.layers);if(!l)return;let u=re(d,a);if(!u)return;let f=c.default.relative(r,u).split(c.default.sep).join("/");if(f.startsWith(".."))return;let g=S(f,i.layers);if(!g)return;P(i.rules,l,g,{fromPath:p,toPath:f,layers:i.layers})&&N(e,s,"forbiddenImport",{ruleId:"LAYER_IMPORT_VIOLATION",file:p,fromLayer:l,toLayer:g,target:f,...s.importKind==="type"?{typeOnly:!0}:{},message:`${l} must not import ${g}.`},{fromLayer:l,toLayer:g,specifier:a});return}};return{ImportDeclaration:o,ExportNamedDeclaration:o,ExportAllDeclaration:o}}},ce={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(n){let t=n.arguments?.[0],i=w(t),r=V({publishCall:ae(n),rawIntentName:i,objectHasIntent:C(t,"intent"),arkPublishCandidate:!1,hasSource:!0});if(r.some(o=>o.ruleId==="RAW_EVENT_PUBLISH")){let o=r.find(s=>s.ruleId==="RAW_EVENT_PUBLISH");N(e,n,"rawPublish",{...o,file:x(e)})}}}}},ue={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(n){let t=n.arguments?.[0],i=n.arguments?.[2],o=V({publishCall:ae(n),rawIntentName:w(t),objectHasIntent:C(t,"intent"),arkPublishCandidate:!0,hasSource:Pe(t)||C(i,"source")}).find(s=>s.ruleId==="PUBLISH_MISSING_SOURCE");o&&N(e,n,"missingSource",{...o,file:x(e)})}}}},de={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` explicitly overrides."},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.'},schema:[{type:"object",properties:{globals:{type:"array",items:{type:"string"}}},additionalProperties:!1}]},create(e){let n=x(e),t=e.options?.[0],i=G(n),r=i?D(i):null,o=i?c.default.dirname(i):null,s=null,a="this layer";if(t?.globals)s=new Set(t.globals);else if(r&&o&&n){let l=c.default.isAbsolute(n)?n:c.default.resolve(n),u=c.default.relative(o,l).split(c.default.sep).join("/"),f=r.layers?.find(g=>g.name===S(u,r.layers));f?.forbiddenGlobals?.length?(s=new Set(f.forbiddenGlobals),a=f.name):s=null}if(!s)return{};let d=typeof H(e)?.getScope=="function",p=(l,u)=>{let f=c.default.isAbsolute(n)?n:c.default.resolve(n),g=o?c.default.relative(o,f).split(c.default.sep).join("/"):n;N(e,l,r?"forbiddenGlobal":"forbiddenGlobalDefault",{ruleId:"FORBIDDEN_GLOBAL",file:g,fromLayer:a,target:u,message:`${a} must not use the ambient global "${u}".`},{name:u,layer:a})};return{MemberExpression(l){if(l.parent?.type==="MemberExpression"&&l.parent.object===l)return;let u=oe(l);if(!u||te(e,u.root,u.segments[0]))return;let f=u.segments[0]==="globalThis",g=f?u.segments.slice(1):u.segments,L;for(let _=g.length;_>=(f?1:2);_-=1){let v=g.slice(0,_).join(".");if(s.has(v)){L=v;break}}L?p(l,L):!d&&s.has(u.segments[0])&&p(l,u.segments[0])},CallExpression(l){if(d)return;let u=l.callee?.type==="Identifier"?l.callee.name:void 0;u&&s.has(u)&&p(l,u)},NewExpression(l){if(d)return;let u=l.callee?.type==="Identifier"?l.callee.name:void 0;u&&s.has(u)&&p(l,u)},Identifier(l){!d||!l.name||!s.has(l.name)||!Oe(e,l)||te(e,l,l.name)||p(l,l.name)}}}},je={"no-domain-infra-imports":le,"no-raw-event-publish":ce,"require-publish-source":ue,"no-forbidden-globals":de},E={rules:je};E.configs={recommended:{plugins:{ark:E},rules:{"ark/no-domain-infra-imports":"error","ark/no-raw-event-publish":"error","ark/require-publish-source":"error","ark/no-forbidden-globals":"error"}}};var Fe=E;0&&(module.exports={findConfigPath,globToRegExp,isEdgeDenied,layerForRelativePath,loadArkConfig,noDomainInfraImports,noForbiddenGlobals,noRawEventPublish,patternSpecificity,plugin,requirePublishSource,resolveRelativeImport});
|
|
1
|
+
"use strict";var be=Object.create;var C=Object.defineProperty;var he=Object.getOwnPropertyDescriptor;var Ae=Object.getOwnPropertyNames;var ke=Object.getPrototypeOf,Se=Object.prototype.hasOwnProperty;var Ie=(e,t)=>{for(var n in t)C(e,n,{get:t[n],enumerable:!0})},U=(e,t,n,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of Ae(t))!Se.call(e,r)&&r!==n&&C(e,r,{get:()=>t[r],enumerable:!(i=he(t,r))||i.enumerable});return e};var q=(e,t,n)=>(n=e!=null?be(ke(e)):{},U(t||!e||!e.__esModule?C(n,"default",{value:e,enumerable:!0}):n,e)),we=e=>U(C({},"__esModule",{value:!0}),e);var Be={};Ie(Be,{default:()=>Ge,findConfigPath:()=>L,globToRegExp:()=>R,isEdgeDenied:()=>F,layerForRelativePath:()=>h,loadArkConfig:()=>_,noDeniedCapabilities:()=>ye,noDomainInfraImports:()=>fe,noForbiddenGlobals:()=>me,noRawEventPublish:()=>pe,patternSpecificity:()=>j,plugin:()=>N,requirePublishSource:()=>ge,resolveRelativeImport:()=>ae});module.exports=we(Be);var S=q(require("fs"),1),u=q(require("path"),1);var W=new Map;function Y(e){return/[.*+?^${}()|[\]\\]/.test(e)?`\\${e}`:e}function P(e){let t="";for(let n=0;n<e.length;n+=1){let i=e[n];if(i==="\\"&&n+1<e.length){let r=e[n+1];if("*?{}[],".includes(r)||r==="\\"){t+="\\"+r,n+=1;continue}t+="/";continue}t+=i}return t}function Ce(e){let t=0;for(let n=0;n<e.length;n+=1){let i=e[n];if(i==="\\"){n+=1;continue}if(i==="{")t+=1;else if(i==="}"&&(t-=1,t<0))return!1}return t===0}function R(e){let t=W.get(e);if(t)return t;let n=P(e),i=Ce(n),r="",s=0;for(let c=0;c<n.length;c+=1){let p=n[c];p==="\\"&&c+1<n.length?(r+=Y(n[c+1]),c+=1):p==="*"?n[c+1]==="*"?n[c+2]==="/"?(r+="(?:.*/)?",c+=2):(r+=".*",c+=1):r+="[^/]*":p==="?"?r+="[^/]":p==="{"&&i?(r+="(?:",s+=1):p==="}"&&i&&s>0?(r+=")",s-=1):p===","&&i&&s>0?r+="|":r+=Y(p)}let l=new RegExp(`^${r}$`);return W.set(e,l),l}function j(e){let t=P(String(e)),i=t.split("*")[0].split("/").filter(Boolean).length,r=t.replace(/\*/g,"").length;return i*1e4+r}function h(e,t){let n=String(e).split(/[/\\]/).join("/"),i,r=-1;for(let s of t??[])if(!(s.exclude??[]).some(l=>R(l).test(n))){for(let l of s.patterns??[])if(R(l).test(n)){let c=j(l);c>r&&(r=c,i=s.name)}}return i}function z(e,t){if(!t?.length)return;let n=String(e).split(/[/\\]/).filter(Boolean),i=new Set(t.map(r=>String(r).toLowerCase()));for(let r=0;r<n.length-1;r+=1)if(i.has(n[r].toLowerCase()))return`${n[r]}/${n[r+1]}`}function Re(e){let t=new Set;for(let n of e??[]){let r=P(String(n)).split("/").filter(Boolean);for(let s=0;s<r.length;s+=1){let l=r[s];if((l==="**"||l==="*")&&s>0){let c=r[s-1];c&&!c.includes("*")&&!c.includes("{")&&!c.includes("}")&&t.add(c)}}}return[...t]}function xe(e,t,n){if(Array.isArray(e.sliceFolders)&&e.sliceFolders.length>0)return e.sliceFolders.filter(r=>typeof r=="string"&&r.length>0);let i=(n??[]).find(r=>r.name===t);return Re(i?.patterns)}function Ee(e,t,n,i){for(let r of e??[])if(!(r.from!==t||r.to!==n)&&r.allowed===!1){if(r.peerIsolation){let s=i?.fromPath,l=i?.toPath;if(!s||!l)continue;let c=xe(r,t,i?.layers);if(c.length===0)continue;let p=z(s,c),g=z(l,c);if(!p||!g)continue;if(p!==g)return r;continue}if(t!==n)return r}}function F(e,t,n,i){return Ee(e,t,n,i)!==void 0}var J=Object.freeze(["network","filesystem","clock","randomness","environment","process","persistence"]),Ne=Object.freeze({fetch:"network",XMLHttpRequest:"network",Date:"clock","Date.now":"clock","Math.random":"randomness","process.env":"environment",process:"process"}),Ue=Object.freeze(Object.keys(Ne).sort()),V=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 Z(e){if(!e||e.startsWith(".")||e.startsWith("/"))return null;let t=V[e];if(t)return t;let n=e.indexOf("/");if(n<0)return null;let i=e.slice(0,n),r=V[i];if(r)return r;let s=e.indexOf("/",n+1);return s<0?null:V[e.slice(0,s)]??null}function X(e){if(e?.pure===!0)return[...J].sort();let n=(e?.capabilities?.deny??[]).filter(i=>J.includes(i));return[...new Set(n)].sort()}var M="https://unpkg.com/arkgate@2/schemas/ark.config.schema.json",Q=["DomainModel","ApplicationOrchestration","PersistenceAdapters","IntegrationAdapters","WorkflowSagaEngine","BackgroundJobsScheduling","PresentationAdapters","ReportingReadModels","ExtensibilityMetadata","SecurityAuditObservability","Kernel"],Le=new Set(["PresentationAdapters->ApplicationOrchestration","ApplicationOrchestration->DomainModel","WorkflowSagaEngine->ApplicationOrchestration","WorkflowSagaEngine->DomainModel","BackgroundJobsScheduling->ApplicationOrchestration"]);function _e(){let e=[];for(let t of Q)for(let n of Q)t===n||Le.has(`${t}->${n}`)||e.push({from:t,to:n,allowed:!1});return e}var te=_e();var b={type:"array",items:{type:"string",minLength:1},uniqueItems:!0},ee={$schema:"https://json-schema.org/draft/2020-12/schema",$id:M,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:M,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:{...b,minItems:1,default:["src"]},exclude:{...b,default:[]},excludeGenerated:{type:"boolean",default:!0},frameworkOverlay:{type:"string",minLength:1},layers:{type:"array",default:[],items:{$ref:"#/$defs/layer"}},rules:{type:"array",default:te,items:{$ref:"#/$defs/rule"}},cyclePolicy:{type:"string",enum:["strict","soft","framework-soft","off"],default:"strict"},dynamicImportAllowlist:{...b,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:{...b,minItems:1},exclude:b,intentPrefixes:b,description:{type:"string",minLength:1},forbiddenGlobals:b,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:{...b,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
|
+
${n.map(i=>`- ${i.path}: ${i.message}`).join(`
|
|
3
|
+
`)}`),this.name="ArkConfigValidationError",this.source=t,this.issues=n}};function ne(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function D(e,t){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t)?`${e}.${t}`:`${e}[${JSON.stringify(t)}]`}function A(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Oe(e,t){let n="#/$defs/";if(e.startsWith(n))return t.$defs[e.slice(n.length)]}function x(e,t,n,i,r){if(t.$ref){let s=Oe(t.$ref,i);if(!s){r.push({path:n,message:`schema reference ${t.$ref} cannot be resolved`});return}x(e,s,n,i,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(s=>Object.is(s,e))){r.push({path:n,message:`must be one of ${t.enum.map(String).join(", ")}`});return}if(t.type==="object"){if(!ne(e)){r.push({path:n,message:`must be an object; received ${A(e)}`});return}let s=t.properties??{};for(let l of t.required??[])e[l]===void 0&&r.push({path:D(n,l),message:"is required"});if(t.additionalProperties===!1)for(let l of Object.keys(e))l in s||r.push({path:D(n,l),message:"unknown field"});for(let[l,c]of Object.entries(s))e[l]!==void 0&&x(e[l],c,D(n,l),i,r);return}if(t.type==="array"){if(!Array.isArray(e)){r.push({path:n,message:`must be an array; received ${A(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 s=e.map(l=>JSON.stringify(l));new Set(s).size!==s.length&&r.push({path:n,message:"must not contain duplicate items"})}t.items&&e.forEach((s,l)=>x(s,t.items,`${n}[${l}]`,i,r));return}if(t.type==="string"){if(typeof e!="string"){r.push({path:n,message:`must be a string; received ${A(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 ${A(e)}`});return}if(t.type==="integer"){if(!Number.isInteger(e)){r.push({path:n,message:`must be an integer; received ${A(e)}`});return}t.minimum!==void 0&&e<t.minimum&&r.push({path:n,message:`must be at least ${t.minimum}`})}}function $e(e){return{...e,$schema:e.$schema===void 0?M: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?te.map(t=>({...t})):e.rules}}function Pe(e,t="ark.config.json"){if(!ne(e))throw new k(t,[{path:"$",message:`must be an object; received ${A(e)}`}]);let n=e.schemaVersion===void 0?"unversioned":null;if(e.schemaVersion!==void 0&&e.schemaVersion!=="1.0")throw new k(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(e.schemaVersion)}; expected 1.0`}]);return{candidate:$e(e),migratedFrom:n}}function je(e,t="ark.config.json"){let{candidate:n,migratedFrom:i}=Pe(e,t),r=[];if(x(n,ee,"$",ee,r),r.length>0)throw new k(t,r);return{config:n,migratedFrom:i}}function re(e,t="ark.config.json"){let n;try{n=JSON.parse(e)}catch(i){throw new k(t,[{path:"$",message:`invalid JSON: ${i instanceof Error?i.message:String(i)}`}])}return je(n,t)}function m(e){return typeof e=="string"&&e.length>0?e:void 0}function ie(e,t){return Number.isInteger(e)&&Number(e)>0?Number(e):t}function Fe(e,t,n){return e==="LAYER_IMPORT_VIOLATION"?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.`:e==="FORBIDDEN_GLOBAL"?`Inject ${t.target??"the capability"} through a port, then preflight again.`:e==="CAPABILITY_VIOLATION"?`Define a ${m(n.capability)??"capability"} port in ${t.fromLayer??"the walled layer"}, bind the implementation outside it, then preflight again.`:e==="CIRCULAR_DEPENDENCY"?"Extract the shared dependency into a third module, then preflight again.":e==="RAW_EVENT_PUBLISH"?"Publish through a registered intent creator, then run Ark again.":e==="PUBLISH_MISSING_SOURCE"?"Add metadata.source to the publish call, then run Ark again.":`Resolve ${e} without weakening ark.config.json, then run Ark again.`}function se(e,t="error"){let n=m(e.ruleId)??m(e.code)??"ARK_UNKNOWN",i=e.severity==="warning"?"warning":t,r={...m(e.target)?{target:m(e.target)}:{},...m(e.fromLayer)?{fromLayer:m(e.fromLayer)}:{},...m(e.toLayer)?{toLayer:m(e.toLayer)}:{},...typeof e.typeOnly=="boolean"?{typeOnly:e.typeOnly}:{}};return{ruleId:n,severity:i,message:m(e.message)??n,location:{file:m(e.file)??"<unknown>",line:ie(e.line,1),column:ie(e.column,1)},evidence:r,nextAction:m(e.nextAction)??Fe(n,r,e)}}var oe={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 Ve(e){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function T(e){if(!e.publishCall)return[];let t=[];return(e.rawIntentName!==void 0&&Ve(e.rawIntentName)||e.objectHasIntent)&&t.push({ruleId:"RAW_EVENT_PUBLISH",message:oe.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&t.push({ruleId:"PUBLISH_MISSING_SOURCE",message:oe.PUBLISH_MISSING_SOURCE}),t}function I(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,i,r){let s=se({...i,line:i.line??t.loc?.start?.line,column:i.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:s}),s}function L(e){if(!e||e==="<input>"||e.startsWith("stdin"))return null;let t=u.default.dirname(u.default.resolve(e));for(;;){let n=u.default.join(t,"ark.config.json");if(S.default.existsSync(n))return n;let i=u.default.dirname(t);if(i===t)return null;t=i}}var v=new Map;function _(e){if(v.has(e))return v.get(e)??null;if(!S.default.existsSync(e))return null;let t=re(S.default.readFileSync(e,"utf8"),e).config;return v.set(e,t),t}function ae(e,t){if(!t.startsWith("."))return null;let n=u.default.resolve(u.default.dirname(e),t),i=[n,`${n}.ts`,`${n}.tsx`,`${n}.mts`,`${n}.cts`,`${n}.js`,`${n}.jsx`,u.default.join(n,"index.ts"),u.default.join(n,"index.tsx"),u.default.join(n,"index.js")];for(let r of i)try{if(S.default.existsSync(r)&&S.default.statSync(r).isFile())return r}catch{}return`${n}.ts`}function O(e){return typeof e?.value=="string"?e.value:void 0}function B(e){return e?.name??O(e)}function H(e){return e.sourceCode??e.getSourceCode?.()}function le(e,t){let n=H(e)?.getScope?.(t);for(;n;){let i=n.references?.find(r=>r.identifier===t);if(i)return i;n=n.upper??void 0}}function G(e,t,n){let i=le(e,t);if(i?.resolved)return(i.resolved.defs?.length??0)>0;let r=H(e)?.getScope?.(t);for(;r;){let s=r.set?.get(n);if(s)return(s.defs?.length??0)>0;r=r.upper??void 0}return!1}function De(e,t){let n=le(e,t);return n?n.isValueReference!==!1:t.parent?.type==="VariableDeclarator"&&t.parent.init===t}function ce(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=ce(e.object),i=B(e.property);if(!(!n||!i))return{root:n.root,segments:[...n.segments,i]}}function Me(e){return B(e.callee?.property)}function ue(e,t){return e?.properties?.find(n=>B(n.key)===t)}function E(e,t){return ue(e,t)!==void 0}function Te(e){let t=ue(e,"metadata")?.value;return E(t,"source")}function de(e){return Me(e)==="publish"}var fe={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=I(e),n=L(t),i=n?_(n):null,r=n?u.default.dirname(n):null,s=l=>{let c=O(l.source);if(c&&i&&r&&t){let p=u.default.isAbsolute(t)?t:u.default.resolve(t),g=u.default.relative(r,p).split(u.default.sep).join("/"),o=h(g,i.layers);if(!o)return;let a=ae(p,c);if(!a)return;let d=u.default.relative(r,a).split(u.default.sep).join("/");if(d.startsWith(".."))return;let f=h(d,i.layers);if(!f)return;F(i.rules,o,f,{fromPath:g,toPath:d,layers:i.layers})&&w(e,l,"forbiddenImport",{ruleId:"LAYER_IMPORT_VIOLATION",file:g,fromLayer:o,toLayer:f,target:d,...l.importKind==="type"?{typeOnly:!0}:{},message:`${o} must not import ${f}.`},{fromLayer:o,toLayer:f,specifier:c});return}};return{ImportDeclaration:s,ExportNamedDeclaration:s,ExportAllDeclaration:s}}},pe={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],i=O(n),r=T({publishCall:de(t),rawIntentName:i,objectHasIntent:E(n,"intent"),arkPublishCandidate:!1,hasSource:!0});if(r.some(s=>s.ruleId==="RAW_EVENT_PUBLISH")){let s=r.find(l=>l.ruleId==="RAW_EVENT_PUBLISH");w(e,t,"rawPublish",{...s,file:I(e)})}}}}},ge={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],i=t.arguments?.[2],s=T({publishCall:de(t),rawIntentName:O(n),objectHasIntent:E(n,"intent"),arkPublishCandidate:!0,hasSource:Te(n)||E(i,"source")}).find(l=>l.ruleId==="PUBLISH_MISSING_SOURCE");s&&w(e,t,"missingSource",{...s,file:I(e)})}}}},me={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` explicitly overrides."},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.'},schema:[{type:"object",properties:{globals:{type:"array",items:{type:"string"}}},additionalProperties:!1}]},create(e){let t=I(e),n=e.options?.[0],i=L(t),r=i?_(i):null,s=i?u.default.dirname(i):null,l=null,c="this layer";if(n?.globals)l=new Set(n.globals);else if(r&&s&&t){let o=u.default.isAbsolute(t)?t:u.default.resolve(t),a=u.default.relative(s,o).split(u.default.sep).join("/"),d=r.layers?.find(f=>f.name===h(a,r.layers));d?.forbiddenGlobals?.length?(l=new Set(d.forbiddenGlobals),c=d.name):l=null}if(!l)return{};let p=typeof H(e)?.getScope=="function",g=(o,a)=>{let d=u.default.isAbsolute(t)?t:u.default.resolve(t),f=s?u.default.relative(s,d).split(u.default.sep).join("/"):t;w(e,o,r?"forbiddenGlobal":"forbiddenGlobalDefault",{ruleId:"FORBIDDEN_GLOBAL",file:f,fromLayer:c,target:a,message:`${c} must not use the ambient global "${a}".`},{name:a,layer:c})};return{MemberExpression(o){if(o.parent?.type==="MemberExpression"&&o.parent.object===o)return;let a=ce(o);if(!a||G(e,a.root,a.segments[0]))return;let d=a.segments[0]==="globalThis",f=d?a.segments.slice(1):a.segments,y;for(let $=f.length;$>=(d?1:2);$-=1){let K=f.slice(0,$).join(".");if(l.has(K)){y=K;break}}y?g(o,y):!p&&l.has(a.segments[0])&&g(o,a.segments[0])},CallExpression(o){if(p)return;let a=o.callee?.type==="Identifier"?o.callee.name:void 0;a&&l.has(a)&&g(o,a)},NewExpression(o){if(p)return;let a=o.callee?.type==="Identifier"?o.callee.name:void 0;a&&l.has(a)&&g(o,a)},Identifier(o){!p||!o.name||!l.has(o.name)||!De(e,o)||G(e,o,o.name)||g(o,o.name)}}}},ye={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=I(e),n=L(t),i=n?_(n):null,r=n?u.default.dirname(n):null;if(!i||!r||!t)return{};let s=u.default.isAbsolute(t)?t:u.default.resolve(t),l=u.default.relative(r,s).split(u.default.sep).join("/"),c=i.layers?.find(o=>o.name===h(l,i.layers));if(!c)return{};let p=new Set(X(c));if(p.size===0)return{};let g=(o,a,d)=>{if(d||typeof a!="string")return;let f=Z(a);!f||!p.has(f)||w(e,o,"deniedCapability",{ruleId:"CAPABILITY_VIOLATION",file:l,fromLayer:c.name,target:a,capability:f,message:`${c.name} denies the ${f} capability; found import of "${a}".`},{layer:c.name,capability:f,specifier:a})};return{ImportDeclaration(o){let a=o,d=(a.specifiers??[]).filter(y=>y.type==="ImportSpecifier"),f=d.length>0&&d.length===(a.specifiers??[]).length&&d.every(y=>y.importKind==="type");g(o,a.source?.value,a.importKind==="type"||f)},ImportExpression(o){let a=o;a.source?.type==="Literal"&&g(o,a.source.value,!1)},ExportNamedDeclaration(o){let a=o;if(!a.source)return;let d=a.specifiers??[],f=d.length>0&&d.every(y=>y.exportKind==="type");g(o,a.source.value,a.exportKind==="type"||f)},ExportAllDeclaration(o){let a=o;g(o,a.source?.value,a.exportKind==="type")},CallExpression(o){let a=o;a.callee?.type==="Identifier"&&a.callee.name==="require"&&a.arguments?.[0]?.type==="Literal"&&!G(e,o,"require")&&g(o,a.arguments[0].value,!1)}}}},ve={"no-domain-infra-imports":fe,"no-raw-event-publish":pe,"require-publish-source":ge,"no-forbidden-globals":me,"no-denied-capabilities":ye},N={rules:ve};N.configs={recommended:{plugins:{ark:N},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 Ge=N;0&&(module.exports={findConfigPath,globToRegExp,isEdgeDenied,layerForRelativePath,loadArkConfig,noDeniedCapabilities,noDomainInfraImports,noForbiddenGlobals,noRawEventPublish,patternSpecificity,plugin,requirePublishSource,resolveRelativeImport});
|
package/dist/eslint/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as ArkConfig } from '../
|
|
1
|
+
import { A as ArkConfig } from '../configTypes-DAPvBqK6.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Pure layer-glob matching for ark.config.json.
|
|
@@ -138,8 +138,9 @@ declare const noDomainInfraImports: ArkRule;
|
|
|
138
138
|
declare const noRawEventPublish: ArkRule;
|
|
139
139
|
declare const requirePublishSource: ArkRule;
|
|
140
140
|
declare const noForbiddenGlobals: ArkRule;
|
|
141
|
+
declare const noDeniedCapabilities: ArkRule;
|
|
141
142
|
declare const plugin: ArkEslintPlugin;
|
|
142
143
|
|
|
143
144
|
// @ts-ignore
|
|
144
145
|
export = plugin;
|
|
145
|
-
export { findConfigPath, globToRegExp, isEdgeDenied, layerForRelativePath, loadArkConfig, noDomainInfraImports, noForbiddenGlobals, noRawEventPublish, patternSpecificity, plugin, requirePublishSource, resolveRelativeImport };
|
|
146
|
+
export { findConfigPath, globToRegExp, isEdgeDenied, layerForRelativePath, loadArkConfig, noDeniedCapabilities, noDomainInfraImports, noForbiddenGlobals, noRawEventPublish, patternSpecificity, plugin, requirePublishSource, resolveRelativeImport };
|
package/dist/eslint/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as ArkConfig } from '../
|
|
1
|
+
import { A as ArkConfig } from '../configTypes-DAPvBqK6.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Pure layer-glob matching for ark.config.json.
|
|
@@ -138,6 +138,7 @@ declare const noDomainInfraImports: ArkRule;
|
|
|
138
138
|
declare const noRawEventPublish: ArkRule;
|
|
139
139
|
declare const requirePublishSource: ArkRule;
|
|
140
140
|
declare const noForbiddenGlobals: ArkRule;
|
|
141
|
+
declare const noDeniedCapabilities: ArkRule;
|
|
141
142
|
declare const plugin: ArkEslintPlugin;
|
|
142
143
|
|
|
143
|
-
export { plugin as default, findConfigPath, globToRegExp, isEdgeDenied, layerForRelativePath, loadArkConfig, noDomainInfraImports, noForbiddenGlobals, noRawEventPublish, patternSpecificity, plugin, requirePublishSource, resolveRelativeImport };
|
|
144
|
+
export { plugin as default, findConfigPath, globToRegExp, isEdgeDenied, layerForRelativePath, loadArkConfig, noDeniedCapabilities, noDomainInfraImports, noForbiddenGlobals, noRawEventPublish, patternSpecificity, plugin, requirePublishSource, resolveRelativeImport };
|
package/dist/eslint/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
${
|
|
3
|
-
`)}`),this.name="ArkConfigValidationError",this.source=n,this.issues=t}};function W(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function _(e,n){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(n)?`${e}.${n}`:`${e}[${JSON.stringify(n)}]`}function b(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function ue(e,n){let t="#/$defs/";if(e.startsWith(t))return n.$defs[e.slice(t.length)]}function k(e,n,t,i,r){if(n.$ref){let o=ue(n.$ref,i);if(!o){r.push({path:t,message:`schema reference ${n.$ref} cannot be resolved`});return}k(e,o,t,i,r);return}if(n.const!==void 0&&!Object.is(e,n.const)){r.push({path:t,message:`must equal ${JSON.stringify(n.const)}`});return}if(n.enum&&!n.enum.some(o=>Object.is(o,e))){r.push({path:t,message:`must be one of ${n.enum.map(String).join(", ")}`});return}if(n.type==="object"){if(!W(e)){r.push({path:t,message:`must be an object; received ${b(e)}`});return}let o=n.properties??{};for(let s of n.required??[])e[s]===void 0&&r.push({path:_(t,s),message:"is required"});if(n.additionalProperties===!1)for(let s of Object.keys(e))s in o||r.push({path:_(t,s),message:"unknown field"});for(let[s,a]of Object.entries(o))e[s]!==void 0&&k(e[s],a,_(t,s),i,r);return}if(n.type==="array"){if(!Array.isArray(e)){r.push({path:t,message:`must be an array; received ${b(e)}`});return}if(n.minItems!==void 0&&e.length<n.minItems&&r.push({path:t,message:`must contain at least ${n.minItems} item(s)`}),n.uniqueItems){let o=e.map(s=>JSON.stringify(s));new Set(o).size!==o.length&&r.push({path:t,message:"must not contain duplicate items"})}n.items&&e.forEach((o,s)=>k(o,n.items,`${t}[${s}]`,i,r));return}if(n.type==="string"){if(typeof e!="string"){r.push({path:t,message:`must be a string; received ${b(e)}`});return}n.minLength!==void 0&&e.length<n.minLength&&r.push({path:t,message:`must contain at least ${n.minLength} character(s)`});return}if(n.type==="boolean"){typeof e!="boolean"&&r.push({path:t,message:`must be a boolean; received ${b(e)}`});return}if(n.type==="integer"){if(!Number.isInteger(e)){r.push({path:t,message:`must be an integer; received ${b(e)}`});return}n.minimum!==void 0&&e<n.minimum&&r.push({path:t,message:`must be at least ${n.minimum}`})}}function de(e){return{...e,$schema:e.$schema===void 0?O: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?K.map(n=>({...n})):e.rules}}function fe(e,n="ark.config.json"){if(!W(e))throw new h(n,[{path:"$",message:`must be an object; received ${b(e)}`}]);let t=e.schemaVersion===void 0?"unversioned":null;if(e.schemaVersion!==void 0&&e.schemaVersion!=="1.0")throw new h(n,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(e.schemaVersion)}; expected 1.0`}]);return{candidate:de(e),migratedFrom:t}}function pe(e,n="ark.config.json"){let{candidate:t,migratedFrom:i}=fe(e,n),r=[];if(k(t,B,"$",B,r),r.length>0)throw new h(n,r);return{config:t,migratedFrom:i}}function q(e,n="ark.config.json"){let t;try{t=JSON.parse(e)}catch(i){throw new h(n,[{path:"$",message:`invalid JSON: ${i instanceof Error?i.message:String(i)}`}])}return pe(t,n)}function m(e){return typeof e=="string"&&e.length>0?e:void 0}function J(e,n){return Number.isInteger(e)&&Number(e)>0?Number(e):n}function ge(e,n,t){return e==="LAYER_IMPORT_VIOLATION"?n.typeOnly||t.targetTypeOnlyExports===!0||t.namedBindingsTypeOnly===!0?"Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.":t.peerIsolation===!0?"Extract the shared dependency to a shared layer, then preflight again.":`Define a port in ${n.fromLayer??"the source layer"}, inject the ${n.toLayer??"outer-layer"} implementation, then preflight again.`:e==="FORBIDDEN_GLOBAL"?`Inject ${n.target??"the capability"} through a port, then preflight again.`:e==="CIRCULAR_DEPENDENCY"?"Extract the shared dependency into a third module, then preflight again.":e==="RAW_EVENT_PUBLISH"?"Publish through a registered intent creator, then run Ark again.":e==="PUBLISH_MISSING_SOURCE"?"Add metadata.source to the publish call, then run Ark again.":`Resolve ${e} without weakening ark.config.json, then run Ark again.`}function Y(e,n="error"){let t=m(e.ruleId)??m(e.code)??"ARK_UNKNOWN",i=e.severity==="warning"?"warning":n,r={...m(e.target)?{target:m(e.target)}:{},...m(e.fromLayer)?{fromLayer:m(e.fromLayer)}:{},...m(e.toLayer)?{toLayer:m(e.toLayer)}:{},...typeof e.typeOnly=="boolean"?{typeOnly:e.typeOnly}:{}};return{ruleId:t,severity:i,message:m(e.message)??t,location:{file:m(e.file)??"<unknown>",line:J(e.line,1),column:J(e.column,1)},evidence:r,nextAction:m(e.nextAction)??ge(t,r,e)}}var z={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 me(e){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function $(e){if(!e.publishCall)return[];let n=[];return(e.rawIntentName!==void 0&&me(e.rawIntentName)||e.objectHasIntent)&&n.push({ruleId:"RAW_EVENT_PUBLISH",message:z.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&n.push({ruleId:"PUBLISH_MISSING_SOURCE",message:z.PUBLISH_MISSING_SOURCE}),n}function R(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 n=e.getFilename();if(typeof n=="string"&&n.length>0)return n}catch{}return""}function C(e,n,t,i,r){let o=Y({...i,line:i.line??n.loc?.start?.line,column:i.column??(typeof n.loc?.start?.column=="number"?n.loc.start.column+1:void 0)});return e.report({node:n,messageId:t,...r?{data:r}:{},diagnostic:o}),o}function Q(e){if(!e||e==="<input>"||e.startsWith("stdin"))return null;let n=u.dirname(u.resolve(e));for(;;){let t=u.join(n,"ark.config.json");if(A.existsSync(t))return t;let i=u.dirname(n);if(i===n)return null;n=i}}var P=new Map;function X(e){if(P.has(e))return P.get(e)??null;if(!A.existsSync(e))return null;let n=q(A.readFileSync(e,"utf8"),e).config;return P.set(e,n),n}function ye(e,n){if(!n.startsWith("."))return null;let t=u.resolve(u.dirname(e),n),i=[t,`${t}.ts`,`${t}.tsx`,`${t}.mts`,`${t}.cts`,`${t}.js`,`${t}.jsx`,u.join(t,"index.ts"),u.join(t,"index.tsx"),u.join(t,"index.js")];for(let r of i)try{if(A.existsSync(r)&&A.statSync(r).isFile())return r}catch{}return`${t}.ts`}function E(e){return typeof e?.value=="string"?e.value:void 0}function F(e){return e?.name??E(e)}function V(e){return e.sourceCode??e.getSourceCode?.()}function ee(e,n){let t=V(e)?.getScope?.(n);for(;t;){let i=t.references?.find(r=>r.identifier===n);if(i)return i;t=t.upper??void 0}}function Z(e,n,t){let i=ee(e,n);if(i?.resolved)return(i.resolved.defs?.length??0)>0;let r=V(e)?.getScope?.(n);for(;r;){let o=r.set?.get(t);if(o)return(o.defs?.length??0)>0;r=r.upper??void 0}return!1}function be(e,n){let t=ee(e,n);return t?t.isValueReference!==!1:n.parent?.type==="VariableDeclarator"&&n.parent.init===n}function ne(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 t=ne(e.object),i=F(e.property);if(!(!t||!i))return{root:t.root,segments:[...t.segments,i]}}function he(e){return F(e.callee?.property)}function te(e,n){return e?.properties?.find(t=>F(t.key)===n)}function I(e,n){return te(e,n)!==void 0}function Ae(e){let n=te(e,"metadata")?.value;return I(n,"source")}function re(e){return he(e)==="publish"}var Se={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 n=R(e),t=Q(n),i=t?X(t):null,r=t?u.dirname(t):null,o=s=>{let a=E(s.source);if(a&&i&&r&&n){let d=u.isAbsolute(n)?n:u.resolve(n),p=u.relative(r,d).split(u.sep).join("/"),l=S(p,i.layers);if(!l)return;let c=ye(d,a);if(!c)return;let f=u.relative(r,c).split(u.sep).join("/");if(f.startsWith(".."))return;let g=S(f,i.layers);if(!g)return;v(i.rules,l,g,{fromPath:p,toPath:f,layers:i.layers})&&C(e,s,"forbiddenImport",{ruleId:"LAYER_IMPORT_VIOLATION",file:p,fromLayer:l,toLayer:g,target:f,...s.importKind==="type"?{typeOnly:!0}:{},message:`${l} must not import ${g}.`},{fromLayer:l,toLayer:g,specifier:a});return}};return{ImportDeclaration:o,ExportNamedDeclaration:o,ExportAllDeclaration:o}}},ke={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(n){let t=n.arguments?.[0],i=E(t),r=$({publishCall:re(n),rawIntentName:i,objectHasIntent:I(t,"intent"),arkPublishCandidate:!1,hasSource:!0});if(r.some(o=>o.ruleId==="RAW_EVENT_PUBLISH")){let o=r.find(s=>s.ruleId==="RAW_EVENT_PUBLISH");C(e,n,"rawPublish",{...o,file:R(e)})}}}}},Ie={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(n){let t=n.arguments?.[0],i=n.arguments?.[2],o=$({publishCall:re(n),rawIntentName:E(t),objectHasIntent:I(t,"intent"),arkPublishCandidate:!0,hasSource:Ae(t)||I(i,"source")}).find(s=>s.ruleId==="PUBLISH_MISSING_SOURCE");o&&C(e,n,"missingSource",{...o,file:R(e)})}}}},Re={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` explicitly overrides."},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.'},schema:[{type:"object",properties:{globals:{type:"array",items:{type:"string"}}},additionalProperties:!1}]},create(e){let n=R(e),t=e.options?.[0],i=Q(n),r=i?X(i):null,o=i?u.dirname(i):null,s=null,a="this layer";if(t?.globals)s=new Set(t.globals);else if(r&&o&&n){let l=u.isAbsolute(n)?n:u.resolve(n),c=u.relative(o,l).split(u.sep).join("/"),f=r.layers?.find(g=>g.name===S(c,r.layers));f?.forbiddenGlobals?.length?(s=new Set(f.forbiddenGlobals),a=f.name):s=null}if(!s)return{};let d=typeof V(e)?.getScope=="function",p=(l,c)=>{let f=u.isAbsolute(n)?n:u.resolve(n),g=o?u.relative(o,f).split(u.sep).join("/"):n;C(e,l,r?"forbiddenGlobal":"forbiddenGlobalDefault",{ruleId:"FORBIDDEN_GLOBAL",file:g,fromLayer:a,target:c,message:`${a} must not use the ambient global "${c}".`},{name:c,layer:a})};return{MemberExpression(l){if(l.parent?.type==="MemberExpression"&&l.parent.object===l)return;let c=ne(l);if(!c||Z(e,c.root,c.segments[0]))return;let f=c.segments[0]==="globalThis",g=f?c.segments.slice(1):c.segments,x;for(let N=g.length;N>=(f?1:2);N-=1){let M=g.slice(0,N).join(".");if(s.has(M)){x=M;break}}x?p(l,x):!d&&s.has(c.segments[0])&&p(l,c.segments[0])},CallExpression(l){if(d)return;let c=l.callee?.type==="Identifier"?l.callee.name:void 0;c&&s.has(c)&&p(l,c)},NewExpression(l){if(d)return;let c=l.callee?.type==="Identifier"?l.callee.name:void 0;c&&s.has(c)&&p(l,c)},Identifier(l){!d||!l.name||!s.has(l.name)||!be(e,l)||Z(e,l,l.name)||p(l,l.name)}}}},Ce={"no-domain-infra-imports":Se,"no-raw-event-publish":ke,"require-publish-source":Ie,"no-forbidden-globals":Re},j={rules:Ce};j.configs={recommended:{plugins:{ark:j},rules:{"ark/no-domain-infra-imports":"error","ark/no-raw-event-publish":"error","ark/require-publish-source":"error","ark/no-forbidden-globals":"error"}}};var Fe=j;export{Fe as default,Q as findConfigPath,w as globToRegExp,v as isEdgeDenied,S as layerForRelativePath,X as loadArkConfig,Se as noDomainInfraImports,Re as noForbiddenGlobals,ke as noRawEventPublish,H as patternSpecificity,j as plugin,Ie as requirePublishSource,ye as resolveRelativeImport};
|
|
1
|
+
import S from"fs";import u from"path";var B=new Map;function H(e){return/[.*+?^${}()|[\]\\]/.test(e)?`\\${e}`:e}function L(e){let t="";for(let n=0;n<e.length;n+=1){let i=e[n];if(i==="\\"&&n+1<e.length){let r=e[n+1];if("*?{}[],".includes(r)||r==="\\"){t+="\\"+r,n+=1;continue}t+="/";continue}t+=i}return t}function le(e){let t=0;for(let n=0;n<e.length;n+=1){let i=e[n];if(i==="\\"){n+=1;continue}if(i==="{")t+=1;else if(i==="}"&&(t-=1,t<0))return!1}return t===0}function N(e){let t=B.get(e);if(t)return t;let n=L(e),i=le(n),r="",s=0;for(let c=0;c<n.length;c+=1){let p=n[c];p==="\\"&&c+1<n.length?(r+=H(n[c+1]),c+=1):p==="*"?n[c+1]==="*"?n[c+2]==="/"?(r+="(?:.*/)?",c+=2):(r+=".*",c+=1):r+="[^/]*":p==="?"?r+="[^/]":p==="{"&&i?(r+="(?:",s+=1):p==="}"&&i&&s>0?(r+=")",s-=1):p===","&&i&&s>0?r+="|":r+=H(p)}let l=new RegExp(`^${r}$`);return B.set(e,l),l}function U(e){let t=L(String(e)),i=t.split("*")[0].split("/").filter(Boolean).length,r=t.replace(/\*/g,"").length;return i*1e4+r}function k(e,t){let n=String(e).split(/[/\\]/).join("/"),i,r=-1;for(let s of t??[])if(!(s.exclude??[]).some(l=>N(l).test(n))){for(let l of s.patterns??[])if(N(l).test(n)){let c=U(l);c>r&&(r=c,i=s.name)}}return i}function K(e,t){if(!t?.length)return;let n=String(e).split(/[/\\]/).filter(Boolean),i=new Set(t.map(r=>String(r).toLowerCase()));for(let r=0;r<n.length-1;r+=1)if(i.has(n[r].toLowerCase()))return`${n[r]}/${n[r+1]}`}function ce(e){let t=new Set;for(let n of e??[]){let r=L(String(n)).split("/").filter(Boolean);for(let s=0;s<r.length;s+=1){let l=r[s];if((l==="**"||l==="*")&&s>0){let c=r[s-1];c&&!c.includes("*")&&!c.includes("{")&&!c.includes("}")&&t.add(c)}}}return[...t]}function ue(e,t,n){if(Array.isArray(e.sliceFolders)&&e.sliceFolders.length>0)return e.sliceFolders.filter(r=>typeof r=="string"&&r.length>0);let i=(n??[]).find(r=>r.name===t);return ce(i?.patterns)}function de(e,t,n,i){for(let r of e??[])if(!(r.from!==t||r.to!==n)&&r.allowed===!1){if(r.peerIsolation){let s=i?.fromPath,l=i?.toPath;if(!s||!l)continue;let c=ue(r,t,i?.layers);if(c.length===0)continue;let p=K(s,c),g=K(l,c);if(!p||!g)continue;if(p!==g)return r;continue}if(t!==n)return r}}function q(e,t,n,i){return de(e,t,n,i)!==void 0}var W=Object.freeze(["network","filesystem","clock","randomness","environment","process","persistence"]),fe=Object.freeze({fetch:"network",XMLHttpRequest:"network",Date:"clock","Date.now":"clock","Math.random":"randomness","process.env":"environment",process:"process"}),$e=Object.freeze(Object.keys(fe).sort()),_=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 Y(e){if(!e||e.startsWith(".")||e.startsWith("/"))return null;let t=_[e];if(t)return t;let n=e.indexOf("/");if(n<0)return null;let i=e.slice(0,n),r=_[i];if(r)return r;let s=e.indexOf("/",n+1);return s<0?null:_[e.slice(0,s)]??null}function z(e){if(e?.pure===!0)return[...W].sort();let n=(e?.capabilities?.deny??[]).filter(i=>W.includes(i));return[...new Set(n)].sort()}var $="https://unpkg.com/arkgate@2/schemas/ark.config.schema.json",J=["DomainModel","ApplicationOrchestration","PersistenceAdapters","IntegrationAdapters","WorkflowSagaEngine","BackgroundJobsScheduling","PresentationAdapters","ReportingReadModels","ExtensibilityMetadata","SecurityAuditObservability","Kernel"],pe=new Set(["PresentationAdapters->ApplicationOrchestration","ApplicationOrchestration->DomainModel","WorkflowSagaEngine->ApplicationOrchestration","WorkflowSagaEngine->DomainModel","BackgroundJobsScheduling->ApplicationOrchestration"]);function ge(){let e=[];for(let t of J)for(let n of J)t===n||pe.has(`${t}->${n}`)||e.push({from:t,to:n,allowed:!1});return e}var X=ge();var b={type:"array",items:{type:"string",minLength:1},uniqueItems:!0},Z={$schema:"https://json-schema.org/draft/2020-12/schema",$id:$,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:$,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:{...b,minItems:1,default:["src"]},exclude:{...b,default:[]},excludeGenerated:{type:"boolean",default:!0},frameworkOverlay:{type:"string",minLength:1},layers:{type:"array",default:[],items:{$ref:"#/$defs/layer"}},rules:{type:"array",default:X,items:{$ref:"#/$defs/rule"}},cyclePolicy:{type:"string",enum:["strict","soft","framework-soft","off"],default:"strict"},dynamicImportAllowlist:{...b,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:{...b,minItems:1},exclude:b,intentPrefixes:b,description:{type:"string",minLength:1},forbiddenGlobals:b,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:{...b,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}}}}},A=class extends Error{issues;source;constructor(t,n){super(`Invalid ArkGate config (${t}):
|
|
2
|
+
${n.map(i=>`- ${i.path}: ${i.message}`).join(`
|
|
3
|
+
`)}`),this.name="ArkConfigValidationError",this.source=t,this.issues=n}};function Q(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 h(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function me(e,t){let n="#/$defs/";if(e.startsWith(n))return t.$defs[e.slice(n.length)]}function C(e,t,n,i,r){if(t.$ref){let s=me(t.$ref,i);if(!s){r.push({path:n,message:`schema reference ${t.$ref} cannot be resolved`});return}C(e,s,n,i,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(s=>Object.is(s,e))){r.push({path:n,message:`must be one of ${t.enum.map(String).join(", ")}`});return}if(t.type==="object"){if(!Q(e)){r.push({path:n,message:`must be an object; received ${h(e)}`});return}let s=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 s||r.push({path:O(n,l),message:"unknown field"});for(let[l,c]of Object.entries(s))e[l]!==void 0&&C(e[l],c,O(n,l),i,r);return}if(t.type==="array"){if(!Array.isArray(e)){r.push({path:n,message:`must be an array; received ${h(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 s=e.map(l=>JSON.stringify(l));new Set(s).size!==s.length&&r.push({path:n,message:"must not contain duplicate items"})}t.items&&e.forEach((s,l)=>C(s,t.items,`${n}[${l}]`,i,r));return}if(t.type==="string"){if(typeof e!="string"){r.push({path:n,message:`must be a string; received ${h(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 ${h(e)}`});return}if(t.type==="integer"){if(!Number.isInteger(e)){r.push({path:n,message:`must be an integer; received ${h(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?$: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?X.map(t=>({...t})):e.rules}}function be(e,t="ark.config.json"){if(!Q(e))throw new A(t,[{path:"$",message:`must be an object; received ${h(e)}`}]);let n=e.schemaVersion===void 0?"unversioned":null;if(e.schemaVersion!==void 0&&e.schemaVersion!=="1.0")throw new A(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(e.schemaVersion)}; expected 1.0`}]);return{candidate:ye(e),migratedFrom:n}}function he(e,t="ark.config.json"){let{candidate:n,migratedFrom:i}=be(e,t),r=[];if(C(n,Z,"$",Z,r),r.length>0)throw new A(t,r);return{config:n,migratedFrom:i}}function ee(e,t="ark.config.json"){let n;try{n=JSON.parse(e)}catch(i){throw new A(t,[{path:"$",message:`invalid JSON: ${i instanceof Error?i.message:String(i)}`}])}return he(n,t)}function m(e){return typeof e=="string"&&e.length>0?e:void 0}function te(e,t){return Number.isInteger(e)&&Number(e)>0?Number(e):t}function Ae(e,t,n){return e==="LAYER_IMPORT_VIOLATION"?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.`:e==="FORBIDDEN_GLOBAL"?`Inject ${t.target??"the capability"} through a port, then preflight again.`:e==="CAPABILITY_VIOLATION"?`Define a ${m(n.capability)??"capability"} port in ${t.fromLayer??"the walled layer"}, bind the implementation outside it, then preflight again.`:e==="CIRCULAR_DEPENDENCY"?"Extract the shared dependency into a third module, then preflight again.":e==="RAW_EVENT_PUBLISH"?"Publish through a registered intent creator, then run Ark again.":e==="PUBLISH_MISSING_SOURCE"?"Add metadata.source to the publish call, then run Ark again.":`Resolve ${e} without weakening ark.config.json, then run Ark again.`}function ne(e,t="error"){let n=m(e.ruleId)??m(e.code)??"ARK_UNKNOWN",i=e.severity==="warning"?"warning":t,r={...m(e.target)?{target:m(e.target)}:{},...m(e.fromLayer)?{fromLayer:m(e.fromLayer)}:{},...m(e.toLayer)?{toLayer:m(e.toLayer)}:{},...typeof e.typeOnly=="boolean"?{typeOnly:e.typeOnly}:{}};return{ruleId:n,severity:i,message:m(e.message)??n,location:{file:m(e.file)??"<unknown>",line:te(e.line,1),column:te(e.column,1)},evidence:r,nextAction:m(e.nextAction)??Ae(n,r,e)}}var re={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 ke(e){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function P(e){if(!e.publishCall)return[];let t=[];return(e.rawIntentName!==void 0&&ke(e.rawIntentName)||e.objectHasIntent)&&t.push({ruleId:"RAW_EVENT_PUBLISH",message:re.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&t.push({ruleId:"PUBLISH_MISSING_SOURCE",message:re.PUBLISH_MISSING_SOURCE}),t}function I(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,i,r){let s=ne({...i,line:i.line??t.loc?.start?.line,column:i.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:s}),s}function D(e){if(!e||e==="<input>"||e.startsWith("stdin"))return null;let t=u.dirname(u.resolve(e));for(;;){let n=u.join(t,"ark.config.json");if(S.existsSync(n))return n;let i=u.dirname(t);if(i===t)return null;t=i}}var j=new Map;function M(e){if(j.has(e))return j.get(e)??null;if(!S.existsSync(e))return null;let t=ee(S.readFileSync(e,"utf8"),e).config;return j.set(e,t),t}function Se(e,t){if(!t.startsWith("."))return null;let n=u.resolve(u.dirname(e),t),i=[n,`${n}.ts`,`${n}.tsx`,`${n}.mts`,`${n}.cts`,`${n}.js`,`${n}.jsx`,u.join(n,"index.ts"),u.join(n,"index.tsx"),u.join(n,"index.js")];for(let r of i)try{if(S.existsSync(r)&&S.statSync(r).isFile())return r}catch{}return`${n}.ts`}function x(e){return typeof e?.value=="string"?e.value:void 0}function T(e){return e?.name??x(e)}function v(e){return e.sourceCode??e.getSourceCode?.()}function ie(e,t){let n=v(e)?.getScope?.(t);for(;n;){let i=n.references?.find(r=>r.identifier===t);if(i)return i;n=n.upper??void 0}}function F(e,t,n){let i=ie(e,t);if(i?.resolved)return(i.resolved.defs?.length??0)>0;let r=v(e)?.getScope?.(t);for(;r;){let s=r.set?.get(n);if(s)return(s.defs?.length??0)>0;r=r.upper??void 0}return!1}function Ie(e,t){let n=ie(e,t);return n?n.isValueReference!==!1:t.parent?.type==="VariableDeclarator"&&t.parent.init===t}function se(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=se(e.object),i=T(e.property);if(!(!n||!i))return{root:n.root,segments:[...n.segments,i]}}function we(e){return T(e.callee?.property)}function oe(e,t){return e?.properties?.find(n=>T(n.key)===t)}function R(e,t){return oe(e,t)!==void 0}function Ce(e){let t=oe(e,"metadata")?.value;return R(t,"source")}function ae(e){return we(e)==="publish"}var Re={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=I(e),n=D(t),i=n?M(n):null,r=n?u.dirname(n):null,s=l=>{let c=x(l.source);if(c&&i&&r&&t){let p=u.isAbsolute(t)?t:u.resolve(t),g=u.relative(r,p).split(u.sep).join("/"),o=k(g,i.layers);if(!o)return;let a=Se(p,c);if(!a)return;let d=u.relative(r,a).split(u.sep).join("/");if(d.startsWith(".."))return;let f=k(d,i.layers);if(!f)return;q(i.rules,o,f,{fromPath:g,toPath:d,layers:i.layers})&&w(e,l,"forbiddenImport",{ruleId:"LAYER_IMPORT_VIOLATION",file:g,fromLayer:o,toLayer:f,target:d,...l.importKind==="type"?{typeOnly:!0}:{},message:`${o} must not import ${f}.`},{fromLayer:o,toLayer:f,specifier:c});return}};return{ImportDeclaration:s,ExportNamedDeclaration:s,ExportAllDeclaration:s}}},xe={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],i=x(n),r=P({publishCall:ae(t),rawIntentName:i,objectHasIntent:R(n,"intent"),arkPublishCandidate:!1,hasSource:!0});if(r.some(s=>s.ruleId==="RAW_EVENT_PUBLISH")){let s=r.find(l=>l.ruleId==="RAW_EVENT_PUBLISH");w(e,t,"rawPublish",{...s,file:I(e)})}}}}},Ee={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],i=t.arguments?.[2],s=P({publishCall:ae(t),rawIntentName:x(n),objectHasIntent:R(n,"intent"),arkPublishCandidate:!0,hasSource:Ce(n)||R(i,"source")}).find(l=>l.ruleId==="PUBLISH_MISSING_SOURCE");s&&w(e,t,"missingSource",{...s,file:I(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` explicitly overrides."},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.'},schema:[{type:"object",properties:{globals:{type:"array",items:{type:"string"}}},additionalProperties:!1}]},create(e){let t=I(e),n=e.options?.[0],i=D(t),r=i?M(i):null,s=i?u.dirname(i):null,l=null,c="this layer";if(n?.globals)l=new Set(n.globals);else if(r&&s&&t){let o=u.isAbsolute(t)?t:u.resolve(t),a=u.relative(s,o).split(u.sep).join("/"),d=r.layers?.find(f=>f.name===k(a,r.layers));d?.forbiddenGlobals?.length?(l=new Set(d.forbiddenGlobals),c=d.name):l=null}if(!l)return{};let p=typeof v(e)?.getScope=="function",g=(o,a)=>{let d=u.isAbsolute(t)?t:u.resolve(t),f=s?u.relative(s,d).split(u.sep).join("/"):t;w(e,o,r?"forbiddenGlobal":"forbiddenGlobalDefault",{ruleId:"FORBIDDEN_GLOBAL",file:f,fromLayer:c,target:a,message:`${c} must not use the ambient global "${a}".`},{name:a,layer:c})};return{MemberExpression(o){if(o.parent?.type==="MemberExpression"&&o.parent.object===o)return;let a=se(o);if(!a||F(e,a.root,a.segments[0]))return;let d=a.segments[0]==="globalThis",f=d?a.segments.slice(1):a.segments,y;for(let E=f.length;E>=(d?1:2);E-=1){let G=f.slice(0,E).join(".");if(l.has(G)){y=G;break}}y?g(o,y):!p&&l.has(a.segments[0])&&g(o,a.segments[0])},CallExpression(o){if(p)return;let a=o.callee?.type==="Identifier"?o.callee.name:void 0;a&&l.has(a)&&g(o,a)},NewExpression(o){if(p)return;let a=o.callee?.type==="Identifier"?o.callee.name:void 0;a&&l.has(a)&&g(o,a)},Identifier(o){!p||!o.name||!l.has(o.name)||!Ie(e,o)||F(e,o,o.name)||g(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=I(e),n=D(t),i=n?M(n):null,r=n?u.dirname(n):null;if(!i||!r||!t)return{};let s=u.isAbsolute(t)?t:u.resolve(t),l=u.relative(r,s).split(u.sep).join("/"),c=i.layers?.find(o=>o.name===k(l,i.layers));if(!c)return{};let p=new Set(z(c));if(p.size===0)return{};let g=(o,a,d)=>{if(d||typeof a!="string")return;let f=Y(a);!f||!p.has(f)||w(e,o,"deniedCapability",{ruleId:"CAPABILITY_VIOLATION",file:l,fromLayer:c.name,target:a,capability:f,message:`${c.name} denies the ${f} capability; found import of "${a}".`},{layer:c.name,capability:f,specifier:a})};return{ImportDeclaration(o){let a=o,d=(a.specifiers??[]).filter(y=>y.type==="ImportSpecifier"),f=d.length>0&&d.length===(a.specifiers??[]).length&&d.every(y=>y.importKind==="type");g(o,a.source?.value,a.importKind==="type"||f)},ImportExpression(o){let a=o;a.source?.type==="Literal"&&g(o,a.source.value,!1)},ExportNamedDeclaration(o){let a=o;if(!a.source)return;let d=a.specifiers??[],f=d.length>0&&d.every(y=>y.exportKind==="type");g(o,a.source.value,a.exportKind==="type"||f)},ExportAllDeclaration(o){let a=o;g(o,a.source?.value,a.exportKind==="type")},CallExpression(o){let a=o;a.callee?.type==="Identifier"&&a.callee.name==="require"&&a.arguments?.[0]?.type==="Literal"&&!F(e,o,"require")&&g(o,a.arguments[0].value,!1)}}}},_e={"no-domain-infra-imports":Re,"no-raw-event-publish":xe,"require-publish-source":Ee,"no-forbidden-globals":Ne,"no-denied-capabilities":Le},V={rules:_e};V.configs={recommended:{plugins:{ark:V},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 Ke=V;export{Ke as default,D as findConfigPath,N as globToRegExp,q as isEdgeDenied,k as layerForRelativePath,M as loadArkConfig,Le as noDeniedCapabilities,Re as noDomainInfraImports,Ne as noForbiddenGlobals,xe as noRawEventPublish,U as patternSpecificity,V as plugin,Ee as requirePublishSource,Se as resolveRelativeImport};
|