@craft-ts/dev-tools 0.7.0-beta.13

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 (237) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -0
  3. package/generators.json +26 -0
  4. package/package.json +119 -0
  5. package/src/bin/craft-architecture-check.d.ts +2 -0
  6. package/src/bin/craft-architecture-check.js +51 -0
  7. package/src/bin/craft-architecture-check.js.map +1 -0
  8. package/src/bin/craft-brand.d.ts +2 -0
  9. package/src/bin/craft-brand.js +9 -0
  10. package/src/bin/craft-brand.js.map +1 -0
  11. package/src/bin/craft-graph.d.ts +2 -0
  12. package/src/bin/craft-graph.js +72 -0
  13. package/src/bin/craft-graph.js.map +1 -0
  14. package/src/bin/craft-migrate-architecture.d.ts +2 -0
  15. package/src/bin/craft-migrate-architecture.js +64 -0
  16. package/src/bin/craft-migrate-architecture.js.map +1 -0
  17. package/src/bin/craft-migrate-components.d.ts +2 -0
  18. package/src/bin/craft-migrate-components.js +67 -0
  19. package/src/bin/craft-migrate-components.js.map +1 -0
  20. package/src/bin/craft-migrate-primitives.d.ts +2 -0
  21. package/src/bin/craft-migrate-primitives.js +71 -0
  22. package/src/bin/craft-migrate-primitives.js.map +1 -0
  23. package/src/bin/craft-migrate-routes.d.ts +2 -0
  24. package/src/bin/craft-migrate-routes.js +77 -0
  25. package/src/bin/craft-migrate-routes.js.map +1 -0
  26. package/src/bin/craft-migrate-services.d.ts +2 -0
  27. package/src/bin/craft-migrate-services.js +75 -0
  28. package/src/bin/craft-migrate-services.js.map +1 -0
  29. package/src/bin/craft-migrate-template.d.ts +2 -0
  30. package/src/bin/craft-migrate-template.js +68 -0
  31. package/src/bin/craft-migrate-template.js.map +1 -0
  32. package/src/bin/craft-migrate.d.ts +2 -0
  33. package/src/bin/craft-migrate.js +91 -0
  34. package/src/bin/craft-migrate.js.map +1 -0
  35. package/src/bin/craft.d.ts +2 -0
  36. package/src/bin/craft.js +318 -0
  37. package/src/bin/craft.js.map +1 -0
  38. package/src/eslint-rules/anchor-has-href.cjs +35 -0
  39. package/src/eslint-rules/app-start-registry-match.cjs +355 -0
  40. package/src/eslint-rules/button-has-type.cjs +50 -0
  41. package/src/eslint-rules/control-has-accessible-name.cjs +56 -0
  42. package/src/eslint-rules/craft-component-name-match.cjs +8 -0
  43. package/src/eslint-rules/craft-computed-name-match.cjs +63 -0
  44. package/src/eslint-rules/craft-css-token-registry.cjs +17 -0
  45. package/src/eslint-rules/craft-css-var-naming.cjs +18 -0
  46. package/src/eslint-rules/craft-css-vars-contract.cjs +34 -0
  47. package/src/eslint-rules/craft-directive-name-match.cjs +8 -0
  48. package/src/eslint-rules/craft-method-name-match.cjs +8 -0
  49. package/src/eslint-rules/craft-name-match-utils.cjs +179 -0
  50. package/src/eslint-rules/craft-primitive-context.cjs +46 -0
  51. package/src/eslint-rules/craft-signal-source-name-match.cjs +8 -0
  52. package/src/eslint-rules/craft-source-name-match.cjs +8 -0
  53. package/src/eslint-rules/craft-styles-scope-safe.cjs +25 -0
  54. package/src/eslint-rules/css-rule-utils.cjs +109 -0
  55. package/src/eslint-rules/global-exception-registry-match.cjs +562 -0
  56. package/src/eslint-rules/heading-has-content.cjs +31 -0
  57. package/src/eslint-rules/html-helpers.cjs +115 -0
  58. package/src/eslint-rules/hyperscript-walk.cjs +249 -0
  59. package/src/eslint-rules/iframe-has-title.cjs +26 -0
  60. package/src/eslint-rules/img-has-alt.cjs +28 -0
  61. package/src/eslint-rules/index.cjs +209 -0
  62. package/src/eslint-rules/index.d.cts +5 -0
  63. package/src/eslint-rules/label-has-associated-control.cjs +67 -0
  64. package/src/eslint-rules/no-async-await.cjs +46 -0
  65. package/src/eslint-rules/no-craft-computed-side-effects.cjs +301 -0
  66. package/src/eslint-rules/no-craft-use-in-template.cjs +89 -0
  67. package/src/eslint-rules/no-craft-use.cjs +42 -0
  68. package/src/eslint-rules/no-direct-temporal-globals.cjs +73 -0
  69. package/src/eslint-rules/no-effect-in-params.cjs +265 -0
  70. package/src/eslint-rules/no-effect-outside-loaders.cjs +3 -0
  71. package/src/eslint-rules/no-ephemeral-template-form-state.cjs +131 -0
  72. package/src/eslint-rules/no-hardcoded-design-values.cjs +15 -0
  73. package/src/eslint-rules/no-heading-level-skip.cjs +101 -0
  74. package/src/eslint-rules/no-imperative-craft-resource-trigger.cjs +291 -0
  75. package/src/eslint-rules/no-important-in-component-styles.cjs +12 -0
  76. package/src/eslint-rules/no-injection-token.cjs +173 -0
  77. package/src/eslint-rules/no-invalid-insertion-pipe.cjs +162 -0
  78. package/src/eslint-rules/no-manual-route-provider-list.cjs +85 -0
  79. package/src/eslint-rules/no-noninteractive-element-interactions.cjs +66 -0
  80. package/src/eslint-rules/no-positive-tabindex.cjs +44 -0
  81. package/src/eslint-rules/no-redundant-primitive-insertion.cjs +361 -0
  82. package/src/eslint-rules/no-render-writes.cjs +106 -0
  83. package/src/eslint-rules/no-throw.cjs +132 -0
  84. package/src/eslint-rules/no-transition-actions.cjs +133 -0
  85. package/src/eslint-rules/no-type-assertions-in-template.cjs +211 -0
  86. package/src/eslint-rules/no-widened-route-provider-context.cjs +39 -0
  87. package/src/eslint-rules/prefer-browser-boundaries.cjs +92 -0
  88. package/src/eslint-rules/prefer-craft-http-transport.cjs +103 -0
  89. package/src/eslint-rules/prefer-craft-template-blocks.cjs +492 -0
  90. package/src/eslint-rules/prefer-direct-yieldable-callback.cjs +105 -0
  91. package/src/eslint-rules/prefer-named-html-helpers.cjs +33 -0
  92. package/src/eslint-rules/prefer-query-method-over-state-trigger.cjs +282 -0
  93. package/src/eslint-rules/prefer-relative-heading.cjs +43 -0
  94. package/src/eslint-rules/provide-host-name-match-component.cjs +455 -0
  95. package/src/eslint-rules/recommended-config.cjs +77 -0
  96. package/src/eslint-rules/require-assert-exhaustive-route-exceptions.cjs +259 -0
  97. package/src/eslint-rules/require-cascade-route-di-check.cjs +223 -0
  98. package/src/eslint-rules/require-child-route-mount-check.cjs +224 -0
  99. package/src/eslint-rules/require-component-monitoring.cjs +212 -0
  100. package/src/eslint-rules/require-craft-exception-handler.cjs +142 -0
  101. package/src/eslint-rules/require-craft-method-for-yieldable-callback.cjs +236 -0
  102. package/src/eslint-rules/require-craft-resource-trigger-yield.cjs +209 -0
  103. package/src/eslint-rules/require-effect-adapters.cjs +86 -0
  104. package/src/eslint-rules/require-exception-component-di-check.cjs +314 -0
  105. package/src/eslint-rules/require-focus-visible.cjs +36 -0
  106. package/src/eslint-rules/require-interactive-local-name.cjs +105 -0
  107. package/src/eslint-rules/require-lazy-load-with-retry.cjs +148 -0
  108. package/src/eslint-rules/require-outlet-heading-section.cjs +70 -0
  109. package/src/eslint-rules/require-pending-component-di-check.cjs +490 -0
  110. package/src/eslint-rules/require-primitive-context.cjs +156 -0
  111. package/src/eslint-rules/require-primitive-derived-property.cjs +598 -0
  112. package/src/eslint-rules/require-primitive-generator-unwrap.cjs +274 -0
  113. package/src/eslint-rules/require-reactive-template-bindings.cjs +305 -0
  114. package/src/eslint-rules/require-reduced-motion.cjs +38 -0
  115. package/src/eslint-rules/require-route-heading-outline.cjs +166 -0
  116. package/src/eslint-rules/require-yieldable-insertion-write.cjs +98 -0
  117. package/src/eslint-rules/require-yieldable-reactive-read.cjs +102 -0
  118. package/src/eslint-rules/require-yieldable-template-method.cjs +424 -0
  119. package/src/eslint-rules/role-has-required-aria.cjs +38 -0
  120. package/src/eslint-rules/server-function-client-match.cjs +283 -0
  121. package/src/eslint-rules/target-blank-noopener.cjs +31 -0
  122. package/src/eslint-rules/template-element-name-unique.cjs +124 -0
  123. package/src/eslint-rules/valid-aria.cjs +38 -0
  124. package/src/generators/route/compat.d.ts +3 -0
  125. package/src/generators/route/compat.js +6 -0
  126. package/src/generators/route/compat.js.map +1 -0
  127. package/src/generators/route/generator.d.ts +31 -0
  128. package/src/generators/route/generator.js +402 -0
  129. package/src/generators/route/generator.js.map +1 -0
  130. package/src/generators/route/schema.json +50 -0
  131. package/src/generators/route/split-schema.json +19 -0
  132. package/src/index.d.ts +21 -0
  133. package/src/index.js +23 -0
  134. package/src/index.js.map +1 -0
  135. package/src/scripts/angular-brand-codemod.d.ts +139 -0
  136. package/src/scripts/angular-brand-codemod.js +2318 -0
  137. package/src/scripts/angular-brand-codemod.js.map +1 -0
  138. package/src/scripts/angular-brand-codemod.ts +3985 -0
  139. package/src/scripts/architecture/migrate-architecture.d.ts +14 -0
  140. package/src/scripts/architecture/migrate-architecture.js +534 -0
  141. package/src/scripts/architecture/migrate-architecture.js.map +1 -0
  142. package/src/scripts/architecture/migrate-architecture.ts +653 -0
  143. package/src/scripts/architecture-graph.d.ts +368 -0
  144. package/src/scripts/architecture-graph.js +2191 -0
  145. package/src/scripts/architecture-graph.js.map +1 -0
  146. package/src/scripts/architecture-graph.ts +3136 -0
  147. package/src/scripts/components/migrate-components.d.ts +20 -0
  148. package/src/scripts/components/migrate-components.js +163 -0
  149. package/src/scripts/components/migrate-components.js.map +1 -0
  150. package/src/scripts/components/migrate-components.ts +227 -0
  151. package/src/scripts/components/migration-diagnostic.d.ts +7 -0
  152. package/src/scripts/components/migration-diagnostic.js +2 -0
  153. package/src/scripts/components/migration-diagnostic.js.map +1 -0
  154. package/src/scripts/components/migration-diagnostic.ts +8 -0
  155. package/src/scripts/create/create-project.d.ts +20 -0
  156. package/src/scripts/create/create-project.js +783 -0
  157. package/src/scripts/create/create-project.js.map +1 -0
  158. package/src/scripts/create/create-project.ts +849 -0
  159. package/src/scripts/data-flow-graph.d.ts +36 -0
  160. package/src/scripts/data-flow-graph.js +186 -0
  161. package/src/scripts/data-flow-graph.js.map +1 -0
  162. package/src/scripts/data-flow-graph.ts +240 -0
  163. package/src/scripts/dependency-graph.d.ts +152 -0
  164. package/src/scripts/dependency-graph.js +4456 -0
  165. package/src/scripts/dependency-graph.js.map +1 -0
  166. package/src/scripts/dependency-graph.ts +5908 -0
  167. package/src/scripts/effect-dependency-graph.d.ts +89 -0
  168. package/src/scripts/effect-dependency-graph.js +487 -0
  169. package/src/scripts/effect-dependency-graph.js.map +1 -0
  170. package/src/scripts/effect-dependency-graph.ts +630 -0
  171. package/src/scripts/migrate.d.ts +37 -0
  172. package/src/scripts/migrate.js +98 -0
  173. package/src/scripts/migrate.js.map +1 -0
  174. package/src/scripts/migrate.ts +162 -0
  175. package/src/scripts/migration-workspace.d.ts +2 -0
  176. package/src/scripts/migration-workspace.js +67 -0
  177. package/src/scripts/migration-workspace.js.map +1 -0
  178. package/src/scripts/migration-workspace.ts +93 -0
  179. package/src/scripts/primitives/migrate-named-primitives.d.ts +50 -0
  180. package/src/scripts/primitives/migrate-named-primitives.js +487 -0
  181. package/src/scripts/primitives/migrate-named-primitives.js.map +1 -0
  182. package/src/scripts/primitives/migrate-named-primitives.ts +627 -0
  183. package/src/scripts/primitives/migrate-primitive-generators.d.ts +30 -0
  184. package/src/scripts/primitives/migrate-primitive-generators.js +309 -0
  185. package/src/scripts/primitives/migrate-primitive-generators.js.map +1 -0
  186. package/src/scripts/primitives/migrate-primitive-generators.ts +410 -0
  187. package/src/scripts/primitives/migrate-primitives.d.ts +27 -0
  188. package/src/scripts/primitives/migrate-primitives.js +427 -0
  189. package/src/scripts/primitives/migrate-primitives.js.map +1 -0
  190. package/src/scripts/primitives/migrate-primitives.ts +655 -0
  191. package/src/scripts/primitives/migrate-yieldable-reactive-reads.d.ts +23 -0
  192. package/src/scripts/primitives/migrate-yieldable-reactive-reads.js +382 -0
  193. package/src/scripts/primitives/migrate-yieldable-reactive-reads.js.map +1 -0
  194. package/src/scripts/primitives/migrate-yieldable-reactive-reads.ts +499 -0
  195. package/src/scripts/primitives/migration-diagnostic.d.ts +8 -0
  196. package/src/scripts/primitives/migration-diagnostic.js +2 -0
  197. package/src/scripts/primitives/migration-diagnostic.js.map +1 -0
  198. package/src/scripts/primitives/migration-diagnostic.ts +15 -0
  199. package/src/scripts/routes/migrate-routes.d.ts +34 -0
  200. package/src/scripts/routes/migrate-routes.js +657 -0
  201. package/src/scripts/routes/migrate-routes.js.map +1 -0
  202. package/src/scripts/routes/migrate-routes.ts +895 -0
  203. package/src/scripts/routes/migration-diagnostic.d.ts +16 -0
  204. package/src/scripts/routes/migration-diagnostic.js +2 -0
  205. package/src/scripts/routes/migration-diagnostic.js.map +1 -0
  206. package/src/scripts/routes/migration-diagnostic.ts +25 -0
  207. package/src/scripts/routes/route-command.d.ts +75 -0
  208. package/src/scripts/routes/route-command.js +1169 -0
  209. package/src/scripts/routes/route-command.js.map +1 -0
  210. package/src/scripts/routes/route-command.ts +1807 -0
  211. package/src/scripts/routes/verify-routes.d.ts +60 -0
  212. package/src/scripts/routes/verify-routes.js +1121 -0
  213. package/src/scripts/routes/verify-routes.js.map +1 -0
  214. package/src/scripts/routes/verify-routes.ts +1511 -0
  215. package/src/scripts/services/config.d.ts +2 -0
  216. package/src/scripts/services/config.js +38 -0
  217. package/src/scripts/services/config.js.map +1 -0
  218. package/src/scripts/services/config.ts +60 -0
  219. package/src/scripts/services/migrate-services.d.ts +29 -0
  220. package/src/scripts/services/migrate-services.js +924 -0
  221. package/src/scripts/services/migrate-services.js.map +1 -0
  222. package/src/scripts/services/migrate-services.ts +1348 -0
  223. package/src/scripts/services/migration-diagnostic.d.ts +8 -0
  224. package/src/scripts/services/migration-diagnostic.js +2 -0
  225. package/src/scripts/services/migration-diagnostic.js.map +1 -0
  226. package/src/scripts/services/migration-diagnostic.ts +28 -0
  227. package/src/scripts/template/migrate-template.d.ts +16 -0
  228. package/src/scripts/template/migrate-template.js +349 -0
  229. package/src/scripts/template/migrate-template.js.map +1 -0
  230. package/src/scripts/template/migrate-template.ts +418 -0
  231. package/src/template-migration.d.ts +1 -0
  232. package/src/template-migration.js +2 -0
  233. package/src/template-migration.js.map +1 -0
  234. package/src/testing.d.ts +19 -0
  235. package/src/testing.js +2 -0
  236. package/src/testing.js.map +1 -0
  237. package/src/tsconfig.codemod.json +13 -0
