@effected/schemastore 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -165,201 +165,6 @@ declare class CanonicalJson {
165
165
  static readonly serialize: (value: unknown, options?: CanonicalJsonOptions | undefined) => Effect.Effect<string, CanonicalJsonError, never>;
166
166
  }
167
167
  //#endregion
168
- //#region src/SchemaVersioning.d.ts
169
- declare const InvalidSchemaVersionError_base: Schema.Class<InvalidSchemaVersionError, Schema.TaggedStruct<"InvalidSchemaVersionError", {
170
- /** The raw input string that failed to parse. */
171
- readonly input: Schema.String;
172
- }>, import("effect/Cause").YieldableError>;
173
- /**
174
- * Indicates that a string is not a valid SchemaStore version label.
175
- *
176
- * Raised by {@link SchemaVersioning.parse}.
177
- *
178
- * @public
179
- */
180
- declare class InvalidSchemaVersionError extends InvalidSchemaVersionError_base {
181
- get message(): string;
182
- }
183
- /**
184
- * A schema version label: a branded string holding a **full three-component
185
- * SemVer** — `major.minor.patch` with an optional prerelease, validated by
186
- * `@effected/semver` itself. Build metadata is rejected (see below).
187
- *
188
- * `1.2` and `1` are NOT accepted, though SchemaStore's own corpus uses such
189
- * labels: requiring all three components makes a label unambiguous to split
190
- * back out of `<name>-<version>.json` or its URL, which is what consumers
191
- * do with it. The file-name convention around the label stays SchemaStore's.
192
- *
193
- * The label round-trips verbatim into file names and catalog `versions`
194
- * keys; ordering parses it directly (see {@link SchemaVersioning.Order}).
195
- *
196
- * @public
197
- */
198
- declare const SchemaVersion: Schema.brand<Schema.String, "SchemaVersion">;
199
- /**
200
- * The type of a validated SchemaStore version label.
201
- *
202
- * @public
203
- */
204
- type SchemaVersion = typeof SchemaVersion.Type;
205
- /**
206
- * The `url`/`versions` half of a catalog entry, as assembled by
207
- * {@link SchemaVersioning.catalogUrls}.
208
- *
209
- * @public
210
- */
211
- interface CatalogUrls {
212
- /** The catalog `url` — the unversioned file, or the latest versioned file. */
213
- readonly url: string;
214
- /**
215
- * The versioned catalog's `versions` map (label → url), inserted — and,
216
- * since a three-component label can never be integer-like, enumerated
217
- * and serialized — in ascending version order.
218
- */
219
- readonly versions?: Readonly<Record<string, string>>;
220
- }
221
- /**
222
- * Both SchemaStore catalog modes as pure derivations: unversioned (a plain
223
- * `name.json` file, `url` only) and versioned (`name-<version>.json` files
224
- * — SchemaStore's own suffix convention — a `versions` map, and `url`
225
- * pointing at the latest version).
226
- *
227
- * Version labels are full three-component SemVer, so ordering is plain
228
- * SemVer precedence: `1.10.0` above `1.9.0`, `2.0.0-beta` below `2.0.0`.
229
- *
230
- * @public
231
- */
232
- declare class SchemaVersioning {
233
- private constructor();
234
- /**
235
- * Parses a version label. Pure and synchronous — the primitive form;
236
- * {@link SchemaVersioning.parse} is the same check behind a span.
237
- */
238
- static parseResult(input: string): Result.Result<SchemaVersion, InvalidSchemaVersionError>;
239
- /**
240
- * Effect form of {@link SchemaVersioning.parseResult}, adding only the
241
- * `SchemaVersioning.parse` span. Defined in terms of the `Result`
242
- * primitive — synchronous callers can use that variant directly.
243
- */
244
- static readonly parse: (input: string) => Effect.Effect<string & import("effect/Brand").Brand<"SchemaVersion">, InvalidSchemaVersionError, never>;
245
- /**
246
- * `Order` instance over version labels: plain SemVer precedence.
247
- * `1.10.0` sorts above `1.9.0` (numeric, not lexical) and `2.0.0-beta`
248
- * below `2.0.0` (prerelease precedence).
249
- */
250
- static readonly Order: Order.Order<SchemaVersion>;
251
- /**
252
- * The highest version label by {@link SchemaVersioning.Order}, or
253
- * `Option.none()` for an empty collection.
254
- */
255
- static latest(versions: ReadonlyArray<SchemaVersion>): Option.Option<SchemaVersion>;
256
- /**
257
- * Derives the schema file name for a catalog name: `name.json`
258
- * unversioned, `name-<version>.json` versioned.
259
- *
260
- * The name must be a simple file base name (no separators, no
261
- * whitespace); anything else is a wiring mistake and throws.
262
- */
263
- static fileName(name: string, version?: SchemaVersion): string;
264
- /**
265
- * The canonical URL a schema file is hosted at: `baseUrl` joined with
266
- * {@link SchemaVersioning.fileName}.
267
- */
268
- static schemaUrl(baseUrl: string, name: string, version?: SchemaVersion): string;
269
- /**
270
- * Assembles the `url`/`versions` half of a catalog entry.
271
- *
272
- * Omitting `versions` selects the unversioned mode (`url` only,
273
- * pointing at the plain `name.json`). Providing them selects the
274
- * versioned mode: the `versions` map carries every label, and `url`
275
- * points at the latest version's file. An **empty** `versions` array is
276
- * a contradiction (versioned mode with no versions) and throws — pass
277
- * `undefined` for the unversioned mode.
278
- *
279
- * Labels are inserted in ascending {@link SchemaVersioning.Order} and
280
- * stay that way on serialization. Requiring three components is what
281
- * buys this: JavaScript enumerates array-index-like keys first, so the
282
- * old grammar's bare-major label (`"2"`) jumped ahead of every dotted
283
- * one regardless of insertion order. No SemVer label is integer-like,
284
- * so that hazard is gone. Deriving ordering from the labels themselves
285
- * (as {@link SchemaVersioning.latest} does) is still the robust read.
286
- */
287
- static catalogUrls(options: {
288
- readonly baseUrl: string;
289
- readonly name: string;
290
- readonly versions?: ReadonlyArray<SchemaVersion>;
291
- }): CatalogUrls;
292
- }
293
- //#endregion
294
- //#region src/CatalogEntry.d.ts
295
- declare const CatalogLintFinding_base: Schema.Class<CatalogLintFinding, Schema.Struct<{
296
- /** Which hygiene check fired. */
297
- readonly check: Schema.Literals<readonly ["GenericFileMatch", "ComplexFileMatch"]>;
298
- /** The `fileMatch` pattern the finding is about. */
299
- readonly pattern: Schema.String;
300
- /** Human-readable explanation with the SchemaStore rationale. */
301
- readonly message: Schema.String;
302
- }>, {}>;
303
- /**
304
- * A fileMatch hygiene finding: a value in a lint report, not an error —
305
- * SchemaStore reviewers reject entries over these, so surfacing them
306
- * locally is the point, but a warned entry is still a valid entry.
307
- *
308
- * @public
309
- */
310
- declare class CatalogLintFinding extends CatalogLintFinding_base {}
311
- declare const CatalogEntry_base: Schema.Class<CatalogEntry, Schema.Struct<{
312
- /** The schema's display name in the catalog. */
313
- readonly name: Schema.String;
314
- /** The catalog description. */
315
- readonly description: Schema.String;
316
- /** Glob patterns editors match files against. */
317
- readonly fileMatch: Schema.$Array<Schema.String>;
318
- /** The schema URL — the unversioned file, or the latest version. */
319
- readonly url: Schema.String;
320
- /**
321
- * Versioned mode only: label → schema URL. Inserted ascending, but key
322
- * order is not a contract — bare-major labels enumerate first (see
323
- * `SchemaVersioning.catalogUrls`); derive ordering from the labels.
324
- */
325
- readonly versions: Schema.optionalKey<Schema.$Record<Schema.String, Schema.String>>;
326
- }>, {}>;
327
- /**
328
- * A SchemaStore `catalog.json` entry: the class is the schema, so decoding
329
- * an existing entry and encoding one for submission are the same artifact.
330
- * `versions` is present only for versioned catalogs
331
- * ({@link SchemaVersioning.catalogUrls} assembles both modes).
332
- *
333
- * @public
334
- */
335
- declare class CatalogEntry extends CatalogEntry_base {
336
- /**
337
- * Assembles an entry from a catalog identity plus
338
- * {@link SchemaVersioning.catalogUrls}' inputs: pass `versions` for the
339
- * versioned mode (the `versions` map and latest-pointing `url` are
340
- * derived), omit it for the unversioned mode.
341
- */
342
- static assemble(options: {
343
- readonly name: string;
344
- readonly description: string;
345
- readonly fileMatch: ReadonlyArray<string>;
346
- readonly baseUrl: string;
347
- readonly fileBaseName?: string;
348
- readonly versions?: ReadonlyArray<SchemaVersion>;
349
- }): CatalogEntry;
350
- /**
351
- * The fileMatch hygiene lint over this entry's patterns — pure shape
352
- * analysis (no glob engine): generic patterns SchemaStore rejects and
353
- * complex constructs it asks contributors to expand.
354
- */
355
- lint(): ReadonlyArray<CatalogLintFinding>;
356
- /**
357
- * {@link CatalogEntry.lint} over a bare pattern list, for callers
358
- * checking patterns before an entry exists.
359
- */
360
- static lintFileMatch(patterns: ReadonlyArray<string>): ReadonlyArray<CatalogLintFinding>;
361
- }
362
- //#endregion
363
168
  //#region src/DocumentDiff.d.ts
