@graftland/package-manager 0.2.0-beta.1 → 0.2.0-beta.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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;qBAgDa,OAAK,OAAA;YAMN,eAAe,MAAM;;;;;;WAGd;;qBAFN,4BAA4B;;;;;;;;;;WAUtB;;qBAFN,8BAA8B;YAoC/B,QACT,sBACA,2BACA,wBACA,cAAc,gBACd,OAAO;iBACO;;WAEP;;WAEA,QAAQ;;WAER;;;qBAqDG,cAAW,SAAA,YAAA,OAAA,uDAAA,sBAAA,cAAA,gBAAA,OAAA,aAAA,WAAA,aAAA,KAAA;iBAmEP;WACP;;WAEA;WACA;;;YAGE,QAAQ,QAAQ,gBAAgB;iBAC3B;WACP;WACA,QAAQ;WACR,OAAO;;iBAEA;WACP,OAAO,cAAc;WACrB,SAAS,cAAc;WACvB,SAAS,cAAc;;qBAEpB,YAAS,QAAY,OAAK,OAAS,UAAQ;iBA0BvC,sBAAsB;WAC7B,OAAO;;WAEP,wBAAwB,OAAO,OAAO;;iBAE/B;WACP;WACA,OAAO;WACP,WAAW,OAAO,OAAO,OAAO;;WAEhC,kBAAkB,OAAO,OAAO,OAAO,OAAO;;WAE9C,UAAU,SAAS,YAAY,OAAO,OAAO,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA6BzD,OAAI,SACP,YACP,OAAO,OACT,gBACA,OACA,WAAW,aAAa,KAAK,OAAO,oBAAoB"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;qBAiDa,OAAK,OAAA;YAMN,eAAe,MAAM;;;;;;WAGd;;qBAFN,4BAA4B;;;;;;;;;;WAUtB;;qBAFN,8BAA8B;YAoC/B,QACT,sBACA,2BACA,wBACA,cAAc,gBACd,OAAO;iBACO;;WAEP;;WAEA,QAAQ;;WAER;;;qBAkGG,cAAW,SAAA,YAAA,OAAA,uDAAA,sBAAA,cAAA,gBAAA,OAAA,aAAA,WAAA,aAAA,KAAA;iBAmEP;WACP;;WAEA;WACA;;;YAGE,QAAQ,QAAQ,gBAAgB;iBAC3B;WACP;WACA,QAAQ;WACR,OAAO;;iBAEA;WACP,OAAO,cAAc;WACrB,SAAS,cAAc;WACvB,SAAS,cAAc;;qBAEpB,YAAS,QAAY,OAAK,OAAS,UAAQ;iBA0BvC,sBAAsB;WAC7B,OAAO;;WAEP,wBAAwB,OAAO,OAAO;;iBAE/B;WACP;WACA,OAAO;WACP,WAAW,OAAO,OAAO,OAAO;;WAEhC,kBAAkB,OAAO,OAAO,OAAO,OAAO;;WAE9C,UAAU,SAAS,YAAY,OAAO,OAAO,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA6BzD,OAAI,SACP,YACP,OAAO,OACT,gBACA,OACA,WAAW,aAAa,KAAK,OAAO,oBAAoB"}
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { PackageManagerDeclaration, PackageManagerName } from "./PackageManagerD
2
2
  import { terminateProcess } from "./process.js";
3
3
  import { UnsupportedFileTypeError, inspectManagedFiles, withSnapshot, writeSnapshot } from "./snapshot.js";
4
4
  import { Changes, PackageName, applyChanges, decodeManifest, encodeManifest, fields } from "./manifest.js";
5
- import { Array, Cause, Data, Effect, Exit, FileSystem, HashMap, Option, Path, PlatformError, Record, Ref, Schema, Semaphore, Stream, String } from "effect";
5
+ import { Array, Cause, Data, Effect, Exit, FileSystem, HashMap, Match, Option, Path, PlatformError, Record, Ref, Schema, Semaphore, Stream, String } from "effect";
6
6
  import { ChildProcess, ChildProcessSpawner } from "effect/process";
7
7
  // src/index.ts
8
8
  /** Non-interactive package installation. Provide platform services at the application boundary. */
@@ -54,7 +54,47 @@ const backupFiles = [
54
54
  ".pnp.cjs",
55
55
  ".pnp.loader.mjs"
56
56
  ];
