arkgate 2.12.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/README.md +90 -51
  3. package/bin/ark-check.mjs +156 -39
  4. package/bin/ark-mcp.mjs +119 -6
  5. package/bin/ark-shared.mjs +216 -129
  6. package/bin/ark.mjs +134 -34
  7. package/bin/lib/adapter-contract.mjs +93 -0
  8. package/bin/lib/agent-gates.mjs +13 -0
  9. package/bin/lib/analysis-engine.mjs +1171 -0
  10. package/bin/lib/architecture-scan.mjs +84 -127
  11. package/bin/lib/ci-and-commands.mjs +40 -3
  12. package/bin/lib/codex-home.mjs +7 -0
  13. package/bin/lib/config-contract.mjs +331 -0
  14. package/bin/lib/config-warnings.mjs +7 -205
  15. package/bin/lib/doctor-plan.mjs +43 -16
  16. package/bin/lib/enforcement-profiles.mjs +97 -0
  17. package/bin/lib/field-install.mjs +67 -10
  18. package/bin/lib/gate-files.mjs +42 -3
  19. package/bin/lib/graph-cycles.mjs +4 -54
  20. package/bin/lib/hook-templates.mjs +33 -1
  21. package/bin/lib/host-support-matrix.mjs +83 -0
  22. package/bin/lib/install-migrate.mjs +99 -30
  23. package/bin/lib/mcp-adoption.mjs +35 -3
  24. package/bin/lib/open-html.mjs +75 -0
  25. package/bin/lib/presets.mjs +45 -4
  26. package/bin/lib/safety-diagnostics.mjs +36 -15
  27. package/bin/lib/scan-files.mjs +12 -1
  28. package/bin/lib/skill-install.mjs +72 -1
  29. package/bin/lib/source-policy.mjs +36 -0
  30. package/bin/lib/start-preview.mjs +271 -0
  31. package/bin/lib/ts-resolve.mjs +13 -3
  32. package/bin/lib/weakest-link.mjs +417 -0
  33. package/bin/lib/write-path-capabilities.mjs +186 -0
  34. package/bin/lib/write-path-detect.mjs +62 -99
  35. package/compat/nestjs.cjs +2 -0
  36. package/compat/nestjs.d.ts +2 -0
  37. package/compat/nestjs.js +1 -0
  38. package/compat/runtime.cjs +2 -0
  39. package/compat/runtime.d.ts +2 -0
  40. package/compat/runtime.js +1 -0
  41. package/dist/configContract-BxSIwVRo.d.cts +259 -0
  42. package/dist/configContract-BxSIwVRo.d.ts +259 -0
  43. package/dist/eslint/index.cjs +500 -61
  44. package/dist/eslint/index.d.cts +36 -20
  45. package/dist/eslint/index.d.ts +36 -20
  46. package/dist/eslint/index.js +500 -61
  47. package/dist/index.cjs +1349 -2741
  48. package/dist/index.d.cts +449 -483
  49. package/dist/index.d.ts +449 -483
  50. package/dist/index.js +1325 -2687
  51. package/docs/agent-guide.md +58 -34
  52. package/docs/ai-gates.md +79 -21
  53. package/docs/configuration.md +97 -0
  54. package/docs/enthusiast/README.md +3 -3
  55. package/docs/enthusiast/how-to-agent-gates.md +7 -3
  56. package/docs/migrate-from-ark-runtime-kernel.md +5 -3
  57. package/docs/package-surface.md +19 -19
  58. package/docs/production-hardening.md +31 -5
  59. package/docs/threat-model.md +65 -0
  60. package/docs/typescript-support.md +30 -3
  61. package/package.json +46 -11
  62. package/schemas/ark.analysis-result.schema.json +91 -0
  63. package/schemas/ark.config.schema.json +750 -0
  64. package/server.json +2 -2
  65. package/templates/hooks/pre-commit-ark +37 -0
  66. package/templates/skills/ark-architect.md +3 -2
  67. package/templates/skills/ark-coverage.md +2 -2
  68. package/templates/skills/ark-runtime.md +8 -5
  69. package/templates/skills/ark-upgrade.md +36 -16
  70. package/tests/fixtures/ts-consumer/ark.config.json +2 -0
  71. package/dist/eslint/index.cjs.map +0 -1
  72. package/dist/eslint/index.js.map +0 -1
  73. package/dist/index.cjs.map +0 -1
  74. package/dist/index.js.map +0 -1
  75. package/dist/nestjs/index.cjs +0 -2498
  76. package/dist/nestjs/index.cjs.map +0 -1
  77. package/dist/nestjs/index.d.cts +0 -22
  78. package/dist/nestjs/index.d.ts +0 -22
  79. package/dist/nestjs/index.js +0 -2474
  80. package/dist/nestjs/index.js.map +0 -1
  81. package/dist/runtime/index.cjs +0 -3352
  82. package/dist/runtime/index.cjs.map +0 -1
  83. package/dist/runtime/index.d.cts +0 -2
  84. package/dist/runtime/index.d.ts +0 -2
  85. package/dist/runtime/index.js +0 -3270
  86. package/dist/runtime/index.js.map +0 -1
  87. package/dist/types-BZ17b9i5.d.cts +0 -1068
  88. package/dist/types-BZ17b9i5.d.ts +0 -1068
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/pedroknigge/arkgate",
7
7
  "source": "github"
8
8
  },
9
- "version": "2.12.0",
9
+ "version": "3.0.0",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "arkgate",
14
- "version": "2.12.0",
14
+ "version": "3.0.0",
15
15
  "runtimeHint": "npx",
16
16
  "transport": {
17
17
  "type": "stdio"
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env bash
2
+ # Maintained by ArkGate (Q3) — human-edit architecture gate.
3
+ # Install (git hooks):
4
+ # cp templates/hooks/pre-commit-ark .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
5
+ # Or with husky:
6
+ # cp templates/hooks/pre-commit-ark .husky/pre-commit && chmod +x .husky/pre-commit
7
+ #
8
+ # Blocks commit when ark-check fails. Does not replace CI branch protection.
9
+ set -euo pipefail
10
+
11
+ ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
12
+ cd "$ROOT"
13
+
14
+ run_check() {
15
+ if [ -f package.json ] && node -e "const p=require('./package.json');process.exit(p.scripts&&p.scripts['check:architecture']?0:1)" 2>/dev/null; then
16
+ npm run -s check:architecture
17
+ return $?
18
+ fi
19
+ if [ -f bin/ark-check.mjs ]; then
20
+ node bin/ark-check.mjs --root . --config ark.config.json --strict-config
21
+ return $?
22
+ fi
23
+ if command -v arkgate-check >/dev/null 2>&1; then
24
+ arkgate-check --root . --config ark.config.json --strict-config
25
+ return $?
26
+ fi
27
+ if command -v npx >/dev/null 2>&1; then
28
+ # --yes: pre-commit is non-interactive; never hang on npx install prompts
29
+ npx --yes --no-install arkgate-check --root . --config ark.config.json --strict-config 2>/dev/null \
30
+ || npx --yes arkgate-check --root . --config ark.config.json --strict-config
31
+ return $?
32
+ fi
33
+ echo "ark-check not found — install arkgate or add check:architecture script" >&2
34
+ return 1
35
+ }
36
+
37
+ run_check
@@ -44,8 +44,9 @@ the same files or weaken the gate.
44
44
 
45
45
  1. **Detect the shape** — call MCP tool **`ark_recommend`** (or run
46
46
  `ark-check --recommend --json`). Read `archetype`, `preset`, `confidence`,
47
- `adoptInOrder.phase1`, `analogy`, and `why`. Ask at most **two** questions only
48
- if `confidence < 0.5`:
47
+ `adoptInOrder.phase1`, `analogy`, `why`, `evidence`, and `requiresConfirmation`.
48
+ Ask at most **two** questions only if `requiresConfirmation` is true (or for compatibility
49
+ with older ArkGate output, `confidence < 0.5`):
49
50
  - "Will this app save data between sessions?"
50
51
  - "Is this one app or several in one repository?"
51
52
 
@@ -56,7 +56,7 @@ feature dirs, plan clusters), you **may** dispatch **subagents**:
56
56
  |------|----------------|--------------------|
57
57
  | **Suggest / Setup** | Thin or new tree | “Ark will propose a starting shape — you don’t switch a mode.” |
58
58
  | **Adapt / Align** | Contract ≠ folders or open debt | “Gates don’t fully protect you yet — classify + fix plan.” |