364
169
  /**
365
170
  * What differs between two schema documents:
@@ -571,7 +376,456 @@ declare class StoreDocument extends StoreDocument_base {
571
376
  * {@link CanonicalJson.serializeResult} — one serializer, so the
572
377
  * document and any consumer-serialized value cannot drift.
573
378
  */
574
- serializeResult(options?: CanonicalJsonOptions): Result.Result<string, CanonicalJsonError>;
379
+ serializeResult(options?: CanonicalJsonOptions): Result.Result<string, CanonicalJsonError>;
380
+ }
381
+ //#endregion
382
+ //#region src/SchemaFile.d.ts
383
+ declare const SchemaFileReadError_base: Schema.Class<SchemaFileReadError, Schema.TaggedStruct<"SchemaFileReadError", {
384
+ /** The path that could not be read. */
385
+ readonly path: Schema.String;
386
+ /** The underlying filesystem failure, preserved structurally. */
387
+ readonly cause: Schema.Defect;
388
+ }>, import("effect/Cause").YieldableError>;
389
+ /**
390
+ * Indicates that a schema file could not be read from the filesystem (a
391
+ * filesystem error other than not-found).
392
+ *
393
+ * @public
394
+ */
395
+ declare class SchemaFileReadError extends SchemaFileReadError_base {
396
+ get message(): string;
397
+ }
398
+ declare const SchemaFileNotFoundError_base: Schema.Class<SchemaFileNotFoundError, Schema.TaggedStruct<"SchemaFileNotFoundError", {
399
+ /** The path where the schema file was expected. */
400
+ readonly path: Schema.String;
401
+ }>, import("effect/Cause").YieldableError>;
402
+ /**
403
+ * Indicates that no schema file exists at the expected path. Carries its
404
+ * own tag for `catchTag` routing.
405
+ *
406
+ * @public
407
+ */
408
+ declare class SchemaFileNotFoundError extends SchemaFileNotFoundError_base {
409
+ get message(): string;
410
+ }
411
+ declare const SchemaFileWriteError_base: Schema.Class<SchemaFileWriteError, Schema.TaggedStruct<"SchemaFileWriteError", {
412
+ /** The path that could not be written. */
413
+ readonly path: Schema.String;
414
+ /** The underlying filesystem failure, preserved structurally. */
415
+ readonly cause: Schema.Defect;
416
+ }>, import("effect/Cause").YieldableError>;
417
+ /**
418
+ * Indicates that a schema file could not be written to the filesystem.
419
+ * Narrowed to the filesystem failure only — a serialization failure
420
+ * surfaces as its own `CanonicalJsonError`, never wrapped here.
421
+ *
422
+ * @public
423
+ */
424
+ declare class SchemaFileWriteError extends SchemaFileWriteError_base {
425
+ get message(): string;
426
+ }
427
+ /**
428
+ * What {@link SchemaFileShape.write} did to the filesystem: `"written"`
429
+ * when it wrote, `"unchanged"` when it left the file alone — reported as a
430
+ * value so the caller decides what to surface, never a log.
431
+ *
432
+ * @public
433
+ */
434
+ type WriteOutcome = "written" | "unchanged";
435
+ /**
436
+ * How the document being written relates to what was already on disk:
437
+ * {@link SchemaChange} plus `"created"` for a file that did not exist, so
438
+ * there was nothing to compare against.
439
+ *
440
+ * @public
441
+ */
442
+ type WriteChange = SchemaChange | "created";
443
+ /**
444
+ * The result of {@link SchemaFileShape.write}: what happened to the file,
445
+ * and what the difference MEANT.
446
+ *
447
+ * The two are independent on purpose. `change` answers the versioning
448
+ * question — `"annotations"` means only prose and editor affordances moved,
449
+ * so the document replaces its predecessor transparently and needs no new
450
+ * {@link SchemaVersioning} version, while `"contract"` means an assertion
451
+ * keyword moved and a consumer's document valid yesterday may be invalid
452
+ * today. `outcome` answers only whether bytes were written, which under
453
+ * `compare: "bytes"` can be `"written"` even when `change` is `"none"`.
454
+ *
455
+ * @public
456
+ */
457
+ interface WriteResult {
458
+ /**
459
+ * Whether the file was written. **This is the authoritative answer to
460
+ * "did the filesystem get touched"** — always, under either `compare`
461
+ * mode. Do not infer it from `change`: under `compare: "bytes"` a
462
+ * `change` of `"none"` still writes.
463
+ */
464
+ readonly outcome: WriteOutcome;
465
+ /** What differs between the previous content and the new document. */
466
+ readonly change: WriteChange;
467
+ }
468
+ /**
469
+ * The result of {@link SchemaFileShape.check}: the same two answers
470
+ * {@link WriteResult} carries, for a call that touched nothing.
471
+ *
472
+ * `wouldWrite` is `outcome`'s counterpart — it honors `compare`, so it
473
+ * answers "would `write` do anything with these same options", which
474
+ * `change` alone cannot under `compare: "bytes"`.
475
+ *
476
+ * @public
477
+ */
478
+ interface CheckResult {
479
+ /** Whether a `write` with the same options would touch the file. */
480
+ readonly wouldWrite: boolean;
481
+ /** What differs between the on-disk content and the new document. */
482
+ readonly change: WriteChange;
483
+ }
484
+ /**
485
+ * Options for {@link SchemaFileShape.write} and
486
+ * {@link SchemaFileShape.check}: the {@link CanonicalJsonOptions} the
487
+ * document serializes under, plus how `write` decides whether to touch the
488
+ * file.
489
+ *
490
+ * @public
491
+ */
492
+ interface SchemaWriteOptions extends CanonicalJsonOptions {
493
+ /**
494
+ * How `write` decides the file needs rewriting:
495
+ *
496
+ * - `"value"` (the default) — compare the parsed content. Immune to any
497
+ * other tool that reformats the file, which is the common case: a repo
498
+ * whose pre-commit hook runs Biome or Prettier over `*.json` would
499
+ * otherwise see every run rewrite the file forever, because the
500
+ * formatter's bytes never match `CanonicalJson`'s.
501
+ * - `"bytes"` — compare the exact text, so the file on disk is only ever
502
+ * `CanonicalJson`'s own output. Choose this when the emitted bytes are
503
+ * themselves the artifact and no other tool is allowed to touch them.
504
+ *
505
+ * `change` in the {@link WriteResult} is classified by content either
506
+ * way; this option decides only whether a byte-level difference is
507
+ * enough to rewrite.
508
+ */
509
+ readonly compare?: "bytes" | "value";
510
+ }
511
+ /**
512
+ * The shape of the {@link SchemaFile} service — the value produced by
513
+ * {@link SchemaFile.make} and carried by its layer.
514
+ *
515
+ * @public
516
+ */
517
+ interface SchemaFileShape {
518
+ /**
519
+ * Read a schema file's exact text (the drift-test read side: compare it
520
+ * against `StoreDocument.serializeResult`). Fails with
521
+ * `SchemaFileNotFoundError` (ENOENT) or `SchemaFileReadError` (other
522
+ * filesystem errors).
523
+ */
524
+ readonly read: (path: string) => Effect.Effect<string, SchemaFileReadError | SchemaFileNotFoundError>;
525
+ /**
526
+ * Serialize a document to canonical JSON and write it **only if the
527
+ * on-disk content differs** (a missing file counts as different),
528
+ * creating parent directories as needed. Answers a {@link WriteResult}
529
+ * as a value: what happened to the file, and what the difference meant.
530
+ * Fails with a `CanonicalJsonError` (the document does not serialize),
531
+ * `SchemaFileReadError` (the existing content could not be read for
532
+ * comparison) or `SchemaFileWriteError` (the filesystem write failed).
533
+ */
534
+ readonly write: (path: string, document: StoreDocument, options?: SchemaWriteOptions) => Effect.Effect<WriteResult, CanonicalJsonError | SchemaFileReadError | SchemaFileWriteError>;
535
+ /**
536
+ * The same comparison {@link SchemaFileShape.write} makes, **without
537
+ * touching the filesystem** — the drift-check half of the pair, for a
538
+ * CI job that must assert a committed schema is current rather than
539
+ * regenerate it.
540
+ *
541
+ * Answers both questions the writer answers: `change` classifies the
542
+ * content (immune to a formatter having reflowed the committed file),
543
+ * and `wouldWrite` honors `compare`, so it agrees with `write` under
544
+ * either mode. Checking drift is `change`; predicting the writer is
545
+ * `wouldWrite`.
546
+ */
547
+ readonly check: (path: string, document: StoreDocument, options?: SchemaWriteOptions) => Effect.Effect<CheckResult, CanonicalJsonError | SchemaFileReadError>;
548
+ }
549
+ declare const SchemaFile_base: Context.ServiceClass<SchemaFile, "@effected/schemastore/SchemaFile", SchemaFileShape>;
550
+ /**
551
+ * Reads and writes emitted schema documents over core `FileSystem` /
552
+ * `Path` — the package's one IO surface. The layer requires those
553
+ * services; provide `@effect/platform-node`'s `NodeFileSystem` / `NodePath`
554
+ * (or a bun equivalent) at the application boundary.
555
+ *
556
+ * `write` is write-if-changed, and by default compares **content**: an
557
+ * unchanged document never touches the file even if another tool has
558
+ * reformatted it, so a generator committed to a repo whose pre-commit hook
559
+ * formats JSON does not churn on every run. It also reports what the
560
+ * difference meant — `"annotations"` (prose only, replaces its predecessor
561
+ * transparently) versus `"contract"` (an assertion moved, so a new
562
+ * `SchemaVersioning` version is warranted). `check` makes the same
563
+ * comparison without writing, which is what a CI drift job wants.
564
+ *
565
+ * @example
566
+ * ```ts
567
+ * import { SchemaFile, StoreDocument } from "@effected/schemastore";
568
+ * import { NodeFileSystem, NodePath } from "@effect/platform-node";
569
+ * import { Effect, Layer, Schema } from "effect";
570
+ *
571
+ * const program = Effect.gen(function* () {
572
+ * const files = yield* SchemaFile;
573
+ * const document = yield* StoreDocument.fromSchema(Schema.Struct({ name: Schema.String }), {
574
+ * $id: "https://example.com/config.schema.json",
575
+ * });
576
+ * return yield* files.write("schemas/config.schema.json", document);
577
+ * }).pipe(
578
+ * Effect.provide(SchemaFile.layer),
579
+ * Effect.provide(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer)),
580
+ * );
581
+ * ```
582
+ *
583
+ * @public
584
+ */
585
+ declare class SchemaFile extends SchemaFile_base {
586
+ /** Build the service implementation from `FileSystem` / `Path` in context; use {@link SchemaFile.layer} to provide it. */
587
+ static readonly make: Effect.Effect<SchemaFileShape, never, FileSystem.FileSystem | Path.Path>;
588
+ /**
589
+ * The live layer. Requires core `FileSystem` / `Path`, provided by the
590
+ * consumer's platform implementation at the edge.
591
+ */
592
+ static readonly layer: Layer.Layer<SchemaFile, never, FileSystem.FileSystem | Path.Path>;
593
+ }
594
+ //#endregion
595
+ //#region src/SchemaVersioning.d.ts
596
+ declare const InvalidSchemaVersionError_base: Schema.Class<InvalidSchemaVersionError, Schema.TaggedStruct<"InvalidSchemaVersionError", {
597
+ /** The raw input string that failed to parse. */
598
+ readonly input: Schema.String;
599
+ }>, import("effect/Cause").YieldableError>;
600
+ /**
601
+ * Indicates that a string is not a valid SchemaStore version label.
602
+ *
603
+ * Raised by {@link SchemaVersioning.parse}.
604
+ *
605
+ * @public
606
+ */
607
+ declare class InvalidSchemaVersionError extends InvalidSchemaVersionError_base {
608
+ get message(): string;
609
+ }
610
+ /**
611
+ * A schema version label: a branded string holding a **full three-component
612
+ * SemVer** — `major.minor.patch` with an optional prerelease, validated by
613
+ * `@effected/semver` itself. Build metadata is rejected (see below).
614
+ *
615
+ * `1.2` and `1` are NOT accepted, though SchemaStore's own corpus uses such
616
+ * labels: requiring all three components makes a label unambiguous to split
617
+ * back out of `<name>-<version>.json` or its URL, which is what consumers
618
+ * do with it. The file-name convention around the label stays SchemaStore's.
619
+ *
620
+ * The label round-trips verbatim into file names and catalog `versions`
621
+ * keys; ordering parses it directly (see {@link SchemaVersioning.Order}).
622
+ *
623
+ * @public
624
+ */
625
+ declare const SchemaVersion: Schema.brand<Schema.String, "SchemaVersion">;
626
+ /**
627
+ * The type of a validated SchemaStore version label.
628
+ *
629
+ * @public
630
+ */
631
+ type SchemaVersion = typeof SchemaVersion.Type;
632
+ /**
633
+ * The `url`/`versions` half of a catalog entry, as assembled by
634
+ * {@link SchemaVersioning.catalogUrls}.
635
+ *
636
+ * @public
637
+ */
638
+ interface CatalogUrls {
639
+ /** The catalog `url` — the unversioned file, or the latest versioned file. */
640
+ readonly url: string;
641
+ /**
642
+ * The versioned catalog's `versions` map (label → url), inserted — and,
643
+ * since a three-component label can never be integer-like, enumerated
644
+ * and serialized — in ascending version order.
645
+ */
646
+ readonly versions?: Readonly<Record<string, string>>;
647
+ }
648
+ /**
649
+ * Both SchemaStore catalog modes as pure derivations: unversioned (a plain
650
+ * `name.json` file, `url` only) and versioned (`name-<version>.json` files
651
+ * — SchemaStore's own suffix convention — a `versions` map, and `url`
652
+ * pointing at the latest version).
653
+ *
654
+ * Version labels are full three-component SemVer, so ordering is plain
655
+ * SemVer precedence: `1.10.0` above `1.9.0`, `2.0.0-beta` below `2.0.0`.
656
+ *
657
+ * @public
658
+ */
659
+ declare class SchemaVersioning {
660
+ private constructor();
661
+ /**
662
+ * Parses a version label. Pure and synchronous — the primitive form;
663
+ * {@link SchemaVersioning.parse} is the same check behind a span.
664
+ */
665
+ static parseResult(input: string): Result.Result<SchemaVersion, InvalidSchemaVersionError>;
666
+ /**
667
+ * Effect form of {@link SchemaVersioning.parseResult}, adding only the
668
+ * `SchemaVersioning.parse` span. Defined in terms of the `Result`
669
+ * primitive — synchronous callers can use that variant directly.
670
+ */
671
+ static readonly parse: (input: string) => Effect.Effect<string & import("effect/Brand").Brand<"SchemaVersion">, InvalidSchemaVersionError, never>;
672
+ /**
673
+ * `Order` instance over version labels: plain SemVer precedence.
674
+ * `1.10.0` sorts above `1.9.0` (numeric, not lexical) and `2.0.0-beta`
675
+ * below `2.0.0` (prerelease precedence).
676
+ */
677
+ static readonly Order: Order.Order<SchemaVersion>;
678
+ /**
679
+ * The highest version label by {@link SchemaVersioning.Order}, or
680
+ * `Option.none()` for an empty collection.
681
+ */
682
+ static latest(versions: ReadonlyArray<SchemaVersion>): Option.Option<SchemaVersion>;
683
+ /**
684
+ * Whether a label names a pinned, published document — i.e. it is NOT a
685
+ * prerelease. SemVer §9 makes a prerelease's own instability explicit, so
686
+ * a contract change inside one is not a break for anyone.
687
+ *
688
+ * ONE predicate consumed by two policies so they cannot drift:
689
+ * `SchemaPipeline`'s `"block-versioned"` guard (a pinned versioned target
690
+ * refuses an in-place contract change) and {@link SchemaVersioning.next}
691
+ * (a non-pinned label is not bumped). If the two used different tests, a
692
+ * caller could be refused a write AND told to keep the same label — a
693
+ * deadlock.
694
+ */
695
+ static isPinned(version: SchemaVersion): boolean;
696
+ /**
697
+ * The version label a change classification calls for. Pure and
698
+ * synchronous; total over validated labels — a non-label input is a wiring
699
+ * bug and dies as a defect, the same as {@link SchemaVersioning.Order}.
700
+ *
701
+ * - `change !== "contract"` (`"none"`, `"annotations"`, `"created"`) →
702
+ * `current`. A created file has no predecessor to break; an annotation
703
+ * change is transparently replaceable (`DocumentDiff`).
704
+ * - `current` is not pinned (a prerelease) → `current`. A prerelease
705
+ * declares its own instability; the pipeline's `"block-versioned"`
706
+ * policy uses the same {@link SchemaVersioning.isPinned}, so the gate
707
+ * and the bump agree.
708
+ * - `major === 0` → MINOR bump (`0.4.0` → `0.5.0`): on the 0.x line MINOR
709
+ * is the breaking axis.
710
+ * - otherwise → MAJOR bump (`5.0.0` → `6.0.0`).
711
+ *
712
+ * The bump's job is to be strictly greater and conspicuous, NOT to encode
713
+ * SemVer compatibility: `DocumentDiff` cannot tell an added optional
714
+ * property from a removed required one, so every contract change reads as
715
+ * breaking. Each label is its own file and URL, so an over-bump costs a
716
+ * file; an under-bump would overwrite a pinned document. `next` never
717
+ * introduces a prerelease from a stable input.
718
+ *
719
+ * A component that would be bumped past `Number.MAX_SAFE_INTEGER` cannot be
720
+ * represented, and throws this module's explicit invariant `Error` naming
721
+ * the label and the ceiling rather than `SemVer.make`'s bare schema failure.
722
+ */
723
+ static next(current: SchemaVersion, change: WriteChange): SchemaVersion;
724
+ /**
725
+ * Derives the schema file name for a catalog name: `name.json`
726
+ * unversioned, `name-<version>.json` versioned.
727
+ *
728
+ * The name must be a simple file base name (no separators, no
729
+ * whitespace); anything else is a wiring mistake and throws.
730
+ */
731
+ static fileName(name: string, version?: SchemaVersion): string;
732
+ /**
733
+ * The canonical URL a schema file is hosted at: `baseUrl` joined with
734
+ * {@link SchemaVersioning.fileName}.
735
+ */
736
+ static schemaUrl(baseUrl: string, name: string, version?: SchemaVersion): string;
737
+ /**
738
+ * Assembles the `url`/`versions` half of a catalog entry.
739
+ *
740
+ * Omitting `versions` selects the unversioned mode (`url` only,
741
+ * pointing at the plain `name.json`). Providing them selects the
742
+ * versioned mode: the `versions` map carries every label, and `url`
743
+ * points at the latest version's file. An **empty** `versions` array is
744
+ * a contradiction (versioned mode with no versions) and throws — pass
745
+ * `undefined` for the unversioned mode.
746
+ *
747
+ * Labels are inserted in ascending {@link SchemaVersioning.Order} and
748
+ * stay that way on serialization. Requiring three components is what
749
+ * buys this: JavaScript enumerates array-index-like keys first, so the
750
+ * old grammar's bare-major label (`"2"`) jumped ahead of every dotted
751
+ * one regardless of insertion order. No SemVer label is integer-like,
752
+ * so that hazard is gone. Deriving ordering from the labels themselves
753
+ * (as {@link SchemaVersioning.latest} does) is still the robust read.
754
+ */
755
+ static catalogUrls(options: {
756
+ readonly baseUrl: string;
757
+ readonly name: string;
758
+ readonly versions?: ReadonlyArray<SchemaVersion>;
759
+ }): CatalogUrls;
760
+ }
761
+ //#endregion
762
+ //#region src/CatalogEntry.d.ts
763
+ declare const CatalogLintFinding_base: Schema.Class<CatalogLintFinding, Schema.Struct<{
764
+ /** Which hygiene check fired. */
765
+ readonly check: Schema.Literals<readonly ["GenericFileMatch", "ComplexFileMatch"]>;
766
+ /** The `fileMatch` pattern the finding is about. */
767
+ readonly pattern: Schema.String;
768
+ /** Human-readable explanation with the SchemaStore rationale. */
769
+ readonly message: Schema.String;
770
+ }>, {}>;
771
+ /**
772
+ * A fileMatch hygiene finding: a value in a lint report, not an error —
773
+ * SchemaStore reviewers reject entries over these, so surfacing them
774
+ * locally is the point, but a warned entry is still a valid entry.
775
+ *
776
+ * @public
777
+ */
778
+ declare class CatalogLintFinding extends CatalogLintFinding_base {}
779
+ declare const CatalogEntry_base: Schema.Class<CatalogEntry, Schema.Struct<{
780
+ /** The schema's display name in the catalog. */
781
+ readonly name: Schema.String;
782
+ /** The catalog description. */
783
+ readonly description: Schema.String;
784
+ /** Glob patterns editors match files against. */
785
+ readonly fileMatch: Schema.$Array<Schema.String>;
786
+ /** The schema URL — the unversioned file, or the latest version. */
787
+ readonly url: Schema.String;
788
+ /**
789
+ * Versioned mode only: label → schema URL. Inserted ascending, but key
790
+ * order is not a contract — bare-major labels enumerate first (see
791
+ * `SchemaVersioning.catalogUrls`); derive ordering from the labels.
792
+ */
793
+ readonly versions: Schema.optionalKey<Schema.$Record<Schema.String, Schema.String>>;
794
+ }>, {}>;
795
+ /**
796
+ * A SchemaStore `catalog.json` entry: the class is the schema, so decoding
797
+ * an existing entry and encoding one for submission are the same artifact.
798
+ * `versions` is present only for versioned catalogs
799
+ * ({@link SchemaVersioning.catalogUrls} assembles both modes).
800
+ *
801
+ * @public
802
+ */
803
+ declare class CatalogEntry extends CatalogEntry_base {
804
+ /**
805
+ * Assembles an entry from a catalog identity plus
806
+ * {@link SchemaVersioning.catalogUrls}' inputs: pass `versions` for the
807
+ * versioned mode (the `versions` map and latest-pointing `url` are
808
+ * derived), omit it for the unversioned mode.
809
+ */
810
+ static assemble(options: {
811
+ readonly name: string;
812
+ readonly description: string;
813
+ readonly fileMatch: ReadonlyArray<string>;
814
+ readonly baseUrl: string;
815
+ readonly fileBaseName?: string;
816
+ readonly versions?: ReadonlyArray<SchemaVersion>;
817
+ }): CatalogEntry;
818
+ /**
819
+ * The fileMatch hygiene lint over this entry's patterns — pure shape
820
+ * analysis (no glob engine): generic patterns SchemaStore rejects and
821
+ * complex constructs it asks contributors to expand.
822
+ */
823
+ lint(): ReadonlyArray<CatalogLintFinding>;
824
+ /**
825
+ * {@link CatalogEntry.lint} over a bare pattern list, for callers
826
+ * checking patterns before an entry exists.
827
+ */
828
+ static lintFileMatch(patterns: ReadonlyArray<string>): ReadonlyArray<CatalogLintFinding>;
575
829
  }
