@pmelab/gtd 62.1.0 → 64.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/.claude-plugin/plugin.json +1 -1
- package/README.md +8 -3
- package/dist/chunks/{flows-DENt0uWV.mjs → flows-B7ZRCe-B.mjs} +1 -1
- package/dist/chunks/{rolldown-runtime-8H4AJuhK.mjs → rolldown-runtime-D7D4PA-g.mjs} +0 -1
- package/dist/chunks/{steering-DF_BMadH.mjs → steering-DkA51qDa.mjs} +1 -1
- package/dist/chunks/{testkit-CPzWnl3P.mjs → testkit-zGjNeQNL.mjs} +1144 -345
- package/dist/flows.mjs +1 -1
- package/dist/gtd.bundle.mjs +4023 -1326
- package/dist/gtd.core.mjs +120 -119
- package/dist/steering.mjs +1 -1
- package/dist/testkit/gherkin.mjs +2 -2
- package/dist/testkit.mjs +2 -2
- package/dist/workflow-kit.mjs +2 -2
- package/package.json +1 -1
- package/src/cli/Cli.test.ts +5 -8
- package/src/cli/Cli.ts +10 -28
- package/src/cli/Output.ts +2 -1
- package/src/cli/Program.test.ts +55 -53
- package/src/cli/Program.ts +36 -45
- package/src/cli/ToolCommands.ts +3 -4
- package/src/edge/Edge.test.ts +38 -12
- package/src/edge/Edge.ts +98 -13
- package/src/edge/index.ts +1 -0
- package/src/emit/SteeringMode.test.ts +4 -2
- package/src/emit/SteeringMode.ts +10 -9
- package/src/gherkin/messages.test.ts +1 -1
- package/src/gherkin/module-config.feature +1 -1
- package/src/gherkin/testkit.feature +2 -2
- package/src/install/Version.ts +14 -13
- package/src/platform/Host.ts +75 -22
- package/src/platform/NodePinFile.ts +44 -0
- package/src/platform/NodeRuntime.ts +19 -0
- package/src/platform/NodeSync.ts +64 -0
- package/src/platform/PinFile.ts +0 -50
- package/src/platform/Sha.ts +121 -0
- package/src/platform/SyncIo.ts +35 -0
- package/src/platform/Workspace.test.ts +8 -3
- package/src/platform/Workspace.ts +82 -123
- package/src/platform/WorktreeState.test.ts +8 -4
- package/src/platform/WorktreeState.ts +21 -19
- package/src/platform/index.ts +18 -5
- package/src/replay/Format.test.ts +32 -10
- package/src/replay/Format.ts +5 -2
- package/src/replay/Replay.test.ts +110 -3
- package/src/replay/Replay.ts +120 -28
- package/src/replay/Trailers.test.ts +10 -10
- package/src/replay/Trailers.ts +32 -1
- package/src/replay/index.ts +2 -0
- package/src/step/planStart.test.ts +2 -2
- package/src/step/planStep.test.ts +4 -4
- package/src/testkit/TestRepo.test.ts +1 -1
- package/src/testkit/TestRepo.ts +4 -18
- package/src/twins/Host.test.ts +151 -0
- package/src/twins/Layers.test.ts +2 -2
- package/src/twins/Layers.ts +44 -18
- package/src/twins/Path.test.ts +52 -0
- package/src/twins/SyncIo.test.ts +153 -0
- package/src/twins/SyncIo.ts +159 -0
- package/src/twins/cliIo.ts +8 -1
- package/src/twins/index.ts +5 -0
- package/src/workflow/discovery.test.ts +6 -5
- package/src/workflow/discovery.ts +43 -32
- package/src/workflow/fingerprint.test.ts +23 -3
- package/src/workflow/fingerprint.ts +9 -11
- package/src/workflow/load.test.ts +16 -6
- package/src/workflow/load.ts +107 -69
- package/src/workflow/pin.test.ts +3 -2
- package/src/workflow/pin.ts +17 -11
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
import { C as moveScript, D as revertScript, E as restoreScript, H as installContext, K as markedOptions, O as showScript, S as checkScript, T as removeScript, Y as protocolMismatch, i as configure, k as writeScript$1, n as configProblems, o as isFactoryWorkflow, p as numeric, s as answered, t as flows_exports, x as applyScript } from "./flows-
|
|
1
|
+
import { C as moveScript, D as revertScript, E as restoreScript, H as installContext, K as markedOptions, O as showScript, S as checkScript, T as removeScript, Y as protocolMismatch, i as configure, k as writeScript$1, n as configProblems, o as isFactoryWorkflow, p as numeric, s as answered, t as flows_exports, x as applyScript } from "./flows-B7ZRCe-B.mjs";
|
|
2
2
|
import { M as renderFormat, O as ACCESS_REFUSAL, a as renderScriptJson, d as renderLandPlain, f as DEMAND_BRIEFING, g as statusOf, h as renderBeatJson, i as judgeJson, k as PIN_PATH, l as noopText, m as beatDocument, n as resolveSession, o as scriptFields, p as renderBeatPlain, r as selectPath, s as landFields, t as globMatches, u as renderLandJson, v as beatKindOf, y as demandOf } from "./wire-CPNSm_9N.mjs";
|
|
3
|
-
import { M as headingSectionBodies, N as formatFinding, a as sections, c as openThreads, i as sectionBodies, l as steeringFormatFor, o as threads, r as openQuestions, s as BUILT_IN_MODE_NAMES, t as parseCodeThreads, u as unansweredQuestions } from "./steering-
|
|
3
|
+
import { M as headingSectionBodies, N as formatFinding, a as sections, c as openThreads, i as sectionBodies, l as steeringFormatFor, o as threads, r as openQuestions, s as BUILT_IN_MODE_NAMES, t as parseCodeThreads, u as unansweredQuestions } from "./steering-DkA51qDa.mjs";
|
|
4
4
|
import { createRequire, isBuiltin } from "node:module";
|
|
5
5
|
import * as Crypto from "node:crypto";
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
|
-
import { PassThrough, finished } from "node:stream";
|
|
8
|
-
import * as NodeChildProcess from "node:child_process";
|
|
9
|
-
import { execFileSync } from "node:child_process";
|
|
10
|
-
import * as NFS from "node:fs";
|
|
11
|
-
import { existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
12
|
-
import * as Path from "node:path";
|
|
13
|
-
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
14
7
|
import * as OS from "node:os";
|
|
15
8
|
import { homedir, tmpdir } from "node:os";
|
|
16
|
-
import { gzipSync } from "node:zlib";
|
|
17
9
|
import * as NodeUrl from "node:url";
|
|
18
|
-
import { fileURLToPath
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
19
11
|
import { stripVTControlCharacters } from "node:util";
|
|
20
12
|
import { runInThisContext } from "node:vm";
|
|
13
|
+
import { gzipSync } from "node:zlib";
|
|
14
|
+
import * as NodeChildProcess from "node:child_process";
|
|
15
|
+
import { execFileSync } from "node:child_process";
|
|
16
|
+
import * as NFS from "node:fs";
|
|
17
|
+
import { lstatSync, mkdirSync, readFileSync, readlinkSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
18
|
+
import { PassThrough, finished } from "node:stream";
|
|
19
|
+
import * as Path from "node:path";
|
|
21
20
|
import "vitest";
|
|
22
21
|
//#region node_modules/effect/dist/Pipeable.js
|
|
23
22
|
/**
|
|
@@ -3399,7 +3398,7 @@ const map$6 = /*#__PURE__*/ dual(2, (self, f) => isNone(self) ? none() : some(f(
|
|
|
3399
3398
|
* @category sequencing
|
|
3400
3399
|
* @since 2.0.0
|
|
3401
3400
|
*/
|
|
3402
|
-
const flatMap$
|
|
3401
|
+
const flatMap$4 = /*#__PURE__*/ dual(2, (self, f) => isNone(self) ? none() : f(self.value));
|
|
3403
3402
|
//#endregion
|
|
3404
3403
|
//#region node_modules/effect/dist/Context.js
|
|
3405
3404
|
/**
|
|
@@ -5636,7 +5635,7 @@ var FiberImpl = class {
|
|
|
5636
5635
|
return;
|
|
5637
5636
|
}
|
|
5638
5637
|
const interruptChildren = fiberMiddleware.interruptChildren && fiberMiddleware.interruptChildren(this);
|
|
5639
|
-
if (interruptChildren !== void 0) return this.evaluate(flatMap$
|
|
5638
|
+
if (interruptChildren !== void 0) return this.evaluate(flatMap$2(interruptChildren, () => exit));
|
|
5640
5639
|
this._exit = exit;
|
|
5641
5640
|
this.cache.runtimeMetrics?.recordFiberEnd(this.context, this._exit);
|
|
5642
5641
|
if (this._parent) {
|
|
@@ -5671,7 +5670,7 @@ var FiberImpl = class {
|
|
|
5671
5670
|
if (!yielding && !cache.preventYield && cache.scheduler.shouldYield(this)) {
|
|
5672
5671
|
yielding = true;
|
|
5673
5672
|
const prev = current;
|
|
5674
|
-
current = flatMap$
|
|
5673
|
+
current = flatMap$2(yieldNow, () => prev);
|
|
5675
5674
|
}
|
|
5676
5675
|
current = cache.tracerContext ? cache.tracerContext(current, this) : current[evaluate$1](this);
|
|
5677
5676
|
if (current === Yield) {
|
|
@@ -5954,7 +5953,7 @@ const asyncFinalizer = /*#__PURE__*/ makePrimitive({
|
|
|
5954
5953
|
}
|
|
5955
5954
|
},
|
|
5956
5955
|
[contE](cause, _fiber) {
|
|
5957
|
-
return hasInterrupts(cause) ? flatMap$
|
|
5956
|
+
return hasInterrupts(cause) ? flatMap$2(combineFinalizerCause(exitFailCause(cause), this[args]()), () => failCause$3(cause)) : failCause$3(cause);
|
|
5958
5957
|
}
|
|
5959
5958
|
});
|
|
5960
5959
|
/** @internal */
|
|
@@ -6015,7 +6014,7 @@ const fromIteratorEagerUnsafe = (evaluate) => {
|
|
|
6015
6014
|
return suspend$3(() => {
|
|
6016
6015
|
if (isFirstExecution) {
|
|
6017
6016
|
isFirstExecution = false;
|
|
6018
|
-
return flatMap$
|
|
6017
|
+
return flatMap$2(state.value, (value) => fromIteratorUnsafe(iterator, value));
|
|
6019
6018
|
} else return suspend$3(() => fromIteratorUnsafe(evaluate()));
|
|
6020
6019
|
});
|
|
6021
6020
|
}
|
|
@@ -6138,13 +6137,13 @@ const raceAllFirst = (all, options) => withFiber$1((parent) => {
|
|
|
6138
6137
|
/** @internal */
|
|
6139
6138
|
const raceFirst$1 = /*#__PURE__*/ dual((args) => isEffect(args[1]), (self, that, options) => raceAllFirst([self, that], options));
|
|
6140
6139
|
/** @internal */
|
|
6141
|
-
const flatMap$
|
|
6140
|
+
const flatMap$2 = /*#__PURE__*/ dual(2, (self, f) => new ContImpl(self, andThenCont, f));
|
|
6142
6141
|
/** @internal */
|
|
6143
6142
|
const effectIsExit = (effect) => effect[ExitTypeId] !== void 0;
|
|
6144
6143
|
/** @internal */
|
|
6145
6144
|
const flatMapEager$1 = /*#__PURE__*/ dual(2, (self, f) => {
|
|
6146
6145
|
if (effectIsExit(self)) return self._tag === "Success" ? f(self.value) : self;
|
|
6147
|
-
return flatMap$
|
|
6146
|
+
return flatMap$2(self, f);
|
|
6148
6147
|
});
|
|
6149
6148
|
/** @internal */
|
|
6150
6149
|
const map$2 = /*#__PURE__*/ dual(2, (self, f) => new ContImpl(self, mapCont, f));
|
|
@@ -6195,11 +6194,11 @@ const provideService$1 = function() {
|
|
|
6195
6194
|
};
|
|
6196
6195
|
const provideServiceImpl = (self, service, implementation) => updateContext(self, add$1(service, implementation));
|
|
6197
6196
|
/** @internal */
|
|
6198
|
-
const zipWith$1 = /*#__PURE__*/ dual((args) => isEffect(args[1]), (self, that, f, options) => options?.concurrent ? map$2(all$1([self, that], { concurrency: 2 }), ([a, a2]) => f(a, a2)) : flatMap$
|
|
6197
|
+
const zipWith$1 = /*#__PURE__*/ dual((args) => isEffect(args[1]), (self, that, f, options) => options?.concurrent ? map$2(all$1([self, that], { concurrency: 2 }), ([a, a2]) => f(a, a2)) : flatMap$2(self, (a) => map$2(that, (a2) => f(a, a2))));
|
|
6199
6198
|
/** @internal */
|
|
6200
6199
|
const forever$1 = /*#__PURE__*/ dual((args) => isEffect(args[0]), (self, options) => whileLoop$1({
|
|
6201
6200
|
while: constTrue,
|
|
6202
|
-
body: constant(options?.disableYield ? self : flatMap$
|
|
6201
|
+
body: constant(options?.disableYield ? self : flatMap$2(self, (_) => yieldNow)),
|
|
6203
6202
|
step: constVoid
|
|
6204
6203
|
}));
|
|
6205
6204
|
/** @internal */
|
|
@@ -6474,7 +6473,7 @@ const scopedWith$1 = (f) => suspend$3(() => {
|
|
|
6474
6473
|
return onExit$1(f(scope), (exit) => suspend$3(() => scopeCloseUnsafe(scope, exit) ?? void_$2));
|
|
6475
6474
|
});
|
|
6476
6475
|
/** @internal */
|
|
6477
|
-
const acquireRelease$1 = (acquire, release, options) => contextWith((context) => uninterruptibleMask((restore) => flatMap$
|
|
6476
|
+
const acquireRelease$1 = (acquire, release, options) => contextWith((context) => uninterruptibleMask((restore) => flatMap$2(scope, (scope) => tap$1(options?.interruptible ? restore(acquire) : acquire, (a) => scopeAddFinalizerExit(scope, (exit) => provideContext(release(a, exit), context))))));
|
|
6478
6477
|
const OnExitImpl = /*#__PURE__*/ function() {
|
|
6479
6478
|
const Proto = /*#__PURE__*/ makePrimitiveProto({
|
|
6480
6479
|
op: "OnExit",
|
|
@@ -6496,7 +6495,7 @@ const OnExitImpl = /*#__PURE__*/ function() {
|
|
|
6496
6495
|
} catch (defect) {
|
|
6497
6496
|
eff = exitDie(defect);
|
|
6498
6497
|
}
|
|
6499
|
-
return eff ? flatMap$
|
|
6498
|
+
return eff ? flatMap$2(eff, (_) => exit) : exit;
|
|
6500
6499
|
},
|
|
6501
6500
|
[contE](cause, _, exit) {
|
|
6502
6501
|
exit ??= exitFailCause(cause);
|
|
@@ -6506,7 +6505,7 @@ const OnExitImpl = /*#__PURE__*/ function() {
|
|
|
6506
6505
|
} catch (defect) {
|
|
6507
6506
|
eff = exitDie(defect);
|
|
6508
6507
|
}
|
|
6509
|
-
return eff ? flatMap$
|
|
6508
|
+
return eff ? flatMap$2(combineFinalizerCause(exit, eff), (_) => exit) : exit;
|
|
6510
6509
|
}
|
|
6511
6510
|
});
|
|
6512
6511
|
const OnExitImpl = function(effect, onExit, interruptible) {
|
|
@@ -6641,7 +6640,7 @@ const resolveConcurrency = (concurrency) => concurrency === "unbounded" ? Number
|
|
|
6641
6640
|
const iterateEager = () => (options) => {
|
|
6642
6641
|
const onItem = options.onItem;
|
|
6643
6642
|
const step = options.step;
|
|
6644
|
-
const resumeSequential = (state, items, index, end, effect) => flatMap$
|
|
6643
|
+
const resumeSequential = (state, items, index, end, effect) => flatMap$2(exit$1(effect), (itemExit) => step(state, items[index], itemExit, index) ?? runSequential(state, items, index + 1, end) ?? void_$2);
|
|
6645
6644
|
const runSequential = (state, items, index = 0, end = items.length) => {
|
|
6646
6645
|
for (; index < end; index++) {
|
|
6647
6646
|
const item = items[index];
|
|
@@ -6670,7 +6669,7 @@ const iterateConcurrentImpl = (options) => {
|
|
|
6670
6669
|
const defect = exitDie(error);
|
|
6671
6670
|
terminal = defect;
|
|
6672
6671
|
done = true;
|
|
6673
|
-
return fibers && fibers.size > 0 ? flatMap$
|
|
6672
|
+
return fibers && fibers.size > 0 ? flatMap$2(uninterruptible(fiberInterruptAll(Array.from(fibers))), () => terminal ?? defect) : defect;
|
|
6674
6673
|
};
|
|
6675
6674
|
const go = () => {
|
|
6676
6675
|
let paused = false;
|
|
@@ -6694,7 +6693,7 @@ const iterateConcurrentImpl = (options) => {
|
|
|
6694
6693
|
if (result) return cb(result);
|
|
6695
6694
|
return suspend$3(() => {
|
|
6696
6695
|
terminal ??= exitVoid;
|
|
6697
|
-
return flatMap$
|
|
6696
|
+
return flatMap$2(fibers ? fiberInterruptAll(fibers) : void_$2, () => terminal?._tag === "Failure" ? terminal : void_$2);
|
|
6698
6697
|
});
|
|
6699
6698
|
});
|
|
6700
6699
|
else {
|
|
@@ -6793,7 +6792,7 @@ const forkIn$1 = /*#__PURE__*/ dual((args) => isEffect(args[0]), (self, scope, o
|
|
|
6793
6792
|
return succeed$4(fiber);
|
|
6794
6793
|
}));
|
|
6795
6794
|
/** @internal */
|
|
6796
|
-
const forkScoped$1 = /*#__PURE__*/ dual((args) => isEffect(args[0]), (self, options) => flatMap$
|
|
6795
|
+
const forkScoped$1 = /*#__PURE__*/ dual((args) => isEffect(args[0]), (self, options) => flatMap$2(scope, (scope) => forkIn$1(self, scope, options)));
|
|
6797
6796
|
/** @internal */
|
|
6798
6797
|
const runForkWith = (context) => (effect, options) => {
|
|
6799
6798
|
const fiber = new FiberImpl(options?.scheduler ? add$1(context, Scheduler, options.scheduler) : context, options?.uninterruptible !== true);
|
|
@@ -6843,6 +6842,17 @@ const runSyncExitWith = (context) => {
|
|
|
6843
6842
|
};
|
|
6844
6843
|
/** @internal */
|
|
6845
6844
|
const runSyncExit$1 = /*#__PURE__*/ runSyncExitWith(/*#__PURE__*/ empty$3());
|
|
6845
|
+
/** @internal */
|
|
6846
|
+
const runSyncWith = (context) => {
|
|
6847
|
+
const runSyncExit = runSyncExitWith(context);
|
|
6848
|
+
return (effect) => {
|
|
6849
|
+
const exit = runSyncExit(effect);
|
|
6850
|
+
if (exit._tag === "Failure") throw causeSquash(exit.cause);
|
|
6851
|
+
return exit.value;
|
|
6852
|
+
};
|
|
6853
|
+
};
|
|
6854
|
+
/** @internal */
|
|
6855
|
+
const runSync$2 = /*#__PURE__*/ runSyncWith(/*#__PURE__*/ empty$3());
|
|
6846
6856
|
const succeedTrue = /*#__PURE__*/ succeed$4(true);
|
|
6847
6857
|
const succeedFalse = /*#__PURE__*/ succeed$4(false);
|
|
6848
6858
|
var Latch = class {
|
|
@@ -6903,7 +6913,7 @@ var Latch = class {
|
|
|
6903
6913
|
return true;
|
|
6904
6914
|
}
|
|
6905
6915
|
close = /*#__PURE__*/ sync$1(() => this.closeUnsafe());
|
|
6906
|
-
whenOpen = (self) => flatMap$
|
|
6916
|
+
whenOpen = (self) => flatMap$2(this.await, () => self);
|
|
6907
6917
|
isOpen() {
|
|
6908
6918
|
return this._isOpen;
|
|
6909
6919
|
}
|
|
@@ -8454,6 +8464,43 @@ const effectImpl = (service, effect) => effectContext(map$2(effect, (value) => m
|
|
|
8454
8464
|
* @since 2.0.0
|
|
8455
8465
|
*/
|
|
8456
8466
|
const effectContext = (effect) => fromBuildMemo((_, scope) => provide$2(effect, scope));
|
|
8467
|
+
const unwrapKey = /*#__PURE__*/ Service("effect/Layer/unwrap");
|
|
8468
|
+
/**
|
|
8469
|
+
* Unwraps a `Layer` from an `Effect`, flattening the nested structure.
|
|
8470
|
+
*
|
|
8471
|
+
* **When to use**
|
|
8472
|
+
*
|
|
8473
|
+
* Use when you have an `Effect` that produces a `Layer` and you want to
|
|
8474
|
+
* use that layer directly.
|
|
8475
|
+
*
|
|
8476
|
+
* **Details**
|
|
8477
|
+
*
|
|
8478
|
+
* The resulting Layer will have the combined error and dependency types from
|
|
8479
|
+
* both the outer Effect and the inner Layer.
|
|
8480
|
+
*
|
|
8481
|
+
* **Example** (Unwrapping an effectful layer)
|
|
8482
|
+
*
|
|
8483
|
+
* ```ts import.meta.vitest
|
|
8484
|
+
* import { Context, Effect, Layer } from "effect"
|
|
8485
|
+
*
|
|
8486
|
+
* class Database extends Context.Service<Database, {
|
|
8487
|
+
* readonly query: (sql: string) => Effect.Effect<string>
|
|
8488
|
+
* }>()("Database") {}
|
|
8489
|
+
*
|
|
8490
|
+
* const layerEffect = Effect.succeed(
|
|
8491
|
+
* Layer.succeed(Database, { query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result")) })
|
|
8492
|
+
* )
|
|
8493
|
+
*
|
|
8494
|
+
* const unwrappedLayer = Layer.unwrap(layerEffect)
|
|
8495
|
+
* const program = Database.use((database) => database.query("SELECT 1"))
|
|
8496
|
+
* Effect.runSync(Effect.provide(program, unwrappedLayer)) // => "result"
|
|
8497
|
+
* ```
|
|
8498
|
+
*
|
|
8499
|
+
* @stability stable
|
|
8500
|
+
* @category converting
|
|
8501
|
+
* @since 4.0.0
|
|
8502
|
+
*/
|
|
8503
|
+
const unwrap$6 = (self) => flatMap$1(effect(unwrapKey)(self), get(unwrapKey));
|
|
8457
8504
|
const mergeAllEffect = (layers, memoMap, scope) => {
|
|
8458
8505
|
const parentScope = forkUnsafe(scope, "parallel");
|
|
8459
8506
|
return forEach$2(layers, (layer) => layer.build(memoMap, forkUnsafe(parentScope, "sequential")), { concurrency: layers.length }).pipe(map$2((context) => mergeAll$1(...context)));
|
|
@@ -8508,7 +8555,7 @@ const mergeAllEffect = (layers, memoMap, scope) => {
|
|
|
8508
8555
|
* @since 2.0.0
|
|
8509
8556
|
*/
|
|
8510
8557
|
const mergeAll = (...layers) => fromBuildMemo((memoMap, scope) => mergeAllEffect(layers, memoMap, scope));
|
|
8511
|
-
const provideWith = (self, that, f) => fromBuildMemo((memoMap, scope) => flatMap$
|
|
8558
|
+
const provideWith = (self, that, f) => fromBuildMemo((memoMap, scope) => flatMap$2(Array.isArray(that) ? mergeAllEffect(that, memoMap, scope) : that.build(memoMap, scope), (context) => self.build(memoMap, scope).pipe(provideContext(context), map$2((merged) => f(merged, context)))));
|
|
8512
8559
|
/**
|
|
8513
8560
|
* Feeds the output services of the dependency layer into the requirements of
|
|
8514
8561
|
* this layer, returning a layer that provides both sets of services.
|
|
@@ -8597,6 +8644,83 @@ const provideWith = (self, that, f) => fromBuildMemo((memoMap, scope) => flatMap
|
|
|
8597
8644
|
* @since 2.0.0
|
|
8598
8645
|
*/
|
|
8599
8646
|
const provideMerge = /*#__PURE__*/ dual(2, (self, that) => provideWith(self, that, (self, that) => merge$2(that, self)));
|
|
8647
|
+
/**
|
|
8648
|
+
* Constructs a layer dynamically based on the output of this layer.
|
|
8649
|
+
*
|
|
8650
|
+
* **Example** (Creating services from layer output)
|
|
8651
|
+
*
|
|
8652
|
+
* ```ts import.meta.vitest
|
|
8653
|
+
* import { Context, Effect, Layer } from "effect"
|
|
8654
|
+
*
|
|
8655
|
+
* class Config extends Context.Service<Config, {
|
|
8656
|
+
* readonly dbUrl: string
|
|
8657
|
+
* readonly logLevel: string
|
|
8658
|
+
* }>()("Config") {}
|
|
8659
|
+
*
|
|
8660
|
+
* class Database extends Context.Service<Database, {
|
|
8661
|
+
* readonly query: (sql: string) => Effect.Effect<string>
|
|
8662
|
+
* }>()("Database") {}
|
|
8663
|
+
*
|
|
8664
|
+
* class Logger extends Context.Service<Logger, {
|
|
8665
|
+
* readonly log: (msg: string) => Effect.Effect<void>
|
|
8666
|
+
* }>()("Logger") {}
|
|
8667
|
+
*
|
|
8668
|
+
* const logs: Array<string> = []
|
|
8669
|
+
*
|
|
8670
|
+
* // Base config layer
|
|
8671
|
+
* const configLayer = Layer.succeed(Config, {
|
|
8672
|
+
* dbUrl: "postgres://localhost:5432/mydb",
|
|
8673
|
+
* logLevel: "debug"
|
|
8674
|
+
* })
|
|
8675
|
+
*
|
|
8676
|
+
* // Dynamically create services based on config
|
|
8677
|
+
* const dynamicServiceLayer = configLayer.pipe(
|
|
8678
|
+
* Layer.flatMap((context) => {
|
|
8679
|
+
* const config = Context.get(context, Config)
|
|
8680
|
+
*
|
|
8681
|
+
* // Create database layer based on config
|
|
8682
|
+
* const dbLayer = Layer.succeed(Database, {
|
|
8683
|
+
* query: Effect.fn("Database.query")((sql: string) =>
|
|
8684
|
+
* Effect.succeed(
|
|
8685
|
+
* `Querying ${config.dbUrl}: ${sql}`
|
|
8686
|
+
* ))
|
|
8687
|
+
* })
|
|
8688
|
+
*
|
|
8689
|
+
* // Create logger layer based on config
|
|
8690
|
+
* const loggerLayer = Layer.succeed(Logger, {
|
|
8691
|
+
* log: Effect.fn("Logger.log")((msg: string) =>
|
|
8692
|
+
* config.logLevel === "debug"
|
|
8693
|
+
* ? Effect.sync(() => logs.push(`[DEBUG] ${msg}`))
|
|
8694
|
+
* : Effect.sync(() => logs.push(msg))
|
|
8695
|
+
* )
|
|
8696
|
+
* })
|
|
8697
|
+
*
|
|
8698
|
+
* // Return combined layer
|
|
8699
|
+
* return Layer.mergeAll(dbLayer, loggerLayer)
|
|
8700
|
+
* })
|
|
8701
|
+
* )
|
|
8702
|
+
*
|
|
8703
|
+
* // Use the dynamic services
|
|
8704
|
+
* const program = Effect.gen(function*() {
|
|
8705
|
+
* const database = yield* Database
|
|
8706
|
+
* const logger = yield* Logger
|
|
8707
|
+
*
|
|
8708
|
+
* yield* logger.log("Starting database query")
|
|
8709
|
+
* const result = yield* database.query("SELECT * FROM users")
|
|
8710
|
+
*
|
|
8711
|
+
* return result
|
|
8712
|
+
* }).pipe(
|
|
8713
|
+
* Effect.provide(dynamicServiceLayer)
|
|
8714
|
+
* )
|
|
8715
|
+
* Effect.runSync(program) // => "Querying postgres://localhost:5432/mydb: SELECT * FROM users"
|
|
8716
|
+
* logs // => ["[DEBUG] Starting database query"]
|
|
8717
|
+
* ```
|
|
8718
|
+
*
|
|
8719
|
+
* @stability stable
|
|
8720
|
+
* @category sequencing
|
|
8721
|
+
* @since 2.0.0
|
|
8722
|
+
*/
|
|
8723
|
+
const flatMap$1 = /*#__PURE__*/ dual(2, (self, f) => fromBuild((memoMap, scope) => flatMap$2(self.build(memoMap, scope), (context) => f(context).build(memoMap, scope))));
|
|
8600
8724
|
//#endregion
|
|
8601
8725
|
//#region node_modules/effect/dist/internal/dateTime.js
|
|
8602
8726
|
/** @internal */
|
|
@@ -8807,7 +8931,7 @@ const matchEffect$1 = /*#__PURE__*/ dual(2, (self, options) => matchCauseEffect$
|
|
|
8807
8931
|
}));
|
|
8808
8932
|
//#endregion
|
|
8809
8933
|
//#region node_modules/effect/dist/internal/layer.js
|
|
8810
|
-
const provideLayer = (self, layer, options) => scopedWith$1((scope) => flatMap$
|
|
8934
|
+
const provideLayer = (self, layer, options) => scopedWith$1((scope) => flatMap$2(options?.local ? buildWithMemoMap(layer, makeMemoMapUnsafe(), scope) : buildWithScope(layer, scope), (context) => provideContext(self, context)));
|
|
8811
8935
|
/** @internal */
|
|
8812
8936
|
const provide$1 = /*#__PURE__*/ dual((args) => isEffect(args[0]), (self, source, options) => isContext(source) ? provideContext(self, source) : provideLayer(self, Array.isArray(source) ? mergeAll(...source) : source, options));
|
|
8813
8937
|
//#endregion
|
|
@@ -9681,7 +9805,7 @@ const withFiber = withFiber$1;
|
|
|
9681
9805
|
* @category sequencing
|
|
9682
9806
|
* @since 2.0.0
|
|
9683
9807
|
*/
|
|
9684
|
-
const flatMap = flatMap$
|
|
9808
|
+
const flatMap = flatMap$2;
|
|
9685
9809
|
/**
|
|
9686
9810
|
* Runs this effect and then runs another effect, optionally using the first
|
|
9687
9811
|
* effect's success value to choose the next effect.
|
|
@@ -11221,6 +11345,65 @@ const runFork = runFork$1;
|
|
|
11221
11345
|
*/
|
|
11222
11346
|
const runPromise = runPromise$1;
|
|
11223
11347
|
/**
|
|
11348
|
+
* Executes an effect synchronously and returns its success value.
|
|
11349
|
+
*
|
|
11350
|
+
* **When to use**
|
|
11351
|
+
*
|
|
11352
|
+
* Use when you need to execute an effect that is guaranteed to complete
|
|
11353
|
+
* synchronously.
|
|
11354
|
+
*
|
|
11355
|
+
* **Details**
|
|
11356
|
+
*
|
|
11357
|
+
* If the effect fails, dies, or is interrupted, `runSync` throws the squashed
|
|
11358
|
+
* cause instead of returning a value. Typed errors can be thrown directly,
|
|
11359
|
+
* including non-Error values. Asynchronous work throws `Cause.AsyncFiberError`.
|
|
11360
|
+
* Use `runSyncExit` when you want the failure captured as an `Exit`.
|
|
11361
|
+
*
|
|
11362
|
+
* **Example** (Running a synchronous effect)
|
|
11363
|
+
*
|
|
11364
|
+
* ```ts import.meta.vitest
|
|
11365
|
+
* import { Effect } from "effect"
|
|
11366
|
+
* const output: Array<unknown> = []
|
|
11367
|
+
*
|
|
11368
|
+
* const program = Effect.sync(() => {
|
|
11369
|
+
* void output.push("Hello, World!")
|
|
11370
|
+
* return 1
|
|
11371
|
+
* })
|
|
11372
|
+
*
|
|
11373
|
+
* const result = Effect.runSync(program)
|
|
11374
|
+
* void output.push(result)
|
|
11375
|
+
* output // => ["Hello, World!", 1]
|
|
11376
|
+
* ```
|
|
11377
|
+
*
|
|
11378
|
+
* **Example** (Throwing for failed or async effects)
|
|
11379
|
+
*
|
|
11380
|
+
* ```ts import.meta.vitest
|
|
11381
|
+
* import { Effect } from "effect"
|
|
11382
|
+
* const output: Array<unknown> = []
|
|
11383
|
+
*
|
|
11384
|
+
* try {
|
|
11385
|
+
* // Attempt to run an effect that fails
|
|
11386
|
+
* Effect.runSync(Effect.fail("my error"))
|
|
11387
|
+
* } catch (e) {
|
|
11388
|
+
* void output.push("failed effect")
|
|
11389
|
+
* }
|
|
11390
|
+
* try {
|
|
11391
|
+
* // Attempt to run an effect that involves async work
|
|
11392
|
+
* Effect.runSync(Effect.promise(() => Promise.resolve(1)))
|
|
11393
|
+
* } catch (e) {
|
|
11394
|
+
* void output.push("async effect")
|
|
11395
|
+
* }
|
|
11396
|
+
* output // => ["failed effect", "async effect"]
|
|
11397
|
+
* ```
|
|
11398
|
+
*
|
|
11399
|
+
* @see {@link runSyncExit} for a version that returns an `Exit` type instead of
|
|
11400
|
+
* throwing an error.
|
|
11401
|
+
* @stability stable
|
|
11402
|
+
* @category running
|
|
11403
|
+
* @since 2.0.0
|
|
11404
|
+
*/
|
|
11405
|
+
const runSync$1 = runSync$2;
|
|
11406
|
+
/**
|
|
11224
11407
|
* Runs an effect synchronously and captures the outcome safely as an `Exit` type, which
|
|
11225
11408
|
* represents the outcome (success or failure) of the effect.
|
|
11226
11409
|
*
|
|
@@ -13265,7 +13448,7 @@ const coerce = (value) => isRedacted(value) ? value : String(value);
|
|
|
13265
13448
|
//#endregion
|
|
13266
13449
|
//#region node_modules/effect/dist/internal/schema/annotations.js
|
|
13267
13450
|
/** @internal */
|
|
13268
|
-
function resolve$
|
|
13451
|
+
function resolve$3(ast) {
|
|
13269
13452
|
return ast.checks ? ast.checks[ast.checks.length - 1].annotations : ast.annotations;
|
|
13270
13453
|
}
|
|
13271
13454
|
/** @internal */
|
|
@@ -13274,7 +13457,7 @@ const SENTINELS_ANNOTATION_KEY = "~sentinels";
|
|
|
13274
13457
|
const CONSTRUCTOR_ANNOTATION_KEY = "~constructor";
|
|
13275
13458
|
/** @internal */
|
|
13276
13459
|
const getExpected = /*#__PURE__*/ memoize((ast) => {
|
|
13277
|
-
const identifier = resolve$
|
|
13460
|
+
const identifier = resolve$3(ast)?.identifier;
|
|
13278
13461
|
if (typeof identifier === "string") return identifier;
|
|
13279
13462
|
return ast.getExpected(getExpected);
|
|
13280
13463
|
});
|
|
@@ -15993,8 +16176,8 @@ function compile$1(ast, compile, compileField, base, specialize) {
|
|
|
15993
16176
|
/** @internal */
|
|
15994
16177
|
const invalid = /*#__PURE__*/ Symbol();
|
|
15995
16178
|
const cache = /*#__PURE__*/ new WeakMap();
|
|
15996
|
-
const decodeChild = (ast) => resolve$
|
|
15997
|
-
const makeChild = (ast) => resolve$
|
|
16179
|
+
const decodeChild = (ast) => resolve$2(ast).parser;
|
|
16180
|
+
const makeChild = (ast) => resolve$2(ast).makeEffect;
|
|
15998
16181
|
const makeField = (ast) => compileField(ast, makeChild);
|
|
15999
16182
|
var InterpretedEntry = class {
|
|
16000
16183
|
ast;
|
|
@@ -16012,7 +16195,7 @@ var InterpretedEntry = class {
|
|
|
16012
16195
|
}
|
|
16013
16196
|
};
|
|
16014
16197
|
/** @internal */
|
|
16015
|
-
function resolve$
|
|
16198
|
+
function resolve$2(ast) {
|
|
16016
16199
|
const cached = cache.get(ast);
|
|
16017
16200
|
if (cached !== void 0) return cached;
|
|
16018
16201
|
const entry = new InterpretedEntry(ast);
|
|
@@ -16183,7 +16366,7 @@ function throwConstructorDefect(error) {
|
|
|
16183
16366
|
}
|
|
16184
16367
|
function makeConstructorSync(ast) {
|
|
16185
16368
|
let entry;
|
|
16186
|
-
return (input, options) => constructSync(entry ??= resolve$
|
|
16369
|
+
return (input, options) => constructSync(entry ??= resolve$2(ast), input, options);
|
|
16187
16370
|
}
|
|
16188
16371
|
function constructSync(entry, input, options) {
|
|
16189
16372
|
const parseOptions = options?.disableChecks ? options.parseOptions ? {
|
|
@@ -16202,8 +16385,8 @@ function constructSync(entry, input, options) {
|
|
|
16202
16385
|
}
|
|
16203
16386
|
return runSync(parserResult(entry.makeEffect(input, parseOptions), input), constructorDefectMessage);
|
|
16204
16387
|
}
|
|
16205
|
-
const normalCompiler = (ast) => resolve$
|
|
16206
|
-
const constructorCompiler = (ast) => resolve$
|
|
16388
|
+
const normalCompiler = (ast) => resolve$2(ast).parser;
|
|
16389
|
+
const constructorCompiler = (ast) => resolve$2(ast).makeEffect;
|
|
16207
16390
|
//#endregion
|
|
16208
16391
|
//#region node_modules/effect/dist/internal/schema/make.js
|
|
16209
16392
|
/** @internal */
|
|
@@ -17627,7 +17810,7 @@ const splitSegments = (pattern) => {
|
|
|
17627
17810
|
const drive = /^[A-Za-z]:/;
|
|
17628
17811
|
const isDrive = (segment) => segment.length === 2 && drive.test(segment);
|
|
17629
17812
|
const isRoot = (name) => name === "" || name.length === 3 && name[2] === "/" && drive.test(name);
|
|
17630
|
-
const isAbsolute
|
|
17813
|
+
const isAbsolute = (path) => path.startsWith("/") || path[2] === "/" && drive.test(path);
|
|
17631
17814
|
const normalize = (raw) => {
|
|
17632
17815
|
const segments = [];
|
|
17633
17816
|
let i = 0;
|
|
@@ -17970,7 +18153,7 @@ const needsDirectory = (compiled, states) => {
|
|
|
17970
18153
|
}
|
|
17971
18154
|
return false;
|
|
17972
18155
|
};
|
|
17973
|
-
const join
|
|
18156
|
+
const join = (parent, name) => {
|
|
17974
18157
|
if (parent === "") return name === "" ? "/" : name;
|
|
17975
18158
|
return parent.endsWith("/") ? parent + name : `${parent}/${name}`;
|
|
17976
18159
|
};
|
|
@@ -17985,10 +18168,10 @@ const walk = /*#__PURE__*/ fnUntraced(function* (fs, compiled, options) {
|
|
|
17985
18168
|
const root = options?.root ?? "";
|
|
17986
18169
|
const excludes = compile(options?.exclude ?? [], true);
|
|
17987
18170
|
const excludeNeedsDirectory = excludes.nodes.some((node) => node._tag === "End" && node.directoryOnly);
|
|
17988
|
-
const toFsPath = (relative) => isAbsolute
|
|
18171
|
+
const toFsPath = (relative) => isAbsolute(relative) ? relative : relative === "" ? root === "" ? "." : root : join(root, relative);
|
|
17989
18172
|
const isUnlisted = (value) => value === "." || value === ".." || isRoot(value);
|
|
17990
18173
|
const probe = (parent, name, named) => {
|
|
17991
|
-
const path = toFsPath(join
|
|
18174
|
+
const path = toFsPath(join(parent, name));
|
|
17992
18175
|
return fs.stat(path).pipe(map((info) => info.type), catchReason("PlatformError", "NotFound", () => fs.readLink(path).pipe(as("SymbolicLink"), catchIf(isNotLink, () => succeed$1(void 0)))), catchIf(named ? isMissing : isIgnorable, () => succeed$1(void 0)), map((type) => type === void 0 ? void 0 : {
|
|
17993
18176
|
name,
|
|
17994
18177
|
type
|
|
@@ -18012,7 +18195,7 @@ const walk = /*#__PURE__*/ fnUntraced(function* (fs, compiled, options) {
|
|
|
18012
18195
|
const result = step(compiled, states, name);
|
|
18013
18196
|
const next = union(result);
|
|
18014
18197
|
if (next.size === 0) return;
|
|
18015
|
-
const relative = join
|
|
18198
|
+
const relative = join(parent, name);
|
|
18016
18199
|
const nextExcluded = union(step(excludes, excluded, name));
|
|
18017
18200
|
const descend = canDescend(compiled, next);
|
|
18018
18201
|
const isDirectory = !descend && !needsDirectory(compiled, next) && !excludeNeedsDirectory ? false : type !== "Unknown" && type !== "SymbolicLink" ? type === "Directory" : yield* fs.stat(toFsPath(relative)).pipe(map((info) => info.type === "Directory"), catchIf(isIgnorable, () => succeed$1(false)));
|
|
@@ -19295,6 +19478,388 @@ const make$3 = (impl) => Path$1.of({
|
|
|
19295
19478
|
return relative === ".." || relative.startsWith(`..${impl.sep}`) || impl.isAbsolute(relative) ? none() : some(candidate);
|
|
19296
19479
|
}
|
|
19297
19480
|
});
|
|
19481
|
+
/**
|
|
19482
|
+
* The following functions are adapted from the Node.js source code:
|
|
19483
|
+
* https://github.com/nodejs/node/blob/main/lib/internal/url.js
|
|
19484
|
+
*
|
|
19485
|
+
* The following license applies to these functions:
|
|
19486
|
+
* - MIT
|
|
19487
|
+
*/
|
|
19488
|
+
function normalizeStringPosix(path, allowAboveRoot) {
|
|
19489
|
+
let res = "";
|
|
19490
|
+
let lastSegmentLength = 0;
|
|
19491
|
+
let lastSlash = -1;
|
|
19492
|
+
let dots = 0;
|
|
19493
|
+
let code;
|
|
19494
|
+
for (let i = 0; i <= path.length; ++i) {
|
|
19495
|
+
if (i < path.length) code = path.charCodeAt(i);
|
|
19496
|
+
else if (code === 47) break;
|
|
19497
|
+
else code = 47;
|
|
19498
|
+
if (code === 47) {
|
|
19499
|
+
if (lastSlash === i - 1 || dots === 1) {} else if (lastSlash !== i - 1 && dots === 2) {
|
|
19500
|
+
if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 || res.charCodeAt(res.length - 2) !== 46) {
|
|
19501
|
+
if (res.length > 2) {
|
|
19502
|
+
const lastSlashIndex = res.lastIndexOf("/");
|
|
19503
|
+
if (lastSlashIndex !== res.length - 1) {
|
|
19504
|
+
if (lastSlashIndex === -1) {
|
|
19505
|
+
res = "";
|
|
19506
|
+
lastSegmentLength = 0;
|
|
19507
|
+
} else {
|
|
19508
|
+
res = res.slice(0, lastSlashIndex);
|
|
19509
|
+
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
|
|
19510
|
+
}
|
|
19511
|
+
lastSlash = i;
|
|
19512
|
+
dots = 0;
|
|
19513
|
+
continue;
|
|
19514
|
+
}
|
|
19515
|
+
} else if (res.length === 2 || res.length === 1) {
|
|
19516
|
+
res = "";
|
|
19517
|
+
lastSegmentLength = 0;
|
|
19518
|
+
lastSlash = i;
|
|
19519
|
+
dots = 0;
|
|
19520
|
+
continue;
|
|
19521
|
+
}
|
|
19522
|
+
}
|
|
19523
|
+
if (allowAboveRoot) {
|
|
19524
|
+
if (res.length > 0) res += "/..";
|
|
19525
|
+
else res = "..";
|
|
19526
|
+
lastSegmentLength = 2;
|
|
19527
|
+
}
|
|
19528
|
+
} else {
|
|
19529
|
+
if (res.length > 0) res += "/" + path.slice(lastSlash + 1, i);
|
|
19530
|
+
else res = path.slice(lastSlash + 1, i);
|
|
19531
|
+
lastSegmentLength = i - lastSlash - 1;
|
|
19532
|
+
}
|
|
19533
|
+
lastSlash = i;
|
|
19534
|
+
dots = 0;
|
|
19535
|
+
} else if (code === 46 && dots !== -1) ++dots;
|
|
19536
|
+
else dots = -1;
|
|
19537
|
+
}
|
|
19538
|
+
return res;
|
|
19539
|
+
}
|
|
19540
|
+
function _format(sep, pathObject) {
|
|
19541
|
+
const dir = pathObject.dir || pathObject.root;
|
|
19542
|
+
const base = pathObject.base || (pathObject.name || "") + (pathObject.ext || "");
|
|
19543
|
+
if (!dir) return base;
|
|
19544
|
+
if (dir === pathObject.root) return dir + base;
|
|
19545
|
+
return dir + sep + base;
|
|
19546
|
+
}
|
|
19547
|
+
function fromFileUrl(url) {
|
|
19548
|
+
if (url.protocol !== "file:") return fail$4(new BadArgument({
|
|
19549
|
+
module: "Path",
|
|
19550
|
+
method: "fromFileUrl",
|
|
19551
|
+
description: "URL must be of scheme file"
|
|
19552
|
+
}));
|
|
19553
|
+
else if (url.hostname !== "") return fail$4(new BadArgument({
|
|
19554
|
+
module: "Path",
|
|
19555
|
+
method: "fromFileUrl",
|
|
19556
|
+
description: "Invalid file URL host"
|
|
19557
|
+
}));
|
|
19558
|
+
const pathname = url.pathname;
|
|
19559
|
+
for (let n = 0; n < pathname.length; n++) if (pathname[n] === "%") {
|
|
19560
|
+
const third = pathname.codePointAt(n + 2) | 32;
|
|
19561
|
+
if (pathname[n + 1] === "2" && third === 102) return fail$4(new BadArgument({
|
|
19562
|
+
module: "Path",
|
|
19563
|
+
method: "fromFileUrl",
|
|
19564
|
+
description: "must not include encoded / characters"
|
|
19565
|
+
}));
|
|
19566
|
+
}
|
|
19567
|
+
return try_({
|
|
19568
|
+
try: () => decodeURIComponent(pathname),
|
|
19569
|
+
catch: (cause) => new BadArgument({
|
|
19570
|
+
module: "Path",
|
|
19571
|
+
method: "fromFileUrl",
|
|
19572
|
+
cause
|
|
19573
|
+
})
|
|
19574
|
+
});
|
|
19575
|
+
}
|
|
19576
|
+
const resolve$1 = function resolve() {
|
|
19577
|
+
let resolvedPath = "";
|
|
19578
|
+
let resolvedAbsolute = false;
|
|
19579
|
+
let cwd = void 0;
|
|
19580
|
+
for (let i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
|
|
19581
|
+
let path;
|
|
19582
|
+
if (i >= 0) path = arguments[i];
|
|
19583
|
+
else {
|
|
19584
|
+
const process = globalThis.process;
|
|
19585
|
+
if (cwd === void 0 && "process" in globalThis && typeof process === "object" && process !== null && typeof process.cwd === "function") cwd = process.cwd();
|
|
19586
|
+
path = cwd;
|
|
19587
|
+
}
|
|
19588
|
+
if (path.length === 0) continue;
|
|
19589
|
+
resolvedPath = path + "/" + resolvedPath;
|
|
19590
|
+
resolvedAbsolute = path.charCodeAt(0) === 47;
|
|
19591
|
+
}
|
|
19592
|
+
resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute);
|
|
19593
|
+
if (resolvedAbsolute) if (resolvedPath.length > 0) return "/" + resolvedPath;
|
|
19594
|
+
else return "/";
|
|
19595
|
+
else if (resolvedPath.length > 0) return resolvedPath;
|
|
19596
|
+
else return ".";
|
|
19597
|
+
};
|
|
19598
|
+
const CHAR_FORWARD_SLASH = 47;
|
|
19599
|
+
function toFileUrl(filepath) {
|
|
19600
|
+
const outURL = new URL("file://");
|
|
19601
|
+
let resolved = resolve$1(filepath);
|
|
19602
|
+
if (filepath.charCodeAt(filepath.length - 1) === CHAR_FORWARD_SLASH && resolved[resolved.length - 1] !== "/") resolved += "/";
|
|
19603
|
+
outURL.pathname = encodePathChars(resolved);
|
|
19604
|
+
return succeed$1(outURL);
|
|
19605
|
+
}
|
|
19606
|
+
const percentRegExp = /%/g;
|
|
19607
|
+
const backslashRegExp = /\\/g;
|
|
19608
|
+
const newlineRegExp = /\n/g;
|
|
19609
|
+
const carriageReturnRegExp = /\r/g;
|
|
19610
|
+
const tabRegExp = /\t/g;
|
|
19611
|
+
function encodePathChars(filepath) {
|
|
19612
|
+
if (filepath.includes("%")) filepath = filepath.replace(percentRegExp, "%25");
|
|
19613
|
+
if (filepath.includes("\\")) filepath = filepath.replace(backslashRegExp, "%5C");
|
|
19614
|
+
if (filepath.includes("\n")) filepath = filepath.replace(newlineRegExp, "%0A");
|
|
19615
|
+
if (filepath.includes("\r")) filepath = filepath.replace(carriageReturnRegExp, "%0D");
|
|
19616
|
+
if (filepath.includes(" ")) filepath = filepath.replace(tabRegExp, "%09");
|
|
19617
|
+
return filepath;
|
|
19618
|
+
}
|
|
19619
|
+
const posixImpl = /*#__PURE__*/ make$3({
|
|
19620
|
+
resolve: resolve$1,
|
|
19621
|
+
normalize(path) {
|
|
19622
|
+
if (path.length === 0) return ".";
|
|
19623
|
+
const isAbsolute = path.charCodeAt(0) === 47;
|
|
19624
|
+
const trailingSeparator = path.charCodeAt(path.length - 1) === 47;
|
|
19625
|
+
path = normalizeStringPosix(path, !isAbsolute);
|
|
19626
|
+
if (path.length === 0 && !isAbsolute) path = ".";
|
|
19627
|
+
if (path.length > 0 && trailingSeparator) path += "/";
|
|
19628
|
+
if (isAbsolute) return "/" + path;
|
|
19629
|
+
return path;
|
|
19630
|
+
},
|
|
19631
|
+
isAbsolute(path) {
|
|
19632
|
+
return path.length > 0 && path.charCodeAt(0) === 47;
|
|
19633
|
+
},
|
|
19634
|
+
join() {
|
|
19635
|
+
if (arguments.length === 0) return ".";
|
|
19636
|
+
let joined;
|
|
19637
|
+
for (let i = 0; i < arguments.length; ++i) {
|
|
19638
|
+
const arg = arguments[i];
|
|
19639
|
+
if (arg.length > 0) if (joined === void 0) joined = arg;
|
|
19640
|
+
else joined += "/" + arg;
|
|
19641
|
+
}
|
|
19642
|
+
if (joined === void 0) return ".";
|
|
19643
|
+
return posixImpl.normalize(joined);
|
|
19644
|
+
},
|
|
19645
|
+
relative(from, to) {
|
|
19646
|
+
if (from === to) return "";
|
|
19647
|
+
from = posixImpl.resolve(from);
|
|
19648
|
+
to = posixImpl.resolve(to);
|
|
19649
|
+
if (from === to) return "";
|
|
19650
|
+
let fromStart = 1;
|
|
19651
|
+
for (; fromStart < from.length; ++fromStart) if (from.charCodeAt(fromStart) !== 47) break;
|
|
19652
|
+
const fromEnd = from.length;
|
|
19653
|
+
const fromLen = fromEnd - fromStart;
|
|
19654
|
+
let toStart = 1;
|
|
19655
|
+
for (; toStart < to.length; ++toStart) if (to.charCodeAt(toStart) !== 47) break;
|
|
19656
|
+
const toLen = to.length - toStart;
|
|
19657
|
+
const length = fromLen < toLen ? fromLen : toLen;
|
|
19658
|
+
let lastCommonSep = -1;
|
|
19659
|
+
let i = 0;
|
|
19660
|
+
for (; i <= length; ++i) {
|
|
19661
|
+
if (i === length) {
|
|
19662
|
+
if (toLen > length) {
|
|
19663
|
+
if (to.charCodeAt(toStart + i) === 47) return to.slice(toStart + i + 1);
|
|
19664
|
+
else if (i === 0) return to.slice(toStart + i);
|
|
19665
|
+
} else if (fromLen > length) {
|
|
19666
|
+
if (from.charCodeAt(fromStart + i) === 47) lastCommonSep = i;
|
|
19667
|
+
else if (i === 0) lastCommonSep = 0;
|
|
19668
|
+
}
|
|
19669
|
+
break;
|
|
19670
|
+
}
|
|
19671
|
+
const fromCode = from.charCodeAt(fromStart + i);
|
|
19672
|
+
if (fromCode !== to.charCodeAt(toStart + i)) break;
|
|
19673
|
+
else if (fromCode === 47) lastCommonSep = i;
|
|
19674
|
+
}
|
|
19675
|
+
let out = "";
|
|
19676
|
+
for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) if (i === fromEnd || from.charCodeAt(i) === 47) if (out.length === 0) out += "..";
|
|
19677
|
+
else out += "/..";
|
|
19678
|
+
if (out.length > 0) return out + to.slice(toStart + lastCommonSep);
|
|
19679
|
+
else {
|
|
19680
|
+
toStart += lastCommonSep;
|
|
19681
|
+
if (to.charCodeAt(toStart) === 47) ++toStart;
|
|
19682
|
+
return to.slice(toStart);
|
|
19683
|
+
}
|
|
19684
|
+
},
|
|
19685
|
+
dirname(path) {
|
|
19686
|
+
if (path.length === 0) return ".";
|
|
19687
|
+
let code = path.charCodeAt(0);
|
|
19688
|
+
const hasRoot = code === 47;
|
|
19689
|
+
let end = -1;
|
|
19690
|
+
let matchedSlash = true;
|
|
19691
|
+
for (let i = path.length - 1; i >= 1; --i) {
|
|
19692
|
+
code = path.charCodeAt(i);
|
|
19693
|
+
if (code === 47) {
|
|
19694
|
+
if (!matchedSlash) {
|
|
19695
|
+
end = i;
|
|
19696
|
+
break;
|
|
19697
|
+
}
|
|
19698
|
+
} else matchedSlash = false;
|
|
19699
|
+
}
|
|
19700
|
+
if (end === -1) return hasRoot ? "/" : ".";
|
|
19701
|
+
if (hasRoot && end === 1) return "//";
|
|
19702
|
+
return path.slice(0, end);
|
|
19703
|
+
},
|
|
19704
|
+
basename(path, ext) {
|
|
19705
|
+
let start = 0;
|
|
19706
|
+
let end = -1;
|
|
19707
|
+
let matchedSlash = true;
|
|
19708
|
+
let i;
|
|
19709
|
+
if (ext !== void 0 && ext.length > 0 && ext.length <= path.length) {
|
|
19710
|
+
if (ext.length === path.length && ext === path) return "";
|
|
19711
|
+
let extIdx = ext.length - 1;
|
|
19712
|
+
let firstNonSlashEnd = -1;
|
|
19713
|
+
for (i = path.length - 1; i >= 0; --i) {
|
|
19714
|
+
const code = path.charCodeAt(i);
|
|
19715
|
+
if (code === 47) {
|
|
19716
|
+
if (!matchedSlash) {
|
|
19717
|
+
start = i + 1;
|
|
19718
|
+
break;
|
|
19719
|
+
}
|
|
19720
|
+
} else {
|
|
19721
|
+
if (firstNonSlashEnd === -1) {
|
|
19722
|
+
matchedSlash = false;
|
|
19723
|
+
firstNonSlashEnd = i + 1;
|
|
19724
|
+
}
|
|
19725
|
+
if (extIdx >= 0) if (code === ext.charCodeAt(extIdx)) {
|
|
19726
|
+
if (--extIdx === -1) end = i;
|
|
19727
|
+
} else {
|
|
19728
|
+
extIdx = -1;
|
|
19729
|
+
end = firstNonSlashEnd;
|
|
19730
|
+
}
|
|
19731
|
+
}
|
|
19732
|
+
}
|
|
19733
|
+
if (start === end) end = firstNonSlashEnd;
|
|
19734
|
+
else if (end === -1) end = path.length;
|
|
19735
|
+
return path.slice(start, end);
|
|
19736
|
+
} else {
|
|
19737
|
+
for (i = path.length - 1; i >= 0; --i) if (path.charCodeAt(i) === 47) {
|
|
19738
|
+
if (!matchedSlash) {
|
|
19739
|
+
start = i + 1;
|
|
19740
|
+
break;
|
|
19741
|
+
}
|
|
19742
|
+
} else if (end === -1) {
|
|
19743
|
+
matchedSlash = false;
|
|
19744
|
+
end = i + 1;
|
|
19745
|
+
}
|
|
19746
|
+
if (end === -1) return "";
|
|
19747
|
+
return path.slice(start, end);
|
|
19748
|
+
}
|
|
19749
|
+
},
|
|
19750
|
+
extname(path) {
|
|
19751
|
+
let startDot = -1;
|
|
19752
|
+
let startPart = 0;
|
|
19753
|
+
let end = -1;
|
|
19754
|
+
let matchedSlash = true;
|
|
19755
|
+
let preDotState = 0;
|
|
19756
|
+
for (let i = path.length - 1; i >= 0; --i) {
|
|
19757
|
+
const code = path.charCodeAt(i);
|
|
19758
|
+
if (code === 47) {
|
|
19759
|
+
if (!matchedSlash) {
|
|
19760
|
+
startPart = i + 1;
|
|
19761
|
+
break;
|
|
19762
|
+
}
|
|
19763
|
+
continue;
|
|
19764
|
+
}
|
|
19765
|
+
if (end === -1) {
|
|
19766
|
+
matchedSlash = false;
|
|
19767
|
+
end = i + 1;
|
|
19768
|
+
}
|
|
19769
|
+
if (code === 46) {
|
|
19770
|
+
if (startDot === -1) startDot = i;
|
|
19771
|
+
else if (preDotState !== 1) preDotState = 1;
|
|
19772
|
+
} else if (startDot !== -1) preDotState = -1;
|
|
19773
|
+
}
|
|
19774
|
+
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) return "";
|
|
19775
|
+
return path.slice(startDot, end);
|
|
19776
|
+
},
|
|
19777
|
+
format: function format(pathObject) {
|
|
19778
|
+
if (pathObject === null || typeof pathObject !== "object") throw new TypeError("The \"pathObject\" argument must be of type Object. Received type " + typeof pathObject);
|
|
19779
|
+
return _format("/", pathObject);
|
|
19780
|
+
},
|
|
19781
|
+
parse(path) {
|
|
19782
|
+
const ret = {
|
|
19783
|
+
root: "",
|
|
19784
|
+
dir: "",
|
|
19785
|
+
base: "",
|
|
19786
|
+
ext: "",
|
|
19787
|
+
name: ""
|
|
19788
|
+
};
|
|
19789
|
+
if (path.length === 0) return ret;
|
|
19790
|
+
let code = path.charCodeAt(0);
|
|
19791
|
+
const isAbsolute = code === 47;
|
|
19792
|
+
let start;
|
|
19793
|
+
if (isAbsolute) {
|
|
19794
|
+
ret.root = "/";
|
|
19795
|
+
start = 1;
|
|
19796
|
+
} else start = 0;
|
|
19797
|
+
let startDot = -1;
|
|
19798
|
+
let startPart = 0;
|
|
19799
|
+
let end = -1;
|
|
19800
|
+
let matchedSlash = true;
|
|
19801
|
+
let i = path.length - 1;
|
|
19802
|
+
let preDotState = 0;
|
|
19803
|
+
for (; i >= start; --i) {
|
|
19804
|
+
code = path.charCodeAt(i);
|
|
19805
|
+
if (code === 47) {
|
|
19806
|
+
if (!matchedSlash) {
|
|
19807
|
+
startPart = i + 1;
|
|
19808
|
+
break;
|
|
19809
|
+
}
|
|
19810
|
+
continue;
|
|
19811
|
+
}
|
|
19812
|
+
if (end === -1) {
|
|
19813
|
+
matchedSlash = false;
|
|
19814
|
+
end = i + 1;
|
|
19815
|
+
}
|
|
19816
|
+
if (code === 46) {
|
|
19817
|
+
if (startDot === -1) startDot = i;
|
|
19818
|
+
else if (preDotState !== 1) preDotState = 1;
|
|
19819
|
+
} else if (startDot !== -1) preDotState = -1;
|
|
19820
|
+
}
|
|
19821
|
+
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
|
|
19822
|
+
if (end !== -1) if (startPart === 0 && isAbsolute) ret.base = ret.name = path.slice(1, end);
|
|
19823
|
+
else ret.base = ret.name = path.slice(startPart, end);
|
|
19824
|
+
} else {
|
|
19825
|
+
if (startPart === 0 && isAbsolute) {
|
|
19826
|
+
ret.name = path.slice(1, startDot);
|
|
19827
|
+
ret.base = path.slice(1, end);
|
|
19828
|
+
} else {
|
|
19829
|
+
ret.name = path.slice(startPart, startDot);
|
|
19830
|
+
ret.base = path.slice(startPart, end);
|
|
19831
|
+
}
|
|
19832
|
+
ret.ext = path.slice(startDot, end);
|
|
19833
|
+
}
|
|
19834
|
+
if (startPart > 0) ret.dir = path.slice(0, startPart - 1);
|
|
19835
|
+
else if (isAbsolute) ret.dir = "/";
|
|
19836
|
+
return ret;
|
|
19837
|
+
},
|
|
19838
|
+
sep: "/",
|
|
19839
|
+
fromFileUrl,
|
|
19840
|
+
toFileUrl,
|
|
19841
|
+
toNamespacedPath: identity
|
|
19842
|
+
});
|
|
19843
|
+
/**
|
|
19844
|
+
* Layer that provides the built-in POSIX `Path` implementation.
|
|
19845
|
+
*
|
|
19846
|
+
* **When to use**
|
|
19847
|
+
*
|
|
19848
|
+
* Use when you need an effect that requires the `Path` service to run with the
|
|
19849
|
+
* built-in POSIX path implementation.
|
|
19850
|
+
*
|
|
19851
|
+
* **Details**
|
|
19852
|
+
*
|
|
19853
|
+
* The layer provides a static service whose separator is `/` and whose
|
|
19854
|
+
* operations use POSIX path semantics.
|
|
19855
|
+
*
|
|
19856
|
+
* @see {@link Path} for accessing the `Path` service from an effect
|
|
19857
|
+
*
|
|
19858
|
+
* @stability unstable
|
|
19859
|
+
* @category layers
|
|
19860
|
+
* @since 4.0.0
|
|
19861
|
+
*/
|
|
19862
|
+
const layer$4 = /*#__PURE__*/ succeed$2(Path$1)(posixImpl);
|
|
19298
19863
|
//#endregion
|
|
19299
19864
|
//#region node_modules/effect/dist/process/ChildProcessSpawner.js
|
|
19300
19865
|
/**
|
|
@@ -19843,20 +20408,95 @@ const shellScript = (templates, expressions) => {
|
|
|
19843
20408
|
return script;
|
|
19844
20409
|
};
|
|
19845
20410
|
//#endregion
|
|
20411
|
+
//#region src/platform/NodeRuntime.ts
|
|
20412
|
+
const gzip = (source) => gzipSync(source, { level: 9 });
|
|
20413
|
+
/** Compiles `source` as a script in this realm, reporting errors against `filename`. */
|
|
20414
|
+
const compileScript = (source, filename) => runInThisContext(source, { filename });
|
|
20415
|
+
//#endregion
|
|
20416
|
+
//#region src/platform/NodeSync.ts
|
|
20417
|
+
const MAX_BUFFER = 256 * 1024 * 1024;
|
|
20418
|
+
const isAbsent = (e) => {
|
|
20419
|
+
const code = e.code;
|
|
20420
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
20421
|
+
};
|
|
20422
|
+
const orAbsent = (read) => {
|
|
20423
|
+
try {
|
|
20424
|
+
return read();
|
|
20425
|
+
} catch (e) {
|
|
20426
|
+
if (isAbsent(e)) return void 0;
|
|
20427
|
+
throw e;
|
|
20428
|
+
}
|
|
20429
|
+
};
|
|
20430
|
+
const infoOf = (stats) => ({
|
|
20431
|
+
type: stats.isSymbolicLink() ? "symlink" : stats.isDirectory() ? "directory" : stats.isFile() ? "file" : "other",
|
|
20432
|
+
uid: process.platform === "win32" ? void 0 : stats.uid
|
|
20433
|
+
});
|
|
20434
|
+
const nodeSyncIo = {
|
|
20435
|
+
readFile: (path) => orAbsent(() => readFileSync(path)),
|
|
20436
|
+
readText: (path) => orAbsent(() => readFileSync(path, "utf8")),
|
|
20437
|
+
writeText: (path, content) => writeFileSync(path, content, "utf8"),
|
|
20438
|
+
makeDirectory: (path) => {
|
|
20439
|
+
mkdirSync(path, { recursive: true });
|
|
20440
|
+
},
|
|
20441
|
+
rename: renameSync,
|
|
20442
|
+
stat: (path) => orAbsent(() => infoOf(statSync(path))),
|
|
20443
|
+
lstat: (path) => orAbsent(() => infoOf(lstatSync(path))),
|
|
20444
|
+
readLink: (path) => readlinkSync(path, "utf8"),
|
|
20445
|
+
realPath: (path) => realpathSync(path),
|
|
20446
|
+
git: (cwd, args) => execFileSync("git", args, {
|
|
20447
|
+
cwd,
|
|
20448
|
+
encoding: "utf8",
|
|
20449
|
+
maxBuffer: MAX_BUFFER
|
|
20450
|
+
}),
|
|
20451
|
+
gitBytes: (cwd, args) => execFileSync("git", args, {
|
|
20452
|
+
cwd,
|
|
20453
|
+
maxBuffer: MAX_BUFFER,
|
|
20454
|
+
stdio: [
|
|
20455
|
+
"ignore",
|
|
20456
|
+
"pipe",
|
|
20457
|
+
"ignore"
|
|
20458
|
+
]
|
|
20459
|
+
}),
|
|
20460
|
+
hash: (algorithm, ...parts) => {
|
|
20461
|
+
const hash = createHash(algorithm);
|
|
20462
|
+
for (const part of parts) hash.update(part);
|
|
20463
|
+
return hash.digest("hex");
|
|
20464
|
+
}
|
|
20465
|
+
};
|
|
20466
|
+
//#endregion
|
|
19846
20467
|
//#region src/platform/Host.ts
|
|
19847
20468
|
const toError$3 = (e) => e instanceof Error ? e : new Error(String(e));
|
|
19848
|
-
|
|
19849
|
-
|
|
19850
|
-
|
|
19851
|
-
|
|
19852
|
-
|
|
19853
|
-
|
|
19854
|
-
|
|
19855
|
-
|
|
19856
|
-
|
|
20469
|
+
/** `TMPDIR` when set and non-empty, else the host's own `fallback`. */
|
|
20470
|
+
const scratchDirOf = (env, fallback) => {
|
|
20471
|
+
const dir = env["TMPDIR"];
|
|
20472
|
+
return dir !== void 0 && dir.length > 0 ? dir : fallback;
|
|
20473
|
+
};
|
|
20474
|
+
const readAll = (stream) => tryPromise({
|
|
20475
|
+
try: async () => {
|
|
20476
|
+
const chunks = [];
|
|
20477
|
+
for await (const chunk of stream) chunks.push(Buffer.from(chunk));
|
|
20478
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
20479
|
+
},
|
|
20480
|
+
catch: toError$3
|
|
19857
20481
|
});
|
|
20482
|
+
const makeHostOps = (opts, proc) => {
|
|
20483
|
+
const script = proc.argv[1];
|
|
20484
|
+
return {
|
|
20485
|
+
scratchDir: scratchDirOf(opts.env, tmpdir()),
|
|
20486
|
+
realPath: (path) => try_({
|
|
20487
|
+
try: () => nodeSyncIo.realPath(path),
|
|
20488
|
+
catch: toError$3
|
|
20489
|
+
}),
|
|
20490
|
+
pid: proc.pid,
|
|
20491
|
+
uid: proc.getuid?.(),
|
|
20492
|
+
self: script === void 0 ? ["gtd"] : [proc.execPath, script],
|
|
20493
|
+
stdin: suspend$2(() => readAll(proc.stdin)),
|
|
20494
|
+
...opts
|
|
20495
|
+
};
|
|
20496
|
+
};
|
|
20497
|
+
/** Every member not given in `opts` comes from Node: `proc`, its OS and its file system. */
|
|
19858
20498
|
var Host = class Host extends Service()("Host") {
|
|
19859
|
-
static layer = (opts) => succeed$2(Host, makeHostOps(opts));
|
|
20499
|
+
static layer = (opts, proc = process) => succeed$2(Host, makeHostOps(opts, proc));
|
|
19860
20500
|
static Live = Host.layer({
|
|
19861
20501
|
root: process.cwd(),
|
|
19862
20502
|
home: homedir(),
|
|
@@ -20135,16 +20775,14 @@ var GitService = class GitService extends Service()("GitService") {
|
|
|
20135
20775
|
//#endregion
|
|
20136
20776
|
//#region src/platform/Workspace.ts
|
|
20137
20777
|
const toError$2 = (e) => e instanceof Error ? e : new Error(String(e));
|
|
20138
|
-
const MAX_BUFFER = 256 * 1024 * 1024;
|
|
20139
20778
|
const HASH_CHUNK = 500;
|
|
20779
|
+
const encoder$2 = new TextEncoder();
|
|
20140
20780
|
/** A full sha1 or sha256 object id — the only refs an immutable, disk-cached read is safe to key on. */
|
|
20141
20781
|
const isObjectHash = (ref) => /^[0-9a-f]{40}$|^[0-9a-f]{64}$/.test(ref);
|
|
20142
20782
|
const GTD_CACHE_SUBDIR = "gtd-cache";
|
|
20143
|
-
const
|
|
20144
|
-
const blobCachePath = (commonDir, id) => join(commonDir, GTD_CACHE_SUBDIR, "blobs", id);
|
|
20145
|
-
const readTextCache = (path) => {
|
|
20783
|
+
const readTextCache = (io, path) => {
|
|
20146
20784
|
try {
|
|
20147
|
-
return
|
|
20785
|
+
return io.readText(path);
|
|
20148
20786
|
} catch {
|
|
20149
20787
|
return;
|
|
20150
20788
|
}
|
|
@@ -20157,17 +20795,17 @@ const readTextCache = (path) => {
|
|
|
20157
20795
|
* cache is a best-effort speedup, never a correctness dependency, and the
|
|
20158
20796
|
* alternative — failing a gtd command over a cache write — is worse.
|
|
20159
20797
|
*/
|
|
20160
|
-
const writeTextCacheAtomic = (path, content) => {
|
|
20798
|
+
const writeTextCacheAtomic = (io, paths, pid, path, content) => {
|
|
20161
20799
|
try {
|
|
20162
|
-
|
|
20163
|
-
const tmp = `${path}.tmp-${
|
|
20164
|
-
|
|
20165
|
-
|
|
20800
|
+
io.makeDirectory(paths.dirname(path));
|
|
20801
|
+
const tmp = `${path}.tmp-${pid}-${Math.random().toString(36).slice(2)}`;
|
|
20802
|
+
io.writeText(tmp, content);
|
|
20803
|
+
io.rename(tmp, path);
|
|
20166
20804
|
} catch {}
|
|
20167
20805
|
};
|
|
20168
20806
|
/** A commit's `path → blob id` map, serialized as JSON. `undefined` on ANY read failure — missing, truncated, unparseable — so the caller falls back to asking git. */
|
|
20169
|
-
const readTreeCache = (
|
|
20170
|
-
const raw = readTextCache(
|
|
20807
|
+
const readTreeCache = (io, file) => {
|
|
20808
|
+
const raw = readTextCache(io, file);
|
|
20171
20809
|
if (raw === void 0) return void 0;
|
|
20172
20810
|
try {
|
|
20173
20811
|
const parsed = JSON.parse(raw);
|
|
@@ -20182,7 +20820,7 @@ const readTreeCache = (commonDir, hash) => {
|
|
|
20182
20820
|
return;
|
|
20183
20821
|
}
|
|
20184
20822
|
};
|
|
20185
|
-
const writeTreeCache = (
|
|
20823
|
+
const writeTreeCache = (io, paths, pid, file, tree) => writeTextCacheAtomic(io, paths, pid, file, JSON.stringify(Object.fromEntries(tree)));
|
|
20186
20824
|
/** Parses a `:`-prefixed raw-diff header token into its status letter and new blob id. */
|
|
20187
20825
|
const parseRawDiffHeader = (header) => {
|
|
20188
20826
|
const fields = header.slice(1).split(" ").filter((f) => f.length > 0);
|
|
@@ -20266,21 +20904,17 @@ const parseLsTree = (out) => {
|
|
|
20266
20904
|
* failed read rather than from the key itself, so callers that cache must
|
|
20267
20905
|
* check `ok` before doing so.
|
|
20268
20906
|
*/
|
|
20269
|
-
const fetchTree = (root, ref) => {
|
|
20907
|
+
const fetchTree = (io, root, ref) => {
|
|
20270
20908
|
try {
|
|
20271
20909
|
return {
|
|
20272
20910
|
ok: true,
|
|
20273
|
-
entries: parseLsTree(
|
|
20911
|
+
entries: parseLsTree(io.git(root, [
|
|
20274
20912
|
"ls-tree",
|
|
20275
20913
|
"-r",
|
|
20276
20914
|
"-z",
|
|
20277
20915
|
"--full-tree",
|
|
20278
20916
|
ref
|
|
20279
|
-
]
|
|
20280
|
-
cwd: root,
|
|
20281
|
-
encoding: "utf8",
|
|
20282
|
-
maxBuffer: MAX_BUFFER
|
|
20283
|
-
}))
|
|
20917
|
+
]))
|
|
20284
20918
|
};
|
|
20285
20919
|
} catch {
|
|
20286
20920
|
return {
|
|
@@ -20289,26 +20923,10 @@ const fetchTree = (root, ref) => {
|
|
|
20289
20923
|
};
|
|
20290
20924
|
}
|
|
20291
20925
|
};
|
|
20292
|
-
const assertRepoRelative = (path) => {
|
|
20293
|
-
if (isAbsolute(path)) throw new Error(`Workspace: "${path}" is an absolute path — this member takes repo-relative paths only (use "atPath"/"writeAtPath" to read/write outside the repo)`);
|
|
20926
|
+
const assertRepoRelative = (paths, path) => {
|
|
20927
|
+
if (paths.isAbsolute(path)) throw new Error(`Workspace: "${path}" is an absolute path — this member takes repo-relative paths only (use "atPath"/"writeAtPath" to read/write outside the repo)`);
|
|
20294
20928
|
return path;
|
|
20295
20929
|
};
|
|
20296
|
-
const readFileOrAbsent = (path) => {
|
|
20297
|
-
try {
|
|
20298
|
-
return readFileSync(path, "utf8");
|
|
20299
|
-
} catch (e) {
|
|
20300
|
-
if (e.code === "ENOENT") return void 0;
|
|
20301
|
-
throw e;
|
|
20302
|
-
}
|
|
20303
|
-
};
|
|
20304
|
-
const lstatOrAbsent = (path) => {
|
|
20305
|
-
try {
|
|
20306
|
-
return lstatSync(path);
|
|
20307
|
-
} catch (e) {
|
|
20308
|
-
if (e.code === "ENOENT") return void 0;
|
|
20309
|
-
throw e;
|
|
20310
|
-
}
|
|
20311
|
-
};
|
|
20312
20930
|
/**
|
|
20313
20931
|
* Runs `git log --raw` for `base..head` (or all of `head`'s first-parent
|
|
20314
20932
|
* history when `base` is `undefined`) and splits the output into one
|
|
@@ -20316,8 +20934,9 @@ const lstatOrAbsent = (path) => {
|
|
|
20316
20934
|
* count, same hashes, in order — since the two disagreeing means the episode
|
|
20317
20935
|
* bounds no longer match git's own history.
|
|
20318
20936
|
*/
|
|
20319
|
-
const runRawLogReplay = (root, base, head, commits) => {
|
|
20320
|
-
const
|
|
20937
|
+
const runRawLogReplay = (io, root, base, head, commits) => {
|
|
20938
|
+
const range = base !== void 0 ? `${base}..${head}` : head;
|
|
20939
|
+
const chunks = io.git(root, [
|
|
20321
20940
|
"log",
|
|
20322
20941
|
"--first-parent",
|
|
20323
20942
|
"--reverse",
|
|
@@ -20326,12 +20945,8 @@ const runRawLogReplay = (root, base, head, commits) => {
|
|
|
20326
20945
|
"-r",
|
|
20327
20946
|
"--no-abbrev",
|
|
20328
20947
|
"--format=%x01%H",
|
|
20329
|
-
|
|
20330
|
-
]
|
|
20331
|
-
cwd: root,
|
|
20332
|
-
encoding: "utf8",
|
|
20333
|
-
maxBuffer: MAX_BUFFER
|
|
20334
|
-
}).split("").filter((c) => c.length > 0);
|
|
20948
|
+
range
|
|
20949
|
+
]).split("").filter((c) => c.length > 0);
|
|
20335
20950
|
if (chunks.length !== commits.length) throw new Error(`gtd: episode reconstruction expected ${commits.length} commit(s) from git log, got ${chunks.length} — the episode bounds disagree with git's own history`);
|
|
20336
20951
|
return chunks.map((chunk, i) => {
|
|
20337
20952
|
const nul = chunk.indexOf("\0");
|
|
@@ -20357,8 +20972,11 @@ const foldRawDiffs = (baseTree, replay, rememberTree) => {
|
|
|
20357
20972
|
}
|
|
20358
20973
|
return result;
|
|
20359
20974
|
};
|
|
20360
|
-
const makeWorkspaceOps = (root, git, commonDir) => {
|
|
20361
|
-
const resolveAny = (path) => isAbsolute(path) ? path : join(root, path);
|
|
20975
|
+
const makeWorkspaceOps = ({ root, pid }, git, commonDir, paths, io) => {
|
|
20976
|
+
const resolveAny = (path) => paths.isAbsolute(path) ? path : paths.join(root, path);
|
|
20977
|
+
const repoPath = (path) => paths.join(root, assertRepoRelative(paths, path));
|
|
20978
|
+
const treeCachePath = (dir, hash) => paths.join(dir, GTD_CACHE_SUBDIR, "trees", hash);
|
|
20979
|
+
const blobCachePath = (dir, id) => paths.join(dir, GTD_CACHE_SUBDIR, "blobs", id);
|
|
20362
20980
|
const treeMemo = /* @__PURE__ */ new Map();
|
|
20363
20981
|
const blobMemo = /* @__PURE__ */ new Map();
|
|
20364
20982
|
/** A commit's tree, from the in-process memo or the on-disk cache — no subprocess. `undefined` on a cold miss. */
|
|
@@ -20366,15 +20984,15 @@ const makeWorkspaceOps = (root, git, commonDir) => {
|
|
|
20366
20984
|
const memoHit = treeMemo.get(hash);
|
|
20367
20985
|
if (memoHit !== void 0) return memoHit;
|
|
20368
20986
|
if (commonDir === void 0) return void 0;
|
|
20369
|
-
const cached = readTreeCache(commonDir, hash);
|
|
20987
|
+
const cached = readTreeCache(io, treeCachePath(commonDir, hash));
|
|
20370
20988
|
if (cached !== void 0) treeMemo.set(hash, cached);
|
|
20371
20989
|
return cached;
|
|
20372
20990
|
};
|
|
20373
20991
|
const rememberTree = (hash, tree) => {
|
|
20374
20992
|
treeMemo.set(hash, tree);
|
|
20375
|
-
if (commonDir !== void 0) writeTreeCache(commonDir, hash, tree);
|
|
20993
|
+
if (commonDir !== void 0) writeTreeCache(io, paths, pid, treeCachePath(commonDir, hash), tree);
|
|
20376
20994
|
};
|
|
20377
|
-
const readSync = (path) =>
|
|
20995
|
+
const readSync = (path) => io.readText(repoPath(path));
|
|
20378
20996
|
/**
|
|
20379
20997
|
* A blob's content, keyed on its OWN id rather than a (path, ref) pair —
|
|
20380
20998
|
* the same content read from two different commits costs one read, not
|
|
@@ -20386,7 +21004,7 @@ const makeWorkspaceOps = (root, git, commonDir) => {
|
|
|
20386
21004
|
const memoHit = blobMemo.get(id);
|
|
20387
21005
|
if (memoHit !== void 0) return memoHit;
|
|
20388
21006
|
if (commonDir !== void 0) {
|
|
20389
|
-
const cached = readTextCache(blobCachePath(commonDir, id));
|
|
21007
|
+
const cached = readTextCache(io, blobCachePath(commonDir, id));
|
|
20390
21008
|
if (cached !== void 0) {
|
|
20391
21009
|
blobMemo.set(id, cached);
|
|
20392
21010
|
return cached;
|
|
@@ -20394,34 +21012,26 @@ const makeWorkspaceOps = (root, git, commonDir) => {
|
|
|
20394
21012
|
}
|
|
20395
21013
|
let content;
|
|
20396
21014
|
try {
|
|
20397
|
-
content =
|
|
21015
|
+
content = io.git(root, [
|
|
20398
21016
|
"cat-file",
|
|
20399
21017
|
"-p",
|
|
20400
21018
|
id
|
|
20401
|
-
]
|
|
20402
|
-
cwd: root,
|
|
20403
|
-
encoding: "utf8",
|
|
20404
|
-
maxBuffer: MAX_BUFFER
|
|
20405
|
-
});
|
|
21019
|
+
]);
|
|
20406
21020
|
} catch {
|
|
20407
21021
|
return;
|
|
20408
21022
|
}
|
|
20409
21023
|
blobMemo.set(id, content);
|
|
20410
|
-
if (commonDir !== void 0) writeTextCacheAtomic(blobCachePath(commonDir, id), content);
|
|
21024
|
+
if (commonDir !== void 0) writeTextCacheAtomic(io, paths, pid, blobCachePath(commonDir, id), content);
|
|
20411
21025
|
return content;
|
|
20412
21026
|
};
|
|
20413
21027
|
const readCommittedSync = (path, ref = "HEAD") => {
|
|
20414
|
-
const relPath = assertRepoRelative(path);
|
|
21028
|
+
const relPath = assertRepoRelative(paths, path);
|
|
20415
21029
|
if (isObjectHash(ref)) {
|
|
20416
21030
|
const id = treeSync(ref).get(relPath);
|
|
20417
21031
|
return id === void 0 ? void 0 : readBlobSync(id);
|
|
20418
21032
|
}
|
|
20419
21033
|
try {
|
|
20420
|
-
return
|
|
20421
|
-
cwd: root,
|
|
20422
|
-
encoding: "utf8",
|
|
20423
|
-
maxBuffer: MAX_BUFFER
|
|
20424
|
-
});
|
|
21034
|
+
return io.git(root, ["show", `${ref}:${relPath}`]);
|
|
20425
21035
|
} catch {
|
|
20426
21036
|
return;
|
|
20427
21037
|
}
|
|
@@ -20431,7 +21041,7 @@ const makeWorkspaceOps = (root, git, commonDir) => {
|
|
|
20431
21041
|
const cached = peekTree(ref);
|
|
20432
21042
|
if (cached !== void 0) return cached;
|
|
20433
21043
|
}
|
|
20434
|
-
const { ok, entries } = fetchTree(root, ref);
|
|
21044
|
+
const { ok, entries } = fetchTree(io, root, ref);
|
|
20435
21045
|
if (!ok) {
|
|
20436
21046
|
if (onFailure === "throw") throw new Error(`gtd: failed to read the tree at "${ref}" — git ls-tree did not succeed`);
|
|
20437
21047
|
return entries;
|
|
@@ -20466,18 +21076,13 @@ const makeWorkspaceOps = (root, git, commonDir) => {
|
|
|
20466
21076
|
return result;
|
|
20467
21077
|
}
|
|
20468
21078
|
const head = commits[commits.length - 1];
|
|
20469
|
-
const replay = runRawLogReplay(root, base, head, commits);
|
|
21079
|
+
const replay = runRawLogReplay(io, root, base, head, commits);
|
|
20470
21080
|
for (const [hash, tree] of foldRawDiffs(baseTree, replay, rememberTree)) result.set(hash, tree);
|
|
20471
21081
|
return result;
|
|
20472
21082
|
},
|
|
20473
21083
|
catch: toError$2
|
|
20474
21084
|
});
|
|
20475
|
-
const gitSync = (args
|
|
20476
|
-
cwd: root,
|
|
20477
|
-
encoding: "utf8",
|
|
20478
|
-
maxBuffer: MAX_BUFFER,
|
|
20479
|
-
...input !== void 0 ? { input } : {}
|
|
20480
|
-
});
|
|
21085
|
+
const gitSync = (args) => io.git(root, args);
|
|
20481
21086
|
const worktreeSync = () => {
|
|
20482
21087
|
const entries = new Map(treeSync("HEAD"));
|
|
20483
21088
|
const status = gitSync([
|
|
@@ -20492,10 +21097,10 @@ const makeWorkspaceOps = (root, git, commonDir) => {
|
|
|
20492
21097
|
for (const record of status.split("\0")) {
|
|
20493
21098
|
if (record.length < 4) continue;
|
|
20494
21099
|
const path = record.slice(3).replace(/\/$/, "");
|
|
20495
|
-
const stat =
|
|
21100
|
+
const stat = io.lstat(paths.join(root, path));
|
|
20496
21101
|
if (stat === void 0) entries.delete(path);
|
|
20497
|
-
else if (stat.
|
|
20498
|
-
else if (stat.
|
|
21102
|
+
else if (stat.type === "symlink") entries.set(path, blobId(io.readLink(paths.join(root, path))));
|
|
21103
|
+
else if (stat.type === "directory") if (io.lstat(paths.join(root, path, ".git")) === void 0) entries.delete(path);
|
|
20499
21104
|
else entries.set(path, gitlinkOf(path));
|
|
20500
21105
|
else files.push(path);
|
|
20501
21106
|
}
|
|
@@ -20513,10 +21118,7 @@ const makeWorkspaceOps = (root, git, commonDir) => {
|
|
|
20513
21118
|
/** A submodule's (or embedded repository's) checked-out commit — what `git add` records for it. */
|
|
20514
21119
|
const gitlinkOf = (path) => {
|
|
20515
21120
|
try {
|
|
20516
|
-
return
|
|
20517
|
-
cwd: join(root, path),
|
|
20518
|
-
encoding: "utf8"
|
|
20519
|
-
}).trim();
|
|
21121
|
+
return io.git(paths.join(root, path), ["rev-parse", "HEAD"]).trim();
|
|
20520
21122
|
} catch {
|
|
20521
21123
|
return;
|
|
20522
21124
|
}
|
|
@@ -20524,8 +21126,8 @@ const makeWorkspaceOps = (root, git, commonDir) => {
|
|
|
20524
21126
|
let objectFormat;
|
|
20525
21127
|
const blobId = (content) => {
|
|
20526
21128
|
objectFormat ??= gitSync(["rev-parse", "--show-object-format"]).trim();
|
|
20527
|
-
const bytes =
|
|
20528
|
-
return
|
|
21129
|
+
const bytes = encoder$2.encode(content);
|
|
21130
|
+
return io.hash(objectFormat === "sha256" ? "sha256" : "sha1", `blob ${bytes.length}\0`, bytes);
|
|
20529
21131
|
};
|
|
20530
21132
|
return {
|
|
20531
21133
|
treeSync,
|
|
@@ -20537,27 +21139,28 @@ const makeWorkspaceOps = (root, git, commonDir) => {
|
|
|
20537
21139
|
catch: toError$2
|
|
20538
21140
|
}),
|
|
20539
21141
|
write: (path, content) => try_({
|
|
20540
|
-
try: () =>
|
|
21142
|
+
try: () => io.writeText(repoPath(path), content),
|
|
20541
21143
|
catch: toError$2
|
|
20542
21144
|
}),
|
|
20543
21145
|
committed: (path, ref = "HEAD") => try_({
|
|
20544
|
-
try: () => assertRepoRelative(path),
|
|
21146
|
+
try: () => assertRepoRelative(paths, path),
|
|
20545
21147
|
catch: toError$2
|
|
20546
21148
|
}).pipe(flatMap((relPath) => git.readFileAtRef(ref, relPath).pipe(catch_(() => succeed$1(void 0))))),
|
|
20547
21149
|
readCommittedSync,
|
|
20548
|
-
atPath: (path) =>
|
|
21150
|
+
atPath: (path) => io.readText(resolveAny(path)),
|
|
20549
21151
|
writeAtPath: (path, content) => try_({
|
|
20550
|
-
try: () =>
|
|
21152
|
+
try: () => io.writeText(resolveAny(path), content),
|
|
20551
21153
|
catch: toError$2
|
|
20552
21154
|
})
|
|
20553
21155
|
};
|
|
20554
21156
|
};
|
|
20555
21157
|
var Workspace = class Workspace extends Service()("Workspace") {
|
|
20556
21158
|
static Live = effect(Workspace, gen(function* () {
|
|
20557
|
-
const
|
|
21159
|
+
const host = yield* Host;
|
|
20558
21160
|
const git = yield* GitService;
|
|
21161
|
+
const paths = yield* Path$1;
|
|
20559
21162
|
const commonDir = yield* git.gitCommonDir().pipe(catch_(() => succeed$1(void 0)));
|
|
20560
|
-
return makeWorkspaceOps(
|
|
21163
|
+
return makeWorkspaceOps(host, git, commonDir, paths, nodeSyncIo);
|
|
20561
21164
|
}));
|
|
20562
21165
|
};
|
|
20563
21166
|
//#endregion
|
|
@@ -21494,7 +22097,7 @@ const bigintToNumber = (value, field) => {
|
|
|
21494
22097
|
if (!Number.isSafeInteger(number)) throw new RangeError(`${field} exceeds the safe integer range: ${value}`);
|
|
21495
22098
|
return number;
|
|
21496
22099
|
};
|
|
21497
|
-
const bigintToNumberOption = (value) => flatMap$
|
|
22100
|
+
const bigintToNumberOption = (value) => flatMap$4(fromNullishOr(value), toNumber);
|
|
21498
22101
|
const toUint64 = (value) => BigInt.asUintN(64, value);
|
|
21499
22102
|
const toUint64Option = (value) => map$6(fromNullishOr(value), toUint64);
|
|
21500
22103
|
const positionToNumber = (position, method) => try_({
|
|
@@ -21848,7 +22451,7 @@ const watchNode = (path, info, options) => callback((queue) => acquireRelease(sy
|
|
|
21848
22451
|
});
|
|
21849
22452
|
return watcher;
|
|
21850
22453
|
}), (watcher) => sync(() => watcher.close())).pipe(tap(() => sync(() => options?.onReady?.()))));
|
|
21851
|
-
const watch = (backend, path, options) => all([stat(path), serviceOption(WatchBackend)]).pipe(map(([stat, callerBackend]) => orElse(callerBackend, () => backend).pipe(flatMap$
|
|
22454
|
+
const watch = (backend, path, options) => all([stat(path), serviceOption(WatchBackend)]).pipe(map(([stat, callerBackend]) => orElse(callerBackend, () => backend).pipe(flatMap$4((_) => _.register(path, stat, options)), getOrElse(() => watchNode(path, stat, options)))), unwrap$2);
|
|
21852
22455
|
const writeFile = (path, data, options) => callback$1((resume, signal) => {
|
|
21853
22456
|
try {
|
|
21854
22457
|
NFS.writeFile(path, data, {
|
|
@@ -21956,11 +22559,12 @@ const GITDIR_POINTER = /^gitdir:\s*(.+)$/m;
|
|
|
21956
22559
|
*/
|
|
21957
22560
|
const worktreeGitDir = gen(function* () {
|
|
21958
22561
|
const { root } = yield* Host;
|
|
22562
|
+
const path = yield* Path$1;
|
|
21959
22563
|
const content = yield* (yield* Workspace).read(".git").pipe(catch_(() => succeed$1(void 0)));
|
|
21960
22564
|
const pointer = content === void 0 ? null : GITDIR_POINTER.exec(content);
|
|
21961
22565
|
if (pointer === null) return ".git";
|
|
21962
22566
|
const gitdir = pointer[1].trim();
|
|
21963
|
-
return isAbsolute(gitdir) ? gitdir : join(root, gitdir);
|
|
22567
|
+
return path.isAbsolute(gitdir) ? gitdir : path.join(root, gitdir);
|
|
21964
22568
|
});
|
|
21965
22569
|
/**
|
|
21966
22570
|
* The per-worktree loop log path `gtd next --json` reports as `log`.
|
|
@@ -21973,11 +22577,16 @@ const worktreeGitDir = gen(function* () {
|
|
|
21973
22577
|
*/
|
|
21974
22578
|
const loopLogPath = gen(function* () {
|
|
21975
22579
|
const { root, env } = yield* Host;
|
|
22580
|
+
const path = yield* Path$1;
|
|
21976
22581
|
const override = env.GTD_LOOP_LOG;
|
|
21977
22582
|
if (override !== void 0 && override !== "") return override;
|
|
21978
22583
|
const gitDirEnv = env.GIT_DIR;
|
|
21979
|
-
if (gitDirEnv !== void 0 && gitDirEnv !== "")
|
|
21980
|
-
|
|
22584
|
+
if (gitDirEnv !== void 0 && gitDirEnv !== "") {
|
|
22585
|
+
const gitDir = path.isAbsolute(gitDirEnv) ? gitDirEnv : path.join(root, gitDirEnv);
|
|
22586
|
+
return path.join(gitDir, "gtd-loop.log");
|
|
22587
|
+
}
|
|
22588
|
+
const gitDir = yield* worktreeGitDir;
|
|
22589
|
+
return path.join(gitDir, "gtd-loop.log");
|
|
21981
22590
|
});
|
|
21982
22591
|
//#endregion
|
|
21983
22592
|
//#region src/platform/RetainedHistory.ts
|
|
@@ -22001,6 +22610,73 @@ const restorability = (git, headHash, tipHash) => gen(function* () {
|
|
|
22001
22610
|
reason: "HEAD has advanced past the retained tip — restoring would discard commits built on top of it"
|
|
22002
22611
|
};
|
|
22003
22612
|
});
|
|
22613
|
+
Uint32Array.from([
|
|
22614
|
+
1116352408,
|
|
22615
|
+
1899447441,
|
|
22616
|
+
3049323471,
|
|
22617
|
+
3921009573,
|
|
22618
|
+
961987163,
|
|
22619
|
+
1508970993,
|
|
22620
|
+
2453635748,
|
|
22621
|
+
2870763221,
|
|
22622
|
+
3624381080,
|
|
22623
|
+
310598401,
|
|
22624
|
+
607225278,
|
|
22625
|
+
1426881987,
|
|
22626
|
+
1925078388,
|
|
22627
|
+
2162078206,
|
|
22628
|
+
2614888103,
|
|
22629
|
+
3248222580,
|
|
22630
|
+
3835390401,
|
|
22631
|
+
4022224774,
|
|
22632
|
+
264347078,
|
|
22633
|
+
604807628,
|
|
22634
|
+
770255983,
|
|
22635
|
+
1249150122,
|
|
22636
|
+
1555081692,
|
|
22637
|
+
1996064986,
|
|
22638
|
+
2554220882,
|
|
22639
|
+
2821834349,
|
|
22640
|
+
2952996808,
|
|
22641
|
+
3210313671,
|
|
22642
|
+
3336571891,
|
|
22643
|
+
3584528711,
|
|
22644
|
+
113926993,
|
|
22645
|
+
338241895,
|
|
22646
|
+
666307205,
|
|
22647
|
+
773529912,
|
|
22648
|
+
1294757372,
|
|
22649
|
+
1396182291,
|
|
22650
|
+
1695183700,
|
|
22651
|
+
1986661051,
|
|
22652
|
+
2177026350,
|
|
22653
|
+
2456956037,
|
|
22654
|
+
2730485921,
|
|
22655
|
+
2820302411,
|
|
22656
|
+
3259730800,
|
|
22657
|
+
3345764771,
|
|
22658
|
+
3516065817,
|
|
22659
|
+
3600352804,
|
|
22660
|
+
4094571909,
|
|
22661
|
+
275423344,
|
|
22662
|
+
430227734,
|
|
22663
|
+
506948616,
|
|
22664
|
+
659060556,
|
|
22665
|
+
883997877,
|
|
22666
|
+
958139571,
|
|
22667
|
+
1322822218,
|
|
22668
|
+
1537002063,
|
|
22669
|
+
1747873779,
|
|
22670
|
+
1955562222,
|
|
22671
|
+
2024104815,
|
|
22672
|
+
2227730452,
|
|
22673
|
+
2361852424,
|
|
22674
|
+
2428436474,
|
|
22675
|
+
2756734187,
|
|
22676
|
+
3204031479,
|
|
22677
|
+
3329325298
|
|
22678
|
+
]);
|
|
22679
|
+
new TextEncoder();
|
|
22004
22680
|
//#endregion
|
|
22005
22681
|
//#region src/flags/index.ts
|
|
22006
22682
|
const FLAGS$1 = [];
|
|
@@ -22032,19 +22708,21 @@ const resolveFlags = (env, registry = FLAGS$1) => {
|
|
|
22032
22708
|
const flagSettings = (flags) => Object.fromEntries(Object.entries(flags).map(([name, on]) => [`flag_${name}`, on ? "1" : "0"]));
|
|
22033
22709
|
//#endregion
|
|
22034
22710
|
//#region src/install/Version.ts
|
|
22035
|
-
const
|
|
22036
|
-
|
|
22037
|
-
let dir = dirname(fileURLToPath(fromUrl));
|
|
22711
|
+
const readPackageJson = (fromUrl) => {
|
|
22712
|
+
let dir = new URL(".", fromUrl);
|
|
22038
22713
|
for (let i = 0; i < 5; i++) {
|
|
22039
|
-
const candidate =
|
|
22040
|
-
|
|
22041
|
-
|
|
22714
|
+
const candidate = new URL("package.json", dir);
|
|
22715
|
+
try {
|
|
22716
|
+
const source = nodeSyncIo.readText(fileURLToPath(candidate));
|
|
22717
|
+
if (source !== void 0) return source;
|
|
22718
|
+
} catch {}
|
|
22719
|
+
dir = new URL("..", dir);
|
|
22042
22720
|
}
|
|
22043
22721
|
throw new Error(`package.json not found by walking up from ${fromUrl}`);
|
|
22044
22722
|
};
|
|
22045
22723
|
/** The global a pin's workflow registers itself under, ahead of the engine in the same file. */
|
|
22046
22724
|
const PIN_KEY = Symbol.for("@pmelab/gtd/pin");
|
|
22047
|
-
const GTD_VERSION = globalThis[PIN_KEY]?.version ??
|
|
22725
|
+
const GTD_VERSION = globalThis[PIN_KEY]?.version ?? JSON.parse(readPackageJson(import.meta.url)).version;
|
|
22048
22726
|
//#endregion
|
|
22049
22727
|
//#region src/install/Install.ts
|
|
22050
22728
|
const WORKFLOWS_REPO = "https://github.com/pmelab/gtd-workflows";
|
|
@@ -22810,9 +23488,9 @@ const withoutPath = (tree, hidden) => {
|
|
|
22810
23488
|
const OLDEST_READABLE_FORMAT = 1;
|
|
22811
23489
|
const short$1 = (hash) => hash.slice(0, 7);
|
|
22812
23490
|
const formatFault = (commits) => {
|
|
22813
|
-
const foreign = commits.find((c) => c.format < OLDEST_READABLE_FORMAT || c.format >
|
|
23491
|
+
const foreign = commits.find((c) => c.format < OLDEST_READABLE_FORMAT || c.format > 4);
|
|
22814
23492
|
if (foreign === void 0) return void 0;
|
|
22815
|
-
return `gtd: commit ${short$1(foreign.hash)} is written in history format ${foreign.format}, but this gtd reads formats ${OLDEST_READABLE_FORMAT} to
|
|
23493
|
+
return `gtd: commit ${short$1(foreign.hash)} is written in history format ${foreign.format}, but this gtd reads formats ${OLDEST_READABLE_FORMAT} to 4 — run a gtd release that reads format ${foreign.format} to continue this process`;
|
|
22816
23494
|
};
|
|
22817
23495
|
//#endregion
|
|
22818
23496
|
//#region src/replay/Trailers.ts
|
|
@@ -22849,11 +23527,21 @@ const parseStepId = (raw) => {
|
|
|
22849
23527
|
occurrence: Number(match[2])
|
|
22850
23528
|
};
|
|
22851
23529
|
};
|
|
23530
|
+
/** Step names a workflow may not take: gtd's own beats live under it. */
|
|
23531
|
+
const ENGINE_PREFIX = "gtd:";
|
|
22852
23532
|
/** What a pin and an unpin beat rest at, and their commits name — neither is a step of the workflow. */
|
|
22853
23533
|
const PIN_STATES = {
|
|
23534
|
+
add: `${ENGINE_PREFIX}pin`,
|
|
23535
|
+
remove: `${ENGINE_PREFIX}unpin`
|
|
23536
|
+
};
|
|
23537
|
+
/** The names formats 2 and 3 gave the pin beats, before `gtd:` reserved them. */
|
|
23538
|
+
const LEGACY_PIN_STATES = {
|
|
22854
23539
|
add: "pin",
|
|
22855
23540
|
remove: "unpin"
|
|
22856
23541
|
};
|
|
23542
|
+
const SCOPED_PIN_FORMAT = 4;
|
|
23543
|
+
/** The name a commit in `format` gives a pin beat; format 1 has none. */
|
|
23544
|
+
const pinStateIn = (format, change) => format >= SCOPED_PIN_FORMAT ? PIN_STATES[change] : format >= 2 ? LEGACY_PIN_STATES[change] : void 0;
|
|
22857
23545
|
const isVerdict$1 = (value) => {
|
|
22858
23546
|
if (typeof value !== "object" || value === null) return false;
|
|
22859
23547
|
const v = value;
|
|
@@ -22943,6 +23631,9 @@ const TRAILER_READERS = {
|
|
|
22943
23631
|
"Gtd-Pin": (value, into) => {
|
|
22944
23632
|
if (value === "add" || value === "remove") into.pin ??= value;
|
|
22945
23633
|
},
|
|
23634
|
+
"Gtd-Anchor": (value, into) => {
|
|
23635
|
+
if (HASH_RE.test(value)) into.anchor ??= value;
|
|
23636
|
+
},
|
|
22946
23637
|
"Gtd-Format": (value, into) => {
|
|
22947
23638
|
if (FORMAT_RE.test(value)) into.format ??= Number(value);
|
|
22948
23639
|
},
|
|
@@ -22966,6 +23657,7 @@ const parseCommitMessage = (message) => {
|
|
|
22966
23657
|
kept: void 0,
|
|
22967
23658
|
approved: void 0,
|
|
22968
23659
|
pin: void 0,
|
|
23660
|
+
anchor: void 0,
|
|
22969
23661
|
judge: [],
|
|
22970
23662
|
marks: [],
|
|
22971
23663
|
vars: {},
|
|
@@ -22995,15 +23687,19 @@ const formatCommitMessage = (spec) => {
|
|
|
22995
23687
|
...(spec.marks ?? []).map((m) => `Gtd-Mark: ${m.name}=${m.value}`),
|
|
22996
23688
|
...trailer("Approved", spec.approved && encodeApproval(spec.approved)),
|
|
22997
23689
|
...trailer("Pin", spec.pin),
|
|
23690
|
+
...trailer("Anchor", spec.anchor),
|
|
22998
23691
|
...trailer("Branch", spec.branch),
|
|
22999
23692
|
...trailer("Version", spec.version),
|
|
23000
|
-
`Gtd-Format:
|
|
23693
|
+
`Gtd-Format: 4`
|
|
23001
23694
|
];
|
|
23002
23695
|
return `${formatSubject(spec.actor, spec.to, spec.from)}\n\n${lines.join("\n")}`;
|
|
23003
23696
|
};
|
|
23004
23697
|
//#endregion
|
|
23005
23698
|
//#region src/replay/Replay.ts
|
|
23006
|
-
const
|
|
23699
|
+
const namesUnpin = (commit) => {
|
|
23700
|
+
const unpin = pinStateIn(commit.parsed.format, "remove");
|
|
23701
|
+
return unpin !== void 0 && commit.parsed.parsed?.to === unpin;
|
|
23702
|
+
};
|
|
23007
23703
|
const actorOfKind = (kind) => kind === "run" ? "check" : kind === "agent" ? "agent" : kind === "human" ? "human" : "judge";
|
|
23008
23704
|
const memoryScopeOf = (name) => {
|
|
23009
23705
|
const dot = name.lastIndexOf(".");
|
|
@@ -23091,6 +23787,27 @@ const budgeted = (evidence, budgetBytes) => {
|
|
|
23091
23787
|
};
|
|
23092
23788
|
const sameSkills = (a, b) => (a?.length ?? 0) === (b?.length ?? 0) && (a ?? []).every((skill, i) => skill === b?.[i]);
|
|
23093
23789
|
const samePersona = (a, b) => a.model === b.model && a.system === b.system && sameSkills(a.skills, b.skills) && sameAccess(a.access, b.access);
|
|
23790
|
+
const anchorOf = (parsed) => parsed.pin === "add" ? parsed.anchor : void 0;
|
|
23791
|
+
const isAnchor = (commit) => anchorOf(commit.parsed) !== void 0;
|
|
23792
|
+
/**
|
|
23793
|
+
* The commits every anchor sets aside — those between the commit it names and
|
|
23794
|
+
* itself — or a fault for an anchor naming no commit still read.
|
|
23795
|
+
*/
|
|
23796
|
+
const setAsideBy = (base, all) => {
|
|
23797
|
+
const setAside = /* @__PURE__ */ new Set();
|
|
23798
|
+
if (all.every((c) => anchorOf(c.parsed) === void 0)) return { setAside };
|
|
23799
|
+
let kept = [];
|
|
23800
|
+
for (const commit of all) {
|
|
23801
|
+
const anchor = anchorOf(commit.parsed);
|
|
23802
|
+
if (anchor !== void 0) {
|
|
23803
|
+
const at = anchor === base ? -1 : kept.indexOf(anchor);
|
|
23804
|
+
if (anchor !== base && at === -1) return { fault: `gtd: commit ${short(commit.hash)} anchors the process at ${short(anchor)}, which is no commit replay reads in this episode` };
|
|
23805
|
+
for (const hash of kept.splice(at + 1)) setAside.add(hash);
|
|
23806
|
+
}
|
|
23807
|
+
kept.push(commit.hash);
|
|
23808
|
+
}
|
|
23809
|
+
return { setAside };
|
|
23810
|
+
};
|
|
23094
23811
|
/**
|
|
23095
23812
|
* Replay one episode: run its entry flow, answering each step from the
|
|
23096
23813
|
* episode's commits in order, until a step has no commit left — the rest.
|
|
@@ -23164,19 +23881,39 @@ const withoutPin = (input) => {
|
|
|
23164
23881
|
};
|
|
23165
23882
|
const replay = async (raw) => {
|
|
23166
23883
|
const input = withoutPin(raw);
|
|
23167
|
-
const
|
|
23884
|
+
const all = input.episode.commits.map((c) => ({
|
|
23168
23885
|
...c,
|
|
23169
23886
|
parsed: parseCommitMessage(c.message)
|
|
23170
23887
|
}));
|
|
23888
|
+
const resolved = setAsideBy(input.episode.base.hash, all);
|
|
23889
|
+
if ("fault" in resolved) return {
|
|
23890
|
+
kind: "failed",
|
|
23891
|
+
message: resolved.fault
|
|
23892
|
+
};
|
|
23893
|
+
const setAside = resolved.setAside;
|
|
23894
|
+
const commits = all.filter((c) => !setAside.has(c.hash));
|
|
23895
|
+
const pinned = (raw.episode.commits.at(-1)?.tree ?? raw.episode.base.tree).paths().includes(PIN_PATH);
|
|
23171
23896
|
const treeAt = (hash) => hash === input.episode.base.hash ? input.episode.base.tree : commits.find((c) => c.hash === hash)?.tree ?? (hash === input.start ? input.startTree : void 0);
|
|
23172
23897
|
let cursor = 0;
|
|
23173
23898
|
let pendingUsed = false;
|
|
23174
23899
|
let position = input.episode.base;
|
|
23175
23900
|
const skipPins = () => {
|
|
23176
|
-
while (cursor < commits.length && commits[cursor].parsed.pin !== void 0)
|
|
23901
|
+
while (cursor < commits.length && commits[cursor].parsed.pin !== void 0) {
|
|
23902
|
+
const commit = commits[cursor];
|
|
23903
|
+
cursor++;
|
|
23904
|
+
if (!isAnchor(commit)) continue;
|
|
23905
|
+
position = commit;
|
|
23906
|
+
const to = commit.parsed.parsed?.to;
|
|
23907
|
+
expectedNext = to === void 0 || namesUnpin(commit) ? void 0 : {
|
|
23908
|
+
name: to,
|
|
23909
|
+
hash: commit.hash
|
|
23910
|
+
};
|
|
23911
|
+
anchorPending = expectedNext !== void 0;
|
|
23912
|
+
}
|
|
23177
23913
|
};
|
|
23178
23914
|
let previousPosition = input.episode.base;
|
|
23179
23915
|
let expectedNext;
|
|
23916
|
+
let anchorPending = false;
|
|
23180
23917
|
const occurrences = /* @__PURE__ */ new Map();
|
|
23181
23918
|
const personas = /* @__PURE__ */ new Map();
|
|
23182
23919
|
const scopes = [];
|
|
@@ -23319,14 +24056,18 @@ const replay = async (raw) => {
|
|
|
23319
24056
|
}
|
|
23320
24057
|
return samePersona(seen, identity) ? void 0 : `gtd: "${step.name}" runs with a different model, system prompt, skills or file access than an earlier agent step in its conversation in memory scope "${step.memoryScope || "root"}" — a conversation has one identity`;
|
|
23321
24058
|
};
|
|
23322
|
-
const DIVERGED = "the workflow changed under this process; run `gtd abandon` to start over";
|
|
24059
|
+
const DIVERGED = pinned ? "the workflow changed under this process; run `gtd repin` to continue under it from there, or `gtd abandon` to start over" : "the workflow changed under this process; run `gtd abandon` to start over";
|
|
24060
|
+
const diverged = (message, reached) => ({
|
|
24061
|
+
kind: "divergence",
|
|
24062
|
+
message: `${message} — ${DIVERGED}`,
|
|
24063
|
+
at: position.hash,
|
|
24064
|
+
reached: reached?.name
|
|
24065
|
+
});
|
|
23323
24066
|
const blockerAt = (step) => {
|
|
23324
24067
|
const expected = expectedNext;
|
|
23325
24068
|
expectedNext = void 0;
|
|
23326
|
-
|
|
23327
|
-
|
|
23328
|
-
message: `gtd: commit ${short(expected.hash)} names "${expected.name}" as the next step, but replay reached "${step.name}" — ${DIVERGED}`
|
|
23329
|
-
};
|
|
24069
|
+
anchorPending = false;
|
|
24070
|
+
if (expected !== void 0 && expected.name !== step.name) return diverged(`gtd: commit ${short(expected.hash)} names "${expected.name}" as the next step, but replay reached "${step.name}"`, step);
|
|
23330
24071
|
const error = unknownOptions(step) ?? approveConflict(step) ?? modeWithoutFile(step) ?? checkIdentity(step);
|
|
23331
24072
|
return error === void 0 ? void 0 : {
|
|
23332
24073
|
kind: "failed",
|
|
@@ -23337,33 +24078,30 @@ const replay = async (raw) => {
|
|
|
23337
24078
|
if (step.request.kind !== "human" || step.request.options.approve !== true) return void 0;
|
|
23338
24079
|
const approved = commit.parsed.approved;
|
|
23339
24080
|
if (approved !== void 0 && formatStepId(approved.step) === expected) return void 0;
|
|
23340
|
-
return {
|
|
23341
|
-
kind: "divergence",
|
|
23342
|
-
message: `gtd: commit ${short(commit.hash)} completes approval gate "${expected}" but records no Gtd-Approved for it — ${DIVERGED}`
|
|
23343
|
-
};
|
|
24081
|
+
return diverged(`gtd: commit ${short(commit.hash)} completes approval gate "${expected}" but records no Gtd-Approved for it`, step);
|
|
23344
24082
|
};
|
|
23345
24083
|
const consumeCommit = (step, commit) => {
|
|
23346
24084
|
const recorded = commit.parsed.step;
|
|
23347
24085
|
const expected = formatStepId(step.id);
|
|
23348
24086
|
if (recorded === void 0 || formatStepId(recorded) !== expected) {
|
|
23349
24087
|
const what = recorded === void 0 ? "records no step" : `records step "${formatStepId(recorded)}"`;
|
|
23350
|
-
return finish({
|
|
23351
|
-
kind: "divergence",
|
|
23352
|
-
message: `gtd: commit ${short(commit.hash)} ${what}, but replay expected "${expected}" — ${DIVERGED}`
|
|
23353
|
-
});
|
|
24088
|
+
return finish(diverged(`gtd: commit ${short(commit.hash)} ${what}, but replay expected "${expected}"`, step));
|
|
23354
24089
|
}
|
|
23355
24090
|
const unapproved = missingApproval(step, commit, expected);
|
|
23356
24091
|
if (unapproved !== void 0) return finish(unapproved);
|
|
23357
24092
|
cursor++;
|
|
23358
24093
|
advance(commit);
|
|
24094
|
+
expectNextOf(commit);
|
|
23359
24095
|
skipPins();
|
|
23360
24096
|
noteKept(step, commit);
|
|
24097
|
+
return replyFor(step.request, commit.parsed.judge);
|
|
24098
|
+
};
|
|
24099
|
+
const expectNextOf = (commit) => {
|
|
23361
24100
|
const to = commit.parsed.parsed?.to;
|
|
23362
|
-
if (to !== void 0 &&
|
|
24101
|
+
if (to !== void 0 && !namesUnpin(commit)) expectedNext = {
|
|
23363
24102
|
name: to,
|
|
23364
24103
|
hash: commit.hash
|
|
23365
24104
|
};
|
|
23366
|
-
return replyFor(step.request, commit.parsed.judge);
|
|
23367
24105
|
};
|
|
23368
24106
|
const noteKept = (step, commit) => {
|
|
23369
24107
|
if (commit.parsed.kept === void 0) return;
|
|
@@ -23406,7 +24144,7 @@ const replay = async (raw) => {
|
|
|
23406
24144
|
return landPending(step, input.pending);
|
|
23407
24145
|
};
|
|
23408
24146
|
const requestFault = (request) => {
|
|
23409
|
-
if (
|
|
24147
|
+
if (scoped(request.name).startsWith("gtd:")) return `gtd: step "${scoped(request.name)}": the "${ENGINE_PREFIX}" prefix is reserved for gtd's own beats — rename the step`;
|
|
23410
24148
|
if (request.kind === "run" && typeof request.body !== "string") return `gtd: step "${scoped(request.name)}": a run() body is a shell script string — decide in flow code, then render the script (see checkScript and friends)`;
|
|
23411
24149
|
if (request.kind !== "agent") return void 0;
|
|
23412
24150
|
const name = scoped(request.name);
|
|
@@ -23435,10 +24173,8 @@ const replay = async (raw) => {
|
|
|
23435
24173
|
};
|
|
23436
24174
|
const endOfFlow = (via) => {
|
|
23437
24175
|
skipPins();
|
|
23438
|
-
|
|
23439
|
-
|
|
23440
|
-
message: `gtd: the flow ended, but commit ${short(commits[cursor].hash)} continues the episode — ${DIVERGED}`
|
|
23441
|
-
} : {
|
|
24176
|
+
if (anchorPending && cursor === commits.length) return diverged(`gtd: the flow ended, but commit ${short(expectedNext.hash)} continues the process at "${expectedNext.name}"`, void 0);
|
|
24177
|
+
return cursor < commits.length ? diverged(`gtd: the flow ended, but commit ${short(commits[cursor].hash)} continues the episode`, void 0) : {
|
|
23442
24178
|
kind: "ended",
|
|
23443
24179
|
via,
|
|
23444
24180
|
trace,
|
|
@@ -23448,6 +24184,7 @@ const replay = async (raw) => {
|
|
|
23448
24184
|
};
|
|
23449
24185
|
const changesSince = (hash) => {
|
|
23450
24186
|
const tree = treeAt(hash);
|
|
24187
|
+
if (setAside.has(hash)) throw new Error(`gtd: changesSince(${hash}): a repin set ${short(hash)} aside — pass a hash this run read from head() or start()`);
|
|
23451
24188
|
if (tree === void 0) throw new Error(`gtd: changesSince(${hash}): ${hash} is not the episode base or one of its commits — pass a hash this run read from head() or start(), not one captured earlier, read from state, or from another branch`);
|
|
23452
24189
|
return changesBetween(tree, position.tree);
|
|
23453
24190
|
};
|
|
@@ -23669,17 +24406,17 @@ const LEGACY_RC_FILES = [
|
|
|
23669
24406
|
"gtd.config.yaml"
|
|
23670
24407
|
];
|
|
23671
24408
|
/** The directories searched for a leftover rc file: the walk, plus `home` when the walk misses it. */
|
|
23672
|
-
const legacyRcDirs = (root, home) => {
|
|
23673
|
-
const chain = walkUp(root, home);
|
|
24409
|
+
const legacyRcDirs = (path, root, home) => {
|
|
24410
|
+
const chain = walkUp(path, root, home);
|
|
23674
24411
|
return chain.includes(home) ? chain : [...chain, home];
|
|
23675
24412
|
};
|
|
23676
|
-
const legacyRcFileAt = (path) => {
|
|
24413
|
+
const legacyRcFileAt = (io, path, uid) => {
|
|
23677
24414
|
try {
|
|
23678
|
-
const stat =
|
|
23679
|
-
if (
|
|
24415
|
+
const stat = io.stat(path);
|
|
24416
|
+
if (stat?.type !== "file") return void 0;
|
|
23680
24417
|
return {
|
|
23681
24418
|
path,
|
|
23682
|
-
owned:
|
|
24419
|
+
owned: uid === void 0 || stat.uid === uid
|
|
23683
24420
|
};
|
|
23684
24421
|
} catch {
|
|
23685
24422
|
return;
|
|
@@ -23693,30 +24430,33 @@ const legacyRcFileAt = (path) => {
|
|
|
23693
24430
|
* shared implementation, not a second copy in `src/twins/Layers.ts` that
|
|
23694
24431
|
* could silently diverge.
|
|
23695
24432
|
*/
|
|
23696
|
-
const walkUp = (from, home) => {
|
|
24433
|
+
const walkUp = (path, from, home) => {
|
|
23697
24434
|
const chain = [];
|
|
23698
24435
|
let dir = from;
|
|
23699
24436
|
while (true) {
|
|
23700
24437
|
chain.push(dir);
|
|
23701
24438
|
if (dir === home) break;
|
|
23702
|
-
const parent = dirname(dir);
|
|
24439
|
+
const parent = path.dirname(dir);
|
|
23703
24440
|
if (parent === dir) break;
|
|
23704
24441
|
dir = parent;
|
|
23705
24442
|
}
|
|
23706
24443
|
return chain;
|
|
23707
24444
|
};
|
|
23708
|
-
const
|
|
23709
|
-
|
|
23710
|
-
|
|
23711
|
-
|
|
23712
|
-
const
|
|
23713
|
-
|
|
23714
|
-
filepath
|
|
23715
|
-
|
|
23716
|
-
|
|
23717
|
-
|
|
23718
|
-
|
|
23719
|
-
|
|
24445
|
+
const liveDiscovery = (io, path, uid) => ({
|
|
24446
|
+
legacyRcFiles: (root, home) => sync(() => legacyRcDirs(path, root, home).flatMap((dir) => LEGACY_RC_FILES.flatMap((name) => legacyRcFileAt(io, path.join(dir, name), uid) ?? []))),
|
|
24447
|
+
workflowModule: (root, home) => try_({
|
|
24448
|
+
try: () => {
|
|
24449
|
+
for (const dir of walkUp(path, root, home)) {
|
|
24450
|
+
const filepath = path.join(dir, WORKFLOW_MODULE);
|
|
24451
|
+
const source = io.readText(filepath);
|
|
24452
|
+
if (source !== void 0) return {
|
|
24453
|
+
filepath,
|
|
24454
|
+
source
|
|
24455
|
+
};
|
|
24456
|
+
}
|
|
24457
|
+
},
|
|
24458
|
+
catch: (e) => e instanceof Error ? e : new Error(String(e))
|
|
24459
|
+
})
|
|
23720
24460
|
});
|
|
23721
24461
|
/**
|
|
23722
24462
|
* The discovery seam — a `Context.Service`, not part of `WorkflowFiles` (that
|
|
@@ -23729,31 +24469,29 @@ const workflowModule = (root, home) => try_({
|
|
|
23729
24469
|
* each have a production and a fake layer behind one service.
|
|
23730
24470
|
*/
|
|
23731
24471
|
var ConfigDiscovery = class ConfigDiscovery extends Service()("ConfigDiscovery") {
|
|
23732
|
-
static Live =
|
|
23733
|
-
|
|
23734
|
-
|
|
23735
|
-
});
|
|
24472
|
+
static Live = effect(ConfigDiscovery, gen(function* () {
|
|
24473
|
+
return liveDiscovery(nodeSyncIo, yield* Path$1, (yield* Host).uid);
|
|
24474
|
+
}));
|
|
23736
24475
|
};
|
|
23737
24476
|
//#endregion
|
|
23738
24477
|
//#region src/workflow/fingerprint.ts
|
|
23739
24478
|
const HASH_LENGTH = 16;
|
|
23740
24479
|
const CONTROL_CHARACTER = /[\x00-\x1f\x7f-\x9f]/;
|
|
23741
|
-
const
|
|
23742
|
-
const realOr = (path) => {
|
|
24480
|
+
const realOr = (io, path) => {
|
|
23743
24481
|
try {
|
|
23744
|
-
return
|
|
24482
|
+
return io.realPath(path);
|
|
23745
24483
|
} catch {
|
|
23746
24484
|
return path;
|
|
23747
24485
|
}
|
|
23748
24486
|
};
|
|
23749
|
-
const configFingerprint = (files, root, read) => {
|
|
24487
|
+
const configFingerprint = (io, paths, files, root, read) => {
|
|
23750
24488
|
const fingerprint = {};
|
|
23751
|
-
const realRoot = realOr(root);
|
|
24489
|
+
const realRoot = realOr(io, root);
|
|
23752
24490
|
for (const file of files) {
|
|
23753
|
-
const path = relative(realRoot, realOr(file)).split(sep).join("/");
|
|
24491
|
+
const path = paths.relative(realRoot, realOr(io, file)).split(paths.sep).join("/");
|
|
23754
24492
|
if (CONTROL_CHARACTER.test(path)) continue;
|
|
23755
24493
|
const content = read(file);
|
|
23756
|
-
if (content !== void 0) fingerprint[path] =
|
|
24494
|
+
if (content !== void 0) fingerprint[path] = io.hash("sha256", content).slice(0, HASH_LENGTH);
|
|
23757
24495
|
}
|
|
23758
24496
|
return fingerprint;
|
|
23759
24497
|
};
|
|
@@ -23847,19 +24585,20 @@ const configModule = gen(function* () {
|
|
|
23847
24585
|
*/
|
|
23848
24586
|
const load = gen(function* () {
|
|
23849
24587
|
const host = yield* Host;
|
|
24588
|
+
const paths = yield* Path$1;
|
|
23850
24589
|
yield* refuseLegacyRc;
|
|
23851
24590
|
const pinned = pinnedWorkflow();
|
|
23852
24591
|
const module = pinned === void 0 ? yield* configModule : {
|
|
23853
|
-
filepath: resolve(host.root, pinned.origin),
|
|
24592
|
+
filepath: paths.resolve(host.root, pinned.origin),
|
|
23854
24593
|
source: ""
|
|
23855
24594
|
};
|
|
23856
24595
|
yield* (yield* Narrator).narrate(`config: ${pinned === void 0 ? "module" : "pinned module"} ${module.filepath}`);
|
|
23857
24596
|
const shipped = pinned === void 0 ? yield* tryPromise({
|
|
23858
|
-
try: shippedModules,
|
|
24597
|
+
try: () => shippedModules(paths),
|
|
23859
24598
|
catch: (e) => new GtdError(`gtd: ${e instanceof Error ? e.message : String(e)}`)
|
|
23860
24599
|
}) : void 0;
|
|
23861
24600
|
const loaded = yield* tryPromise({
|
|
23862
|
-
try: async () => loadWorkflows(module.filepath, shipped === void 0 ? runPinned(pinned, module.filepath, host.root) : await evaluate(module, shipped)),
|
|
24601
|
+
try: async () => loadWorkflows(module.filepath, shipped === void 0 ? runPinned(paths, pinned, module.filepath, host.root) : await evaluate(paths, module, shipped)),
|
|
23863
24602
|
catch: (e) => new GtdError(`gtd: config:\n - ${module.filepath}: ${e instanceof Error ? e.message : String(e)}`)
|
|
23864
24603
|
});
|
|
23865
24604
|
const compiled = loaded.config;
|
|
@@ -23881,13 +24620,13 @@ const load = gen(function* () {
|
|
|
23881
24620
|
initial
|
|
23882
24621
|
});
|
|
23883
24622
|
const names = [...loaded.catalogue.keys()].sort();
|
|
23884
|
-
const resolve
|
|
24623
|
+
const resolve = (name, entry) => ({
|
|
23885
24624
|
name,
|
|
23886
24625
|
def: define(entry),
|
|
23887
24626
|
vars: { ...entry.defaults },
|
|
23888
24627
|
env: { ...entry.envDefaults }
|
|
23889
24628
|
});
|
|
23890
|
-
const defaultWorkflow = resolve
|
|
24629
|
+
const defaultWorkflow = resolve(loaded.default.name, loaded.default);
|
|
23891
24630
|
return {
|
|
23892
24631
|
workflow: defaultWorkflow.def,
|
|
23893
24632
|
defaultWorkflow,
|
|
@@ -23895,41 +24634,41 @@ const load = gen(function* () {
|
|
|
23895
24634
|
doors: loaded.doors,
|
|
23896
24635
|
workflowNamed: (name) => {
|
|
23897
24636
|
const entry = loaded.catalogue.get(name);
|
|
23898
|
-
return entry === void 0 ? void 0 : resolve
|
|
24637
|
+
return entry === void 0 ? void 0 : resolve(name, entry);
|
|
23899
24638
|
},
|
|
23900
24639
|
...compiled.ui !== void 0 ? { ui: compiled.ui } : {},
|
|
23901
24640
|
warnings: diagnostics.filter((d) => d.severity === "warning"),
|
|
23902
|
-
fingerprint: pinned?.fingerprint ?? configFingerprint(loaded.file.dependencyFiles, host.root, module.read ?? readFromDisk),
|
|
23903
|
-
drift: pinned === void 0 ? [] : changedConfigFiles(pinned.fingerprint, configFingerprint(Object.keys(pinned.fingerprint).map((file) => resolve(host.root, file)), host.root, readFromDisk))
|
|
24641
|
+
fingerprint: pinned?.fingerprint ?? configFingerprint(nodeSyncIo, paths, loaded.file.dependencyFiles, host.root, module.read ?? readFromDisk),
|
|
24642
|
+
drift: pinned === void 0 ? [] : changedConfigFiles(pinned.fingerprint, configFingerprint(nodeSyncIo, paths, Object.keys(pinned.fingerprint).map((file) => paths.resolve(host.root, file)), host.root, readFromDisk))
|
|
23904
24643
|
};
|
|
23905
24644
|
});
|
|
23906
24645
|
const readFromDisk = (path) => {
|
|
23907
24646
|
try {
|
|
23908
|
-
return
|
|
24647
|
+
return nodeSyncIo.readText(path);
|
|
23909
24648
|
} catch {
|
|
23910
24649
|
return;
|
|
23911
24650
|
}
|
|
23912
24651
|
};
|
|
23913
24652
|
const PACKAGE = "@pmelab/gtd";
|
|
23914
24653
|
const FLOWS = `${PACKAGE}/flows`;
|
|
23915
|
-
const ownPackage = () => {
|
|
23916
|
-
const start = dirname(
|
|
23917
|
-
for (let dir = start;; dir = dirname(dir)) {
|
|
23918
|
-
const
|
|
23919
|
-
if (
|
|
23920
|
-
const pkg = JSON.parse(
|
|
24654
|
+
const ownPackage = (paths) => {
|
|
24655
|
+
const start = paths.dirname(runSync$1(paths.fromFileUrl(new URL(import.meta.url))));
|
|
24656
|
+
for (let dir = start;; dir = paths.dirname(dir)) {
|
|
24657
|
+
const source = readFromDisk(paths.join(dir, "package.json"));
|
|
24658
|
+
if (source !== void 0) {
|
|
24659
|
+
const pkg = JSON.parse(source);
|
|
23921
24660
|
if (pkg.name === PACKAGE) return {
|
|
23922
24661
|
root: dir,
|
|
23923
24662
|
pkg
|
|
23924
24663
|
};
|
|
23925
24664
|
}
|
|
23926
|
-
if (dirname(dir) === dir) throw new Error(`no ${PACKAGE} package.json above ${start}`);
|
|
24665
|
+
if (paths.dirname(dir) === dir) throw new Error(`no ${PACKAGE} package.json above ${start}`);
|
|
23927
24666
|
}
|
|
23928
24667
|
};
|
|
23929
24668
|
let shippedMemo;
|
|
23930
|
-
const shippedModules = () => shippedMemo ??= (async () => {
|
|
24669
|
+
const shippedModules = (paths) => shippedMemo ??= (async () => {
|
|
23931
24670
|
const fromSource = import.meta.url.endsWith(".ts");
|
|
23932
|
-
const files = subpathFiles(fromSource ? "source" : "default");
|
|
24671
|
+
const files = subpathFiles(paths, fromSource ? "source" : "default");
|
|
23933
24672
|
const modules = { [FLOWS]: flows_exports };
|
|
23934
24673
|
const missing = {};
|
|
23935
24674
|
if (process.features.require_module && !fromSource) return {
|
|
@@ -23937,7 +24676,7 @@ const shippedModules = () => shippedMemo ??= (async () => {
|
|
|
23937
24676
|
missing
|
|
23938
24677
|
};
|
|
23939
24678
|
for (const [id, path] of files) try {
|
|
23940
|
-
modules[id] = await import(
|
|
24679
|
+
modules[id] = await import(fileUrl(paths, path));
|
|
23941
24680
|
} catch {
|
|
23942
24681
|
missing[id] = path;
|
|
23943
24682
|
}
|
|
@@ -23947,13 +24686,13 @@ const shippedModules = () => shippedMemo ??= (async () => {
|
|
|
23947
24686
|
};
|
|
23948
24687
|
})();
|
|
23949
24688
|
/** The file each subpath a config may import serves: all but `flows`, the engine's own, and the testkit's. */
|
|
23950
|
-
const subpathFiles = (condition) => {
|
|
23951
|
-
const { root, pkg } = ownPackage();
|
|
24689
|
+
const subpathFiles = (paths, condition) => {
|
|
24690
|
+
const { root, pkg } = ownPackage(paths);
|
|
23952
24691
|
const files = /* @__PURE__ */ new Map();
|
|
23953
24692
|
for (const [subpath, target] of Object.entries(pkg.exports)) {
|
|
23954
24693
|
const file = typeof target === "string" ? target : target[condition];
|
|
23955
24694
|
if (subpath === "./flows" || subpath.startsWith("./testkit") || file === void 0) continue;
|
|
23956
|
-
files.set(`${PACKAGE}/${subpath.replace(/^\.\//, "")}`, join(root, file));
|
|
24695
|
+
files.set(`${PACKAGE}/${subpath.replace(/^\.\//, "")}`, paths.join(root, file));
|
|
23957
24696
|
}
|
|
23958
24697
|
return files;
|
|
23959
24698
|
};
|
|
@@ -24147,16 +24886,16 @@ const shadowingDoors = (doors, catalogue, origin) => [...doors].filter(([name, d
|
|
|
24147
24886
|
path: ["doors", name],
|
|
24148
24887
|
origin
|
|
24149
24888
|
}));
|
|
24150
|
-
const moduleCandidates = (path) => [
|
|
24889
|
+
const moduleCandidates = (paths, path) => [
|
|
24151
24890
|
path,
|
|
24152
24891
|
path.replace(/\.js$/, ".ts"),
|
|
24153
24892
|
path.replace(/\.mjs$/, ".mts"),
|
|
24154
24893
|
`${path}.ts`,
|
|
24155
|
-
join(path, "index.ts")
|
|
24894
|
+
paths.join(path, "index.ts")
|
|
24156
24895
|
];
|
|
24157
|
-
const execute = (chunk, filepath, require) => {
|
|
24896
|
+
const execute = (paths, chunk, filepath, require) => {
|
|
24158
24897
|
const cjs = { exports: {} };
|
|
24159
|
-
chunk(cjs.exports, require, cjs, filepath, dirname(filepath));
|
|
24898
|
+
chunk(cjs.exports, require, cjs, filepath, paths.dirname(filepath));
|
|
24160
24899
|
return cjs.exports;
|
|
24161
24900
|
};
|
|
24162
24901
|
/**
|
|
@@ -24168,40 +24907,40 @@ const execute = (chunk, filepath, require) => {
|
|
|
24168
24907
|
* it, as Node would. `@pmelab/gtd/*` is the running gtd's own copy, and
|
|
24169
24908
|
* `files` leaves out every package file.
|
|
24170
24909
|
*/
|
|
24171
|
-
const evaluate = async (module, shipped) => {
|
|
24172
|
-
const { code, files } = await bundleConfig(module, {
|
|
24910
|
+
const evaluate = async (paths, module, shipped) => {
|
|
24911
|
+
const { code, files } = await bundleConfig(paths, module, {
|
|
24173
24912
|
kind: "evaluate",
|
|
24174
24913
|
shipped
|
|
24175
24914
|
});
|
|
24176
24915
|
const fromConfig = createRequire(module.filepath);
|
|
24177
24916
|
const require = Object.assign((id) => id in shipped.modules ? shipped.modules[id] : fromConfig(id in shipped.missing ? shipped.missing[id] : id), { resolve: fromConfig.resolve });
|
|
24178
|
-
const chunk =
|
|
24917
|
+
const chunk = compileScript(`(function (exports, require, module, __filename, __dirname) {${code}\n})`, module.filepath);
|
|
24179
24918
|
return {
|
|
24180
|
-
exports: execute(chunk, module.filepath, require),
|
|
24919
|
+
exports: execute(paths, chunk, module.filepath, require),
|
|
24181
24920
|
files
|
|
24182
24921
|
};
|
|
24183
24922
|
};
|
|
24184
24923
|
const pinnedWorkflow = () => globalThis[PIN_KEY];
|
|
24185
|
-
const runPinned = (pinned, filepath, root) => ({
|
|
24186
|
-
exports: execute(pinned.run, filepath, (id) => id === FLOWS ? flows_exports : createRequire(import.meta.url)(id)),
|
|
24187
|
-
files: Object.keys(pinned.fingerprint).map((file) => resolve(root, file))
|
|
24924
|
+
const runPinned = (paths, pinned, filepath, root) => ({
|
|
24925
|
+
exports: execute(paths, pinned.run, filepath, (id) => id === FLOWS ? flows_exports : createRequire(import.meta.url)(id)),
|
|
24926
|
+
files: Object.keys(pinned.fingerprint).map((file) => paths.resolve(root, file))
|
|
24188
24927
|
});
|
|
24189
24928
|
/**
|
|
24190
24929
|
* The statement that registers `module`'s workflow for a pin: the config and
|
|
24191
24930
|
* everything it imports — npm packages and gtd's own subpaths included — in
|
|
24192
24931
|
* one function, so the engine runs it without a config, a package or rolldown.
|
|
24193
24932
|
*/
|
|
24194
|
-
const pinnedWorkflowSource = async (module, root, version) => {
|
|
24195
|
-
const gtd =
|
|
24196
|
-
const { code, files } = await bundleConfig(module, {
|
|
24933
|
+
const pinnedWorkflowSource = async (paths, module, root, version) => {
|
|
24934
|
+
const gtd = nodeSyncIo.realPath(ownPackage(paths).root) + paths.sep;
|
|
24935
|
+
const { code, files } = await bundleConfig(paths, module, {
|
|
24197
24936
|
kind: "pin",
|
|
24198
|
-
shipped: subpathFiles(import.meta.url.endsWith(".ts") ? "source" : "default")
|
|
24937
|
+
shipped: subpathFiles(paths, import.meta.url.endsWith(".ts") ? "source" : "default")
|
|
24199
24938
|
});
|
|
24200
24939
|
const own = (file) => file.startsWith(gtd) && !module.filepath.startsWith(gtd);
|
|
24201
24940
|
const registered = {
|
|
24202
24941
|
version,
|
|
24203
|
-
origin: relative(root, module.filepath),
|
|
24204
|
-
fingerprint: configFingerprint(files.filter((file) => !own(file)), root, module.read ?? readFromDisk)
|
|
24942
|
+
origin: paths.relative(root, module.filepath),
|
|
24943
|
+
fingerprint: configFingerprint(nodeSyncIo, paths, files.filter((file) => !own(file)), root, module.read ?? readFromDisk)
|
|
24205
24944
|
};
|
|
24206
24945
|
return `globalThis[Symbol.for(${JSON.stringify(PIN_KEY.description)})] = { ...${JSON.stringify(registered)}, run: function (exports, require, module, __filename, __dirname) {\n${code}\n} };\n`;
|
|
24207
24946
|
};
|
|
@@ -24215,11 +24954,11 @@ const shippedImport = (source, mode) => {
|
|
|
24215
24954
|
external: true
|
|
24216
24955
|
} : void 0;
|
|
24217
24956
|
};
|
|
24218
|
-
const bundleConfig = async (module, mode) => {
|
|
24957
|
+
const bundleConfig = async (paths, module, mode) => {
|
|
24219
24958
|
const { read } = module;
|
|
24220
24959
|
const served = /* @__PURE__ */ new Set();
|
|
24221
24960
|
const fromRead = (path) => {
|
|
24222
|
-
const hit = moduleCandidates(path).find((candidate) => read?.(candidate) !== void 0);
|
|
24961
|
+
const hit = moduleCandidates(paths, path).find((candidate) => read?.(candidate) !== void 0);
|
|
24223
24962
|
if (hit === void 0) return null;
|
|
24224
24963
|
served.add(hit);
|
|
24225
24964
|
return hit;
|
|
@@ -24233,18 +24972,18 @@ const bundleConfig = async (module, mode) => {
|
|
|
24233
24972
|
if (importer === void 0) return source === module.filepath ? source : null;
|
|
24234
24973
|
const shipped = shippedImport(source, mode);
|
|
24235
24974
|
if (shipped !== void 0) return shipped;
|
|
24236
|
-
if (/^\.\.?(\/|$)/.test(source)) return fromRead(join(dirname(importer), source));
|
|
24237
|
-
if (!isPackage(source)) return null;
|
|
24975
|
+
if (/^\.\.?(\/|$)/.test(source)) return fromRead(paths.join(paths.dirname(importer), source));
|
|
24976
|
+
if (!isPackage(paths, source)) return null;
|
|
24238
24977
|
const resolved = await this.resolve(source, importer, { skipSelf: true });
|
|
24239
|
-
return mode.kind === "pin" ? resolved : packageImport(source, resolved);
|
|
24978
|
+
return mode.kind === "pin" ? resolved : packageImport(paths, source, resolved);
|
|
24240
24979
|
},
|
|
24241
24980
|
load(id) {
|
|
24242
|
-
if (mode.kind === "pin" && isNativeAddon(id)) throw new Error(nativeAddon(id));
|
|
24981
|
+
if (mode.kind === "pin" && isNativeAddon(paths, id)) throw new Error(nativeAddon(paths, id));
|
|
24243
24982
|
if (id === module.filepath) return module.source;
|
|
24244
24983
|
return served.has(id) ? read(id) : null;
|
|
24245
24984
|
},
|
|
24246
24985
|
transform(code, id) {
|
|
24247
|
-
return code.replace(/\bimport\.meta\.(url|filename|dirname)\b/g, (_, key) => JSON.stringify(key === "url" ?
|
|
24986
|
+
return code.replace(/\bimport\.meta\.(url|filename|dirname)\b/g, (_, key) => JSON.stringify(key === "url" ? fileUrl(paths, id) : key === "filename" ? id : paths.dirname(id)));
|
|
24248
24987
|
}
|
|
24249
24988
|
}],
|
|
24250
24989
|
external: (id) => isBuiltin(id),
|
|
@@ -24274,34 +25013,42 @@ const bundleConfig = async (module, mode) => {
|
|
|
24274
25013
|
await bundle.close();
|
|
24275
25014
|
}
|
|
24276
25015
|
};
|
|
24277
|
-
const isNativeAddon = (id) => {
|
|
25016
|
+
const isNativeAddon = (paths, id) => {
|
|
24278
25017
|
if (id.endsWith(".node")) return true;
|
|
25018
|
+
const { sep } = paths;
|
|
24279
25019
|
const at = id.lastIndexOf(`node_modules${sep}`);
|
|
24280
25020
|
if (at < 0) return false;
|
|
24281
25021
|
const rest = id.slice(at + `node_modules${sep}`.length).split(sep);
|
|
24282
|
-
|
|
25022
|
+
const root = id.slice(0, at) + ["node_modules", ...rest.slice(0, rest[0].startsWith("@") ? 2 : 1)].join(sep);
|
|
25023
|
+
try {
|
|
25024
|
+
return nodeSyncIo.stat(paths.join(root, "binding.gyp")) !== void 0;
|
|
25025
|
+
} catch {
|
|
25026
|
+
return false;
|
|
25027
|
+
}
|
|
24283
25028
|
};
|
|
24284
|
-
const nativeAddon = (path) => {
|
|
25029
|
+
const nativeAddon = (paths, path) => {
|
|
25030
|
+
const { sep } = paths;
|
|
24285
25031
|
const at = path.lastIndexOf(`node_modules${sep}`);
|
|
24286
25032
|
const rest = at < 0 ? [] : path.slice(at + `node_modules${sep}`.length).split(sep);
|
|
24287
25033
|
const name = rest[0]?.startsWith("@") ? rest.slice(0, 2).join("/") : rest[0];
|
|
24288
25034
|
return `${name === void 0 ? path : `package "${name}"`} loads a native addon (${path}), which a pin cannot carry`;
|
|
24289
25035
|
};
|
|
24290
|
-
const packageImport = (source, resolved) => resolved !== null && isEsModule(resolved.id) ? resolved.id : {
|
|
25036
|
+
const packageImport = (paths, source, resolved) => resolved !== null && isEsModule(paths, resolved.id) ? resolved.id : {
|
|
24291
25037
|
id: resolved?.id ?? source,
|
|
24292
25038
|
external: true
|
|
24293
25039
|
};
|
|
24294
|
-
const isEsModule = (path) => {
|
|
25040
|
+
const isEsModule = (paths, path) => {
|
|
24295
25041
|
if (/\.(mjs|[cm]?tsx?)$/.test(path)) return true;
|
|
24296
25042
|
if (!/\.jsx?$/.test(path)) return false;
|
|
24297
|
-
for (let dir = dirname(path); dirname(dir) !== dir; dir = dirname(dir)) {
|
|
24298
|
-
const
|
|
24299
|
-
if (
|
|
25043
|
+
for (let dir = paths.dirname(path); paths.dirname(dir) !== dir; dir = paths.dirname(dir)) {
|
|
25044
|
+
const source = readFromDisk(paths.join(dir, "package.json"));
|
|
25045
|
+
if (source !== void 0) return JSON.parse(source).type === "module";
|
|
24300
25046
|
}
|
|
24301
25047
|
return false;
|
|
24302
25048
|
};
|
|
24303
25049
|
const inPackage = (path) => /[\\/]node_modules[\\/]/.test(path);
|
|
24304
|
-
const isPackage = (specifier) => !isAbsolute(specifier) && !isBuiltin(specifier);
|
|
25050
|
+
const isPackage = (paths, specifier) => !paths.isAbsolute(specifier) && !isBuiltin(specifier);
|
|
25051
|
+
const fileUrl = (paths, path) => runSync$1(paths.toFileUrl(path)).href;
|
|
24305
25052
|
const bundleError = (e) => {
|
|
24306
25053
|
const errors = e?.errors;
|
|
24307
25054
|
if (errors === void 0) return e;
|
|
@@ -24558,11 +25305,15 @@ var EngineCore = class EngineCore extends Service()("EngineCore") {
|
|
|
24558
25305
|
static layer = (path) => succeed$2(EngineCore, { source: suspend$2(() => {
|
|
24559
25306
|
const file = path();
|
|
24560
25307
|
return try_({
|
|
24561
|
-
try: () =>
|
|
25308
|
+
try: () => {
|
|
25309
|
+
const source = nodeSyncIo.readText(file);
|
|
25310
|
+
if (source === void 0) throw new Error("absent");
|
|
25311
|
+
return source;
|
|
25312
|
+
},
|
|
24562
25313
|
catch: () => new GtdError(`gtd bundle: no engine at ${file} — build gtd first`)
|
|
24563
25314
|
});
|
|
24564
25315
|
}) });
|
|
24565
|
-
static Live = EngineCore.layer(() => join(ownPackage().root, "dist", "gtd.core.mjs"));
|
|
25316
|
+
static Live = unwrap$6(map(Path$1, (path) => EngineCore.layer(() => path.join(ownPackage(path).root, "dist", "gtd.core.mjs"))));
|
|
24566
25317
|
};
|
|
24567
25318
|
/**
|
|
24568
25319
|
* A pin: one plain-text header line a launcher reads without unpacking, then
|
|
@@ -24571,11 +25322,12 @@ var EngineCore = class EngineCore extends Service()("EngineCore") {
|
|
|
24571
25322
|
*/
|
|
24572
25323
|
const pin = (module, root, version) => gen(function* () {
|
|
24573
25324
|
const core = yield* (yield* EngineCore).source;
|
|
25325
|
+
const path = yield* Path$1;
|
|
24574
25326
|
const source = (yield* tryPromise({
|
|
24575
|
-
try: () => pinnedWorkflowSource(module, root, version),
|
|
25327
|
+
try: () => pinnedWorkflowSource(path, module, root, version),
|
|
24576
25328
|
catch: (e) => new GtdError(`gtd bundle: ${module.filepath}: ${e instanceof Error ? e.message : String(e)}`)
|
|
24577
25329
|
})) + core.replace(/^#!.*\n/, "");
|
|
24578
|
-
return Buffer.concat([Buffer.from(`gtdpin 1 gtd ${version}\n`),
|
|
25330
|
+
return Buffer.concat([Buffer.from(`gtdpin 1 gtd ${version}\n`), gzip(source)]);
|
|
24579
25331
|
});
|
|
24580
25332
|
//#endregion
|
|
24581
25333
|
//#region src/emit/GitScript.ts
|
|
@@ -25030,7 +25782,7 @@ const modeCommandSteps = (resolved, file) => {
|
|
|
25030
25782
|
return steps;
|
|
25031
25783
|
};
|
|
25032
25784
|
/** `<Host.scratchDir>/gtd-mode-sample-<mode>-<pid>.md` — an absolute literal baked in at emit time, never a shell variable. `<pid>` avoids collisions between concurrent `gtd` processes. Never a `/tmp` literal or `mktemp` (`tests/tooling/no-tmp-assumption.test.ts` scans for both). */
|
|
25033
|
-
const scratchSamplePath = (scratchDir, mode) => join(scratchDir, `gtd-mode-sample-${mode}-${
|
|
25785
|
+
const scratchSamplePath = (path, { scratchDir, pid }, mode) => path.join(scratchDir, `gtd-mode-sample-${mode}-${pid}.md`);
|
|
25034
25786
|
/**
|
|
25035
25787
|
* The contradiction round-trip/skip-notice steps for `resolved`'s `mode:`.
|
|
25036
25788
|
* No `formatCommand` means nothing to round-trip: empty. Otherwise: a live
|
|
@@ -25043,8 +25795,7 @@ const modeContradictionSteps = (resolved) => gen(function* () {
|
|
|
25043
25795
|
if (resolved.formatCommand === void 0) return [];
|
|
25044
25796
|
const { capabilities } = resolved;
|
|
25045
25797
|
if (capabilities.format !== void 0 && capabilities.liveValidate !== void 0) {
|
|
25046
|
-
const
|
|
25047
|
-
const samplePath = scratchSamplePath(scratchDir, resolved.mode);
|
|
25798
|
+
const samplePath = scratchSamplePath(yield* Path$1, yield* Host, resolved.mode);
|
|
25048
25799
|
const formatCommand = withFileVar(resolved.formatCommand, samplePath);
|
|
25049
25800
|
return [{
|
|
25050
25801
|
kind: "command",
|
|
@@ -25378,11 +26129,21 @@ const approvalEntryOf = (hash, message) => message.approved === void 0 ? [] : [{
|
|
|
25378
26129
|
email: message.approved.email,
|
|
25379
26130
|
commit: hash
|
|
25380
26131
|
}];
|
|
26132
|
+
const setAsideIn = (history, location) => {
|
|
26133
|
+
const anchored = setAsideBy(hashAt(history, location.baseIndex) ?? EMPTY_TREE$1, history.slice(location.baseIndex + 1).map((c) => ({
|
|
26134
|
+
hash: c.hash,
|
|
26135
|
+
parsed: parseCommitMessage(c.message)
|
|
26136
|
+
})));
|
|
26137
|
+
return "setAside" in anchored ? anchored.setAside : /* @__PURE__ */ new Set();
|
|
26138
|
+
};
|
|
25381
26139
|
const buildRun = (history, location, closingHash) => {
|
|
25382
26140
|
const allProcessCommits = history.slice(location.processStart);
|
|
25383
26141
|
const allParsed = allProcessCommits.map((c) => parseCommitMessage(c.message));
|
|
25384
26142
|
const processCommits = allProcessCommits.filter((_, i) => allParsed[i].pin !== "add");
|
|
25385
26143
|
const parsed = allParsed.filter((m) => m.pin !== "add");
|
|
26144
|
+
const setAside = setAsideIn(history, location);
|
|
26145
|
+
const answering = parsed.filter((_, i) => !setAside.has(processCommits[i].hash));
|
|
26146
|
+
const answeringCommits = processCommits.filter((c) => !setAside.has(c.hash));
|
|
25386
26147
|
const first = parsed[0];
|
|
25387
26148
|
const startParentHash = hashAt(history, location.processStart - 1) ?? EMPTY_TREE$1;
|
|
25388
26149
|
const head = history[history.length - 1];
|
|
@@ -25394,10 +26155,10 @@ const buildRun = (history, location, closingHash) => {
|
|
|
25394
26155
|
diffBase: first?.reviewBase ?? startParentHash,
|
|
25395
26156
|
trace: processCommits.map((c, i) => traceEntryOf(c.hash, parsed[i])),
|
|
25396
26157
|
costEntries: parsed.flatMap((m, i) => costEntriesOf(processCommits[i].hash, m)),
|
|
25397
|
-
judgeVerdicts:
|
|
26158
|
+
judgeVerdicts: answering.flatMap((m) => m.judge),
|
|
25398
26159
|
pinCommitted: allParsed.some((m) => m.pin === "add"),
|
|
25399
|
-
marks:
|
|
25400
|
-
approvals:
|
|
26160
|
+
marks: answering.flatMap((m) => m.marks),
|
|
26161
|
+
approvals: answeringCommits.flatMap((c, i) => approvalEntryOf(c.hash, answering[i])),
|
|
25401
26162
|
pinnedVars: { ...first?.vars },
|
|
25402
26163
|
headTurn: headTurnOf(head),
|
|
25403
26164
|
closingHash,
|
|
@@ -25434,7 +26195,7 @@ const subjectIsBoundary = (def, subject) => {
|
|
|
25434
26195
|
const parsed = parseCommitMessage(subject).parsed;
|
|
25435
26196
|
if (parsed === void 0 || !ACTORS.has(parsed.actor)) return true;
|
|
25436
26197
|
if (parsed.actor === "human" && parsed.from === void 0) return false;
|
|
25437
|
-
if (parsed.actor === "check" && parsed.from === PIN_STATES.add) return false;
|
|
26198
|
+
if (parsed.actor === "check" && (parsed.from === PIN_STATES.add || parsed.from === LEGACY_PIN_STATES.add)) return false;
|
|
25438
26199
|
return parsed.to === def.initial;
|
|
25439
26200
|
};
|
|
25440
26201
|
/** The last page index (walking newest→oldest, same direction as `locateEpisode`) whose subject is a boundary, or `undefined` if none in `page`. */
|
|
@@ -25826,6 +26587,43 @@ const restAt = (ref, startOverrides = {}) => gen(function* () {
|
|
|
25826
26587
|
};
|
|
25827
26588
|
});
|
|
25828
26589
|
const currentRest = restAt(void 0);
|
|
26590
|
+
/**
|
|
26591
|
+
* Where `gtd repin` anchors a pinned process the live workflow no longer
|
|
26592
|
+
* replays: the commit replay last stood on, and the step the live workflow
|
|
26593
|
+
* reaches from there on HEAD's tree — the unpin when the flow ends instead.
|
|
26594
|
+
* `undefined` when there is nothing to anchor: no divergence, or no pin.
|
|
26595
|
+
*/
|
|
26596
|
+
const repinAnchor = gen(function* () {
|
|
26597
|
+
const git = yield* GitService;
|
|
26598
|
+
const config = yield* (yield* ConfigService).load;
|
|
26599
|
+
const workspace = yield* Workspace;
|
|
26600
|
+
const run = yield* computeProcessRun(git, config.workflow);
|
|
26601
|
+
const { def, setup } = yield* setupFor(config, run, workspace, yield* Host);
|
|
26602
|
+
const diverged = yield* promise(() => replayFor(setup));
|
|
26603
|
+
if (diverged.kind !== "divergence" || !headHasPin(workspace, run)) return void 0;
|
|
26604
|
+
const trial = {
|
|
26605
|
+
hash: "",
|
|
26606
|
+
message: formatCommitMessage({
|
|
26607
|
+
actor: "check",
|
|
26608
|
+
from: PIN_STATES.add,
|
|
26609
|
+
to: PIN_STATES.add,
|
|
26610
|
+
pin: "add",
|
|
26611
|
+
anchor: diverged.at
|
|
26612
|
+
}),
|
|
26613
|
+
tree: setup.episode.at(-1)?.tree ?? setup.base.tree
|
|
26614
|
+
};
|
|
26615
|
+
const outcome = yield* promise(() => replayFor({
|
|
26616
|
+
...setup,
|
|
26617
|
+
episode: [...setup.episode, trial]
|
|
26618
|
+
}));
|
|
26619
|
+
if (outcome.kind !== "divergence") return yield* fail$4(replayError(outcome) ?? /* @__PURE__ */ new Error("gtd repin: replay found no step"));
|
|
26620
|
+
const to = outcome.reached ?? PIN_STATES.remove;
|
|
26621
|
+
if (run.workflow === void 0 && to === def.initial) return yield* fail$4(/* @__PURE__ */ new Error(`gtd repin: the live workflow continues this process at "${to}", the default workflow's first step, where a finished process waits — run \`gtd abandon\` to start over`));
|
|
26622
|
+
return {
|
|
26623
|
+
at: diverged.at,
|
|
26624
|
+
to
|
|
26625
|
+
};
|
|
26626
|
+
});
|
|
25829
26627
|
/** The step the process rests at and the trace that reached it — at an unpin, the flow's last step. */
|
|
25830
26628
|
const restingOn = (outcome, pin) => {
|
|
25831
26629
|
if (outcome.kind === "rest") return {
|
|
@@ -26156,15 +26954,16 @@ const summaryFor = (run) => gen(function* () {
|
|
|
26156
26954
|
* repository cannot be read at a ref, so it is left out. A file only the old
|
|
26157
26955
|
* config read needs no check: whichever file imported it differs too.
|
|
26158
26956
|
*/
|
|
26159
|
-
const inRepoConfigPaths = (root, files) => {
|
|
26160
|
-
const
|
|
26161
|
-
for (const file of files)
|
|
26162
|
-
return [...
|
|
26957
|
+
const inRepoConfigPaths = (path, root, files) => {
|
|
26958
|
+
const found = /* @__PURE__ */ new Set();
|
|
26959
|
+
for (const file of files) found.add(path.relative(root, path.resolve(root, file)).split(path.sep).join("/"));
|
|
26960
|
+
return [...found].filter((p) => p !== "" && !p.startsWith("..") && !path.isAbsolute(p)).sort();
|
|
26163
26961
|
};
|
|
26164
26962
|
const configRefusal = (command, tip, paths, reason = "A history ref replays under today's config, so it is unreadable after the config changes") => /* @__PURE__ */ new Error(`${command}: refused — config differs at ${tip.slice(0, 7)}: ${paths.join(", ")}. ${reason}`);
|
|
26165
26963
|
const differingConfigPaths = (git, workspace, root, config, hash) => gen(function* () {
|
|
26166
26964
|
const differing = [];
|
|
26167
|
-
|
|
26965
|
+
const configPaths = inRepoConfigPaths(yield* Path$1, root, Object.keys(config.fingerprint));
|
|
26966
|
+
for (const path of configPaths) if ((yield* orElseSucceed(git.readFileAtRef(hash, path), () => void 0)) !== workspace.readSync(path)) differing.push(path);
|
|
26168
26967
|
return differing;
|
|
26169
26968
|
});
|
|
26170
26969
|
const notClosedError = (hash, outcome) => outcome.kind === "rest" ? /* @__PURE__ */ new Error(`gtd history: ${hash.slice(0, 7)} does not close a process under today's config`) : replayError(outcome) ?? /* @__PURE__ */ new Error("gtd history: replay failed");
|
|
@@ -26944,12 +27743,11 @@ const runHistoryJudgesCommand = (refs, vars, jsonl, json, out) => gen(function*
|
|
|
26944
27743
|
});
|
|
26945
27744
|
//#endregion
|
|
26946
27745
|
//#region src/cli/Program.ts
|
|
26947
|
-
const selfGtd = () => {
|
|
26948
|
-
const script = process.argv[1];
|
|
26949
|
-
return script === void 0 ? ["gtd"] : [process.execPath, script];
|
|
26950
|
-
};
|
|
26951
27746
|
/** `needs: "none"` skips the repo-root guard — the server is keyed on file name, not workflow state. */
|
|
26952
|
-
const runLspCommand = () =>
|
|
27747
|
+
const runLspCommand = () => gen(function* () {
|
|
27748
|
+
const { self } = yield* Host;
|
|
27749
|
+
return yield* (yield* ToolCommands).lsp(self);
|
|
27750
|
+
});
|
|
26953
27751
|
/**
|
|
26954
27752
|
* Prints the briefing, emitted by the binary itself so it's always current.
|
|
26955
27753
|
* gtd ships no workflow, so where no `gtd.config.ts` governs it says where to
|
|
@@ -27057,19 +27855,6 @@ const parseJudgeDocument = (rendered) => try_({
|
|
|
27057
27855
|
try: () => JSON.parse(rendered),
|
|
27058
27856
|
catch: () => /* @__PURE__ */ new Error("gtd judge answer: the resolved rest's \"judge:\" template did not render valid JSON")
|
|
27059
27857
|
});
|
|
27060
|
-
/**
|
|
27061
|
-
* The first stdin-consuming command outside `src/lsp/Lsp.ts` (which reads LSP's
|
|
27062
|
-
* own length-prefixed frames off `process.stdin`, not a one-shot read) —
|
|
27063
|
-
* reads stdin to completion and decodes it as UTF-8 text, no framing.
|
|
27064
|
-
*/
|
|
27065
|
-
const readStdin = () => tryPromise({
|
|
27066
|
-
try: async () => {
|
|
27067
|
-
const chunks = [];
|
|
27068
|
-
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
27069
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
27070
|
-
},
|
|
27071
|
-
catch: (e) => e instanceof Error ? e : new Error(String(e))
|
|
27072
|
-
});
|
|
27073
27858
|
/** A stdin that is no judgment is the caller's mistake, a usage error like `gtd judge answer`'s. */
|
|
27074
27859
|
const judgmentShape = (input) => {
|
|
27075
27860
|
let doc;
|
|
@@ -27086,7 +27871,7 @@ const runJudgeRunCommand = (command, out) => gen(function* () {
|
|
|
27086
27871
|
const host = yield* Host;
|
|
27087
27872
|
const tools = yield* ToolCommands;
|
|
27088
27873
|
yield* refuseLegacyRc;
|
|
27089
|
-
const input = yield*
|
|
27874
|
+
const input = yield* host.stdin;
|
|
27090
27875
|
yield* judgmentShape(input);
|
|
27091
27876
|
const verdict = yield* tryPromise({
|
|
27092
27877
|
try: () => tools.judge({
|
|
@@ -27134,7 +27919,7 @@ const runJudgeAnswerCommand = (json, out) => gen(function* () {
|
|
|
27134
27919
|
const rendered = yield* renderRest(rest);
|
|
27135
27920
|
if (rendered.judge === void 0) return yield* fail$4(/* @__PURE__ */ new Error(`gtd judge answer: refused — the resolved rest ("${rest.state}") declares no "judge:"`));
|
|
27136
27921
|
const ids = (yield* parseJudgeDocument(rendered.judge)).questions.map((q) => q.id);
|
|
27137
|
-
const raw = yield*
|
|
27922
|
+
const raw = yield* (yield* Host).stdin;
|
|
27138
27923
|
const parsed = yield* try_({
|
|
27139
27924
|
try: () => JSON.parse(raw),
|
|
27140
27925
|
catch: () => new SelectorUsageError("gtd judge answer: stdin is not valid JSON")
|
|
@@ -27339,20 +28124,28 @@ const runRestoreCommand = (json, out) => gen(function* () {
|
|
|
27339
28124
|
]).required;
|
|
27340
28125
|
yield* writeScript(json, out, combinedScript(required, ""));
|
|
27341
28126
|
});
|
|
27342
|
-
/**
|
|
27343
|
-
|
|
27344
|
-
* so reading the rest first refuses a live workflow that cannot replay the
|
|
27345
|
-
* process before any pin is written.
|
|
27346
|
-
*/
|
|
27347
|
-
const runRepinCommand = (json, out) => gen(function* () {
|
|
28127
|
+
/** The step an ordinary repin rests at — the one the process rests at now. */
|
|
28128
|
+
const repinTarget = gen(function* () {
|
|
27348
28129
|
const rest = yield* currentRest;
|
|
27349
28130
|
const refusal = rest.pin === "add" ? "the next beat pins the process already — run it (`gtd next --json=content`), then land" : rest.pin === "remove" ? "the flow has ended — its unpin beat is next, so there is nothing to repin" : noProcessUnderway(rest) ? "no gtd process is underway — nothing to repin" : void 0;
|
|
27350
28131
|
if (refusal !== void 0) return yield* fail$4(/* @__PURE__ */ new Error(`gtd repin: ${refusal}`));
|
|
28132
|
+
return rest.step.name;
|
|
28133
|
+
});
|
|
28134
|
+
/**
|
|
28135
|
+
* `gtd repin`: the pin beat's script and landing, on demand. It runs live: a
|
|
28136
|
+
* pinned process the live workflow no longer replays is anchored where it
|
|
28137
|
+
* diverged, and any other failure to read the rest refuses before any pin is
|
|
28138
|
+
* written.
|
|
28139
|
+
*/
|
|
28140
|
+
const runRepinCommand = (json, out) => gen(function* () {
|
|
28141
|
+
const anchor = yield* repinAnchor;
|
|
28142
|
+
const to = anchor?.to ?? (yield* repinTarget);
|
|
27351
28143
|
const message = formatCommitMessage({
|
|
27352
28144
|
actor: "check",
|
|
27353
28145
|
from: PIN_STATES.add,
|
|
27354
|
-
to
|
|
27355
|
-
pin: "add"
|
|
28146
|
+
to,
|
|
28147
|
+
pin: "add",
|
|
28148
|
+
...anchor !== void 0 ? { anchor: anchor.at } : {}
|
|
27356
28149
|
});
|
|
27357
28150
|
yield* writeScript(json, out, combinedScript(emitScripts([
|
|
27358
28151
|
{
|
|
@@ -27365,7 +28158,7 @@ const runRepinCommand = (json, out) => gen(function* () {
|
|
|
27365
28158
|
},
|
|
27366
28159
|
{
|
|
27367
28160
|
kind: "outcome",
|
|
27368
|
-
command: transitionOutcome(PIN_STATES.add,
|
|
28161
|
+
command: transitionOutcome(PIN_STATES.add, to)
|
|
27369
28162
|
}
|
|
27370
28163
|
]).required, ""));
|
|
27371
28164
|
});
|
|
@@ -28039,7 +28832,7 @@ With no <name>, list every workflow and door it can start. An unknown <name>, th
|
|
|
28039
28832
|
arity: "none",
|
|
28040
28833
|
group: "Start and stop",
|
|
28041
28834
|
summary: "Pin the process to the live gtd and workflow",
|
|
28042
|
-
details: `Switch the process underway to the live engine and workflow, the way out when the workflow needs a fix mid-process: the printed script runs the pin beat again, writing \`gtd bundle\`'s output to .gtd/process.gtdpin and committing that file alone. Always runs on the gtd on PATH, never on the pin. Refuses when no process is underway, when the next beat is a pin or unpin beat already, or when the live workflow
|
|
28835
|
+
details: `Switch the process underway to the live engine and workflow, the way out when the workflow needs a fix mid-process: the printed script runs the pin beat again, writing \`gtd bundle\`'s output to .gtd/process.gtdpin and committing that file alone. Always runs on the gtd on PATH, never on the pin. When the live workflow no longer replays the process, the commit continues from the divergence: a Gtd-Anchor trailer names the last commit it replays, the later commits stay as files only, and the process rests at the step the live workflow reached there. Refuses when no process is underway, when the next beat is a pin or unpin beat already, when an unpinned process diverges, or when the live workflow would continue at the default workflow's first step.
|
|
28043
28836
|
|
|
28044
28837
|
s=$(gtd repin --json=script) && sh -c "$s"`
|
|
28045
28838
|
},
|
|
@@ -29312,6 +30105,7 @@ const toError = (e) => e instanceof Error ? e : new Error(String(e));
|
|
|
29312
30105
|
* one). Mirrors the guard the deleted `makeInMemoryFileSystem` carried.
|
|
29313
30106
|
*/
|
|
29314
30107
|
const isGitDirKey = (key) => key.startsWith(".git/");
|
|
30108
|
+
const paths = runSync$1(provide(Path$1, layer$4));
|
|
29315
30109
|
/**
|
|
29316
30110
|
* `Workspace` for the in-memory tier. `readSync`/`read`/`write`/`committed`
|
|
29317
30111
|
* take a repo-relative key ONLY (mirroring the live adapter's
|
|
@@ -29327,12 +30121,12 @@ const isGitDirKey = (key) => key.startsWith(".git/");
|
|
|
29327
30121
|
*/
|
|
29328
30122
|
const makeInMemoryWorkspaceOps = (repo, root) => {
|
|
29329
30123
|
const toKey = (path) => {
|
|
29330
|
-
if (!isAbsolute(path)) return path;
|
|
30124
|
+
if (!paths.isAbsolute(path)) return path;
|
|
29331
30125
|
const prefix = `${root}/`;
|
|
29332
30126
|
return path === root ? "" : path.startsWith(prefix) ? path.slice(prefix.length) : path;
|
|
29333
30127
|
};
|
|
29334
30128
|
const assertRepoRelative = (path) => {
|
|
29335
|
-
if (isAbsolute(path)) throw new Error(`Workspace: "${path}" is an absolute path — this member takes repo-relative paths only (use "atPath" to read outside the repo)`);
|
|
30129
|
+
if (paths.isAbsolute(path)) throw new Error(`Workspace: "${path}" is an absolute path — this member takes repo-relative paths only (use "atPath" to read outside the repo)`);
|
|
29336
30130
|
return path;
|
|
29337
30131
|
};
|
|
29338
30132
|
const readAt = (key) => isGitDirKey(key) ? void 0 : repo.readFile(key);
|
|
@@ -29379,7 +30173,7 @@ const makeInMemoryConfigDiscovery = (repo, root) => {
|
|
|
29379
30173
|
const workspace = makeInMemoryWorkspaceOps(repo, root);
|
|
29380
30174
|
return succeed$2(ConfigDiscovery, {
|
|
29381
30175
|
legacyRcFiles: (levelRoot, levelHome) => try_({
|
|
29382
|
-
try: () => legacyRcDirs(levelRoot, levelHome).flatMap((dir) => LEGACY_RC_FILES.map((name) => join(dir, name)).filter((filepath) => workspace.atPath(filepath) !== void 0).map((path) => ({
|
|
30176
|
+
try: () => legacyRcDirs(paths, levelRoot, levelHome).flatMap((dir) => LEGACY_RC_FILES.map((name) => paths.join(dir, name)).filter((filepath) => workspace.atPath(filepath) !== void 0).map((path) => ({
|
|
29383
30177
|
path,
|
|
29384
30178
|
owned: true
|
|
29385
30179
|
}))),
|
|
@@ -29387,8 +30181,8 @@ const makeInMemoryConfigDiscovery = (repo, root) => {
|
|
|
29387
30181
|
}),
|
|
29388
30182
|
workflowModule: (levelRoot, levelHome) => try_({
|
|
29389
30183
|
try: () => {
|
|
29390
|
-
for (const dir of walkUp(levelRoot, levelHome)) {
|
|
29391
|
-
const filepath = join(dir, WORKFLOW_MODULE);
|
|
30184
|
+
for (const dir of walkUp(paths, levelRoot, levelHome)) {
|
|
30185
|
+
const filepath = paths.join(dir, WORKFLOW_MODULE);
|
|
29392
30186
|
const source = workspace.atPath(filepath);
|
|
29393
30187
|
if (source !== void 0) return {
|
|
29394
30188
|
filepath,
|
|
@@ -29420,26 +30214,41 @@ const gitTestLayer = (repo, root = "/repo") => succeed$2(GitService, fakeGitOper
|
|
|
29420
30214
|
* itself; explicit env wins.
|
|
29421
30215
|
*/
|
|
29422
30216
|
const ambientFlagEnv = (env = process.env) => Object.fromEntries(Object.entries(env).filter((e) => e[0].startsWith("GTD_FLAG_") && e[1] !== void 0));
|
|
30217
|
+
/** Where the in-memory host's scratch files go when `TMPDIR` is unset. */
|
|
30218
|
+
const IN_MEMORY_SCRATCH = "/scratch";
|
|
30219
|
+
const IN_MEMORY_PID = 4242;
|
|
30220
|
+
/**
|
|
30221
|
+
* The in-memory `Host`: nothing read off the real process or `node:os`.
|
|
30222
|
+
* `realPath` is a no-op (there is no real filesystem to resolve symlinks
|
|
30223
|
+
* against), so both sides of `assertRunningFromRepoRoot`'s comparison
|
|
30224
|
+
* collapse to `root`.
|
|
30225
|
+
*/
|
|
30226
|
+
const inMemoryHost = (root, home, env, stdin = "") => ({
|
|
30227
|
+
root,
|
|
30228
|
+
home,
|
|
30229
|
+
env,
|
|
30230
|
+
scratchDir: scratchDirOf(env, IN_MEMORY_SCRATCH),
|
|
30231
|
+
realPath: () => succeed$1(root),
|
|
30232
|
+
pid: IN_MEMORY_PID,
|
|
30233
|
+
uid: void 0,
|
|
30234
|
+
self: ["gtd"],
|
|
30235
|
+
stdin: succeed$1(stdin)
|
|
30236
|
+
});
|
|
29423
30237
|
/** Every layer `makeProgram` needs — `CommandRequirements`'s return type here IS the guarantee: a new port added there fails this function's typecheck instead of silently under-providing. */
|
|
29424
30238
|
function testLayers(repo, opts = {}) {
|
|
29425
30239
|
const root = opts.root ?? "/repo";
|
|
29426
30240
|
const home = opts.home ?? root;
|
|
29427
|
-
return mergeAll(gitTestLayer(repo, root), succeed$2(Workspace, makeInMemoryWorkspaceOps(repo, root)), Host
|
|
29428
|
-
|
|
29429
|
-
|
|
29430
|
-
|
|
29431
|
-
...ambientFlagEnv(),
|
|
29432
|
-
...opts.env
|
|
29433
|
-
},
|
|
29434
|
-
realPath: () => succeed$1(root)
|
|
29435
|
-
}), ConfigService.Live, makeInMemoryConfigDiscovery(repo, root), succeed$2(EngineCore, { source: succeed$1(IN_MEMORY_ENGINE) }), Narrator.layer(opts.narrate ?? (() => {}), opts.verbose ?? true), succeed$2(ToolCommands, {
|
|
30241
|
+
return mergeAll(gitTestLayer(repo, root), succeed$2(Workspace, makeInMemoryWorkspaceOps(repo, root)), succeed$2(Host, inMemoryHost(root, home, {
|
|
30242
|
+
...ambientFlagEnv(),
|
|
30243
|
+
...opts.env
|
|
30244
|
+
}, opts.stdin)), ConfigService.Live, makeInMemoryConfigDiscovery(repo, root), succeed$2(EngineCore, { source: succeed$1(IN_MEMORY_ENGINE) }), Narrator.layer(opts.narrate ?? (() => {}), opts.verbose ?? true), layer$4, succeed$2(ToolCommands, {
|
|
29436
30245
|
...missingToolCommands,
|
|
29437
30246
|
...opts.tools
|
|
29438
30247
|
}), CommandRunner.layer(() => fail$4(/* @__PURE__ */ new Error("gtd ui: CommandRunner has no test double wired into testLayers()"))));
|
|
29439
30248
|
}
|
|
29440
30249
|
//#endregion
|
|
29441
30250
|
//#region src/twins/cliIo.ts
|
|
29442
|
-
const makeCapturingCliIo = (repo, env = {}, tools = {}) => {
|
|
30251
|
+
const makeCapturingCliIo = (repo, env = {}, tools = {}, stdin = "") => {
|
|
29443
30252
|
let stdout = Buffer.alloc(0);
|
|
29444
30253
|
let stderr = "";
|
|
29445
30254
|
let exitCode = 0;
|
|
@@ -29458,7 +30267,8 @@ const makeCapturingCliIo = (repo, env = {}, tools = {}) => {
|
|
|
29458
30267
|
env,
|
|
29459
30268
|
narrate: (line) => stderr += line,
|
|
29460
30269
|
verbose,
|
|
29461
|
-
tools
|
|
30270
|
+
tools,
|
|
30271
|
+
stdin
|
|
29462
30272
|
})
|
|
29463
30273
|
},
|
|
29464
30274
|
result: () => ({
|
|
@@ -30394,6 +31204,8 @@ const unrecognized = (block, output = "") => ({
|
|
|
30394
31204
|
error: `${UNRECOGNIZED_PREFIX}${block.split("\n")[0]}`
|
|
30395
31205
|
});
|
|
30396
31206
|
Narrator.layer(() => {}, false);
|
|
31207
|
+
new TextEncoder();
|
|
31208
|
+
new TextDecoder("utf-8", { ignoreBOM: true });
|
|
30397
31209
|
//#endregion
|
|
30398
31210
|
//#region src/testkit/imports.ts
|
|
30399
31211
|
const IDENTIFIER$1 = /^[A-Za-z_$][\w$]*$/;
|
|
@@ -30619,8 +31431,8 @@ const testRepo = (options) => {
|
|
|
30619
31431
|
...Object.fromEntries(Object.keys(process.env).filter((name) => name.startsWith("GTD_FLAG_")).map((name) => [name, void 0])),
|
|
30620
31432
|
...env
|
|
30621
31433
|
} : env;
|
|
30622
|
-
const
|
|
30623
|
-
const { io, result } = makeCapturingCliIo(repo, seenEnv());
|
|
31434
|
+
const gtdWithStdin = async (stdin, ...args) => {
|
|
31435
|
+
const { io, result } = makeCapturingCliIo(repo, seenEnv(), {}, stdin);
|
|
30624
31436
|
if (typeof workflow !== "string") registry().set(id, workflow);
|
|
30625
31437
|
for (const [specifier, module] of modules) registry().set(moduleKey(specifier), module);
|
|
30626
31438
|
try {
|
|
@@ -30635,20 +31447,7 @@ const testRepo = (options) => {
|
|
|
30635
31447
|
}
|
|
30636
31448
|
return result();
|
|
30637
31449
|
};
|
|
30638
|
-
const
|
|
30639
|
-
const saved = Object.getOwnPropertyDescriptor(process, "stdin");
|
|
30640
|
-
const fake = new PassThrough();
|
|
30641
|
-
Object.defineProperty(process, "stdin", {
|
|
30642
|
-
value: fake,
|
|
30643
|
-
configurable: true
|
|
30644
|
-
});
|
|
30645
|
-
fake.end(stdin);
|
|
30646
|
-
try {
|
|
30647
|
-
return await fn();
|
|
30648
|
-
} finally {
|
|
30649
|
-
if (saved) Object.defineProperty(process, "stdin", saved);
|
|
30650
|
-
}
|
|
30651
|
-
};
|
|
31450
|
+
const gtd = (...args) => gtdWithStdin("", ...args);
|
|
30652
31451
|
const apply = (what, script) => {
|
|
30653
31452
|
if (script.length === 0) return;
|
|
30654
31453
|
const applied = applyEmittedScript(repo, commands, script);
|
|
@@ -30656,7 +31455,7 @@ const testRepo = (options) => {
|
|
|
30656
31455
|
};
|
|
30657
31456
|
/** Runs a write command, then the script its `--json=script` twin prints. */
|
|
30658
31457
|
const runWrite = async (args, stdin, refused = (error) => error) => {
|
|
30659
|
-
const call = (...extra) => stdin
|
|
31458
|
+
const call = (...extra) => gtdWithStdin(stdin ?? "", ...args, ...extra);
|
|
30660
31459
|
const reported = await call();
|
|
30661
31460
|
if (reported.exitCode !== 0) throw refused(failed(`gtd ${args.join(" ")}`, reported));
|
|
30662
31461
|
const script = await call("--json=script");
|