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,44 +1,44 @@
1
- import { comparePaths, normalizeNewlines, normalizePath } from "../primitives/index.ts";
2
- import { BARREL_INDEX_PATH, GENERATED_DIR } from "./constants.ts";
3
-
4
- function toBarrelExportPath(filePath: string): string {
5
- const normalized = normalizePath(filePath);
6
- const generatedPrefix = `${GENERATED_DIR}/`;
7
-
8
- if (!normalized.startsWith(generatedPrefix)) {
9
- throw new Error(`barrel export path must live under ${GENERATED_DIR}: ${filePath}`);
10
- }
11
-
12
- const relative = normalized.slice(generatedPrefix.length);
13
- const withoutExtension = relative.replace(/\.tsx?$/, "");
14
- return `./${withoutExtension}`;
15
- }
16
-
17
- /**
18
- * Build barrel index.ts body (sorted re-exports, no header).
19
- */
20
- export function buildBarrelIndexBody(exportFilePaths: string[]): string {
21
- const candidates = exportFilePaths
22
- .map(normalizePath)
23
- .filter(
1
+ import { comparePaths, normalizeNewlines, normalizePath } from "../primitives/index.ts";
2
+ import { BARREL_INDEX_PATH, GENERATED_DIR } from "./constants.ts";
3
+
4
+ function toBarrelExportPath(filePath: string): string {
5
+ const normalized = normalizePath(filePath);
6
+ const generatedPrefix = `${GENERATED_DIR}/`;
7
+
8
+ if (!normalized.startsWith(generatedPrefix)) {
9
+ throw new Error(`barrel export path must live under ${GENERATED_DIR}: ${filePath}`);
10
+ }
11
+
12
+ const relative = normalized.slice(generatedPrefix.length);
13
+ const withoutExtension = relative.replace(/\.tsx?$/, "");
14
+ return `./${withoutExtension}`;
15
+ }
16
+
17
+ /**
18
+ * Build barrel index.ts body (sorted re-exports, no header).
19
+ */
20
+ export function buildBarrelIndexBody(exportFilePaths: string[]): string {
21
+ const candidates = exportFilePaths
22
+ .map(normalizePath)
23
+ .filter(
24
24
  (path) =>
25
25
  path.startsWith(`${GENERATED_DIR}/`) &&
26
26
  (path.endsWith(".ts") || path.endsWith(".tsx")) &&
27
27
  !path.endsWith(".d.ts") &&
28
28
  path !== BARREL_INDEX_PATH,
29
29
  );
30
-
31
- const unique = [...new Set(candidates)];
32
- unique.sort(comparePaths);
33
-
34
- const lines = unique.map((path) => {
35
- const exportPath = toBarrelExportPath(path);
36
- return `export * from "${exportPath}";`;
37
- });
38
-
39
- if (lines.length === 0) {
40
- return normalizeNewlines("// Forge generated barrel\n");
41
- }
42
-
43
- return normalizeNewlines(lines.join("\n"));
44
- }
30
+
31
+ const unique = [...new Set(candidates)];
32
+ unique.sort(comparePaths);
33
+
34
+ const lines = unique.map((path) => {
35
+ const exportPath = toBarrelExportPath(path);
36
+ return `export * from "${exportPath}";`;
37
+ });
38
+
39
+ if (lines.length === 0) {
40
+ return normalizeNewlines("// Forge generated barrel\n");
41
+ }
42
+
43
+ return normalizeNewlines(lines.join("\n"));
44
+ }
@@ -1,7 +1,7 @@
1
- import { GENERATOR_VERSION } from "../package-graph/constants.ts";
2
-
3
- export const FORGE_LOCK_SCHEMA_VERSION = "1.0.0";
4
- export const GENERATED_DIR = "src/forge/_generated";
5
- export const FORGE_LOCK_PATH = "forge.lock";
6
- export const BARREL_INDEX_PATH = `${GENERATED_DIR}/index.ts`;
7
- export { GENERATOR_VERSION };
1
+ import { GENERATOR_VERSION } from "../package-graph/constants.ts";
2
+
3
+ export const FORGE_LOCK_SCHEMA_VERSION = "1.0.0";
4
+ export const GENERATED_DIR = "src/forge/_generated";
5
+ export const FORGE_LOCK_PATH = "forge.lock";
6
+ export const BARREL_INDEX_PATH = `${GENERATED_DIR}/index.ts`;
7
+ export { GENERATOR_VERSION };
@@ -1,237 +1,234 @@
1
- import { join } from "node:path";
2
- import type { Diagnostic } from "../types/diagnostic.ts";
3
- import type { EmitFile, EmitMode, EmitPlan } from "../types/emit.ts";
4
- import { runWithConcurrency } from "../cache/scheduler.ts";
5
- import { nodeFileSystem } from "../fs/index.ts";
6
- import type { FileSystem } from "../fs/index.ts";
7
- import {
8
- forgeDrift,
9
- forgeOrphanedGeneratedFile,
10
- forgeWriteError,
11
- } from "../diagnostics/create.ts";
12
- import {
13
- hashStable,
14
- normalizePath,
15
- stableSortByPath,
16
- stableSortEmitFiles,
17
- stripDeterministicHeader,
18
- } from "../primitives/index.ts";
19
- import { buildBarrelIndexBody } from "./barrel.ts";
20
- import {
21
- BARREL_INDEX_PATH,
22
- FORGE_LOCK_PATH,
23
- GENERATED_DIR,
24
- } from "./constants.ts";
25
- import { serializeForgeLock } from "./lock.ts";
26
- import { render, renderBody, type RenderContext } from "./render.ts";
27
- import {
28
- readTextFileIfExists,
29
- removeFileIfExists,
30
- writeFileAtomic,
31
- } from "./write.ts";
32
-
33
- export interface EmitOptions {
34
- workspaceRoot: string;
35
- mode: EmitMode;
36
- fs?: FileSystem;
37
- }
38
-
39
- export interface EmitResult {
40
- changed: string[];
41
- unchanged: string[];
42
- removed: string[];
43
- wouldChange: string[];
44
- warnings: Diagnostic[];
45
- errors: Diagnostic[];
46
- exitCode: 0 | 1;
47
- }
48
-
49
- function resolveWorkspacePath(workspaceRoot: string, relativePath: string): string {
50
- return join(workspaceRoot, normalizePath(relativePath));
51
- }
52
-
53
- function bodiesDiffer(rendered: string, onDisk: string | null): boolean {
54
- const renderedBody = stripDeterministicHeader(rendered);
55
- if (onDisk === null) {
56
- return true;
57
- }
58
- const diskBody = stripDeterministicHeader(onDisk);
59
- return renderedBody !== diskBody;
60
- }
61
-
62
- function buildRenderContext(plan: EmitPlan): RenderContext {
63
- return {
64
- generatorVersion: plan.lock.generatorVersion,
65
- inputHash: plan.lock.inputHash,
66
- };
67
- }
68
-
69
- function upsertBarrelFile(files: EmitFile[], exportPaths: string[]): EmitFile[] {
70
- const body = buildBarrelIndexBody(exportPaths);
71
- const barrel: EmitFile = {
72
- path: BARREL_INDEX_PATH,
73
- content: body,
74
- contentHash: hashStable(body),
75
- };
76
-
77
- const withoutBarrel = files.filter((file) => file.path !== BARREL_INDEX_PATH);
78
- return stableSortEmitFiles([...withoutBarrel, barrel]);
79
- }
80
-
81
- function preparePlannedFiles(plan: EmitPlan): EmitFile[] {
82
- const exportPaths = plan.files.map((file) => file.path);
83
- return upsertBarrelFile(plan.files, exportPaths);
84
- }
85
-
86
- async function classifyFile(
87
- file: EmitFile,
88
- context: RenderContext,
89
- workspaceRoot: string,
90
- fs: FileSystem = nodeFileSystem,
91
- ): Promise<"changed" | "unchanged"> {
92
- const rendered = render(file, context);
93
- const absolutePath = resolveWorkspacePath(workspaceRoot, file.path);
94
- const onDisk = await readTextFileIfExists(absolutePath, fs);
95
- return bodiesDiffer(rendered, onDisk) ? "changed" : "unchanged";
96
- }
97
-
98
- export async function emit(plan: EmitPlan, options: EmitOptions): Promise<EmitResult> {
99
- const { workspaceRoot, mode } = options;
100
- const fs = options.fs ?? nodeFileSystem;
101
- const context = buildRenderContext(plan);
102
- const plannedFiles = preparePlannedFiles(plan);
103
- const sortedOrphans = stableSortByPath([...plan.orphanedFiles]);
104
-
105
- const changed: string[] = [];
106
- const unchanged: string[] = [];
107
- const wouldChange: string[] = [];
108
- const removed: string[] = [];
109
- const warnings: Diagnostic[] = [];
110
- const errors: Diagnostic[] = [];
111
-
112
- const classifications = await runWithConcurrency(
113
- plannedFiles,
114
- 8,
115
- async (file) => {
116
- const rendered = render(file, context);
117
- const absolutePath = resolveWorkspacePath(workspaceRoot, file.path);
118
- const onDisk = await readTextFileIfExists(absolutePath, fs);
119
- return {
120
- file,
121
- rendered,
122
- differs: bodiesDiffer(rendered, onDisk),
123
- };
124
- },
125
- );
126
-
127
- for (const { file, rendered, differs } of classifications) {
128
- if (differs) {
129
- changed.push(file.path);
130
- wouldChange.push(file.path);
131
-
132
- if (mode === "check" || mode === "dry-run") {
133
- warnings.push(forgeDrift(file.path));
134
- continue;
135
- }
136
-
137
- try {
138
- const absolutePath = resolveWorkspacePath(workspaceRoot, file.path);
139
- await writeFileAtomic(absolutePath, rendered, fs);
140
- } catch {
141
- errors.push(forgeWriteError(file.path));
142
- }
143
- } else {
144
- unchanged.push(file.path);
145
- }
146
- }
147
-
148
- for (const orphan of sortedOrphans) {
149
- const normalizedOrphan = normalizePath(orphan);
150
- if (!normalizedOrphan.startsWith(`${GENERATED_DIR}/`)) {
151
- continue;
152
- }
153
-
154
- const absoluteOrphan = resolveWorkspacePath(workspaceRoot, normalizedOrphan);
155
-
156
- if (mode === "check") {
157
- errors.push(forgeOrphanedGeneratedFile(normalizedOrphan));
158
- continue;
159
- }
160
-
161
- if (mode === "dry-run") {
162
- wouldChange.push(normalizedOrphan);
163
- continue;
164
- }
165
-
166
- const removedOrphan = await removeFileIfExists(absoluteOrphan, fs);
167
- if (removedOrphan) {
168
- removed.push(normalizedOrphan);
169
- }
170
- }
171
-
172
- const lockDiffers = await lockWouldChange(plan, workspaceRoot, fs);
173
-
174
- if (lockDiffers) {
175
- wouldChange.push(FORGE_LOCK_PATH);
176
- if (mode === "check" || mode === "dry-run") {
177
- warnings.push(forgeDrift(FORGE_LOCK_PATH));
178
- }
179
- }
180
-
181
- if (mode === "write" && errors.length === 0) {
182
- const lockContent = serializeForgeLock(plan.lock);
183
- const lockAbsolutePath = resolveWorkspacePath(workspaceRoot, FORGE_LOCK_PATH);
184
-
185
- try {
186
- if (lockDiffers) {
187
- await writeFileAtomic(lockAbsolutePath, lockContent, fs);
188
- changed.push(FORGE_LOCK_PATH);
189
- } else {
190
- unchanged.push(FORGE_LOCK_PATH);
191
- }
192
- } catch {
193
- errors.push(forgeWriteError(FORGE_LOCK_PATH));
194
- }
195
- }
196
-
197
- const driftFailure =
198
- mode === "check" &&
199
- (changed.length > 0 || sortedOrphans.length > 0 || lockDiffers);
200
- const exitCode: 0 | 1 =
201
- errors.length > 0 || driftFailure ? 1 : 0;
202
-
203
- return {
204
- changed,
205
- unchanged,
206
- removed,
207
- wouldChange,
208
- warnings,
209
- errors,
210
- exitCode,
211
- };
212
- }
213
-
214
- async function lockWouldChange(
215
- plan: EmitPlan,
216
- workspaceRoot: string,
217
- fs: FileSystem = nodeFileSystem,
218
- ): Promise<boolean> {
219
- const lockContent = serializeForgeLock(plan.lock);
220
- const lockAbsolutePath = resolveWorkspacePath(workspaceRoot, FORGE_LOCK_PATH);
221
- const onDisk = await readTextFileIfExists(lockAbsolutePath, fs);
222
- return onDisk !== lockContent;
223
- }
224
-
225
- /**
226
- * Exposed for tests: classify a single file without writing.
227
- */
228
- export async function classifyPlannedFile(
229
- file: EmitFile,
230
- context: RenderContext,
231
- workspaceRoot: string,
232
- fs: FileSystem = nodeFileSystem,
233
- ): Promise<"changed" | "unchanged"> {
234
- return classifyFile(file, context, workspaceRoot, fs);
235
- }
236
-
237
- export { render, renderBody, serializeForgeLock, buildBarrelIndexBody };
1
+ import { join } from "node:path";
2
+ import type { Diagnostic } from "../types/diagnostic.ts";
3
+ import type { EmitFile, EmitMode, EmitPlan } from "../types/emit.ts";
4
+ import { runWithConcurrency } from "../cache/scheduler.ts";
5
+ import { nodeFileSystem } from "../fs/index.ts";
6
+ import type { FileSystem } from "../fs/index.ts";
7
+ import {
8
+ forgeDrift,
9
+ forgeOrphanedGeneratedFile,
10
+ forgeWriteError,
11
+ } from "../diagnostics/create.ts";
12
+ import {
13
+ hashStable,
14
+ normalizePath,
15
+ stableSortByPath,
16
+ stableSortEmitFiles,
17
+ } from "../primitives/index.ts";
18
+ import { buildBarrelIndexBody } from "./barrel.ts";
19
+ import {
20
+ BARREL_INDEX_PATH,
21
+ FORGE_LOCK_PATH,
22
+ GENERATED_DIR,
23
+ } from "./constants.ts";
24
+ import { serializeForgeLock } from "./lock.ts";
25
+ import { render, renderBody, type RenderContext } from "./render.ts";
26
+ import {
27
+ readTextFileIfExists,
28
+ removeFileIfExists,
29
+ writeFileAtomic,
30
+ } from "./write.ts";
31
+
32
+ export interface EmitOptions {
33
+ workspaceRoot: string;
34
+ mode: EmitMode;
35
+ fs?: FileSystem;
36
+ }
37
+
38
+ export interface EmitResult {
39
+ changed: string[];
40
+ unchanged: string[];
41
+ removed: string[];
42
+ wouldChange: string[];
43
+ warnings: Diagnostic[];
44
+ errors: Diagnostic[];
45
+ exitCode: 0 | 1;
46
+ }
47
+
48
+ function resolveWorkspacePath(workspaceRoot: string, relativePath: string): string {
49
+ return join(workspaceRoot, normalizePath(relativePath));
50
+ }
51
+
52
+ function bodiesDiffer(rendered: string, onDisk: string | null): boolean {
53
+ if (onDisk === null) {
54
+ return true;
55
+ }
56
+ return rendered !== onDisk;
57
+ }
58
+
59
+ function buildRenderContext(plan: EmitPlan): RenderContext {
60
+ return {
61
+ generatorVersion: plan.lock.generatorVersion,
62
+ inputHash: plan.lock.inputHash,
63
+ };
64
+ }
65
+
66
+ function upsertBarrelFile(files: EmitFile[], exportPaths: string[]): EmitFile[] {
67
+ const body = buildBarrelIndexBody(exportPaths);
68
+ const barrel: EmitFile = {
69
+ path: BARREL_INDEX_PATH,
70
+ content: body,
71
+ contentHash: hashStable(body),
72
+ };
73
+
74
+ const withoutBarrel = files.filter((file) => file.path !== BARREL_INDEX_PATH);
75
+ return stableSortEmitFiles([...withoutBarrel, barrel]);
76
+ }
77
+
78
+ function preparePlannedFiles(plan: EmitPlan): EmitFile[] {
79
+ const exportPaths = plan.files.map((file) => file.path);
80
+ return upsertBarrelFile(plan.files, exportPaths);
81
+ }
82
+
83
+ async function classifyFile(
84
+ file: EmitFile,
85
+ context: RenderContext,
86
+ workspaceRoot: string,
87
+ fs: FileSystem = nodeFileSystem,
88
+ ): Promise<"changed" | "unchanged"> {
89
+ const rendered = render(file, context);
90
+ const absolutePath = resolveWorkspacePath(workspaceRoot, file.path);
91
+ const onDisk = await readTextFileIfExists(absolutePath, fs);
92
+ return bodiesDiffer(rendered, onDisk) ? "changed" : "unchanged";
93
+ }
94
+
95
+ export async function emit(plan: EmitPlan, options: EmitOptions): Promise<EmitResult> {
96
+ const { workspaceRoot, mode } = options;
97
+ const fs = options.fs ?? nodeFileSystem;
98
+ const context = buildRenderContext(plan);
99
+ const plannedFiles = preparePlannedFiles(plan);
100
+ const sortedOrphans = stableSortByPath([...plan.orphanedFiles]);
101
+
102
+ const changed: string[] = [];
103
+ const unchanged: string[] = [];
104
+ const wouldChange: string[] = [];
105
+ const removed: string[] = [];
106
+ const warnings: Diagnostic[] = [];
107
+ const errors: Diagnostic[] = [];
108
+
109
+ const classifications = await runWithConcurrency(
110
+ plannedFiles,
111
+ 8,
112
+ async (file) => {
113
+ const rendered = render(file, context);
114
+ const absolutePath = resolveWorkspacePath(workspaceRoot, file.path);
115
+ const onDisk = await readTextFileIfExists(absolutePath, fs);
116
+ return {
117
+ file,
118
+ rendered,
119
+ differs: bodiesDiffer(rendered, onDisk),
120
+ };
121
+ },
122
+ );
123
+
124
+ for (const { file, rendered, differs } of classifications) {
125
+ if (differs) {
126
+ changed.push(file.path);
127
+ wouldChange.push(file.path);
128
+
129
+ if (mode === "check" || mode === "dry-run") {
130
+ warnings.push(forgeDrift(file.path));
131
+ continue;
132
+ }
133
+
134
+ try {
135
+ const absolutePath = resolveWorkspacePath(workspaceRoot, file.path);
136
+ await writeFileAtomic(absolutePath, rendered, fs);
137
+ } catch {
138
+ errors.push(forgeWriteError(file.path));
139
+ }
140
+ } else {
141
+ unchanged.push(file.path);
142
+ }
143
+ }
144
+
145
+ for (const orphan of sortedOrphans) {
146
+ const normalizedOrphan = normalizePath(orphan);
147
+ if (!normalizedOrphan.startsWith(`${GENERATED_DIR}/`)) {
148
+ continue;
149
+ }
150
+
151
+ const absoluteOrphan = resolveWorkspacePath(workspaceRoot, normalizedOrphan);
152
+
153
+ if (mode === "check") {
154
+ errors.push(forgeOrphanedGeneratedFile(normalizedOrphan));
155
+ continue;
156
+ }
157
+
158
+ if (mode === "dry-run") {
159
+ wouldChange.push(normalizedOrphan);
160
+ continue;
161
+ }
162
+
163
+ const removedOrphan = await removeFileIfExists(absoluteOrphan, fs);
164
+ if (removedOrphan) {
165
+ removed.push(normalizedOrphan);
166
+ }
167
+ }
168
+
169
+ const lockDiffers = await lockWouldChange(plan, workspaceRoot, fs);
170
+
171
+ if (lockDiffers) {
172
+ wouldChange.push(FORGE_LOCK_PATH);
173
+ if (mode === "check" || mode === "dry-run") {
174
+ warnings.push(forgeDrift(FORGE_LOCK_PATH));
175
+ }
176
+ }
177
+
178
+ if (mode === "write" && errors.length === 0) {
179
+ const lockContent = serializeForgeLock(plan.lock);
180
+ const lockAbsolutePath = resolveWorkspacePath(workspaceRoot, FORGE_LOCK_PATH);
181
+
182
+ try {
183
+ if (lockDiffers) {
184
+ await writeFileAtomic(lockAbsolutePath, lockContent, fs);
185
+ changed.push(FORGE_LOCK_PATH);
186
+ } else {
187
+ unchanged.push(FORGE_LOCK_PATH);
188
+ }
189
+ } catch {
190
+ errors.push(forgeWriteError(FORGE_LOCK_PATH));
191
+ }
192
+ }
193
+
194
+ const driftFailure =
195
+ mode === "check" &&
196
+ (changed.length > 0 || sortedOrphans.length > 0 || lockDiffers);
197
+ const exitCode: 0 | 1 =
198
+ errors.length > 0 || driftFailure ? 1 : 0;
199
+
200
+ return {
201
+ changed,
202
+ unchanged,
203
+ removed,
204
+ wouldChange,
205
+ warnings,
206
+ errors,
207
+ exitCode,
208
+ };
209
+ }
210
+
211
+ async function lockWouldChange(
212
+ plan: EmitPlan,
213
+ workspaceRoot: string,
214
+ fs: FileSystem = nodeFileSystem,
215
+ ): Promise<boolean> {
216
+ const lockContent = serializeForgeLock(plan.lock);
217
+ const lockAbsolutePath = resolveWorkspacePath(workspaceRoot, FORGE_LOCK_PATH);
218
+ const onDisk = await readTextFileIfExists(lockAbsolutePath, fs);
219
+ return onDisk !== lockContent;
220
+ }
221
+
222
+ /**
223
+ * Exposed for tests: classify a single file without writing.
224
+ */
225
+ export async function classifyPlannedFile(
226
+ file: EmitFile,
227
+ context: RenderContext,
228
+ workspaceRoot: string,
229
+ fs: FileSystem = nodeFileSystem,
230
+ ): Promise<"changed" | "unchanged"> {
231
+ return classifyFile(file, context, workspaceRoot, fs);
232
+ }
233
+
234
+ export { render, renderBody, serializeForgeLock, buildBarrelIndexBody };
@@ -1,24 +1,24 @@
1
- export {
2
- FORGE_LOCK_SCHEMA_VERSION,
3
- GENERATED_DIR,
4
- FORGE_LOCK_PATH,
5
- BARREL_INDEX_PATH,
6
- GENERATOR_VERSION,
7
- } from "./constants.ts";
8
- export { detectArtifactKind } from "./artifact-kind.ts";
9
- export type { ArtifactKind } from "./artifact-kind.ts";
10
- export { render, renderBody } from "./render.ts";
11
- export type { RenderContext } from "./render.ts";
12
- export { serializeForgeLock } from "./lock.ts";
13
- export { buildBarrelIndexBody } from "./barrel.ts";
14
- export {
15
- readTextFileIfExists,
16
- writeFileAtomic,
17
- removeFileIfExists,
18
- } from "./write.ts";
19
- export {
20
- emit,
21
- classifyPlannedFile,
22
- type EmitOptions,
23
- type EmitResult,
24
- } from "./emit.ts";
1
+ export {
2
+ FORGE_LOCK_SCHEMA_VERSION,
3
+ GENERATED_DIR,
4
+ FORGE_LOCK_PATH,
5
+ BARREL_INDEX_PATH,
6
+ GENERATOR_VERSION,
7
+ } from "./constants.ts";
8
+ export { detectArtifactKind } from "./artifact-kind.ts";
9
+ export type { ArtifactKind } from "./artifact-kind.ts";
10
+ export { render, renderBody } from "./render.ts";
11
+ export type { RenderContext } from "./render.ts";
12
+ export { serializeForgeLock } from "./lock.ts";
13
+ export { buildBarrelIndexBody } from "./barrel.ts";
14
+ export {
15
+ readTextFileIfExists,
16
+ writeFileAtomic,
17
+ removeFileIfExists,
18
+ } from "./write.ts";
19
+ export {
20
+ emit,
21
+ classifyPlannedFile,
22
+ type EmitOptions,
23
+ type EmitResult,
24
+ } from "./emit.ts";