@graftland/package-manager 0.3.3 → 0.4.1

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.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { PackageManagerDeclaration } from "./PackageManagerDeclaration.js";
2
- import { SnapshotCleanupFailure, UnsupportedFileTypeError } from "./snapshot.js";
3
1
  import { Changes, PackageName } from "./manifest.js";
2
+ import { PackageManagerDeclaration, PackageManagerName } from "./PackageManagerDeclaration.js";
3
+ import { SnapshotCleanupFailure, UnsupportedFileTypeError } from "./snapshot.js";
4
4
  import { Cause, Effect, FileSystem, HashMap, Option, Path, PlatformError, Schema } from "effect";
5
5
  import { ChildProcessSpawner } from "effect/process";
6
6
  export declare const Agent: Schema.Literals<readonly ["npm", "pnpm", "yarn-classic", "yarn-berry"]>;
@@ -34,6 +34,22 @@ export interface Options {
34
34
  /** Optional npm_config_user_agent supplied by the caller; used only as a fallback. */
35
35
  readonly userAgent?: string;
36
36
  }
37
+ /**
38
+ * The agent that runs a package manager. Yarn 1 is Classic and later versions
39
+ * are Berry; without a version, `yarn` means the Classic global binary.
40
+ */
41
+ export declare const agentFor: (name: typeof PackageManagerName.Type, version?: string) => Agent;
42
+ /** A package manager and its version, as named by `npm_config_user_agent`. */
43
+ export interface UserAgent {
44
+ readonly name: typeof PackageManagerName.Type;
45
+ readonly version: string;
46
+ }
47
+ /** The first `name/version` of a user agent; other package managers, such as Bun, are none. */
48
+ export declare const parseUserAgent: (userAgent: string) => Option.Option<UserAgent>;
49
+ /** What runs a project script, such as `npm run` in `npm run dev`. */
50
+ export declare const runPrefix: (agent: Agent) => string;
51
+ /** The command that installs a project's dependencies. */
52
+ export declare const installCommand: (agent: Agent) => string;
37
53
  /** packageManager, then lockfile, then caller user-agent, then npm. Conflicting lockfiles fail. */
38
54
  export declare const detectAgent: (options: Options) => Effect.Effect<"npm" | "pnpm" | "yarn-berry" | "yarn-classic", PackageManagerError | PlatformError.PlatformError | Schema.SchemaError, FileSystem.FileSystem | Path.Path>;
39
55
  export interface PackageState {
@@ -1 +1 @@
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;iBAsEP;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,SAAA,YAAA,OAAA,OAAA,gBAAA,OAAA,oBAAA,sBAAA,WAAA,aAAA,KAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;qBAqDa,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,WAAQ,aACP,mBAAmB,MAAI,qBAElC;;iBAQc;WACP,aAAa,mBAAmB;WAChC;;;qBAUG,iBAAc,sBAAwB,OAAO,OAAO;;qBAOpD,YAAS,OAAW;;qBAIpB,iBAAc,OAAW;;qBAIzB,cAAW,SAAA,YAAA,OAAA,uDAAA,sBAAA,cAAA,gBAAA,OAAA,aAAA,WAAA,aAAA,KAAA;iBAsEP;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,SAAA,YAAA,OAAA,OAAA,gBAAA,OAAA,oBAAA,sBAAA,WAAA,aAAA,KAAA"}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
+ import { Changes, PackageName, applyChanges, decodeManifest, encodeManifest, fields } from "./manifest.js";
1
2
  import { PackageManagerDeclaration, PackageManagerName } from "./PackageManagerDeclaration.js";
2
3
  import { terminateProcess } from "./process.js";
3
4
  import { UnsupportedFileTypeError, inspectManagedFiles, withSnapshot, writeSnapshot } from "./snapshot.js";
4
- import { Changes, PackageName, applyChanges, decodeManifest, encodeManifest, fields } from "./manifest.js";
5
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
@@ -120,7 +120,25 @@ const decodeUserAgent = Schema.decodeUnknownOption(Schema.TemplateLiteralParser(
120
120
  Digits,
121
121
  Schema.String
122
122
  ]));
123
- const agentOf = (name, major) => name === "yarn" ? major === "1" ? "yarn-classic" : "yarn-berry" : name;
123
+ /**
124
+ * The agent that runs a package manager. Yarn 1 is Classic and later versions
125
+ * are Berry; without a version, `yarn` means the Classic global binary.
126
+ */
127
+ const agentFor = (name, version) => name === "yarn" ? version === void 0 || String.split(".")(version)[0] === "1" ? "yarn-classic" : "yarn-berry" : name;
128
+ const decodeUserAgentName = Schema.decodeUnknownOption(Schema.TemplateLiteralParser([
129
+ Executable,
130
+ "/",
131
+ Schema.String.check(Schema.isMinLength(1))
132
+ ]));
133
+ /** The first `name/version` of a user agent; other package managers, such as Bun, are none. */
134
+ const parseUserAgent = (userAgent) => Option.map(decodeUserAgentName(String.split(" ")(userAgent)[0] ?? ""), ([name, , version]) => ({
135
+ name,
136
+ version
137
+ }));
138
+ /** What runs a project script, such as `npm run` in `npm run dev`. */
139
+ const runPrefix = (agent) => agent === "npm" ? "npm run" : agent === "pnpm" ? "pnpm" : "yarn";
140
+ /** The command that installs a project's dependencies. */
141
+ const installCommand = (agent) => agent === "npm" ? "npm install" : agent === "pnpm" ? "pnpm install" : "yarn";
124
142
  /** packageManager, then lockfile, then caller user-agent, then npm. Conflicting lockfiles fail. */
125
143
  const detectAgent = Effect.fnUntraced(function* (options) {
126
144
  if (options.agent !== void 0) return yield* Schema.decodeEffect(Agent)(options.agent);
@@ -132,7 +150,7 @@ const detectAgent = Effect.fnUntraced(function* (options) {
132
150
  operation: "detect",
133
151
  message: `Unsupported packageManager: ${manifest.packageManager}\n${error.message}`
134
152
  })));
135
- return agentOf(name, `${version.major}`);
153
+ return agentFor(name, `${version.major}`);
136
154
  }
137
155
  const found = yield* Effect.filter(lockfiles, (file) => fs.exists(path.join(options.directory, file)));
138
156
  const kinds = Array.dedupe(Array.map(found, (file) => file === "pnpm-lock.yaml" ? "pnpm" : file === "yarn.lock" ? "yarn" : "npm"));
@@ -150,7 +168,7 @@ const detectAgent = Effect.fnUntraced(function* (options) {
150
168
  if (kinds[0] !== void 0) return kinds[0];
151
169
  return Option.match(decodeUserAgent(options.userAgent ?? ""), {
152
170
  onNone: () => "npm",
153
- onSome: ([name, , major]) => agentOf(name, major)
171
+ onSome: ([name, , major]) => agentFor(name, major)
154
172
  });
155
173
  });
156
174
  const diffState = (before, after) => {
@@ -343,6 +361,6 @@ const make = Effect.fnUntraced(function* (options) {
343
361
  install
344
362
  };
345
363
  });
346
- export { Agent, Changes, PackageManagerDeclaration, PackageManagerError, PackageName, RecoveryRequiredError, UnsupportedFileTypeError, detectAgent, diffState, make };
364
+ export { Agent, Changes, PackageManagerDeclaration, PackageManagerError, PackageName, RecoveryRequiredError, UnsupportedFileTypeError, agentFor, detectAgent, diffState, installCommand, make, parseUserAgent, runPrefix };
347
365
 
