@eddacraft/anvil-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/LICENSE +14 -0
  2. package/dist/antipattern/index.d.ts +11 -0
  3. package/dist/antipattern/index.d.ts.map +1 -0
  4. package/dist/antipattern/index.js +31 -0
  5. package/dist/antipattern/patterns-css.d.ts +17 -0
  6. package/dist/antipattern/patterns-css.d.ts.map +1 -0
  7. package/dist/antipattern/patterns-css.js +72 -0
  8. package/dist/antipattern/patterns-html.d.ts +21 -0
  9. package/dist/antipattern/patterns-html.d.ts.map +1 -0
  10. package/dist/antipattern/patterns-html.js +139 -0
  11. package/dist/antipattern/patterns.d.ts +72 -0
  12. package/dist/antipattern/patterns.d.ts.map +1 -0
  13. package/dist/antipattern/patterns.js +301 -0
  14. package/dist/antipattern/scanner.d.ts +32 -0
  15. package/dist/antipattern/scanner.d.ts.map +1 -0
  16. package/dist/antipattern/scanner.js +89 -0
  17. package/dist/antipattern/types.d.ts +318 -0
  18. package/dist/antipattern/types.d.ts.map +1 -0
  19. package/dist/antipattern/types.js +278 -0
  20. package/dist/architecture/analyzer.d.ts +123 -0
  21. package/dist/architecture/analyzer.d.ts.map +1 -0
  22. package/dist/architecture/analyzer.js +321 -0
  23. package/dist/architecture/baseline.d.ts +112 -0
  24. package/dist/architecture/baseline.d.ts.map +1 -0
  25. package/dist/architecture/baseline.js +245 -0
  26. package/dist/architecture/compiler.d.ts +24 -0
  27. package/dist/architecture/compiler.d.ts.map +1 -0
  28. package/dist/architecture/compiler.js +57 -0
  29. package/dist/architecture/context.d.ts +129 -0
  30. package/dist/architecture/context.d.ts.map +1 -0
  31. package/dist/architecture/context.js +116 -0
  32. package/dist/architecture/dc-generator.d.ts +9 -0
  33. package/dist/architecture/dc-generator.d.ts.map +1 -0
  34. package/dist/architecture/dc-generator.js +220 -0
  35. package/dist/architecture/definition-schema.d.ts +128 -0
  36. package/dist/architecture/definition-schema.d.ts.map +1 -0
  37. package/dist/architecture/definition-schema.js +94 -0
  38. package/dist/architecture/edge-detector-html.d.ts +6 -0
  39. package/dist/architecture/edge-detector-html.d.ts.map +1 -0
  40. package/dist/architecture/edge-detector-html.js +5 -0
  41. package/dist/architecture/edge-detector-web.d.ts +32 -0
  42. package/dist/architecture/edge-detector-web.d.ts.map +1 -0
  43. package/dist/architecture/edge-detector-web.js +133 -0
  44. package/dist/architecture/edge-detector.d.ts +116 -0
  45. package/dist/architecture/edge-detector.d.ts.map +1 -0
  46. package/dist/architecture/edge-detector.js +229 -0
  47. package/dist/architecture/entry-detector.d.ts +44 -0
  48. package/dist/architecture/entry-detector.d.ts.map +1 -0
  49. package/dist/architecture/entry-detector.js +263 -0
  50. package/dist/architecture/index.d.ts +21 -0
  51. package/dist/architecture/index.d.ts.map +1 -0
  52. package/dist/architecture/index.js +48 -0
  53. package/dist/architecture/layer-detector.d.ts +60 -0
  54. package/dist/architecture/layer-detector.d.ts.map +1 -0
  55. package/dist/architecture/layer-detector.js +331 -0
  56. package/dist/architecture/rego-generator.d.ts +25 -0
  57. package/dist/architecture/rego-generator.d.ts.map +1 -0
  58. package/dist/architecture/rego-generator.js +229 -0
  59. package/dist/architecture/templates/index.d.ts +39 -0
  60. package/dist/architecture/templates/index.d.ts.map +1 -0
  61. package/dist/architecture/templates/index.js +124 -0
  62. package/dist/architecture/types.d.ts +280 -0
  63. package/dist/architecture/types.d.ts.map +1 -0
  64. package/dist/architecture/types.js +269 -0
  65. package/dist/architecture/yaml-parser.d.ts +13 -0
  66. package/dist/architecture/yaml-parser.d.ts.map +1 -0
  67. package/dist/architecture/yaml-parser.js +234 -0
  68. package/dist/config/constants.d.ts +9 -0
  69. package/dist/config/constants.d.ts.map +1 -0
  70. package/dist/config/constants.js +20 -0
  71. package/dist/config/index.d.ts +9 -0
  72. package/dist/config/index.d.ts.map +1 -0
  73. package/dist/config/index.js +8 -0
  74. package/dist/config/loader.d.ts +41 -0
  75. package/dist/config/loader.d.ts.map +1 -0
  76. package/dist/config/loader.js +76 -0
  77. package/dist/config/nudge-config.d.ts +35 -0
  78. package/dist/config/nudge-config.d.ts.map +1 -0
  79. package/dist/config/nudge-config.js +34 -0
  80. package/dist/config/types.d.ts +30 -0
  81. package/dist/config/types.d.ts.map +1 -0
  82. package/dist/config/types.js +4 -0
  83. package/dist/contracts/index.d.ts +14 -0
  84. package/dist/contracts/index.d.ts.map +1 -0
  85. package/dist/contracts/index.js +13 -0
  86. package/dist/contracts/schemas/aps.schema.d.ts +269 -0
  87. package/dist/contracts/schemas/aps.schema.d.ts.map +1 -0
  88. package/dist/contracts/schemas/aps.schema.js +183 -0
  89. package/dist/contracts/schemas/index.d.ts +12 -0
  90. package/dist/contracts/schemas/index.d.ts.map +1 -0
  91. package/dist/contracts/schemas/index.js +14 -0
  92. package/dist/contracts/schemas/json-schema.d.ts +14 -0
  93. package/dist/contracts/schemas/json-schema.d.ts.map +1 -0
  94. package/dist/contracts/schemas/json-schema.js +31 -0
  95. package/dist/contracts/schemas/warning.schema.d.ts +171 -0
  96. package/dist/contracts/schemas/warning.schema.d.ts.map +1 -0
  97. package/dist/contracts/schemas/warning.schema.js +123 -0
  98. package/dist/contracts/types/gate.types.d.ts +194 -0
  99. package/dist/contracts/types/gate.types.d.ts.map +1 -0
  100. package/dist/contracts/types/gate.types.js +19 -0
  101. package/dist/contracts/types/index.d.ts +9 -0
  102. package/dist/contracts/types/index.d.ts.map +1 -0
  103. package/dist/contracts/types/index.js +8 -0
  104. package/dist/crypto/hash.d.ts +47 -0
  105. package/dist/crypto/hash.d.ts.map +1 -0
  106. package/dist/crypto/hash.js +110 -0
  107. package/dist/crypto/index.d.ts +7 -0
  108. package/dist/crypto/index.d.ts.map +1 -0
  109. package/dist/crypto/index.js +6 -0
  110. package/dist/drift/index.d.ts +6 -0
  111. package/dist/drift/index.d.ts.map +1 -0
  112. package/dist/drift/index.js +5 -0
  113. package/dist/drift/report-generator.d.ts +21 -0
  114. package/dist/drift/report-generator.d.ts.map +1 -0
  115. package/dist/drift/report-generator.js +240 -0
  116. package/dist/drift/snapshot-capture.d.ts +26 -0
  117. package/dist/drift/snapshot-capture.d.ts.map +1 -0
  118. package/dist/drift/snapshot-capture.js +195 -0
  119. package/dist/drift/snapshot-compare.d.ts +50 -0
  120. package/dist/drift/snapshot-compare.d.ts.map +1 -0
  121. package/dist/drift/snapshot-compare.js +142 -0
  122. package/dist/drift/snapshot-schema.d.ts +197 -0
  123. package/dist/drift/snapshot-schema.d.ts.map +1 -0
  124. package/dist/drift/snapshot-schema.js +193 -0
  125. package/dist/drift/snapshot-storage.d.ts +25 -0
  126. package/dist/drift/snapshot-storage.d.ts.map +1 -0
  127. package/dist/drift/snapshot-storage.js +179 -0
  128. package/dist/explain/antipattern-explainer.d.ts +4 -0
  129. package/dist/explain/antipattern-explainer.d.ts.map +1 -0
  130. package/dist/explain/antipattern-explainer.js +196 -0
  131. package/dist/explain/boundary-explainer.d.ts +5 -0
  132. package/dist/explain/boundary-explainer.d.ts.map +1 -0
  133. package/dist/explain/boundary-explainer.js +261 -0
  134. package/dist/explain/explain-service.d.ts +19 -0
  135. package/dist/explain/explain-service.d.ts.map +1 -0
  136. package/dist/explain/explain-service.js +106 -0
  137. package/dist/explain/index.d.ts +7 -0
  138. package/dist/explain/index.d.ts.map +1 -0
  139. package/dist/explain/index.js +5 -0
  140. package/dist/explain/template-loader.d.ts +9 -0
  141. package/dist/explain/template-loader.d.ts.map +1 -0
  142. package/dist/explain/template-loader.js +51 -0
  143. package/dist/explain/types.d.ts +46 -0
  144. package/dist/explain/types.d.ts.map +1 -0
  145. package/dist/explain/types.js +31 -0
  146. package/dist/index.d.ts +26 -0
  147. package/dist/index.d.ts.map +1 -0
  148. package/dist/index.js +37 -0
  149. package/dist/provenance/collector.d.ts +86 -0
  150. package/dist/provenance/collector.d.ts.map +1 -0
  151. package/dist/provenance/collector.js +425 -0
  152. package/dist/provenance/git-ai-standard/git-notes.d.ts +85 -0
  153. package/dist/provenance/git-ai-standard/git-notes.d.ts.map +1 -0
  154. package/dist/provenance/git-ai-standard/git-notes.js +292 -0
  155. package/dist/provenance/git-ai-standard/index.d.ts +44 -0
  156. package/dist/provenance/git-ai-standard/index.d.ts.map +1 -0
  157. package/dist/provenance/git-ai-standard/index.js +47 -0
  158. package/dist/provenance/git-ai-standard/serializer.d.ts +54 -0
  159. package/dist/provenance/git-ai-standard/serializer.d.ts.map +1 -0
  160. package/dist/provenance/git-ai-standard/serializer.js +224 -0
  161. package/dist/provenance/git-ai-standard/session.d.ts +51 -0
  162. package/dist/provenance/git-ai-standard/session.d.ts.map +1 -0
  163. package/dist/provenance/git-ai-standard/session.js +118 -0
  164. package/dist/provenance/git-ai-standard/types.d.ts +173 -0
  165. package/dist/provenance/git-ai-standard/types.d.ts.map +1 -0
  166. package/dist/provenance/git-ai-standard/types.js +109 -0
  167. package/dist/provenance/index.d.ts +5 -0
  168. package/dist/provenance/index.d.ts.map +1 -0
  169. package/dist/provenance/index.js +6 -0
  170. package/dist/provenance/store.d.ts +83 -0
  171. package/dist/provenance/store.d.ts.map +1 -0
  172. package/dist/provenance/store.js +248 -0
  173. package/dist/provenance/types.d.ts +160 -0
  174. package/dist/provenance/types.d.ts.map +1 -0
  175. package/dist/provenance/types.js +112 -0
  176. package/dist/suppression/index.d.ts +4 -0
  177. package/dist/suppression/index.d.ts.map +1 -0
  178. package/dist/suppression/index.js +3 -0
  179. package/dist/suppression/parser.d.ts +31 -0
  180. package/dist/suppression/parser.d.ts.map +1 -0
  181. package/dist/suppression/parser.js +219 -0
  182. package/dist/suppression/service.d.ts +29 -0
  183. package/dist/suppression/service.d.ts.map +1 -0
  184. package/dist/suppression/service.js +132 -0
  185. package/dist/suppression/store.d.ts +61 -0
  186. package/dist/suppression/store.d.ts.map +1 -0
  187. package/dist/suppression/store.js +169 -0
  188. package/dist/utils/debug.d.ts +48 -0
  189. package/dist/utils/debug.d.ts.map +1 -0
  190. package/dist/utils/debug.js +100 -0
  191. package/dist/utils/index.d.ts +4 -0
  192. package/dist/utils/index.d.ts.map +1 -0
  193. package/dist/utils/index.js +3 -0
  194. package/dist/utils/path-safety.d.ts +21 -0
  195. package/dist/utils/path-safety.d.ts.map +1 -0
  196. package/dist/utils/path-safety.js +49 -0
  197. package/dist/utils/severity.d.ts +37 -0
  198. package/dist/utils/severity.d.ts.map +1 -0
  199. package/dist/utils/severity.js +22 -0
  200. package/dist/validation/aps-validator.d.ts +66 -0
  201. package/dist/validation/aps-validator.d.ts.map +1 -0
  202. package/dist/validation/aps-validator.js +173 -0
  203. package/dist/validation/errors.d.ts +52 -0
  204. package/dist/validation/errors.d.ts.map +1 -0
  205. package/dist/validation/errors.js +115 -0
  206. package/dist/validation/index.d.ts +8 -0
  207. package/dist/validation/index.d.ts.map +1 -0
  208. package/dist/validation/index.js +13 -0
  209. package/dist/warnings/index.d.ts +2 -0
  210. package/dist/warnings/index.d.ts.map +1 -0
  211. package/dist/warnings/index.js +1 -0
  212. package/dist/warnings/warning-id.d.ts +180 -0
  213. package/dist/warnings/warning-id.d.ts.map +1 -0
  214. package/dist/warnings/warning-id.js +257 -0
  215. package/package.json +79 -0
