create-next-hydra 0.3.0 → 0.4.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/README.md +126 -27
- package/dist/add-command.d.ts +11 -0
- package/dist/add-command.d.ts.map +1 -0
- package/dist/add-command.js +75 -0
- package/dist/addition.d.ts +67 -0
- package/dist/addition.d.ts.map +1 -0
- package/dist/addition.js +422 -0
- package/dist/application-git.d.ts +20 -0
- package/dist/application-git.d.ts.map +1 -0
- package/dist/application-git.js +57 -0
- package/dist/baseline.d.ts +22 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +90 -0
- package/dist/cli.js +5 -18
- package/dist/commands.d.ts +22 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +197 -0
- package/dist/composition-lint.d.ts +20 -0
- package/dist/composition-lint.d.ts.map +1 -0
- package/dist/composition-lint.js +288 -0
- package/dist/composition.d.ts +24 -0
- package/dist/composition.d.ts.map +1 -0
- package/dist/composition.js +420 -0
- package/dist/create-command.d.ts +21 -0
- package/dist/create-command.d.ts.map +1 -0
- package/dist/create-command.js +128 -0
- package/dist/demo-instrumentation.d.ts +5 -0
- package/dist/demo-instrumentation.d.ts.map +1 -0
- package/dist/demo-instrumentation.js +300 -0
- package/dist/environment.d.ts +7 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +81 -0
- package/dist/errors.d.ts +156 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +140 -0
- package/dist/file-policy.d.ts +6 -0
- package/dist/file-policy.d.ts.map +1 -0
- package/dist/file-policy.js +38 -0
- package/dist/files.d.ts +7 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +66 -0
- package/dist/hosting.d.ts +5 -0
- package/dist/hosting.d.ts.map +1 -0
- package/dist/hosting.js +107 -0
- package/dist/index.d.ts +4 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -89
- package/dist/initialization.d.ts +14 -0
- package/dist/initialization.d.ts.map +1 -0
- package/dist/initialization.js +266 -0
- package/dist/lint-workspaces.d.ts +2 -0
- package/dist/lint-workspaces.d.ts.map +1 -0
- package/dist/lint-workspaces.js +62 -0
- package/dist/model.d.ts +143 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +61 -0
- package/dist/package-catalog.d.ts +8 -0
- package/dist/package-catalog.d.ts.map +1 -0
- package/dist/package-catalog.js +61 -0
- package/dist/packages.d.ts +57 -0
- package/dist/packages.d.ts.map +1 -0
- package/dist/packages.js +237 -0
- package/dist/process-group.d.ts +5 -0
- package/dist/process-group.d.ts.map +1 -0
- package/dist/process-group.js +23 -0
- package/dist/reference-workspace-bindings.d.ts +38 -0
- package/dist/reference-workspace-bindings.d.ts.map +1 -0
- package/dist/reference-workspace-bindings.js +41 -0
- package/dist/reference-workspace.d.ts +6 -0
- package/dist/reference-workspace.d.ts.map +1 -0
- package/dist/reference-workspace.js +62 -0
- package/dist/registry.d.ts +187 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +107 -0
- package/dist/runtime.d.ts +9 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +22 -0
- package/dist/selection.d.ts +704 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/selection.js +424 -0
- package/dist/shadcn-protocol.d.ts +22 -0
- package/dist/shadcn-protocol.d.ts.map +1 -0
- package/dist/shadcn-protocol.js +13 -0
- package/dist/shadcn-worker.d.ts +2 -0
- package/dist/shadcn-worker.d.ts.map +1 -0
- package/dist/shadcn-worker.js +43 -0
- package/dist/shadcn.d.ts +22 -0
- package/dist/shadcn.d.ts.map +1 -0
- package/dist/shadcn.js +101 -0
- package/dist/source-changes.d.ts +55 -0
- package/dist/source-changes.d.ts.map +1 -0
- package/dist/source-changes.js +225 -0
- package/dist/source-inventory.d.ts +25 -0
- package/dist/source-inventory.d.ts.map +1 -0
- package/dist/source-inventory.js +212 -0
- package/dist/source-root.d.ts +4 -0
- package/dist/source-root.d.ts.map +1 -0
- package/dist/source-root.js +20 -0
- package/dist/staging.d.ts +19 -0
- package/dist/staging.d.ts.map +1 -0
- package/dist/staging.js +42 -0
- package/dist/tasks.d.ts +44 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +137 -0
- package/dist/templates.d.ts +217 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +161 -0
- package/dist/test-reference-workspace.d.ts +2 -0
- package/dist/test-reference-workspace.d.ts.map +1 -0
- package/dist/test-reference-workspace.js +6 -0
- package/dist/types.d.ts +2 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/typescript-paths.d.ts +10 -0
- package/dist/typescript-paths.d.ts.map +1 -0
- package/dist/typescript-paths.js +52 -0
- package/dist/workspace-dependencies.d.ts +26 -0
- package/dist/workspace-dependencies.d.ts.map +1 -0
- package/dist/workspace-dependencies.js +478 -0
- package/dist/workspace-files.d.ts +24 -0
- package/dist/workspace-files.d.ts.map +1 -0
- package/dist/workspace-files.js +395 -0
- package/dist/workspace-lock.d.ts +12 -0
- package/dist/workspace-lock.d.ts.map +1 -0
- package/dist/workspace-lock.js +104 -0
- package/dist/workspace-snapshots.d.ts +24 -0
- package/dist/workspace-snapshots.d.ts.map +1 -0
- package/dist/workspace-snapshots.js +218 -0
- package/dist/workspace-sources.d.ts +27 -0
- package/dist/workspace-sources.d.ts.map +1 -0
- package/dist/workspace-sources.js +88 -0
- package/dist/workspace-state.d.ts +325 -0
- package/dist/workspace-state.d.ts.map +1 -0
- package/dist/workspace-state.js +571 -0
- package/dist/workspaces.d.ts +73 -0
- package/dist/workspaces.d.ts.map +1 -0
- package/dist/workspaces.js +396 -0
- package/package.json +27 -20
- package/schema/selection-definition.json +143 -4
- package/dist/clone.d.ts +0 -9
- package/dist/clone.d.ts.map +0 -1
- package/dist/clone.js +0 -19
- package/dist/composition/add.d.ts +0 -14
- package/dist/composition/add.d.ts.map +0 -1
- package/dist/composition/add.js +0 -484
- package/dist/composition/catalog.d.ts +0 -26
- package/dist/composition/catalog.d.ts.map +0 -1
- package/dist/composition/catalog.js +0 -317
- package/dist/composition/errors.d.ts +0 -5
- package/dist/composition/errors.d.ts.map +0 -1
- package/dist/composition/errors.js +0 -8
- package/dist/composition/format.d.ts +0 -6
- package/dist/composition/format.d.ts.map +0 -1
- package/dist/composition/format.js +0 -65
- package/dist/composition/install.d.ts +0 -16
- package/dist/composition/install.d.ts.map +0 -1
- package/dist/composition/install.js +0 -175
- package/dist/composition/packages.d.ts +0 -6
- package/dist/composition/packages.d.ts.map +0 -1
- package/dist/composition/packages.js +0 -55
- package/dist/composition/paths.d.ts +0 -4
- package/dist/composition/paths.d.ts.map +0 -1
- package/dist/composition/paths.js +0 -47
- package/dist/composition/planner.d.ts +0 -4
- package/dist/composition/planner.d.ts.map +0 -1
- package/dist/composition/planner.js +0 -366
- package/dist/composition/schema.d.ts +0 -294
- package/dist/composition/schema.d.ts.map +0 -1
- package/dist/composition/schema.js +0 -166
- package/dist/composition/types.d.ts +0 -111
- package/dist/composition/types.d.ts.map +0 -1
- package/dist/composition/types.js +0 -6
- package/dist/composition/typescript-paths.d.ts +0 -6
- package/dist/composition/typescript-paths.d.ts.map +0 -1
- package/dist/composition/typescript-paths.js +0 -157
- package/dist/composition/use.d.ts +0 -23
- package/dist/composition/use.d.ts.map +0 -1
- package/dist/composition/use.js +0 -187
- package/dist/composition/workspace.d.ts +0 -10
- package/dist/composition/workspace.d.ts.map +0 -1
- package/dist/composition/workspace.js +0 -210
- package/dist/constants.d.ts +0 -11
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js +0 -39
- package/dist/fs-utils.d.ts +0 -9
- package/dist/fs-utils.d.ts.map +0 -1
- package/dist/fs-utils.js +0 -64
- package/dist/git.d.ts +0 -28
- package/dist/git.d.ts.map +0 -1
- package/dist/git.js +0 -107
- package/dist/instruction-renderer.d.ts +0 -17
- package/dist/instruction-renderer.d.ts.map +0 -1
- package/dist/instruction-renderer.js +0 -132
- package/dist/logger.d.ts +0 -13
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -26
- package/dist/prompts.d.ts +0 -7
- package/dist/prompts.d.ts.map +0 -1
- package/dist/prompts.js +0 -54
- package/dist/sanitize.d.ts +0 -10
- package/dist/sanitize.d.ts.map +0 -1
- package/dist/sanitize.js +0 -110
- package/dist/scaffold.d.ts +0 -7
- package/dist/scaffold.d.ts.map +0 -1
- package/dist/scaffold.js +0 -330
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js +0 -2
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Effect, FileSystem, Path, Schema } from "effect";
|
|
2
|
+
import { applyEdits, modify, parse } from "jsonc-parser";
|
|
3
|
+
import { InvalidComposition } from "./errors.js";
|
|
4
|
+
const Config = Schema.Struct({
|
|
5
|
+
compilerOptions: Schema.optionalKey(Schema.Struct({
|
|
6
|
+
baseUrl: Schema.optionalKey(Schema.String),
|
|
7
|
+
paths: Schema.optionalKey(Schema.Record(Schema.String, Schema.Array(Schema.String))),
|
|
8
|
+
})),
|
|
9
|
+
});
|
|
10
|
+
export const applyTypeScriptAliases = Effect.fn("Composition.applyTypeScriptAliases")(function* (root, aliases, ownedAliases) {
|
|
11
|
+
const fs = yield* FileSystem.FileSystem;
|
|
12
|
+
const path = yield* Path.Path;
|
|
13
|
+
for (const cwd of new Set([...ownedAliases, ...aliases].map((alias) => alias.cwd))) {
|
|
14
|
+
const filename = path.join(root, cwd, "tsconfig.json");
|
|
15
|
+
const desired = aliases.filter((entry) => entry.cwd === cwd);
|
|
16
|
+
if (desired.length === 0 && !(yield* fs.exists(filename))) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
let text = yield* fs.readFileString(filename);
|
|
20
|
+
const errors = [];
|
|
21
|
+
const data = parse(text, errors, { allowTrailingComma: true });
|
|
22
|
+
if (errors.length) {
|
|
23
|
+
return yield* new InvalidComposition({
|
|
24
|
+
message: `Invalid TypeScript configuration: ${cwd}/tsconfig.json`,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const config = yield* Schema.decodeUnknownEffect(Config)(data);
|
|
28
|
+
const consumer = path.join(root, cwd, config.compilerOptions?.baseUrl ?? ".");
|
|
29
|
+
const updates = new Map();
|
|
30
|
+
for (const alias of ownedAliases.filter((entry) => entry.cwd === cwd)) {
|
|
31
|
+
updates.set(alias.alias, undefined);
|
|
32
|
+
updates.set(`${alias.alias}/*`, undefined);
|
|
33
|
+
}
|
|
34
|
+
for (const alias of desired) {
|
|
35
|
+
const relative = path
|
|
36
|
+
.relative(consumer, path.join(root, alias.sourcePath))
|
|
37
|
+
.split(path.sep)
|
|
38
|
+
.join("/");
|
|
39
|
+
const target = relative.startsWith(".") ? relative : `./${relative}`;
|
|
40
|
+
updates.set(alias.alias, [target]);
|
|
41
|
+
if (!/\.[cm]?[jt]sx?$/u.test(alias.sourcePath)) {
|
|
42
|
+
updates.set(`${alias.alias}/*`, [`${target}/*`]);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
for (const [name, value] of updates) {
|
|
46
|
+
text = applyEdits(text, modify(text, ["compilerOptions", "paths", name], value, {
|
|
47
|
+
formattingOptions: { insertSpaces: true, tabSize: 2 },
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
yield* fs.writeFileString(filename, text).pipe(Effect.uninterruptible);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Context, Effect, FileSystem, Layer, Path } from "effect";
|
|
2
|
+
import type { PlatformError } from "effect";
|
|
3
|
+
import { ChildProcessSpawner } from "effect/unstable/process";
|
|
4
|
+
import { DependencyInstallationFailed, WorkspaceConflict } from "./errors.ts";
|
|
5
|
+
import type { PreparedFile } from "./model.ts";
|
|
6
|
+
import type { WorkspaceStateError, WorkspaceWriteAccess, WorkspaceObservation } from "./workspace-state.ts";
|
|
7
|
+
export interface DependencyOptions {
|
|
8
|
+
readonly install?: "install" | "skip";
|
|
9
|
+
readonly offline?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export type DependencyError = DependencyInstallationFailed | WorkspaceConflict | PlatformError.PlatformError | WorkspaceStateError;
|
|
12
|
+
export interface DependencyOutcome {
|
|
13
|
+
readonly dependencies: "pending" | "current";
|
|
14
|
+
readonly dependencyReasons: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
declare const WorkspaceDependencies_base: Context.ServiceClass<WorkspaceDependencies, "create-next-hydra/WorkspaceDependencies", {
|
|
17
|
+
readonly install: (directory: string) => Effect.Effect<void, DependencyInstallationFailed>;
|
|
18
|
+
readonly recover: (access: WorkspaceWriteAccess) => Effect.Effect<void, DependencyError>;
|
|
19
|
+
readonly inspect: (observation: Pick<WorkspaceObservation, "directory" | "entries" | "installation">, files: readonly PreparedFile[], options: Pick<DependencyOptions, "offline">) => Effect.Effect<DependencyOutcome, DependencyError>;
|
|
20
|
+
readonly reconcile: (directory: string, files: readonly PreparedFile[], options: DependencyOptions, access?: WorkspaceWriteAccess) => Effect.Effect<DependencyOutcome, DependencyError>;
|
|
21
|
+
}>;
|
|
22
|
+
export declare class WorkspaceDependencies extends WorkspaceDependencies_base {
|
|
23
|
+
static readonly layer: Layer.Layer<WorkspaceDependencies, never, FileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner>;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=workspace-dependencies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-dependencies.d.ts","sourceRoot":"","sources":["../src/workspace-dependencies.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,OAAO,EACP,MAAM,EACN,UAAU,EACV,KAAK,EAGL,IAAI,EAGL,MAAM,QAAQ,CAAC;AAChB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,EAAgB,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG5E,OAAO,EACL,4BAA4B,EAC5B,iBAAiB,EAElB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,KAAK,EAEV,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACrB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AACD,MAAM,MAAM,eAAe,GACvB,4BAA4B,GAC5B,iBAAiB,GACjB,aAAa,CAAC,aAAa,GAC3B,mBAAmB,CAAC;AACxB,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,YAAY,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/C;;sBAgMqB,CAChB,SAAS,EAAE,MAAM,KACd,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,4BAA4B,CAAC;sBACpC,CAChB,MAAM,EAAE,oBAAoB,KACzB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAAC;sBACvB,CAChB,WAAW,EAAE,IAAI,CACf,oBAAoB,EACpB,WAAW,GAAG,SAAS,GAAG,cAAc,CACzC,EACD,KAAK,EAAE,SAAS,YAAY,EAAE,EAC9B,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,KACxC,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,eAAe,CAAC;wBAClC,CAClB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,SAAS,YAAY,EAAE,EAC9B,OAAO,EAAE,iBAAiB,EAC1B,MAAM,CAAC,EAAE,oBAAoB,KAC1B,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,eAAe,CAAC;;AAtB1D,qBAAa,qBAAsB,SAAQ,0BAwBG;IAC5C,MAAM,CAAC,QAAQ,CAAC,KAAK,yHA0fnB;CACH"}
|
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { isDeepStrictEqual } from "node:util";
|
|
4
|
+
import { Array as EffectArray, Config, Context, Effect, FileSystem, Layer, Order, Option, Path, Schema, Stream, } from "effect";
|
|
5
|
+
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
|
|
6
|
+
import { parseDocument } from "yaml";
|
|
7
|
+
import { DependencyInstallationFailed, WorkspaceConflict, WorkspaceRecoveryRequired, } from "./errors.js";
|
|
8
|
+
import { relativeFile } from "./files.js";
|
|
9
|
+
import { confirmStopped, groupExists } from "./process-group.js";
|
|
10
|
+
import { sameFingerprint } from "./workspace-state.js";
|
|
11
|
+
const fingerprint = (content, mode) => ({
|
|
12
|
+
hash: createHash("sha256").update(content).digest("hex"),
|
|
13
|
+
mode,
|
|
14
|
+
});
|
|
15
|
+
const ManifestDependencies = Schema.Struct({
|
|
16
|
+
dependencies: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
|
|
17
|
+
devDependencies: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
|
|
18
|
+
optionalDependencies: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
|
|
19
|
+
});
|
|
20
|
+
// Hooks and local packages can read inputs outside the prepared manifest set.
|
|
21
|
+
// Delegate their reconciliation to pnpm instead of inventing a second resolver.
|
|
22
|
+
const hasUnboundedInputs = (value) => {
|
|
23
|
+
if (Schema.is(Schema.String)(value)) {
|
|
24
|
+
return /^(?:file|link|portal):/u.test(value);
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(value)) {
|
|
27
|
+
return value.some(hasUnboundedInputs);
|
|
28
|
+
}
|
|
29
|
+
if (Schema.is(Schema.JsonObject)(value)) {
|
|
30
|
+
return Object.entries(value).some(([key, entry]) => ([
|
|
31
|
+
"preinstall",
|
|
32
|
+
"install",
|
|
33
|
+
"postinstall",
|
|
34
|
+
"prepare",
|
|
35
|
+
"pnpm:devPreinstall",
|
|
36
|
+
"pnpmfile",
|
|
37
|
+
"globalPnpmfile",
|
|
38
|
+
].includes(key) &&
|
|
39
|
+
Boolean(entry)) ||
|
|
40
|
+
(["injected", "injectWorkspacePackages"].includes(key) &&
|
|
41
|
+
entry === true) ||
|
|
42
|
+
hasUnboundedInputs(entry));
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
};
|
|
46
|
+
// These structured pnpm settings can be mirrored into scripts from workspace YAML.
|
|
47
|
+
// They affect installation even when they match an ancestor's configuration.
|
|
48
|
+
const structuredPnpmSettings = {
|
|
49
|
+
npm_config_catalog: "catalog",
|
|
50
|
+
npm_config_catalogs: "catalogs",
|
|
51
|
+
npm_config_overrides: "overrides",
|
|
52
|
+
npm_config_patched_dependencies: "patchedDependencies",
|
|
53
|
+
};
|
|
54
|
+
const inputsFor = (directory, files, fs, path) => Effect.gen(function* () {
|
|
55
|
+
const settings = files.find((file) => file.target === "pnpm-workspace.yaml");
|
|
56
|
+
const document = yield* Effect.try(() => parseDocument(new TextDecoder().decode(settings?.content)));
|
|
57
|
+
if (document.errors.length) {
|
|
58
|
+
return yield* Effect.fail(document.errors[0]);
|
|
59
|
+
}
|
|
60
|
+
const decode = yield* Effect.try(() => Schema.decodeUnknownEffect(Schema.JsonObject)(document.toJS()));
|
|
61
|
+
const config = yield* decode;
|
|
62
|
+
const patches = yield* Schema.decodeEffect(Schema.Struct({
|
|
63
|
+
patchedDependencies: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
|
|
64
|
+
}))(config);
|
|
65
|
+
const targets = new Set(["pnpm-workspace.yaml", "pnpm-lock.yaml"]);
|
|
66
|
+
const outputs = [];
|
|
67
|
+
let unbounded = hasUnboundedInputs(config);
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
if (file.target === "package.json" ||
|
|
70
|
+
file.target.endsWith("/package.json")) {
|
|
71
|
+
targets.add(file.target);
|
|
72
|
+
const manifest = yield* Schema.decodeEffect(Schema.fromJsonString(Schema.JsonObject))(new TextDecoder().decode(file.content));
|
|
73
|
+
unbounded ||= hasUnboundedInputs(manifest);
|
|
74
|
+
const dependencies = yield* Schema.decodeEffect(ManifestDependencies)(manifest);
|
|
75
|
+
const parent = file.target.slice(0, -"package.json".length);
|
|
76
|
+
for (const name of Object.keys({
|
|
77
|
+
...dependencies.dependencies,
|
|
78
|
+
...dependencies.devDependencies,
|
|
79
|
+
})) {
|
|
80
|
+
if (!Object.hasOwn(dependencies.optionalDependencies ?? {}, name)) {
|
|
81
|
+
outputs.push(`${parent}node_modules/${yield* relativeFile(name)}/package.json`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const target of Object.values(patches.patchedDependencies ?? {})) {
|
|
87
|
+
targets.add(yield* relativeFile(target));
|
|
88
|
+
}
|
|
89
|
+
const ordered = EffectArray.sort([...targets], Order.String);
|
|
90
|
+
const selected = [];
|
|
91
|
+
for (const target of ordered) {
|
|
92
|
+
const file = files.find((candidate) => candidate.target === target);
|
|
93
|
+
if (!file) {
|
|
94
|
+
return yield* new DependencyInstallationFailed({
|
|
95
|
+
directory,
|
|
96
|
+
phase: "inputs",
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
selected.push(file);
|
|
100
|
+
}
|
|
101
|
+
const hash = createHash("sha256");
|
|
102
|
+
for (const file of selected) {
|
|
103
|
+
hash
|
|
104
|
+
.update(file.target)
|
|
105
|
+
.update("\0")
|
|
106
|
+
.update(String(file.mode))
|
|
107
|
+
.update("\0")
|
|
108
|
+
.update(String(file.content.length))
|
|
109
|
+
.update("\0")
|
|
110
|
+
.update(file.content);
|
|
111
|
+
}
|
|
112
|
+
hash.update("pnpm-environment\0");
|
|
113
|
+
for (const name of EffectArray.sort(Object.keys(process.env), Order.String)) {
|
|
114
|
+
if (Object.hasOwn(structuredPnpmSettings, name.toLowerCase())) {
|
|
115
|
+
hash
|
|
116
|
+
.update(name.toLowerCase())
|
|
117
|
+
.update("\0")
|
|
118
|
+
.update(process.env[name] ?? "")
|
|
119
|
+
.update("\0");
|
|
120
|
+
const decoded = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Json))(process.env[name]);
|
|
121
|
+
if (Option.isNone(decoded)) {
|
|
122
|
+
unbounded = true;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
unbounded ||= hasUnboundedInputs(decoded.value);
|
|
126
|
+
if (name.toLowerCase() === "npm_config_patched_dependencies") {
|
|
127
|
+
const environmentPatches = Schema.decodeUnknownOption(Schema.Record(Schema.String, Schema.String))(decoded.value);
|
|
128
|
+
if (Option.isNone(environmentPatches)) {
|
|
129
|
+
unbounded = true;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
for (const patch of EffectArray.sort(Object.values(environmentPatches.value), Order.String)) {
|
|
133
|
+
const patchPath = path.resolve(directory, patch);
|
|
134
|
+
if (!(yield* fs.exists(patchPath)) ||
|
|
135
|
+
(yield* fs.stat(patchPath)).type !== "File") {
|
|
136
|
+
unbounded = true;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const content = yield* fs.readFile(patchPath);
|
|
140
|
+
hash
|
|
141
|
+
.update(patchPath)
|
|
142
|
+
.update("\0")
|
|
143
|
+
.update(String(content.length))
|
|
144
|
+
.update("\0")
|
|
145
|
+
.update(content);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return { files: selected, key: hash.digest("hex"), outputs, unbounded };
|
|
151
|
+
});
|
|
152
|
+
export class WorkspaceDependencies extends Context.Service()("create-next-hydra/WorkspaceDependencies") {
|
|
153
|
+
static layer = Layer.effect(WorkspaceDependencies, Effect.gen(function* () {
|
|
154
|
+
const fs = yield* FileSystem.FileSystem;
|
|
155
|
+
const path = yield* Path.Path;
|
|
156
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
157
|
+
const runInstall = (directory, offline, lifecycle) => Effect.scoped(Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
|
|
158
|
+
const failure = (phase) => new DependencyInstallationFailed({ directory, phase });
|
|
159
|
+
yield* lifecycle?.starting ?? Effect.void;
|
|
160
|
+
const handle = yield* spawner
|
|
161
|
+
.spawn(ChildProcess.make("pnpm", [
|
|
162
|
+
"install",
|
|
163
|
+
...(offline ? ["--offline"] : []),
|
|
164
|
+
"--no-frozen-lockfile",
|
|
165
|
+
], {
|
|
166
|
+
cwd: directory,
|
|
167
|
+
forceKillAfter: "100 millis",
|
|
168
|
+
stderr: "inherit",
|
|
169
|
+
stdin: "ignore",
|
|
170
|
+
stdout: "inherit",
|
|
171
|
+
}))
|
|
172
|
+
.pipe(Effect.mapError(() => failure("launch")), Effect.tapError(() => lifecycle?.stopped ?? Effect.void));
|
|
173
|
+
return yield* restore(handle.exitCode.pipe(Effect.mapError(() => failure("exit")))).pipe(Effect.tap(() => groupExists(handle.pid).pipe(Effect.mapError(() => failure("stop")), Effect.flatMap((exists) => exists ? Effect.fail(failure("stop")) : Effect.void))), Effect.onExit(() => confirmStopped(handle).pipe(Effect.mapError(() => failure("stop")), Effect.andThen(lifecycle?.stopped ?? Effect.void), Effect.onExit(() => handle.unref.pipe(Effect.mapError(() => failure("stop")), Effect.asVoid)))));
|
|
174
|
+
})));
|
|
175
|
+
const install = Effect.fn("WorkspaceDependencies.install")(function* (directory) {
|
|
176
|
+
const code = yield* runInstall(directory, false);
|
|
177
|
+
if (code !== 0) {
|
|
178
|
+
return yield* new DependencyInstallationFailed({
|
|
179
|
+
directory,
|
|
180
|
+
phase: "exit",
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
const observe = (file) => Effect.gen(function* () {
|
|
185
|
+
const stat = yield* fs.stat(file);
|
|
186
|
+
return fingerprint(yield* fs.readFile(file), stat.mode % 0o1000);
|
|
187
|
+
});
|
|
188
|
+
const optionalObservation = (file) => observe(file).pipe(Effect.orElseSucceed(() => null));
|
|
189
|
+
const manifestPresent = (file) => fs.stat(file).pipe(Effect.map((stat) => stat.type === "File"), Effect.orElseSucceed(() => false));
|
|
190
|
+
const recover = Effect.fn("WorkspaceDependencies.recover")(function* (access) {
|
|
191
|
+
const observation = yield* access.observation;
|
|
192
|
+
if (observation.pending?.kind !== "installation") {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (!observation.pending.stopped) {
|
|
196
|
+
return yield* new WorkspaceRecoveryRequired({
|
|
197
|
+
directory: access.directory,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const conflicts = [];
|
|
201
|
+
for (const entry of observation.entries) {
|
|
202
|
+
if (!sameFingerprint(yield* optionalObservation(path.join(access.directory, entry.target)), entry.applied)) {
|
|
203
|
+
conflicts.push(entry.target);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (conflicts.length > 0) {
|
|
207
|
+
return yield* new WorkspaceConflict({ paths: conflicts });
|
|
208
|
+
}
|
|
209
|
+
// An interrupted lockfile rewrite is not proven pnpm normalization.
|
|
210
|
+
// Preserve all source and installed output; a later reconcile may retry.
|
|
211
|
+
yield* access.recoverInstall(observation.revision);
|
|
212
|
+
});
|
|
213
|
+
const externalConfiguration = (directory) => Effect.gen(function* () {
|
|
214
|
+
const roots = new Set([homedir()]);
|
|
215
|
+
let parent = directory;
|
|
216
|
+
while (!roots.has(parent)) {
|
|
217
|
+
roots.add(parent);
|
|
218
|
+
const next = path.dirname(parent);
|
|
219
|
+
if (next === parent) {
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
parent = next;
|
|
223
|
+
}
|
|
224
|
+
const mirrored = new Map();
|
|
225
|
+
for (const root of roots) {
|
|
226
|
+
const settingsPath = path.join(root, "pnpm-workspace.yaml");
|
|
227
|
+
if (yield* fs.exists(settingsPath)) {
|
|
228
|
+
const document = parseDocument(yield* fs.readFileString(settingsPath));
|
|
229
|
+
if (document.errors.length) {
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
const decodedSettings = Schema.decodeUnknownOption(Schema.JsonObject)(document.toJS());
|
|
233
|
+
if (Option.isNone(decodedSettings)) {
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
const settings = decodedSettings.value;
|
|
237
|
+
for (const [name, key] of Object.entries(structuredPnpmSettings)) {
|
|
238
|
+
const values = mirrored.get(name) ?? [];
|
|
239
|
+
values.push(settings[key]);
|
|
240
|
+
const patches = Schema.decodeUnknownOption(Schema.Record(Schema.String, Schema.String))(settings[key]);
|
|
241
|
+
if (key === "patchedDependencies" && Option.isSome(patches)) {
|
|
242
|
+
values.push(Object.fromEntries(Object.entries(patches.value).map(([name, file]) => [
|
|
243
|
+
name,
|
|
244
|
+
path.resolve(root, file),
|
|
245
|
+
])));
|
|
246
|
+
}
|
|
247
|
+
mirrored.set(name, values);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (yield* fs.exists(path.join(root, ".pnpmfile.cjs"))) {
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
const npmrc = path.join(root, ".npmrc");
|
|
254
|
+
if (yield* fs.exists(npmrc)) {
|
|
255
|
+
// Login credentials do not change an already installed graph. Accept
|
|
256
|
+
// only literal auth assignments/comments; unknown INI syntax and all
|
|
257
|
+
// other settings still require pnpm. Never hash or retain values.
|
|
258
|
+
const content = yield* fs.readFileString(npmrc);
|
|
259
|
+
if (content.split(/\r?\n/u).some((line) => {
|
|
260
|
+
const setting = line.trim();
|
|
261
|
+
return (setting !== "" &&
|
|
262
|
+
!/^[#;]/u.test(setting) &&
|
|
263
|
+
!/^(?:\/\/[^\s=]+:)?(?:_authToken|_auth|username|_password)\s*=/u.test(setting));
|
|
264
|
+
})) {
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// Invocation metadata does not affect installation. Other npm configuration
|
|
270
|
+
// and PNPM_* settings may; keep values out of both hashes and diagnostics.
|
|
271
|
+
const metadata = new Set([
|
|
272
|
+
"npm_config_user_agent",
|
|
273
|
+
"npm_config_recursive",
|
|
274
|
+
"npm_config_manage_package_manager_versions",
|
|
275
|
+
"npm_config_verify_deps_before_run",
|
|
276
|
+
]);
|
|
277
|
+
return Object.entries(process.env).some(([name, value]) => {
|
|
278
|
+
const key = name.toLowerCase();
|
|
279
|
+
// pnpm run exports workspace objects as JSON, plus default registries.
|
|
280
|
+
// Accept mirrors only when they equal declared configuration; overrides
|
|
281
|
+
// and unknown settings must still be reconciled by pnpm.
|
|
282
|
+
const candidates = mirrored.get(key);
|
|
283
|
+
if (candidates) {
|
|
284
|
+
const decoded = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Json))(value);
|
|
285
|
+
if (Option.isSome(decoded) &&
|
|
286
|
+
candidates.some((candidate) => isDeepStrictEqual(candidate, decoded.value))) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if ((key === "npm_config_registry" &&
|
|
291
|
+
value === "https://registry.npmjs.org/") ||
|
|
292
|
+
(key === "npm_config__jsr_registry" &&
|
|
293
|
+
value === "https://npm.jsr.io/") ||
|
|
294
|
+
(key === "npm_config_frozen_lockfile" && value === "")) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
return ((key.startsWith("npm_config_") && !metadata.has(key)) ||
|
|
298
|
+
(name.startsWith("PNPM_") &&
|
|
299
|
+
![
|
|
300
|
+
"PNPM_HOME",
|
|
301
|
+
"PNPM_PACKAGE_NAME",
|
|
302
|
+
"PNPM_SCRIPT_SRC_DIR",
|
|
303
|
+
].includes(name)));
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
const inspect = Effect.fn("WorkspaceDependencies.inspect")(function* (observation, files, options) {
|
|
307
|
+
const { directory, installation: previous } = observation;
|
|
308
|
+
if (!previous) {
|
|
309
|
+
return {
|
|
310
|
+
dependencies: "pending",
|
|
311
|
+
dependencyReasons: ["No successful installation recorded"],
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
const failure = () => new DependencyInstallationFailed({ directory, phase: "inputs" });
|
|
315
|
+
const inputs = yield* inputsFor(directory, files, fs, path).pipe(Effect.mapError(failure));
|
|
316
|
+
const reasons = [];
|
|
317
|
+
if (inputs.key !== previous.inputs) {
|
|
318
|
+
reasons.push("Installation inputs changed");
|
|
319
|
+
}
|
|
320
|
+
const applied = new Map(observation.entries.map((entry) => [entry.target, entry.applied]));
|
|
321
|
+
for (const file of inputs.files) {
|
|
322
|
+
const expected = applied.get(file.target);
|
|
323
|
+
if (!expected ||
|
|
324
|
+
!sameFingerprint(yield* optionalObservation(path.join(directory, file.target)), expected)) {
|
|
325
|
+
reasons.push(`Applied installation input changed: ${file.target}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const environment = yield* Config.String("NODE_ENV").pipe(Config.withDefault("development"), Effect.mapError(failure));
|
|
329
|
+
// Inspection verifies recorded installation, not the executable a future install
|
|
330
|
+
// might select. Never bootstrap a package manager during read-only inspection.
|
|
331
|
+
if (!previous.toolchain.startsWith(`${process.version}/${process.platform}/${process.arch}/pnpm-`) ||
|
|
332
|
+
!previous.toolchain.endsWith(`/offline-${options.offline === true}/production-${environment === "production"}`)) {
|
|
333
|
+
reasons.push("Runtime or installation options changed");
|
|
334
|
+
}
|
|
335
|
+
if (inputs.unbounded || (yield* externalConfiguration(directory))) {
|
|
336
|
+
reasons.push("External installation inputs require reconciliation");
|
|
337
|
+
}
|
|
338
|
+
for (const [target, expected] of [
|
|
339
|
+
["pnpm-lock.yaml", previous.lockfile],
|
|
340
|
+
["node_modules/.modules.yaml", previous.modules],
|
|
341
|
+
["node_modules/.pnpm/lock.yaml", previous.virtualLock],
|
|
342
|
+
]) {
|
|
343
|
+
if (!sameFingerprint(yield* optionalObservation(path.join(directory, target)), expected)) {
|
|
344
|
+
reasons.push(`Installation output missing or changed: ${target}`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
for (const target of inputs.outputs) {
|
|
348
|
+
if (!(yield* manifestPresent(path.join(directory, target)))) {
|
|
349
|
+
reasons.push(`Installed dependency missing: ${target}`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
dependencies: reasons.length ? "pending" : "current",
|
|
354
|
+
dependencyReasons: reasons,
|
|
355
|
+
};
|
|
356
|
+
});
|
|
357
|
+
const reconcile = Effect.fn("WorkspaceDependencies.reconcile")(function* (directory, files, options, access) {
|
|
358
|
+
if (options.install === "skip") {
|
|
359
|
+
return {
|
|
360
|
+
dependencies: "pending",
|
|
361
|
+
dependencyReasons: ["Installation skipped"],
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
const failure = (phase) => new DependencyInstallationFailed({ directory, phase });
|
|
365
|
+
if (process.platform === "win32") {
|
|
366
|
+
return yield* failure("launch");
|
|
367
|
+
}
|
|
368
|
+
const inputs = yield* inputsFor(directory, files, fs, path).pipe(Effect.mapError(() => failure("inputs")));
|
|
369
|
+
const preserved = access ? (yield* access.observation).preserved : [];
|
|
370
|
+
const workspaceOwnedInputs = inputs.files.filter((file) => preserved.includes(file.target));
|
|
371
|
+
if (workspaceOwnedInputs.length > 0) {
|
|
372
|
+
return {
|
|
373
|
+
dependencies: "pending",
|
|
374
|
+
dependencyReasons: [
|
|
375
|
+
`Installation skipped for preserved pnpm inputs: ${workspaceOwnedInputs.map((file) => file.target).join(", ")}. Run pnpm install in the workspace`,
|
|
376
|
+
],
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
const expected = access
|
|
380
|
+
? new Map((yield* access.appliedEntries).map((entry) => [
|
|
381
|
+
entry.target,
|
|
382
|
+
entry.applied,
|
|
383
|
+
]))
|
|
384
|
+
: new Map(files.map((file) => [
|
|
385
|
+
file.target,
|
|
386
|
+
fingerprint(file.content, file.mode),
|
|
387
|
+
]));
|
|
388
|
+
const verifyInputs = (includeLock) => Effect.gen(function* () {
|
|
389
|
+
for (const file of inputs.files) {
|
|
390
|
+
if (!includeLock && file.target === "pnpm-lock.yaml") {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
const actual = yield* optionalObservation(path.join(directory, file.target));
|
|
394
|
+
if (!sameFingerprint(actual, expected.get(file.target) ?? null)) {
|
|
395
|
+
return yield* failure("inputs");
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
const current = yield* inputsFor(directory, files, fs, path).pipe(Effect.mapError(() => failure("inputs")));
|
|
399
|
+
if (current.key !== inputs.key) {
|
|
400
|
+
return yield* failure("inputs");
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
yield* verifyInputs(true);
|
|
404
|
+
// Version discovery uses the same executable and working directory as install.
|
|
405
|
+
const version = yield* Effect.scoped(Effect.gen(function* () {
|
|
406
|
+
const handle = yield* spawner.spawn(ChildProcess.make("pnpm", ["--version"], {
|
|
407
|
+
cwd: directory,
|
|
408
|
+
stderr: "ignore",
|
|
409
|
+
stdin: "ignore",
|
|
410
|
+
}));
|
|
411
|
+
const [output, code] = yield* Effect.all([
|
|
412
|
+
Stream.mkString(Stream.decodeText(handle.stdout)),
|
|
413
|
+
handle.exitCode,
|
|
414
|
+
], { concurrency: "unbounded" });
|
|
415
|
+
if (code !== 0 ||
|
|
416
|
+
!/^\d+\.\d+\.\d+(?:-[\w.-]+)?\s*$/u.test(output)) {
|
|
417
|
+
return yield* failure("version");
|
|
418
|
+
}
|
|
419
|
+
return output.trim();
|
|
420
|
+
})).pipe(Effect.mapError(() => failure("version")));
|
|
421
|
+
yield* verifyInputs(true);
|
|
422
|
+
const environment = yield* Config.String("NODE_ENV").pipe(Config.withDefault("development"), Effect.mapError(() => failure("inputs")));
|
|
423
|
+
const toolchain = `${process.version}/${process.platform}/${process.arch}/pnpm-${version}/offline-${options.offline === true}/production-${environment === "production"}`;
|
|
424
|
+
const previous = access ? yield* access.installation : null;
|
|
425
|
+
const modulesPath = path.join(directory, "node_modules/.modules.yaml");
|
|
426
|
+
const virtualLockPath = path.join(directory, "node_modules/.pnpm/lock.yaml");
|
|
427
|
+
const lockPath = path.join(directory, "pnpm-lock.yaml");
|
|
428
|
+
const outputsPresent = Effect.gen(function* () {
|
|
429
|
+
for (const output of inputs.outputs) {
|
|
430
|
+
if (!(yield* manifestPresent(path.join(directory, output)))) {
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return true;
|
|
435
|
+
});
|
|
436
|
+
if (access && previous?.toolchain === toolchain) {
|
|
437
|
+
const observed = yield* inspect({
|
|
438
|
+
directory,
|
|
439
|
+
entries: yield* access.appliedEntries,
|
|
440
|
+
installation: previous,
|
|
441
|
+
}, files, options);
|
|
442
|
+
if (observed.dependencies === "current") {
|
|
443
|
+
return observed;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const code = yield* runInstall(directory, options.offline === true, access
|
|
447
|
+
? {
|
|
448
|
+
starting: access.beginInstall(inputs.key),
|
|
449
|
+
stopped: access.installStopped,
|
|
450
|
+
}
|
|
451
|
+
: undefined).pipe(Effect.tapErrorTag("DependencyInstallationFailed", (error) => Effect.gen(function* () {
|
|
452
|
+
if (error.phase === "launch" && access) {
|
|
453
|
+
yield* verifyInputs(true);
|
|
454
|
+
yield* access.finishInstall(null, yield* observe(lockPath));
|
|
455
|
+
}
|
|
456
|
+
})));
|
|
457
|
+
yield* verifyInputs(false);
|
|
458
|
+
const lockfile = yield* observe(lockPath).pipe(Effect.mapError(() => failure("outputs")));
|
|
459
|
+
if (code !== 0) {
|
|
460
|
+
if (access &&
|
|
461
|
+
sameFingerprint(lockfile, expected.get("pnpm-lock.yaml") ?? null)) {
|
|
462
|
+
yield* access.finishInstall(null, lockfile);
|
|
463
|
+
}
|
|
464
|
+
return yield* failure("exit");
|
|
465
|
+
}
|
|
466
|
+
const modules = yield* observe(modulesPath).pipe(Effect.mapError(() => failure("outputs")));
|
|
467
|
+
const virtualLock = yield* observe(virtualLockPath).pipe(Effect.mapError(() => failure("outputs")));
|
|
468
|
+
if (!(yield* outputsPresent)) {
|
|
469
|
+
return yield* failure("outputs");
|
|
470
|
+
}
|
|
471
|
+
if (access) {
|
|
472
|
+
yield* access.finishInstall({ inputs: inputs.key, lockfile, modules, toolchain, virtualLock }, lockfile);
|
|
473
|
+
}
|
|
474
|
+
return { dependencies: "current", dependencyReasons: [] };
|
|
475
|
+
});
|
|
476
|
+
return WorkspaceDependencies.of({ inspect, install, reconcile, recover });
|
|
477
|
+
}));
|
|
478
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Context, Effect, FileSystem, Layer, Path } from "effect";
|
|
2
|
+
import type { PlatformError } from "effect";
|
|
3
|
+
import { MaterializationFailed, WorkspaceConflict } from "./errors.ts";
|
|
4
|
+
import type { FileChange, PreparedFile, PreparedWorkspace } from "./model.ts";
|
|
5
|
+
import type { WorkspaceWriteAccess, WorkspaceStateError, WorkspaceObservation } from "./workspace-state.ts";
|
|
6
|
+
export type WorkspaceFileError = WorkspaceConflict | MaterializationFailed | PlatformError.PlatformError;
|
|
7
|
+
declare const WorkspaceFiles_base: Context.ServiceClass<WorkspaceFiles, "create-next-hydra/WorkspaceFiles", {
|
|
8
|
+
readonly recover: (access: WorkspaceWriteAccess) => Effect.Effect<void, WorkspaceFileError | WorkspaceStateError>;
|
|
9
|
+
readonly capture: (directory: string, entries: WorkspaceObservation["entries"]) => Effect.Effect<readonly PreparedFile[], WorkspaceFileError>;
|
|
10
|
+
readonly read: (directory: string, targets: readonly string[]) => Effect.Effect<{
|
|
11
|
+
readonly files: readonly PreparedFile[];
|
|
12
|
+
readonly conflicts: readonly string[];
|
|
13
|
+
}, WorkspaceFileError>;
|
|
14
|
+
readonly inspect: (observation: WorkspaceObservation, files: PreparedWorkspace["files"], preserve?: readonly string[]) => Effect.Effect<readonly FileChange[], WorkspaceFileError>;
|
|
15
|
+
readonly apply: (access: WorkspaceWriteAccess, files: PreparedWorkspace["files"], initialization: readonly string[], preserve?: readonly string[]) => Effect.Effect<{
|
|
16
|
+
readonly removedFiles: readonly string[];
|
|
17
|
+
readonly complete: Effect.Effect<void, WorkspaceFileError | WorkspaceStateError>;
|
|
18
|
+
}, WorkspaceFileError | WorkspaceStateError>;
|
|
19
|
+
}>;
|
|
20
|
+
export declare class WorkspaceFiles extends WorkspaceFiles_base {
|
|
21
|
+
static readonly layer: Layer.Layer<WorkspaceFiles, never, FileSystem.FileSystem | Path.Path>;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=workspace-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-files.d.ts","sourceRoot":"","sources":["../src/workspace-files.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAY,MAAM,QAAQ,CAAC;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE5C,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EAElB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAG9E,OAAO,KAAK,EAGV,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,sBAAsB,CAAC;AAqC9B,MAAM,MAAM,kBAAkB,GAC1B,iBAAiB,GACjB,qBAAqB,GACrB,aAAa,CAAC,aAAa,CAAC;;sBAIV,CAChB,MAAM,EAAE,oBAAoB,KACzB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,GAAG,mBAAmB,CAAC;sBAChD,CAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,oBAAoB,CAAC,SAAS,CAAC,KACrC,MAAM,CAAC,MAAM,CAAC,SAAS,YAAY,EAAE,EAAE,kBAAkB,CAAC;mBAChD,CACb,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,SAAS,MAAM,EAAE,KACvB,MAAM,CAAC,MAAM,CAChB;QACE,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;QACxC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;KACvC,EACD,kBAAkB,CACnB;sBACiB,CAChB,WAAW,EAAE,oBAAoB,EACjC,KAAK,EAAE,iBAAiB,CAAC,OAAO,CAAC,EACjC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,KACzB,MAAM,CAAC,MAAM,CAAC,SAAS,UAAU,EAAE,EAAE,kBAAkB,CAAC;oBAC7C,CACd,MAAM,EAAE,oBAAoB,EAC5B,KAAK,EAAE,iBAAiB,CAAC,OAAO,CAAC,EACjC,cAAc,EAAE,SAAS,MAAM,EAAE,EACjC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,KACzB,MAAM,CAAC,MAAM,CAChB;QACE,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;QACzC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAC9B,IAAI,EACJ,kBAAkB,GAAG,mBAAmB,CACzC,CAAC;KACH,EACD,kBAAkB,GAAG,mBAAmB,CACzC;;AAvCL,qBAAa,cAAe,SAAQ,mBAyCG;IACrC,MAAM,CAAC,QAAQ,CAAC,KAAK,wEAuenB;CACH"}
|