fleetfizz 0.9.1 → 0.11.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/dist/fleetfizz.mjs +1303 -161
- package/package.json +3 -2
- package/schema.json +321 -21
package/dist/fleetfizz.mjs
CHANGED
|
@@ -1849,7 +1849,7 @@ const emptyContext$1 = {
|
|
|
1849
1849
|
* @category formatting
|
|
1850
1850
|
* @since 2.0.0
|
|
1851
1851
|
*/
|
|
1852
|
-
function format$
|
|
1852
|
+
function format$3(input, options) {
|
|
1853
1853
|
const space = options?.space ?? 0;
|
|
1854
1854
|
const ancestors = /* @__PURE__ */ new WeakSet();
|
|
1855
1855
|
const gap = !space ? "" : typeof space === "number" ? " ".repeat(space) : space;
|
|
@@ -2012,7 +2012,7 @@ function formatJson(input, options) {
|
|
|
2012
2012
|
return JSON.stringify(input, function(key, value) {
|
|
2013
2013
|
const original = Object.getOwnPropertyDescriptor(this, key)?.value;
|
|
2014
2014
|
const redacted = hasProperty(original, symbolRedactable) ? redact(original) : redact(value);
|
|
2015
|
-
if (typeof redacted === "bigint") return format$
|
|
2015
|
+
if (typeof redacted === "bigint") return format$3(redacted);
|
|
2016
2016
|
if (typeof redacted !== "object" || redacted === null) return redacted;
|
|
2017
2017
|
const current = redacted instanceof Error && !hasProperty(redacted, "toJSON") ? {
|
|
2018
2018
|
...redacted,
|
|
@@ -2148,7 +2148,7 @@ const toJson = (input) => {
|
|
|
2148
2148
|
const toStringUnknown = (u, whitespace = 2) => {
|
|
2149
2149
|
if (typeof u === "string") return u;
|
|
2150
2150
|
try {
|
|
2151
|
-
return typeof u === "object" ? formatJson(u, { space: whitespace }) : format$
|
|
2151
|
+
return typeof u === "object" ? formatJson(u, { space: whitespace }) : format$3(u, { space: whitespace });
|
|
2152
2152
|
} catch {
|
|
2153
2153
|
return String(u);
|
|
2154
2154
|
}
|
|
@@ -2198,7 +2198,7 @@ const BaseProto = {
|
|
|
2198
2198
|
return this.toJSON();
|
|
2199
2199
|
},
|
|
2200
2200
|
toString() {
|
|
2201
|
-
return format$
|
|
2201
|
+
return format$3(this.toJSON());
|
|
2202
2202
|
}
|
|
2203
2203
|
};
|
|
2204
2204
|
//#endregion
|
|
@@ -2361,7 +2361,7 @@ const PipeInspectableProto = {
|
|
|
2361
2361
|
return { ...this };
|
|
2362
2362
|
},
|
|
2363
2363
|
toString() {
|
|
2364
|
-
return format$
|
|
2364
|
+
return format$3(this.toJSON(), {
|
|
2365
2365
|
ignoreToString: true,
|
|
2366
2366
|
space: 2
|
|
2367
2367
|
});
|
|
@@ -2411,7 +2411,7 @@ var CauseImpl = class {
|
|
|
2411
2411
|
};
|
|
2412
2412
|
}
|
|
2413
2413
|
toString() {
|
|
2414
|
-
return `Cause(${format$
|
|
2414
|
+
return `Cause(${format$3(this.reasons)})`;
|
|
2415
2415
|
}
|
|
2416
2416
|
[NodeInspectSymbol]() {
|
|
2417
2417
|
return this.toJSON();
|
|
@@ -2454,7 +2454,7 @@ var ReasonBase = class {
|
|
|
2454
2454
|
return pipeArguments(this, arguments);
|
|
2455
2455
|
}
|
|
2456
2456
|
toString() {
|
|
2457
|
-
return format$
|
|
2457
|
+
return format$3(this);
|
|
2458
2458
|
}
|
|
2459
2459
|
[NodeInspectSymbol]() {
|
|
2460
2460
|
return this.toString();
|
|
@@ -2469,7 +2469,7 @@ var Fail = class extends ReasonBase {
|
|
|
2469
2469
|
this.error = error;
|
|
2470
2470
|
}
|
|
2471
2471
|
toString() {
|
|
2472
|
-
return `Fail(${format$
|
|
2472
|
+
return `Fail(${format$3(this.error)})`;
|
|
2473
2473
|
}
|
|
2474
2474
|
toJSON() {
|
|
2475
2475
|
return {
|
|
@@ -2517,7 +2517,7 @@ var Die = class extends ReasonBase {
|
|
|
2517
2517
|
this.defect = defect;
|
|
2518
2518
|
}
|
|
2519
2519
|
toString() {
|
|
2520
|
-
return `Die(${format$
|
|
2520
|
+
return `Die(${format$3(this.defect)})`;
|
|
2521
2521
|
}
|
|
2522
2522
|
toJSON() {
|
|
2523
2523
|
return {
|
|
@@ -2578,7 +2578,7 @@ const makeExit = (options) => {
|
|
|
2578
2578
|
},
|
|
2579
2579
|
...makePrimitiveProto(options),
|
|
2580
2580
|
toString() {
|
|
2581
|
-
return `${options.op}(${format$
|
|
2581
|
+
return `${options.op}(${format$3(this[args])})`;
|
|
2582
2582
|
},
|
|
2583
2583
|
toJSON() {
|
|
2584
2584
|
return {
|
|
@@ -2768,7 +2768,7 @@ const SomeProto = /*#__PURE__*/ Object.defineProperty(/*#__PURE__*/ Object.assig
|
|
|
2768
2768
|
return combine$1(hash(this._tag))(hash(this.value));
|
|
2769
2769
|
},
|
|
2770
2770
|
toString() {
|
|
2771
|
-
return `some(${format$
|
|
2771
|
+
return `some(${format$3(this.value)})`;
|
|
2772
2772
|
},
|
|
2773
2773
|
toJSON() {
|
|
2774
2774
|
return {
|
|
@@ -2840,7 +2840,7 @@ const SuccessProto = /*#__PURE__*/ Object.assign(/*#__PURE__*/ Object.create(Com
|
|
|
2840
2840
|
return combine$1(hash(this._tag))(hash(this.success));
|
|
2841
2841
|
},
|
|
2842
2842
|
toString() {
|
|
2843
|
-
return `success(${format$
|
|
2843
|
+
return `success(${format$3(this.success)})`;
|
|
2844
2844
|
},
|
|
2845
2845
|
toJSON() {
|
|
2846
2846
|
return {
|
|
@@ -2860,7 +2860,7 @@ const FailureProto = /*#__PURE__*/ Object.assign(/*#__PURE__*/ Object.create(Com
|
|
|
2860
2860
|
return combine$1(hash(this._tag))(hash(this.failure));
|
|
2861
2861
|
},
|
|
2862
2862
|
toString() {
|
|
2863
|
-
return `failure(${format$
|
|
2863
|
+
return `failure(${format$3(this.failure)})`;
|
|
2864
2864
|
},
|
|
2865
2865
|
toJSON() {
|
|
2866
2866
|
return {
|
|
@@ -4720,7 +4720,10 @@ const bigint0$3 = /*#__PURE__*/ BigInt(0);
|
|
|
4720
4720
|
const bigint1$2 = /*#__PURE__*/ BigInt(1);
|
|
4721
4721
|
const bigint2 = /*#__PURE__*/ BigInt(2);
|
|
4722
4722
|
const bigint10$1 = /*#__PURE__*/ BigInt(10);
|
|
4723
|
+
const bigint24 = /*#__PURE__*/ BigInt(24);
|
|
4724
|
+
const bigint60 = /*#__PURE__*/ BigInt(60);
|
|
4723
4725
|
const bigint1e3 = /*#__PURE__*/ BigInt(1e3);
|
|
4726
|
+
const bigint1e6 = /*#__PURE__*/ BigInt(1e6);
|
|
4724
4727
|
const roundTiesAwayFromZero = (input) => BigInt(input < 0 ? Math.ceil(input - .5) : Math.floor(input + .5));
|
|
4725
4728
|
const roundMillisToNanos = (millis) => roundTiesAwayFromZero(millis * 1e6);
|
|
4726
4729
|
const parseNanos = (input, scale) => {
|
|
@@ -4930,6 +4933,79 @@ const isDuration = (u) => hasProperty(u, TypeId$32);
|
|
|
4930
4933
|
*/
|
|
4931
4934
|
const isFinite$1 = (self) => self.value._tag !== "Infinity" && self.value._tag !== "NegativeInfinity";
|
|
4932
4935
|
/**
|
|
4936
|
+
* Checks whether a Duration is zero.
|
|
4937
|
+
*
|
|
4938
|
+
* **Example** (Checking for zero durations)
|
|
4939
|
+
*
|
|
4940
|
+
* ```ts import.meta.vitest
|
|
4941
|
+
* import { Duration } from "effect"
|
|
4942
|
+
*
|
|
4943
|
+
* Duration.isZero(Duration.zero) // => true
|
|
4944
|
+
* Duration.isZero(Duration.seconds(1)) // => false
|
|
4945
|
+
* ```
|
|
4946
|
+
*
|
|
4947
|
+
* @stability stable
|
|
4948
|
+
* @category predicates
|
|
4949
|
+
* @since 3.5.0
|
|
4950
|
+
*/
|
|
4951
|
+
const isZero$1 = (self) => {
|
|
4952
|
+
switch (self.value._tag) {
|
|
4953
|
+
case "Millis": return self.value.millis === 0;
|
|
4954
|
+
case "Nanos": return self.value.nanos === bigint0$3;
|
|
4955
|
+
case "Infinity":
|
|
4956
|
+
case "NegativeInfinity": return false;
|
|
4957
|
+
}
|
|
4958
|
+
};
|
|
4959
|
+
/**
|
|
4960
|
+
* Returns `true` if the duration is negative (strictly less than zero).
|
|
4961
|
+
*
|
|
4962
|
+
* **Example** (Checking for negative durations)
|
|
4963
|
+
*
|
|
4964
|
+
* ```ts import.meta.vitest
|
|
4965
|
+
* import { Duration } from "effect"
|
|
4966
|
+
*
|
|
4967
|
+
* Duration.isNegative(Duration.seconds(-5)) // => true
|
|
4968
|
+
* Duration.isNegative(Duration.zero) // => false
|
|
4969
|
+
* Duration.isNegative(Duration.negativeInfinity) // => true
|
|
4970
|
+
* ```
|
|
4971
|
+
*
|
|
4972
|
+
* @stability stable
|
|
4973
|
+
* @category predicates
|
|
4974
|
+
* @since 4.0.0
|
|
4975
|
+
*/
|
|
4976
|
+
const isNegative = (self) => {
|
|
4977
|
+
switch (self.value._tag) {
|
|
4978
|
+
case "Millis": return self.value.millis < 0;
|
|
4979
|
+
case "Nanos": return self.value.nanos < bigint0$3;
|
|
4980
|
+
case "NegativeInfinity": return true;
|
|
4981
|
+
case "Infinity": return false;
|
|
4982
|
+
}
|
|
4983
|
+
};
|
|
4984
|
+
/**
|
|
4985
|
+
* Returns the absolute value of the duration.
|
|
4986
|
+
*
|
|
4987
|
+
* **Example** (Taking absolute duration values)
|
|
4988
|
+
*
|
|
4989
|
+
* ```ts import.meta.vitest
|
|
4990
|
+
* import { Duration } from "effect"
|
|
4991
|
+
*
|
|
4992
|
+
* Duration.abs(Duration.seconds(-5)) // => Duration.seconds(5)
|
|
4993
|
+
* Duration.abs(Duration.negativeInfinity) // => Duration.infinity
|
|
4994
|
+
* ```
|
|
4995
|
+
*
|
|
4996
|
+
* @stability stable
|
|
4997
|
+
* @category math
|
|
4998
|
+
* @since 4.0.0
|
|
4999
|
+
*/
|
|
5000
|
+
const abs = (self) => {
|
|
5001
|
+
switch (self.value._tag) {
|
|
5002
|
+
case "Infinity":
|
|
5003
|
+
case "NegativeInfinity": return infinity;
|
|
5004
|
+
case "Millis": return self.value.millis < 0 ? make$27(-self.value.millis) : self;
|
|
5005
|
+
case "Nanos": return self.value.nanos < bigint0$3 ? make$27(-self.value.nanos) : self;
|
|
5006
|
+
}
|
|
5007
|
+
};
|
|
5008
|
+
/**
|
|
4933
5009
|
* A Duration representing zero time.
|
|
4934
5010
|
*
|
|
4935
5011
|
* **Example** (Referencing the zero duration)
|
|
@@ -5245,6 +5321,104 @@ const Equivalence$1 = (self, that) => matchPair(self, that, {
|
|
|
5245
5321
|
* @since 2.0.0
|
|
5246
5322
|
*/
|
|
5247
5323
|
const equals = /*#__PURE__*/ dual(2, (self, that) => Equivalence$1(self, that));
|
|
5324
|
+
/**
|
|
5325
|
+
* Decomposes a `Duration` into normalized signed components.
|
|
5326
|
+
*
|
|
5327
|
+
* **Details**
|
|
5328
|
+
*
|
|
5329
|
+
* Finite durations are returned as `{ days, hours, minutes, seconds, millis,
|
|
5330
|
+
* nanos }`. Infinite durations return every component as `Infinity` or
|
|
5331
|
+
* `-Infinity`.
|
|
5332
|
+
*
|
|
5333
|
+
* **Example** (Decomposing durations into parts)
|
|
5334
|
+
*
|
|
5335
|
+
* ```ts import.meta.vitest
|
|
5336
|
+
* import { Duration } from "effect"
|
|
5337
|
+
*
|
|
5338
|
+
* // Create a complex duration by adding multiple parts
|
|
5339
|
+
* const duration = Duration.sum(
|
|
5340
|
+
* Duration.sum(
|
|
5341
|
+
* Duration.sum(Duration.days(1), Duration.hours(2)),
|
|
5342
|
+
* Duration.sum(Duration.minutes(30), Duration.seconds(45))
|
|
5343
|
+
* ),
|
|
5344
|
+
* Duration.millis(123)
|
|
5345
|
+
* )
|
|
5346
|
+
* Duration.parts(duration) // => ({ days: 1, hours: 2, minutes: 30, seconds: 45, millis: 123, nanos: 0 })
|
|
5347
|
+
*
|
|
5348
|
+
* const complex = Duration.sum(Duration.hours(25), Duration.minutes(90))
|
|
5349
|
+
* Duration.parts(complex) // => ({ days: 1, hours: 2, minutes: 30, seconds: 0, millis: 0, nanos: 0 })
|
|
5350
|
+
* ```
|
|
5351
|
+
*
|
|
5352
|
+
* @stability stable
|
|
5353
|
+
* @category converting
|
|
5354
|
+
* @since 3.8.0
|
|
5355
|
+
*/
|
|
5356
|
+
const parts = (self) => {
|
|
5357
|
+
if (self.value._tag === "Infinity") return {
|
|
5358
|
+
days: Infinity,
|
|
5359
|
+
hours: Infinity,
|
|
5360
|
+
minutes: Infinity,
|
|
5361
|
+
seconds: Infinity,
|
|
5362
|
+
millis: Infinity,
|
|
5363
|
+
nanos: Infinity
|
|
5364
|
+
};
|
|
5365
|
+
if (self.value._tag === "NegativeInfinity") return {
|
|
5366
|
+
days: -Infinity,
|
|
5367
|
+
hours: -Infinity,
|
|
5368
|
+
minutes: -Infinity,
|
|
5369
|
+
seconds: -Infinity,
|
|
5370
|
+
millis: -Infinity,
|
|
5371
|
+
nanos: -Infinity
|
|
5372
|
+
};
|
|
5373
|
+
const n = toNanosUnsafe(self);
|
|
5374
|
+
const neg = n < bigint0$3;
|
|
5375
|
+
const a = neg ? -n : n;
|
|
5376
|
+
const ms = a / bigint1e6;
|
|
5377
|
+
const sec = ms / bigint1e3;
|
|
5378
|
+
const min = sec / bigint60;
|
|
5379
|
+
const hr = min / bigint60;
|
|
5380
|
+
const d = hr / bigint24;
|
|
5381
|
+
const sign = neg ? -1 : 1;
|
|
5382
|
+
return {
|
|
5383
|
+
days: sign * Number(d),
|
|
5384
|
+
hours: sign * Number(hr % bigint24),
|
|
5385
|
+
minutes: sign * Number(min % bigint60),
|
|
5386
|
+
seconds: sign * Number(sec % bigint60),
|
|
5387
|
+
millis: sign * Number(ms % bigint1e3),
|
|
5388
|
+
nanos: sign * Number(a % bigint1e6)
|
|
5389
|
+
};
|
|
5390
|
+
};
|
|
5391
|
+
/**
|
|
5392
|
+
* Converts a `Duration` to a human readable string.
|
|
5393
|
+
*
|
|
5394
|
+
* **Example** (Formatting durations)
|
|
5395
|
+
*
|
|
5396
|
+
* ```ts import.meta.vitest
|
|
5397
|
+
* import { Duration } from "effect"
|
|
5398
|
+
*
|
|
5399
|
+
* Duration.format(Duration.millis(1000)) // => "1s"
|
|
5400
|
+
* Duration.format(Duration.millis(1001)) // => "1s 1ms"
|
|
5401
|
+
* ```
|
|
5402
|
+
*
|
|
5403
|
+
* @stability stable
|
|
5404
|
+
* @category converting
|
|
5405
|
+
* @since 2.0.0
|
|
5406
|
+
*/
|
|
5407
|
+
const format$2 = (self) => {
|
|
5408
|
+
if (self.value._tag === "Infinity") return "Infinity";
|
|
5409
|
+
if (self.value._tag === "NegativeInfinity") return "-Infinity";
|
|
5410
|
+
if (isZero$1(self)) return "0";
|
|
5411
|
+
if (isNegative(self)) return "-" + format$2(abs(self));
|
|
5412
|
+
const fragments = parts(self);
|
|
5413
|
+
const pieces = [];
|
|
5414
|
+
if (fragments.days !== 0) pieces.push(`${fragments.days}d`);
|
|
5415
|
+
if (fragments.hours !== 0) pieces.push(`${fragments.hours}h`);
|
|
5416
|
+
if (fragments.minutes !== 0) pieces.push(`${fragments.minutes}m`);
|
|
5417
|
+
if (fragments.seconds !== 0) pieces.push(`${fragments.seconds}s`);
|
|
5418
|
+
if (fragments.millis !== 0) pieces.push(`${fragments.millis}ms`);
|
|
5419
|
+
if (fragments.nanos !== 0) pieces.push(`${fragments.nanos}ns`);
|
|
5420
|
+
return pieces.join(" ");
|
|
5421
|
+
};
|
|
5248
5422
|
//#endregion
|
|
5249
5423
|
//#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/Scheduler.js
|
|
5250
5424
|
/**
|
|
@@ -7057,6 +7231,16 @@ const exitPrimitive = /*#__PURE__*/ makePrimitive$1({
|
|
|
7057
7231
|
/** @internal */
|
|
7058
7232
|
const timeoutOrElse$1 = /*#__PURE__*/ dual(2, (self, options) => flatMap$2(timeoutOption(self, options.duration), (option) => isNone(option) ? options.orElse() : succeed$6(option.value)));
|
|
7059
7233
|
/** @internal */
|
|
7234
|
+
const timeoutErrorFromDuration = (duration) => new TimeoutError(`Operation timed out after '${format$2(duration)}'`);
|
|
7235
|
+
/** @internal */
|
|
7236
|
+
const timeout$1 = /*#__PURE__*/ dual(2, (self, duration) => {
|
|
7237
|
+
const decoded = fromInputUnsafe$1(duration);
|
|
7238
|
+
return timeoutOrElse$1(self, {
|
|
7239
|
+
duration: decoded,
|
|
7240
|
+
orElse: () => fail$4(timeoutErrorFromDuration(decoded))
|
|
7241
|
+
});
|
|
7242
|
+
});
|
|
7243
|
+
/** @internal */
|
|
7060
7244
|
const timeoutOption = /*#__PURE__*/ dual(2, (self, duration) => raceFirst$1(asSome(self), as$1(sleep$1(duration), none())));
|
|
7061
7245
|
/** @internal */
|
|
7062
7246
|
const ScopeTypeId = "~effect/Scope";
|
|
@@ -7249,6 +7433,8 @@ const onExitFilter = /*#__PURE__*/ dual(3, (self, filter, f) => onExit$1(self, (
|
|
|
7249
7433
|
}));
|
|
7250
7434
|
/** @internal */
|
|
7251
7435
|
const onError$1 = /*#__PURE__*/ dual(2, (self, f) => onExitFilter(self, exitFilterCause, f));
|
|
7436
|
+
/** @internal */
|
|
7437
|
+
const acquireUseRelease$1 = (acquire, use, release) => uninterruptibleMask$1((restore) => flatMap$2(acquire, (a) => onExitPrimitive$1(suspend$3(() => restore(use(a))), (exit) => release(a, exit), true)));
|
|
7252
7438
|
const infiniteTTL = /*#__PURE__*/ constant(Infinity);
|
|
7253
7439
|
const makeCachedUnsafe = (self, ttlMillis) => {
|
|
7254
7440
|
let expiresAt = 0;
|
|
@@ -7809,6 +7995,15 @@ const sleep$1 = (duration) => clockWith$1((clock) => clock.sleep(fromInputUnsafe
|
|
|
7809
7995
|
/** @internal */
|
|
7810
7996
|
const currentTimeMillis$1 = /*#__PURE__*/ clockWith$1((clock) => clock.currentTimeMillis);
|
|
7811
7997
|
/** @internal */
|
|
7998
|
+
const TimeoutErrorTypeId = "~effect/Cause/TimeoutError";
|
|
7999
|
+
/** @internal */
|
|
8000
|
+
var TimeoutError = class extends (/*#__PURE__*/ TaggedError$2("TimeoutError")) {
|
|
8001
|
+
[TimeoutErrorTypeId] = TimeoutErrorTypeId;
|
|
8002
|
+
constructor(message) {
|
|
8003
|
+
super({ message });
|
|
8004
|
+
}
|
|
8005
|
+
};
|
|
8006
|
+
/** @internal */
|
|
7812
8007
|
const IllegalArgumentErrorTypeId = "~effect/Cause/IllegalArgumentError";
|
|
7813
8008
|
/** @internal */
|
|
7814
8009
|
var IllegalArgumentError$1 = class extends (/*#__PURE__*/ TaggedError$2("IllegalArgumentError")) {
|
|
@@ -11846,6 +12041,45 @@ const ignore = ignore$1;
|
|
|
11846
12041
|
*/
|
|
11847
12042
|
const orElseSucceed = orElseSucceed$1;
|
|
11848
12043
|
/**
|
|
12044
|
+
* Adds a time limit to an effect, triggering a timeout if the effect exceeds
|
|
12045
|
+
* the duration.
|
|
12046
|
+
*
|
|
12047
|
+
* **When to use**
|
|
12048
|
+
*
|
|
12049
|
+
* Use when you need a timeout of an `Effect` to be represented as a typed
|
|
12050
|
+
* failure.
|
|
12051
|
+
*
|
|
12052
|
+
* **Details**
|
|
12053
|
+
*
|
|
12054
|
+
* The `timeout` function allows you to specify a time limit for an
|
|
12055
|
+
* effect's execution. If the effect does not complete within the given time, a
|
|
12056
|
+
* `TimeoutError` is raised. This can be useful for controlling how long your
|
|
12057
|
+
* program waits for a task to finish, ensuring that it doesn't hang
|
|
12058
|
+
* indefinitely if the task takes too long.
|
|
12059
|
+
*
|
|
12060
|
+
* **Gotchas**
|
|
12061
|
+
*
|
|
12062
|
+
* If the timeout wins, the source effect is interrupted.
|
|
12063
|
+
*
|
|
12064
|
+
* **Example** (Failing when work takes too long)
|
|
12065
|
+
*
|
|
12066
|
+
* ```ts import.meta.vitest
|
|
12067
|
+
* import { Effect } from "effect"
|
|
12068
|
+
*
|
|
12069
|
+
* const timedEffect = Effect.never.pipe(Effect.timeout(0))
|
|
12070
|
+
* const error = await Effect.runPromise(Effect.flip(timedEffect))
|
|
12071
|
+
* error._tag // => "TimeoutError"
|
|
12072
|
+
* ```
|
|
12073
|
+
*
|
|
12074
|
+
* @see {@link timeoutOption} for returning `Option.none` on timeout.
|
|
12075
|
+
* @see {@link timeoutOrElse} for a version that allows specifying both success and timeout handlers.
|
|
12076
|
+
*
|
|
12077
|
+
* @stability stable
|
|
12078
|
+
* @category delays & timeouts
|
|
12079
|
+
* @since 2.0.0
|
|
12080
|
+
*/
|
|
12081
|
+
const timeout = timeout$1;
|
|
12082
|
+
/**
|
|
11849
12083
|
* Applies a timeout to an effect, lazily evaluating `orElse` after interrupting
|
|
11850
12084
|
* the source if the timeout is reached.
|
|
11851
12085
|
*
|
|
@@ -12458,6 +12692,82 @@ const scopedWith = scopedWith$1;
|
|
|
12458
12692
|
*/
|
|
12459
12693
|
const acquireRelease = acquireRelease$1;
|
|
12460
12694
|
/**
|
|
12695
|
+
* Runs resource acquisition, usage, and release as one bracketed effect.
|
|
12696
|
+
*
|
|
12697
|
+
* **When to use**
|
|
12698
|
+
*
|
|
12699
|
+
* Use to bracket acquire, use, and release logic in one effect.
|
|
12700
|
+
*
|
|
12701
|
+
* **Details**
|
|
12702
|
+
*
|
|
12703
|
+
* `acquireUseRelease` does the following:
|
|
12704
|
+
*
|
|
12705
|
+
* 1. Ensures that the `Effect` value that acquires the resource will not be
|
|
12706
|
+
* interrupted. Note that acquisition may still fail due to internal
|
|
12707
|
+
* reasons (such as an uncaught exception).
|
|
12708
|
+
* 2. Ensures that the `release` `Effect` value will not be interrupted,
|
|
12709
|
+
* and will be executed as long as the acquisition `Effect` value
|
|
12710
|
+
* successfully acquires the resource.
|
|
12711
|
+
*
|
|
12712
|
+
* During the time period between the acquisition and release of the resource,
|
|
12713
|
+
* the `use` `Effect` value will be executed.
|
|
12714
|
+
*
|
|
12715
|
+
* If the `release` `Effect` value fails, then the entire `Effect` value will
|
|
12716
|
+
* fail, even if the `use` `Effect` value succeeds. If this fail-fast behavior
|
|
12717
|
+
* is not desired, errors produced by the `release` `Effect` value can be caught
|
|
12718
|
+
* and ignored.
|
|
12719
|
+
*
|
|
12720
|
+
* **Example** (Acquiring resources with cleanup)
|
|
12721
|
+
*
|
|
12722
|
+
* ```ts import.meta.vitest
|
|
12723
|
+
* import { Effect, Exit } from "effect"
|
|
12724
|
+
* const output: Array<unknown> = []
|
|
12725
|
+
*
|
|
12726
|
+
* interface Database {
|
|
12727
|
+
* readonly connection: string
|
|
12728
|
+
* readonly query: (sql: string) => Effect.Effect<string>
|
|
12729
|
+
* }
|
|
12730
|
+
*
|
|
12731
|
+
* const program = Effect.acquireUseRelease(
|
|
12732
|
+
* // Acquire - connect to database
|
|
12733
|
+
* Effect.gen(function*() {
|
|
12734
|
+
* yield* Effect.sync(() => { output.push("Connecting to database...") })
|
|
12735
|
+
* return {
|
|
12736
|
+
* connection: "db://localhost:5432",
|
|
12737
|
+
* query: (sql: string) => Effect.succeed(`Result for: ${sql}`)
|
|
12738
|
+
* }
|
|
12739
|
+
* }),
|
|
12740
|
+
* // Use - perform database operations
|
|
12741
|
+
* (db) =>
|
|
12742
|
+
* Effect.gen(function*() {
|
|
12743
|
+
* yield* Effect.sync(() => { output.push(`Connected to ${db.connection}`) })
|
|
12744
|
+
* const result = yield* db.query("SELECT * FROM users")
|
|
12745
|
+
* yield* Effect.sync(() => { output.push(`Query result: ${result}`) })
|
|
12746
|
+
* return result
|
|
12747
|
+
* }),
|
|
12748
|
+
* // Release - close database connection
|
|
12749
|
+
* (db, exit) =>
|
|
12750
|
+
* Effect.gen(function*() {
|
|
12751
|
+
* if (Exit.isSuccess(exit)) {
|
|
12752
|
+
* yield* Effect.sync(() => { output.push(`Closing connection to ${db.connection} (success)`) })
|
|
12753
|
+
* } else {
|
|
12754
|
+
* yield* Effect.sync(() => { output.push(`Closing connection to ${db.connection} (failure)`) })
|
|
12755
|
+
* }
|
|
12756
|
+
* })
|
|
12757
|
+
* )
|
|
12758
|
+
*
|
|
12759
|
+
* await Effect.runPromise(program)
|
|
12760
|
+
* output // => ["Connecting to database...", "Connected to db://localhost:5432", "Query result: Result for: SELECT * FROM users", "Closing connection to db://localhost:5432 (success)"]
|
|
12761
|
+
* ```
|
|
12762
|
+
*
|
|
12763
|
+
* @see {@link acquireRelease} for scoped resources whose use happens later
|
|
12764
|
+
*
|
|
12765
|
+
* @stability stable
|
|
12766
|
+
* @category resource management
|
|
12767
|
+
* @since 2.0.0
|
|
12768
|
+
*/
|
|
12769
|
+
const acquireUseRelease = acquireUseRelease$1;
|
|
12770
|
+
/**
|
|
12461
12771
|
* Adds a finalizer to the current scope.
|
|
12462
12772
|
*
|
|
12463
12773
|
* **When to use**
|
|
@@ -18066,7 +18376,7 @@ const defaultLeafHook = (issue) => {
|
|
|
18066
18376
|
*/
|
|
18067
18377
|
const defaultCheckHook = (issue) => findMessage(issue.issue) ?? findMessage(issue);
|
|
18068
18378
|
function formatInput(issue) {
|
|
18069
|
-
return hasInput(issue) ? format$
|
|
18379
|
+
return hasInput(issue) ? format$3(issue.input) : void 0;
|
|
18070
18380
|
}
|
|
18071
18381
|
function findExpected(issue) {
|
|
18072
18382
|
const expected = issue.annotations?.expected;
|
|
@@ -18978,9 +19288,9 @@ const decodeJsonError = (input) => {
|
|
|
18978
19288
|
const encodeUnknownAsJson = (input) => {
|
|
18979
19289
|
try {
|
|
18980
19290
|
const json = formatJson(input);
|
|
18981
|
-
return json === void 0 ? format$
|
|
19291
|
+
return json === void 0 ? format$3(input) : JSON.parse(json);
|
|
18982
19292
|
} catch {
|
|
18983
|
-
return format$
|
|
19293
|
+
return format$3(input);
|
|
18984
19294
|
}
|
|
18985
19295
|
};
|
|
18986
19296
|
const encodeJsonError = (input, options, encodeDefect) => {
|
|
@@ -19346,7 +19656,7 @@ const Literal$1 = class extends ASTNodeImpl {
|
|
|
19346
19656
|
literal;
|
|
19347
19657
|
constructor(literal, annotations, checks, encoding, context) {
|
|
19348
19658
|
super(annotations, checks, encoding, context);
|
|
19349
|
-
if (typeof literal === "number" && !globalThis.Number.isFinite(literal)) throw new Error(`A numeric literal must be finite, got ${format$
|
|
19659
|
+
if (typeof literal === "number" && !globalThis.Number.isFinite(literal)) throw new Error(`A numeric literal must be finite, got ${format$3(literal)}`);
|
|
19350
19660
|
this.literal = literal;
|
|
19351
19661
|
}
|
|
19352
19662
|
/** @internal */
|
|
@@ -25686,7 +25996,7 @@ function isPattern(regExp, annotations) {
|
|
|
25686
25996
|
const source = regExp.source;
|
|
25687
25997
|
const flags = regExp.flags;
|
|
25688
25998
|
const canExport = /^[dg]*uy?$/.test(flags);
|
|
25689
|
-
const runtimeRegExp = flags === "" ? `new RegExp(${format$
|
|
25999
|
+
const runtimeRegExp = flags === "" ? `new RegExp(${format$3(source)})` : `new RegExp(${format$3(source)}, ${format$3(flags)})`;
|
|
25690
26000
|
return isPattern$1(regExp, {
|
|
25691
26001
|
toJsonSchema: () => canExport ? { pattern: flags.endsWith("y") ? `^(?:${source})` : source } : [{}, true],
|
|
25692
26002
|
toCode: () => ({ runtime: `Schema.isPattern(${runtimeRegExp})` }),
|
|
@@ -27118,7 +27428,7 @@ const String$5 = /*#__PURE__*/ makePrimitive("String", (value) => succeed$3(valu
|
|
|
27118
27428
|
*/
|
|
27119
27429
|
const Choice = (choices) => {
|
|
27120
27430
|
const choiceMap = new Map(choices);
|
|
27121
|
-
const validChoices = choices.map(([key]) => format$
|
|
27431
|
+
const validChoices = choices.map(([key]) => format$3(key)).join(" | ");
|
|
27122
27432
|
const primitive = makePrimitive("Choice", (value) => {
|
|
27123
27433
|
if (choiceMap.has(value)) return succeed$3(choiceMap.get(value));
|
|
27124
27434
|
return fail$1(validChoices);
|
|
@@ -33032,7 +33342,7 @@ const runWith = (command, config) => {
|
|
|
33032
33342
|
};
|
|
33033
33343
|
//#endregion
|
|
33034
33344
|
//#region package.json
|
|
33035
|
-
var version = "0.
|
|
33345
|
+
var version = "0.11.0";
|
|
33036
33346
|
//#endregion
|
|
33037
33347
|
//#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/Logger.js
|
|
33038
33348
|
const TypeId = "~effect/Logger";
|
|
@@ -33877,7 +34187,7 @@ var ParseOptions;
|
|
|
33877
34187
|
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
|
|
33878
34188
|
* Therefore always check the errors list to find out if the input was valid.
|
|
33879
34189
|
*/
|
|
33880
|
-
function parse$
|
|
34190
|
+
function parse$2(text, errors = [], options = ParseOptions.DEFAULT) {
|
|
33881
34191
|
let currentProperty = null;
|
|
33882
34192
|
let currentParent = [];
|
|
33883
34193
|
const previousParents = [];
|
|
@@ -34453,7 +34763,7 @@ var SyntaxKind;
|
|
|
34453
34763
|
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
|
|
34454
34764
|
* Therefore, always check the errors list to find out if the input was valid.
|
|
34455
34765
|
*/
|
|
34456
|
-
const parse = parse$
|
|
34766
|
+
const parse$1 = parse$2;
|
|
34457
34767
|
/**
|
|
34458
34768
|
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
|
|
34459
34769
|
*/
|
|
@@ -35989,6 +36299,7 @@ const Origin = String$6.check(isPattern(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/, {
|
|
|
35989
36299
|
const Ref = NonEmptyString;
|
|
35990
36300
|
const isPin = (ref) => /^[0-9a-f]{40}$/.test(ref);
|
|
35991
36301
|
const ProviderName = Literals$2([
|
|
36302
|
+
"files",
|
|
35992
36303
|
"vp",
|
|
35993
36304
|
"brew",
|
|
35994
36305
|
"cask",
|
|
@@ -36072,12 +36383,41 @@ const ReposSection = section({ cloneRoot: optionalKey(variants(withShorthand(Str
|
|
|
36072
36383
|
decode: (path) => ({ path }),
|
|
36073
36384
|
encode: (root) => root.path
|
|
36074
36385
|
}))).annotateKey({ description: `Where a Machine keeps its Managed Clones: a path, or a list of \`{ path, platforms?, machines? }\` where a Machine gets the first that targets it. \`~\` expands on that Machine. Default: \`${DEFAULT_CLONE_ROOT}\`.` }) }, Record(NonEmptyString, variants(TrackedRepo)), `Tracked Repos, keyed by name, which names the Managed Clone and the \`repos:<name>\` Item. Each is \`{ origin, ref, install, ... }\`, ${VARIANTS}.`);
|
|
36386
|
+
const HomePath = NonEmptyString.check(isPattern(/^(\/|~\/)./, {
|
|
36387
|
+
title: "path",
|
|
36388
|
+
description: "an absolute path, or one under ~"
|
|
36389
|
+
}));
|
|
36390
|
+
const OWN_DIRS = ["~/.config/fleetfizz", "~/.local/state/fleetfizz"];
|
|
36391
|
+
const SharedFilePath = HomePath.check(makeFilter((path) => {
|
|
36392
|
+
const p = path.replace(/\/+$/, "");
|
|
36393
|
+
const dir = OWN_DIRS.find((d) => p === "~" || d === p || d.startsWith(`${p}/`) || p.startsWith(`${d}/`));
|
|
36394
|
+
return dir === void 0 || `overlaps ${dir}, which a Ship writes itself`;
|
|
36395
|
+
}, {
|
|
36396
|
+
title: "Shared File path",
|
|
36397
|
+
description: "a path outside fleetfizz's own directories"
|
|
36398
|
+
}));
|
|
36399
|
+
const SharedFile = Struct({
|
|
36400
|
+
...Targeting,
|
|
36401
|
+
path: SharedFilePath.annotateKey({ description: "A file or directory, absolute or under `~`, which expands on each Machine." }),
|
|
36402
|
+
exclude: optionalKey(ArraySchema(NonEmptyString)).annotateKey({ description: "Globs inside a directory that are neither copied nor deleted. One without a `/` matches a name at any depth. `.DS_Store` and `._*` are always excluded. Every Variant of one Shared File has the same exclude." })
|
|
36403
|
+
});
|
|
36404
|
+
const sameExclude = (a, b) => [...new Set(a.exclude ?? [])].toSorted().join("\n") === [...new Set(b.exclude ?? [])].toSorted().join("\n");
|
|
36405
|
+
const FilesSection = section({}, Record(NonEmptyString, variants(withShorthand(SharedFile, SharedFilePath, {
|
|
36406
|
+
decode: (path) => ({ path }),
|
|
36407
|
+
encode: (file) => file.path
|
|
36408
|
+
}))).check(makeFilter((items) => {
|
|
36409
|
+
const badName = Object.keys(items).find((n) => !/^[A-Za-z0-9._-]+$/.test(n) || n === "." || n === "..");
|
|
36410
|
+
if (badName !== void 0) return `"${badName}": a Shared File's name uses only letters, digits, ., _ and -, and is not . or ..`;
|
|
36411
|
+
const name = Object.entries(items).find(([, item]) => Array.isArray(item) && item.some((v) => !sameExclude(v, item[0])))?.[0];
|
|
36412
|
+
return name === void 0 || `"${name}": every Variant needs the same exclude`;
|
|
36413
|
+
}, {
|
|
36414
|
+
title: "Shared Files",
|
|
36415
|
+
description: "names of letters, digits, ., _ and -, and one exclude across a Shared File's Variants"
|
|
36416
|
+
})), `Shared Files, keyed by name, which names the \`files:<name>\` Item. Each is a path, \`{ path, exclude?, platforms?, machines? }\`, ${VARIANTS}.`);
|
|
36075
36417
|
const FleetConfig = Struct({
|
|
36076
36418
|
$schema: optionalKey(String$6),
|
|
36077
|
-
shippedAt: optionalKey(String$6),
|
|
36078
|
-
shippedFrom: optionalKey(NonEmptyString),
|
|
36079
|
-
shippedTo: optionalKey(Record(NonEmptyString, String$6)),
|
|
36080
36419
|
machines: ArraySchema(NonEmptyString).check(isMinLength(1, { message: "list at least one Machine" }), isUnique({ message: "list each Machine once" })).annotateKey({ description: "The Fleet: each Machine's tailnet name. A tailnet member that is not listed is not part of the Fleet." }),
|
|
36420
|
+
files: optionalKey(FilesSection).annotateKey({ description: "Files and directories copied from the Starting Machine to every targeted Machine. A directory is mirrored." }),
|
|
36081
36421
|
repos: optionalKey(ReposSection).annotateKey({ description: "Each Tracked Repo is cloned on every targeted Machine and installed from its origin ref." }),
|
|
36082
36422
|
vp: optionalKey(VpSection).annotateKey({ description: "The vite-plus toolchain." }),
|
|
36083
36423
|
brew: optionalKey(BrewSection).annotateKey({ description: "Homebrew Formulae. Needs Homebrew on the Machine; a Ship never installs it." }),
|
|
@@ -36133,10 +36473,17 @@ const repeatedKeys = (text) => {
|
|
|
36133
36473
|
if (tree !== void 0) visit(tree, []);
|
|
36134
36474
|
return problems;
|
|
36135
36475
|
};
|
|
36476
|
+
const STAMPS = [
|
|
36477
|
+
"shippedAt",
|
|
36478
|
+
"shippedFrom",
|
|
36479
|
+
"shippedTo"
|
|
36480
|
+
];
|
|
36136
36481
|
const movedKeys = (parsed) => {
|
|
36137
36482
|
if (typeof parsed !== "object" || parsed === null) return [];
|
|
36138
36483
|
const config = parsed;
|
|
36484
|
+
const stamps = STAMPS.filter((key) => key in parsed);
|
|
36139
36485
|
return [
|
|
36486
|
+
...stamps.length > 0 ? [`${stamps.join(", ")}: no longer written; a Ship keeps them in its Install Record now, so delete them`] : [],
|
|
36140
36487
|
...typeof config.machines === "object" && config.machines !== null && !Array.isArray(config.machines) ? ["machines is now a list of names, and machines.<name>.clones is repos.cloneRoot"] : [],
|
|
36141
36488
|
...Array.isArray(config.repos) ? ["repos is now { items: { <name>: { origin, ref, install } } }, keyed by each repo's name"] : [],
|
|
36142
36489
|
...typeof config.vp === "object" && config.vp !== null && "globals" in config.vp ? ["vp.globals is now vp.items"] : []
|
|
@@ -36144,7 +36491,7 @@ const movedKeys = (parsed) => {
|
|
|
36144
36491
|
};
|
|
36145
36492
|
const parseConfig = fn("parseConfig")(function* (path, text) {
|
|
36146
36493
|
const parseErrors = [];
|
|
36147
|
-
const parsed = parse(text, parseErrors, { allowTrailingComma: true });
|
|
36494
|
+
const parsed = parse$1(text, parseErrors, { allowTrailingComma: true });
|
|
36148
36495
|
if (parseErrors.length > 0) return yield* new ConfigError({
|
|
36149
36496
|
path,
|
|
36150
36497
|
problems: parseErrors.map((e) => `line ${lineOf(text, e.offset)}: ${printParseErrorCode(e.error)}`)
|
|
@@ -36171,49 +36518,26 @@ const loadConfig = fn("loadConfig")(function* (path) {
|
|
|
36171
36518
|
const text = yield* fs.readFileString(path);
|
|
36172
36519
|
return yield* parseConfig(path, text);
|
|
36173
36520
|
});
|
|
36174
|
-
|
|
36175
|
-
|
|
36176
|
-
|
|
36177
|
-
|
|
36178
|
-
};
|
|
36179
|
-
const
|
|
36180
|
-
|
|
36181
|
-
|
|
36182
|
-
|
|
36183
|
-
|
|
36184
|
-
]
|
|
36185
|
-
|
|
36186
|
-
|
|
36187
|
-
|
|
36188
|
-
|
|
36189
|
-
|
|
36190
|
-
|
|
36191
|
-
...options,
|
|
36192
|
-
getInsertionIndex: insertAfter(STAMP_KEYS.slice(0, STAMP_KEYS.indexOf(key)))
|
|
36193
|
-
}));
|
|
36194
|
-
const previous = parse(text, void 0, { allowTrailingComma: true });
|
|
36195
|
-
const fleet = machinesOf(previous);
|
|
36196
|
-
const shippedTo = {
|
|
36197
|
-
...Object.fromEntries(Object.entries(shippedToOf(previous)).filter(([name]) => fleet.includes(name))),
|
|
36198
|
-
...Object.fromEntries([...stamp.reached, ...stamp.own === void 0 ? [] : [stamp.own]].map((name) => [name, stamp.shippedAt]))
|
|
36199
|
-
};
|
|
36200
|
-
let out = set(text, "shippedAt", stamp.shippedAt);
|
|
36201
|
-
out = set(out, "shippedFrom", stamp.shippedFrom);
|
|
36202
|
-
return set(out, "shippedTo", shippedTo);
|
|
36203
|
-
};
|
|
36204
|
-
const machinesOf = (config) => {
|
|
36205
|
-
if (typeof config !== "object" || config === null || !("machines" in config)) return [];
|
|
36206
|
-
const value = config.machines;
|
|
36207
|
-
return Array.isArray(value) ? value.filter((name) => typeof name === "string") : [];
|
|
36208
|
-
};
|
|
36209
|
-
const shippedToOf = (config) => {
|
|
36210
|
-
if (typeof config !== "object" || config === null || !("shippedTo" in config)) return {};
|
|
36211
|
-
const value = config.shippedTo;
|
|
36212
|
-
if (typeof value !== "object" || value === null) return {};
|
|
36213
|
-
return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
|
|
36521
|
+
const ConfigRecord = Struct({
|
|
36522
|
+
ship: NonEmptyString,
|
|
36523
|
+
sha: String$6,
|
|
36524
|
+
reached: Record(NonEmptyString, String$6)
|
|
36525
|
+
});
|
|
36526
|
+
const decodeConfigRecord = decodeUnknownOption(ConfigRecord);
|
|
36527
|
+
const decodeConfigRecordJson = decodeUnknownOption(fromJsonString(ConfigRecord));
|
|
36528
|
+
const encodeConfigRecord = encodeSync(fromJsonString(ConfigRecord));
|
|
36529
|
+
const shaOf = (text) => promise(() => globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(text))).pipe(map$5((digest) => [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("")));
|
|
36530
|
+
const reachedAfter = (previous, at, machines, fleet) => ({
|
|
36531
|
+
...Object.fromEntries(Object.entries(previous).filter(([name]) => fleet.includes(name))),
|
|
36532
|
+
...Object.fromEntries(machines.map((name) => [name, at]))
|
|
36533
|
+
});
|
|
36534
|
+
const mergeReached = (all) => {
|
|
36535
|
+
const merged = {};
|
|
36536
|
+
for (const reached of all) for (const [name, at] of Object.entries(reached)) if (isNewer(at, merged[name])) merged[name] = at;
|
|
36537
|
+
return merged;
|
|
36214
36538
|
};
|
|
36215
36539
|
const isNewer = (remote, local) => remote !== void 0 && (local === void 0 || remote > local);
|
|
36216
|
-
const
|
|
36540
|
+
const staleWhy = (copy, base) => copy.record === void 0 ? "unrecorded" : copy.record.sha !== copy.sha ? "edited" : isNewer(copy.record.reached[copy.machine], base[copy.machine]) ? "newer" : void 0;
|
|
36217
36541
|
//#endregion
|
|
36218
36542
|
//#region ../../packages/remote/src/shell.ts
|
|
36219
36543
|
const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
@@ -36232,6 +36556,7 @@ const prelude = [
|
|
|
36232
36556
|
`export PATH="$PATH:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin"`,
|
|
36233
36557
|
`ff_json() { printf '%s' "$1" | sed -e 's/\\\\/\\\\\\\\/g' -e 's/"/\\\\"/g' | tr -d '\\n'; }`,
|
|
36234
36558
|
`ff_state() { printf 'ff-state %s\\n' "$1"; }`,
|
|
36559
|
+
`ff_sha() { if command -v shasum >/dev/null 2>&1; then shasum -a 256; else sha256sum; fi | cut -d" " -f1; }`,
|
|
36235
36560
|
`ff_at() { ff_state "{\\"provider\\":\\"$(ff_json "$1")\\",\\"fact\\":\\"at\\",\\"item\\":\\"$(ff_json "$2")\\",\\"at\\":\\"$(ff_json "$3")\\"}"; }`
|
|
36236
36561
|
].join("\n");
|
|
36237
36562
|
const fingerprintFn = [
|
|
@@ -36250,6 +36575,7 @@ const LOGS_DIR = `${STATE_DIR}/logs`;
|
|
|
36250
36575
|
const CONFIG_DIR_SH = "\"${XDG_CONFIG_HOME:-$HOME/.config}/fleetfizz\"";
|
|
36251
36576
|
const recordDir = (provider) => `${quotePath(RECORDS_DIR)}/${quote(provider)}`;
|
|
36252
36577
|
const recordFile = (provider, item) => `${recordDir(provider)}/${quote(`${item}.json`)}`;
|
|
36578
|
+
const CONFIG_RECORD = `${RECORDS_DIR}/config.json`;
|
|
36253
36579
|
const LEGACY_RECORD_PROVIDER = "repos";
|
|
36254
36580
|
const recordFact = (provider, item) => [
|
|
36255
36581
|
`ff_r=${recordFile(provider, item)}`,
|
|
@@ -36266,7 +36592,7 @@ const checkScript = (input) => {
|
|
|
36266
36592
|
lines.push(provider.observe(items, input.scope));
|
|
36267
36593
|
if (provider.rule === "record") for (const item of items) lines.push(...recordFact(provider.name, item.name));
|
|
36268
36594
|
}
|
|
36269
|
-
lines.push(`ff_c=${CONFIG_DIR_SH}/${quote(CONFIG_FILE)}`, `if [ -f "$ff_c" ]; then ff_state "{\\"fact\\":\\"config\\",\\"present\\":true,\\"
|
|
36595
|
+
lines.push(`ff_c=${CONFIG_DIR_SH}/${quote(CONFIG_FILE)}`, `if [ -f "$ff_c" ]; then ff_b=$(base64 < "$ff_c" | tr -d '\\n'); ff_state "{\\"fact\\":\\"config\\",\\"present\\":true,\\"sha\\":\\"$(printf '%s' "$ff_b" | base64 -d | ff_sha)\\",\\"base64\\":\\"$ff_b\\"}"; else ff_state '{"fact":"config","present":false}'; fi`, `ff_r=${quotePath(CONFIG_RECORD)}`, `if [ -f "$ff_r" ]; then ff_state "{\\"fact\\":\\"configRecord\\",\\"record\\":$(tr -d '\\n' < "$ff_r")}"; fi`);
|
|
36270
36596
|
lines.push(`ff_state '{"fact":"end"}'`);
|
|
36271
36597
|
return lines.join("\n");
|
|
36272
36598
|
};
|
|
@@ -36283,7 +36609,7 @@ const StepSkip = Struct({
|
|
|
36283
36609
|
id: String$6,
|
|
36284
36610
|
reason: String$6
|
|
36285
36611
|
});
|
|
36286
|
-
const decodeFact$
|
|
36612
|
+
const decodeFact$4 = decodeUnknownResult(fromJsonString(Fact));
|
|
36287
36613
|
const decodeStart = decodeUnknownResult(fromJsonString(StepStart));
|
|
36288
36614
|
const decodeDone = decodeUnknownResult(fromJsonString(StepDone));
|
|
36289
36615
|
const decodeSkip = decodeUnknownResult(fromJsonString(StepSkip));
|
|
@@ -36293,7 +36619,7 @@ const DONE = "ff-done ";
|
|
|
36293
36619
|
const SKIP = "ff-skip ";
|
|
36294
36620
|
const parseLine = (line) => {
|
|
36295
36621
|
if (line.startsWith(STATE)) {
|
|
36296
|
-
const result = decodeFact$
|
|
36622
|
+
const result = decodeFact$4(line.slice(9));
|
|
36297
36623
|
if (result._tag === "Success") return {
|
|
36298
36624
|
kind: "state",
|
|
36299
36625
|
fact: result.success
|
|
@@ -36339,12 +36665,14 @@ const marked = (id, run, failed = []) => [
|
|
|
36339
36665
|
doneLine(id, true),
|
|
36340
36666
|
"fi"
|
|
36341
36667
|
];
|
|
36342
|
-
const configStep = () => {
|
|
36668
|
+
const configStep = (record) => {
|
|
36343
36669
|
const dir = CONFIG_DIR_SH;
|
|
36344
36670
|
const write = (variable, file) => `printf '%s' "$${variable}" | base64 -d > ${dir}/${quote(`${file}.tmp`)} && mv -f ${dir}/${quote(`${file}.tmp`)} ${dir}/${quote(file)}`;
|
|
36345
|
-
|
|
36671
|
+
const recordFile = quotePath(CONFIG_RECORD);
|
|
36672
|
+
const recordTmp = quotePath(`${CONFIG_RECORD}.tmp`);
|
|
36673
|
+
return marked(CONFIG_STEP, [`( mkdir -p ${dir} ${quotePath(RECORDS_DIR)} && ${write("ff_config", CONFIG_FILE)} && ${write("ff_schema", SCHEMA_FILE)} && printf '%s\\n' ${quote(record)} > ${recordTmp} && mv -f ${recordTmp} ${recordFile} )`]);
|
|
36346
36674
|
};
|
|
36347
|
-
const providerSteps = (plan) => {
|
|
36675
|
+
const providerSteps = (plan, offsets) => {
|
|
36348
36676
|
if (plan.decisions.length === 0 && plan.steps.length === 0) return [];
|
|
36349
36677
|
const held = `ff_held_${plan.name}`;
|
|
36350
36678
|
const items = [...new Set(plan.steps.flatMap((step) => step.items))];
|
|
@@ -36353,6 +36681,7 @@ const providerSteps = (plan) => {
|
|
|
36353
36681
|
`if [ -n "${s.items.map((item) => `\${${flag(item)}:-}`).join("")}" ]; then`,
|
|
36354
36682
|
skipLine(s.id, "'an earlier step failed'"),
|
|
36355
36683
|
"else",
|
|
36684
|
+
...(s.input ?? []).map((_, i) => `ff_input_${i}="$ff_in_${(offsets.get(s) ?? 0) + i}"`),
|
|
36356
36685
|
...marked(s.id, s.run, [...s.items.map((item) => `${flag(item)}=1`), `${held}=1`]),
|
|
36357
36686
|
"fi"
|
|
36358
36687
|
];
|
|
@@ -36368,10 +36697,21 @@ const providerSteps = (plan) => {
|
|
|
36368
36697
|
"fi"
|
|
36369
36698
|
];
|
|
36370
36699
|
};
|
|
36371
|
-
const
|
|
36700
|
+
const inputsOf = (plan) => plan.providers.flatMap((p) => p.steps.flatMap((step) => step.input ?? []));
|
|
36701
|
+
const inputOffsets = (plan) => {
|
|
36702
|
+
const offsets = /* @__PURE__ */ new Map();
|
|
36703
|
+
let next = 0;
|
|
36704
|
+
for (const step of plan.providers.flatMap((p) => p.steps)) {
|
|
36705
|
+
offsets.set(step, next);
|
|
36706
|
+
next += step.input?.length ?? 0;
|
|
36707
|
+
}
|
|
36708
|
+
return offsets;
|
|
36709
|
+
};
|
|
36710
|
+
const applyStdin = (token, config, schema, plan) => [
|
|
36372
36711
|
token ?? "",
|
|
36373
36712
|
encodeBase64(config),
|
|
36374
|
-
encodeBase64(schema)
|
|
36713
|
+
encodeBase64(schema),
|
|
36714
|
+
...inputsOf(plan).map((input) => input.reveal())
|
|
36375
36715
|
];
|
|
36376
36716
|
const encodeBase64 = (text) => {
|
|
36377
36717
|
const bytes = new TextEncoder().encode(text);
|
|
@@ -36379,18 +36719,30 @@ const encodeBase64 = (text) => {
|
|
|
36379
36719
|
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
36380
36720
|
return btoa(binary);
|
|
36381
36721
|
};
|
|
36382
|
-
const applyScript = (plan) =>
|
|
36722
|
+
const applyScript = (plan, configRecord) => {
|
|
36723
|
+
const offsets = inputOffsets(plan);
|
|
36724
|
+
return [
|
|
36725
|
+
prelude,
|
|
36726
|
+
fingerprintFn,
|
|
36727
|
+
`ff_lit() { printf '%s' "$1"; }`,
|
|
36728
|
+
`IFS= read -r ff_token || ff_token=""`,
|
|
36729
|
+
`IFS= read -r ff_config || ff_config=""`,
|
|
36730
|
+
`IFS= read -r ff_schema || ff_schema=""`,
|
|
36731
|
+
...inputsOf(plan).map((_, i) => `IFS= read -r ff_in_${i} || exit 1`),
|
|
36732
|
+
`ff_git() { if [ -n "$ff_token" ]; then FF_TOKEN="$ff_token" git -c credential.helper= -c 'credential.helper=!f() { echo username=x-access-token; echo "password=$FF_TOKEN"; }; f' "$@"; else git "$@"; fi; }`,
|
|
36733
|
+
...configStep(configRecord),
|
|
36734
|
+
...plan.providers.flatMap((p) => providerSteps(p, offsets)),
|
|
36735
|
+
doneLine("end", true)
|
|
36736
|
+
].join("\n");
|
|
36737
|
+
};
|
|
36738
|
+
const stepScript = (step) => [
|
|
36383
36739
|
prelude,
|
|
36384
36740
|
fingerprintFn,
|
|
36385
36741
|
`ff_lit() { printf '%s' "$1"; }`,
|
|
36386
|
-
`IFS= read -r
|
|
36387
|
-
|
|
36388
|
-
`IFS= read -r ff_schema || ff_schema=""`,
|
|
36389
|
-
`ff_git() { if [ -n "$ff_token" ]; then FF_TOKEN="$ff_token" git -c credential.helper= -c 'credential.helper=!f() { echo username=x-access-token; echo "password=$FF_TOKEN"; }; f' "$@"; else git "$@"; fi; }`,
|
|
36390
|
-
...configStep(),
|
|
36391
|
-
...plan.providers.flatMap(providerSteps),
|
|
36392
|
-
doneLine("end", true)
|
|
36742
|
+
...(step.input ?? []).flatMap((_, i) => [`IFS= read -r ff_in_${i} || ff_in_${i}=""`, `ff_input_${i}="$ff_in_${i}"`]),
|
|
36743
|
+
...marked(step.id, step.run)
|
|
36393
36744
|
].join("\n");
|
|
36745
|
+
const stepStdin = (step) => (step.input ?? []).map((i) => i.reveal());
|
|
36394
36746
|
//#endregion
|
|
36395
36747
|
//#region ../../packages/remote/src/tailnet.ts
|
|
36396
36748
|
const StatusNode = Struct({
|
|
@@ -36544,6 +36896,20 @@ const resolveVersions = fn("resolveVersions")(function* (packages) {
|
|
|
36544
36896
|
const entries = yield* forEach$1(unique, (pkg) => resolveOne(pkg).pipe(map$5((v) => [pkg, v])), { concurrency: 8 });
|
|
36545
36897
|
return Object.fromEntries(entries);
|
|
36546
36898
|
});
|
|
36899
|
+
//#endregion
|
|
36900
|
+
//#region src/configRecord.ts
|
|
36901
|
+
const recordPath = () => CONFIG_RECORD.replace(/^~/, process.env.HOME ?? "~");
|
|
36902
|
+
const readConfigRecord = gen(function* () {
|
|
36903
|
+
const text = yield* (yield* FileSystem).readFileString(recordPath()).pipe(option);
|
|
36904
|
+
return getOrUndefined$1(flatMap$3(text, decodeConfigRecordJson));
|
|
36905
|
+
});
|
|
36906
|
+
const writeConfigRecord = fn("writeConfigRecord")(function* (json) {
|
|
36907
|
+
const fs = yield* FileSystem;
|
|
36908
|
+
const path = recordPath();
|
|
36909
|
+
yield* fs.makeDirectory(path.slice(0, path.lastIndexOf("/")), { recursive: true });
|
|
36910
|
+
yield* fs.writeFileString(`${path}.tmp`, `${json}\n`);
|
|
36911
|
+
yield* fs.rename(`${path}.tmp`, path);
|
|
36912
|
+
});
|
|
36547
36913
|
var LogsError = class extends Error {
|
|
36548
36914
|
name = "LogsError";
|
|
36549
36915
|
};
|
|
@@ -36616,7 +36982,7 @@ const logs = fn("logs")(function* (flags) {
|
|
|
36616
36982
|
for (const id of local.toReversed()) yield* log(id);
|
|
36617
36983
|
return;
|
|
36618
36984
|
}
|
|
36619
|
-
const id = flags.id ?? (yield* latestShip(local, self
|
|
36985
|
+
const id = flags.id ?? (yield* latestShip(local, self));
|
|
36620
36986
|
const ship = parseShipId(id);
|
|
36621
36987
|
if (ship === void 0) return yield* fail$1(new LogsError(`not a Ship id: ${id} (fizz logs --list shows them)`));
|
|
36622
36988
|
let missing = false;
|
|
@@ -36627,17 +36993,13 @@ const logs = fn("logs")(function* (flags) {
|
|
|
36627
36993
|
});
|
|
36628
36994
|
if (missing) return yield* fail$1(new LogsError(`no log for ${id} on ${ship.from}; a Machine keeps its last 20`));
|
|
36629
36995
|
});
|
|
36630
|
-
const latestShip = fn("latestShip")(function* (local, self
|
|
36996
|
+
const latestShip = fn("latestShip")(function* (local, self) {
|
|
36631
36997
|
const newest = local.at(-1);
|
|
36632
|
-
const
|
|
36633
|
-
const from =
|
|
36634
|
-
const at = Date.parse(
|
|
36998
|
+
const record = yield* readConfigRecord;
|
|
36999
|
+
const from = parseShipId(record?.ship ?? "")?.from;
|
|
37000
|
+
const at = self === void 0 ? Infinity : Date.parse(record?.reached[self] ?? "");
|
|
36635
37001
|
const newestStarted = parseShipId(newest ?? "")?.started ?? -Infinity;
|
|
36636
|
-
if (from !== void 0 && from !== self && at > newestStarted)
|
|
36637
|
-
const there = (yield* shipIds(yield* targetOf(from))).filter((id) => (parseShipId(id)?.started ?? Infinity) <= at).at(-1);
|
|
36638
|
-
if (there === void 0) return yield* fail$1(new LogsError(`the last Ship to reach this Machine (from ${from} at ${config?.shippedAt}) has no log there`));
|
|
36639
|
-
return there;
|
|
36640
|
-
}
|
|
37002
|
+
if (record !== void 0 && from !== void 0 && from !== self && at > newestStarted) return record.ship;
|
|
36641
37003
|
if (newest === void 0) return yield* fail$1(new LogsError("no Ship Log on this Machine yet"));
|
|
36642
37004
|
return newest;
|
|
36643
37005
|
});
|
|
@@ -60836,7 +61198,7 @@ if (process$1.env["DEV"] === "true") {
|
|
|
60836
61198
|
} catch {}
|
|
60837
61199
|
if (isDevtoolsInstalled) await init_devtools().then(() => devtools_exports);
|
|
60838
61200
|
}
|
|
60839
|
-
const diff = (before, after) => {
|
|
61201
|
+
const diff$1 = (before, after) => {
|
|
60840
61202
|
if (before === after) return;
|
|
60841
61203
|
if (!before) return after;
|
|
60842
61204
|
const changed = {};
|
|
@@ -61011,8 +61373,8 @@ const hostConfig = {
|
|
|
61011
61373
|
const rootNode = findRootNode(node);
|
|
61012
61374
|
if (rootNode) rootNode.isStaticDirty = true;
|
|
61013
61375
|
}
|
|
61014
|
-
const props = diff(oldProps, newProps);
|
|
61015
|
-
const style = diff(oldProps["style"], newProps["style"]);
|
|
61376
|
+
const props = diff$1(oldProps, newProps);
|
|
61377
|
+
const style = diff$1(oldProps["style"], newProps["style"]);
|
|
61016
61378
|
if (!props && !style) return;
|
|
61017
61379
|
if (props) for (const [key, value] of Object.entries(props)) {
|
|
61018
61380
|
if (key === "style") {
|
|
@@ -64727,6 +65089,14 @@ const useInput = (inputHandler, options = {}) => {
|
|
|
64727
65089
|
}, [options.isActive, internal_eventEmitter]);
|
|
64728
65090
|
};
|
|
64729
65091
|
//#endregion
|
|
65092
|
+
//#region ../../node_modules/.pnpm/ink@8.0.0_@types+react@19.3.0_react@19.3.0/node_modules/ink/build/hooks/use-app.js
|
|
65093
|
+
/**
|
|
65094
|
+
A React hook that returns app lifecycle methods like `exit()` and `waitUntilRenderFlush()`.
|
|
65095
|
+
*/
|
|
65096
|
+
function useApp() {
|
|
65097
|
+
return (0, import_react.use)(AppContext);
|
|
65098
|
+
}
|
|
65099
|
+
//#endregion
|
|
64730
65100
|
//#region ../../node_modules/.pnpm/ink@8.0.0_@types+react@19.3.0_react@19.3.0/node_modules/ink/build/hooks/use-stdout.js
|
|
64731
65101
|
/**
|
|
64732
65102
|
A React hook that returns the stdout stream where Ink renders your app.
|
|
@@ -64886,9 +65256,25 @@ const QUESTION_CHOICES = {
|
|
|
64886
65256
|
"downgrade"
|
|
64887
65257
|
],
|
|
64888
65258
|
config: ["overwrite", "stop"],
|
|
64889
|
-
mode: ["managed", "skip"]
|
|
65259
|
+
mode: ["managed", "skip"],
|
|
65260
|
+
file: ["overwrite", "skip"]
|
|
64890
65261
|
};
|
|
64891
65262
|
const isQuestionKind = (kind) => kind in QUESTION_CHOICES;
|
|
65263
|
+
var Hidden = class {
|
|
65264
|
+
#value;
|
|
65265
|
+
constructor(value) {
|
|
65266
|
+
this.#value = value;
|
|
65267
|
+
}
|
|
65268
|
+
reveal() {
|
|
65269
|
+
return this.#value;
|
|
65270
|
+
}
|
|
65271
|
+
toJSON() {
|
|
65272
|
+
return "[hidden]";
|
|
65273
|
+
}
|
|
65274
|
+
toString() {
|
|
65275
|
+
return "[hidden]";
|
|
65276
|
+
}
|
|
65277
|
+
};
|
|
64892
65278
|
const rangeQuestion = (subject, installed, range, bumpPath) => ({
|
|
64893
65279
|
kind: "range",
|
|
64894
65280
|
subject,
|
|
@@ -65022,8 +65408,13 @@ const EngineFact = Union([
|
|
|
65022
65408
|
Struct({
|
|
65023
65409
|
fact: Literal("config"),
|
|
65024
65410
|
present: Boolean$4,
|
|
65411
|
+
sha: optionalKey(String$6),
|
|
65025
65412
|
base64: optionalKey(String$6)
|
|
65026
65413
|
}),
|
|
65414
|
+
Struct({
|
|
65415
|
+
fact: Literal("configRecord"),
|
|
65416
|
+
record: Unknown
|
|
65417
|
+
}),
|
|
65027
65418
|
Struct({
|
|
65028
65419
|
fact: Literal("record"),
|
|
65029
65420
|
provider: String$6,
|
|
@@ -65066,9 +65457,18 @@ const assembleState = (facts) => {
|
|
|
65066
65457
|
};
|
|
65067
65458
|
break;
|
|
65068
65459
|
case "config":
|
|
65069
|
-
if (f.present && f.base64 !== void 0) state = {
|
|
65460
|
+
if (f.present && f.base64 !== void 0 && f.sha !== void 0) state = {
|
|
65070
65461
|
...state,
|
|
65071
|
-
config:
|
|
65462
|
+
config: {
|
|
65463
|
+
text: decodeBase64(f.base64),
|
|
65464
|
+
sha: f.sha
|
|
65465
|
+
}
|
|
65466
|
+
};
|
|
65467
|
+
break;
|
|
65468
|
+
case "configRecord":
|
|
65469
|
+
state = {
|
|
65470
|
+
...state,
|
|
65471
|
+
configRecord: f.record
|
|
65072
65472
|
};
|
|
65073
65473
|
break;
|
|
65074
65474
|
case "record": state = {
|
|
@@ -65167,7 +65567,7 @@ const LedgerState = Struct({
|
|
|
65167
65567
|
started: Number$2,
|
|
65168
65568
|
ended: optionalKey(Number$2)
|
|
65169
65569
|
});
|
|
65170
|
-
const itemsOf = (p) => [...p.providers.flatMap((pp) => pp.decisions.map((d) => {
|
|
65570
|
+
const itemsOf = (p, config) => [...p.providers.flatMap((pp) => pp.decisions.map((d) => {
|
|
65171
65571
|
const base = {
|
|
65172
65572
|
name: address(pp.name, d.item),
|
|
65173
65573
|
kind: pp.rule,
|
|
@@ -65210,7 +65610,8 @@ const itemsOf = (p) => [...p.providers.flatMap((pp) => pp.decisions.map((d) => {
|
|
|
65210
65610
|
})), {
|
|
65211
65611
|
name: CONFIG_STEP,
|
|
65212
65612
|
kind: "config",
|
|
65213
|
-
|
|
65613
|
+
...config,
|
|
65614
|
+
changes: config.from !== config.to,
|
|
65214
65615
|
status: "idle",
|
|
65215
65616
|
steps: [{
|
|
65216
65617
|
id: CONFIG_STEP,
|
|
@@ -65306,10 +65707,10 @@ const makeLedger = fn("makeLedger")(function* (input) {
|
|
|
65306
65707
|
}
|
|
65307
65708
|
})).pipe(andThen(logInfo(`resolving ${n} items`))),
|
|
65308
65709
|
resolved: update(({ resolving: _, ...state }) => state).pipe(andThen(logInfo("resolved"))),
|
|
65309
|
-
plan: (name, p) => machine(name, (m) => ({
|
|
65710
|
+
plan: (name, p, config) => machine(name, (m) => ({
|
|
65310
65711
|
...m,
|
|
65311
65712
|
status: "planned",
|
|
65312
|
-
items: itemsOf(p)
|
|
65713
|
+
items: itemsOf(p, config)
|
|
65313
65714
|
})).pipe(andThen(forEach$1(p.providers.flatMap((pp) => pp.decisions.map((d) => ({
|
|
65314
65715
|
pp,
|
|
65315
65716
|
d
|
|
@@ -67365,10 +67766,10 @@ const Cell = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
|
67365
67766
|
const value = (i) => i.to === void 0 ? i.from ?? "" : i.from === void 0 ? `+ ${i.to}` : i.from === i.to ? i.to : `${i.from} → ${i.to}`;
|
|
67366
67767
|
const SPINNER = cli_spinners_default.dots;
|
|
67367
67768
|
const runGlyph = (live, now) => (live ? SPINNER.frames[Math.floor(now / SPINNER.interval) % SPINNER.frames.length] : void 0) ?? "▸";
|
|
67368
|
-
const
|
|
67769
|
+
const UNCHANGED = {
|
|
67369
67770
|
text: "",
|
|
67370
|
-
|
|
67371
|
-
}
|
|
67771
|
+
quiet: true
|
|
67772
|
+
};
|
|
67372
67773
|
const statusOf = (i, dryRun, glyph) => {
|
|
67373
67774
|
switch (i.status) {
|
|
67374
67775
|
case "running": return {
|
|
@@ -67386,11 +67787,11 @@ const statusOf = (i, dryRun, glyph) => {
|
|
|
67386
67787
|
detail: i.detail
|
|
67387
67788
|
};
|
|
67388
67789
|
case "ok":
|
|
67790
|
+
if (!i.changes) return UNCHANGED;
|
|
67389
67791
|
if (i.kind === "config") return {
|
|
67390
67792
|
text: "✓ copied",
|
|
67391
|
-
tone: "
|
|
67793
|
+
tone: "ok"
|
|
67392
67794
|
};
|
|
67393
|
-
if (!i.changes) return unchanged(i);
|
|
67394
67795
|
if (i.kind === "record") return i.from !== void 0 && i.from === i.to ? {
|
|
67395
67796
|
text: "✓ rebuilt",
|
|
67396
67797
|
tone: "ok",
|
|
@@ -67408,9 +67809,10 @@ const statusOf = (i, dryRun, glyph) => {
|
|
|
67408
67809
|
if (i.changes) return {
|
|
67409
67810
|
text: dryRun ? "· planned" : "· queued",
|
|
67410
67811
|
tone: "dim",
|
|
67411
|
-
detail: i.reason
|
|
67812
|
+
detail: i.reason,
|
|
67813
|
+
quiet: true
|
|
67412
67814
|
};
|
|
67413
|
-
return
|
|
67815
|
+
return UNCHANGED;
|
|
67414
67816
|
}
|
|
67415
67817
|
};
|
|
67416
67818
|
const countsOf = (m) => {
|
|
@@ -67560,7 +67962,7 @@ const MachineLine = (props) => {
|
|
|
67560
67962
|
const ItemLine = (props) => {
|
|
67561
67963
|
const { i, w } = props;
|
|
67562
67964
|
const status = statusOf(i, props.dryRun, props.glyph);
|
|
67563
|
-
const quiet =
|
|
67965
|
+
const quiet = status.quiet === true ? "dim" : void 0;
|
|
67564
67966
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
67565
67967
|
columnGap: GAP,
|
|
67566
67968
|
children: [
|
|
@@ -67794,7 +68196,7 @@ const report = fn("report")(function* () {
|
|
|
67794
68196
|
]
|
|
67795
68197
|
})));
|
|
67796
68198
|
});
|
|
67797
|
-
const decodeFact$
|
|
68199
|
+
const decodeFact$3 = decodeUnknownOption(Union([Struct({
|
|
67798
68200
|
fact: Literal("brew"),
|
|
67799
68201
|
version: NullOr(String$6)
|
|
67800
68202
|
}), AtFact]));
|
|
@@ -67846,7 +68248,7 @@ function homebrew(name) {
|
|
|
67846
68248
|
let brew;
|
|
67847
68249
|
const at = /* @__PURE__ */ new Map();
|
|
67848
68250
|
for (const raw of facts) {
|
|
67849
|
-
const fact = decodeFact$
|
|
68251
|
+
const fact = decodeFact$3(raw);
|
|
67850
68252
|
if (fact._tag === "None") continue;
|
|
67851
68253
|
const f = fact.value;
|
|
67852
68254
|
if (f.fact === "brew") brew = f.version ?? void 0;
|
|
@@ -67923,6 +68325,624 @@ function homebrew(name) {
|
|
|
67923
68325
|
}
|
|
67924
68326
|
const brew = homebrew("brew");
|
|
67925
68327
|
const cask = homebrew("cask");
|
|
68328
|
+
//#endregion
|
|
68329
|
+
//#region ../../packages/providers/src/files/index.ts
|
|
68330
|
+
const ALWAYS_EXCLUDED = [".DS_Store", "._*"];
|
|
68331
|
+
const SIZE_LIMIT = 10485760;
|
|
68332
|
+
const decodeFact$2 = decodeUnknownOption(Union([Struct({
|
|
68333
|
+
fact: Literal("entry"),
|
|
68334
|
+
item: String$6,
|
|
68335
|
+
path: String$6,
|
|
68336
|
+
entry: String$6,
|
|
68337
|
+
size: Number$2
|
|
68338
|
+
}), Struct({
|
|
68339
|
+
fact: Literal("openssl"),
|
|
68340
|
+
present: Boolean$4
|
|
68341
|
+
})]));
|
|
68342
|
+
const FileRecord = Struct({
|
|
68343
|
+
at: String$6,
|
|
68344
|
+
entries: Record(String$6, String$6)
|
|
68345
|
+
});
|
|
68346
|
+
const decodeRecord$1 = decodeUnknownOption(FileRecord);
|
|
68347
|
+
const jsonRecord = encodeSync(fromJsonString(FileRecord));
|
|
68348
|
+
const fingerprint = (entries) => [...entries].toSorted(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([rel, entry]) => `${rel}\t${entry}\n`).join("");
|
|
68349
|
+
const short$1 = (entries) => (string$1(fingerprint(entries)) >>> 0).toString(16).padStart(8, "0").slice(0, 7);
|
|
68350
|
+
const recordEntries = (record) => new Map(Object.entries(record.entries));
|
|
68351
|
+
const kindOf = (entries) => ({
|
|
68352
|
+
f: "a file",
|
|
68353
|
+
d: "a directory",
|
|
68354
|
+
l: "a symlink",
|
|
68355
|
+
"?": "something it cannot copy"
|
|
68356
|
+
})[entries.get(".")?.[0] ?? ""] ?? "missing";
|
|
68357
|
+
const depth = (rel) => rel === "." ? 0 : rel.split("/").length;
|
|
68358
|
+
const changedPaths = (a, b) => {
|
|
68359
|
+
const paths = [.../* @__PURE__ */ new Set([...a.keys(), ...b.keys()])].filter((rel) => a.get(rel) !== b.get(rel)).toSorted();
|
|
68360
|
+
const shown = paths.slice(0, 3).join(", ");
|
|
68361
|
+
return paths.length > 3 ? `${shown} and ${paths.length - 3} more` : shown;
|
|
68362
|
+
};
|
|
68363
|
+
const SHELL_FNS = [
|
|
68364
|
+
`ff_mode() { stat -c %a "$1" 2>/dev/null || stat -f %Lp "$1"; }`,
|
|
68365
|
+
`ff_entry() {`,
|
|
68366
|
+
` if [ -L "$3" ]; then ff_e="l $(readlink "$3")"; ff_s=0`,
|
|
68367
|
+
` elif [ -d "$3" ]; then if [ -r "$3" ] && [ -x "$3" ]; then ff_e="d $(ff_mode "$3")"; else ff_e="?"; fi; ff_s=0`,
|
|
68368
|
+
` elif [ -f "$3" ]; then if [ -r "$3" ]; then ff_e="f $(ff_mode "$3") $(ff_sha < "$3")"; ff_s=$(wc -c < "$3" | tr -d " "); else ff_e="?"; ff_s=0; fi`,
|
|
68369
|
+
` else ff_e="?"; ff_s=0; fi`,
|
|
68370
|
+
` ff_state "{\\"provider\\":\\"files\\",\\"fact\\":\\"entry\\",\\"item\\":\\"$(ff_json "$1")\\",\\"path\\":\\"$(ff_json "$2")\\",\\"entry\\":\\"$(ff_json "$ff_e")\\",\\"size\\":$ff_s}"`,
|
|
68371
|
+
`}`
|
|
68372
|
+
].join("\n");
|
|
68373
|
+
const pruneExpr = (exclude) => [...ALWAYS_EXCLUDED, ...exclude].map((pattern) => pattern.includes("/") ? `-path ${quote(`./${pattern.replace(/^(\.?\/)+|\/+$/g, "")}`)}` : `-name ${quote(pattern)}`).join(" -o ");
|
|
68374
|
+
const observe$1 = (items) => {
|
|
68375
|
+
if (items.length === 0) return "";
|
|
68376
|
+
return [
|
|
68377
|
+
SHELL_FNS,
|
|
68378
|
+
`if command -v openssl >/dev/null 2>&1; then ff_o=true; else ff_o=false; fi`,
|
|
68379
|
+
`ff_state "{\\"provider\\":\\"files\\",\\"fact\\":\\"openssl\\",\\"present\\":$ff_o}"`,
|
|
68380
|
+
...items.flatMap((item) => {
|
|
68381
|
+
const name = quote(item.name);
|
|
68382
|
+
return [
|
|
68383
|
+
`ff_p=${quotePath(item.path)}`,
|
|
68384
|
+
`if [ -d "$ff_p" ] && [ ! -L "$ff_p" ]; then`,
|
|
68385
|
+
` ff_entry ${name} . "$ff_p"`,
|
|
68386
|
+
` (cd "$ff_p" && find . -mindepth 1 \\( ${pruneExpr(item.exclude)} \\) -prune -o -print) | while IFS= read -r ff_rel; do ff_rel=\${ff_rel#./}; ff_entry ${name} "$ff_rel" "$ff_p/$ff_rel"; done`,
|
|
68387
|
+
`elif [ -e "$ff_p" ] || [ -L "$ff_p" ]; then`,
|
|
68388
|
+
` ff_entry ${name} . "$ff_p"`,
|
|
68389
|
+
"fi"
|
|
68390
|
+
];
|
|
68391
|
+
})
|
|
68392
|
+
].join("\n");
|
|
68393
|
+
};
|
|
68394
|
+
const parse = (facts, records) => {
|
|
68395
|
+
const items = /* @__PURE__ */ new Map();
|
|
68396
|
+
let openssl = false;
|
|
68397
|
+
for (const raw of facts) {
|
|
68398
|
+
const fact = decodeFact$2(raw);
|
|
68399
|
+
if (fact._tag === "None") continue;
|
|
68400
|
+
const f = fact.value;
|
|
68401
|
+
if (f.fact === "openssl") {
|
|
68402
|
+
openssl = f.present;
|
|
68403
|
+
continue;
|
|
68404
|
+
}
|
|
68405
|
+
const item = items.get(f.item) ?? {
|
|
68406
|
+
entries: /* @__PURE__ */ new Map(),
|
|
68407
|
+
size: 0
|
|
68408
|
+
};
|
|
68409
|
+
item.entries.set(f.path, f.entry);
|
|
68410
|
+
item.size += f.size;
|
|
68411
|
+
items.set(f.item, item);
|
|
68412
|
+
}
|
|
68413
|
+
const decoded = /* @__PURE__ */ new Map();
|
|
68414
|
+
for (const [name, raw] of Object.entries(records)) {
|
|
68415
|
+
const record = decodeRecord$1(raw);
|
|
68416
|
+
if (record._tag === "Some") decoded.set(name, record.value);
|
|
68417
|
+
}
|
|
68418
|
+
return {
|
|
68419
|
+
items,
|
|
68420
|
+
records: decoded,
|
|
68421
|
+
openssl
|
|
68422
|
+
};
|
|
68423
|
+
};
|
|
68424
|
+
const at = (rel) => rel === "." ? `"$ff_p"` : `"$ff_p"/${quote(rel)}`;
|
|
68425
|
+
const readContents = (files) => gen(function* () {
|
|
68426
|
+
const contents = /* @__PURE__ */ new Map();
|
|
68427
|
+
const script = files.map(({ abs }, n) => `ff_b=$(base64 < ${abs}) || exit 1; printf 'ff-file ${n} %s\\n' "$(printf '%s' "$ff_b" | tr -d '\\n')"`).join("\n");
|
|
68428
|
+
return (yield* runScript({
|
|
68429
|
+
name: "local",
|
|
68430
|
+
local: true
|
|
68431
|
+
}, script, [], (line) => sync(() => {
|
|
68432
|
+
const match = /^ff-file (\d+) (.*)$/.exec(line);
|
|
68433
|
+
if (match !== null) contents.set(Number(match[1]), new Hidden(match[2] ?? ""));
|
|
68434
|
+
}))) === 0 && contents.size === files.length ? contents : void 0;
|
|
68435
|
+
});
|
|
68436
|
+
const resolveSources = fn("resolveSources")(function* (items, scope) {
|
|
68437
|
+
const facts = [];
|
|
68438
|
+
yield* runScript({
|
|
68439
|
+
name: scope.machine.name,
|
|
68440
|
+
local: true
|
|
68441
|
+
}, checkScript({
|
|
68442
|
+
scope,
|
|
68443
|
+
providers: [{
|
|
68444
|
+
provider: files,
|
|
68445
|
+
items
|
|
68446
|
+
}]
|
|
68447
|
+
}), [], (line) => sync(() => {
|
|
68448
|
+
const parsed = parseLine(line);
|
|
68449
|
+
if (parsed.kind === "state") facts.push(parsed.fact);
|
|
68450
|
+
}));
|
|
68451
|
+
const state = assembleState(facts);
|
|
68452
|
+
const observed = parse(state.facts.files ?? [], state.records.files ?? {});
|
|
68453
|
+
const sources = /* @__PURE__ */ new Map();
|
|
68454
|
+
for (const item of items) {
|
|
68455
|
+
const copy = observed.items.get(item.name);
|
|
68456
|
+
if (!state.complete) {
|
|
68457
|
+
sources.set(item.name, {
|
|
68458
|
+
item,
|
|
68459
|
+
kind: "unreadable",
|
|
68460
|
+
reason: "could not read it here"
|
|
68461
|
+
});
|
|
68462
|
+
continue;
|
|
68463
|
+
}
|
|
68464
|
+
if (copy === void 0) {
|
|
68465
|
+
sources.set(item.name, {
|
|
68466
|
+
item,
|
|
68467
|
+
kind: "missing"
|
|
68468
|
+
});
|
|
68469
|
+
continue;
|
|
68470
|
+
}
|
|
68471
|
+
const unreadable = [...copy.entries].find(([, entry]) => entry === "?")?.[0];
|
|
68472
|
+
if (unreadable !== void 0) {
|
|
68473
|
+
sources.set(item.name, {
|
|
68474
|
+
item,
|
|
68475
|
+
kind: "unreadable",
|
|
68476
|
+
reason: `cannot read or copy ${unreadable === "." ? item.path : unreadable} here`
|
|
68477
|
+
});
|
|
68478
|
+
continue;
|
|
68479
|
+
}
|
|
68480
|
+
if (copy.size > 10485760) {
|
|
68481
|
+
sources.set(item.name, {
|
|
68482
|
+
item,
|
|
68483
|
+
kind: "unreadable",
|
|
68484
|
+
reason: `${(copy.size / 1024 / 1024).toFixed(1)} MB, over the ${SIZE_LIMIT / 1024 / 1024} MB limit`
|
|
68485
|
+
});
|
|
68486
|
+
continue;
|
|
68487
|
+
}
|
|
68488
|
+
const list = [...copy.entries].flatMap(([rel, entry]) => entry.startsWith("f ") ? [{
|
|
68489
|
+
rel,
|
|
68490
|
+
abs: rel === "." ? quotePath(item.path) : `${quotePath(item.path)}/${quote(rel)}`
|
|
68491
|
+
}] : []);
|
|
68492
|
+
const read = yield* readContents(list);
|
|
68493
|
+
if (read === void 0) {
|
|
68494
|
+
sources.set(item.name, {
|
|
68495
|
+
item,
|
|
68496
|
+
kind: "unreadable",
|
|
68497
|
+
reason: "could not read it here"
|
|
68498
|
+
});
|
|
68499
|
+
continue;
|
|
68500
|
+
}
|
|
68501
|
+
const record = observed.records.get(item.name);
|
|
68502
|
+
sources.set(item.name, {
|
|
68503
|
+
item,
|
|
68504
|
+
kind: "present",
|
|
68505
|
+
entries: copy.entries,
|
|
68506
|
+
...record === void 0 ? {} : { record },
|
|
68507
|
+
contents: new Map(list.map(({ rel }, n) => [rel, read.get(n) ?? new Hidden("")]))
|
|
68508
|
+
});
|
|
68509
|
+
}
|
|
68510
|
+
return {
|
|
68511
|
+
sources,
|
|
68512
|
+
openssl: observed.openssl
|
|
68513
|
+
};
|
|
68514
|
+
});
|
|
68515
|
+
const copiesOf = (checked, from) => {
|
|
68516
|
+
const copies = /* @__PURE__ */ new Map();
|
|
68517
|
+
for (const { machine, items, facts, records } of checked) {
|
|
68518
|
+
if (machine.name === from) continue;
|
|
68519
|
+
const observed = parse(facts, records);
|
|
68520
|
+
for (const item of items) {
|
|
68521
|
+
const copy = observed.items.get(item.name);
|
|
68522
|
+
if (copy === void 0 || copy.size > 10485760 || [...copy.entries.values()].includes("?")) continue;
|
|
68523
|
+
const record = observed.records.get(item.name);
|
|
68524
|
+
copies.set(item.name, [...copies.get(item.name) ?? [], {
|
|
68525
|
+
machine: machine.name,
|
|
68526
|
+
item,
|
|
68527
|
+
entries: copy.entries,
|
|
68528
|
+
...record === void 0 ? {} : { record },
|
|
68529
|
+
openssl: observed.openssl
|
|
68530
|
+
}]);
|
|
68531
|
+
}
|
|
68532
|
+
}
|
|
68533
|
+
return copies;
|
|
68534
|
+
};
|
|
68535
|
+
const PULL = "pull:";
|
|
68536
|
+
const DIFF = "diff:";
|
|
68537
|
+
const askWhy = (current, record, source, from) => {
|
|
68538
|
+
if (record === void 0) return "differs, and no Ship wrote it";
|
|
68539
|
+
const recorded = recordEntries(record);
|
|
68540
|
+
if (fingerprint(recorded) !== fingerprint(current)) return `was edited there${current.size === 1 && recorded.size === 1 ? "" : ` (${changedPaths(recorded, current)})`}`;
|
|
68541
|
+
if (fingerprint(recorded) !== (source.record === void 0 ? void 0 : fingerprint(recordEntries(source.record))) && record.at > (source.record?.at ?? "")) return `came from a Ship at ${record.at.slice(0, 16).replace("T", " ")} that ${from} missed`;
|
|
68542
|
+
};
|
|
68543
|
+
const fileQuestion = (subject, found, problem, pull, from) => ({
|
|
68544
|
+
kind: "file",
|
|
68545
|
+
subject,
|
|
68546
|
+
found,
|
|
68547
|
+
problem,
|
|
68548
|
+
choices: [
|
|
68549
|
+
{
|
|
68550
|
+
value: "overwrite",
|
|
68551
|
+
title: "overwrite it with this Machine's copy"
|
|
68552
|
+
},
|
|
68553
|
+
...pull === void 0 || !pull.openssl ? [] : [{
|
|
68554
|
+
value: `${PULL}${pull.machine}`,
|
|
68555
|
+
title: `use ${pull.machine}'s copy (${short$1(pull.entries)}) on ${from} instead`
|
|
68556
|
+
}],
|
|
68557
|
+
{
|
|
68558
|
+
value: "skip",
|
|
68559
|
+
title: "skip on these Machines (leave as is, shown as skipped)"
|
|
68560
|
+
}
|
|
68561
|
+
],
|
|
68562
|
+
default: "skip",
|
|
68563
|
+
...pull === void 0 || !pull.openssl ? {} : { diffs: [{
|
|
68564
|
+
value: `${DIFF}${pull.machine}`,
|
|
68565
|
+
title: `view the diff from ${pull.machine}'s copy to this Machine's`
|
|
68566
|
+
}] }
|
|
68567
|
+
});
|
|
68568
|
+
const missingQuestion = (subject, found, copies, from) => ({
|
|
68569
|
+
kind: "file",
|
|
68570
|
+
subject,
|
|
68571
|
+
found,
|
|
68572
|
+
problem: `is not on ${from}`,
|
|
68573
|
+
choices: [...Map.groupBy(copies.filter((c) => c.openssl), (c) => fingerprint(c.entries)).values().flatMap((same) => {
|
|
68574
|
+
const [first] = same;
|
|
68575
|
+
return first === void 0 ? [] : [{
|
|
68576
|
+
value: `${PULL}${first.machine}`,
|
|
68577
|
+
title: `pull ${short$1(first.entries)} from ${same.map((c) => c.machine).join(", ")}`
|
|
68578
|
+
}];
|
|
68579
|
+
}), {
|
|
68580
|
+
value: "skip",
|
|
68581
|
+
title: "skip it everywhere"
|
|
68582
|
+
}],
|
|
68583
|
+
default: "skip"
|
|
68584
|
+
});
|
|
68585
|
+
const decideFile = (item, observed, resolved, ctx) => {
|
|
68586
|
+
const { from } = resolved;
|
|
68587
|
+
const source = resolved.sources.get(item.name);
|
|
68588
|
+
if (source === void 0) return {
|
|
68589
|
+
kind: "skip",
|
|
68590
|
+
item: item.name,
|
|
68591
|
+
reason: `${from} has no Variant for it; ship from a Machine it targets`
|
|
68592
|
+
};
|
|
68593
|
+
const subject = address("files", item.name);
|
|
68594
|
+
const answer = ctx.answer("file", subject);
|
|
68595
|
+
const pulling = answer?.startsWith(PULL) === true;
|
|
68596
|
+
const copies = resolved.copies.get(item.name) ?? [];
|
|
68597
|
+
const mine = copies.find((c) => c.machine === ctx.machine.name);
|
|
68598
|
+
if (source.kind === "missing") {
|
|
68599
|
+
const notHere = {
|
|
68600
|
+
kind: "skip",
|
|
68601
|
+
item: item.name,
|
|
68602
|
+
reason: `not on ${from}`
|
|
68603
|
+
};
|
|
68604
|
+
if (mine === void 0) return notHere;
|
|
68605
|
+
if (pulling) return {
|
|
68606
|
+
...notHere,
|
|
68607
|
+
at: short$1(mine.entries),
|
|
68608
|
+
reason: `pulling it from ${answer?.slice(5)}`
|
|
68609
|
+
};
|
|
68610
|
+
if (answer === "skip" || !resolved.openssl || !copies.some((c) => c.openssl)) {
|
|
68611
|
+
const without = resolved.openssl ? copies.filter((c) => !c.openssl).map((c) => c.machine) : [from];
|
|
68612
|
+
return {
|
|
68613
|
+
...notHere,
|
|
68614
|
+
at: short$1(mine.entries),
|
|
68615
|
+
...answer !== "skip" && without.length > 0 ? { reason: `not on ${from}; install openssl on ${without.join(", ")} to pull it` } : {}
|
|
68616
|
+
};
|
|
68617
|
+
}
|
|
68618
|
+
return {
|
|
68619
|
+
kind: "ask",
|
|
68620
|
+
item: item.name,
|
|
68621
|
+
at: short$1(mine.entries),
|
|
68622
|
+
question: missingQuestion(subject, short$1(mine.entries), copies, from)
|
|
68623
|
+
};
|
|
68624
|
+
}
|
|
68625
|
+
if (source.kind === "unreadable") return {
|
|
68626
|
+
kind: "fail",
|
|
68627
|
+
item: item.name,
|
|
68628
|
+
reason: `${from}: ${source.reason}`
|
|
68629
|
+
};
|
|
68630
|
+
const current = observed.items.get(item.name)?.entries;
|
|
68631
|
+
const record = observed.records.get(item.name);
|
|
68632
|
+
const ours = fingerprint(source.entries);
|
|
68633
|
+
const change = {
|
|
68634
|
+
item,
|
|
68635
|
+
entries: source.entries,
|
|
68636
|
+
...current === void 0 ? {} : { current },
|
|
68637
|
+
contents: source.contents,
|
|
68638
|
+
at: resolved.at
|
|
68639
|
+
};
|
|
68640
|
+
const recorded = record === void 0 ? void 0 : fingerprint(recordEntries(record));
|
|
68641
|
+
if (ctx.machine.name === from || current !== void 0 && fingerprint(current) === ours) return {
|
|
68642
|
+
kind: "leave",
|
|
68643
|
+
item: item.name,
|
|
68644
|
+
at: short$1(source.entries),
|
|
68645
|
+
...recorded === ours ? {} : { upkeep: change }
|
|
68646
|
+
};
|
|
68647
|
+
const to = short$1(source.entries);
|
|
68648
|
+
if (current === void 0) return {
|
|
68649
|
+
kind: "change",
|
|
68650
|
+
item: item.name,
|
|
68651
|
+
to,
|
|
68652
|
+
change
|
|
68653
|
+
};
|
|
68654
|
+
if (kindOf(current) !== kindOf(source.entries)) return {
|
|
68655
|
+
kind: "fail",
|
|
68656
|
+
item: item.name,
|
|
68657
|
+
at: short$1(current),
|
|
68658
|
+
reason: `${kindOf(current)} here, ${kindOf(source.entries)} on ${from}`
|
|
68659
|
+
};
|
|
68660
|
+
const fromAt = short$1(current);
|
|
68661
|
+
const why = askWhy(current, record, source, from);
|
|
68662
|
+
if (why === void 0) return {
|
|
68663
|
+
kind: "change",
|
|
68664
|
+
item: item.name,
|
|
68665
|
+
from: fromAt,
|
|
68666
|
+
to,
|
|
68667
|
+
change
|
|
68668
|
+
};
|
|
68669
|
+
if (pulling) return {
|
|
68670
|
+
kind: "skip",
|
|
68671
|
+
item: item.name,
|
|
68672
|
+
at: fromAt,
|
|
68673
|
+
reason: `pulling it from ${answer?.slice(5)}`
|
|
68674
|
+
};
|
|
68675
|
+
if (answer === "skip") return {
|
|
68676
|
+
kind: "skip",
|
|
68677
|
+
item: item.name,
|
|
68678
|
+
at: fromAt,
|
|
68679
|
+
reason: why
|
|
68680
|
+
};
|
|
68681
|
+
if (answer === "overwrite") return {
|
|
68682
|
+
kind: "change",
|
|
68683
|
+
item: item.name,
|
|
68684
|
+
from: fromAt,
|
|
68685
|
+
to,
|
|
68686
|
+
reason: why,
|
|
68687
|
+
change
|
|
68688
|
+
};
|
|
68689
|
+
return {
|
|
68690
|
+
kind: "ask",
|
|
68691
|
+
item: item.name,
|
|
68692
|
+
at: fromAt,
|
|
68693
|
+
question: fileQuestion(subject, fromAt, why, resolved.openssl ? mine : void 0, from)
|
|
68694
|
+
};
|
|
68695
|
+
};
|
|
68696
|
+
const CIPHER = "-aes-256-cbc -pbkdf2 -a -A";
|
|
68697
|
+
const writeRecord = (name, entries, at) => {
|
|
68698
|
+
const file = recordFile("files", name);
|
|
68699
|
+
const temporary = `${recordDir("files")}/${quote(`${name}.json.tmp`)}`;
|
|
68700
|
+
return [
|
|
68701
|
+
`mkdir -p ${recordDir("files")}`,
|
|
68702
|
+
`ff_lit ${quote(jsonRecord({
|
|
68703
|
+
at,
|
|
68704
|
+
entries: Object.fromEntries(entries)
|
|
68705
|
+
}))} > ${temporary}`,
|
|
68706
|
+
`mv -f ${temporary} ${file}`
|
|
68707
|
+
];
|
|
68708
|
+
};
|
|
68709
|
+
const copyStep = (change) => {
|
|
68710
|
+
const { item, entries, current, contents } = change;
|
|
68711
|
+
const typeOf = (entry) => entry?.[0];
|
|
68712
|
+
const deletes = [...current ?? []].filter(([rel, entry]) => {
|
|
68713
|
+
const ours = entries.get(rel);
|
|
68714
|
+
return ours === void 0 || typeOf(ours) !== typeOf(entry);
|
|
68715
|
+
}).toSorted(([a], [b]) => depth(b) - depth(a));
|
|
68716
|
+
const writes = [...entries].filter(([rel, entry]) => current?.get(rel) !== entry).toSorted(([a], [b]) => depth(a) - depth(b));
|
|
68717
|
+
const input = change.key === void 0 ? [] : [change.key];
|
|
68718
|
+
const decode = change.key === void 0 ? "base64 -d" : `FF_KEY="$ff_input_0" openssl enc -d ${CIPHER} -pass env:FF_KEY`;
|
|
68719
|
+
const write = ([rel, entry]) => {
|
|
68720
|
+
const [type, mode = ""] = entry.split(" ");
|
|
68721
|
+
if (type === "d") return [`(umask 077 && mkdir -p ${at(rel)})`];
|
|
68722
|
+
if (type === "l") return [`rm -f ${at(rel)}`, `ln -s -- ${quote(entry.slice(2))} ${at(rel)}`];
|
|
68723
|
+
const n = input.push(contents.get(rel) ?? new Hidden("")) - 1;
|
|
68724
|
+
return [
|
|
68725
|
+
`ff_t=$(mktemp "$(dirname ${at(rel)})/.fftmp.XXXXXX")`,
|
|
68726
|
+
`printf '%s' "$ff_input_${n}" | ${decode} > "$ff_t"`,
|
|
68727
|
+
`chmod ${mode} "$ff_t"`,
|
|
68728
|
+
`mv -f "$ff_t" ${at(rel)}`,
|
|
68729
|
+
`ff_t=""`
|
|
68730
|
+
];
|
|
68731
|
+
};
|
|
68732
|
+
const modes = writes.filter(([, entry]) => entry.startsWith("d ")).toReversed().map(([rel, entry]) => `chmod ${entry.slice(2)} ${at(rel)}`);
|
|
68733
|
+
const links = [...entries].filter(([, entry]) => entry.startsWith("l ")).map(([rel]) => rel);
|
|
68734
|
+
const reached = short$1(entries);
|
|
68735
|
+
return {
|
|
68736
|
+
id: `files:${item.name}:copy`,
|
|
68737
|
+
items: [item.name],
|
|
68738
|
+
doing: "copying",
|
|
68739
|
+
run: [
|
|
68740
|
+
`ff_p=${quotePath(item.path)}`,
|
|
68741
|
+
"(",
|
|
68742
|
+
" set -e",
|
|
68743
|
+
` ff_t=""`,
|
|
68744
|
+
` trap '[ -z "$ff_t" ] || rm -f "$ff_t"' EXIT`,
|
|
68745
|
+
` trap 'exit 1' HUP INT TERM`,
|
|
68746
|
+
...[
|
|
68747
|
+
`mkdir -p "$(dirname "$ff_p")"`,
|
|
68748
|
+
...deletes.map(([rel, entry]) => entry.startsWith("d ") ? `rmdir ${at(rel)}` : `rm -f ${at(rel)}`),
|
|
68749
|
+
...writes.flatMap(write),
|
|
68750
|
+
...modes,
|
|
68751
|
+
...writeRecord(item.name, entries, change.at)
|
|
68752
|
+
].map((l) => ` ${l}`),
|
|
68753
|
+
")",
|
|
68754
|
+
`ff_rc=$?`,
|
|
68755
|
+
`if [ "$ff_rc" -eq 0 ]; then`,
|
|
68756
|
+
` ff_dl=""`,
|
|
68757
|
+
...links.map((rel) => ` [ -e ${at(rel)} ] || ff_dl="$ff_dl, "${quote(rel)}`),
|
|
68758
|
+
` if [ -n "$ff_dl" ]; then ff_at files ${quote(item.name)} "${reached}, dangling link: \${ff_dl#, }"; else ff_at files ${quote(item.name)} ${reached}; fi`,
|
|
68759
|
+
"fi",
|
|
68760
|
+
`(exit "$ff_rc")`
|
|
68761
|
+
],
|
|
68762
|
+
input
|
|
68763
|
+
};
|
|
68764
|
+
};
|
|
68765
|
+
const fetchFiles = (copy, rels) => gen(function* () {
|
|
68766
|
+
const key = [...globalThis.crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(32))].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
68767
|
+
const contents = /* @__PURE__ */ new Map();
|
|
68768
|
+
let finished = false;
|
|
68769
|
+
const code = yield* runScript({
|
|
68770
|
+
name: copy.machine,
|
|
68771
|
+
local: false
|
|
68772
|
+
}, [
|
|
68773
|
+
prelude,
|
|
68774
|
+
`IFS= read -r FF_KEY || FF_KEY=""`,
|
|
68775
|
+
"export FF_KEY",
|
|
68776
|
+
`ff_p=${quotePath(copy.item.path)}`,
|
|
68777
|
+
...rels.map((rel, n) => `ff_c=$(openssl enc ${CIPHER} -pass env:FF_KEY -in ${at(rel)}) || exit 1; printf 'ff-file ${n} %s\\n' "$ff_c"`),
|
|
68778
|
+
`printf 'ff-pulled\\n'`
|
|
68779
|
+
].join("\n"), [key], (line) => sync(() => {
|
|
68780
|
+
const match = /^ff-file (\d+) (.*)$/.exec(line);
|
|
68781
|
+
if (match !== null) contents.set(Number(match[1]), new Hidden(match[2] ?? ""));
|
|
68782
|
+
else if (line === "ff-pulled") finished = true;
|
|
68783
|
+
}));
|
|
68784
|
+
return !finished || contents.size !== rels.length ? { error: code === 255 ? `could not reach ${copy.machine}` : `could not read its copy on ${copy.machine}` } : {
|
|
68785
|
+
key: new Hidden(key),
|
|
68786
|
+
contents: new Map(rels.map((rel, n) => [rel, contents.get(n) ?? new Hidden("")]))
|
|
68787
|
+
};
|
|
68788
|
+
});
|
|
68789
|
+
const runHere = (from, step) => gen(function* () {
|
|
68790
|
+
let ok = false;
|
|
68791
|
+
yield* runScript({
|
|
68792
|
+
name: from,
|
|
68793
|
+
local: true
|
|
68794
|
+
}, stepScript(step), stepStdin(step), (line) => sync(() => {
|
|
68795
|
+
const parsed = parseLine(line);
|
|
68796
|
+
if (parsed.kind === "done" && parsed.done.id === step.id) ok = parsed.done.ok;
|
|
68797
|
+
}));
|
|
68798
|
+
return ok;
|
|
68799
|
+
});
|
|
68800
|
+
const pull = (name, machine, resolved) => gen(function* () {
|
|
68801
|
+
const source = resolved.sources.get(name);
|
|
68802
|
+
const copy = resolved.copies.get(name)?.find((c) => c.machine === machine);
|
|
68803
|
+
if (source === void 0 || copy === void 0 || source.kind === "unreadable") return { error: `cannot pull ${machine}'s copy here` };
|
|
68804
|
+
const fetched = yield* fetchFiles(copy, [...copy.entries].flatMap(([rel, entry]) => entry.startsWith("f ") ? [rel] : []));
|
|
68805
|
+
if ("error" in fetched) return fetched;
|
|
68806
|
+
const step = copyStep({
|
|
68807
|
+
item: source.item,
|
|
68808
|
+
entries: copy.entries,
|
|
68809
|
+
...source.kind === "present" ? { current: source.entries } : {},
|
|
68810
|
+
contents: fetched.contents,
|
|
68811
|
+
at: copy.record?.at ?? resolved.at,
|
|
68812
|
+
key: fetched.key
|
|
68813
|
+
});
|
|
68814
|
+
return (yield* runHere(resolved.from, step)) ? { done: `pulled ${short$1(copy.entries)} from ${machine}` } : { error: `could not write ${machine}'s copy on ${resolved.from}` };
|
|
68815
|
+
}).pipe(catch_((e) => succeed$3({ error: e.message })));
|
|
68816
|
+
const differing = (a, b) => [.../* @__PURE__ */ new Set([...a.keys(), ...b.keys()])].filter((rel) => a.get(rel) !== b.get(rel)).toSorted();
|
|
68817
|
+
const notShownByGit = (name, old, now) => differing(old.entries, now.entries).flatMap((rel) => {
|
|
68818
|
+
const a = old.entries.get(rel);
|
|
68819
|
+
const b = now.entries.get(rel);
|
|
68820
|
+
const path = rel === "." ? name : `${name}/${rel}`;
|
|
68821
|
+
const mode = (entry) => entry.split(" ")[1] ?? "";
|
|
68822
|
+
const executable = (entry) => (Number.parseInt(mode(entry), 8) & 64) !== 0;
|
|
68823
|
+
if (a?.startsWith("d ") === true && b?.startsWith("d ") === true) return [`${path}/ mode ${mode(a)} → ${mode(b)}`];
|
|
68824
|
+
if (a?.startsWith("d ") === true) return [`${path}/ only on ${old.machine}`];
|
|
68825
|
+
if (b?.startsWith("d ") === true) return [`${path}/ only on ${now.machine}`];
|
|
68826
|
+
if (a?.startsWith("f ") === true && b?.startsWith("f ") === true && mode(a) !== mode(b) && executable(a) === executable(b)) return [`${path} mode ${mode(a)} → ${mode(b)}`];
|
|
68827
|
+
return [];
|
|
68828
|
+
});
|
|
68829
|
+
const diffStep = (input) => {
|
|
68830
|
+
const rels = differing(input.old.entries, input.new.entries);
|
|
68831
|
+
const inputs = [];
|
|
68832
|
+
const side = (s) => {
|
|
68833
|
+
const decode = s.key === void 0 ? "base64 -d" : `FF_KEY="$ff_input_${inputs.push(s.key) - 1}" openssl enc -d ${CIPHER} -pass env:FF_KEY`;
|
|
68834
|
+
return [
|
|
68835
|
+
`ff_p=${quote(`${input.dir}/${s.machine}/${input.name}`)}`,
|
|
68836
|
+
kindOf(s.entries) === "a directory" ? `mkdir -p "$ff_p"` : `mkdir -p "$(dirname "$ff_p")"`,
|
|
68837
|
+
...rels.flatMap((rel) => {
|
|
68838
|
+
const entry = s.entries.get(rel);
|
|
68839
|
+
if (entry?.startsWith("l ")) return [`mkdir -p "$(dirname ${at(rel)})"`, `ln -s -- ${quote(entry.slice(2))} ${at(rel)}`];
|
|
68840
|
+
if (!entry?.startsWith("f ")) return [];
|
|
68841
|
+
const n = inputs.push(s.contents.get(rel) ?? new Hidden("")) - 1;
|
|
68842
|
+
return [
|
|
68843
|
+
`mkdir -p "$(dirname ${at(rel)})"`,
|
|
68844
|
+
`printf '%s' "$ff_input_${n}" | ${decode} > ${at(rel)}`,
|
|
68845
|
+
`chmod ${entry.split(" ")[1] ?? ""} ${at(rel)}`
|
|
68846
|
+
];
|
|
68847
|
+
})
|
|
68848
|
+
];
|
|
68849
|
+
};
|
|
68850
|
+
const run = [...side(input.old), ...side(input.new)];
|
|
68851
|
+
return {
|
|
68852
|
+
id: `files:${input.name}:diff`,
|
|
68853
|
+
items: [input.name],
|
|
68854
|
+
doing: "diffing",
|
|
68855
|
+
run: [
|
|
68856
|
+
"(",
|
|
68857
|
+
" set -e",
|
|
68858
|
+
...run.map((l) => ` ${l}`),
|
|
68859
|
+
")"
|
|
68860
|
+
],
|
|
68861
|
+
input: inputs
|
|
68862
|
+
};
|
|
68863
|
+
};
|
|
68864
|
+
const diff = (name, machine, resolved, dir) => gen(function* () {
|
|
68865
|
+
const source = resolved.sources.get(name);
|
|
68866
|
+
const copy = resolved.copies.get(name)?.find((c) => c.machine === machine);
|
|
68867
|
+
if (source?.kind !== "present" || copy === void 0) return { error: `cannot diff ${machine}'s copy here` };
|
|
68868
|
+
const fetched = yield* fetchFiles(copy, differing(copy.entries, source.entries).filter((rel) => copy.entries.get(rel)?.startsWith("f ") === true));
|
|
68869
|
+
if ("error" in fetched) return fetched;
|
|
68870
|
+
const sides = {
|
|
68871
|
+
old: {
|
|
68872
|
+
machine,
|
|
68873
|
+
entries: copy.entries,
|
|
68874
|
+
contents: fetched.contents,
|
|
68875
|
+
key: fetched.key
|
|
68876
|
+
},
|
|
68877
|
+
new: {
|
|
68878
|
+
machine: resolved.from,
|
|
68879
|
+
entries: source.entries,
|
|
68880
|
+
contents: source.contents
|
|
68881
|
+
}
|
|
68882
|
+
};
|
|
68883
|
+
return (yield* runHere(resolved.from, diffStep({
|
|
68884
|
+
name,
|
|
68885
|
+
dir,
|
|
68886
|
+
...sides
|
|
68887
|
+
}))) ? {
|
|
68888
|
+
old: machine,
|
|
68889
|
+
new: resolved.from,
|
|
68890
|
+
notShown: notShownByGit(name, sides.old, sides.new)
|
|
68891
|
+
} : { error: `could not write the diff on ${resolved.from}` };
|
|
68892
|
+
}).pipe(catch_((e) => succeed$3({ error: e.message })));
|
|
68893
|
+
const recordStep = (change) => ({
|
|
68894
|
+
id: `files:${change.item.name}:record`,
|
|
68895
|
+
items: [change.item.name],
|
|
68896
|
+
doing: "recording",
|
|
68897
|
+
run: [
|
|
68898
|
+
"(",
|
|
68899
|
+
" set -e",
|
|
68900
|
+
...writeRecord(change.item.name, change.entries, change.at).map((l) => ` ${l}`),
|
|
68901
|
+
")"
|
|
68902
|
+
]
|
|
68903
|
+
});
|
|
68904
|
+
const files = {
|
|
68905
|
+
name: "files",
|
|
68906
|
+
rule: "record",
|
|
68907
|
+
dependsOn: [],
|
|
68908
|
+
items: (_section, listed) => listed.map(({ name, def }) => ({
|
|
68909
|
+
name,
|
|
68910
|
+
path: def.path,
|
|
68911
|
+
exclude: def.exclude ?? []
|
|
68912
|
+
})),
|
|
68913
|
+
observe: (items) => observe$1(items),
|
|
68914
|
+
parse,
|
|
68915
|
+
resolve: (items, { starting: all, checked }) => gen(function* () {
|
|
68916
|
+
const at = formatIso(yield* now);
|
|
68917
|
+
const needed = new Set(items.map((item) => item.name));
|
|
68918
|
+
const starting = {
|
|
68919
|
+
...all,
|
|
68920
|
+
items: all.items.filter((item) => needed.has(item.name))
|
|
68921
|
+
};
|
|
68922
|
+
const { sources, openssl } = starting.items.length === 0 ? {
|
|
68923
|
+
sources: /* @__PURE__ */ new Map(),
|
|
68924
|
+
openssl: false
|
|
68925
|
+
} : yield* resolveSources(starting.items, starting).pipe(timeout("2 minutes"), orElseSucceed(() => ({
|
|
68926
|
+
sources: new Map(starting.items.map((item) => [item.name, {
|
|
68927
|
+
item,
|
|
68928
|
+
kind: "unreadable",
|
|
68929
|
+
reason: "could not read it here"
|
|
68930
|
+
}])),
|
|
68931
|
+
openssl: false
|
|
68932
|
+
})));
|
|
68933
|
+
return {
|
|
68934
|
+
from: starting.machine.name,
|
|
68935
|
+
at,
|
|
68936
|
+
sources,
|
|
68937
|
+
copies: copiesOf(checked, starting.machine.name),
|
|
68938
|
+
openssl
|
|
68939
|
+
};
|
|
68940
|
+
}),
|
|
68941
|
+
decide: ({ items, observed, resolved, ctx }) => items.map((item) => decideFile(item, observed, resolved, ctx)),
|
|
68942
|
+
act: (decisions) => decisions.flatMap((d) => d.kind === "change" ? [copyStep(d.change)] : d.kind === "leave" && d.upkeep !== void 0 ? [recordStep(d.upkeep)] : []),
|
|
68943
|
+
adopt: (item, answer, resolved) => answer.startsWith(PULL) ? pull(item, answer.slice(5), resolved) : void 0,
|
|
68944
|
+
diff: (item, choice, resolved, dir) => choice.startsWith(DIFF) ? diff(item, choice.slice(5), resolved, dir) : void 0
|
|
68945
|
+
};
|
|
67926
68946
|
const decodeFact$1 = decodeUnknownOption(Union([
|
|
67927
68947
|
Struct({
|
|
67928
68948
|
fact: Literal("cloneRoot"),
|
|
@@ -68353,6 +69373,7 @@ const decideRanged = (item, observed, resolved, ctx) => {
|
|
|
68353
69373
|
//#endregion
|
|
68354
69374
|
//#region ../../packages/providers/src/index.ts
|
|
68355
69375
|
const PROVIDERS = [
|
|
69376
|
+
files,
|
|
68356
69377
|
{
|
|
68357
69378
|
name: "vp",
|
|
68358
69379
|
rule: "range",
|
|
@@ -68525,6 +69546,25 @@ const PROVIDERS = [
|
|
|
68525
69546
|
repos
|
|
68526
69547
|
];
|
|
68527
69548
|
//#endregion
|
|
69549
|
+
//#region src/diff.ts
|
|
69550
|
+
const showDiff = fn("showDiff")(function* (dir, sides) {
|
|
69551
|
+
const spawner = yield* ChildProcessSpawner;
|
|
69552
|
+
return yield* scoped(gen(function* () {
|
|
69553
|
+
return yield* (yield* spawner.spawn(make$13("git", [
|
|
69554
|
+
"diff",
|
|
69555
|
+
"--no-index",
|
|
69556
|
+
"--",
|
|
69557
|
+
sides.old,
|
|
69558
|
+
sides.new
|
|
69559
|
+
], {
|
|
69560
|
+
cwd: dir,
|
|
69561
|
+
stdin: "inherit",
|
|
69562
|
+
stdout: "inherit",
|
|
69563
|
+
stderr: "inherit"
|
|
69564
|
+
}))).exitCode;
|
|
69565
|
+
}));
|
|
69566
|
+
});
|
|
69567
|
+
//#endregion
|
|
68528
69568
|
//#region src/live.tsx
|
|
68529
69569
|
const cell = (initial) => {
|
|
68530
69570
|
let value = initial;
|
|
@@ -68542,6 +69582,10 @@ const cell = (initial) => {
|
|
|
68542
69582
|
};
|
|
68543
69583
|
};
|
|
68544
69584
|
const LiveLedger = (props) => {
|
|
69585
|
+
const { suspendTerminal } = useApp();
|
|
69586
|
+
(0, import_react.useEffect)(() => {
|
|
69587
|
+
props.suspend.current = suspendTerminal;
|
|
69588
|
+
}, [props.suspend, suspendTerminal]);
|
|
68545
69589
|
const { state, now } = (0, import_react.useSyncExternalStore)(props.store.subscribe, props.store.get);
|
|
68546
69590
|
const asking = (0, import_react.useSyncExternalStore)(props.asking.subscribe, props.asking.get);
|
|
68547
69591
|
const [full, setFull] = (0, import_react.useState)(false);
|
|
@@ -68581,9 +69625,11 @@ const LiveLedger = (props) => {
|
|
|
68581
69625
|
};
|
|
68582
69626
|
const startLive = fn("startLive")(function* (store, color) {
|
|
68583
69627
|
const asking = cell(void 0);
|
|
69628
|
+
const suspend = {};
|
|
68584
69629
|
const instance = render(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LiveLedger, {
|
|
68585
69630
|
store,
|
|
68586
69631
|
asking,
|
|
69632
|
+
suspend,
|
|
68587
69633
|
color
|
|
68588
69634
|
}), { exitOnCtrlC: false });
|
|
68589
69635
|
yield* addFinalizer(() => promise(async () => {
|
|
@@ -68592,20 +69638,29 @@ const startLive = fn("startLive")(function* (store, color) {
|
|
|
68592
69638
|
await instance.waitUntilExit();
|
|
68593
69639
|
}));
|
|
68594
69640
|
yield* forkScoped(repeat$1(store.tick, spaced(`${SPINNER.interval} millis`)));
|
|
68595
|
-
return {
|
|
68596
|
-
|
|
68597
|
-
|
|
68598
|
-
|
|
68599
|
-
|
|
68600
|
-
|
|
68601
|
-
|
|
68602
|
-
|
|
68603
|
-
|
|
68604
|
-
|
|
68605
|
-
|
|
68606
|
-
|
|
69641
|
+
return {
|
|
69642
|
+
ask: process.stdin.isTTY ? (choices) => callback$1((resume) => {
|
|
69643
|
+
asking.set({
|
|
69644
|
+
titles: choices.map((c) => c.title),
|
|
69645
|
+
answer: (index) => {
|
|
69646
|
+
const choice = choices[index];
|
|
69647
|
+
if (choice === void 0) return;
|
|
69648
|
+
asking.set(void 0);
|
|
69649
|
+
resume(succeed$3(choice.value));
|
|
69650
|
+
}
|
|
69651
|
+
});
|
|
69652
|
+
return sync(() => asking.set(void 0));
|
|
69653
|
+
}) : void 0,
|
|
69654
|
+
aside: (effect) => {
|
|
69655
|
+
const suspendTerminal = suspend.current;
|
|
69656
|
+
return suspendTerminal === void 0 ? effect : acquireUseRelease(promise(() => suspendTerminal()), () => effect, (suspension) => promise(() => suspension.resume()));
|
|
69657
|
+
}
|
|
69658
|
+
};
|
|
68607
69659
|
});
|
|
68608
|
-
const noLive = {
|
|
69660
|
+
const noLive = {
|
|
69661
|
+
ask: void 0,
|
|
69662
|
+
aside: (effect) => effect
|
|
69663
|
+
};
|
|
68609
69664
|
const canRedraw = () => Boolean(process.stdout.isTTY) && process.env.TERM !== "dumb";
|
|
68610
69665
|
//#endregion
|
|
68611
69666
|
//#region src/machine.ts
|
|
@@ -68760,11 +69815,18 @@ const askQuestions = fn("askQuestions")(function* (input) {
|
|
|
68760
69815
|
yield* input.ledger.note("answer", "not asked (dry run)");
|
|
68761
69816
|
continue;
|
|
68762
69817
|
}
|
|
68763
|
-
const choices = question.choices.map((c) => ({
|
|
69818
|
+
const choices = [...new Map(group.flatMap((a) => a.question.choices).map((c) => [c.value, c])).values()].map((c) => ({
|
|
68764
69819
|
value: c.value,
|
|
68765
69820
|
title: c.value === "bump" && bumps.length > 0 ? `bump the config to ${bumps.map((b) => bumpedRange(b.range, b.newest)).join(" and ")}` : c.title
|
|
68766
69821
|
}));
|
|
68767
|
-
const
|
|
69822
|
+
const diffs = [...new Map(group.flatMap((a) => a.question.diffs ?? []).map((c) => [c.value, c])).values()];
|
|
69823
|
+
const isDiff = (value) => diffs.some((d) => d.value === value);
|
|
69824
|
+
let answer = input.answer(question.kind, question.subject);
|
|
69825
|
+
while (answer === void 0) {
|
|
69826
|
+
const picked = input.ask === void 0 ? question.default : yield* input.ask([...choices, ...diffs]);
|
|
69827
|
+
if (isDiff(picked)) yield* input.diff(question, picked);
|
|
69828
|
+
else answer = picked;
|
|
69829
|
+
}
|
|
68768
69830
|
yield* input.ledger.note("answer", choices.find((c) => c.value === answer)?.title ?? answer);
|
|
68769
69831
|
answered.push({
|
|
68770
69832
|
question,
|
|
@@ -68797,27 +69859,43 @@ const unshippable = (config, tailnet, only, source) => {
|
|
|
68797
69859
|
const outsideFleet = only.filter((name) => !fleet.has(name));
|
|
68798
69860
|
if (outsideFleet.length > 0) return `not in machines in ${source}: ${outsideFleet.join(", ")}`;
|
|
68799
69861
|
};
|
|
68800
|
-
const
|
|
69862
|
+
const minute = (iso) => iso?.slice(0, 16).replace("T", " ") ?? "an unknown time";
|
|
69863
|
+
const staleConfigs = fn("staleConfigs")(function* (text, base, own, checked, ledger) {
|
|
68801
69864
|
const copies = [];
|
|
68802
69865
|
for (const { machine, state } of checked) {
|
|
68803
|
-
if (state.config === void 0) continue;
|
|
68804
|
-
const parsed = yield* result(parseConfig(machine.name, state.config));
|
|
68805
|
-
if (parsed._tag === "Failure")
|
|
68806
|
-
|
|
69866
|
+
if (machine.name === own || state.config === void 0 || state.config.text === text) continue;
|
|
69867
|
+
const parsed = yield* result(parseConfig(machine.name, state.config.text));
|
|
69868
|
+
if (parsed._tag === "Failure") {
|
|
69869
|
+
yield* ledger.note("info", `the config on ${machine.name} does not parse; it will be overwritten`);
|
|
69870
|
+
continue;
|
|
69871
|
+
}
|
|
69872
|
+
const record = getOrUndefined$1(decodeConfigRecord(state.configRecord));
|
|
69873
|
+
const why = staleWhy({
|
|
69874
|
+
machine: machine.name,
|
|
69875
|
+
sha: state.config.sha,
|
|
69876
|
+
record
|
|
69877
|
+
}, base);
|
|
69878
|
+
if (why === void 0) continue;
|
|
69879
|
+
copies.push({
|
|
68807
69880
|
machine: machine.name,
|
|
69881
|
+
text: state.config.text,
|
|
69882
|
+
sha: state.config.sha,
|
|
68808
69883
|
config: parsed.success,
|
|
68809
|
-
|
|
69884
|
+
record,
|
|
69885
|
+
why: why === "unrecorded" ? "differs, and no Ship wrote it" : why === "edited" ? "was edited there" : `got a Ship from ${parseShipId(record?.ship ?? "")?.from ?? "another Machine"} at ${minute(record?.reached[machine.name])} that missed this Machine`
|
|
68810
69886
|
});
|
|
68811
69887
|
}
|
|
68812
|
-
return [...Map.groupBy(
|
|
69888
|
+
return [...Map.groupBy(copies, (c) => c.text)].flatMap(([text, group]) => {
|
|
69889
|
+
const [first] = group;
|
|
68813
69890
|
if (first === void 0) return [];
|
|
68814
|
-
const
|
|
68815
|
-
const machines = [machine, ...rest.map((c) => c.machine)];
|
|
69891
|
+
const machines = group.map((c) => c.machine);
|
|
68816
69892
|
return [{
|
|
68817
69893
|
text,
|
|
68818
|
-
|
|
69894
|
+
sha: first.sha,
|
|
69895
|
+
config: first.config,
|
|
68819
69896
|
machines,
|
|
68820
|
-
|
|
69897
|
+
reached: mergeReached(group.flatMap((c) => c.record === void 0 ? [] : [c.record.reached])),
|
|
69898
|
+
description: `the config on ${machines.join(", ")} ${first.why}`
|
|
68821
69899
|
}];
|
|
68822
69900
|
});
|
|
68823
69901
|
});
|
|
@@ -68832,10 +69910,10 @@ const ship = fn("ship")(function* (flags) {
|
|
|
68832
69910
|
yield* logInfo(`Ship ${id}: fleetfizz ${flags.version}, node ${process.version} on ${process.platform}`);
|
|
68833
69911
|
yield* logInfo(`fizz ${process.argv.slice(2).join(" ")}`);
|
|
68834
69912
|
for (const m of tailnet.fleet) yield* logInfo(`tailnet: ${m.name} ${m.platform}${m.online ? "" : " offline"}${m.ssh ? "" : " no-ssh"}${m.name === tailnet.self.name ? " (this Machine)" : ""}`);
|
|
68835
|
-
while ((yield* shipPass(flags, tailnet, hidden)) === "adopted");
|
|
69913
|
+
while ((yield* shipPass(flags, tailnet, hidden, id)) === "adopted");
|
|
68836
69914
|
}).pipe(onExit((exit) => isSuccess(exit) ? logInfo("the Ship ended") : logError("the Ship stopped", exit.cause)), provide(layer$1(logger === void 0 ? [] : [logger])), provideService(MinimumLogLevel, "Debug"));
|
|
68837
69915
|
}, scoped);
|
|
68838
|
-
const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
69916
|
+
const shipPass = fn("shipPass")(function* (flags, tailnet, hidden, id) {
|
|
68839
69917
|
const fs = yield* FileSystem;
|
|
68840
69918
|
const parsed = parseAnswers(flags.answers);
|
|
68841
69919
|
if ("invalid" in parsed) return yield* fail$1(new ShipError(parsed.invalid));
|
|
@@ -68853,6 +69931,7 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
68853
69931
|
if (problem !== void 0) return yield* fail$1(new ShipError(problem));
|
|
68854
69932
|
const fleet = new Set(config.machines);
|
|
68855
69933
|
const own = flags.config === void 0 ? tailnet.self.name : void 0;
|
|
69934
|
+
const base = (own === void 0 ? void 0 : yield* readConfigRecord)?.reached ?? {};
|
|
68856
69935
|
const order = config.machines;
|
|
68857
69936
|
const targetsList = tailnet.fleet.filter((m) => fleet.has(m.name) && (flags.only.length === 0 || flags.only.includes(m.name))).toSorted((a, b) => order.indexOf(a.name) - order.indexOf(b.name));
|
|
68858
69937
|
const count = `${targetsList.length} machine${targetsList.length === 1 ? "" : "s"}`;
|
|
@@ -68923,13 +70002,13 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
68923
70002
|
machine: machine.name,
|
|
68924
70003
|
phase: "check"
|
|
68925
70004
|
})), { concurrency: "unbounded" }).pipe(map$5((list) => list.filter((g) => g !== void 0)));
|
|
68926
|
-
const stale = yield* staleConfigs(
|
|
70005
|
+
const stale = yield* staleConfigs(yield* fs.readFileString(path), base, own, checked, ledger);
|
|
68927
70006
|
if (stale.length > 0) {
|
|
68928
|
-
const what =
|
|
70007
|
+
const what = stale.map((s) => s.description).join("; ");
|
|
68929
70008
|
const answers = stale.flatMap((s) => s.machines).map((m) => given("config", m));
|
|
68930
70009
|
if (flags.dryRun) yield* ledger.note("info", `${what}; a Ship would ask before overwriting it`);
|
|
68931
70010
|
else if (answers.every((a) => a === "overwrite")) yield* ledger.note("info", `${what}; overwriting it, as answered`);
|
|
68932
|
-
else if (answers.includes("stop")) return yield* fail$1(new ShipError("
|
|
70011
|
+
else if (answers.includes("stop")) return yield* fail$1(new ShipError("stopped before overwriting the config there"));
|
|
68933
70012
|
else if (live.ask === void 0) return yield* fail$1(new ShipError(`${what}; copy it here first, or pass --overwrite-config`));
|
|
68934
70013
|
else {
|
|
68935
70014
|
const usable = [];
|
|
@@ -68955,12 +70034,17 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
68955
70034
|
]);
|
|
68956
70035
|
if (choice === "stop") {
|
|
68957
70036
|
yield* ledger.note("answer", "stop");
|
|
68958
|
-
return yield* fail$1(new ShipError("
|
|
70037
|
+
return yield* fail$1(new ShipError("stopped before overwriting the config there"));
|
|
68959
70038
|
}
|
|
68960
70039
|
if (choice === "overwrite") yield* ledger.note("answer", "overwrite it");
|
|
68961
70040
|
else {
|
|
68962
70041
|
yield* ledger.note("answer", `use the config on ${choice.machines[0]}`);
|
|
68963
70042
|
yield* fs.writeFileString(path, choice.text);
|
|
70043
|
+
if (own !== void 0) yield* writeConfigRecord(encodeConfigRecord({
|
|
70044
|
+
ship: id,
|
|
70045
|
+
sha: choice.sha,
|
|
70046
|
+
reached: reachedAfter(mergeReached([base, choice.reached]), formatIso(yield* now), [own], choice.config.machines)
|
|
70047
|
+
}));
|
|
68964
70048
|
adopted = true;
|
|
68965
70049
|
return "adopted";
|
|
68966
70050
|
}
|
|
@@ -68975,7 +70059,30 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
68975
70059
|
if (lent.token !== void 0) hidden.add(lent.token);
|
|
68976
70060
|
yield* logInfo(lent.token !== void 0 ? "gh token: lent" : `gh token: none (${lent.reason ?? "no repos"})`);
|
|
68977
70061
|
if (lent.token === void 0 && lent.reason !== void 0) yield* ledger.note("info", `no gh token (${lent.reason}); private repos will fail`);
|
|
68978
|
-
const
|
|
70062
|
+
const self = {
|
|
70063
|
+
name: tailnet.self.name,
|
|
70064
|
+
platform: tailnet.self.platform
|
|
70065
|
+
};
|
|
70066
|
+
const resolved = Object.fromEntries(yield* forEach$1(targeted, ({ provider, items }) => provider.resolve(items, {
|
|
70067
|
+
...lent.token === void 0 ? {} : { token: lent.token },
|
|
70068
|
+
starting: {
|
|
70069
|
+
machine: self,
|
|
70070
|
+
config,
|
|
70071
|
+
items: targetedItems(provider, config, self)
|
|
70072
|
+
},
|
|
70073
|
+
checked: checked.map(({ machine, state }) => {
|
|
70074
|
+
const identity = {
|
|
70075
|
+
name: machine.name,
|
|
70076
|
+
platform: machine.platform
|
|
70077
|
+
};
|
|
70078
|
+
return {
|
|
70079
|
+
machine: identity,
|
|
70080
|
+
items: targetedItems(provider, config, identity),
|
|
70081
|
+
facts: state.facts[provider.name] ?? [],
|
|
70082
|
+
records: state.records[provider.name] ?? {}
|
|
70083
|
+
};
|
|
70084
|
+
})
|
|
70085
|
+
}).pipe(map$5((r) => [provider.name, r])), { concurrency: "unbounded" }));
|
|
68979
70086
|
yield* ledger.resolved;
|
|
68980
70087
|
const planAll = (cfg, answer) => checked.map((g) => ({
|
|
68981
70088
|
...g,
|
|
@@ -68998,6 +70105,18 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
68998
70105
|
let cfg = config;
|
|
68999
70106
|
const decided = /* @__PURE__ */ new Map();
|
|
69000
70107
|
const answer = (kind, subject) => decided.get(`${kind}:${subject}`) ?? given(kind, subject);
|
|
70108
|
+
const diff = (question, choice) => scoped(gen(function* () {
|
|
70109
|
+
const at = parseAddress(question.subject);
|
|
70110
|
+
const provider = providers.find((p) => p.name === at?.provider);
|
|
70111
|
+
if (at === void 0 || provider === void 0) return;
|
|
70112
|
+
const dir = yield* fs.makeTempDirectoryScoped({ prefix: "fleetfizz-diff-" });
|
|
70113
|
+
const sides = yield* provider.diff?.(at.item, choice, resolved[provider.name], dir) ?? succeed$3(void 0);
|
|
70114
|
+
if (sides === void 0) return;
|
|
70115
|
+
if ("error" in sides) return yield* ledger.note("info", `${question.subject}: ${sides.error}`);
|
|
70116
|
+
const code = yield* live.aside(showDiff(dir, sides));
|
|
70117
|
+
if (code > 1) yield* ledger.note("info", `${question.subject}: git diff exited with ${code}`);
|
|
70118
|
+
if (sides.notShown.length > 0) yield* ledger.note("info", `${question.subject}: also differs, not shown by git: ${sides.notShown.join(", ")}`);
|
|
70119
|
+
})).pipe(catch_((e) => ledger.note("info", `${question.subject}: could not show the diff: ${e.message}`)));
|
|
69001
70120
|
let planned = planAll(cfg, answer);
|
|
69002
70121
|
for (;;) {
|
|
69003
70122
|
const asked = planned.flatMap((g) => g.plan.providers.flatMap((pp) => pp.decisions.flatMap((d) => d.kind === "ask" ? [{
|
|
@@ -69009,6 +70128,7 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
69009
70128
|
answer,
|
|
69010
70129
|
ledger,
|
|
69011
70130
|
ask: live.ask,
|
|
70131
|
+
diff,
|
|
69012
70132
|
dryRun: flags.dryRun
|
|
69013
70133
|
});
|
|
69014
70134
|
let progressed = false;
|
|
@@ -69027,33 +70147,55 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
69027
70147
|
if (!progressed) break;
|
|
69028
70148
|
planned = planAll(cfg, answer);
|
|
69029
70149
|
}
|
|
69030
|
-
|
|
70150
|
+
const adopting = [...decided].flatMap(([key, choice]) => {
|
|
70151
|
+
const subject = key.slice(key.indexOf(":") + 1);
|
|
70152
|
+
const at = parseAddress(subject);
|
|
70153
|
+
const provider = providers.find((p) => p.name === at?.provider);
|
|
70154
|
+
const run = at === void 0 || provider === void 0 ? void 0 : provider.adopt?.(at.item, choice, resolved[provider.name]);
|
|
70155
|
+
return run === void 0 ? [] : [{
|
|
70156
|
+
subject,
|
|
70157
|
+
run
|
|
70158
|
+
}];
|
|
70159
|
+
});
|
|
70160
|
+
if (adopting.length > 0 && !flags.dryRun) {
|
|
70161
|
+
for (const { subject, run } of adopting) {
|
|
70162
|
+
changing = true;
|
|
70163
|
+
const result = yield* run;
|
|
70164
|
+
if ("error" in result) return yield* fail$1(new ShipError(`${subject}: ${result.error}`));
|
|
70165
|
+
yield* ledger.note("info", `${subject}: ${result.done}; starting over`);
|
|
70166
|
+
}
|
|
70167
|
+
adopted = true;
|
|
70168
|
+
return "adopted";
|
|
70169
|
+
}
|
|
70170
|
+
const text = yield* fs.readFileString(path);
|
|
70171
|
+
const sha = yield* shaOf(text);
|
|
70172
|
+
for (const { machine, state, plan: p } of planned) yield* ledger.plan(machine.name, p, {
|
|
70173
|
+
...state.config === void 0 ? {} : { from: state.config.sha.slice(0, 7) },
|
|
70174
|
+
to: sha.slice(0, 7)
|
|
70175
|
+
});
|
|
69031
70176
|
if (flags.dryRun) return;
|
|
69032
|
-
const
|
|
69033
|
-
|
|
69034
|
-
|
|
69035
|
-
|
|
69036
|
-
|
|
69037
|
-
shippedFrom: tailnet.self.name,
|
|
69038
|
-
reached,
|
|
69039
|
-
own
|
|
70177
|
+
const at = formatIso(yield* now);
|
|
70178
|
+
const recordFor = (reached) => encodeConfigRecord({
|
|
70179
|
+
ship: id,
|
|
70180
|
+
sha,
|
|
70181
|
+
reached: reachedAfter(base, at, [...reached, ...own === void 0 ? [] : [own]], cfg.machines)
|
|
69040
70182
|
});
|
|
69041
|
-
|
|
69042
|
-
|
|
69043
|
-
|
|
70183
|
+
changing = true;
|
|
70184
|
+
const record = recordFor(planned.map((g) => g.machine.name));
|
|
70185
|
+
if (own !== void 0) yield* writeConfigRecord(record);
|
|
70186
|
+
yield* logInfo(`the config's record: ${record}`);
|
|
69044
70187
|
const schema = `${schemaJson(fleetConfigJsonSchema())}\n`;
|
|
69045
|
-
const stdin = applyStdin(lent.token, stamped, schema);
|
|
69046
70188
|
const delivered = yield* forEach$1(planned, fn("applyOne")(function* ({ machine, local, plan: p }) {
|
|
69047
70189
|
return (yield* apply({
|
|
69048
70190
|
name: machine.name,
|
|
69049
70191
|
local
|
|
69050
|
-
}, p, applyScript(p),
|
|
70192
|
+
}, p, applyScript(p, record), applyStdin(lent.token, text, schema, p), ledger)).done.includes("config") ? machine.name : void 0;
|
|
69051
70193
|
}, (effect, { machine }) => annotateLogs(effect, {
|
|
69052
70194
|
machine: machine.name,
|
|
69053
70195
|
phase: "apply"
|
|
69054
70196
|
})), { concurrency: "unbounded" }).pipe(map$5((list) => list.filter((name) => name !== void 0)));
|
|
69055
70197
|
yield* logInfo(`the config reached: ${delivered.join(", ")}`);
|
|
69056
|
-
yield*
|
|
70198
|
+
if (own !== void 0) yield* writeConfigRecord(recordFor(delivered));
|
|
69057
70199
|
}, scoped);
|
|
69058
70200
|
//#endregion
|
|
69059
70201
|
//#region src/command.ts
|