@@ -0,0 +1,220 @@
1
+ import { writeFile, mkdir, readFile } from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
3
+ import { join, dirname } from 'node:path';
4
+ import { createHash } from 'node:crypto';
5
+ import { createDebugger } from '../utils/debug.js';
6
+ const debug = createDebugger('compiler');
7
+ export const DC_CONFIG_FILENAME = 'dependency-cruiser.js';
8
+ export const ANVIL_DIR = '.anvil';
9
+ export function getDCConfigPath(workspaceRoot) {
10
+ return join(workspaceRoot, ANVIL_DIR, DC_CONFIG_FILENAME);
11
+ }
12
+ export function dcConfigExists(workspaceRoot) {
13
+ return existsSync(getDCConfigPath(workspaceRoot));
14
+ }
15
+ export async function needsRegeneration(workspaceRoot, definition) {
16
+ const configPath = getDCConfigPath(workspaceRoot);
17
+ if (!existsSync(configPath)) {
18
+ return true;
19
+ }
20
+ const existingContent = await readFile(configPath, 'utf-8');
21
+ const hashMatch = existingContent.match(/\/\/ hash: ([a-f0-9]+)/);
22
+ if (!hashMatch) {
23
+ return true;
24
+ }
25
+ const currentHash = computeDefinitionHash(definition);
26
+ return hashMatch[1] !== currentHash;
27
+ }
28
+ function computeDefinitionHash(definition) {
29
+ const content = JSON.stringify(definition);
30
+ return createHash('sha256').update(content).digest('hex').slice(0, 16);
31
+ }
32
+ export async function writeDCConfig(workspaceRoot, definition) {
33
+ const configPath = getDCConfigPath(workspaceRoot);
34
+ debug('writing dependency-cruiser config', configPath);
35
+ const configDir = dirname(configPath);
36
+ if (!existsSync(configDir)) {
37
+ await mkdir(configDir, { recursive: true });
38
+ }
39
+ const content = generateDCConfig(definition);
40
+ await writeFile(configPath, content, 'utf-8');
41
+ return configPath;
42
+ }
43
+ export function generateDCConfig(definition) {
44
+ const hash = computeDefinitionHash(definition);
45
+ const rules = generateRules(definition);
46
+ const options = definition.options ?? {
47
+ detect_orphans: true,
48
+ detect_circular: true,
49
+ default_severity: 'error',
50
+ exclude_patterns: ['**/*.test.ts', '**/*.spec.ts', '**/__tests__/**', '**/node_modules/**'],
51
+ };
52
+ const config = {
53
+ forbidden: rules,
54
+ options: {
55
+ doNotFollow: {
56
+ path: 'node_modules',
57
+ },
58
+ exclude: {
59
+ path: options.exclude_patterns,
60
+ },
61
+ tsPreCompilationDeps: true,
62
+ enhancedResolveOptions: {
63
+ exportsFields: ['exports'],
64
+ conditionNames: ['import', 'require', 'node', 'default'],
65
+ },
66
+ },
67
+ };
68
+ return `// Auto-generated by Anvil - do not edit manually
69
+ // hash: ${hash}
70
+ // Generated from: .anvil/architecture.yaml
71
+
72
+ /** @type {import('dependency-cruiser').IConfiguration} */
73
+ module.exports = ${JSON.stringify(config, null, 2)};
74
+ `;
75
+ }
76
+ /**
77
+ * Deduplicates rules by name, allowing later rules to override earlier ones.
78
+ *
79
+ * When multiple rules share the same name, the LAST rule wins but appears at
80
+ * the position of the FIRST occurrence. This allows user-defined rules in the
81
+ * architecture definition to override auto-generated rules (e.g., no-circular)
82
+ * while maintaining a predictable output order.
83
+ */
84
+ function deduplicateRules(rules) {
85
+ const byName = new Map();
86
+ for (let i = 0; i < rules.length; i++) {
87
+ const existing = byName.get(rules[i].name);
88
+ byName.set(rules[i].name, {
89
+ index: existing?.index ?? i,
90
+ rule: rules[i],
91
+ });
92
+ }
93
+ return Array.from(byName.values())
94
+ .sort((a, b) => a.index - b.index)
95
+ .map((entry) => entry.rule);
96
+ }
97
+ function generateRules(definition) {
98
+ const rules = [];
99
+ const options = definition.options ?? {
100
+ detect_orphans: true,
101
+ detect_circular: true,
102
+ default_severity: 'error',
103
+ };
104
+ if (options.detect_circular) {
105
+ rules.push({
106
+ name: 'no-circular',
107
+ severity: mapSeverity(options.default_severity),
108
+ comment: 'Circular dependencies are not allowed',
109
+ from: {},
110
+ to: { circular: true },
111
+ });
112
+ }
113
+ if (options.detect_orphans) {
114
+ rules.push({
115
+ name: 'no-orphans',
116
+ severity: mapSeverity(options.default_severity ?? 'warn'),
117
+ comment: 'Modules without dependents or dependencies',
118
+ from: {
119
+ pathNot: ['\\.d\\.ts$', '\\.test\\.(ts|js)$', '\\.spec\\.(ts|js)$', 'index\\.(ts|js)$'],
120
+ },
121
+ to: {},
122
+ });
123
+ }
124
+ const layerNames = Object.keys(definition.layers);
125
+ for (const [layerName, layerDef] of Object.entries(definition.layers)) {
126
+ const allowedDeps = layerDef.depends_on;
127
+ const disallowedLayers = layerNames.filter((l) => l !== layerName && !allowedDeps.includes(l));
128
+ if (disallowedLayers.length > 0) {
129
+ const fromPattern = layerPathsToRegex(layerDef.patterns);
130
+ const toPatterns = disallowedLayers
131
+ .map((l) => definition.layers[l]?.patterns ?? [])
132
+ .flat()
133
+ .map((p) => globToRegex(p));
134
+ if (toPatterns.length > 0) {
135
+ rules.push({
136
+ name: `no-${layerName}-to-disallowed`,
137
+ severity: mapSeverity(options.default_severity),
138
+ comment: `${layerName} layer must not depend on: ${disallowedLayers.join(', ')}`,
139
+ from: { path: fromPattern },
140
+ to: { path: toPatterns.join('|') },
141
+ });
142
+ }
143
+ }
144
+ }
145
+ for (const rule of definition.rules) {
146
+ const fromLayer = definition.layers[rule.from];
147
+ const toLayer = definition.layers[rule.to];
148
+ if (!fromLayer || !toLayer)
149
+ continue;
150
+ if (!rule.allowed) {
151
+ rules.push({
152
+ name: rule.name,
153
+ severity: mapSeverity(rule.severity),
154
+ comment: rule.message,
155
+ from: { path: layerPathsToRegex(fromLayer.patterns) },
156
+ to: { path: layerPathsToRegex(toLayer.patterns) },
157
+ });
158
+ }
159
+ }
160
+ return deduplicateRules(rules);
161
+ }
162
+ function mapSeverity(severity) {
163
+ switch (severity) {
164
+ case 'error':
165
+ return 'error';
166
+ case 'warn':
167
+ case 'warning':
168
+ return 'warn';
169
+ case 'info':
170
+ return 'info';
171
+ case 'ignore':
172
+ return 'ignore';
173
+ default:
174
+ throw new Error(`Unknown architecture rule severity "${severity}". Expected: error, warn, warning, info, ignore.`);
175
+ }
176
+ }
177
+ const REGEX_METACHARACTERS = /[+?()[\]{}|^$\\]/g;
178
+ function globToRegex(glob) {
179
+ let result = '';
180
+ let i = 0;
181
+ while (i < glob.length) {
182
+ const char = glob[i];
183
+ const nextChar = glob[i + 1];
184
+ const charAfterNext = glob[i + 2];
185
+ if (char === '*' && nextChar === '*') {
186
+ if (charAfterNext === '/') {
187
+ result += '(.*\\/)?';
188
+ i += 3;
189
+ }
190
+ else {
191
+ result += '.*';
192
+ i += 2;
193
+ }
194
+ }
195
+ else if (char === '*') {
196
+ result += '[^\\/]*';
197
+ i += 1;
198
+ }
199
+ else if (char === '.') {
200
+ result += '\\.';
201
+ i += 1;
202
+ }
203
+ else if (char === '/') {
204
+ result += '\\/';
205
+ i += 1;
206
+ }
207
+ else if (REGEX_METACHARACTERS.test(char)) {
208
+ result += '\\' + char;
209
+ i += 1;
210
+ }
211
+ else {
212
+ result += char;
213
+ i += 1;
214
+ }
215
+ }
216
+ return result;
217
+ }
218
+ function layerPathsToRegex(patterns) {
219
+ return patterns.map((p) => globToRegex(p) + '$').join('|');
220
+ }
@@ -0,0 +1,128 @@
1
+ import { z } from 'zod';
2
+ export declare const ARCHITECTURE_DEFINITION_VERSION: "0.1.0";
3
+ export declare const ArchitectureTemplateSchema: z.ZodEnum<{
4
+ custom: "custom";
5
+ starter: "starter";
6
+ layered: "layered";
7
+ hexagonal: "hexagonal";
8
+ clean: "clean";
9
+ ddd: "ddd";
10
+ monorepo: "monorepo";
11
+ serverless: "serverless";
12
+ "nx-workspace": "nx-workspace";
13
+ }>;
14
+ export type ArchitectureTemplate = z.infer<typeof ArchitectureTemplateSchema>;
15
+ export declare const LayerDefinitionSchema: z.ZodObject<{
16
+ patterns: z.ZodArray<z.ZodString>;
17
+ depends_on: z.ZodDefault<z.ZodArray<z.ZodString>>;
18
+ description: z.ZodOptional<z.ZodString>;
19
+ }, z.core.$strip>;
20
+ export type LayerDefinition = z.infer<typeof LayerDefinitionSchema>;
21
+ export declare const BoundedContextSchema: z.ZodObject<{
22
+ layers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
23
+ patterns: z.ZodArray<z.ZodString>;
24
+ depends_on: z.ZodDefault<z.ZodArray<z.ZodString>>;
25
+ description: z.ZodOptional<z.ZodString>;
26
+ }, z.core.$strip>>>;
27
+ allowed_dependencies: z.ZodDefault<z.ZodArray<z.ZodString>>;
28
+ description: z.ZodOptional<z.ZodString>;
29
+ }, z.core.$strip>;
30
+ export type BoundedContext = z.infer<typeof BoundedContextSchema>;
31
+ export declare const RuleSeveritySchema: z.ZodEnum<{
32
+ error: "error";
33
+ info: "info";
34
+ warn: "warn";
35
+ ignore: "ignore";
36
+ }>;
37
+ export type RuleSeverity = z.infer<typeof RuleSeveritySchema>;
38
+ export declare const ArchitectureRuleSchema: z.ZodObject<{
39
+ name: z.ZodString;
40
+ from: z.ZodString;
41
+ to: z.ZodString;
42
+ severity: z.ZodDefault<z.ZodEnum<{
43
+ error: "error";
44
+ info: "info";
45
+ warn: "warn";
46
+ ignore: "ignore";
47
+ }>>;
48
+ allowed: z.ZodDefault<z.ZodBoolean>;
49
+ message: z.ZodOptional<z.ZodString>;
50
+ }, z.core.$strip>;
51
+ export type ArchitectureRule = z.infer<typeof ArchitectureRuleSchema>;
52
+ export declare const ArchitectureOptionsSchema: z.ZodObject<{
53
+ detect_orphans: z.ZodDefault<z.ZodBoolean>;
54
+ detect_circular: z.ZodDefault<z.ZodBoolean>;
55
+ default_severity: z.ZodDefault<z.ZodEnum<{
56
+ error: "error";
57
+ info: "info";
58
+ warn: "warn";
59
+ ignore: "ignore";
60
+ }>>;
61
+ exclude_patterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
62
+ }, z.core.$strip>;
63
+ export type ArchitectureOptions = z.infer<typeof ArchitectureOptionsSchema>;
64
+ export declare const ArchitectureDefinitionSchema: z.ZodObject<{
65
+ schema_version: z.ZodDefault<z.ZodLiteral<"0.1.0">>;
66
+ template: z.ZodDefault<z.ZodEnum<{
67
+ custom: "custom";
68
+ starter: "starter";
69
+ layered: "layered";
70
+ hexagonal: "hexagonal";
71
+ clean: "clean";
72
+ ddd: "ddd";
73
+ monorepo: "monorepo";
74
+ serverless: "serverless";
75
+ "nx-workspace": "nx-workspace";
76
+ }>>;
77
+ layers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
78
+ patterns: z.ZodArray<z.ZodString>;
79
+ depends_on: z.ZodDefault<z.ZodArray<z.ZodString>>;
80
+ description: z.ZodOptional<z.ZodString>;
81
+ }, z.core.$strip>>>;
82
+ bounded_contexts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
83
+ layers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
84
+ patterns: z.ZodArray<z.ZodString>;
85
+ depends_on: z.ZodDefault<z.ZodArray<z.ZodString>>;
86
+ description: z.ZodOptional<z.ZodString>;
87
+ }, z.core.$strip>>>;
88
+ allowed_dependencies: z.ZodDefault<z.ZodArray<z.ZodString>>;
89
+ description: z.ZodOptional<z.ZodString>;
90
+ }, z.core.$strip>>>;
91
+ rules: z.ZodDefault<z.ZodArray<z.ZodObject<{
92
+ name: z.ZodString;
93
+ from: z.ZodString;
94
+ to: z.ZodString;
95
+ severity: z.ZodDefault<z.ZodEnum<{
96
+ error: "error";
97
+ info: "info";
98
+ warn: "warn";
99
+ ignore: "ignore";
100
+ }>>;
101
+ allowed: z.ZodDefault<z.ZodBoolean>;
102
+ message: z.ZodOptional<z.ZodString>;
103
+ }, z.core.$strip>>>;
104
+ options: z.ZodOptional<z.ZodObject<{
105
+ detect_orphans: z.ZodDefault<z.ZodBoolean>;
106
+ detect_circular: z.ZodDefault<z.ZodBoolean>;
107
+ default_severity: z.ZodDefault<z.ZodEnum<{
108
+ error: "error";
109
+ info: "info";
110
+ warn: "warn";
111
+ ignore: "ignore";
112
+ }>>;
113
+ exclude_patterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
114
+ }, z.core.$strip>>;
115
+ }, z.core.$strip>;
116
+ export type ArchitectureDefinition = z.infer<typeof ArchitectureDefinitionSchema>;
117
+ export declare const AVAILABLE_TEMPLATES: ArchitectureTemplate[];
118
+ export declare function getAvailableTemplates(): ArchitectureTemplate[];
119
+ export declare function isValidTemplate(template: string): template is ArchitectureTemplate;
120
+ export declare function validateArchitectureDefinition(data: unknown): {
121
+ success: true;
122
+ data: ArchitectureDefinition;
123
+ } | {
124
+ success: false;
125
+ error: z.ZodError;
126
+ };
127
+ export declare function getDefaultOptions(): ArchitectureOptions;
128
+ //# sourceMappingURL=definition-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition-schema.d.ts","sourceRoot":"","sources":["../../src/architecture/definition-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,+BAA+B,EAAG,OAAgB,CAAC;AAEhE,eAAO,MAAM,0BAA0B;;;;;;;;;;EAUrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,eAAO,MAAM,qBAAqB;;;;iBAIhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,eAAO,MAAM,oBAAoB;;;;;;;;iBAI/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,kBAAkB;;;;;EAA8C,CAAC;AAC9E,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;iBAOjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,eAAO,MAAM,yBAAyB;;;;;;;;;;iBAapC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBASvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF,eAAO,MAAM,mBAAmB,EAAE,oBAAoB,EAUrD,CAAC;AAEF,wBAAgB,qBAAqB,IAAI,oBAAoB,EAAE,CAE9D;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,IAAI,oBAAoB,CAElF;AAED,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,OAAO,GACZ;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,sBAAsB,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAA;CAAE,CAMzF;AAED,wBAAgB,iBAAiB,IAAI,mBAAmB,CAavD"}
@@ -0,0 +1,94 @@
1
+ import { z } from 'zod';
2
+ export const ARCHITECTURE_DEFINITION_VERSION = '0.1.0';
3
+ export const ArchitectureTemplateSchema = z.enum([
4
+ 'starter',
5
+ 'layered',
6
+ 'hexagonal',
7
+ 'clean',
8
+ 'ddd',
9
+ 'monorepo',
10
+ 'serverless',
11
+ 'nx-workspace',
12
+ 'custom',
13
+ ]);
14
+ export const LayerDefinitionSchema = z.object({
15
+ patterns: z.array(z.string()).min(1),
16
+ depends_on: z.array(z.string()).default([]),
17
+ description: z.string().optional(),
18
+ });
19
+ export const BoundedContextSchema = z.object({
20
+ layers: z.record(z.string(), LayerDefinitionSchema).optional(),
21
+ allowed_dependencies: z.array(z.string()).default([]),
22
+ description: z.string().optional(),
23
+ });
24
+ export const RuleSeveritySchema = z.enum(['error', 'warn', 'info', 'ignore']);
25
+ export const ArchitectureRuleSchema = z.object({
26
+ name: z.string(),
27
+ from: z.string(),
28
+ to: z.string(),
29
+ severity: RuleSeveritySchema.default('error'),
30
+ allowed: z.boolean().default(false),
31
+ message: z.string().optional(),
32
+ });
33
+ export const ArchitectureOptionsSchema = z.object({
34
+ detect_orphans: z.boolean().default(true),
35
+ detect_circular: z.boolean().default(true),
36
+ default_severity: RuleSeveritySchema.default('error'),
37
+ exclude_patterns: z
38
+ .array(z.string())
39
+ .default([
40
+ '**/*.test.ts',
41
+ '**/*.spec.ts',
42
+ '**/__tests__/**',
43
+ '**/__fixtures__/**',
44
+ '**/node_modules/**',
45
+ ]),
46
+ });
47
+ export const ArchitectureDefinitionSchema = z.object({
48
+ schema_version: z
49
+ .literal(ARCHITECTURE_DEFINITION_VERSION)
50
+ .default(ARCHITECTURE_DEFINITION_VERSION),
51
+ template: ArchitectureTemplateSchema.default('custom'),
52
+ layers: z.record(z.string(), LayerDefinitionSchema).default({}),
53
+ bounded_contexts: z.record(z.string(), BoundedContextSchema).optional(),
54
+ rules: z.array(ArchitectureRuleSchema).default([]),
55
+ options: ArchitectureOptionsSchema.optional(),
56
+ });
57
+ export const AVAILABLE_TEMPLATES = [
58
+ 'starter',
59
+ 'layered',
60
+ 'hexagonal',
61
+ 'clean',
62
+ 'ddd',
63
+ 'monorepo',
64
+ 'serverless',
65
+ 'nx-workspace',
66
+ 'custom',
67
+ ];
68
+ export function getAvailableTemplates() {
69
+ return [...AVAILABLE_TEMPLATES];
70
+ }
71
+ export function isValidTemplate(template) {
72
+ return AVAILABLE_TEMPLATES.includes(template);
73
+ }
74
+ export function validateArchitectureDefinition(data) {
75
+ const result = ArchitectureDefinitionSchema.safeParse(data);
76
+ if (result.success) {
77
+ return { success: true, data: result.data };
78
+ }
79
+ return { success: false, error: result.error };
80
+ }
81
+ export function getDefaultOptions() {
82
+ return {
83
+ detect_orphans: true,
84
+ detect_circular: true,
85
+ default_severity: 'error',
86
+ exclude_patterns: [
87
+ '**/*.test.ts',
88
+ '**/*.spec.ts',
89
+ '**/__tests__/**',
90
+ '**/__fixtures__/**',
91
+ '**/node_modules/**',
92
+ ],
93
+ };
94
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @deprecated This file has been renamed to edge-detector-web.ts
3
+ * This re-export exists for backward compatibility.
4
+ */
5
+ export { extractHtmlEdges, extractCssEdges } from './edge-detector-web.js';
6
+ //# sourceMappingURL=edge-detector-html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge-detector-html.d.ts","sourceRoot":"","sources":["../../src/architecture/edge-detector-html.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @deprecated This file has been renamed to edge-detector-web.ts
3
+ * This re-export exists for backward compatibility.
4
+ */
5
+ export { extractHtmlEdges, extractCssEdges } from './edge-detector-web.js';
@@ -0,0 +1,32 @@
1
+ /**
2
+ * HTML/CSS Edge Detector
3
+ *
4
+ * Extracts dependency edges from HTML and CSS files.
5
+ * Detects:
6
+ * - <script src="..."> references
7
+ * - <link href="..."> stylesheet references
8
+ * - CSS @import url("...") references
9
+ * - CSS url() references
10
+ *
11
+ * External URLs (http/https, data:, //) are skipped per D-002.
12
+ *
13
+ * @module architecture/edge-detector-web
14
+ */
15
+ import type { ImportEdge } from './edge-detector.js';
16
+ /**
17
+ * Extract dependency edges from HTML file content
18
+ *
19
+ * @param filePath - Path to HTML file (relative to workspace)
20
+ * @param content - File content
21
+ * @returns Array of import edges found in the file
22
+ */
23
+ export declare function extractHtmlEdges(filePath: string, content: string): ImportEdge[];
24
+ /**
25
+ * Extract dependency edges from CSS file content
26
+ *
27
+ * @param filePath - Path to CSS file (relative to workspace)
28
+ * @param content - File content
29
+ * @returns Array of import edges found in the file
30
+ */
31
+ export declare function extractCssEdges(filePath: string, content: string): ImportEdge[];
32
+ //# sourceMappingURL=edge-detector-web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge-detector-web.d.ts","sourceRoot":"","sources":["../../src/architecture/edge-detector-web.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAiCrD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,CA6ChF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,CA0C/E"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * HTML/CSS Edge Detector
3
+ *
4
+ * Extracts dependency edges from HTML and CSS files.
5
+ * Detects:
6
+ * - <script src="..."> references
7
+ * - <link href="..."> stylesheet references
8
+ * - CSS @import url("...") references
9
+ * - CSS url() references
10
+ *
11
+ * External URLs (http/https, data:, //) are skipped per D-002.
12
+ *
13
+ * @module architecture/edge-detector-web
14
+ */
15
+ import { resolveImportPath } from './edge-detector.js';
16
+ // HTML regexes
17
+ const SCRIPT_SRC_REGEX = /<script[^>]+src\s*=\s*["']([^"']+)["']/g;
18
+ const LINK_TAG_REGEX = /<link\s[^>]*>/g;
19
+ const HREF_ATTR_REGEX = /href\s*=\s*["']([^"']+)["']/;
20
+ // CSS regexes
21
+ const CSS_IMPORT_REGEX = /@import\s+(?:url\(\s*(?:["']([^"']+)["']|([^)]+?))\s*\)|["']([^"']+)["'])/g;
22
+ const CSS_URL_REGEX = /url\(\s*["']?([^"')]+)["']?\s*\)/g;
23
+ /**
24
+ * Check if a URL is external (http/https, data:, or protocol-relative //)
25
+ */
26
+ function isExternalUrl(url) {
27
+ return (url.startsWith('http://') ||
28
+ url.startsWith('https://') ||
29
+ url.startsWith('data:') ||
30
+ url.startsWith('//'));
31
+ }
32
+ /**
33
+ * Check if a <link> tag is a stylesheet reference
34
+ * Matches rel="stylesheet" or .css extension in href
35
+ */
36
+ function isStylesheetLink(fullTag, href) {
37
+ return /rel\s*=\s*["']stylesheet["']/i.test(fullTag) || href.endsWith('.css');
38
+ }
39
+ /**
40
+ * Extract dependency edges from HTML file content
41
+ *
42
+ * @param filePath - Path to HTML file (relative to workspace)
43
+ * @param content - File content
44
+ * @returns Array of import edges found in the file
45
+ */
46
+ export function extractHtmlEdges(filePath, content) {
47
+ const edges = [];
48
+ const lines = content.split('\n');
49
+ for (let i = 0; i < lines.length; i++) {
50
+ const line = lines[i];
51
+ const lineNumber = i + 1;
52
+ // <script src="...">
53
+ let match;
54
+ SCRIPT_SRC_REGEX.lastIndex = 0;
55
+ while ((match = SCRIPT_SRC_REGEX.exec(line)) !== null) {
56
+ const specifier = match[1];
57
+ if (!isExternalUrl(specifier)) {
58
+ edges.push({
59
+ from: filePath,
60
+ to: resolveImportPath(specifier, filePath),
61
+ line: lineNumber,
62
+ type: 'import',
63
+ specifier,
64
+ });
65
+ }
66
+ }
67
+ // <link href="..."> (stylesheet only)
68
+ LINK_TAG_REGEX.lastIndex = 0;
69
+ while ((match = LINK_TAG_REGEX.exec(line)) !== null) {
70
+ const fullTag = match[0];
71
+ const hrefMatch = fullTag.match(HREF_ATTR_REGEX);
72
+ if (hrefMatch) {
73
+ const specifier = hrefMatch[1];
74
+ if (!isExternalUrl(specifier) && isStylesheetLink(fullTag, specifier)) {
75
+ edges.push({
76
+ from: filePath,
77
+ to: resolveImportPath(specifier, filePath),
78
+ line: lineNumber,
79
+ type: 'import',
80
+ specifier,
81
+ });
82
+ }
83
+ }
84
+ }
85
+ }
86
+ return edges;
87
+ }
88
+ /**
89
+ * Extract dependency edges from CSS file content
90
+ *
91
+ * @param filePath - Path to CSS file (relative to workspace)
92
+ * @param content - File content
93
+ * @returns Array of import edges found in the file
94
+ */
95
+ export function extractCssEdges(filePath, content) {
96
+ const edges = [];
97
+ const lines = content.split('\n');
98
+ for (let i = 0; i < lines.length; i++) {
99
+ const line = lines[i];
100
+ const lineNumber = i + 1;
101
+ // @import url("...") or @import "..." or @import url(...)
102
+ let match;
103
+ CSS_IMPORT_REGEX.lastIndex = 0;
104
+ while ((match = CSS_IMPORT_REGEX.exec(line)) !== null) {
105
+ const specifier = (match[1] ?? match[2] ?? match[3])?.trim();
106
+ if (specifier && !isExternalUrl(specifier)) {
107
+ edges.push({
108
+ from: filePath,
109
+ to: resolveImportPath(specifier, filePath),
110
+ line: lineNumber,
111
+ type: 'import',
112
+ specifier,
113
+ });
114
+ }
115
+ }
116
+ // url() references (skip external and data: URIs)
117
+ CSS_URL_REGEX.lastIndex = 0;
118
+ while ((match = CSS_URL_REGEX.exec(line)) !== null) {
119
+ const specifier = match[1];
120
+ // Skip if already captured by @import, external, or data: URI
121
+ if (!isExternalUrl(specifier) && !line.includes('@import')) {
122
+ edges.push({
123
+ from: filePath,
124
+ to: resolveImportPath(specifier, filePath),
125
+ line: lineNumber,
126
+ type: 'import',
127
+ specifier,
128
+ });
129
+ }
130
+ }
131
+ }
132
+ return edges;
133
+ }