348
366
  //# sourceMappingURL=index.js.map
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\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.decodeUnknownEffect(\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\tconst [name, , version] = yield* decodePackageManager(\n\t\t\tmanifest.packageManager,\n\t\t).pipe(\n\t\t\t// Keep the schema issue: it names the constraint the declaration failed.\n\t\t\tEffect.mapError((error) =>\n\t\t\t\tPackageManagerError.make({\n\t\t\t\t\toperation: \"detect\",\n\t\t\t\t\tmessage: `Unsupported packageManager: ${manifest.packageManager}\\n${error.message}`,\n\t\t\t\t}),\n\t\t\t),\n\t\t)\n\t\treturn agentOf(name, `${version.major}`)\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 = Effect.fnUntraced(function* (\n\toptions: Options,\n): Effect.fn.Return<\n\tPackageManager,\n\tError,\n\tFileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner\n> {\n\tconst fs = yield* FileSystem.FileSystem\n\tconst path = yield* Path.Path\n\tconst spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n\tconst directory = path.resolve(options.directory)\n\tyield* inspectManagedFiles(fs, path, directory, backupFiles)\n\tconst agent = yield* detectAgent({ ...options, directory })\n\tconst lock = yield* Semaphore.make(1)\n\tconst transaction = yield* Ref.make<ManagerState>(ManagerState.Ready())\n\tconst manifestFile = path.join(directory, \"package.json\")\n\tconst readManifest = readSource(fs, manifestFile).pipe(\n\t\tEffect.flatMap(decodeManifest),\n\t)\n\tconst installed = Schema.decodeUnknownEffect(\n\t\tSchema.fromJsonString(\n\t\t\tSchema.Struct({ version: Schema.optionalKey(Schema.String) }),\n\t\t),\n\t)\n\tconst stateOf = Effect.fnUntraced(function* (manifest: Manifest) {\n\t\tif (yield* fs.exists(path.join(directory, \".pnp.cjs\")))\n\t\t\treturn yield* PackageManagerError.make({\n\t\t\t\toperation: \"state\",\n\t\t\t\tmessage:\n\t\t\t\t\t\"Plug'n'Play is not supported; configure Yarn with nodeLinker: node-modules\",\n\t\t\t})\n\t\tconst deps = Array.reduce(\n\t\t\tfields,\n\t\t\t{} as Readonly<Record<string, string>>,\n\t\t\t(all, field) => ({ ...all, ...manifest[field] }),\n\t\t)\n\t\tconst entries = yield* Effect.forEach(\n\t\t\tRecord.toEntries(deps),\n\t\t\tEffect.fnUntraced(function* ([name, spec]) {\n\t\t\t\tconst file = path.join(directory, \"node_modules\", name, \"package.json\")\n\t\t\t\tconst source = yield* fs.readFileString(file).pipe(\n\t\t\t\t\tEffect.asSome,\n\t\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t\t(cause) =>\n\t\t\t\t\t\t\tArray.every(\n\t\t\t\t\t\t\t\tcause.reasons,\n\t\t\t\t\t\t\t\t(reason) =>\n\t\t\t\t\t\t\t\t\tCause.isFailReason(reason) &&\n\t\t\t\t\t\t\t\t\treason.error.reason.module === \"FileSystem\" &&\n\t\t\t\t\t\t\t\t\treason.error.reason._tag === \"NotFound\",\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t() => Effect.succeedNone,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\tif (Option.isNone(source))\n\t\t\t\t\treturn [name, { spec, version: undefined, path: undefined }] as const\n\t\t\t\tconst pkg = yield* installed(source.value)\n\t\t\t\treturn [\n\t\t\t\t\tname,\n\t\t\t\t\t{\n\t\t\t\t\t\tspec,\n\t\t\t\t\t\tversion: pkg.version,\n\t\t\t\t\t\tpath: yield* fs.realPath(path.dirname(file)),\n\t\t\t\t\t},\n\t\t\t\t] as const\n\t\t\t}),\n\t\t\t{ concurrency: 16 },\n\t\t)\n\t\treturn HashMap.fromIterable<string, PackageState>(entries)\n\t})\n\tconst readState = Effect.flatMap(readManifest, stateOf)\n\tconst termination: ChildProcess.KillOptions = {\n\t\tforceKillAfter: \"5 seconds\",\n\t}\n\tconst run = Effect.fnUntraced(function* (operation: \"install\" | \"recover\") {\n\t\tconst command = strategy(agent, operation)\n\t\t// Acquire and install the release handler atomically, then wait interruptibly.\n\t\treturn yield* Effect.acquireUseRelease(\n\t\t\tspawner\n\t\t\t\t.spawn(\n\t\t\t\t\tChildProcess.make(command.executable, command.args, {\n\t\t\t\t\t\tcwd: directory,\n\t\t\t\t\t\textendEnv: true,\n\t\t\t\t\t\tstdin: \"ignore\",\n\t\t\t\t\t\tstdout: \"inherit\",\n\t\t\t\t\t\tstderr: \"inherit\",\n\t\t\t\t\t\tdetached: true,\n\t\t\t\t\t\t...termination,\n\t\t\t\t\t\tenv: command.env,\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\t.pipe(\n\t\t\t\t\tEffect.tap(() =>\n\t\t\t\t\t\tRef.set(\n\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\tManagerState.Active({ writer: WriterState.Running() }),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\tEffect.fnUntraced(function* (process) {\n\t\t\t\tconst code = yield* process.exitCode.pipe(\n\t\t\t\t\tEffect.timeoutOrElse({\n\t\t\t\t\t\tduration: \"10 minutes\",\n\t\t\t\t\t\torElse: () =>\n\t\t\t\t\t\t\tPackageManagerError.make({\n\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\tmessage: `${command.executable} ${operation} timed out`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\tif (code !== 0)\n\t\t\t\t\treturn yield* PackageManagerError.make({\n\t\t\t\t\t\toperation,\n\t\t\t\t\t\texitCode: code,\n\t\t\t\t\t\tmessage: `${command.executable} ${operation} exited with code ${code}`,\n\t\t\t\t\t})\n\t\t\t}),\n\t\t\t// The command owns its group; a stopped leader may leave live descendants.\n\t\t\t// Observe kill failures here rather than relying on best-effort scope cleanup.\n\t\t\tEffect.fnUntraced(function* (process) {\n\t\t\t\tconst result = yield* terminateProcess(process, termination)\n\t\t\t\tyield* Ref.set(\n\t\t\t\t\ttransaction,\n\t\t\t\t\tManagerState.Active({\n\t\t\t\t\t\twriter:\n\t\t\t\t\t\t\tresult._tag === \"OriginalGroupStopped\"\n\t\t\t\t\t\t\t\t? WriterState.ManagedWritersIdle()\n\t\t\t\t\t\t\t\t: WriterState.Unconfirmed({\n\t\t\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\t\t\tprocessId: process.pid,\n\t\t\t\t\t\t\t\t\t\tcause: result.cause,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\tif (result._tag === \"Unconfirmed\")\n\t\t\t\t\treturn yield* Effect.failCause(result.cause)\n\t\t\t}),\n\t\t)\n\t}, Effect.scoped)\n\t// AbortSignal does not join accepted OS writes. Keep the writer owned until completion.\n\tconst withFileWriter = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n\t\tEffect.acquireUseRelease(\n\t\t\tRef.set(\n\t\t\t\ttransaction,\n\t\t\t\tManagerState.Active({ writer: WriterState.FileWrite() }),\n\t\t\t),\n\t\t\t() => effect,\n\t\t\t() =>\n\t\t\t\tRef.set(\n\t\t\t\t\ttransaction,\n\t\t\t\t\tManagerState.Active({ writer: WriterState.ManagedWritersIdle() }),\n\t\t\t\t),\n\t\t).pipe(Effect.uninterruptible)\n\n\tconst install = Effect.fnUntraced(function* (changes: Changes) {\n\t\tconst state = yield* Ref.get(transaction)\n\t\tif (state._tag === \"RecoveryRequired\") return yield* state.error\n\t\tconst validated = yield* Schema.decodeEffect(Changes)(changes)\n\t\tconst inspected = yield* inspectManagedFiles(\n\t\t\tfs,\n\t\t\tpath,\n\t\t\tdirectory,\n\t\t\tbackupFiles,\n\t\t)\n\t\tconst backups = yield* Effect.forEach(\n\t\t\tinspected,\n\t\t\tEffect.fnUntraced(function* ({ name, file, metadata }) {\n\t\t\t\tconst contents = yield* Option.match(metadata, {\n\t\t\t\t\tonNone: () => Effect.succeedNone,\n\t\t\t\t\tonSome: (info) =>\n\t\t\t\t\t\tfs\n\t\t\t\t\t\t\t.readFile(file)\n\t\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\t\tEffect.map((bytes) =>\n\t\t\t\t\t\t\t\t\tOption.some<Contents>({ ...info, bytes }),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t),\n\t\t\t\t})\n\t\t\t\treturn { name, file, contents }\n\t\t\t}),\n\t\t)\n\t\tconst original = yield* readSource(fs, manifestFile)\n\t\tconst manifest = yield* decodeManifest(original)\n\t\tconst before = yield* stateOf(manifest)\n\t\tconst text = yield* encodeManifest(\n\t\t\tapplyChanges(manifest, validated),\n\t\t\toriginal,\n\t\t)\n\n\t\tconst agentLocks = strategy(agent, \"install\").lockfiles\n\t\tconst hasLock = Array.some(\n\t\t\tbackups,\n\t\t\t({ name, contents }) =>\n\t\t\t\tArray.contains(agentLocks, name) && Option.isSome(contents),\n\t\t)\n\t\tconst completed = yield* withSnapshot(\n\t\t\tfs,\n\t\t\tRef.get(transaction).pipe(Effect.map((state) => state._tag === \"Ready\")),\n\t\t\tEffect.fnUntraced(function* (snapshotDirectory) {\n\t\t\t\tconst snapshot = yield* writeSnapshot(\n\t\t\t\t\tfs,\n\t\t\t\t\tpath,\n\t\t\t\t\tsnapshotDirectory,\n\t\t\t\t\tdirectory,\n\t\t\t\t\tagent,\n\t\t\t\t\tbackups,\n\t\t\t\t)\n\t\t\t\tconst restore = Effect.flatMap(Ref.get(transaction), (state) =>\n\t\t\t\t\tstate._tag === \"Active\" && state.writer._tag === \"ManagedWritersIdle\"\n\t\t\t\t\t\t? withFileWriter(snapshot.restore(state.writer))\n\t\t\t\t\t\t: Effect.void,\n\t\t\t\t)\n\t\t\t\treturn yield* Effect.acquireUseRelease(\n\t\t\t\t\tRef.set(\n\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\tManagerState.Active({\n\t\t\t\t\t\t\twriter: WriterState.ManagedWritersIdle(),\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.fnUntraced(function* () {\n\t\t\t\t\t\tyield* withFileWriter(fs.writeFileString(manifestFile, text))\n\t\t\t\t\t\tyield* run(\"install\")\n\t\t\t\t\t\tconst after = yield* readState\n\t\t\t\t\t\treturn { agent, ...diffState(before, after) }\n\t\t\t\t\t}),\n\t\t\t\t\tEffect.fnUntraced(function* (_, exit) {\n\t\t\t\t\t\tif (Exit.isFailure(exit)) {\n\t\t\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tstate._tag !== \"Active\" ||\n\t\t\t\t\t\t\t\tstate.writer._tag !== \"ManagedWritersIdle\"\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\tyield* withFileWriter(snapshot.restore(state.writer))\n\t\t\t\t\t\t\tif (hasLock)\n\t\t\t\t\t\t\t\tyield* run(\"recover\").pipe(\n\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\tEffect.onExit(() => restore),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Idle managed writers alone do not prove that the transaction completed.\n\t\t\t\t\t\tyield* Ref.set(transaction, ManagerState.Ready())\n\t\t\t\t\t}),\n\t\t\t\t).pipe(\n\t\t\t\t\tEffect.onExit(\n\t\t\t\t\t\tEffect.fnUntraced(function* (exit) {\n\t\t\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\t\t\tif (Exit.isSuccess(exit) || state._tag !== \"Active\") return\n\t\t\t\t\t\t\tconst writer = state.writer\n\t\t\t\t\t\t\tconst error = RecoveryRequiredError.make({\n\t\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\t\tsnapshotDirectory,\n\t\t\t\t\t\t\t\toperation:\n\t\t\t\t\t\t\t\t\twriter._tag === \"Unconfirmed\" ? writer.operation : \"rollback\",\n\t\t\t\t\t\t\t\t...(writer._tag === \"Unconfirmed\"\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\tprocessId: writer.processId,\n\t\t\t\t\t\t\t\t\t\t\tterminationCause: writer.cause,\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\tmessage: `Manual recovery is required. Stop all writers before restoring the snapshot at ${snapshotDirectory}`,\n\t\t\t\t\t\t\t\trecoveryCause: exit.cause,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tyield* Ref.set(\n\t\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\t\tManagerState.RecoveryRequired({ error }),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn yield* error\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}),\n\t\t)\n\t\treturn {\n\t\t\t...completed.value,\n\t\t\tsnapshotCleanupFailure: completed.snapshotCleanupFailure,\n\t\t}\n\t}, lock.withPermits(1))\n\treturn {\n\t\tdirectory,\n\t\tagent,\n\t\trecoveryRequired: Ref.get(transaction).pipe(\n\t\t\tEffect.map((state) =>\n\t\t\t\tstate._tag === \"RecoveryRequired\"\n\t\t\t\t\t? Option.some(state.error)\n\t\t\t\t\t: Option.none(),\n\t\t\t),\n\t\t),\n\t\treadState: lock.withPermits(1)(readState),\n\t\tinstall,\n\t}\n})\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\tEffect,\n\tExit,\n\tFileSystem,\n\tHashMap,\n\tMatch,\n\tOption,\n\tPath,\n\tPlatformError,\n\tRecord,\n\tRef,\n\tSchema,\n\tSemaphore,\n\tStream,\n\tString,\n} from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/process\"\n\nimport {\n\tapplyChanges,\n\tChanges,\n\tdecodeManifest,\n\tencodeManifest,\n\tfields,\n\ttype Manifest,\n} from \"./manifest\"\nimport {\n\tPackageManagerDeclaration,\n\tPackageManagerName,\n} from \"./PackageManagerDeclaration\"\nimport { terminateProcess } from \"./process\"\nimport {\n\tinspectManagedFiles,\n\tUnsupportedFileTypeError,\n\twithSnapshot,\n\twriteSnapshot,\n\ttype Contents,\n\ttype SnapshotCleanupFailure,\n} from \"./snapshot\"\n\nexport { PackageManagerDeclaration } from \"./PackageManagerDeclaration\"\n\nexport {\n\tUnsupportedFileTypeError,\n\ttype SnapshotCleanupFailure,\n} from \"./snapshot\"\n\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.decodeUnknownEffect(\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)\n/**\n * The agent that runs a package manager. Yarn 1 is Classic and later versions\n * are Berry; without a version, `yarn` means the Classic global binary.\n */\nexport const agentFor = (\n\tname: typeof PackageManagerName.Type,\n\tversion?: string,\n): Agent =>\n\tname === \"yarn\"\n\t\t? version === undefined || String.split(\".\")(version)[0] === \"1\"\n\t\t\t? \"yarn-classic\"\n\t\t\t: \"yarn-berry\"\n\t\t: name\n\n/** A package manager and its version, as named by `npm_config_user_agent`. */\nexport interface UserAgent {\n\treadonly name: typeof PackageManagerName.Type\n\treadonly version: string\n}\nconst decodeUserAgentName = Schema.decodeUnknownOption(\n\tSchema.TemplateLiteralParser([\n\t\tExecutable,\n\t\t\"/\",\n\t\tSchema.String.check(Schema.isMinLength(1)),\n\t]),\n)\n/** The first `name/version` of a user agent; other package managers, such as Bun, are none. */\nexport const parseUserAgent = (userAgent: string): Option.Option<UserAgent> =>\n\tOption.map(\n\t\tdecodeUserAgentName(String.split(\" \")(userAgent)[0] ?? \"\"),\n\t\t([name, , version]) => ({ name, version }),\n\t)\n\n/** What runs a project script, such as `npm run` in `npm run dev`. */\nexport const runPrefix = (agent: Agent): string =>\n\tagent === \"npm\" ? \"npm run\" : agent === \"pnpm\" ? \"pnpm\" : \"yarn\"\n\n/** The command that installs a project's dependencies. */\nexport const installCommand = (agent: Agent): string =>\n\tagent === \"npm\" ? \"npm install\" : agent === \"pnpm\" ? \"pnpm install\" : \"yarn\"\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\tconst [name, , version] = yield* decodePackageManager(\n\t\t\tmanifest.packageManager,\n\t\t).pipe(\n\t\t\t// Keep the schema issue: it names the constraint the declaration failed.\n\t\t\tEffect.mapError((error) =>\n\t\t\t\tPackageManagerError.make({\n\t\t\t\t\toperation: \"detect\",\n\t\t\t\t\tmessage: `Unsupported packageManager: ${manifest.packageManager}\\n${error.message}`,\n\t\t\t\t}),\n\t\t\t),\n\t\t)\n\t\treturn agentFor(name, `${version.major}`)\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]) => agentFor(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 = Effect.fnUntraced(function* (\n\toptions: Options,\n): Effect.fn.Return<\n\tPackageManager,\n\tError,\n\tFileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner\n> {\n\tconst fs = yield* FileSystem.FileSystem\n\tconst path = yield* Path.Path\n\tconst spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n\tconst directory = path.resolve(options.directory)\n\tyield* inspectManagedFiles(fs, path, directory, backupFiles)\n\tconst agent = yield* detectAgent({ ...options, directory })\n\tconst lock = yield* Semaphore.make(1)\n\tconst transaction = yield* Ref.make<ManagerState>(ManagerState.Ready())\n\tconst manifestFile = path.join(directory, \"package.json\")\n\tconst readManifest = readSource(fs, manifestFile).pipe(\n\t\tEffect.flatMap(decodeManifest),\n\t)\n\tconst installed = Schema.decodeUnknownEffect(\n\t\tSchema.fromJsonString(\n\t\t\tSchema.Struct({ version: Schema.optionalKey(Schema.String) }),\n\t\t),\n\t)\n\tconst stateOf = Effect.fnUntraced(function* (manifest: Manifest) {\n\t\tif (yield* fs.exists(path.join(directory, \".pnp.cjs\")))\n\t\t\treturn yield* PackageManagerError.make({\n\t\t\t\toperation: \"state\",\n\t\t\t\tmessage:\n\t\t\t\t\t\"Plug'n'Play is not supported; configure Yarn with nodeLinker: node-modules\",\n\t\t\t})\n\t\tconst deps = Array.reduce(\n\t\t\tfields,\n\t\t\t{} as Readonly<Record<string, string>>,\n\t\t\t(all, field) => ({ ...all, ...manifest[field] }),\n\t\t)\n\t\tconst entries = yield* Effect.forEach(\n\t\t\tRecord.toEntries(deps),\n\t\t\tEffect.fnUntraced(function* ([name, spec]) {\n\t\t\t\tconst file = path.join(directory, \"node_modules\", name, \"package.json\")\n\t\t\t\tconst source = yield* fs.readFileString(file).pipe(\n\t\t\t\t\tEffect.asSome,\n\t\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t\t(cause) =>\n\t\t\t\t\t\t\tArray.every(\n\t\t\t\t\t\t\t\tcause.reasons,\n\t\t\t\t\t\t\t\t(reason) =>\n\t\t\t\t\t\t\t\t\tCause.isFailReason(reason) &&\n\t\t\t\t\t\t\t\t\treason.error.reason.module === \"FileSystem\" &&\n\t\t\t\t\t\t\t\t\treason.error.reason._tag === \"NotFound\",\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t() => Effect.succeedNone,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\tif (Option.isNone(source))\n\t\t\t\t\treturn [name, { spec, version: undefined, path: undefined }] as const\n\t\t\t\tconst pkg = yield* installed(source.value)\n\t\t\t\treturn [\n\t\t\t\t\tname,\n\t\t\t\t\t{\n\t\t\t\t\t\tspec,\n\t\t\t\t\t\tversion: pkg.version,\n\t\t\t\t\t\tpath: yield* fs.realPath(path.dirname(file)),\n\t\t\t\t\t},\n\t\t\t\t] as const\n\t\t\t}),\n\t\t\t{ concurrency: 16 },\n\t\t)\n\t\treturn HashMap.fromIterable<string, PackageState>(entries)\n\t})\n\tconst readState = Effect.flatMap(readManifest, stateOf)\n\tconst termination: ChildProcess.KillOptions = {\n\t\tforceKillAfter: \"5 seconds\",\n\t}\n\tconst run = Effect.fnUntraced(function* (operation: \"install\" | \"recover\") {\n\t\tconst command = strategy(agent, operation)\n\t\t// Acquire and install the release handler atomically, then wait interruptibly.\n\t\treturn yield* Effect.acquireUseRelease(\n\t\t\tspawner\n\t\t\t\t.spawn(\n\t\t\t\t\tChildProcess.make(command.executable, command.args, {\n\t\t\t\t\t\tcwd: directory,\n\t\t\t\t\t\textendEnv: true,\n\t\t\t\t\t\tstdin: \"ignore\",\n\t\t\t\t\t\tstdout: \"inherit\",\n\t\t\t\t\t\tstderr: \"inherit\",\n\t\t\t\t\t\tdetached: true,\n\t\t\t\t\t\t...termination,\n\t\t\t\t\t\tenv: command.env,\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\t.pipe(\n\t\t\t\t\tEffect.tap(() =>\n\t\t\t\t\t\tRef.set(\n\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\tManagerState.Active({ writer: WriterState.Running() }),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\tEffect.fnUntraced(function* (process) {\n\t\t\t\tconst code = yield* process.exitCode.pipe(\n\t\t\t\t\tEffect.timeoutOrElse({\n\t\t\t\t\t\tduration: \"10 minutes\",\n\t\t\t\t\t\torElse: () =>\n\t\t\t\t\t\t\tPackageManagerError.make({\n\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\tmessage: `${command.executable} ${operation} timed out`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\tif (code !== 0)\n\t\t\t\t\treturn yield* PackageManagerError.make({\n\t\t\t\t\t\toperation,\n\t\t\t\t\t\texitCode: code,\n\t\t\t\t\t\tmessage: `${command.executable} ${operation} exited with code ${code}`,\n\t\t\t\t\t})\n\t\t\t}),\n\t\t\t// The command owns its group; a stopped leader may leave live descendants.\n\t\t\t// Observe kill failures here rather than relying on best-effort scope cleanup.\n\t\t\tEffect.fnUntraced(function* (process) {\n\t\t\t\tconst result = yield* terminateProcess(process, termination)\n\t\t\t\tyield* Ref.set(\n\t\t\t\t\ttransaction,\n\t\t\t\t\tManagerState.Active({\n\t\t\t\t\t\twriter:\n\t\t\t\t\t\t\tresult._tag === \"OriginalGroupStopped\"\n\t\t\t\t\t\t\t\t? WriterState.ManagedWritersIdle()\n\t\t\t\t\t\t\t\t: WriterState.Unconfirmed({\n\t\t\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\t\t\tprocessId: process.pid,\n\t\t\t\t\t\t\t\t\t\tcause: result.cause,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\tif (result._tag === \"Unconfirmed\")\n\t\t\t\t\treturn yield* Effect.failCause(result.cause)\n\t\t\t}),\n\t\t)\n\t}, Effect.scoped)\n\t// AbortSignal does not join accepted OS writes. Keep the writer owned until completion.\n\tconst withFileWriter = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n\t\tEffect.acquireUseRelease(\n\t\t\tRef.set(\n\t\t\t\ttransaction,\n\t\t\t\tManagerState.Active({ writer: WriterState.FileWrite() }),\n\t\t\t),\n\t\t\t() => effect,\n\t\t\t() =>\n\t\t\t\tRef.set(\n\t\t\t\t\ttransaction,\n\t\t\t\t\tManagerState.Active({ writer: WriterState.ManagedWritersIdle() }),\n\t\t\t\t),\n\t\t).pipe(Effect.uninterruptible)\n\n\tconst install = Effect.fnUntraced(function* (changes: Changes) {\n\t\tconst state = yield* Ref.get(transaction)\n\t\tif (state._tag === \"RecoveryRequired\") return yield* state.error\n\t\tconst validated = yield* Schema.decodeEffect(Changes)(changes)\n\t\tconst inspected = yield* inspectManagedFiles(\n\t\t\tfs,\n\t\t\tpath,\n\t\t\tdirectory,\n\t\t\tbackupFiles,\n\t\t)\n\t\tconst backups = yield* Effect.forEach(\n\t\t\tinspected,\n\t\t\tEffect.fnUntraced(function* ({ name, file, metadata }) {\n\t\t\t\tconst contents = yield* Option.match(metadata, {\n\t\t\t\t\tonNone: () => Effect.succeedNone,\n\t\t\t\t\tonSome: (info) =>\n\t\t\t\t\t\tfs\n\t\t\t\t\t\t\t.readFile(file)\n\t\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\t\tEffect.map((bytes) =>\n\t\t\t\t\t\t\t\t\tOption.some<Contents>({ ...info, bytes }),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t),\n\t\t\t\t})\n\t\t\t\treturn { name, file, contents }\n\t\t\t}),\n\t\t)\n\t\tconst original = yield* readSource(fs, manifestFile)\n\t\tconst manifest = yield* decodeManifest(original)\n\t\tconst before = yield* stateOf(manifest)\n\t\tconst text = yield* encodeManifest(\n\t\t\tapplyChanges(manifest, validated),\n\t\t\toriginal,\n\t\t)\n\n\t\tconst agentLocks = strategy(agent, \"install\").lockfiles\n\t\tconst hasLock = Array.some(\n\t\t\tbackups,\n\t\t\t({ name, contents }) =>\n\t\t\t\tArray.contains(agentLocks, name) && Option.isSome(contents),\n\t\t)\n\t\tconst completed = yield* withSnapshot(\n\t\t\tfs,\n\t\t\tRef.get(transaction).pipe(Effect.map((state) => state._tag === \"Ready\")),\n\t\t\tEffect.fnUntraced(function* (snapshotDirectory) {\n\t\t\t\tconst snapshot = yield* writeSnapshot(\n\t\t\t\t\tfs,\n\t\t\t\t\tpath,\n\t\t\t\t\tsnapshotDirectory,\n\t\t\t\t\tdirectory,\n\t\t\t\t\tagent,\n\t\t\t\t\tbackups,\n\t\t\t\t)\n\t\t\t\tconst restore = Effect.flatMap(Ref.get(transaction), (state) =>\n\t\t\t\t\tstate._tag === \"Active\" && state.writer._tag === \"ManagedWritersIdle\"\n\t\t\t\t\t\t? withFileWriter(snapshot.restore(state.writer))\n\t\t\t\t\t\t: Effect.void,\n\t\t\t\t)\n\t\t\t\treturn yield* Effect.acquireUseRelease(\n\t\t\t\t\tRef.set(\n\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\tManagerState.Active({\n\t\t\t\t\t\t\twriter: WriterState.ManagedWritersIdle(),\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.fnUntraced(function* () {\n\t\t\t\t\t\tyield* withFileWriter(fs.writeFileString(manifestFile, text))\n\t\t\t\t\t\tyield* run(\"install\")\n\t\t\t\t\t\tconst after = yield* readState\n\t\t\t\t\t\treturn { agent, ...diffState(before, after) }\n\t\t\t\t\t}),\n\t\t\t\t\tEffect.fnUntraced(function* (_, exit) {\n\t\t\t\t\t\tif (Exit.isFailure(exit)) {\n\t\t\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tstate._tag !== \"Active\" ||\n\t\t\t\t\t\t\t\tstate.writer._tag !== \"ManagedWritersIdle\"\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\tyield* withFileWriter(snapshot.restore(state.writer))\n\t\t\t\t\t\t\tif (hasLock)\n\t\t\t\t\t\t\t\tyield* run(\"recover\").pipe(\n\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\tEffect.onExit(() => restore),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Idle managed writers alone do not prove that the transaction completed.\n\t\t\t\t\t\tyield* Ref.set(transaction, ManagerState.Ready())\n\t\t\t\t\t}),\n\t\t\t\t).pipe(\n\t\t\t\t\tEffect.onExit(\n\t\t\t\t\t\tEffect.fnUntraced(function* (exit) {\n\t\t\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\t\t\tif (Exit.isSuccess(exit) || state._tag !== \"Active\") return\n\t\t\t\t\t\t\tconst writer = state.writer\n\t\t\t\t\t\t\tconst error = RecoveryRequiredError.make({\n\t\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\t\tsnapshotDirectory,\n\t\t\t\t\t\t\t\toperation:\n\t\t\t\t\t\t\t\t\twriter._tag === \"Unconfirmed\" ? writer.operation : \"rollback\",\n\t\t\t\t\t\t\t\t...(writer._tag === \"Unconfirmed\"\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\tprocessId: writer.processId,\n\t\t\t\t\t\t\t\t\t\t\tterminationCause: writer.cause,\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\tmessage: `Manual recovery is required. Stop all writers before restoring the snapshot at ${snapshotDirectory}`,\n\t\t\t\t\t\t\t\trecoveryCause: exit.cause,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tyield* Ref.set(\n\t\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\t\tManagerState.RecoveryRequired({ error }),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn yield* error\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}),\n\t\t)\n\t\treturn {\n\t\t\t...completed.value,\n\t\t\tsnapshotCleanupFailure: completed.snapshotCleanupFailure,\n\t\t}\n\t}, lock.withPermits(1))\n\treturn {\n\t\tdirectory,\n\t\tagent,\n\t\trecoveryRequired: Ref.get(transaction).pipe(\n\t\t\tEffect.map((state) =>\n\t\t\t\tstate._tag === \"RecoveryRequired\"\n\t\t\t\t\t? Option.some(state.error)\n\t\t\t\t\t: Option.none(),\n\t\t\t),\n\t\t),\n\t\treadState: lock.withPermits(1)(readState),\n\t\tinstall,\n\t}\n})\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.js","names":[],"sources":["../src/manifest.ts"],"sourcesContent":["import {\n\tArray,\n\tEffect,\n\tHashMap,\n\tChunk,\n\tOption,\n\tRecord,\n\tSchema,\n\tString,\n} from \"effect\"\n\nexport const PackageName = Schema.String.check(\n\tSchema.isPattern(\n\t\t/^(?!(?:node_modules|favicon\\.ico)$)(?:@[a-z0-9-~][a-z0-9-._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*$/i,\n\t),\n\tSchema.isMaxLength(214),\n\tSchema.isTrimmed(),\n)\nconst isPackageName = Schema.is(PackageName)\n// A refined Record key filters unmatched keys. Validate names after retaining\n// every key so invalid input cannot silently become a partial change set.\nconst dependencyNames = Schema.makeFilter(\n\t(record: Readonly<Record<string, unknown>>) =>\n\t\tArray.map(\n\t\t\tArray.filter(Record.keys(record), (name) => !isPackageName(name)),\n\t\t\t(name) => ({\n\t\t\t\tpath: [name],\n\t\t\t\tissue: \"Invalid dependency name\",\n\t\t\t}),\n\t\t),\n)\nconst Dependencies = Schema.Record(Schema.String, Schema.String).check(\n\tdependencyNames,\n)\nexport const Manifest = Schema.StructWithRest(\n\tSchema.Struct({\n\t\tpackageManager: Schema.optionalKey(Schema.String),\n\t\tdependencies: Schema.optionalKey(Dependencies),\n\t\tdevDependencies: Schema.optionalKey(Dependencies),\n\t\toptionalDependencies: Schema.optionalKey(Dependencies),\n\t}),\n\t[Schema.Record(Schema.String, Schema.Unknown)],\n)\nexport type Manifest = typeof Manifest.Type\nconst decodeJsonManifest = Schema.decodeUnknownEffect(\n\tSchema.fromJsonString(Manifest),\n)\n// A BOM is a source-format marker, not part of the JSON value.\nexport const decodeManifest = (source: string) =>\n\tdecodeJsonManifest(String.replace(/^\\uFEFF/, \"\")(source))\n\nexport const Changes = Schema.Record(\n\tSchema.String,\n\tSchema.Union([Schema.String.check(Schema.isPattern(/\\S/)), Schema.Null]),\n).check(dependencyNames)\n/** A spec adds or updates a dependency; null removes it from all dependency fields. */\nexport type Changes = typeof Changes.Type\nexport const fields = [\n\t\"dependencies\",\n\t\"devDependencies\",\n\t\"optionalDependencies\",\n] as const\n\nexport const applyChanges = (\n\tmanifest: Manifest,\n\tchanges: Changes,\n): Manifest => {\n\tconst edits = Record.toEntries(changes)\n\tif (edits.length === 0) return manifest\n\tlet next = manifest\n\tconst additions = Array.flatMap(edits, ([name, spec]) =>\n\t\tspec !== null &&\n\t\t!Array.some(fields, (field) => Record.has(manifest[field] ?? {}, name))\n\t\t\t? [[name, spec] as const]\n\t\t\t: [],\n\t)\n\tfor (const field of fields) {\n\t\tconst entries = Array.flatMap(\n\t\t\tRecord.toEntries(manifest[field] ?? {}),\n\t\t\t([name, old]) => {\n\t\t\t\tconst spec = Record.has(changes, name) ? changes[name]! : old\n\t\t\t\treturn spec === null ? [] : [[name, spec] as const]\n\t\t\t},\n\t\t)\n\t\tif (field === \"dependencies\" && additions.length > 0) {\n\t\t\tnext = {\n\t\t\t\t...next,\n\t\t\t\t[field]: Record.fromEntries([...entries, ...additions]),\n\t\t\t}\n\t\t} else if (manifest[field] !== undefined) {\n\t\t\tnext = { ...next, [field]: Record.fromEntries(entries) }\n\t\t}\n\t}\n\treturn next\n}\n\nconst decodeKey = Schema.decodeEffect(Schema.fromJsonString(Schema.String))\nconst encodeString = Schema.encodeEffect(Schema.fromJsonString(Schema.String))\n\ninterface Member {\n\treadonly name: string\n\treadonly leading: string\n\treadonly key: string\n\treadonly colon: string\n\treadonly value: string\n\treadonly trailing: string\n}\n\n/** Read member spans from a JSON object already validated by decodeManifest. */\nconst readObject = Effect.fnUntraced(function* (source: string) {\n\t// Strings are single tokens, so punctuation inside them cannot affect nesting.\n\tconst tokens = Array.fromIterable(\n\t\tString.matchAll(/\"(?:\\\\[\\s\\S]|[^\"\\\\])*\"|[{}\\[\\]:,]|[^\\s{}\\[\\]:,]+/g)(\n\t\t\tsource,\n\t\t),\n\t)\n\tlet members = Chunk.empty<Member>()\n\tlet cursor = 1\n\tlet start = (tokens[0]?.index ?? 0) + 1\n\twhile (cursor < tokens.length && tokens[cursor]?.[0] !== \"}\") {\n\t\tconst key = tokens[cursor]!\n\t\tconst valueStart = tokens[cursor + 2]!.index\n\t\tcursor += 2\n\t\tlet depth = 0\n\t\tlet end = valueStart\n\t\tdo {\n\t\t\tconst token = tokens[cursor++]!\n\t\t\tif (token[0] === \"{\" || token[0] === \"[\") depth++\n\t\t\tif (token[0] === \"}\" || token[0] === \"]\") depth--\n\t\t\tend = token.index! + token[0].length\n\t\t} while (depth > 0)\n\t\tmembers = Chunk.append(members, {\n\t\t\tname: yield* decodeKey(key[0]),\n\t\t\tleading: String.slice(start, key.index)(source),\n\t\t\tkey: key[0],\n\t\t\tcolon: String.slice(key.index! + key[0].length, valueStart)(source),\n\t\t\tvalue: String.slice(valueStart, end)(source),\n\t\t\ttrailing:\n\t\t\t\ttokens[cursor]?.[0] === \",\"\n\t\t\t\t\t? String.slice(end, tokens[cursor]!.index)(source)\n\t\t\t\t\t: \"\",\n\t\t})\n\t\tstart = end\n\t\tif (tokens[cursor]?.[0] !== \",\") break\n\t\tstart = tokens[cursor++]!.index! + 1\n\t}\n\tconst close = tokens[cursor]!.index\n\tconst tail = String.slice(start, close)(source)\n\treturn {\n\t\tmembers: Chunk.toArray(members),\n\t\ttail,\n\t\topen: tokens[0]!.index!,\n\t\tclose,\n\t}\n})\n\n/** Ordered edits preserve insertion order; None removes every occurrence. */\ntype Edit = readonly [string, Option.Option<string>]\ntype Edits = ReadonlyArray<Edit>\n\nconst editObject = Effect.fnUntraced(function* (\n\tsource: string,\n\tchanges: Edits,\n) {\n\tconst { members, tail, open, close } = yield* readObject(source)\n\tconst edits = HashMap.fromIterable(changes)\n\tconst first = Array.head(members)\n\tconst lastOccurrences = HashMap.fromIterable(\n\t\tArray.map(members, (member, index) => [member.name, index] as const),\n\t)\n\tconst retained = Array.flatMap(members, (member, index) =>\n\t\tOption.match(HashMap.get(edits, member.name), {\n\t\t\tonNone: () => [member],\n\t\t\tonSome: (change) =>\n\t\t\t\tOption.match(change, {\n\t\t\t\t\t// Removing every occurrence prevents an earlier value from becoming active.\n\t\t\t\t\tonNone: () => [],\n\t\t\t\t\tonSome: (value) => [\n\t\t\t\t\t\tOption.getOrUndefined(HashMap.get(lastOccurrences, member.name)) ===\n\t\t\t\t\t\tindex\n\t\t\t\t\t\t\t? { ...member, value }\n\t\t\t\t\t\t\t: member,\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t}),\n\t)\n\tconst added = yield* Effect.forEach(\n\t\tArray.filter(changes, ([name]) => !HashMap.has(lastOccurrences, name)),\n\t\tEffect.fnUntraced(function* ([name, change]) {\n\t\t\tif (Option.isNone(change)) return []\n\t\t\tconst key = yield* encodeString(name)\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tname,\n\t\t\t\t\tleading: Option.match(first, {\n\t\t\t\t\t\tonNone: () => tail,\n\t\t\t\t\t\tonSome: (member) => member.leading,\n\t\t\t\t\t}),\n\t\t\t\t\tkey,\n\t\t\t\t\tcolon: Option.match(first, {\n\t\t\t\t\t\tonNone: () => \": \",\n\t\t\t\t\t\tonSome: (member) => member.colon,\n\t\t\t\t\t}),\n\t\t\t\t\tvalue: change.value,\n\t\t\t\t\ttrailing: \"\",\n\t\t\t\t},\n\t\t\t]\n\t\t}),\n\t)\n\treturn (\n\t\tString.slice(0, open + 1)(source) +\n\t\tArray.join(\n\t\t\tArray.map(\n\t\t\t\t[...retained, ...Array.flatten(added)],\n\t\t\t\t(member) =>\n\t\t\t\t\tmember.leading +\n\t\t\t\t\tmember.key +\n\t\t\t\t\tmember.colon +\n\t\t\t\t\tmember.value +\n\t\t\t\t\tmember.trailing,\n\t\t\t),\n\t\t\t\",\",\n\t\t) +\n\t\ttail +\n\t\tString.slice(close)(source)\n\t)\n})\n\nexport const encodeManifest = Effect.fnUntraced(function* (\n\tmanifest: Manifest,\n\toriginal: string,\n) {\n\tconst before = yield* decodeManifest(original)\n\tconst { members } = yield* readObject(original)\n\tconst changes = yield* Effect.reduce(\n\t\tfields,\n\t\t() => Chunk.empty<Edit>(),\n\t\tEffect.fnUntraced(function* (changes, field) {\n\t\t\tconst oldSpecs = HashMap.fromIterable(\n\t\t\t\tRecord.toEntries(before[field] ?? {}),\n\t\t\t)\n\t\t\tconst nextSpecs = HashMap.fromIterable(\n\t\t\t\tRecord.toEntries(manifest[field] ?? {}),\n\t\t\t)\n\t\t\tconst specs = yield* Effect.reduce(\n\t\t\t\tArray.dedupe([\n\t\t\t\t\t...Record.keys(before[field] ?? {}),\n\t\t\t\t\t...Record.keys(manifest[field] ?? {}),\n\t\t\t\t]),\n\t\t\t\t() => Chunk.empty<Edit>(),\n\t\t\t\tEffect.fnUntraced(function* (specs, name) {\n\t\t\t\t\tconst old = HashMap.get(oldSpecs, name)\n\t\t\t\t\tconst next = HashMap.get(nextSpecs, name)\n\t\t\t\t\tif (Option.getOrUndefined(old) === Option.getOrUndefined(next))\n\t\t\t\t\t\treturn specs\n\t\t\t\t\tconst value = yield* Option.match(next, {\n\t\t\t\t\t\tonNone: () => Effect.succeedNone,\n\t\t\t\t\t\tonSome: (value) => encodeString(value).pipe(Effect.asSome),\n\t\t\t\t\t})\n\t\t\t\t\treturn Chunk.append(specs, [name, value] as const)\n\t\t\t\t}),\n\t\t\t)\n\t\t\tif (Chunk.isEmpty(specs)) return changes\n\t\t\tconst section = Array.findLast(members, (member) => member.name === field)\n\t\t\tconst source = Option.match(section, {\n\t\t\t\tonNone: () => \"{}\",\n\t\t\t\tonSome: (member) => member.value,\n\t\t\t})\n\t\t\tconst edited = yield* editObject(source, Chunk.toArray(specs))\n\t\t\treturn Chunk.append(changes, [field, Option.some(edited)] as const)\n\t\t}),\n\t)\n\treturn Chunk.isEmpty(changes)\n\t\t? original\n\t\t: yield* editObject(original, Chunk.toArray(changes))\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"manifest.js","names":[],"sources":["../src/manifest.ts"],"sourcesContent":["import {\n\tArray,\n\tChunk,\n\tEffect,\n\tHashMap,\n\tOption,\n\tRecord,\n\tSchema,\n\tString,\n} from \"effect\"\n\nexport const PackageName = Schema.String.check(\n\tSchema.isPattern(\n\t\t/^(?!(?:node_modules|favicon\\.ico)$)(?:@[a-z0-9-~][a-z0-9-._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*$/i,\n\t),\n\tSchema.isMaxLength(214),\n\tSchema.isTrimmed(),\n)\nconst isPackageName = Schema.is(PackageName)\n// A refined Record key filters unmatched keys. Validate names after retaining\n// every key so invalid input cannot silently become a partial change set.\nconst dependencyNames = Schema.makeFilter(\n\t(record: Readonly<Record<string, unknown>>) =>\n\t\tArray.map(\n\t\t\tArray.filter(Record.keys(record), (name) => !isPackageName(name)),\n\t\t\t(name) => ({\n\t\t\t\tpath: [name],\n\t\t\t\tissue: \"Invalid dependency name\",\n\t\t\t}),\n\t\t),\n)\nconst Dependencies = Schema.Record(Schema.String, Schema.String).check(\n\tdependencyNames,\n)\nexport const Manifest = Schema.StructWithRest(\n\tSchema.Struct({\n\t\tpackageManager: Schema.optionalKey(Schema.String),\n\t\tdependencies: Schema.optionalKey(Dependencies),\n\t\tdevDependencies: Schema.optionalKey(Dependencies),\n\t\toptionalDependencies: Schema.optionalKey(Dependencies),\n\t}),\n\t[Schema.Record(Schema.String, Schema.Unknown)],\n)\nexport type Manifest = typeof Manifest.Type\nconst decodeJsonManifest = Schema.decodeUnknownEffect(\n\tSchema.fromJsonString(Manifest),\n)\n// A BOM is a source-format marker, not part of the JSON value.\nexport const decodeManifest = (source: string) =>\n\tdecodeJsonManifest(String.replace(/^\\uFEFF/, \"\")(source))\n\nexport const Changes = Schema.Record(\n\tSchema.String,\n\tSchema.Union([Schema.String.check(Schema.isPattern(/\\S/)), Schema.Null]),\n).check(dependencyNames)\n/** A spec adds or updates a dependency; null removes it from all dependency fields. */\nexport type Changes = typeof Changes.Type\nexport const fields = [\n\t\"dependencies\",\n\t\"devDependencies\",\n\t\"optionalDependencies\",\n] as const\n\nexport const applyChanges = (\n\tmanifest: Manifest,\n\tchanges: Changes,\n): Manifest => {\n\tconst edits = Record.toEntries(changes)\n\tif (edits.length === 0) return manifest\n\tlet next = manifest\n\tconst additions = Array.flatMap(edits, ([name, spec]) =>\n\t\tspec !== null &&\n\t\t!Array.some(fields, (field) => Record.has(manifest[field] ?? {}, name))\n\t\t\t? [[name, spec] as const]\n\t\t\t: [],\n\t)\n\tfor (const field of fields) {\n\t\tconst entries = Array.flatMap(\n\t\t\tRecord.toEntries(manifest[field] ?? {}),\n\t\t\t([name, old]) => {\n\t\t\t\tconst spec = Record.has(changes, name) ? changes[name]! : old\n\t\t\t\treturn spec === null ? [] : [[name, spec] as const]\n\t\t\t},\n\t\t)\n\t\tif (field === \"dependencies\" && additions.length > 0) {\n\t\t\tnext = {\n\t\t\t\t...next,\n\t\t\t\t[field]: Record.fromEntries([...entries, ...additions]),\n\t\t\t}\n\t\t} else if (manifest[field] !== undefined) {\n\t\t\tnext = { ...next, [field]: Record.fromEntries(entries) }\n\t\t}\n\t}\n\treturn next\n}\n\nconst decodeKey = Schema.decodeEffect(Schema.fromJsonString(Schema.String))\nconst encodeString = Schema.encodeEffect(Schema.fromJsonString(Schema.String))\n\ninterface Member {\n\treadonly name: string\n\treadonly leading: string\n\treadonly key: string\n\treadonly colon: string\n\treadonly value: string\n\treadonly trailing: string\n}\n\n/** Read member spans from a JSON object already validated by decodeManifest. */\nconst readObject = Effect.fnUntraced(function* (source: string) {\n\t// Strings are single tokens, so punctuation inside them cannot affect nesting.\n\tconst tokens = Array.fromIterable(\n\t\tString.matchAll(/\"(?:\\\\[\\s\\S]|[^\"\\\\])*\"|[{}\\[\\]:,]|[^\\s{}\\[\\]:,]+/g)(\n\t\t\tsource,\n\t\t),\n\t)\n\tlet members = Chunk.empty<Member>()\n\tlet cursor = 1\n\tlet start = (tokens[0]?.index ?? 0) + 1\n\twhile (cursor < tokens.length && tokens[cursor]?.[0] !== \"}\") {\n\t\tconst key = tokens[cursor]!\n\t\tconst valueStart = tokens[cursor + 2]!.index\n\t\tcursor += 2\n\t\tlet depth = 0\n\t\tlet end = valueStart\n\t\tdo {\n\t\t\tconst token = tokens[cursor++]!\n\t\t\tif (token[0] === \"{\" || token[0] === \"[\") depth++\n\t\t\tif (token[0] === \"}\" || token[0] === \"]\") depth--\n\t\t\tend = token.index! + token[0].length\n\t\t} while (depth > 0)\n\t\tmembers = Chunk.append(members, {\n\t\t\tname: yield* decodeKey(key[0]),\n\t\t\tleading: String.slice(start, key.index)(source),\n\t\t\tkey: key[0],\n\t\t\tcolon: String.slice(key.index! + key[0].length, valueStart)(source),\n\t\t\tvalue: String.slice(valueStart, end)(source),\n\t\t\ttrailing:\n\t\t\t\ttokens[cursor]?.[0] === \",\"\n\t\t\t\t\t? String.slice(end, tokens[cursor]!.index)(source)\n\t\t\t\t\t: \"\",\n\t\t})\n\t\tstart = end\n\t\tif (tokens[cursor]?.[0] !== \",\") break\n\t\tstart = tokens[cursor++]!.index! + 1\n\t}\n\tconst close = tokens[cursor]!.index\n\tconst tail = String.slice(start, close)(source)\n\treturn {\n\t\tmembers: Chunk.toArray(members),\n\t\ttail,\n\t\topen: tokens[0]!.index!,\n\t\tclose,\n\t}\n})\n\n/** Ordered edits preserve insertion order; None removes every occurrence. */\ntype Edit = readonly [string, Option.Option<string>]\ntype Edits = ReadonlyArray<Edit>\n\nconst editObject = Effect.fnUntraced(function* (\n\tsource: string,\n\tchanges: Edits,\n) {\n\tconst { members, tail, open, close } = yield* readObject(source)\n\tconst edits = HashMap.fromIterable(changes)\n\tconst first = Array.head(members)\n\tconst lastOccurrences = HashMap.fromIterable(\n\t\tArray.map(members, (member, index) => [member.name, index] as const),\n\t)\n\tconst retained = Array.flatMap(members, (member, index) =>\n\t\tOption.match(HashMap.get(edits, member.name), {\n\t\t\tonNone: () => [member],\n\t\t\tonSome: (change) =>\n\t\t\t\tOption.match(change, {\n\t\t\t\t\t// Removing every occurrence prevents an earlier value from becoming active.\n\t\t\t\t\tonNone: () => [],\n\t\t\t\t\tonSome: (value) => [\n\t\t\t\t\t\tOption.getOrUndefined(HashMap.get(lastOccurrences, member.name)) ===\n\t\t\t\t\t\tindex\n\t\t\t\t\t\t\t? { ...member, value }\n\t\t\t\t\t\t\t: member,\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t}),\n\t)\n\tconst added = yield* Effect.forEach(\n\t\tArray.filter(changes, ([name]) => !HashMap.has(lastOccurrences, name)),\n\t\tEffect.fnUntraced(function* ([name, change]) {\n\t\t\tif (Option.isNone(change)) return []\n\t\t\tconst key = yield* encodeString(name)\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tname,\n\t\t\t\t\tleading: Option.match(first, {\n\t\t\t\t\t\tonNone: () => tail,\n\t\t\t\t\t\tonSome: (member) => member.leading,\n\t\t\t\t\t}),\n\t\t\t\t\tkey,\n\t\t\t\t\tcolon: Option.match(first, {\n\t\t\t\t\t\tonNone: () => \": \",\n\t\t\t\t\t\tonSome: (member) => member.colon,\n\t\t\t\t\t}),\n\t\t\t\t\tvalue: change.value,\n\t\t\t\t\ttrailing: \"\",\n\t\t\t\t},\n\t\t\t]\n\t\t}),\n\t)\n\treturn (\n\t\tString.slice(0, open + 1)(source) +\n\t\tArray.join(\n\t\t\tArray.map(\n\t\t\t\t[...retained, ...Array.flatten(added)],\n\t\t\t\t(member) =>\n\t\t\t\t\tmember.leading +\n\t\t\t\t\tmember.key +\n\t\t\t\t\tmember.colon +\n\t\t\t\t\tmember.value +\n\t\t\t\t\tmember.trailing,\n\t\t\t),\n\t\t\t\",\",\n\t\t) +\n\t\ttail +\n\t\tString.slice(close)(source)\n\t)\n})\n\nexport const encodeManifest = Effect.fnUntraced(function* (\n\tmanifest: Manifest,\n\toriginal: string,\n) {\n\tconst before = yield* decodeManifest(original)\n\tconst { members } = yield* readObject(original)\n\tconst changes = yield* Effect.reduce(\n\t\tfields,\n\t\t() => Chunk.empty<Edit>(),\n\t\tEffect.fnUntraced(function* (changes, field) {\n\t\t\tconst oldSpecs = HashMap.fromIterable(\n\t\t\t\tRecord.toEntries(before[field] ?? {}),\n\t\t\t)\n\t\t\tconst nextSpecs = HashMap.fromIterable(\n\t\t\t\tRecord.toEntries(manifest[field] ?? {}),\n\t\t\t)\n\t\t\tconst specs = yield* Effect.reduce(\n\t\t\t\tArray.dedupe([\n\t\t\t\t\t...Record.keys(before[field] ?? {}),\n\t\t\t\t\t...Record.keys(manifest[field] ?? {}),\n\t\t\t\t]),\n\t\t\t\t() => Chunk.empty<Edit>(),\n\t\t\t\tEffect.fnUntraced(function* (specs, name) {\n\t\t\t\t\tconst old = HashMap.get(oldSpecs, name)\n\t\t\t\t\tconst next = HashMap.get(nextSpecs, name)\n\t\t\t\t\tif (Option.getOrUndefined(old) === Option.getOrUndefined(next))\n\t\t\t\t\t\treturn specs\n\t\t\t\t\tconst value = yield* Option.match(next, {\n\t\t\t\t\t\tonNone: () => Effect.succeedNone,\n\t\t\t\t\t\tonSome: (value) => encodeString(value).pipe(Effect.asSome),\n\t\t\t\t\t})\n\t\t\t\t\treturn Chunk.append(specs, [name, value] as const)\n\t\t\t\t}),\n\t\t\t)\n\t\t\tif (Chunk.isEmpty(specs)) return changes\n\t\t\tconst section = Array.findLast(members, (member) => member.name === field)\n\t\t\tconst source = Option.match(section, {\n\t\t\t\tonNone: () => \"{}\",\n\t\t\t\tonSome: (member) => member.value,\n\t\t\t})\n\t\t\tconst edited = yield* editObject(source, Chunk.toArray(specs))\n\t\t\treturn Chunk.append(changes, [field, Option.some(edited)] as const)\n\t\t}),\n\t)\n\treturn Chunk.isEmpty(changes)\n\t\t? original\n\t\t: yield* editObject(original, Chunk.toArray(changes))\n})\n"],"mappings":";;AAWA,MAAa,cAAc,OAAO,OAAO,MACxC,OAAO,UACN,6FACD,GACA,OAAO,YAAY,GAAG,GACtB,OAAO,UAAU,CAClB;AACA,MAAM,gBAAgB,OAAO,GAAG,WAAW;AAG3C,MAAM,kBAAkB,OAAO,YAC7B,WACA,MAAM,IACL,MAAM,OAAO,OAAO,KAAK,MAAM,IAAI,SAAS,CAAC,cAAc,IAAI,CAAC,IAC/D,UAAU;CACV,MAAM,CAAC,IAAI;CACX,OAAO;AACR,EACD,CACF;AACA,MAAM,eAAe,OAAO,OAAO,OAAO,QAAQ,OAAO,MAAM,CAAC,CAAC,MAChE,eACD;AACA,MAAa,WAAW,OAAO,eAC9B,OAAO,OAAO;CACb,gBAAgB,OAAO,YAAY,OAAO,MAAM;CAChD,cAAc,OAAO,YAAY,YAAY;CAC7C,iBAAiB,OAAO,YAAY,YAAY;CAChD,sBAAsB,OAAO,YAAY,YAAY;AACtD,CAAC,GACD,CAAC,OAAO,OAAO,OAAO,QAAQ,OAAO,OAAO,CAAC,CAC9C;AAEA,MAAM,qBAAqB,OAAO,oBACjC,OAAO,eAAe,QAAQ,CAC/B;AAEA,MAAa,kBAAkB,WAC9B,mBAAmB,OAAO,QAAQ,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;AAEzD,MAAa,UAAU,OAAO,OAC7B,OAAO,QACP,OAAO,MAAM,CAAC,OAAO,OAAO,MAAM,OAAO,UAAU,IAAI,CAAC,GAAG,OAAO,IAAI,CAAC,CACxE,CAAC,CAAC,MAAM,eAAe;AAGvB,MAAa,SAAS;CACrB;CACA;CACA;AACD;AAEA,MAAa,gBACZ,UACA,YACc;CACd,MAAM,QAAQ,OAAO,UAAU,OAAO;CACtC,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,IAAI,OAAO;CACX,MAAM,YAAY,MAAM,QAAQ,QAAQ,CAAC,MAAM,UAC9C,SAAS,QACT,CAAC,MAAM,KAAK,SAAS,UAAU,OAAO,IAAI,SAAS,UAAU,CAAC,GAAG,IAAI,CAAC,IACnE,CAAC,CAAC,MAAM,IAAI,CAAU,IACtB,CAAC,CACL;CACA,KAAK,MAAM,SAAS,QAAQ;EAC3B,MAAM,UAAU,MAAM,QACrB,OAAO,UAAU,SAAS,UAAU,CAAC,CAAC,IACrC,CAAC,MAAM,SAAS;GAChB,MAAM,OAAO,OAAO,IAAI,SAAS,IAAI,IAAI,QAAQ,QAAS;GAC1D,OAAO,SAAS,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAU;EACnD,CACD;EACA,IAAI,UAAU,kBAAkB,UAAU,SAAS,GAClD,OAAO;GACN,GAAG;IACF,QAAQ,OAAO,YAAY,CAAC,GAAG,SAAS,GAAG,SAAS,CAAC;EACvD;OACM,IAAI,SAAS,WAAW,KAAA,GAC9B,OAAO;GAAE,GAAG;IAAO,QAAQ,OAAO,YAAY,OAAO;EAAE;CAEzD;CACA,OAAO;AACR;AAEA,MAAM,YAAY,OAAO,aAAa,OAAO,eAAe,OAAO,MAAM,CAAC;AAC1E,MAAM,eAAe,OAAO,aAAa,OAAO,eAAe,OAAO,MAAM,CAAC;;AAY7E,MAAM,aAAa,OAAO,WAAW,WAAW,QAAgB;CAE/D,MAAM,SAAS,MAAM,aACpB,OAAO,SAAS,mDAAmD,CAAC,CACnE,MACD,CACD;CACA,IAAI,UAAU,MAAM,MAAc;CAClC,IAAI,SAAS;CACb,IAAI,SAAS,OAAO,EAAE,EAAE,SAAS,KAAK;CACtC,OAAO,SAAS,OAAO,UAAU,OAAO,OAAO,GAAG,OAAO,KAAK;EAC7D,MAAM,MAAM,OAAO;EACnB,MAAM,aAAa,OAAO,SAAS,EAAE,CAAE;EACvC,UAAU;EACV,IAAI,QAAQ;EACZ,IAAI,MAAM;EACV,GAAG;GACF,MAAM,QAAQ,OAAO;GACrB,IAAI,MAAM,OAAO,OAAO,MAAM,OAAO,KAAK;GAC1C,IAAI,MAAM,OAAO,OAAO,MAAM,OAAO,KAAK;GAC1C,MAAM,MAAM,QAAS,MAAM,EAAE,CAAC;EAC/B,SAAS,QAAQ;EACjB,UAAU,MAAM,OAAO,SAAS;GAC/B,MAAM,OAAO,UAAU,IAAI,EAAE;GAC7B,SAAS,OAAO,MAAM,OAAO,IAAI,KAAK,CAAC,CAAC,MAAM;GAC9C,KAAK,IAAI;GACT,OAAO,OAAO,MAAM,IAAI,QAAS,IAAI,EAAE,CAAC,QAAQ,UAAU,CAAC,CAAC,MAAM;GAClE,OAAO,OAAO,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM;GAC3C,UACC,OAAO,OAAO,GAAG,OAAO,MACrB,OAAO,MAAM,KAAK,OAAO,OAAO,CAAE,KAAK,CAAC,CAAC,MAAM,IAC/C;EACL,CAAC;EACD,QAAQ;EACR,IAAI,OAAO,OAAO,GAAG,OAAO,KAAK;EACjC,QAAQ,OAAO,SAAS,CAAE,QAAS;CACpC;CACA,MAAM,QAAQ,OAAO,OAAO,CAAE;CAC9B,MAAM,OAAO,OAAO,MAAM,OAAO,KAAK,CAAC,CAAC,MAAM;CAC9C,OAAO;EACN,SAAS,MAAM,QAAQ,OAAO;EAC9B;EACA,MAAM,OAAO,EAAE,CAAE;EACjB;CACD;AACD,CAAC;AAMD,MAAM,aAAa,OAAO,WAAW,WACpC,QACA,SACC;CACD,MAAM,EAAE,SAAS,MAAM,MAAM,UAAU,OAAO,WAAW,MAAM;CAC/D,MAAM,QAAQ,QAAQ,aAAa,OAAO;CAC1C,MAAM,QAAQ,MAAM,KAAK,OAAO;CAChC,MAAM,kBAAkB,QAAQ,aAC/B,MAAM,IAAI,UAAU,QAAQ,UAAU,CAAC,OAAO,MAAM,KAAK,CAAU,CACpE;CACA,MAAM,WAAW,MAAM,QAAQ,UAAU,QAAQ,UAChD,OAAO,MAAM,QAAQ,IAAI,OAAO,OAAO,IAAI,GAAG;EAC7C,cAAc,CAAC,MAAM;EACrB,SAAS,WACR,OAAO,MAAM,QAAQ;GAEpB,cAAc,CAAC;GACf,SAAS,UAAU,CAClB,OAAO,eAAe,QAAQ,IAAI,iBAAiB,OAAO,IAAI,CAAC,MAC/D,QACG;IAAE,GAAG;IAAQ;GAAM,IACnB,MACJ;EACD,CAAC;CACH,CAAC,CACF;CACA,MAAM,QAAQ,OAAO,OAAO,QAC3B,MAAM,OAAO,UAAU,CAAC,UAAU,CAAC,QAAQ,IAAI,iBAAiB,IAAI,CAAC,GACrE,OAAO,WAAW,WAAW,CAAC,MAAM,SAAS;EAC5C,IAAI,OAAO,OAAO,MAAM,GAAG,OAAO,CAAC;EACnC,MAAM,MAAM,OAAO,aAAa,IAAI;EACpC,OAAO,CACN;GACC;GACA,SAAS,OAAO,MAAM,OAAO;IAC5B,cAAc;IACd,SAAS,WAAW,OAAO;GAC5B,CAAC;GACD;GACA,OAAO,OAAO,MAAM,OAAO;IAC1B,cAAc;IACd,SAAS,WAAW,OAAO;GAC5B,CAAC;GACD,OAAO,OAAO;GACd,UAAU;EACX,CACD;CACD,CAAC,CACF;CACA,OACC,OAAO,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,IAChC,MAAM,KACL,MAAM,IACL,CAAC,GAAG,UAAU,GAAG,MAAM,QAAQ,KAAK,CAAC,IACpC,WACA,OAAO,UACP,OAAO,MACP,OAAO,QACP,OAAO,QACP,OAAO,QACT,GACA,GACD,IACA,OACA,OAAO,MAAM,KAAK,CAAC,CAAC,MAAM;AAE5B,CAAC;AAED,MAAa,iBAAiB,OAAO,WAAW,WAC/C,UACA,UACC;CACD,MAAM,SAAS,OAAO,eAAe,QAAQ;CAC7C,MAAM,EAAE,YAAY,OAAO,WAAW,QAAQ;CAC9C,MAAM,UAAU,OAAO,OAAO,OAC7B,cACM,MAAM,MAAY,GACxB,OAAO,WAAW,WAAW,SAAS,OAAO;EAC5C,MAAM,WAAW,QAAQ,aACxB,OAAO,UAAU,OAAO,UAAU,CAAC,CAAC,CACrC;EACA,MAAM,YAAY,QAAQ,aACzB,OAAO,UAAU,SAAS,UAAU,CAAC,CAAC,CACvC;EACA,MAAM,QAAQ,OAAO,OAAO,OAC3B,MAAM,OAAO,CACZ,GAAG,OAAO,KAAK,OAAO,UAAU,CAAC,CAAC,GAClC,GAAG,OAAO,KAAK,SAAS,UAAU,CAAC,CAAC,CACrC,CAAC,SACK,MAAM,MAAY,GACxB,OAAO,WAAW,WAAW,OAAO,MAAM;GACzC,MAAM,MAAM,QAAQ,IAAI,UAAU,IAAI;GACtC,MAAM,OAAO,QAAQ,IAAI,WAAW,IAAI;GACxC,IAAI,OAAO,eAAe,GAAG,MAAM,OAAO,eAAe,IAAI,GAC5D,OAAO;GACR,MAAM,QAAQ,OAAO,OAAO,MAAM,MAAM;IACvC,cAAc,OAAO;IACrB,SAAS,UAAU,aAAa,KAAK,CAAC,CAAC,KAAK,OAAO,MAAM;GAC1D,CAAC;GACD,OAAO,MAAM,OAAO,OAAO,CAAC,MAAM,KAAK,CAAU;EAClD,CAAC,CACF;EACA,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO;EACjC,MAAM,UAAU,MAAM,SAAS,UAAU,WAAW,OAAO,SAAS,KAAK;EACzE,MAAM,SAAS,OAAO,MAAM,SAAS;GACpC,cAAc;GACd,SAAS,WAAW,OAAO;EAC5B,CAAC;EACD,MAAM,SAAS,OAAO,WAAW,QAAQ,MAAM,QAAQ,KAAK,CAAC;EAC7D,OAAO,MAAM,OAAO,SAAS,CAAC,OAAO,OAAO,KAAK,MAAM,CAAC,CAAU;CACnE,CAAC,CACF;CACA,OAAO,MAAM,QAAQ,OAAO,IACzB,WACA,OAAO,WAAW,UAAU,MAAM,QAAQ,OAAO,CAAC;AACtD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"process.js","names":[],"sources":["../src/process.ts"],"sourcesContent":["// The spawner exposes no process-group existence check after kill loses errno.\nimport * as NodeProcess from \"node:process\"\nimport {\n\tArray,\n\tCause,\n\tData,\n\tEffect,\n\tExit,\n\tPlatformError,\n\tSchedule,\n\tSchema,\n} from \"effect\"\nimport type { ChildProcess, ChildProcessSpawner } from \"effect/process\"\n\nconst missingGroup = Schema.is(Schema.Struct({ code: Schema.Literal(\"ESRCH\") }))\nconst unsignallableGroup = Schema.is(\n\tSchema.Struct({ code: Schema.Literal(\"EPERM\") }),\n)\n// Node's kill(false) fallback loses errno, but has a specific signal-failure shape.\nconst failedSignal = Schema.is(\n\tSchema.Struct({ message: Schema.Literal(\"Failed to kill child process\") }),\n)\nconst isSignalFailure = (error: PlatformError.PlatformError) =>\n\terror.reason.module === \"ChildProcess\" &&\n\terror.reason.method === \"kill\" &&\n\t(missingGroup(error.cause) ||\n\t\t(error.reason._tag === \"Unknown\" && failedSignal(error.cause)))\n\n/** Signal zero checks the group without signalling any member. */\nexport const groupExists = (pid: ChildProcessSpawner.ProcessId) =>\n\tEffect.try({\n\t\ttry: () => {\n\t\t\tNodeProcess.kill(-pid, 0)\n\t\t\treturn true\n\t\t},\n\t\tcatch: (cause) =>\n\t\t\tPlatformError.systemError({\n\t\t\t\t_tag: \"Unknown\",\n\t\t\t\tmodule: \"ChildProcess\",\n\t\t\t\tmethod: \"probeGroup\",\n\t\t\t\tcause,\n\t\t\t}),\n\t}).pipe(\n\t\tEffect.catchIf(\n\t\t\t(error) => missingGroup(error.cause) || unsignallableGroup(error.cause),\n\t\t\t// EPERM can mean zombies on macOS. It never establishes group absence.\n\t\t\t(error) => Effect.succeed(!missingGroup(error.cause)),\n\t\t),\n\t)\n\n/** Close the original group, including descendants that remain in it after leader exit. */\nexport const closeProcess = Effect.fnUntraced(function* (\n\tprocess: ChildProcessSpawner.ChildProcessHandle,\n\toptions: ChildProcess.KillOptions,\n\tprobe: typeof groupExists = groupExists,\n) {\n\tconst killed = yield* Effect.suspend(() => process.kill(options)).pipe(\n\t\tEffect.exit,\n\t)\n\t// Windows retains its as-is adapter contract; POSIX requires group evidence.\n\tif (NodeProcess.platform === \"win32\") return yield* killed\n\tconst checked = yield* Effect.suspend(() => probe(process.pid)).pipe(\n\t\tEffect.exit,\n\t\tEffect.repeat({\n\t\t\twhile: (exit) => Exit.isSuccess(exit) && exit.value,\n\t\t\ttimes: 100,\n\t\t\tschedule: Schedule.spaced(\"20 millis\"),\n\t\t}),\n\t)\n\tif (Exit.isFailure(checked))\n\t\treturn yield* Effect.failCause(\n\t\t\tExit.isFailure(killed)\n\t\t\t\t? Cause.combine(killed.cause, checked.cause)\n\t\t\t\t: checked.cause,\n\t\t)\n\tif (checked.value) {\n\t\tif (Exit.isFailure(killed)) return yield* Effect.failCause(killed.cause)\n\t\treturn yield* PlatformError.systemError({\n\t\t\t_tag: \"Unknown\",\n\t\t\tmodule: \"ChildProcess\",\n\t\t\tmethod: \"confirmGroupStopped\",\n\t\t\tdescription: `Process group ${process.pid} still exists after the two-second confirmation window`,\n\t\t})\n\t}\n\tif (Exit.isFailure(killed)) {\n\t\t// Absence resolves only a failed signal to a nonexistent group, not other cleanup failures.\n\t\tconst remaining = Array.filter(\n\t\t\tkilled.cause.reasons,\n\t\t\t(reason) =>\n\t\t\t\t!(Cause.isFailReason(reason) && isSignalFailure(reason.error)),\n\t\t)\n\t\tif (remaining.length > 0)\n\t\t\treturn yield* Effect.failCause(Cause.fromReasons(remaining))\n\t}\n})\n\nexport type Termination = Data.TaggedEnum<{\n\tOriginalGroupStopped: {}\n\tUnconfirmed: { readonly cause: Cause.Cause<PlatformError.PlatformError> }\n}>\n\nexport const Termination = Data.taggedEnum<Termination>()\n\nexport type OriginalGroupStopped = Data.TaggedEnum.Value<\n\tTermination,\n\t\"OriginalGroupStopped\"\n>\n\n/** Only proves termination of the original group, not detached groups or daemons. */\nexport const terminateProcess = (\n\tprocess: ChildProcessSpawner.ChildProcessHandle,\n\toptions: ChildProcess.KillOptions,\n) =>\n\tEffect.suspend(() => closeProcess(process, options)).pipe(\n\t\tEffect.exit,\n\t\tEffect.map((exit): Termination =>\n\t\t\tExit.isSuccess(exit)\n\t\t\t\t? Termination.OriginalGroupStopped()\n\t\t\t\t: Termination.Unconfirmed({ cause: exit.cause }),\n\t\t),\n\t)\n"],"mappings":";;;AAcA,MAAM,eAAe,OAAO,GAAG,OAAO,OAAO,EAAE,MAAM,OAAO,QAAQ,OAAO,EAAE,CAAC,CAAC;AAC/E,MAAM,qBAAqB,OAAO,GACjC,OAAO,OAAO,EAAE,MAAM,OAAO,QAAQ,OAAO,EAAE,CAAC,CAChD;AAEA,MAAM,eAAe,OAAO,GAC3B,OAAO,OAAO,EAAE,SAAS,OAAO,QAAQ,8BAA8B,EAAE,CAAC,CAC1E;AACA,MAAM,mBAAmB,UACxB,MAAM,OAAO,WAAW,kBACxB,MAAM,OAAO,WAAW,WACvB,aAAa,MAAM,KAAK,KACvB,MAAM,OAAO,SAAS,aAAa,aAAa,MAAM,KAAK;;AAG9D,MAAa,eAAe,QAC3B,OAAO,IAAI;CACV,WAAW;EACV,YAAY,KAAK,CAAC,KAAK,CAAC;EACxB,OAAO;CACR;CACA,QAAQ,UACP,cAAc,YAAY;EACzB,MAAM;EACN,QAAQ;EACR,QAAQ;EACR;CACD,CAAC;AACH,CAAC,CAAC,CAAC,KACF,OAAO,SACL,UAAU,aAAa,MAAM,KAAK,KAAK,mBAAmB,MAAM,KAAK,IAErE,UAAU,OAAO,QAAQ,CAAC,aAAa,MAAM,KAAK,CAAC,CACrD,CACD;;AAGD,MAAa,eAAe,OAAO,WAAW,WAC7C,SACA,SACA,QAA4B,aAC3B;CACD,MAAM,SAAS,OAAO,OAAO,cAAc,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,KACjE,OAAO,IACR;CAEA,IAAI,YAAY,aAAa,SAAS,OAAO,OAAO;CACpD,MAAM,UAAU,OAAO,OAAO,cAAc,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,KAC/D,OAAO,MACP,OAAO,OAAO;EACb,QAAQ,SAAS,KAAK,UAAU,IAAI,KAAK,KAAK;EAC9C,OAAO;EACP,UAAU,SAAS,OAAO,WAAW;CACtC,CAAC,CACF;CACA,IAAI,KAAK,UAAU,OAAO,GACzB,OAAO,OAAO,OAAO,UACpB,KAAK,UAAU,MAAM,IAClB,MAAM,QAAQ,OAAO,OAAO,QAAQ,KAAK,IACzC,QAAQ,KACZ;CACD,IAAI,QAAQ,OAAO;EAClB,IAAI,KAAK,UAAU,MAAM,GAAG,OAAO,OAAO,OAAO,UAAU,OAAO,KAAK;EACvE,OAAO,OAAO,cAAc,YAAY;GACvC,MAAM;GACN,QAAQ;GACR,QAAQ;GACR,aAAa,iBAAiB,QAAQ,IAAI;EAC3C,CAAC;CACF;CACA,IAAI,KAAK,UAAU,MAAM,GAAG;EAE3B,MAAM,YAAY,MAAM,OACvB,OAAO,MAAM,UACZ,WACA,EAAE,MAAM,aAAa,MAAM,KAAK,gBAAgB,OAAO,KAAK,EAC9D;EACA,IAAI,UAAU,SAAS,GACtB,OAAO,OAAO,OAAO,UAAU,MAAM,YAAY,SAAS,CAAC;CAC7D;AACD,CAAC;AAOD,MAAa,cAAc,KAAK,WAAwB;;AAQxD,MAAa,oBACZ,SACA,YAEA,OAAO,cAAc,aAAa,SAAS,OAAO,CAAC,CAAC,CAAC,KACpD,OAAO,MACP,OAAO,KAAK,SACX,KAAK,UAAU,IAAI,IAChB,YAAY,qBAAqB,IACjC,YAAY,YAAY,EAAE,OAAO,KAAK,MAAM,CAAC,CACjD,CACD"}
1
+ {"version":3,"file":"process.js","names":[],"sources":["../src/process.ts"],"sourcesContent":["// The spawner exposes no process-group existence check after kill loses errno.\nimport * as NodeProcess from \"node:process\"\n\nimport {\n\tArray,\n\tCause,\n\tData,\n\tEffect,\n\tExit,\n\tPlatformError,\n\tSchedule,\n\tSchema,\n} from \"effect\"\nimport type { ChildProcess, ChildProcessSpawner } from \"effect/process\"\n\nconst missingGroup = Schema.is(Schema.Struct({ code: Schema.Literal(\"ESRCH\") }))\nconst unsignallableGroup = Schema.is(\n\tSchema.Struct({ code: Schema.Literal(\"EPERM\") }),\n)\n// Node's kill(false) fallback loses errno, but has a specific signal-failure shape.\nconst failedSignal = Schema.is(\n\tSchema.Struct({ message: Schema.Literal(\"Failed to kill child process\") }),\n)\nconst isSignalFailure = (error: PlatformError.PlatformError) =>\n\terror.reason.module === \"ChildProcess\" &&\n\terror.reason.method === \"kill\" &&\n\t(missingGroup(error.cause) ||\n\t\t(error.reason._tag === \"Unknown\" && failedSignal(error.cause)))\n\n/** Signal zero checks the group without signalling any member. */\nexport const groupExists = (pid: ChildProcessSpawner.ProcessId) =>\n\tEffect.try({\n\t\ttry: () => {\n\t\t\tNodeProcess.kill(-pid, 0)\n\t\t\treturn true\n\t\t},\n\t\tcatch: (cause) =>\n\t\t\tPlatformError.systemError({\n\t\t\t\t_tag: \"Unknown\",\n\t\t\t\tmodule: \"ChildProcess\",\n\t\t\t\tmethod: \"probeGroup\",\n\t\t\t\tcause,\n\t\t\t}),\n\t}).pipe(\n\t\tEffect.catchIf(\n\t\t\t(error) => missingGroup(error.cause) || unsignallableGroup(error.cause),\n\t\t\t// EPERM can mean zombies on macOS. It never establishes group absence.\n\t\t\t(error) => Effect.succeed(!missingGroup(error.cause)),\n\t\t),\n\t)\n\n/** Close the original group, including descendants that remain in it after leader exit. */\nexport const closeProcess = Effect.fnUntraced(function* (\n\tprocess: ChildProcessSpawner.ChildProcessHandle,\n\toptions: ChildProcess.KillOptions,\n\tprobe: typeof groupExists = groupExists,\n) {\n\tconst killed = yield* Effect.suspend(() => process.kill(options)).pipe(\n\t\tEffect.exit,\n\t)\n\t// Windows retains its as-is adapter contract; POSIX requires group evidence.\n\tif (NodeProcess.platform === \"win32\") return yield* killed\n\tconst checked = yield* Effect.suspend(() => probe(process.pid)).pipe(\n\t\tEffect.exit,\n\t\tEffect.repeat({\n\t\t\twhile: (exit) => Exit.isSuccess(exit) && exit.value,\n\t\t\ttimes: 100,\n\t\t\tschedule: Schedule.spaced(\"20 millis\"),\n\t\t}),\n\t)\n\tif (Exit.isFailure(checked))\n\t\treturn yield* Effect.failCause(\n\t\t\tExit.isFailure(killed)\n\t\t\t\t? Cause.combine(killed.cause, checked.cause)\n\t\t\t\t: checked.cause,\n\t\t)\n\tif (checked.value) {\n\t\tif (Exit.isFailure(killed)) return yield* Effect.failCause(killed.cause)\n\t\treturn yield* PlatformError.systemError({\n\t\t\t_tag: \"Unknown\",\n\t\t\tmodule: \"ChildProcess\",\n\t\t\tmethod: \"confirmGroupStopped\",\n\t\t\tdescription: `Process group ${process.pid} still exists after the two-second confirmation window`,\n\t\t})\n\t}\n\tif (Exit.isFailure(killed)) {\n\t\t// Absence resolves only a failed signal to a nonexistent group, not other cleanup failures.\n\t\tconst remaining = Array.filter(\n\t\t\tkilled.cause.reasons,\n\t\t\t(reason) =>\n\t\t\t\t!(Cause.isFailReason(reason) && isSignalFailure(reason.error)),\n\t\t)\n\t\tif (remaining.length > 0)\n\t\t\treturn yield* Effect.failCause(Cause.fromReasons(remaining))\n\t}\n})\n\nexport type Termination = Data.TaggedEnum<{\n\tOriginalGroupStopped: {}\n\tUnconfirmed: { readonly cause: Cause.Cause<PlatformError.PlatformError> }\n}>\n\nexport const Termination = Data.taggedEnum<Termination>()\n\nexport type OriginalGroupStopped = Data.TaggedEnum.Value<\n\tTermination,\n\t\"OriginalGroupStopped\"\n>\n\n/** Only proves termination of the original group, not detached groups or daemons. */\nexport const terminateProcess = (\n\tprocess: ChildProcessSpawner.ChildProcessHandle,\n\toptions: ChildProcess.KillOptions,\n) =>\n\tEffect.suspend(() => closeProcess(process, options)).pipe(\n\t\tEffect.exit,\n\t\tEffect.map((exit): Termination =>\n\t\t\tExit.isSuccess(exit)\n\t\t\t\t? Termination.OriginalGroupStopped()\n\t\t\t\t: Termination.Unconfirmed({ cause: exit.cause }),\n\t\t),\n\t)\n"],"mappings":";;;AAeA,MAAM,eAAe,OAAO,GAAG,OAAO,OAAO,EAAE,MAAM,OAAO,QAAQ,OAAO,EAAE,CAAC,CAAC;AAC/E,MAAM,qBAAqB,OAAO,GACjC,OAAO,OAAO,EAAE,MAAM,OAAO,QAAQ,OAAO,EAAE,CAAC,CAChD;AAEA,MAAM,eAAe,OAAO,GAC3B,OAAO,OAAO,EAAE,SAAS,OAAO,QAAQ,8BAA8B,EAAE,CAAC,CAC1E;AACA,MAAM,mBAAmB,UACxB,MAAM,OAAO,WAAW,kBACxB,MAAM,OAAO,WAAW,WACvB,aAAa,MAAM,KAAK,KACvB,MAAM,OAAO,SAAS,aAAa,aAAa,MAAM,KAAK;;AAG9D,MAAa,eAAe,QAC3B,OAAO,IAAI;CACV,WAAW;EACV,YAAY,KAAK,CAAC,KAAK,CAAC;EACxB,OAAO;CACR;CACA,QAAQ,UACP,cAAc,YAAY;EACzB,MAAM;EACN,QAAQ;EACR,QAAQ;EACR;CACD,CAAC;AACH,CAAC,CAAC,CAAC,KACF,OAAO,SACL,UAAU,aAAa,MAAM,KAAK,KAAK,mBAAmB,MAAM,KAAK,IAErE,UAAU,OAAO,QAAQ,CAAC,aAAa,MAAM,KAAK,CAAC,CACrD,CACD;;AAGD,MAAa,eAAe,OAAO,WAAW,WAC7C,SACA,SACA,QAA4B,aAC3B;CACD,MAAM,SAAS,OAAO,OAAO,cAAc,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,KACjE,OAAO,IACR;CAEA,IAAI,YAAY,aAAa,SAAS,OAAO,OAAO;CACpD,MAAM,UAAU,OAAO,OAAO,cAAc,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,KAC/D,OAAO,MACP,OAAO,OAAO;EACb,QAAQ,SAAS,KAAK,UAAU,IAAI,KAAK,KAAK;EAC9C,OAAO;EACP,UAAU,SAAS,OAAO,WAAW;CACtC,CAAC,CACF;CACA,IAAI,KAAK,UAAU,OAAO,GACzB,OAAO,OAAO,OAAO,UACpB,KAAK,UAAU,MAAM,IAClB,MAAM,QAAQ,OAAO,OAAO,QAAQ,KAAK,IACzC,QAAQ,KACZ;CACD,IAAI,QAAQ,OAAO;EAClB,IAAI,KAAK,UAAU,MAAM,GAAG,OAAO,OAAO,OAAO,UAAU,OAAO,KAAK;EACvE,OAAO,OAAO,cAAc,YAAY;GACvC,MAAM;GACN,QAAQ;GACR,QAAQ;GACR,aAAa,iBAAiB,QAAQ,IAAI;EAC3C,CAAC;CACF;CACA,IAAI,KAAK,UAAU,MAAM,GAAG;EAE3B,MAAM,YAAY,MAAM,OACvB,OAAO,MAAM,UACZ,WACA,EAAE,MAAM,aAAa,MAAM,KAAK,gBAAgB,OAAO,KAAK,EAC9D;EACA,IAAI,UAAU,SAAS,GACtB,OAAO,OAAO,OAAO,UAAU,MAAM,YAAY,SAAS,CAAC;CAC7D;AACD,CAAC;AAOD,MAAa,cAAc,KAAK,WAAwB;;AAQxD,MAAa,oBACZ,SACA,YAEA,OAAO,cAAc,aAAa,SAAS,OAAO,CAAC,CAAC,CAAC,KACpD,OAAO,MACP,OAAO,KAAK,SACX,KAAK,UAAU,IAAI,IAChB,YAAY,qBAAqB,IACjC,YAAY,YAAY,EAAE,OAAO,KAAK,MAAM,CAAC,CACjD,CACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.d.ts","names":[],"sources":["../src/snapshot.ts"],"mappings":";iBAiBiB;WACP;WACA,OAAO,MAAM,MAAM,cAAc;;;;;;;WAoDxB;;qBAFN,iCAAiC"}
1
+ {"version":3,"file":"snapshot.d.ts","names":[],"sources":["../src/snapshot.ts"],"mappings":";iBAoBiB;WACP;WACA,OAAO,MAAM,MAAM,cAAc;;;;;;;WAoDxB;;qBAFN,iCAAiC"}
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.js","names":[],"sources":["../src/snapshot.ts"],"sourcesContent":["import {\n\tArray,\n\tCause,\n\tEffect,\n\tEqual,\n\tExit,\n\tOption,\n\tRandom,\n\tSchema,\n\tScope,\n} from \"effect\"\nimport type { FileSystem, Path, PlatformError } from \"effect\"\n/** File writes have completed and the original installer group is stopped or absent. */\nexport interface ManagedWritersIdle {\n\treadonly _tag: \"ManagedWritersIdle\"\n}\n\nexport interface SnapshotCleanupFailure {\n\treadonly snapshotDirectory: string\n\treadonly cause: Cause.Cause<PlatformError.PlatformError>\n}\n\n/** Separate the transaction's outcome from disposal of its snapshot. */\nexport const withSnapshot = <A, E, R>(\n\tfs: FileSystem.FileSystem,\n\tcanDelete: Effect.Effect<boolean>,\n\tuse: (directory: string) => Effect.Effect<A, E, R>,\n) =>\n\tEffect.uninterruptibleMask((resume) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst snapshotDirectory = yield* fs.makeTempDirectory({\n\t\t\t\tprefix: \"graft-recovery-\",\n\t\t\t})\n\t\t\tconst outcome = yield* resume(\n\t\t\t\tEffect.suspend(() => use(snapshotDirectory)),\n\t\t\t).pipe(Effect.exit)\n\t\t\tconst cleanup = (yield* canDelete)\n\t\t\t\t? yield* Effect.suspend(() =>\n\t\t\t\t\t\tfs.remove(snapshotDirectory, { recursive: true }),\n\t\t\t\t\t).pipe(Effect.exit)\n\t\t\t\t: Exit.void\n\t\t\tif (Exit.isFailure(outcome)) {\n\t\t\t\treturn yield* Effect.failCause(\n\t\t\t\t\tExit.isFailure(cleanup)\n\t\t\t\t\t\t? Cause.combine(outcome.cause, cleanup.cause)\n\t\t\t\t\t\t: outcome.cause,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn yield* resume(\n\t\t\t\tEffect.succeed({\n\t\t\t\t\tvalue: outcome.value,\n\t\t\t\t\tsnapshotCleanupFailure: Exit.isFailure(cleanup)\n\t\t\t\t\t\t? Option.some<SnapshotCleanupFailure>({\n\t\t\t\t\t\t\t\tsnapshotDirectory,\n\t\t\t\t\t\t\t\tcause: cleanup.cause,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: Option.none<SnapshotCleanupFailure>(),\n\t\t\t\t}),\n\t\t\t).pipe(\n\t\t\t\t// Cancellation can prevent delivery, but must not hide a cleanup failure.\n\t\t\t\tEffect.onExit((exit) =>\n\t\t\t\t\tExit.isFailure(exit) && Exit.isFailure(cleanup)\n\t\t\t\t\t\t? Effect.failCause(cleanup.cause)\n\t\t\t\t\t\t: Effect.void,\n\t\t\t\t),\n\t\t\t)\n\t\t}),\n\t)\n\nexport class UnsupportedFileTypeError extends Schema.TaggedError<\n\tUnsupportedFileTypeError,\n\t{ readonly brand: unique symbol }\n>()(\"UnsupportedFileTypeError\", {\n\tpath: Schema.String,\n\tfileType: Schema.String,\n\tmessage: Schema.String,\n}) {}\n\ninterface RegularFileMetadata {\n\treadonly mode: number\n\treadonly uid: Option.Option<number>\n\treadonly gid: Option.Option<number>\n}\nexport interface Contents extends RegularFileMetadata {\n\treadonly bytes: Uint8Array\n}\nexport interface Backup {\n\treadonly name: string\n\treadonly file: string\n\treadonly contents: Option.Option<Contents>\n}\n\n/** lstat distinguishes missing paths from links, including dangling links. */\nexport const inspectManagedFiles = Effect.fnUntraced(function* (\n\tfs: FileSystem.FileSystem,\n\tpath: Path.Path,\n\tdirectory: string,\n\tnames: ReadonlyArray<string>,\n) {\n\treturn yield* Effect.forEach(\n\t\tnames,\n\t\tEffect.fnUntraced(function* (name) {\n\t\t\tconst file = path.join(directory, name)\n\t\t\tconst metadata = yield* fs.lstat(file).pipe(\n\t\t\t\tEffect.flatMap((info) =>\n\t\t\t\t\tinfo.type === \"File\"\n\t\t\t\t\t\t? Effect.succeed<RegularFileMetadata>({\n\t\t\t\t\t\t\t\tmode: info.mode & 0o7777,\n\t\t\t\t\t\t\t\tuid: info.uid,\n\t\t\t\t\t\t\t\tgid: info.gid,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: Effect.fail(\n\t\t\t\t\t\t\t\tUnsupportedFileTypeError.make({\n\t\t\t\t\t\t\t\t\tpath: file,\n\t\t\t\t\t\t\t\t\tfileType: info.type,\n\t\t\t\t\t\t\t\t\tmessage: `Managed paths must be regular files or absent: ${file} is ${info.type}`,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\tEffect.asSome,\n\t\t\t\t// Only an entirely missing-path failure establishes absence.\n\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t(cause) =>\n\t\t\t\t\t\tArray.every(\n\t\t\t\t\t\t\tcause.reasons,\n\t\t\t\t\t\t\t(reason) =>\n\t\t\t\t\t\t\t\tCause.isFailReason(reason) &&\n\t\t\t\t\t\t\t\treason.error._tag === \"PlatformError\" &&\n\t\t\t\t\t\t\t\treason.error.reason.module === \"FileSystem\" &&\n\t\t\t\t\t\t\t\treason.error.reason.method === \"lstat\" &&\n\t\t\t\t\t\t\t\treason.error.reason._tag === \"NotFound\",\n\t\t\t\t\t\t),\n\t\t\t\t\t() => Effect.succeedNone,\n\t\t\t\t),\n\t\t\t)\n\t\t\treturn { name, file, metadata }\n\t\t}),\n\t)\n})\n\nconst encodeInventory = Schema.encodeEffect(\n\tSchema.fromJsonString(\n\t\tSchema.Struct({\n\t\t\tdirectory: Schema.String,\n\t\t\tagent: Schema.String,\n\t\t\tfiles: Schema.Array(\n\t\t\t\tSchema.Struct({\n\t\t\t\t\tname: Schema.String,\n\t\t\t\t\tbackup: Schema.NullOr(Schema.String),\n\t\t\t\t\tmode: Schema.NullOr(Schema.Int),\n\t\t\t\t\tuid: Schema.NullOr(Schema.Int),\n\t\t\t\t\tgid: Schema.NullOr(Schema.Int),\n\t\t\t\t}),\n\t\t\t),\n\t\t}),\n\t\t{ space: 2 },\n\t),\n)\n\n/** Persist every byte before allowing the installer to modify the project. */\nexport const writeSnapshot = Effect.fnUntraced(function* (\n\tfs: FileSystem.FileSystem,\n\tpath: Path.Path,\n\tsnapshotDirectory: string,\n\tdirectory: string,\n\tagent: string,\n\tbackups: ReadonlyArray<Backup>,\n) {\n\tconst files = yield* Effect.forEach(\n\t\tbackups,\n\t\tEffect.fnUntraced(function* (entry, index) {\n\t\t\tif (Option.isNone(entry.contents))\n\t\t\t\treturn {\n\t\t\t\t\tname: entry.name,\n\t\t\t\t\tbackup: null,\n\t\t\t\t\tmode: null,\n\t\t\t\t\tuid: null,\n\t\t\t\t\tgid: null,\n\t\t\t\t}\n\t\t\tconst { bytes, mode, uid, gid } = entry.contents.value\n\t\t\tconst backup = `${index}.bin`\n\t\t\tyield* fs.writeFile(path.join(snapshotDirectory, backup), bytes, {\n\t\t\t\tmode: 0o600,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tname: entry.name,\n\t\t\t\tbackup,\n\t\t\t\tmode,\n\t\t\t\tuid: Option.getOrNull(uid),\n\t\t\t\tgid: Option.getOrNull(gid),\n\t\t\t}\n\t\t}),\n\t)\n\tconst inventory = yield* encodeInventory({ directory, agent, files })\n\tyield* fs.writeFileString(\n\t\tpath.join(snapshotDirectory, \"snapshot.json\"),\n\t\tinventory,\n\t\t{ mode: 0o600 },\n\t)\n\treturn {\n\t\t// The caller must establish that file writes and the original process group have stopped.\n\t\trestore: (_idle: ManagedWritersIdle) =>\n\t\t\tArray.reduce<Backup, Effect.Effect<void, PlatformError.PlatformError>>(\n\t\t\t\tbackups,\n\t\t\t\tEffect.void,\n\t\t\t\t(previous, { file, contents }) =>\n\t\t\t\t\tprevious.pipe(\n\t\t\t\t\t\tEffect.onExit(() =>\n\t\t\t\t\t\t\tOption.match(contents, {\n\t\t\t\t\t\t\t\tonNone: () => fs.remove(file, { force: true }),\n\t\t\t\t\t\t\t\tonSome: (contents) => restoreFile(fs, path, file, contents),\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),\n\t}\n}, Effect.uninterruptible)\n\n/** Create privately and restore metadata before making the replacement visible. */\nconst restoreFile = Effect.fnUntraced(function* (\n\tfs: FileSystem.FileSystem,\n\tpath: Path.Path,\n\ttarget: string,\n\tcontents: Contents,\n) {\n\tyield* fs.makeDirectory(path.dirname(target), { recursive: true })\n\tconst suffix = yield* Random.nextIntBetween(0, 0xffffffff)\n\tconst temporary = `${target}.${suffix.toString(16)}.tmp`\n\tyield* Effect.acquireUseRelease(\n\t\tScope.make(),\n\t\t(scope) =>\n\t\t\tEffect.acquireUseRelease(\n\t\t\t\tfs\n\t\t\t\t\t.open(temporary, { flag: \"wx\", mode: 0o600 })\n\t\t\t\t\t.pipe(Effect.provideService(Scope.Scope, scope)),\n\t\t\t\tEffect.fnUntraced(function* (file) {\n\t\t\t\t\tyield* file.writeAll(contents.bytes)\n\t\t\t\t\tconst current = yield* file.stat\n\t\t\t\t\tconst { uid, gid } = contents\n\t\t\t\t\tif (\n\t\t\t\t\t\tOption.isSome(uid) &&\n\t\t\t\t\t\tOption.isSome(gid) &&\n\t\t\t\t\t\t(!Equal.equals(uid, current.uid) || !Equal.equals(gid, current.gid))\n\t\t\t\t\t) {\n\t\t\t\t\t\tyield* fs.chown(temporary, uid.value, gid.value)\n\t\t\t\t\t}\n\t\t\t\t\tyield* fs.chmod(temporary, contents.mode)\n\t\t\t\t\tyield* file.sync\n\t\t\t\t\tyield* Scope.close(scope, Exit.void)\n\t\t\t\t\tyield* fs.rename(temporary, target)\n\t\t\t\t}),\n\t\t\t\t(_, exit) =>\n\t\t\t\t\tExit.isSuccess(exit)\n\t\t\t\t\t\t? Effect.void\n\t\t\t\t\t\t: Scope.close(scope, exit).pipe(\n\t\t\t\t\t\t\t\tEffect.onExit(() => fs.remove(temporary, { force: true })),\n\t\t\t\t\t\t\t),\n\t\t\t),\n\t\t(scope, exit) => Scope.close(scope, exit),\n\t)\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"snapshot.js","names":[],"sources":["../src/snapshot.ts"],"sourcesContent":["import {\n\tArray,\n\tCause,\n\tEffect,\n\tEqual,\n\tExit,\n\tOption,\n\tRandom,\n\tSchema,\n\tScope,\n\ttype FileSystem,\n\ttype Path,\n\ttype PlatformError,\n} from \"effect\"\n\n/** File writes have completed and the original installer group is stopped or absent. */\nexport interface ManagedWritersIdle {\n\treadonly _tag: \"ManagedWritersIdle\"\n}\n\nexport interface SnapshotCleanupFailure {\n\treadonly snapshotDirectory: string\n\treadonly cause: Cause.Cause<PlatformError.PlatformError>\n}\n\n/** Separate the transaction's outcome from disposal of its snapshot. */\nexport const withSnapshot = <A, E, R>(\n\tfs: FileSystem.FileSystem,\n\tcanDelete: Effect.Effect<boolean>,\n\tuse: (directory: string) => Effect.Effect<A, E, R>,\n) =>\n\tEffect.uninterruptibleMask((resume) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst snapshotDirectory = yield* fs.makeTempDirectory({\n\t\t\t\tprefix: \"graft-recovery-\",\n\t\t\t})\n\t\t\tconst outcome = yield* resume(\n\t\t\t\tEffect.suspend(() => use(snapshotDirectory)),\n\t\t\t).pipe(Effect.exit)\n\t\t\tconst cleanup = (yield* canDelete)\n\t\t\t\t? yield* Effect.suspend(() =>\n\t\t\t\t\t\tfs.remove(snapshotDirectory, { recursive: true }),\n\t\t\t\t\t).pipe(Effect.exit)\n\t\t\t\t: Exit.void\n\t\t\tif (Exit.isFailure(outcome)) {\n\t\t\t\treturn yield* Effect.failCause(\n\t\t\t\t\tExit.isFailure(cleanup)\n\t\t\t\t\t\t? Cause.combine(outcome.cause, cleanup.cause)\n\t\t\t\t\t\t: outcome.cause,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn yield* resume(\n\t\t\t\tEffect.succeed({\n\t\t\t\t\tvalue: outcome.value,\n\t\t\t\t\tsnapshotCleanupFailure: Exit.isFailure(cleanup)\n\t\t\t\t\t\t? Option.some<SnapshotCleanupFailure>({\n\t\t\t\t\t\t\t\tsnapshotDirectory,\n\t\t\t\t\t\t\t\tcause: cleanup.cause,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: Option.none<SnapshotCleanupFailure>(),\n\t\t\t\t}),\n\t\t\t).pipe(\n\t\t\t\t// Cancellation can prevent delivery, but must not hide a cleanup failure.\n\t\t\t\tEffect.onExit((exit) =>\n\t\t\t\t\tExit.isFailure(exit) && Exit.isFailure(cleanup)\n\t\t\t\t\t\t? Effect.failCause(cleanup.cause)\n\t\t\t\t\t\t: Effect.void,\n\t\t\t\t),\n\t\t\t)\n\t\t}),\n\t)\n\nexport class UnsupportedFileTypeError extends Schema.TaggedError<\n\tUnsupportedFileTypeError,\n\t{ readonly brand: unique symbol }\n>()(\"UnsupportedFileTypeError\", {\n\tpath: Schema.String,\n\tfileType: Schema.String,\n\tmessage: Schema.String,\n}) {}\n\ninterface RegularFileMetadata {\n\treadonly mode: number\n\treadonly uid: Option.Option<number>\n\treadonly gid: Option.Option<number>\n}\nexport interface Contents extends RegularFileMetadata {\n\treadonly bytes: Uint8Array\n}\nexport interface Backup {\n\treadonly name: string\n\treadonly file: string\n\treadonly contents: Option.Option<Contents>\n}\n\n/** lstat distinguishes missing paths from links, including dangling links. */\nexport const inspectManagedFiles = Effect.fnUntraced(function* (\n\tfs: FileSystem.FileSystem,\n\tpath: Path.Path,\n\tdirectory: string,\n\tnames: ReadonlyArray<string>,\n) {\n\treturn yield* Effect.forEach(\n\t\tnames,\n\t\tEffect.fnUntraced(function* (name) {\n\t\t\tconst file = path.join(directory, name)\n\t\t\tconst metadata = yield* fs.lstat(file).pipe(\n\t\t\t\tEffect.flatMap((info) =>\n\t\t\t\t\tinfo.type === \"File\"\n\t\t\t\t\t\t? Effect.succeed<RegularFileMetadata>({\n\t\t\t\t\t\t\t\tmode: info.mode & 0o7777,\n\t\t\t\t\t\t\t\tuid: info.uid,\n\t\t\t\t\t\t\t\tgid: info.gid,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: Effect.fail(\n\t\t\t\t\t\t\t\tUnsupportedFileTypeError.make({\n\t\t\t\t\t\t\t\t\tpath: file,\n\t\t\t\t\t\t\t\t\tfileType: info.type,\n\t\t\t\t\t\t\t\t\tmessage: `Managed paths must be regular files or absent: ${file} is ${info.type}`,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\tEffect.asSome,\n\t\t\t\t// Only an entirely missing-path failure establishes absence.\n\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t(cause) =>\n\t\t\t\t\t\tArray.every(\n\t\t\t\t\t\t\tcause.reasons,\n\t\t\t\t\t\t\t(reason) =>\n\t\t\t\t\t\t\t\tCause.isFailReason(reason) &&\n\t\t\t\t\t\t\t\treason.error._tag === \"PlatformError\" &&\n\t\t\t\t\t\t\t\treason.error.reason.module === \"FileSystem\" &&\n\t\t\t\t\t\t\t\treason.error.reason.method === \"lstat\" &&\n\t\t\t\t\t\t\t\treason.error.reason._tag === \"NotFound\",\n\t\t\t\t\t\t),\n\t\t\t\t\t() => Effect.succeedNone,\n\t\t\t\t),\n\t\t\t)\n\t\t\treturn { name, file, metadata }\n\t\t}),\n\t)\n})\n\nconst encodeInventory = Schema.encodeEffect(\n\tSchema.fromJsonString(\n\t\tSchema.Struct({\n\t\t\tdirectory: Schema.String,\n\t\t\tagent: Schema.String,\n\t\t\tfiles: Schema.Array(\n\t\t\t\tSchema.Struct({\n\t\t\t\t\tname: Schema.String,\n\t\t\t\t\tbackup: Schema.NullOr(Schema.String),\n\t\t\t\t\tmode: Schema.NullOr(Schema.Int),\n\t\t\t\t\tuid: Schema.NullOr(Schema.Int),\n\t\t\t\t\tgid: Schema.NullOr(Schema.Int),\n\t\t\t\t}),\n\t\t\t),\n\t\t}),\n\t\t{ space: 2 },\n\t),\n)\n\n/** Persist every byte before allowing the installer to modify the project. */\nexport const writeSnapshot = Effect.fnUntraced(function* (\n\tfs: FileSystem.FileSystem,\n\tpath: Path.Path,\n\tsnapshotDirectory: string,\n\tdirectory: string,\n\tagent: string,\n\tbackups: ReadonlyArray<Backup>,\n) {\n\tconst files = yield* Effect.forEach(\n\t\tbackups,\n\t\tEffect.fnUntraced(function* (entry, index) {\n\t\t\tif (Option.isNone(entry.contents))\n\t\t\t\treturn {\n\t\t\t\t\tname: entry.name,\n\t\t\t\t\tbackup: null,\n\t\t\t\t\tmode: null,\n\t\t\t\t\tuid: null,\n\t\t\t\t\tgid: null,\n\t\t\t\t}\n\t\t\tconst { bytes, mode, uid, gid } = entry.contents.value\n\t\t\tconst backup = `${index}.bin`\n\t\t\tyield* fs.writeFile(path.join(snapshotDirectory, backup), bytes, {\n\t\t\t\tmode: 0o600,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tname: entry.name,\n\t\t\t\tbackup,\n\t\t\t\tmode,\n\t\t\t\tuid: Option.getOrNull(uid),\n\t\t\t\tgid: Option.getOrNull(gid),\n\t\t\t}\n\t\t}),\n\t)\n\tconst inventory = yield* encodeInventory({ directory, agent, files })\n\tyield* fs.writeFileString(\n\t\tpath.join(snapshotDirectory, \"snapshot.json\"),\n\t\tinventory,\n\t\t{ mode: 0o600 },\n\t)\n\treturn {\n\t\t// The caller must establish that file writes and the original process group have stopped.\n\t\trestore: (_idle: ManagedWritersIdle) =>\n\t\t\tArray.reduce<Backup, Effect.Effect<void, PlatformError.PlatformError>>(\n\t\t\t\tbackups,\n\t\t\t\tEffect.void,\n\t\t\t\t(previous, { file, contents }) =>\n\t\t\t\t\tprevious.pipe(\n\t\t\t\t\t\tEffect.onExit(() =>\n\t\t\t\t\t\t\tOption.match(contents, {\n\t\t\t\t\t\t\t\tonNone: () => fs.remove(file, { force: true }),\n\t\t\t\t\t\t\t\tonSome: (contents) => restoreFile(fs, path, file, contents),\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),\n\t}\n}, Effect.uninterruptible)\n\n/** Create privately and restore metadata before making the replacement visible. */\nconst restoreFile = Effect.fnUntraced(function* (\n\tfs: FileSystem.FileSystem,\n\tpath: Path.Path,\n\ttarget: string,\n\tcontents: Contents,\n) {\n\tyield* fs.makeDirectory(path.dirname(target), { recursive: true })\n\tconst suffix = yield* Random.nextIntBetween(0, 0xffffffff)\n\tconst temporary = `${target}.${suffix.toString(16)}.tmp`\n\tyield* Effect.acquireUseRelease(\n\t\tScope.make(),\n\t\t(scope) =>\n\t\t\tEffect.acquireUseRelease(\n\t\t\t\tfs\n\t\t\t\t\t.open(temporary, { flag: \"wx\", mode: 0o600 })\n\t\t\t\t\t.pipe(Effect.provideService(Scope.Scope, scope)),\n\t\t\t\tEffect.fnUntraced(function* (file) {\n\t\t\t\t\tyield* file.writeAll(contents.bytes)\n\t\t\t\t\tconst current = yield* file.stat\n\t\t\t\t\tconst { uid, gid } = contents\n\t\t\t\t\tif (\n\t\t\t\t\t\tOption.isSome(uid) &&\n\t\t\t\t\t\tOption.isSome(gid) &&\n\t\t\t\t\t\t(!Equal.equals(uid, current.uid) || !Equal.equals(gid, current.gid))\n\t\t\t\t\t) {\n\t\t\t\t\t\tyield* fs.chown(temporary, uid.value, gid.value)\n\t\t\t\t\t}\n\t\t\t\t\tyield* fs.chmod(temporary, contents.mode)\n\t\t\t\t\tyield* file.sync\n\t\t\t\t\tyield* Scope.close(scope, Exit.void)\n\t\t\t\t\tyield* fs.rename(temporary, target)\n\t\t\t\t}),\n\t\t\t\t(_, exit) =>\n\t\t\t\t\tExit.isSuccess(exit)\n\t\t\t\t\t\t? Effect.void\n\t\t\t\t\t\t: Scope.close(scope, exit).pipe(\n\t\t\t\t\t\t\t\tEffect.onExit(() => fs.remove(temporary, { force: true })),\n\t\t\t\t\t\t\t),\n\t\t\t),\n\t\t(scope, exit) => Scope.close(scope, exit),\n\t)\n})\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.3.3",
6
+ "version": "0.4.1",
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
@@ -3,7 +3,6 @@ import {
3
3
  Array,
4
4
  Cause,
5
5
  Data,
6
- Ref,
7
6
  Effect,
8
7
  Exit,
9
8
  FileSystem,
@@ -13,38 +12,43 @@ import {
13
12
  Path,
14
13
  PlatformError,
15
14
  Record,
15
+ Ref,
16
16
  Schema,
17
17
  Semaphore,
18
- String,
19
18
  Stream,
19
+ String,
20
20
  } from "effect"
21
21
  import { ChildProcess, ChildProcessSpawner } from "effect/process"
22
+
23
+ import {
24
+ applyChanges,
25
+ Changes,
26
+ decodeManifest,
27
+ encodeManifest,
28
+ fields,
29
+ type Manifest,
30
+ } from "./manifest"
22
31
  import {
23
32
  PackageManagerDeclaration,
24
33
  PackageManagerName,
25
34
  } from "./PackageManagerDeclaration"
26
- export { PackageManagerDeclaration } from "./PackageManagerDeclaration"
27
35
  import { terminateProcess } from "./process"
28
36
  import {
29
- writeSnapshot,
30
- withSnapshot,
31
- type SnapshotCleanupFailure,
32
37
  inspectManagedFiles,
33
38
  UnsupportedFileTypeError,
39
+ withSnapshot,
40
+ writeSnapshot,
34
41
  type Contents,
42
+ type SnapshotCleanupFailure,
35
43
  } from "./snapshot"
44
+
45
+ export { PackageManagerDeclaration } from "./PackageManagerDeclaration"
46
+
36
47
  export {
37
48
  UnsupportedFileTypeError,
38
49
  type SnapshotCleanupFailure,
39
50
  } from "./snapshot"
40
- import {
41
- applyChanges,
42
- Changes,
43
- decodeManifest,
44
- encodeManifest,
45
- fields,
46
- type Manifest,
47
- } from "./manifest"
51
+
48
52
  export { Changes, PackageName } from "./manifest"
49
53
 
50
54
  export const Agent = Schema.Literals([
@@ -204,8 +208,46 @@ const decodePackageManager = Schema.decodeUnknownEffect(
204
208
  const decodeUserAgent = Schema.decodeUnknownOption(
205
209
  Schema.TemplateLiteralParser([Executable, "/", Digits, Schema.String]),
206
210
  )
207
- const agentOf = (name: typeof Executable.Type, major: string): Agent =>
208
- name === "yarn" ? (major === "1" ? "yarn-classic" : "yarn-berry") : name
211
+ /**
212
+ * The agent that runs a package manager. Yarn 1 is Classic and later versions
213
+ * are Berry; without a version, `yarn` means the Classic global binary.
214
+ */
215
+ export const agentFor = (
216
+ name: typeof PackageManagerName.Type,
217
+ version?: string,
218
+ ): Agent =>
219
+ name === "yarn"
220
+ ? version === undefined || String.split(".")(version)[0] === "1"
221
+ ? "yarn-classic"
222
+ : "yarn-berry"
223
+ : name
224
+
225
+ /** A package manager and its version, as named by `npm_config_user_agent`. */
226
+ export interface UserAgent {
227
+ readonly name: typeof PackageManagerName.Type
228
+ readonly version: string
229
+ }
230
+ const decodeUserAgentName = Schema.decodeUnknownOption(
231
+ Schema.TemplateLiteralParser([
232
+ Executable,
233
+ "/",
234
+ Schema.String.check(Schema.isMinLength(1)),
235
+ ]),
236
+ )
237
+ /** The first `name/version` of a user agent; other package managers, such as Bun, are none. */
238
+ export const parseUserAgent = (userAgent: string): Option.Option<UserAgent> =>
239
+ Option.map(
240
+ decodeUserAgentName(String.split(" ")(userAgent)[0] ?? ""),
241
+ ([name, , version]) => ({ name, version }),
242
+ )
243
+
244
+ /** What runs a project script, such as `npm run` in `npm run dev`. */
245
+ export const runPrefix = (agent: Agent): string =>
246
+ agent === "npm" ? "npm run" : agent === "pnpm" ? "pnpm" : "yarn"
247
+
248
+ /** The command that installs a project's dependencies. */
249
+ export const installCommand = (agent: Agent): string =>
250
+ agent === "npm" ? "npm install" : agent === "pnpm" ? "pnpm install" : "yarn"
209
251
 
210
252
  /** packageManager, then lockfile, then caller user-agent, then npm. Conflicting lockfiles fail. */
211
253
  export const detectAgent = Effect.fnUntraced(function* (options: Options) {
@@ -229,7 +271,7 @@ export const detectAgent = Effect.fnUntraced(function* (options: Options) {
229
271
  }),
230
272
  ),
231
273
  )
232
- return agentOf(name, `${version.major}`)
274
+ return agentFor(name, `${version.major}`)
233
275
  }
234
276
  const found = yield* Effect.filter(lockfiles, (file) =>
235
277
  fs.exists(path.join(options.directory, file)),
@@ -274,7 +316,7 @@ export const detectAgent = Effect.fnUntraced(function* (options: Options) {
274
316
  if (kinds[0] !== undefined) return kinds[0]
275
317
  return Option.match(decodeUserAgent(options.userAgent ?? ""), {
276
318
  onNone: () => "npm" as const,
277
- onSome: ([name, , major]) => agentOf(name, major),
319
+ onSome: ([name, , major]) => agentFor(name, major),
278
320
  })
279
321
  })
280
322
 
package/src/manifest.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  Array,
3
+ Chunk,
3
4
  Effect,
4
5
  HashMap,
5
- Chunk,
6
6
  Option,
7
7
  Record,
8
8
  Schema,
package/src/process.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // The spawner exposes no process-group existence check after kill loses errno.
2
2
  import * as NodeProcess from "node:process"
3
+
3
4
  import {
4
5
  Array,
5
6
  Cause,
package/src/snapshot.ts CHANGED
@@ -8,8 +8,11 @@ import {
8
8
  Random,
9
9
  Schema,
10
10
  Scope,
11
+ type FileSystem,
12
+ type Path,
13
+ type PlatformError,
11
14
  } from "effect"
12
- import type { FileSystem, Path, PlatformError } from "effect"
15
+
13
16
  /** File writes have completed and the original installer group is stopped or absent. */
14
17
  export interface ManagedWritersIdle {
15
18
  readonly _tag: "ManagedWritersIdle"