fleetfizz 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9379,7 +9379,19 @@ const Proto$7 = {
9379
9379
  return toDateUtc$1(this).toJSON();
9380
9380
  }
9381
9381
  };
9382
- ({ ...Proto$7 });
9382
+ const ProtoUtc = {
9383
+ ...Proto$7,
9384
+ _tag: "Utc",
9385
+ [symbol$1]() {
9386
+ return number$1(this.epochMilliseconds);
9387
+ },
9388
+ [symbol](that) {
9389
+ return isDateTime$1(that) && that._tag === "Utc" && this.epochMilliseconds === that.epochMilliseconds;
9390
+ },
9391
+ toString() {
9392
+ return `DateTime.Utc(${toDateUtc$1(this).toJSON()})`;
9393
+ }
9394
+ };
9383
9395
  ({ ...Proto$7 });
9384
9396
  const ProtoTimeZone = {
9385
9397
  [TimeZoneTypeId]: TimeZoneTypeId,
@@ -9390,7 +9402,23 @@ const ProtoTimeZone = {
9390
9402
  ({ ...ProtoTimeZone });
9391
9403
  ({ ...ProtoTimeZone });
9392
9404
  /** @internal */
9405
+ const isDateTime$1 = (u) => hasProperty(u, TypeId$27);
9406
+ const makeUtc = (epochMillis) => {
9407
+ const self = Object.create(ProtoUtc);
9408
+ self.epochMilliseconds = epochMillis;
9409
+ Object.defineProperty(self, "partsUtc", {
9410
+ value: void 0,
9411
+ enumerable: false,
9412
+ writable: true
9413
+ });
9414
+ return self;
9415
+ };
9416
+ /** @internal */
9417
+ const now$1 = /*#__PURE__*/ map$7(currentTimeMillis, makeUtc);
9418
+ /** @internal */
9393
9419
  const toDateUtc$1 = (self) => new Date(self.epochMilliseconds);
9420
+ /** @internal */
9421
+ const formatIso$1 = (self) => toDateUtc$1(self).toISOString();
9394
9422
  globalThis.Number;
9395
9423
  /**
9396
9424
  * Returns the number rounded with the given precision.
@@ -16538,17 +16566,56 @@ function resolve$1(ast) {
16538
16566
  return ast.checks ? ast.checks[ast.checks.length - 1].annotations : ast.annotations;
16539
16567
  }
16540
16568
  /** @internal */
16569
+ function resolveAt(key) {
16570
+ return (ast) => resolve$1(ast)?.[key];
16571
+ }
16572
+ /** @internal */
16541
16573
  const STRUCTURAL_ANNOTATION_KEY = "~structural";
16542
16574
  /** @internal */
16575
+ const IDENTIFIER_FALLBACK_KEY = "~identifier";
16576
+ /** @internal */
16543
16577
  const SENTINELS_ANNOTATION_KEY = "~sentinels";
16544
16578
  /** @internal */
16545
16579
  const CONSTRUCTOR_ANNOTATION_KEY = "~constructor";
16546
16580
  /** @internal */
16581
+ const jsonSchemaAnnotationKeys = [
16582
+ "title",
16583
+ "description",
16584
+ "default",
16585
+ "examples",
16586
+ "readOnly",
16587
+ "writeOnly",
16588
+ "format",
16589
+ "contentEncoding",
16590
+ "contentMediaType",
16591
+ "contentSchema"
16592
+ ];
16593
+ /** @internal */
16594
+ const resolveIdentifier = /*#__PURE__*/ resolveAt("identifier");
16595
+ /** @internal */
16596
+ const resolveIdentifierFallback = /*#__PURE__*/ resolveAt(IDENTIFIER_FALLBACK_KEY);
16597
+ /** @internal */
16547
16598
  const getExpected = /*#__PURE__*/ memoize((ast) => {
16548
16599
  const identifier = resolve$1(ast)?.identifier;
16549
16600
  if (typeof identifier === "string") return identifier;
16550
16601
  return ast.getExpected(getExpected);
16551
16602
  });
16603
+ /** @internal */
16604
+ const annotationExcludedKeys = /*#__PURE__*/ new Set([
16605
+ SENTINELS_ANNOTATION_KEY,
16606
+ STRUCTURAL_ANNOTATION_KEY,
16607
+ "representation",
16608
+ "arbitraryConstraint",
16609
+ "toJsonSchema",
16610
+ "toCode",
16611
+ "toEquivalence",
16612
+ "toFormatter",
16613
+ "toCodec",
16614
+ "toCodecJson",
16615
+ "toCodecStringTree",
16616
+ "toCodecIso",
16617
+ "toCodecArbitrary"
16618
+ ]);
16552
16619
  //#endregion
16553
16620
  //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/internal/schema/parser.js
16554
16621
  /** @internal */
@@ -17077,6 +17144,48 @@ function getSchemaIssueOrThrow(cause, message) {
17077
17144
  if (issue === void 0) throw new Error(message, { cause });
17078
17145
  return issue;
17079
17146
  }
17147
+ /**
17148
+ * Gets the current time using the `Clock` service and converts it to a `DateTime`.
17149
+ *
17150
+ * **Example** (Getting the current DateTime)
17151
+ *
17152
+ * ```ts import.meta.vitest
17153
+ * import { DateTime, Effect } from "effect"
17154
+ * import { TestClock } from "effect/testing"
17155
+ *
17156
+ * await Effect.runPromise(Effect.map(DateTime.now, DateTime.isDateTime)) // => true
17157
+ * ```
17158
+ *
17159
+ * @stability stable
17160
+ * @category constructors
17161
+ * @since 3.6.0
17162
+ */
17163
+ const now = now$1;
17164
+ /**
17165
+ * Formats a `DateTime` as a UTC ISO string.
17166
+ *
17167
+ * **Details**
17168
+ *
17169
+ * Always returns the UTC representation in ISO 8601 format, ignoring any time zone.
17170
+ *
17171
+ * **Example** (Formatting DateTime values as ISO strings)
17172
+ *
17173
+ * ```ts import.meta.vitest
17174
+ * import { DateTime } from "effect"
17175
+ *
17176
+ * DateTime.formatIso(DateTime.makeUnsafe("2024-01-01T12:30:45.123Z")) // => "2024-01-01T12:30:45.123Z"
17177
+ *
17178
+ * const zoned = DateTime.makeZonedUnsafe("2024-01-01T12:30:45.123Z", {
17179
+ * timeZone: "Europe/London"
17180
+ * })
17181
+ * DateTime.formatIso(zoned) // => "2024-01-01T12:30:45.123Z"
17182
+ * ```
17183
+ *
17184
+ * @stability stable
17185
+ * @category formatting
17186
+ * @since 3.6.0
17187
+ */
17188
+ const formatIso = formatIso$1;
17080
17189
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
17081
17190
  const codes = /*#__PURE__*/ (/* @__PURE__ */ new Uint8Array(123)).fill(255);
17082
17191
  for (let i = 0; i < 64; i++) codes[/*#__PURE__*/ alphabet.charCodeAt(i)] = i;
@@ -19470,6 +19579,10 @@ function record(key, value) {
19470
19579
  function isOptional(ast) {
19471
19580
  return ast.context?.isOptional ?? false;
19472
19581
  }
19582
+ /** @internal */
19583
+ function isMutable(ast) {
19584
+ return ast.context?.isMutable ?? false;
19585
+ }
19473
19586
  function isStructuralCheck(check) {
19474
19587
  return check.annotations?.["~structural"] === true || check._tag === "FilterGroup" && check.checks.every(isStructuralCheck);
19475
19588
  }
@@ -19640,6 +19753,11 @@ const parameterFromString = /*#__PURE__*/ applyToSelfOrLastLinkEncodingIdempoten
19640
19753
  case "Union": return ast.recur(parameterFromString);
19641
19754
  }
19642
19755
  });
19756
+ /**
19757
+ * any string, including newlines
19758
+ * @internal
19759
+ */
19760
+ const STRING_PATTERN = "[\\s\\S]*?";
19643
19761
  const isStringFiniteRegExp = /*#__PURE__*/ new globalThis.RegExp(`^${FINITE_PATTERN}$`);
19644
19762
  const isStringNumberRegExp = /*#__PURE__*/ new globalThis.RegExp(`^(?:${FINITE_PATTERN}|Infinity|-Infinity|NaN)$`);
19645
19763
  /** @internal */
@@ -19750,6 +19868,10 @@ const Json$1 = /*#__PURE__*/ new Declaration([], () => (input, ast, options) =>
19750
19868
  toCodecJson: () => void 0,
19751
19869
  toCodecStringTree: () => unknownToStringTree
19752
19870
  });
