@hawkeyexl/manni 0.3.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -177,9 +177,9 @@ interface FieldError {
177
177
  /**
178
178
  * The file `line` and `col` refer to, when it is not the result's own file.
179
179
  *
180
- * Set only for a value a sidecar manifest supplied (proposal 0037), spelled
180
+ * Set only for a value a manifest supplied (proposal 0037), spelled
181
181
  * the way the run spells file labels. Absent means the document itself,
182
- * which is every violation that existed before sidecars. The finding's
182
+ * which is every violation that existed before external metadata. The finding's
183
183
  * subject file stays `ValidationResult.file`: that is its baseline identity
184
184
  * and its SARIF location, and this names where the *value* was written.
185
185
  */
@@ -257,6 +257,49 @@ interface FingerprintContext {
257
257
  runBase?: string;
258
258
  }
259
259
 
260
+ /** One external-metadata manifest (proposals 0037, 0038, 0039) joined to a collection. */
261
+ interface ExternalMetadataConfig {
262
+ file: string;
263
+ keys: string[];
264
+ tokenEnv?: string;
265
+ join?: string;
266
+ }
267
+ /** One named document set, as declared under the top-level `collections:` key. */
268
+ interface CollectionConfig {
269
+ name: string;
270
+ paths: string[];
271
+ exclude: string[];
272
+ externalMetadata: ExternalMetadataConfig[];
273
+ /** Where the collection is published; `manni a11y check` seeds from it. */
274
+ url?: string;
275
+ }
276
+ /**
277
+ * Whether a URL may name an external-metadata manifest: `https://` anywhere,
278
+ * or `http://` on a loopback host only, where plaintext leaks nothing.
279
+ * Userinfo is refused because the URL is printed in every diagnostic and a
280
+ * secret in it would print too. Returns the reason it may not, or `null`.
281
+ */
282
+ declare function externalMetadataUrlProblem(url: string): string | null;
283
+ /**
284
+ * Parse the top-level `collections:` value. Errors are built with `toError`,
285
+ * so each tool reports a bad family file in its own error class.
286
+ */
287
+ declare function parseCollections(raw: unknown, source: string, toError: (message: string) => Error): CollectionConfig[];
288
+ /**
289
+ * The collections a run covers: every one when `names` is undefined, else the
290
+ * named ones in **declaration** order, however they were named on the command
291
+ * line. Repeats collapse; an unknown name is an error, because a typo that
292
+ * silently narrowed the run to nothing would read as a clean pass.
293
+ */
294
+ declare function selectCollections(collections: readonly CollectionConfig[], names: readonly string[] | undefined, source: string, toError: (message: string) => Error): CollectionConfig[];
295
+ /**
296
+ * Is this file a member of the collection? `relPath` is posix and already
297
+ * relative to the config file's directory, so this is arithmetic on a string:
298
+ * no `stat`, nothing that can fail, and nothing that costs per file. A path
299
+ * that climbed out of the config directory, and stdin, are members of nothing.
300
+ */
301
+ declare function isMember(collection: CollectionConfig, relPath: string): boolean;
302
+
260
303
  interface SchemaOverride {
261
304
  /**
262
305
  * The glob, or globs, this override governs. A file matches when **any** of
@@ -268,20 +311,23 @@ interface SchemaOverride {
268
311
  * caller may hand `runValidate` a config object it built itself, and that
269
312
  * caller's bare string must keep working. Read it through `overrideGlobs`
270
313
  * rather than directly — that is the one place the two shapes collapse.
314
+ *
315
+ * Optional since proposal 0041: an entry carries **exactly one** of `files`
316
+ * or `collection`.
271
317
  */
272
- files: string | string[];
318
+ files?: string | string[];
273
319
  schemas: string[];
274
320
  /**
275
- * Optional collection name (proposal 0027): the override group becomes a
276
- * SQL view of that name over the `docs` projection, holding exactly the
277
- * files this override won schema resolution for. Any string is legal —
278
- * view names are quoted identifiers — except the refusals `parseConfig`
279
- * makes: a duplicate, `docs` (the table every query already reads), a
280
- * blank name, and a name starting `sqlite_` (SQLite reserves the prefix).
281
- * A `name:` also requires `schemas:`, because a schema-less override never
282
- * wins resolution and its view would be empty by construction.
321
+ * The collection this override governs (proposal 0041), in place of globs of
322
+ * its own: it matches the members of the named collection, which are decided
323
+ * once, at the top level of the family file, for every tool. Must name a
324
+ * collection `collections:` defines — checked by `loadConfig`, which is the
325
+ * first place both halves of the file are known.
326
+ *
327
+ * This is what `overrides[].name` was for under 0027, inverted: a collection
328
+ * is the thing that has a name, and an override points at it.
283
329
  */
284
- name?: string;
330
+ collection?: string;
285
331
  /**
286
332
  * Extra element paths for files matching `files`. Unlike `schemas`, which the
287
333
  * first matching override *replaces* because a schema set is a complete
@@ -357,40 +403,6 @@ interface CheckConfig {
357
403
  /** One SQL statement whose rows follow the finding column convention. */
358
404
  query: string;
359
405
  }
360
- /**
361
- * One sidecar manifest (proposal 0037): a YAML file that supplies a fixed set
362
- * of top-level keys for named documents, merged into each document's metadata
363
- * before schema resolution. The private half of a public docset.
364
- */
365
- interface SidecarConfig {
366
- /**
367
- * Manifest path, relative to the config file's directory, or an `https://`
368
- * URL (proposal 0038) fetched at the start of every run. A URL serves a
369
- * manifest in another public repository as readily as a private one; only
370
- * the private one needs `tokenEnv`.
371
- */
372
- file: string;
373
- /**
374
- * The top-level keys this manifest owns. Ownership is disjoint across
375
- * entries, so two manifests can never disagree about one key, and a
376
- * document carrying an owned key is a finding rather than a tiebreak.
377
- */
378
- keys: string[];
379
- /**
380
- * Name of an environment variable whose value is sent as a bearer token
381
- * when `file` is a URL. The token never appears in the config, a message,
382
- * or a report. Refused on a path `file`, where it would do nothing.
383
- */
384
- tokenEnv?: string;
385
- /**
386
- * `path` (the default), or the top-level frontmatter field whose value the
387
- * manifest's keys name (proposal 0039). A field join survives a rename;
388
- * what it cannot survive is two documents sharing one value, which is a
389
- * finding on both. Never `$schema`, and never a key this entry owns: the
390
- * value that selects an entry cannot come from the entry.
391
- */
392
- join?: string;
393
- }
394
406
  /** Defaults for the `fill` command; every key is overridable by a CLI flag. */
395
407
  interface FillConfig {
396
408
  provider?: string;
@@ -412,8 +424,6 @@ interface SchemaCacheConfig {
412
424
  ttlHours?: number;
413
425
  }
414
426
  interface DocmetaConfig {
415
- paths?: string[];
416
- exclude?: string[];
417
427
  /**
418
428
  * The default schema set. Each entry is either a reference string or a
419
429
  * `{ ref, source?, integrity? }` mapping — see `SchemaEntry`.
@@ -425,11 +435,6 @@ interface DocmetaConfig {
425
435
  * run's file set is the config-resolved corpus. See `CheckConfig`.
426
436
  */
427
437
  checks?: CheckConfig[];
428
- /**
429
- * Sidecar manifests whose keys are merged into the documents they name.
430
- * See `SidecarConfig`.
431
- */
432
- sidecars?: SidecarConfig[];
433
438
  /**
434
439
  * Element paths to lift in addition to each format's convention. See
435
440
  * `parseElementPath` for the syntax.
@@ -479,17 +484,28 @@ interface LoadedConfig {
479
484
  /** Absolute path to the file the config was read from. */
480
485
  path: string;
481
486
  /**
482
- * Directory holding that file. Relative paths written *in* the config —
483
- * `paths:`, `exclude:`, local-file schema refs — are meaningful relative to
487
+ * Directory holding that file. Relative paths written *in* the config — a
488
+ * collection's `paths:`, local-file schema refs — are meaningful relative to
484
489
  * this, not to the directory the command happened to be invoked from.
485
490
  */
486
491
  dir: string;
492
+ /**
493
+ * The file as the user would name it, which is how every config message
494
+ * spells it. Threaded from `ConfigFile.source` so `selectCollections` can
495
+ * name the file a `--collection` failed to find a collection in.
496
+ */
497
+ source: string;
487
498
  /**
488
499
  * The family-file key the config was read from under (`"meta"`), when it
489
500
  * was. Absent for a pre-family file, whose top level is the config. A
490
501
  * command that writes the file back (`schemas vendor`) needs to know.
491
502
  */
492
503
  section?: string;
504
+ /**
505
+ * Every collection the family file declares (proposal 0041), in declaration
506
+ * order. `[]` for a legacy per-tool file, which carries no family-wide keys.
507
+ */
508
+ collections: CollectionConfig[];
493
509
  }
494
510
 
495
511
  /**
@@ -546,24 +562,37 @@ interface RunConfigOptions {
546
562
  configPath?: string;
547
563
  /** `--no-config`: skip discovery and run on the built-in defaults. */
548
564
  noConfig?: boolean;
549
- /** Positional inputs; empty means fall back to the config's `paths:`. */
565
+ /**
566
+ * Positional inputs; empty means fall back to the configured collections'
567
+ * `paths:` (proposal 0041).
568
+ */
550
569
  inputs: string[];
570
+ /**
571
+ * `--collection <name>`, repeatable: the collections this run covers. Empty
572
+ * or absent means every declared collection — the same thing, because
573
+ * commander's repeatable collector hands a caller `[]` when the flag was
574
+ * never passed (see `selectCollections`).
575
+ */
576
+ collections?: string[];
551
577
  onConfigLoaded?: (info: ConfigNotice) => void;
552
578
  }
553
579
  interface RunConfig {
554
580
  /** The config, with its local file schema refs already rebased. */
555
581
  config: DocmetaConfig | null;
556
- /** What to resolve: the positional inputs, or the config's `paths:`. */
582
+ /**
583
+ * What to resolve: the positional inputs, or the selected collections'
584
+ * `paths:`, concatenated in declaration order.
585
+ */
557
586
  inputs: string[];
558
587
  /**
559
588
  * Directory those inputs — and so every resolved file path, and every file
560
589
  * read — are relative to.
561
590
  *
562
- * A run uses *either* positional paths *or* config `paths:`, never both, so
563
- * there is exactly one base per run and no ambiguity about which it is.
564
- * Positional paths are typed by a person standing in a shell, so they stay
565
- * relative to the working directory; `paths:` globs were written next to the
566
- * config, so they resolve from there.
591
+ * A run uses *either* positional paths *or* the collections' `paths:`, never
592
+ * both, so there is exactly one base per run and no ambiguity about which it
593
+ * is. Positional paths are typed by a person standing in a shell, so they
594
+ * stay relative to the working directory; a collection's globs were written
595
+ * next to the config, so they resolve from there.
567
596
  */
568
597
  base: string;
569
598
  /**
@@ -584,6 +613,40 @@ interface RunConfig {
584
613
  * assumed filename names the wrong file in every one of those setups.
585
614
  */
586
615
  configPath?: string;
616
+ /**
617
+ * The key the tool's config sits under in that file — `meta` for a family
618
+ * file, `undefined` for a legacy per-tool file whose whole document is the
619
+ * section.
620
+ *
621
+ * Anything that *rewrites* the config by hand has to know: `schemas:` is at
622
+ * the top level of a legacy file and one level down in a family file, so a
623
+ * rewriter that assumes the top level silently finds nothing in the shape
624
+ * proposal 0033 made the default. `query`'s DDL repointing did exactly that
625
+ * until the 0041 fixtures stopped being legacy files and made it visible.
626
+ */
627
+ configSection?: string;
628
+ /**
629
+ * The collections this run covers (proposal 0041): every declared one, or
630
+ * the ones `--collection` named. `[]` when no config governs the run.
631
+ *
632
+ * Selected even when the inputs are positional, because membership is what
633
+ * external metadata attaches to (rule 4) and a file the operator typed is
634
+ * still a member of whatever collections contain it.
635
+ */
636
+ collections: CollectionConfig[];
637
+ /**
638
+ * Every collection the config declares, in declaration order — the same list
639
+ * `collections` narrows. `query` needs both: membership and manifests follow
640
+ * the *selected* set, while every *declared* collection is a SQL view (0041
641
+ * rule 11), so a statement naming one the run did not select still answers.
642
+ */
643
+ declaredCollections: CollectionConfig[];
644
+ /**
645
+ * Whether `inputs` came from the collections rather than the command line.
646
+ * The corpus invariant every scoped check reads: a positional path means the
647
+ * operator chose part of the corpus.
648
+ */
649
+ fromCollections: boolean;
587
650
  }
588
651
  /**
589
652
  * Settle the three things every command core needs from config before it can
@@ -602,6 +665,12 @@ interface ValidateOptions {
602
665
  configPath?: string;
603
666
  /** `--no-config`: skip config discovery and use the built-in defaults. */
604
667
  noConfig?: boolean;
668
+ /**
669
+ * `--collection <name>`, repeatable: run over the named configured
670
+ * collections instead of every declared one (proposal 0041). Cannot be
671
+ * combined with positional paths; `-` is allowed beside it.
672
+ */
673
+ collections?: string[];
605
674
  cwd?: string;
606
675
  /** Content for the `-` (stdin) input, injected by the CLI/tests. */
607
676
  stdinContent?: string;
@@ -668,6 +737,12 @@ interface GetOptions {
668
737
  configPath?: string;
669
738
  /** `--no-config`: skip config discovery and use the built-in defaults. */
670
739
  noConfig?: boolean;
740
+ /**
741
+ * `--collection <name>`, repeatable: run over the named configured
742
+ * collections instead of every declared one (proposal 0041). Cannot be
743
+ * combined with positional paths; `-` is allowed beside it.
744
+ */
745
+ collections?: string[];
671
746
  cwd?: string;
672
747
  /** Content for the `-` (stdin) input, injected by the CLI/tests. */
673
748
  stdinContent?: string;
@@ -730,6 +805,12 @@ interface QueryOptions {
730
805
  configPath?: string;
731
806
  /** `--no-config`: skip config discovery and use the built-in defaults. */
732
807
  noConfig?: boolean;
808
+ /**
809
+ * `--collection <name>`, repeatable: run over the named configured
810
+ * collections instead of every declared one (proposal 0041). Cannot be
811
+ * combined with positional paths; `-` is allowed beside it.
812
+ */
813
+ collections?: string[];
733
814
  cwd?: string;
734
815
  /** Content for the `-` (stdin) input, injected by the CLI/tests. */
735
816
  stdinContent?: string;
@@ -1125,6 +1206,12 @@ interface FillOptions {
1125
1206
  configPath?: string;
1126
1207
  /** `--no-config`: skip config discovery and use the built-in defaults. */
1127
1208
  noConfig?: boolean;
1209
+ /**
1210
+ * `--collection <name>`, repeatable: run over the named configured
1211
+ * collections instead of every declared one (proposal 0041). Cannot be
1212
+ * combined with positional paths; `-` is allowed beside it.
1213
+ */
1214
+ collections?: string[];
1128
1215
  cwd?: string;
1129
1216
  /** Content for the `-` (stdin) input, injected by the CLI/tests. */
1130
1217
  stdinContent?: string;
@@ -1176,31 +1263,38 @@ declare function runFill(opts: FillOptions): Promise<FillRun>;
1176
1263
 
1177
1264
  /**
1178
1265
  * The `schema` ref a collision finding carries, and so its baseline and rule
1179
- * identity: `sidecar:owned/sidecar`. Shaped like `check:<name>` so
1266
+ * identity: `external:owned/external`. Shaped like `check:<name>` so
1180
1267
  * `classifyRef` passes it through as a built-in id rather than resolving it
1181
1268
  * as a cwd-relative file path, and reserved in the registry for the same
1182
1269
  * reason `check` is.
1183
1270
  */
1184
- declare const SIDECAR_OWNED_SCHEMA = "sidecar:owned";
1271
+ declare const EXTERNAL_OWNED_SCHEMA = "external:owned";
1185
1272
  /**
1186
1273
  * The `schema` ref of a duplicate-join finding (0039): two documents carry
1187
1274
  * the same value of a join field, so one manifest entry matched both.
1188
1275
  */
1189
- declare const SIDECAR_DUPLICATE_SCHEMA = "sidecar:duplicate";
1190
- /** The `keyword` every sidecar finding carries: no Ajv keyword produced it. */
1191
- declare const SIDECAR_KEYWORD = "sidecar";
1276
+ declare const EXTERNAL_DUPLICATE_SCHEMA = "external:duplicate";
1277
+ /**
1278
+ * The `keyword` every external-metadata finding carries: no Ajv keyword
1279
+ * produced it.
1280
+ */
1281
+ declare const EXTERNAL_KEYWORD = "external";
1192
1282
  /** The join that names documents by path, and the default. */
1193
1283
  declare const PATH_JOIN = "path";
1194
- /** One value a sidecar supplied, and where it was written. */
1195
- interface SidecarValue {
1284
+ /** One value a manifest supplied, and where it was written. */
1285
+ interface ExternalMetadataValue {
1196
1286
  value: unknown;
1287
+ /** The collection whose manifest supplied it (proposal 0041). */
1288
+ collection: string;
1197
1289
  /** Manifest path as the run reports it: relative to the run's base, posix — or the URL. */
1198
1290
  file: string;
1199
1291
  /** 1-based line of the key in the manifest, when known. */
1200
1292
  line?: number;
1201
1293
  }
1202
1294
  /** One manifest entry, for the orphan checks. */
1203
- interface SidecarEntry {
1295
+ interface ExternalMetadataEntry {
1296
+ /** The collection whose manifest declared it (proposal 0041). */
1297
+ collection: string;
1204
1298
  /** `path`, or the frontmatter field this entry is keyed by. */
1205
1299
  join: string;
1206
1300
  /** Absolute document path, for a path entry. */
@@ -1212,19 +1306,29 @@ interface SidecarEntry {
1212
1306
  /** 1-based line of the entry in the manifest, when known. */
1213
1307
  line?: number;
1214
1308
  }
1215
- /** Every sidecar of a run, loaded once and consulted per document. */
1216
- interface SidecarIndex {
1217
- /** Owned key -> manifest path as reported. */
1218
- owners: ReadonlyMap<string, string>;
1219
- /** Path-joined sidecars: absolute document path -> owned key -> value. */
1220
- byPath: ReadonlyMap<string, ReadonlyMap<string, SidecarValue>>;
1221
- /** Field-joined sidecars: field -> value -> owned key -> value. */
1222
- byField: ReadonlyMap<string, ReadonlyMap<string, ReadonlyMap<string, SidecarValue>>>;
1309
+ /** Every manifest of a run, loaded once and consulted per document. */
1310
+ interface ExternalMetadataIndex {
1311
+ /**
1312
+ * Owned key -> every collection that owns it, with the manifest that does.
1313
+ *
1314
+ * A list rather than one entry, because two collections may legitimately
1315
+ * each own the same key (0041 rule 5): `guides` and `blog` can both supply
1316
+ * `owner`. Ambiguity only exists for a file that is a member of both, which
1317
+ * is decided per file in `mergeExternalMetadata`.
1318
+ */
1319
+ owners: ReadonlyMap<string, readonly {
1320
+ collection: string;
1321
+ file: string;
1322
+ }[]>;
1323
+ /** Path-joined manifests: absolute document path -> owned key -> value. */
1324
+ byPath: ReadonlyMap<string, ReadonlyMap<string, ExternalMetadataValue>>;
1325
+ /** Field-joined manifests: field -> value -> owned key -> value. */
1326
+ byField: ReadonlyMap<string, ReadonlyMap<string, ReadonlyMap<string, ExternalMetadataValue>>>;
1223
1327
  /** Every manifest entry, in manifest order. */
1224
- entries: readonly SidecarEntry[];
1328
+ entries: readonly ExternalMetadataEntry[];
1225
1329
  }
1226
- interface LoadSidecarsOptions {
1227
- /** Directory `sidecars[].file` and every manifest key resolve from. */
1330
+ interface LoadExternalMetadataOptions {
1331
+ /** Directory `externalMetadata[].file` and every manifest key resolve from. */
1228
1332
  configDir: string;
1229
1333
  /** Directory the run's file labels are relative to; manifests are reported the same way. */
1230
1334
  base: string;
@@ -1241,20 +1345,24 @@ interface SourceLocation {
1241
1345
  line?: number;
1242
1346
  col?: number;
1243
1347
  }
1244
- /** A key the document carries that a sidecar owns. */
1245
- interface SidecarCollision {
1348
+ /** A key the document carries that a manifest owns. */
1349
+ interface ExternalMetadataCollision {
1246
1350
  key: string;
1247
1351
  /** The owning manifest, as the run reports it. */
1248
1352
  file: string;
1353
+ /** The collection that manifest belongs to (proposal 0041). */
1354
+ collection: string;
1249
1355
  }
1250
1356
  /** A field-joined entry a document matched (0039). */
1251
- interface SidecarJoin {
1357
+ interface ExternalMetadataJoin {
1252
1358
  field: string;
1253
1359
  value: string;
1254
1360
  /** The manifest, as the run reports it. */
1255
1361
  file: string;
1362
+ /** The collection that manifest belongs to (proposal 0041). */
1363
+ collection: string;
1256
1364
  }
1257
- /** What `mergeSidecars` hands back. */
1365
+ /** What `mergeExternalMetadata` hands back. */
1258
1366
  interface MergedMetadata {
1259
1367
  /** The document's metadata with every owned key the manifest supplied. */
1260
1368
  extracted: ExtractedMetadata;
@@ -1263,9 +1371,9 @@ interface MergedMetadata {
1263
1371
  * with the manifest that owns it — so the finding names it without a
1264
1372
  * second lookup, and without a fallback for an index that is not there.
1265
1373
  */
1266
- collisions: SidecarCollision[];
1267
- /** Field-joined entries this document matched, one per sidecar at most. */
1268
- joins: SidecarJoin[];
1374
+ collisions: ExternalMetadataCollision[];
1375
+ /** Field-joined entries this document matched, one per manifest at most. */
1376
+ joins: ExternalMetadataJoin[];
1269
1377
  /**
1270
1378
  * Where a merged pointer's value lives. Answers only for a value the
1271
1379
  * manifest supplied — a bare key or its `/key` pointer, and anything
@@ -1274,19 +1382,20 @@ interface MergedMetadata {
1274
1382
  */
1275
1383
  locate: (pointer: string) => SourceLocation | undefined;
1276
1384
  }
1277
- /** The join a sidecar config asks for: `path` unless it names a field. */
1278
- declare function sidecarJoin(sidecar: Pick<SidecarConfig, "join">): string;
1385
+ /** The join an external-metadata config asks for: `path` unless it names a field. */
1386
+ declare function externalMetadataJoin(manifest: Pick<ExternalMetadataConfig, "join">): string;
1279
1387
  /**
1280
- * Load every configured manifest. Resolves to `null` when the config declares
1281
- * none, which is every setup that existed before this feature.
1388
+ * Load every manifest of every collection the run covers. Resolves to `null`
1389
+ * when none of them declares one, which is every setup that existed before
1390
+ * this feature.
1282
1391
  *
1283
1392
  * Every refusal is a `DocmetaError`: the manifest is a config-supplied input,
1284
1393
  * and one that is missing or misshapen is the run's problem, not a
1285
1394
  * document's (0014).
1286
1395
  */
1287
- declare function loadSidecars(config: DocmetaConfig | null | undefined, opts: LoadSidecarsOptions): Promise<SidecarIndex | null>;
1396
+ declare function loadExternalMetadata(collections: readonly CollectionConfig[], opts: LoadExternalMetadataOptions): Promise<ExternalMetadataIndex | null>;
1288
1397
  /**
1289
- * Merge the sidecar values for one document into its extracted metadata.
1398
+ * Merge the external metadata for one document into its extracted metadata.
1290
1399
  *
1291
1400
  * `label` is the document as the run spells it, relative to `base`; a path
1292
1401
  * entry matches on the resolved absolute path, never on the spelling, so a
@@ -1295,13 +1404,17 @@ declare function loadSidecars(config: DocmetaConfig | null | undefined, opts: Lo
1295
1404
  * field, wherever the document lives. Stdin never matches a path entry:
1296
1405
  * there is no file behind it.
1297
1406
  *
1407
+ * `memberOf` names the collections this document belongs to, and only their
1408
+ * manifests are consulted (0041 rule 4). It is the caller's `memberOf(...)`
1409
+ * result, computed once per file.
1410
+ *
1298
1411
  * `present`, `format` and the document's own positions are untouched. A
1299
- * sidecar key is not in the document, so `lineFor` keeps answering
1412
+ * manifest key is not in the document, so `lineFor` keeps answering
1300
1413
  * `undefined` for it, and `locate` answers instead.
1301
1414
  */
1302
- declare function mergeSidecars(label: string, extracted: ExtractedMetadata, index: SidecarIndex | null, base: string): MergedMetadata;
1303
- /** The `/key` pointer for a sidecar finding, RFC 6901 escaped. */
1304
- declare function sidecarPointer(key: string): string;
1415
+ declare function mergeExternalMetadata(label: string, extracted: ExtractedMetadata, index: ExternalMetadataIndex | null, memberOf: readonly string[], base: string): MergedMetadata;
1416
+ /** The `/key` pointer for an external-metadata finding, RFC 6901 escaped. */
1417
+ declare function externalMetadataPointer(key: string): string;
1305
1418
  /**
1306
1419
  * Path entries naming no loaded document.
1307
1420
  *
@@ -1310,16 +1423,16 @@ declare function sidecarPointer(key: string): string;
1310
1423
  * positional path means the operator chose to look at part of the corpus,
1311
1424
  * and an entry for the rest is expected, not orphaned.
1312
1425
  */
1313
- declare function orphanEntries(index: SidecarIndex | null, loaded: readonly string[], base: string): SidecarEntry[];
1426
+ declare function orphanEntries(index: ExternalMetadataIndex | null, loaded: readonly string[], base: string, collections?: readonly string[]): ExternalMetadataEntry[];
1314
1427
  /**
1315
1428
  * Field entries no loaded document matched (0039). `matched` is field -> the
1316
1429
  * values documents in the run carried, which the caller collects from each
1317
1430
  * document's `joins` — so this runs after the per-file loop, under the same
1318
1431
  * corpus invariant as `orphanEntries`.
1319
1432
  */
1320
- declare function orphanJoins(index: SidecarIndex | null, matched: ReadonlyMap<string, ReadonlySet<string>>): SidecarEntry[];
1433
+ declare function orphanJoins(index: ExternalMetadataIndex | null, matched: ReadonlyMap<string, ReadonlySet<string>>, collections?: readonly string[]): ExternalMetadataEntry[];
1321
1434
  /** The operational error an orphan check raises, naming the first entry. */
1322
- declare function orphanError(orphans: readonly SidecarEntry[]): DocmetaError;
1435
+ declare function orphanError(orphans: readonly ExternalMetadataEntry[]): DocmetaError;
1323
1436
 
1324
1437
  declare class Validator {
1325
1438
  private readonly schemaOptions;
@@ -1369,7 +1482,7 @@ declare class Validator {
1369
1482
  validate(data: Record<string, unknown>, refs: string[], lineFor: (pointer: string) => number | undefined, colFor?: (pointer: string) => number | undefined,
1370
1483
  /**
1371
1484
  * Where a merged value lives when it is not in the document (proposal
1372
- * 0037): a sidecar manifest's file and line. Consulted before `lineFor`,
1485
+ * 0037): a manifest's file and line. Consulted before `lineFor`,
1373
1486
  * and additive in the way `colFor` was.
1374
1487
  */
1375
1488
  locate?: (pointer: string) => SourceLocation | undefined): Promise<FieldError[]>;
@@ -1389,7 +1502,7 @@ declare const DEFAULT_SCHEMAS: readonly string[];
1389
1502
  * The ref *string* is what `resolveSchemaSet` returns and therefore what every
1390
1503
  * report names, what every baseline fingerprint is taken over, and what keys
1391
1504
  * `Validator`'s compile cache. Widening those to carry a mapping would have
1392
- * changed all three; the `{source, integrity}` sidecar travels separately, in
1505
+ * changed all three; the `{source, integrity}` pair travels separately, in
1393
1506
  * the pin map below.
1394
1507
  */
1395
1508
  declare function schemaEntryRef(entry: SchemaEntry): string;
@@ -1459,9 +1572,63 @@ interface ResolveParams {
1459
1572
  * failure mode this key exists to remove.
1460
1573
  */
1461
1574
  onNotice?: (message: string) => void;
1575
+ /**
1576
+ * The collections this file belongs to (proposal 0041), from `memberOf`.
1577
+ *
1578
+ * What an `overrides[].collection` entry is matched against, in place of the
1579
+ * glob match a `files:` entry gets. Absent means the caller decided
1580
+ * membership does not apply — a library caller with no config file, or a
1581
+ * command that has not resolved it — and a `collection:` override then
1582
+ * matches nothing, rather than everything.
1583
+ */
1584
+ memberOf?: readonly string[];
1462
1585
  }
1586
+
1463
1587
  declare function resolveSchemaSet(params: ResolveParams): string[];
1464
1588
 
1589
+ /**
1590
+ * Collection views, and the membership adapter that decides what a collection
1591
+ * contains.
1592
+ *
1593
+ * `memberOf` is proposal 0041's rule 10: which collections a file belongs to,
1594
+ * as path arithmetic against the config file's directory. It is what external
1595
+ * metadata attaches to, what an `overrides[].collection` resolves through, and
1596
+ * — since 0041 rule 7 — what a SQL view holds.
1597
+ *
1598
+ * One meaning of "collection", everywhere. 0027 built each view from the files
1599
+ * an override *won schema resolution for*, so views were disjoint, only a
1600
+ * collection an override pointed at had a view at all, and a file whose own
1601
+ * `$schema` outranked the override was announced as not being in it. 0041
1602
+ * makes a view the set the config says it is: every declared collection is a
1603
+ * view, two views may overlap, and `FROM authors` reads as "the files the
1604
+ * authors collection selects".
1605
+ *
1606
+ * That restores 0021's founding rule, which 0027 had to bend: a plain read
1607
+ * resolves no schemas. Nothing in this module resolves anything, so there is
1608
+ * no trust refusal to demote a file over, no per-file walk to pay for, and no
1609
+ * notice to print. Resolution still honours `overrides[].collection` — it
1610
+ * matches through `memberOf` in `resolve-schema.ts`, which is untouched.
1611
+ *
1612
+ * Shared by both projection consumers — `query` and the corpus checks
1613
+ * `validate` runs — so the two cannot drift on what a collection contains.
1614
+ */
1615
+
1616
+ /**
1617
+ * Names of the collections this file belongs to, in declaration order.
1618
+ *
1619
+ * Where the two bases meet. A run labels files relative to `base` — the
1620
+ * working directory for positional paths, the config's directory otherwise —
1621
+ * while membership is decided relative to the config file's directory, because
1622
+ * that is where a collection's globs were written (0004). So the label is
1623
+ * resolved against `base` and then measured from `configDir`.
1624
+ *
1625
+ * Pure path arithmetic (0041 rule 10): no `stat`, nothing that can fail, and
1626
+ * nothing that costs per file. Stdin and anything outside `configDir` are
1627
+ * members of nothing — stdin because there is no file behind it, and `isMember`
1628
+ * refuses a `../` path on its own.
1629
+ */
1630
+ declare function memberOf(collections: readonly CollectionConfig[], configDir: string, base: string, label: string): string[];
1631
+
1465
1632
  /** What a well-formed pin looks like, for error messages. */
1466
1633
  declare const INTEGRITY_SHAPE = "sha256-<64 hex characters>";
1467
1634
  /**
@@ -1476,10 +1643,10 @@ declare function integrityOf(bytes: Uint8Array): string;
1476
1643
  declare function isIntegrity(value: string): boolean;
1477
1644
 
1478
1645
  /** Default network timeout, matching the schema fetch. */
1479
- declare const SIDECAR_FETCH_TIMEOUT_MS = 10000;
1646
+ declare const EXTERNAL_METADATA_FETCH_TIMEOUT_MS = 10000;
1480
1647
  /** Body cap, matching the schema fetch. */
1481
- declare const SIDECAR_FETCH_MAX_BYTES: number;
1482
- interface SidecarFetchOptions {
1648
+ declare const EXTERNAL_METADATA_FETCH_MAX_BYTES: number;
1649
+ interface ExternalMetadataFetchOptions {
1483
1650
  /** Environment variable holding a bearer token. Absent means an anonymous request. */
1484
1651
  tokenEnv?: string;
1485
1652
  timeoutMs?: number;
@@ -1489,15 +1656,8 @@ interface SidecarFetchOptions {
1489
1656
  /** For tests: the environment to read `tokenEnv` from. Defaults to `process.env`. */
1490
1657
  env?: Record<string, string | undefined>;
1491
1658
  }
1492
- /**
1493
- * Whether a URL may name a sidecar manifest: `https://` anywhere, or `http://`
1494
- * on a loopback host only, where plaintext leaks nothing. Userinfo is refused
1495
- * because the URL is printed in every diagnostic and a secret in it would
1496
- * print too. Returns the reason it may not, or `null`.
1497
- */
1498
- declare function sidecarUrlProblem(url: string): string | null;
1499
1659
  /** Fetch one remote manifest. Resolves to its text; throws `DocmetaError`. */
1500
- declare function fetchSidecar(url: string, opts?: SidecarFetchOptions): Promise<string>;
1660
+ declare function fetchExternalMetadata(url: string, opts?: ExternalMetadataFetchOptions): Promise<string>;
1501
1661
 
1502
1662
  /**
1503
1663
  * Where the cache lives, relative to the project root.
@@ -1846,4 +2006,4 @@ declare function extractorForExtension(ext: string): MetadataExtractor | undefin
1846
2006
  /** Extensions handled by implemented extractors (used for directory walks). */
1847
2007
  declare function supportedExtensions(): string[];
1848
2008
 
1849
- export { type ApplyOptions, type BaselineSummary, COMMON_FORMATS, type CheckConfig, type CommonFormat, type ConfigNotice, DEFAULT_SCHEMAS, DEFAULT_TTL_HOURS, DEFAULT_VENDOR_DIR, type DocmetaConfig, DocmetaError, type DocumentRefTrust, type ExtractOptions, type ExtractedMetadata, FILL_FORMATS, type FetchedSchema, type FieldError, type FillConfig, type FillFileResult, type FillOptions, type FillReportFormat, type FillReportOptions, type FillRun, type FillSummary, type FilledField, type FingerprintContext, type FrontmatterFlavor, type FrontmatterLocation, type GetFileResult, type GetOptions, type GetReportOptions, INTEGRITY_SHAPE, type JunitOptions, type LoadSchemaOptions, type LoadSidecarsOptions, type LoadedConfig, type MergedMetadata, type MetadataExtractor, type MetadataPatch, PATH_JOIN, QUERY_FORMATS, type QueryChange, type QueryFormat, type QueryOptions, type QueryReportOptions, type QueryRun, REPORT_FORMATS, type ReadOptions, type ReportFormat, type ReportOptions, type RunConfig, type RunConfigOptions, type RunSummary, SCHEMA_CACHE_DIR, SCHEMA_CACHE_VERSION, SIDECAR_DUPLICATE_SCHEMA, SIDECAR_FETCH_MAX_BYTES, SIDECAR_FETCH_TIMEOUT_MS, SIDECAR_KEYWORD, SIDECAR_OWNED_SCHEMA, type SarifOptions, SchemaCache, type SchemaCacheConfig, type SchemaCacheEntry, type SchemaEntry, type SchemaPin, type SchemaRefEntry, type SchemaTrustConfig, type SidecarCollision, type SidecarConfig, type SidecarEntry, type SidecarFetchOptions, type SidecarIndex, type SidecarJoin, type SidecarValue, type SkipReason, type SourceLocation, type ValidateOptions, type ValidateRun, type ValidationResult, Validator, type VendorOptions, type VendorResult, applyFrontmatter, classifyRef, collectSchemaPins, extractFrontmatter, extractorForExtension, fetchSchemaBytes, fetchSidecar, frontmatterInnerText, getSchemasInfo, integrityOf, isCommonFormat, isFillFormat, isIntegrity, isQueryFormat, isReportFormat, listBuiltins, loadConfig, loadSchema, loadSidecars, locateFrontmatter, mergeSidecars, orphanEntries, orphanError, orphanJoins, parseConfig, rebaseConfigSchemaRefs, render, renderFill, renderFillGithub, renderGet, renderJunit, renderQuery, renderQueryCsv, renderSarif, resolveRunConfig, resolveSchemaSet, runFill, runGet, runQuery, runValidate, runVendorSchema, schemaCacheDir, schemaEntryRef, schemaLoadOptions, sidecarJoin, sidecarPointer, sidecarUrlProblem, stringifyValue, supportedExtensions, vendorFileName, writeFileAtomic };
2009
+ export { type ApplyOptions, type BaselineSummary, COMMON_FORMATS, type CheckConfig, type CollectionConfig, type CommonFormat, type ConfigNotice, DEFAULT_SCHEMAS, DEFAULT_TTL_HOURS, DEFAULT_VENDOR_DIR, type DocmetaConfig, DocmetaError, type DocumentRefTrust, EXTERNAL_DUPLICATE_SCHEMA, EXTERNAL_KEYWORD, EXTERNAL_METADATA_FETCH_MAX_BYTES, EXTERNAL_METADATA_FETCH_TIMEOUT_MS, EXTERNAL_OWNED_SCHEMA, type ExternalMetadataCollision, type ExternalMetadataConfig, type ExternalMetadataEntry, type ExternalMetadataFetchOptions, type ExternalMetadataIndex, type ExternalMetadataJoin, type ExternalMetadataValue, type ExtractOptions, type ExtractedMetadata, FILL_FORMATS, type FetchedSchema, type FieldError, type FillConfig, type FillFileResult, type FillOptions, type FillReportFormat, type FillReportOptions, type FillRun, type FillSummary, type FilledField, type FingerprintContext, type FrontmatterFlavor, type FrontmatterLocation, type GetFileResult, type GetOptions, type GetReportOptions, INTEGRITY_SHAPE, type JunitOptions, type LoadExternalMetadataOptions, type LoadSchemaOptions, type LoadedConfig, type MergedMetadata, type MetadataExtractor, type MetadataPatch, PATH_JOIN, QUERY_FORMATS, type QueryChange, type QueryFormat, type QueryOptions, type QueryReportOptions, type QueryRun, REPORT_FORMATS, type ReadOptions, type ReportFormat, type ReportOptions, type RunConfig, type RunConfigOptions, type RunSummary, SCHEMA_CACHE_DIR, SCHEMA_CACHE_VERSION, type SarifOptions, SchemaCache, type SchemaCacheConfig, type SchemaCacheEntry, type SchemaEntry, type SchemaPin, type SchemaRefEntry, type SchemaTrustConfig, type SkipReason, type SourceLocation, type ValidateOptions, type ValidateRun, type ValidationResult, Validator, type VendorOptions, type VendorResult, applyFrontmatter, classifyRef, collectSchemaPins, externalMetadataJoin, externalMetadataPointer, externalMetadataUrlProblem, extractFrontmatter, extractorForExtension, fetchExternalMetadata, fetchSchemaBytes, frontmatterInnerText, getSchemasInfo, integrityOf, isCommonFormat, isFillFormat, isIntegrity, isMember, isQueryFormat, isReportFormat, listBuiltins, loadConfig, loadExternalMetadata, loadSchema, locateFrontmatter, memberOf, mergeExternalMetadata, orphanEntries, orphanError, orphanJoins, parseCollections, parseConfig, rebaseConfigSchemaRefs, render, renderFill, renderFillGithub, renderGet, renderJunit, renderQuery, renderQueryCsv, renderSarif, resolveRunConfig, resolveSchemaSet, runFill, runGet, runQuery, runValidate, runVendorSchema, schemaCacheDir, schemaEntryRef, schemaLoadOptions, selectCollections, stringifyValue, supportedExtensions, vendorFileName, writeFileAtomic };