fleetfizz 0.1.0 → 0.2.1

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.
@@ -6708,6 +6708,8 @@ const tapEffectCont = function(value) {
6708
6708
  return new ContImpl(this.payload, succeedPayload, value);
6709
6709
  };
6710
6710
  /** @internal */
6711
+ const asSome = (self) => map$7(self, some);
6712
+ /** @internal */
6711
6713
  const andThen$1 = /*#__PURE__*/ dual(2, (self, f) => new ContImpl(self, isEffect$1(f) ? returnPayload : andThenCont, f));
6712
6714
  /** @internal */
6713
6715
  const tap$1 = /*#__PURE__*/ dual(2, (self, f) => new ContImpl(self, isEffect$1(f) ? tapEffectCont : tapCont, f));
@@ -6959,6 +6961,10 @@ const exitPrimitive = /*#__PURE__*/ makePrimitive$1({
6959
6961
  }
6960
6962
  });
6961
6963
  /** @internal */
6964
+ const timeoutOrElse$1 = /*#__PURE__*/ dual(2, (self, options) => flatMap$2(timeoutOption(self, options.duration), (option) => isNone(option) ? options.orElse() : succeed$6(option.value)));
6965
+ /** @internal */
6966
+ const timeoutOption = /*#__PURE__*/ dual(2, (self, duration) => raceFirst$1(asSome(self), as$1(sleep$1(duration), none())));
6967
+ /** @internal */
6962
6968
  const ScopeTypeId = "~effect/Scope";
6963
6969
  /** @internal */
6964
6970
  const ScopeCloseableTypeId = "~effect/Scope/Closeable";
@@ -9379,7 +9385,19 @@ const Proto$7 = {
9379
9385
  return toDateUtc$1(this).toJSON();
9380
9386
  }
9381
9387
  };
9382
- ({ ...Proto$7 });
9388
+ const ProtoUtc = {
9389
+ ...Proto$7,
9390
+ _tag: "Utc",
9391
+ [symbol$1]() {
9392
+ return number$1(this.epochMilliseconds);
9393
+ },
9394
+ [symbol](that) {
9395
+ return isDateTime$1(that) && that._tag === "Utc" && this.epochMilliseconds === that.epochMilliseconds;
9396
+ },
9397
+ toString() {
9398
+ return `DateTime.Utc(${toDateUtc$1(this).toJSON()})`;
9399
+ }
9400
+ };
9383
9401
  ({ ...Proto$7 });
9384
9402
  const ProtoTimeZone = {
9385
9403
  [TimeZoneTypeId]: TimeZoneTypeId,
@@ -9390,7 +9408,23 @@ const ProtoTimeZone = {
9390
9408
  ({ ...ProtoTimeZone });
9391
9409
  ({ ...ProtoTimeZone });
9392
9410
  /** @internal */
9411
+ const isDateTime$1 = (u) => hasProperty(u, TypeId$27);
9412
+ const makeUtc = (epochMillis) => {
9413
+ const self = Object.create(ProtoUtc);
9414
+ self.epochMilliseconds = epochMillis;
9415
+ Object.defineProperty(self, "partsUtc", {
9416
+ value: void 0,
9417
+ enumerable: false,
9418
+ writable: true
9419
+ });
9420
+ return self;
9421
+ };
9422
+ /** @internal */
9423
+ const now$1 = /*#__PURE__*/ map$7(currentTimeMillis, makeUtc);
9424
+ /** @internal */
9393
9425
  const toDateUtc$1 = (self) => new Date(self.epochMilliseconds);
9426
+ /** @internal */
9427
+ const formatIso$1 = (self) => toDateUtc$1(self).toISOString();
9394
9428
  globalThis.Number;
9395
9429
  /**
9396
9430
  * Returns the number rounded with the given precision.
@@ -11078,6 +11112,35 @@ const ignore = ignore$1;
11078
11112
  */
11079
11113
  const orElseSucceed = orElseSucceed$1;
11080
11114
  /**
11115
+ * Applies a timeout to an effect, lazily evaluating `orElse` after interrupting
11116
+ * the source if the timeout is reached.
11117
+ *
11118
+ * **Example** (Falling back on timeout)
11119
+ *
11120
+ * ```ts import.meta.vitest
11121
+ * import { Effect } from "effect"
11122
+ * const output: Array<unknown> = []
11123
+ *
11124
+ * const program = Effect.timeoutOrElse(Effect.never, {
11125
+ * duration: 0,
11126
+ * orElse: () => Effect.sync(() => { output.push("Query timed out, using cached data") }).pipe(
11127
+ * Effect.as("Cached result")
11128
+ * )
11129
+ * })
11130
+ *
11131
+ * void output.push(await Effect.runPromise(program))
11132
+ * output // => ["Query timed out, using cached data", "Cached result"]
11133
+ * ```
11134
+ *
11135
+ * @see {@link timeout} for failing with a `TimeoutError`.
11136
+ * @see {@link timeoutOption} for returning `Option.none` on timeout.
11137
+ *
11138
+ * @stability stable
11139
+ * @category delays & timeouts
11140
+ * @since 4.0.0
11141
+ */
11142
+ const timeoutOrElse = timeoutOrElse$1;
11143
+ /**
11081
11144
  * Returns an effect that suspends the current fiber for the specified duration
11082
11145
  * without blocking a JavaScript thread.
11083
11146
  *
@@ -16538,17 +16601,56 @@ function resolve$1(ast) {
16538
16601
  return ast.checks ? ast.checks[ast.checks.length - 1].annotations : ast.annotations;
16539
16602
  }
16540
16603
  /** @internal */
16604
+ function resolveAt(key) {
16605
+ return (ast) => resolve$1(ast)?.[key];
16606
+ }
16607
+ /** @internal */
16541
16608
  const STRUCTURAL_ANNOTATION_KEY = "~structural";
16542
16609
  /** @internal */
16610
+ const IDENTIFIER_FALLBACK_KEY = "~identifier";
16611
+ /** @internal */
16543
16612
  const SENTINELS_ANNOTATION_KEY = "~sentinels";
16544
16613
  /** @internal */
16545
16614
  const CONSTRUCTOR_ANNOTATION_KEY = "~constructor";
16546
16615
  /** @internal */
16616
+ const jsonSchemaAnnotationKeys = [
16617
+ "title",
16618
+ "description",
16619
+ "default",
16620
+ "examples",
16621
+ "readOnly",
16622
+ "writeOnly",
16623
+ "format",
16624
+ "contentEncoding",
16625
+ "contentMediaType",
16626
+ "contentSchema"
16627
+ ];
16628
+ /** @internal */
16629
+ const resolveIdentifier = /*#__PURE__*/ resolveAt("identifier");
16630
+ /** @internal */
16631
+ const resolveIdentifierFallback = /*#__PURE__*/ resolveAt(IDENTIFIER_FALLBACK_KEY);
16632
+ /** @internal */
16547
16633
  const getExpected = /*#__PURE__*/ memoize((ast) => {
16548
16634
  const identifier = resolve$1(ast)?.identifier;
16549
16635
  if (typeof identifier === "string") return identifier;
16550
16636
  return ast.getExpected(getExpected);
16551
16637
  });
16638
+ /** @internal */
16639
+ const annotationExcludedKeys = /*#__PURE__*/ new Set([
16640
+ SENTINELS_ANNOTATION_KEY,
16641
+ STRUCTURAL_ANNOTATION_KEY,
16642
+ "representation",
16643
+ "arbitraryConstraint",
16644
+ "toJsonSchema",
16645
+ "toCode",
16646
+ "toEquivalence",
16647
+ "toFormatter",
16648
+ "toCodec",
16649
+ "toCodecJson",
16650
+ "toCodecStringTree",
16651
+ "toCodecIso",
16652
+ "toCodecArbitrary"
16653
+ ]);
16552
16654
  //#endregion
16553
16655
  //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/internal/schema/parser.js
16554
16656
  /** @internal */
@@ -17077,6 +17179,48 @@ function getSchemaIssueOrThrow(cause, message) {
17077
17179
  if (issue === void 0) throw new Error(message, { cause });
17078
17180
  return issue;
17079
17181
  }
17182
+ /**
17183
+ * Gets the current time using the `Clock` service and converts it to a `DateTime`.
17184
+ *
17185
+ * **Example** (Getting the current DateTime)
17186
+ *
17187
+ * ```ts import.meta.vitest
17188
+ * import { DateTime, Effect } from "effect"
17189
+ * import { TestClock } from "effect/testing"
17190
+ *
17191
+ * await Effect.runPromise(Effect.map(DateTime.now, DateTime.isDateTime)) // => true
17192
+ * ```
17193
+ *
17194
+ * @stability stable
17195
+ * @category constructors
17196
+ * @since 3.6.0
17197
+ */
17198
+ const now = now$1;
17199
+ /**
17200
+ * Formats a `DateTime` as a UTC ISO string.
17201
+ *
17202
+ * **Details**
17203
+ *
17204
+ * Always returns the UTC representation in ISO 8601 format, ignoring any time zone.
17205
+ *
17206
+ * **Example** (Formatting DateTime values as ISO strings)
17207
+ *
17208
+ * ```ts import.meta.vitest
17209
+ * import { DateTime } from "effect"
17210
+ *
17211
+ * DateTime.formatIso(DateTime.makeUnsafe("2024-01-01T12:30:45.123Z")) // => "2024-01-01T12:30:45.123Z"
17212
+ *
17213
+ * const zoned = DateTime.makeZonedUnsafe("2024-01-01T12:30:45.123Z", {
17214
+ * timeZone: "Europe/London"
17215
+ * })
17216
+ * DateTime.formatIso(zoned) // => "2024-01-01T12:30:45.123Z"
17217
+ * ```
17218
+ *
17219
+ * @stability stable
17220
+ * @category formatting
17221
+ * @since 3.6.0
17222
+ */
17223
+ const formatIso = formatIso$1;
17080
17224
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
17081
17225
  const codes = /*#__PURE__*/ (/* @__PURE__ */ new Uint8Array(123)).fill(255);
17082
17226
  for (let i = 0; i < 64; i++) codes[/*#__PURE__*/ alphabet.charCodeAt(i)] = i;
@@ -19470,6 +19614,10 @@ function record(key, value) {
19470
19614
  function isOptional(ast) {
19471
19615
  return ast.context?.isOptional ?? false;
19472
19616
  }
19617
+ /** @internal */
19618
+ function isMutable(ast) {
19619
+ return ast.context?.isMutable ?? false;
19620
+ }
19473
19621
  function isStructuralCheck(check) {
19474
19622
  return check.annotations?.["~structural"] === true || check._tag === "FilterGroup" && check.checks.every(isStructuralCheck);
19475
19623
  }
@@ -19640,6 +19788,11 @@ const parameterFromString = /*#__PURE__*/ applyToSelfOrLastLinkEncodingIdempoten
19640
19788
  case "Union": return ast.recur(parameterFromString);
19641
19789
  }
19642
19790
  });
19791
+ /**
19792
+ * any string, including newlines
19793
+ * @internal
19794
+ */
19795
+ const STRING_PATTERN = "[\\s\\S]*?";
19643
19796
  const isStringFiniteRegExp = /*#__PURE__*/ new globalThis.RegExp(`^${FINITE_PATTERN}$`);
19644
19797
  const isStringNumberRegExp = /*#__PURE__*/ new globalThis.RegExp(`^(?:${FINITE_PATTERN}|Infinity|-Infinity|NaN)$`);
19645
19798
  /** @internal */
@@ -19750,6 +19903,10 @@ const Json$1 = /*#__PURE__*/ new Declaration([], () => (input, ast, options) =>
19750
19903
  toCodecJson: () => void 0,
19751
19904
  toCodecStringTree: () => unknownToStringTree
19752
19905
  });