57
- const executable = (agent) => String.startsWith("yarn")(agent) ? "yarn" : agent;
57
+ const strategy = (agent, operation) => {
58
+ const recovery = operation === "recover";
59
+ return Match.value(agent).pipe(Match.when("npm", () => ({
60
+ executable: "npm",
61
+ args: [
62
+ "install",
63
+ "--no-audit",
64
+ "--no-fund",
65
+ ...recovery ? ["--prefer-offline"] : []
66
+ ],
67
+ env: { CI: "true" },
68
+ lockfiles: ["package-lock.json", "npm-shrinkwrap.json"]
69
+ })), Match.when("pnpm", () => ({
70
+ executable: "pnpm",
71
+ args: [
72
+ "install",
73
+ recovery ? "--frozen-lockfile" : "--no-frozen-lockfile",
74
+ "--config.confirmModulesPurge=false"
75
+ ],
76
+ env: { CI: "true" },
77
+ lockfiles: ["pnpm-lock.yaml"]
78
+ })), Match.when("yarn-classic", () => ({
79
+ executable: "yarn",
80
+ args: [
81
+ "install",
82
+ "--non-interactive",
83
+ ...recovery ? ["--frozen-lockfile", "--prefer-offline"] : []
84
+ ],
85
+ env: { CI: "true" },
86
+ lockfiles: ["yarn.lock"]
87
+ })), Match.when("yarn-berry", () => ({
88
+ executable: "yarn",
89
+ args: ["install", ...recovery ? ["--immutable"] : []],
90
+ env: {
91
+ CI: "true",
92
+ YARN_ENABLE_IMMUTABLE_INSTALLS: recovery ? "true" : "false",
93
+ YARN_NODE_LINKER: "node-modules"
94
+ },
95
+ lockfiles: ["yarn.lock"]
96
+ })), Match.exhaustive);
97
+ };
58
98
  const readSource = Effect.fnUntraced(function* (fs, file) {
59
99
  const bytes = yield* fs.readFile(file);
60
100
  return yield* Effect.try({
@@ -194,22 +234,8 @@ const make = (options) => Effect.gen(function* () {
194
234
  const readState = Effect.flatMap(readManifest, stateOf);
195
235
  const termination = { forceKillAfter: "5 seconds" };
196
236
  const run = Effect.fnUntraced(function* (operation) {
197
- const recovery = operation === "recover";
198
- const args = agent === "npm" ? [
199
- "install",
200
- "--no-audit",
201
- "--no-fund",
202
- ...recovery ? ["--prefer-offline"] : []
203
- ] : agent === "pnpm" ? [
204
- "install",
205
- recovery ? "--frozen-lockfile" : "--no-frozen-lockfile",
206
- "--config.confirmModulesPurge=false"
207
- ] : agent === "yarn-classic" ? [
208
- "install",
209
- "--non-interactive",
210
- ...recovery ? ["--frozen-lockfile", "--prefer-offline"] : []
211
- ] : ["install", ...recovery ? ["--immutable"] : []];
212
- return yield* Effect.acquireUseRelease(spawner.spawn(ChildProcess.make(executable(agent), args, {
237
+ const command = strategy(agent, operation);
238
+ return yield* Effect.acquireUseRelease(spawner.spawn(ChildProcess.make(command.executable, command.args, {
213
239
  cwd: directory,
214
240
  extendEnv: true,
215
241
  stdin: "ignore",
@@ -217,25 +243,19 @@ const make = (options) => Effect.gen(function* () {
217
243
  stderr: "inherit",
218
244
  detached: true,
219
245
  ...termination,
220
- env: {
221
- CI: "true",
222
- ...agent === "yarn-berry" ? {
223
- YARN_ENABLE_IMMUTABLE_INSTALLS: recovery ? "true" : "false",
224
- YARN_NODE_LINKER: "node-modules"
225
- } : {}
226
- }
246
+ env: command.env
227
247
  })).pipe(Effect.tap(() => Ref.set(transaction, ManagerState.Active({ writer: WriterState.Running() })))), (process) => Effect.gen(function* () {
228
248
  const code = yield* process.exitCode.pipe(Effect.timeoutOrElse({
229
249
  duration: "10 minutes",
230
250
  orElse: () => PackageManagerError.make({
231
251
  operation,
232
- message: `${executable(agent)} ${operation} timed out`
252
+ message: `${command.executable} ${operation} timed out`
233
253
  })
234
254
  }));
235
255
  if (code !== 0) return yield* PackageManagerError.make({
236
256
  operation,
237
257
  exitCode: code,
238
- message: `${executable(agent)} ${operation} exited with code ${code}`
258
+ message: `${command.executable} ${operation} exited with code ${code}`
239
259
  });
240
260
  }), (process) => Effect.gen(function* () {
241
261
  const result = yield* terminateProcess(process, termination);
@@ -270,7 +290,7 @@ const make = (options) => Effect.gen(function* () {
270
290
  const manifest = yield* decodeManifest(original);
271
291
  const before = yield* stateOf(manifest);
272
292
  const text = yield* encodeManifest(applyChanges(manifest, validated), original);
273
- const agentLocks = agent === "npm" ? ["package-lock.json", "npm-shrinkwrap.json"] : agent === "pnpm" ? ["pnpm-lock.yaml"] : ["yarn.lock"];
293
+ const agentLocks = strategy(agent, "install").lockfiles;
274
294
  const hasLock = Array.some(backups, ({ name, contents }) => Array.contains(agentLocks, name) && Option.isSome(contents));
275
295
  const completed = yield* withSnapshot(fs, Ref.get(transaction).pipe(Effect.map((state) => state._tag === "Ready")), (snapshotDirectory) => Effect.gen(function* () {
276
296
  const snapshot = yield* writeSnapshot(fs, path, snapshotDirectory, directory, agent, backups);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/** Non-interactive package installation. Provide platform services at the application boundary. */\nimport {\n\tArray,\n\tCause,\n\tData,\n\tRef,\n\tEffect,\n\tExit,\n\tFileSystem,\n\tHashMap,\n\tOption,\n\tPath,\n\tPlatformError,\n\tRecord,\n\tSchema,\n\tSemaphore,\n\tString,\n\tStream,\n} from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/process\"\nimport {\n\tPackageManagerDeclaration,\n\tPackageManagerName,\n} from \"./PackageManagerDeclaration\"\nexport { PackageManagerDeclaration } from \"./PackageManagerDeclaration\"\nimport { terminateProcess } from \"./process\"\nimport {\n\twriteSnapshot,\n\twithSnapshot,\n\ttype SnapshotCleanupFailure,\n\tinspectManagedFiles,\n\tUnsupportedFileTypeError,\n\ttype Contents,\n} from \"./snapshot\"\nexport {\n\tUnsupportedFileTypeError,\n\ttype SnapshotCleanupFailure,\n} from \"./snapshot\"\nimport {\n\tapplyChanges,\n\tChanges,\n\tdecodeManifest,\n\tencodeManifest,\n\tfields,\n\ttype Manifest,\n} from \"./manifest\"\nexport { Changes, PackageName } from \"./manifest\"\n\nexport const Agent = Schema.Literals([\n\t\"npm\",\n\t\"pnpm\",\n\t\"yarn-classic\",\n\t\"yarn-berry\",\n])\nexport type Agent = typeof Agent.Type\nexport class PackageManagerError extends Schema.TaggedError<\n\tPackageManagerError,\n\t{ readonly brand: unique symbol }\n>()(\"PackageManagerError\", {\n\toperation: Schema.Literals([\"detect\", \"install\", \"recover\", \"state\"]),\n\tmessage: Schema.String,\n\texitCode: Schema.optionalKey(Schema.Int),\n}) {}\nexport class RecoveryRequiredError extends Schema.TaggedError<\n\tRecoveryRequiredError,\n\t{ readonly brand: unique symbol }\n>()(\"RecoveryRequiredError\", {\n\tdirectory: Schema.String,\n\tsnapshotDirectory: Schema.String,\n\toperation: Schema.Literals([\"install\", \"recover\", \"rollback\"]),\n\tprocessId: Schema.optionalKey(Schema.Int),\n\tmessage: Schema.String,\n\tterminationCause: Schema.optionalKey(\n\t\tSchema.Cause(Schema.Unknown, Schema.Unknown),\n\t),\n\trecoveryCause: Schema.Cause(Schema.Unknown, Schema.Unknown),\n}) {}\n\ntype WriterState = Data.TaggedEnum<{\n\tManagedWritersIdle: {}\n\tRunning: {}\n\tFileWrite: {}\n\tUnconfirmed: {\n\t\treadonly operation: \"install\" | \"recover\"\n\t\treadonly processId: number\n\t\treadonly cause: Cause.Cause<PlatformError.PlatformError>\n\t}\n}>\n\nconst WriterState = Data.taggedEnum<WriterState>()\n\ntype ManagerState = Data.TaggedEnum<{\n\tReady: {}\n\tActive: { readonly writer: WriterState }\n\tRecoveryRequired: { readonly error: RecoveryRequiredError }\n}>\n\nconst ManagerState = Data.taggedEnum<ManagerState>()\n\nexport type Error =\n\t| PackageManagerError\n\t| UnsupportedFileTypeError\n\t| RecoveryRequiredError\n\t| PlatformError.PlatformError\n\t| Schema.SchemaError\nexport interface Options {\n\t/** Project directory, containing package.json. Use the workspace root for workspace installs. */\n\treadonly directory: string\n\t/** Explicit selection takes precedence over detection. */\n\treadonly agent?: Agent\n\t/** Optional npm_config_user_agent supplied by the caller; used only as a fallback. */\n\treadonly userAgent?: string\n}\nconst lockfiles = [\n\t\"package-lock.json\",\n\t\"npm-shrinkwrap.json\",\n\t\"pnpm-lock.yaml\",\n\t\"yarn.lock\",\n] as const\nconst backupFiles = [\n\t\"package.json\",\n\t...lockfiles,\n\t\".npmrc\",\n\t\"pnpm-workspace.yaml\",\n\t\".yarnrc\",\n\t\".yarnrc.yml\",\n\t\".yarn/install-state.gz\",\n\t\".pnp.cjs\",\n\t\".pnp.loader.mjs\",\n]\nconst executable = (agent: Agent) =>\n\tString.startsWith(\"yarn\")(agent) ? \"yarn\" : agent\n\nconst readSource = Effect.fnUntraced(function* (\n\tfs: FileSystem.FileSystem,\n\tfile: string,\n) {\n\tconst bytes = yield* fs.readFile(file)\n\treturn yield* Effect.try({\n\t\ttry: () =>\n\t\t\tnew TextDecoder(\"utf-8\", { ignoreBOM: true, fatal: true }).decode(bytes),\n\t\tcatch: (cause) =>\n\t\t\tPlatformError.badArgument({\n\t\t\t\tmodule: \"FileSystem\",\n\t\t\t\tmethod: \"readManifest\",\n\t\t\t\tdescription: \"The manifest must contain valid UTF-8\",\n\t\t\t\tcause,\n\t\t\t}),\n\t})\n})\n\nconst Executable = PackageManagerName\nconst Digits = Schema.String.check(Schema.isPattern(/^\\d+$/))\nconst decodePackageManager = Schema.decodeUnknownOption(\n\tPackageManagerDeclaration,\n)\n/** `name/version ...`, as package managers set `npm_config_user_agent`. */\nconst decodeUserAgent = Schema.decodeUnknownOption(\n\tSchema.TemplateLiteralParser([Executable, \"/\", Digits, Schema.String]),\n)\nconst agentOf = (name: typeof Executable.Type, major: string): Agent =>\n\tname === \"yarn\" ? (major === \"1\" ? \"yarn-classic\" : \"yarn-berry\") : name\n\n/** packageManager, then lockfile, then caller user-agent, then npm. Conflicting lockfiles fail. */\nexport const detectAgent = Effect.fnUntraced(function* (options: Options) {\n\tif (options.agent !== undefined)\n\t\treturn yield* Schema.decodeEffect(Agent)(options.agent)\n\tconst fs = yield* FileSystem.FileSystem\n\tconst path = yield* Path.Path\n\tconst manifest = yield* readSource(\n\t\tfs,\n\t\tpath.join(options.directory, \"package.json\"),\n\t).pipe(Effect.flatMap(decodeManifest))\n\tif (manifest.packageManager !== undefined) {\n\t\treturn yield* Option.match(decodePackageManager(manifest.packageManager), {\n\t\t\tonNone: () =>\n\t\t\t\tPackageManagerError.make({\n\t\t\t\t\toperation: \"detect\",\n\t\t\t\t\tmessage: `Unsupported packageManager: ${manifest.packageManager}`,\n\t\t\t\t}),\n\t\t\tonSome: ([name, , version]) =>\n\t\t\t\tEffect.succeed(agentOf(name, `${version.major}`)),\n\t\t})\n\t}\n\tconst found = yield* Effect.filter(lockfiles, (file) =>\n\t\tfs.exists(path.join(options.directory, file)),\n\t)\n\tconst kinds = Array.dedupe(\n\t\tArray.map(found, (file) =>\n\t\t\tfile === \"pnpm-lock.yaml\"\n\t\t\t\t? \"pnpm\"\n\t\t\t\t: file === \"yarn.lock\"\n\t\t\t\t\t? \"yarn\"\n\t\t\t\t\t: \"npm\",\n\t\t),\n\t)\n\tif (kinds.length > 1)\n\t\treturn yield* PackageManagerError.make({\n\t\t\toperation: \"detect\",\n\t\t\tmessage: \"Conflicting lockfiles; select an agent explicitly\",\n\t\t})\n\tif (kinds[0] === \"yarn\") {\n\t\tconst metadata = yield* fs\n\t\t\t.stream(path.join(options.directory, \"yarn.lock\"))\n\t\t\t.pipe(\n\t\t\t\tStream.decodeText(),\n\t\t\t\tStream.mapAccum(\n\t\t\t\t\t// The initial newline models the start of the file.\n\t\t\t\t\t() => \"\\n\",\n\t\t\t\t\t(tail, chunk) => {\n\t\t\t\t\t\tconst text = tail + chunk\n\t\t\t\t\t\t// Preserve all ECMAScript line terminators, including across chunk boundaries.\n\t\t\t\t\t\t// Eleven characters retain a partial marker and its preceding character.\n\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\tString.slice(-11)(text),\n\t\t\t\t\t\t\t[/[\\n\\r\\u2028\\u2029]__metadata:/.test(text)],\n\t\t\t\t\t\t]\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t\tStream.filter((found) => found),\n\t\t\t\tStream.runHead,\n\t\t\t)\n\t\treturn Option.isSome(metadata) ? \"yarn-berry\" : \"yarn-classic\"\n\t}\n\tif (kinds[0] !== undefined) return kinds[0]\n\treturn Option.match(decodeUserAgent(options.userAgent ?? \"\"), {\n\t\tonNone: () => \"npm\" as const,\n\t\tonSome: ([name, , major]) => agentOf(name, major),\n\t})\n})\n\nexport interface PackageState {\n\treadonly spec: string\n\t/** Missing optional or not-yet-installed dependencies have no version/path. */\n\treadonly version: string | undefined\n\treadonly path: string | undefined\n}\n/** Direct dependencies only, keyed by their manifest names (including aliases). */\nexport type State = HashMap.HashMap<string, PackageState>\nexport interface PackageChange {\n\treadonly name: string\n\treadonly before: PackageState | undefined\n\treadonly after: PackageState | undefined\n}\nexport interface StateDiff {\n\treadonly added: ReadonlyArray<PackageChange>\n\treadonly removed: ReadonlyArray<PackageChange>\n\treadonly updated: ReadonlyArray<PackageChange>\n}\nexport const diffState = (before: State, after: State): StateDiff => {\n\tconst changes = Array.map(\n\t\tArray.sort(\n\t\t\tArray.dedupe([...HashMap.keys(before), ...HashMap.keys(after)]),\n\t\t\tString.Order,\n\t\t),\n\t\t(name) => ({\n\t\t\tname,\n\t\t\tbefore: Option.getOrUndefined(HashMap.get(before, name)),\n\t\t\tafter: Option.getOrUndefined(HashMap.get(after, name)),\n\t\t}),\n\t)\n\treturn {\n\t\tadded: Array.filter(changes, (change) => change.before === undefined),\n\t\tremoved: Array.filter(changes, (change) => change.after === undefined),\n\t\tupdated: Array.filter(\n\t\t\tchanges,\n\t\t\t({ before, after }) =>\n\t\t\t\tbefore !== undefined &&\n\t\t\t\tafter !== undefined &&\n\t\t\t\t(before.spec !== after.spec ||\n\t\t\t\t\tbefore.version !== after.version ||\n\t\t\t\t\tbefore.path !== after.path),\n\t\t),\n\t}\n}\nexport interface InstallResult extends StateDiff {\n\treadonly agent: Agent\n\t/** The install committed; retry only snapshot deletion, not installation. */\n\treadonly snapshotCleanupFailure: Option.Option<SnapshotCleanupFailure>\n}\nexport interface PackageManager {\n\treadonly directory: string\n\treadonly agent: Agent\n\treadonly readState: Effect.Effect<State, Error>\n\t/** Present when manual recovery is required; the manager cannot be reused. */\n\treadonly recoveryRequired: Effect.Effect<Option.Option<RecoveryRequiredError>>\n\t/** Serialized per manager. Empty changes installs the current manifest, including first installs. */\n\treadonly install: (changes: Changes) => Effect.Effect<InstallResult, Error>\n}\n\n/**\n * Own one manager per project. The caller must give this manager and its child\n * processes exclusive write access to the project for each install, including\n * recovery. Do not edit project files or run another installer concurrently.\n * Calls are serialized only within this manager; no cross-process lock is taken.\n * Managed paths must be regular files or absent. make and install reject symbolic\n * links and other file types with UnsupportedFileTypeError before any write.\n *\n * Accepted file writes finish before cancellation can start rollback or release\n * the lock. File writes and the original process group participate in the transaction.\n * Scripts must not create escaped writers in independent groups or daemons.\n * Failed or interrupted installs before commit restore backed-up package files byte-for-byte\n * with recorded permissions and ownership, and remove tracked files that did not\n * exist before installation. Writes made\n * during installation are overwritten by rollback, never merged. When a lockfile\n * existed, recovery also attempts to repair node_modules. Recovery failures are\n * combined with the original cause. If process termination cannot be confirmed,\n * no further restoration or recovery runs. Any failed rollback step also requires\n * manual recovery, even if all writers stopped. Only successful installation or\n * complete rollback permits snapshot deletion and reuse. RecoveryRequiredError\n * and recoveryRequired expose the retained snapshot directory and complete cause;\n * this manager rejects\n * all later installs. Complete manual recovery before making a new manager.\n * Committed installs report snapshot deletion failures in snapshotCleanupFailure\n * rather than failing or rolling back. No crash recovery is promised.\n */\nexport const make = (\n\toptions: Options,\n): Effect.Effect<\n\tPackageManager,\n\tError,\n\tFileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner\n> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem\n\t\tconst path = yield* Path.Path\n\t\tconst spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n\t\tconst directory = path.resolve(options.directory)\n\t\tyield* inspectManagedFiles(fs, path, directory, backupFiles)\n\t\tconst agent = yield* detectAgent({ ...options, directory })\n\t\tconst lock = yield* Semaphore.make(1)\n\t\tconst transaction = yield* Ref.make<ManagerState>(ManagerState.Ready())\n\t\tconst manifestFile = path.join(directory, \"package.json\")\n\t\tconst readManifest = readSource(fs, manifestFile).pipe(\n\t\t\tEffect.flatMap(decodeManifest),\n\t\t)\n\t\tconst installed = Schema.decodeUnknownEffect(\n\t\t\tSchema.fromJsonString(\n\t\t\t\tSchema.Struct({ version: Schema.optionalKey(Schema.String) }),\n\t\t\t),\n\t\t)\n\t\tconst stateOf = Effect.fnUntraced(function* (manifest: Manifest) {\n\t\t\tif (yield* fs.exists(path.join(directory, \".pnp.cjs\")))\n\t\t\t\treturn yield* PackageManagerError.make({\n\t\t\t\t\toperation: \"state\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Plug'n'Play is not supported; configure Yarn with nodeLinker: node-modules\",\n\t\t\t\t})\n\t\t\tconst deps = Array.reduce(\n\t\t\t\tfields,\n\t\t\t\t{} as Readonly<Record<string, string>>,\n\t\t\t\t(all, field) => ({ ...all, ...manifest[field] }),\n\t\t\t)\n\t\t\tconst entries = yield* Effect.forEach(\n\t\t\t\tRecord.toEntries(deps),\n\t\t\t\t([name, spec]) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst file = path.join(\n\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\t\"node_modules\",\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\"package.json\",\n\t\t\t\t\t\t)\n\t\t\t\t\t\tconst source = yield* fs.readFileString(file).pipe(\n\t\t\t\t\t\t\tEffect.asSome,\n\t\t\t\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t\t\t\t(cause) =>\n\t\t\t\t\t\t\t\t\tArray.every(\n\t\t\t\t\t\t\t\t\t\tcause.reasons,\n\t\t\t\t\t\t\t\t\t\t(reason) =>\n\t\t\t\t\t\t\t\t\t\t\tCause.isFailReason(reason) &&\n\t\t\t\t\t\t\t\t\t\t\treason.error.reason.module === \"FileSystem\" &&\n\t\t\t\t\t\t\t\t\t\t\treason.error.reason._tag === \"NotFound\",\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t() => Effect.succeedNone,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (Option.isNone(source))\n\t\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\t{ spec, version: undefined, path: undefined },\n\t\t\t\t\t\t\t] as const\n\t\t\t\t\t\tconst pkg = yield* installed(source.value)\n\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tspec,\n\t\t\t\t\t\t\t\tversion: pkg.version,\n\t\t\t\t\t\t\t\tpath: yield* fs.realPath(path.dirname(file)),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t] as const\n\t\t\t\t\t}),\n\t\t\t\t{ concurrency: 16 },\n\t\t\t)\n\t\t\treturn HashMap.fromIterable<string, PackageState>(entries)\n\t\t})\n\t\tconst readState = Effect.flatMap(readManifest, stateOf)\n\t\tconst termination: ChildProcess.KillOptions = {\n\t\t\tforceKillAfter: \"5 seconds\",\n\t\t}\n\t\tconst run = Effect.fnUntraced(function* (operation: \"install\" | \"recover\") {\n\t\t\tconst recovery = operation === \"recover\"\n\t\t\tconst args =\n\t\t\t\tagent === \"npm\"\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t\"install\",\n\t\t\t\t\t\t\t\"--no-audit\",\n\t\t\t\t\t\t\t\"--no-fund\",\n\t\t\t\t\t\t\t...(recovery ? [\"--prefer-offline\"] : []),\n\t\t\t\t\t\t]\n\t\t\t\t\t: agent === \"pnpm\"\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\"install\",\n\t\t\t\t\t\t\t\trecovery ? \"--frozen-lockfile\" : \"--no-frozen-lockfile\",\n\t\t\t\t\t\t\t\t\"--config.confirmModulesPurge=false\",\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t: agent === \"yarn-classic\"\n\t\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\t\"install\",\n\t\t\t\t\t\t\t\t\t\"--non-interactive\",\n\t\t\t\t\t\t\t\t\t...(recovery\n\t\t\t\t\t\t\t\t\t\t? [\"--frozen-lockfile\", \"--prefer-offline\"]\n\t\t\t\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t: [\"install\", ...(recovery ? [\"--immutable\"] : [])]\n\t\t\t// Acquire and install the release handler atomically, then wait interruptibly.\n\t\t\treturn yield* Effect.acquireUseRelease(\n\t\t\t\tspawner\n\t\t\t\t\t.spawn(\n\t\t\t\t\t\tChildProcess.make(executable(agent), args, {\n\t\t\t\t\t\t\tcwd: directory,\n\t\t\t\t\t\t\textendEnv: true,\n\t\t\t\t\t\t\tstdin: \"ignore\",\n\t\t\t\t\t\t\tstdout: \"inherit\",\n\t\t\t\t\t\t\tstderr: \"inherit\",\n\t\t\t\t\t\t\tdetached: true,\n\t\t\t\t\t\t\t...termination,\n\t\t\t\t\t\t\tenv: {\n\t\t\t\t\t\t\t\tCI: \"true\",\n\t\t\t\t\t\t\t\t...(agent === \"yarn-berry\"\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\tYARN_ENABLE_IMMUTABLE_INSTALLS: recovery\n\t\t\t\t\t\t\t\t\t\t\t\t? \"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t: \"false\",\n\t\t\t\t\t\t\t\t\t\t\tYARN_NODE_LINKER: \"node-modules\",\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\t.pipe(\n\t\t\t\t\t\tEffect.tap(() =>\n\t\t\t\t\t\t\tRef.set(\n\t\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\t\tManagerState.Active({ writer: WriterState.Running() }),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t(process) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst code = yield* process.exitCode.pipe(\n\t\t\t\t\t\t\tEffect.timeoutOrElse({\n\t\t\t\t\t\t\t\tduration: \"10 minutes\",\n\t\t\t\t\t\t\t\torElse: () =>\n\t\t\t\t\t\t\t\t\tPackageManagerError.make({\n\t\t\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\t\t\tmessage: `${executable(agent)} ${operation} timed out`,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (code !== 0)\n\t\t\t\t\t\t\treturn yield* PackageManagerError.make({\n\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\texitCode: code,\n\t\t\t\t\t\t\t\tmessage: `${executable(agent)} ${operation} exited with code ${code}`,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t}),\n\t\t\t\t// The command owns its group; a stopped leader may leave live descendants.\n\t\t\t\t// Observe kill failures here rather than relying on best-effort scope cleanup.\n\t\t\t\t(process) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst result = yield* terminateProcess(process, termination)\n\t\t\t\t\t\tyield* Ref.set(\n\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\tManagerState.Active({\n\t\t\t\t\t\t\t\twriter:\n\t\t\t\t\t\t\t\t\tresult._tag === \"OriginalGroupStopped\"\n\t\t\t\t\t\t\t\t\t\t? WriterState.ManagedWritersIdle()\n\t\t\t\t\t\t\t\t\t\t: WriterState.Unconfirmed({\n\t\t\t\t\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\t\t\t\t\tprocessId: process.pid,\n\t\t\t\t\t\t\t\t\t\t\t\tcause: result.cause,\n\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (result._tag === \"Unconfirmed\")\n\t\t\t\t\t\t\treturn yield* Effect.failCause(result.cause)\n\t\t\t\t\t}),\n\t\t\t)\n\t\t}, Effect.scoped)\n\t\t// AbortSignal does not join accepted OS writes. Keep the writer owned until completion.\n\t\tconst withFileWriter = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n\t\t\tEffect.acquireUseRelease(\n\t\t\t\tRef.set(\n\t\t\t\t\ttransaction,\n\t\t\t\t\tManagerState.Active({ writer: WriterState.FileWrite() }),\n\t\t\t\t),\n\t\t\t\t() => effect,\n\t\t\t\t() =>\n\t\t\t\t\tRef.set(\n\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\tManagerState.Active({ writer: WriterState.ManagedWritersIdle() }),\n\t\t\t\t\t),\n\t\t\t).pipe(Effect.uninterruptible)\n\n\t\tconst install = (changes: Changes) =>\n\t\t\tlock.withPermits(1)(\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\tif (state._tag === \"RecoveryRequired\") return yield* state.error\n\t\t\t\t\tconst validated = yield* Schema.decodeEffect(Changes)(changes)\n\t\t\t\t\tconst inspected = yield* inspectManagedFiles(\n\t\t\t\t\t\tfs,\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\tbackupFiles,\n\t\t\t\t\t)\n\t\t\t\t\tconst backups = yield* Effect.forEach(\n\t\t\t\t\t\tinspected,\n\t\t\t\t\t\t({ name, file, metadata }) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tconst contents = yield* Option.match(metadata, {\n\t\t\t\t\t\t\t\t\tonNone: () => Effect.succeedNone,\n\t\t\t\t\t\t\t\t\tonSome: (info) =>\n\t\t\t\t\t\t\t\t\t\tfs\n\t\t\t\t\t\t\t\t\t\t\t.readFile(file)\n\t\t\t\t\t\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\t\t\t\t\t\tEffect.map((bytes) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tOption.some<Contents>({ ...info, bytes }),\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\treturn { name, file, contents }\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\tconst original = yield* readSource(fs, manifestFile)\n\t\t\t\t\tconst manifest = yield* decodeManifest(original)\n\t\t\t\t\tconst before = yield* stateOf(manifest)\n\t\t\t\t\tconst text = yield* encodeManifest(\n\t\t\t\t\t\tapplyChanges(manifest, validated),\n\t\t\t\t\t\toriginal,\n\t\t\t\t\t)\n\n\t\t\t\t\tconst agentLocks =\n\t\t\t\t\t\tagent === \"npm\"\n\t\t\t\t\t\t\t? [\"package-lock.json\", \"npm-shrinkwrap.json\"]\n\t\t\t\t\t\t\t: agent === \"pnpm\"\n\t\t\t\t\t\t\t\t? [\"pnpm-lock.yaml\"]\n\t\t\t\t\t\t\t\t: [\"yarn.lock\"]\n\t\t\t\t\tconst hasLock = Array.some(\n\t\t\t\t\t\tbackups,\n\t\t\t\t\t\t({ name, contents }) =>\n\t\t\t\t\t\t\tArray.contains(agentLocks, name) && Option.isSome(contents),\n\t\t\t\t\t)\n\t\t\t\t\tconst completed = yield* withSnapshot(\n\t\t\t\t\t\tfs,\n\t\t\t\t\t\tRef.get(transaction).pipe(\n\t\t\t\t\t\t\tEffect.map((state) => state._tag === \"Ready\"),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t(snapshotDirectory) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tconst snapshot = yield* writeSnapshot(\n\t\t\t\t\t\t\t\t\tfs,\n\t\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\t\tsnapshotDirectory,\n\t\t\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\t\t\tagent,\n\t\t\t\t\t\t\t\t\tbackups,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tconst restore = Effect.flatMap(Ref.get(transaction), (state) =>\n\t\t\t\t\t\t\t\t\tstate._tag === \"Active\" &&\n\t\t\t\t\t\t\t\t\tstate.writer._tag === \"ManagedWritersIdle\"\n\t\t\t\t\t\t\t\t\t\t? withFileWriter(snapshot.restore(state.writer))\n\t\t\t\t\t\t\t\t\t\t: Effect.void,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\treturn yield* Effect.acquireUseRelease(\n\t\t\t\t\t\t\t\t\tRef.set(\n\t\t\t\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\t\t\t\tManagerState.Active({\n\t\t\t\t\t\t\t\t\t\t\twriter: WriterState.ManagedWritersIdle(),\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\t\t\t\tyield* withFileWriter(\n\t\t\t\t\t\t\t\t\t\t\t\tfs.writeFileString(manifestFile, text),\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\tyield* run(\"install\")\n\t\t\t\t\t\t\t\t\t\t\tconst after = yield* readState\n\t\t\t\t\t\t\t\t\t\t\treturn { agent, ...diffState(before, after) }\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t(_, exit) =>\n\t\t\t\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\t\t\t\tif (Exit.isFailure(exit)) {\n\t\t\t\t\t\t\t\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t\t\t\tstate._tag !== \"Active\" ||\n\t\t\t\t\t\t\t\t\t\t\t\t\tstate.writer._tag !== \"ManagedWritersIdle\"\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t\t\tyield* withFileWriter(snapshot.restore(state.writer))\n\t\t\t\t\t\t\t\t\t\t\t\tif (hasLock)\n\t\t\t\t\t\t\t\t\t\t\t\t\tyield* run(\"recover\").pipe(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// Even a failed recovery must restore files, once its writer has stopped.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tEffect.onExit(() => restore),\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t// Idle managed writers alone do not prove that the transaction completed.\n\t\t\t\t\t\t\t\t\t\t\tyield* Ref.set(transaction, ManagerState.Ready())\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t).pipe(\n\t\t\t\t\t\t\t\t\tEffect.onExit((exit) =>\n\t\t\t\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\t\t\t\t\t\t\tif (Exit.isSuccess(exit) || state._tag !== \"Active\")\n\t\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t\tconst writer = state.writer\n\t\t\t\t\t\t\t\t\t\t\tconst error = RecoveryRequiredError.make({\n\t\t\t\t\t\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\t\t\t\t\t\tsnapshotDirectory,\n\t\t\t\t\t\t\t\t\t\t\t\toperation:\n\t\t\t\t\t\t\t\t\t\t\t\t\twriter._tag === \"Unconfirmed\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? writer.operation\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"rollback\",\n\t\t\t\t\t\t\t\t\t\t\t\t...(writer._tag === \"Unconfirmed\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprocessId: writer.processId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tterminationCause: writer.cause,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t\t\t\t\tmessage: `Manual recovery is required. Stop all writers before restoring the snapshot at ${snapshotDirectory}`,\n\t\t\t\t\t\t\t\t\t\t\t\trecoveryCause: exit.cause,\n\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\tyield* Ref.set(\n\t\t\t\t\t\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\t\t\t\t\t\tManagerState.RecoveryRequired({ error }),\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\treturn yield* error\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...completed.value,\n\t\t\t\t\t\tsnapshotCleanupFailure: completed.snapshotCleanupFailure,\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t)\n\t\treturn {\n\t\t\tdirectory,\n\t\t\tagent,\n\t\t\trecoveryRequired: Ref.get(transaction).pipe(\n\t\t\t\tEffect.map((state) =>\n\t\t\t\t\tstate._tag === \"RecoveryRequired\"\n\t\t\t\t\t\t? Option.some(state.error)\n\t\t\t\t\t\t: Option.none(),\n\t\t\t\t),\n\t\t\t),\n\t\t\treadState: lock.withPermits(1)(readState),\n\t\t\tinstall,\n\t\t}\n\t})\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/** Non-interactive package installation. Provide platform services at the application boundary. */\nimport {\n\tArray,\n\tCause,\n\tData,\n\tRef,\n\tEffect,\n\tExit,\n\tFileSystem,\n\tHashMap,\n\tMatch,\n\tOption,\n\tPath,\n\tPlatformError,\n\tRecord,\n\tSchema,\n\tSemaphore,\n\tString,\n\tStream,\n} from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/process\"\nimport {\n\tPackageManagerDeclaration,\n\tPackageManagerName,\n} from \"./PackageManagerDeclaration\"\nexport { PackageManagerDeclaration } from \"./PackageManagerDeclaration\"\nimport { terminateProcess } from \"./process\"\nimport {\n\twriteSnapshot,\n\twithSnapshot,\n\ttype SnapshotCleanupFailure,\n\tinspectManagedFiles,\n\tUnsupportedFileTypeError,\n\ttype Contents,\n} from \"./snapshot\"\nexport {\n\tUnsupportedFileTypeError,\n\ttype SnapshotCleanupFailure,\n} from \"./snapshot\"\nimport {\n\tapplyChanges,\n\tChanges,\n\tdecodeManifest,\n\tencodeManifest,\n\tfields,\n\ttype Manifest,\n} from \"./manifest\"\nexport { Changes, PackageName } from \"./manifest\"\n\nexport const Agent = Schema.Literals([\n\t\"npm\",\n\t\"pnpm\",\n\t\"yarn-classic\",\n\t\"yarn-berry\",\n])\nexport type Agent = typeof Agent.Type\nexport class PackageManagerError extends Schema.TaggedError<\n\tPackageManagerError,\n\t{ readonly brand: unique symbol }\n>()(\"PackageManagerError\", {\n\toperation: Schema.Literals([\"detect\", \"install\", \"recover\", \"state\"]),\n\tmessage: Schema.String,\n\texitCode: Schema.optionalKey(Schema.Int),\n}) {}\nexport class RecoveryRequiredError extends Schema.TaggedError<\n\tRecoveryRequiredError,\n\t{ readonly brand: unique symbol }\n>()(\"RecoveryRequiredError\", {\n\tdirectory: Schema.String,\n\tsnapshotDirectory: Schema.String,\n\toperation: Schema.Literals([\"install\", \"recover\", \"rollback\"]),\n\tprocessId: Schema.optionalKey(Schema.Int),\n\tmessage: Schema.String,\n\tterminationCause: Schema.optionalKey(\n\t\tSchema.Cause(Schema.Unknown, Schema.Unknown),\n\t),\n\trecoveryCause: Schema.Cause(Schema.Unknown, Schema.Unknown),\n}) {}\n\ntype WriterState = Data.TaggedEnum<{\n\tManagedWritersIdle: {}\n\tRunning: {}\n\tFileWrite: {}\n\tUnconfirmed: {\n\t\treadonly operation: \"install\" | \"recover\"\n\t\treadonly processId: number\n\t\treadonly cause: Cause.Cause<PlatformError.PlatformError>\n\t}\n}>\n\nconst WriterState = Data.taggedEnum<WriterState>()\n\ntype ManagerState = Data.TaggedEnum<{\n\tReady: {}\n\tActive: { readonly writer: WriterState }\n\tRecoveryRequired: { readonly error: RecoveryRequiredError }\n}>\n\nconst ManagerState = Data.taggedEnum<ManagerState>()\n\nexport type Error =\n\t| PackageManagerError\n\t| UnsupportedFileTypeError\n\t| RecoveryRequiredError\n\t| PlatformError.PlatformError\n\t| Schema.SchemaError\nexport interface Options {\n\t/** Project directory, containing package.json. Use the workspace root for workspace installs. */\n\treadonly directory: string\n\t/** Explicit selection takes precedence over detection. */\n\treadonly agent?: Agent\n\t/** Optional npm_config_user_agent supplied by the caller; used only as a fallback. */\n\treadonly userAgent?: string\n}\nconst lockfiles = [\n\t\"package-lock.json\",\n\t\"npm-shrinkwrap.json\",\n\t\"pnpm-lock.yaml\",\n\t\"yarn.lock\",\n] as const\nconst backupFiles = [\n\t\"package.json\",\n\t...lockfiles,\n\t\".npmrc\",\n\t\"pnpm-workspace.yaml\",\n\t\".yarnrc\",\n\t\".yarnrc.yml\",\n\t\".yarn/install-state.gz\",\n\t\".pnp.cjs\",\n\t\".pnp.loader.mjs\",\n]\nconst strategy = (agent: Agent, operation: \"install\" | \"recover\") => {\n\tconst recovery = operation === \"recover\"\n\treturn Match.value(agent).pipe(\n\t\tMatch.when(\"npm\", () => ({\n\t\t\texecutable: \"npm\",\n\t\t\targs: [\n\t\t\t\t\"install\",\n\t\t\t\t\"--no-audit\",\n\t\t\t\t\"--no-fund\",\n\t\t\t\t...(recovery ? [\"--prefer-offline\"] : []),\n\t\t\t],\n\t\t\tenv: { CI: \"true\" },\n\t\t\tlockfiles: [\"package-lock.json\", \"npm-shrinkwrap.json\"],\n\t\t})),\n\t\tMatch.when(\"pnpm\", () => ({\n\t\t\texecutable: \"pnpm\",\n\t\t\targs: [\n\t\t\t\t\"install\",\n\t\t\t\trecovery ? \"--frozen-lockfile\" : \"--no-frozen-lockfile\",\n\t\t\t\t\"--config.confirmModulesPurge=false\",\n\t\t\t],\n\t\t\tenv: { CI: \"true\" },\n\t\t\tlockfiles: [\"pnpm-lock.yaml\"],\n\t\t})),\n\t\tMatch.when(\"yarn-classic\", () => ({\n\t\t\texecutable: \"yarn\",\n\t\t\targs: [\n\t\t\t\t\"install\",\n\t\t\t\t\"--non-interactive\",\n\t\t\t\t...(recovery ? [\"--frozen-lockfile\", \"--prefer-offline\"] : []),\n\t\t\t],\n\t\t\tenv: { CI: \"true\" },\n\t\t\tlockfiles: [\"yarn.lock\"],\n\t\t})),\n\t\tMatch.when(\"yarn-berry\", () => ({\n\t\t\texecutable: \"yarn\",\n\t\t\targs: [\"install\", ...(recovery ? [\"--immutable\"] : [])],\n\t\t\tenv: {\n\t\t\t\tCI: \"true\",\n\t\t\t\tYARN_ENABLE_IMMUTABLE_INSTALLS: recovery ? \"true\" : \"false\",\n\t\t\t\tYARN_NODE_LINKER: \"node-modules\",\n\t\t\t},\n\t\t\tlockfiles: [\"yarn.lock\"],\n\t\t})),\n\t\tMatch.exhaustive,\n\t)\n}\n\nconst readSource = Effect.fnUntraced(function* (\n\tfs: FileSystem.FileSystem,\n\tfile: string,\n) {\n\tconst bytes = yield* fs.readFile(file)\n\treturn yield* Effect.try({\n\t\ttry: () =>\n\t\t\tnew TextDecoder(\"utf-8\", { ignoreBOM: true, fatal: true }).decode(bytes),\n\t\tcatch: (cause) =>\n\t\t\tPlatformError.badArgument({\n\t\t\t\tmodule: \"FileSystem\",\n\t\t\t\tmethod: \"readManifest\",\n\t\t\t\tdescription: \"The manifest must contain valid UTF-8\",\n\t\t\t\tcause,\n\t\t\t}),\n\t})\n})\n\nconst Executable = PackageManagerName\nconst Digits = Schema.String.check(Schema.isPattern(/^\\d+$/))\nconst decodePackageManager = Schema.decodeUnknownOption(\n\tPackageManagerDeclaration,\n)\n/** `name/version ...`, as package managers set `npm_config_user_agent`. */\nconst decodeUserAgent = Schema.decodeUnknownOption(\n\tSchema.TemplateLiteralParser([Executable, \"/\", Digits, Schema.String]),\n)\nconst agentOf = (name: typeof Executable.Type, major: string): Agent =>\n\tname === \"yarn\" ? (major === \"1\" ? \"yarn-classic\" : \"yarn-berry\") : name\n\n/** packageManager, then lockfile, then caller user-agent, then npm. Conflicting lockfiles fail. */\nexport const detectAgent = Effect.fnUntraced(function* (options: Options) {\n\tif (options.agent !== undefined)\n\t\treturn yield* Schema.decodeEffect(Agent)(options.agent)\n\tconst fs = yield* FileSystem.FileSystem\n\tconst path = yield* Path.Path\n\tconst manifest = yield* readSource(\n\t\tfs,\n\t\tpath.join(options.directory, \"package.json\"),\n\t).pipe(Effect.flatMap(decodeManifest))\n\tif (manifest.packageManager !== undefined) {\n\t\treturn yield* Option.match(decodePackageManager(manifest.packageManager), {\n\t\t\tonNone: () =>\n\t\t\t\tPackageManagerError.make({\n\t\t\t\t\toperation: \"detect\",\n\t\t\t\t\tmessage: `Unsupported packageManager: ${manifest.packageManager}`,\n\t\t\t\t}),\n\t\t\tonSome: ([name, , version]) =>\n\t\t\t\tEffect.succeed(agentOf(name, `${version.major}`)),\n\t\t})\n\t}\n\tconst found = yield* Effect.filter(lockfiles, (file) =>\n\t\tfs.exists(path.join(options.directory, file)),\n\t)\n\tconst kinds = Array.dedupe(\n\t\tArray.map(found, (file) =>\n\t\t\tfile === \"pnpm-lock.yaml\"\n\t\t\t\t? \"pnpm\"\n\t\t\t\t: file === \"yarn.lock\"\n\t\t\t\t\t? \"yarn\"\n\t\t\t\t\t: \"npm\",\n\t\t),\n\t)\n\tif (kinds.length > 1)\n\t\treturn yield* PackageManagerError.make({\n\t\t\toperation: \"detect\",\n\t\t\tmessage: \"Conflicting lockfiles; select an agent explicitly\",\n\t\t})\n\tif (kinds[0] === \"yarn\") {\n\t\tconst metadata = yield* fs\n\t\t\t.stream(path.join(options.directory, \"yarn.lock\"))\n\t\t\t.pipe(\n\t\t\t\tStream.decodeText(),\n\t\t\t\tStream.mapAccum(\n\t\t\t\t\t// The initial newline models the start of the file.\n\t\t\t\t\t() => \"\\n\",\n\t\t\t\t\t(tail, chunk) => {\n\t\t\t\t\t\tconst text = tail + chunk\n\t\t\t\t\t\t// Preserve all ECMAScript line terminators, including across chunk boundaries.\n\t\t\t\t\t\t// Eleven characters retain a partial marker and its preceding character.\n\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\tString.slice(-11)(text),\n\t\t\t\t\t\t\t[/[\\n\\r\\u2028\\u2029]__metadata:/.test(text)],\n\t\t\t\t\t\t]\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t\tStream.filter((found) => found),\n\t\t\t\tStream.runHead,\n\t\t\t)\n\t\treturn Option.isSome(metadata) ? \"yarn-berry\" : \"yarn-classic\"\n\t}\n\tif (kinds[0] !== undefined) return kinds[0]\n\treturn Option.match(decodeUserAgent(options.userAgent ?? \"\"), {\n\t\tonNone: () => \"npm\" as const,\n\t\tonSome: ([name, , major]) => agentOf(name, major),\n\t})\n})\n\nexport interface PackageState {\n\treadonly spec: string\n\t/** Missing optional or not-yet-installed dependencies have no version/path. */\n\treadonly version: string | undefined\n\treadonly path: string | undefined\n}\n/** Direct dependencies only, keyed by their manifest names (including aliases). */\nexport type State = HashMap.HashMap<string, PackageState>\nexport interface PackageChange {\n\treadonly name: string\n\treadonly before: PackageState | undefined\n\treadonly after: PackageState | undefined\n}\nexport interface StateDiff {\n\treadonly added: ReadonlyArray<PackageChange>\n\treadonly removed: ReadonlyArray<PackageChange>\n\treadonly updated: ReadonlyArray<PackageChange>\n}\nexport const diffState = (before: State, after: State): StateDiff => {\n\tconst changes = Array.map(\n\t\tArray.sort(\n\t\t\tArray.dedupe([...HashMap.keys(before), ...HashMap.keys(after)]),\n\t\t\tString.Order,\n\t\t),\n\t\t(name) => ({\n\t\t\tname,\n\t\t\tbefore: Option.getOrUndefined(HashMap.get(before, name)),\n\t\t\tafter: Option.getOrUndefined(HashMap.get(after, name)),\n\t\t}),\n\t)\n\treturn {\n\t\tadded: Array.filter(changes, (change) => change.before === undefined),\n\t\tremoved: Array.filter(changes, (change) => change.after === undefined),\n\t\tupdated: Array.filter(\n\t\t\tchanges,\n\t\t\t({ before, after }) =>\n\t\t\t\tbefore !== undefined &&\n\t\t\t\tafter !== undefined &&\n\t\t\t\t(before.spec !== after.spec ||\n\t\t\t\t\tbefore.version !== after.version ||\n\t\t\t\t\tbefore.path !== after.path),\n\t\t),\n\t}\n}\nexport interface InstallResult extends StateDiff {\n\treadonly agent: Agent\n\t/** The install committed; retry only snapshot deletion, not installation. */\n\treadonly snapshotCleanupFailure: Option.Option<SnapshotCleanupFailure>\n}\nexport interface PackageManager {\n\treadonly directory: string\n\treadonly agent: Agent\n\treadonly readState: Effect.Effect<State, Error>\n\t/** Present when manual recovery is required; the manager cannot be reused. */\n\treadonly recoveryRequired: Effect.Effect<Option.Option<RecoveryRequiredError>>\n\t/** Serialized per manager. Empty changes installs the current manifest, including first installs. */\n\treadonly install: (changes: Changes) => Effect.Effect<InstallResult, Error>\n}\n\n/**\n * Own one manager per project. The caller must give this manager and its child\n * processes exclusive write access to the project for each install, including\n * recovery. Do not edit project files or run another installer concurrently.\n * Calls are serialized only within this manager; no cross-process lock is taken.\n * Managed paths must be regular files or absent. make and install reject symbolic\n * links and other file types with UnsupportedFileTypeError before any write.\n *\n * Accepted file writes finish before cancellation can start rollback or release\n * the lock. File writes and the original process group participate in the transaction.\n * Scripts must not create escaped writers in independent groups or daemons.\n * Failed or interrupted installs before commit restore backed-up package files byte-for-byte\n * with recorded permissions and ownership, and remove tracked files that did not\n * exist before installation. Writes made\n * during installation are overwritten by rollback, never merged. When a lockfile\n * existed, recovery also attempts to repair node_modules. Recovery failures are\n * combined with the original cause. If process termination cannot be confirmed,\n * no further restoration or recovery runs. Any failed rollback step also requires\n * manual recovery, even if all writers stopped. Only successful installation or\n * complete rollback permits snapshot deletion and reuse. RecoveryRequiredError\n * and recoveryRequired expose the retained snapshot directory and complete cause;\n * this manager rejects\n * all later installs. Complete manual recovery before making a new manager.\n * Committed installs report snapshot deletion failures in snapshotCleanupFailure\n * rather than failing or rolling back. No crash recovery is promised.\n */\nexport const make = (\n\toptions: Options,\n): Effect.Effect<\n\tPackageManager,\n\tError,\n\tFileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner\n> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem\n\t\tconst path = yield* Path.Path\n\t\tconst spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n\t\tconst directory = path.resolve(options.directory)\n\t\tyield* inspectManagedFiles(fs, path, directory, backupFiles)\n\t\tconst agent = yield* detectAgent({ ...options, directory })\n\t\tconst lock = yield* Semaphore.make(1)\n\t\tconst transaction = yield* Ref.make<ManagerState>(ManagerState.Ready())\n\t\tconst manifestFile = path.join(directory, \"package.json\")\n\t\tconst readManifest = readSource(fs, manifestFile).pipe(\n\t\t\tEffect.flatMap(decodeManifest),\n\t\t)\n\t\tconst installed = Schema.decodeUnknownEffect(\n\t\t\tSchema.fromJsonString(\n\t\t\t\tSchema.Struct({ version: Schema.optionalKey(Schema.String) }),\n\t\t\t),\n\t\t)\n\t\tconst stateOf = Effect.fnUntraced(function* (manifest: Manifest) {\n\t\t\tif (yield* fs.exists(path.join(directory, \".pnp.cjs\")))\n\t\t\t\treturn yield* PackageManagerError.make({\n\t\t\t\t\toperation: \"state\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Plug'n'Play is not supported; configure Yarn with nodeLinker: node-modules\",\n\t\t\t\t})\n\t\t\tconst deps = Array.reduce(\n\t\t\t\tfields,\n\t\t\t\t{} as Readonly<Record<string, string>>,\n\t\t\t\t(all, field) => ({ ...all, ...manifest[field] }),\n\t\t\t)\n\t\t\tconst entries = yield* Effect.forEach(\n\t\t\t\tRecord.toEntries(deps),\n\t\t\t\t([name, spec]) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst file = path.join(\n\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\t\"node_modules\",\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\"package.json\",\n\t\t\t\t\t\t)\n\t\t\t\t\t\tconst source = yield* fs.readFileString(file).pipe(\n\t\t\t\t\t\t\tEffect.asSome,\n\t\t\t\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t\t\t\t(cause) =>\n\t\t\t\t\t\t\t\t\tArray.every(\n\t\t\t\t\t\t\t\t\t\tcause.reasons,\n\t\t\t\t\t\t\t\t\t\t(reason) =>\n\t\t\t\t\t\t\t\t\t\t\tCause.isFailReason(reason) &&\n\t\t\t\t\t\t\t\t\t\t\treason.error.reason.module === \"FileSystem\" &&\n\t\t\t\t\t\t\t\t\t\t\treason.error.reason._tag === \"NotFound\",\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t() => Effect.succeedNone,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (Option.isNone(source))\n\t\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\t{ spec, version: undefined, path: undefined },\n\t\t\t\t\t\t\t] as const\n\t\t\t\t\t\tconst pkg = yield* installed(source.value)\n\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tspec,\n\t\t\t\t\t\t\t\tversion: pkg.version,\n\t\t\t\t\t\t\t\tpath: yield* fs.realPath(path.dirname(file)),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t] as const\n\t\t\t\t\t}),\n\t\t\t\t{ concurrency: 16 },\n\t\t\t)\n\t\t\treturn HashMap.fromIterable<string, PackageState>(entries)\n\t\t})\n\t\tconst readState = Effect.flatMap(readManifest, stateOf)\n\t\tconst termination: ChildProcess.KillOptions = {\n\t\t\tforceKillAfter: \"5 seconds\",\n\t\t}\n\t\tconst run = Effect.fnUntraced(function* (operation: \"install\" | \"recover\") {\n\t\t\tconst command = strategy(agent, operation)\n\t\t\t// Acquire and install the release handler atomically, then wait interruptibly.\n\t\t\treturn yield* Effect.acquireUseRelease(\n\t\t\t\tspawner\n\t\t\t\t\t.spawn(\n\t\t\t\t\t\tChildProcess.make(command.executable, command.args, {\n\t\t\t\t\t\t\tcwd: directory,\n\t\t\t\t\t\t\textendEnv: true,\n\t\t\t\t\t\t\tstdin: \"ignore\",\n\t\t\t\t\t\t\tstdout: \"inherit\",\n\t\t\t\t\t\t\tstderr: \"inherit\",\n\t\t\t\t\t\t\tdetached: true,\n\t\t\t\t\t\t\t...termination,\n\t\t\t\t\t\t\tenv: command.env,\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\t.pipe(\n\t\t\t\t\t\tEffect.tap(() =>\n\t\t\t\t\t\t\tRef.set(\n\t\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\t\tManagerState.Active({ writer: WriterState.Running() }),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t(process) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst code = yield* process.exitCode.pipe(\n\t\t\t\t\t\t\tEffect.timeoutOrElse({\n\t\t\t\t\t\t\t\tduration: \"10 minutes\",\n\t\t\t\t\t\t\t\torElse: () =>\n\t\t\t\t\t\t\t\t\tPackageManagerError.make({\n\t\t\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\t\t\tmessage: `${command.executable} ${operation} timed out`,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (code !== 0)\n\t\t\t\t\t\t\treturn yield* PackageManagerError.make({\n\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\texitCode: code,\n\t\t\t\t\t\t\t\tmessage: `${command.executable} ${operation} exited with code ${code}`,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t}),\n\t\t\t\t// The command owns its group; a stopped leader may leave live descendants.\n\t\t\t\t// Observe kill failures here rather than relying on best-effort scope cleanup.\n\t\t\t\t(process) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst result = yield* terminateProcess(process, termination)\n\t\t\t\t\t\tyield* Ref.set(\n\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\tManagerState.Active({\n\t\t\t\t\t\t\t\twriter:\n\t\t\t\t\t\t\t\t\tresult._tag === \"OriginalGroupStopped\"\n\t\t\t\t\t\t\t\t\t\t? WriterState.ManagedWritersIdle()\n\t\t\t\t\t\t\t\t\t\t: WriterState.Unconfirmed({\n\t\t\t\t\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\t\t\t\t\tprocessId: process.pid,\n\t\t\t\t\t\t\t\t\t\t\t\tcause: result.cause,\n\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (result._tag === \"Unconfirmed\")\n\t\t\t\t\t\t\treturn yield* Effect.failCause(result.cause)\n\t\t\t\t\t}),\n\t\t\t)\n\t\t}, Effect.scoped)\n\t\t// AbortSignal does not join accepted OS writes. Keep the writer owned until completion.\n\t\tconst withFileWriter = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n\t\t\tEffect.acquireUseRelease(\n\t\t\t\tRef.set(\n\t\t\t\t\ttransaction,\n\t\t\t\t\tManagerState.Active({ writer: WriterState.FileWrite() }),\n\t\t\t\t),\n\t\t\t\t() => effect,\n\t\t\t\t() =>\n\t\t\t\t\tRef.set(\n\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\tManagerState.Active({ writer: WriterState.ManagedWritersIdle() }),\n\t\t\t\t\t),\n\t\t\t).pipe(Effect.uninterruptible)\n\n\t\tconst install = (changes: Changes) =>\n\t\t\tlock.withPermits(1)(\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\tif (state._tag === \"RecoveryRequired\") return yield* state.error\n\t\t\t\t\tconst validated = yield* Schema.decodeEffect(Changes)(changes)\n\t\t\t\t\tconst inspected = yield* inspectManagedFiles(\n\t\t\t\t\t\tfs,\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\tbackupFiles,\n\t\t\t\t\t)\n\t\t\t\t\tconst backups = yield* Effect.forEach(\n\t\t\t\t\t\tinspected,\n\t\t\t\t\t\t({ name, file, metadata }) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tconst contents = yield* Option.match(metadata, {\n\t\t\t\t\t\t\t\t\tonNone: () => Effect.succeedNone,\n\t\t\t\t\t\t\t\t\tonSome: (info) =>\n\t\t\t\t\t\t\t\t\t\tfs\n\t\t\t\t\t\t\t\t\t\t\t.readFile(file)\n\t\t\t\t\t\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\t\t\t\t\t\tEffect.map((bytes) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tOption.some<Contents>({ ...info, bytes }),\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\treturn { name, file, contents }\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\tconst original = yield* readSource(fs, manifestFile)\n\t\t\t\t\tconst manifest = yield* decodeManifest(original)\n\t\t\t\t\tconst before = yield* stateOf(manifest)\n\t\t\t\t\tconst text = yield* encodeManifest(\n\t\t\t\t\t\tapplyChanges(manifest, validated),\n\t\t\t\t\t\toriginal,\n\t\t\t\t\t)\n\n\t\t\t\t\tconst agentLocks = strategy(agent, \"install\").lockfiles\n\t\t\t\t\tconst hasLock = Array.some(\n\t\t\t\t\t\tbackups,\n\t\t\t\t\t\t({ name, contents }) =>\n\t\t\t\t\t\t\tArray.contains(agentLocks, name) && Option.isSome(contents),\n\t\t\t\t\t)\n\t\t\t\t\tconst completed = yield* withSnapshot(\n\t\t\t\t\t\tfs,\n\t\t\t\t\t\tRef.get(transaction).pipe(\n\t\t\t\t\t\t\tEffect.map((state) => state._tag === \"Ready\"),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t(snapshotDirectory) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tconst snapshot = yield* writeSnapshot(\n\t\t\t\t\t\t\t\t\tfs,\n\t\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\t\tsnapshotDirectory,\n\t\t\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\t\t\tagent,\n\t\t\t\t\t\t\t\t\tbackups,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tconst restore = Effect.flatMap(Ref.get(transaction), (state) =>\n\t\t\t\t\t\t\t\t\tstate._tag === \"Active\" &&\n\t\t\t\t\t\t\t\t\tstate.writer._tag === \"ManagedWritersIdle\"\n\t\t\t\t\t\t\t\t\t\t? withFileWriter(snapshot.restore(state.writer))\n\t\t\t\t\t\t\t\t\t\t: Effect.void,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\treturn yield* Effect.acquireUseRelease(\n\t\t\t\t\t\t\t\t\tRef.set(\n\t\t\t\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\t\t\t\tManagerState.Active({\n\t\t\t\t\t\t\t\t\t\t\twriter: WriterState.ManagedWritersIdle(),\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\t\t\t\tyield* withFileWriter(\n\t\t\t\t\t\t\t\t\t\t\t\tfs.writeFileString(manifestFile, text),\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\tyield* run(\"install\")\n\t\t\t\t\t\t\t\t\t\t\tconst after = yield* readState\n\t\t\t\t\t\t\t\t\t\t\treturn { agent, ...diffState(before, after) }\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t(_, exit) =>\n\t\t\t\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\t\t\t\tif (Exit.isFailure(exit)) {\n\t\t\t\t\t\t\t\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t\t\t\tstate._tag !== \"Active\" ||\n\t\t\t\t\t\t\t\t\t\t\t\t\tstate.writer._tag !== \"ManagedWritersIdle\"\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t\t\tyield* withFileWriter(snapshot.restore(state.writer))\n\t\t\t\t\t\t\t\t\t\t\t\tif (hasLock)\n\t\t\t\t\t\t\t\t\t\t\t\t\tyield* run(\"recover\").pipe(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// Even a failed recovery must restore files, once its writer has stopped.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tEffect.onExit(() => restore),\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t// Idle managed writers alone do not prove that the transaction completed.\n\t\t\t\t\t\t\t\t\t\t\tyield* Ref.set(transaction, ManagerState.Ready())\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t).pipe(\n\t\t\t\t\t\t\t\t\tEffect.onExit((exit) =>\n\t\t\t\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\t\t\t\t\t\t\tif (Exit.isSuccess(exit) || state._tag !== \"Active\")\n\t\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t\tconst writer = state.writer\n\t\t\t\t\t\t\t\t\t\t\tconst error = RecoveryRequiredError.make({\n\t\t\t\t\t\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\t\t\t\t\t\tsnapshotDirectory,\n\t\t\t\t\t\t\t\t\t\t\t\toperation:\n\t\t\t\t\t\t\t\t\t\t\t\t\twriter._tag === \"Unconfirmed\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? writer.operation\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"rollback\",\n\t\t\t\t\t\t\t\t\t\t\t\t...(writer._tag === \"Unconfirmed\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprocessId: writer.processId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tterminationCause: writer.cause,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t\t\t\t\tmessage: `Manual recovery is required. Stop all writers before restoring the snapshot at ${snapshotDirectory}`,\n\t\t\t\t\t\t\t\t\t\t\t\trecoveryCause: exit.cause,\n\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\tyield* Ref.set(\n\t\t\t\t\t\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\t\t\t\t\t\tManagerState.RecoveryRequired({ error }),\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\treturn yield* error\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...completed.value,\n\t\t\t\t\t\tsnapshotCleanupFailure: completed.snapshotCleanupFailure,\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t)\n\t\treturn {\n\t\t\tdirectory,\n\t\t\tagent,\n\t\t\trecoveryRequired: Ref.get(transaction).pipe(\n\t\t\t\tEffect.map((state) =>\n\t\t\t\t\tstate._tag === \"RecoveryRequired\"\n\t\t\t\t\t\t? Option.some(state.error)\n\t\t\t\t\t\t: Option.none(),\n\t\t\t\t),\n\t\t\t),\n\t\t\treadState: lock.withPermits(1)(readState),\n\t\t\tinstall,\n\t\t}\n\t})\n"],"mappings":"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"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=22.19.0"
5
5
  },
6
- "version": "0.2.0-beta.1",
6
+ "version": "0.2.0-beta.2",
7
7
  "type": "module",
8
8
  "description": "Installs dependencies with npm, pnpm or Yarn, and rolls back on failure",
9
9
  "license": "Apache-2.0",
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  Exit,
9
9
  FileSystem,
10
10
  HashMap,
11
+ Match,
11
12
  Option,
12
13
  Path,
13
14
  PlatformError,
@@ -128,8 +129,53 @@ const backupFiles = [
128
129
  ".pnp.cjs",
129
130
  ".pnp.loader.mjs",
130
131
  ]
131
- const executable = (agent: Agent) =>
132
- String.startsWith("yarn")(agent) ? "yarn" : agent
132
+ const strategy = (agent: Agent, operation: "install" | "recover") => {
133
+ const recovery = operation === "recover"
134
+ return Match.value(agent).pipe(
135
+ Match.when("npm", () => ({
136
+ executable: "npm",
137
+ args: [
138
+ "install",
139
+ "--no-audit",
140
+ "--no-fund",
141
+ ...(recovery ? ["--prefer-offline"] : []),
142
+ ],
143
+ env: { CI: "true" },
144
+ lockfiles: ["package-lock.json", "npm-shrinkwrap.json"],
145
+ })),
146
+ Match.when("pnpm", () => ({
147
+ executable: "pnpm",
148
+ args: [
149
+ "install",
150
+ recovery ? "--frozen-lockfile" : "--no-frozen-lockfile",
151
+ "--config.confirmModulesPurge=false",
152
+ ],
153
+ env: { CI: "true" },
154
+ lockfiles: ["pnpm-lock.yaml"],
155
+ })),
156
+ Match.when("yarn-classic", () => ({
157
+ executable: "yarn",
158
+ args: [
159
+ "install",
160
+ "--non-interactive",
161
+ ...(recovery ? ["--frozen-lockfile", "--prefer-offline"] : []),
162
+ ],
163
+ env: { CI: "true" },
164
+ lockfiles: ["yarn.lock"],
165
+ })),
166
+ Match.when("yarn-berry", () => ({
167
+ executable: "yarn",
168
+ args: ["install", ...(recovery ? ["--immutable"] : [])],
169
+ env: {
170
+ CI: "true",
171
+ YARN_ENABLE_IMMUTABLE_INSTALLS: recovery ? "true" : "false",
172
+ YARN_NODE_LINKER: "node-modules",
173
+ },
174
+ lockfiles: ["yarn.lock"],
175
+ })),
176
+ Match.exhaustive,
177
+ )
178
+ }
133
179
 
134
180
  const readSource = Effect.fnUntraced(function* (
135
181
  fs: FileSystem.FileSystem,
@@ -399,35 +445,12 @@ export const make = (
399
445
  forceKillAfter: "5 seconds",
400
446
  }
401
447
  const run = Effect.fnUntraced(function* (operation: "install" | "recover") {
402
- const recovery = operation === "recover"
403
- const args =
404
- agent === "npm"
405
- ? [
406
- "install",
407
- "--no-audit",
408
- "--no-fund",
409
- ...(recovery ? ["--prefer-offline"] : []),
410
- ]
411
- : agent === "pnpm"
412
- ? [
413
- "install",
414
- recovery ? "--frozen-lockfile" : "--no-frozen-lockfile",
415
- "--config.confirmModulesPurge=false",
416
- ]
417
- : agent === "yarn-classic"
418
- ? [
419
- "install",
420
- "--non-interactive",
421
- ...(recovery
422
- ? ["--frozen-lockfile", "--prefer-offline"]
423
- : []),
424
- ]
425
- : ["install", ...(recovery ? ["--immutable"] : [])]
448
+ const command = strategy(agent, operation)
426
449
  // Acquire and install the release handler atomically, then wait interruptibly.
427
450
  return yield* Effect.acquireUseRelease(
428
451
  spawner
429
452
  .spawn(
430
- ChildProcess.make(executable(agent), args, {
453
+ ChildProcess.make(command.executable, command.args, {
431
454
  cwd: directory,
432
455
  extendEnv: true,
433
456
  stdin: "ignore",
@@ -435,17 +458,7 @@ export const make = (
435
458
  stderr: "inherit",
436
459
  detached: true,
437
460
  ...termination,
438
- env: {
439
- CI: "true",
440
- ...(agent === "yarn-berry"
441
- ? {
442
- YARN_ENABLE_IMMUTABLE_INSTALLS: recovery
443
- ? "true"
444
- : "false",
445
- YARN_NODE_LINKER: "node-modules",
446
- }
447
- : {}),
448
- },
461
+ env: command.env,
449
462
  }),
450
463
  )
451
464
  .pipe(
@@ -464,7 +477,7 @@ export const make = (
464
477
  orElse: () =>
465
478
  PackageManagerError.make({
466
479
  operation,
467
- message: `${executable(agent)} ${operation} timed out`,
480
+ message: `${command.executable} ${operation} timed out`,
468
481
  }),
469
482
  }),
470
483
  )
@@ -472,7 +485,7 @@ export const make = (
472
485
  return yield* PackageManagerError.make({
473
486
  operation,
474
487
  exitCode: code,
475
- message: `${executable(agent)} ${operation} exited with code ${code}`,
488
+ message: `${command.executable} ${operation} exited with code ${code}`,
476
489
  })
477
490
  }),
478
491
  // The command owns its group; a stopped leader may leave live descendants.
@@ -551,12 +564,7 @@ export const make = (
551
564
  original,
552
565
  )
553
566
 
554
- const agentLocks =
555
- agent === "npm"
556
- ? ["package-lock.json", "npm-shrinkwrap.json"]
557
- : agent === "pnpm"
558
- ? ["pnpm-lock.yaml"]
559
- : ["yarn.lock"]
567
+ const agentLocks = strategy(agent, "install").lockfiles
560
568
  const hasLock = Array.some(
561
569
  backups,
562
570
  ({ name, contents }) =>