forgeos 0.1.0-alpha.0 → 0.1.0-alpha.2

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 (283) hide show
  1. package/.npmignore +9 -1
  2. package/AGENTS.md +6 -1
  3. package/CHANGELOG.md +30 -0
  4. package/CONTRIBUTING.md +22 -1
  5. package/README.md +30 -3
  6. package/bin/forge.mjs +4 -3
  7. package/package.json +3 -1
  8. package/packages/eslint-plugin-forge/index.ts +15 -15
  9. package/packages/eslint-plugin-forge/package.json +10 -10
  10. package/packages/eslint-plugin-forge/src/check-source.ts +95 -95
  11. package/packages/eslint-plugin-forge/src/load-artifacts.ts +24 -24
  12. package/packages/eslint-plugin-forge/src/rule-no-forge-guard-violation.ts +93 -93
  13. package/src/forge/_generated/actionSubscriptions.json +2 -2
  14. package/src/forge/_generated/actionSubscriptions.ts +3 -3
  15. package/src/forge/_generated/agentAdapterManifest.json +2 -2
  16. package/src/forge/_generated/agentAdapterManifest.ts +3 -3
  17. package/src/forge/_generated/agentContract.json +2 -2
  18. package/src/forge/_generated/agentContract.ts +6786 -2
  19. package/src/forge/_generated/agentQuickstart.md +1 -1
  20. package/src/forge/_generated/aiContext.ts +1 -1
  21. package/src/forge/_generated/aiModels.json +1 -1
  22. package/src/forge/_generated/aiModels.ts +1 -1
  23. package/src/forge/_generated/aiProviders.json +1 -1
  24. package/src/forge/_generated/aiProviders.ts +1 -1
  25. package/src/forge/_generated/aiRegistry.json +2 -2
  26. package/src/forge/_generated/aiRegistry.ts +3 -3
  27. package/src/forge/_generated/api.json +2 -2
  28. package/src/forge/_generated/api.ts +1 -1
  29. package/src/forge/_generated/appGraph.json +2 -2
  30. package/src/forge/_generated/appGraph.ts +1297 -1141
  31. package/src/forge/_generated/appMap.md +1 -1
  32. package/src/forge/_generated/artifactManifest.json +2 -2
  33. package/src/forge/_generated/artifactManifest.ts +2 -2
  34. package/src/forge/_generated/authClaims.json +1 -1
  35. package/src/forge/_generated/authClaims.ts +1 -1
  36. package/src/forge/_generated/authConfig.json +1 -1
  37. package/src/forge/_generated/authConfig.ts +1 -1
  38. package/src/forge/_generated/authContext.ts +1 -1
  39. package/src/forge/_generated/authRegistry.json +1 -1
  40. package/src/forge/_generated/authRegistry.ts +1 -1
  41. package/src/forge/_generated/buildInfo.json +2 -2
  42. package/src/forge/_generated/buildInfo.ts +4 -4
  43. package/src/forge/_generated/capabilityMap.json +2 -2
  44. package/src/forge/_generated/capabilityMap.md +1 -1
  45. package/src/forge/_generated/capabilityMap.ts +2 -2
  46. package/src/forge/_generated/client.ts +1 -1
  47. package/src/forge/_generated/clientApi.ts +1 -1
  48. package/src/forge/_generated/clientManifest.json +2 -2
  49. package/src/forge/_generated/clientManifest.ts +3 -3
  50. package/src/forge/_generated/clientTypes.ts +1 -1
  51. package/src/forge/_generated/configRegistry.json +1 -1
  52. package/src/forge/_generated/configRegistry.ts +1 -1
  53. package/src/forge/_generated/dataGraph.json +2 -2
  54. package/src/forge/_generated/dataGraph.ts +3 -3
  55. package/src/forge/_generated/db.json +1 -1
  56. package/src/forge/_generated/db.ts +1 -1
  57. package/src/forge/_generated/dbSecurityManifest.json +1 -1
  58. package/src/forge/_generated/dbSecurityManifest.ts +1 -1
  59. package/src/forge/_generated/dbSessionContext.json +1 -1
  60. package/src/forge/_generated/dbSessionContext.ts +1 -1
  61. package/src/forge/_generated/deployManifest.json +2 -2
  62. package/src/forge/_generated/deployManifest.ts +7 -7
  63. package/src/forge/_generated/devManifest.json +2 -2
  64. package/src/forge/_generated/devManifest.ts +3 -3
  65. package/src/forge/_generated/envSchema.json +1 -1
  66. package/src/forge/_generated/envSchema.ts +1 -1
  67. package/src/forge/_generated/frontendGraph.json +1 -1
  68. package/src/forge/_generated/frontendGraph.ts +1 -1
  69. package/src/forge/_generated/importGuards.json +2 -2
  70. package/src/forge/_generated/importGuards.ts +35 -1
  71. package/src/forge/_generated/index.ts +1 -1
  72. package/src/forge/_generated/liveProductionManifest.json +1 -1
  73. package/src/forge/_generated/liveProductionManifest.ts +1 -1
  74. package/src/forge/_generated/liveProtocol.json +1 -1
  75. package/src/forge/_generated/liveProtocol.ts +1 -1
  76. package/src/forge/_generated/liveQueryRegistry.json +2 -2
  77. package/src/forge/_generated/liveQueryRegistry.ts +3 -3
  78. package/src/forge/_generated/liveTransportConfig.json +1 -1
  79. package/src/forge/_generated/liveTransportConfig.ts +1 -1
  80. package/src/forge/_generated/makeRegistry.json +2 -2
  81. package/src/forge/_generated/makeRegistry.ts +2 -2
  82. package/src/forge/_generated/makeTemplates.json +1 -1
  83. package/src/forge/_generated/makeTemplates.ts +1 -1
  84. package/src/forge/_generated/mockMap.json +1 -1
  85. package/src/forge/_generated/mockMap.ts +1 -1
  86. package/src/forge/_generated/operationPlaybooks.md +7 -5
  87. package/src/forge/_generated/packageGraph.json +2 -2
  88. package/src/forge/_generated/packageGraph.ts +90964 -14284
  89. package/src/forge/_generated/packageUpgradeRegistry.json +2 -2
  90. package/src/forge/_generated/packageUpgradeRegistry.ts +2 -2
  91. package/src/forge/_generated/permissionMatrix.json +2 -2
  92. package/src/forge/_generated/permissionMatrix.ts +3 -3
  93. package/src/forge/_generated/policyRegistry.json +2 -2
  94. package/src/forge/_generated/policyRegistry.ts +3 -3
  95. package/src/forge/_generated/queryRegistry.json +2 -2
  96. package/src/forge/_generated/queryRegistry.ts +3 -3
  97. package/src/forge/_generated/react.d.ts +1 -1
  98. package/src/forge/_generated/react.ts +1 -1
  99. package/src/forge/_generated/reactManifest.json +2 -2
  100. package/src/forge/_generated/reactManifest.ts +3 -3
  101. package/src/forge/_generated/releaseManifest.json +2 -2
  102. package/src/forge/_generated/releaseManifest.ts +3 -3
  103. package/src/forge/_generated/rlsPolicies.json +1 -1
  104. package/src/forge/_generated/rlsPolicies.sql +1 -1
  105. package/src/forge/_generated/rlsPolicies.ts +1 -1
  106. package/src/forge/_generated/runtimeGraph.json +2 -2
  107. package/src/forge/_generated/runtimeGraph.ts +3 -3
  108. package/src/forge/_generated/runtimeMatrix.json +2 -2
  109. package/src/forge/_generated/runtimeMatrix.ts +106177 -7917
  110. package/src/forge/_generated/runtimeRegistry.ts +1 -1
  111. package/src/forge/_generated/runtimeRules.md +1 -1
  112. package/src/forge/_generated/secretRegistry.json +1 -1
  113. package/src/forge/_generated/secretRegistry.ts +1 -1
  114. package/src/forge/_generated/secretsContext.ts +1 -1
  115. package/src/forge/_generated/serverApi.ts +1 -1
  116. package/src/forge/_generated/sourceMapManifest.json +2 -2
  117. package/src/forge/_generated/sourceMapManifest.ts +2 -2
  118. package/src/forge/_generated/sqlPlan.json +1 -1
  119. package/src/forge/_generated/sqlPlan.ts +1 -1
  120. package/src/forge/_generated/subscriptionManifest.json +2 -2
  121. package/src/forge/_generated/subscriptionManifest.ts +3 -3
  122. package/src/forge/_generated/symbolicationManifest.json +2 -2
  123. package/src/forge/_generated/symbolicationManifest.ts +2 -2
  124. package/src/forge/_generated/telemetryRegistry.json +2 -2
  125. package/src/forge/_generated/telemetryRegistry.ts +3 -3
  126. package/src/forge/_generated/telemetrySinks.json +2 -2
  127. package/src/forge/_generated/telemetrySinks.ts +2 -2
  128. package/src/forge/_generated/tenantScope.json +2 -2
  129. package/src/forge/_generated/tenantScope.ts +3 -3
  130. package/src/forge/_generated/testGraph.json +2 -2
  131. package/src/forge/_generated/testGraph.ts +129 -75
  132. package/src/forge/_generated/testPlanRegistry.json +2 -2
  133. package/src/forge/_generated/testPlanRegistry.ts +2 -2
  134. package/src/forge/_generated/uiRoutes.json +1 -1
  135. package/src/forge/_generated/uiRoutes.ts +1 -1
  136. package/src/forge/_generated/uiScenarios.json +1 -1
  137. package/src/forge/_generated/uiScenarios.ts +1 -1
  138. package/src/forge/_generated/uiTestManifest.json +2 -2
  139. package/src/forge/_generated/uiTestManifest.ts +2 -2
  140. package/src/forge/_generated/workflowRegistry.json +2 -2
  141. package/src/forge/_generated/workflowRegistry.ts +3 -3
  142. package/src/forge/_generated/workflowSubscriptions.json +2 -2
  143. package/src/forge/_generated/workflowSubscriptions.ts +3 -3
  144. package/src/forge/cli/commands.ts +861 -861
  145. package/src/forge/cli/deps.ts +178 -11
  146. package/src/forge/cli/dev.ts +32 -5
  147. package/src/forge/cli/index.ts +7 -7
  148. package/src/forge/cli/main.ts +54 -54
  149. package/src/forge/cli/new.ts +29 -1
  150. package/src/forge/cli/output.ts +97 -97
  151. package/src/forge/cli/parse.ts +679 -673
  152. package/src/forge/cli/version.ts +1 -1
  153. package/src/forge/compiler/agent-contract/build.ts +28 -0
  154. package/src/forge/compiler/agent-contract/types.ts +16 -0
  155. package/src/forge/compiler/app-graph/build.ts +112 -112
  156. package/src/forge/compiler/app-graph/classify.ts +10 -10
  157. package/src/forge/compiler/app-graph/dup-symbol.ts +29 -29
  158. package/src/forge/compiler/app-graph/extract.ts +123 -123
  159. package/src/forge/compiler/app-graph/forge-apis.ts +29 -29
  160. package/src/forge/compiler/app-graph/index.ts +11 -11
  161. package/src/forge/compiler/app-graph/module-graph.ts +316 -316
  162. package/src/forge/compiler/app-graph/parser.ts +119 -119
  163. package/src/forge/compiler/app-graph/symbols.ts +48 -48
  164. package/src/forge/compiler/app-graph/tsconfig-hash.ts +62 -62
  165. package/src/forge/compiler/app-graph/types.ts +43 -43
  166. package/src/forge/compiler/app-graph/versions.ts +14 -14
  167. package/src/forge/compiler/cache/index.ts +17 -17
  168. package/src/forge/compiler/cache/key.ts +46 -46
  169. package/src/forge/compiler/cache/scheduler.ts +72 -72
  170. package/src/forge/compiler/cache/store.ts +78 -78
  171. package/src/forge/compiler/classifier/capabilities.ts +78 -78
  172. package/src/forge/compiler/classifier/classify.ts +113 -113
  173. package/src/forge/compiler/classifier/contexts.ts +188 -188
  174. package/src/forge/compiler/classifier/index.ts +18 -18
  175. package/src/forge/compiler/classifier/runtime-matrix.ts +45 -45
  176. package/src/forge/compiler/classifier/secrets.ts +41 -41
  177. package/src/forge/compiler/classifier/signals.ts +129 -129
  178. package/src/forge/compiler/diagnostics/codes.ts +125 -120
  179. package/src/forge/compiler/diagnostics/create.ts +87 -87
  180. package/src/forge/compiler/diagnostics/index.ts +41 -41
  181. package/src/forge/compiler/emitter/artifact-kind.ts +14 -14
  182. package/src/forge/compiler/emitter/barrel.ts +38 -38
  183. package/src/forge/compiler/emitter/constants.ts +7 -7
  184. package/src/forge/compiler/emitter/emit.ts +234 -237
  185. package/src/forge/compiler/emitter/index.ts +24 -24
  186. package/src/forge/compiler/emitter/lock.ts +61 -61
  187. package/src/forge/compiler/emitter/render.ts +73 -73
  188. package/src/forge/compiler/guards/artifacts.ts +96 -96
  189. package/src/forge/compiler/guards/check-import-guards.ts +106 -106
  190. package/src/forge/compiler/guards/index.ts +11 -11
  191. package/src/forge/compiler/guards/propagate-contexts.ts +57 -57
  192. package/src/forge/compiler/index.ts +17 -17
  193. package/src/forge/compiler/integration/add.ts +493 -493
  194. package/src/forge/compiler/integration/index.ts +17 -17
  195. package/src/forge/compiler/integration/plan.ts +279 -279
  196. package/src/forge/compiler/integration/render.ts +189 -189
  197. package/src/forge/compiler/integration/snapshot.ts +52 -52
  198. package/src/forge/compiler/orchestrator/discover.ts +214 -214
  199. package/src/forge/compiler/orchestrator/guards.ts +5 -5
  200. package/src/forge/compiler/orchestrator/index.ts +27 -27
  201. package/src/forge/compiler/orchestrator/manifest.ts +69 -69
  202. package/src/forge/compiler/orchestrator/orphans.ts +51 -51
  203. package/src/forge/compiler/orchestrator/plan.ts +804 -804
  204. package/src/forge/compiler/orchestrator/run.ts +178 -178
  205. package/src/forge/compiler/orchestrator/serialize.ts +859 -859
  206. package/src/forge/compiler/orchestrator/types.ts +23 -23
  207. package/src/forge/compiler/orchestrator/verify.ts +35 -35
  208. package/src/forge/compiler/package-graph/capabilities-stub.ts +33 -33
  209. package/src/forge/compiler/package-graph/checksum.ts +107 -97
  210. package/src/forge/compiler/package-graph/compiler.ts +444 -363
  211. package/src/forge/compiler/package-graph/constants.ts +4 -4
  212. package/src/forge/compiler/package-graph/exports-discovery.ts +91 -84
  213. package/src/forge/compiler/package-graph/extract-dts.ts +32 -32
  214. package/src/forge/compiler/package-graph/index.ts +24 -24
  215. package/src/forge/compiler/package-graph/jsdoc.ts +50 -50
  216. package/src/forge/compiler/package-graph/oracle.ts +326 -0
  217. package/src/forge/compiler/package-graph/read-file.ts +21 -21
  218. package/src/forge/compiler/package-graph/resolve.ts +131 -127
  219. package/src/forge/compiler/package-manager/adapter.ts +232 -232
  220. package/src/forge/compiler/package-manager/commands.ts +47 -47
  221. package/src/forge/compiler/package-manager/detect.ts +65 -65
  222. package/src/forge/compiler/package-manager/executor.ts +29 -29
  223. package/src/forge/compiler/package-manager/index.ts +22 -22
  224. package/src/forge/compiler/package-manager/parse-spec.ts +16 -16
  225. package/src/forge/compiler/package-manager/version.ts +20 -20
  226. package/src/forge/compiler/primitives/compare.ts +26 -26
  227. package/src/forge/compiler/primitives/hash.ts +42 -33
  228. package/src/forge/compiler/primitives/header.ts +43 -43
  229. package/src/forge/compiler/primitives/index.ts +45 -45
  230. package/src/forge/compiler/primitives/paths.ts +24 -24
  231. package/src/forge/compiler/primitives/serialize.ts +66 -66
  232. package/src/forge/compiler/primitives/sort.ts +87 -87
  233. package/src/forge/compiler/recipes/definitions.ts +269 -269
  234. package/src/forge/compiler/recipes/helpers.ts +37 -37
  235. package/src/forge/compiler/recipes/index.ts +21 -21
  236. package/src/forge/compiler/recipes/registry.ts +87 -87
  237. package/src/forge/compiler/sandbox/artifact-sanitize.ts +26 -26
  238. package/src/forge/compiler/sandbox/backends/child.ts +123 -123
  239. package/src/forge/compiler/sandbox/backends/docker.ts +173 -173
  240. package/src/forge/compiler/sandbox/index.ts +51 -51
  241. package/src/forge/compiler/sandbox/inspect.ts +143 -143
  242. package/src/forge/compiler/sandbox/inspector-entry.ts +115 -115
  243. package/src/forge/compiler/sandbox/limits.ts +31 -31
  244. package/src/forge/compiler/sandbox/scrub-env.ts +60 -60
  245. package/src/forge/compiler/sandbox/secret-scan.ts +54 -54
  246. package/src/forge/compiler/sandbox/serialize.ts +106 -106
  247. package/src/forge/compiler/sandbox/types.ts +7 -7
  248. package/src/forge/compiler/types/app-graph.ts +71 -71
  249. package/src/forge/compiler/types/capability.ts +29 -29
  250. package/src/forge/compiler/types/classification.ts +9 -9
  251. package/src/forge/compiler/types/cli.ts +85 -85
  252. package/src/forge/compiler/types/diagnostic.ts +2 -2
  253. package/src/forge/compiler/types/emit.ts +25 -25
  254. package/src/forge/compiler/types/import-guards.ts +19 -19
  255. package/src/forge/compiler/types/index.ts +98 -98
  256. package/src/forge/compiler/types/integration.ts +25 -25
  257. package/src/forge/compiler/types/json.ts +3 -3
  258. package/src/forge/compiler/types/lock.ts +37 -37
  259. package/src/forge/compiler/types/package-graph.ts +122 -77
  260. package/src/forge/compiler/types/runtime-matrix.ts +16 -16
  261. package/src/forge/compiler/types/runtime.ts +30 -30
  262. package/src/forge/compiler/types/sandbox.ts +24 -24
  263. package/src/forge/dev/server.ts +16 -2
  264. package/src/forge/refactor/index.ts +10 -2
  265. package/src/forge/refactor/runtime-rename.ts +598 -0
  266. package/src/forge/runtime/executor.ts +3 -2
  267. package/src/forge/runtime/live/live-query-runner.ts +2 -1
  268. package/src/forge/runtime/outbox/process.ts +2 -1
  269. package/src/forge/runtime/query/run-query.ts +2 -1
  270. package/src/forge/runtime/runner/run-entry.ts +2 -1
  271. package/src/forge/runtime/telemetry/sinks/posthog.ts +4 -5
  272. package/src/forge/runtime/telemetry/sinks/sentry.ts +4 -5
  273. package/src/forge/runtime/workflows/resolve-step.ts +2 -1
  274. package/src/forge/version.ts +3 -0
  275. package/templates/b2b-support-web/src/actions/captureTicketCreated.ts +7 -2
  276. package/templates/b2b-support-web/src/commands/closeTicket.ts +6 -1
  277. package/templates/b2b-support-web/src/commands/createTicket.ts +8 -2
  278. package/templates/b2b-support-web/src/queries/getTicket.ts +8 -1
  279. package/templates/b2b-support-web/web/components/CreateTicketForm.tsx +1 -2
  280. package/templates/b2b-support-web/web/components/PolicyDeniedDemo.tsx +1 -2
  281. package/templates/b2b-support-web/web/components/TicketList.tsx +1 -2
  282. package/templates/b2b-support-web/web/components/TraceDetails.tsx +1 -1
  283. package/templates/b2b-support-web/web/lib/forge.ts +1 -0