59
- | **Enforce / Guard** | Coverage + clean edges + honest cores | “You arrived here — keep CI/write gates on.” |
59
+ | **Enforce / Guard** | Coverage + clean edges + honest cores | “You arrived here — keep the host-appropriate write path and CI check on.” |
60
60
 
61
61
  Never say “your architecture is guarded” while `goal.met` is false, governed% is low,
62
62
  or false-green doctor gaps are open.
@@ -73,7 +73,7 @@ or false-green doctor gaps are open.
73
73
 
74
74
  1. Config + `ark-check --strict-config` (dead preset globs advisory; unclassified files still fail strict).
75
75
  2. Baseline policy (orphan empty file? wire or delete).
76
- 3. Write gates + `/ark-*` skills per detected agent.
76
+ 3. Host-appropriate write path + `/ark-*` skills per detected agent.
77
77
  4. CI workflow + monorepo install reality (`frontend/package.json`?).
78
78
  5. ESLint `arkgate/eslint` if ESLint exists.
79
79
  6. Domain `forbiddenGlobals`.
@@ -1,11 +1,14 @@
1
1
  ---
2
2
  name: ark-runtime
3
- description: Replace hand-rolled infra with the Ark runtime kernel event bus, outbox, audit, sagas, projections, policies, NestJS. Finds candidates, wires one, verifies.
3
+ description: Evaluate the experimental Ark runtime kernel against hand-rolled event bus, outbox, audit, saga, projection, policy, or NestJS code. Finds one candidate, wires one, verifies.
4
4
  ---
5
5
 
6
- # /ark-runtime — Adopt the runtime kernel (opt-in features)
6
+ # /ark-runtime — Evaluate the runtime kernel (experimental opt-in)
7
7
 
8
- `arkgate` is not just static checking: it ships a runtime kernel
8
+ The runtime kernel is currently **experimental** and is not required for ArkGate enforcement or
9
+ presented as production-ready. Use this skill only when the user explicitly wants to evaluate it.
10
+
11
+ `arkgate` ships the experimental runtime kernel
9
12
  (`createArkKernel`) with an event bus, event contracts, outbox, audit trail,
10
13
  policy engine, workflow/saga coordination, projections, observability hooks,
11
14
  and NestJS adapters. This skill migrates hand-rolled versions of those to the
@@ -43,12 +46,12 @@ the same files or weaken the gate.
43
46
  2. **Pick ONE target** — the smallest, most self-contained candidate (fewest
44
47
  call sites). Migrating everything at once is how adoptions die. List the
45
48
  rest as follow-ups in the report.
46
- 3. **Migrate** — import from `arkgate` (root export) or
49
+ 3. **Migrate** — import from `arkgate/runtime` (preferred experimental subpath) or
47
50
  `arkgate/nestjs`, and read the package's `docs/agent-guide.md`
48
51
  (in `node_modules/arkgate/docs/`) for the runtime API before
49
52
  writing code. Wire the kernel at the composition root; keep the domain
50
53
  ignorant of it (handlers/ports, not kernel imports inside domain code —
51
- the write gate will enforce this anyway). Note: the kernel bounds in-memory
54
+ the architecture check enforces this; Claude/Grok hooks can block it earlier). Note: the kernel bounds in-memory
52
55
  history by default (`maxHistorySize` 1000); mention this if the hand-rolled
53
56
  version retained everything.
54
57
  4. **Delete the hand-rolled version** once call sites are moved — the point is
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: ark-upgrade
3
- description: Update arkgate to the latest published version, then refresh gates and /ark-* skills for every agent CLI and re-verify the architecture check. Autonomous.
3
+ description: Update arkgate to the latest published version, then refresh gates and /ark-* skills for the active agent host (defer inactive hosts like Codex when not in use) and re-verify the architecture check. Autonomous.
4
4
  ---
5
5
 
6
6
  # /ark-upgrade — Update ArkGate and refresh its gates
@@ -38,6 +38,13 @@ read the **Adoption** section — host gaps, Codex home temp paths, optional-but
38
38
  core layers, missing origin snapshot, baseline policy. Fix commands are printed per gap.
39
39
  HTML reports include the same Adoption card (separate from the 0–100 fitness score).
40
40
 
41
+ **Active host vs deferred hosts:** green the **session host** (Grok, Claude, Cursor, …)
42
+ and repo gates first. Codex home (`$CODEX_HOME` prompts + `config.toml` MCP multi-project)
43
+ is **deferred** unless this session is Codex or the user asked to fix Codex. Doctor marks
44
+ those gaps `deferred` / info and does not put them in Top actions. A temp/upgrade MCP
45
+ `--root` stays urgent (fail-closed rewrite). Never set **Incomplete?** because of deferred
46
+ Codex debt.
47
+
41
48
  ## Dual engine (mandatory)
42
49
 
43
50
  | Engine | Role |
@@ -58,7 +65,8 @@ the same files or weaken the gate.
58
65
  ## Fast path
59
66
 
60
67
  One command does the whole flow — update the package, refresh gates + `/ark-*` skills
61
- (and Codex home prompts), migrate command runners, and run the strict check:
68
+ (and best-effort Codex home prompts when `~/.codex` exists), migrate command runners,
69
+ and run the strict check:
62
70
 
63
71
  ```
64
72
  arkgate upgrade
@@ -112,13 +120,18 @@ npx arkgate-check --install-agent-gates --skills-only --force
112
120
  losing customizations. If the changelog says a GATE file changed, report the
113
121
  diff and let the user decide; never rewrite settings/CI/AGENTS.md without
114
122
  explicit approval.
115
- If you use Codex, its prompts live in `$CODEX_HOME/prompts` (`~/.codex/prompts`),
116
- not the repo, so a repo refresh never updates them. Refresh them there too:
117
- `ark-check --install-agent-gates --skills-only --codex-home --force`. Keep
118
- `--skills-only` without it, `--force` also rewrites customized gate files
119
- (AGENTS.md, CI, settings). This writes to the user's home dir — say so. (A normal
120
- `ark-check` now flags stale Codex-home skills when copies exist, so you don't have
121
- to remember.)
123
+ **Active host first.** Refresh skills for the host running this skill (e.g.
124
+ `.grok/skills/`, `.claude/skills/`, `.cursor/commands/`). Repo-local copies for
125
+ other detected hosts are fine to refresh in the same pass when cheap.
126
+ **Codex is deferred when you are not on Codex.** Prompts live in
127
+ `$CODEX_HOME/prompts` (`~/.codex/prompts`), not the repo. `ark upgrade` may
128
+ best-effort refresh that home when it exists; still list Codex under
129
+ **Deferred hosts** and do **not** chase MCP multi-project / stale home skills
130
+ until the user is on Codex (or asks). Fix command when needed:
131
+ `ark-check --install-agent-gates --skills-only --codex-home --force`
132
+ (and `--tools codex` / `--force` for primary MCP rebind). Exception: temp or
133
+ `ark-upgrade` MCP `--root` paths — leave fail-closed rewrite to the CLI; do not
134
+ block completion on multi-project noise.
122
135
  **Migrate stale command runners.** The package-manager-aware command templates
123
136
  (`pnpm exec` / `yarn` / `npx`) only apply to NEWLY written files, so a repo that adopted
124
137
  Ark before they shipped keeps a stale `npx` in its EXISTING gate files
@@ -138,10 +151,14 @@ npx arkgate-check --install-agent-gates --skills-only --force
138
151
 
139
152
  ## Operating rules
140
153
 
141
- - Cover EVERY detected agent CLI (`.claude/`, `.cursor/`, `.codex/`,
142
- `.windsurf/`, `.clinerules/`, `.kiro/`), not just the one running this skill —
143
- gates and skills must stay in sync across tools or the weakest tool becomes
144
- the hole in the fence.
154
+ - **Must green:** the **active session host** (skills + gates that host uses) and
155
+ shared repo surfaces (`.mcp.json` dual-bin, command runners, architecture check).
156
+ - **May defer:** other hosts not used in this session. Always list them under
157
+ **Deferred hosts** with the fix command — do not treat them as Incomplete.
158
+ Codex home (global `$CODEX_HOME`) is the common case on Grok/Claude.
159
+ - **Optional sync:** if other repo-local tool dirs already exist (`.cursor/`,
160
+ `.claude/`, …), refreshing their `/ark-*` skills is good hygiene when cheap;
161
+ it is not a reason to fail the skill when the active host is already current.
145
162
  - Never run `--force` blindly; customized files are the user's.
146
163
  - Stop only if the changelog documents a breaking config change with two valid
147
164
  migration paths — then present both with a recommendation.
@@ -158,8 +175,8 @@ npx arkgate-check --install-agent-gates --skills-only --force
158
175
 
159
176
  End with a passing check. Report: latest published version, old → new version
160
177
  (or "already latest"), changelog entries that mattered here (plain language),
161
- files written/refreshed per tool, skipped customized files needing a manual
162
- look, and the final check status.
178
+ files written/refreshed for the **active host**, deferred hosts (if any),
179
+ skipped customized files needing a manual look, and the final check status.
163
180
 
164
181
  ## Completion contract (skill incomplete if missing)
165
182
 
@@ -168,9 +185,12 @@ End with **exactly** these headings (markdown `###`):
168
185
  ### Completion