19871
+ /** @internal */
19872
+ const unknownToJson = /*#__PURE__*/ new Link(Json$1, /*#__PURE__*/ passthrough());
19873
+ /** @internal */
19874
+ 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
19875
  /**
19754
19876
  * Returns true if the value is a StringTree value.
19755
19877
  *
@@ -22226,7 +22348,223 @@ function make$5(ast, options) {
22226
22348
  return self;
22227
22349
  }
22228
22350
  //#endregion
22351
+ //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/JsonPointer.js
22352
+ /**
22353
+ * Helpers for escaping JSON Pointer path segments and converting JSON Pointer
22354
+ * URI fragments. JSON Pointer uses `/` to separate path tokens inside a JSON
22355
+ * document, so token text must encode literal `~` and `/` characters. URI
22356
+ * fragments additionally apply percent-encoding after JSON Pointer escaping.
22357
+ *
22358
+ * @stability stable
22359
+ * @since 4.0.0
22360
+ */
22361
+ /**
22362
+ * 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.
22363
+ *
22364
+ * **When to use**
22365
+ *
22366
+ * Use when you need to escape a single JSON Pointer path segment.
22367
+ *
22368
+ * **Details**
22369
+ *
22370
+ * - Returns a new escaped string
22371
+ * - Replaces `~` (tilde) with `~0` and `/` (forward slash) with `~1`
22372
+ * - Returns the input unchanged if it contains no special characters
22373
+ * - Empty strings are valid and returned unchanged
22374
+ *
22375
+ * **Gotchas**
22376
+ *
22377
+ * The replacement order matters: `~` is replaced before `/` to prevent double-escaping.
22378
+ *
22379
+ * **Example** (Escaping special characters)
22380
+ *
22381
+ * ```ts import.meta.vitest
22382
+ * import { JsonPointer } from "effect"
22383
+ *
22384
+ * JsonPointer.escapeToken("a/b") // => "a~1b"
22385
+ * JsonPointer.escapeToken("c~d") // => "c~0d"
22386
+ * JsonPointer.escapeToken("path/to~key") // => "path~1to~0key"
22387
+ * ```
22388
+ *
22389
+ * @see {@link unescapeToken} The inverse operation for decoding escaped tokens
22390
+ * @stability stable
22391
+ * @category encoding
22392
+ * @since 4.0.0
22393
+ */
22394
+ function escapeToken(token) {
22395
+ return token.replace(/~/g, "~0").replace(/\//g, "~1");
22396
+ }
22397
+ /**
22398
+ * Decodes a JSON Pointer reference token according to RFC 6901 escaping rules.
22399
+ *
22400
+ * **When to use**
22401
+ *
22402
+ * Use when you need to decode a single escaped JSON Pointer path segment.
22403
+ *
22404
+ * **Details**
22405
+ *
22406
+ * - Returns a new unescaped string
22407
+ * - Replaces `~1` with `/` (forward slash) and `~0` with `~` (tilde)
22408
+ * - Returns the input unchanged if it contains no escaped sequences
22409
+ * - Empty strings are valid and returned unchanged
22410
+ *
22411
+ * **Gotchas**
22412
+ *
22413
+ * The replacement order matters: `~1` is replaced before `~0` to prevent incorrect decoding.
22414
+ *
22415
+ * **Example** (Unescaping special characters)
22416
+ *
22417
+ * ```ts import.meta.vitest
22418
+ * import { JsonPointer } from "effect"
22419
+ *
22420
+ * JsonPointer.unescapeToken("a~1b") // => "a/b"
22421
+ * JsonPointer.unescapeToken("c~0d") // => "c~d"
22422
+ * JsonPointer.unescapeToken("path~1to~0key") // => "path/to~key"
22423
+ * ```
22424
+ *
22425
+ * @see {@link escapeToken} The inverse operation for encoding tokens
22426
+ * @stability stable
22427
+ * @category decoding
22428
+ * @since 4.0.0
22429
+ */
22430
+ function unescapeToken(token) {
22431
+ return token.replace(/~1/g, "/").replace(/~0/g, "~");
22432
+ }
22433
+ /** @internal */
22434
+ function formatUriFragmentToken(token) {
22435
+ return encodeURI(escapeToken(token)).replace(/#/g, "%23");
22436
+ }
22437
+ /** @internal */
22438
+ function decodeUriFragment(fragment) {
22439
+ if (fragment.length === 0 || fragment === "#") return "";
22440
+ if (!fragment.startsWith("#")) return void 0;
22441
+ const encoded = fragment.slice(1);
22442
+ try {
22443
+ if (encodeURI(encoded).replace(/%25/g, "%").replace(/#/g, "%23") !== encoded) return void 0;
22444
+ const pointer = decodeURIComponent(encoded);
22445
+ return pointer.startsWith("/") && !/~(?:[^01]|$)/.test(pointer) ? pointer : void 0;
22446
+ } catch {
22447
+ return;
22448
+ }
22449
+ }
22450
+ //#endregion
22451
+ //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/JsonSchema.js
22452
+ /**
22453
+ * Helpers for normalizing and converting JSON Schema and OpenAPI schema
22454
+ * documents. Supported inputs include JSON Schema Draft-07, Draft 2020-12,
22455
+ * OpenAPI 3.0, and OpenAPI 3.1; conversions normalize through
22456
+ * `Document<"draft-2020-12">` before emitting another dialect, including
22457
+ * JSON Schema Draft-04. The module also defines document types, meta-schema
22458
+ * constants, and OpenAPI component-key helpers.
22459
+ *
22460
+ * @stability stable
22461
+ * @since 4.0.0
22462
+ */
22463
+ /**
22464
+ * Represents the `$schema` meta-schema URI for JSON Schema Draft 2020-12.
22465
+ *
22466
+ * **When to use**
22467
+ *
22468
+ * Use when you need to populate the `$schema` field while emitting a JSON
22469
+ * Schema document that should declare JSON Schema Draft 2020-12.
22470
+ *
22471
+ * **Details**
22472
+ *
22473
+ * The exported value is the literal string
22474
+ * `https://json-schema.org/draft/2020-12/schema`.
22475
+ *
22476
+ * @see {@link META_SCHEMA_URI_DRAFT_07} for the Draft-07 `$schema` URI
22477
+ *
22478
+ * @stability stable
22479
+ * @category constants
22480
+ * @since 4.0.0
22481
+ */
22482
+ const META_SCHEMA_URI_DRAFT_2020_12 = "https://json-schema.org/draft/2020-12/schema";
22483
+ function transformSchema(node, transform) {
22484
+ return walk(node, false, true);
22485
+ function walk(node, inheritedResource, isRoot = false) {
22486
+ if (!isObject(node)) return node;
22487
+ const inEmbeddedResource = inheritedResource || !isRoot && createsResource(node.$id);
22488
+ const out = {};
22489
+ for (const key of Object.keys(node)) {
22490
+ const value = node[key];
22491
+ let transformed = value;
22492
+ switch (key) {
22493
+ case "$defs":
22494
+ case "properties":
22495
+ case "patternProperties":
22496
+ case "dependentSchemas":
22497
+ transformed = mapObject(value, (value) => walk(value, inEmbeddedResource)) ?? value;
22498
+ break;
22499
+ case "allOf":
22500
+ case "anyOf":
22501
+ case "oneOf":
22502
+ case "prefixItems":
22503
+ transformed = Array.isArray(value) ? value.map((value) => walk(value, inEmbeddedResource)) : value;
22504
+ break;
22505
+ case "not":
22506
+ case "additionalProperties":
22507
+ case "propertyNames":
22508
+ case "unevaluatedProperties":
22509
+ case "items":
22510
+ case "contains":
22511
+ case "unevaluatedItems":
22512
+ case "if":
22513
+ case "then":
22514
+ case "else":
22515
+ case "contentSchema": transformed = walk(value, inEmbeddedResource);
22516
+ }
22517
+ assignProperty(out, key, transformed);
22518
+ }
22519
+ transform(out, inEmbeddedResource);
22520
+ return out;
22521
+ }
22522
+ }
22523
+ /** @internal */
22524
+ function rewriteRefs(schema, rewrite) {
22525
+ return transformSchema(schema, (schema) => {
22526
+ rewriteSchemaRef(schema, rewrite);
22527
+ });
22528
+ }
22529
+ function rewriteSchemaRef(schema, rewrite) {
22530
+ if (typeof schema.$ref === "string") assignProperty(schema, "$ref", rewrite(schema.$ref, "$ref"));
22531
+ if (typeof schema.$dynamicRef === "string") assignProperty(schema, "$dynamicRef", rewrite(schema.$dynamicRef, "$dynamicRef"));
22532
+ }
22533
+ function mapObject(value, f) {
22534
+ if (!isObject(value)) return void 0;
22535
+ const out = {};
22536
+ for (const key of Object.keys(value)) assignProperty(out, key, f(value[key], key));
22537
+ return out;
22538
+ }
22539
+ function createsResource(id) {
22540
+ return typeof id === "string" && id.length > 0 && id[0] !== "#";
22541
+ }
22542
+ const PRE_2020_TO_2020_COLLISIONS = [
22543
+ "$anchor",
22544
+ "$defs",
22545
+ "$dynamicAnchor",
22546
+ "$dynamicRef",
22547
+ "$vocabulary",
22548
+ "contentSchema",
22549
+ "dependentRequired",
22550
+ "dependentSchemas",
22551
+ "maxContains",
22552
+ "minContains",
22553
+ "prefixItems",
22554
+ "unevaluatedItems",
22555
+ "unevaluatedProperties"
22556
+ ];
22557
+ [...PRE_2020_TO_2020_COLLISIONS];
22558
+ [...PRE_2020_TO_2020_COLLISIONS];
22559
+ //#endregion
22229
22560
  //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/internal/schema/toCodec.js
22561
+ /** @internal */
22562
+ const toCodecJsonAST = /*#__PURE__*/ applyToSelfOrLastLinkEncodingIdempotent((ast) => {
22563
+ const out = toCodecJsonASTStep(ast, toCodecJsonAST);
22564
+ const context = ast.context;
22565
+ if (out === ast || context === void 0) return out;
22566
+ return replaceContextLastLink(out, withoutConstructorDefault(context));
22567
+ });
22230
22568
  function withoutConstructorDefault(context) {
22231
22569
  return context.constructorDefault === void 0 ? context : new Context(context.isOptional, context.isMutable, void 0, context.annotations);
22232
22570
  }
@@ -22240,6 +22578,44 @@ function makeReorder(getPriority) {
22240
22578
  return sortedTypes;
22241
22579
  };
22242
22580
  }
22581
+ const toCodecJsonReorder = /*#__PURE__*/ makeReorder((ast) => {
22582
+ switch (ast._tag) {
22583
+ case "BigInt":
22584
+ case "Symbol":
22585
+ case "UniqueSymbol": return 0;
22586
+ default: return 1;
22587
+ }
22588
+ });
22589
+ function toCodecJsonASTStep(ast, recur) {
22590
+ switch (ast._tag) {
22591
+ case "Declaration": {
22592
+ const getLink = ast.annotations?.toCodecJson ?? ast.annotations?.toCodec;
22593
+ if (!isFunction(getLink)) return replaceEncoding(ast, [unknownToJson]);
22594
+ const link = getLink(ast.typeParameters.map((tp) => make$5(toEncoded(tp))));
22595
+ return link === void 0 ? ast : replaceEncoding(ast, [mapLink(link, recur)]);
22596
+ }
22597
+ case "Unknown": return replaceEncoding(ast, [unknownToJson]);
22598
+ case "ObjectKeyword": return replaceEncoding(ast, [objectKeywordToJson]);
22599
+ case "Undefined":
22600
+ case "Void":
22601
+ case "Literal":
22602
+ case "Number": return ast.toCodecJson();
22603
+ case "UniqueSymbol":
22604
+ case "Symbol":
22605
+ case "BigInt": return ast.toCodecStringTree();
22606
+ case "Objects":
22607
+ validateCanonicalObjectPropertyNames(ast);
22608
+ return ast.recur(recur, parameterFromString);
22609
+ case "Union": {
22610
+ const sortedTypes = toCodecJsonReorder(ast.types);
22611
+ if (sortedTypes !== ast.types) return new Union$1(sortedTypes, ast.options, ast.annotations, ast.checks, ast.encoding, ast.context, ast.encodingChecks).recur(recur);
22612
+ return ast.recur(recur);
22613
+ }
22614
+ case "Arrays":
22615
+ case "Suspend": return ast.recur(recur);
22616
+ }
22617
+ return ast;
22618
+ }
22243
22619
  /** @internal */
22244
22620
  function toCodecStringTree$1(schema) {
22245
22621
  return make$5(toCodecStringTreeAST(schema.ast), { schema });
@@ -22305,6 +22681,849 @@ const toCodecStringTreeAST = /*#__PURE__*/ applyToSelfOrLastLinkEncodingIdempote
22305
22681
  if (out !== ast && ast.context !== void 0) return replaceContextLastLink(out, withoutConstructorDefault(ast.context));
22306
22682
  return out;
22307
22683
  }, { stopAt: isCodecArrayFromSingleLink });
22684
+ globalThis.RegExp;
22685
+ /**
22686
+ * Escapes special characters in a regular expression pattern.
22687
+ *
22688
+ * **When to use**
22689
+ *
22690
+ * Use to turn literal text into a safe regular expression pattern fragment.
22691
+ *
22692
+ * **Example** (Escaping a pattern string)
22693
+ *
22694
+ * ```ts import.meta.vitest
22695
+ * import { RegExp } from "effect"
22696
+ *
22697
+ * RegExp.escape("a*b") // => "a\\*b"
22698
+ * ```
22699
+ *
22700
+ * @stability stable
22701
+ * @category transforming
22702
+ * @since 2.0.0
22703
+ */
22704
+ const escape = (string) => string.replace(/[/\\^$*+?.()|[\]{}]/g, "\\$&");
22705
+ //#endregion
22706
+ //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/internal/errors.js
22707
+ /** @internal */
22708
+ function errorWithPath(message, path) {
22709
+ if (path.length > 0) message += `\n at ${formatPath(path)}`;
22710
+ return new Error(message);
22711
+ }
22712
+ //#endregion
22713
+ //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/internal/schema/toJsonSchemaDocument.js
22714
+ function formatDefinitionReference(key) {
22715
+ return `#/$defs/${formatUriFragmentToken(key)}`;
22716
+ }
22717
+ const jsonSchemaAnnotationExcludedKeys = /*#__PURE__*/ new Set([
22718
+ ...annotationExcludedKeys,
22719
+ IDENTIFIER_FALLBACK_KEY,
22720
+ ...jsonSchemaAnnotationKeys
22721
+ ]);
22722
+ function collectJsonSchemaAnnotations(annotations, options) {
22723
+ if (annotations === void 0) return void 0;
22724
+ const out = {};
22725
+ const title = annotations.title;
22726
+ if (typeof title === "string") out.title = title;
22727
+ const description = annotations.description;
22728
+ const expected = annotations.expected;
22729
+ if (typeof description === "string") out.description = description;
22730
+ else if (options?.generateDescriptions === true && typeof expected === "string") out.description = expected;
22731
+ const defaultValue = annotations.default;
22732
+ if (isJson(defaultValue)) out.default = defaultValue;
22733
+ const examples = annotations.examples;
22734
+ if (Array.isArray(examples) && isJson(examples)) out.examples = examples;
22735
+ const readOnly = annotations.readOnly;
22736
+ if (typeof readOnly === "boolean") out.readOnly = readOnly;
22737
+ const writeOnly = annotations.writeOnly;
22738
+ if (typeof writeOnly === "boolean") out.writeOnly = writeOnly;
22739
+ const format = annotations.format;
22740
+ if (typeof format === "string") out.format = format;
22741
+ const contentEncoding = annotations.contentEncoding;
22742
+ if (typeof contentEncoding === "string") out.contentEncoding = contentEncoding;
22743
+ const contentMediaType = annotations.contentMediaType;
22744
+ if (typeof contentMediaType === "string") out.contentMediaType = contentMediaType;
22745
+ const contentSchema = annotations.contentSchema;
22746
+ if (isJson(contentSchema)) out.contentSchema = contentSchema;
22747
+ if (options?.includeAnnotationKey !== void 0) for (const [key, value] of Object.entries(annotations)) {
22748
+ if (jsonSchemaAnnotationExcludedKeys.has(key) || !options.includeAnnotationKey(key)) continue;
22749
+ if (isJson(value)) assignProperty(out, key, value);
22750
+ }
22751
+ return Object.keys(out).length === 0 ? void 0 : out;
22752
+ }
22753
+ function extractJsonSchemaNumberType(schema) {
22754
+ let type = schema.type === "number" || schema.type === "integer" ? schema.type : void 0;
22755
+ let out = schema;
22756
+ if (type !== void 0) {
22757
+ out = { ...schema };
22758
+ delete out.type;
22759
+ }
22760
+ if (Array.isArray(out.allOf)) {
22761
+ const members = [];
22762
+ let changed = false;
22763
+ for (const member of out.allOf) {
22764
+ const extracted = extractJsonSchemaNumberType(member);
22765
+ if (extracted.type !== void 0) {
22766
+ changed = true;
22767
+ if (type === void 0 || extracted.type === "integer") type = extracted.type;
22768
+ }
22769
+ if (Object.keys(extracted.schema).length > 0) members.push(extracted.schema);
22770
+ }
22771
+ if (changed) {
22772
+ const { allOf: _, ...rest } = out;
22773
+ out = members.length === 0 ? rest : {
22774
+ ...rest,
22775
+ allOf: members
22776
+ };
22777
+ }
22778
+ }
22779
+ return {
22780
+ type,
22781
+ schema: out
22782
+ };
22783
+ }
22784
+ function isJsonSchemaNumberEncoding(schema) {
22785
+ if (!Array.isArray(schema.anyOf) || schema.anyOf.length !== 2 || schema.anyOf[0]?.type !== "number") return false;
22786
+ const special = schema.anyOf[1];
22787
+ return special?.type === "string" && Array.isArray(special.enum) && special.enum.length === 3 && special.enum.includes("NaN") && special.enum.includes("Infinity") && special.enum.includes("-Infinity");
22788
+ }
22789
+ const inlineableCheckKeywords = "|type|format|pattern|multipleOf|minimum|maximum|exclusiveMinimum|exclusiveMaximum|minLength|maxLength|minItems|maxItems|uniqueItems|minProperties|maxProperties|propertyNames|";
22790
+ function hasOnlyKeywords(schema, allowed) {
22791
+ return Object.keys(schema).every((key) => allowed.includes(`|${key}|`));
22792
+ }
22793
+ function hasNoCollisions(left, rightKeys) {
22794
+ return typeof left.$ref !== "string" && rightKeys.every((key) => !Object.hasOwn(left, key));
22795
+ }
22796
+ const promotableAnnotationKeywords = "|title|description|default|examples|readOnly|writeOnly|";
22797
+ const inlineableAnnotatedCheckKeywords = inlineableCheckKeywords + promotableAnnotationKeywords;
22798
+ function appendJsonSchema(left, right, inlineCheck) {
22799
+ if (Object.keys(left).length === 0) return right;
22800
+ const rightKeys = Object.keys(right);
22801
+ if (rightKeys.length === 0) return left;
22802
+ const leftType = left.type === "number" || left.type === "integer" ? left.type : void 0;
22803
+ const isNumberEncoding = isJsonSchemaNumberEncoding(left);
22804
+ if (leftType !== void 0 || isNumberEncoding) {
22805
+ const extracted = extractJsonSchemaNumberType(right);
22806
+ if (extracted.type !== void 0) {
22807
+ const type = leftType === "integer" || extracted.type === "integer" ? "integer" : "number";
22808
+ const base = {
22809
+ ...left,
22810
+ type
22811
+ };
22812
+ if (isNumberEncoding) delete base.anyOf;
22813
+ const extractedKeys = Object.keys(extracted.schema);
22814
+ if (extractedKeys.length === 0) return base;
22815
+ return hasOnlyKeywords(extracted.schema, promotableAnnotationKeywords) && hasNoCollisions(base, extractedKeys) ? {
22816
+ ...base,
22817
+ ...extracted.schema
22818
+ } : appendJsonSchema(base, extracted.schema, inlineCheck);
22819
+ }
22820
+ }
22821
+ if (inlineCheck && hasNoCollisions(left, rightKeys)) return {
22822
+ ...left,
22823
+ ...right
22824
+ };
22825
+ const members = Array.isArray(right.allOf) && rightKeys.length === 1 ? right.allOf : [right];
22826
+ if (Array.isArray(left.allOf)) return {
22827
+ ...left,
22828
+ allOf: [...left.allOf, ...members]
22829
+ };
22830
+ if (typeof left.$ref === "string") return { allOf: [left, ...members] };
22831
+ return {
22832
+ ...left,
22833
+ allOf: members
22834
+ };
22835
+ }
22836
+ function isExact(exactness, seen = /* @__PURE__ */ new Set()) {
22837
+ if (typeof exactness === "boolean") return exactness;
22838
+ if (seen.has(exactness)) return true;
22839
+ seen.add(exactness);
22840
+ return exactness.every((dependency) => isExact(dependency, seen));
22841
+ }
22842
+ function isApproximateCheckOutput(output) {
22843
+ return Array.isArray(output);
22844
+ }
22845
+ function compileJsonSchema(representations, rootPaths, references, options) {
22846
+ const compiledDefinitions = /* @__PURE__ */ new Map();
22847
+ const definitionAliases = /* @__PURE__ */ new Map();
22848
+ const compiledRepresentations = /* @__PURE__ */ new WeakMap();
22849
+ const pendingOneOf = /* @__PURE__ */ new Map();
22850
+ const fallbackDefinitions = /* @__PURE__ */ new Map();
22851
+ const referenceKeys = Object.keys(references);
22852
+ for (const key of referenceKeys) compileDefinition(key, ["references", key]);
22853
+ const compiledSchemas = map$8(representations, (representation, index) => recur(representation, rootPaths[index], []));
22854
+ for (const [branches, schemas] of pendingOneOf.values()) if (!isExact(branches)) {
22855
+ for (const schema of schemas) if (Array.isArray(schema.oneOf)) {
22856
+ schema.anyOf = schema.oneOf;
22857
+ delete schema.oneOf;
22858
+ }
22859
+ }
22860
+ for (const key of referenceKeys) {
22861
+ const representation = references[key];
22862
+ const fallback = getIdentifierFallback(representation);
22863
+ if (fallback === void 0) continue;
22864
+ const schema = compiledDefinitions.get(key)[0];
22865
+ const candidates = fallbackDefinitions.get(fallback);
22866
+ const match = candidates?.find((candidate) => equals$1(compiledDefinitions.get(candidate)[0], schema));
22867
+ if (match === void 0) {
22868
+ if (candidates === void 0) fallbackDefinitions.set(fallback, [key]);
22869
+ else candidates.push(key);
22870
+ } else definitionAliases.set(key, match);
22871
+ }
22872
+ const schemas = map$8(compiledSchemas, finalizeJsonSchema);
22873
+ const definitions = {};
22874
+ for (const key of referenceKeys) if (!definitionAliases.has(key)) assignProperty(definitions, key, finalizeJsonSchema(compiledDefinitions.get(key)[0]));
22875
+ return {
22876
+ dialect: "draft-2020-12",
22877
+ schemas,
22878
+ definitions
22879
+ };
22880
+ function registerPendingOneOf(schema) {
22881
+ if (Array.isArray(schema.oneOf)) pendingOneOf.get(schema.oneOf)?.[1].push(schema);
22882
+ return schema;
22883
+ }
22884
+ function compileDefinition(key, path) {
22885
+ if (!Object.hasOwn(references, key)) throw errorWithPath(`Invalid reference ${key}`, [...path, "$ref"]);
22886
+ const cached = compiledDefinitions.get(key);
22887
+ if (cached !== void 0) return cached;
22888
+ const result = [{}, []];
22889
+ compiledDefinitions.set(key, result);
22890
+ result[0] = recur(references[key], ["references", key], result[1]);
22891
+ return result;
22892
+ }
22893
+ function finalizeJsonSchema(schema) {
22894
+ return rewriteRefs(schema, ($ref) => {
22895
+ const pointer = decodeUriFragment($ref);
22896
+ if (pointer === void 0) {
22897
+ if (/^#(?:\/|%2f)/i.test($ref)) throw new Error(`Invalid JSON Pointer URI fragment ${JSON.stringify($ref)}`);
22898
+ return $ref;
22899
+ }
22900
+ if (!pointer.startsWith("/$defs/")) return $ref;
22901
+ const separator = pointer.indexOf("/", 7);
22902
+ const canonical = definitionAliases.get(unescapeToken(pointer.slice(7, separator < 0 ? void 0 : separator)));
22903
+ if (canonical === void 0) return $ref;
22904
+ return $ref.replace(/^#(?:\/|%2f).*?(?:\/|%2f).*?(?=\/|%2f|$)/i, formatDefinitionReference(canonical));
22905
+ });
22906
+ }
22907
+ function getIdentifierFallback(representation) {
22908
+ if (representation._tag === "Reference") return void 0;
22909
+ const annotations = representation.checks.length === 0 ? representation.annotations : representation.checks[representation.checks.length - 1].annotations;
22910
+ return typeof annotations?.identifier !== "string" && typeof annotations?.["~identifier"] === "string" ? annotations[IDENTIFIER_FALLBACK_KEY] : void 0;
22911
+ }
22912
+ function compileCheck(check, type, path, dependencies) {
22913
+ const annotations = check.annotations;
22914
+ const callback = annotations?.toJsonSchema;
22915
+ if (callback !== void 0) {
22916
+ const result = callback({
22917
+ type,
22918
+ schemas: check.representation?.schemas?.map((schema, index) => recur(schema, [
22919
+ ...path,
22920
+ "representation",
22921
+ "schemas",
22922
+ index
22923
+ ], dependencies)) ?? []
22924
+ });
22925
+ const approximate = isApproximateCheckOutput(result);
22926
+ if (approximate) dependencies.push(false);
22927
+ const fragment = approximate ? result[0] : result;
22928
+ const ordinary = collectJsonSchemaAnnotations(annotations, options);
22929
+ const schema = ordinary === void 0 ? fragment : {
22930
+ ...fragment,
22931
+ ...ordinary
22932
+ };
22933
+ const allowed = ordinary === void 0 ? inlineableCheckKeywords : inlineableAnnotatedCheckKeywords;
22934
+ return [schema, check._tag === "Filter" && hasOnlyKeywords(schema, allowed) && (ordinary === void 0 || hasOnlyKeywords(ordinary, promotableAnnotationKeywords)) ? true : void 0];
22935
+ }
22936
+ if (check._tag === "Filter") {
22937
+ dependencies.push(false);
22938
+ return;
22939
+ }
22940
+ const schemas = check.checks.flatMap((child, index) => {
22941
+ const result = compileCheck(child, type, [
22942
+ ...path,
22943
+ "checks",
22944
+ index
22945
+ ], dependencies);
22946
+ return result === void 0 ? [] : [result[0]];
22947
+ });
22948
+ if (schemas.length === 0) return void 0;
22949
+ const ordinary = collectJsonSchemaAnnotations(annotations, options);
22950
+ const schema = { allOf: schemas };
22951
+ return [ordinary === void 0 ? schema : {
22952
+ ...schema,
22953
+ ...ordinary
22954
+ }];
22955
+ }
22956
+ function recur(representation, path, dependencies) {
22957
+ if (representation._tag === "Reference") {
22958
+ dependencies.push(compileDefinition(representation.$ref, path)[1]);
22959
+ return { $ref: formatDefinitionReference(representation.$ref) };
22960
+ }
22961
+ const cached = compiledRepresentations.get(representation);
22962
+ if (cached !== void 0) {
22963
+ dependencies.push(cached[1]);
22964
+ return cached[0];
22965
+ }
22966
+ const local = [];
22967
+ dependencies.push(local);
22968
+ let output = on(representation, path, local);
22969
+ const ordinary = collectJsonSchemaAnnotations(representation.annotations, options);
22970
+ if (ordinary !== void 0) output = {
22971
+ ...output,
22972
+ ...ordinary
22973
+ };
22974
+ for (let index = 0; index < representation.checks.length; index++) {
22975
+ const type = typeof output.type === "string" && isJsonSchemaType(output.type) ? output.type : void 0;
22976
+ const check = compileCheck(representation.checks[index], type, [
22977
+ ...path,
22978
+ "checks",
22979
+ index
22980
+ ], local);
22981
+ if (check !== void 0) output = appendJsonSchema(output, ...check);
22982
+ }
22983
+ compiledRepresentations.set(representation, [output, local]);
22984
+ return registerPendingOneOf(output);
22985
+ }
22986
+ function on(representation, path, dependencies) {
22987
+ switch (representation._tag) {
22988
+ case "Any":
22989
+ case "Unknown": return {};
22990
+ case "ObjectKeyword": return { anyOf: [{ type: "object" }, { type: "array" }] };
22991
+ case "Void":
22992
+ case "Undefined":
22993
+ case "Null": return { type: "null" };
22994
+ case "BigInt": return {
22995
+ type: "string",
22996
+ allOf: [{ pattern: "^-?\\d+$" }]
22997
+ };
22998
+ case "Symbol": return {
22999
+ type: "string",
23000
+ allOf: [{ pattern: "^Symbol\\(([\\s\\S]*)\\)$" }]
23001
+ };
23002
+ case "UniqueSymbol": return globalThis.Symbol.keyFor(representation.symbol) === void 0 ? { not: {} } : {
23003
+ type: "string",
23004
+ enum: [globalThis.String(representation.symbol)]
23005
+ };
23006
+ case "Declaration":
23007
+ dependencies.push(false);
23008
+ return {};
23009
+ case "Suspend": return recur(representation.thunk, [...path, "thunk"], dependencies);
23010
+ case "Never": return { not: {} };
23011
+ case "String": return { type: "string" };
23012
+ case "Number": return { anyOf: [{ type: "number" }, {
23013
+ type: "string",
23014
+ enum: [
23015
+ "NaN",
23016
+ "Infinity",
23017
+ "-Infinity"
23018
+ ]
23019
+ }] };
23020
+ case "Boolean": return { type: "boolean" };
23021
+ case "Literal": {
23022
+ const literal = representation.literal;
23023
+ return typeof literal === "bigint" ? {
23024
+ type: "string",
23025
+ enum: [globalThis.String(literal)]
23026
+ } : {
23027
+ type: typeof literal,
23028
+ enum: [literal]
23029
+ };
23030
+ }
23031
+ case "Enum": {
23032
+ const types = representation.enums.map(([title, literal], index) => {
23033
+ if (typeof literal === "number" && !globalThis.Number.isFinite(literal)) throw errorWithPath(`Invalid numeric enum value ${globalThis.String(literal)}`, [
23034
+ ...path,
23035
+ "enums",
23036
+ index,
23037
+ 1
23038
+ ]);
23039
+ return {
23040
+ type: typeof literal,
23041
+ enum: [literal],
23042
+ title
23043
+ };
23044
+ });
23045
+ return types.length === 0 ? { not: {} } : { anyOf: types };
23046
+ }
23047
+ case "TemplateLiteral": return {
23048
+ type: "string",
23049
+ pattern: `^${representation.parts.map(getPartPattern).join("")}$`
23050
+ };
23051
+ case "Arrays": {
23052
+ if (representation.rest.length > 1) throw errorWithPath("Invalid schema representation document", [...path, "rest"]);
23053
+ const out = { type: "array" };
23054
+ let minItems = representation.elements.length;
23055
+ const prefixItems = representation.elements.map((element, index) => {
23056
+ if (element.isOptional) minItems--;
23057
+ const schema = recur(element.type, [
23058
+ ...path,
23059
+ "elements",
23060
+ index,
23061
+ "type"
23062
+ ], dependencies);
23063
+ const annotations = collectJsonSchemaAnnotations(element.annotations, options);
23064
+ return annotations === void 0 ? schema : registerPendingOneOf(appendJsonSchema(schema, annotations));
23065
+ });
23066
+ if (prefixItems.length > 0) {
23067
+ out.prefixItems = prefixItems;
23068
+ out.maxItems = representation.elements.length;
23069
+ if (minItems > 0) out.minItems = minItems;
23070
+ } else out.items = false;
23071
+ if (representation.rest.length === 1) {
23072
+ delete out.maxItems;
23073
+ const rest = recur(representation.rest[0], [
23074
+ ...path,
23075
+ "rest",
23076
+ 0
23077
+ ], dependencies);
23078
+ if (Object.keys(rest).length > 0) out.items = rest;
23079
+ else delete out.items;
23080
+ }
23081
+ return out;
23082
+ }
23083
+ case "Objects": {
23084
+ if (representation.propertySignatures.length === 0 && representation.indexSignatures.length === 0) return { not: { type: "null" } };
23085
+ const out = { type: "object" };
23086
+ const properties = {};
23087
+ const required = [];
23088
+ for (let index = 0; index < representation.propertySignatures.length; index++) {
23089
+ const property = representation.propertySignatures[index];
23090
+ if (typeof property.name !== "string") throw errorWithPath("Invalid schema representation document", [
23091
+ ...path,
23092
+ "propertySignatures",
23093
+ index,
23094
+ "name"
23095
+ ]);
23096
+ const name = property.name;
23097
+ const schema = recur(property.type, [
23098
+ ...path,
23099
+ "propertySignatures",
23100
+ index,
23101
+ "type"
23102
+ ], dependencies);
23103
+ const annotations = collectJsonSchemaAnnotations(property.annotations, options);
23104
+ assignProperty(properties, name, annotations === void 0 ? schema : registerPendingOneOf(appendJsonSchema(schema, annotations)));
23105
+ if (!property.isOptional) required.push(name);
23106
+ }
23107
+ if (representation.propertySignatures.length > 0) out.properties = properties;
23108
+ if (required.length > 0) out.required = required;
23109
+ const patternProperties = {};
23110
+ const additionalPropertySchemas = [];
23111
+ const indexValueSchemas = [];
23112
+ let hasUnrepresentableIndexPattern = false;
23113
+ for (let index = 0; index < representation.indexSignatures.length; index++) {
23114
+ const signature = representation.indexSignatures[index];
23115
+ let type = recur(signature.type, [
23116
+ ...path,
23117
+ "indexSignatures",
23118
+ index,
23119
+ "type"
23120
+ ], dependencies);
23121
+ if (equals$1(type, { not: {} })) type = false;
23122
+ indexValueSchemas.push(type);
23123
+ const patterns = getParameterPatterns(signature.parameter, [
23124
+ ...path,
23125
+ "indexSignatures",
23126
+ index,
23127
+ "parameter"
23128
+ ], /* @__PURE__ */ new Set());
23129
+ if (patterns === void 0) {
23130
+ hasUnrepresentableIndexPattern = true;
23131
+ dependencies.push(false);
23132
+ continue;
23133
+ }
23134
+ if (patterns.length === 0) additionalPropertySchemas.push(type);
23135
+ else for (const pattern of patterns) {
23136
+ const previous = patternProperties[pattern];
23137
+ assignProperty(patternProperties, pattern, previous === void 0 ? type : previous === false || type === false ? false : registerPendingOneOf(appendJsonSchema(previous, type)));
23138
+ }
23139
+ }
23140
+ const hasPatternProperties = Object.keys(patternProperties).length > 0;
23141
+ if (hasPatternProperties) out.patternProperties = patternProperties;
23142
+ if (hasUnrepresentableIndexPattern && options?.onExcessProperty === "error") {
23143
+ const propertyNames = [];
23144
+ if (representation.propertySignatures.length > 0) propertyNames.push({ enum: Object.keys(properties) });
23145
+ for (let index = 0; index < representation.indexSignatures.length; index++) propertyNames.push(recur(representation.indexSignatures[index].parameter, [
23146
+ ...path,
23147
+ "indexSignatures",
23148
+ index,
23149
+ "parameter"
23150
+ ], dependencies));
23151
+ out.propertyNames = propertyNames.length === 1 ? propertyNames[0] : { anyOf: propertyNames };
23152
+ out.additionalProperties = indexValueSchemas.length === 1 ? indexValueSchemas[0] : { anyOf: indexValueSchemas };
23153
+ if (additionalPropertySchemas.length > 0) out.allOf = additionalPropertySchemas.map((type) => ({
23154
+ type: "object",
23155
+ additionalProperties: type
23156
+ }));
23157
+ } else if (additionalPropertySchemas.length === 0) out.additionalProperties = hasUnrepresentableIndexPattern || options?.onExcessProperty !== "error";
23158
+ else if (additionalPropertySchemas.length === 1 && representation.propertySignatures.length === 0 && !hasPatternProperties) out.additionalProperties = additionalPropertySchemas[0];
23159
+ else if (additionalPropertySchemas.length > 0) out.allOf = additionalPropertySchemas.map((type) => ({
23160
+ type: "object",
23161
+ additionalProperties: type
23162
+ }));
23163
+ if (typeof out.additionalProperties === "object" && out.additionalProperties !== null && Object.keys(out.additionalProperties).length === 0) delete out.additionalProperties;
23164
+ return out;
23165
+ }
23166
+ case "Union": {
23167
+ const branches = [];
23168
+ const types = representation.types.map((type, index) => recur(type, [
23169
+ ...path,
23170
+ "types",
23171
+ index
23172
+ ], branches));
23173
+ dependencies.push(branches);
23174
+ if (types.length === 0) return { not: {} };
23175
+ const mode = representation.options?.mode ?? "anyOf";
23176
+ if (mode === "anyOf" && types.length > 1) {
23177
+ const compacted = compactEnums(types);
23178
+ if (compacted !== void 0) return compacted;
23179
+ }
23180
+ if (mode === "oneOf") pendingOneOf.set(types, [branches, []]);
23181
+ return { [mode]: types };
23182
+ }
23183
+ }
23184
+ }
23185
+ function getParameterPatterns(parameter, path, seenReferences) {
23186
+ switch (parameter._tag) {
23187
+ case "Reference": {
23188
+ if (!Object.hasOwn(references, parameter.$ref)) throw errorWithPath(`Invalid reference ${parameter.$ref}`, [...path, "$ref"]);
23189
+ compileDefinition(parameter.$ref, path);
23190
+ if (seenReferences.has(parameter.$ref)) return void 0;
23191
+ const next = new Set(seenReferences).add(parameter.$ref);
23192
+ return getParameterPatterns(references[parameter.$ref], ["references", parameter.$ref], next);
23193
+ }
23194
+ case "String": {
23195
+ if (parameter.checks.length === 0) return [];
23196
+ if (parameter.checks.length !== 1 || parameter.checks[0]._tag !== "Filter" || parameter.checks[0].annotations?.toJsonSchema === void 0) return;
23197
+ const dependencies = [];
23198
+ const schema = recur(parameter, path, dependencies);
23199
+ if (!isExact(dependencies)) return void 0;
23200
+ return schema.type === "string" && typeof schema.pattern === "string" && hasOnlyKeywords(schema, "|type|pattern|title|description|default|examples|readOnly|writeOnly|") ? [schema.pattern] : void 0;
23201
+ }
23202
+ case "TemplateLiteral": return parameter.checks.length === 0 ? [`^${parameter.parts.map(getPartPattern).join("")}$`] : void 0;
23203
+ case "Union": {
23204
+ if (parameter.types.length === 0 || parameter.checks.length > 0 || parameter.options?.mode === "oneOf") return;
23205
+ const patterns = [];
23206
+ for (let index = 0; index < parameter.types.length; index++) {
23207
+ const memberPatterns = getParameterPatterns(parameter.types[index], [
23208
+ ...path,
23209
+ "types",
23210
+ index
23211
+ ], seenReferences);
23212
+ if (memberPatterns === void 0) return void 0;
23213
+ if (memberPatterns.length === 0) return [];
23214
+ patterns.push(...memberPatterns);
23215
+ }
23216
+ return patterns;
23217
+ }
23218
+ default: throw errorWithPath("Invalid schema representation document", path);
23219
+ }
23220
+ }
23221
+ }
23222
+ function isJsonSchemaType(input) {
23223
+ return input === "string" || input === "number" || input === "boolean" || input === "array" || input === "object" || input === "null" || input === "integer";
23224
+ }
23225
+ function compactEnums(schemas) {
23226
+ let sharedType = void 0;
23227
+ const values = [];
23228
+ for (const schema of schemas) {
23229
+ if (Object.keys(schema).length !== 2 || schema.type === void 0 || !Array.isArray(schema.enum) || schema.enum.length === 0) return;
23230
+ if (sharedType === void 0) sharedType = schema.type;
23231
+ else if (schema.type !== sharedType) return void 0;
23232
+ for (const value of schema.enum) {
23233
+ if (values.includes(value)) return void 0;
23234
+ values.push(value);
23235
+ }
23236
+ }
23237
+ return {
23238
+ type: sharedType,
23239
+ enum: values
23240
+ };
23241
+ }
23242
+ function getPartPattern(part) {
23243
+ switch (part._tag) {
23244
+ case "Literal": return escape(globalThis.String(part.literal));
23245
+ case "String": return STRING_PATTERN;
23246
+ case "Number": return FINITE_PATTERN;
23247
+ case "TemplateLiteral": return part.parts.map(getPartPattern).join("");
23248
+ case "Union": return `(?:${part.types.map(getPartPattern).join("|")})`;
23249
+ default: throw errorWithPath("Invalid schema representation document", []);
23250
+ }
23251
+ }
23252
+ /** @internal */
23253
+ function toJsonSchemaDocument$1(document, options) {
23254
+ const output = compileJsonSchema([document.representation], [["representation"]], document.references, options);
23255
+ return {
23256
+ dialect: output.dialect,
23257
+ schema: output.schemas[0],
23258
+ definitions: output.definitions
23259
+ };
23260
+ }
23261
+ //#endregion
23262
+ //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/internal/schema/toRepresentation.js
23263
+ const defaultReferencePolicy = ({ identifier }) => identifier;
23264
+ function annotationsField(annotations) {
23265
+ return annotations === void 0 ? void 0 : { annotations };
23266
+ }
23267
+ /** @internal */
23268
+ function toRepresentation(ast, options) {
23269
+ const { references, representations } = toRepresentations([ast], options);
23270
+ return {
23271
+ representation: representations[0],
23272
+ references
23273
+ };
23274
+ }
23275
+ /** @internal */
23276
+ function toRepresentations(asts, options) {
23277
+ const references = {};
23278
+ const referenceOwners = /* @__PURE__ */ new Map();
23279
+ const buildingReferences = /* @__PURE__ */ new Set();
23280
+ const candidates = /* @__PURE__ */ new Map();
23281
+ const visitingCandidates = /* @__PURE__ */ new Set();
23282
+ for (const ast of asts) visit(ast);
23283
+ const referencePolicy = options?.referencePolicy ?? defaultReferencePolicy;
23284
+ for (const candidatesByIdentifier of candidates.values()) for (const candidate of candidatesByIdentifier.values()) {
23285
+ const requestedReference = referencePolicy({
23286
+ ast: candidate.ast,
23287
+ occurrences: candidate.occurrences,
23288
+ identifier: candidate.identifier
23289
+ });
23290
+ if (requestedReference !== void 0) candidate.reference = getReference(requestedReference, candidate, requestedReference === candidate.identifier || !requestedReference.endsWith("_") ? "_" : "");
23291
+ else if (candidate.isRecursive) candidate.reference = getReference(`${candidate.ast._tag}_`, candidate, "");
23292
+ }
23293
+ return {
23294
+ representations: map$8(asts, (ast) => recur(ast)),
23295
+ references
23296
+ };
23297
+ function getReference(prefix, owner, separator = "_") {
23298
+ let candidate = prefix;
23299
+ let suffix = 0;
23300
+ while (referenceOwners.has(candidate)) {
23301
+ if (referenceOwners.get(candidate) === owner) return candidate;
23302
+ candidate = `${prefix}${separator}${++suffix}`;
23303
+ }
23304
+ referenceOwners.set(candidate, owner);
23305
+ return candidate;
23306
+ }
23307
+ function annotateReference(ast, candidate, reference) {
23308
+ const fallback = candidate.fallback;
23309
+ if (fallback !== void 0) return resolveIdentifierFallback(ast) === fallback ? ast : annotate$1(ast, { [IDENTIFIER_FALLBACK_KEY]: fallback });
23310
+ return reference === candidate.identifier ? ast : annotate$1(ast, { identifier: reference });
23311
+ }
23312
+ function makeReference(reference, ast) {
23313
+ if (!Object.hasOwn(references, reference) && !buildingReferences.has(reference)) {
23314
+ buildingReferences.add(reference);
23315
+ const representation = on(ast);
23316
+ buildingReferences.delete(reference);
23317
+ assignProperty(references, reference, representation);
23318
+ }
23319
+ return {
23320
+ _tag: "Reference",
23321
+ $ref: reference
23322
+ };
23323
+ }
23324
+ function getCandidate(input) {
23325
+ const ast = getLastEncoding(input);
23326
+ const owner = getContextOwner(ast);
23327
+ let identifier = resolveIdentifier(ast);
23328
+ const fallback = identifier === void 0 ? (ast !== input ? resolveIdentifier(input) : void 0) ?? resolveIdentifierFallback(ast) : void 0;
23329
+ if (fallback !== void 0) identifier = `${fallback}Encoded`;
23330
+ let candidatesByIdentifier = candidates.get(owner);
23331
+ if (candidatesByIdentifier === void 0) {
23332
+ candidatesByIdentifier = /* @__PURE__ */ new Map();
23333
+ candidates.set(owner, candidatesByIdentifier);
23334
+ }
23335
+ let candidate = candidatesByIdentifier.get(identifier);
23336
+ if (candidate === void 0) {
23337
+ candidate = {
23338
+ ast: owner,
23339
+ identifier,
23340
+ fallback,
23341
+ occurrences: 0,
23342
+ isRecursive: false,
23343
+ reference: void 0
23344
+ };
23345
+ candidatesByIdentifier.set(identifier, candidate);
23346
+ }
23347
+ return candidate;
23348
+ }
23349
+ function visit(input) {
23350
+ const candidate = getCandidate(input);
23351
+ const ast = candidate.ast;
23352
+ candidate.occurrences++;
23353
+ if (visitingCandidates.has(candidate)) {
23354
+ candidate.isRecursive = true;
23355
+ return;
23356
+ }
23357
+ if (candidate.occurrences > 1) return;
23358
+ visitingCandidates.add(candidate);
23359
+ visitChecks(ast.checks);
23360
+ switch (ast._tag) {
23361
+ case "Declaration":
23362
+ case "Arrays":
23363
+ case "Objects":
23364
+ case "Union":
23365
+ ast.recur((child) => {
23366
+ visit(child);
23367
+ return child;
23368
+ });
23369
+ break;
23370
+ case "TemplateLiteral":
23371
+ ast.parts.forEach(visit);
23372
+ break;
23373
+ case "Suspend": visit(ast.thunk());
23374
+ }
23375
+ visitingCandidates.delete(candidate);
23376
+ }
23377
+ function visitChecks(checks) {
23378
+ checks?.forEach((check) => {
23379
+ check.annotations?.representation?.schemas?.forEach((schema) => visit(toType(schema)));
23380
+ if (check._tag === "FilterGroup") visitChecks(check.checks);
23381
+ });
23382
+ }
23383
+ function recur(input) {
23384
+ const candidate = getCandidate(input);
23385
+ const ast = candidate.ast;
23386
+ const reference = candidate.reference;
23387
+ if (reference !== void 0) return makeReference(reference, candidate.identifier === void 0 ? ast : annotateReference(ast, candidate, reference));
23388
+ return on(ast);
23389
+ }
23390
+ function on(ast) {
23391
+ const checks = fromChecks(ast.checks);
23392
+ switch (ast._tag) {
23393
+ case "Declaration": return {
23394
+ _tag: "Declaration",
23395
+ typeParameters: ast.typeParameters.map((ast) => recur(ast)),
23396
+ checks,
23397
+ ...fromDeclarationAnnotations(ast.annotations)
23398
+ };
23399
+ case "Null":
23400
+ case "Undefined":
23401
+ case "Void":
23402
+ case "Never":
23403
+ case "Unknown":
23404
+ case "Any":
23405
+ case "String":
23406
+ case "Boolean":
23407
+ case "Number":
23408
+ case "BigInt":
23409
+ case "Symbol":
23410
+ case "ObjectKeyword": return {
23411
+ _tag: ast._tag,
23412
+ checks,
23413
+ ...annotationsField(ast.annotations)
23414
+ };
23415
+ case "Literal": return {
23416
+ _tag: "Literal",
23417
+ literal: ast.literal,
23418
+ checks,
23419
+ ...annotationsField(ast.annotations)
23420
+ };
23421
+ case "UniqueSymbol": return {
23422
+ _tag: "UniqueSymbol",
23423
+ symbol: ast.symbol,
23424
+ checks,
23425
+ ...annotationsField(ast.annotations)
23426
+ };
23427
+ case "Enum": return {
23428
+ _tag: "Enum",
23429
+ enums: ast.enums,
23430
+ checks,
23431
+ ...annotationsField(ast.annotations)
23432
+ };
23433
+ case "TemplateLiteral": return {
23434
+ _tag: "TemplateLiteral",
23435
+ parts: ast.parts.map((ast) => recur(ast)),
23436
+ checks,
23437
+ ...annotationsField(ast.annotations)
23438
+ };
23439
+ case "Arrays": return {
23440
+ _tag: "Arrays",
23441
+ elements: ast.elements.map((element) => {
23442
+ const projected = getLastEncoding(element);
23443
+ const annotations = projected.context?.annotations;
23444
+ return {
23445
+ isOptional: isOptional(projected),
23446
+ type: recur(element),
23447
+ ...annotationsField(annotations)
23448
+ };
23449
+ }),
23450
+ rest: ast.rest.map((ast) => recur(ast)),
23451
+ checks,
23452
+ ...annotationsField(ast.annotations)
23453
+ };
23454
+ case "Objects": return {
23455
+ _tag: "Objects",
23456
+ propertySignatures: ast.propertySignatures.map((property) => {
23457
+ const projected = getLastEncoding(property.type);
23458
+ const annotations = projected.context?.annotations;
23459
+ return {
23460
+ name: property.name,
23461
+ type: recur(property.type),
23462
+ isOptional: isOptional(projected),
23463
+ isMutable: isMutable(projected),
23464
+ ...annotationsField(annotations)
23465
+ };
23466
+ }),
23467
+ indexSignatures: ast.indexSignatures.map((index) => ({
23468
+ parameter: recur(index.parameter),
23469
+ type: recur(index.type)
23470
+ })),
23471
+ checks,
23472
+ ...annotationsField(ast.annotations)
23473
+ };
23474
+ case "Union": return {
23475
+ _tag: "Union",
23476
+ types: ast.types.map((ast) => recur(ast)),
23477
+ ...ast.options === void 0 ? {} : { options: ast.options },
23478
+ checks,
23479
+ ...annotationsField(ast.annotations)
23480
+ };
23481
+ case "Suspend": return {
23482
+ _tag: "Suspend",
23483
+ checks: [],
23484
+ thunk: recur(ast.thunk()),
23485
+ ...annotationsField(ast.annotations)
23486
+ };
23487
+ }
23488
+ }
23489
+ function fromChecks(checks) {
23490
+ return checks?.map(fromCheck) ?? [];
23491
+ }
23492
+ function fromCheck(check) {
23493
+ switch (check._tag) {
23494
+ case "Filter": return {
23495
+ _tag: "Filter",
23496
+ aborted: check.aborted,
23497
+ ...fromCheckAnnotations(check.annotations)
23498
+ };
23499
+ case "FilterGroup": return {
23500
+ _tag: "FilterGroup",
23501
+ checks: map$8(check.checks, fromCheck),
23502
+ ...fromCheckAnnotations(check.annotations)
23503
+ };
23504
+ }
23505
+ }
23506
+ function fromDeclarationAnnotations(annotations) {
23507
+ if (annotations === void 0) return void 0;
23508
+ const { representation, ...ordinary } = annotations;
23509
+ return {
23510
+ ...representation === void 0 ? void 0 : { representation },
23511
+ ...Object.keys(ordinary).length === 0 ? void 0 : { annotations: ordinary }
23512
+ };
23513
+ }
23514
+ function fromCheckAnnotations(annotations) {
23515
+ if (annotations === void 0) return void 0;
23516
+ const { representation, ...ordinary } = annotations;
23517
+ const projected = representation === void 0 ? void 0 : representation.schemas === void 0 ? representation : {
23518
+ ...representation,
23519
+ schemas: representation.schemas.map((schema) => recur(toType(schema)))
23520
+ };
23521
+ return {
23522
+ ...projected === void 0 ? void 0 : { representation: projected },
23523
+ ...Object.keys(ordinary).length === 0 ? void 0 : { annotations: ordinary }
23524
+ };
23525
+ }
23526
+ }
22308
23527
  //#endregion
22309
23528
  //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/Struct.js
22310
23529
  /**
@@ -23599,6 +24818,81 @@ const TaggedError = (identifier) => {
23599
24818
  };
23600
24819
  };
23601
24820
  /**
24821
+ * Returns a JSON Schema document using draft 2020-12.
24822
+ *
24823
+ * **When to use**
24824
+ *
24825
+ * Use when you need a draft-2020-12 description of the canonical JSON form of a runtime schema.
24826
+ *
24827
+ * **Details**
24828
+ *
24829
+ * The document describes the encoded side of `Schema.toCodecJson(schema)`.
24830
+ * Use that codec to decode JSON inputs. For example, it decodes JSON `null`
24831
+ * to JavaScript `undefined` for a field defined with `Schema.optional(Schema.String)`.
24832
+ * Decoding the same input with the original schema rejects `null`.
24833
+ *
24834
+ * The `options` parameter controls reference extraction and generation details
24835
+ * such as excess properties and synthesized check descriptions; it does not
24836
+ * change the draft target. The reference policy receives canonical JSON
24837
+ * encoded ASTs. By default, anonymous non-recursive candidates remain inline,
24838
+ * while candidates with resolved identifiers become definitions. Declarations
24839
+ * are lowered through their `toCodecJson` or `toCodec` annotation when
24840
+ * available before the representation document is compiled.
24841
+ *
24842
+ * The generated document is intended for preliminary validation. JSON Schema
24843
+ * and Effect checks do not always have identical semantics, so the Effect
24844
+ * decoder remains the final authority. Passing JSON Schema validation does not
24845
+ * guarantee that Effect decoding will succeed.
24846
+ *
24847
+ * **Gotchas**
24848
+ *
24849
+ * JSON Schema generation is best-effort. String length uses Unicode code points
24850
+ * in JSON Schema and UTF-16 code units in Effect. A generated `pattern` cannot
24851
+ * retain JavaScript RegExp flags. Object property checks apply to the original
24852
+ * input in JSON Schema but to the decoded object in Effect. When a `oneOf`
24853
+ * branch contains a known approximation, the compiler emits `anyOf` so that
24854
+ * the approximation cannot create a false rejection. Unions with only exact
24855
+ * branches retain `oneOf`. Custom `toJsonSchema` callbacks return `[schema, true]` for safe,
24856
+ * looser approximations and are responsible for the semantics they declare. When
24857
+ * canonical JSON derivation adds an artificial transformation, checks and
24858
+ * annotations on its source node are not copied to the JSON target, so they do
24859
+ * not appear in the emitted document. Opaque declarations without a structural
24860
+ * codec are represented by an unconstrained JSON Schema. The default
24861
+ * `onExcessProperty: "ignore"` matches the decoder default and leaves
24862
+ * index-signature keys without exact selectors open. In `"error"` mode, the compiler
24863
+ * constrains those keys with `propertyNames` and applies a permissive choice of
24864
+ * candidate index value schemas. The Effect decoder enforces the exact
24865
+ * key-value association.
24866
+ *
24867
+ * **Example** (Decoding JSON with the matching codec)
24868
+ *
24869
+ * ```ts import.meta.vitest
24870
+ * import { Schema } from "effect"
24871
+ *
24872
+ * const schema = Schema.Struct({
24873
+ * name: Schema.optional(Schema.String)
24874
+ * })
24875
+ *
24876
+ * const document = Schema.toJsonSchemaDocument(schema)
24877
+ * const jsonCodec = Schema.toCodecJson(schema)
24878
+ *
24879
+ * Schema.decodeUnknownResult(schema)({ name: null })._tag // => "Failure"
24880
+ * Schema.decodeUnknownSync(jsonCodec)({ name: null }) // => { name: undefined }
24881
+ * Schema.decodeUnknownSync(jsonCodec)({}) // => {}
24882
+ * Schema.encodeSync(jsonCodec)({ name: undefined }) // => { name: null }
24883
+ * ```
24884
+ *
24885
+ * @see {@link toCodecJson} for decoding and encoding the canonical JSON representation
24886
+ * @see {@link SchemaRepresentation.toJsonSchemaDocument} for compiling an existing live representation document
24887
+ *
24888
+ * @stability stable
24889
+ * @category converting
24890
+ * @since 4.0.0
24891
+ */
24892
+ function toJsonSchemaDocument(schema, options) {
24893
+ return toJsonSchemaDocument$1(toRepresentation(toCodecJsonAST(schema.ast), options), options);
24894
+ }
24895
+ /**
23602
24896
  * Converts a schema to the StringTree canonical codec, where every leaf value
23603
24897
  * becomes a string while preserving the original structure.
23604
24898
  *
@@ -30390,7 +31684,7 @@ const runWith = (command, config) => {
30390
31684
  };
30391
31685
  //#endregion
30392
31686
  //#region package.json
30393
- var version = "0.1.0";
31687
+ var version = "0.2.0";
30394
31688
  //#endregion
30395
31689
  //#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/Ref.js
30396
31690
  /**
@@ -33269,14 +34563,35 @@ const VpSection = Struct({
33269
34563
  const MachineSettings = Struct({ clones: optionalKey(NonEmptyString) });
33270
34564
  const FleetConfig = Struct({
33271
34565
  $schema: optionalKey(String$5),
33272
- machines: optionalKey(Record(NonEmptyString, MachineSettings)),
34566
+ shippedAt: optionalKey(String$5),
34567
+ shippedFrom: optionalKey(NonEmptyString),
34568
+ shippedTo: optionalKey(Record(NonEmptyString, String$5)),
34569
+ machines: Record(NonEmptyString, MachineSettings).check(makeFilter((machines) => Object.keys(machines).length > 0, {
34570
+ title: "Fleet",
34571
+ description: "at least one Machine"
34572
+ })),
33273
34573
  repos: optionalKey(ArraySchema(TrackedRepo)),
33274
34574
  vp: optionalKey(VpSection)
33275
34575
  });
33276
34576
  const repoName = (repo) => repo.name ?? repo.origin.replace("/", "-");
34577
+ const SELF_PACKAGE = "fleetfizz";
34578
+ //#endregion
34579
+ //#region ../../packages/config/src/jsonSchema.ts
34580
+ const fleetConfigJsonSchema = () => {
34581
+ const document = toJsonSchemaDocument(FleetConfig, { onExcessProperty: "error" });
34582
+ return {
34583
+ $schema: META_SCHEMA_URI_DRAFT_2020_12,
34584
+ title: "fleetfizz config",
34585
+ ...document.schema,
34586
+ $defs: document.definitions
34587
+ };
34588
+ };
33277
34589
  //#endregion
33278
34590
  //#region ../../packages/config/src/load.ts
33279
34591
  const CONFIG_FILE = "fleet.config.jsonc";
34592
+ const SCHEMA_FILE = "fleet.schema.json";
34593
+ const configDir = (env) => `${env.XDG_CONFIG_HOME || `${env.HOME ?? "~"}/.config`}/fleetfizz`;
34594
+ const configPath = (env) => `${configDir(env)}/${CONFIG_FILE}`;
33280
34595
  var ConfigError = class extends TaggedError()("ConfigError", {
33281
34596
  path: String$5,
33282
34597
  problems: ArraySchema(String$5)
@@ -33285,23 +34600,11 @@ var ConfigError = class extends TaggedError()("ConfigError", {
33285
34600
  return [`${this.path}:`, ...this.problems.map((p) => ` ${p}`)].join("\n");
33286
34601
  }
33287
34602
  };
33288
- var ConfigNotFound = class extends TaggedError()("ConfigNotFound", { start: String$5 }) {
34603
+ var ConfigNotFound = class extends TaggedError()("ConfigNotFound", { path: String$5 }) {
33289
34604
  get message() {
33290
- return `no ${CONFIG_FILE} found in ${this.start} or any parent directory`;
34605
+ return `no config at ${this.path}; write one there (the README has an example) or pass --config`;
33291
34606
  }
33292
34607
  };
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
34608
  const lineOf = (text, offset) => text.slice(0, offset).split("\n").length;
33306
34609
  const semanticProblems = (config) => {
33307
34610
  const problems = [];
@@ -33311,6 +34614,7 @@ const semanticProblems = (config) => {
33311
34614
  if (seen.has(name)) problems.push(`repos: duplicate name "${name}"`);
33312
34615
  seen.add(name);
33313
34616
  }
34617
+ if (config.vp?.globals?.["fleetfizz"] !== void 0) problems.push(`vp.globals: ${SELF_PACKAGE} is pinned by the running version; remove it`);
33314
34618
  return problems;
33315
34619
  };
33316
34620
  const parseConfig = fn("parseConfig")(function* (path, text) {
@@ -33333,10 +34637,48 @@ const parseConfig = fn("parseConfig")(function* (path, text) {
33333
34637
  });
33334
34638
  const formatIssue = makeFormatterDefault();
33335
34639
  const loadConfig = fn("loadConfig")(function* (path) {
33336
- const text = yield* (yield* FileSystem).readFileString(path);
34640
+ const fs = yield* FileSystem;
34641
+ if (!(yield* fs.exists(path))) return yield* new ConfigNotFound({ path });
34642
+ const text = yield* fs.readFileString(path);
33337
34643
  return yield* parseConfig(path, text);
33338
34644
  });
33339
34645
  //#endregion
34646
+ //#region ../../packages/config/src/stamp.ts
34647
+ const insertAfter = (before) => (properties) => {
34648
+ return before.map((key) => properties.indexOf(key)).reduce((a, b) => Math.max(a, b), -1) + 1;
34649
+ };
34650
+ const STAMP_KEYS = [
34651
+ "$schema",
34652
+ "shippedAt",
34653
+ "shippedFrom",
34654
+ "shippedTo"
34655
+ ];
34656
+ const stampText = (text, stamp) => {
34657
+ const options = { formattingOptions: {
34658
+ insertSpaces: true,
34659
+ tabSize: 2
34660
+ } };
34661
+ const set = (current, key, value) => applyEdits(current, modify(current, [key], value, {
34662
+ ...options,
34663
+ getInsertionIndex: insertAfter(STAMP_KEYS.slice(0, STAMP_KEYS.indexOf(key)))
34664
+ }));
34665
+ const previous = parse(text, void 0, { allowTrailingComma: true });
34666
+ const shippedTo = {
34667
+ ...shippedToOf(previous),
34668
+ ...Object.fromEntries(stamp.reached.map((name) => [name, stamp.shippedAt]))
34669
+ };
34670
+ let out = set(text, "shippedAt", stamp.shippedAt);
34671
+ out = set(out, "shippedFrom", stamp.shippedFrom);
34672
+ return set(out, "shippedTo", shippedTo);
34673
+ };
34674
+ const shippedToOf = (config) => {
34675
+ if (typeof config !== "object" || config === null || !("shippedTo" in config)) return {};
34676
+ const value = config.shippedTo;
34677
+ if (typeof value !== "object" || value === null) return {};
34678
+ return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
34679
+ };
34680
+ const isNewer = (remote, local) => remote !== void 0 && (local === void 0 || remote > local);
34681
+ //#endregion
33340
34682
  //#region ../../packages/core/src/range.ts
33341
34683
  const newest$1 = (available) => available.filter((v) => import_semver.prerelease(v) === null).sort(import_semver.rcompare)[0];
33342
34684
  const decideRange = (range, installed, available) => {
@@ -33417,17 +34759,12 @@ const planVp = (input) => {
33417
34759
  skipped: [],
33418
34760
  failures: []
33419
34761
  };
33420
- const vp = input.config.vp;
33421
- if (vp === void 0 || !targets(vp, input.machine)) return out;
33422
34762
  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;
34763
+ const vp = input.config.vp;
34764
+ const targeted = vp !== void 0 && targets(vp, machine);
34765
+ const missing = state.vp === void 0 || state.vp.version === null;
34766
+ if (missing) out.steps.push({ kind: "bootstrap" });
34767
+ const installed = missing ? void 0 : state.vp?.version ?? void 0;
33431
34768
  const decide = (pkg, range, current, shown = pkg) => {
33432
34769
  if (range === void 0) return void 0;
33433
34770
  const decision = decideRange(range, current, input.versions[pkg] ?? []);
@@ -33468,31 +34805,35 @@ const planVp = (input) => {
33468
34805
  }
33469
34806
  }
33470
34807
  };
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
34808
+ const nodeNow = missing ? void 0 : state.node ?? void 0;
34809
+ let nodeTarget;
34810
+ if (targeted) {
34811
+ const vpTarget = decide(VP_PACKAGE, vp.version, installed, "vp");
34812
+ if (vpTarget !== void 0) {
34813
+ if (isHomebrew(state.vp?.path)) out.failures.push({
34814
+ what: "vp",
34815
+ reason: `vp ${installed} was installed with Homebrew; run brew upgrade vite-plus to get ${vpTarget}`
34816
+ });
34817
+ else out.steps.push({
34818
+ kind: "vp",
34819
+ from: installed ?? null,
34820
+ version: vpTarget
34821
+ });
34822
+ }
34823
+ nodeTarget = decide(NODE_PACKAGE, vp.node, nodeNow, "node");
34824
+ if (nodeTarget !== void 0) out.steps.push({
34825
+ kind: "node",
34826
+ from: nodeNow ?? null,
34827
+ version: nodeTarget
33481
34828
  });
33482
34829
  }
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
34830
  const nodeDefault = nodeTarget ?? nodeNow;
33491
- const byName = new Map(state.globals.map((g) => [g.name, g]));
34831
+ const byName = new Map((missing ? [] : state.globals).map((g) => [g.name, g]));
33492
34832
  const installs = [];
33493
- for (const [name, range] of Object.entries(vp.globals ?? {})) {
34833
+ const wanted = Object.entries(targeted ? vp.globals ?? {} : {});
34834
+ for (const [name, range] of [...wanted, [SELF_PACKAGE, input.self]]) {
33494
34835
  const current = byName.get(name);
33495
- const version = decide(name, range, current?.version);
34836
+ const version = name === "fleetfizz" ? decideSelf(current?.version, input.self, out) : decide(name, range, current?.version);
33496
34837
  if (version !== void 0) installs.push(current === void 0 ? {
33497
34838
  name,
33498
34839
  version
@@ -33513,6 +34854,13 @@ const planVp = (input) => {
33513
34854
  });
33514
34855
  return out;
33515
34856
  };
34857
+ const decideSelf = (installed, self, out) => {
34858
+ if (installed === void 0 || import_semver.lt(installed, self)) return self;
34859
+ if (import_semver.gt(installed, self)) out.failures.push({
34860
+ what: SELF_PACKAGE,
34861
+ reason: `runs ${SELF_PACKAGE} ${installed}, newer than this ${self}; upgrade here first`
34862
+ });
34863
+ };
33516
34864
  const planRepos = (input) => {
33517
34865
  const repos = [];
33518
34866
  const failures = [];
@@ -33631,6 +34979,11 @@ const Fact = Union([
33631
34979
  name: String$5,
33632
34980
  record: InstallRecordSchema
33633
34981
  }),
34982
+ Struct({
34983
+ fact: Literal("config"),
34984
+ present: Boolean$4,
34985
+ base64: optionalKey(String$5)
34986
+ }),
33634
34987
  Struct({
33635
34988
  fact: Literal("output"),
33636
34989
  repo: String$5,
@@ -33645,6 +34998,7 @@ const emptyState = {
33645
34998
  records: {},
33646
34999
  outputs: {}
33647
35000
  };
35001
+ const decodeBase64 = (base64) => new TextDecoder().decode(Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)));
33648
35002
  const assembleState = (facts) => {
33649
35003
  let state = emptyState;
33650
35004
  for (const fact of facts) switch (fact.fact) {
@@ -33714,6 +35068,12 @@ const assembleState = (facts) => {
33714
35068
  }
33715
35069
  };
33716
35070
  break;
35071
+ case "config":
35072
+ if (fact.present && fact.base64 !== void 0) state = {
35073
+ ...state,
35074
+ config: decodeBase64(fact.base64)
35075
+ };
35076
+ break;
33717
35077
  case "output": state = {
33718
35078
  ...state,
33719
35079
  outputs: {
@@ -33745,6 +35105,7 @@ const fingerprintFn = [
33745
35105
  "}"
33746
35106
  ].join("\n");
33747
35107
  const RECORDS_DIR = `~/.local/state/fleetfizz/records`;
35108
+ const CONFIG_DIR_SH = "\"${XDG_CONFIG_HOME:-$HOME/.config}/fleetfizz\"";
33748
35109
  const gatherScript = (input) => {
33749
35110
  const lines = [
33750
35111
  prelude,
@@ -33773,6 +35134,7 @@ const gatherScript = (input) => {
33773
35134
  const name = quote(repo.name);
33774
35135
  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
35136
  }
35137
+ 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
35138
  lines.push(`ff_state '{"fact":"end"}'`);
33777
35139
  return lines.join("\n");
33778
35140
  };
@@ -33823,10 +35185,19 @@ const jsonString = encodeSync(fromJsonString(String$5));
33823
35185
  const jsonRecord = encodeSync(fromJsonString(Record(String$5, String$5)));
33824
35186
  const stepId = (repo, step) => `repo:${repo}:${step}`;
33825
35187
  const vpStepId = (step) => step.kind === "reinstall" ? `vp:reinstall:${step.name}` : step.kind === "globals" ? "vp:globals" : `vp:${step.kind}`;
35188
+ const CONFIG_STEP = "config";
35189
+ const VP_INSTALL = [
35190
+ "curl -fsSL https://vite.plus | bash",
35191
+ `ff_env="\${XDG_CONFIG_HOME:-$HOME/.config}/vite-plus/env"`,
35192
+ `{ [ ! -f "$ff_env" ] || . "$ff_env"; }`,
35193
+ `export PATH="\${VP_HOME:-$HOME/.vite-plus}/bin:\${XDG_DATA_HOME:-$HOME/.local/share}/vite-plus/bin:$PATH"`,
35194
+ "{ hash -r 2>/dev/null || true; }"
35195
+ ].join(" && ");
33826
35196
  const vpStep = (step) => {
33827
35197
  const id = vpStepId(step);
33828
35198
  const command = (() => {
33829
35199
  switch (step.kind) {
35200
+ case "bootstrap": return VP_INSTALL;
33830
35201
  case "vp": return `command vp upgrade ${quote(step.version)}`;
33831
35202
  case "node": return `command vp env default ${quote(step.version)}`;
33832
35203
  case "reinstall": return `command vp install -g ${quote(`${step.name}@${step.version}`)}`;
@@ -33835,11 +35206,21 @@ const vpStep = (step) => {
33835
35206
  })();
33836
35207
  return [
33837
35208
  stepLine(id),
33838
- `( ${command} )`,
35209
+ step.kind === "bootstrap" ? command : `( ${command} )`,
33839
35210
  `ff_code=$?`,
33840
35211
  `if [ "$ff_code" -ne 0 ]; then ${doneLine(id, false, "$ff_code")}; else ${doneLine(id, true)}; fi`
33841
35212
  ];
33842
35213
  };
35214
+ const configStep = () => {
35215
+ const dir = CONFIG_DIR_SH;
35216
+ const write = (variable, file) => `printf '%s' "$${variable}" | base64 -d > ${dir}/${quote(`${file}.tmp`)} && mv -f ${dir}/${quote(`${file}.tmp`)} ${dir}/${quote(file)}`;
35217
+ return [
35218
+ stepLine(CONFIG_STEP),
35219
+ `( mkdir -p ${dir} && ${write("ff_config", CONFIG_FILE)} && ${write("ff_schema", SCHEMA_FILE)} )`,
35220
+ `ff_code=$?`,
35221
+ `if [ "$ff_code" -ne 0 ]; then ${doneLine(CONFIG_STEP, false, "$ff_code")}; else ${doneLine(CONFIG_STEP, true)}; fi`
35222
+ ];
35223
+ };
33843
35224
  const writeRecord = (repo) => {
33844
35225
  return [
33845
35226
  `ff_outs=""`,
@@ -33892,16 +35273,30 @@ const repoSteps = (repo, input) => {
33892
35273
  "fi"
33893
35274
  ];
33894
35275
  };
35276
+ const applyStdin = (token, config, schema) => [
35277
+ token ?? "",
35278
+ encodeBase64(config),
35279
+ encodeBase64(schema)
35280
+ ];
35281
+ const encodeBase64 = (text) => {
35282
+ const bytes = new TextEncoder().encode(text);
35283
+ let binary = "";
35284
+ for (const byte of bytes) binary += String.fromCharCode(byte);
35285
+ return btoa(binary);
35286
+ };
33895
35287
  const applyScript = (input) => {
33896
35288
  const lines = [
33897
35289
  prelude,
33898
35290
  fingerprintFn,
33899
35291
  `ff_lit() { printf '%s' "$1"; }`,
33900
35292
  `IFS= read -r ff_token || ff_token=""`,
35293
+ `IFS= read -r ff_config || ff_config=""`,
35294
+ `IFS= read -r ff_schema || ff_schema=""`,
33901
35295
  `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
35296
  `ff_root=${quotePath(input.cloneRoot)}`,
33903
35297
  ...input.cloneRoot.startsWith("~") ? [`mkdir -p "$ff_root"`] : []
33904
35298
  ];
35299
+ lines.push(...configStep());
33905
35300
  for (const step of input.plan.vp) lines.push(...vpStep(step));
33906
35301
  for (const repo of input.plan.repos) lines.push(...repoSteps(repo, input));
33907
35302
  lines.push(doneLine("end", true));
@@ -34135,14 +35530,19 @@ const gather = fn("gather")(function* (target, script, ledger) {
34135
35530
  state
34136
35531
  };
34137
35532
  });
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`])]);
35533
+ const runLabels = (p) => Object.fromEntries([
35534
+ [CONFIG_STEP, "config"],
35535
+ ...p.vp.map((step) => {
35536
+ switch (step.kind) {
35537
+ case "bootstrap": return [vpStepId(step), "install vp"];
35538
+ case "vp": return [vpStepId(step), `vp upgrade ${step.version}`];
35539
+ case "node": return [vpStepId(step), `node default ${step.version}`];
35540
+ case "reinstall": return [vpStepId(step), `${step.name} reinstall`];
35541
+ case "globals": return [vpStepId(step), `install ${step.packages.map((g) => `${g.name}@${g.version}`).join(" ")}`];
35542
+ }
35543
+ }),
35544
+ ...p.repos.map((r) => [stepId(r.name, "install"), `${r.name} install`])
35545
+ ]);
34146
35546
  const apply = fn("apply")(function* (target, p, script, secrets, ledger) {
34147
35547
  const labels = runLabels(p);
34148
35548
  let current;
@@ -34174,16 +35574,20 @@ const apply = fn("apply")(function* (target, p, script, secrets, ledger) {
34174
35574
  case "text": if (line.text.trim() !== "") (current?.output ?? head).push(line.text.trimEnd());
34175
35575
  }
34176
35576
  }), ledger);
34177
- if (run.kind !== "ran") return run.kind;
35577
+ const applied = (outcome) => ({
35578
+ outcome,
35579
+ done
35580
+ });
35581
+ if (run.kind !== "ran") return applied(run.kind);
34178
35582
  if (current !== void 0) {
34179
35583
  yield* ledger.fail(target.name, [`${current.id} did not finish`, ...current.output]);
34180
- return "failed";
35584
+ return applied("failed");
34181
35585
  }
34182
35586
  if (!done.includes("end")) {
34183
35587
  yield* ledger.fail(target.name, ["apply did not finish", ...head]);
34184
- return "failed";
35588
+ return applied("failed");
34185
35589
  }
34186
- if (failed.length > 0 || p.failures.length > 0) return "failed";
35590
+ if (failed.length > 0 || p.failures.length > 0) return applied("failed");
34187
35591
  const installed = p.repos.filter((r) => done.includes(stepId(r.name, "install")));
34188
35592
  const upToDate = p.repos.length - installed.length;
34189
35593
  const parts = [
@@ -34192,7 +35596,7 @@ const apply = fn("apply")(function* (target, p, script, secrets, ledger) {
34192
35596
  ...upToDate > 0 ? [`${upToDate} repo${upToDate === 1 ? "" : "s"} up to date`] : []
34193
35597
  ];
34194
35598
  yield* ledger.line(target.name, "OK", parts.length === 0 ? "up to date" : parts.join(" · "));
34195
- return "ok";
35599
+ return applied("ok");
34196
35600
  });
34197
35601
  //#endregion
34198
35602
  //#region src/questions.ts
@@ -34242,6 +35646,7 @@ const askQuestions = fn("askQuestions")(function* (questions, flags, ledger) {
34242
35646
  const SEP = " · ";
34243
35647
  const vpPart = (step) => {
34244
35648
  switch (step.kind) {
35649
+ case "bootstrap": return "install vp";
34245
35650
  case "vp": return `vp ${step.from ?? "none"} -> ${step.version}`;
34246
35651
  case "node": return `node ${step.from ?? "none"} -> ${step.version}`;
34247
35652
  case "reinstall": return `${step.name} reinstall on Node`;
@@ -34252,10 +35657,14 @@ const repoPart = (repo) => {
34252
35657
  if (repo.decision.run) return `${repo.name} install (${repo.decision.reason})`;
34253
35658
  };
34254
35659
  const summarize = (plan) => {
34255
- const parts = [...plan.vp.map(vpPart), ...plan.repos.map(repoPart).filter((p) => p !== void 0)];
35660
+ const parts = [
35661
+ "config",
35662
+ ...plan.vp.map(vpPart),
35663
+ ...plan.repos.map(repoPart).filter((p) => p !== void 0)
35664
+ ];
34256
35665
  const upToDate = plan.repos.filter((r) => !r.decision.run).length;
34257
35666
  if (upToDate > 0) parts.push(`${upToDate} repo${upToDate === 1 ? "" : "s"} up to date`);
34258
- return parts.length === 0 ? "up to date" : parts.join(SEP);
35667
+ return parts.join(SEP);
34259
35668
  };
34260
35669
  //#endregion
34261
35670
  //#region src/ship.ts
@@ -34265,7 +35674,7 @@ var ShipError = class extends Error {
34265
35674
  const unknownMachines = (config, tailnet, only) => {
34266
35675
  const known = new Set(tailnet.fleet.map((m) => m.name));
34267
35676
  const referenced = [
34268
- ...Object.keys(config.machines ?? {}),
35677
+ ...Object.keys(config.machines),
34269
35678
  ...(config.repos ?? []).flatMap((r) => r.machines ?? []),
34270
35679
  ...config.vp?.machines ?? [],
34271
35680
  ...only
@@ -34282,14 +35691,33 @@ const packagesToResolve = (config) => {
34282
35691
  ];
34283
35692
  };
34284
35693
  const rebuildOf = (flags) => flags.rebuild.length === 0 ? "none" : flags.rebuild.includes("all") ? "all" : flags.rebuild;
35694
+ const staleConfigs = fn("staleConfigs")(function* (local, inputIsLocalFile, gathered, ledger) {
35695
+ const stale = [];
35696
+ for (const { machine, local: isLocal, state } of gathered) {
35697
+ if (isLocal && inputIsLocalFile || state.config === void 0) continue;
35698
+ const parsed = yield* result(parseConfig(machine.name, state.config));
35699
+ if (parsed._tag === "Failure") {
35700
+ yield* ledger.line(machine.name, "INFO", "its config does not parse; it will be overwritten");
35701
+ continue;
35702
+ }
35703
+ const remote = parsed.success;
35704
+ if (isNewer(remote.shippedTo?.[machine.name], local.shippedTo?.[machine.name])) stale.push(`${machine.name} (shipped ${remote.shippedAt} from ${remote.shippedFrom ?? "unknown"})`);
35705
+ }
35706
+ return stale;
35707
+ });
35708
+ const schemaJson = encodeSync(fromJsonString(Unknown, { space: 2 }));
34285
35709
  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);
35710
+ const fs = yield* FileSystem;
35711
+ const path = flags.config ?? configPath(process.env);
35712
+ const config = yield* loadConfig(path);
34289
35713
  const tailnet = yield* readTailnet();
34290
35714
  const unknown = unknownMachines(config, tailnet, flags.only);
34291
35715
  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));
35716
+ const fleet = new Set(Object.keys(config.machines));
35717
+ if (!fleet.has(tailnet.self.name)) return yield* fail$1(new ShipError(`this Machine (${tailnet.self.name}) is not in machines in ${path}`));
35718
+ const outsideFleet = flags.only.filter((name) => !fleet.has(name));
35719
+ if (outsideFleet.length > 0) return yield* fail$1(new ShipError(`not in machines in ${path}: ${outsideFleet.join(", ")}`));
35720
+ const targetsList = tailnet.fleet.filter((m) => fleet.has(m.name) && (flags.only.length === 0 || flags.only.includes(m.name)));
34293
35721
  const ledger = yield* makeLedger(Math.max(5, ...targetsList.map((m) => m.name.length)));
34294
35722
  const count = `${targetsList.length} machine${targetsList.length === 1 ? "" : "s"}`;
34295
35723
  yield* ledger.line("fleet", "INFO", [
@@ -34308,7 +35736,7 @@ const ship = fn("ship")(function* (flags) {
34308
35736
  yield* ledger.line("fleet", "DONE", `${okWord} ${n("ok")} skipped ${n("skipped")} failed ${n("failed")}`);
34309
35737
  if (n("failed") > 0) process.exitCode = 1;
34310
35738
  });
34311
- const cloneRootOf = (machine) => config.machines?.[machine.name]?.clones ?? "~/.local/share/fleetfizz/clones";
35739
+ const cloneRootOf = (machine) => config.machines[machine.name]?.clones ?? "~/.local/share/fleetfizz/clones";
34312
35740
  const gatherRepos = (config.repos ?? []).map((r) => ({
34313
35741
  name: repoName(r),
34314
35742
  outputs: r.outputs ?? []
@@ -34343,6 +35771,17 @@ const ship = fn("ship")(function* (flags) {
34343
35771
  state: result.state
34344
35772
  };
34345
35773
  }), { concurrency: "unbounded" }).pipe(map$5((list) => list.filter((g) => g !== void 0)));
35774
+ const stale = yield* staleConfigs(config, flags.config === void 0, gathered, ledger);
35775
+ if (stale.length > 0) {
35776
+ const what = `newer config on ${stale.join(", ")}`;
35777
+ if (flags.dryRun) yield* ledger.line("fleet", "INFO", `${what}; a Ship would ask before overwriting it`);
35778
+ else if (flags.overwriteConfig) yield* ledger.line("fleet", "INFO", `${what}; overwriting it (--overwrite-config)`);
35779
+ else if (!hasTty()) return yield* fail$1(new ShipError(`${what}; copy it here first, or pass --overwrite-config`));
35780
+ else {
35781
+ yield* ledger.line("fleet", "ASK", what);
35782
+ 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"));
35783
+ }
35784
+ }
34346
35785
  const versions = yield* resolveVersions(packagesToResolve(config));
34347
35786
  const planAll = (cfg, answers) => gathered.map((g) => ({
34348
35787
  ...g,
@@ -34353,21 +35792,22 @@ const ship = fn("ship")(function* (flags) {
34353
35792
  platform: g.machine.platform
34354
35793
  },
34355
35794
  state: g.state,
35795
+ self: flags.version,
34356
35796
  versions,
34357
35797
  rebuild: rebuildOf(flags),
34358
35798
  answers
34359
35799
  })
34360
35800
  }));
34361
- let planned = planAll(config, {});
35801
+ let cfg = config;
35802
+ let planned = planAll(cfg, {});
34362
35803
  const questions = planned.flatMap((g) => g.plan.questions);
34363
35804
  if (questions.length > 0 && !flags.dryRun) {
34364
35805
  const decided = yield* askQuestions(questions, flags, ledger);
34365
- let cfg = config;
34366
35806
  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}`);
35807
+ const range = yield* bumpConfig(path, pkg, decision.installed);
35808
+ yield* ledger.line("fleet", "INFO", `bumped ${pkg} to ${range} in ${path}`);
34369
35809
  }
34370
- if (Object.values(decided).some((d) => d.kind === "bump")) cfg = yield* loadConfig(configPath);
35810
+ if (Object.values(decided).some((d) => d.kind === "bump")) cfg = yield* loadConfig(path);
34371
35811
  const answers = Object.fromEntries(Object.entries(decided).flatMap(([pkg, d]) => d.kind === "bump" ? [] : [[pkg, d.kind]]));
34372
35812
  planned = planAll(cfg, answers);
34373
35813
  }
@@ -34379,14 +35819,21 @@ const ship = fn("ship")(function* (flags) {
34379
35819
  yield* record(machine.name, p.failures.length > 0 ? "failed" : "ok");
34380
35820
  }
34381
35821
  if (flags.dryRun) return yield* finish("planned");
35822
+ const shippedAt = formatIso(yield* now);
35823
+ const original = yield* fs.readFileString(path);
35824
+ const stampWith = (reached) => stampText(original, {
35825
+ shippedAt,
35826
+ shippedFrom: tailnet.self.name,
35827
+ reached: [...reached, ...flags.config === void 0 ? [tailnet.self.name] : []]
35828
+ });
35829
+ const stamped = stampWith(planned.map((g) => g.machine.name));
35830
+ yield* fs.writeFileString(path, stamped);
35831
+ const schema = `${schemaJson(fleetConfigJsonSchema())}\n`;
34382
35832
  const lent = planned.some((g) => g.plan.repos.length > 0) ? yield* readLentToken() : { token: void 0 };
34383
35833
  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
- }
35834
+ const machineNames = [...fleet];
35835
+ const stdin = applyStdin(lent.token, stamped, schema);
35836
+ const delivered = yield* forEach$1(planned, fn("applyOne")(function* ({ machine, local, plan: p }) {
34390
35837
  const script = applyScript({
34391
35838
  plan: p,
34392
35839
  cloneRoot: cloneRootOf(machine),
@@ -34396,12 +35843,14 @@ const ship = fn("ship")(function* (flags) {
34396
35843
  machines: machineNames
34397
35844
  }
34398
35845
  });
34399
- const result = yield* apply({
35846
+ const applied = yield* apply({
34400
35847
  name: machine.name,
34401
35848
  local
34402
- }, p, script, [lent.token ?? ""], ledger);
34403
- yield* record(machine.name, result);
34404
- }), { concurrency: "unbounded" });
35849
+ }, p, script, stdin, ledger);
35850
+ yield* record(machine.name, applied.outcome);
35851
+ return applied.done.includes("config") ? machine.name : void 0;
35852
+ }), { concurrency: "unbounded" }).pipe(map$5((list) => list.filter((name) => name !== void 0)));
35853
+ yield* fs.writeFileString(path, stampWith(delivered));
34405
35854
  yield* finish("ok");
34406
35855
  });
34407
35856
  //#endregion
@@ -34412,14 +35861,17 @@ const shipCommand = make$1("ship", {
34412
35861
  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
35862
  bump: Boolean$1("bump").pipe(withDefault(false), withDescription$1("Answer every above-range question by bumping the config.")),
34414
35863
  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."))
35864
+ overwriteConfig: Boolean$1("overwrite-config").pipe(withDefault(false), withDescription$1("Ship even when another Machine holds a newer config, overwriting it with this one.")),
35865
+ config: String$1("config").pipe(optional, withDescription$1("Config file. Default: $XDG_CONFIG_HOME/fleetfizz/fleet.config.jsonc."))
34416
35866
  }).pipe(withDescription("Ship the config to every reachable Machine."), withHandler((flags) => ship({
34417
35867
  dryRun: flags.dryRun,
34418
35868
  only: flags.only,
34419
35869
  rebuild: flags.rebuild,
34420
35870
  bump: flags.bump,
34421
35871
  enforce: flags.enforce,
34422
- config: flags.config._tag === "Some" ? flags.config.value : void 0
35872
+ overwriteConfig: flags.overwriteConfig,
35873
+ config: flags.config._tag === "Some" ? flags.config.value : void 0,
35874
+ version
34423
35875
  }).pipe(catch_((e) => gen(function* () {
34424
35876
  yield* error(e.message);
34425
35877
  process.exitCode = 1;