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
@@ -3,235 +3,235 @@ import { readFileSync, writeFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { nodeFileSystem } from "../fs/index.ts";
5
5
  import type { PackageManager } from "../types/runtime.ts";
6
- import type { PmAddOptions, PmAddResult } from "../types/cli.ts";
7
- import { buildAddCommand } from "./commands.ts";
8
- import {
9
- detectPackageManager,
10
- getLockfileCandidates,
11
- getLockfileForPm,
12
- } from "./detect.ts";
13
- import {
14
- defaultCommandExecutor,
15
- PackageManagerCommandError,
16
- type CommandExecutor,
17
- } from "./executor.ts";
18
- import { parsePackageName } from "./parse-spec.ts";
19
- import { readInstalledVersion } from "./version.ts";
20
-
21
- export interface PackageManagerAdapter {
22
- readonly name: PackageManager;
23
- readonly lockfile: string;
24
- add(spec: string, opts: PmAddOptions): Promise<PmAddResult>;
25
- dryRunAdd(spec: string, opts: PmAddOptions): Promise<PmAddResult>;
26
- /** Install into a temp dir; caller owns cleanup unless using dryRunAdd. */
27
- dryRunAddWithPath(spec: string, opts: PmAddOptions): Promise<DryRunAddResult>;
28
- detectResolvedVersion(spec: string, cwd: string): Promise<string>;
29
- }
30
-
31
- export interface DryRunAddResult extends PmAddResult {
32
- /** Absolute path to the temp install directory (for downstream analysis). */
33
- installPath: string;
34
- }
35
-
36
- export interface CreatePackageManagerAdapterOptions {
37
- executor?: CommandExecutor;
38
- /** When true, retain the temp directory after dryRunAdd (for debugging). */
39
- retainDryRunDir?: boolean;
40
- }
41
-
42
- function hashFile(path: string): string | null {
43
- if (!nodeFileSystem.exists(path)) {
44
- return null;
45
- }
46
- const content = readFileSync(path);
47
- return createHash("sha256").update(content).digest("hex");
48
- }
49
-
50
- function hashLockfiles(cwd: string, pm: PackageManager): string | null {
51
- const hashes: string[] = [];
52
- for (const file of getLockfileCandidates(pm)) {
53
- const h = hashFile(join(cwd, file));
54
- if (h !== null) {
55
- hashes.push(`${file}:${h}`);
56
- }
57
- }
58
- return hashes.length > 0 ? hashes.join("|") : null;
59
- }
60
-
61
- function lockfileChanged(
62
- before: string | null,
63
- after: string | null,
64
- pm: PackageManager,
65
- cwd: string,
66
- ): boolean {
67
- if (before !== after) {
68
- return true;
69
- }
70
- // Lockfile may be created on first install.
71
- if (before === null) {
72
- return getLockfileCandidates(pm).some((f) =>
73
- nodeFileSystem.exists(join(cwd, f)),
74
- );
75
- }
76
- return false;
77
- }
78
-
79
- async function runInstall(
80
- pm: PackageManager,
81
- spec: string,
82
- opts: PmAddOptions,
83
- executor: CommandExecutor,
84
- ): Promise<PmAddResult> {
85
- const ignoreScripts = opts.ignoreScripts ?? true;
86
- const argv = buildAddCommand(pm, spec, { ignoreScripts });
87
- const lockBefore = hashLockfiles(opts.cwd, pm);
88
-
89
- const result = await executor.run(argv, { cwd: opts.cwd });
90
- if (result.exitCode !== 0) {
91
- throw new PackageManagerCommandError(
92
- `Package manager ${pm} failed to add ${spec}: ${result.stderr || result.stdout}`,
93
- argv,
94
- result,
95
- );
96
- }
97
-
98
- const packageName = parsePackageName(spec);
99
- const resolvedVersion = readInstalledVersion(packageName, opts.cwd);
100
- if (!resolvedVersion) {
101
- throw new Error(
102
- `Could not detect installed version for ${packageName} in ${opts.cwd}`,
103
- );
104
- }
105
-
106
- const lockAfter = hashLockfiles(opts.cwd, pm);
107
-
108
- return {
109
- resolvedVersion,
110
- lockfileChanged: lockfileChanged(lockBefore, lockAfter, pm, opts.cwd),
111
- };
112
- }
113
-
114
- class PackageManagerAdapterImpl implements PackageManagerAdapter {
115
- readonly name: PackageManager;
116
- readonly lockfile: string;
117
- private readonly executor: CommandExecutor;
118
- private readonly retainDryRunDir: boolean;
119
-
120
- constructor(
121
- name: PackageManager,
122
- options: CreatePackageManagerAdapterOptions = {},
123
- ) {
124
- this.name = name;
125
- this.lockfile = getLockfileForPm(name);
126
- this.executor = options.executor ?? defaultCommandExecutor;
127
- this.retainDryRunDir = options.retainDryRunDir ?? false;
128
- }
129
-
130
- async add(spec: string, opts: PmAddOptions): Promise<PmAddResult> {
131
- return runInstall(this.name, spec, opts, this.executor);
132
- }
133
-
134
- async dryRunAdd(spec: string, opts: PmAddOptions): Promise<PmAddResult> {
135
- const result = await this.dryRunAddWithPath(spec, opts);
136
- if (!this.retainDryRunDir) {
137
- try {
138
- nodeFileSystem.remove(result.installPath);
139
- } catch {
140
- // best-effort cleanup
141
- }
142
- }
143
- return {
144
- resolvedVersion: result.resolvedVersion,
145
- integrity: result.integrity,
146
- lockfileChanged: false,
147
- };
148
- }
149
-
150
- /** Internal: dry-run install returning the temp directory path. */
151
- async dryRunAddWithPath(
152
- spec: string,
153
- opts: PmAddOptions,
154
- ): Promise<DryRunAddResult> {
155
- const cacheBase = join(opts.cwd, ".forge", "cache", "dry-run");
156
- nodeFileSystem.mkdirp(cacheBase);
157
- const tempDir = nodeFileSystem.makeTempDir(join(cacheBase, "add-"));
158
-
159
- const minimalPkg = {
160
- name: "forge-dry-run",
161
- private: true,
162
- version: "0.0.0",
163
- };
164
- writeFileSync(
165
- join(tempDir, "package.json"),
166
- `${JSON.stringify(minimalPkg, null, 2)}\n`,
167
- "utf8",
168
- );
169
-
170
- const installResult = await runInstall(
171
- this.name,
172
- spec,
173
- {
174
- cwd: tempDir,
175
- ignoreScripts: opts.ignoreScripts ?? true,
176
- },
177
- this.executor,
178
- );
179
-
180
- return {
181
- ...installResult,
182
- lockfileChanged: false,
183
- installPath: tempDir,
184
- };
185
- }
186
-
187
- async detectResolvedVersion(spec: string, cwd: string): Promise<string> {
188
- const version = readInstalledVersion(spec, cwd);
189
- if (!version) {
190
- throw new Error(
191
- `Package ${parsePackageName(spec)} is not installed in ${cwd}`,
192
- );
193
- }
194
- return version;
195
- }
196
- }
197
-
198
- export function createPackageManagerAdapter(
199
- pm: PackageManager,
200
- options?: CreatePackageManagerAdapterOptions,
201
- ): PackageManagerAdapter {
202
- return new PackageManagerAdapterImpl(pm, options);
203
- }
204
-
205
- export function detectAndCreatePackageManagerAdapter(
206
- workspaceRoot: string,
207
- options?: CreatePackageManagerAdapterOptions,
208
- ): PackageManagerAdapter {
209
- const pm = detectPackageManager(workspaceRoot);
210
- return createPackageManagerAdapter(pm, options);
211
- }
212
-
213
- /** Fallback when dry-run install is unavailable: recipe-known plan placeholder. */
214
- export function dryRunRecipeFallbackMessage(alias: string): string {
215
- return (
216
- `Dry-run for "${alias}": .d.ts analysis requires a real install. ` +
217
- `Use dryRunAdd to install into a temp directory, or run forge add without --dry-run.`
218
- );
219
- }
220
-
221
- export {
222
- detectPackageManager,
223
- detectPackageManagerFromLockfiles,
224
- getLockfileForPm,
225
- getLockfileCandidates,
226
- parsePackageManagerField,
227
- LOCKFILE_PM_MAP,
228
- } from "./detect.ts";
229
- export { buildAddCommand } from "./commands.ts";
230
- export { parsePackageName } from "./parse-spec.ts";
231
- export { readInstalledVersion } from "./version.ts";
232
- export {
233
- defaultCommandExecutor,
234
- PackageManagerCommandError,
235
- type CommandExecutor,
236
- type CommandRunResult,
237
- } from "./executor.ts";
6
+ import type { PmAddOptions, PmAddResult } from "../types/cli.ts";
7
+ import { buildAddCommand } from "./commands.ts";
8
+ import {
9
+ detectPackageManager,
10
+ getLockfileCandidates,
11
+ getLockfileForPm,
12
+ } from "./detect.ts";
13
+ import {
14
+ defaultCommandExecutor,
15
+ PackageManagerCommandError,
16
+ type CommandExecutor,
17
+ } from "./executor.ts";
18
+ import { parsePackageName } from "./parse-spec.ts";
19
+ import { readInstalledVersion } from "./version.ts";
20
+
21
+ export interface PackageManagerAdapter {
22
+ readonly name: PackageManager;
23
+ readonly lockfile: string;
24
+ add(spec: string, opts: PmAddOptions): Promise<PmAddResult>;
25
+ dryRunAdd(spec: string, opts: PmAddOptions): Promise<PmAddResult>;
26
+ /** Install into a temp dir; caller owns cleanup unless using dryRunAdd. */
27
+ dryRunAddWithPath(spec: string, opts: PmAddOptions): Promise<DryRunAddResult>;
28
+ detectResolvedVersion(spec: string, cwd: string): Promise<string>;
29
+ }
30
+
31
+ export interface DryRunAddResult extends PmAddResult {
32
+ /** Absolute path to the temp install directory (for downstream analysis). */
33
+ installPath: string;
34
+ }
35
+
36
+ export interface CreatePackageManagerAdapterOptions {
37
+ executor?: CommandExecutor;
38
+ /** When true, retain the temp directory after dryRunAdd (for debugging). */
39
+ retainDryRunDir?: boolean;
40
+ }
41
+
42
+ function hashFile(path: string): string | null {
43
+ if (!nodeFileSystem.exists(path)) {
44
+ return null;
45
+ }
46
+ const content = readFileSync(path);
47
+ return createHash("sha256").update(content).digest("hex");
48
+ }
49
+
50
+ function hashLockfiles(cwd: string, pm: PackageManager): string | null {
51
+ const hashes: string[] = [];
52
+ for (const file of getLockfileCandidates(pm)) {
53
+ const h = hashFile(join(cwd, file));
54
+ if (h !== null) {
55
+ hashes.push(`${file}:${h}`);
56
+ }
57
+ }
58
+ return hashes.length > 0 ? hashes.join("|") : null;
59
+ }
60
+
61
+ function lockfileChanged(
62
+ before: string | null,
63
+ after: string | null,
64
+ pm: PackageManager,
65
+ cwd: string,
66
+ ): boolean {
67
+ if (before !== after) {
68
+ return true;
69
+ }
70
+ // Lockfile may be created on first install.
71
+ if (before === null) {
72
+ return getLockfileCandidates(pm).some((f) =>
73
+ nodeFileSystem.exists(join(cwd, f)),
74
+ );
75
+ }
76
+ return false;
77
+ }
78
+
79
+ async function runInstall(
80
+ pm: PackageManager,
81
+ spec: string,
82
+ opts: PmAddOptions,
83
+ executor: CommandExecutor,
84
+ ): Promise<PmAddResult> {
85
+ const ignoreScripts = opts.ignoreScripts ?? true;
86
+ const argv = buildAddCommand(pm, spec, { ignoreScripts });
87
+ const lockBefore = hashLockfiles(opts.cwd, pm);
88
+
89
+ const result = await executor.run(argv, { cwd: opts.cwd });
90
+ if (result.exitCode !== 0) {
91
+ throw new PackageManagerCommandError(
92
+ `Package manager ${pm} failed to add ${spec}: ${result.stderr || result.stdout}`,
93
+ argv,
94
+ result,
95
+ );
96
+ }
97
+
98
+ const packageName = parsePackageName(spec);
99
+ const resolvedVersion = readInstalledVersion(packageName, opts.cwd);
100
+ if (!resolvedVersion) {
101
+ throw new Error(
102
+ `Could not detect installed version for ${packageName} in ${opts.cwd}`,
103
+ );
104
+ }
105
+
106
+ const lockAfter = hashLockfiles(opts.cwd, pm);
107
+
108
+ return {
109
+ resolvedVersion,
110
+ lockfileChanged: lockfileChanged(lockBefore, lockAfter, pm, opts.cwd),
111
+ };
112
+ }
113
+
114
+ class PackageManagerAdapterImpl implements PackageManagerAdapter {
115
+ readonly name: PackageManager;
116
+ readonly lockfile: string;
117
+ private readonly executor: CommandExecutor;
118
+ private readonly retainDryRunDir: boolean;
119
+
120
+ constructor(
121
+ name: PackageManager,
122
+ options: CreatePackageManagerAdapterOptions = {},
123
+ ) {
124
+ this.name = name;
125
+ this.lockfile = getLockfileForPm(name);
126
+ this.executor = options.executor ?? defaultCommandExecutor;
127
+ this.retainDryRunDir = options.retainDryRunDir ?? false;
128
+ }
129
+
130
+ async add(spec: string, opts: PmAddOptions): Promise<PmAddResult> {
131
+ return runInstall(this.name, spec, opts, this.executor);
132
+ }
133
+
134
+ async dryRunAdd(spec: string, opts: PmAddOptions): Promise<PmAddResult> {
135
+ const result = await this.dryRunAddWithPath(spec, opts);
136
+ if (!this.retainDryRunDir) {
137
+ try {
138
+ nodeFileSystem.remove(result.installPath);
139
+ } catch {
140
+ // best-effort cleanup
141
+ }
142
+ }
143
+ return {
144
+ resolvedVersion: result.resolvedVersion,
145
+ integrity: result.integrity,
146
+ lockfileChanged: false,
147
+ };
148
+ }
149
+
150
+ /** Internal: dry-run install returning the temp directory path. */
151
+ async dryRunAddWithPath(
152
+ spec: string,
153
+ opts: PmAddOptions,
154
+ ): Promise<DryRunAddResult> {
155
+ const cacheBase = join(opts.cwd, ".forge", "cache", "dry-run");
156
+ nodeFileSystem.mkdirp(cacheBase);
157
+ const tempDir = nodeFileSystem.makeTempDir(join(cacheBase, "add-"));
158
+
159
+ const minimalPkg = {
160
+ name: "forge-dry-run",
161
+ private: true,
162
+ version: "0.0.0",
163
+ };
164
+ writeFileSync(
165
+ join(tempDir, "package.json"),
166
+ `${JSON.stringify(minimalPkg, null, 2)}\n`,
167
+ "utf8",
168
+ );
169
+
170
+ const installResult = await runInstall(
171
+ this.name,
172
+ spec,
173
+ {
174
+ cwd: tempDir,
175
+ ignoreScripts: opts.ignoreScripts ?? true,
176
+ },
177
+ this.executor,
178
+ );
179
+
180
+ return {
181
+ ...installResult,
182
+ lockfileChanged: false,
183
+ installPath: tempDir,
184
+ };
185
+ }
186
+
187
+ async detectResolvedVersion(spec: string, cwd: string): Promise<string> {
188
+ const version = readInstalledVersion(spec, cwd);
189
+ if (!version) {
190
+ throw new Error(
191
+ `Package ${parsePackageName(spec)} is not installed in ${cwd}`,
192
+ );
193
+ }
194
+ return version;
195
+ }
196
+ }
197
+
198
+ export function createPackageManagerAdapter(
199
+ pm: PackageManager,
200
+ options?: CreatePackageManagerAdapterOptions,
201
+ ): PackageManagerAdapter {
202
+ return new PackageManagerAdapterImpl(pm, options);
203
+ }
204
+
205
+ export function detectAndCreatePackageManagerAdapter(
206
+ workspaceRoot: string,
207
+ options?: CreatePackageManagerAdapterOptions,
208
+ ): PackageManagerAdapter {
209
+ const pm = detectPackageManager(workspaceRoot);
210
+ return createPackageManagerAdapter(pm, options);
211
+ }
212
+
213
+ /** Fallback when dry-run install is unavailable: recipe-known plan placeholder. */
214
+ export function dryRunRecipeFallbackMessage(alias: string): string {
215
+ return (
216
+ `Dry-run for "${alias}": .d.ts analysis requires a real install. ` +
217
+ `Use dryRunAdd to install into a temp directory, or run forge add without --dry-run.`
218
+ );
219
+ }
220
+
221
+ export {
222
+ detectPackageManager,
223
+ detectPackageManagerFromLockfiles,
224
+ getLockfileForPm,
225
+ getLockfileCandidates,
226
+ parsePackageManagerField,
227
+ LOCKFILE_PM_MAP,
228
+ } from "./detect.ts";
229
+ export { buildAddCommand } from "./commands.ts";
230
+ export { parsePackageName } from "./parse-spec.ts";
231
+ export { readInstalledVersion } from "./version.ts";
232
+ export {
233
+ defaultCommandExecutor,
234
+ PackageManagerCommandError,
235
+ type CommandExecutor,
236
+ type CommandRunResult,
237
+ } from "./executor.ts";
@@ -1,47 +1,47 @@
1
- import type { PackageManager } from "../types/runtime.ts";
2
-
3
- export interface BuildAddCommandOptions {
4
- ignoreScripts: boolean;
5
- }
6
-
7
- /**
8
- * Build argv for adding a dependency with lifecycle scripts disabled by default.
9
- */
10
- export function buildAddCommand(
11
- pm: PackageManager,
12
- spec: string,
13
- options: BuildAddCommandOptions,
14
- ): string[] {
15
- const ignoreScripts = options.ignoreScripts;
16
-
17
- switch (pm) {
18
- case "bun": {
19
- const args = ["bun", "add", spec];
20
- if (ignoreScripts) {
21
- args.push("--ignore-scripts");
22
- }
23
- return args;
24
- }
25
- case "npm": {
26
- const args = ["npm", "install", spec, "--save", "--no-fund", "--no-audit"];
27
- if (ignoreScripts) {
28
- args.push("--ignore-scripts");
29
- }
30
- return args;
31
- }
32
- case "pnpm": {
33
- const args = ["pnpm", "add", spec];
34
- if (ignoreScripts) {
35
- args.push("--ignore-scripts");
36
- }
37
- return args;
38
- }
39
- case "yarn": {
40
- const args = ["yarn", "add", spec];
41
- if (ignoreScripts) {
42
- args.push("--ignore-scripts");
43
- }
44
- return args;
45
- }
46
- }
47
- }
1
+ import type { PackageManager } from "../types/runtime.ts";
2
+
3
+ export interface BuildAddCommandOptions {
4
+ ignoreScripts: boolean;
5
+ }
6
+
7
+ /**
8
+ * Build argv for adding a dependency with lifecycle scripts disabled by default.
9
+ */
10
+ export function buildAddCommand(
11
+ pm: PackageManager,
12
+ spec: string,
13
+ options: BuildAddCommandOptions,
14
+ ): string[] {
15
+ const ignoreScripts = options.ignoreScripts;
16
+
17
+ switch (pm) {
18
+ case "bun": {
19
+ const args = ["bun", "add", spec];
20
+ if (ignoreScripts) {
21
+ args.push("--ignore-scripts");
22
+ }
23
+ return args;
24
+ }
25
+ case "npm": {
26
+ const args = ["npm", "install", spec, "--save", "--no-fund", "--no-audit"];
27
+ if (ignoreScripts) {
28
+ args.push("--ignore-scripts");
29
+ }
30
+ return args;
31
+ }
32
+ case "pnpm": {
33
+ const args = ["pnpm", "add", spec];
34
+ if (ignoreScripts) {
35
+ args.push("--ignore-scripts");
36
+ }
37
+ return args;
38
+ }
39
+ case "yarn": {
40
+ const args = ["yarn", "add", spec];
41
+ if (ignoreScripts) {
42
+ args.push("--ignore-scripts");
43
+ }
44
+ return args;
45
+ }
46
+ }
47
+ }