576
830
  //#endregion
577
831
  //#region src/DocumentLint.d.ts
@@ -603,7 +857,7 @@ declare class DocumentLintFinding extends DocumentLintFinding_base {}
603
857
  * `#/definitions/...` pointer, is a warning).
604
858
  * - `UnknownKeyword` — no keyword outside Draft-07 plus the declared
605
859
  * non-standard families ({@link KeywordFamilies}: `x-taplo*`, `x-tombi-*`,
606
- * `x-intellij-*` and the vscode set), which ajv strict mode would reject.
860
+ * `x-intellij-*`, `x-ai-*` and the vscode set), which ajv strict mode would reject.
607
861
  * - `DescriptionWithoutUrl` — advisory: SchemaStore's description
608
862
  * convention ends the root description with a docs URL line.
609
863
  *
@@ -623,10 +877,12 @@ declare class DocumentLint {
623
877
  //#endregion
624
878
  //#region src/KeywordFamilies.d.ts
625
879
  /**
626
- * The one owner of the declared non-standard keyword families the
627
- * language-server keyword sets SchemaStore's CONTRIBUTING enumerates as
628
- * legitimately consumed by editor toolchains, which ajv strict mode would
629
- * otherwise reject:
880
+ * The one owner of the declared non-standard keyword families, in two
881
+ * groups.
882
+ *
883
+ * **Upstream language-server families** — mirrored from SchemaStore's
884
+ * CONTRIBUTING, the keyword sets legitimately consumed by editor
885
+ * toolchains that ajv strict mode would otherwise reject:
630
886
  *
631
887
  * - **vscode-json-languageservice** (exact names): `allowTrailingCommas`,
632
888
  * `defaultSnippets`, `enumDescriptions`, `markdownDescription`,
@@ -639,6 +895,43 @@ declare class DocumentLint {
639
895
  * - **IntelliJ**: the `x-intellij-` prefix (`x-intellij-language-injection`,
640
896
  * `x-intellij-html-description`, `x-intellij-enum-metadata`).
641
897
  *
898
+ * **The house machine-annotation family** — `x-ai-` (WITH the trailing
899
+ * dash; bare `x-ai` and a look-alike prefix like `x-aida-foo` are NOT
900
+ * declared), owned by this package rather than mirrored from anywhere:
901
+ *
902
+ * - It is a NAMESPACE, not a vocabulary — any `x-ai-*` key is declared, and
903
+ * this package does not enumerate specific keys.
904
+ * - The key itself must be one ajv can register: after the prefix, only
905
+ * `[A-Za-z0-9_$:-]` (ajv holds a keyword name to
906
+ * `/^[a-z_$][a-z0-9_$:-]*$/i`). A dot, a space, a slash, an `@`, a `+` or
907
+ * any non-ASCII character makes the engine gate reject the whole document
908
+ * — as a root-pathed `ValidationFinding`, not an error.
909
+ * - A value under a declared `x-ai-*` key must be JSON — `CanonicalJson`
910
+ * fails typed (`NonJsonValueError`) on anything else, the same as
911
+ * every other emitted value.
912
+ * - The one recommended, non-binding key is `x-ai-hint`: a string carrying
913
+ * an instruction to a machine reader about the annotated value.
914
+ * - `x-ai-example` is deliberately NOT recommended. Draft-07's own
915
+ * `examples` keyword already exists, is carried by the assembly, and
916
+ * classifies as a CONTRACT change in `DocumentDiff`; an `x-ai-*`
917
+ * key classifies as ANNOTATIONS. The two would be two example channels
918
+ * with opposite version semantics.
919
+ * - A declared-family value must not contain an `$id` — or a repeated
920
+ * `$anchor` — at ANY depth, not merely as its own top-level key: ajv's
921
+ * reference collection walks unknown keywords looking for them, so a
922
+ * colliding one buried anywhere inside an annotation payload fails the
923
+ * compile, surfacing as a blocking root-pathed `ValidationFinding` rather
924
+ * than a silent no-op. An empty-string `$id` collides too — it resolves
925
+ * to the root id.
926
+ * - No upstream tool sanctions `x-ai-`: a document carrying it that is
927
+ * submitted to schemastore.org needs the corresponding entry added to
928
+ * that repo's own validation config. Until then it is intended for
929
+ * self-hosted publication.
930
+ * - `DocumentDiff` classifies a delta confined to `x-ai-*` keys as
931
+ * `"annotations"`, so adopting the family on an already-published
932
+ * versioned document rewrites that file in place — correct, because
933
+ * annotations are transparently replaceable.
934
+ *
642
935
  * Both consumers of the registry route through {@link KeywordFamilies.isDeclared}:
643
936
  * `DocumentLint`'s `UnknownKeyword` check (a declared key is not flagged) and
644
937
  * `AnnotationCarriers` (only declared keys are re-grafted after the Draft-07
@@ -647,7 +940,7 @@ declare class DocumentLint {
647
940
  /**
648
941
  * The declared non-standard keyword families as one predicate: the
649
942
  * vscode-json-languageservice set by exact name, plus the `x-taplo`,
650
- * `x-tombi-` and `x-intellij-` prefixes.
943
+ * `x-tombi-`, `x-intellij-` and `x-ai-` prefixes.
651
944
  *
652
945
  * @public
653
946
  */
@@ -656,224 +949,12 @@ declare class KeywordFamilies {
656
949
  /**
657
950
  * Whether `key` belongs to a declared non-standard keyword family.
658
951
  * Draft-07's own keywords are a separate vocabulary — this predicate
659
- * answers only for the language-server extension families.
952
+ * answers only for the language-server extension families and the
953
+ * house `x-ai-` machine-annotation namespace.
660
954
  */
661
955
  static isDeclared(key: string): boolean;
662
956
  }
663
957
  //#endregion
664
- //#region src/SchemaFile.d.ts
665
- declare const SchemaFileReadError_base: Schema.Class<SchemaFileReadError, Schema.TaggedStruct<"SchemaFileReadError", {
666
- /** The path that could not be read. */
667
- readonly path: Schema.String;
668
- /** The underlying filesystem failure, preserved structurally. */
669
- readonly cause: Schema.Defect;
670
- }>, import("effect/Cause").YieldableError>;
671
- /**
672
- * Indicates that a schema file could not be read from the filesystem (a
673
- * filesystem error other than not-found).
674
- *
675
- * @public
676
- */
677
- declare class SchemaFileReadError extends SchemaFileReadError_base {
678
- get message(): string;
679
- }
680
- declare const SchemaFileNotFoundError_base: Schema.Class<SchemaFileNotFoundError, Schema.TaggedStruct<"SchemaFileNotFoundError", {
681
- /** The path where the schema file was expected. */
682
- readonly path: Schema.String;
683
- }>, import("effect/Cause").YieldableError>;
684
- /**
685
- * Indicates that no schema file exists at the expected path. Carries its
686
- * own tag for `catchTag` routing.
687
- *
688
- * @public
689
- */
690
- declare class SchemaFileNotFoundError extends SchemaFileNotFoundError_base {
691
- get message(): string;
692
- }
693
- declare const SchemaFileWriteError_base: Schema.Class<SchemaFileWriteError, Schema.TaggedStruct<"SchemaFileWriteError", {
694
- /** The path that could not be written. */
695
- readonly path: Schema.String;
696
- /** The underlying filesystem failure, preserved structurally. */
697
- readonly cause: Schema.Defect;
698
- }>, import("effect/Cause").YieldableError>;
699
- /**
700
- * Indicates that a schema file could not be written to the filesystem.
701
- * Narrowed to the filesystem failure only — a serialization failure
702
- * surfaces as its own `CanonicalJsonError`, never wrapped here.
703
- *
704
- * @public
705
- */
706
- declare class SchemaFileWriteError extends SchemaFileWriteError_base {
707
- get message(): string;
708
- }
709
- /**
710
- * What {@link SchemaFileShape.write} did to the filesystem: `"written"`
711
- * when it wrote, `"unchanged"` when it left the file alone — reported as a
712
- * value so the caller decides what to surface, never a log.
713
- *
714
- * @public
715
- */
716
- type WriteOutcome = "written" | "unchanged";
717
- /**
718
- * How the document being written relates to what was already on disk:
719
- * {@link SchemaChange} plus `"created"` for a file that did not exist, so
720
- * there was nothing to compare against.
721
- *
722
- * @public
723
- */
724
- type WriteChange = SchemaChange | "created";
725
- /**
726
- * The result of {@link SchemaFileShape.write}: what happened to the file,
727
- * and what the difference MEANT.
728
- *
729
- * The two are independent on purpose. `change` answers the versioning
730
- * question — `"annotations"` means only prose and editor affordances moved,
731
- * so the document replaces its predecessor transparently and needs no new
732
- * {@link SchemaVersioning} version, while `"contract"` means an assertion
733
- * keyword moved and a consumer's document valid yesterday may be invalid
734
- * today. `outcome` answers only whether bytes were written, which under
735
- * `compare: "bytes"` can be `"written"` even when `change` is `"none"`.
736
- *
737
- * @public
738
- */
739
- interface WriteResult {
740
- /**
741
- * Whether the file was written. **This is the authoritative answer to
742
- * "did the filesystem get touched"** — always, under either `compare`
743
- * mode. Do not infer it from `change`: under `compare: "bytes"` a
744
- * `change` of `"none"` still writes.
745
- */
746
- readonly outcome: WriteOutcome;
747
- /** What differs between the previous content and the new document. */
748
- readonly change: WriteChange;
749
- }
750
- /**
751
- * The result of {@link SchemaFileShape.check}: the same two answers
752
- * {@link WriteResult} carries, for a call that touched nothing.
753
- *
754
- * `wouldWrite` is `outcome`'s counterpart — it honors `compare`, so it
755
- * answers "would `write` do anything with these same options", which
756
- * `change` alone cannot under `compare: "bytes"`.
757
- *
758
- * @public
759
- */
760
- interface CheckResult {
761
- /** Whether a `write` with the same options would touch the file. */
762
- readonly wouldWrite: boolean;
763
- /** What differs between the on-disk content and the new document. */
764
- readonly change: WriteChange;
765
- }
766
- /**
767
- * Options for {@link SchemaFileShape.write} and
768
- * {@link SchemaFileShape.check}: the {@link CanonicalJsonOptions} the
769
- * document serializes under, plus how `write` decides whether to touch the
770
- * file.
771
- *
772
- * @public
773
- */
774
- interface SchemaWriteOptions extends CanonicalJsonOptions {
775
- /**
776
- * How `write` decides the file needs rewriting:
777
- *
778
- * - `"value"` (the default) — compare the parsed content. Immune to any
779
- * other tool that reformats the file, which is the common case: a repo
780
- * whose pre-commit hook runs Biome or Prettier over `*.json` would
781
- * otherwise see every run rewrite the file forever, because the
782
- * formatter's bytes never match `CanonicalJson`'s.
783
- * - `"bytes"` — compare the exact text, so the file on disk is only ever
784
- * `CanonicalJson`'s own output. Choose this when the emitted bytes are
785
- * themselves the artifact and no other tool is allowed to touch them.
786
- *
787
- * `change` in the {@link WriteResult} is classified by content either
788
- * way; this option decides only whether a byte-level difference is
789
- * enough to rewrite.
790
- */
791
- readonly compare?: "bytes" | "value";
792
- }
793
- /**
794
- * The shape of the {@link SchemaFile} service — the value produced by
795
- * {@link SchemaFile.make} and carried by its layer.
796
- *
797
- * @public
798
- */
799
- interface SchemaFileShape {
800
- /**
801
- * Read a schema file's exact text (the drift-test read side: compare it
802
- * against `StoreDocument.serializeResult`). Fails with
803
- * `SchemaFileNotFoundError` (ENOENT) or `SchemaFileReadError` (other
804
- * filesystem errors).
805
- */
806
- readonly read: (path: string) => Effect.Effect<string, SchemaFileReadError | SchemaFileNotFoundError>;
807
- /**
808
- * Serialize a document to canonical JSON and write it **only if the
809
- * on-disk content differs** (a missing file counts as different),
810
- * creating parent directories as needed. Answers a {@link WriteResult}
811
- * as a value: what happened to the file, and what the difference meant.
812
- * Fails with a `CanonicalJsonError` (the document does not serialize),
813
- * `SchemaFileReadError` (the existing content could not be read for
814
- * comparison) or `SchemaFileWriteError` (the filesystem write failed).
815
- */
816
- readonly write: (path: string, document: StoreDocument, options?: SchemaWriteOptions) => Effect.Effect<WriteResult, CanonicalJsonError | SchemaFileReadError | SchemaFileWriteError>;
817
- /**
818
- * The same comparison {@link SchemaFileShape.write} makes, **without
819
- * touching the filesystem** — the drift-check half of the pair, for a
820
- * CI job that must assert a committed schema is current rather than
821
- * regenerate it.
822
- *
823
- * Answers both questions the writer answers: `change` classifies the
824
- * content (immune to a formatter having reflowed the committed file),
825
- * and `wouldWrite` honors `compare`, so it agrees with `write` under
826
- * either mode. Checking drift is `change`; predicting the writer is
827
- * `wouldWrite`.
828
- */
829
- readonly check: (path: string, document: StoreDocument, options?: SchemaWriteOptions) => Effect.Effect<CheckResult, CanonicalJsonError | SchemaFileReadError>;
830
- }
831
- declare const SchemaFile_base: Context.ServiceClass<SchemaFile, "@effected/schemastore/SchemaFile", SchemaFileShape>;
832
- /**
833
- * Reads and writes emitted schema documents over core `FileSystem` /
834
- * `Path` — the package's one IO surface. The layer requires those
835
- * services; provide `@effect/platform-node`'s `NodeFileSystem` / `NodePath`
836
- * (or a bun equivalent) at the application boundary.
837
- *
838
- * `write` is write-if-changed, and by default compares **content**: an
839
- * unchanged document never touches the file even if another tool has
840
- * reformatted it, so a generator committed to a repo whose pre-commit hook
841
- * formats JSON does not churn on every run. It also reports what the
842
- * difference meant — `"annotations"` (prose only, replaces its predecessor
843
- * transparently) versus `"contract"` (an assertion moved, so a new
844
- * `SchemaVersioning` version is warranted). `check` makes the same
845
- * comparison without writing, which is what a CI drift job wants.
846
- *
847
- * @example
848
- * ```ts
849
- * import { SchemaFile, StoreDocument } from "@effected/schemastore";
850
- * import { NodeFileSystem, NodePath } from "@effect/platform-node";
851
- * import { Effect, Layer, Schema } from "effect";
852
- *
853
- * const program = Effect.gen(function* () {
854
- * const files = yield* SchemaFile;
855
- * const document = yield* StoreDocument.fromSchema(Schema.Struct({ name: Schema.String }), {
856
- * $id: "https://example.com/config.schema.json",
857
- * });
858
- * return yield* files.write("schemas/config.schema.json", document);
859
- * }).pipe(
860
- * Effect.provide(SchemaFile.layer),
861
- * Effect.provide(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer)),
862
- * );
863
- * ```
864
- *
865
- * @public
866
- */
867
- declare class SchemaFile extends SchemaFile_base {
868
- /** Build the service implementation from `FileSystem` / `Path` in context; use {@link SchemaFile.layer} to provide it. */
869
- static readonly make: Effect.Effect<SchemaFileShape, never, FileSystem.FileSystem | Path.Path>;
870
- /**
871
- * The live layer. Requires core `FileSystem` / `Path`, provided by the
872
- * consumer's platform implementation at the edge.
873
- */
874
- static readonly layer: Layer.Layer<SchemaFile, never, FileSystem.FileSystem | Path.Path>;
875
- }
876
- //#endregion
877
958
  //#region src/SchemaTarget.d.ts
878
959
  /**
879
960
  * A single schema publication target: an Effect Schema source paired with
@@ -903,7 +984,20 @@ interface SchemaTarget {
903
984
  readonly name?: string;
904
985
  /** The destination path the document is written to (`SchemaFile`). */
905
986
  readonly path: string;
906
- /** The version label, for versioned catalog mode. Omit for unversioned. */
987
+ /**
988
+ * The version label, for versioned catalog mode. Omit for unversioned.
989
+ *
990
+ * It carries a second meaning the catalog does not: presence of a
991
+ * **pinned** label (one with no prerelease) declares that consumers pin
992
+ * this document's URL, so `SchemaPipeline.run` refuses to rewrite it in
993
+ * place when its validation contract changes — bump `version`, `$id` and
994
+ * `path` together instead, or pass `contractChanges: "allow"`. That is
995
+ * only coherent when the version participates in `path`
996
+ * (`schemas/<version>/<name>-<version>.json`): a versioned target at a
997
+ * fixed path compares the same file forever, and bumping `version` does
998
+ * not move it. A prerelease label declares its own instability and is
999
+ * rewritten in place.
1000
+ */
907
1001
  readonly version?: SchemaVersion;
908
1002
  }