19906
+ /** @internal */
19907
+ const unknownToJson = /*#__PURE__*/ new Link(Json$1, /*#__PURE__*/ passthrough());
19908
+ /** @internal */
19909
+ const objectKeywordToJson = /*#__PURE__*/ new Link(/*#__PURE__*/ new Union$1([/*#__PURE__*/ new Arrays(false, [], [Json$1]), /*#__PURE__*/ new Objects([], [/*#__PURE__*/ new IndexSignature(string, Json$1)])]), /*#__PURE__*/ passthrough());
19753
19910
  /**
19754
19911
  * Returns true if the value is a StringTree value.
19755
19912
  *
@@ -22226,7 +22383,223 @@ function make$5(ast, options) {
22226
22383
  return self;
22227
22384
  }
22228
22385
  //#endregion
22386
+ //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/JsonPointer.js
22387
+ /**
22388
+ * Helpers for escaping JSON Pointer path segments and converting JSON Pointer
22389
+ * URI fragments. JSON Pointer uses `/` to separate path tokens inside a JSON
22390
+ * document, so token text must encode literal `~` and `/` characters. URI
22391
+ * fragments additionally apply percent-encoding after JSON Pointer escaping.
22392
+ *
22393
+ * @stability stable
22394
+ * @since 4.0.0
22395
+ */
22396
+ /**
22397
+ * Escapes a JSON Pointer reference token according to RFC 6901 by encoding special characters so the token can be safely used as a segment in a JSON Pointer.
22398
+ *
22399
+ * **When to use**
22400
+ *
22401
+ * Use when you need to escape a single JSON Pointer path segment.
22402
+ *
22403
+ * **Details**
22404
+ *
22405
+ * - Returns a new escaped string
22406
+ * - Replaces `~` (tilde) with `~0` and `/` (forward slash) with `~1`
22407
+ * - Returns the input unchanged if it contains no special characters
22408
+ * - Empty strings are valid and returned unchanged
22409
+ *
22410
+ * **Gotchas**
22411
+ *
22412
+ * The replacement order matters: `~` is replaced before `/` to prevent double-escaping.
22413
+ *
22414
+ * **Example** (Escaping special characters)
22415
+ *
22416
+ * ```ts import.meta.vitest
22417
+ * import { JsonPointer } from "effect"
22418
+ *
22419
+ * JsonPointer.escapeToken("a/b") // => "a~1b"
22420
+ * JsonPointer.escapeToken("c~d") // => "c~0d"
22421
+ * JsonPointer.escapeToken("path/to~key") // => "path~1to~0key"
22422
+ * ```
22423
+ *
22424
+ * @see {@link unescapeToken} The inverse operation for decoding escaped tokens
22425
+ * @stability stable
22426
+ * @category encoding
22427
+ * @since 4.0.0
22428
+ */
22429
+ function escapeToken(token) {
22430
+ return token.replace(/~/g, "~0").replace(/\//g, "~1");
22431
+ }
22432
+ /**
22433
+ * Decodes a JSON Pointer reference token according to RFC 6901 escaping rules.
22434
+ *
22435
+ * **When to use**
22436
+ *
22437
+ * Use when you need to decode a single escaped JSON Pointer path segment.
22438
+ *
22439
+ * **Details**
22440
+ *
22441
+ * - Returns a new unescaped string
22442
+ * - Replaces `~1` with `/` (forward slash) and `~0` with `~` (tilde)
22443
+ * - Returns the input unchanged if it contains no escaped sequences
22444
+ * - Empty strings are valid and returned unchanged
22445
+ *
22446
+ * **Gotchas**
22447
+ *
22448
+ * The replacement order matters: `~1` is replaced before `~0` to prevent incorrect decoding.
22449
+ *
22450
+ * **Example** (Unescaping special characters)
22451
+ *
22452
+ * ```ts import.meta.vitest
22453
+ * import { JsonPointer } from "effect"
22454
+ *
22455
+ * JsonPointer.unescapeToken("a~1b") // => "a/b"
22456
+ * JsonPointer.unescapeToken("c~0d") // => "c~d"
22457
+ * JsonPointer.unescapeToken("path~1to~0key") // => "path/to~key"
22458
+ * ```
22459
+ *
22460
+ * @see {@link escapeToken} The inverse operation for encoding tokens
22461
+ * @stability stable
22462
+ * @category decoding
22463
+ * @since 4.0.0
22464
+ */
22465
+ function unescapeToken(token) {
22466
+ return token.replace(/~1/g, "/").replace(/~0/g, "~");
22467
+ }
22468
+ /** @internal */
22469
+ function formatUriFragmentToken(token) {
22470
+ return encodeURI(escapeToken(token)).replace(/#/g, "%23");
22471
+ }
22472
+ /** @internal */
22473
+ function decodeUriFragment(fragment) {
22474
+ if (fragment.length === 0 || fragment === "#") return "";
22475
+ if (!fragment.startsWith("#")) return void 0;
22476
+ const encoded = fragment.slice(1);
22477
+ try {
22478
+ if (encodeURI(encoded).replace(/%25/g, "%").replace(/#/g, "%23") !== encoded) return void 0;
22479
+ const pointer = decodeURIComponent(encoded);
22480
+ return pointer.startsWith("/") && !/~(?:[^01]|$)/.test(pointer) ? pointer : void 0;
22481
+ } catch {
22482
+ return;
22483
+ }
22484
+ }
22485
+ //#endregion
22486
+ //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/JsonSchema.js
22487
+ /**
22488
+ * Helpers for normalizing and converting JSON Schema and OpenAPI schema
22489
+ * documents. Supported inputs include JSON Schema Draft-07, Draft 2020-12,
22490
+ * OpenAPI 3.0, and OpenAPI 3.1; conversions normalize through
22491
+ * `Document<"draft-2020-12">` before emitting another dialect, including
22492
+ * JSON Schema Draft-04. The module also defines document types, meta-schema
22493
+ * constants, and OpenAPI component-key helpers.
22494
+ *
22495
+ * @stability stable
22496
+ * @since 4.0.0
22497
+ */
22498
+ /**
22499
+ * Represents the `$schema` meta-schema URI for JSON Schema Draft 2020-12.
22500
+ *
22501
+ * **When to use**
22502
+ *
22503
+ * Use when you need to populate the `$schema` field while emitting a JSON
22504
+ * Schema document that should declare JSON Schema Draft 2020-12.
22505
+ *
22506
+ * **Details**
22507
+ *
22508
+ * The exported value is the literal string
22509
+ * `https://json-schema.org/draft/2020-12/schema`.
22510
+ *
22511
+ * @see {@link META_SCHEMA_URI_DRAFT_07} for the Draft-07 `$schema` URI
22512
+ *
22513
+ * @stability stable
22514
+ * @category constants
22515
+ * @since 4.0.0
22516
+ */
22517
+ const META_SCHEMA_URI_DRAFT_2020_12 = "https://json-schema.org/draft/2020-12/schema";
22518
+ function transformSchema(node, transform) {
22519
+ return walk(node, false, true);
22520
+ function walk(node, inheritedResource, isRoot = false) {
22521
+ if (!isObject(node)) return node;
22522
+ const inEmbeddedResource = inheritedResource || !isRoot && createsResource(node.$id);
22523
+ const out = {};
22524
+ for (const key of Object.keys(node)) {
22525
+ const value = node[key];
22526
+ let transformed = value;
22527
+ switch (key) {
22528
+ case "$defs":
22529
+ case "properties":
22530
+ case "patternProperties":
22531
+ case "dependentSchemas":
22532
+ transformed = mapObject(value, (value) => walk(value, inEmbeddedResource)) ?? value;
22533
+ break;
22534
+ case "allOf":
22535
+ case "anyOf":
22536
+ case "oneOf":
22537
+ case "prefixItems":
22538
+ transformed = Array.isArray(value) ? value.map((value) => walk(value, inEmbeddedResource)) : value;
22539
+ break;
22540
+ case "not":
22541
+ case "additionalProperties":
22542
+ case "propertyNames":
22543
+ case "unevaluatedProperties":
22544
+ case "items":
22545
+ case "contains":
22546
+ case "unevaluatedItems":
22547
+ case "if":
22548
+ case "then":
22549
+ case "else":
22550
+ case "contentSchema": transformed = walk(value, inEmbeddedResource);
22551
+ }
22552
+ assignProperty(out, key, transformed);
22553
+ }
22554
+ transform(out, inEmbeddedResource);
22555
+ return out;
22556
+ }
22557
+ }
22558
+ /** @internal */
22559
+ function rewriteRefs(schema, rewrite) {
22560
+ return transformSchema(schema, (schema) => {
22561
+ rewriteSchemaRef(schema, rewrite);
22562
+ });
22563
+ }
22564
+ function rewriteSchemaRef(schema, rewrite) {
22565
+ if (typeof schema.$ref === "string") assignProperty(schema, "$ref", rewrite(schema.$ref, "$ref"));
22566
+ if (typeof schema.$dynamicRef === "string") assignProperty(schema, "$dynamicRef", rewrite(schema.$dynamicRef, "$dynamicRef"));
22567
+ }
22568
+ function mapObject(value, f) {
22569
+ if (!isObject(value)) return void 0;
22570
+ const out = {};
22571
+ for (const key of Object.keys(value)) assignProperty(out, key, f(value[key], key));
22572
+ return out;
22573
+ }
22574
+ function createsResource(id) {
22575
+ return typeof id === "string" && id.length > 0 && id[0] !== "#";
22576
+ }
22577
+ const PRE_2020_TO_2020_COLLISIONS = [
22578
+ "$anchor",
22579
+ "$defs",
22580
+ "$dynamicAnchor",
22581
+ "$dynamicRef",
22582
+ "$vocabulary",
22583
+ "contentSchema",
22584
+ "dependentRequired",
22585
+ "dependentSchemas",
22586
+ "maxContains",
22587
+ "minContains",
22588
+ "prefixItems",
22589
+ "unevaluatedItems",
22590
+ "unevaluatedProperties"
22591
+ ];
22592
+ [...PRE_2020_TO_2020_COLLISIONS];
22593
+ [...PRE_2020_TO_2020_COLLISIONS];
22594
+ //#endregion
22229
22595
  //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/internal/schema/toCodec.js
22596
+ /** @internal */
22597
+ const toCodecJsonAST = /*#__PURE__*/ applyToSelfOrLastLinkEncodingIdempotent((ast) => {
22598
+ const out = toCodecJsonASTStep(ast, toCodecJsonAST);
22599
+ const context = ast.context;
22600
+ if (out === ast || context === void 0) return out;
22601
+ return replaceContextLastLink(out, withoutConstructorDefault(context));
22602
+ });
22230
22603
  function withoutConstructorDefault(context) {
22231
22604
  return context.constructorDefault === void 0 ? context : new Context(context.isOptional, context.isMutable, void 0, context.annotations);
22232
22605
  }
@@ -22240,6 +22613,44 @@ function makeReorder(getPriority) {
22240
22613
  return sortedTypes;
22241
22614
  };
22242
22615
  }
22616
+ const toCodecJsonReorder = /*#__PURE__*/ makeReorder((ast) => {
22617
+ switch (ast._tag) {
22618
+ case "BigInt":
22619
+ case "Symbol":
22620
+ case "UniqueSymbol": return 0;
22621
+ default: return 1;
22622
+ }
22623
+ });
22624
+ function toCodecJsonASTStep(ast, recur) {
22625
+ switch (ast._tag) {
22626
+ case "Declaration": {
22627
+ const getLink = ast.annotations?.toCodecJson ?? ast.annotations?.toCodec;
22628
+ if (!isFunction(getLink)) return replaceEncoding(ast, [unknownToJson]);
22629
+ const link = getLink(ast.typeParameters.map((tp) => make$5(toEncoded(tp))));
22630
+ return link === void 0 ? ast : replaceEncoding(ast, [mapLink(link, recur)]);
22631
+ }
22632
+ case "Unknown": return replaceEncoding(ast, [unknownToJson]);
22633
+ case "ObjectKeyword": return replaceEncoding(ast, [objectKeywordToJson]);
22634
+ case "Undefined":
22635
+ case "Void":
22636
+ case "Literal":
22637
+ case "Number": return ast.toCodecJson();
22638
+ case "UniqueSymbol":
22639
+ case "Symbol":
22640
+ case "BigInt": return ast.toCodecStringTree();
22641
+ case "Objects":
22642
+ validateCanonicalObjectPropertyNames(ast);
22643
+ return ast.recur(recur, parameterFromString);
22644
+ case "Union": {
22645
+ const sortedTypes = toCodecJsonReorder(ast.types);
22646
+ if (sortedTypes !== ast.types) return new Union$1(sortedTypes, ast.options, ast.annotations, ast.checks, ast.encoding, ast.context, ast.encodingChecks).recur(recur);
22647
+ return ast.recur(recur);
22648
+ }
22649
+ case "Arrays":
22650
+ case "Suspend": return ast.recur(recur);
22651
+ }
22652
+ return ast;
22653
+ }
22243
22654
  /** @internal */
22244
22655
  function toCodecStringTree$1(schema) {
22245
22656
  return make$5(toCodecStringTreeAST(schema.ast), { schema });
@@ -22305,6 +22716,849 @@ const toCodecStringTreeAST = /*#__PURE__*/ applyToSelfOrLastLinkEncodingIdempote
22305
22716
  if (out !== ast && ast.context !== void 0) return replaceContextLastLink(out, withoutConstructorDefault(ast.context));
22306
22717
  return out;
22307
22718
  }, { stopAt: isCodecArrayFromSingleLink });
22719
+ globalThis.RegExp;
22720
+ /**
22721
+ * Escapes special characters in a regular expression pattern.
22722
+ *
22723
+ * **When to use**
22724
+ *
22725
+ * Use to turn literal text into a safe regular expression pattern fragment.
22726
+ *
22727
+ * **Example** (Escaping a pattern string)
22728
+ *
22729
+ * ```ts import.meta.vitest
22730
+ * import { RegExp } from "effect"
22731
+ *
22732
+ * RegExp.escape("a*b") // => "a\\*b"
22733
+ * ```
22734
+ *
22735
+ * @stability stable
22736
+ * @category transforming
22737
+ * @since 2.0.0
22738
+ */
22739
+ const escape = (string) => string.replace(/[/\\^$*+?.()|[\]{}]/g, "\\$&");
22740
+ //#endregion
22741
+ //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/internal/errors.js
22742
+ /** @internal */
22743
+ function errorWithPath(message, path) {
22744
+ if (path.length > 0) message += `\n at ${formatPath(path)}`;
22745
+ return new Error(message);
22746
+ }
22747
+ //#endregion
22748
+ //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/internal/schema/toJsonSchemaDocument.js
22749
+ function formatDefinitionReference(key) {
22750
+ return `#/$defs/${formatUriFragmentToken(key)}`;
22751
+ }
22752
+ const jsonSchemaAnnotationExcludedKeys = /*#__PURE__*/ new Set([
22753
+ ...annotationExcludedKeys,
22754
+ IDENTIFIER_FALLBACK_KEY,
22755
+ ...jsonSchemaAnnotationKeys
22756
+ ]);
22757
+ function collectJsonSchemaAnnotations(annotations, options) {
22758
+ if (annotations === void 0) return void 0;
22759
+ const out = {};
22760
+ const title = annotations.title;
22761
+ if (typeof title === "string") out.title = title;
22762
+ const description = annotations.description;
22763
+ const expected = annotations.expected;
22764
+ if (typeof description === "string") out.description = description;
22765
+ else if (options?.generateDescriptions === true && typeof expected === "string") out.description = expected;
22766
+ const defaultValue = annotations.default;
22767
+ if (isJson(defaultValue)) out.default = defaultValue;
22768
+ const examples = annotations.examples;
22769
+ if (Array.isArray(examples) && isJson(examples)) out.examples = examples;
22770
+ const readOnly = annotations.readOnly;
22771
+ if (typeof readOnly === "boolean") out.readOnly = readOnly;
22772
+ const writeOnly = annotations.writeOnly;
22773
+ if (typeof writeOnly === "boolean") out.writeOnly = writeOnly;
22774
+ const format = annotations.format;
22775
+ if (typeof format === "string") out.format = format;
22776
+ const contentEncoding = annotations.contentEncoding;
22777
+ if (typeof contentEncoding === "string") out.contentEncoding = contentEncoding;
22778
+ const contentMediaType = annotations.contentMediaType;
22779
+ if (typeof contentMediaType === "string") out.contentMediaType = contentMediaType;
22780
+ const contentSchema = annotations.contentSchema;
22781
+ if (isJson(contentSchema)) out.contentSchema = contentSchema;
22782
+ if (options?.includeAnnotationKey !== void 0) for (const [key, value] of Object.entries(annotations)) {
22783
+ if (jsonSchemaAnnotationExcludedKeys.has(key) || !options.includeAnnotationKey(key)) continue;
22784
+ if (isJson(value)) assignProperty(out, key, value);
22785
+ }
22786
+ return Object.keys(out).length === 0 ? void 0 : out;
22787
+ }
22788
+ function extractJsonSchemaNumberType(schema) {
22789
+ let type = schema.type === "number" || schema.type === "integer" ? schema.type : void 0;
22790
+ let out = schema;
22791
+ if (type !== void 0) {
22792
+ out = { ...schema };
22793
+ delete out.type;
22794
+ }
22795
+ if (Array.isArray(out.allOf)) {
22796
+ const members = [];
22797
+ let changed = false;
22798
+ for (const member of out.allOf) {
22799
+ const extracted = extractJsonSchemaNumberType(member);
22800
+ if (extracted.type !== void 0) {
22801
+ changed = true;
22802
+ if (type === void 0 || extracted.type === "integer") type = extracted.type;
22803
+ }
22804
+ if (Object.keys(extracted.schema).length > 0) members.push(extracted.schema);
22805
+ }
22806
+ if (changed) {
22807
+ const { allOf: _, ...rest } = out;
22808
+ out = members.length === 0 ? rest : {
22809
+ ...rest,
22810
+ allOf: members
22811
+ };
22812
+ }
22813
+ }
22814
+ return {
22815
+ type,
22816
+ schema: out
22817
+ };
22818
+ }
22819
+ function isJsonSchemaNumberEncoding(schema) {
22820
+ if (!Array.isArray(schema.anyOf) || schema.anyOf.length !== 2 || schema.anyOf[0]?.type !== "number") return false;
22821
+ const special = schema.anyOf[1];
22822
+ return special?.type === "string" && Array.isArray(special.enum) && special.enum.length === 3 && special.enum.includes("NaN") && special.enum.includes("Infinity") && special.enum.includes("-Infinity");
22823
+ }
22824
+ const inlineableCheckKeywords = "|type|format|pattern|multipleOf|minimum|maximum|exclusiveMinimum|exclusiveMaximum|minLength|maxLength|minItems|maxItems|uniqueItems|minProperties|maxProperties|propertyNames|";
22825
+ function hasOnlyKeywords(schema, allowed) {
22826
+ return Object.keys(schema).every((key) => allowed.includes(`|${key}|`));
22827
+ }
22828
+ function hasNoCollisions(left, rightKeys) {
22829
+ return typeof left.$ref !== "string" && rightKeys.every((key) => !Object.hasOwn(left, key));
22830
+ }
22831
+ const promotableAnnotationKeywords = "|title|description|default|examples|readOnly|writeOnly|";
22832
+ const inlineableAnnotatedCheckKeywords = inlineableCheckKeywords + promotableAnnotationKeywords;
22833
+ function appendJsonSchema(left, right, inlineCheck) {
22834
+ if (Object.keys(left).length === 0) return right;
22835
+ const rightKeys = Object.keys(right);
22836
+ if (rightKeys.length === 0) return left;
22837
+ const leftType = left.type === "number" || left.type === "integer" ? left.type : void 0;
22838
+ const isNumberEncoding = isJsonSchemaNumberEncoding(left);
22839
+ if (leftType !== void 0 || isNumberEncoding) {
22840
+ const extracted = extractJsonSchemaNumberType(right);
22841
+ if (extracted.type !== void 0) {
22842
+ const type = leftType === "integer" || extracted.type === "integer" ? "integer" : "number";
22843
+ const base = {
22844
+ ...left,
22845
+ type
22846
+ };
22847
+ if (isNumberEncoding) delete base.anyOf;
22848
+ const extractedKeys = Object.keys(extracted.schema);
22849
+ if (extractedKeys.length === 0) return base;
22850
+ return hasOnlyKeywords(extracted.schema, promotableAnnotationKeywords) && hasNoCollisions(base, extractedKeys) ? {
22851
+ ...base,
22852
+ ...extracted.schema
22853
+ } : appendJsonSchema(base, extracted.schema, inlineCheck);
22854
+ }
22855
+ }
22856
+ if (inlineCheck && hasNoCollisions(left, rightKeys)) return {
22857
+ ...left,
22858
+ ...right
22859
+ };
22860
+ const members = Array.isArray(right.allOf) && rightKeys.length === 1 ? right.allOf : [right];
22861
+ if (Array.isArray(left.allOf)) return {
22862
+ ...left,
22863
+ allOf: [...left.allOf, ...members]
22864
+ };
22865
+ if (typeof left.$ref === "string") return { allOf: [left, ...members] };
22866
+ return {
22867
+ ...left,
22868
+ allOf: members
22869
+ };
22870
+ }
22871
+ function isExact(exactness, seen = /* @__PURE__ */ new Set()) {
22872
+ if (typeof exactness === "boolean") return exactness;
22873
+ if (seen.has(exactness)) return true;
22874
+ seen.add(exactness);
22875
+ return exactness.every((dependency) => isExact(dependency, seen));
22876
+ }
22877
+ function isApproximateCheckOutput(output) {
22878
+ return Array.isArray(output);
22879
+ }
22880
+ function compileJsonSchema(representations, rootPaths, references, options) {
22881
+ const compiledDefinitions = /* @__PURE__ */ new Map();
22882
+ const definitionAliases = /* @__PURE__ */ new Map();
22883
+ const compiledRepresentations = /* @__PURE__ */ new WeakMap();
22884
+ const pendingOneOf = /* @__PURE__ */ new Map();
22885
+ const fallbackDefinitions = /* @__PURE__ */ new Map();
22886
+ const referenceKeys = Object.keys(references);
22887
+ for (const key of referenceKeys) compileDefinition(key, ["references", key]);
22888
+ const compiledSchemas = map$8(representations, (representation, index) => recur(representation, rootPaths[index], []));
22889
+ for (const [branches, schemas] of pendingOneOf.values()) if (!isExact(branches)) {
22890
+ for (const schema of schemas) if (Array.isArray(schema.oneOf)) {
22891
+ schema.anyOf = schema.oneOf;
22892
+ delete schema.oneOf;
22893
+ }
22894
+ }
22895
+ for (const key of referenceKeys) {
22896
+ const representation = references[key];
22897
+ const fallback = getIdentifierFallback(representation);
22898
+ if (fallback === void 0) continue;
22899
+ const schema = compiledDefinitions.get(key)[0];
22900
+ const candidates = fallbackDefinitions.get(fallback);
22901
+ const match = candidates?.find((candidate) => equals$1(compiledDefinitions.get(candidate)[0], schema));
22902
+ if (match === void 0) {
22903
+ if (candidates === void 0) fallbackDefinitions.set(fallback, [key]);
22904
+ else candidates.push(key);
22905
+ } else definitionAliases.set(key, match);
22906
+ }
22907
+ const schemas = map$8(compiledSchemas, finalizeJsonSchema);
22908
+ const definitions = {};
22909
+ for (const key of referenceKeys) if (!definitionAliases.has(key)) assignProperty(definitions, key, finalizeJsonSchema(compiledDefinitions.get(key)[0]));
22910
+ return {
22911
+ dialect: "draft-2020-12",
22912
+ schemas,
22913
+ definitions
22914
+ };
22915
+ function registerPendingOneOf(schema) {
22916
+ if (Array.isArray(schema.oneOf)) pendingOneOf.get(schema.oneOf)?.[1].push(schema);
22917
+ return schema;
22918
+ }
22919
+ function compileDefinition(key, path) {
22920
+ if (!Object.hasOwn(references, key)) throw errorWithPath(`Invalid reference ${key}`, [...path, "$ref"]);
22921
+ const cached = compiledDefinitions.get(key);
22922
+ if (cached !== void 0) return cached;
22923
+ const result = [{}, []];
22924
+ compiledDefinitions.set(key, result);
22925
+ result[0] = recur(references[key], ["references", key], result[1]);
22926
+ return result;
22927
+ }
22928
+ function finalizeJsonSchema(schema) {
22929
+ return rewriteRefs(schema, ($ref) => {
22930
+ const pointer = decodeUriFragment($ref);
22931
+ if (pointer === void 0) {
22932
+ if (/^#(?:\/|%2f)/i.test($ref)) throw new Error(`Invalid JSON Pointer URI fragment ${JSON.stringify($ref)}`);
22933
+ return $ref;
22934
+ }
22935
+ if (!pointer.startsWith("/$defs/")) return $ref;
22936
+ const separator = pointer.indexOf("/", 7);
22937
+ const canonical = definitionAliases.get(unescapeToken(pointer.slice(7, separator < 0 ? void 0 : separator)));
22938
+ if (canonical === void 0) return $ref;
22939
+ return $ref.replace(/^#(?:\/|%2f).*?(?:\/|%2f).*?(?=\/|%2f|$)/i, formatDefinitionReference(canonical));
22940
+ });
22941
+ }
22942
+ function getIdentifierFallback(representation) {
22943
+ if (representation._tag === "Reference") return void 0;
22944
+ const annotations = representation.checks.length === 0 ? representation.annotations : representation.checks[representation.checks.length - 1].annotations;
22945
+ return typeof annotations?.identifier !== "string" && typeof annotations?.["~identifier"] === "string" ? annotations[IDENTIFIER_FALLBACK_KEY] : void 0;
22946
+ }
22947
+ function compileCheck(check, type, path, dependencies) {
22948
+ const annotations = check.annotations;
22949
+ const callback = annotations?.toJsonSchema;
22950
+ if (callback !== void 0) {
22951
+ const result = callback({
22952
+ type,
22953
+ schemas: check.representation?.schemas?.map((schema, index) => recur(schema, [
22954
+ ...path,
22955
+ "representation",
22956
+ "schemas",
22957
+ index
22958
+ ], dependencies)) ?? []
22959
+ });
22960
+ const approximate = isApproximateCheckOutput(result);
22961
+ if (approximate) dependencies.push(false);
22962
+ const fragment = approximate ? result[0] : result;
22963
+ const ordinary = collectJsonSchemaAnnotations(annotations, options);
22964
+ const schema = ordinary === void 0 ? fragment : {
22965
+ ...fragment,
22966
+ ...ordinary
22967
+ };
22968
+ const allowed = ordinary === void 0 ? inlineableCheckKeywords : inlineableAnnotatedCheckKeywords;
22969
+ return [schema, check._tag === "Filter" && hasOnlyKeywords(schema, allowed) && (ordinary === void 0 || hasOnlyKeywords(ordinary, promotableAnnotationKeywords)) ? true : void 0];
22970
+ }
22971
+ if (check._tag === "Filter") {
22972
+ dependencies.push(false);
22973
+ return;
22974
+ }
22975
+ const schemas = check.checks.flatMap((child, index) => {
22976
+ const result = compileCheck(child, type, [
22977
+ ...path,
22978
+ "checks",
22979
+ index
22980
+ ], dependencies);
22981
+ return result === void 0 ? [] : [result[0]];
22982
+ });
22983
+ if (schemas.length === 0) return void 0;
22984
+ const ordinary = collectJsonSchemaAnnotations(annotations, options);
22985
+ const schema = { allOf: schemas };
22986
+ return [ordinary === void 0 ? schema : {
22987
+ ...schema,
22988
+ ...ordinary
22989
+ }];
22990
+ }
22991
+ function recur(representation, path, dependencies) {
22992
+ if (representation._tag === "Reference") {
22993
+ dependencies.push(compileDefinition(representation.$ref, path)[1]);
22994
+ return { $ref: formatDefinitionReference(representation.$ref) };
22995
+ }
22996
+ const cached = compiledRepresentations.get(representation);
22997
+ if (cached !== void 0) {
22998
+ dependencies.push(cached[1]);
22999
+ return cached[0];
23000
+ }
23001
+ const local = [];
23002
+ dependencies.push(local);
23003
+ let output = on(representation, path, local);
23004
+ const ordinary = collectJsonSchemaAnnotations(representation.annotations, options);
23005
+ if (ordinary !== void 0) output = {
23006
+ ...output,
23007
+ ...ordinary
23008
+ };
23009
+ for (let index = 0; index < representation.checks.length; index++) {
23010
+ const type = typeof output.type === "string" && isJsonSchemaType(output.type) ? output.type : void 0;
23011
+ const check = compileCheck(representation.checks[index], type, [
23012
+ ...path,
23013
+ "checks",
23014
+ index
23015
+ ], local);
23016
+ if (check !== void 0) output = appendJsonSchema(output, ...check);
23017
+ }
23018
+ compiledRepresentations.set(representation, [output, local]);
23019
+ return registerPendingOneOf(output);
23020
+ }
23021
+ function on(representation, path, dependencies) {
23022
+ switch (representation._tag) {
23023
+ case "Any":
23024
+ case "Unknown": return {};
23025
+ case "ObjectKeyword": return { anyOf: [{ type: "object" }, { type: "array" }] };
23026
+ case "Void":
23027
+ case "Undefined":
23028
+ case "Null": return { type: "null" };
23029
+ case "BigInt": return {
23030
+ type: "string",
23031
+ allOf: [{ pattern: "^-?\\d+$" }]
23032
+ };
23033
+ case "Symbol": return {
23034
+ type: "string",
23035
+ allOf: [{ pattern: "^Symbol\\(([\\s\\S]*)\\)$" }]
23036
+ };
23037
+ case "UniqueSymbol": return globalThis.Symbol.keyFor(representation.symbol) === void 0 ? { not: {} } : {
23038
+ type: "string",
23039
+ enum: [globalThis.String(representation.symbol)]
23040
+ };
23041
+ case "Declaration":
23042
+ dependencies.push(false);
23043
+ return {};
23044
+ case "Suspend": return recur(representation.thunk, [...path, "thunk"], dependencies);
23045
+ case "Never": return { not: {} };
23046
+ case "String": return { type: "string" };
23047
+ case "Number": return { anyOf: [{ type: "number" }, {
23048
+ type: "string",
23049
+ enum: [
23050
+ "NaN",
23051
+ "Infinity",
23052
+ "-Infinity"
23053
+ ]
23054
+ }] };
23055
+ case "Boolean": return { type: "boolean" };
23056
+ case "Literal": {
23057
+ const literal = representation.literal;
23058
+ return typeof literal === "bigint" ? {
23059
+ type: "string",
23060
+ enum: [globalThis.String(literal)]
23061
+ } : {
23062
+ type: typeof literal,
23063
+ enum: [literal]
23064
+ };
23065
+ }
23066
+ case "Enum": {
23067
+ const types = representation.enums.map(([title, literal], index) => {
23068
+ if (typeof literal === "number" && !globalThis.Number.isFinite(literal)) throw errorWithPath(`Invalid numeric enum value ${globalThis.String(literal)}`, [
23069
+ ...path,
23070
+ "enums",
23071
+ index,
23072
+ 1
23073
+ ]);
23074
+ return {
23075
+ type: typeof literal,
23076
+ enum: [literal],
23077
+ title
23078
+ };
23079
+ });
23080
+ return types.length === 0 ? { not: {} } : { anyOf: types };
23081
+ }
23082
+ case "TemplateLiteral": return {
23083
+ type: "string",
23084
+ pattern: `^${representation.parts.map(getPartPattern).join("")}$`
23085
+ };
23086
+ case "Arrays": {
23087
+ if (representation.rest.length > 1) throw errorWithPath("Invalid schema representation document", [...path, "rest"]);
23088
+ const out = { type: "array" };
23089
+ let minItems = representation.elements.length;
23090
+ const prefixItems = representation.elements.map((element, index) => {
23091
+ if (element.isOptional) minItems--;
23092
+ const schema = recur(element.type, [
23093
+ ...path,
23094
+ "elements",
23095
+ index,
23096
+ "type"
23097
+ ], dependencies);
23098
+ const annotations = collectJsonSchemaAnnotations(element.annotations, options);
23099
+ return annotations === void 0 ? schema : registerPendingOneOf(appendJsonSchema(schema, annotations));
23100
+ });
23101
+ if (prefixItems.length > 0) {
23102
+ out.prefixItems = prefixItems;
23103
+ out.maxItems = representation.elements.length;
23104
+ if (minItems > 0) out.minItems = minItems;
23105
+ } else out.items = false;
23106
+ if (representation.rest.length === 1) {
23107
+ delete out.maxItems;
23108
+ const rest = recur(representation.rest[0], [
23109
+ ...path,
23110
+ "rest",
23111
+ 0
23112
+ ], dependencies);
23113
+ if (Object.keys(rest).length > 0) out.items = rest;
23114
+ else delete out.items;
23115
+ }
23116
+ return out;
23117
+ }
23118
+ case "Objects": {
23119
+ if (representation.propertySignatures.length === 0 && representation.indexSignatures.length === 0) return { not: { type: "null" } };
23120
+ const out = { type: "object" };
23121
+ const properties = {};
23122
+ const required = [];
23123
+ for (let index = 0; index < representation.propertySignatures.length; index++) {
23124
+ const property = representation.propertySignatures[index];
23125
+ if (typeof property.name !== "string") throw errorWithPath("Invalid schema representation document", [
23126
+ ...path,
23127
+ "propertySignatures",
23128
+ index,
23129
+ "name"
23130
+ ]);
23131
+ const name = property.name;
23132
+ const schema = recur(property.type, [
23133
+ ...path,
23134
+ "propertySignatures",
23135
+ index,
23136
+ "type"
23137
+ ], dependencies);
23138
+ const annotations = collectJsonSchemaAnnotations(property.annotations, options);
23139
+ assignProperty(properties, name, annotations === void 0 ? schema : registerPendingOneOf(appendJsonSchema(schema, annotations)));
23140
+ if (!property.isOptional) required.push(name);
23141
+ }
23142
+ if (representation.propertySignatures.length > 0) out.properties = properties;
23143
+ if (required.length > 0) out.required = required;
23144
+ const patternProperties = {};
23145
+ const additionalPropertySchemas = [];
23146
+ const indexValueSchemas = [];
23147
+ let hasUnrepresentableIndexPattern = false;
23148
+ for (let index = 0; index < representation.indexSignatures.length; index++) {
23149
+ const signature = representation.indexSignatures[index];
23150
+ let type = recur(signature.type, [
23151
+ ...path,
23152
+ "indexSignatures",
23153
+ index,
23154
+ "type"
23155
+ ], dependencies);
23156
+ if (equals$1(type, { not: {} })) type = false;
23157
+ indexValueSchemas.push(type);
23158
+ const patterns = getParameterPatterns(signature.parameter, [
23159
+ ...path,
23160
+ "indexSignatures",
23161
+ index,
23162
+ "parameter"
23163
+ ], /* @__PURE__ */ new Set());
23164
+ if (patterns === void 0) {
23165
+ hasUnrepresentableIndexPattern = true;
23166
+ dependencies.push(false);
23167
+ continue;
23168
+ }
23169
+ if (patterns.length === 0) additionalPropertySchemas.push(type);
23170
+ else for (const pattern of patterns) {
23171
+ const previous = patternProperties[pattern];
23172
+ assignProperty(patternProperties, pattern, previous === void 0 ? type : previous === false || type === false ? false : registerPendingOneOf(appendJsonSchema(previous, type)));
23173
+ }
23174
+ }
23175
+ const hasPatternProperties = Object.keys(patternProperties).length > 0;
23176
+ if (hasPatternProperties) out.patternProperties = patternProperties;
23177
+ if (hasUnrepresentableIndexPattern && options?.onExcessProperty === "error") {
23178
+ const propertyNames = [];
23179
+ if (representation.propertySignatures.length > 0) propertyNames.push({ enum: Object.keys(properties) });
23180
+ for (let index = 0; index < representation.indexSignatures.length; index++) propertyNames.push(recur(representation.indexSignatures[index].parameter, [
23181
+ ...path,
23182
+ "indexSignatures",
23183
+ index,
23184
+ "parameter"
23185
+ ], dependencies));
23186
+ out.propertyNames = propertyNames.length === 1 ? propertyNames[0] : { anyOf: propertyNames };
23187
+ out.additionalProperties = indexValueSchemas.length === 1 ? indexValueSchemas[0] : { anyOf: indexValueSchemas };
23188
+ if (additionalPropertySchemas.length > 0) out.allOf = additionalPropertySchemas.map((type) => ({
23189
+ type: "object",
23190
+ additionalProperties: type
23191
+ }));
23192
+ } else if (additionalPropertySchemas.length === 0) out.additionalProperties = hasUnrepresentableIndexPattern || options?.onExcessProperty !== "error";
23193
+ else if (additionalPropertySchemas.length === 1 && representation.propertySignatures.length === 0 && !hasPatternProperties) out.additionalProperties = additionalPropertySchemas[0];
23194
+ else if (additionalPropertySchemas.length > 0) out.allOf = additionalPropertySchemas.map((type) => ({
23195
+ type: "object",
23196
+ additionalProperties: type
23197
+ }));
23198
+ if (typeof out.additionalProperties === "object" && out.additionalProperties !== null && Object.keys(out.additionalProperties).length === 0) delete out.additionalProperties;
23199
+ return out;
23200
+ }
23201
+ case "Union": {
23202
+ const branches = [];
23203
+ const types = representation.types.map((type, index) => recur(type, [
23204
+ ...path,
23205
+ "types",
23206
+ index
23207
+ ], branches));
23208
+ dependencies.push(branches);
23209
+ if (types.length === 0) return { not: {} };
23210
+ const mode = representation.options?.mode ?? "anyOf";
23211
+ if (mode === "anyOf" && types.length > 1) {
23212
+ const compacted = compactEnums(types);
23213
+ if (compacted !== void 0) return compacted;
23214
+ }
23215
+ if (mode === "oneOf") pendingOneOf.set(types, [branches, []]);
23216
+ return { [mode]: types };
23217
+ }
23218
+ }
23219
+ }
23220
+ function getParameterPatterns(parameter, path, seenReferences) {
23221
+ switch (parameter._tag) {
23222
+ case "Reference": {
23223
+ if (!Object.hasOwn(references, parameter.$ref)) throw errorWithPath(`Invalid reference ${parameter.$ref}`, [...path, "$ref"]);
23224
+ compileDefinition(parameter.$ref, path);
23225
+ if (seenReferences.has(parameter.$ref)) return void 0;
23226
+ const next = new Set(seenReferences).add(parameter.$ref);
23227
+ return getParameterPatterns(references[parameter.$ref], ["references", parameter.$ref], next);
23228
+ }
23229
+ case "String": {
23230
+ if (parameter.checks.length === 0) return [];
23231
+ if (parameter.checks.length !== 1 || parameter.checks[0]._tag !== "Filter" || parameter.checks[0].annotations?.toJsonSchema === void 0) return;
23232
+ const dependencies = [];
23233
+ const schema = recur(parameter, path, dependencies);
23234
+ if (!isExact(dependencies)) return void 0;
23235
+ return schema.type === "string" && typeof schema.pattern === "string" && hasOnlyKeywords(schema, "|type|pattern|title|description|default|examples|readOnly|writeOnly|") ? [schema.pattern] : void 0;
23236
+ }
23237
+ case "TemplateLiteral": return parameter.checks.length === 0 ? [`^${parameter.parts.map(getPartPattern).join("")}$`] : void 0;
23238
+ case "Union": {
23239
+ if (parameter.types.length === 0 || parameter.checks.length > 0 || parameter.options?.mode === "oneOf") return;
23240
+ const patterns = [];
23241
+ for (let index = 0; index < parameter.types.length; index++) {
23242
+ const memberPatterns = getParameterPatterns(parameter.types[index], [
23243
+ ...path,
23244
+ "types",
23245
+ index
23246
+ ], seenReferences);
23247
+ if (memberPatterns === void 0) return void 0;
23248
+ if (memberPatterns.length === 0) return [];
23249
+ patterns.push(...memberPatterns);
23250
+ }
23251
+ return patterns;
23252
+ }
23253
+ default: throw errorWithPath("Invalid schema representation document", path);
23254
+ }
23255
+ }
23256
+ }
23257
+ function isJsonSchemaType(input) {
23258
+ return input === "string" || input === "number" || input === "boolean" || input === "array" || input === "object" || input === "null" || input === "integer";
23259
+ }
23260
+ function compactEnums(schemas) {
23261
+ let sharedType = void 0;
23262
+ const values = [];
23263
+ for (const schema of schemas) {
23264
+ if (Object.keys(schema).length !== 2 || schema.type === void 0 || !Array.isArray(schema.enum) || schema.enum.length === 0) return;
23265
+ if (sharedType === void 0) sharedType = schema.type;
23266
+ else if (schema.type !== sharedType) return void 0;
23267
+ for (const value of schema.enum) {
23268
+ if (values.includes(value)) return void 0;
23269
+ values.push(value);
23270
+ }
23271
+ }
23272
+ return {
23273
+ type: sharedType,
23274
+ enum: values
23275
+ };
23276
+ }
23277
+ function getPartPattern(part) {
23278
+ switch (part._tag) {
23279
+ case "Literal": return escape(globalThis.String(part.literal));
23280
+ case "String": return STRING_PATTERN;
23281
+ case "Number": return FINITE_PATTERN;
23282
+ case "TemplateLiteral": return part.parts.map(getPartPattern).join("");
23283
+ case "Union": return `(?:${part.types.map(getPartPattern).join("|")})`;
23284
+ default: throw errorWithPath("Invalid schema representation document", []);
23285
+ }
23286
+ }
23287
+ /** @internal */
23288
+ function toJsonSchemaDocument$1(document, options) {
23289
+ const output = compileJsonSchema([document.representation], [["representation"]], document.references, options);
23290
+ return {
23291
+ dialect: output.dialect,
23292
+ schema: output.schemas[0],
23293
+ definitions: output.definitions
23294
+ };
23295
+ }
23296
+ //#endregion
23297
+ //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/internal/schema/toRepresentation.js
23298
+ const defaultReferencePolicy = ({ identifier }) => identifier;
23299
+ function annotationsField(annotations) {
23300
+ return annotations === void 0 ? void 0 : { annotations };
23301
+ }
23302
+ /** @internal */
23303
+ function toRepresentation(ast, options) {
23304
+ const { references, representations } = toRepresentations([ast], options);
23305
+ return {
23306
+ representation: representations[0],
23307
+ references
23308
+ };
23309
+ }
23310
+ /** @internal */
23311
+ function toRepresentations(asts, options) {
23312
+ const references = {};
23313
+ const referenceOwners = /* @__PURE__ */ new Map();
23314
+ const buildingReferences = /* @__PURE__ */ new Set();
23315
+ const candidates = /* @__PURE__ */ new Map();
23316
+ const visitingCandidates = /* @__PURE__ */ new Set();
23317
+ for (const ast of asts) visit(ast);
23318
+ const referencePolicy = options?.referencePolicy ?? defaultReferencePolicy;
23319
+ for (const candidatesByIdentifier of candidates.values()) for (const candidate of candidatesByIdentifier.values()) {
23320
+ const requestedReference = referencePolicy({
23321
+ ast: candidate.ast,
23322
+ occurrences: candidate.occurrences,
23323
+ identifier: candidate.identifier
23324
+ });
23325
+ if (requestedReference !== void 0) candidate.reference = getReference(requestedReference, candidate, requestedReference === candidate.identifier || !requestedReference.endsWith("_") ? "_" : "");
23326
+ else if (candidate.isRecursive) candidate.reference = getReference(`${candidate.ast._tag}_`, candidate, "");
23327
+ }
23328
+ return {
23329
+ representations: map$8(asts, (ast) => recur(ast)),
23330
+ references
23331
+ };
23332
+ function getReference(prefix, owner, separator = "_") {
23333
+ let candidate = prefix;
23334
+ let suffix = 0;
23335
+ while (referenceOwners.has(candidate)) {
23336
+ if (referenceOwners.get(candidate) === owner) return candidate;
23337
+ candidate = `${prefix}${separator}${++suffix}`;
23338
+ }
23339
+ referenceOwners.set(candidate, owner);
23340
+ return candidate;
23341
+ }
23342
+ function annotateReference(ast, candidate, reference) {
23343
+ const fallback = candidate.fallback;
23344
+ if (fallback !== void 0) return resolveIdentifierFallback(ast) === fallback ? ast : annotate$1(ast, { [IDENTIFIER_FALLBACK_KEY]: fallback });
23345
+ return reference === candidate.identifier ? ast : annotate$1(ast, { identifier: reference });
23346
+ }
23347
+ function makeReference(reference, ast) {
23348
+ if (!Object.hasOwn(references, reference) && !buildingReferences.has(reference)) {
23349
+ buildingReferences.add(reference);
23350
+ const representation = on(ast);
23351
+ buildingReferences.delete(reference);
23352
+ assignProperty(references, reference, representation);
23353
+ }
23354
+ return {
23355
+ _tag: "Reference",
23356
+ $ref: reference
23357
+ };
23358
+ }
23359
+ function getCandidate(input) {
23360
+ const ast = getLastEncoding(input);
23361
+ const owner = getContextOwner(ast);
23362
+ let identifier = resolveIdentifier(ast);
23363
+ const fallback = identifier === void 0 ? (ast !== input ? resolveIdentifier(input) : void 0) ?? resolveIdentifierFallback(ast) : void 0;
23364
+ if (fallback !== void 0) identifier = `${fallback}Encoded`;
23365
+ let candidatesByIdentifier = candidates.get(owner);
23366
+ if (candidatesByIdentifier === void 0) {
23367
+ candidatesByIdentifier = /* @__PURE__ */ new Map();
23368
+ candidates.set(owner, candidatesByIdentifier);
23369
+ }
23370
+ let candidate = candidatesByIdentifier.get(identifier);
23371
+ if (candidate === void 0) {
23372
+ candidate = {
23373
+ ast: owner,
23374
+ identifier,
23375
+ fallback,
23376
+ occurrences: 0,
23377
+ isRecursive: false,
23378
+ reference: void 0
23379
+ };
23380
+ candidatesByIdentifier.set(identifier, candidate);
23381
+ }
23382
+ return candidate;
23383
+ }
23384
+ function visit(input) {
23385
+ const candidate = getCandidate(input);
23386
+ const ast = candidate.ast;
23387
+ candidate.occurrences++;
23388
+ if (visitingCandidates.has(candidate)) {
23389
+ candidate.isRecursive = true;
23390
+ return;
23391
+ }
23392
+ if (candidate.occurrences > 1) return;
23393
+ visitingCandidates.add(candidate);
23394
+ visitChecks(ast.checks);
23395
+ switch (ast._tag) {
23396
+ case "Declaration":
23397
+ case "Arrays":
23398
+ case "Objects":
23399
+ case "Union":
23400
+ ast.recur((child) => {
23401
+ visit(child);
23402
+ return child;
23403
+ });
23404
+ break;
23405
+ case "TemplateLiteral":
23406
+ ast.parts.forEach(visit);
23407
+ break;
23408
+ case "Suspend": visit(ast.thunk());
23409
+ }
23410
+ visitingCandidates.delete(candidate);
23411
+ }
23412
+ function visitChecks(checks) {
23413
+ checks?.forEach((check) => {
23414
+ check.annotations?.representation?.schemas?.forEach((schema) => visit(toType(schema)));
23415
+ if (check._tag === "FilterGroup") visitChecks(check.checks);
23416
+ });
23417
+ }
23418
+ function recur(input) {
23419
+ const candidate = getCandidate(input);
23420
+ const ast = candidate.ast;
23421
+ const reference = candidate.reference;
23422
+ if (reference !== void 0) return makeReference(reference, candidate.identifier === void 0 ? ast : annotateReference(ast, candidate, reference));
23423
+ return on(ast);
23424
+ }
23425
+ function on(ast) {
23426
+ const checks = fromChecks(ast.checks);
23427
+ switch (ast._tag) {
23428
+ case "Declaration": return {
23429
+ _tag: "Declaration",
23430
+ typeParameters: ast.typeParameters.map((ast) => recur(ast)),
23431
+ checks,
23432
+ ...fromDeclarationAnnotations(ast.annotations)
23433
+ };
23434
+ case "Null":
23435
+ case "Undefined":
23436
+ case "Void":
23437
+ case "Never":
23438
+ case "Unknown":
23439
+ case "Any":
23440
+ case "String":
23441
+ case "Boolean":
23442
+ case "Number":
23443
+ case "BigInt":
23444
+ case "Symbol":
23445
+ case "ObjectKeyword": return {
23446
+ _tag: ast._tag,
23447
+ checks,
23448
+ ...annotationsField(ast.annotations)
23449
+ };
23450
+ case "Literal": return {
23451
+ _tag: "Literal",
23452
+ literal: ast.literal,
23453
+ checks,
23454
+ ...annotationsField(ast.annotations)
23455
+ };
23456
+ case "UniqueSymbol": return {
23457
+ _tag: "UniqueSymbol",
23458
+ symbol: ast.symbol,
23459
+ checks,
23460
+ ...annotationsField(ast.annotations)
23461
+ };
23462
+ case "Enum": return {
23463
+ _tag: "Enum",
23464
+ enums: ast.enums,
23465
+ checks,
23466
+ ...annotationsField(ast.annotations)
23467
+ };
23468
+ case "TemplateLiteral": return {
23469
+ _tag: "TemplateLiteral",
23470
+ parts: ast.parts.map((ast) => recur(ast)),
23471
+ checks,
23472
+ ...annotationsField(ast.annotations)
23473
+ };
23474
+ case "Arrays": return {
23475
+ _tag: "Arrays",
23476
+ elements: ast.elements.map((element) => {
23477
+ const projected = getLastEncoding(element);
23478
+ const annotations = projected.context?.annotations;
23479
+ return {
23480
+ isOptional: isOptional(projected),
23481
+ type: recur(element),
23482
+ ...annotationsField(annotations)
23483
+ };
23484
+ }),
23485
+ rest: ast.rest.map((ast) => recur(ast)),
23486
+ checks,
23487
+ ...annotationsField(ast.annotations)
23488
+ };
23489
+ case "Objects": return {
23490
+ _tag: "Objects",
23491
+ propertySignatures: ast.propertySignatures.map((property) => {
23492
+ const projected = getLastEncoding(property.type);
23493
+ const annotations = projected.context?.annotations;
23494
+ return {
23495
+ name: property.name,
23496
+ type: recur(property.type),
23497
+ isOptional: isOptional(projected),
23498
+ isMutable: isMutable(projected),
23499
+ ...annotationsField(annotations)
23500
+ };
23501
+ }),
23502
+ indexSignatures: ast.indexSignatures.map((index) => ({
23503
+ parameter: recur(index.parameter),
23504
+ type: recur(index.type)
23505
+ })),
23506
+ checks,
23507
+ ...annotationsField(ast.annotations)
23508
+ };
23509
+ case "Union": return {
23510
+ _tag: "Union",
23511
+ types: ast.types.map((ast) => recur(ast)),
23512
+ ...ast.options === void 0 ? {} : { options: ast.options },
23513
+ checks,
23514
+ ...annotationsField(ast.annotations)
23515
+ };
23516
+ case "Suspend": return {
23517
+ _tag: "Suspend",
23518
+ checks: [],
23519
+ thunk: recur(ast.thunk()),
23520
+ ...annotationsField(ast.annotations)
23521
+ };
23522
+ }
23523
+ }
23524
+ function fromChecks(checks) {
23525
+ return checks?.map(fromCheck) ?? [];
23526
+ }
23527
+ function fromCheck(check) {
23528
+ switch (check._tag) {
23529
+ case "Filter": return {
23530
+ _tag: "Filter",
23531
+ aborted: check.aborted,
23532
+ ...fromCheckAnnotations(check.annotations)
23533
+ };
23534
+ case "FilterGroup": return {
23535
+ _tag: "FilterGroup",
23536
+ checks: map$8(check.checks, fromCheck),
23537
+ ...fromCheckAnnotations(check.annotations)
23538
+ };
23539
+ }
23540
+ }
23541
+ function fromDeclarationAnnotations(annotations) {
23542
+ if (annotations === void 0) return void 0;
23543
+ const { representation, ...ordinary } = annotations;
23544
+ return {
23545
+ ...representation === void 0 ? void 0 : { representation },
23546
+ ...Object.keys(ordinary).length === 0 ? void 0 : { annotations: ordinary }
23547
+ };
23548
+ }
23549
+ function fromCheckAnnotations(annotations) {
23550
+ if (annotations === void 0) return void 0;
23551
+ const { representation, ...ordinary } = annotations;
23552
+ const projected = representation === void 0 ? void 0 : representation.schemas === void 0 ? representation : {
23553
+ ...representation,
23554
+ schemas: representation.schemas.map((schema) => recur(toType(schema)))
23555
+ };
23556
+ return {
23557
+ ...projected === void 0 ? void 0 : { representation: projected },
23558
+ ...Object.keys(ordinary).length === 0 ? void 0 : { annotations: ordinary }
23559
+ };
23560
+ }
23561
+ }
22308
23562
  //#endregion
22309
23563
  //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/Struct.js
22310
23564
  /**
@@ -23599,6 +24853,81 @@ const TaggedError = (identifier) => {
23599
24853
  };
23600
24854
  };
23601
24855
  /**
24856
+ * Returns a JSON Schema document using draft 2020-12.
24857
+ *
24858
+ * **When to use**
24859
+ *
24860
+ * Use when you need a draft-2020-12 description of the canonical JSON form of a runtime schema.
24861
+ *
24862
+ * **Details**
24863
+ *
24864
+ * The document describes the encoded side of `Schema.toCodecJson(schema)`.
24865
+ * Use that codec to decode JSON inputs. For example, it decodes JSON `null`
24866
+ * to JavaScript `undefined` for a field defined with `Schema.optional(Schema.String)`.
24867
+ * Decoding the same input with the original schema rejects `null`.
24868
+ *
24869
+ * The `options` parameter controls reference extraction and generation details
24870
+ * such as excess properties and synthesized check descriptions; it does not
24871
+ * change the draft target. The reference policy receives canonical JSON
24872
+ * encoded ASTs. By default, anonymous non-recursive candidates remain inline,
24873
+ * while candidates with resolved identifiers become definitions. Declarations
24874
+ * are lowered through their `toCodecJson` or `toCodec` annotation when
24875
+ * available before the representation document is compiled.
24876
+ *
24877
+ * The generated document is intended for preliminary validation. JSON Schema
24878
+ * and Effect checks do not always have identical semantics, so the Effect
24879
+ * decoder remains the final authority. Passing JSON Schema validation does not
24880
+ * guarantee that Effect decoding will succeed.
24881
+ *
24882
+ * **Gotchas**
24883
+ *
24884
+ * JSON Schema generation is best-effort. String length uses Unicode code points
24885
+ * in JSON Schema and UTF-16 code units in Effect. A generated `pattern` cannot
24886
+ * retain JavaScript RegExp flags. Object property checks apply to the original
24887
+ * input in JSON Schema but to the decoded object in Effect. When a `oneOf`
24888
+ * branch contains a known approximation, the compiler emits `anyOf` so that
24889
+ * the approximation cannot create a false rejection. Unions with only exact
24890
+ * branches retain `oneOf`. Custom `toJsonSchema` callbacks return `[schema, true]` for safe,
24891
+ * looser approximations and are responsible for the semantics they declare. When
24892
+ * canonical JSON derivation adds an artificial transformation, checks and
24893
+ * annotations on its source node are not copied to the JSON target, so they do
24894
+ * not appear in the emitted document. Opaque declarations without a structural
24895
+ * codec are represented by an unconstrained JSON Schema. The default
24896
+ * `onExcessProperty: "ignore"` matches the decoder default and leaves
24897
+ * index-signature keys without exact selectors open. In `"error"` mode, the compiler
24898
+ * constrains those keys with `propertyNames` and applies a permissive choice of
24899
+ * candidate index value schemas. The Effect decoder enforces the exact
24900
+ * key-value association.
24901
+ *
24902
+ * **Example** (Decoding JSON with the matching codec)
24903
+ *
24904
+ * ```ts import.meta.vitest
24905
+ * import { Schema } from "effect"
24906
+ *
24907
+ * const schema = Schema.Struct({
24908
+ * name: Schema.optional(Schema.String)
24909
+ * })
24910
+ *
24911
+ * const document = Schema.toJsonSchemaDocument(schema)
24912
+ * const jsonCodec = Schema.toCodecJson(schema)
24913
+ *
24914
+ * Schema.decodeUnknownResult(schema)({ name: null })._tag // => "Failure"
24915
+ * Schema.decodeUnknownSync(jsonCodec)({ name: null }) // => { name: undefined }
24916
+ * Schema.decodeUnknownSync(jsonCodec)({}) // => {}
24917
+ * Schema.encodeSync(jsonCodec)({ name: undefined }) // => { name: null }
24918
+ * ```
24919
+ *
24920
+ * @see {@link toCodecJson} for decoding and encoding the canonical JSON representation
24921
+ * @see {@link SchemaRepresentation.toJsonSchemaDocument} for compiling an existing live representation document
24922
+ *
24923
+ * @stability stable
24924
+ * @category converting
24925
+ * @since 4.0.0
24926
+ */
24927
+ function toJsonSchemaDocument(schema, options) {
24928
+ return toJsonSchemaDocument$1(toRepresentation(toCodecJsonAST(schema.ast), options), options);
24929
+ }
24930
+ /**
23602
24931
  * Converts a schema to the StringTree canonical codec, where every leaf value
23603
24932
  * becomes a string while preserving the original structure.
23604
24933
  *
@@ -30390,7 +31719,7 @@ const runWith = (command, config) => {
30390
31719
  };
30391
31720
  //#endregion
30392
31721
  //#region package.json
30393
- var version = "0.1.0";
31722
+ var version = "0.2.1";
30394
31723
  //#endregion
30395
31724
  //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/Ref.js
30396
31725
  /**
@@ -33269,14 +34598,35 @@ const VpSection = Struct({
33269
34598
  const MachineSettings = Struct({ clones: optionalKey(NonEmptyString) });
33270
34599
  const FleetConfig = Struct({
33271
34600
  $schema: optionalKey(String$5),
33272
- machines: optionalKey(Record(NonEmptyString, MachineSettings)),
34601
+ shippedAt: optionalKey(String$5),
34602
+ shippedFrom: optionalKey(NonEmptyString),
34603
+ shippedTo: optionalKey(Record(NonEmptyString, String$5)),
34604
+ machines: Record(NonEmptyString, MachineSettings).check(makeFilter((machines) => Object.keys(machines).length > 0, {
34605
+ title: "Fleet",
34606
+ description: "at least one Machine"
34607
+ })),
33273
34608
  repos: optionalKey(ArraySchema(TrackedRepo)),
33274
34609
  vp: optionalKey(VpSection)
33275
34610
  });
33276
34611
  const repoName = (repo) => repo.name ?? repo.origin.replace("/", "-");
34612
+ const SELF_PACKAGE = "fleetfizz";
34613
+ //#endregion
34614
+ //#region ../../packages/config/src/jsonSchema.ts
34615
+ const fleetConfigJsonSchema = () => {
34616
+ const document = toJsonSchemaDocument(FleetConfig, { onExcessProperty: "error" });
34617
+ return {
34618
+ $schema: META_SCHEMA_URI_DRAFT_2020_12,
34619
+ title: "fleetfizz config",
34620
+ ...document.schema,
34621
+ $defs: document.definitions
34622
+ };
34623
+ };
33277
34624
  //#endregion
33278
34625
  //#region ../../packages/config/src/load.ts
33279
34626
  const CONFIG_FILE = "fleet.config.jsonc";
34627
+ const SCHEMA_FILE = "fleet.schema.json";
34628
+ const configDir = (env) => `${env.XDG_CONFIG_HOME || `${env.HOME ?? "~"}/.config`}/fleetfizz`;
34629
+ const configPath = (env) => `${configDir(env)}/${CONFIG_FILE}`;
33280
34630
  var ConfigError = class extends TaggedError()("ConfigError", {
33281
34631
  path: String$5,
33282
34632
  problems: ArraySchema(String$5)
@@ -33285,23 +34635,11 @@ var ConfigError = class extends TaggedError()("ConfigError", {
33285
34635
  return [`${this.path}:`, ...this.problems.map((p) => ` ${p}`)].join("\n");
33286
34636
  }
33287
34637
  };
33288
- var ConfigNotFound = class extends TaggedError()("ConfigNotFound", { start: String$5 }) {
34638
+ var ConfigNotFound = class extends TaggedError()("ConfigNotFound", { path: String$5 }) {
33289
34639
  get message() {
33290
- return `no ${CONFIG_FILE} found in ${this.start} or any parent directory`;
34640
+ return `no config at ${this.path}; write one there (the README has an example) or pass --config`;
33291
34641
  }
33292
34642
  };
33293
- const findConfig = fn("findConfig")(function* (start) {
33294
- const fs = yield* FileSystem;
33295
- const path = yield* Path$1;
33296
- let dir = path.resolve(start);
33297
- for (;;) {
33298
- const candidate = path.join(dir, CONFIG_FILE);
33299
- if (yield* fs.exists(candidate)) return candidate;
33300
- const parent = path.dirname(dir);
33301
- if (parent === dir) return yield* new ConfigNotFound({ start });
33302
- dir = parent;
33303
- }
33304
- });
33305
34643
  const lineOf = (text, offset) => text.slice(0, offset).split("\n").length;
33306
34644
  const semanticProblems = (config) => {
33307
34645
  const problems = [];
@@ -33311,6 +34649,7 @@ const semanticProblems = (config) => {
33311
34649
  if (seen.has(name)) problems.push(`repos: duplicate name "${name}"`);
33312
34650
  seen.add(name);
33313
34651
  }
34652
+ if (config.vp?.globals?.["fleetfizz"] !== void 0) problems.push(`vp.globals: ${SELF_PACKAGE} is pinned by the running version; remove it`);
33314
34653
  return problems;
33315
34654
  };
33316
34655
  const parseConfig = fn("parseConfig")(function* (path, text) {
@@ -33333,10 +34672,48 @@ const parseConfig = fn("parseConfig")(function* (path, text) {
33333
34672
  });
33334
34673
  const formatIssue = makeFormatterDefault();
33335
34674
  const loadConfig = fn("loadConfig")(function* (path) {
33336
- const text = yield* (yield* FileSystem).readFileString(path);
34675
+ const fs = yield* FileSystem;
34676
+ if (!(yield* fs.exists(path))) return yield* new ConfigNotFound({ path });
34677
+ const text = yield* fs.readFileString(path);
33337
34678
  return yield* parseConfig(path, text);
33338
34679
  });
33339
34680
  //#endregion
34681
+ //#region ../../packages/config/src/stamp.ts
34682
+ const insertAfter = (before) => (properties) => {
34683
+ return before.map((key) => properties.indexOf(key)).reduce((a, b) => Math.max(a, b), -1) + 1;
34684
+ };
34685
+ const STAMP_KEYS = [
34686
+ "$schema",
34687
+ "shippedAt",
34688
+ "shippedFrom",
34689
+ "shippedTo"
34690
+ ];
34691
+ const stampText = (text, stamp) => {
34692
+ const options = { formattingOptions: {
34693
+ insertSpaces: true,
34694
+ tabSize: 2
34695
+ } };
34696
+ const set = (current, key, value) => applyEdits(current, modify(current, [key], value, {
34697
+ ...options,
34698
+ getInsertionIndex: insertAfter(STAMP_KEYS.slice(0, STAMP_KEYS.indexOf(key)))
34699
+ }));
34700
+ const previous = parse(text, void 0, { allowTrailingComma: true });
34701
+ const shippedTo = {
34702
+ ...shippedToOf(previous),
34703
+ ...Object.fromEntries(stamp.reached.map((name) => [name, stamp.shippedAt]))
34704
+ };
34705
+ let out = set(text, "shippedAt", stamp.shippedAt);
34706
+ out = set(out, "shippedFrom", stamp.shippedFrom);
34707
+ return set(out, "shippedTo", shippedTo);
34708
+ };
34709
+ const shippedToOf = (config) => {
34710
+ if (typeof config !== "object" || config === null || !("shippedTo" in config)) return {};
34711
+ const value = config.shippedTo;
34712
+ if (typeof value !== "object" || value === null) return {};
34713
+ return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
34714
+ };
34715
+ const isNewer = (remote, local) => remote !== void 0 && (local === void 0 || remote > local);
34716
+ //#endregion
33340
34717
  //#region ../../packages/core/src/range.ts
33341
34718
  const newest$1 = (available) => available.filter((v) => import_semver.prerelease(v) === null).sort(import_semver.rcompare)[0];
33342
34719
  const decideRange = (range, installed, available) => {
@@ -33417,17 +34794,12 @@ const planVp = (input) => {
33417
34794
  skipped: [],
33418
34795
  failures: []
33419
34796
  };
33420
- const vp = input.config.vp;
33421
- if (vp === void 0 || !targets(vp, input.machine)) return out;
33422
34797
  const { state, machine } = input;
33423
- if (state.vp === void 0 || state.vp.version === null) {
33424
- out.failures.push({
33425
- what: "vp",
33426
- reason: "vp is not installed (not on PATH in a login shell)"
33427
- });
33428
- return out;
33429
- }
33430
- const installed = state.vp.version;
34798
+ const vp = input.config.vp;
34799
+ const targeted = vp !== void 0 && targets(vp, machine);
34800
+ const missing = state.vp === void 0 || state.vp.version === null;
34801
+ if (missing) out.steps.push({ kind: "bootstrap" });
34802
+ const installed = missing ? void 0 : state.vp?.version ?? void 0;
33431
34803
  const decide = (pkg, range, current, shown = pkg) => {
33432
34804
  if (range === void 0) return void 0;
33433
34805
  const decision = decideRange(range, current, input.versions[pkg] ?? []);
@@ -33468,31 +34840,35 @@ const planVp = (input) => {
33468
34840
  }
33469
34841
  }
33470
34842
  };
33471
- const vpTarget = decide(VP_PACKAGE, vp.version, installed, "vp");
33472
- if (vpTarget !== void 0) {
33473
- if (isHomebrew(state.vp.path)) out.failures.push({
33474
- what: "vp",
33475
- reason: `vp ${installed} was installed with Homebrew; run brew upgrade vite-plus to get ${vpTarget}`
33476
- });
33477
- else out.steps.push({
33478
- kind: "vp",
33479
- from: installed,
33480
- version: vpTarget
34843
+ const nodeNow = missing ? void 0 : state.node ?? void 0;
34844
+ let nodeTarget;
34845
+ if (targeted) {
34846
+ const vpTarget = decide(VP_PACKAGE, vp.version, installed, "vp");
34847
+ if (vpTarget !== void 0) {
34848
+ if (isHomebrew(state.vp?.path)) out.failures.push({
34849
+ what: "vp",
34850
+ reason: `vp ${installed} was installed with Homebrew; run brew upgrade vite-plus to get ${vpTarget}`
34851
+ });
34852
+ else out.steps.push({
34853
+ kind: "vp",
34854
+ from: installed ?? null,
34855
+ version: vpTarget
34856
+ });
34857
+ }
34858
+ nodeTarget = decide(NODE_PACKAGE, vp.node, nodeNow, "node");
34859
+ if (nodeTarget !== void 0) out.steps.push({
34860
+ kind: "node",
34861
+ from: nodeNow ?? null,
34862
+ version: nodeTarget
33481
34863
  });
33482
34864
  }
33483
- const nodeNow = state.node ?? void 0;
33484
- const nodeTarget = decide(NODE_PACKAGE, vp.node, nodeNow, "node");
33485
- if (nodeTarget !== void 0) out.steps.push({
33486
- kind: "node",
33487
- from: nodeNow ?? null,
33488
- version: nodeTarget
33489
- });
33490
34865
  const nodeDefault = nodeTarget ?? nodeNow;
33491
- const byName = new Map(state.globals.map((g) => [g.name, g]));
34866
+ const byName = new Map((missing ? [] : state.globals).map((g) => [g.name, g]));
33492
34867
  const installs = [];
33493
- for (const [name, range] of Object.entries(vp.globals ?? {})) {
34868
+ const wanted = Object.entries(targeted ? vp.globals ?? {} : {});
34869
+ for (const [name, range] of [...wanted, [SELF_PACKAGE, input.self]]) {
33494
34870
  const current = byName.get(name);
33495
- const version = decide(name, range, current?.version);
34871
+ const version = name === "fleetfizz" ? decideSelf(current?.version, input.self, out) : decide(name, range, current?.version);
33496
34872
  if (version !== void 0) installs.push(current === void 0 ? {
33497
34873
  name,
33498
34874
  version
@@ -33513,6 +34889,13 @@ const planVp = (input) => {
33513
34889
  });
33514
34890
  return out;
33515
34891
  };
34892
+ const decideSelf = (installed, self, out) => {
34893
+ if (installed === void 0 || import_semver.lt(installed, self)) return self;
34894
+ if (import_semver.gt(installed, self)) out.failures.push({
34895
+ what: SELF_PACKAGE,
34896
+ reason: `runs ${SELF_PACKAGE} ${installed}, newer than this ${self}; upgrade here first`
34897
+ });
34898
+ };
33516
34899
  const planRepos = (input) => {
33517
34900
  const repos = [];
33518
34901
  const failures = [];
@@ -33631,6 +35014,11 @@ const Fact = Union([
33631
35014
  name: String$5,
33632
35015
  record: InstallRecordSchema
33633
35016
  }),
35017
+ Struct({
35018
+ fact: Literal("config"),
35019
+ present: Boolean$4,
35020
+ base64: optionalKey(String$5)
35021
+ }),
33634
35022
  Struct({
33635
35023
  fact: Literal("output"),
33636
35024
  repo: String$5,
@@ -33645,6 +35033,7 @@ const emptyState = {
33645
35033
  records: {},
33646
35034
  outputs: {}
33647
35035
  };
35036
+ const decodeBase64 = (base64) => new TextDecoder().decode(Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)));
33648
35037
  const assembleState = (facts) => {
33649
35038
  let state = emptyState;
33650
35039
  for (const fact of facts) switch (fact.fact) {
@@ -33714,6 +35103,12 @@ const assembleState = (facts) => {
33714
35103
  }
33715
35104
  };
33716
35105
  break;
35106
+ case "config":
35107
+ if (fact.present && fact.base64 !== void 0) state = {
35108
+ ...state,
35109
+ config: decodeBase64(fact.base64)
35110
+ };
35111
+ break;
33717
35112
  case "output": state = {
33718
35113
  ...state,
33719
35114
  outputs: {
@@ -33745,6 +35140,7 @@ const fingerprintFn = [
33745
35140
  "}"
33746
35141
  ].join("\n");
33747
35142
  const RECORDS_DIR = `~/.local/state/fleetfizz/records`;
35143
+ const CONFIG_DIR_SH = "\"${XDG_CONFIG_HOME:-$HOME/.config}/fleetfizz\"";
33748
35144
  const gatherScript = (input) => {
33749
35145
  const lines = [
33750
35146
  prelude,
@@ -33773,6 +35169,7 @@ const gatherScript = (input) => {
33773
35169
  const name = quote(repo.name);
33774
35170
  lines.push(`ff_r=${quotePath(RECORDS_DIR)}/${quote(`${repo.name}.json`)}`, `if [ -f "$ff_r" ]; then ff_state "{\\"fact\\":\\"record\\",\\"name\\":\\"$(ff_json ${name})\\",\\"record\\":$(tr -d '\\n' < "$ff_r")}"; fi`);
33775
35171
  }
35172
+ lines.push(`ff_c=${CONFIG_DIR_SH}/${quote(CONFIG_FILE)}`, `if [ -f "$ff_c" ]; then ff_state "{\\"fact\\":\\"config\\",\\"present\\":true,\\"base64\\":\\"$(base64 < "$ff_c" | tr -d '\\n')\\"}"; else ff_state '{"fact":"config","present":false}'; fi`);
33776
35173
  lines.push(`ff_state '{"fact":"end"}'`);
33777
35174
  return lines.join("\n");
33778
35175
  };
@@ -33823,10 +35220,19 @@ const jsonString = encodeSync(fromJsonString(String$5));
33823
35220
  const jsonRecord = encodeSync(fromJsonString(Record(String$5, String$5)));
33824
35221
  const stepId = (repo, step) => `repo:${repo}:${step}`;
33825
35222
  const vpStepId = (step) => step.kind === "reinstall" ? `vp:reinstall:${step.name}` : step.kind === "globals" ? "vp:globals" : `vp:${step.kind}`;
35223
+ const CONFIG_STEP = "config";
35224
+ const VP_INSTALL = [
35225
+ "curl -fsSL https://vite.plus | bash",
35226
+ `ff_env="\${XDG_CONFIG_HOME:-$HOME/.config}/vite-plus/env"`,
35227
+ `{ [ ! -f "$ff_env" ] || . "$ff_env"; }`,
35228
+ `export PATH="\${VP_HOME:-$HOME/.vite-plus}/bin:\${XDG_DATA_HOME:-$HOME/.local/share}/vite-plus/bin:$PATH"`,
35229
+ "{ hash -r 2>/dev/null || true; }"
35230
+ ].join(" && ");
33826
35231
  const vpStep = (step) => {
33827
35232
  const id = vpStepId(step);
33828
35233
  const command = (() => {
33829
35234
  switch (step.kind) {
35235
+ case "bootstrap": return VP_INSTALL;
33830
35236
  case "vp": return `command vp upgrade ${quote(step.version)}`;
33831
35237
  case "node": return `command vp env default ${quote(step.version)}`;
33832
35238
  case "reinstall": return `command vp install -g ${quote(`${step.name}@${step.version}`)}`;
@@ -33835,11 +35241,21 @@ const vpStep = (step) => {
33835
35241
  })();
33836
35242
  return [
33837
35243
  stepLine(id),
33838
- `( ${command} )`,
35244
+ step.kind === "bootstrap" ? command : `( ${command} )`,
33839
35245
  `ff_code=$?`,
33840
35246
  `if [ "$ff_code" -ne 0 ]; then ${doneLine(id, false, "$ff_code")}; else ${doneLine(id, true)}; fi`
33841
35247
  ];
33842
35248
  };
35249
+ const configStep = () => {
35250
+ const dir = CONFIG_DIR_SH;
35251
+ const write = (variable, file) => `printf '%s' "$${variable}" | base64 -d > ${dir}/${quote(`${file}.tmp`)} && mv -f ${dir}/${quote(`${file}.tmp`)} ${dir}/${quote(file)}`;
35252
+ return [
35253
+ stepLine(CONFIG_STEP),
35254
+ `( mkdir -p ${dir} && ${write("ff_config", CONFIG_FILE)} && ${write("ff_schema", SCHEMA_FILE)} )`,
35255
+ `ff_code=$?`,
35256
+ `if [ "$ff_code" -ne 0 ]; then ${doneLine(CONFIG_STEP, false, "$ff_code")}; else ${doneLine(CONFIG_STEP, true)}; fi`
35257
+ ];
35258
+ };
33843
35259
  const writeRecord = (repo) => {
33844
35260
  return [
33845
35261
  `ff_outs=""`,
@@ -33892,16 +35308,30 @@ const repoSteps = (repo, input) => {
33892
35308
  "fi"
33893
35309
  ];
33894
35310
  };
35311
+ const applyStdin = (token, config, schema) => [
35312
+ token ?? "",
35313
+ encodeBase64(config),
35314
+ encodeBase64(schema)
35315
+ ];
35316
+ const encodeBase64 = (text) => {
35317
+ const bytes = new TextEncoder().encode(text);
35318
+ let binary = "";
35319
+ for (const byte of bytes) binary += String.fromCharCode(byte);
35320
+ return btoa(binary);
35321
+ };
33895
35322
  const applyScript = (input) => {
33896
35323
  const lines = [
33897
35324
  prelude,
33898
35325
  fingerprintFn,
33899
35326
  `ff_lit() { printf '%s' "$1"; }`,
33900
35327
  `IFS= read -r ff_token || ff_token=""`,
35328
+ `IFS= read -r ff_config || ff_config=""`,
35329
+ `IFS= read -r ff_schema || ff_schema=""`,
33901
35330
  `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; }`,
33902
35331
  `ff_root=${quotePath(input.cloneRoot)}`,
33903
35332
  ...input.cloneRoot.startsWith("~") ? [`mkdir -p "$ff_root"`] : []
33904
35333
  ];
35334
+ lines.push(...configStep());
33905
35335
  for (const step of input.plan.vp) lines.push(...vpStep(step));
33906
35336
  for (const repo of input.plan.repos) lines.push(...repoSteps(repo, input));
33907
35337
  lines.push(doneLine("end", true));
@@ -34009,7 +35439,8 @@ const command = (target, script, secrets) => {
34009
35439
  const options = {
34010
35440
  stdin: encodeText(make$18(secrets.map((s) => `${s}\n`).join(""))),
34011
35441
  stdout: "pipe",
34012
- stderr: "pipe"
35442
+ stderr: "pipe",
35443
+ forceKillAfter: "5 seconds"
34013
35444
  };
34014
35445
  return target.local ? make$13(process.env.SHELL ?? "/bin/sh", ["-lc", script], options) : make$13("tailscale", [
34015
35446
  "ssh",
@@ -34117,13 +35548,17 @@ const runOn = fn("runOn")(function* (target, script, secrets, onLine, ledger) {
34117
35548
  }
34118
35549
  return run;
34119
35550
  });
35551
+ const GATHER_TIMEOUT = "2 minutes";
34120
35552
  const gather = fn("gather")(function* (target, script, ledger) {
34121
35553
  const facts = [];
34122
35554
  const output = [];
34123
35555
  const run = yield* runOn(target, script, [], (line) => sync(() => {
34124
35556
  if (line.kind === "state") facts.push(line.fact);
34125
35557
  else if (line.kind === "text" && line.text.trim() !== "") output.push(line.text.trimEnd());
34126
- }), ledger);
35558
+ }), ledger).pipe(timeoutOrElse({
35559
+ duration: GATHER_TIMEOUT,
35560
+ orElse: () => ledger.fail(target.name, [`gather took more than ${GATHER_TIMEOUT}; something on that Machine is blocking`, ...output]).pipe(as({ kind: "failed" }))
35561
+ }));
34127
35562
  if (run.kind !== "ran") return { kind: run.kind };
34128
35563
  const state = assembleState(facts);
34129
35564
  if (!state.complete) {
@@ -34135,14 +35570,19 @@ const gather = fn("gather")(function* (target, script, ledger) {
34135
35570
  state
34136
35571
  };
34137
35572
  });
34138
- const runLabels = (p) => Object.fromEntries([...p.vp.map((step) => {
34139
- switch (step.kind) {
34140
- case "vp": return [vpStepId(step), `vp upgrade ${step.version}`];
34141
- case "node": return [vpStepId(step), `node default ${step.version}`];
34142
- case "reinstall": return [vpStepId(step), `${step.name} reinstall`];
34143
- case "globals": return [vpStepId(step), `install ${step.packages.map((g) => `${g.name}@${g.version}`).join(" ")}`];
34144
- }
34145
- }), ...p.repos.map((r) => [stepId(r.name, "install"), `${r.name} install`])]);
35573
+ const runLabels = (p) => Object.fromEntries([
35574
+ [CONFIG_STEP, "config"],
35575
+ ...p.vp.map((step) => {
35576
+ switch (step.kind) {
35577
+ case "bootstrap": return [vpStepId(step), "install vp"];
35578
+ case "vp": return [vpStepId(step), `vp upgrade ${step.version}`];
35579
+ case "node": return [vpStepId(step), `node default ${step.version}`];
35580
+ case "reinstall": return [vpStepId(step), `${step.name} reinstall`];
35581
+ case "globals": return [vpStepId(step), `install ${step.packages.map((g) => `${g.name}@${g.version}`).join(" ")}`];
35582
+ }
35583
+ }),
35584
+ ...p.repos.map((r) => [stepId(r.name, "install"), `${r.name} install`])
35585
+ ]);
34146
35586
  const apply = fn("apply")(function* (target, p, script, secrets, ledger) {
34147
35587
  const labels = runLabels(p);
34148
35588
  let current;
@@ -34174,16 +35614,20 @@ const apply = fn("apply")(function* (target, p, script, secrets, ledger) {
34174
35614
  case "text": if (line.text.trim() !== "") (current?.output ?? head).push(line.text.trimEnd());
34175
35615
  }
34176
35616
  }), ledger);
34177
- if (run.kind !== "ran") return run.kind;
35617
+ const applied = (outcome) => ({
35618
+ outcome,
35619
+ done
35620
+ });
35621
+ if (run.kind !== "ran") return applied(run.kind);
34178
35622
  if (current !== void 0) {
34179
35623
  yield* ledger.fail(target.name, [`${current.id} did not finish`, ...current.output]);
34180
- return "failed";
35624
+ return applied("failed");
34181
35625
  }
34182
35626
  if (!done.includes("end")) {
34183
35627
  yield* ledger.fail(target.name, ["apply did not finish", ...head]);
34184
- return "failed";
35628
+ return applied("failed");
34185
35629
  }
34186
- if (failed.length > 0 || p.failures.length > 0) return "failed";
35630
+ if (failed.length > 0 || p.failures.length > 0) return applied("failed");
34187
35631
  const installed = p.repos.filter((r) => done.includes(stepId(r.name, "install")));
34188
35632
  const upToDate = p.repos.length - installed.length;
34189
35633
  const parts = [
@@ -34192,7 +35636,7 @@ const apply = fn("apply")(function* (target, p, script, secrets, ledger) {
34192
35636
  ...upToDate > 0 ? [`${upToDate} repo${upToDate === 1 ? "" : "s"} up to date`] : []
34193
35637
  ];
34194
35638
  yield* ledger.line(target.name, "OK", parts.length === 0 ? "up to date" : parts.join(" · "));
34195
- return "ok";
35639
+ return applied("ok");
34196
35640
  });
34197
35641
  //#endregion
34198
35642
  //#region src/questions.ts
@@ -34242,6 +35686,7 @@ const askQuestions = fn("askQuestions")(function* (questions, flags, ledger) {
34242
35686
  const SEP = " · ";
34243
35687
  const vpPart = (step) => {
34244
35688
  switch (step.kind) {
35689
+ case "bootstrap": return "install vp";
34245
35690
  case "vp": return `vp ${step.from ?? "none"} -> ${step.version}`;
34246
35691
  case "node": return `node ${step.from ?? "none"} -> ${step.version}`;
34247
35692
  case "reinstall": return `${step.name} reinstall on Node`;
@@ -34252,10 +35697,14 @@ const repoPart = (repo) => {
34252
35697
  if (repo.decision.run) return `${repo.name} install (${repo.decision.reason})`;
34253
35698
  };
34254
35699
  const summarize = (plan) => {
34255
- const parts = [...plan.vp.map(vpPart), ...plan.repos.map(repoPart).filter((p) => p !== void 0)];
35700
+ const parts = [
35701
+ "config",
35702
+ ...plan.vp.map(vpPart),
35703
+ ...plan.repos.map(repoPart).filter((p) => p !== void 0)
35704
+ ];
34256
35705
  const upToDate = plan.repos.filter((r) => !r.decision.run).length;
34257
35706
  if (upToDate > 0) parts.push(`${upToDate} repo${upToDate === 1 ? "" : "s"} up to date`);
34258
- return parts.length === 0 ? "up to date" : parts.join(SEP);
35707
+ return parts.join(SEP);
34259
35708
  };
34260
35709
  //#endregion
34261
35710
  //#region src/ship.ts
@@ -34265,7 +35714,7 @@ var ShipError = class extends Error {
34265
35714
  const unknownMachines = (config, tailnet, only) => {
34266
35715
  const known = new Set(tailnet.fleet.map((m) => m.name));
34267
35716
  const referenced = [
34268
- ...Object.keys(config.machines ?? {}),
35717
+ ...Object.keys(config.machines),
34269
35718
  ...(config.repos ?? []).flatMap((r) => r.machines ?? []),
34270
35719
  ...config.vp?.machines ?? [],
34271
35720
  ...only
@@ -34282,14 +35731,33 @@ const packagesToResolve = (config) => {
34282
35731
  ];
34283
35732
  };
34284
35733
  const rebuildOf = (flags) => flags.rebuild.length === 0 ? "none" : flags.rebuild.includes("all") ? "all" : flags.rebuild;
35734
+ const staleConfigs = fn("staleConfigs")(function* (local, inputIsLocalFile, gathered, ledger) {
35735
+ const stale = [];
35736
+ for (const { machine, local: isLocal, state } of gathered) {
35737
+ if (isLocal && inputIsLocalFile || state.config === void 0) continue;
35738
+ const parsed = yield* result(parseConfig(machine.name, state.config));
35739
+ if (parsed._tag === "Failure") {
35740
+ yield* ledger.line(machine.name, "INFO", "its config does not parse; it will be overwritten");
35741
+ continue;
35742
+ }
35743
+ const remote = parsed.success;
35744
+ if (isNewer(remote.shippedTo?.[machine.name], local.shippedTo?.[machine.name])) stale.push(`${machine.name} (shipped ${remote.shippedAt} from ${remote.shippedFrom ?? "unknown"})`);
35745
+ }
35746
+ return stale;
35747
+ });
35748
+ const schemaJson = encodeSync(fromJsonString(Unknown, { space: 2 }));
34285
35749
  const ship = fn("ship")(function* (flags) {
34286
- const path = yield* Path$1;
34287
- const configPath = flags.config ?? (yield* findConfig(path.resolve(".")));
34288
- const config = yield* loadConfig(configPath);
35750
+ const fs = yield* FileSystem;
35751
+ const path = flags.config ?? configPath(process.env);
35752
+ const config = yield* loadConfig(path);
34289
35753
  const tailnet = yield* readTailnet();
34290
35754
  const unknown = unknownMachines(config, tailnet, flags.only);
34291
35755
  if (unknown.length > 0) return yield* fail$1(new ShipError(`unknown Machine: ${unknown.join(", ")} (the tailnet has: ${tailnet.fleet.map((m) => m.name).join(", ")})`));
34292
- const targetsList = tailnet.fleet.filter((m) => flags.only.length === 0 || flags.only.includes(m.name));
35756
+ const fleet = new Set(Object.keys(config.machines));
35757
+ if (!fleet.has(tailnet.self.name)) return yield* fail$1(new ShipError(`this Machine (${tailnet.self.name}) is not in machines in ${path}`));
35758
+ const outsideFleet = flags.only.filter((name) => !fleet.has(name));
35759
+ if (outsideFleet.length > 0) return yield* fail$1(new ShipError(`not in machines in ${path}: ${outsideFleet.join(", ")}`));
35760
+ const targetsList = tailnet.fleet.filter((m) => fleet.has(m.name) && (flags.only.length === 0 || flags.only.includes(m.name)));
34293
35761
  const ledger = yield* makeLedger(Math.max(5, ...targetsList.map((m) => m.name.length)));
34294
35762
  const count = `${targetsList.length} machine${targetsList.length === 1 ? "" : "s"}`;
34295
35763
  yield* ledger.line("fleet", "INFO", [
@@ -34308,7 +35776,7 @@ const ship = fn("ship")(function* (flags) {
34308
35776
  yield* ledger.line("fleet", "DONE", `${okWord} ${n("ok")} skipped ${n("skipped")} failed ${n("failed")}`);
34309
35777
  if (n("failed") > 0) process.exitCode = 1;
34310
35778
  });
34311
- const cloneRootOf = (machine) => config.machines?.[machine.name]?.clones ?? "~/.local/share/fleetfizz/clones";
35779
+ const cloneRootOf = (machine) => config.machines[machine.name]?.clones ?? "~/.local/share/fleetfizz/clones";
34312
35780
  const gatherRepos = (config.repos ?? []).map((r) => ({
34313
35781
  name: repoName(r),
34314
35782
  outputs: r.outputs ?? []
@@ -34343,6 +35811,17 @@ const ship = fn("ship")(function* (flags) {
34343
35811
  state: result.state
34344
35812
  };
34345
35813
  }), { concurrency: "unbounded" }).pipe(map$5((list) => list.filter((g) => g !== void 0)));
35814
+ const stale = yield* staleConfigs(config, flags.config === void 0, gathered, ledger);
35815
+ if (stale.length > 0) {
35816
+ const what = `newer config on ${stale.join(", ")}`;
35817
+ if (flags.dryRun) yield* ledger.line("fleet", "INFO", `${what}; a Ship would ask before overwriting it`);
35818
+ else if (flags.overwriteConfig) yield* ledger.line("fleet", "INFO", `${what}; overwriting it (--overwrite-config)`);
35819
+ else if (!hasTty()) return yield* fail$1(new ShipError(`${what}; copy it here first, or pass --overwrite-config`));
35820
+ else {
35821
+ yield* ledger.line("fleet", "ASK", what);
35822
+ if (!(yield* run$2(Confirm({ message: "Overwrite it with this Machine's config?" })))) return yield* fail$1(new ShipError("aborted: the config here is older"));
35823
+ }
35824
+ }
34346
35825
  const versions = yield* resolveVersions(packagesToResolve(config));
34347
35826
  const planAll = (cfg, answers) => gathered.map((g) => ({
34348
35827
  ...g,
@@ -34353,21 +35832,22 @@ const ship = fn("ship")(function* (flags) {
34353
35832
  platform: g.machine.platform
34354
35833
  },
34355
35834
  state: g.state,
35835
+ self: flags.version,
34356
35836
  versions,
34357
35837
  rebuild: rebuildOf(flags),
34358
35838
  answers
34359
35839
  })
34360
35840
  }));
34361
- let planned = planAll(config, {});
35841
+ let cfg = config;
35842
+ let planned = planAll(cfg, {});
34362
35843
  const questions = planned.flatMap((g) => g.plan.questions);
34363
35844
  if (questions.length > 0 && !flags.dryRun) {
34364
35845
  const decided = yield* askQuestions(questions, flags, ledger);
34365
- let cfg = config;
34366
35846
  for (const [pkg, decision] of Object.entries(decided)) if (decision.kind === "bump") {
34367
- const range = yield* bumpConfig(configPath, pkg, decision.installed);
34368
- yield* ledger.line("fleet", "INFO", `bumped ${pkg} to ${range} in ${configPath}`);
35847
+ const range = yield* bumpConfig(path, pkg, decision.installed);
35848
+ yield* ledger.line("fleet", "INFO", `bumped ${pkg} to ${range} in ${path}`);
34369
35849
  }
34370
- if (Object.values(decided).some((d) => d.kind === "bump")) cfg = yield* loadConfig(configPath);
35850
+ if (Object.values(decided).some((d) => d.kind === "bump")) cfg = yield* loadConfig(path);
34371
35851
  const answers = Object.fromEntries(Object.entries(decided).flatMap(([pkg, d]) => d.kind === "bump" ? [] : [[pkg, d.kind]]));
34372
35852
  planned = planAll(cfg, answers);
34373
35853
  }
@@ -34379,14 +35859,21 @@ const ship = fn("ship")(function* (flags) {
34379
35859
  yield* record(machine.name, p.failures.length > 0 ? "failed" : "ok");
34380
35860
  }
34381
35861
  if (flags.dryRun) return yield* finish("planned");
35862
+ const shippedAt = formatIso(yield* now);
35863
+ const original = yield* fs.readFileString(path);
35864
+ const stampWith = (reached) => stampText(original, {
35865
+ shippedAt,
35866
+ shippedFrom: tailnet.self.name,
35867
+ reached: [...reached, ...flags.config === void 0 ? [tailnet.self.name] : []]
35868
+ });
35869
+ const stamped = stampWith(planned.map((g) => g.machine.name));
35870
+ yield* fs.writeFileString(path, stamped);
35871
+ const schema = `${schemaJson(fleetConfigJsonSchema())}\n`;
34382
35872
  const lent = planned.some((g) => g.plan.repos.length > 0) ? yield* readLentToken() : { token: void 0 };
34383
35873
  if (lent.token === void 0 && lent.reason !== void 0) yield* ledger.line("fleet", "INFO", `no gh token (${lent.reason}); private repos will fail`);
34384
- const machineNames = tailnet.fleet.map((m) => m.name);
34385
- yield* forEach$1(planned, fn("applyOne")(function* ({ machine, local, plan: p }) {
34386
- if (p.repos.length === 0 && p.vp.length === 0) {
34387
- if (p.failures.length === 0) yield* ledger.line(machine.name, "OK", "up to date");
34388
- return;
34389
- }
35874
+ const machineNames = [...fleet];
35875
+ const stdin = applyStdin(lent.token, stamped, schema);
35876
+ const delivered = yield* forEach$1(planned, fn("applyOne")(function* ({ machine, local, plan: p }) {
34390
35877
  const script = applyScript({
34391
35878
  plan: p,
34392
35879
  cloneRoot: cloneRootOf(machine),
@@ -34396,12 +35883,14 @@ const ship = fn("ship")(function* (flags) {
34396
35883
  machines: machineNames
34397
35884
  }
34398
35885
  });
34399
- const result = yield* apply({
35886
+ const applied = yield* apply({
34400
35887
  name: machine.name,
34401
35888
  local
34402
- }, p, script, [lent.token ?? ""], ledger);
34403
- yield* record(machine.name, result);
34404
- }), { concurrency: "unbounded" });
35889
+ }, p, script, stdin, ledger);
35890
+ yield* record(machine.name, applied.outcome);
35891
+ return applied.done.includes("config") ? machine.name : void 0;
35892
+ }), { concurrency: "unbounded" }).pipe(map$5((list) => list.filter((name) => name !== void 0)));
35893
+ yield* fs.writeFileString(path, stampWith(delivered));
34405
35894
  yield* finish("ok");
34406
35895
  });
34407
35896
  //#endregion
@@ -34412,14 +35901,17 @@ const shipCommand = make$1("ship", {
34412
35901
  rebuild: String$1("rebuild").pipe(atLeast(0), withDescription$1("Run a repo's Install Command even when its record matches; `all` for every repo.")),
34413
35902
  bump: Boolean$1("bump").pipe(withDefault(false), withDescription$1("Answer every above-range question by bumping the config.")),
34414
35903
  enforce: Boolean$1("enforce").pipe(withDefault(false), withDescription$1("Answer every above-range question by downgrading into the range.")),
34415
- config: String$1("config").pipe(optional, withDescription$1("Config file. Default: search upward from cwd for fleet.config.jsonc."))
35904
+ overwriteConfig: Boolean$1("overwrite-config").pipe(withDefault(false), withDescription$1("Ship even when another Machine holds a newer config, overwriting it with this one.")),
35905
+ config: String$1("config").pipe(optional, withDescription$1("Config file. Default: $XDG_CONFIG_HOME/fleetfizz/fleet.config.jsonc."))
34416
35906
  }).pipe(withDescription("Ship the config to every reachable Machine."), withHandler((flags) => ship({
34417
35907
  dryRun: flags.dryRun,
34418
35908
  only: flags.only,
34419
35909
  rebuild: flags.rebuild,
34420
35910
  bump: flags.bump,
34421
35911
  enforce: flags.enforce,
34422
- config: flags.config._tag === "Some" ? flags.config.value : void 0
35912
+ overwriteConfig: flags.overwriteConfig,
35913
+ config: flags.config._tag === "Some" ? flags.config.value : void 0,
35914
+ version
34423
35915
  }).pipe(catch_((e) => gen(function* () {
34424
35916
  yield* error(e.message);
34425
35917
  process.exitCode = 1;