@@ -0,0 +1,36 @@
1
+ import { type SourceFile } from 'ts-morph';
2
+ import type { DependencyGraphEdge, DependencyGraphNode } from './dependency-graph';
3
+ export interface DataClassificationNodeDetails {
4
+ readonly classification: string;
5
+ readonly source: 'schema-annotation';
6
+ readonly schemaName: string;
7
+ }
8
+ export interface ExternalOutputNodeDetails {
9
+ readonly boundary: 'server-function-response';
10
+ readonly serverFunctionId?: string;
11
+ readonly schemaName?: string;
12
+ readonly classifications: readonly string[];
13
+ }
14
+ export interface DataFlowRelationDetails {
15
+ readonly classification: string;
16
+ readonly resolution: 'static' | 'unknown';
17
+ }
18
+ declare module './dependency-graph' {
19
+ interface DependencyGraphNodeRegistry {
20
+ 'data-classification': DataClassificationNodeDetails;
21
+ 'external-output': ExternalOutputNodeDetails;
22
+ }
23
+ interface DependencyGraphEdgeRegistry {
24
+ 'exposes-data': DataFlowRelationDetails;
25
+ 'flows-data': DataFlowRelationDetails;
26
+ }
27
+ }
28
+ /**
29
+ * Extracts schema annotations and server-function response boundaries. The
30
+ * propagation is intentionally conservative: only statically named schema
31
+ * variables are followed, and no transform is assumed to remove sensitivity.
32
+ */
33
+ export declare function collectDataFlowGraph(sourceFiles: readonly SourceFile[]): {
34
+ nodes: DependencyGraphNode[];
35
+ edges: DependencyGraphEdge[];
36
+ };
@@ -0,0 +1,186 @@
1
+ import { Node, SyntaxKind, } from 'ts-morph';
2
+ const classificationId = (filePath, name, value) => `data-classification:${filePath}#${name}:${value}`;
3
+ const outputId = (filePath, line) => `external-output:${filePath}:${line}`;
4
+ /**
5
+ * Extracts schema annotations and server-function response boundaries. The
6
+ * propagation is intentionally conservative: only statically named schema
7
+ * variables are followed, and no transform is assumed to remove sensitivity.
8
+ */
9
+ export function collectDataFlowGraph(sourceFiles) {
10
+ const nodes = new Map();
11
+ const edges = new Map();
12
+ const schemas = new Map();
13
+ const schemaByName = new Map();
14
+ const addNode = (node) => {
15
+ if (!nodes.has(node.id))
16
+ nodes.set(node.id, node);
17
+ };
18
+ const addEdge = (edge) => {
19
+ const key = `${edge.from}:${edge.kind}:${edge.to}`;
20
+ if (!edges.has(key))
21
+ edges.set(key, edge);
22
+ };
23
+ const proof = (node, pattern) => ({
24
+ filePath: node.getSourceFile().getFilePath(),
25
+ line: node.getStartLineNumber(),
26
+ pattern,
27
+ });
28
+ for (const sourceFile of sourceFiles) {
29
+ for (const declaration of sourceFile.getVariableDeclarations()) {
30
+ const initializer = declaration.getInitializer();
31
+ if (!initializer || !containsSchema(initializer))
32
+ continue;
33
+ const name = declaration.getName();
34
+ const info = {
35
+ declaration,
36
+ filePath: sourceFile.getFilePath(),
37
+ name,
38
+ classifications: new Set(findSchemaAnnotations(initializer)),
39
+ };
40
+ schemas.set(`${sourceFile.getFilePath()}#${name}`, info);
41
+ schemaByName.set(name, [...(schemaByName.get(name) ?? []), info]);
42
+ }
43
+ }
44
+ const classifications = new Map();
45
+ for (const info of schemas.values()) {
46
+ for (const value of info.classifications) {
47
+ const annotation = findSchemaAnnotation(info.declaration.getInitializer(), value);
48
+ if (!annotation)
49
+ continue;
50
+ classifications.set(`${info.filePath}#${info.name}:${value}`, {
51
+ schema: info,
52
+ value,
53
+ node: annotation,
54
+ });
55
+ addNode({
56
+ id: classificationId(info.filePath, info.name, value),
57
+ kind: 'data-classification',
58
+ label: `${info.name} [${value}]`,
59
+ filePath: info.filePath,
60
+ line: info.declaration.getStartLineNumber(),
61
+ details: {
62
+ classification: value,
63
+ source: 'schema-annotation',
64
+ schemaName: info.name,
65
+ },
66
+ });
67
+ }
68
+ }
69
+ const classificationsFor = (info, seen = new Set()) => {
70
+ const key = `${info.filePath}#${info.name}`;
71
+ if (seen.has(key))
72
+ return new Set();
73
+ const nextSeen = new Set([...seen, key]);
74
+ const result = new Set(info.classifications);
75
+ const initializer = info.declaration.getInitializer();
76
+ if (!initializer)
77
+ return result;
78
+ for (const identifier of initializer.getDescendantsOfKind(SyntaxKind.Identifier)) {
79
+ for (const target of schemaByName.get(identifier.getText()) ?? []) {
80
+ for (const value of classificationsFor(target, nextSeen))
81
+ result.add(value);
82
+ }
83
+ }
84
+ return result;
85
+ };
86
+ for (const sourceFile of sourceFiles) {
87
+ if (!sourceFile.getBaseName().endsWith('.fn-serveur.ts'))
88
+ continue;
89
+ const serverFunction = sourceFile
90
+ .getDescendantsOfKind(SyntaxKind.CallExpression)
91
+ .find((call) => call.getExpression().getText() === 'serverFunction');
92
+ if (!serverFunction)
93
+ continue;
94
+ const id = serverFunction
95
+ .getArguments()[0]
96
+ ?.asKind(SyntaxKind.StringLiteral)
97
+ ?.getLiteralValue();
98
+ const options = serverFunction
99
+ .getArguments()[2]
100
+ ?.asKind(SyntaxKind.ObjectLiteralExpression);
101
+ const output = options
102
+ ?.getProperty('output')
103
+ ?.asKind(SyntaxKind.PropertyAssignment)
104
+ ?.getInitializer();
105
+ if (!output)
106
+ continue;
107
+ const schemaName = output.asKind(SyntaxKind.Identifier)?.getText();
108
+ const schema = schemaName
109
+ ? [...(schemaByName.get(schemaName) ?? [])][0]
110
+ : undefined;
111
+ const values = schema ? [...classificationsFor(schema)] : [];
112
+ const outputNode = {
113
+ id: outputId(sourceFile.getFilePath(), serverFunction.getStartLineNumber()),
114
+ kind: 'external-output',
115
+ label: `${id ?? 'server-function'} response`,
116
+ filePath: sourceFile.getFilePath(),
117
+ line: serverFunction.getStartLineNumber(),
118
+ details: {
119
+ boundary: 'server-function-response',
120
+ ...(id === undefined ? {} : { serverFunctionId: id }),
121
+ ...(schemaName === undefined ? {} : { schemaName }),
122
+ classifications: values,
123
+ },
124
+ };
125
+ addNode(outputNode);
126
+ for (const value of values) {
127
+ const source = [...classifications.values()].find((candidate) => candidate.value === value &&
128
+ classificationsFor(candidate.schema).has(value));
129
+ if (!source)
130
+ continue;
131
+ addEdge({
132
+ from: classificationId(source.schema.filePath, source.schema.name, value),
133
+ to: outputNode.id,
134
+ kind: 'exposes-data',
135
+ evidence: 'ast',
136
+ details: { classification: value, resolution: 'static' },
137
+ proof: proof(output, `server function output: ${schemaName ?? 'unknown schema'}`),
138
+ });
139
+ }
140
+ }
141
+ return { nodes: [...nodes.values()], edges: [...edges.values()] };
142
+ }
143
+ function containsSchema(node) {
144
+ return [
145
+ ...(Node.isCallExpression(node) ? [node] : []),
146
+ ...node.getDescendantsOfKind(SyntaxKind.CallExpression),
147
+ ].some((call) => call.getExpression().getText().startsWith('Schema.'));
148
+ }
149
+ function findSchemaAnnotations(node) {
150
+ const values = new Set();
151
+ for (const call of [
152
+ ...(Node.isCallExpression(node) ? [node] : []),
153
+ ...node.getDescendantsOfKind(SyntaxKind.CallExpression),
154
+ ]) {
155
+ const value = readSensitivity(call);
156
+ if (value)
157
+ values.add(value);
158
+ }
159
+ return [...values];
160
+ }
161
+ function findSchemaAnnotation(node, value) {
162
+ if (!node)
163
+ return undefined;
164
+ return [
165
+ ...(Node.isCallExpression(node) ? [node] : []),
166
+ ...node.getDescendantsOfKind(SyntaxKind.CallExpression),
167
+ ].find((call) => readSensitivity(call) === value);
168
+ }
169
+ function readSensitivity(call) {
170
+ // Effect expose l'annotation en méthode (`Schema.String.annotate({ … })`) ;
171
+ // `annotations` reste accepté pour les variantes qui l'orthographient ainsi.
172
+ const callee = call.getExpression().getText();
173
+ if (!callee.endsWith('annotate') && !callee.endsWith('annotations')) {
174
+ return undefined;
175
+ }
176
+ const object = call
177
+ .getArguments()[0]
178
+ ?.asKind(SyntaxKind.ObjectLiteralExpression);
179
+ return object
180
+ ?.getProperty('sensitivity')
181
+ ?.asKind(SyntaxKind.PropertyAssignment)
182
+ ?.getInitializer()
183
+ ?.asKind(SyntaxKind.StringLiteral)
184
+ ?.getLiteralValue();
185
+ }
186
+ //# sourceMappingURL=data-flow-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-flow-graph.js","sourceRoot":"","sources":["../../../../../libs/dev-tools/src/scripts/data-flow-graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,240 @@
1
+ import {
2
+ Node,
3
+ SyntaxKind,
4
+ type CallExpression,
5
+ type SourceFile,
6
+ type VariableDeclaration,
7
+ } from 'ts-morph';
8
+ import type {
9
+ DependencyGraphEdge,
10
+ DependencyGraphNode,
11
+ DependencyGraphProof,
12
+ } from './dependency-graph';
13
+
14
+ export interface DataClassificationNodeDetails {
15
+ readonly classification: string;
16
+ readonly source: 'schema-annotation';
17
+ readonly schemaName: string;
18
+ }
19
+
20
+ export interface ExternalOutputNodeDetails {
21
+ readonly boundary: 'server-function-response';
22
+ readonly serverFunctionId?: string;
23
+ readonly schemaName?: string;
24
+ readonly classifications: readonly string[];
25
+ }
26
+
27
+ export interface DataFlowRelationDetails {
28
+ readonly classification: string;
29
+ readonly resolution: 'static' | 'unknown';
30
+ }
31
+
32
+ declare module './dependency-graph' {
33
+ interface DependencyGraphNodeRegistry {
34
+ 'data-classification': DataClassificationNodeDetails;
35
+ 'external-output': ExternalOutputNodeDetails;
36
+ }
37
+
38
+ interface DependencyGraphEdgeRegistry {
39
+ 'exposes-data': DataFlowRelationDetails;
40
+ 'flows-data': DataFlowRelationDetails;
41
+ }
42
+ }
43
+
44
+ type SchemaInfo = {
45
+ readonly declaration: VariableDeclaration;
46
+ readonly filePath: string;
47
+ readonly name: string;
48
+ readonly classifications: Set<string>;
49
+ };
50
+
51
+ const classificationId = (filePath: string, name: string, value: string) =>
52
+ `data-classification:${filePath}#${name}:${value}`;
53
+
54
+ const outputId = (filePath: string, line: number) =>
55
+ `external-output:${filePath}:${line}`;
56
+
57
+ /**
58
+ * Extracts schema annotations and server-function response boundaries. The
59
+ * propagation is intentionally conservative: only statically named schema
60
+ * variables are followed, and no transform is assumed to remove sensitivity.
61
+ */
62
+ export function collectDataFlowGraph(
63
+ sourceFiles: readonly SourceFile[],
64
+ ): { nodes: DependencyGraphNode[]; edges: DependencyGraphEdge[] } {
65
+ const nodes = new Map<string, DependencyGraphNode>();
66
+ const edges = new Map<string, DependencyGraphEdge>();
67
+ const schemas = new Map<string, SchemaInfo>();
68
+ const schemaByName = new Map<string, SchemaInfo[]>();
69
+
70
+ const addNode = (node: DependencyGraphNode) => {
71
+ if (!nodes.has(node.id)) nodes.set(node.id, node);
72
+ };
73
+ const addEdge = (edge: DependencyGraphEdge) => {
74
+ const key = `${edge.from}:${edge.kind}:${edge.to}`;
75
+ if (!edges.has(key)) edges.set(key, edge);
76
+ };
77
+ const proof = (node: Node, pattern: string): DependencyGraphProof => ({
78
+ filePath: node.getSourceFile().getFilePath(),
79
+ line: node.getStartLineNumber(),
80
+ pattern,
81
+ });
82
+
83
+ for (const sourceFile of sourceFiles) {
84
+ for (const declaration of sourceFile.getVariableDeclarations()) {
85
+ const initializer = declaration.getInitializer();
86
+ if (!initializer || !containsSchema(initializer)) continue;
87
+ const name = declaration.getName();
88
+ const info: SchemaInfo = {
89
+ declaration,
90
+ filePath: sourceFile.getFilePath(),
91
+ name,
92
+ classifications: new Set(findSchemaAnnotations(initializer)),
93
+ };
94
+ schemas.set(`${sourceFile.getFilePath()}#${name}`, info);
95
+ schemaByName.set(name, [...(schemaByName.get(name) ?? []), info]);
96
+ }
97
+ }
98
+
99
+ const classifications = new Map<string, { schema: SchemaInfo; value: string; node: Node }>();
100
+ for (const info of schemas.values()) {
101
+ for (const value of info.classifications) {
102
+ const annotation = findSchemaAnnotation(info.declaration.getInitializer(), value);
103
+ if (!annotation) continue;
104
+ classifications.set(`${info.filePath}#${info.name}:${value}`, {
105
+ schema: info,
106
+ value,
107
+ node: annotation,
108
+ });
109
+ addNode({
110
+ id: classificationId(info.filePath, info.name, value),
111
+ kind: 'data-classification',
112
+ label: `${info.name} [${value}]`,
113
+ filePath: info.filePath,
114
+ line: info.declaration.getStartLineNumber(),
115
+ details: {
116
+ classification: value,
117
+ source: 'schema-annotation',
118
+ schemaName: info.name,
119
+ },
120
+ });
121
+ }
122
+ }
123
+
124
+ const classificationsFor = (info: SchemaInfo, seen = new Set<string>()): Set<string> => {
125
+ const key = `${info.filePath}#${info.name}`;
126
+ if (seen.has(key)) return new Set();
127
+ const nextSeen = new Set([...seen, key]);
128
+ const result = new Set(info.classifications);
129
+ const initializer = info.declaration.getInitializer();
130
+ if (!initializer) return result;
131
+ for (const identifier of initializer.getDescendantsOfKind(SyntaxKind.Identifier)) {
132
+ for (const target of schemaByName.get(identifier.getText()) ?? []) {
133
+ for (const value of classificationsFor(target, nextSeen)) result.add(value);
134
+ }
135
+ }
136
+ return result;
137
+ };
138
+
139
+ for (const sourceFile of sourceFiles) {
140
+ if (!sourceFile.getBaseName().endsWith('.fn-serveur.ts')) continue;
141
+ const serverFunction = sourceFile
142
+ .getDescendantsOfKind(SyntaxKind.CallExpression)
143
+ .find((call) => call.getExpression().getText() === 'serverFunction');
144
+ if (!serverFunction) continue;
145
+ const id = serverFunction
146
+ .getArguments()[0]
147
+ ?.asKind(SyntaxKind.StringLiteral)
148
+ ?.getLiteralValue();
149
+ const options = serverFunction
150
+ .getArguments()[2]
151
+ ?.asKind(SyntaxKind.ObjectLiteralExpression);
152
+ const output = options
153
+ ?.getProperty('output')
154
+ ?.asKind(SyntaxKind.PropertyAssignment)
155
+ ?.getInitializer();
156
+ if (!output) continue;
157
+ const schemaName = output.asKind(SyntaxKind.Identifier)?.getText();
158
+ const schema = schemaName
159
+ ? [...(schemaByName.get(schemaName) ?? [])][0]
160
+ : undefined;
161
+ const values = schema ? [...classificationsFor(schema)] : [];
162
+ const outputNode: DependencyGraphNode = {
163
+ id: outputId(sourceFile.getFilePath(), serverFunction.getStartLineNumber()),
164
+ kind: 'external-output',
165
+ label: `${id ?? 'server-function'} response`,
166
+ filePath: sourceFile.getFilePath(),
167
+ line: serverFunction.getStartLineNumber(),
168
+ details: {
169
+ boundary: 'server-function-response',
170
+ ...(id === undefined ? {} : { serverFunctionId: id }),
171
+ ...(schemaName === undefined ? {} : { schemaName }),
172
+ classifications: values,
173
+ },
174
+ };
175
+ addNode(outputNode);
176
+
177
+ for (const value of values) {
178
+ const source = [...classifications.values()].find(
179
+ (candidate) => candidate.value === value &&
180
+ classificationsFor(candidate.schema).has(value),
181
+ );
182
+ if (!source) continue;
183
+ addEdge({
184
+ from: classificationId(source.schema.filePath, source.schema.name, value),
185
+ to: outputNode.id,
186
+ kind: 'exposes-data',
187
+ evidence: 'ast',
188
+ details: { classification: value, resolution: 'static' },
189
+ proof: proof(output, `server function output: ${schemaName ?? 'unknown schema'}`),
190
+ });
191
+ }
192
+ }
193
+
194
+ return { nodes: [...nodes.values()], edges: [...edges.values()] };
195
+ }
196
+
197
+ function containsSchema(node: Node): boolean {
198
+ return [
199
+ ...(Node.isCallExpression(node) ? [node] : []),
200
+ ...node.getDescendantsOfKind(SyntaxKind.CallExpression),
201
+ ].some((call) => call.getExpression().getText().startsWith('Schema.'));
202
+ }
203
+
204
+ function findSchemaAnnotations(node: Node): string[] {
205
+ const values = new Set<string>();
206
+ for (const call of [
207
+ ...(Node.isCallExpression(node) ? [node] : []),
208
+ ...node.getDescendantsOfKind(SyntaxKind.CallExpression),
209
+ ]) {
210
+ const value = readSensitivity(call);
211
+ if (value) values.add(value);
212
+ }
213
+ return [...values];
214
+ }
215
+
216
+ function findSchemaAnnotation(node: Node | undefined, value: string): Node | undefined {
217
+ if (!node) return undefined;
218
+ return [
219
+ ...(Node.isCallExpression(node) ? [node] : []),
220
+ ...node.getDescendantsOfKind(SyntaxKind.CallExpression),
221
+ ].find((call) => readSensitivity(call) === value);
222
+ }
223
+
224
+ function readSensitivity(call: CallExpression): string | undefined {
225
+ // Effect expose l'annotation en méthode (`Schema.String.annotate({ … })`) ;
226
+ // `annotations` reste accepté pour les variantes qui l'orthographient ainsi.
227
+ const callee = call.getExpression().getText();
228
+ if (!callee.endsWith('annotate') && !callee.endsWith('annotations')) {
229
+ return undefined;
230
+ }
231
+ const object = call
232
+ .getArguments()[0]
233
+ ?.asKind(SyntaxKind.ObjectLiteralExpression);
234
+ return object
235
+ ?.getProperty('sensitivity')
236
+ ?.asKind(SyntaxKind.PropertyAssignment)
237
+ ?.getInitializer()
238
+ ?.asKind(SyntaxKind.StringLiteral)
239
+ ?.getLiteralValue();
240
+ }
@@ -0,0 +1,152 @@
1
+ import { Project, SourceFile } from 'ts-morph';
2
+ import './effect-dependency-graph.js';
3
+ import './data-flow-graph.js';
4
+ /**
5
+ * The graph's built-in vocabulary. Values are deliberately detail records
6
+ * rather than node classes: consumers can augment this interface with module
7
+ * augmentation without making the graph core import their runtime.
8
+ */
9
+ export interface DependencyGraphNodeRegistry {
10
+ route: Record<string, unknown>;
11
+ 'route-hook': Record<string, unknown>;
12
+ 'route-check': Record<string, unknown>;
13
+ 'app-config': Record<string, unknown>;
14
+ component: Record<string, unknown>;
15
+ service: Record<string, unknown>;
16
+ property: Record<string, unknown>;
17
+ primitive: Record<string, unknown>;
18
+ source: Record<string, unknown>;
19
+ 'http-endpoint': Record<string, unknown>;
20
+ unique: Record<string, unknown>;
21
+ 'template-element': Record<string, unknown>;
22
+ 'server-function-family': Record<string, unknown>;
23
+ 'server-function-contract': Record<string, unknown>;
24
+ 'server-function-client': Record<string, unknown>;
25
+ 'server-function-server': Record<string, unknown>;
26
+ 'server-function-misnamed': Record<string, unknown>;
27
+ 'server-function-middleware': Record<string, unknown>;
28
+ 'server-function-middleware-misnamed': Record<string, unknown>;
29
+ 'client-function-middleware': Record<string, unknown>;
30
+ 'client-function-middleware-misnamed': Record<string, unknown>;
31
+ handshake: Record<string, unknown>;
32
+ }
33
+ export type DependencyGraphNodeKind = keyof DependencyGraphNodeRegistry;
34
+ export type DependencyGraphNodeFor<K extends DependencyGraphNodeKind = DependencyGraphNodeKind> = {
35
+ id: string;
36
+ kind: K;
37
+ label: string;
38
+ filePath?: string;
39
+ line?: number;
40
+ details?: DependencyGraphNodeRegistry[K];
41
+ };
42
+ /**
43
+ * The built-in relation vocabulary. Relation details are open for the same
44
+ * reason as node details: an adapter can add a typed relation without
45
+ * changing this module.
46
+ */
47
+ export interface DependencyGraphEdgeRegistry {
48
+ loads: Record<string, unknown>;
49
+ renders: Record<string, unknown>;
50
+ contains: Record<string, unknown>;
51
+ checks: Record<string, unknown>;
52
+ 'depends-on': Record<string, unknown>;
53
+ provides: Record<string, unknown>;
54
+ 'uses-property': Record<string, unknown>;
55
+ calls: Record<string, unknown>;
56
+ reads: Record<string, unknown>;
57
+ writes: Record<string, unknown>;
58
+ subscribes: Record<string, unknown>;
59
+ triggers: Record<string, unknown>;
60
+ }
61
+ export type DependencyGraphEdgeKind = keyof DependencyGraphEdgeRegistry;
62
+ export type DependencyGraphEdgeFor<K extends DependencyGraphEdgeKind = DependencyGraphEdgeKind> = {
63
+ from: string;
64
+ to: string;
65
+ kind: K;
66
+ evidence: 'ast' | 'type';
67
+ details?: DependencyGraphEdgeRegistry[K];
68
+ proof?: DependencyGraphProof;
69
+ };
70
+ export type DependencyGraphProof = {
71
+ filePath: string;
72
+ line?: number;
73
+ symbol?: string;
74
+ pattern?: string;
75
+ };
76
+ export type DependencyGraphDiagnostic = {
77
+ code: string;
78
+ message: string;
79
+ proof?: DependencyGraphProof;
80
+ };
81
+ export type RouteCheckMechanism = 'ValidateCascadeRoutesFile' | 'RouteCheckedDI' | 'RouteExceptionComponentCheckedDI' | 'CanRun' | 'assertExhaustiveRouteExceptions';
82
+ export type DependencyGraphNode = {
83
+ id: string;
84
+ kind: DependencyGraphNodeKind;
85
+ label: string;
86
+ filePath?: string;
87
+ line?: number;
88
+ details?: Record<string, unknown>;
89
+ };
90
+ export type DependencyGraphHttpEndpoint = {
91
+ method: string;
92
+ url: string;
93
+ line: number;
94
+ };
95
+ export type DependencyGraphTemporalOperation = {
96
+ operation: string;
97
+ delay?: string;
98
+ line: number;
99
+ };
100
+ export type DependencyGraphEdge = {
101
+ from: string;
102
+ to: string;
103
+ kind: DependencyGraphEdgeKind;
104
+ evidence: 'ast' | 'type';
105
+ details?: Record<string, unknown>;
106
+ proof?: DependencyGraphProof;
107
+ };
108
+ export type DependencyGraph = {
109
+ version: 1;
110
+ rootDir: string;
111
+ tsConfigFilePath: string;
112
+ nodes: DependencyGraphNode[];
113
+ edges: DependencyGraphEdge[];
114
+ diagnostics?: DependencyGraphDiagnostic[];
115
+ };
116
+ export type AnalyzeDependencyGraphOptions = {
117
+ rootDir?: string;
118
+ tsConfigFilePath?: string;
119
+ include?: readonly string[];
120
+ /** Additional collectors are opt-in; importing their types has no effect. */
121
+ collectors?: readonly DependencyGraphCollector[];
122
+ /** Repository-declared middleware capabilities used by security rules. */
123
+ middlewareCapabilities?: Readonly<Record<string, readonly string[]>>;
124
+ };
125
+ export type DependencyGraphCollectorContext = {
126
+ readonly project: Project;
127
+ readonly rootDir: string;
128
+ readonly sourceFiles: readonly SourceFile[];
129
+ readonly graph: Readonly<DependencyGraph>;
130
+ };
131
+ export type DependencyGraphContribution = {
132
+ readonly nodes?: readonly DependencyGraphNode[];
133
+ readonly edges?: readonly DependencyGraphEdge[];
134
+ readonly diagnostics?: readonly DependencyGraphDiagnostic[];
135
+ };
136
+ export type DependencyGraphCollector = {
137
+ readonly name: string;
138
+ readonly collect: (context: DependencyGraphCollectorContext) => DependencyGraphContribution;
139
+ };
140
+ export type WriteDependencyGraphOptions = AnalyzeDependencyGraphOptions & {
141
+ outputPath: string;
142
+ format?: 'json' | 'mermaid' | 'html' | 'both' | 'all';
143
+ };
144
+ export declare function analyzeDependencyGraph(options?: AnalyzeDependencyGraphOptions): DependencyGraph;
145
+ export declare function writeDependencyGraph(options: WriteDependencyGraphOptions): Promise<DependencyGraph>;
146
+ export declare function dependencyGraphToMermaid(graph: DependencyGraph): string;
147
+ /**
148
+ * Creates a self-contained, static explorer. The graph is embedded in the file
149
+ * so opening the HTML never needs the source tree, a server, or application
150
+ * runtime instrumentation.
151
+ */
152
+ export declare function dependencyGraphToHtml(graph: DependencyGraph): string;