909
1003
  /**
@@ -1045,9 +1139,12 @@ declare class SchemaValidator extends SchemaValidator_base {
1045
1139
  * `validate` checks the document against the Draft-07 meta-schema and
1046
1140
  * then compiles it, reporting BOTH as {@link ValidationFinding} values:
1047
1141
  * meta-schema failures keep ajv's structured `instancePath` and
1048
- * `keyword`, while a strict-mode rejection (which ajv raises by throwing
1049
- * at compile time) becomes a root-pathed finding. The error channel
1050
- * stays reserved for the engine failing as a mechanism.
1142
+ * `keyword`, while a rejection ajv raises by *throwing* becomes a
1143
+ * root-pathed finding both a strict-mode compile failure and a
1144
+ * declared keyword whose NAME ajv's own grammar
1145
+ * (`/^[a-z_$][a-z0-9_$:-]*$/i`) refuses, such as an `x-ai-*` key
1146
+ * carrying a dot or a space. The error channel stays reserved for the
1147
+ * engine failing as a mechanism.
1051
1148
  *
1052
1149
  * `strict` defaults to `true` — SchemaStore's gate. Each call builds its
1053
1150
  * own ajv instance, so documents sharing an `$id` never collide.
@@ -1121,6 +1218,63 @@ declare const SchemaGateError_base: Schema.Class<SchemaGateError, Schema.TaggedS
1121
1218
  declare class SchemaGateError extends SchemaGateError_base {
1122
1219
  get message(): string;
1123
1220
  }
1221
+ /**
1222
+ * How {@link SchemaPipeline.run} treats a target whose document would change
1223
+ * its validation contract.
1224
+ *
1225
+ * - `"block-versioned"` (the default) — a target carrying a PINNED `version`
1226
+ * ({@link SchemaVersioning.isPinned}) is a published, URL-pinned document:
1227
+ * a `"contract"` change fails with {@link SchemaContractChangeError} BEFORE
1228
+ * any write. A target with no `version`, or with a prerelease label, is a
1229
+ * document that replaces its predecessor in place and is rewritten as
1230
+ * before.
1231
+ * - `"allow"` — classify and report only, never refuse: the pre-guard
1232
+ * behaviour. Also the sanctioned REPAIR path for a published file whose
1233
+ * text no longer parses — `SchemaFile` classifies unparseable text as
1234
+ * `"contract"` so it stays regenerable, and under the default that
1235
+ * classification is refused.
1236
+ *
1237
+ * The policy reads `change`, which is content-classified under either
1238
+ * `write.compare` mode, so `"bytes"` neither strengthens nor weakens it.
1239
+ *
1240
+ * The policy is only coherent when the version participates in `path`
1241
+ * (`schemas/<version>/<name>-<version>.json`): a versioned target at a fixed
1242
+ * path compares the same file forever, and bumping `version` does not move
1243
+ * it.
1244
+ *
1245
+ * @public
1246
+ */
1247
+ type ContractChangePolicy = "block-versioned" | "allow";
1248
+ declare const ContractChangeTarget_base: Schema.Class<ContractChangeTarget, Schema.Struct<{
1249
+ /** The target's `$id`. */
1250
+ readonly $id: Schema.String;
1251
+ /** The path the document would have been written to. */
1252
+ readonly path: Schema.String;
1253
+ /** The target's pinned label. */
1254
+ readonly version: Schema.brand<Schema.String, "SchemaVersion">;
1255
+ /** The label to publish under instead — {@link SchemaVersioning.next} of `version`. */
1256
+ readonly nextVersion: Schema.brand<Schema.String, "SchemaVersion">;
1257
+ }>, {}>;
1258
+ /**
1259
+ * One published document whose validation contract would change.
1260
+ *
1261
+ * @public
1262
+ */
1263
+ declare class ContractChangeTarget extends ContractChangeTarget_base {}
1264
+ declare const SchemaContractChangeError_base: Schema.Class<SchemaContractChangeError, Schema.TaggedStruct<"SchemaContractChangeError", {
1265
+ /** Every published target whose contract would change, in target order. */
1266
+ readonly targets: Schema.$Array<typeof ContractChangeTarget>;
1267
+ }>, import("effect/Cause").YieldableError>;
1268
+ /**
1269
+ * Indicates that at least one published target's contract changed under the
1270
+ * active {@link ContractChangePolicy}. Raised BEFORE any target is written
1271
+ * and total over the targets, so two broken documents surface in one run.
1272
+ *
1273
+ * @public
1274
+ */
1275
+ declare class SchemaContractChangeError extends SchemaContractChangeError_base {
1276
+ get message(): string;
1277
+ }
1124
1278
  /**
1125
1279
  * What the pipeline did with one target.
1126
1280
  *
@@ -1161,6 +1315,17 @@ interface PipelineCheckResult {
1161
1315
  * never mistaken for clean drift.
1162
1316
  */