@@ -1,320 +1,320 @@
1
- import path from "node:path";
2
- import ts from "typescript";
3
- import { compareBytes } from "../primitives/compare.ts";
4
- import { hashStable } from "../primitives/hash.ts";
5
- import { normalizePath } from "../primitives/paths.ts";
6
- import { stableSortStrings } from "../primitives/sort.ts";
7
- import type {
8
- AppGraph,
9
- ImportKind,
10
- LocalImport,
11
- ModuleGraph,
12
- ModuleNode,
13
- PackageImport,
14
- SourceFile,
15
- } from "../types/app-graph.ts";
16
- import type { RuntimeContext } from "../types/runtime.ts";
17
- import { FORGE_KIND_TO_CONTEXT } from "./forge-apis.ts";
18
- import { loadTsconfig } from "./tsconfig-hash.ts";
19
- import type { RawSymbol } from "./types.ts";
20
-
21
- export function moduleIdForFile(file: string): string {
22
- return hashStable(normalizePath(file));
23
- }
24
-
25
- export function parsePackageSpecifier(
26
- specifier: string,
27
- ): { packageName: string; subpath: string } | null {
28
- if (
29
- specifier.startsWith(".") ||
30
- specifier.startsWith("/") ||
31
- specifier.startsWith("node:")
32
- ) {
33
- return null;
34
- }
35
-
36
- const parts = specifier.split("/");
37
- if (specifier.startsWith("@") && parts.length >= 2) {
38
- return {
39
- packageName: `${parts[0]}/${parts[1]}`,
40
- subpath: parts.length > 2 ? `/${parts.slice(2).join("/")}` : "",
41
- };
42
- }
43
-
44
- return {
45
- packageName: parts[0] ?? specifier,
46
- subpath: parts.length > 1 ? `/${parts.slice(1).join("/")}` : "",
47
- };
48
- }
49
-
50
- function compareModuleNodes(a: ModuleNode, b: ModuleNode): number {
51
- return compareBytes(a.id, b.id);
52
- }
53
-
54
- function stableSortModuleNodes(nodes: ModuleNode[]): ModuleNode[] {
55
- return [...nodes].sort(compareModuleNodes);
56
- }
57
-
58
- function declaredContextsForFile(
59
- file: string,
60
- symbols: RawSymbol[],
61
- ): RuntimeContext[] {
62
- const contexts = new Set<RuntimeContext>();
63
- for (const symbol of symbols) {
64
- if (symbol.file !== file) {
65
- continue;
66
- }
67
- const context = FORGE_KIND_TO_CONTEXT[symbol.kind];
68
- if (context) {
69
- contexts.add(context);
70
- }
71
- }
72
- return stableSortStrings([...contexts]) as RuntimeContext[];
73
- }
74
-
75
- function spanFromNode(node: ts.Node, sourceFile: ts.SourceFile): {
76
- start: number;
77
- end: number;
78
- } {
79
- const start = node.getStart(sourceFile);
80
- const end = node.getEnd();
81
- return { start, end };
82
- }
83
-
84
- function toWorkspaceRelativePath(
85
- absoluteOrRelativePath: string,
86
- workspaceRoot: string,
87
- ): string {
88
- const normalized = normalizePath(absoluteOrRelativePath);
89
- const root = normalizePath(path.resolve(workspaceRoot).replace(/\\/g, "/"));
90
- const rootWithSlash = root.endsWith("/") ? root : `${root}/`;
91
-
92
- if (normalized.startsWith(rootWithSlash)) {
93
- return normalized.slice(rootWithSlash.length);
94
- }
95
-
96
- if (normalized === root) {
97
- return "";
98
- }
99
-
100
- return normalized;
101
- }
102
-
103
- function resolveLocalTarget(
104
- specifier: string,
105
- fromFile: string,
106
- program: ts.Program,
107
- workspaceRoot: string,
108
- ): string | null {
109
- const resolved = ts.resolveModuleName(
110
- specifier,
111
- fromFile,
112
- program.getCompilerOptions(),
113
- ts.sys,
114
- );
115
-
116
- const fileName = resolved.resolvedModule?.resolvedFileName;
117
- if (!fileName) {
118
- return null;
119
- }
120
-
121
- return toWorkspaceRelativePath(fileName, workspaceRoot);
122
- }
123
-
124
- function collectImports(
125
- sourceFile: ts.SourceFile,
126
- program: ts.Program,
127
- workspaceRoot: string,
128
- ): { packageImports: PackageImport[]; localImports: LocalImport[] } {
129
- const packageImports: PackageImport[] = [];
130
- const localImports: LocalImport[] = [];
131
- const fromFile = normalizePath(sourceFile.fileName);
132
-
133
- function recordPackageImport(
134
- specifier: string,
135
- span: { start: number; end: number },
136
- importKind: ImportKind,
137
- ): void {
138
- const parsed = parsePackageSpecifier(specifier);
139
- if (!parsed) {
140
- return;
141
- }
142
- packageImports.push({
143
- specifier,
144
- packageName: parsed.packageName,
145
- subpath: parsed.subpath,
146
- span,
147
- importKind,
148
- });
149
- }
150
-
151
- function visit(node: ts.Node): void {
152
- if (ts.isImportDeclaration(node) && node.moduleSpecifier) {
153
- if (ts.isStringLiteral(node.moduleSpecifier)) {
154
- const specifier = node.moduleSpecifier.text;
155
- const span = spanFromNode(node.moduleSpecifier, sourceFile);
156
- const parsed = parsePackageSpecifier(specifier);
157
- if (parsed) {
158
- recordPackageImport(specifier, span, "static");
159
- } else {
160
- const target = resolveLocalTarget(
161
- specifier,
162
- fromFile,
163
- program,
164
- workspaceRoot,
165
- );
166
- if (target) {
167
- localImports.push({
168
- toModuleId: moduleIdForFile(target),
169
- span,
170
- });
171
- }
172
- }
173
- }
174
- }
175
-
176
- if (
177
- ts.isCallExpression(node) &&
178
- node.expression.kind === ts.SyntaxKind.ImportKeyword &&
179
- node.arguments[0] &&
180
- ts.isStringLiteral(node.arguments[0])
181
- ) {
182
- const specifier = node.arguments[0].text;
183
- const span = spanFromNode(node.arguments[0], sourceFile);
184
- const parsed = parsePackageSpecifier(specifier);
185
- if (parsed) {
186
- recordPackageImport(specifier, span, "dynamic");
187
- } else {
188
- const target = resolveLocalTarget(
189
- specifier,
190
- fromFile,
191
- program,
192
- workspaceRoot,
193
- );
194
- if (target) {
195
- localImports.push({
196
- toModuleId: moduleIdForFile(target),
197
- span,
198
- });
199
- }
200
- }
201
- }
202
-
203
- if (
204
- ts.isCallExpression(node) &&
205
- ts.isIdentifier(node.expression) &&
206
- node.expression.text === "require" &&
207
- node.arguments[0] &&
208
- ts.isStringLiteral(node.arguments[0])
209
- ) {
210
- const specifier = node.arguments[0].text;
211
- const span = spanFromNode(node.arguments[0], sourceFile);
212
- const parsed = parsePackageSpecifier(specifier);
213
- if (parsed) {
214
- recordPackageImport(specifier, span, "require");
215
- } else {
216
- const target = resolveLocalTarget(
217
- specifier,
218
- fromFile,
219
- program,
220
- workspaceRoot,
221
- );
222
- if (target) {
223
- localImports.push({
224
- toModuleId: moduleIdForFile(target),
225
- span,
226
- });
227
- }
228
- }
229
- }
230
-
231
- ts.forEachChild(node, visit);
232
- }
233
-
234
- visit(sourceFile);
235
- return { packageImports, localImports };
236
- }
237
-
238
- function sourcesUnchangedSincePrior(
239
- sources: SourceFile[],
240
- prior?: AppGraph,
241
- ): boolean {
1
+ import path from "node:path";
2
+ import ts from "typescript";
3
+ import { compareBytes } from "../primitives/compare.ts";
4
+ import { hashStable } from "../primitives/hash.ts";
5
+ import { normalizePath } from "../primitives/paths.ts";
6
+ import { stableSortStrings } from "../primitives/sort.ts";
7
+ import type {
8
+ AppGraph,
9
+ ImportKind,
10
+ LocalImport,
11
+ ModuleGraph,
12
+ ModuleNode,
13
+ PackageImport,
14
+ SourceFile,
15
+ } from "../types/app-graph.ts";
16
+ import type { RuntimeContext } from "../types/runtime.ts";
17
+ import { FORGE_KIND_TO_CONTEXT } from "./forge-apis.ts";
18
+ import { loadTsconfig } from "./tsconfig-hash.ts";
19
+ import type { RawSymbol } from "./types.ts";
20
+
21
+ export function moduleIdForFile(file: string): string {
22
+ return hashStable(normalizePath(file));
23
+ }
24
+
25
+ export function parsePackageSpecifier(
26
+ specifier: string,
27
+ ): { packageName: string; subpath: string } | null {
28
+ if (
29
+ specifier.startsWith(".") ||
30
+ specifier.startsWith("/") ||
31
+ specifier.startsWith("node:")
32
+ ) {
33
+ return null;
34
+ }
35
+
36
+ const parts = specifier.split("/");
37
+ if (specifier.startsWith("@") && parts.length >= 2) {
38
+ return {
39
+ packageName: `${parts[0]}/${parts[1]}`,
40
+ subpath: parts.length > 2 ? `/${parts.slice(2).join("/")}` : "",
41
+ };
42
+ }
43
+
44
+ return {
45
+ packageName: parts[0] ?? specifier,
46
+ subpath: parts.length > 1 ? `/${parts.slice(1).join("/")}` : "",
47
+ };
48
+ }
49
+
50
+ function compareModuleNodes(a: ModuleNode, b: ModuleNode): number {
51
+ return compareBytes(a.id, b.id);
52
+ }
53
+
54
+ function stableSortModuleNodes(nodes: ModuleNode[]): ModuleNode[] {
55
+ return [...nodes].sort(compareModuleNodes);
56
+ }
57
+
58
+ function declaredContextsForFile(
59
+ file: string,
60
+ symbols: RawSymbol[],
61
+ ): RuntimeContext[] {
62
+ const contexts = new Set<RuntimeContext>();
63
+ for (const symbol of symbols) {
64
+ if (symbol.file !== file) {
65
+ continue;
66
+ }
67
+ const context = FORGE_KIND_TO_CONTEXT[symbol.kind];
68
+ if (context) {
69
+ contexts.add(context);
70
+ }
71
+ }
72
+ return stableSortStrings([...contexts]) as RuntimeContext[];
73
+ }
74
+
75
+ function spanFromNode(node: ts.Node, sourceFile: ts.SourceFile): {
76
+ start: number;
77
+ end: number;
78
+ } {
79
+ const start = node.getStart(sourceFile);
80
+ const end = node.getEnd();
81
+ return { start, end };
82
+ }
83
+
84
+ function toWorkspaceRelativePath(
85
+ absoluteOrRelativePath: string,
86
+ workspaceRoot: string,
87
+ ): string {
88
+ const normalized = normalizePath(absoluteOrRelativePath);
89
+ const root = normalizePath(path.resolve(workspaceRoot).replace(/\\/g, "/"));
90
+ const rootWithSlash = root.endsWith("/") ? root : `${root}/`;
91
+
92
+ if (normalized.startsWith(rootWithSlash)) {
93
+ return normalized.slice(rootWithSlash.length);
94
+ }
95
+
96
+ if (normalized === root) {
97
+ return "";
98
+ }
99
+
100
+ return normalized;
101
+ }
102
+
103
+ function resolveLocalTarget(
104
+ specifier: string,
105
+ fromFile: string,
106
+ program: ts.Program,
107
+ workspaceRoot: string,
108
+ ): string | null {
109
+ const resolved = ts.resolveModuleName(
110
+ specifier,
111
+ fromFile,
112
+ program.getCompilerOptions(),
113
+ ts.sys,
114
+ );
115
+
116
+ const fileName = resolved.resolvedModule?.resolvedFileName;
117
+ if (!fileName) {
118
+ return null;
119
+ }
120
+
121
+ return toWorkspaceRelativePath(fileName, workspaceRoot);
122
+ }
123
+
124
+ function collectImports(
125
+ sourceFile: ts.SourceFile,
126
+ program: ts.Program,
127
+ workspaceRoot: string,
128
+ ): { packageImports: PackageImport[]; localImports: LocalImport[] } {
129
+ const packageImports: PackageImport[] = [];
130
+ const localImports: LocalImport[] = [];
131
+ const fromFile = normalizePath(sourceFile.fileName);
132
+
133
+ function recordPackageImport(
134
+ specifier: string,
135
+ span: { start: number; end: number },
136
+ importKind: ImportKind,
137
+ ): void {
138
+ const parsed = parsePackageSpecifier(specifier);
139
+ if (!parsed) {
140
+ return;
141
+ }
142
+ packageImports.push({
143
+ specifier,
144
+ packageName: parsed.packageName,
145
+ subpath: parsed.subpath,
146
+ span,
147
+ importKind,
148
+ });
149
+ }
150
+
151
+ function visit(node: ts.Node): void {
152
+ if (ts.isImportDeclaration(node) && node.moduleSpecifier) {
153
+ if (ts.isStringLiteral(node.moduleSpecifier)) {
154
+ const specifier = node.moduleSpecifier.text;
155
+ const span = spanFromNode(node.moduleSpecifier, sourceFile);
156
+ const parsed = parsePackageSpecifier(specifier);
157
+ if (parsed) {
158
+ recordPackageImport(specifier, span, "static");
159
+ } else {
160
+ const target = resolveLocalTarget(
161
+ specifier,
162
+ fromFile,
163
+ program,
164
+ workspaceRoot,
165
+ );
166
+ if (target) {
167
+ localImports.push({
168
+ toModuleId: moduleIdForFile(target),
169
+ span,
170
+ });
171
+ }
172
+ }
173
+ }
174
+ }
175
+
176
+ if (
177
+ ts.isCallExpression(node) &&
178
+ node.expression.kind === ts.SyntaxKind.ImportKeyword &&
179
+ node.arguments[0] &&
180
+ ts.isStringLiteral(node.arguments[0])
181
+ ) {
182
+ const specifier = node.arguments[0].text;
183
+ const span = spanFromNode(node.arguments[0], sourceFile);
184
+ const parsed = parsePackageSpecifier(specifier);
185
+ if (parsed) {
186
+ recordPackageImport(specifier, span, "dynamic");
187
+ } else {
188
+ const target = resolveLocalTarget(
189
+ specifier,
190
+ fromFile,
191
+ program,
192
+ workspaceRoot,
193
+ );
194
+ if (target) {
195
+ localImports.push({
196
+ toModuleId: moduleIdForFile(target),
197
+ span,
198
+ });
199
+ }
200
+ }
201
+ }
202
+
203
+ if (
204
+ ts.isCallExpression(node) &&
205
+ ts.isIdentifier(node.expression) &&
206
+ node.expression.text === "require" &&
207
+ node.arguments[0] &&
208
+ ts.isStringLiteral(node.arguments[0])
209
+ ) {
210
+ const specifier = node.arguments[0].text;
211
+ const span = spanFromNode(node.arguments[0], sourceFile);
212
+ const parsed = parsePackageSpecifier(specifier);
213
+ if (parsed) {
214
+ recordPackageImport(specifier, span, "require");
215
+ } else {
216
+ const target = resolveLocalTarget(
217
+ specifier,
218
+ fromFile,
219
+ program,
220
+ workspaceRoot,
221
+ );
222
+ if (target) {
223
+ localImports.push({
224
+ toModuleId: moduleIdForFile(target),
225
+ span,
226
+ });
227
+ }
228
+ }
229
+ }
230
+
231
+ ts.forEachChild(node, visit);
232
+ }
233
+
234
+ visit(sourceFile);
235
+ return { packageImports, localImports };
236
+ }
237
+
238
+ function sourcesUnchangedSincePrior(
239
+ sources: SourceFile[],
240
+ prior?: AppGraph,
241
+ ): boolean {
242
242
  if (!prior) {
243
243
  return false;
244
244
  }
245
245
  const priorHashes = new Map(Object.entries(prior.sourceHashes ?? {}));
246
- if (priorHashes.size !== sources.length) {
247
- return false;
248
- }
249
- for (const source of sources) {
250
- if (priorHashes.get(source.path) !== source.contentHash) {
251
- return false;
252
- }
253
- }
254
- return true;
255
- }
256
-
257
- export function buildModuleGraph(
258
- sources: SourceFile[],
259
- rawSymbols: RawSymbol[],
260
- workspaceRoot: string,
261
- tsconfigPath?: string,
262
- prior?: AppGraph,
263
- ): ModuleGraph {
264
- if (sourcesUnchangedSincePrior(sources, prior)) {
265
- return prior!.moduleGraph;
266
- }
267
-
268
- const parsedConfig = loadTsconfig(workspaceRoot, tsconfigPath);
269
- const fileNames = sources.map((source) =>
270
- path.resolve(workspaceRoot, source.path).replace(/\\/g, "/"),
271
- );
272
-
273
- const program = ts.createProgram(fileNames, {
274
- ...parsedConfig.options,
275
- noEmit: true,
276
- skipLibCheck: true,
277
- });
278
-
279
- const nodes: ModuleNode[] = [];
280
-
281
- for (const source of sources) {
282
- const normalizedFile = normalizePath(source.path);
283
- const absolutePath = fileNames.find(
284
- (name) => normalizePath(name).endsWith(normalizedFile),
285
- );
286
-
287
- const sourceFile = absolutePath
288
- ? program.getSourceFile(absolutePath)
289
- : undefined;
290
-
291
- if (!sourceFile) {
292
- nodes.push({
293
- id: moduleIdForFile(normalizedFile),
294
- file: normalizedFile,
295
- directPackageImports: [],
296
- localImports: [],
297
- declaredContexts: declaredContextsForFile(normalizedFile, rawSymbols),
298
- effectiveContexts: [],
299
- });
300
- continue;
301
- }
302
-
303
- const { packageImports, localImports } = collectImports(
304
- sourceFile,
305
- program,
306
- workspaceRoot,
307
- );
308
-
309
- nodes.push({
310
- id: moduleIdForFile(normalizedFile),
311
- file: normalizedFile,
312
- directPackageImports: packageImports,
313
- localImports,
314
- declaredContexts: declaredContextsForFile(normalizedFile, rawSymbols),
315
- effectiveContexts: [],
316
- });
317
- }
318
-
319
- return { nodes: stableSortModuleNodes(nodes) };
320
- }
246
+ if (priorHashes.size !== sources.length) {
247
+ return false;
248
+ }
249
+ for (const source of sources) {
250
+ if (priorHashes.get(source.path) !== source.contentHash) {
251
+ return false;
252
+ }
253
+ }
254
+ return true;
255
+ }
256
+
257
+ export function buildModuleGraph(
258
+ sources: SourceFile[],
259
+ rawSymbols: RawSymbol[],
260
+ workspaceRoot: string,
261
+ tsconfigPath?: string,
262
+ prior?: AppGraph,
263
+ ): ModuleGraph {
264
+ if (sourcesUnchangedSincePrior(sources, prior)) {
265
+ return prior!.moduleGraph;
266
+ }
267
+
268
+ const parsedConfig = loadTsconfig(workspaceRoot, tsconfigPath);
269
+ const fileNames = sources.map((source) =>
270
+ path.resolve(workspaceRoot, source.path).replace(/\\/g, "/"),
271
+ );
272
+
273
+ const program = ts.createProgram(fileNames, {
274
+ ...parsedConfig.options,
275
+ noEmit: true,
276
+ skipLibCheck: true,
277
+ });
278
+
279
+ const nodes: ModuleNode[] = [];
280
+
281
+ for (const source of sources) {
282
+ const normalizedFile = normalizePath(source.path);
283
+ const absolutePath = fileNames.find(
284
+ (name) => normalizePath(name).endsWith(normalizedFile),
285
+ );
286
+
287
+ const sourceFile = absolutePath
288
+ ? program.getSourceFile(absolutePath)
289
+ : undefined;
290
+
291
+ if (!sourceFile) {
292
+ nodes.push({
293
+ id: moduleIdForFile(normalizedFile),
294
+ file: normalizedFile,
295
+ directPackageImports: [],
296
+ localImports: [],
297
+ declaredContexts: declaredContextsForFile(normalizedFile, rawSymbols),
298
+ effectiveContexts: [],
299
+ });
300
+ continue;
301
+ }
302
+
303
+ const { packageImports, localImports } = collectImports(
304
+ sourceFile,
305
+ program,
306
+ workspaceRoot,
307
+ );
308
+
309
+ nodes.push({
310
+ id: moduleIdForFile(normalizedFile),
311
+ file: normalizedFile,
312
+ directPackageImports: packageImports,
313
+ localImports,
314
+ declaredContexts: declaredContextsForFile(normalizedFile, rawSymbols),
315
+ effectiveContexts: [],
316
+ });
317
+ }
318
+
319
+ return { nodes: stableSortModuleNodes(nodes) };
320
+ }