169
186
  - **Sensor:** commands/tools run
170
187
  - **Opened:** real paths read (or `n/a` only if pure install/upgrade with no source analysis)
188
+ - **Active host:** e.g. `grok` / `claude` / `cursor` / `codex` (skills/gates OK or note)
189
+ - **Deferred hosts:** `none` | e.g. `codex — home MCP/prompts; fix when using Codex`
171
190
  - **Result:** one-line outcome
172
191
  - **Handoff:** `/ark-…` / CLI / `none`
173
192
  - **Incomplete?** `no` | `yes — <what is missing>`
174
193
 
175
194
  If a **STOP** handoff applies and you continued as if done, set **Incomplete?** to `yes`.
176
- **Skill incomplete if missing** any of the bullets above.
195
+ **Deferred hosts (including Codex when not on Codex) never make Incomplete? yes.**
196
+ **Skill incomplete if missing** any of the bullets above (use `none` for Deferred hosts when empty).
@@ -1,4 +1,6 @@
1
1
  {
2
+ "$schema": "https://unpkg.com/arkgate@2/schemas/ark.config.schema.json",
3
+ "schemaVersion": "1.0",
2
4
  "include": ["src"],
3
5
  "layers": [
4
6
  { "name": "DomainModel", "patterns": ["src/domain/**"], "forbiddenGlobals": ["fetch"] },
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/eslint/index.ts","../../src/domain/layerMatch.ts"],"sourcesContent":["/**\n * arkgate/eslint — editor-side architecture gate.\n *\n * Layer / import / forbidden-globals rules load `ark.config.json` from the linted\n * project (walk-up from the file) and use the same glob specificity + edge semantics\n * as ark-check. Matching primitives come from the canonical\n * `src/domain/layerMatch.ts` (CLI loads the generated `bin/ark-layer-match.mjs`) —\n * no Kernel imports.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport {\n globToRegExp,\n patternSpecificity,\n layerForRelativePath,\n isEdgeDenied,\n} from '../domain/layerMatch';\n\nexport { globToRegExp, patternSpecificity, layerForRelativePath, isEdgeDenied };\n\ntype RuleContext = {\n report(descriptor: Record<string, unknown>): void;\n /** ESLint 9+ / 10: preferred path on the context object. */\n filename?: string;\n /** ESLint 8-style physical path when linting with processors / virtual files. */\n physicalFilename?: string;\n /** ESLint ≤8 API — still present on some hosts; removed in ESLint 10. */\n getFilename?: () => string;\n options?: unknown[];\n};\n\n/** Resolve the file path being linted across ESLint 8–10 context shapes. */\nfunction lintedFilename(context: RuleContext): string {\n if (typeof context.physicalFilename === 'string' && context.physicalFilename.length > 0) {\n return context.physicalFilename;\n }\n if (typeof context.filename === 'string' && context.filename.length > 0) {\n return context.filename;\n }\n if (typeof context.getFilename === 'function') {\n try {\n const name = context.getFilename();\n if (typeof name === 'string' && name.length > 0) return name;\n } catch {\n /* ignore */\n }\n }\n return '';\n}\n\ntype RuleListener = Record<string, (node: AstNode) => void>;\n\ntype AstNode = {\n type?: string;\n name?: string;\n value?: unknown;\n source?: AstNode;\n callee?: AstNode;\n object?: AstNode;\n property?: AstNode;\n key?: AstNode;\n arguments?: AstNode[];\n properties?: AstNode[];\n importKind?: string;\n specifiers?: AstNode[];\n};\n\ntype ArkRule = {\n meta: {\n type: 'problem';\n docs: { description: string };\n messages: Record<string, string>;\n schema: unknown[];\n };\n create(context: RuleContext): RuleListener;\n};\n\ntype ArkEslintPlugin = {\n rules: Record<string, ArkRule>;\n configs?: Record<string, unknown>;\n};\n\ntype LayerConfig = {\n name: string;\n patterns?: string[];\n exclude?: string[];\n forbiddenGlobals?: string[];\n};\n\ntype EdgeRule = { from: string; to: string; allowed?: boolean };\n\ntype ArkConfig = {\n layers?: LayerConfig[];\n rules?: EdgeRule[];\n};\n\n// ── Config I/O (editor-only; matching primitives come from ark-layer-match.mjs) ──\n\nexport function findConfigPath(startFile: string): string | null {\n if (!startFile || startFile === '<input>' || startFile.startsWith('stdin')) return null;\n let dir = path.dirname(path.resolve(startFile));\n for (;;) {\n const candidate = path.join(dir, 'ark.config.json');\n if (fs.existsSync(candidate)) return candidate;\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nconst _configCache = new Map<string, ArkConfig | null>();\n\nexport function loadArkConfig(configPath: string): ArkConfig | null {\n if (_configCache.has(configPath)) return _configCache.get(configPath) ?? null;\n try {\n const raw = JSON.parse(fs.readFileSync(configPath, 'utf8')) as ArkConfig;\n _configCache.set(configPath, raw);\n return raw;\n } catch {\n _configCache.set(configPath, null);\n return null;\n }\n}\n\n/** Resolve relative import specifier to an absolute path candidate (TS-oriented). */\nexport function resolveRelativeImport(fromFile: string, specifier: string): string | null {\n if (!specifier.startsWith('.')) return null;\n const base = path.resolve(path.dirname(fromFile), specifier);\n const candidates = [\n base,\n `${base}.ts`,\n `${base}.tsx`,\n `${base}.mts`,\n `${base}.cts`,\n `${base}.js`,\n `${base}.jsx`,\n path.join(base, 'index.ts'),\n path.join(base, 'index.tsx'),\n path.join(base, 'index.js'),\n ];\n for (const c of candidates) {\n try {\n if (fs.existsSync(c) && fs.statSync(c).isFile()) return c;\n } catch {\n /* continue */\n }\n }\n // Prefer .ts for layer matching when the target is not on disk yet (editor typing).\n return `${base}.ts`;\n}\n\n// ── AST helpers ────────────────────────────────────────────────────────────\n\nfunction stringValue(node: AstNode | undefined): string | undefined {\n return typeof node?.value === 'string' ? node.value : undefined;\n}\n\nfunction propertyName(node: AstNode | undefined): string | undefined {\n return node?.name ?? stringValue(node);\n}\n\nfunction calleePropertyName(node: AstNode): string | undefined {\n return propertyName(node.callee?.property);\n}\n\nfunction objectProperty(node: AstNode | undefined, name: string): AstNode | undefined {\n return node?.properties?.find((property) => propertyName(property.key) === name);\n}\n\nfunction objectHasProperty(node: AstNode | undefined, name: string): boolean {\n return objectProperty(node, name) !== undefined;\n}\n\nfunction objectHasMetadataSource(node: AstNode | undefined): boolean {\n const metadata = objectProperty(node, 'metadata')?.value as AstNode | undefined;\n return objectHasProperty(metadata, 'source');\n}\n\nfunction looksLikeIntent(value: string): boolean {\n return /^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\\.[A-Za-z0-9_.]+$/.test(\n value\n );\n}\n\nfunction isPublishCall(node: AstNode): boolean {\n return calleePropertyName(node) === 'publish';\n}\n\n/** Heuristic fallback when no ark.config.json (pre-contract projects). */\nfunction isDomainFileHeuristic(filename: string): boolean {\n const normalized = filename.split('\\\\').join('/').toLowerCase();\n return normalized.includes('/domain/') || normalized.endsWith('/domain.ts');\n}\n\nfunction isInfraImportHeuristic(specifier: string): boolean {\n const normalized = specifier.toLowerCase();\n return [\n 'adapter',\n 'adapters',\n 'infrastructure',\n 'persistence',\n 'repository',\n 'repositories',\n 'integration',\n 'database',\n 'db',\n ].some((token) => normalized.includes(token));\n}\n\nconst DEFAULT_FORBIDDEN_GLOBALS = ['fetch', 'process', 'Date.now', 'Math.random'];\n\n// ── Rules ──────────────────────────────────────────────────────────────────\n\n/**\n * Config-driven layer import boundary (primary editor gate).\n * Replaces path-token domain/infra heuristics when ark.config.json is present.\n * Rule id kept as `no-domain-infra-imports` for recommended-config / upgrade stability.\n */\nexport const noDomainInfraImports: ArkRule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow imports that violate ark.config.json layer rules (same contract as arkgate-check). Falls back to domain→infra path heuristics when no config is found.',\n },\n messages: {\n forbiddenImport:\n 'Architecture: {{fromLayer}} must not import {{toLayer}} (ark.config.json). Specifier: {{specifier}}',\n forbiddenImportHeuristic:\n 'Domain code must not import infrastructure, adapters, repositories, or database modules.',\n },\n schema: [],\n },\n create(context) {\n const filename = lintedFilename(context);\n const configPath = findConfigPath(filename);\n const config = configPath ? loadArkConfig(configPath) : null;\n const root = configPath ? path.dirname(configPath) : null;\n\n const check = (node: AstNode) => {\n const source = stringValue(node.source);\n if (!source) return;\n\n if (config && root && filename) {\n const absFile = path.isAbsolute(filename) ? filename : path.resolve(filename);\n const relFile = path.relative(root, absFile).split(path.sep).join('/');\n const fromLayer = layerForRelativePath(relFile, config.layers);\n if (!fromLayer) return;\n\n const targetAbs = resolveRelativeImport(absFile, source);\n if (!targetAbs) return; // package import — CI resolves via TS; editor skips non-relative\n\n const relTarget = path.relative(root, targetAbs).split(path.sep).join('/');\n // Outside project or up-and-out: skip\n if (relTarget.startsWith('..')) return;\n\n const toLayer = layerForRelativePath(relTarget, config.layers);\n if (!toLayer) return;\n if (\n isEdgeDenied(config.rules, fromLayer, toLayer, {\n fromPath: relFile,\n toPath: relTarget,\n layers: config.layers,\n })\n ) {\n context.report({\n node,\n messageId: 'forbiddenImport',\n data: { fromLayer, toLayer, specifier: source },\n });\n }\n return;\n }\n\n // No contract: legacy heuristic so bare domain folders still get a signal.\n if (isDomainFileHeuristic(filename) && isInfraImportHeuristic(source)) {\n context.report({ node, messageId: 'forbiddenImportHeuristic' });\n }\n };\n\n return {\n ImportDeclaration: check,\n ExportNamedDeclaration: check,\n ExportAllDeclaration: check,\n };\n },\n};\n\nexport const noRawEventPublish: ArkRule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Require event bus publish calls to use registered intent creators instead of raw event objects or intent strings.',\n },\n messages: {\n rawPublish:\n 'Publish through a registered intent creator; raw event objects or intent strings bypass Ark contracts.',\n },\n schema: [],\n },\n create(context) {\n return {\n CallExpression(node) {\n if (!isPublishCall(node)) return;\n const firstArg = node.arguments?.[0];\n const firstValue = stringValue(firstArg);\n if ((firstValue && looksLikeIntent(firstValue)) || objectHasProperty(firstArg, 'intent')) {\n context.report({ node, messageId: 'rawPublish' });\n }\n },\n };\n },\n};\n\nexport const requirePublishSource: ArkRule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Require event bus publish calls to include source metadata.',\n },\n messages: {\n missingSource: 'Strict Ark publish calls must include metadata.source.',\n },\n schema: [],\n },\n create(context) {\n return {\n CallExpression(node) {\n if (!isPublishCall(node)) return;\n const firstArg = node.arguments?.[0];\n const metadataArg = node.arguments?.[2];\n if (objectHasMetadataSource(firstArg) || objectHasProperty(metadataArg, 'source')) {\n return;\n }\n context.report({ node, messageId: 'missingSource' });\n },\n };\n },\n};\n\nexport const noForbiddenGlobals: ArkRule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow ambient globals from the layer’s forbiddenGlobals in ark.config.json (same purity surface as arkgate-check). Option `globals` overrides. Without config, defaults apply only on domain-like paths.',\n },\n messages: {\n forbiddenGlobal:\n 'Ambient global \"{{name}}\" is forbidden in {{layer}} (ark.config.json); inject the capability through a port instead.',\n forbiddenGlobalDefault:\n 'Ambient global \"{{name}}\" is forbidden here; inject the capability through a port instead.',\n },\n schema: [\n {\n type: 'object',\n properties: {\n globals: { type: 'array', items: { type: 'string' } },\n },\n additionalProperties: false,\n },\n ],\n },\n create(context) {\n const filename = lintedFilename(context);\n const option = context.options?.[0] as { globals?: string[] } | undefined;\n const configPath = findConfigPath(filename);\n const config = configPath ? loadArkConfig(configPath) : null;\n const root = configPath ? path.dirname(configPath) : null;\n\n let globals: Set<string> | null = null;\n let layerName = 'this layer';\n\n if (option?.globals) {\n globals = new Set(option.globals);\n } else if (config && root && filename) {\n const absFile = path.isAbsolute(filename) ? filename : path.resolve(filename);\n const relFile = path.relative(root, absFile).split(path.sep).join('/');\n const layer = config.layers?.find(\n (l) => l.name === layerForRelativePath(relFile, config.layers)\n );\n if (layer?.forbiddenGlobals?.length) {\n globals = new Set(layer.forbiddenGlobals);\n layerName = layer.name;\n } else {\n // Layer has no purity list — do not invent defaults (matches CI).\n globals = null;\n }\n } else if (isDomainFileHeuristic(filename)) {\n globals = new Set(DEFAULT_FORBIDDEN_GLOBALS);\n }\n\n if (!globals) {\n return {} as RuleListener;\n }\n\n const report = (node: AstNode, name: string) =>\n context.report({\n node,\n messageId: config ? 'forbiddenGlobal' : 'forbiddenGlobalDefault',\n data: { name, layer: layerName },\n });\n\n return {\n MemberExpression(node) {\n const base = node.object?.type === 'Identifier' ? node.object.name : undefined;\n if (!base) return;\n const dotted = `${base}.${propertyName(node.property) ?? ''}`;\n if (globals!.has(dotted)) report(node, dotted);\n else if (globals!.has(base)) report(node, base);\n },\n CallExpression(node) {\n const callee = node.callee?.type === 'Identifier' ? node.callee.name : undefined;\n if (callee && globals!.has(callee)) report(node, callee);\n },\n NewExpression(node) {\n const callee = node.callee?.type === 'Identifier' ? node.callee.name : undefined;\n if (callee && globals!.has(callee)) report(node, callee);\n },\n };\n },\n};\n\nconst rules = {\n 'no-domain-infra-imports': noDomainInfraImports,\n 'no-raw-event-publish': noRawEventPublish,\n 'require-publish-source': requirePublishSource,\n 'no-forbidden-globals': noForbiddenGlobals,\n};\n\nconst plugin: ArkEslintPlugin = { rules };\n\nplugin.configs = {\n recommended: {\n plugins: { ark: plugin },\n rules: {\n 'ark/no-domain-infra-imports': 'error',\n 'ark/no-raw-event-publish': 'error',\n 'ark/require-publish-source': 'error',\n 'ark/no-forbidden-globals': 'error',\n },\n },\n};\n\nexport { plugin };\nexport default plugin;\n","/**\n * Pure layer-glob matching for ark.config.json.\n *\n * **Canonical algorithm** for CLI, ESLint, and library consumers.\n * The CLI load path uses the generated `bin/ark-layer-match.mjs`\n * (`npm run generate:layer-match` / `npm run check:layer-match`).\n * Behavioral parity tests remain a safety net.\n */\n\nexport type LayerConfig = {\n name: string;\n patterns?: string[];\n exclude?: string[];\n forbiddenGlobals?: string[];\n};\n\n/**\n * Layer-to-layer dependency rule from ark.config.json.\n *\n * - Classic: `{ from, to, allowed: false }` denies **cross-layer** edges only.\n * Same-layer is always allowed without peerIsolation (historical short-circuit).\n * - `peerIsolation: true` + `allowed: false`: deny only when slice ids differ\n * (same **or** cross layer). Same-slice → allow. Needs fromPath/toPath; missing\n * paths/slices → fail-open.\n */\nexport type EdgeRule = {\n from: string;\n to: string;\n allowed?: boolean;\n /**\n * When true with `allowed: false`: deny only when importer and importee resolve\n * to different slice ids (parent/name, e.g. features/auth). Works same-layer\n * and cross-layer. See findDeniedEdgeRule.\n */\n peerIsolation?: boolean;\n /**\n * Path segment names that own the slice id as the *next* segment\n * (e.g. `[\"features\"]` → `src/features/auth/...` has slice `auth`).\n * When omitted, inferred from the layer's glob patterns (segment before `**`/`*`).\n */\n sliceFolders?: string[];\n /** Optional override message for scanners / write-gate. */\n message?: string;\n};\n\n/** Options for path-aware edge checks (peer isolation). */\nexport type EdgeCheckOptions = {\n /** Repo-relative path of the importing file. */\n fromPath?: string;\n /** Repo-relative path of the imported module. */\n toPath?: string;\n /** Layer configs — used to infer sliceFolders when the rule omits them. */\n layers?: LayerConfig[];\n};\n\nconst regexpCache = new Map<string, RegExp>();\n\nfunction escapeLiteral(ch: string): string {\n return /[.*+?^${}()|[\\]\\\\]/.test(ch) ? `\\\\${ch}` : ch;\n}\n\n/**\n * Normalize path separators to `/` without destroying glob escape sequences.\n * `src\\domain\\x` → `src/domain/x` (Windows paths); `src/\\{legacy\\}/**` keeps `\\{` / `\\}`.\n * A plain `pattern.split('\\\\').join('/')` would eat those escapes.\n */\nfunction normalizeGlobSeparators(pattern: string): string {\n let out = '';\n for (let i = 0; i < pattern.length; i += 1) {\n const c = pattern[i];\n if (c === '\\\\' && i + 1 < pattern.length) {\n const next = pattern[i + 1];\n // Keep escapes for glob metacharacters (and escaped backslash).\n if ('*?{}[],'.includes(next) || next === '\\\\') {\n out += '\\\\' + next;\n i += 1;\n continue;\n }\n // Otherwise treat `\\` as a path separator (Windows).\n out += '/';\n continue;\n }\n out += c;\n }\n return out;\n}\n\nfunction bracesBalanced(glob: string): boolean {\n let depth = 0;\n for (let i = 0; i < glob.length; i += 1) {\n const c = glob[i];\n if (c === '\\\\') {\n i += 1;\n continue;\n }\n if (c === '{') depth += 1;\n else if (c === '}') {\n depth -= 1;\n if (depth < 0) return false;\n }\n }\n return depth === 0;\n}\n\nexport function globToRegExp(pattern: string): RegExp {\n const cached = regexpCache.get(pattern);\n if (cached) return cached;\n\n const glob = normalizeGlobSeparators(pattern);\n const useBraces = bracesBalanced(glob);\n let out = '';\n let braceDepth = 0;\n for (let i = 0; i < glob.length; i += 1) {\n const c = glob[i];\n if (c === '\\\\' && i + 1 < glob.length) {\n out += escapeLiteral(glob[i + 1]);\n i += 1;\n } else if (c === '*') {\n if (glob[i + 1] === '*') {\n if (glob[i + 2] === '/') {\n out += '(?:.*/)?';\n i += 2;\n } else {\n out += '.*';\n i += 1;\n }\n } else {\n out += '[^/]*';\n }\n } else if (c === '?') {\n out += '[^/]';\n } else if (c === '{' && useBraces) {\n out += '(?:';\n braceDepth += 1;\n } else if (c === '}' && useBraces && braceDepth > 0) {\n out += ')';\n braceDepth -= 1;\n } else if (c === ',' && useBraces && braceDepth > 0) {\n out += '|';\n } else {\n out += escapeLiteral(c);\n }\n }\n const re = new RegExp(`^${out}$`);\n regexpCache.set(pattern, re);\n return re;\n}\n\nexport function patternSpecificity(pattern: string): number {\n const glob = normalizeGlobSeparators(String(pattern));\n const beforeWildcard = glob.split('*')[0];\n const literalSegments = beforeWildcard.split('/').filter(Boolean).length;\n const literalLength = glob.replace(/\\*/g, '').length;\n return literalSegments * 10000 + literalLength;\n}\n\nexport function layerForRelativePath(\n relPath: string,\n layers: LayerConfig[] | undefined\n): string | undefined {\n // File paths (not globs): any OS separator → posix relative.\n const rel = String(relPath).split(/[/\\\\]/).join('/');\n let bestName: string | undefined;\n let bestScore = -1;\n for (const layer of layers ?? []) {\n if ((layer.exclude ?? []).some((pattern) => globToRegExp(pattern).test(rel))) {\n continue;\n }\n for (const pattern of layer.patterns ?? []) {\n if (globToRegExp(pattern).test(rel)) {\n const score = patternSpecificity(pattern);\n if (score > bestScore) {\n bestScore = score;\n bestName = layer.name;\n }\n }\n }\n }\n return bestName;\n}\n\n/**\n * Extract the slice id under a known folder name.\n * Includes the parent folder so `features/auth` ≠ `modules/auth`.\n * `src/features/auth/api.ts` + folders `[\"features\"]` → `\"features/auth\"`.\n */\nexport function sliceIdForPath(\n relPath: string,\n sliceFolders: string[] | undefined\n): string | undefined {\n if (!sliceFolders?.length) return undefined;\n const parts = String(relPath)\n .split(/[/\\\\]/)\n .filter(Boolean);\n const folders = new Set(sliceFolders.map((s) => String(s).toLowerCase()));\n for (let i = 0; i < parts.length - 1; i += 1) {\n if (folders.has(parts[i].toLowerCase())) {\n return `${parts[i]}/${parts[i + 1]}`;\n }\n }\n return undefined;\n}\n\n/**\n * Infer slice parent folders from layer globs: the path segment immediately\n * before a `*` or `**` wildcard (e.g. `src/features/**` → `features`).\n */\nexport function inferSliceFoldersFromPatterns(\n patterns: string[] | undefined\n): string[] {\n const out = new Set<string>();\n for (const pattern of patterns ?? []) {\n const glob = normalizeGlobSeparators(String(pattern));\n const parts = glob.split('/').filter(Boolean);\n for (let i = 0; i < parts.length; i += 1) {\n const part = parts[i];\n if ((part === '**' || part === '*') && i > 0) {\n const prev = parts[i - 1];\n if (prev && !prev.includes('*') && !prev.includes('{') && !prev.includes('}')) {\n out.add(prev);\n }\n }\n }\n }\n return [...out];\n}\n\nfunction resolveSliceFolders(\n rule: EdgeRule,\n layerName: string,\n layers: LayerConfig[] | undefined\n): string[] {\n if (Array.isArray(rule.sliceFolders) && rule.sliceFolders.length > 0) {\n return rule.sliceFolders.filter((s) => typeof s === 'string' && s.length > 0);\n }\n const layer = (layers ?? []).find((l) => l.name === layerName);\n return inferSliceFoldersFromPatterns(layer?.patterns);\n}\n\n/**\n * Find the first denying rule for a layer edge.\n *\n * Semantics (locked):\n * - Classic (`allowed: false`, no peerIsolation): deny cross-layer edges only.\n * Same-layer is always allowed (historical short-circuit).\n * - `peerIsolation: true` + `allowed: false`: deny only when importer and importee\n * resolve to **different** slice ids (same or cross layer). Same-slice → allow.\n * Missing paths or unclassifiable slices → fail-open (do not deny).\n */\nexport function findDeniedEdgeRule(\n rules: EdgeRule[] | undefined,\n from: string,\n to: string,\n options?: EdgeCheckOptions\n): EdgeRule | undefined {\n for (const rule of rules ?? []) {\n if (rule.from !== from || rule.to !== to) continue;\n if (rule.allowed !== false) continue;\n\n if (rule.peerIsolation) {\n const fromPath = options?.fromPath;\n const toPath = options?.toPath;\n if (!fromPath || !toPath) continue;\n\n const folders = resolveSliceFolders(rule, from, options?.layers);\n if (folders.length === 0) continue;\n\n const fromSlice = sliceIdForPath(fromPath, folders);\n const toSlice = sliceIdForPath(toPath, folders);\n if (!fromSlice || !toSlice) continue;\n if (fromSlice !== toSlice) return rule;\n continue; // same slice: this peerIsolation rule does not deny\n }\n\n // Classic deny — same-layer always allowed without peerIsolation\n if (from === to) continue;\n return rule;\n }\n return undefined;\n}\n\nexport function isEdgeDenied(\n rules: EdgeRule[] | undefined,\n from: string,\n to: string,\n options?: EdgeCheckOptions\n): boolean {\n return findDeniedEdgeRule(rules, from, to, options) !== undefined;\n}\n\n/** Codegen globs skipped by default scan (emitted into the CLI derived matcher). */\nexport const DEFAULT_GENERATED_FILE_GLOBS = [\n '**/*.gen.ts',\n '**/*.gen.tsx',\n '**/*.generated.ts',\n '**/*.generated.tsx',\n];\n\nexport type ScanExcludeConfig = {\n exclude?: string[];\n excludeGenerated?: boolean;\n};\n\nexport function scanExcludePatterns(config?: ScanExcludeConfig | null): string[] {\n const custom = Array.isArray(config?.exclude)\n ? config!.exclude!.filter((p) => typeof p === 'string')\n : [];\n const generated =\n config?.excludeGenerated === false ? [] : DEFAULT_GENERATED_FILE_GLOBS;\n return [...generated, ...custom];\n}\n\nexport function isScanExcludedRelative(\n relPath: string,\n config?: ScanExcludeConfig | null\n): boolean {\n const rel = String(relPath).split(/[/\\\\]/).join('/');\n return scanExcludePatterns(config).some((pattern) => globToRegExp(pattern).test(rel));\n}\n"],"mappings":"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","names":["rules","path","fs"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/eslint/index.ts","../../src/domain/layerMatch.ts"],"sourcesContent":["/**\n * arkgate/eslint — editor-side architecture gate.\n *\n * Layer / import / forbidden-globals rules load `ark.config.json` from the linted\n * project (walk-up from the file) and use the same glob specificity + edge semantics\n * as ark-check. Matching primitives come from the canonical\n * `src/domain/layerMatch.ts` (CLI loads the generated `bin/ark-layer-match.mjs`) —\n * no Kernel imports.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport {\n globToRegExp,\n patternSpecificity,\n layerForRelativePath,\n isEdgeDenied,\n} from '../domain/layerMatch';\n\nexport { globToRegExp, patternSpecificity, layerForRelativePath, isEdgeDenied };\n\ntype RuleContext = {\n report(descriptor: Record<string, unknown>): void;\n /** ESLint 9+ / 10: preferred path on the context object. */\n filename?: string;\n /** ESLint 8-style physical path when linting with processors / virtual files. */\n physicalFilename?: string;\n /** ESLint ≤8 API — still present on some hosts; removed in ESLint 10. */\n getFilename?: () => string;\n options?: unknown[];\n};\n\n/** Resolve the file path being linted across ESLint 8–10 context shapes. */\nfunction lintedFilename(context: RuleContext): string {\n if (typeof context.physicalFilename === 'string' && context.physicalFilename.length > 0) {\n return context.physicalFilename;\n }\n if (typeof context.filename === 'string' && context.filename.length > 0) {\n return context.filename;\n }\n if (typeof context.getFilename === 'function') {\n try {\n const name = context.getFilename();\n if (typeof name === 'string' && name.length > 0) return name;\n } catch {\n /* ignore */\n }\n }\n return '';\n}\n\ntype RuleListener = Record<string, (node: AstNode) => void>;\n\ntype AstNode = {\n type?: string;\n name?: string;\n value?: unknown;\n source?: AstNode;\n callee?: AstNode;\n object?: AstNode;\n property?: AstNode;\n key?: AstNode;\n arguments?: AstNode[];\n properties?: AstNode[];\n importKind?: string;\n specifiers?: AstNode[];\n};\n\ntype ArkRule = {\n meta: {\n type: 'problem';\n docs: { description: string };\n messages: Record<string, string>;\n schema: unknown[];\n };\n create(context: RuleContext): RuleListener;\n};\n\ntype ArkEslintPlugin = {\n rules: Record<string, ArkRule>;\n configs?: Record<string, unknown>;\n};\n\ntype LayerConfig = {\n name: string;\n patterns?: string[];\n exclude?: string[];\n forbiddenGlobals?: string[];\n};\n\ntype EdgeRule = { from: string; to: string; allowed?: boolean };\n\ntype ArkConfig = {\n layers?: LayerConfig[];\n rules?: EdgeRule[];\n};\n\n// ── Config I/O (editor-only; matching primitives come from ark-layer-match.mjs) ──\n\nexport function findConfigPath(startFile: string): string | null {\n if (!startFile || startFile === '<input>' || startFile.startsWith('stdin')) return null;\n let dir = path.dirname(path.resolve(startFile));\n for (;;) {\n const candidate = path.join(dir, 'ark.config.json');\n if (fs.existsSync(candidate)) return candidate;\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nconst _configCache = new Map<string, ArkConfig | null>();\n\nexport function loadArkConfig(configPath: string): ArkConfig | null {\n if (_configCache.has(configPath)) return _configCache.get(configPath) ?? null;\n try {\n const raw = JSON.parse(fs.readFileSync(configPath, 'utf8')) as ArkConfig;\n _configCache.set(configPath, raw);\n return raw;\n } catch {\n _configCache.set(configPath, null);\n return null;\n }\n}\n\n/** Resolve relative import specifier to an absolute path candidate (TS-oriented). */\nexport function resolveRelativeImport(fromFile: string, specifier: string): string | null {\n if (!specifier.startsWith('.')) return null;\n const base = path.resolve(path.dirname(fromFile), specifier);\n const candidates = [\n base,\n `${base}.ts`,\n `${base}.tsx`,\n `${base}.mts`,\n `${base}.cts`,\n `${base}.js`,\n `${base}.jsx`,\n path.join(base, 'index.ts'),\n path.join(base, 'index.tsx'),\n path.join(base, 'index.js'),\n ];\n for (const c of candidates) {\n try {\n if (fs.existsSync(c) && fs.statSync(c).isFile()) return c;\n } catch {\n /* continue */\n }\n }\n // Prefer .ts for layer matching when the target is not on disk yet (editor typing).\n return `${base}.ts`;\n}\n\n// ── AST helpers ────────────────────────────────────────────────────────────\n\nfunction stringValue(node: AstNode | undefined): string | undefined {\n return typeof node?.value === 'string' ? node.value : undefined;\n}\n\nfunction propertyName(node: AstNode | undefined): string | undefined {\n return node?.name ?? stringValue(node);\n}\n\nfunction calleePropertyName(node: AstNode): string | undefined {\n return propertyName(node.callee?.property);\n}\n\nfunction objectProperty(node: AstNode | undefined, name: string): AstNode | undefined {\n return node?.properties?.find((property) => propertyName(property.key) === name);\n}\n\nfunction objectHasProperty(node: AstNode | undefined, name: string): boolean {\n return objectProperty(node, name) !== undefined;\n}\n\nfunction objectHasMetadataSource(node: AstNode | undefined): boolean {\n const metadata = objectProperty(node, 'metadata')?.value as AstNode | undefined;\n return objectHasProperty(metadata, 'source');\n}\n\nfunction looksLikeIntent(value: string): boolean {\n return /^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\\.[A-Za-z0-9_.]+$/.test(\n value\n );\n}\n\nfunction isPublishCall(node: AstNode): boolean {\n return calleePropertyName(node) === 'publish';\n}\n\n/** Heuristic fallback when no ark.config.json (pre-contract projects). */\nfunction isDomainFileHeuristic(filename: string): boolean {\n const normalized = filename.split('\\\\').join('/').toLowerCase();\n return normalized.includes('/domain/') || normalized.endsWith('/domain.ts');\n}\n\nfunction isInfraImportHeuristic(specifier: string): boolean {\n const normalized = specifier.toLowerCase();\n return [\n 'adapter',\n 'adapters',\n 'infrastructure',\n 'persistence',\n 'repository',\n 'repositories',\n 'integration',\n 'database',\n 'db',\n ].some((token) => normalized.includes(token));\n}\n\nconst DEFAULT_FORBIDDEN_GLOBALS = ['fetch', 'process', 'Date.now', 'Math.random'];\n\n// ── Rules ──────────────────────────────────────────────────────────────────\n\n/**\n * Config-driven layer import boundary (primary editor gate).\n * Replaces path-token domain/infra heuristics when ark.config.json is present.\n * Rule id kept as `no-domain-infra-imports` for recommended-config / upgrade stability.\n */\nexport const noDomainInfraImports: ArkRule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow imports that violate ark.config.json layer rules (same contract as arkgate-check). Falls back to domain→infra path heuristics when no config is found.',\n },\n messages: {\n forbiddenImport:\n 'Architecture: {{fromLayer}} must not import {{toLayer}} (ark.config.json). Specifier: {{specifier}}',\n forbiddenImportHeuristic:\n 'Domain code must not import infrastructure, adapters, repositories, or database modules.',\n },\n schema: [],\n },\n create(context) {\n const filename = lintedFilename(context);\n const configPath = findConfigPath(filename);\n const config = configPath ? loadArkConfig(configPath) : null;\n const root = configPath ? path.dirname(configPath) : null;\n\n const check = (node: AstNode) => {\n const source = stringValue(node.source);\n if (!source) return;\n\n if (config && root && filename) {\n const absFile = path.isAbsolute(filename) ? filename : path.resolve(filename);\n const relFile = path.relative(root, absFile).split(path.sep).join('/');\n const fromLayer = layerForRelativePath(relFile, config.layers);\n if (!fromLayer) return;\n\n const targetAbs = resolveRelativeImport(absFile, source);\n if (!targetAbs) return; // package import — CI resolves via TS; editor skips non-relative\n\n const relTarget = path.relative(root, targetAbs).split(path.sep).join('/');\n // Outside project or up-and-out: skip\n if (relTarget.startsWith('..')) return;\n\n const toLayer = layerForRelativePath(relTarget, config.layers);\n if (!toLayer) return;\n if (\n isEdgeDenied(config.rules, fromLayer, toLayer, {\n fromPath: relFile,\n toPath: relTarget,\n layers: config.layers,\n })\n ) {\n context.report({\n node,\n messageId: 'forbiddenImport',\n data: { fromLayer, toLayer, specifier: source },\n });\n }\n return;\n }\n\n // No contract: legacy heuristic so bare domain folders still get a signal.\n if (isDomainFileHeuristic(filename) && isInfraImportHeuristic(source)) {\n context.report({ node, messageId: 'forbiddenImportHeuristic' });\n }\n };\n\n return {\n ImportDeclaration: check,\n ExportNamedDeclaration: check,\n ExportAllDeclaration: check,\n };\n },\n};\n\nexport const noRawEventPublish: ArkRule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Require event bus publish calls to use registered intent creators instead of raw event objects or intent strings.',\n },\n messages: {\n rawPublish:\n 'Publish through a registered intent creator; raw event objects or intent strings bypass Ark contracts.',\n },\n schema: [],\n },\n create(context) {\n return {\n CallExpression(node) {\n if (!isPublishCall(node)) return;\n const firstArg = node.arguments?.[0];\n const firstValue = stringValue(firstArg);\n if ((firstValue && looksLikeIntent(firstValue)) || objectHasProperty(firstArg, 'intent')) {\n context.report({ node, messageId: 'rawPublish' });\n }\n },\n };\n },\n};\n\nexport const requirePublishSource: ArkRule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Require event bus publish calls to include source metadata.',\n },\n messages: {\n missingSource: 'Strict Ark publish calls must include metadata.source.',\n },\n schema: [],\n },\n create(context) {\n return {\n CallExpression(node) {\n if (!isPublishCall(node)) return;\n const firstArg = node.arguments?.[0];\n const metadataArg = node.arguments?.[2];\n if (objectHasMetadataSource(firstArg) || objectHasProperty(metadataArg, 'source')) {\n return;\n }\n context.report({ node, messageId: 'missingSource' });\n },\n };\n },\n};\n\nexport const noForbiddenGlobals: ArkRule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow ambient globals from the layer’s forbiddenGlobals in ark.config.json (same purity surface as arkgate-check). Option `globals` overrides. Without config, defaults apply only on domain-like paths.',\n },\n messages: {\n forbiddenGlobal:\n 'Ambient global \"{{name}}\" is forbidden in {{layer}} (ark.config.json); inject the capability through a port instead.',\n forbiddenGlobalDefault:\n 'Ambient global \"{{name}}\" is forbidden here; inject the capability through a port instead.',\n },\n schema: [\n {\n type: 'object',\n properties: {\n globals: { type: 'array', items: { type: 'string' } },\n },\n additionalProperties: false,\n },\n ],\n },\n create(context) {\n const filename = lintedFilename(context);\n const option = context.options?.[0] as { globals?: string[] } | undefined;\n const configPath = findConfigPath(filename);\n const config = configPath ? loadArkConfig(configPath) : null;\n const root = configPath ? path.dirname(configPath) : null;\n\n let globals: Set<string> | null = null;\n let layerName = 'this layer';\n\n if (option?.globals) {\n globals = new Set(option.globals);\n } else if (config && root && filename) {\n const absFile = path.isAbsolute(filename) ? filename : path.resolve(filename);\n const relFile = path.relative(root, absFile).split(path.sep).join('/');\n const layer = config.layers?.find(\n (l) => l.name === layerForRelativePath(relFile, config.layers)\n );\n if (layer?.forbiddenGlobals?.length) {\n globals = new Set(layer.forbiddenGlobals);\n layerName = layer.name;\n } else {\n // Layer has no purity list — do not invent defaults (matches CI).\n globals = null;\n }\n } else if (isDomainFileHeuristic(filename)) {\n globals = new Set(DEFAULT_FORBIDDEN_GLOBALS);\n }\n\n if (!globals) {\n return {} as RuleListener;\n }\n\n const report = (node: AstNode, name: string) =>\n context.report({\n node,\n messageId: config ? 'forbiddenGlobal' : 'forbiddenGlobalDefault',\n data: { name, layer: layerName },\n });\n\n return {\n MemberExpression(node) {\n const base = node.object?.type === 'Identifier' ? node.object.name : undefined;\n if (!base) return;\n const dotted = `${base}.${propertyName(node.property) ?? ''}`;\n if (globals!.has(dotted)) report(node, dotted);\n else if (globals!.has(base)) report(node, base);\n },\n CallExpression(node) {\n const callee = node.callee?.type === 'Identifier' ? node.callee.name : undefined;\n if (callee && globals!.has(callee)) report(node, callee);\n },\n NewExpression(node) {\n const callee = node.callee?.type === 'Identifier' ? node.callee.name : undefined;\n if (callee && globals!.has(callee)) report(node, callee);\n },\n };\n },\n};\n\nconst rules = {\n 'no-domain-infra-imports': noDomainInfraImports,\n 'no-raw-event-publish': noRawEventPublish,\n 'require-publish-source': requirePublishSource,\n 'no-forbidden-globals': noForbiddenGlobals,\n};\n\nconst plugin: ArkEslintPlugin = { rules };\n\nplugin.configs = {\n recommended: {\n plugins: { ark: plugin },\n rules: {\n 'ark/no-domain-infra-imports': 'error',\n 'ark/no-raw-event-publish': 'error',\n 'ark/require-publish-source': 'error',\n 'ark/no-forbidden-globals': 'error',\n },\n },\n};\n\nexport { plugin };\nexport default plugin;\n","/**\n * Pure layer-glob matching for ark.config.json.\n *\n * **Canonical algorithm** for CLI, ESLint, and library consumers.\n * The CLI load path uses the generated `bin/ark-layer-match.mjs`\n * (`npm run generate:layer-match` / `npm run check:layer-match`).\n * Behavioral parity tests remain a safety net.\n */\n\nexport type LayerConfig = {\n name: string;\n patterns?: string[];\n exclude?: string[];\n forbiddenGlobals?: string[];\n};\n\n/**\n * Layer-to-layer dependency rule from ark.config.json.\n *\n * - Classic: `{ from, to, allowed: false }` denies **cross-layer** edges only.\n * Same-layer is always allowed without peerIsolation (historical short-circuit).\n * - `peerIsolation: true` + `allowed: false`: deny only when slice ids differ\n * (same **or** cross layer). Same-slice → allow. Needs fromPath/toPath; missing\n * paths/slices → fail-open.\n */\nexport type EdgeRule = {\n from: string;\n to: string;\n allowed?: boolean;\n /**\n * When true with `allowed: false`: deny only when importer and importee resolve\n * to different slice ids (parent/name, e.g. features/auth). Works same-layer\n * and cross-layer. See findDeniedEdgeRule.\n */\n peerIsolation?: boolean;\n /**\n * Path segment names that own the slice id as the *next* segment\n * (e.g. `[\"features\"]` → `src/features/auth/...` has slice `auth`).\n * When omitted, inferred from the layer's glob patterns (segment before `**`/`*`).\n */\n sliceFolders?: string[];\n /** Optional override message for scanners / write-gate. */\n message?: string;\n};\n\n/** Options for path-aware edge checks (peer isolation). */\nexport type EdgeCheckOptions = {\n /** Repo-relative path of the importing file. */\n fromPath?: string;\n /** Repo-relative path of the imported module. */\n toPath?: string;\n /** Layer configs — used to infer sliceFolders when the rule omits them. */\n layers?: LayerConfig[];\n};\n\nconst regexpCache = new Map<string, RegExp>();\n\nfunction escapeLiteral(ch: string): string {\n return /[.*+?^${}()|[\\]\\\\]/.test(ch) ? `\\\\${ch}` : ch;\n}\n\n/**\n * Normalize path separators to `/` without destroying glob escape sequences.\n * `src\\domain\\x` → `src/domain/x` (Windows paths); `src/\\{legacy\\}/**` keeps `\\{` / `\\}`.\n * A plain `pattern.split('\\\\').join('/')` would eat those escapes.\n */\nfunction normalizeGlobSeparators(pattern: string): string {\n let out = '';\n for (let i = 0; i < pattern.length; i += 1) {\n const c = pattern[i];\n if (c === '\\\\' && i + 1 < pattern.length) {\n const next = pattern[i + 1];\n // Keep escapes for glob metacharacters (and escaped backslash).\n if ('*?{}[],'.includes(next) || next === '\\\\') {\n out += '\\\\' + next;\n i += 1;\n continue;\n }\n // Otherwise treat `\\` as a path separator (Windows).\n out += '/';\n continue;\n }\n out += c;\n }\n return out;\n}\n\nfunction bracesBalanced(glob: string): boolean {\n let depth = 0;\n for (let i = 0; i < glob.length; i += 1) {\n const c = glob[i];\n if (c === '\\\\') {\n i += 1;\n continue;\n }\n if (c === '{') depth += 1;\n else if (c === '}') {\n depth -= 1;\n if (depth < 0) return false;\n }\n }\n return depth === 0;\n}\n\nexport function globToRegExp(pattern: string): RegExp {\n const cached = regexpCache.get(pattern);\n if (cached) return cached;\n\n const glob = normalizeGlobSeparators(pattern);\n const useBraces = bracesBalanced(glob);\n let out = '';\n let braceDepth = 0;\n for (let i = 0; i < glob.length; i += 1) {\n const c = glob[i];\n if (c === '\\\\' && i + 1 < glob.length) {\n out += escapeLiteral(glob[i + 1]);\n i += 1;\n } else if (c === '*') {\n if (glob[i + 1] === '*') {\n if (glob[i + 2] === '/') {\n out += '(?:.*/)?';\n i += 2;\n } else {\n out += '.*';\n i += 1;\n }\n } else {\n out += '[^/]*';\n }\n } else if (c === '?') {\n out += '[^/]';\n } else if (c === '{' && useBraces) {\n out += '(?:';\n braceDepth += 1;\n } else if (c === '}' && useBraces && braceDepth > 0) {\n out += ')';\n braceDepth -= 1;\n } else if (c === ',' && useBraces && braceDepth > 0) {\n out += '|';\n } else {\n out += escapeLiteral(c);\n }\n }\n const re = new RegExp(`^${out}$`);\n regexpCache.set(pattern, re);\n return re;\n}\n\nexport function patternSpecificity(pattern: string): number {\n const glob = normalizeGlobSeparators(String(pattern));\n const beforeWildcard = glob.split('*')[0];\n const literalSegments = beforeWildcard.split('/').filter(Boolean).length;\n const literalLength = glob.replace(/\\*/g, '').length;\n return literalSegments * 10000 + literalLength;\n}\n\nexport function layerForRelativePath(\n relPath: string,\n layers: LayerConfig[] | undefined\n): string | undefined {\n // File paths (not globs): any OS separator → posix relative.\n const rel = String(relPath).split(/[/\\\\]/).join('/');\n let bestName: string | undefined;\n let bestScore = -1;\n for (const layer of layers ?? []) {\n if ((layer.exclude ?? []).some((pattern) => globToRegExp(pattern).test(rel))) {\n continue;\n }\n for (const pattern of layer.patterns ?? []) {\n if (globToRegExp(pattern).test(rel)) {\n const score = patternSpecificity(pattern);\n if (score > bestScore) {\n bestScore = score;\n bestName = layer.name;\n }\n }\n }\n }\n return bestName;\n}\n\n/**\n * Extract the slice id under a known folder name.\n * Includes the parent folder so `features/auth` ≠ `modules/auth`.\n * `src/features/auth/api.ts` + folders `[\"features\"]` → `\"features/auth\"`.\n */\nexport function sliceIdForPath(\n relPath: string,\n sliceFolders: string[] | undefined\n): string | undefined {\n if (!sliceFolders?.length) return undefined;\n const parts = String(relPath)\n .split(/[/\\\\]/)\n .filter(Boolean);\n const folders = new Set(sliceFolders.map((s) => String(s).toLowerCase()));\n for (let i = 0; i < parts.length - 1; i += 1) {\n if (folders.has(parts[i].toLowerCase())) {\n return `${parts[i]}/${parts[i + 1]}`;\n }\n }\n return undefined;\n}\n\n/**\n * Infer slice parent folders from layer globs: the path segment immediately\n * before a `*` or `**` wildcard (e.g. `src/features/**` → `features`).\n */\nexport function inferSliceFoldersFromPatterns(\n patterns: string[] | undefined\n): string[] {\n const out = new Set<string>();\n for (const pattern of patterns ?? []) {\n const glob = normalizeGlobSeparators(String(pattern));\n const parts = glob.split('/').filter(Boolean);\n for (let i = 0; i < parts.length; i += 1) {\n const part = parts[i];\n if ((part === '**' || part === '*') && i > 0) {\n const prev = parts[i - 1];\n if (prev && !prev.includes('*') && !prev.includes('{') && !prev.includes('}')) {\n out.add(prev);\n }\n }\n }\n }\n return [...out];\n}\n\nfunction resolveSliceFolders(\n rule: EdgeRule,\n layerName: string,\n layers: LayerConfig[] | undefined\n): string[] {\n if (Array.isArray(rule.sliceFolders) && rule.sliceFolders.length > 0) {\n return rule.sliceFolders.filter((s) => typeof s === 'string' && s.length > 0);\n }\n const layer = (layers ?? []).find((l) => l.name === layerName);\n return inferSliceFoldersFromPatterns(layer?.patterns);\n}\n\n/**\n * Find the first denying rule for a layer edge.\n *\n * Semantics (locked):\n * - Classic (`allowed: false`, no peerIsolation): deny cross-layer edges only.\n * Same-layer is always allowed (historical short-circuit).\n * - `peerIsolation: true` + `allowed: false`: deny only when importer and importee\n * resolve to **different** slice ids (same or cross layer). Same-slice → allow.\n * Missing paths or unclassifiable slices → fail-open (do not deny).\n */\nexport function findDeniedEdgeRule(\n rules: EdgeRule[] | undefined,\n from: string,\n to: string,\n options?: EdgeCheckOptions\n): EdgeRule | undefined {\n for (const rule of rules ?? []) {\n if (rule.from !== from || rule.to !== to) continue;\n if (rule.allowed !== false) continue;\n\n if (rule.peerIsolation) {\n const fromPath = options?.fromPath;\n const toPath = options?.toPath;\n if (!fromPath || !toPath) continue;\n\n const folders = resolveSliceFolders(rule, from, options?.layers);\n if (folders.length === 0) continue;\n\n const fromSlice = sliceIdForPath(fromPath, folders);\n const toSlice = sliceIdForPath(toPath, folders);\n if (!fromSlice || !toSlice) continue;\n if (fromSlice !== toSlice) return rule;\n continue; // same slice: this peerIsolation rule does not deny\n }\n\n // Classic deny — same-layer always allowed without peerIsolation\n if (from === to) continue;\n return rule;\n }\n return undefined;\n}\n\nexport function isEdgeDenied(\n rules: EdgeRule[] | undefined,\n from: string,\n to: string,\n options?: EdgeCheckOptions\n): boolean {\n return findDeniedEdgeRule(rules, from, to, options) !== undefined;\n}\n\n/** Codegen globs skipped by default scan (emitted into the CLI derived matcher). */\nexport const DEFAULT_GENERATED_FILE_GLOBS = [\n '**/*.gen.ts',\n '**/*.gen.tsx',\n '**/*.generated.ts',\n '**/*.generated.tsx',\n];\n\nexport type ScanExcludeConfig = {\n exclude?: string[];\n excludeGenerated?: boolean;\n};\n\nexport function scanExcludePatterns(config?: ScanExcludeConfig | null): string[] {\n const custom = Array.isArray(config?.exclude)\n ? config!.exclude!.filter((p) => typeof p === 'string')\n : [];\n const generated =\n config?.excludeGenerated === false ? [] : DEFAULT_GENERATED_FILE_GLOBS;\n return [...generated, ...custom];\n}\n\nexport function isScanExcludedRelative(\n relPath: string,\n config?: ScanExcludeConfig | null\n): boolean {\n const rel = String(relPath).split(/[/\\\\]/).join('/');\n return scanExcludePatterns(config).some((pattern) => globToRegExp(pattern).test(rel));\n}\n"],"mappings":"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","names":["rules"]}