@projitect/cli-internals 0.0.0-stage → 1.0.0
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/LICENSE +21 -0
- package/dist/applier.d.ts +12 -0
- package/dist/applier.d.ts.map +1 -0
- package/dist/applier.js +69 -0
- package/dist/applier.js.map +1 -0
- package/dist/commands/add.d.ts +34 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +64 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/build.d.ts +39 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +108 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/dispatch.d.ts +5 -0
- package/dist/commands/dispatch.d.ts.map +1 -0
- package/dist/commands/dispatch.js +211 -0
- package/dist/commands/dispatch.js.map +1 -0
- package/dist/commands/explain.d.ts +10 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +46 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/init.d.ts +33 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +128 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/inspect-json.d.ts +51 -0
- package/dist/commands/inspect-json.d.ts.map +1 -0
- package/dist/commands/inspect-json.js +58 -0
- package/dist/commands/inspect-json.js.map +1 -0
- package/dist/commands/inspect.d.ts +41 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +74 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/remodel.d.ts +22 -0
- package/dist/commands/remodel.d.ts.map +1 -0
- package/dist/commands/remodel.js +49 -0
- package/dist/commands/remodel.js.map +1 -0
- package/dist/config-cascade.d.ts +25 -0
- package/dist/config-cascade.d.ts.map +1 -0
- package/dist/config-cascade.js +45 -0
- package/dist/config-cascade.js.map +1 -0
- package/dist/differ.d.ts +31 -0
- package/dist/differ.d.ts.map +1 -0
- package/dist/differ.js +134 -0
- package/dist/differ.js.map +1 -0
- package/dist/edit-pjt.d.ts +19 -0
- package/dist/edit-pjt.d.ts.map +1 -0
- package/dist/edit-pjt.js +40 -0
- package/dist/edit-pjt.js.map +1 -0
- package/dist/filesystem-impl.d.ts +25 -0
- package/dist/filesystem-impl.d.ts.map +1 -0
- package/dist/filesystem-impl.js +153 -0
- package/dist/filesystem-impl.js.map +1 -0
- package/dist/git.d.ts +29 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +63 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/ipc/client.d.ts +11 -0
- package/dist/ipc/client.d.ts.map +1 -0
- package/dist/ipc/client.js +45 -0
- package/dist/ipc/client.js.map +1 -0
- package/dist/ipc/index.d.ts +5 -0
- package/dist/ipc/index.d.ts.map +1 -0
- package/dist/ipc/index.js +5 -0
- package/dist/ipc/index.js.map +1 -0
- package/dist/ipc/protocol.d.ts +251 -0
- package/dist/ipc/protocol.d.ts.map +1 -0
- package/dist/ipc/protocol.js +41 -0
- package/dist/ipc/protocol.js.map +1 -0
- package/dist/ipc/server.d.ts +9 -0
- package/dist/ipc/server.d.ts.map +1 -0
- package/dist/ipc/server.js +23 -0
- package/dist/ipc/server.js.map +1 -0
- package/dist/ipc/transport.d.ts +12 -0
- package/dist/ipc/transport.d.ts.map +1 -0
- package/dist/ipc/transport.js +19 -0
- package/dist/ipc/transport.js.map +1 -0
- package/dist/loader.d.ts +45 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +65 -0
- package/dist/loader.js.map +1 -0
- package/dist/lockfile.d.ts +22 -0
- package/dist/lockfile.d.ts.map +1 -0
- package/dist/lockfile.js +59 -0
- package/dist/lockfile.js.map +1 -0
- package/dist/permissions.d.ts +20 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +86 -0
- package/dist/permissions.js.map +1 -0
- package/dist/plan.d.ts +278 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +212 -0
- package/dist/plan.js.map +1 -0
- package/dist/platform/file-system.d.ts +3 -0
- package/dist/platform/file-system.d.ts.map +1 -0
- package/dist/platform/file-system.js +109 -0
- package/dist/platform/file-system.js.map +1 -0
- package/dist/platform/index.d.ts +17 -0
- package/dist/platform/index.d.ts.map +1 -0
- package/dist/platform/index.js +20 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/process-runner.d.ts +41 -0
- package/dist/platform/process-runner.d.ts.map +1 -0
- package/dist/platform/process-runner.js +120 -0
- package/dist/platform/process-runner.js.map +1 -0
- package/dist/platform/stdio.d.ts +9 -0
- package/dist/platform/stdio.d.ts.map +1 -0
- package/dist/platform/stdio.js +11 -0
- package/dist/platform/stdio.js.map +1 -0
- package/dist/platform/terminal.d.ts +3 -0
- package/dist/platform/terminal.d.ts.map +1 -0
- package/dist/platform/terminal.js +81 -0
- package/dist/platform/terminal.js.map +1 -0
- package/dist/pm.d.ts +31 -0
- package/dist/pm.d.ts.map +1 -0
- package/dist/pm.js +91 -0
- package/dist/pm.js.map +1 -0
- package/dist/post-apply.d.ts +20 -0
- package/dist/post-apply.d.ts.map +1 -0
- package/dist/post-apply.js +29 -0
- package/dist/post-apply.js.map +1 -0
- package/dist/project-file.d.ts +8 -0
- package/dist/project-file.d.ts.map +1 -0
- package/dist/project-file.js +12 -0
- package/dist/project-file.js.map +1 -0
- package/dist/region.d.ts +105 -0
- package/dist/region.d.ts.map +1 -0
- package/dist/region.js +94 -0
- package/dist/region.js.map +1 -0
- package/dist/remover.d.ts +16 -0
- package/dist/remover.d.ts.map +1 -0
- package/dist/remover.js +88 -0
- package/dist/remover.js.map +1 -0
- package/package.json +52 -3
- package/src/applier.ts +91 -0
- package/src/commands/add.ts +88 -0
- package/src/commands/build.ts +156 -0
- package/src/commands/dispatch.ts +345 -0
- package/src/commands/explain.ts +81 -0
- package/src/commands/init.ts +193 -0
- package/src/commands/inspect-json.ts +68 -0
- package/src/commands/inspect.ts +106 -0
- package/src/commands/remodel.ts +72 -0
- package/src/config-cascade.ts +60 -0
- package/src/differ.ts +188 -0
- package/src/edit-pjt.ts +56 -0
- package/src/filesystem-impl.ts +230 -0
- package/src/git.ts +87 -0
- package/src/index.ts +54 -0
- package/src/ipc/client.ts +108 -0
- package/src/ipc/index.ts +12 -0
- package/src/ipc/protocol.ts +48 -0
- package/src/ipc/server.ts +62 -0
- package/src/ipc/transport.ts +28 -0
- package/src/loader.ts +115 -0
- package/src/lockfile.ts +81 -0
- package/src/permissions.ts +106 -0
- package/src/plan.ts +459 -0
- package/src/platform/file-system.ts +155 -0
- package/src/platform/index.ts +26 -0
- package/src/platform/process-runner.ts +150 -0
- package/src/platform/stdio.ts +11 -0
- package/src/platform/terminal.ts +89 -0
- package/src/pm.ts +131 -0
- package/src/post-apply.ts +45 -0
- package/src/project-file.ts +15 -0
- package/src/region.ts +139 -0
- package/src/remover.ts +127 -0
- package/README.md +0 -3
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { StructX } from "@nunofyobiz/effect-extras";
|
|
2
|
+
import { ProjitectConfig } from "@projitect/core";
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the four config layers into a single `ProjitectConfig`. Later layers win.
|
|
5
|
+
*
|
|
6
|
+
* Layers, in priority order (low to high):
|
|
7
|
+
* 1. `ProjitectConfig.defaults`
|
|
8
|
+
* 2. `env`: parsed `PJT_*` environment variables
|
|
9
|
+
* 3. `blueprintFile`: the `config:` field of the user's `.pjt.ts`
|
|
10
|
+
* 4. `cliArgs`: command-line overrides
|
|
11
|
+
*/
|
|
12
|
+
export const resolveConfig = (layers) =>
|
|
13
|
+
// Absent layers map to `{}` — the cascade reducer's identity, so they fold as a no-op.
|
|
14
|
+
ProjitectConfig.resolve(layers.env ?? {}, layers.blueprintFile ?? {}, layers.cliArgs ?? {});
|
|
15
|
+
/**
|
|
16
|
+
* Parse the relevant `PJT_*` environment variables into a partial config. Unknown vars are
|
|
17
|
+
* ignored. Boolean vars accept `1`/`0`/`true`/`false`; numeric vars are parsed with `Number`.
|
|
18
|
+
*/
|
|
19
|
+
export const parseEnv = (env) => StructX.filterDefined({
|
|
20
|
+
projectRoot: env["PJT_PROJECT_ROOT"],
|
|
21
|
+
blueprintFile: env["PJT_BLUEPRINT_FILE"],
|
|
22
|
+
requireCleanGit: parseBool(env["PJT_REQUIRE_CLEAN_GIT"]),
|
|
23
|
+
jsonOutput: parseBool(env["PJT_JSON_OUTPUT"]),
|
|
24
|
+
verbosity: parseNumber(env["PJT_VERBOSITY"]),
|
|
25
|
+
});
|
|
26
|
+
const parseBool = (v) => {
|
|
27
|
+
if (v === undefined) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
if (v === "1" || v.toLowerCase() === "true") {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
if (v === "0" || v.toLowerCase() === "false") {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
};
|
|
38
|
+
const parseNumber = (v) => {
|
|
39
|
+
if (v === undefined) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const n = Number(v);
|
|
43
|
+
return Number.isFinite(n) ? n : undefined;
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=config-cascade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-cascade.js","sourceRoot":"","sources":["../src/config-cascade.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAOjD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAI7B,EAAmC,EAAE;AACpC,uFAAuF;AACvF,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,EAAE,MAAM,CAAC,aAAa,IAAI,EAAE,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;AAE7F;;;GAGG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,GAAiD,EAC1B,EAAE,CACzB,OAAO,CAAC,aAAa,CAAC;IACpB,WAAW,EAAE,GAAG,CAAC,kBAAkB,CAAC;IACpC,aAAa,EAAE,GAAG,CAAC,oBAAoB,CAAC;IACxC,eAAe,EAAE,SAAS,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACxD,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC7C,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;CAC7C,CAAC,CAAA;AAEJ,MAAM,SAAS,GAAG,CAAC,CAAqB,EAAuB,EAAE;IAC/D,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;QACpB,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;QAC5C,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,OAAO,EAAE,CAAC;QAC7C,OAAO,KAAK,CAAA;IACd,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AAED,MAAM,WAAW,GAAG,CAAC,CAAqB,EAAsB,EAAE;IAChE,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;QACpB,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;IACnB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AAC3C,CAAC,CAAA"}
|
package/dist/differ.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { BlueprintFileSystem, PjtLock, Errors } from "@projitect/core";
|
|
3
|
+
import type { ProjectPlan, UpgradeRecord } from "./plan.js";
|
|
4
|
+
export interface FileDiff {
|
|
5
|
+
readonly path: string;
|
|
6
|
+
/** "create" — file absent, will be created; "modify" — exists but content drifts; "ok" — in sync. */
|
|
7
|
+
readonly status: "create" | "modify" | "ok";
|
|
8
|
+
readonly summary: string;
|
|
9
|
+
}
|
|
10
|
+
export interface PlanDiff {
|
|
11
|
+
readonly files: readonly FileDiff[];
|
|
12
|
+
readonly hasDrift: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Compare each file in the plan against the project filesystem. Returns a structured diff suitable for either
|
|
16
|
+
* human rendering (`renderPlanDiff`) or machine consumption (`--json`).
|
|
17
|
+
*/
|
|
18
|
+
export declare const diffPlan: (params: {
|
|
19
|
+
readonly plan: ProjectPlan;
|
|
20
|
+
}) => Effect.Effect<PlanDiff, Errors.RegionMissingEnd | Errors.RegionDuplicate, BlueprintFileSystem>;
|
|
21
|
+
export declare const renderPlanDiff: (diff: PlanDiff) => string;
|
|
22
|
+
/**
|
|
23
|
+
* Render the full inspect output: file-diff lines + lockfile-driven removals + version upgrades.
|
|
24
|
+
* Used by `pjt inspect` and `pjt remodel --dry-run` (future) to give a complete picture.
|
|
25
|
+
*/
|
|
26
|
+
export declare const renderInspectReport: (params: {
|
|
27
|
+
readonly diff: PlanDiff;
|
|
28
|
+
readonly removals: readonly PjtLock.LockOperation[];
|
|
29
|
+
readonly upgrades: readonly UpgradeRecord[];
|
|
30
|
+
}) => string;
|
|
31
|
+
//# sourceMappingURL=differ.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"differ.d.ts","sourceRoot":"","sources":["../src/differ.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAS,MAAM,QAAQ,CAAA;AAC7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAI3E,OAAO,KAAK,EAEV,WAAW,EAKX,aAAa,EACd,MAAM,WAAW,CAAA;AAElB,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,qGAAqG;IACrG,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAAA;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAA;IACnC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAC3B;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,GAAI,QAAQ;IAC/B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAC3B,KAAG,MAAM,CAAC,MAAM,CACf,QAAQ,EACR,MAAM,CAAC,gBAAgB,GAAG,MAAM,CAAC,eAAe,EAChD,mBAAmB,CAUpB,CAAA;AAuGD,eAAO,MAAM,cAAc,GAAI,MAAM,QAAQ,KAAG,MAM/C,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,QAAQ;IAC1C,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,CAAC,aAAa,EAAE,CAAA;IACnD,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAA;CAC5C,KAAG,MAWH,CAAA"}
|
package/dist/differ.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { Array, Effect, Match } from "effect";
|
|
2
|
+
import { RecordX } from "@nunofyobiz/effect-extras";
|
|
3
|
+
import { readIfExists } from "./project-file.js";
|
|
4
|
+
import { findRegion } from "./region.js";
|
|
5
|
+
/**
|
|
6
|
+
* Compare each file in the plan against the project filesystem. Returns a structured diff suitable for either
|
|
7
|
+
* human rendering (`renderPlanDiff`) or machine consumption (`--json`).
|
|
8
|
+
*/
|
|
9
|
+
export const diffPlan = (params) => {
|
|
10
|
+
const { plan } = params;
|
|
11
|
+
return Effect.gen(function* () {
|
|
12
|
+
const files = yield* Effect.forEach(plan.files, (file) => readIfExists(file.path).pipe(Effect.flatMap((current) => diffFile({ file, current }))));
|
|
13
|
+
const hasDrift = Array.some(files, (file) => file.status !== "ok");
|
|
14
|
+
return { files, hasDrift };
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
const diffFile = (params) => Match.valueTags(params.file, {
|
|
18
|
+
Region: (file) => diffRegion(file, params.current),
|
|
19
|
+
Merge: (file) => Effect.succeed(diffMerge(file, params.current)),
|
|
20
|
+
Owned: (file) => Effect.succeed(diffOwned(file, params.current)),
|
|
21
|
+
Seed: (file) => Effect.succeed(diffSeed(file, params.current)),
|
|
22
|
+
});
|
|
23
|
+
const diffRegion = (file, current) => Effect.gen(function* () {
|
|
24
|
+
if (current === null) {
|
|
25
|
+
return {
|
|
26
|
+
path: file.path,
|
|
27
|
+
status: "create",
|
|
28
|
+
summary: `+ create ${file.path} (${file.regions.length} region${file.regions.length === 1 ? "" : "s"})`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const drifted = [];
|
|
32
|
+
for (const region of file.regions) {
|
|
33
|
+
const found = yield* findRegion({
|
|
34
|
+
fileContent: current,
|
|
35
|
+
ownerId: region.ownerId,
|
|
36
|
+
commentPrefix: file.commentPrefix,
|
|
37
|
+
commentSuffix: file.commentSuffix,
|
|
38
|
+
path: file.path,
|
|
39
|
+
});
|
|
40
|
+
const expected = region.content.trimEnd();
|
|
41
|
+
if (found._tag === "Absent") {
|
|
42
|
+
drifted.push(`missing region ${region.ownerId}`);
|
|
43
|
+
}
|
|
44
|
+
else if (found.content.trimEnd() !== expected) {
|
|
45
|
+
drifted.push(`region ${region.ownerId} content drift`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (drifted.length === 0) {
|
|
49
|
+
return { path: file.path, status: "ok", summary: ` ok ${file.path}` };
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
path: file.path,
|
|
53
|
+
status: "modify",
|
|
54
|
+
summary: `~ modify ${file.path} — ${drifted.join(", ")}`,
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
const diffMerge = (file, current) => {
|
|
58
|
+
if (current === null) {
|
|
59
|
+
return {
|
|
60
|
+
path: file.path,
|
|
61
|
+
status: "create",
|
|
62
|
+
summary: `+ create ${file.path}`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const parsed = JSON.parse(current);
|
|
67
|
+
const target = RecordX.deepMerge(parsed, file.value);
|
|
68
|
+
if (canonicalJson(parsed) === canonicalJson(target)) {
|
|
69
|
+
return { path: file.path, status: "ok", summary: ` ok ${file.path}` };
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
path: file.path,
|
|
73
|
+
status: "modify",
|
|
74
|
+
summary: `~ modify ${file.path} (JSON merge)`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return {
|
|
79
|
+
path: file.path,
|
|
80
|
+
status: "modify",
|
|
81
|
+
summary: `~ modify ${file.path} (unparseable JSON)`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const diffOwned = (file, current) => {
|
|
86
|
+
if (current === null) {
|
|
87
|
+
return { path: file.path, status: "create", summary: `+ create ${file.path}` };
|
|
88
|
+
}
|
|
89
|
+
if (current === file.content) {
|
|
90
|
+
return { path: file.path, status: "ok", summary: ` ok ${file.path}` };
|
|
91
|
+
}
|
|
92
|
+
return { path: file.path, status: "modify", summary: `~ modify ${file.path}` };
|
|
93
|
+
};
|
|
94
|
+
const diffSeed = (file, current) => {
|
|
95
|
+
if (current === null) {
|
|
96
|
+
return { path: file.path, status: "create", summary: `+ create ${file.path} (seed)` };
|
|
97
|
+
}
|
|
98
|
+
return { path: file.path, status: "ok", summary: ` ok ${file.path} (seed, never enforced)` };
|
|
99
|
+
};
|
|
100
|
+
// Stable JSON string for structural comparison: deep key-sort, then stringify.
|
|
101
|
+
const canonicalJson = (v) => JSON.stringify(RecordX.canonicalize(v));
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
// Render a diff for the terminal
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
export const renderPlanDiff = (diff) => {
|
|
106
|
+
if (!diff.hasDrift) {
|
|
107
|
+
return "Project is in sync with blueprints. No changes needed.";
|
|
108
|
+
}
|
|
109
|
+
const lines = diff.files.map((f) => f.summary);
|
|
110
|
+
return lines.join("\n");
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Render the full inspect output: file-diff lines + lockfile-driven removals + version upgrades.
|
|
114
|
+
* Used by `pjt inspect` and `pjt remodel --dry-run` (future) to give a complete picture.
|
|
115
|
+
*/
|
|
116
|
+
export const renderInspectReport = (params) => {
|
|
117
|
+
const { diff, removals, upgrades } = params;
|
|
118
|
+
const lines = Array.flatten([
|
|
119
|
+
Array.map(upgrades, (u) => `↑ upgrade ${u.blueprintId} ${u.from} → ${u.to}`),
|
|
120
|
+
Array.map(removals, removalSummary),
|
|
121
|
+
Array.map(diff.files, (f) => f.summary),
|
|
122
|
+
]);
|
|
123
|
+
if (lines.length === 0) {
|
|
124
|
+
return "Project is in sync with blueprints. No changes needed.";
|
|
125
|
+
}
|
|
126
|
+
return lines.join("\n");
|
|
127
|
+
};
|
|
128
|
+
const removalSummary = (op) => Match.valueTags(op, {
|
|
129
|
+
Region: (region) => `- remove ${region.ownerId} region from ${region.path} (blueprint left .pjt.ts)`,
|
|
130
|
+
Merge: (merge) => `- remove merge keys ${merge.ownedKeys.join(", ")} from ${merge.path} (blueprint left .pjt.ts)`,
|
|
131
|
+
Owned: (owned) => `- delete ${owned.path} (blueprint left .pjt.ts)`,
|
|
132
|
+
Seed: (seed) => ` (seed ${seed.ownerId} for ${seed.path} retained; blueprint left but seed mode is write-once)`,
|
|
133
|
+
});
|
|
134
|
+
//# sourceMappingURL=differ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"differ.js","sourceRoot":"","sources":["../src/differ.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Errors } from "@projitect/core";
|
|
3
|
+
export interface SpliceParams {
|
|
4
|
+
readonly projectRoot: string;
|
|
5
|
+
readonly blueprintFile: string;
|
|
6
|
+
/** Import statement to splice (one line, no trailing newline). Deduped. */
|
|
7
|
+
readonly importLine: string;
|
|
8
|
+
/** Blueprint call lines (one entry per line, no leading indent, no trailing newline). */
|
|
9
|
+
readonly callLines: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Read `.pjt.ts`, splice an import and call lines in via the shared `spliceBlueprintFile`, and
|
|
13
|
+
* write it back. The marker convention and splice behavior live in `@projitect/blueprint`.
|
|
14
|
+
*
|
|
15
|
+
* Fails with `pjt.add.markers-missing` if either marker pair is missing — the user has
|
|
16
|
+
* hand-edited the file and we won't guess. They restore the markers, then re-run `pjt add`.
|
|
17
|
+
*/
|
|
18
|
+
export declare const splice: (params: SpliceParams) => Effect.Effect<void, Errors.AddMarkersMissing | Errors.FsReadFailed | Errors.FsWriteFailed>;
|
|
19
|
+
//# sourceMappingURL=edit-pjt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-pjt.d.ts","sourceRoot":"","sources":["../src/edit-pjt.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAGxC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,yFAAyF;IACzF,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;CACtC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,GACjB,QAAQ,YAAY,KACnB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,iBAAiB,GAAG,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,aAAa,CA+BxF,CAAA"}
|
package/dist/edit-pjt.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
import { Errors } from "@projitect/core";
|
|
5
|
+
import { spliceBlueprintFile } from "@projitect/blueprint";
|
|
6
|
+
/**
|
|
7
|
+
* Read `.pjt.ts`, splice an import and call lines in via the shared `spliceBlueprintFile`, and
|
|
8
|
+
* write it back. The marker convention and splice behavior live in `@projitect/blueprint`.
|
|
9
|
+
*
|
|
10
|
+
* Fails with `pjt.add.markers-missing` if either marker pair is missing — the user has
|
|
11
|
+
* hand-edited the file and we won't guess. They restore the markers, then re-run `pjt add`.
|
|
12
|
+
*/
|
|
13
|
+
export const splice = (params) => Effect.gen(function* () {
|
|
14
|
+
const full = path.join(params.projectRoot, params.blueprintFile);
|
|
15
|
+
const raw = yield* Effect.tryPromise({
|
|
16
|
+
try: () => fs.readFile(full, "utf8"),
|
|
17
|
+
catch: (e) => new Errors.FsReadFailed({
|
|
18
|
+
id: "pjt.fs.read-failed",
|
|
19
|
+
path: params.blueprintFile,
|
|
20
|
+
cause: e instanceof Error ? e.message : String(e),
|
|
21
|
+
message: `Could not read ${params.blueprintFile}`,
|
|
22
|
+
}),
|
|
23
|
+
});
|
|
24
|
+
const next = yield* spliceBlueprintFile({
|
|
25
|
+
content: raw,
|
|
26
|
+
blueprintFile: params.blueprintFile,
|
|
27
|
+
importLine: params.importLine,
|
|
28
|
+
callLines: params.callLines,
|
|
29
|
+
});
|
|
30
|
+
yield* Effect.tryPromise({
|
|
31
|
+
try: () => fs.writeFile(full, next, "utf8"),
|
|
32
|
+
catch: (e) => new Errors.FsWriteFailed({
|
|
33
|
+
id: "pjt.fs.write-failed",
|
|
34
|
+
path: params.blueprintFile,
|
|
35
|
+
cause: e instanceof Error ? e.message : String(e),
|
|
36
|
+
message: `Could not write ${params.blueprintFile}`,
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=edit-pjt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-pjt.js","sourceRoot":"","sources":["../src/edit-pjt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAW1D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CACpB,MAAoB,EACwE,EAAE,CAC9F,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,aAAa,CAAC,CAAA;IAChE,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;QACnC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACpC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CACX,IAAI,MAAM,CAAC,YAAY,CAAC;YACtB,EAAE,EAAE,oBAAoB;YACxB,IAAI,EAAE,MAAM,CAAC,aAAa;YAC1B,KAAK,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;YACjD,OAAO,EAAE,kBAAkB,MAAM,CAAC,aAAa,EAAE;SAClD,CAAC;KACL,CAAC,CAAA;IAEF,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,mBAAmB,CAAC;QACtC,OAAO,EAAE,GAAG;QACZ,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAC,CAAA;IAEF,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;QACvB,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC;QAC3C,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CACX,IAAI,MAAM,CAAC,aAAa,CAAC;YACvB,EAAE,EAAE,qBAAqB;YACzB,IAAI,EAAE,MAAM,CAAC,aAAa;YAC1B,KAAK,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;YACjD,OAAO,EAAE,mBAAmB,MAAM,CAAC,aAAa,EAAE;SACnD,CAAC;KACL,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Layer } from "effect";
|
|
2
|
+
import { BlueprintFileSystem } from "@projitect/core";
|
|
3
|
+
import type { Permission } from "@projitect/core";
|
|
4
|
+
/**
|
|
5
|
+
* Build the permission-gated `BlueprintFileSystem` Layer for one blueprint. The CLI provides a
|
|
6
|
+
* fresh layer per blueprint, baking in that blueprint's `id` and declared `permissions`. All FS
|
|
7
|
+
* paths get resolved relative to `projectRoot`; absolute paths or paths escaping `projectRoot`
|
|
8
|
+
* are rejected with `pjt.fs.permission-denied`.
|
|
9
|
+
*
|
|
10
|
+
* This is the **soft sandbox**. A worker-process replacement is a tracked v2 follow-up; this
|
|
11
|
+
* implementation enforces the same interface so the swap will be transparent to blueprint
|
|
12
|
+
* authors.
|
|
13
|
+
*/
|
|
14
|
+
export declare const makeRealLayer: (config: {
|
|
15
|
+
readonly blueprintId: string;
|
|
16
|
+
readonly permissions: readonly Permission.Permission[];
|
|
17
|
+
readonly projectRoot: string;
|
|
18
|
+
}) => Layer.Layer<BlueprintFileSystem>;
|
|
19
|
+
/**
|
|
20
|
+
* The ungated project-root `BlueprintFileSystem` the engine itself uses to read and write the
|
|
21
|
+
* lockfile, diff the plan and apply changes. Same disk implementation as `makeRealLayer` with no
|
|
22
|
+
* permission check; paths that are absolute or escape `projectRoot` are still rejected.
|
|
23
|
+
*/
|
|
24
|
+
export declare const makeProjectRootLayer: (projectRoot: string) => Layer.Layer<BlueprintFileSystem>;
|
|
25
|
+
//# sourceMappingURL=filesystem-impl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem-impl.d.ts","sourceRoot":"","sources":["../src/filesystem-impl.ts"],"names":[],"mappings":"AAEA,OAAO,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,EAAE,mBAAmB,EAAU,MAAM,iBAAiB,CAAA;AAC7D,OAAO,KAAK,EAAE,UAAU,EAA4B,MAAM,iBAAiB,CAAA;AAI3E;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,GAAI,QAAQ;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,SAAS,UAAU,CAAC,UAAU,EAAE,CAAA;IACtD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B,KAAG,KAAK,CAAC,KAAK,CAAC,mBAAmB,CAK/B,CAAA;AAEJ;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,aAAa,MAAM,KAAG,KAAK,CAAC,KAAK,CAAC,mBAAmB,CACR,CAAA"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { Effect, Layer } from "effect";
|
|
4
|
+
import { BlueprintFileSystem, Errors } from "@projitect/core";
|
|
5
|
+
import { isPermitted } from "./permissions.js";
|
|
6
|
+
/**
|
|
7
|
+
* Build the permission-gated `BlueprintFileSystem` Layer for one blueprint. The CLI provides a
|
|
8
|
+
* fresh layer per blueprint, baking in that blueprint's `id` and declared `permissions`. All FS
|
|
9
|
+
* paths get resolved relative to `projectRoot`; absolute paths or paths escaping `projectRoot`
|
|
10
|
+
* are rejected with `pjt.fs.permission-denied`.
|
|
11
|
+
*
|
|
12
|
+
* This is the **soft sandbox**. A worker-process replacement is a tracked v2 follow-up; this
|
|
13
|
+
* implementation enforces the same interface so the swap will be transparent to blueprint
|
|
14
|
+
* authors.
|
|
15
|
+
*/
|
|
16
|
+
export const makeRealLayer = (config) => makeDiskLayer({
|
|
17
|
+
blueprintId: config.blueprintId,
|
|
18
|
+
projectRoot: config.projectRoot,
|
|
19
|
+
isAllowed: (op, p) => isPermitted(config.permissions, op, p),
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* The ungated project-root `BlueprintFileSystem` the engine itself uses to read and write the
|
|
23
|
+
* lockfile, diff the plan and apply changes. Same disk implementation as `makeRealLayer` with no
|
|
24
|
+
* permission check; paths that are absolute or escape `projectRoot` are still rejected.
|
|
25
|
+
*/
|
|
26
|
+
export const makeProjectRootLayer = (projectRoot) => makeDiskLayer({ blueprintId: "pjt:engine", projectRoot, isAllowed: () => true });
|
|
27
|
+
const makeDiskLayer = (config) => {
|
|
28
|
+
const { blueprintId, projectRoot, isAllowed } = config;
|
|
29
|
+
const resolved = path.resolve(projectRoot);
|
|
30
|
+
const denied = (p, op, message) => new Errors.FsPermissionDenied({
|
|
31
|
+
id: "pjt.fs.permission-denied",
|
|
32
|
+
path: p,
|
|
33
|
+
operation: op,
|
|
34
|
+
blueprintId,
|
|
35
|
+
message,
|
|
36
|
+
});
|
|
37
|
+
// Real path of `full`, or — when it does not exist yet (a write or mkdir target) — of its nearest
|
|
38
|
+
// existing ancestor with the missing remainder re-appended. A dangling symlink is followed to its
|
|
39
|
+
// (missing) target rather than treated as a missing name, so a link to a not-yet-created file
|
|
40
|
+
// outside the project cannot slip through; `hops` bounds circular links.
|
|
41
|
+
const maxLinkHops = 40;
|
|
42
|
+
const realPathOf = async (full, hops = 0) => {
|
|
43
|
+
try {
|
|
44
|
+
return await fs.realpath(full);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
const parent = path.dirname(full);
|
|
48
|
+
const stat = await fs.lstat(full).catch(() => undefined);
|
|
49
|
+
if (stat?.isSymbolicLink() === true && hops < maxLinkHops) {
|
|
50
|
+
const target = await fs.readlink(full);
|
|
51
|
+
return realPathOf(path.resolve(await realPathOf(parent, hops), target), hops + 1);
|
|
52
|
+
}
|
|
53
|
+
if (parent === full) {
|
|
54
|
+
return full;
|
|
55
|
+
}
|
|
56
|
+
return path.join(await realPathOf(parent, hops), path.basename(full));
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
// Accident-prevention, not hostile-code containment: the real path is resolved at check time, so
|
|
60
|
+
// a link swapped between the check and the FS call is not caught.
|
|
61
|
+
const resolvePath = (p, op) => Effect.gen(function* () {
|
|
62
|
+
if (path.isAbsolute(p)) {
|
|
63
|
+
return yield* denied(p, op, `Absolute paths are not allowed in blueprints: ${p}`);
|
|
64
|
+
}
|
|
65
|
+
const full = path.resolve(resolved, p);
|
|
66
|
+
if (!full.startsWith(`${resolved}${path.sep}`) && full !== resolved) {
|
|
67
|
+
return yield* denied(p, op, `Path escapes project root: ${p}`);
|
|
68
|
+
}
|
|
69
|
+
if (!isAllowed(op, p)) {
|
|
70
|
+
return yield* denied(p, op, `Blueprint ${blueprintId} did not declare ${op} permission for ${p}`);
|
|
71
|
+
}
|
|
72
|
+
const [realRoot, realFull] = yield* Effect.promise(() => Promise.all([realPathOf(resolved), realPathOf(full)]));
|
|
73
|
+
const realRelative = path.relative(realRoot, realFull).split(path.sep).join("/");
|
|
74
|
+
if (realRelative === ".." ||
|
|
75
|
+
realRelative.startsWith("../") ||
|
|
76
|
+
path.isAbsolute(realRelative)) {
|
|
77
|
+
return yield* denied(p, op, `Path resolves outside project root via symlink: ${p}`);
|
|
78
|
+
}
|
|
79
|
+
if (!isAllowed(op, realRelative)) {
|
|
80
|
+
return yield* denied(p, op, `Blueprint ${blueprintId} did not declare ${op} permission for ${p} (resolves to ${realRelative})`);
|
|
81
|
+
}
|
|
82
|
+
return full;
|
|
83
|
+
});
|
|
84
|
+
const shape = {
|
|
85
|
+
readFile: (p) => resolvePath(p, "read").pipe(Effect.flatMap((full) => Effect.tryPromise({
|
|
86
|
+
try: () => fs.readFile(full, "utf8"),
|
|
87
|
+
catch: (e) => new Errors.FsReadFailed({
|
|
88
|
+
id: "pjt.fs.read-failed",
|
|
89
|
+
path: p,
|
|
90
|
+
cause: e instanceof Error ? e.message : String(e),
|
|
91
|
+
message: `Failed to read ${p}`,
|
|
92
|
+
}),
|
|
93
|
+
}))),
|
|
94
|
+
writeFile: (p, content) => resolvePath(p, "write").pipe(Effect.flatMap((full) => Effect.tryPromise({
|
|
95
|
+
try: async () => {
|
|
96
|
+
await fs.mkdir(path.dirname(full), { recursive: true });
|
|
97
|
+
await fs.writeFile(full, content, "utf8");
|
|
98
|
+
},
|
|
99
|
+
catch: (e) => new Errors.FsWriteFailed({
|
|
100
|
+
id: "pjt.fs.write-failed",
|
|
101
|
+
path: p,
|
|
102
|
+
cause: e instanceof Error ? e.message : String(e),
|
|
103
|
+
message: `Failed to write ${p}`,
|
|
104
|
+
}),
|
|
105
|
+
}))),
|
|
106
|
+
exists: (p) => resolvePath(p, "exists").pipe(Effect.flatMap((full) => Effect.tryPromise({
|
|
107
|
+
try: () => fs.access(full).then(() => true, () => false),
|
|
108
|
+
catch: () => false,
|
|
109
|
+
}).pipe(Effect.orElseSucceed(() => false)))),
|
|
110
|
+
// Non-recursive on purpose: removes a file or symlink, and fails on a directory so user content
|
|
111
|
+
// that now sits at the path survives.
|
|
112
|
+
remove: (p) => resolvePath(p, "remove").pipe(Effect.flatMap((full) => Effect.tryPromise({
|
|
113
|
+
try: () => fs.rm(full, { force: true }),
|
|
114
|
+
catch: (e) => new Errors.FsWriteFailed({
|
|
115
|
+
id: "pjt.fs.write-failed",
|
|
116
|
+
path: p,
|
|
117
|
+
cause: e instanceof Error ? e.message : String(e),
|
|
118
|
+
message: `Failed to remove ${p}`,
|
|
119
|
+
}),
|
|
120
|
+
}))),
|
|
121
|
+
// Non-recursive on purpose: `rmdir` is atomic about emptiness, so an entry created after the
|
|
122
|
+
// caller last looked is never deleted.
|
|
123
|
+
removeEmptyDir: (p) => resolvePath(p, "removeEmptyDir").pipe(Effect.flatMap((full) => Effect.tryPromise({
|
|
124
|
+
try: () => fs.rmdir(full),
|
|
125
|
+
catch: (e) => new Errors.FsWriteFailed({
|
|
126
|
+
id: "pjt.fs.write-failed",
|
|
127
|
+
path: p,
|
|
128
|
+
cause: e instanceof Error ? e.message : String(e),
|
|
129
|
+
message: `Failed to remove empty directory ${p}`,
|
|
130
|
+
}),
|
|
131
|
+
}))),
|
|
132
|
+
mkdir: (p) => resolvePath(p, "mkdir").pipe(Effect.flatMap((full) => Effect.tryPromise({
|
|
133
|
+
try: () => fs.mkdir(full, { recursive: true }).then(() => undefined),
|
|
134
|
+
catch: (e) => new Errors.FsWriteFailed({
|
|
135
|
+
id: "pjt.fs.write-failed",
|
|
136
|
+
path: p,
|
|
137
|
+
cause: e instanceof Error ? e.message : String(e),
|
|
138
|
+
message: `Failed to mkdir ${p}`,
|
|
139
|
+
}),
|
|
140
|
+
}))),
|
|
141
|
+
listDir: (p) => resolvePath(p, "listDir").pipe(Effect.flatMap((full) => Effect.tryPromise({
|
|
142
|
+
try: () => fs.readdir(full),
|
|
143
|
+
catch: (e) => new Errors.FsReadFailed({
|
|
144
|
+
id: "pjt.fs.read-failed",
|
|
145
|
+
path: p,
|
|
146
|
+
cause: e instanceof Error ? e.message : String(e),
|
|
147
|
+
message: `Failed to list ${p}`,
|
|
148
|
+
}),
|
|
149
|
+
}))),
|
|
150
|
+
};
|
|
151
|
+
return Layer.succeed(BlueprintFileSystem, shape);
|
|
152
|
+
};
|
|
153
|
+
//# sourceMappingURL=filesystem-impl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem-impl.js","sourceRoot":"","sources":["../src/filesystem-impl.ts"],"names":[],"mappings":"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"}
|
package/dist/git.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Errors } from "@projitect/core";
|
|
3
|
+
/**
|
|
4
|
+
* Returns true if `<projectRoot>/.git` exists (file or directory). Used by `pjt init` and
|
|
5
|
+
* `pjt build --force` as a hard prerequisite.
|
|
6
|
+
*/
|
|
7
|
+
export declare const isGitRepo: (projectRoot: string) => Effect.Effect<boolean>;
|
|
8
|
+
/**
|
|
9
|
+
* Result of `git status --porcelain`. `clean === true` means there are no uncommitted changes
|
|
10
|
+
* (including untracked files); `lines` is the parsed porcelain output for diagnostics.
|
|
11
|
+
*/
|
|
12
|
+
export interface GitStatus {
|
|
13
|
+
readonly clean: boolean;
|
|
14
|
+
readonly lines: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Shell out to `git status --porcelain` and parse. Wraps process spawn failures (non-zero exit,
|
|
18
|
+
* git not installed) as `pjt.git.command-failed`. Callers ensure `.git/` exists first.
|
|
19
|
+
*/
|
|
20
|
+
export declare const gitStatus: (params: {
|
|
21
|
+
readonly projectRoot: string;
|
|
22
|
+
}) => Effect.Effect<GitStatus, Errors.GitCommandFailed>;
|
|
23
|
+
/**
|
|
24
|
+
* Raise `pjt.git.not-a-repo` if `<projectRoot>/.git` is missing.
|
|
25
|
+
*/
|
|
26
|
+
export declare const ensureGitRepo: (params: {
|
|
27
|
+
readonly projectRoot: string;
|
|
28
|
+
}) => Effect.Effect<void, Errors.GitNotARepo>;
|
|
29
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAExC;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,aAAa,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAMlE,CAAA;AAEH;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAClC;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,QAAQ;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B,KAAG,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,gBAAgB,CAmCnD,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,QAAQ;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAWtC,CAAA"}
|
package/dist/git.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { Effect } from "effect";
|
|
5
|
+
import { Errors } from "@projitect/core";
|
|
6
|
+
/**
|
|
7
|
+
* Returns true if `<projectRoot>/.git` exists (file or directory). Used by `pjt init` and
|
|
8
|
+
* `pjt build --force` as a hard prerequisite.
|
|
9
|
+
*/
|
|
10
|
+
export const isGitRepo = (projectRoot) => Effect.promise(() => fs.access(path.join(projectRoot, ".git")).then(() => true, () => false));
|
|
11
|
+
/**
|
|
12
|
+
* Shell out to `git status --porcelain` and parse. Wraps process spawn failures (non-zero exit,
|
|
13
|
+
* git not installed) as `pjt.git.command-failed`. Callers ensure `.git/` exists first.
|
|
14
|
+
*/
|
|
15
|
+
export const gitStatus = (params) => {
|
|
16
|
+
const { projectRoot } = params;
|
|
17
|
+
return Effect.tryPromise({
|
|
18
|
+
try: () => new Promise((resolve, reject) => {
|
|
19
|
+
const child = spawn("git", ["status", "--porcelain"], { cwd: projectRoot });
|
|
20
|
+
let stdout = "";
|
|
21
|
+
let stderr = "";
|
|
22
|
+
child.stdout.on("data", (chunk) => {
|
|
23
|
+
stdout = stdout + chunk.toString("utf8");
|
|
24
|
+
});
|
|
25
|
+
child.stderr.on("data", (chunk) => {
|
|
26
|
+
stderr = stderr + chunk.toString("utf8");
|
|
27
|
+
});
|
|
28
|
+
child.on("error", reject);
|
|
29
|
+
child.on("close", (code) => {
|
|
30
|
+
if (code !== 0) {
|
|
31
|
+
reject(new Error(`git status exited with ${String(code)}: ${stderr.trim()}`));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const lines = stdout
|
|
35
|
+
.split("\n")
|
|
36
|
+
.map((l) => l.trim())
|
|
37
|
+
.filter((l) => l.length > 0);
|
|
38
|
+
resolve({ clean: lines.length === 0, lines });
|
|
39
|
+
});
|
|
40
|
+
}),
|
|
41
|
+
catch: (e) => new Errors.GitCommandFailed({
|
|
42
|
+
id: "pjt.git.command-failed",
|
|
43
|
+
command: "git status --porcelain",
|
|
44
|
+
cause: e instanceof Error ? e.message : String(e),
|
|
45
|
+
message: `Failed to run \`git status --porcelain\` in ${projectRoot}`,
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Raise `pjt.git.not-a-repo` if `<projectRoot>/.git` is missing.
|
|
51
|
+
*/
|
|
52
|
+
export const ensureGitRepo = (params) => Effect.gen(function* () {
|
|
53
|
+
const ok = yield* isGitRepo(params.projectRoot);
|
|
54
|
+
if (ok) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
return yield* new Errors.GitNotARepo({
|
|
58
|
+
id: "pjt.git.not-a-repo",
|
|
59
|
+
projectRoot: params.projectRoot,
|
|
60
|
+
message: `No \`.git\` directory at ${params.projectRoot}. \`pjt build --force\` requires a git repository so destructive operations are recoverable. Run \`git init\` (or \`pjt init --yes\`), commit, then re-run.`,
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
//# sourceMappingURL=git.js.map
|
package/dist/git.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { rootCommand, type RootCommand } from "./commands/dispatch.js";
|
|
2
|
+
export { inspect, type InspectResult } from "./commands/inspect.js";
|
|
3
|
+
export { remodel, type RemodelResult } from "./commands/remodel.js";
|
|
4
|
+
export { init, type InitResult } from "./commands/init.js";
|
|
5
|
+
export { explain } from "./commands/explain.js";
|
|
6
|
+
export { build } from "./commands/build.js";
|
|
7
|
+
export { add } from "./commands/add.js";
|
|
8
|
+
export { resolveConfig, parseEnv } from "./config-cascade.js";
|
|
9
|
+
export { loadBlueprintFile, pjt, isEffectTree, type ProjitectFile, type BlueprintTree, } from "./loader.js";
|
|
10
|
+
export { buildPlan, diffLockfile, type ProjectPlan, type FilePlan, type ByBlueprint, type UpgradeRecord, } from "./plan.js";
|
|
11
|
+
export { diffPlan, renderPlanDiff, renderInspectReport, type PlanDiff, type FileDiff, } from "./differ.js";
|
|
12
|
+
export { applyPlan } from "./applier.js";
|
|
13
|
+
export { applyRemovals } from "./remover.js";
|
|
14
|
+
export { readLockfile, writeLockfile, blueprintIds } from "./lockfile.js";
|
|
15
|
+
export { makeRealLayer, makeProjectRootLayer } from "./filesystem-impl.js";
|
|
16
|
+
export { NodePlatformLive, FileSystemLive, StdioLive, TerminalLive, runCommand, nodeSpawner, type ProcessSpawner, type CommandOutput, } from "./platform/index.js";
|
|
17
|
+
export { splice } from "./edit-pjt.js";
|
|
18
|
+
export { detect as detectPackageManager, installDev, readProjitectMetadata, type PackageManager, } from "./pm.js";
|
|
19
|
+
export type { ProjitectPackageMetadata } from "@projitect/blueprint";
|
|
20
|
+
export { isGitRepo, gitStatus, ensureGitRepo } from "./git.js";
|
|
21
|
+
export * as Ipc from "./ipc/index.js";
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACtE,OAAO,EAAE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACnE,OAAO,EAAE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACnE,OAAO,EAAE,IAAI,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAC3C,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,EACL,iBAAiB,EACjB,GAAG,EACH,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,SAAS,EACT,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,aAAa,GACnB,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,QAAQ,EACR,cAAc,EACd,mBAAmB,EACnB,KAAK,QAAQ,EACb,KAAK,QAAQ,GACd,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACzE,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC1E,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,SAAS,EACT,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EACL,MAAM,IAAI,oBAAoB,EAC9B,UAAU,EACV,qBAAqB,EACrB,KAAK,cAAc,GACpB,MAAM,SAAS,CAAA;AAChB,YAAY,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAA;AACpE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC9D,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAA"}
|