@graftland/package-manager 0.2.0-beta.0 → 0.2.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +57 -33
- package/dist/index.js.map +1 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +27 -21
- package/dist/manifest.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +93 -54
- package/src/manifest.ts +48 -25
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;qBAiDa,OAAK,OAAA;YAMN,eAAe,MAAM;;;;;;WAGd;;qBAFN,4BAA4B;;;;;;;;;;WAUtB;;qBAFN,8BAA8B;YAoC/B,QACT,sBACA,2BACA,wBACA,cAAc,gBACd,OAAO;iBACO;;WAEP;;WAEA,QAAQ;;WAER;;;qBAkGG,cAAW,SAAA,YAAA,OAAA,uDAAA,sBAAA,cAAA,gBAAA,OAAA,aAAA,WAAA,aAAA,KAAA;iBAmEP;WACP;;WAEA;WACA;;;YAGE,QAAQ,QAAQ,gBAAgB;iBAC3B;WACP;WACA,QAAQ;WACR,OAAO;;iBAEA;WACP,OAAO,cAAc;WACrB,SAAS,cAAc;WACvB,SAAS,cAAc;;qBAEpB,YAAS,QAAY,OAAK,OAAS,UAAQ;iBA0BvC,sBAAsB;WAC7B,OAAO;;WAEP,wBAAwB,OAAO,OAAO;;iBAE/B;WACP;WACA,OAAO;WACP,WAAW,OAAO,OAAO,OAAO;;WAEhC,kBAAkB,OAAO,OAAO,OAAO,OAAO;;WAE9C,UAAU,SAAS,YAAY,OAAO,OAAO,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA6BzD,OAAI,SACP,YACP,OAAO,OACT,gBACA,OACA,WAAW,aAAa,KAAK,OAAO,oBAAoB"}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { PackageManagerDeclaration, PackageManagerName } from "./PackageManagerD
|
|
|
2
2
|
import { terminateProcess } from "./process.js";
|
|
3
3
|
import { UnsupportedFileTypeError, inspectManagedFiles, withSnapshot, writeSnapshot } from "./snapshot.js";
|
|
4
4
|
import { Changes, PackageName, applyChanges, decodeManifest, encodeManifest, fields } from "./manifest.js";
|
|
5
|
-
import { Array, Data, Effect, Exit, FileSystem, HashMap, Option, Path, PlatformError, Record, Ref, Schema, Semaphore, String } from "effect";
|
|
5
|
+
import { Array, Cause, Data, Effect, Exit, FileSystem, HashMap, Match, Option, Path, PlatformError, Record, Ref, Schema, Semaphore, Stream, String } from "effect";
|
|
6
6
|
import { ChildProcess, ChildProcessSpawner } from "effect/process";
|
|
7
7
|
// src/index.ts
|
|
8
8
|
/** Non-interactive package installation. Provide platform services at the application boundary. */
|
|
@@ -54,7 +54,47 @@ const backupFiles = [
|
|
|
54
54
|
".pnp.cjs",
|
|
55
55
|
".pnp.loader.mjs"
|
|
56
56
|
];
|
|
57
|
-
const
|
|
57
|
+
const strategy = (agent, operation) => {
|
|
58
|
+
const recovery = operation === "recover";
|
|
59
|
+
return Match.value(agent).pipe(Match.when("npm", () => ({
|
|
60
|
+
executable: "npm",
|
|
61
|
+
args: [
|
|
62
|
+
"install",
|
|
63
|
+
"--no-audit",
|
|
64
|
+
"--no-fund",
|
|
65
|
+
...recovery ? ["--prefer-offline"] : []
|
|
66
|
+
],
|
|
67
|
+
env: { CI: "true" },
|
|
68
|
+
lockfiles: ["package-lock.json", "npm-shrinkwrap.json"]
|
|
69
|
+
})), Match.when("pnpm", () => ({
|
|
70
|
+
executable: "pnpm",
|
|
71
|
+
args: [
|
|
72
|
+
"install",
|
|
73
|
+
recovery ? "--frozen-lockfile" : "--no-frozen-lockfile",
|
|
74
|
+
"--config.confirmModulesPurge=false"
|
|
75
|
+
],
|
|
76
|
+
env: { CI: "true" },
|
|
77
|
+
lockfiles: ["pnpm-lock.yaml"]
|
|
78
|
+
})), Match.when("yarn-classic", () => ({
|
|
79
|
+
executable: "yarn",
|
|
80
|
+
args: [
|
|
81
|
+
"install",
|
|
82
|
+
"--non-interactive",
|
|
83
|
+
...recovery ? ["--frozen-lockfile", "--prefer-offline"] : []
|
|
84
|
+
],
|
|
85
|
+
env: { CI: "true" },
|
|
86
|
+
lockfiles: ["yarn.lock"]
|
|
87
|
+
})), Match.when("yarn-berry", () => ({
|
|
88
|
+
executable: "yarn",
|
|
89
|
+
args: ["install", ...recovery ? ["--immutable"] : []],
|
|
90
|
+
env: {
|
|
91
|
+
CI: "true",
|
|
92
|
+
YARN_ENABLE_IMMUTABLE_INSTALLS: recovery ? "true" : "false",
|
|
93
|
+
YARN_NODE_LINKER: "node-modules"
|
|
94
|
+
},
|
|
95
|
+
lockfiles: ["yarn.lock"]
|
|
96
|
+
})), Match.exhaustive);
|
|
97
|
+
};
|
|
58
98
|
const readSource = Effect.fnUntraced(function* (fs, file) {
|
|
59
99
|
const bytes = yield* fs.readFile(file);
|
|
60
100
|
return yield* Effect.try({
|
|
@@ -101,8 +141,11 @@ const detectAgent = Effect.fnUntraced(function* (options) {
|
|
|
101
141
|
message: "Conflicting lockfiles; select an agent explicitly"
|
|
102
142
|
});
|
|
103
143
|
if (kinds[0] === "yarn") {
|
|
104
|
-
const
|
|
105
|
-
|
|
144
|
+
const metadata = yield* fs.stream(path.join(options.directory, "yarn.lock")).pipe(Stream.decodeText(), Stream.mapAccum(() => "\n", (tail, chunk) => {
|
|
145
|
+
const text = tail + chunk;
|
|
146
|
+
return [String.slice(-11)(text), [/[\n\r\u2028\u2029]__metadata:/.test(text)]];
|
|
147
|
+
}), Stream.filter((found) => found), Stream.runHead);
|
|
148
|
+
return Option.isSome(metadata) ? "yarn-berry" : "yarn-classic";
|
|
106
149
|
}
|
|
107
150
|
if (kinds[0] !== void 0) return kinds[0];
|
|
108
151
|
return Option.match(decodeUserAgent(options.userAgent ?? ""), {
|
|
@@ -174,38 +217,25 @@ const make = (options) => Effect.gen(function* () {
|
|
|
174
217
|
}));
|
|
175
218
|
const entries = yield* Effect.forEach(Record.toEntries(deps), ([name, spec]) => Effect.gen(function* () {
|
|
176
219
|
const file = path.join(directory, "node_modules", name, "package.json");
|
|
177
|
-
|
|
220
|
+
const source = yield* fs.readFileString(file).pipe(Effect.asSome, Effect.catchCauseIf((cause) => Array.every(cause.reasons, (reason) => Cause.isFailReason(reason) && reason.error.reason.module === "FileSystem" && reason.error.reason._tag === "NotFound"), () => Effect.succeedNone));
|
|
221
|
+
if (Option.isNone(source)) return [name, {
|
|
178
222
|
spec,
|
|
179
223
|
version: void 0,
|
|
180
224
|
path: void 0
|
|
181
225
|
}];
|
|
182
226
|
return [name, {
|
|
183
227
|
spec,
|
|
184
|
-
version: (yield*
|
|
228
|
+
version: (yield* installed(source.value)).version,
|
|
185
229
|
path: yield* fs.realPath(path.dirname(file))
|
|
186
230
|
}];
|
|
187
|
-
}));
|
|
231
|
+
}), { concurrency: 16 });
|
|
188
232
|
return HashMap.fromIterable(entries);
|
|
189
233
|
});
|
|
190
234
|
const readState = Effect.flatMap(readManifest, stateOf);
|
|
191
235
|
const termination = { forceKillAfter: "5 seconds" };
|
|
192
236
|
const run = Effect.fnUntraced(function* (operation) {
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
"install",
|
|
196
|
-
"--no-audit",
|
|
197
|
-
"--no-fund",
|
|
198
|
-
...recovery ? ["--prefer-offline"] : []
|
|
199
|
-
] : agent === "pnpm" ? [
|
|
200
|
-
"install",
|
|
201
|
-
recovery ? "--frozen-lockfile" : "--no-frozen-lockfile",
|
|
202
|
-
"--config.confirmModulesPurge=false"
|
|
203
|
-
] : agent === "yarn-classic" ? [
|
|
204
|
-
"install",
|
|
205
|
-
"--non-interactive",
|
|
206
|
-
...recovery ? ["--frozen-lockfile", "--prefer-offline"] : []
|
|
207
|
-
] : ["install", ...recovery ? ["--immutable"] : []];
|
|
208
|
-
return yield* Effect.acquireUseRelease(spawner.spawn(ChildProcess.make(executable(agent), args, {
|
|
237
|
+
const command = strategy(agent, operation);
|
|
238
|
+
return yield* Effect.acquireUseRelease(spawner.spawn(ChildProcess.make(command.executable, command.args, {
|
|
209
239
|
cwd: directory,
|
|
210
240
|
extendEnv: true,
|
|
211
241
|
stdin: "ignore",
|
|
@@ -213,25 +243,19 @@ const make = (options) => Effect.gen(function* () {
|
|
|
213
243
|
stderr: "inherit",
|
|
214
244
|
detached: true,
|
|
215
245
|
...termination,
|
|
216
|
-
env:
|
|
217
|
-
CI: "true",
|
|
218
|
-
...agent === "yarn-berry" ? {
|
|
219
|
-
YARN_ENABLE_IMMUTABLE_INSTALLS: recovery ? "true" : "false",
|
|
220
|
-
YARN_NODE_LINKER: "node-modules"
|
|
221
|
-
} : {}
|
|
222
|
-
}
|
|
246
|
+
env: command.env
|
|
223
247
|
})).pipe(Effect.tap(() => Ref.set(transaction, ManagerState.Active({ writer: WriterState.Running() })))), (process) => Effect.gen(function* () {
|
|
224
248
|
const code = yield* process.exitCode.pipe(Effect.timeoutOrElse({
|
|
225
249
|
duration: "10 minutes",
|
|
226
250
|
orElse: () => PackageManagerError.make({
|
|
227
251
|
operation,
|
|
228
|
-
message: `${executable
|
|
252
|
+
message: `${command.executable} ${operation} timed out`
|
|
229
253
|
})
|
|
230
254
|
}));
|
|
231
255
|
if (code !== 0) return yield* PackageManagerError.make({
|
|
232
256
|
operation,
|
|
233
257
|
exitCode: code,
|
|
234
|
-
message: `${executable
|
|
258
|
+
message: `${command.executable} ${operation} exited with code ${code}`
|
|
235
259
|
});
|
|
236
260
|
}), (process) => Effect.gen(function* () {
|
|
237
261
|
const result = yield* terminateProcess(process, termination);
|
|
@@ -266,7 +290,7 @@ const make = (options) => Effect.gen(function* () {
|
|
|
266
290
|
const manifest = yield* decodeManifest(original);
|
|
267
291
|
const before = yield* stateOf(manifest);
|
|
268
292
|
const text = yield* encodeManifest(applyChanges(manifest, validated), original);
|
|
269
|
-
const agentLocks = agent
|
|
293
|
+
const agentLocks = strategy(agent, "install").lockfiles;
|
|
270
294
|
const hasLock = Array.some(backups, ({ name, contents }) => Array.contains(agentLocks, name) && Option.isSome(contents));
|
|
271
295
|
const completed = yield* withSnapshot(fs, Ref.get(transaction).pipe(Effect.map((state) => state._tag === "Ready")), (snapshotDirectory) => Effect.gen(function* () {
|
|
272
296
|
const snapshot = yield* writeSnapshot(fs, path, snapshotDirectory, directory, agent, backups);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/** Non-interactive package installation. Provide platform services at the application boundary. */\nimport {\n\tArray,\n\tCause,\n\tData,\n\tRef,\n\tEffect,\n\tExit,\n\tFileSystem,\n\tHashMap,\n\tOption,\n\tPath,\n\tPlatformError,\n\tRecord,\n\tSchema,\n\tSemaphore,\n\tString,\n} from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/process\"\nimport {\n\tPackageManagerDeclaration,\n\tPackageManagerName,\n} from \"./PackageManagerDeclaration\"\nexport { PackageManagerDeclaration } from \"./PackageManagerDeclaration\"\nimport { terminateProcess } from \"./process\"\nimport {\n\twriteSnapshot,\n\twithSnapshot,\n\ttype SnapshotCleanupFailure,\n\tinspectManagedFiles,\n\tUnsupportedFileTypeError,\n\ttype Contents,\n} from \"./snapshot\"\nexport {\n\tUnsupportedFileTypeError,\n\ttype SnapshotCleanupFailure,\n} from \"./snapshot\"\nimport {\n\tapplyChanges,\n\tChanges,\n\tdecodeManifest,\n\tencodeManifest,\n\tfields,\n\ttype Manifest,\n} from \"./manifest\"\nexport { Changes, PackageName } from \"./manifest\"\n\nexport const Agent = Schema.Literals([\n\t\"npm\",\n\t\"pnpm\",\n\t\"yarn-classic\",\n\t\"yarn-berry\",\n])\nexport type Agent = typeof Agent.Type\nexport class PackageManagerError extends Schema.TaggedError<\n\tPackageManagerError,\n\t{ readonly brand: unique symbol }\n>()(\"PackageManagerError\", {\n\toperation: Schema.Literals([\"detect\", \"install\", \"recover\", \"state\"]),\n\tmessage: Schema.String,\n\texitCode: Schema.optionalKey(Schema.Int),\n}) {}\nexport class RecoveryRequiredError extends Schema.TaggedError<\n\tRecoveryRequiredError,\n\t{ readonly brand: unique symbol }\n>()(\"RecoveryRequiredError\", {\n\tdirectory: Schema.String,\n\tsnapshotDirectory: Schema.String,\n\toperation: Schema.Literals([\"install\", \"recover\", \"rollback\"]),\n\tprocessId: Schema.optionalKey(Schema.Int),\n\tmessage: Schema.String,\n\tterminationCause: Schema.optionalKey(\n\t\tSchema.Cause(Schema.Unknown, Schema.Unknown),\n\t),\n\trecoveryCause: Schema.Cause(Schema.Unknown, Schema.Unknown),\n}) {}\n\ntype WriterState = Data.TaggedEnum<{\n\tManagedWritersIdle: {}\n\tRunning: {}\n\tFileWrite: {}\n\tUnconfirmed: {\n\t\treadonly operation: \"install\" | \"recover\"\n\t\treadonly processId: number\n\t\treadonly cause: Cause.Cause<PlatformError.PlatformError>\n\t}\n}>\n\nconst WriterState = Data.taggedEnum<WriterState>()\n\ntype ManagerState = Data.TaggedEnum<{\n\tReady: {}\n\tActive: { readonly writer: WriterState }\n\tRecoveryRequired: { readonly error: RecoveryRequiredError }\n}>\n\nconst ManagerState = Data.taggedEnum<ManagerState>()\n\nexport type Error =\n\t| PackageManagerError\n\t| UnsupportedFileTypeError\n\t| RecoveryRequiredError\n\t| PlatformError.PlatformError\n\t| Schema.SchemaError\nexport interface Options {\n\t/** Project directory, containing package.json. Use the workspace root for workspace installs. */\n\treadonly directory: string\n\t/** Explicit selection takes precedence over detection. */\n\treadonly agent?: Agent\n\t/** Optional npm_config_user_agent supplied by the caller; used only as a fallback. */\n\treadonly userAgent?: string\n}\nconst lockfiles = [\n\t\"package-lock.json\",\n\t\"npm-shrinkwrap.json\",\n\t\"pnpm-lock.yaml\",\n\t\"yarn.lock\",\n] as const\nconst backupFiles = [\n\t\"package.json\",\n\t...lockfiles,\n\t\".npmrc\",\n\t\"pnpm-workspace.yaml\",\n\t\".yarnrc\",\n\t\".yarnrc.yml\",\n\t\".yarn/install-state.gz\",\n\t\".pnp.cjs\",\n\t\".pnp.loader.mjs\",\n]\nconst executable = (agent: Agent) =>\n\tString.startsWith(\"yarn\")(agent) ? \"yarn\" : agent\n\nconst readSource = Effect.fnUntraced(function* (\n\tfs: FileSystem.FileSystem,\n\tfile: string,\n) {\n\tconst bytes = yield* fs.readFile(file)\n\treturn yield* Effect.try({\n\t\ttry: () =>\n\t\t\tnew TextDecoder(\"utf-8\", { ignoreBOM: true, fatal: true }).decode(bytes),\n\t\tcatch: (cause) =>\n\t\t\tPlatformError.badArgument({\n\t\t\t\tmodule: \"FileSystem\",\n\t\t\t\tmethod: \"readManifest\",\n\t\t\t\tdescription: \"The manifest must contain valid UTF-8\",\n\t\t\t\tcause,\n\t\t\t}),\n\t})\n})\n\nconst Executable = PackageManagerName\nconst Digits = Schema.String.check(Schema.isPattern(/^\\d+$/))\nconst decodePackageManager = Schema.decodeUnknownOption(\n\tPackageManagerDeclaration,\n)\n/** `name/version ...`, as package managers set `npm_config_user_agent`. */\nconst decodeUserAgent = Schema.decodeUnknownOption(\n\tSchema.TemplateLiteralParser([Executable, \"/\", Digits, Schema.String]),\n)\nconst agentOf = (name: typeof Executable.Type, major: string): Agent =>\n\tname === \"yarn\" ? (major === \"1\" ? \"yarn-classic\" : \"yarn-berry\") : name\n\n/** packageManager, then lockfile, then caller user-agent, then npm. Conflicting lockfiles fail. */\nexport const detectAgent = Effect.fnUntraced(function* (options: Options) {\n\tif (options.agent !== undefined)\n\t\treturn yield* Schema.decodeEffect(Agent)(options.agent)\n\tconst fs = yield* FileSystem.FileSystem\n\tconst path = yield* Path.Path\n\tconst manifest = yield* readSource(\n\t\tfs,\n\t\tpath.join(options.directory, \"package.json\"),\n\t).pipe(Effect.flatMap(decodeManifest))\n\tif (manifest.packageManager !== undefined) {\n\t\treturn yield* Option.match(decodePackageManager(manifest.packageManager), {\n\t\t\tonNone: () =>\n\t\t\t\tPackageManagerError.make({\n\t\t\t\t\toperation: \"detect\",\n\t\t\t\t\tmessage: `Unsupported packageManager: ${manifest.packageManager}`,\n\t\t\t\t}),\n\t\t\tonSome: ([name, , version]) =>\n\t\t\t\tEffect.succeed(agentOf(name, `${version.major}`)),\n\t\t})\n\t}\n\tconst found = yield* Effect.filter(lockfiles, (file) =>\n\t\tfs.exists(path.join(options.directory, file)),\n\t)\n\tconst kinds = Array.dedupe(\n\t\tArray.map(found, (file) =>\n\t\t\tfile === \"pnpm-lock.yaml\"\n\t\t\t\t? \"pnpm\"\n\t\t\t\t: file === \"yarn.lock\"\n\t\t\t\t\t? \"yarn\"\n\t\t\t\t\t: \"npm\",\n\t\t),\n\t)\n\tif (kinds.length > 1)\n\t\treturn yield* PackageManagerError.make({\n\t\t\toperation: \"detect\",\n\t\t\tmessage: \"Conflicting lockfiles; select an agent explicitly\",\n\t\t})\n\tif (kinds[0] === \"yarn\") {\n\t\tconst lock = yield* fs.readFileString(\n\t\t\tpath.join(options.directory, \"yarn.lock\"),\n\t\t)\n\t\treturn /^__metadata:/m.test(lock) ? \"yarn-berry\" : \"yarn-classic\"\n\t}\n\tif (kinds[0] !== undefined) return kinds[0]\n\treturn Option.match(decodeUserAgent(options.userAgent ?? \"\"), {\n\t\tonNone: () => \"npm\" as const,\n\t\tonSome: ([name, , major]) => agentOf(name, major),\n\t})\n})\n\nexport interface PackageState {\n\treadonly spec: string\n\t/** Missing optional or not-yet-installed dependencies have no version/path. */\n\treadonly version: string | undefined\n\treadonly path: string | undefined\n}\n/** Direct dependencies only, keyed by their manifest names (including aliases). */\nexport type State = HashMap.HashMap<string, PackageState>\nexport interface PackageChange {\n\treadonly name: string\n\treadonly before: PackageState | undefined\n\treadonly after: PackageState | undefined\n}\nexport interface StateDiff {\n\treadonly added: ReadonlyArray<PackageChange>\n\treadonly removed: ReadonlyArray<PackageChange>\n\treadonly updated: ReadonlyArray<PackageChange>\n}\nexport const diffState = (before: State, after: State): StateDiff => {\n\tconst changes = Array.map(\n\t\tArray.sort(\n\t\t\tArray.dedupe([...HashMap.keys(before), ...HashMap.keys(after)]),\n\t\t\tString.Order,\n\t\t),\n\t\t(name) => ({\n\t\t\tname,\n\t\t\tbefore: Option.getOrUndefined(HashMap.get(before, name)),\n\t\t\tafter: Option.getOrUndefined(HashMap.get(after, name)),\n\t\t}),\n\t)\n\treturn {\n\t\tadded: Array.filter(changes, (change) => change.before === undefined),\n\t\tremoved: Array.filter(changes, (change) => change.after === undefined),\n\t\tupdated: Array.filter(\n\t\t\tchanges,\n\t\t\t({ before, after }) =>\n\t\t\t\tbefore !== undefined &&\n\t\t\t\tafter !== undefined &&\n\t\t\t\t(before.spec !== after.spec ||\n\t\t\t\t\tbefore.version !== after.version ||\n\t\t\t\t\tbefore.path !== after.path),\n\t\t),\n\t}\n}\nexport interface InstallResult extends StateDiff {\n\treadonly agent: Agent\n\t/** The install committed; retry only snapshot deletion, not installation. */\n\treadonly snapshotCleanupFailure: Option.Option<SnapshotCleanupFailure>\n}\nexport interface PackageManager {\n\treadonly directory: string\n\treadonly agent: Agent\n\treadonly readState: Effect.Effect<State, Error>\n\t/** Present when manual recovery is required; the manager cannot be reused. */\n\treadonly recoveryRequired: Effect.Effect<Option.Option<RecoveryRequiredError>>\n\t/** Serialized per manager. Empty changes installs the current manifest, including first installs. */\n\treadonly install: (changes: Changes) => Effect.Effect<InstallResult, Error>\n}\n\n/**\n * Own one manager per project. The caller must give this manager and its child\n * processes exclusive write access to the project for each install, including\n * recovery. Do not edit project files or run another installer concurrently.\n * Calls are serialized only within this manager; no cross-process lock is taken.\n * Managed paths must be regular files or absent. make and install reject symbolic\n * links and other file types with UnsupportedFileTypeError before any write.\n *\n * Accepted file writes finish before cancellation can start rollback or release\n * the lock. File writes and the original process group participate in the transaction.\n * Scripts must not create escaped writers in independent groups or daemons.\n * Failed or interrupted installs before commit restore backed-up package files byte-for-byte\n * with recorded permissions and ownership, and remove tracked files that did not\n * exist before installation. Writes made\n * during installation are overwritten by rollback, never merged. When a lockfile\n * existed, recovery also attempts to repair node_modules. Recovery failures are\n * combined with the original cause. If process termination cannot be confirmed,\n * no further restoration or recovery runs. Any failed rollback step also requires\n * manual recovery, even if all writers stopped. Only successful installation or\n * complete rollback permits snapshot deletion and reuse. RecoveryRequiredError\n * and recoveryRequired expose the retained snapshot directory and complete cause;\n * this manager rejects\n * all later installs. Complete manual recovery before making a new manager.\n * Committed installs report snapshot deletion failures in snapshotCleanupFailure\n * rather than failing or rolling back. No crash recovery is promised.\n */\nexport const make = (\n\toptions: Options,\n): Effect.Effect<\n\tPackageManager,\n\tError,\n\tFileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner\n> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem\n\t\tconst path = yield* Path.Path\n\t\tconst spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n\t\tconst directory = path.resolve(options.directory)\n\t\tyield* inspectManagedFiles(fs, path, directory, backupFiles)\n\t\tconst agent = yield* detectAgent({ ...options, directory })\n\t\tconst lock = yield* Semaphore.make(1)\n\t\tconst transaction = yield* Ref.make<ManagerState>(ManagerState.Ready())\n\t\tconst manifestFile = path.join(directory, \"package.json\")\n\t\tconst readManifest = readSource(fs, manifestFile).pipe(\n\t\t\tEffect.flatMap(decodeManifest),\n\t\t)\n\t\tconst installed = Schema.decodeUnknownEffect(\n\t\t\tSchema.fromJsonString(\n\t\t\t\tSchema.Struct({ version: Schema.optionalKey(Schema.String) }),\n\t\t\t),\n\t\t)\n\t\tconst stateOf = Effect.fnUntraced(function* (manifest: Manifest) {\n\t\t\tif (yield* fs.exists(path.join(directory, \".pnp.cjs\")))\n\t\t\t\treturn yield* PackageManagerError.make({\n\t\t\t\t\toperation: \"state\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Plug'n'Play is not supported; configure Yarn with nodeLinker: node-modules\",\n\t\t\t\t})\n\t\t\tconst deps = Array.reduce(\n\t\t\t\tfields,\n\t\t\t\t{} as Readonly<Record<string, string>>,\n\t\t\t\t(all, field) => ({ ...all, ...manifest[field] }),\n\t\t\t)\n\t\t\tconst entries = yield* Effect.forEach(\n\t\t\t\tRecord.toEntries(deps),\n\t\t\t\t([name, spec]) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst file = path.join(\n\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\t\"node_modules\",\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\"package.json\",\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (!(yield* fs.exists(file)))\n\t\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\t{ spec, version: undefined, path: undefined },\n\t\t\t\t\t\t\t] as const\n\t\t\t\t\t\tconst pkg = yield* fs\n\t\t\t\t\t\t\t.readFileString(file)\n\t\t\t\t\t\t\t.pipe(Effect.flatMap(installed))\n\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tspec,\n\t\t\t\t\t\t\t\tversion: pkg.version,\n\t\t\t\t\t\t\t\tpath: yield* fs.realPath(path.dirname(file)),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t] as const\n\t\t\t\t\t}),\n\t\t\t)\n\t\t\treturn HashMap.fromIterable<string, PackageState>(entries)\n\t\t})\n\t\tconst readState = Effect.flatMap(readManifest, stateOf)\n\t\tconst termination: ChildProcess.KillOptions = {\n\t\t\tforceKillAfter: \"5 seconds\",\n\t\t}\n\t\tconst run = Effect.fnUntraced(function* (operation: \"install\" | \"recover\") {\n\t\t\tconst recovery = operation === \"recover\"\n\t\t\tconst args =\n\t\t\t\tagent === \"npm\"\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t\"install\",\n\t\t\t\t\t\t\t\"--no-audit\",\n\t\t\t\t\t\t\t\"--no-fund\",\n\t\t\t\t\t\t\t...(recovery ? [\"--prefer-offline\"] : []),\n\t\t\t\t\t\t]\n\t\t\t\t\t: agent === \"pnpm\"\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\"install\",\n\t\t\t\t\t\t\t\trecovery ? \"--frozen-lockfile\" : \"--no-frozen-lockfile\",\n\t\t\t\t\t\t\t\t\"--config.confirmModulesPurge=false\",\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t: agent === \"yarn-classic\"\n\t\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\t\"install\",\n\t\t\t\t\t\t\t\t\t\"--non-interactive\",\n\t\t\t\t\t\t\t\t\t...(recovery\n\t\t\t\t\t\t\t\t\t\t? [\"--frozen-lockfile\", \"--prefer-offline\"]\n\t\t\t\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t: [\"install\", ...(recovery ? [\"--immutable\"] : [])]\n\t\t\t// Acquire and install the release handler atomically, then wait interruptibly.\n\t\t\treturn yield* Effect.acquireUseRelease(\n\t\t\t\tspawner\n\t\t\t\t\t.spawn(\n\t\t\t\t\t\tChildProcess.make(executable(agent), args, {\n\t\t\t\t\t\t\tcwd: directory,\n\t\t\t\t\t\t\textendEnv: true,\n\t\t\t\t\t\t\tstdin: \"ignore\",\n\t\t\t\t\t\t\tstdout: \"inherit\",\n\t\t\t\t\t\t\tstderr: \"inherit\",\n\t\t\t\t\t\t\tdetached: true,\n\t\t\t\t\t\t\t...termination,\n\t\t\t\t\t\t\tenv: {\n\t\t\t\t\t\t\t\tCI: \"true\",\n\t\t\t\t\t\t\t\t...(agent === \"yarn-berry\"\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\tYARN_ENABLE_IMMUTABLE_INSTALLS: recovery\n\t\t\t\t\t\t\t\t\t\t\t\t? \"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t: \"false\",\n\t\t\t\t\t\t\t\t\t\t\tYARN_NODE_LINKER: \"node-modules\",\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\t.pipe(\n\t\t\t\t\t\tEffect.tap(() =>\n\t\t\t\t\t\t\tRef.set(\n\t\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\t\tManagerState.Active({ writer: WriterState.Running() }),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t(process) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst code = yield* process.exitCode.pipe(\n\t\t\t\t\t\t\tEffect.timeoutOrElse({\n\t\t\t\t\t\t\t\tduration: \"10 minutes\",\n\t\t\t\t\t\t\t\torElse: () =>\n\t\t\t\t\t\t\t\t\tPackageManagerError.make({\n\t\t\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\t\t\tmessage: `${executable(agent)} ${operation} timed out`,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (code !== 0)\n\t\t\t\t\t\t\treturn yield* PackageManagerError.make({\n\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\texitCode: code,\n\t\t\t\t\t\t\t\tmessage: `${executable(agent)} ${operation} exited with code ${code}`,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t}),\n\t\t\t\t// The command owns its group; a stopped leader may leave live descendants.\n\t\t\t\t// Observe kill failures here rather than relying on best-effort scope cleanup.\n\t\t\t\t(process) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst result = yield* terminateProcess(process, termination)\n\t\t\t\t\t\tyield* Ref.set(\n\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\tManagerState.Active({\n\t\t\t\t\t\t\t\twriter:\n\t\t\t\t\t\t\t\t\tresult._tag === \"OriginalGroupStopped\"\n\t\t\t\t\t\t\t\t\t\t? WriterState.ManagedWritersIdle()\n\t\t\t\t\t\t\t\t\t\t: WriterState.Unconfirmed({\n\t\t\t\t\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\t\t\t\t\tprocessId: process.pid,\n\t\t\t\t\t\t\t\t\t\t\t\tcause: result.cause,\n\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (result._tag === \"Unconfirmed\")\n\t\t\t\t\t\t\treturn yield* Effect.failCause(result.cause)\n\t\t\t\t\t}),\n\t\t\t)\n\t\t}, Effect.scoped)\n\t\t// AbortSignal does not join accepted OS writes. Keep the writer owned until completion.\n\t\tconst withFileWriter = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n\t\t\tEffect.acquireUseRelease(\n\t\t\t\tRef.set(\n\t\t\t\t\ttransaction,\n\t\t\t\t\tManagerState.Active({ writer: WriterState.FileWrite() }),\n\t\t\t\t),\n\t\t\t\t() => effect,\n\t\t\t\t() =>\n\t\t\t\t\tRef.set(\n\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\tManagerState.Active({ writer: WriterState.ManagedWritersIdle() }),\n\t\t\t\t\t),\n\t\t\t).pipe(Effect.uninterruptible)\n\n\t\tconst install = (changes: Changes) =>\n\t\t\tlock.withPermits(1)(\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\tif (state._tag === \"RecoveryRequired\") return yield* state.error\n\t\t\t\t\tconst validated = yield* Schema.decodeEffect(Changes)(changes)\n\t\t\t\t\tconst inspected = yield* inspectManagedFiles(\n\t\t\t\t\t\tfs,\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\tbackupFiles,\n\t\t\t\t\t)\n\t\t\t\t\tconst backups = yield* Effect.forEach(\n\t\t\t\t\t\tinspected,\n\t\t\t\t\t\t({ name, file, metadata }) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tconst contents = yield* Option.match(metadata, {\n\t\t\t\t\t\t\t\t\tonNone: () => Effect.succeedNone,\n\t\t\t\t\t\t\t\t\tonSome: (info) =>\n\t\t\t\t\t\t\t\t\t\tfs\n\t\t\t\t\t\t\t\t\t\t\t.readFile(file)\n\t\t\t\t\t\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\t\t\t\t\t\tEffect.map((bytes) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tOption.some<Contents>({ ...info, bytes }),\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\treturn { name, file, contents }\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\tconst original = yield* readSource(fs, manifestFile)\n\t\t\t\t\tconst manifest = yield* decodeManifest(original)\n\t\t\t\t\tconst before = yield* stateOf(manifest)\n\t\t\t\t\tconst text = yield* encodeManifest(\n\t\t\t\t\t\tapplyChanges(manifest, validated),\n\t\t\t\t\t\toriginal,\n\t\t\t\t\t)\n\n\t\t\t\t\tconst agentLocks =\n\t\t\t\t\t\tagent === \"npm\"\n\t\t\t\t\t\t\t? [\"package-lock.json\", \"npm-shrinkwrap.json\"]\n\t\t\t\t\t\t\t: agent === \"pnpm\"\n\t\t\t\t\t\t\t\t? [\"pnpm-lock.yaml\"]\n\t\t\t\t\t\t\t\t: [\"yarn.lock\"]\n\t\t\t\t\tconst hasLock = Array.some(\n\t\t\t\t\t\tbackups,\n\t\t\t\t\t\t({ name, contents }) =>\n\t\t\t\t\t\t\tArray.contains(agentLocks, name) && Option.isSome(contents),\n\t\t\t\t\t)\n\t\t\t\t\tconst completed = yield* withSnapshot(\n\t\t\t\t\t\tfs,\n\t\t\t\t\t\tRef.get(transaction).pipe(\n\t\t\t\t\t\t\tEffect.map((state) => state._tag === \"Ready\"),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t(snapshotDirectory) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tconst snapshot = yield* writeSnapshot(\n\t\t\t\t\t\t\t\t\tfs,\n\t\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\t\tsnapshotDirectory,\n\t\t\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\t\t\tagent,\n\t\t\t\t\t\t\t\t\tbackups,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tconst restore = Effect.flatMap(Ref.get(transaction), (state) =>\n\t\t\t\t\t\t\t\t\tstate._tag === \"Active\" &&\n\t\t\t\t\t\t\t\t\tstate.writer._tag === \"ManagedWritersIdle\"\n\t\t\t\t\t\t\t\t\t\t? withFileWriter(snapshot.restore(state.writer))\n\t\t\t\t\t\t\t\t\t\t: Effect.void,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\treturn yield* Effect.acquireUseRelease(\n\t\t\t\t\t\t\t\t\tRef.set(\n\t\t\t\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\t\t\t\tManagerState.Active({\n\t\t\t\t\t\t\t\t\t\t\twriter: WriterState.ManagedWritersIdle(),\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\t\t\t\tyield* withFileWriter(\n\t\t\t\t\t\t\t\t\t\t\t\tfs.writeFileString(manifestFile, text),\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\tyield* run(\"install\")\n\t\t\t\t\t\t\t\t\t\t\tconst after = yield* readState\n\t\t\t\t\t\t\t\t\t\t\treturn { agent, ...diffState(before, after) }\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t(_, exit) =>\n\t\t\t\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\t\t\t\tif (Exit.isFailure(exit)) {\n\t\t\t\t\t\t\t\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t\t\t\tstate._tag !== \"Active\" ||\n\t\t\t\t\t\t\t\t\t\t\t\t\tstate.writer._tag !== \"ManagedWritersIdle\"\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t\t\tyield* withFileWriter(snapshot.restore(state.writer))\n\t\t\t\t\t\t\t\t\t\t\t\tif (hasLock)\n\t\t\t\t\t\t\t\t\t\t\t\t\tyield* run(\"recover\").pipe(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// Even a failed recovery must restore files, once its writer has stopped.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tEffect.onExit(() => restore),\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t// Idle managed writers alone do not prove that the transaction completed.\n\t\t\t\t\t\t\t\t\t\t\tyield* Ref.set(transaction, ManagerState.Ready())\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t).pipe(\n\t\t\t\t\t\t\t\t\tEffect.onExit((exit) =>\n\t\t\t\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\t\t\t\t\t\t\tif (Exit.isSuccess(exit) || state._tag !== \"Active\")\n\t\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t\tconst writer = state.writer\n\t\t\t\t\t\t\t\t\t\t\tconst error = RecoveryRequiredError.make({\n\t\t\t\t\t\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\t\t\t\t\t\tsnapshotDirectory,\n\t\t\t\t\t\t\t\t\t\t\t\toperation:\n\t\t\t\t\t\t\t\t\t\t\t\t\twriter._tag === \"Unconfirmed\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? writer.operation\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"rollback\",\n\t\t\t\t\t\t\t\t\t\t\t\t...(writer._tag === \"Unconfirmed\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprocessId: writer.processId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tterminationCause: writer.cause,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t\t\t\t\tmessage: `Manual recovery is required. Stop all writers before restoring the snapshot at ${snapshotDirectory}`,\n\t\t\t\t\t\t\t\t\t\t\t\trecoveryCause: exit.cause,\n\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\tyield* Ref.set(\n\t\t\t\t\t\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\t\t\t\t\t\tManagerState.RecoveryRequired({ error }),\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\treturn yield* error\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...completed.value,\n\t\t\t\t\t\tsnapshotCleanupFailure: completed.snapshotCleanupFailure,\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t)\n\t\treturn {\n\t\t\tdirectory,\n\t\t\tagent,\n\t\t\trecoveryRequired: Ref.get(transaction).pipe(\n\t\t\t\tEffect.map((state) =>\n\t\t\t\t\tstate._tag === \"RecoveryRequired\"\n\t\t\t\t\t\t? Option.some(state.error)\n\t\t\t\t\t\t: Option.none(),\n\t\t\t\t),\n\t\t\t),\n\t\t\treadState: lock.withPermits(1)(readState),\n\t\t\tinstall,\n\t\t}\n\t})\n"],"mappings":";;;;;;;;AA+CA,MAAa,QAAQ,OAAO,SAAS;CACpC;CACA;CACA;CACA;AACD,CAAC;AAED,IAAa,sBAAb,cAAyC,OAAO,YAG9C,CAAC,CAAC,uBAAuB;CAC1B,WAAW,OAAO,SAAS;EAAC;EAAU;EAAW;EAAW;CAAO,CAAC;CACpE,SAAS,OAAO;CAChB,UAAU,OAAO,YAAY,OAAO,GAAG;AACxC,CAAC,CAAC,CAAC,CAAC;AACJ,IAAa,wBAAb,cAA2C,OAAO,YAGhD,CAAC,CAAC,yBAAyB;CAC5B,WAAW,OAAO;CAClB,mBAAmB,OAAO;CAC1B,WAAW,OAAO,SAAS;EAAC;EAAW;EAAW;CAAU,CAAC;CAC7D,WAAW,OAAO,YAAY,OAAO,GAAG;CACxC,SAAS,OAAO;CAChB,kBAAkB,OAAO,YACxB,OAAO,MAAM,OAAO,SAAS,OAAO,OAAO,CAC5C;CACA,eAAe,OAAO,MAAM,OAAO,SAAS,OAAO,OAAO;AAC3D,CAAC,CAAC,CAAC,CAAC;AAaJ,MAAM,cAAc,KAAK,WAAwB;AAQjD,MAAM,eAAe,KAAK,WAAyB;AAgBnD,MAAM,YAAY;CACjB;CACA;CACA;CACA;AACD;AACA,MAAM,cAAc;CACnB;CACA,GAAG;CACH;CACA;CACA;CACA;CACA;CACA;CACA;AACD;AACA,MAAM,cAAc,UACnB,OAAO,WAAW,MAAM,CAAC,CAAC,KAAK,IAAI,SAAS;AAE7C,MAAM,aAAa,OAAO,WAAW,WACpC,IACA,MACC;CACD,MAAM,QAAQ,OAAO,GAAG,SAAS,IAAI;CACrC,OAAO,OAAO,OAAO,IAAI;EACxB,WACC,IAAI,YAAY,SAAS;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC,CAAC,CAAC,OAAO,KAAK;EACxE,QAAQ,UACP,cAAc,YAAY;GACzB,QAAQ;GACR,QAAQ;GACR,aAAa;GACb;EACD,CAAC;CACH,CAAC;AACF,CAAC;AAED,MAAM,aAAa;AACnB,MAAM,SAAS,OAAO,OAAO,MAAM,OAAO,UAAU,OAAO,CAAC;AAC5D,MAAM,uBAAuB,OAAO,oBACnC,yBACD;;AAEA,MAAM,kBAAkB,OAAO,oBAC9B,OAAO,sBAAsB;CAAC;CAAY;CAAK;CAAQ,OAAO;AAAM,CAAC,CACtE;AACA,MAAM,WAAW,MAA8B,UAC9C,SAAS,SAAU,UAAU,MAAM,iBAAiB,eAAgB;;AAGrE,MAAa,cAAc,OAAO,WAAW,WAAW,SAAkB;CACzE,IAAI,QAAQ,UAAU,KAAA,GACrB,OAAO,OAAO,OAAO,aAAa,KAAK,CAAC,CAAC,QAAQ,KAAK;CACvD,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,WAAW,OAAO,WACvB,IACA,KAAK,KAAK,QAAQ,WAAW,cAAc,CAC5C,CAAC,CAAC,KAAK,OAAO,QAAQ,cAAc,CAAC;CACrC,IAAI,SAAS,mBAAmB,KAAA,GAC/B,OAAO,OAAO,OAAO,MAAM,qBAAqB,SAAS,cAAc,GAAG;EACzE,cACC,oBAAoB,KAAK;GACxB,WAAW;GACX,SAAS,+BAA+B,SAAS;EAClD,CAAC;EACF,SAAS,CAAC,QAAQ,aACjB,OAAO,QAAQ,QAAQ,MAAM,GAAG,QAAQ,OAAO,CAAC;CAClD,CAAC;CAEF,MAAM,QAAQ,OAAO,OAAO,OAAO,YAAY,SAC9C,GAAG,OAAO,KAAK,KAAK,QAAQ,WAAW,IAAI,CAAC,CAC7C;CACA,MAAM,QAAQ,MAAM,OACnB,MAAM,IAAI,QAAQ,SACjB,SAAS,mBACN,SACA,SAAS,cACR,SACA,KACL,CACD;CACA,IAAI,MAAM,SAAS,GAClB,OAAO,OAAO,oBAAoB,KAAK;EACtC,WAAW;EACX,SAAS;CACV,CAAC;CACF,IAAI,MAAM,OAAO,QAAQ;EACxB,MAAM,OAAO,OAAO,GAAG,eACtB,KAAK,KAAK,QAAQ,WAAW,WAAW,CACzC;EACA,OAAO,gBAAgB,KAAK,IAAI,IAAI,eAAe;CACpD;CACA,IAAI,MAAM,OAAO,KAAA,GAAW,OAAO,MAAM;CACzC,OAAO,OAAO,MAAM,gBAAgB,QAAQ,aAAa,EAAE,GAAG;EAC7D,cAAc;EACd,SAAS,CAAC,QAAQ,WAAW,QAAQ,MAAM,KAAK;CACjD,CAAC;AACF,CAAC;AAoBD,MAAa,aAAa,QAAe,UAA4B;CACpE,MAAM,UAAU,MAAM,IACrB,MAAM,KACL,MAAM,OAAO,CAAC,GAAG,QAAQ,KAAK,MAAM,GAAG,GAAG,QAAQ,KAAK,KAAK,CAAC,CAAC,GAC9D,OAAO,KACR,IACC,UAAU;EACV;EACA,QAAQ,OAAO,eAAe,QAAQ,IAAI,QAAQ,IAAI,CAAC;EACvD,OAAO,OAAO,eAAe,QAAQ,IAAI,OAAO,IAAI,CAAC;CACtD,EACD;CACA,OAAO;EACN,OAAO,MAAM,OAAO,UAAU,WAAW,OAAO,WAAW,KAAA,CAAS;EACpE,SAAS,MAAM,OAAO,UAAU,WAAW,OAAO,UAAU,KAAA,CAAS;EACrE,SAAS,MAAM,OACd,UACC,EAAE,QAAQ,YACV,WAAW,KAAA,KACX,UAAU,KAAA,MACT,OAAO,SAAS,MAAM,QACtB,OAAO,YAAY,MAAM,WACzB,OAAO,SAAS,MAAM,KACzB;CACD;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,MAAa,QACZ,YAMA,OAAO,IAAI,aAAa;CACvB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,UAAU,OAAO,oBAAoB;CAC3C,MAAM,YAAY,KAAK,QAAQ,QAAQ,SAAS;CAChD,OAAO,oBAAoB,IAAI,MAAM,WAAW,WAAW;CAC3D,MAAM,QAAQ,OAAO,YAAY;EAAE,GAAG;EAAS;CAAU,CAAC;CAC1D,MAAM,OAAO,OAAO,UAAU,KAAK,CAAC;CACpC,MAAM,cAAc,OAAO,IAAI,KAAmB,aAAa,MAAM,CAAC;CACtE,MAAM,eAAe,KAAK,KAAK,WAAW,cAAc;CACxD,MAAM,eAAe,WAAW,IAAI,YAAY,CAAC,CAAC,KACjD,OAAO,QAAQ,cAAc,CAC9B;CACA,MAAM,YAAY,OAAO,oBACxB,OAAO,eACN,OAAO,OAAO,EAAE,SAAS,OAAO,YAAY,OAAO,MAAM,EAAE,CAAC,CAC7D,CACD;CACA,MAAM,UAAU,OAAO,WAAW,WAAW,UAAoB;EAChE,IAAI,OAAO,GAAG,OAAO,KAAK,KAAK,WAAW,UAAU,CAAC,GACpD,OAAO,OAAO,oBAAoB,KAAK;GACtC,WAAW;GACX,SACC;EACF,CAAC;EACF,MAAM,OAAO,MAAM,OAClB,QACA,CAAC,IACA,KAAK,WAAW;GAAE,GAAG;GAAK,GAAG,SAAS;EAAO,EAC/C;EACA,MAAM,UAAU,OAAO,OAAO,QAC7B,OAAO,UAAU,IAAI,IACpB,CAAC,MAAM,UACP,OAAO,IAAI,aAAa;GACvB,MAAM,OAAO,KAAK,KACjB,WACA,gBACA,MACA,cACD;GACA,IAAI,EAAE,OAAO,GAAG,OAAO,IAAI,IAC1B,OAAO,CACN,MACA;IAAE;IAAM,SAAS,KAAA;IAAW,MAAM,KAAA;GAAU,CAC7C;GAID,OAAO,CACN,MACA;IACC;IACA,UAAS,OAPQ,GACjB,eAAe,IAAI,CAAC,CACpB,KAAK,OAAO,QAAQ,SAAS,CAAC,EAAA,CAKjB;IACb,MAAM,OAAO,GAAG,SAAS,KAAK,QAAQ,IAAI,CAAC;GAC5C,CACD;EACD,CAAC,CACH;EACA,OAAO,QAAQ,aAAmC,OAAO;CAC1D,CAAC;CACD,MAAM,YAAY,OAAO,QAAQ,cAAc,OAAO;CACtD,MAAM,cAAwC,EAC7C,gBAAgB,YACjB;CACA,MAAM,MAAM,OAAO,WAAW,WAAW,WAAkC;EAC1E,MAAM,WAAW,cAAc;EAC/B,MAAM,OACL,UAAU,QACP;GACA;GACA;GACA;GACA,GAAI,WAAW,CAAC,kBAAkB,IAAI,CAAC;EACxC,IACC,UAAU,SACT;GACA;GACA,WAAW,sBAAsB;GACjC;EACD,IACC,UAAU,iBACT;GACA;GACA;GACA,GAAI,WACD,CAAC,qBAAqB,kBAAkB,IACxC,CAAC;EACL,IACC,CAAC,WAAW,GAAI,WAAW,CAAC,aAAa,IAAI,CAAC,CAAE;EAEtD,OAAO,OAAO,OAAO,kBACpB,QACE,MACA,aAAa,KAAK,WAAW,KAAK,GAAG,MAAM;GAC1C,KAAK;GACL,WAAW;GACX,OAAO;GACP,QAAQ;GACR,QAAQ;GACR,UAAU;GACV,GAAG;GACH,KAAK;IACJ,IAAI;IACJ,GAAI,UAAU,eACX;KACA,gCAAgC,WAC7B,SACA;KACH,kBAAkB;IACnB,IACC,CAAC;GACL;EACD,CAAC,CACF,CAAC,CACA,KACA,OAAO,UACN,IAAI,IACH,aACA,aAAa,OAAO,EAAE,QAAQ,YAAY,QAAQ,EAAE,CAAC,CACtD,CACD,CACD,IACA,YACA,OAAO,IAAI,aAAa;GACvB,MAAM,OAAO,OAAO,QAAQ,SAAS,KACpC,OAAO,cAAc;IACpB,UAAU;IACV,cACC,oBAAoB,KAAK;KACxB;KACA,SAAS,GAAG,WAAW,KAAK,EAAE,GAAG,UAAU;IAC5C,CAAC;GACH,CAAC,CACF;GACA,IAAI,SAAS,GACZ,OAAO,OAAO,oBAAoB,KAAK;IACtC;IACA,UAAU;IACV,SAAS,GAAG,WAAW,KAAK,EAAE,GAAG,UAAU,oBAAoB;GAChE,CAAC;EACH,CAAC,IAGD,YACA,OAAO,IAAI,aAAa;GACvB,MAAM,SAAS,OAAO,iBAAiB,SAAS,WAAW;GAC3D,OAAO,IAAI,IACV,aACA,aAAa,OAAO,EACnB,QACC,OAAO,SAAS,yBACb,YAAY,mBAAmB,IAC/B,YAAY,YAAY;IACxB;IACA,WAAW,QAAQ;IACnB,OAAO,OAAO;GACf,CAAC,EACL,CAAC,CACF;GACA,IAAI,OAAO,SAAS,eACnB,OAAO,OAAO,OAAO,UAAU,OAAO,KAAK;EAC7C,CAAC,CACH;CACD,GAAG,OAAO,MAAM;CAEhB,MAAM,kBAA2B,WAChC,OAAO,kBACN,IAAI,IACH,aACA,aAAa,OAAO,EAAE,QAAQ,YAAY,UAAU,EAAE,CAAC,CACxD,SACM,cAEL,IAAI,IACH,aACA,aAAa,OAAO,EAAE,QAAQ,YAAY,mBAAmB,EAAE,CAAC,CACjE,CACF,CAAC,CAAC,KAAK,OAAO,eAAe;CAE9B,MAAM,WAAW,YAChB,KAAK,YAAY,CAAC,CAAC,CAClB,OAAO,IAAI,aAAa;EACvB,MAAM,QAAQ,OAAO,IAAI,IAAI,WAAW;EACxC,IAAI,MAAM,SAAS,oBAAoB,OAAO,OAAO,MAAM;EAC3D,MAAM,YAAY,OAAO,OAAO,aAAa,OAAO,CAAC,CAAC,OAAO;EAC7D,MAAM,YAAY,OAAO,oBACxB,IACA,MACA,WACA,WACD;EACA,MAAM,UAAU,OAAO,OAAO,QAC7B,YACC,EAAE,MAAM,MAAM,eACd,OAAO,IAAI,aAAa;GAYvB,OAAO;IAAE;IAAM;IAAM,UAAA,OAXG,OAAO,MAAM,UAAU;KAC9C,cAAc,OAAO;KACrB,SAAS,SACR,GACE,SAAS,IAAI,CAAC,CACd,KACA,OAAO,KAAK,UACX,OAAO,KAAe;MAAE,GAAG;MAAM;KAAM,CAAC,CACzC,CACD;IACH,CAAC;GAC6B;EAC/B,CAAC,CACH;EACA,MAAM,WAAW,OAAO,WAAW,IAAI,YAAY;EACnD,MAAM,WAAW,OAAO,eAAe,QAAQ;EAC/C,MAAM,SAAS,OAAO,QAAQ,QAAQ;EACtC,MAAM,OAAO,OAAO,eACnB,aAAa,UAAU,SAAS,GAChC,QACD;EAEA,MAAM,aACL,UAAU,QACP,CAAC,qBAAqB,qBAAqB,IAC3C,UAAU,SACT,CAAC,gBAAgB,IACjB,CAAC,WAAW;EACjB,MAAM,UAAU,MAAM,KACrB,UACC,EAAE,MAAM,eACR,MAAM,SAAS,YAAY,IAAI,KAAK,OAAO,OAAO,QAAQ,CAC5D;EACA,MAAM,YAAY,OAAO,aACxB,IACA,IAAI,IAAI,WAAW,CAAC,CAAC,KACpB,OAAO,KAAK,UAAU,MAAM,SAAS,OAAO,CAC7C,IACC,sBACA,OAAO,IAAI,aAAa;GACvB,MAAM,WAAW,OAAO,cACvB,IACA,MACA,mBACA,WACA,OACA,OACD;GACA,MAAM,UAAU,OAAO,QAAQ,IAAI,IAAI,WAAW,IAAI,UACrD,MAAM,SAAS,YACf,MAAM,OAAO,SAAS,uBACnB,eAAe,SAAS,QAAQ,MAAM,MAAM,CAAC,IAC7C,OAAO,IACX;GACA,OAAO,OAAO,OAAO,kBACpB,IAAI,IACH,aACA,aAAa,OAAO,EACnB,QAAQ,YAAY,mBAAmB,EACxC,CAAC,CACF,SAEC,OAAO,IAAI,aAAa;IACvB,OAAO,eACN,GAAG,gBAAgB,cAAc,IAAI,CACtC;IACA,OAAO,IAAI,SAAS;IACpB,MAAM,QAAQ,OAAO;IACrB,OAAO;KAAE;KAAO,GAAG,UAAU,QAAQ,KAAK;IAAE;GAC7C,CAAC,IACD,GAAG,SACH,OAAO,IAAI,aAAa;IACvB,IAAI,KAAK,UAAU,IAAI,GAAG;KACzB,MAAM,QAAQ,OAAO,IAAI,IAAI,WAAW;KACxC,IACC,MAAM,SAAS,YACf,MAAM,OAAO,SAAS,sBAEtB;KACD,OAAO,eAAe,SAAS,QAAQ,MAAM,MAAM,CAAC;KACpD,IAAI,SACH,OAAO,IAAI,SAAS,CAAC,CAAC,KAErB,OAAO,aAAa,OAAO,CAC5B;IACF;IAEA,OAAO,IAAI,IAAI,aAAa,aAAa,MAAM,CAAC;GACjD,CAAC,CACH,CAAC,CAAC,KACD,OAAO,QAAQ,SACd,OAAO,IAAI,aAAa;IACvB,MAAM,QAAQ,OAAO,IAAI,IAAI,WAAW;IACxC,IAAI,KAAK,UAAU,IAAI,KAAK,MAAM,SAAS,UAC1C;IACD,MAAM,SAAS,MAAM;IACrB,MAAM,QAAQ,sBAAsB,KAAK;KACxC;KACA;KACA,WACC,OAAO,SAAS,gBACb,OAAO,YACP;KACJ,GAAI,OAAO,SAAS,gBACjB;MACA,WAAW,OAAO;MAClB,kBAAkB,OAAO;KAC1B,IACC,CAAC;KACJ,SAAS,kFAAkF;KAC3F,eAAe,KAAK;IACrB,CAAC;IACD,OAAO,IAAI,IACV,aACA,aAAa,iBAAiB,EAAE,MAAM,CAAC,CACxC;IACA,OAAO,OAAO;GACf,CAAC,CACF,CACD;EACD,CAAC,CACH;EACA,OAAO;GACN,GAAG,UAAU;GACb,wBAAwB,UAAU;EACnC;CACD,CAAC,CACF;CACD,OAAO;EACN;EACA;EACA,kBAAkB,IAAI,IAAI,WAAW,CAAC,CAAC,KACtC,OAAO,KAAK,UACX,MAAM,SAAS,qBACZ,OAAO,KAAK,MAAM,KAAK,IACvB,OAAO,KAAK,CAChB,CACD;EACA,WAAW,KAAK,YAAY,CAAC,CAAC,CAAC,SAAS;EACxC;CACD;AACD,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/** Non-interactive package installation. Provide platform services at the application boundary. */\nimport {\n\tArray,\n\tCause,\n\tData,\n\tRef,\n\tEffect,\n\tExit,\n\tFileSystem,\n\tHashMap,\n\tMatch,\n\tOption,\n\tPath,\n\tPlatformError,\n\tRecord,\n\tSchema,\n\tSemaphore,\n\tString,\n\tStream,\n} from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/process\"\nimport {\n\tPackageManagerDeclaration,\n\tPackageManagerName,\n} from \"./PackageManagerDeclaration\"\nexport { PackageManagerDeclaration } from \"./PackageManagerDeclaration\"\nimport { terminateProcess } from \"./process\"\nimport {\n\twriteSnapshot,\n\twithSnapshot,\n\ttype SnapshotCleanupFailure,\n\tinspectManagedFiles,\n\tUnsupportedFileTypeError,\n\ttype Contents,\n} from \"./snapshot\"\nexport {\n\tUnsupportedFileTypeError,\n\ttype SnapshotCleanupFailure,\n} from \"./snapshot\"\nimport {\n\tapplyChanges,\n\tChanges,\n\tdecodeManifest,\n\tencodeManifest,\n\tfields,\n\ttype Manifest,\n} from \"./manifest\"\nexport { Changes, PackageName } from \"./manifest\"\n\nexport const Agent = Schema.Literals([\n\t\"npm\",\n\t\"pnpm\",\n\t\"yarn-classic\",\n\t\"yarn-berry\",\n])\nexport type Agent = typeof Agent.Type\nexport class PackageManagerError extends Schema.TaggedError<\n\tPackageManagerError,\n\t{ readonly brand: unique symbol }\n>()(\"PackageManagerError\", {\n\toperation: Schema.Literals([\"detect\", \"install\", \"recover\", \"state\"]),\n\tmessage: Schema.String,\n\texitCode: Schema.optionalKey(Schema.Int),\n}) {}\nexport class RecoveryRequiredError extends Schema.TaggedError<\n\tRecoveryRequiredError,\n\t{ readonly brand: unique symbol }\n>()(\"RecoveryRequiredError\", {\n\tdirectory: Schema.String,\n\tsnapshotDirectory: Schema.String,\n\toperation: Schema.Literals([\"install\", \"recover\", \"rollback\"]),\n\tprocessId: Schema.optionalKey(Schema.Int),\n\tmessage: Schema.String,\n\tterminationCause: Schema.optionalKey(\n\t\tSchema.Cause(Schema.Unknown, Schema.Unknown),\n\t),\n\trecoveryCause: Schema.Cause(Schema.Unknown, Schema.Unknown),\n}) {}\n\ntype WriterState = Data.TaggedEnum<{\n\tManagedWritersIdle: {}\n\tRunning: {}\n\tFileWrite: {}\n\tUnconfirmed: {\n\t\treadonly operation: \"install\" | \"recover\"\n\t\treadonly processId: number\n\t\treadonly cause: Cause.Cause<PlatformError.PlatformError>\n\t}\n}>\n\nconst WriterState = Data.taggedEnum<WriterState>()\n\ntype ManagerState = Data.TaggedEnum<{\n\tReady: {}\n\tActive: { readonly writer: WriterState }\n\tRecoveryRequired: { readonly error: RecoveryRequiredError }\n}>\n\nconst ManagerState = Data.taggedEnum<ManagerState>()\n\nexport type Error =\n\t| PackageManagerError\n\t| UnsupportedFileTypeError\n\t| RecoveryRequiredError\n\t| PlatformError.PlatformError\n\t| Schema.SchemaError\nexport interface Options {\n\t/** Project directory, containing package.json. Use the workspace root for workspace installs. */\n\treadonly directory: string\n\t/** Explicit selection takes precedence over detection. */\n\treadonly agent?: Agent\n\t/** Optional npm_config_user_agent supplied by the caller; used only as a fallback. */\n\treadonly userAgent?: string\n}\nconst lockfiles = [\n\t\"package-lock.json\",\n\t\"npm-shrinkwrap.json\",\n\t\"pnpm-lock.yaml\",\n\t\"yarn.lock\",\n] as const\nconst backupFiles = [\n\t\"package.json\",\n\t...lockfiles,\n\t\".npmrc\",\n\t\"pnpm-workspace.yaml\",\n\t\".yarnrc\",\n\t\".yarnrc.yml\",\n\t\".yarn/install-state.gz\",\n\t\".pnp.cjs\",\n\t\".pnp.loader.mjs\",\n]\nconst strategy = (agent: Agent, operation: \"install\" | \"recover\") => {\n\tconst recovery = operation === \"recover\"\n\treturn Match.value(agent).pipe(\n\t\tMatch.when(\"npm\", () => ({\n\t\t\texecutable: \"npm\",\n\t\t\targs: [\n\t\t\t\t\"install\",\n\t\t\t\t\"--no-audit\",\n\t\t\t\t\"--no-fund\",\n\t\t\t\t...(recovery ? [\"--prefer-offline\"] : []),\n\t\t\t],\n\t\t\tenv: { CI: \"true\" },\n\t\t\tlockfiles: [\"package-lock.json\", \"npm-shrinkwrap.json\"],\n\t\t})),\n\t\tMatch.when(\"pnpm\", () => ({\n\t\t\texecutable: \"pnpm\",\n\t\t\targs: [\n\t\t\t\t\"install\",\n\t\t\t\trecovery ? \"--frozen-lockfile\" : \"--no-frozen-lockfile\",\n\t\t\t\t\"--config.confirmModulesPurge=false\",\n\t\t\t],\n\t\t\tenv: { CI: \"true\" },\n\t\t\tlockfiles: [\"pnpm-lock.yaml\"],\n\t\t})),\n\t\tMatch.when(\"yarn-classic\", () => ({\n\t\t\texecutable: \"yarn\",\n\t\t\targs: [\n\t\t\t\t\"install\",\n\t\t\t\t\"--non-interactive\",\n\t\t\t\t...(recovery ? [\"--frozen-lockfile\", \"--prefer-offline\"] : []),\n\t\t\t],\n\t\t\tenv: { CI: \"true\" },\n\t\t\tlockfiles: [\"yarn.lock\"],\n\t\t})),\n\t\tMatch.when(\"yarn-berry\", () => ({\n\t\t\texecutable: \"yarn\",\n\t\t\targs: [\"install\", ...(recovery ? [\"--immutable\"] : [])],\n\t\t\tenv: {\n\t\t\t\tCI: \"true\",\n\t\t\t\tYARN_ENABLE_IMMUTABLE_INSTALLS: recovery ? \"true\" : \"false\",\n\t\t\t\tYARN_NODE_LINKER: \"node-modules\",\n\t\t\t},\n\t\t\tlockfiles: [\"yarn.lock\"],\n\t\t})),\n\t\tMatch.exhaustive,\n\t)\n}\n\nconst readSource = Effect.fnUntraced(function* (\n\tfs: FileSystem.FileSystem,\n\tfile: string,\n) {\n\tconst bytes = yield* fs.readFile(file)\n\treturn yield* Effect.try({\n\t\ttry: () =>\n\t\t\tnew TextDecoder(\"utf-8\", { ignoreBOM: true, fatal: true }).decode(bytes),\n\t\tcatch: (cause) =>\n\t\t\tPlatformError.badArgument({\n\t\t\t\tmodule: \"FileSystem\",\n\t\t\t\tmethod: \"readManifest\",\n\t\t\t\tdescription: \"The manifest must contain valid UTF-8\",\n\t\t\t\tcause,\n\t\t\t}),\n\t})\n})\n\nconst Executable = PackageManagerName\nconst Digits = Schema.String.check(Schema.isPattern(/^\\d+$/))\nconst decodePackageManager = Schema.decodeUnknownOption(\n\tPackageManagerDeclaration,\n)\n/** `name/version ...`, as package managers set `npm_config_user_agent`. */\nconst decodeUserAgent = Schema.decodeUnknownOption(\n\tSchema.TemplateLiteralParser([Executable, \"/\", Digits, Schema.String]),\n)\nconst agentOf = (name: typeof Executable.Type, major: string): Agent =>\n\tname === \"yarn\" ? (major === \"1\" ? \"yarn-classic\" : \"yarn-berry\") : name\n\n/** packageManager, then lockfile, then caller user-agent, then npm. Conflicting lockfiles fail. */\nexport const detectAgent = Effect.fnUntraced(function* (options: Options) {\n\tif (options.agent !== undefined)\n\t\treturn yield* Schema.decodeEffect(Agent)(options.agent)\n\tconst fs = yield* FileSystem.FileSystem\n\tconst path = yield* Path.Path\n\tconst manifest = yield* readSource(\n\t\tfs,\n\t\tpath.join(options.directory, \"package.json\"),\n\t).pipe(Effect.flatMap(decodeManifest))\n\tif (manifest.packageManager !== undefined) {\n\t\treturn yield* Option.match(decodePackageManager(manifest.packageManager), {\n\t\t\tonNone: () =>\n\t\t\t\tPackageManagerError.make({\n\t\t\t\t\toperation: \"detect\",\n\t\t\t\t\tmessage: `Unsupported packageManager: ${manifest.packageManager}`,\n\t\t\t\t}),\n\t\t\tonSome: ([name, , version]) =>\n\t\t\t\tEffect.succeed(agentOf(name, `${version.major}`)),\n\t\t})\n\t}\n\tconst found = yield* Effect.filter(lockfiles, (file) =>\n\t\tfs.exists(path.join(options.directory, file)),\n\t)\n\tconst kinds = Array.dedupe(\n\t\tArray.map(found, (file) =>\n\t\t\tfile === \"pnpm-lock.yaml\"\n\t\t\t\t? \"pnpm\"\n\t\t\t\t: file === \"yarn.lock\"\n\t\t\t\t\t? \"yarn\"\n\t\t\t\t\t: \"npm\",\n\t\t),\n\t)\n\tif (kinds.length > 1)\n\t\treturn yield* PackageManagerError.make({\n\t\t\toperation: \"detect\",\n\t\t\tmessage: \"Conflicting lockfiles; select an agent explicitly\",\n\t\t})\n\tif (kinds[0] === \"yarn\") {\n\t\tconst metadata = yield* fs\n\t\t\t.stream(path.join(options.directory, \"yarn.lock\"))\n\t\t\t.pipe(\n\t\t\t\tStream.decodeText(),\n\t\t\t\tStream.mapAccum(\n\t\t\t\t\t// The initial newline models the start of the file.\n\t\t\t\t\t() => \"\\n\",\n\t\t\t\t\t(tail, chunk) => {\n\t\t\t\t\t\tconst text = tail + chunk\n\t\t\t\t\t\t// Preserve all ECMAScript line terminators, including across chunk boundaries.\n\t\t\t\t\t\t// Eleven characters retain a partial marker and its preceding character.\n\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\tString.slice(-11)(text),\n\t\t\t\t\t\t\t[/[\\n\\r\\u2028\\u2029]__metadata:/.test(text)],\n\t\t\t\t\t\t]\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t\tStream.filter((found) => found),\n\t\t\t\tStream.runHead,\n\t\t\t)\n\t\treturn Option.isSome(metadata) ? \"yarn-berry\" : \"yarn-classic\"\n\t}\n\tif (kinds[0] !== undefined) return kinds[0]\n\treturn Option.match(decodeUserAgent(options.userAgent ?? \"\"), {\n\t\tonNone: () => \"npm\" as const,\n\t\tonSome: ([name, , major]) => agentOf(name, major),\n\t})\n})\n\nexport interface PackageState {\n\treadonly spec: string\n\t/** Missing optional or not-yet-installed dependencies have no version/path. */\n\treadonly version: string | undefined\n\treadonly path: string | undefined\n}\n/** Direct dependencies only, keyed by their manifest names (including aliases). */\nexport type State = HashMap.HashMap<string, PackageState>\nexport interface PackageChange {\n\treadonly name: string\n\treadonly before: PackageState | undefined\n\treadonly after: PackageState | undefined\n}\nexport interface StateDiff {\n\treadonly added: ReadonlyArray<PackageChange>\n\treadonly removed: ReadonlyArray<PackageChange>\n\treadonly updated: ReadonlyArray<PackageChange>\n}\nexport const diffState = (before: State, after: State): StateDiff => {\n\tconst changes = Array.map(\n\t\tArray.sort(\n\t\t\tArray.dedupe([...HashMap.keys(before), ...HashMap.keys(after)]),\n\t\t\tString.Order,\n\t\t),\n\t\t(name) => ({\n\t\t\tname,\n\t\t\tbefore: Option.getOrUndefined(HashMap.get(before, name)),\n\t\t\tafter: Option.getOrUndefined(HashMap.get(after, name)),\n\t\t}),\n\t)\n\treturn {\n\t\tadded: Array.filter(changes, (change) => change.before === undefined),\n\t\tremoved: Array.filter(changes, (change) => change.after === undefined),\n\t\tupdated: Array.filter(\n\t\t\tchanges,\n\t\t\t({ before, after }) =>\n\t\t\t\tbefore !== undefined &&\n\t\t\t\tafter !== undefined &&\n\t\t\t\t(before.spec !== after.spec ||\n\t\t\t\t\tbefore.version !== after.version ||\n\t\t\t\t\tbefore.path !== after.path),\n\t\t),\n\t}\n}\nexport interface InstallResult extends StateDiff {\n\treadonly agent: Agent\n\t/** The install committed; retry only snapshot deletion, not installation. */\n\treadonly snapshotCleanupFailure: Option.Option<SnapshotCleanupFailure>\n}\nexport interface PackageManager {\n\treadonly directory: string\n\treadonly agent: Agent\n\treadonly readState: Effect.Effect<State, Error>\n\t/** Present when manual recovery is required; the manager cannot be reused. */\n\treadonly recoveryRequired: Effect.Effect<Option.Option<RecoveryRequiredError>>\n\t/** Serialized per manager. Empty changes installs the current manifest, including first installs. */\n\treadonly install: (changes: Changes) => Effect.Effect<InstallResult, Error>\n}\n\n/**\n * Own one manager per project. The caller must give this manager and its child\n * processes exclusive write access to the project for each install, including\n * recovery. Do not edit project files or run another installer concurrently.\n * Calls are serialized only within this manager; no cross-process lock is taken.\n * Managed paths must be regular files or absent. make and install reject symbolic\n * links and other file types with UnsupportedFileTypeError before any write.\n *\n * Accepted file writes finish before cancellation can start rollback or release\n * the lock. File writes and the original process group participate in the transaction.\n * Scripts must not create escaped writers in independent groups or daemons.\n * Failed or interrupted installs before commit restore backed-up package files byte-for-byte\n * with recorded permissions and ownership, and remove tracked files that did not\n * exist before installation. Writes made\n * during installation are overwritten by rollback, never merged. When a lockfile\n * existed, recovery also attempts to repair node_modules. Recovery failures are\n * combined with the original cause. If process termination cannot be confirmed,\n * no further restoration or recovery runs. Any failed rollback step also requires\n * manual recovery, even if all writers stopped. Only successful installation or\n * complete rollback permits snapshot deletion and reuse. RecoveryRequiredError\n * and recoveryRequired expose the retained snapshot directory and complete cause;\n * this manager rejects\n * all later installs. Complete manual recovery before making a new manager.\n * Committed installs report snapshot deletion failures in snapshotCleanupFailure\n * rather than failing or rolling back. No crash recovery is promised.\n */\nexport const make = (\n\toptions: Options,\n): Effect.Effect<\n\tPackageManager,\n\tError,\n\tFileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner\n> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem\n\t\tconst path = yield* Path.Path\n\t\tconst spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n\t\tconst directory = path.resolve(options.directory)\n\t\tyield* inspectManagedFiles(fs, path, directory, backupFiles)\n\t\tconst agent = yield* detectAgent({ ...options, directory })\n\t\tconst lock = yield* Semaphore.make(1)\n\t\tconst transaction = yield* Ref.make<ManagerState>(ManagerState.Ready())\n\t\tconst manifestFile = path.join(directory, \"package.json\")\n\t\tconst readManifest = readSource(fs, manifestFile).pipe(\n\t\t\tEffect.flatMap(decodeManifest),\n\t\t)\n\t\tconst installed = Schema.decodeUnknownEffect(\n\t\t\tSchema.fromJsonString(\n\t\t\t\tSchema.Struct({ version: Schema.optionalKey(Schema.String) }),\n\t\t\t),\n\t\t)\n\t\tconst stateOf = Effect.fnUntraced(function* (manifest: Manifest) {\n\t\t\tif (yield* fs.exists(path.join(directory, \".pnp.cjs\")))\n\t\t\t\treturn yield* PackageManagerError.make({\n\t\t\t\t\toperation: \"state\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Plug'n'Play is not supported; configure Yarn with nodeLinker: node-modules\",\n\t\t\t\t})\n\t\t\tconst deps = Array.reduce(\n\t\t\t\tfields,\n\t\t\t\t{} as Readonly<Record<string, string>>,\n\t\t\t\t(all, field) => ({ ...all, ...manifest[field] }),\n\t\t\t)\n\t\t\tconst entries = yield* Effect.forEach(\n\t\t\t\tRecord.toEntries(deps),\n\t\t\t\t([name, spec]) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst file = path.join(\n\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\t\"node_modules\",\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\"package.json\",\n\t\t\t\t\t\t)\n\t\t\t\t\t\tconst source = yield* fs.readFileString(file).pipe(\n\t\t\t\t\t\t\tEffect.asSome,\n\t\t\t\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t\t\t\t(cause) =>\n\t\t\t\t\t\t\t\t\tArray.every(\n\t\t\t\t\t\t\t\t\t\tcause.reasons,\n\t\t\t\t\t\t\t\t\t\t(reason) =>\n\t\t\t\t\t\t\t\t\t\t\tCause.isFailReason(reason) &&\n\t\t\t\t\t\t\t\t\t\t\treason.error.reason.module === \"FileSystem\" &&\n\t\t\t\t\t\t\t\t\t\t\treason.error.reason._tag === \"NotFound\",\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t() => Effect.succeedNone,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (Option.isNone(source))\n\t\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\t{ spec, version: undefined, path: undefined },\n\t\t\t\t\t\t\t] as const\n\t\t\t\t\t\tconst pkg = yield* installed(source.value)\n\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tspec,\n\t\t\t\t\t\t\t\tversion: pkg.version,\n\t\t\t\t\t\t\t\tpath: yield* fs.realPath(path.dirname(file)),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t] as const\n\t\t\t\t\t}),\n\t\t\t\t{ concurrency: 16 },\n\t\t\t)\n\t\t\treturn HashMap.fromIterable<string, PackageState>(entries)\n\t\t})\n\t\tconst readState = Effect.flatMap(readManifest, stateOf)\n\t\tconst termination: ChildProcess.KillOptions = {\n\t\t\tforceKillAfter: \"5 seconds\",\n\t\t}\n\t\tconst run = Effect.fnUntraced(function* (operation: \"install\" | \"recover\") {\n\t\t\tconst command = strategy(agent, operation)\n\t\t\t// Acquire and install the release handler atomically, then wait interruptibly.\n\t\t\treturn yield* Effect.acquireUseRelease(\n\t\t\t\tspawner\n\t\t\t\t\t.spawn(\n\t\t\t\t\t\tChildProcess.make(command.executable, command.args, {\n\t\t\t\t\t\t\tcwd: directory,\n\t\t\t\t\t\t\textendEnv: true,\n\t\t\t\t\t\t\tstdin: \"ignore\",\n\t\t\t\t\t\t\tstdout: \"inherit\",\n\t\t\t\t\t\t\tstderr: \"inherit\",\n\t\t\t\t\t\t\tdetached: true,\n\t\t\t\t\t\t\t...termination,\n\t\t\t\t\t\t\tenv: command.env,\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\t.pipe(\n\t\t\t\t\t\tEffect.tap(() =>\n\t\t\t\t\t\t\tRef.set(\n\t\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\t\tManagerState.Active({ writer: WriterState.Running() }),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t(process) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst code = yield* process.exitCode.pipe(\n\t\t\t\t\t\t\tEffect.timeoutOrElse({\n\t\t\t\t\t\t\t\tduration: \"10 minutes\",\n\t\t\t\t\t\t\t\torElse: () =>\n\t\t\t\t\t\t\t\t\tPackageManagerError.make({\n\t\t\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\t\t\tmessage: `${command.executable} ${operation} timed out`,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (code !== 0)\n\t\t\t\t\t\t\treturn yield* PackageManagerError.make({\n\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\texitCode: code,\n\t\t\t\t\t\t\t\tmessage: `${command.executable} ${operation} exited with code ${code}`,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t}),\n\t\t\t\t// The command owns its group; a stopped leader may leave live descendants.\n\t\t\t\t// Observe kill failures here rather than relying on best-effort scope cleanup.\n\t\t\t\t(process) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst result = yield* terminateProcess(process, termination)\n\t\t\t\t\t\tyield* Ref.set(\n\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\tManagerState.Active({\n\t\t\t\t\t\t\t\twriter:\n\t\t\t\t\t\t\t\t\tresult._tag === \"OriginalGroupStopped\"\n\t\t\t\t\t\t\t\t\t\t? WriterState.ManagedWritersIdle()\n\t\t\t\t\t\t\t\t\t\t: WriterState.Unconfirmed({\n\t\t\t\t\t\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\t\t\t\t\t\tprocessId: process.pid,\n\t\t\t\t\t\t\t\t\t\t\t\tcause: result.cause,\n\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (result._tag === \"Unconfirmed\")\n\t\t\t\t\t\t\treturn yield* Effect.failCause(result.cause)\n\t\t\t\t\t}),\n\t\t\t)\n\t\t}, Effect.scoped)\n\t\t// AbortSignal does not join accepted OS writes. Keep the writer owned until completion.\n\t\tconst withFileWriter = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n\t\t\tEffect.acquireUseRelease(\n\t\t\t\tRef.set(\n\t\t\t\t\ttransaction,\n\t\t\t\t\tManagerState.Active({ writer: WriterState.FileWrite() }),\n\t\t\t\t),\n\t\t\t\t() => effect,\n\t\t\t\t() =>\n\t\t\t\t\tRef.set(\n\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\tManagerState.Active({ writer: WriterState.ManagedWritersIdle() }),\n\t\t\t\t\t),\n\t\t\t).pipe(Effect.uninterruptible)\n\n\t\tconst install = (changes: Changes) =>\n\t\t\tlock.withPermits(1)(\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\tif (state._tag === \"RecoveryRequired\") return yield* state.error\n\t\t\t\t\tconst validated = yield* Schema.decodeEffect(Changes)(changes)\n\t\t\t\t\tconst inspected = yield* inspectManagedFiles(\n\t\t\t\t\t\tfs,\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\tbackupFiles,\n\t\t\t\t\t)\n\t\t\t\t\tconst backups = yield* Effect.forEach(\n\t\t\t\t\t\tinspected,\n\t\t\t\t\t\t({ name, file, metadata }) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tconst contents = yield* Option.match(metadata, {\n\t\t\t\t\t\t\t\t\tonNone: () => Effect.succeedNone,\n\t\t\t\t\t\t\t\t\tonSome: (info) =>\n\t\t\t\t\t\t\t\t\t\tfs\n\t\t\t\t\t\t\t\t\t\t\t.readFile(file)\n\t\t\t\t\t\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\t\t\t\t\t\tEffect.map((bytes) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tOption.some<Contents>({ ...info, bytes }),\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\treturn { name, file, contents }\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\tconst original = yield* readSource(fs, manifestFile)\n\t\t\t\t\tconst manifest = yield* decodeManifest(original)\n\t\t\t\t\tconst before = yield* stateOf(manifest)\n\t\t\t\t\tconst text = yield* encodeManifest(\n\t\t\t\t\t\tapplyChanges(manifest, validated),\n\t\t\t\t\t\toriginal,\n\t\t\t\t\t)\n\n\t\t\t\t\tconst agentLocks = strategy(agent, \"install\").lockfiles\n\t\t\t\t\tconst hasLock = Array.some(\n\t\t\t\t\t\tbackups,\n\t\t\t\t\t\t({ name, contents }) =>\n\t\t\t\t\t\t\tArray.contains(agentLocks, name) && Option.isSome(contents),\n\t\t\t\t\t)\n\t\t\t\t\tconst completed = yield* withSnapshot(\n\t\t\t\t\t\tfs,\n\t\t\t\t\t\tRef.get(transaction).pipe(\n\t\t\t\t\t\t\tEffect.map((state) => state._tag === \"Ready\"),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t(snapshotDirectory) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tconst snapshot = yield* writeSnapshot(\n\t\t\t\t\t\t\t\t\tfs,\n\t\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\t\tsnapshotDirectory,\n\t\t\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\t\t\tagent,\n\t\t\t\t\t\t\t\t\tbackups,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tconst restore = Effect.flatMap(Ref.get(transaction), (state) =>\n\t\t\t\t\t\t\t\t\tstate._tag === \"Active\" &&\n\t\t\t\t\t\t\t\t\tstate.writer._tag === \"ManagedWritersIdle\"\n\t\t\t\t\t\t\t\t\t\t? withFileWriter(snapshot.restore(state.writer))\n\t\t\t\t\t\t\t\t\t\t: Effect.void,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\treturn yield* Effect.acquireUseRelease(\n\t\t\t\t\t\t\t\t\tRef.set(\n\t\t\t\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\t\t\t\tManagerState.Active({\n\t\t\t\t\t\t\t\t\t\t\twriter: WriterState.ManagedWritersIdle(),\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\t\t\t\tyield* withFileWriter(\n\t\t\t\t\t\t\t\t\t\t\t\tfs.writeFileString(manifestFile, text),\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\tyield* run(\"install\")\n\t\t\t\t\t\t\t\t\t\t\tconst after = yield* readState\n\t\t\t\t\t\t\t\t\t\t\treturn { agent, ...diffState(before, after) }\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t(_, exit) =>\n\t\t\t\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\t\t\t\tif (Exit.isFailure(exit)) {\n\t\t\t\t\t\t\t\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t\t\t\tstate._tag !== \"Active\" ||\n\t\t\t\t\t\t\t\t\t\t\t\t\tstate.writer._tag !== \"ManagedWritersIdle\"\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t\t\tyield* withFileWriter(snapshot.restore(state.writer))\n\t\t\t\t\t\t\t\t\t\t\t\tif (hasLock)\n\t\t\t\t\t\t\t\t\t\t\t\t\tyield* run(\"recover\").pipe(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// Even a failed recovery must restore files, once its writer has stopped.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tEffect.onExit(() => restore),\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t// Idle managed writers alone do not prove that the transaction completed.\n\t\t\t\t\t\t\t\t\t\t\tyield* Ref.set(transaction, ManagerState.Ready())\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t).pipe(\n\t\t\t\t\t\t\t\t\tEffect.onExit((exit) =>\n\t\t\t\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\t\t\t\tconst state = yield* Ref.get(transaction)\n\t\t\t\t\t\t\t\t\t\t\tif (Exit.isSuccess(exit) || state._tag !== \"Active\")\n\t\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t\tconst writer = state.writer\n\t\t\t\t\t\t\t\t\t\t\tconst error = RecoveryRequiredError.make({\n\t\t\t\t\t\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\t\t\t\t\t\tsnapshotDirectory,\n\t\t\t\t\t\t\t\t\t\t\t\toperation:\n\t\t\t\t\t\t\t\t\t\t\t\t\twriter._tag === \"Unconfirmed\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? writer.operation\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"rollback\",\n\t\t\t\t\t\t\t\t\t\t\t\t...(writer._tag === \"Unconfirmed\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprocessId: writer.processId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tterminationCause: writer.cause,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t\t\t\t\tmessage: `Manual recovery is required. Stop all writers before restoring the snapshot at ${snapshotDirectory}`,\n\t\t\t\t\t\t\t\t\t\t\t\trecoveryCause: exit.cause,\n\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\tyield* Ref.set(\n\t\t\t\t\t\t\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\t\t\t\t\t\t\tManagerState.RecoveryRequired({ error }),\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\treturn yield* error\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...completed.value,\n\t\t\t\t\t\tsnapshotCleanupFailure: completed.snapshotCleanupFailure,\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t)\n\t\treturn {\n\t\t\tdirectory,\n\t\t\tagent,\n\t\t\trecoveryRequired: Ref.get(transaction).pipe(\n\t\t\t\tEffect.map((state) =>\n\t\t\t\t\tstate._tag === \"RecoveryRequired\"\n\t\t\t\t\t\t? Option.some(state.error)\n\t\t\t\t\t\t: Option.none(),\n\t\t\t\t),\n\t\t\t),\n\t\t\treadState: lock.withPermits(1)(readState),\n\t\t\tinstall,\n\t\t}\n\t})\n"],"mappings":"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"}
|
package/dist/manifest.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","names":[],"sources":["../src/manifest.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","names":[],"sources":["../src/manifest.ts"],"mappings":";qBAWa,aAAW,OAAA;qBAwCX,SAAO,OAAA,QAAA,OAAA,QAAA,OAAA,gBAAA,OAAA,QAAA,OAAA;;YAKR,iBAAiB,QAAQ"}
|
package/dist/manifest.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Array, Effect, HashMap, Option, Record, Schema, String } from "effect";
|
|
1
|
+
import { Array, Chunk, Effect, HashMap, Option, Record, Schema, String } from "effect";
|
|
2
2
|
// src/manifest.ts
|
|
3
3
|
const PackageName = Schema.String.check(Schema.isPattern(/^(?!(?:node_modules|favicon\.ico)$)(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/i), Schema.isMaxLength(214), Schema.isTrimmed());
|
|
4
4
|
const isPackageName = Schema.is(PackageName);
|
|
@@ -22,17 +22,23 @@ const fields = [
|
|
|
22
22
|
"optionalDependencies"
|
|
23
23
|
];
|
|
24
24
|
const applyChanges = (manifest, changes) => {
|
|
25
|
+
const edits = Record.toEntries(changes);
|
|
26
|
+
if (edits.length === 0) return manifest;
|
|
25
27
|
let next = manifest;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
28
|
+
const additions = Array.flatMap(edits, ([name, spec]) => spec !== null && !Array.some(fields, (field) => Record.has(manifest[field] ?? {}, name)) ? [[name, spec]] : []);
|
|
29
|
+
for (const field of fields) {
|
|
30
|
+
const entries = Array.flatMap(Record.toEntries(manifest[field] ?? {}), ([name, old]) => {
|
|
31
|
+
const spec = Record.has(changes, name) ? changes[name] : old;
|
|
32
|
+
return spec === null ? [] : [[name, spec]];
|
|
33
|
+
});
|
|
34
|
+
if (field === "dependencies" && additions.length > 0) next = {
|
|
35
|
+
...next,
|
|
36
|
+
[field]: Record.fromEntries([...entries, ...additions])
|
|
37
|
+
};
|
|
38
|
+
else if (manifest[field] !== void 0) next = {
|
|
39
|
+
...next,
|
|
40
|
+
[field]: Record.fromEntries(entries)
|
|
41
|
+
};
|
|
36
42
|
}
|
|
37
43
|
return next;
|
|
38
44
|
};
|
|
@@ -41,7 +47,7 @@ const encodeString = Schema.encodeEffect(Schema.fromJsonString(Schema.String));
|
|
|
41
47
|
/** Read member spans from a JSON object already validated by decodeManifest. */
|
|
42
48
|
const readObject = Effect.fnUntraced(function* (source) {
|
|
43
49
|
const tokens = Array.fromIterable(String.matchAll(/"(?:\\[\s\S]|[^"\\])*"|[{}\[\]:,]|[^\s{}\[\]:,]+/g)(source));
|
|
44
|
-
let members =
|
|
50
|
+
let members = Chunk.empty();
|
|
45
51
|
let cursor = 1;
|
|
46
52
|
let start = (tokens[0]?.index ?? 0) + 1;
|
|
47
53
|
while (cursor < tokens.length && tokens[cursor]?.[0] !== "}") {
|
|
@@ -56,7 +62,7 @@ const readObject = Effect.fnUntraced(function* (source) {
|
|
|
56
62
|
if (token[0] === "}" || token[0] === "]") depth--;
|
|
57
63
|
end = token.index + token[0].length;
|
|
58
64
|
} while (depth > 0);
|
|
59
|
-
members =
|
|
65
|
+
members = Chunk.append(members, {
|
|
60
66
|
name: yield* decodeKey(key[0]),
|
|
61
67
|
leading: String.slice(start, key.index)(source),
|
|
62
68
|
key: key[0],
|
|
@@ -71,7 +77,7 @@ const readObject = Effect.fnUntraced(function* (source) {
|
|
|
71
77
|
const close = tokens[cursor].index;
|
|
72
78
|
const tail = String.slice(start, close)(source);
|
|
73
79
|
return {
|
|
74
|
-
members,
|
|
80
|
+
members: Chunk.toArray(members),
|
|
75
81
|
tail,
|
|
76
82
|
open: tokens[0].index,
|
|
77
83
|
close
|
|
@@ -115,10 +121,10 @@ const editObject = Effect.fnUntraced(function* (source, changes) {
|
|
|
115
121
|
const encodeManifest = Effect.fnUntraced(function* (manifest, original) {
|
|
116
122
|
const before = yield* decodeManifest(original);
|
|
117
123
|
const { members } = yield* readObject(original);
|
|
118
|
-
const changes = yield* Effect.reduce(fields, () =>
|
|
124
|
+
const changes = yield* Effect.reduce(fields, () => Chunk.empty(), Effect.fnUntraced(function* (changes, field) {
|
|
119
125
|
const oldSpecs = HashMap.fromIterable(Record.toEntries(before[field] ?? {}));
|
|
120
126
|
const nextSpecs = HashMap.fromIterable(Record.toEntries(manifest[field] ?? {}));
|
|
121
|
-
const specs = yield* Effect.reduce(Array.dedupe([...Record.keys(before[field] ?? {}), ...Record.keys(manifest[field] ?? {})]), () =>
|
|
127
|
+
const specs = yield* Effect.reduce(Array.dedupe([...Record.keys(before[field] ?? {}), ...Record.keys(manifest[field] ?? {})]), () => Chunk.empty(), Effect.fnUntraced(function* (specs, name) {
|
|
122
128
|
const old = HashMap.get(oldSpecs, name);
|
|
123
129
|
const next = HashMap.get(nextSpecs, name);
|
|
124
130
|
if (Option.getOrUndefined(old) === Option.getOrUndefined(next)) return specs;
|
|
@@ -126,18 +132,18 @@ const encodeManifest = Effect.fnUntraced(function* (manifest, original) {
|
|
|
126
132
|
onNone: () => Effect.succeedNone,
|
|
127
133
|
onSome: (value) => encodeString(value).pipe(Effect.asSome)
|
|
128
134
|
});
|
|
129
|
-
return
|
|
135
|
+
return Chunk.append(specs, [name, value]);
|
|
130
136
|
}));
|
|
131
|
-
if (specs
|
|
137
|
+
if (Chunk.isEmpty(specs)) return changes;
|
|
132
138
|
const section = Array.findLast(members, (member) => member.name === field);
|
|
133
139
|
const source = Option.match(section, {
|
|
134
140
|
onNone: () => "{}",
|
|
135
141
|
onSome: (member) => member.value
|
|
136
142
|
});
|
|
137
|
-
const edited = yield* editObject(source, specs);
|
|
138
|
-
return
|
|
143
|
+
const edited = yield* editObject(source, Chunk.toArray(specs));
|
|
144
|
+
return Chunk.append(changes, [field, Option.some(edited)]);
|
|
139
145
|
}));
|
|
140
|
-
return changes
|
|
146
|
+
return Chunk.isEmpty(changes) ? original : yield* editObject(original, Chunk.toArray(changes));
|
|
141
147
|
});
|
|
142
148
|
export { Changes, Manifest, PackageName, applyChanges, decodeManifest, encodeManifest, fields };
|
|
143
149
|
|
package/dist/manifest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","names":[],"sources":["../src/manifest.ts"],"sourcesContent":["import { Array, Effect, HashMap, Option, Record, Schema, String } 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\tlet next = manifest\n\tfor (const [name, spec] of Record.toEntries(changes)) {\n\t\tconst existing = Array.filter(fields, (field) =>\n\t\t\tRecord.has(next[field] ?? {}, name),\n\t\t)\n\t\tconst targets =\n\t\t\tspec === null || existing.length > 0\n\t\t\t\t? existing\n\t\t\t\t: ([\"dependencies\"] as const)\n\t\tfor (const field of targets) {\n\t\t\tconst deps = next[field] ?? {}\n\t\t\tnext = {\n\t\t\t\t...next,\n\t\t\t\t[field]:\n\t\t\t\t\tspec === null\n\t\t\t\t\t\t? Record.remove(deps, name)\n\t\t\t\t\t\t: Record.set(deps, name, spec),\n\t\t\t}\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: ReadonlyArray<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 = Array.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 { members, tail, open: tokens[0]!.index!, close }\n})\n\n/** Ordered edits preserve insertion order; None removes every occurrence. */\ntype Edits = ReadonlyArray<readonly [string, Option.Option<string>]>\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(): Edits => [],\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(): Edits => [],\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 Array.append(specs, [name, value] as const)\n\t\t\t\t}),\n\t\t\t)\n\t\t\tif (specs.length === 0) 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, specs)\n\t\t\treturn Array.append(changes, [field, Option.some(edited)] as const)\n\t\t}),\n\t)\n\treturn changes.length === 0 ? original : yield* editObject(original, changes)\n})\n"],"mappings":";;AAEA,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,IAAI,OAAO;CACX,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,UAAU,OAAO,GAAG;EACrD,MAAM,WAAW,MAAM,OAAO,SAAS,UACtC,OAAO,IAAI,KAAK,UAAU,CAAC,GAAG,IAAI,CACnC;EACA,MAAM,UACL,SAAS,QAAQ,SAAS,SAAS,IAChC,WACC,CAAC,cAAc;EACpB,KAAK,MAAM,SAAS,SAAS;GAC5B,MAAM,OAAO,KAAK,UAAU,CAAC;GAC7B,OAAO;IACN,GAAG;KACF,QACA,SAAS,OACN,OAAO,OAAO,MAAM,IAAI,IACxB,OAAO,IAAI,MAAM,MAAM,IAAI;GAChC;EACD;CACD;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,UAAiC,CAAC;CACtC,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;EAAE;EAAS;EAAM,MAAM,OAAO,EAAE,CAAE;EAAQ;CAAM;AACxD,CAAC;AAKD,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,cACa,CAAC,GACd,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,SACY,CAAC,GACd,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,WAAW,GAAG,OAAO;EAC/B,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,KAAK;EAC9C,OAAO,MAAM,OAAO,SAAS,CAAC,OAAO,OAAO,KAAK,MAAM,CAAC,CAAU;CACnE,CAAC,CACF;CACA,OAAO,QAAQ,WAAW,IAAI,WAAW,OAAO,WAAW,UAAU,OAAO;AAC7E,CAAC"}
|
|
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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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
Exit,
|
|
9
9
|
FileSystem,
|
|
10
10
|
HashMap,
|
|
11
|
+
Match,
|
|
11
12
|
Option,
|
|
12
13
|
Path,
|
|
13
14
|
PlatformError,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
Schema,
|
|
16
17
|
Semaphore,
|
|
17
18
|
String,
|
|
19
|
+
Stream,
|
|
18
20
|
} from "effect"
|
|
19
21
|
import { ChildProcess, ChildProcessSpawner } from "effect/process"
|
|
20
22
|
import {
|
|
@@ -127,8 +129,53 @@ const backupFiles = [
|
|
|
127
129
|
".pnp.cjs",
|
|
128
130
|
".pnp.loader.mjs",
|
|
129
131
|
]
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
+
const strategy = (agent: Agent, operation: "install" | "recover") => {
|
|
133
|
+
const recovery = operation === "recover"
|
|
134
|
+
return Match.value(agent).pipe(
|
|
135
|
+
Match.when("npm", () => ({
|
|
136
|
+
executable: "npm",
|
|
137
|
+
args: [
|
|
138
|
+
"install",
|
|
139
|
+
"--no-audit",
|
|
140
|
+
"--no-fund",
|
|
141
|
+
...(recovery ? ["--prefer-offline"] : []),
|
|
142
|
+
],
|
|
143
|
+
env: { CI: "true" },
|
|
144
|
+
lockfiles: ["package-lock.json", "npm-shrinkwrap.json"],
|
|
145
|
+
})),
|
|
146
|
+
Match.when("pnpm", () => ({
|
|
147
|
+
executable: "pnpm",
|
|
148
|
+
args: [
|
|
149
|
+
"install",
|
|
150
|
+
recovery ? "--frozen-lockfile" : "--no-frozen-lockfile",
|
|
151
|
+
"--config.confirmModulesPurge=false",
|
|
152
|
+
],
|
|
153
|
+
env: { CI: "true" },
|
|
154
|
+
lockfiles: ["pnpm-lock.yaml"],
|
|
155
|
+
})),
|
|
156
|
+
Match.when("yarn-classic", () => ({
|
|
157
|
+
executable: "yarn",
|
|
158
|
+
args: [
|
|
159
|
+
"install",
|
|
160
|
+
"--non-interactive",
|
|
161
|
+
...(recovery ? ["--frozen-lockfile", "--prefer-offline"] : []),
|
|
162
|
+
],
|
|
163
|
+
env: { CI: "true" },
|
|
164
|
+
lockfiles: ["yarn.lock"],
|
|
165
|
+
})),
|
|
166
|
+
Match.when("yarn-berry", () => ({
|
|
167
|
+
executable: "yarn",
|
|
168
|
+
args: ["install", ...(recovery ? ["--immutable"] : [])],
|
|
169
|
+
env: {
|
|
170
|
+
CI: "true",
|
|
171
|
+
YARN_ENABLE_IMMUTABLE_INSTALLS: recovery ? "true" : "false",
|
|
172
|
+
YARN_NODE_LINKER: "node-modules",
|
|
173
|
+
},
|
|
174
|
+
lockfiles: ["yarn.lock"],
|
|
175
|
+
})),
|
|
176
|
+
Match.exhaustive,
|
|
177
|
+
)
|
|
178
|
+
}
|
|
132
179
|
|
|
133
180
|
const readSource = Effect.fnUntraced(function* (
|
|
134
181
|
fs: FileSystem.FileSystem,
|
|
@@ -199,10 +246,27 @@ export const detectAgent = Effect.fnUntraced(function* (options: Options) {
|
|
|
199
246
|
message: "Conflicting lockfiles; select an agent explicitly",
|
|
200
247
|
})
|
|
201
248
|
if (kinds[0] === "yarn") {
|
|
202
|
-
const
|
|
203
|
-
path.join(options.directory, "yarn.lock")
|
|
204
|
-
|
|
205
|
-
|
|
249
|
+
const metadata = yield* fs
|
|
250
|
+
.stream(path.join(options.directory, "yarn.lock"))
|
|
251
|
+
.pipe(
|
|
252
|
+
Stream.decodeText(),
|
|
253
|
+
Stream.mapAccum(
|
|
254
|
+
// The initial newline models the start of the file.
|
|
255
|
+
() => "\n",
|
|
256
|
+
(tail, chunk) => {
|
|
257
|
+
const text = tail + chunk
|
|
258
|
+
// Preserve all ECMAScript line terminators, including across chunk boundaries.
|
|
259
|
+
// Eleven characters retain a partial marker and its preceding character.
|
|
260
|
+
return [
|
|
261
|
+
String.slice(-11)(text),
|
|
262
|
+
[/[\n\r\u2028\u2029]__metadata:/.test(text)],
|
|
263
|
+
]
|
|
264
|
+
},
|
|
265
|
+
),
|
|
266
|
+
Stream.filter((found) => found),
|
|
267
|
+
Stream.runHead,
|
|
268
|
+
)
|
|
269
|
+
return Option.isSome(metadata) ? "yarn-berry" : "yarn-classic"
|
|
206
270
|
}
|
|
207
271
|
if (kinds[0] !== undefined) return kinds[0]
|
|
208
272
|
return Option.match(decodeUserAgent(options.userAgent ?? ""), {
|
|
@@ -343,14 +407,26 @@ export const make = (
|
|
|
343
407
|
name,
|
|
344
408
|
"package.json",
|
|
345
409
|
)
|
|
346
|
-
|
|
410
|
+
const source = yield* fs.readFileString(file).pipe(
|
|
411
|
+
Effect.asSome,
|
|
412
|
+
Effect.catchCauseIf(
|
|
413
|
+
(cause) =>
|
|
414
|
+
Array.every(
|
|
415
|
+
cause.reasons,
|
|
416
|
+
(reason) =>
|
|
417
|
+
Cause.isFailReason(reason) &&
|
|
418
|
+
reason.error.reason.module === "FileSystem" &&
|
|
419
|
+
reason.error.reason._tag === "NotFound",
|
|
420
|
+
),
|
|
421
|
+
() => Effect.succeedNone,
|
|
422
|
+
),
|
|
423
|
+
)
|
|
424
|
+
if (Option.isNone(source))
|
|
347
425
|
return [
|
|
348
426
|
name,
|
|
349
427
|
{ spec, version: undefined, path: undefined },
|
|
350
428
|
] as const
|
|
351
|
-
const pkg = yield*
|
|
352
|
-
.readFileString(file)
|
|
353
|
-
.pipe(Effect.flatMap(installed))
|
|
429
|
+
const pkg = yield* installed(source.value)
|
|
354
430
|
return [
|
|
355
431
|
name,
|
|
356
432
|
{
|
|
@@ -360,6 +436,7 @@ export const make = (
|
|
|
360
436
|
},
|
|
361
437
|
] as const
|
|
362
438
|
}),
|
|
439
|
+
{ concurrency: 16 },
|
|
363
440
|
)
|
|
364
441
|
return HashMap.fromIterable<string, PackageState>(entries)
|
|
365
442
|
})
|
|
@@ -368,35 +445,12 @@ export const make = (
|
|
|
368
445
|
forceKillAfter: "5 seconds",
|
|
369
446
|
}
|
|
370
447
|
const run = Effect.fnUntraced(function* (operation: "install" | "recover") {
|
|
371
|
-
const
|
|
372
|
-
const args =
|
|
373
|
-
agent === "npm"
|
|
374
|
-
? [
|
|
375
|
-
"install",
|
|
376
|
-
"--no-audit",
|
|
377
|
-
"--no-fund",
|
|
378
|
-
...(recovery ? ["--prefer-offline"] : []),
|
|
379
|
-
]
|
|
380
|
-
: agent === "pnpm"
|
|
381
|
-
? [
|
|
382
|
-
"install",
|
|
383
|
-
recovery ? "--frozen-lockfile" : "--no-frozen-lockfile",
|
|
384
|
-
"--config.confirmModulesPurge=false",
|
|
385
|
-
]
|
|
386
|
-
: agent === "yarn-classic"
|
|
387
|
-
? [
|
|
388
|
-
"install",
|
|
389
|
-
"--non-interactive",
|
|
390
|
-
...(recovery
|
|
391
|
-
? ["--frozen-lockfile", "--prefer-offline"]
|
|
392
|
-
: []),
|
|
393
|
-
]
|
|
394
|
-
: ["install", ...(recovery ? ["--immutable"] : [])]
|
|
448
|
+
const command = strategy(agent, operation)
|
|
395
449
|
// Acquire and install the release handler atomically, then wait interruptibly.
|
|
396
450
|
return yield* Effect.acquireUseRelease(
|
|
397
451
|
spawner
|
|
398
452
|
.spawn(
|
|
399
|
-
ChildProcess.make(executable
|
|
453
|
+
ChildProcess.make(command.executable, command.args, {
|
|
400
454
|
cwd: directory,
|
|
401
455
|
extendEnv: true,
|
|
402
456
|
stdin: "ignore",
|
|
@@ -404,17 +458,7 @@ export const make = (
|
|
|
404
458
|
stderr: "inherit",
|
|
405
459
|
detached: true,
|
|
406
460
|
...termination,
|
|
407
|
-
env:
|
|
408
|
-
CI: "true",
|
|
409
|
-
...(agent === "yarn-berry"
|
|
410
|
-
? {
|
|
411
|
-
YARN_ENABLE_IMMUTABLE_INSTALLS: recovery
|
|
412
|
-
? "true"
|
|
413
|
-
: "false",
|
|
414
|
-
YARN_NODE_LINKER: "node-modules",
|
|
415
|
-
}
|
|
416
|
-
: {}),
|
|
417
|
-
},
|
|
461
|
+
env: command.env,
|
|
418
462
|
}),
|
|
419
463
|
)
|
|
420
464
|
.pipe(
|
|
@@ -433,7 +477,7 @@ export const make = (
|
|
|
433
477
|
orElse: () =>
|
|
434
478
|
PackageManagerError.make({
|
|
435
479
|
operation,
|
|
436
|
-
message: `${executable
|
|
480
|
+
message: `${command.executable} ${operation} timed out`,
|
|
437
481
|
}),
|
|
438
482
|
}),
|
|
439
483
|
)
|
|
@@ -441,7 +485,7 @@ export const make = (
|
|
|
441
485
|
return yield* PackageManagerError.make({
|
|
442
486
|
operation,
|
|
443
487
|
exitCode: code,
|
|
444
|
-
message: `${executable
|
|
488
|
+
message: `${command.executable} ${operation} exited with code ${code}`,
|
|
445
489
|
})
|
|
446
490
|
}),
|
|
447
491
|
// The command owns its group; a stopped leader may leave live descendants.
|
|
@@ -520,12 +564,7 @@ export const make = (
|
|
|
520
564
|
original,
|
|
521
565
|
)
|
|
522
566
|
|
|
523
|
-
const agentLocks =
|
|
524
|
-
agent === "npm"
|
|
525
|
-
? ["package-lock.json", "npm-shrinkwrap.json"]
|
|
526
|
-
: agent === "pnpm"
|
|
527
|
-
? ["pnpm-lock.yaml"]
|
|
528
|
-
: ["yarn.lock"]
|
|
567
|
+
const agentLocks = strategy(agent, "install").lockfiles
|
|
529
568
|
const hasLock = Array.some(
|
|
530
569
|
backups,
|
|
531
570
|
({ name, contents }) =>
|
package/src/manifest.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Array,
|
|
3
|
+
Effect,
|
|
4
|
+
HashMap,
|
|
5
|
+
Chunk,
|
|
6
|
+
Option,
|
|
7
|
+
Record,
|
|
8
|
+
Schema,
|
|
9
|
+
String,
|
|
10
|
+
} from "effect"
|
|
2
11
|
|
|
3
12
|
export const PackageName = Schema.String.check(
|
|
4
13
|
Schema.isPattern(
|
|
@@ -56,24 +65,30 @@ export const applyChanges = (
|
|
|
56
65
|
manifest: Manifest,
|
|
57
66
|
changes: Changes,
|
|
58
67
|
): Manifest => {
|
|
68
|
+
const edits = Record.toEntries(changes)
|
|
69
|
+
if (edits.length === 0) return manifest
|
|
59
70
|
let next = manifest
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
const additions = Array.flatMap(edits, ([name, spec]) =>
|
|
72
|
+
spec !== null &&
|
|
73
|
+
!Array.some(fields, (field) => Record.has(manifest[field] ?? {}, name))
|
|
74
|
+
? [[name, spec] as const]
|
|
75
|
+
: [],
|
|
76
|
+
)
|
|
77
|
+
for (const field of fields) {
|
|
78
|
+
const entries = Array.flatMap(
|
|
79
|
+
Record.toEntries(manifest[field] ?? {}),
|
|
80
|
+
([name, old]) => {
|
|
81
|
+
const spec = Record.has(changes, name) ? changes[name]! : old
|
|
82
|
+
return spec === null ? [] : [[name, spec] as const]
|
|
83
|
+
},
|
|
63
84
|
)
|
|
64
|
-
|
|
65
|
-
spec === null || existing.length > 0
|
|
66
|
-
? existing
|
|
67
|
-
: (["dependencies"] as const)
|
|
68
|
-
for (const field of targets) {
|
|
69
|
-
const deps = next[field] ?? {}
|
|
85
|
+
if (field === "dependencies" && additions.length > 0) {
|
|
70
86
|
next = {
|
|
71
87
|
...next,
|
|
72
|
-
[field]:
|
|
73
|
-
spec === null
|
|
74
|
-
? Record.remove(deps, name)
|
|
75
|
-
: Record.set(deps, name, spec),
|
|
88
|
+
[field]: Record.fromEntries([...entries, ...additions]),
|
|
76
89
|
}
|
|
90
|
+
} else if (manifest[field] !== undefined) {
|
|
91
|
+
next = { ...next, [field]: Record.fromEntries(entries) }
|
|
77
92
|
}
|
|
78
93
|
}
|
|
79
94
|
return next
|
|
@@ -99,7 +114,7 @@ const readObject = Effect.fnUntraced(function* (source: string) {
|
|
|
99
114
|
source,
|
|
100
115
|
),
|
|
101
116
|
)
|
|
102
|
-
let members
|
|
117
|
+
let members = Chunk.empty<Member>()
|
|
103
118
|
let cursor = 1
|
|
104
119
|
let start = (tokens[0]?.index ?? 0) + 1
|
|
105
120
|
while (cursor < tokens.length && tokens[cursor]?.[0] !== "}") {
|
|
@@ -114,7 +129,7 @@ const readObject = Effect.fnUntraced(function* (source: string) {
|
|
|
114
129
|
if (token[0] === "}" || token[0] === "]") depth--
|
|
115
130
|
end = token.index! + token[0].length
|
|
116
131
|
} while (depth > 0)
|
|
117
|
-
members =
|
|
132
|
+
members = Chunk.append(members, {
|
|
118
133
|
name: yield* decodeKey(key[0]),
|
|
119
134
|
leading: String.slice(start, key.index)(source),
|
|
120
135
|
key: key[0],
|
|
@@ -131,11 +146,17 @@ const readObject = Effect.fnUntraced(function* (source: string) {
|
|
|
131
146
|
}
|
|
132
147
|
const close = tokens[cursor]!.index
|
|
133
148
|
const tail = String.slice(start, close)(source)
|
|
134
|
-
return {
|
|
149
|
+
return {
|
|
150
|
+
members: Chunk.toArray(members),
|
|
151
|
+
tail,
|
|
152
|
+
open: tokens[0]!.index!,
|
|
153
|
+
close,
|
|
154
|
+
}
|
|
135
155
|
})
|
|
136
156
|
|
|
137
157
|
/** Ordered edits preserve insertion order; None removes every occurrence. */
|
|
138
|
-
type
|
|
158
|
+
type Edit = readonly [string, Option.Option<string>]
|
|
159
|
+
type Edits = ReadonlyArray<Edit>
|
|
139
160
|
|
|
140
161
|
const editObject = Effect.fnUntraced(function* (
|
|
141
162
|
source: string,
|
|
@@ -213,7 +234,7 @@ export const encodeManifest = Effect.fnUntraced(function* (
|
|
|
213
234
|
const { members } = yield* readObject(original)
|
|
214
235
|
const changes = yield* Effect.reduce(
|
|
215
236
|
fields,
|
|
216
|
-
()
|
|
237
|
+
() => Chunk.empty<Edit>(),
|
|
217
238
|
Effect.fnUntraced(function* (changes, field) {
|
|
218
239
|
const oldSpecs = HashMap.fromIterable(
|
|
219
240
|
Record.toEntries(before[field] ?? {}),
|
|
@@ -226,7 +247,7 @@ export const encodeManifest = Effect.fnUntraced(function* (
|
|
|
226
247
|
...Record.keys(before[field] ?? {}),
|
|
227
248
|
...Record.keys(manifest[field] ?? {}),
|
|
228
249
|
]),
|
|
229
|
-
()
|
|
250
|
+
() => Chunk.empty<Edit>(),
|
|
230
251
|
Effect.fnUntraced(function* (specs, name) {
|
|
231
252
|
const old = HashMap.get(oldSpecs, name)
|
|
232
253
|
const next = HashMap.get(nextSpecs, name)
|
|
@@ -236,18 +257,20 @@ export const encodeManifest = Effect.fnUntraced(function* (
|
|
|
236
257
|
onNone: () => Effect.succeedNone,
|
|
237
258
|
onSome: (value) => encodeString(value).pipe(Effect.asSome),
|
|
238
259
|
})
|
|
239
|
-
return
|
|
260
|
+
return Chunk.append(specs, [name, value] as const)
|
|
240
261
|
}),
|
|
241
262
|
)
|
|
242
|
-
if (specs
|
|
263
|
+
if (Chunk.isEmpty(specs)) return changes
|
|
243
264
|
const section = Array.findLast(members, (member) => member.name === field)
|
|
244
265
|
const source = Option.match(section, {
|
|
245
266
|
onNone: () => "{}",
|
|
246
267
|
onSome: (member) => member.value,
|
|
247
268
|
})
|
|
248
|
-
const edited = yield* editObject(source, specs)
|
|
249
|
-
return
|
|
269
|
+
const edited = yield* editObject(source, Chunk.toArray(specs))
|
|
270
|
+
return Chunk.append(changes, [field, Option.some(edited)] as const)
|
|
250
271
|
}),
|
|
251
272
|
)
|
|
252
|
-
return
|
|
273
|
+
return Chunk.isEmpty(changes)
|
|
274
|
+
? original
|
|
275
|
+
: yield* editObject(original, Chunk.toArray(changes))
|
|
253
276
|
})
|