1163
1317
  readonly blocked: boolean;
1318
+ /**
1319
+ * Whether the {@link ContractChangePolicy} would refuse this write.
1320
+ *
1321
+ * Read it side by side with {@link PipelineCheckResult.blocked}: `blocked`
1322
+ * answers "would findings block a run under `blocking`", while
1323
+ * `contractBlocked` answers "would the contract policy refuse this write
1324
+ * under `contractChanges`". A drift test that reads `contractBlocked` can
1325
+ * print the right remedy — "regenerate" is wrong advice for a target the
1326
+ * generator will refuse; the fix is a version bump.
1327
+ */
1328
+ readonly contractBlocked: boolean;
1164
1329
  /** What differs between the on-disk content and the new document. */
1165
1330
  readonly change: WriteChange;
1166
1331
  /** Every finding, blocking or not. */
@@ -1195,6 +1360,14 @@ interface SchemaPipelineOptions {
1195
1360
  * which a schema can genuinely exceed.
1196
1361
  */
1197
1362
  readonly blocking?: (finding: PipelineFinding) => boolean;
1363
+ /**
1364
+ * How {@link SchemaPipeline.run} treats a target whose document would
1365
+ * change its validation contract. Defaults to `"block-versioned"`: a
1366
+ * target carrying a pinned `version` is refused rather than rewritten in
1367
+ * place. Pass `"allow"` to classify and report only — including to repair
1368
+ * a published file whose on-disk text no longer parses.
1369
+ */
1370
+ readonly contractChanges?: ContractChangePolicy;
1198
1371
  /** Passed through to `SchemaValidator.validate`. */
1199
1372
  readonly validator?: SchemaValidatorOptions;
1200
1373
  /** Passed through to `SchemaFile.write` / `SchemaFile.check`. */
@@ -1237,13 +1410,45 @@ declare class SchemaPipeline {
1237
1410
  private constructor();
1238
1411
  /**
1239
1412
  * Run every target: build its document, gather both gates' findings,
1240
- * fail with a {@link SchemaGateError} if any block, and write otherwise.
1413
+ * fail with a {@link SchemaGateError} if any block, refuse a published
1414
+ * target whose contract changed, and write otherwise.
1241
1415
  *
1242
- * Targets are processed in order and the run stops at the first gate
1243
- * failure a document that fails its gate is not written, and neither
1244
- * are the targets after it.
1416
+ * **The run writes nothing unless every target passes both gates.** It
1417
+ * is two-phase: every target is generated, gated and (for a target the
1418
+ * {@link ContractChangePolicy} guards) compared against its predecessor
1419
+ * before any file is touched, and only then are the held documents
1420
+ * written in target order. A gate failure on the third target therefore
1421
+ * leaves the first two unwritten.
1422
+ *
1423
+ * Phase 2 itself is sequential and **not transactional**: a filesystem or
1424
+ * serialization failure part-way through it leaves the targets already
1425
+ * written in place, with no rollback. The guarantee is "no writes unless
1426
+ * every gate passes", not "every write or none".
1427
+ *
1428
+ * The gates run in a fixed precedence: {@link SchemaGateError} is
1429
+ * fail-fast on the first blocked target, because a document the engine
1430
+ * rejects would never be written under any contract policy, so its
1431
+ * classification is noise. {@link SchemaContractChangeError} is total
1432
+ * over the remaining targets, so two published documents whose contracts
1433
+ * moved surface in one run.
1434
+ *
1435
+ * @example
1436
+ * ```ts
1437
+ * import { SchemaContractChangeError, SchemaPipeline } from "@effected/schemastore";
1438
+ * import { Effect } from "effect";
1439
+ *
1440
+ * declare const targets: Parameters<typeof SchemaPipeline.run>[0];
1441
+ *
1442
+ * const program = SchemaPipeline.run(targets).pipe(
1443
+ * Effect.catchTag("SchemaContractChangeError", (error: SchemaContractChangeError) =>
1444
+ * Effect.succeed(
1445
+ * error.targets.map((target) => `${target.$id}: ${target.version} -> ${target.nextVersion}`),
1446
+ * ),
1447
+ * ),
1448
+ * );
1449
+ * ```
1245
1450
  */
1246
- static run(targets: ReadonlyArray<SchemaTarget>, options?: SchemaPipelineOptions): Effect.Effect<ReadonlyArray<PipelineResult>, SchemaGateError | SchemaConversionError | SchemaValidatorError | CanonicalJsonError | SchemaFileReadError | SchemaFileWriteError, SchemaFile | SchemaValidator>;
1451
+ static run(targets: ReadonlyArray<SchemaTarget>, options?: SchemaPipelineOptions): Effect.Effect<ReadonlyArray<PipelineResult>, SchemaGateError | SchemaContractChangeError | SchemaConversionError | SchemaValidatorError | CanonicalJsonError | SchemaFileReadError | SchemaFileWriteError, SchemaFile | SchemaValidator>;
1247
1452
  /**
1248
1453
  * The same walk with **no writes** — the drift-check counterpart, for a
1249
1454
  * CI job asserting the committed schemas are current.
@@ -1264,7 +1469,7 @@ declare class SchemaPipeline {
1264
1469
  * result directly — so a caller with one target does not index into an
1265
1470
  * array and prove to the type system that element zero exists.
1266
1471
  */
1267
- static runOne(target: SchemaTarget, options?: SchemaPipelineOptions): Effect.Effect<PipelineResult, SchemaGateError | SchemaConversionError | SchemaValidatorError | CanonicalJsonError | SchemaFileReadError | SchemaFileWriteError, SchemaFile | SchemaValidator>;
1472
+ static runOne(target: SchemaTarget, options?: SchemaPipelineOptions): Effect.Effect<PipelineResult, SchemaGateError | SchemaContractChangeError | SchemaConversionError | SchemaValidatorError | CanonicalJsonError | SchemaFileReadError | SchemaFileWriteError, SchemaFile | SchemaValidator>;
1268
1473
  /**
1269
1474
  * {@link SchemaPipeline.check} for a single target, answering its one
1270
1475
  * result directly.
@@ -1272,5 +1477,5 @@ declare class SchemaPipeline {
1272
1477
  static checkOne(target: SchemaTarget, options?: SchemaPipelineOptions): Effect.Effect<PipelineCheckResult, SchemaConversionError | SchemaValidatorError | CanonicalJsonError | SchemaFileReadError, SchemaFile | SchemaValidator>;
1273
1478
  }
1274
1479
  //#endregion
1275
- export { AnnotationCarriers, CanonicalJson, type CanonicalJsonError, type CanonicalJsonOptions, CarrierDepthExceededError, CatalogEntry, CatalogLintFinding, type CatalogUrls, type CheckResult, DRAFT_07_META_SCHEMA, DocumentDiff, DocumentLint, DocumentLintFinding, InvalidSchemaVersionError, JsonDepthExceededError, KeywordFamilies, NonJsonValueError, type PipelineCheckResult, PipelineFinding, type PipelineResult, type SchemaChange, SchemaConversionError, SchemaFile, SchemaFileNotFoundError, SchemaFileReadError, type SchemaFileShape, SchemaFileWriteError, SchemaGateError, SchemaPipeline, type SchemaPipelineOptions, SchemaTarget, SchemaValidator, SchemaValidatorError, type SchemaValidatorOptions, type SchemaValidatorShape, SchemaVersion, SchemaVersioning, type SchemaWriteOptions, StoreDocument, type StoreDocumentOptions, ValidationFinding, type WriteChange, type WriteOutcome, type WriteResult };
1480
+ export { AnnotationCarriers, CanonicalJson, type CanonicalJsonError, type CanonicalJsonOptions, CarrierDepthExceededError, CatalogEntry, CatalogLintFinding, type CatalogUrls, type CheckResult, type ContractChangePolicy, ContractChangeTarget, DRAFT_07_META_SCHEMA, DocumentDiff, DocumentLint, DocumentLintFinding, InvalidSchemaVersionError, JsonDepthExceededError, KeywordFamilies, NonJsonValueError, type PipelineCheckResult, PipelineFinding, type PipelineResult, type SchemaChange, SchemaContractChangeError, SchemaConversionError, SchemaFile, SchemaFileNotFoundError, SchemaFileReadError, type SchemaFileShape, SchemaFileWriteError, SchemaGateError, SchemaPipeline, type SchemaPipelineOptions, SchemaTarget, SchemaValidator, SchemaValidatorError, type SchemaValidatorOptions, type SchemaValidatorShape, SchemaVersion, SchemaVersioning, type SchemaWriteOptions, StoreDocument, type StoreDocumentOptions, ValidationFinding, type WriteChange, type WriteOutcome, type WriteResult };
1276
1481
  //# sourceMappingURL=index.d.ts.map