@hawkeyexl/manni 4.0.3 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,7 +8,7 @@ var __export = (target, all) => {
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "@hawkeyexl/manni",
11
- version: "4.0.3",
11
+ version: "4.2.0",
12
12
  description: "One command line for documentation that is meant to be checked. `manni meta` validates the presence and format of document metadata (Markdown frontmatter and more) against JSON Schema, built for CI. Formerly docmeta.",
13
13
  type: "module",
14
14
  bin: {
@@ -23,11 +23,20 @@ var package_default = {
23
23
  types: "./dist/index.d.ts",
24
24
  import: "./dist/index.js"
25
25
  },
26
+ "./graph/runtime": {
27
+ types: "./dist/graph/runtime.d.ts",
28
+ import: "./dist/graph/runtime.js"
29
+ },
30
+ "./graph/embed": {
31
+ types: "./dist/graph/embed.d.ts",
32
+ import: "./dist/graph/embed.js"
33
+ },
26
34
  "./package.json": "./package.json"
27
35
  },
28
36
  files: [
29
37
  "dist",
30
38
  "schemas",
39
+ "shapes",
31
40
  "templates"
32
41
  ],
33
42
  repository: {
@@ -46,11 +55,12 @@ var package_default = {
46
55
  },
47
56
  scripts: {
48
57
  prebuild: "npm run check:deps",
49
- build: "tsup",
50
- dev: "tsup --watch",
58
+ build: "node scripts/clean-dist.mjs && tsup",
59
+ dev: "node scripts/clean-dist.mjs && tsup --watch",
51
60
  pretest: "npm run check:deps",
52
61
  test: "vitest run",
53
62
  "test:watch": "vitest",
63
+ "test:graph:real": "vitest run --config vitest.graph-real.config.ts",
54
64
  "test:coverage": "vitest run --coverage",
55
65
  pretypecheck: "npm run check:deps",
56
66
  typecheck: "tsc --noEmit",
@@ -63,6 +73,8 @@ var package_default = {
63
73
  "docs:check-action": "node scripts/check-action-reference.mjs",
64
74
  "docs:check-api": "node scripts/check-api-reference.mjs",
65
75
  "docs:check-links": "node scripts/check-doc-links.mjs",
76
+ "docs:check-docevals": "node dist/cli.js docevals run --deterministic-only --no-generate",
77
+ "docs:check-graph": "node scripts/check-docs-graph.mjs",
66
78
  "docs:check-tables": "node scripts/check-table-rows.mjs",
67
79
  "docs:check-versions": "node scripts/check-versions.mjs",
68
80
  "docs:sync-versions": "node scripts/sync-versions.mjs",
@@ -92,7 +104,8 @@ var package_default = {
92
104
  "@apidevtools/json-schema-ref-parser": "^11.9.3",
93
105
  "@asciidoctor/core": "^3.0.4",
94
106
  "@axe-core/playwright": "^4.13.0",
95
- "@hawkeyexl/inference": "^0.3.2",
107
+ "@hawkeyexl/inference": "^0.4.0",
108
+ "@types/n3": "^1.26.3",
96
109
  "@xmldom/xmldom": "^0.9.12",
97
110
  ajv: "^8.17.1",
98
111
  "ajv-draft-04": "^1.0.0",
@@ -101,10 +114,13 @@ var package_default = {
101
114
  "fast-glob": "^3.3.2",
102
115
  "github-slugger": "^2.0.0",
103
116
  "mdast-util-to-string": "^4.0.0",
117
+ minisearch: "^7.2.0",
118
+ n3: "^1.26.0",
104
119
  parse5: "^8.0.1",
105
120
  picocolors: "^1.1.1",
106
121
  picomatch: "^4.0.7",
107
122
  "playwright-core": "^1.63.0",
123
+ "rdf-validate-shacl": "^0.6.5",
108
124
  "remark-frontmatter": "^5.0.0",
109
125
  "remark-gfm": "^4.0.1",
110
126
  "remark-mdx": "^3.1.1",
@@ -117,26 +133,45 @@ var package_default = {
117
133
  devDependencies: {
118
134
  "@commitlint/cli": "^21.2.2",
119
135
  "@commitlint/config-conventional": "^21.2.2",
136
+ "@comunica/query-sparql-rdfjs-lite": "^5.3.0",
120
137
  "@eslint/js": "^10.0.1",
138
+ "@rdfjs/types": "^2.0.1",
121
139
  "@semantic-release/changelog": "^6.0.3",
122
140
  "@semantic-release/commit-analyzer": "^13.0.1",
123
141
  "@semantic-release/git": "^10.0.1",
124
142
  "@semantic-release/github": "^12.0.8",
125
143
  "@semantic-release/npm": "^13.1.5",
126
144
  "@semantic-release/release-notes-generator": "^14.1.1",
145
+ "@types/jsonld": "^1.5.15",
127
146
  "@types/mdast": "^4.0.4",
128
147
  "@types/node": "^26.3.0",
129
148
  "@types/picomatch": "^4.0.3",
130
149
  "@types/unist": "^3.0.3",
150
+ "@types/yauzl": "^3.4.0",
131
151
  "@vitest/coverage-v8": "^4.1.11",
132
152
  eslint: "^10.9.1",
133
153
  globals: "^17.11.0",
134
154
  husky: "^9.1.7",
155
+ jsonld: "^9.0.0",
156
+ "rdfxml-streaming-parser": "^3.3.0",
135
157
  "semantic-release": "^25.0.9",
136
158
  tsup: "^8.3.5",
137
159
  typescript: "^6.0.3",
138
160
  "typescript-eslint": "^8.68.0",
139
- vitest: "^4.1.11"
161
+ vitest: "^4.1.11",
162
+ yauzl: "^3.4.0"
163
+ },
164
+ peerDependencies: {
165
+ "@huggingface/transformers": ">=4",
166
+ "node-llama-cpp": "^3.19.0"
167
+ },
168
+ peerDependenciesMeta: {
169
+ "@huggingface/transformers": {
170
+ optional: true
171
+ },
172
+ "node-llama-cpp": {
173
+ optional: true
174
+ }
140
175
  }
141
176
  };
142
177
 
@@ -3232,8 +3267,8 @@ var RENAME_ATTEMPTS = 3;
3232
3267
  function errorCode(err) {
3233
3268
  return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
3234
3269
  }
3235
- var wait = (ms) => new Promise((resolve25) => {
3236
- setTimeout(resolve25, ms);
3270
+ var wait = (ms) => new Promise((resolve26) => {
3271
+ setTimeout(resolve26, ms);
3237
3272
  });
3238
3273
  async function writeFileAtomic(path, contents, options = {}) {
3239
3274
  const tmp = join(
@@ -8344,6 +8379,251 @@ var __default47 = {
8344
8379
  }
8345
8380
  };
8346
8381
 
8382
+ // src/meta/schemas/stewardship/1.1.0.json
8383
+ var __default48 = {
8384
+ $schema: "https://json-schema.org/draft/2020-12/schema",
8385
+ $id: "manni:stewardship:1.1.0",
8386
+ title: "manni stewardship vocabulary v1.1.0",
8387
+ description: "Is this page cared for? This vocabulary covers attribution, ownership, review, the document's own dates, and the anchors that make drift detectable. Every field records a fact a person asserts about the document, not one a tool reads off the file. That is why `created`, `last-updated` and `authors` are here. Git records when a path changed and who committed the change, not when the document was written or who wrote it. Those part company at every migration, split, squashed import, typo fix and bulk frontmatter sweep. A published page carries no repo, so a stamped date travels with the document where a derived one cannot. The cost is that a hand-typed `last-updated` goes stale quietly, and no JSON Schema can catch that. The review dates are records, not freshness gates. Tooling that can read a clock derives the due date from `last-reviewed` and `review-interval`. Version 1.1.0 adds `x-manni-graph-output: false` to `owner`, `stakeholders` and `reviewed-by`. Those three name people, so a published graph leaves them out: its Turtle, its JSON-LD, its iiRDS package and its search index. Validation is the same as in 1.0.0.",
8388
+ type: "object",
8389
+ additionalProperties: true,
8390
+ properties: {
8391
+ authors: {
8392
+ type: [
8393
+ "string",
8394
+ "object",
8395
+ "array"
8396
+ ],
8397
+ description: "Who wrote the page: a name, a list of names, or the structured person objects MyST and Docusaurus define. Attribution rather than answerability \u2014 `owner` is the party on the hook for the page now, and the two part company the moment an author moves on. Not a `stringList`, and not the only field here that isn't: `verified-against` and `source-of-truth` take the same string-or-object-or-list shape, as `entryList`. `authors` keeps its own inline copy rather than sharing that definition, because MyST and Docusaurus claim this key too and the composability law holds it at their loosest published definition, which `entryList`'s uniqueness and member floors go past. List members are names or person objects \u2014 never bare numbers or nulls. Never empty in any form: minLength, minItems and minProperties each bind to their own type and are ignored for the others.",
8398
+ items: {
8399
+ type: [
8400
+ "string",
8401
+ "object"
8402
+ ]
8403
+ },
8404
+ minLength: 1,
8405
+ minItems: 1,
8406
+ minProperties: 1,
8407
+ "x-manni-location": "external"
8408
+ },
8409
+ owner: {
8410
+ $ref: "#/$defs/stringList",
8411
+ description: "The person or team answerable for this page \u2014 the one required field of every docs-ownership system from Backstage to GitLab. A name, a handle, a chat channel, a team slug, or a list of them; write the spelling people can actually reach. A published graph does not carry it, because it names people.",
8412
+ "x-manni-location": "external",
8413
+ "x-manni-graph-output": false
8414
+ },
8415
+ stakeholders: {
8416
+ $ref: "#/$defs/stringList",
8417
+ description: "Who has a stake in this page being right: the feature's engineer, the product owner, the support lead who fields its tickets. The standing parties to consult before changing the page \u2014 where `reviewed-by` records who actually did the last pass. Page-level on purpose: the right stakeholders differ page by page, and project-wide ones belong in your project docs, not in every file's frontmatter. A published graph does not carry it, because it names people.",
8418
+ "x-manni-location": "external",
8419
+ "x-manni-graph-output": false
8420
+ },
8421
+ "reviewed-by": {
8422
+ $ref: "#/$defs/stringList",
8423
+ description: "Who last reviewed the page. Named `reviewed-by` rather than `reviewers` because MyST already defines `reviewers` as scholarly person objects. A published graph does not carry it, because it names people.",
8424
+ "x-manni-location": "external",
8425
+ "x-manni-graph-output": false
8426
+ },
8427
+ created: {
8428
+ $ref: "#/$defs/w3cdtf",
8429
+ description: "When the document was written, as a W3CDTF date. The document's date, not the file's: a page migrated from a CMS, imported from another repo, split out of a longer one, or landed through a squashed history has a path in this repo that is younger than the content it holds, and `git log` can only report the path. Where the two agree, git agreeing is a bonus rather than the source.",
8430
+ "x-manni-location": "external"
8431
+ },
8432
+ "last-updated": {
8433
+ $ref: "#/$defs/w3cdtf",
8434
+ description: "When the content last changed in a way a reader would notice, as a W3CDTF date. The editorial fact, not the file's: a commit date also moves for a typo fix, a link sweep, a formatting pass and a bulk frontmatter migration, none of which change what the page says. Distinct from `last-reviewed`, which records the page being checked and found still true \u2014 an update changes the content, a review confirms it, and a page can have either without the other. Spelled with the `last-` prefix for exactly the reason `last-reviewed` carries one: both name the most recent instance of a repeating event, and the two read as the pair they are. That also keeps the key clear of every neighbour's spelling of the same fact, which are `lastmod`, `last_update`, `lastUpdated` and `article:modified_time`. A stale value here is the accepted cost of stamping a date instead of deriving one, and a check comparing it against the repo's own last-change date is the tooling answer, not a schema's.",
8435
+ "x-manni-location": "page"
8436
+ },
8437
+ "last-reviewed": {
8438
+ $ref: "#/$defs/w3cdtf",
8439
+ description: "When the page was last reviewed, as a W3CDTF date. The field freshness tooling reads to derive review deadlines. A record, not a freshness gate \u2014 JSON Schema cannot compare a date to today.",
8440
+ "x-manni-location": "external"
8441
+ },
8442
+ "review-interval": {
8443
+ type: "string",
8444
+ pattern: "^P(?:\\d+W|(?=\\d|T\\d)(?:\\d+Y)?(?:\\d+M)?(?:\\d+D)?(?:T(?=\\d)(?:\\d+H)?(?:\\d+M)?(?:\\d+S)?)?)$",
8445
+ description: "How often the page should be reviewed, as an ISO 8601 duration: `P90D`, `P1Y`. ISO rather than `90d` so the value is machine-comparable. With `last-reviewed`, this is everything review tooling needs to derive the due date \u2014 a stored due date would only ever agree with them or lie.",
8446
+ "x-manni-location": "external"
8447
+ },
8448
+ "verified-against": {
8449
+ $ref: "#/$defs/entryList",
8450
+ description: 'The product version, API revision, or spec the content was last verified against. A string is the short spelling \u2014 `operator 1.4.2` \u2014 and an object the checkable one, such as `{name: operator, version: 1.4.2}`, which a drift check can compare without parsing prose. A list says a page was verified against more than one thing, such as the operator and the cluster it runs on. What turns "reviewed" into "reviewed against something".',
8451
+ "x-manni-location": "external"
8452
+ },
8453
+ "source-of-truth": {
8454
+ $ref: "#/$defs/entryList",
8455
+ description: "Where reality lives for this page: a repo path, an API spec, a schema file. A string names it, an object structures it \u2014 `{path: charts/operator/values.yaml, kind: helm-values}` \u2014 and a list carries several, because a page usually answers to more than one source. The anchor a drift check compares the page against; a page without one cannot be drift-monitored.",
8456
+ "x-manni-location": "external"
8457
+ }
8458
+ },
8459
+ $defs: {
8460
+ stringList: {
8461
+ if: {
8462
+ type: "array"
8463
+ },
8464
+ then: {
8465
+ type: "array",
8466
+ minItems: 1,
8467
+ uniqueItems: true,
8468
+ items: {
8469
+ type: "string",
8470
+ minLength: 1
8471
+ }
8472
+ },
8473
+ else: {
8474
+ type: "string",
8475
+ minLength: 1
8476
+ },
8477
+ description: "One non-empty string, or a non-empty list of unique ones \u2014 the same shape as the `labelList` of manni:graph:1.0.0."
8478
+ },
8479
+ entryList: {
8480
+ type: [
8481
+ "string",
8482
+ "object",
8483
+ "array"
8484
+ ],
8485
+ items: {
8486
+ $comment: "The parent's keyword-per-type pattern again, one level down: minLength binds to a string member, minProperties to an object one, and each is ignored for the other. Repeated here rather than hoisted because a member's floor is a different assertion from the whole value's.",
8487
+ type: [
8488
+ "string",
8489
+ "object"
8490
+ ],
8491
+ minLength: 1,
8492
+ minProperties: 1
8493
+ },
8494
+ minLength: 1,
8495
+ minItems: 1,
8496
+ minProperties: 1,
8497
+ uniqueItems: true,
8498
+ description: 'One entry, or a non-empty list of unique ones, where an entry is a non-empty string or a non-empty object. The shape `authors` carries, made a definition because two more fields need it: the short human spelling and the structured machine-checkable one, without minting a second field name for the same fact. Object keys are left open on purpose, and the recommendations live in each field\'s description. Every non-empty floor binds to its own type \u2014 minLength for the string, minProperties for the object, minItems and uniqueItems for the list \u2014 and `items` repeats the first two, so `""`, `[]`, `{}`, `[""]` and `[{}]` all fail. Unlike `authors`, no other schema claims the keys that use this, so the family is free to set that floor.',
8499
+ $comment: "minLength, minItems, minProperties and uniqueItems each apply to one instance type and are ignored for the others; that is what lets one subschema hold three shapes without an anyOf whose errors name all three branches."
8500
+ },
8501
+ w3cdtf: {
8502
+ type: "string",
8503
+ pattern: "^[0-9]{4}(-(0[1-9]|1[0-2])(-(0[1-9]|[12][0-9]|3[01])(T([01][0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9](\\.[0-9]+)?)?(Z|[+-]([01][0-9]|2[0-3]):[0-5][0-9]))?)?)?$",
8504
+ description: "A W3CDTF date, permitting reduced precision: `2026`, `2026-08`, `2026-08-23`, or a full timestamp \u2014 in which case the timezone designator is required, as the W3CDTF profile itself mandates (no local-time form). Field-ranged (month 01-12, day 01-31, hour 00-23) so an impossible date fails here rather than becoming Invalid Date in the tooling that derives review deadlines; not calendar-exact \u2014 February 31 is a reviewer's catch."
8505
+ }
8506
+ }
8507
+ };
8508
+
8509
+ // src/meta/schemas/stewardship-strict/1.1.0.json
8510
+ var __default49 = {
8511
+ $schema: "https://json-schema.org/draft/2020-12/schema",
8512
+ $id: "manni:stewardship-strict:1.1.0",
8513
+ title: "manni stewardship strict overlay v1.1.0",
8514
+ description: "The strict overlay for manni:stewardship:1.1.0. Stack it beside the open vocabulary. It narrows the form of values the open vocabulary already claims and requires no key.",
8515
+ additionalProperties: true,
8516
+ type: "object",
8517
+ properties: {
8518
+ authors: {
8519
+ type: "array",
8520
+ uniqueItems: true,
8521
+ items: {
8522
+ pattern: "^\\S(?:.*\\S)?$",
8523
+ required: [
8524
+ "name"
8525
+ ],
8526
+ properties: {
8527
+ name: {
8528
+ type: "string",
8529
+ pattern: "^\\S(?:.*\\S)?$"
8530
+ },
8531
+ email: {
8532
+ type: "string",
8533
+ pattern: "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$"
8534
+ },
8535
+ url: {
8536
+ type: "string",
8537
+ pattern: "^https?://\\S+$"
8538
+ }
8539
+ }
8540
+ },
8541
+ description: "Strict takes authors as a list of unique entries. A string entry is one line with no surrounding space. A person object names the person, and its `email` and `url` are well formed."
8542
+ },
8543
+ owner: {
8544
+ $ref: "#/$defs/handles",
8545
+ description: "Strict holds each owner to a handle a GitHub or GitLab CODEOWNERS file accepts, in the string form and the list form alike."
8546
+ },
8547
+ stakeholders: {
8548
+ $ref: "#/$defs/names",
8549
+ description: "Strict holds each stakeholder to one line with no surrounding space. A stakeholder is a person or team, named in any form."
8550
+ },
8551
+ "reviewed-by": {
8552
+ $ref: "#/$defs/names",
8553
+ description: "Strict holds each reviewer to one line with no surrounding space. `manni meta derive` writes review-API logins and commit-trailer names, so no handle form is required."
8554
+ },
8555
+ created: {
8556
+ $ref: "#/$defs/date",
8557
+ description: "Strict holds the creation date to an RFC 3339 full date or date-time. A year or month alone fails, and so does a timestamp without seconds."
8558
+ },
8559
+ "last-updated": {
8560
+ $ref: "#/$defs/date",
8561
+ description: "Strict holds the update date to an RFC 3339 full date or date-time. A year or month alone fails, and so does a timestamp without seconds."
8562
+ },
8563
+ "last-reviewed": {
8564
+ $ref: "#/$defs/date",
8565
+ description: "Strict holds the review date to an RFC 3339 full date or date-time. A year or month alone fails, and so does a timestamp without seconds."
8566
+ },
8567
+ "verified-against": {
8568
+ required: [
8569
+ "name",
8570
+ "version"
8571
+ ],
8572
+ items: {
8573
+ required: [
8574
+ "name",
8575
+ "version"
8576
+ ]
8577
+ },
8578
+ description: "Strict requires an object entry to carry `name` and `version`, alone or in a list. The version's format stays free, and a string entry is unchanged. The top-level `required` holds the single-object form, and `items` holds each object in a list, since `required` applies only to an object."
8579
+ },
8580
+ "source-of-truth": {
8581
+ $ref: "#/$defs/source",
8582
+ items: {
8583
+ $ref: "#/$defs/source"
8584
+ },
8585
+ description: "Strict requires an object entry to carry `path` or `url`, alone or in a list. A string entry is one id, path or URL on one line with no surrounding space. The top-level `$ref` holds the string and single-object forms, and `items` holds each entry in a list, since `required` applies only to an object."
8586
+ }
8587
+ },
8588
+ $defs: {
8589
+ handles: {
8590
+ pattern: "^(?:@@?[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?(?:/[A-Za-z0-9._-]+)*|[^\\s@]+@[^\\s@]+\\.[^\\s@]+)$",
8591
+ items: {
8592
+ pattern: "^(?:@@?[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?(?:/[A-Za-z0-9._-]+)*|[^\\s@]+@[^\\s@]+\\.[^\\s@]+)$"
8593
+ },
8594
+ description: "A handle a CODEOWNERS file accepts, as a string or as each member of a list. That is `@user`, `@org/team`, a GitLab nested group or `@@role`, or an email address."
8595
+ },
8596
+ names: {
8597
+ pattern: "^\\S(?:.*\\S)?$",
8598
+ items: {
8599
+ pattern: "^\\S(?:.*\\S)?$"
8600
+ },
8601
+ description: "One line with no surrounding space, as a string or as each member of a list."
8602
+ },
8603
+ date: {
8604
+ type: "string",
8605
+ pattern: "^[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12][0-9]|3[01])(?:T(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:\\.[0-9]+)?(?:Z|[+-](?:[01][0-9]|2[0-3]):[0-5][0-9]))?$",
8606
+ description: "An RFC 3339 full date, or a date-time with seconds and an offset."
8607
+ },
8608
+ source: {
8609
+ pattern: "^\\S(?:.*\\S)?$",
8610
+ anyOf: [
8611
+ {
8612
+ required: [
8613
+ "path"
8614
+ ]
8615
+ },
8616
+ {
8617
+ required: [
8618
+ "url"
8619
+ ]
8620
+ }
8621
+ ],
8622
+ description: "A source entry. A string is one id, path or URL on one line with no surrounding space. An object carries `path` or `url`."
8623
+ }
8624
+ }
8625
+ };
8626
+
8347
8627
  // src/meta/core/schema-registry.ts
8348
8628
  var BUILTINS = /* @__PURE__ */ new Map([
8349
8629
  ["google:okf:0.1", __default],
@@ -8392,7 +8672,13 @@ var BUILTINS = /* @__PURE__ */ new Map([
8392
8672
  ["manni:graph-strict:1.0.0", __default44],
8393
8673
  ["manni:citations-strict:1.0.0", __default45],
8394
8674
  ["tgdp:templates:1.1", __default46],
8395
- ["tgdp:templates-strict:1.1", __default47]
8675
+ ["tgdp:templates-strict:1.1", __default47],
8676
+ ["manni:stewardship:1.1.0", __default48],
8677
+ ["manni:stewardship-strict:1.1.0", __default49]
8678
+ ]);
8679
+ var ANNOTATION_ONLY_PREDECESSORS = /* @__PURE__ */ new Map([
8680
+ ["manni:stewardship:1.1.0", "manni:stewardship:1.0.0"],
8681
+ ["manni:stewardship-strict:1.1.0", "manni:stewardship-strict:1.0.0"]
8396
8682
  ]);
8397
8683
  function assertPublishableBuiltinId(id) {
8398
8684
  const first = id.split(":")[0];
@@ -8485,7 +8771,9 @@ var PUBLISHED_PATHS = [
8485
8771
  ["graph-strict/1.0.0.json", "manni:graph-strict:1.0.0"],
8486
8772
  ["citations-strict/1.0.0.json", "manni:citations-strict:1.0.0"],
8487
8773
  ["tgdp/1.1.json", "tgdp:templates:1.1"],
8488
- ["tgdp-strict/1.1.json", "tgdp:templates-strict:1.1"]
8774
+ ["tgdp-strict/1.1.json", "tgdp:templates-strict:1.1"],
8775
+ ["stewardship/1.1.0.json", "manni:stewardship:1.1.0"],
8776
+ ["stewardship-strict/1.1.0.json", "manni:stewardship-strict:1.1.0"]
8489
8777
  ];
8490
8778
  var PUBLISHED_ALIAS = new Map(
8491
8779
  PUBLISHED_PATHS.map(([path, id]) => [`${PUBLISHED_BASE}${path}`, id])
@@ -8578,8 +8866,8 @@ var SCHEMA_KEYS = [
8578
8866
  "multipleOf"
8579
8867
  ];
8580
8868
  var RETRY_DELAY_MS = 500;
8581
- var wait2 = (ms) => new Promise((resolve25) => {
8582
- setTimeout(resolve25, ms);
8869
+ var wait2 = (ms) => new Promise((resolve26) => {
8870
+ setTimeout(resolve26, ms);
8583
8871
  });
8584
8872
  var RetryableFetchError = class extends DocmetaError {
8585
8873
  };
@@ -8841,7 +9129,7 @@ var DEFAULT_SCHEMAS = Object.freeze([
8841
9129
  "manni:core:1.0.0",
8842
9130
  "manni:audience:1.0.0",
8843
9131
  "manni:structure:1.0.0",
8844
- "manni:stewardship:1.0.0",
9132
+ "manni:stewardship:1.1.0",
8845
9133
  "manni:lifecycle:1.0.0",
8846
9134
  "manni:ai-context:1.0.0",
8847
9135
  "manni:evals:1.0.0",
@@ -8849,7 +9137,14 @@ var DEFAULT_SCHEMAS = Object.freeze([
8849
9137
  "manni:citations:1.0.0"
8850
9138
  ]);
8851
9139
  var FILE_SCHEMA_KEY = "$schema";
8852
- var DEFAULT_SCHEMA_SET = new Set(DEFAULT_SCHEMAS);
9140
+ function vocabularyOf(id) {
9141
+ const parts = id.split(":");
9142
+ if (parts.length !== 3) return void 0;
9143
+ return `${parts[0] ?? ""}:${parts[1] ?? ""}`;
9144
+ }
9145
+ var DEFAULT_VOCABULARIES = new Set(
9146
+ DEFAULT_SCHEMAS.map(vocabularyOf).filter((v) => v !== void 0)
9147
+ );
8853
9148
  function schemaEntryRef(entry) {
8854
9149
  return typeof entry === "string" ? entry : entry.ref;
8855
9150
  }
@@ -8960,14 +9255,34 @@ function withStrict(refs, registered2) {
8960
9255
  out.push(ref);
8961
9256
  const strictId = strictIdOf(ref);
8962
9257
  if (strictId === void 0) continue;
8963
- if (DEFAULT_SCHEMA_SET.has(ref) && isBuiltinId(strictId)) out.push(strictId);
9258
+ const vocabulary = vocabularyOf(ref);
9259
+ const isDefault = vocabulary !== void 0 && DEFAULT_VOCABULARIES.has(vocabulary);
9260
+ if (isDefault && isBuiltinId(ref) && isBuiltinId(strictId)) out.push(strictId);
8964
9261
  else if (registered2?.has(ref) === true && registered2.has(strictId)) out.push(strictId);
8965
9262
  }
8966
9263
  return dedupe(out);
8967
9264
  }
9265
+ function withoutReplacedVersions(set, listed) {
9266
+ const explicit = /* @__PURE__ */ new Set();
9267
+ for (const ref of listed) {
9268
+ if (!isBuiltinId(ref)) continue;
9269
+ explicit.add(ref);
9270
+ const strictId = strictIdOf(ref);
9271
+ if (strictId !== void 0) explicit.add(strictId);
9272
+ }
9273
+ const pinned = new Set(
9274
+ [...explicit].map(vocabularyOf).filter((v) => v !== void 0)
9275
+ );
9276
+ return set.filter((ref) => {
9277
+ if (explicit.has(ref) || !isBuiltinId(ref)) return true;
9278
+ const vocabulary = vocabularyOf(ref);
9279
+ return vocabulary === void 0 || !pinned.has(vocabulary);
9280
+ });
9281
+ }
8968
9282
  function joined(listed, defaults, strict, registered2) {
8969
9283
  const set = dedupe([...defaults ? DEFAULT_SCHEMAS : [], ...listed]);
8970
- return strict ? withStrict(set, registered2) : set;
9284
+ const paired = strict ? withStrict(set, registered2) : set;
9285
+ return defaults ? withoutReplacedVersions(paired, listed) : paired;
8971
9286
  }
8972
9287
  function isDefaultSetOnly(resolved) {
8973
9288
  if (!resolved.defaults) return false;
@@ -9146,12 +9461,12 @@ function isPlainObject(v) {
9146
9461
  }
9147
9462
 
9148
9463
  // src/meta/core/config.ts
9149
- import { resolve as resolve8 } from "node:path";
9464
+ import { resolve as resolve9 } from "node:path";
9150
9465
  import { parse as parseYaml2 } from "yaml";
9151
9466
 
9152
9467
  // src/shared/config-file.ts
9153
- import { readFile as readFile3 } from "node:fs/promises";
9154
- import { basename as basename2, dirname as dirname3, join as join4, relative as relative3, resolve as resolve6 } from "node:path";
9468
+ import { readFileSync as readFileSync2 } from "node:fs";
9469
+ import { basename as basename2, dirname as dirname3, join as join4, relative as relative3, resolve as resolve7 } from "node:path";
9155
9470
  import { parse as parseYaml } from "yaml";
9156
9471
 
9157
9472
  // src/shared/encryption.ts
@@ -9280,23 +9595,230 @@ function keyedPin(text2, key) {
9280
9595
  return `${KEYED_PIN_PREFIX}${mac}`;
9281
9596
  }
9282
9597
 
9283
- // src/shared/git-root.ts
9284
- import { existsSync } from "node:fs";
9285
- import { dirname as dirname2, join as join3, resolve as resolve4 } from "node:path";
9286
- function findGitRoot(cwd) {
9287
- let dir = resolve4(cwd);
9288
- for (; ; ) {
9289
- if (existsSync(join3(dir, ".git"))) return dir;
9290
- const parent = dirname2(dir);
9291
- if (parent === dir) return null;
9292
- dir = parent;
9293
- }
9294
- }
9295
- function searchPath(cwd) {
9296
- const start = resolve4(cwd);
9297
- const root = findGitRoot(start);
9298
- if (root === null) return [start];
9299
- const chain = [];
9598
+ // src/shared/providers.ts
9599
+ import { resolve as resolve4 } from "node:path";
9600
+ import {
9601
+ DEFAULT_MODELS,
9602
+ DEFAULT_OPENAI_BASE_URL,
9603
+ InferenceError,
9604
+ resolveProviderIdentityAsync
9605
+ } from "@hawkeyexl/inference";
9606
+ var DEFAULT_PROVIDER = "auto";
9607
+ var PROVIDERS = /* @__PURE__ */ new Set([
9608
+ ...Object.keys(DEFAULT_MODELS),
9609
+ DEFAULT_PROVIDER
9610
+ ]);
9611
+ var NAMEABLE_PROVIDERS = Object.keys(DEFAULT_MODELS).filter(
9612
+ (name2) => name2 !== "mock"
9613
+ );
9614
+ function assertKnownProvider(name2, toError) {
9615
+ if (PROVIDERS.has(name2)) return;
9616
+ throw toError(
9617
+ `Unknown provider "${name2}". Available: ${[...NAMEABLE_PROVIDERS, DEFAULT_PROVIDER].join(", ")}.`
9618
+ );
9619
+ }
9620
+ function assertModelHasProvider(name2, model, configKey, toError) {
9621
+ if (name2 !== DEFAULT_PROVIDER || model == null) return;
9622
+ throw toError(
9623
+ `Model "${model}" was given without a provider: a model name does not say which provider owns it. Set --provider or ${configKey} to one of ${NAMEABLE_PROVIDERS.join(", ")}, or drop the model to take the detected provider's default.`
9624
+ );
9625
+ }
9626
+ var PROVIDERS_KEY = "providers";
9627
+ var CONNECTION_KEYS = {
9628
+ anthropic: ["apiKeyEnv"],
9629
+ openai: ["baseUrl", "apiKeyEnv"],
9630
+ "claude-cli": ["command"],
9631
+ "llama-cpp": ["modelsDir", "thoughtTokens"]
9632
+ };
9633
+ var CONNECTION_NAMES = Object.keys(CONNECTION_KEYS);
9634
+ var TOP_LEVEL_KEYS = ["provider", "model", ...CONNECTION_NAMES];
9635
+ function isMapping2(value) {
9636
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9637
+ }
9638
+ function rejectUnknown(raw, allowed, label, source, toError) {
9639
+ for (const key of Object.keys(raw)) {
9640
+ if (!allowed.includes(key)) {
9641
+ throw toError(
9642
+ `${source}: "${label}" has unknown key "${key}". Supported keys: ${allowed.join(", ")}.`
9643
+ );
9644
+ }
9645
+ }
9646
+ }
9647
+ function parseProviders(value, source, dir, toError) {
9648
+ if (value == null) return {};
9649
+ if (!isMapping2(value)) {
9650
+ throw toError(`${source}: "${PROVIDERS_KEY}" must be a mapping.`);
9651
+ }
9652
+ rejectUnknown(value, TOP_LEVEL_KEYS, PROVIDERS_KEY, source, toError);
9653
+ const string = (raw, key, label) => {
9654
+ const v = raw[key];
9655
+ if (v === void 0) return void 0;
9656
+ if (typeof v !== "string") {
9657
+ throw toError(`${source}: "${label}" must be a string.`);
9658
+ }
9659
+ return v;
9660
+ };
9661
+ const parsed2 = {};
9662
+ const provider = string(value, "provider", `${PROVIDERS_KEY}.provider`);
9663
+ if (provider !== void 0) {
9664
+ assertKnownProvider(provider, (message) => toError(`${source}: ${message}`));
9665
+ parsed2.provider = provider;
9666
+ }
9667
+ const model = string(value, "model", `${PROVIDERS_KEY}.model`);
9668
+ if (model !== void 0) {
9669
+ if (provider === void 0 || provider === DEFAULT_PROVIDER) {
9670
+ throw toError(
9671
+ `${source}: "${PROVIDERS_KEY}.model" was given without a provider: a model name does not say which provider owns it. Set ${PROVIDERS_KEY}.provider to one of ${NAMEABLE_PROVIDERS.join(", ")}, or drop the model to take the detected provider's default.`
9672
+ );
9673
+ }
9674
+ parsed2.model = model;
9675
+ }
9676
+ for (const name2 of CONNECTION_NAMES) {
9677
+ const section = value[name2];
9678
+ if (section == null) continue;
9679
+ const label = `${PROVIDERS_KEY}.${name2}`;
9680
+ if (!isMapping2(section)) {
9681
+ throw toError(`${source}: "${label}" must be a mapping.`);
9682
+ }
9683
+ rejectUnknown(section, CONNECTION_KEYS[name2], label, source, toError);
9684
+ const field = (key) => string(section, key, `${label}.${key}`);
9685
+ switch (name2) {
9686
+ case "anthropic": {
9687
+ const apiKeyEnv = field("apiKeyEnv");
9688
+ parsed2.anthropic = apiKeyEnv === void 0 ? {} : { apiKeyEnv };
9689
+ break;
9690
+ }
9691
+ case "openai": {
9692
+ const baseUrl = field("baseUrl");
9693
+ const apiKeyEnv = field("apiKeyEnv");
9694
+ parsed2.openai = {
9695
+ ...baseUrl !== void 0 ? { baseUrl } : {},
9696
+ ...apiKeyEnv !== void 0 ? { apiKeyEnv } : {}
9697
+ };
9698
+ break;
9699
+ }
9700
+ case "claude-cli": {
9701
+ const command = field("command");
9702
+ parsed2["claude-cli"] = command === void 0 ? {} : { command };
9703
+ break;
9704
+ }
9705
+ case "llama-cpp": {
9706
+ const modelsDir = field("modelsDir");
9707
+ const thoughtTokens = section["thoughtTokens"];
9708
+ if (thoughtTokens !== void 0 && (typeof thoughtTokens !== "number" || !Number.isInteger(thoughtTokens) || thoughtTokens < 0)) {
9709
+ throw toError(
9710
+ `${source}: "${label}.thoughtTokens" must be a whole number of 0 or more, got ${JSON.stringify(thoughtTokens)}.`
9711
+ );
9712
+ }
9713
+ parsed2["llama-cpp"] = {
9714
+ ...modelsDir !== void 0 ? { modelsDir: resolve4(dir, modelsDir) } : {},
9715
+ ...thoughtTokens !== void 0 ? { thoughtTokens } : {}
9716
+ };
9717
+ break;
9718
+ }
9719
+ }
9720
+ }
9721
+ return parsed2;
9722
+ }
9723
+ var LOCAL_PROVIDER = "llama-cpp";
9724
+ var LOCAL_FLAG_HELP = "run inference on this machine (llama-cpp); overrides any configured or eval-level provider";
9725
+ var LOCAL_COMPATIBLE = /* @__PURE__ */ new Set([LOCAL_PROVIDER, DEFAULT_PROVIDER, "mock"]);
9726
+ function assertLocalFlag(flag, toError) {
9727
+ if (flag.local !== true || flag.provider === void 0) return;
9728
+ assertKnownProvider(flag.provider, toError);
9729
+ if (LOCAL_COMPATIBLE.has(flag.provider)) return;
9730
+ throw toError(
9731
+ `--local and --provider ${flag.provider} contradict each other: --local runs inference on this machine with ${LOCAL_PROVIDER}. Drop one of them.`
9732
+ );
9733
+ }
9734
+ function selectProvider(flag, levels) {
9735
+ if (flag.local === true) return selectLocal(flag, levels);
9736
+ const provider = flag.provider ?? levels.find((level) => level.provider !== void 0)?.provider ?? DEFAULT_PROVIDER;
9737
+ const model = flag.model ?? levels.find(
9738
+ (level) => level.model !== void 0 && (level.provider === void 0 || level.provider === provider)
9739
+ )?.model;
9740
+ return { provider, model };
9741
+ }
9742
+ function selectLocal(flag, levels) {
9743
+ const provider = flag.provider === "mock" ? "mock" : LOCAL_PROVIDER;
9744
+ const model = flag.model ?? levels.find((level) => level.model !== void 0 && level.provider === provider)?.model;
9745
+ const chosen = flag.provider === void 0 ? levels.find((level) => level.provider !== void 0) : void 0;
9746
+ const replaced = chosen?.provider !== void 0 && chosen.origin !== void 0 && chosen.provider !== provider && chosen.provider !== DEFAULT_PROVIDER ? { provider: chosen.provider, source: chosen.origin } : void 0;
9747
+ return replaced === void 0 ? { provider, model } : { provider, model, replaced };
9748
+ }
9749
+ function localNotice(selection) {
9750
+ const { replaced } = selection;
9751
+ if (replaced === void 0) return void 0;
9752
+ return `--local: using ${LOCAL_PROVIDER} instead of "${replaced.provider}" from ${replaced.source}.`;
9753
+ }
9754
+ function providerSpecFor(connections, selection) {
9755
+ const { provider, model } = selection;
9756
+ const local = connections["llama-cpp"] ?? {};
9757
+ const llamaCpp = {
9758
+ thoughtTokens: local.thoughtTokens ?? 0,
9759
+ ...local.modelsDir !== void 0 ? { modelsDirectory: local.modelsDir } : {}
9760
+ };
9761
+ switch (provider) {
9762
+ case "anthropic":
9763
+ return {
9764
+ provider,
9765
+ model,
9766
+ apiKeyEnv: connections.anthropic?.apiKeyEnv ?? "ANTHROPIC_API_KEY"
9767
+ };
9768
+ case "openai":
9769
+ return {
9770
+ provider,
9771
+ model,
9772
+ apiKeyEnv: connections.openai?.apiKeyEnv ?? "OPENAI_API_KEY",
9773
+ baseUrl: connections.openai?.baseUrl ?? DEFAULT_OPENAI_BASE_URL
9774
+ };
9775
+ case "claude-cli":
9776
+ return { provider, model, command: connections["claude-cli"]?.command ?? "claude" };
9777
+ case "llama-cpp":
9778
+ return { provider, model, llamaCpp };
9779
+ case "mock":
9780
+ return { provider, model };
9781
+ case "auto": {
9782
+ const baseUrl = connections.openai?.baseUrl;
9783
+ const command = connections["claude-cli"]?.command;
9784
+ const configuredLocal = local.thoughtTokens !== void 0 || local.modelsDir !== void 0;
9785
+ return {
9786
+ provider,
9787
+ model,
9788
+ ...baseUrl !== void 0 ? { baseUrl } : {},
9789
+ ...command !== void 0 ? { command } : {},
9790
+ ...configuredLocal ? { llamaCpp } : {}
9791
+ };
9792
+ }
9793
+ }
9794
+ }
9795
+ async function resolveIdentity(spec, toError) {
9796
+ try {
9797
+ return await resolveProviderIdentityAsync(spec);
9798
+ } catch (err) {
9799
+ throw toError(
9800
+ err instanceof InferenceError ? err.message : `Could not resolve provider "${String(spec.provider)}": ${errorMessage(err)}`
9801
+ );
9802
+ }
9803
+ }
9804
+
9805
+ // src/shared/git-root.ts
9806
+ import { existsSync } from "node:fs";
9807
+ import { dirname as dirname2, join as join3, resolve as resolve5 } from "node:path";
9808
+ function findGitRoot(cwd) {
9809
+ let dir = resolve5(cwd);
9810
+ for (; ; ) {
9811
+ if (existsSync(join3(dir, ".git"))) return dir;
9812
+ const parent = dirname2(dir);
9813
+ if (parent === dir) return null;
9814
+ dir = parent;
9815
+ }
9816
+ }
9817
+ function searchPath(cwd) {
9818
+ const start = resolve5(cwd);
9819
+ const root = findGitRoot(start);
9820
+ if (root === null) return [start];
9821
+ const chain = [];
9300
9822
  let dir = start;
9301
9823
  for (; ; ) {
9302
9824
  chain.push(dir);
@@ -9308,11 +9830,11 @@ function searchPath(cwd) {
9308
9830
  }
9309
9831
 
9310
9832
  // src/shared/tools.ts
9311
- import { isAbsolute as isAbsolute3, resolve as resolve5 } from "node:path";
9833
+ import { isAbsolute as isAbsolute3, resolve as resolve6 } from "node:path";
9312
9834
  var TOOL_KEYS = ["vale", "dita-ot"];
9313
9835
  var VALE_KEYS = ["config"];
9314
9836
  var DITA_OT_KEYS = ["home"];
9315
- function isMapping2(value) {
9837
+ function isMapping3(value) {
9316
9838
  return typeof value === "object" && value !== null && !Array.isArray(value);
9317
9839
  }
9318
9840
  function rejectUnknownKeys2(raw, allowed, where, source, toError) {
@@ -9326,7 +9848,7 @@ function rejectUnknownKeys2(raw, allowed, where, source, toError) {
9326
9848
  }
9327
9849
  function parseTools(raw, source, toError) {
9328
9850
  if (raw === null || raw === void 0) return {};
9329
- if (!isMapping2(raw)) {
9851
+ if (!isMapping3(raw)) {
9330
9852
  throw toError(`${source}: "tools" must be a mapping.`);
9331
9853
  }
9332
9854
  rejectUnknownKeys2(raw, TOOL_KEYS, "tools", source, toError);
@@ -9340,7 +9862,7 @@ function parseTools(raw, source, toError) {
9340
9862
  return tools;
9341
9863
  }
9342
9864
  function parseVale(raw, source, toError) {
9343
- if (!isMapping2(raw)) {
9865
+ if (!isMapping3(raw)) {
9344
9866
  throw toError(`${source}: tools.vale must be a mapping.`);
9345
9867
  }
9346
9868
  rejectUnknownKeys2(raw, VALE_KEYS, "tools.vale", source, toError);
@@ -9355,7 +9877,7 @@ function parseVale(raw, source, toError) {
9355
9877
  return vale;
9356
9878
  }
9357
9879
  function parseDitaOt(raw, source, toError) {
9358
- if (!isMapping2(raw)) {
9880
+ if (!isMapping3(raw)) {
9359
9881
  throw toError(`${source}: tools.dita-ot must be a mapping.`);
9360
9882
  }
9361
9883
  rejectUnknownKeys2(raw, DITA_OT_KEYS, "tools.dita-ot", source, toError);
@@ -9372,12 +9894,12 @@ function parseDitaOt(raw, source, toError) {
9372
9894
  function valeConfigPath(tools, configDir) {
9373
9895
  const config = tools.vale?.config;
9374
9896
  if (config === void 0) return void 0;
9375
- return isAbsolute3(config) ? config : resolve5(configDir, config);
9897
+ return isAbsolute3(config) ? config : resolve6(configDir, config);
9376
9898
  }
9377
9899
  function ditaOtHome(tools, configDir) {
9378
9900
  const home = tools["dita-ot"]?.home;
9379
9901
  if (home === void 0) return void 0;
9380
- return isAbsolute3(home) ? home : resolve5(configDir, home);
9902
+ return isAbsolute3(home) ? home : resolve6(configDir, home);
9381
9903
  }
9382
9904
 
9383
9905
  // src/shared/warn.ts
@@ -9392,22 +9914,24 @@ function notice(message) {
9392
9914
  process.stderr.write(`${programName()}: ${message}
9393
9915
  `);
9394
9916
  }
9917
+ var noticed = /* @__PURE__ */ new Set();
9918
+ function noticeOnce(message) {
9919
+ if (noticed.has(message)) return;
9920
+ noticed.add(message);
9921
+ notice(message);
9922
+ }
9395
9923
 
9396
9924
  // src/shared/config-file.ts
9397
9925
  var FAMILY_CONFIG_NAMES = [
9398
9926
  "manni.config.yaml",
9399
9927
  "manni.config.yml"
9400
9928
  ];
9401
- var MOOSE_CONFIG_NAMES = [
9402
- "moose.config.yaml",
9403
- "moose.config.yml"
9404
- ];
9405
9929
  var ENCRYPTION_KEY_FIELD = "encryptionKey";
9406
9930
  var ENCRYPTION_KEY_PREVIOUS_FIELD = "encryptionKeyPrevious";
9407
- async function readDocument(path, source, toError) {
9931
+ function readDocument(path, source, toError) {
9408
9932
  let text2;
9409
9933
  try {
9410
- text2 = await readFile3(path, "utf8");
9934
+ text2 = readFileSync2(path, "utf8");
9411
9935
  } catch {
9412
9936
  return null;
9413
9937
  }
@@ -9428,7 +9952,12 @@ function parseMapping(text2, source, toError) {
9428
9952
  }
9429
9953
  var COLLECTIONS_KEY = "collections";
9430
9954
  var TOOLS_KEY = "tools";
9431
- var FAMILY_KEYS = [COLLECTIONS_KEY, TOOLS_KEY, ENCRYPTION_KEY_FIELD];
9955
+ var FAMILY_KEYS = [
9956
+ COLLECTIONS_KEY,
9957
+ TOOLS_KEY,
9958
+ ENCRYPTION_KEY_FIELD,
9959
+ PROVIDERS_KEY
9960
+ ];
9432
9961
  function slice(document, opts) {
9433
9962
  const { doc } = document;
9434
9963
  if (doc !== null && Object.hasOwn(doc, opts.section)) {
@@ -9473,6 +10002,11 @@ function encryptionKeyOf(document, source, toError) {
9473
10002
  }
9474
10003
  return keys;
9475
10004
  }
10005
+ function providersOf(document, source, dir, toError) {
10006
+ const { doc } = document;
10007
+ if (doc === null || !Object.hasOwn(doc, PROVIDERS_KEY)) return {};
10008
+ return { providers: parseProviders(doc[PROVIDERS_KEY], source, dir, toError) };
10009
+ }
9476
10010
  function tolerantKeysOf(document) {
9477
10011
  const key = document.doc?.[ENCRYPTION_KEY_FIELD];
9478
10012
  const previous = document.doc?.[ENCRYPTION_KEY_PREVIOUS_FIELD];
@@ -9484,42 +10018,33 @@ function tolerantKeysOf(document) {
9484
10018
  function relativeSource(cwd, path) {
9485
10019
  return relative3(cwd, path).replace(/\\/g, "/");
9486
10020
  }
9487
- function mooseWarning(name2) {
9488
- return `"${name2}" is the pre-rename name of the family config file. Rename it to "${FAMILY_CONFIG_NAMES[0] ?? "manni.config.yaml"}".`;
9489
- }
9490
- var FAMILY_FILES = [
9491
- { names: FAMILY_CONFIG_NAMES, kind: "manni" },
9492
- { names: MOOSE_CONFIG_NAMES, kind: "moose" }
9493
- ];
9494
- async function findConfigFile(cwd, opts) {
9495
- const start = resolve6(cwd);
10021
+ function findConfigFileSync(cwd, opts) {
10022
+ const start = resolve7(cwd);
9496
10023
  for (const dir of searchPath(start)) {
9497
- for (const { names, kind } of FAMILY_FILES) {
9498
- for (const name2 of names) {
9499
- const path = join4(dir, name2);
9500
- const source = relativeSource(start, path);
9501
- const document = await readDocument(path, source, opts.toError);
9502
- if (document === null) continue;
9503
- const found = slice(document, opts);
9504
- if (found === null) continue;
9505
- if (kind === "moose") warn(mooseWarning(name2));
9506
- return {
9507
- path,
9508
- dir,
9509
- source,
9510
- text: document.text,
9511
- kind,
9512
- collections: collectionsOf(document, source, opts.toError),
9513
- tools: toolsOf(document, source, opts.toError),
9514
- ...encryptionKeyOf(document, source, opts.toError),
9515
- ...found
9516
- };
9517
- }
10024
+ for (const name2 of FAMILY_CONFIG_NAMES) {
10025
+ const path = join4(dir, name2);
10026
+ const source = relativeSource(start, path);
10027
+ const document = readDocument(path, source, opts.toError);
10028
+ if (document === null) continue;
10029
+ const found = slice(document, opts);
10030
+ if (found === null) continue;
10031
+ return {
10032
+ path,
10033
+ dir,
10034
+ source,
10035
+ text: document.text,
10036
+ kind: "manni",
10037
+ collections: collectionsOf(document, source, opts.toError),
10038
+ tools: toolsOf(document, source, opts.toError),
10039
+ ...encryptionKeyOf(document, source, opts.toError),
10040
+ ...providersOf(document, source, dir, opts.toError),
10041
+ ...found
10042
+ };
9518
10043
  }
9519
10044
  for (const name2 of opts.legacyNames) {
9520
10045
  const path = join4(dir, name2);
9521
10046
  const source = relativeSource(start, path);
9522
- const document = await readDocument(path, source, opts.toError);
10047
+ const document = readDocument(path, source, opts.toError);
9523
10048
  if (document === null) continue;
9524
10049
  warn(
9525
10050
  `"${name2}" is a deprecated config file name. Move its keys under \`${opts.section}:\` in "${FAMILY_CONFIG_NAMES[0] ?? "manni.config.yaml"}", and its paths, exclude and sidecars keys to a top-level collections: list, where sidecars becomes externalMetadata.`
@@ -9539,9 +10064,9 @@ async function findConfigFile(cwd, opts) {
9539
10064
  }
9540
10065
  return null;
9541
10066
  }
9542
- async function readConfigFile(explicitPath, cwd, opts) {
9543
- const path = resolve6(cwd, explicitPath);
9544
- const document = await readDocument(path, explicitPath, opts.toError);
10067
+ function readConfigFileSync(explicitPath, cwd, opts) {
10068
+ const path = resolve7(cwd, explicitPath);
10069
+ const document = readDocument(path, explicitPath, opts.toError);
9545
10070
  if (document === null) {
9546
10071
  throw opts.toError(`Config file not found: "${explicitPath}".`);
9547
10072
  }
@@ -9560,44 +10085,58 @@ async function readConfigFile(explicitPath, cwd, opts) {
9560
10085
  // An unwrapped document carries no family key (else it would be wrapped),
9561
10086
  // so this only ever reads a family file's key.
9562
10087
  ...encryptionKeyOf(document, explicitPath, opts.toError),
10088
+ ...providersOf(document, explicitPath, dirname3(path), opts.toError),
9563
10089
  ...found
9564
10090
  };
9565
10091
  }
9566
- async function findFamilyConfigFile(cwd, toError) {
9567
- const start = resolve6(cwd);
10092
+ function findConfigFile(cwd, opts) {
10093
+ return Promise.resolve().then(() => findConfigFileSync(cwd, opts));
10094
+ }
10095
+ function readConfigFile(explicitPath, cwd, opts) {
10096
+ return Promise.resolve().then(
10097
+ () => readConfigFileSync(explicitPath, cwd, opts)
10098
+ );
10099
+ }
10100
+ function findFamilyConfigFile(cwd, toError) {
10101
+ return Promise.resolve().then(() => findFamilyConfigFileSync(cwd, toError));
10102
+ }
10103
+ function findFamilyConfigFileSync(cwd, toError) {
10104
+ const start = resolve7(cwd);
9568
10105
  for (const dir of searchPath(start)) {
9569
- for (const { names, kind } of FAMILY_FILES) {
9570
- for (const name2 of names) {
9571
- const path = join4(dir, name2);
9572
- const source = relativeSource(start, path);
9573
- const document = await readDocument(path, source, toError);
9574
- if (document === null) continue;
9575
- if (kind === "moose") warn(mooseWarning(name2));
9576
- return {
9577
- path,
9578
- dir,
9579
- source,
9580
- text: document.text,
9581
- value: null,
9582
- wrapped: true,
9583
- kind,
9584
- collections: collectionsOf(document, source, toError),
9585
- tools: toolsOf(document, source, toError),
9586
- ...tolerantKeysOf(document)
9587
- };
9588
- }
10106
+ for (const name2 of FAMILY_CONFIG_NAMES) {
10107
+ const path = join4(dir, name2);
10108
+ const source = relativeSource(start, path);
10109
+ const document = readDocument(path, source, toError);
10110
+ if (document === null) continue;
10111
+ return {
10112
+ path,
10113
+ dir,
10114
+ source,
10115
+ text: document.text,
10116
+ value: null,
10117
+ wrapped: true,
10118
+ kind: "manni",
10119
+ collections: collectionsOf(document, source, toError),
10120
+ tools: toolsOf(document, source, toError),
10121
+ ...tolerantKeysOf(document)
10122
+ };
9589
10123
  }
9590
10124
  }
9591
10125
  return null;
9592
10126
  }
9593
- async function readFamilyConfigFile(explicitPath, cwd, toError) {
9594
- const path = resolve6(cwd, explicitPath);
9595
- const document = await readDocument(path, explicitPath, toError);
10127
+ function readFamilyConfigFile(explicitPath, cwd, toError) {
10128
+ return Promise.resolve().then(
10129
+ () => readFamilyConfigFileSync(explicitPath, cwd, toError)
10130
+ );
10131
+ }
10132
+ function readFamilyConfigFileSync(explicitPath, cwd, toError) {
10133
+ const path = resolve7(cwd, explicitPath);
10134
+ const document = readDocument(path, explicitPath, toError);
9596
10135
  if (document === null) {
9597
10136
  throw toError(`Config file not found: "${explicitPath}".`);
9598
10137
  }
9599
10138
  const { doc } = document;
9600
- const named = [...FAMILY_CONFIG_NAMES, ...MOOSE_CONFIG_NAMES].includes(basename2(path));
10139
+ const named = FAMILY_CONFIG_NAMES.includes(basename2(path));
9601
10140
  const family = doc === null || named || FAMILY_KEYS.some((key) => Object.hasOwn(doc, key));
9602
10141
  return {
9603
10142
  path,
@@ -9614,8 +10153,8 @@ async function readFamilyConfigFile(explicitPath, cwd, toError) {
9614
10153
  }
9615
10154
 
9616
10155
  // src/meta/core/register.ts
9617
- import { readFile as readFile4, readdir, stat as stat4 } from "node:fs/promises";
9618
- import { join as join5, relative as relative4, resolve as resolve7, sep as sep2 } from "node:path";
10156
+ import { readFile as readFile3, readdir, stat as stat4 } from "node:fs/promises";
10157
+ import { join as join5, relative as relative4, resolve as resolve8, sep as sep2 } from "node:path";
9619
10158
  var RESERVED_VENDORS = [
9620
10159
  "manni",
9621
10160
  "check",
@@ -9671,7 +10210,7 @@ function shown(configDir, path) {
9671
10210
  async function loadRegisteredSchemas(register2, configDir) {
9672
10211
  const files = [];
9673
10212
  for (const [i, entry] of register2.entries()) {
9674
- const path = resolve7(configDir, entry);
10213
+ const path = resolve8(configDir, entry);
9675
10214
  let entryStats;
9676
10215
  try {
9677
10216
  entryStats = await stat4(path);
@@ -9701,7 +10240,7 @@ async function loadRegisteredSchemas(register2, configDir) {
9701
10240
  const file = shown(configDir, path);
9702
10241
  let parsed2;
9703
10242
  try {
9704
- parsed2 = JSON.parse(stripBom2(await readFile4(path, "utf8")));
10243
+ parsed2 = JSON.parse(stripBom2(await readFile3(path, "utf8")));
9705
10244
  } catch {
9706
10245
  throw new DocmetaError(`register: ${file} is not valid JSON.`);
9707
10246
  }
@@ -10470,12 +11009,20 @@ function parseFill(value, source) {
10470
11009
  function schemaTrustRoot(cwd, configDir) {
10471
11010
  const git2 = findGitRoot(cwd);
10472
11011
  if (git2 !== null) return { dir: git2, source: "git" };
10473
- if (configDir !== void 0) return { dir: resolve8(configDir), source: "config" };
10474
- return { dir: resolve8(cwd), source: "cwd" };
11012
+ if (configDir !== void 0) return { dir: resolve9(configDir), source: "config" };
11013
+ return { dir: resolve9(cwd), source: "cwd" };
10475
11014
  }
10476
11015
  async function loadConfig(explicitPath, cwd = process.cwd()) {
10477
11016
  const file = explicitPath ? await readConfigFile(explicitPath, cwd, CONFIG_FILE) : await findConfigFile(cwd, CONFIG_FILE);
10478
11017
  if (file === null) return null;
11018
+ return loadedFrom(file);
11019
+ }
11020
+ async function loadMetaSection(explicitPath, cwd = process.cwd()) {
11021
+ if (explicitPath === void 0) return loadConfig(void 0, cwd);
11022
+ const file = await readConfigFile(explicitPath, cwd, CONFIG_FILE);
11023
+ return file.wrapped ? loadedFrom(file) : null;
11024
+ }
11025
+ async function loadedFrom(file) {
10479
11026
  const section = file.wrapped ? META_SECTION : void 0;
10480
11027
  const config = parseConfigValue(file.value, file.source, section);
10481
11028
  assertOverrideCollections(config, file.collections, file.source, section);
@@ -10550,18 +11097,18 @@ import { isAbsolute as isAbsolute4 } from "node:path";
10550
11097
  // src/meta/core/baseline.ts
10551
11098
  import { createHash as createHash3 } from "node:crypto";
10552
11099
  import { existsSync as existsSync2 } from "node:fs";
10553
- import { readFile as readFile5 } from "node:fs/promises";
10554
- import { relative as relative7, resolve as resolve11 } from "node:path";
11100
+ import { readFile as readFile4 } from "node:fs/promises";
11101
+ import { relative as relative7, resolve as resolve12 } from "node:path";
10555
11102
 
10556
11103
  // src/meta/core/load-files.ts
10557
11104
  import { stat as stat5 } from "node:fs/promises";
10558
- import { extname, relative as relative6, resolve as resolve10 } from "node:path";
11105
+ import { extname, relative as relative6, resolve as resolve11 } from "node:path";
10559
11106
  import fg2 from "fast-glob";
10560
11107
  import picomatch3 from "picomatch";
10561
11108
 
10562
11109
  // src/meta/core/gitignore.ts
10563
11110
  import { spawn } from "node:child_process";
10564
- import { dirname as dirname4, relative as relative5, resolve as resolve9, sep as sep3 } from "node:path";
11111
+ import { dirname as dirname4, relative as relative5, resolve as resolve10, sep as sep3 } from "node:path";
10565
11112
  var GITIGNORE_UNAVAILABLE = ".gitignore filtering was requested but git could not answer here (no repository, or no git on PATH); no files were skipped.";
10566
11113
  var unavailable = () => ({
10567
11114
  ignored: /* @__PURE__ */ new Set(),
@@ -10575,7 +11122,7 @@ async function gitIgnored(paths, cwd) {
10575
11122
  const byRoot = /* @__PURE__ */ new Map();
10576
11123
  let inSomeRepo = false;
10577
11124
  for (const spelled of paths) {
10578
- const abs = resolve9(cwd, spelled);
11125
+ const abs = resolve10(cwd, spelled);
10579
11126
  const dir = dirname4(abs);
10580
11127
  let root = rootOf.get(dir);
10581
11128
  if (root === void 0) {
@@ -10693,7 +11240,7 @@ async function walkDirectory(cwd, abs, ignore, framed) {
10693
11240
  });
10694
11241
  const files = [];
10695
11242
  for (const file of found) {
10696
- const rel = toPosix(relative6(cwd, resolve10(root, file)));
11243
+ const rel = toPosix(relative6(cwd, resolve11(root, file)));
10697
11244
  if (!ignoredByGlobs(rel, framed)) files.push(rel);
10698
11245
  }
10699
11246
  return files;
@@ -10724,7 +11271,7 @@ async function resolveTargetSet(opts) {
10724
11271
  for (const input of opts.inputs) {
10725
11272
  if (input === STDIN_TOKEN) continue;
10726
11273
  const posixInput = toPosix(input);
10727
- const abs = resolve10(cwd, posixInput);
11274
+ const abs = resolve11(cwd, posixInput);
10728
11275
  const st = await statOrNull(abs);
10729
11276
  if (st?.isFile()) {
10730
11277
  named.add(toPosix(relative6(cwd, abs)));
@@ -10808,17 +11355,18 @@ var FINGERPRINT_RE = /^[0-9a-f]{16}$/;
10808
11355
  function canonicalFilePath(file, ctx) {
10809
11356
  if (!ctx) return file;
10810
11357
  const from = ctx.runBase ?? ctx.base;
10811
- return relative7(ctx.base, resolve11(from, file)).replace(/\\/g, "/");
11358
+ return relative7(ctx.base, resolve12(from, file)).replace(/\\/g, "/");
10812
11359
  }
10813
11360
  function canonicalSchemaRef(ref, ctx) {
10814
11361
  if (!ctx) return ref;
10815
11362
  if (classifyRef(ref).kind !== "file") return ref;
10816
- const abs = resolve11(ctx.cwd, ref);
11363
+ const abs = resolve12(ctx.cwd, ref);
10817
11364
  return relative7(ctx.base, abs).replace(/\\/g, "/");
10818
11365
  }
10819
11366
  function fingerprint(e, ctx) {
11367
+ const ref = canonicalSchemaRef(e.schema, ctx);
10820
11368
  const parts = [
10821
- canonicalSchemaRef(e.schema, ctx),
11369
+ ANNOTATION_ONLY_PREDECESSORS.get(ref) ?? ref,
10822
11370
  e.instancePath,
10823
11371
  e.keyword,
10824
11372
  e.subject ?? ""
@@ -10887,7 +11435,7 @@ function parseBaseline(text2, source) {
10887
11435
  async function readBaseline(absPath, source) {
10888
11436
  let text2;
10889
11437
  try {
10890
- text2 = await readFile5(absPath, "utf8");
11438
+ text2 = await readFile4(absPath, "utf8");
10891
11439
  } catch (err) {
10892
11440
  if (err.code === "ENOENT") return null;
10893
11441
  throw new DocmetaError(
@@ -10971,17 +11519,17 @@ function countFingerprints(baseline) {
10971
11519
  }
10972
11520
  function resolveBaselineRequest(opts, configured, configDir, cwd, defaults) {
10973
11521
  const named = (label) => ({
10974
- absPath: resolve11(cwd, label),
11522
+ absPath: resolve12(cwd, label),
10975
11523
  label
10976
11524
  });
10977
11525
  const implied = () => {
10978
11526
  const base = configDir ?? cwd;
10979
11527
  if (configured !== void 0) {
10980
- return { absPath: resolve11(base, configured), label: configured };
11528
+ return { absPath: resolve12(base, configured), label: configured };
10981
11529
  }
10982
- const current = resolve11(base, defaults.current);
11530
+ const current = resolve12(base, defaults.current);
10983
11531
  if (defaults.legacy !== void 0) {
10984
- const legacy = resolve11(base, defaults.legacy);
11532
+ const legacy = resolve12(base, defaults.legacy);
10985
11533
  if (!existsSync2(current) && existsSync2(legacy)) {
10986
11534
  warn(
10987
11535
  `"${defaults.legacy}" is the pre-rename baseline file name. Rename it to "${defaults.current}".`
@@ -11487,8 +12035,8 @@ async function readCapped(url, res, maxBytes) {
11487
12035
  }
11488
12036
 
11489
12037
  // src/meta/core/external-metadata.ts
11490
- import { readFile as readFile7 } from "node:fs/promises";
11491
- import { isAbsolute as isAbsolute5, relative as relative9, resolve as resolve13, sep as sep4 } from "node:path";
12038
+ import { readFile as readFile6 } from "node:fs/promises";
12039
+ import { isAbsolute as isAbsolute5, relative as relative9, resolve as resolve14, sep as sep4 } from "node:path";
11492
12040
  import {
11493
12041
  LineCounter as LineCounter3,
11494
12042
  isMap as isMap6,
@@ -11570,8 +12118,8 @@ function isMissing(error) {
11570
12118
 
11571
12119
  // src/shared/encryption-key.ts
11572
12120
  import { spawnSync } from "node:child_process";
11573
- import { readFile as readFile6 } from "node:fs/promises";
11574
- import { basename as basename4, dirname as dirname6, join as join7, relative as relative8, resolve as resolve12 } from "node:path";
12121
+ import { readFile as readFile5 } from "node:fs/promises";
12122
+ import { basename as basename4, dirname as dirname6, join as join7, relative as relative8, resolve as resolve13 } from "node:path";
11575
12123
  import { isDeepStrictEqual } from "node:util";
11576
12124
  import { isMap as isMap5, parseDocument as parseDocument4, parse as parseYaml3, stringify as stringify2 } from "yaml";
11577
12125
 
@@ -11583,8 +12131,8 @@ var RENAME_ATTEMPTS2 = 3;
11583
12131
  function errorCode2(err) {
11584
12132
  return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
11585
12133
  }
11586
- var wait3 = (ms) => new Promise((resolve25) => {
11587
- setTimeout(resolve25, ms);
12134
+ var wait3 = (ms) => new Promise((resolve26) => {
12135
+ setTimeout(resolve26, ms);
11588
12136
  });
11589
12137
  async function writeTextAtomic(path, text2) {
11590
12138
  const tmp = join6(
@@ -11633,14 +12181,14 @@ function resolveEncryptionKey(opts) {
11633
12181
  return { key: void 0, source: "none" };
11634
12182
  }
11635
12183
  function relativeSource2(cwd, path) {
11636
- return relative8(resolve12(cwd), path).replace(/\\/g, "/");
12184
+ return relative8(resolve13(cwd), path).replace(/\\/g, "/");
11637
12185
  }
11638
12186
  function errorCode3(err) {
11639
12187
  return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
11640
12188
  }
11641
12189
  async function readIfPresent(path) {
11642
12190
  try {
11643
- return await readFile6(path, "utf8");
12191
+ return await readFile5(path, "utf8");
11644
12192
  } catch (err) {
11645
12193
  if (errorCode3(err) === "ENOENT") return null;
11646
12194
  throw err;
@@ -11656,8 +12204,8 @@ async function fileTarget(file, toError) {
11656
12204
  return { path: file.path, source: file.source, text: text2 ?? file.text };
11657
12205
  }
11658
12206
  async function defaultTarget(cwd) {
11659
- const root = findGitRoot(cwd) ?? resolve12(cwd);
11660
- for (const name2 of [...FAMILY_CONFIG_NAMES, ...MOOSE_CONFIG_NAMES]) {
12207
+ const root = findGitRoot(cwd) ?? resolve13(cwd);
12208
+ for (const name2 of FAMILY_CONFIG_NAMES) {
11661
12209
  const path2 = join7(root, name2);
11662
12210
  const text2 = await readIfPresent(path2);
11663
12211
  if (text2 !== null) return { path: path2, source: relativeSource2(cwd, path2), text: text2 };
@@ -11679,20 +12227,20 @@ function withKey(text2, key, previous, source, toError) {
11679
12227
  if (previous === null) doc.delete(ENCRYPTION_KEY_PREVIOUS_FIELD);
11680
12228
  return doc.toString();
11681
12229
  }
11682
- function isMapping3(value) {
12230
+ function isMapping4(value) {
11683
12231
  return typeof value === "object" && value !== null && !Array.isArray(value);
11684
12232
  }
11685
12233
  function verify4(before, after, key, previous, source, toError) {
11686
12234
  const old = before === null ? null : parseYaml3(before);
11687
12235
  const expected = Object.fromEntries(
11688
12236
  Object.entries({
11689
- ...isMapping3(old) ? old : {},
12237
+ ...isMapping4(old) ? old : {},
11690
12238
  [ENCRYPTION_KEY_FIELD]: key,
11691
12239
  ...typeof previous === "string" ? { [ENCRYPTION_KEY_PREVIOUS_FIELD]: previous } : {}
11692
12240
  }).filter(([name2]) => previous !== null || name2 !== ENCRYPTION_KEY_PREVIOUS_FIELD)
11693
12241
  );
11694
12242
  const reread = parseYaml3(after);
11695
- if (!isMapping3(reread) || !isDeepStrictEqual(reread, expected)) {
12243
+ if (!isMapping4(reread) || !isDeepStrictEqual(reread, expected)) {
11696
12244
  throw toError(
11697
12245
  `Could not write ${ENCRYPTION_KEY_FIELD} to ${source}; add the key by hand.`
11698
12246
  );
@@ -11992,7 +12540,7 @@ async function loadExternalMetadata(collections, opts) {
11992
12540
  });
11993
12541
  parsed2 = parseManifest(manifest, collection, text2, file, opts.configDir, join12, ownership(declared, manifest));
11994
12542
  } else {
11995
- const abs = isAbsolute5(manifest.file) ? manifest.file : resolve13(opts.configDir, manifest.file);
12543
+ const abs = isAbsolute5(manifest.file) ? manifest.file : resolve14(opts.configDir, manifest.file);
11996
12544
  const label = reportedPath(abs, opts.base);
11997
12545
  file = label;
11998
12546
  parsed2 = await parsedManifests.parse(
@@ -12154,7 +12702,7 @@ function outsideRefusal(manifest, collection, pageRel) {
12154
12702
  }
12155
12703
  async function readPageManifest(abs, file) {
12156
12704
  try {
12157
- return await readFile7(abs, "utf8");
12705
+ return await readFile6(abs, "utf8");
12158
12706
  } catch (err) {
12159
12707
  if (isMissing(err)) return "";
12160
12708
  throw unreadable(file, err);
@@ -12162,7 +12710,7 @@ async function readPageManifest(abs, file) {
12162
12710
  }
12163
12711
  async function readManifest(abs, file) {
12164
12712
  try {
12165
- return await readFile7(abs, "utf8");
12713
+ return await readFile6(abs, "utf8");
12166
12714
  } catch (err) {
12167
12715
  throw unreadable(file, err);
12168
12716
  }
@@ -12211,7 +12759,7 @@ function parseManifest(manifest, collection, text2, file, configDir, join12, own
12211
12759
  `Manifest ${where}: "${spelled}" must be a mapping of owned keys to values.`
12212
12760
  );
12213
12761
  }
12214
- const indexKey = join12 === PATH_JOIN2 ? resolve13(configDir, spelled) : spelled;
12762
+ const indexKey = join12 === PATH_JOIN2 ? resolve14(configDir, spelled) : spelled;
12215
12763
  const values = target.get(indexKey) ?? /* @__PURE__ */ new Map();
12216
12764
  for (const kv of pair.value.items) {
12217
12765
  const key = isScalar5(kv.key) ? String(kv.key.value) : String(kv.key);
@@ -12299,10 +12847,10 @@ function mergeWith(label, extracted, index, memberOf2, base, encryptionKey, mark
12299
12847
  const entryOf = /* @__PURE__ */ new Map();
12300
12848
  const joins = [];
12301
12849
  if (label !== STDIN_LABEL) {
12302
- const byPath = index.byPath.get(resolve13(base, label));
12850
+ const byPath = index.byPath.get(resolve14(base, label));
12303
12851
  if (byPath) {
12304
12852
  sources.push(byPath);
12305
- const abs = resolve13(base, label);
12853
+ const abs = resolve14(base, label);
12306
12854
  entryOf.set(byPath, (sv) => index.entries.find((e) => e.file === sv.file && e.abs === abs));
12307
12855
  }
12308
12856
  }
@@ -12427,7 +12975,7 @@ function externalMetadataPointer(key) {
12427
12975
  }
12428
12976
  function orphanEntries(index, loaded, base, collections) {
12429
12977
  if (!index) return [];
12430
- const have = new Set(loaded.map((l) => resolve13(base, l)));
12978
+ const have = new Set(loaded.map((l) => resolve14(base, l)));
12431
12979
  const orphans = index.entries.filter(
12432
12980
  (e) => inCollections(e, collections) && e.join === PATH_JOIN2 && e.abs !== void 0 && !have.has(e.abs)
12433
12981
  );
@@ -12491,77 +13039,340 @@ function pageOwners(index, key, memberOf2, marked) {
12491
13039
  ];
12492
13040
  }
12493
13041
 
12494
- // src/meta/core/location.ts
12495
- var LOCATION_KEYWORD = "x-manni-location";
12496
- var LOCATION_EXTERNAL_SCHEMA = "location:external";
12497
- var LOCATION_PAGE_SCHEMA = "location:page";
12498
- var LOCATION_FINDING_KEYWORD = "location";
12499
- function isFieldLocation(value) {
12500
- return value === "page" || value === "external";
12501
- }
13042
+ // src/meta/core/collections.ts
13043
+ import { isAbsolute as isAbsolute6, relative as relative10, resolve as resolvePath2, sep as sep5 } from "node:path";
12502
13044
 
12503
- // src/meta/core/validator.ts
12504
- import * as AjvDraft07Ns from "ajv";
12505
- import * as Ajv2019Ns from "ajv/dist/2019.js";
12506
- import * as Ajv2020Ns from "ajv/dist/2020.js";
12507
- import * as AjvDraft04Ns from "ajv-draft-04";
12508
- import * as addFormatsNs from "ajv-formats";
12509
- import { createRequire } from "node:module";
12510
- var draft06MetaSchema = createRequire(import.meta.url)(
12511
- "ajv/dist/refs/json-schema-draft-06.json"
12512
- );
12513
- var AjvDraft07 = AjvDraft07Ns.default;
12514
- var Ajv2019 = Ajv2019Ns.default;
12515
- var Ajv2020 = Ajv2020Ns.default;
12516
- var AjvDraft04 = AjvDraft04Ns.default;
12517
- var addFormats = addFormatsNs.default;
12518
- function dialectOf(schema) {
12519
- const meta = typeof schema.$schema === "string" ? schema.$schema : "";
12520
- if (meta.includes("2019-09")) return "2019";
12521
- if (meta.includes("draft-07") || meta.includes("draft/7") || meta.includes("draft-06") || meta.includes("draft/6")) {
12522
- return "draft7";
13045
+ // src/meta/core/projection.ts
13046
+ var SYSTEM_COLUMNS = ["_path", "_format", "_present", "_data"];
13047
+ var RESERVED = new Set(SYSTEM_COLUMNS);
13048
+ function quoteIdent(name2) {
13049
+ return `"${name2.replaceAll('"', '""')}"`;
13050
+ }
13051
+ function bindValue(value) {
13052
+ if (value === void 0 || value === null) return null;
13053
+ switch (typeof value) {
13054
+ case "boolean":
13055
+ return value ? 1 : 0;
13056
+ case "number":
13057
+ return Number.isFinite(value) ? value : null;
13058
+ case "bigint":
13059
+ case "string":
13060
+ return value;
13061
+ default:
13062
+ return JSON.stringify(value);
12523
13063
  }
12524
- if (meta.includes("draft-04") || meta.includes("draft/4")) return "draft4";
12525
- return "2020";
12526
13064
  }
12527
- function buildAjv(dialect, coerceTypes = false) {
12528
- const opts = { allErrors: true, strict: false, coerceTypes };
12529
- const ajv = dialect === "2019" ? new Ajv2019(opts) : dialect === "draft7" ? new AjvDraft07(opts) : dialect === "draft4" ? new AjvDraft04(opts) : new Ajv2020(opts);
12530
- addFormats(ajv);
12531
- if (dialect === "draft7") ajv.addMetaSchema(draft06MetaSchema);
12532
- registerEncryptKeyword(ajv);
12533
- registerLocationKeyword(ajv);
12534
- registerBuiltins(ajv, dialect);
12535
- return ajv;
13065
+ function corpusDataColumns(entries, extraKeys = []) {
13066
+ const keys = /* @__PURE__ */ new Set();
13067
+ for (const { extracted } of entries) {
13068
+ for (const key of Object.keys(extracted.data)) {
13069
+ if (key !== "" && !RESERVED.has(key)) keys.add(key);
13070
+ }
13071
+ }
13072
+ for (const key of extraKeys) {
13073
+ if (key !== "" && !RESERVED.has(key)) keys.add(key);
13074
+ }
13075
+ return [...keys].sort();
12536
13076
  }
12537
- var markRecorder;
12538
- function registerEncryptKeyword(ajv) {
12539
- ajv.addKeyword({
12540
- keyword: ENCRYPT_KEYWORD,
12541
- metaSchema: { type: "boolean" },
12542
- errors: false,
12543
- validate: (schema, _data, _parent, cxt) => {
12544
- if (schema === true && markRecorder !== void 0) {
12545
- markRecorder.add(cxt?.instancePath ?? "");
12546
- }
12547
- return true;
13077
+ function createDocsTable(db, entries, dataColumns) {
13078
+ db.exec(
13079
+ `CREATE TABLE docs ("_path" TEXT PRIMARY KEY, "_format" TEXT, "_present" INTEGER, "_data" TEXT${dataColumns.map((c) => `, ${quoteIdent(c)}`).join("")})`
13080
+ );
13081
+ const insert = db.prepare(
13082
+ `INSERT INTO docs VALUES (${["?", "?", "?", "?", ...dataColumns.map(() => "?")].join(", ")})`
13083
+ );
13084
+ db.exec("BEGIN");
13085
+ let committed = false;
13086
+ try {
13087
+ for (const { label, extracted } of entries) {
13088
+ insert.run(
13089
+ label,
13090
+ extracted.format,
13091
+ extracted.present ? 1 : 0,
13092
+ JSON.stringify(extracted.data),
13093
+ ...dataColumns.map((c) => bindValue(extracted.data[c]))
13094
+ );
12548
13095
  }
12549
- });
13096
+ db.exec("COMMIT");
13097
+ committed = true;
13098
+ } finally {
13099
+ if (!committed) db.exec("ROLLBACK");
13100
+ }
12550
13101
  }
12551
- function isMarkShapeError(err) {
12552
- return err instanceof Error && err.message.includes(`"${ENCRYPT_KEYWORD}"`);
13102
+ function registerLineFor(db, entries) {
13103
+ const byLabel = new Map(entries.map((e) => [e.label, e.extracted]));
13104
+ db.function("lineFor", { deterministic: true }, (path, key) => {
13105
+ if (typeof path !== "string" || typeof key !== "string") return null;
13106
+ return byLabel.get(path)?.lineFor(key) ?? null;
13107
+ });
12553
13108
  }
12554
- var locationRecorder;
12555
- function topLevelKey(instancePath) {
12556
- if (!instancePath.startsWith("/") || instancePath.indexOf("/", 1) !== -1) {
12557
- return void 0;
13109
+ function assertSingleStatement(sql) {
13110
+ const cut = topLevelSemicolon(sql);
13111
+ if (cut === -1) return;
13112
+ if (!isTrivia(sql.slice(cut + 1))) {
13113
+ throw new DocmetaError(
13114
+ "Run a single SQL statement per query; text after the first `;` would be silently ignored."
13115
+ );
12558
13116
  }
12559
- return instancePath.slice(1).replace(/~1/g, "/").replace(/~0/g, "~");
12560
13117
  }
12561
- function registerLocationKeyword(ajv) {
12562
- ajv.addKeyword({
12563
- keyword: LOCATION_KEYWORD,
12564
- metaSchema: { type: "string", enum: ["page", "external"] },
13118
+ function startsStringOrIdent(sql, i) {
13119
+ const ch = sql[i];
13120
+ return ch === "'" || ch === '"' || ch === "`" || ch === "[";
13121
+ }
13122
+ function startsComment(sql, i) {
13123
+ const ch = sql[i];
13124
+ return ch === "-" && sql[i + 1] === "-" || ch === "/" && sql[i + 1] === "*";
13125
+ }
13126
+ function skipStringOrIdent(sql, i) {
13127
+ const quote = sql[i];
13128
+ if (quote === "[") {
13129
+ let j2 = i + 1;
13130
+ while (j2 < sql.length && sql[j2] !== "]") j2++;
13131
+ return j2 + 1;
13132
+ }
13133
+ let j = i + 1;
13134
+ while (j < sql.length) {
13135
+ if (sql[j] === quote) {
13136
+ if (sql[j + 1] === quote) {
13137
+ j += 2;
13138
+ continue;
13139
+ }
13140
+ return j + 1;
13141
+ }
13142
+ j++;
13143
+ }
13144
+ return j;
13145
+ }
13146
+ function skipComment(sql, i) {
13147
+ if (sql[i] === "-") {
13148
+ let j = i + 2;
13149
+ while (j < sql.length && sql[j] !== "\n") j++;
13150
+ return j < sql.length ? j + 1 : j;
13151
+ }
13152
+ const end = sql.indexOf("*/", i + 2);
13153
+ return end === -1 ? sql.length : end + 2;
13154
+ }
13155
+ function topLevelSemicolon(sql) {
13156
+ let i = 0;
13157
+ while (i < sql.length) {
13158
+ if (startsStringOrIdent(sql, i)) {
13159
+ i = skipStringOrIdent(sql, i);
13160
+ } else if (startsComment(sql, i)) {
13161
+ i = skipComment(sql, i);
13162
+ } else if (sql[i] === ";") {
13163
+ return i;
13164
+ } else {
13165
+ i++;
13166
+ }
13167
+ }
13168
+ return -1;
13169
+ }
13170
+ var PARAM_NAME_HEAD = /^[A-Za-z_][A-Za-z0-9_]*/;
13171
+ function isParamName(name2) {
13172
+ return PARAM_NAME_HEAD.exec(name2)?.[0] === name2;
13173
+ }
13174
+ function collectNamedParameters(sql) {
13175
+ const tokens2 = [];
13176
+ const seen = /* @__PURE__ */ new Set();
13177
+ const prefixOf = /* @__PURE__ */ new Map();
13178
+ let i = 0;
13179
+ while (i < sql.length) {
13180
+ const ch = sql[i];
13181
+ if (startsStringOrIdent(sql, i)) {
13182
+ i = skipStringOrIdent(sql, i);
13183
+ } else if (startsComment(sql, i)) {
13184
+ i = skipComment(sql, i);
13185
+ } else if (ch === "$" || ch === ":" || ch === "@") {
13186
+ if (ch === ":" && sql[i + 1] === ":") {
13187
+ i += 2;
13188
+ continue;
13189
+ }
13190
+ const m = PARAM_NAME_HEAD.exec(sql.slice(i + 1));
13191
+ if (!m) {
13192
+ i++;
13193
+ continue;
13194
+ }
13195
+ const token = ch + m[0];
13196
+ const priorPrefix = prefixOf.get(m[0]);
13197
+ if (priorPrefix !== void 0 && priorPrefix !== ch) {
13198
+ throw new DocmetaError(
13199
+ `The statement references both ${priorPrefix}${m[0]} and ${token} \u2014 one name under two prefixes, which the engine rejects as conflicting. Use one spelling throughout.`
13200
+ );
13201
+ }
13202
+ prefixOf.set(m[0], ch);
13203
+ if (!seen.has(token)) {
13204
+ seen.add(token);
13205
+ tokens2.push(token);
13206
+ }
13207
+ i += 1 + m[0].length;
13208
+ } else {
13209
+ i++;
13210
+ }
13211
+ }
13212
+ return tokens2;
13213
+ }
13214
+ function stripLeadingTrivia(sql) {
13215
+ let i = 0;
13216
+ for (; ; ) {
13217
+ while (i < sql.length && /\s/.test(sql[i] ?? "")) i++;
13218
+ if (startsComment(sql, i)) {
13219
+ i = skipComment(sql, i);
13220
+ continue;
13221
+ }
13222
+ return sql.slice(i);
13223
+ }
13224
+ }
13225
+ function isTrivia(text2) {
13226
+ let i = 0;
13227
+ while (i < text2.length) {
13228
+ const ch = text2[i];
13229
+ if (ch === void 0 || /\s/.test(ch) || ch === ";") {
13230
+ i++;
13231
+ continue;
13232
+ }
13233
+ if (startsComment(text2, i)) {
13234
+ i = skipComment(text2, i);
13235
+ continue;
13236
+ }
13237
+ return false;
13238
+ }
13239
+ return true;
13240
+ }
13241
+ var sqliteModule;
13242
+ function loadSqlite() {
13243
+ sqliteModule ??= (async () => {
13244
+ const original = process.emitWarning.bind(process);
13245
+ const filtered = (warning, ...rest) => {
13246
+ const text2 = typeof warning === "string" ? warning : warning.message;
13247
+ if (text2.includes("SQLite is an experimental feature")) return;
13248
+ Reflect.apply(original, process, [warning, ...rest]);
13249
+ };
13250
+ process.emitWarning = filtered;
13251
+ try {
13252
+ return await import("node:sqlite");
13253
+ } finally {
13254
+ process.emitWarning = original;
13255
+ }
13256
+ })();
13257
+ return sqliteModule;
13258
+ }
13259
+
13260
+ // src/meta/core/collections.ts
13261
+ function memberOf(collections, configDir, base, label) {
13262
+ if (collections.length === 0) return [];
13263
+ if (label === STDIN_LABEL) return [];
13264
+ const rel = relative10(configDir, resolvePath2(base, label)).split(sep5).join("/");
13265
+ if (isAbsolute6(rel)) return [];
13266
+ return collections.filter((c) => isMember(c, rel)).map((c) => c.name);
13267
+ }
13268
+ function retainMembers(files, membersFor) {
13269
+ return files.filter((label) => membersFor(label).length > 0);
13270
+ }
13271
+ function collectionNames(collections) {
13272
+ return (collections ?? []).map((c) => c.name);
13273
+ }
13274
+ function collectCollections(entries, params) {
13275
+ const declared = params.collections ?? [];
13276
+ if (declared.length === 0) return [];
13277
+ const base = params.base ?? ".";
13278
+ const configDir = params.configDir ?? base;
13279
+ const members = /* @__PURE__ */ new Map();
13280
+ for (const entry of entries) {
13281
+ for (const name2 of memberOf(declared, configDir, base, entry.label)) {
13282
+ const list2 = members.get(name2);
13283
+ if (list2) list2.push(entry.label);
13284
+ else members.set(name2, [entry.label]);
13285
+ }
13286
+ }
13287
+ return declared.map((c) => ({ name: c.name, members: members.get(c.name) ?? [] }));
13288
+ }
13289
+ function quoteSqlString(value) {
13290
+ return `'${value.replaceAll("'", "''")}'`;
13291
+ }
13292
+ function createCollectionViews(db, collections) {
13293
+ for (const c of collections) {
13294
+ const where = c.members.length === 0 ? "0" : `_path IN (${c.members.map(quoteSqlString).join(", ")})`;
13295
+ db.exec(
13296
+ `CREATE VIEW ${quoteIdent(c.name)} AS SELECT * FROM docs WHERE ${where}`
13297
+ );
13298
+ }
13299
+ }
13300
+
13301
+ // src/meta/core/location.ts
13302
+ var LOCATION_KEYWORD = "x-manni-location";
13303
+ var LOCATION_EXTERNAL_SCHEMA = "location:external";
13304
+ var LOCATION_PAGE_SCHEMA = "location:page";
13305
+ var LOCATION_FINDING_KEYWORD = "location";
13306
+ function isFieldLocation(value) {
13307
+ return value === "page" || value === "external";
13308
+ }
13309
+
13310
+ // src/meta/core/graph-output.ts
13311
+ var GRAPH_OUTPUT_KEYWORD = "x-manni-graph-output";
13312
+
13313
+ // src/meta/core/validator.ts
13314
+ import * as AjvDraft07Ns from "ajv";
13315
+ import * as Ajv2019Ns from "ajv/dist/2019.js";
13316
+ import * as Ajv2020Ns from "ajv/dist/2020.js";
13317
+ import * as AjvDraft04Ns from "ajv-draft-04";
13318
+ import * as addFormatsNs from "ajv-formats";
13319
+ import { createRequire } from "node:module";
13320
+ var draft06MetaSchema = createRequire(import.meta.url)(
13321
+ "ajv/dist/refs/json-schema-draft-06.json"
13322
+ );
13323
+ var AjvDraft07 = AjvDraft07Ns.default;
13324
+ var Ajv2019 = Ajv2019Ns.default;
13325
+ var Ajv2020 = Ajv2020Ns.default;
13326
+ var AjvDraft04 = AjvDraft04Ns.default;
13327
+ var addFormats = addFormatsNs.default;
13328
+ function dialectOf(schema) {
13329
+ const meta = typeof schema.$schema === "string" ? schema.$schema : "";
13330
+ if (meta.includes("2019-09")) return "2019";
13331
+ if (meta.includes("draft-07") || meta.includes("draft/7") || meta.includes("draft-06") || meta.includes("draft/6")) {
13332
+ return "draft7";
13333
+ }
13334
+ if (meta.includes("draft-04") || meta.includes("draft/4")) return "draft4";
13335
+ return "2020";
13336
+ }
13337
+ function buildAjv(dialect, coerceTypes = false) {
13338
+ const opts = { allErrors: true, strict: false, coerceTypes };
13339
+ const ajv = dialect === "2019" ? new Ajv2019(opts) : dialect === "draft7" ? new AjvDraft07(opts) : dialect === "draft4" ? new AjvDraft04(opts) : new Ajv2020(opts);
13340
+ addFormats(ajv);
13341
+ if (dialect === "draft7") ajv.addMetaSchema(draft06MetaSchema);
13342
+ registerEncryptKeyword(ajv);
13343
+ registerLocationKeyword(ajv);
13344
+ registerGraphOutputKeyword(ajv);
13345
+ registerBuiltins(ajv, dialect);
13346
+ return ajv;
13347
+ }
13348
+ var markRecorder;
13349
+ function registerEncryptKeyword(ajv) {
13350
+ ajv.addKeyword({
13351
+ keyword: ENCRYPT_KEYWORD,
13352
+ metaSchema: { type: "boolean" },
13353
+ errors: false,
13354
+ validate: (schema, _data, _parent, cxt) => {
13355
+ if (schema === true && markRecorder !== void 0) {
13356
+ markRecorder.add(cxt?.instancePath ?? "");
13357
+ }
13358
+ return true;
13359
+ }
13360
+ });
13361
+ }
13362
+ function isMarkShapeError(err) {
13363
+ return err instanceof Error && err.message.includes(`"${ENCRYPT_KEYWORD}"`);
13364
+ }
13365
+ var locationRecorder;
13366
+ function topLevelKey(instancePath) {
13367
+ if (!instancePath.startsWith("/") || instancePath.indexOf("/", 1) !== -1) {
13368
+ return void 0;
13369
+ }
13370
+ return instancePath.slice(1).replace(/~1/g, "/").replace(/~0/g, "~");
13371
+ }
13372
+ function registerLocationKeyword(ajv) {
13373
+ ajv.addKeyword({
13374
+ keyword: LOCATION_KEYWORD,
13375
+ metaSchema: { type: "string", enum: ["page", "external"] },
12565
13376
  errors: false,
12566
13377
  validate: (schema, _data, _parent, cxt) => {
12567
13378
  if (locationRecorder !== void 0 && isFieldLocation(schema)) {
@@ -12569,15 +13380,37 @@ function registerLocationKeyword(ajv) {
12569
13380
  if (key !== void 0) {
12570
13381
  const seen = locationRecorder.get(key);
12571
13382
  if (seen) seen.add(schema);
12572
- else locationRecorder.set(key, /* @__PURE__ */ new Set([schema]));
13383
+ else locationRecorder.set(key, /* @__PURE__ */ new Set([schema]));
13384
+ }
13385
+ }
13386
+ return true;
13387
+ }
13388
+ });
13389
+ }
13390
+ function isLocationShapeError(err) {
13391
+ return err instanceof Error && err.message.includes(`"${LOCATION_KEYWORD}"`);
13392
+ }
13393
+ var graphOutputRecorder;
13394
+ function registerGraphOutputKeyword(ajv) {
13395
+ ajv.addKeyword({
13396
+ keyword: GRAPH_OUTPUT_KEYWORD,
13397
+ metaSchema: { type: "boolean" },
13398
+ errors: false,
13399
+ validate: (schema, _data, _parent, cxt) => {
13400
+ if (graphOutputRecorder !== void 0 && typeof schema === "boolean") {
13401
+ const key = topLevelKey(cxt?.instancePath ?? "");
13402
+ if (key !== void 0) {
13403
+ const seen = graphOutputRecorder.get(key);
13404
+ if (seen) seen.add(schema);
13405
+ else graphOutputRecorder.set(key, /* @__PURE__ */ new Set([schema]));
12573
13406
  }
12574
13407
  }
12575
13408
  return true;
12576
13409
  }
12577
13410
  });
12578
13411
  }
12579
- function isLocationShapeError(err) {
12580
- return err instanceof Error && err.message.includes(`"${LOCATION_KEYWORD}"`);
13412
+ function isGraphOutputShapeError(err) {
13413
+ return err instanceof Error && err.message.includes(`"${GRAPH_OUTPUT_KEYWORD}"`);
12581
13414
  }
12582
13415
  function marksSchemaExternal(schema) {
12583
13416
  const properties = schema["properties"];
@@ -12589,7 +13422,7 @@ function marksSchemaExternal(schema) {
12589
13422
  function isRecord2(value) {
12590
13423
  return typeof value === "object" && value !== null && !Array.isArray(value);
12591
13424
  }
12592
- function definiteLocationConflict(root) {
13425
+ function definiteMarkConflict(root, keyword, isMark) {
12593
13426
  const marks = /* @__PURE__ */ new Map();
12594
13427
  const localRef = (node, embedded) => {
12595
13428
  const ref = node["$ref"];
@@ -12617,8 +13450,8 @@ function definiteLocationConflict(root) {
12617
13450
  if (!isRecord2(node) || visited.has(node)) return;
12618
13451
  visited.add(node);
12619
13452
  const inner = isEmbedded(node, embedded);
12620
- const mark = node[LOCATION_KEYWORD];
12621
- if (isFieldLocation(mark)) {
13453
+ const mark = node[keyword];
13454
+ if (isMark(mark)) {
12622
13455
  const seen = marks.get(key);
12623
13456
  if (seen) seen.add(mark);
12624
13457
  else marks.set(key, /* @__PURE__ */ new Set([mark]));
@@ -12748,12 +13581,22 @@ var Validator = class {
12748
13581
  `${ref}: "${FILE_SCHEMA_KEY}" cannot be stored in external metadata.`
12749
13582
  );
12750
13583
  }
12751
- const contradicted = definiteLocationConflict(schema);
13584
+ const contradicted = definiteMarkConflict(schema, LOCATION_KEYWORD, isFieldLocation);
12752
13585
  if (contradicted !== void 0) {
12753
13586
  throw new DocmetaError(
12754
13587
  `${ref}: "${LOCATION_KEYWORD}" says both "page" and "external" for "${contradicted}".`
12755
13588
  );
12756
13589
  }
13590
+ const graphContradicted = definiteMarkConflict(
13591
+ schema,
13592
+ GRAPH_OUTPUT_KEYWORD,
13593
+ (value) => typeof value === "boolean"
13594
+ );
13595
+ if (graphContradicted !== void 0) {
13596
+ throw new DocmetaError(
13597
+ `${ref}: "${GRAPH_OUTPUT_KEYWORD}" says both true and false for "${graphContradicted}".`
13598
+ );
13599
+ }
12757
13600
  try {
12758
13601
  const ajv = this.ajvFor(dialectOf(schema));
12759
13602
  const id = typeof schema["$id"] === "string" ? schema["$id"] : void 0;
@@ -12775,6 +13618,11 @@ var Validator = class {
12775
13618
  `${ref}: "${LOCATION_KEYWORD}" must be "page" or "external".`
12776
13619
  );
12777
13620
  }
13621
+ if (isGraphOutputShapeError(err)) {
13622
+ throw new DocmetaError(
13623
+ `${ref}: "${GRAPH_OUTPUT_KEYWORD}" must be true or false.`
13624
+ );
13625
+ }
12778
13626
  throw new DocmetaError(
12779
13627
  `Schema "${ref}" failed to compile: ${errorMessage(err)}`
12780
13628
  );
@@ -12865,347 +13713,127 @@ var Validator = class {
12865
13713
  return claimed;
12866
13714
  }
12867
13715
  /**
12868
- * Validate `data` against every schema in `refs`. Returns all violations,
12869
- * each tagged with the schema that produced it and a source line via
12870
- * `lineFor`.
13716
+ * Whether each top-level key of `data` belongs in a published graph under
13717
+ * `refs`, as its `x-manni-graph-output` mark says. A key no schema marks has no
13718
+ * entry, which the caller reads as `true`: absent means harvested.
12871
13719
  *
12872
- * `colFor` is optional and additive: this signature is public, so a fourth
12873
- * *required* parameter — or a widened third one — would be a consumer break.
12874
- * Callers with an extractor that supplies no column pass nothing and get the
12875
- * previous behavior exactly.
13720
+ * A first pass with the recording keyword, as `locationPreferences` runs, so
13721
+ * a mark counts exactly where validation would evaluate it, and a mark nested
13722
+ * inside `graph` is ignored. Only present values are marked: Ajv applies a
13723
+ * property's subschema to a property that exists. `$schema` is stripped
13724
+ * first, as validation does.
13725
+ *
13726
+ * Refs are evaluated in order and a later ref's mark wins, so a house schema
13727
+ * listed after a vocabulary refines it. A schema that says both on paths
13728
+ * every document takes is refused when it compiles. A ref whose conditional
13729
+ * branches say both `true` and `false` gives that key no preference: with
13730
+ * `allErrors`, Ajv evaluates failing branches too, so neither value is that
13731
+ * ref's answer and an earlier ref's stands.
12876
13732
  */
12877
- async validate(data, refs, lineFor, colFor, locate) {
13733
+ async graphOutputPreferences(data, refs) {
12878
13734
  const { [FILE_SCHEMA_KEY]: _omit, ...subject } = data;
12879
13735
  void _omit;
12880
- const errors = [];
13736
+ const preferences = /* @__PURE__ */ new Map();
12881
13737
  for (const ref of refs) {
12882
13738
  const fn = await this.compile(ref);
12883
- const ok = fn(subject);
12884
- if (ok) continue;
12885
- for (const e of fn.errors ?? []) {
12886
- errors.push(toFieldError(ref, e, lineFor, colFor, locate));
12887
- }
12888
- }
12889
- return errors;
12890
- }
12891
- };
12892
- function subjectOf(e) {
12893
- switch (e.keyword) {
12894
- case "required":
12895
- return e.params.missingProperty;
12896
- case "additionalProperties":
12897
- return e.params.additionalProperty;
12898
- case "format":
12899
- return e.params.format;
12900
- case "type":
12901
- return e.params.type;
12902
- default:
12903
- return void 0;
12904
- }
12905
- }
12906
- function toFieldError(schema, e, lineFor, colFor, locate) {
12907
- const defined = e;
12908
- const instancePath = e.instancePath;
12909
- const subject = subjectOf(defined);
12910
- let message = e.message ?? "is invalid";
12911
- let wantsColumn = true;
12912
- if (defined.keyword === "required") {
12913
- message = `must have required property '${defined.params.missingProperty}'`;
12914
- wantsColumn = false;
12915
- } else if (defined.keyword === "additionalProperties") {
12916
- message = `must NOT have additional property '${defined.params.additionalProperty}'`;
12917
- wantsColumn = false;
12918
- }
12919
- const located = defined.keyword === "additionalProperties" ? locate?.(
12920
- `${instancePath}/${escapePointerSegment(defined.params.additionalProperty)}`
12921
- ) ?? locate?.(instancePath) : locate?.(instancePath);
12922
- if (located) {
12923
- return {
12924
- schema,
12925
- instancePath,
12926
- message,
12927
- keyword: e.keyword,
12928
- ...subject != null ? { subject } : {},
12929
- file: located.file,
12930
- ...located.line != null ? { line: located.line } : {},
12931
- ...wantsColumn && located.col != null ? { col: located.col } : {}
12932
- };
12933
- }
12934
- const line = lineFor(instancePath);
12935
- const col = wantsColumn ? colFor?.(instancePath) : void 0;
12936
- return {
12937
- schema,
12938
- instancePath,
12939
- message,
12940
- keyword: e.keyword,
12941
- ...subject != null ? { subject } : {},
12942
- ...line != null ? { line } : {},
12943
- ...col != null ? { col } : {}
12944
- };
12945
- }
12946
-
12947
- // src/meta/core/collections.ts
12948
- import { isAbsolute as isAbsolute6, relative as relative10, resolve as resolvePath2, sep as sep5 } from "node:path";
12949
-
12950
- // src/meta/core/projection.ts
12951
- var SYSTEM_COLUMNS = ["_path", "_format", "_present", "_data"];
12952
- var RESERVED = new Set(SYSTEM_COLUMNS);
12953
- function quoteIdent(name2) {
12954
- return `"${name2.replaceAll('"', '""')}"`;
12955
- }
12956
- function bindValue(value) {
12957
- if (value === void 0 || value === null) return null;
12958
- switch (typeof value) {
12959
- case "boolean":
12960
- return value ? 1 : 0;
12961
- case "number":
12962
- return Number.isFinite(value) ? value : null;
12963
- case "bigint":
12964
- case "string":
12965
- return value;
12966
- default:
12967
- return JSON.stringify(value);
12968
- }
12969
- }
12970
- function corpusDataColumns(entries, extraKeys = []) {
12971
- const keys = /* @__PURE__ */ new Set();
12972
- for (const { extracted } of entries) {
12973
- for (const key of Object.keys(extracted.data)) {
12974
- if (key !== "" && !RESERVED.has(key)) keys.add(key);
12975
- }
12976
- }
12977
- for (const key of extraKeys) {
12978
- if (key !== "" && !RESERVED.has(key)) keys.add(key);
12979
- }
12980
- return [...keys].sort();
12981
- }
12982
- function createDocsTable(db, entries, dataColumns) {
12983
- db.exec(
12984
- `CREATE TABLE docs ("_path" TEXT PRIMARY KEY, "_format" TEXT, "_present" INTEGER, "_data" TEXT${dataColumns.map((c) => `, ${quoteIdent(c)}`).join("")})`
12985
- );
12986
- const insert = db.prepare(
12987
- `INSERT INTO docs VALUES (${["?", "?", "?", "?", ...dataColumns.map(() => "?")].join(", ")})`
12988
- );
12989
- db.exec("BEGIN");
12990
- let committed = false;
12991
- try {
12992
- for (const { label, extracted } of entries) {
12993
- insert.run(
12994
- label,
12995
- extracted.format,
12996
- extracted.present ? 1 : 0,
12997
- JSON.stringify(extracted.data),
12998
- ...dataColumns.map((c) => bindValue(extracted.data[c]))
12999
- );
13000
- }
13001
- db.exec("COMMIT");
13002
- committed = true;
13003
- } finally {
13004
- if (!committed) db.exec("ROLLBACK");
13005
- }
13006
- }
13007
- function registerLineFor(db, entries) {
13008
- const byLabel = new Map(entries.map((e) => [e.label, e.extracted]));
13009
- db.function("lineFor", { deterministic: true }, (path, key) => {
13010
- if (typeof path !== "string" || typeof key !== "string") return null;
13011
- return byLabel.get(path)?.lineFor(key) ?? null;
13012
- });
13013
- }
13014
- function assertSingleStatement(sql) {
13015
- const cut = topLevelSemicolon(sql);
13016
- if (cut === -1) return;
13017
- if (!isTrivia(sql.slice(cut + 1))) {
13018
- throw new DocmetaError(
13019
- "Run a single SQL statement per query; text after the first `;` would be silently ignored."
13020
- );
13021
- }
13022
- }
13023
- function startsStringOrIdent(sql, i) {
13024
- const ch = sql[i];
13025
- return ch === "'" || ch === '"' || ch === "`" || ch === "[";
13026
- }
13027
- function startsComment(sql, i) {
13028
- const ch = sql[i];
13029
- return ch === "-" && sql[i + 1] === "-" || ch === "/" && sql[i + 1] === "*";
13030
- }
13031
- function skipStringOrIdent(sql, i) {
13032
- const quote = sql[i];
13033
- if (quote === "[") {
13034
- let j2 = i + 1;
13035
- while (j2 < sql.length && sql[j2] !== "]") j2++;
13036
- return j2 + 1;
13037
- }
13038
- let j = i + 1;
13039
- while (j < sql.length) {
13040
- if (sql[j] === quote) {
13041
- if (sql[j + 1] === quote) {
13042
- j += 2;
13043
- continue;
13044
- }
13045
- return j + 1;
13046
- }
13047
- j++;
13048
- }
13049
- return j;
13050
- }
13051
- function skipComment(sql, i) {
13052
- if (sql[i] === "-") {
13053
- let j = i + 2;
13054
- while (j < sql.length && sql[j] !== "\n") j++;
13055
- return j < sql.length ? j + 1 : j;
13056
- }
13057
- const end = sql.indexOf("*/", i + 2);
13058
- return end === -1 ? sql.length : end + 2;
13059
- }
13060
- function topLevelSemicolon(sql) {
13061
- let i = 0;
13062
- while (i < sql.length) {
13063
- if (startsStringOrIdent(sql, i)) {
13064
- i = skipStringOrIdent(sql, i);
13065
- } else if (startsComment(sql, i)) {
13066
- i = skipComment(sql, i);
13067
- } else if (sql[i] === ";") {
13068
- return i;
13069
- } else {
13070
- i++;
13071
- }
13072
- }
13073
- return -1;
13074
- }
13075
- var PARAM_NAME_HEAD = /^[A-Za-z_][A-Za-z0-9_]*/;
13076
- function isParamName(name2) {
13077
- return PARAM_NAME_HEAD.exec(name2)?.[0] === name2;
13078
- }
13079
- function collectNamedParameters(sql) {
13080
- const tokens2 = [];
13081
- const seen = /* @__PURE__ */ new Set();
13082
- const prefixOf = /* @__PURE__ */ new Map();
13083
- let i = 0;
13084
- while (i < sql.length) {
13085
- const ch = sql[i];
13086
- if (startsStringOrIdent(sql, i)) {
13087
- i = skipStringOrIdent(sql, i);
13088
- } else if (startsComment(sql, i)) {
13089
- i = skipComment(sql, i);
13090
- } else if (ch === "$" || ch === ":" || ch === "@") {
13091
- if (ch === ":" && sql[i + 1] === ":") {
13092
- i += 2;
13093
- continue;
13094
- }
13095
- const m = PARAM_NAME_HEAD.exec(sql.slice(i + 1));
13096
- if (!m) {
13097
- i++;
13098
- continue;
13099
- }
13100
- const token = ch + m[0];
13101
- const priorPrefix = prefixOf.get(m[0]);
13102
- if (priorPrefix !== void 0 && priorPrefix !== ch) {
13103
- throw new DocmetaError(
13104
- `The statement references both ${priorPrefix}${m[0]} and ${token} \u2014 one name under two prefixes, which the engine rejects as conflicting. Use one spelling throughout.`
13105
- );
13739
+ const seen = /* @__PURE__ */ new Map();
13740
+ graphOutputRecorder = seen;
13741
+ try {
13742
+ fn(subject);
13743
+ } finally {
13744
+ graphOutputRecorder = void 0;
13106
13745
  }
13107
- prefixOf.set(m[0], ch);
13108
- if (!seen.has(token)) {
13109
- seen.add(token);
13110
- tokens2.push(token);
13746
+ for (const [key, values] of seen) {
13747
+ const [value, ...others] = [...values];
13748
+ if (value === void 0 || others.length > 0) continue;
13749
+ preferences.set(key, value);
13111
13750
  }
13112
- i += 1 + m[0].length;
13113
- } else {
13114
- i++;
13115
13751
  }
13752
+ return preferences;
13116
13753
  }
13117
- return tokens2;
13118
- }
13119
- function stripLeadingTrivia(sql) {
13120
- let i = 0;
13121
- for (; ; ) {
13122
- while (i < sql.length && /\s/.test(sql[i] ?? "")) i++;
13123
- if (startsComment(sql, i)) {
13124
- i = skipComment(sql, i);
13125
- continue;
13754
+ /**
13755
+ * Validate `data` against every schema in `refs`. Returns all violations,
13756
+ * each tagged with the schema that produced it and a source line via
13757
+ * `lineFor`.
13758
+ *
13759
+ * `colFor` is optional and additive: this signature is public, so a fourth
13760
+ * *required* parameter — or a widened third one — would be a consumer break.
13761
+ * Callers with an extractor that supplies no column pass nothing and get the
13762
+ * previous behavior exactly.
13763
+ */
13764
+ async validate(data, refs, lineFor, colFor, locate) {
13765
+ const { [FILE_SCHEMA_KEY]: _omit, ...subject } = data;
13766
+ void _omit;
13767
+ const errors = [];
13768
+ for (const ref of refs) {
13769
+ const fn = await this.compile(ref);
13770
+ const ok = fn(subject);
13771
+ if (ok) continue;
13772
+ for (const e of fn.errors ?? []) {
13773
+ errors.push(toFieldError(ref, e, lineFor, colFor, locate));
13774
+ }
13126
13775
  }
13127
- return sql.slice(i);
13776
+ return errors;
13128
13777
  }
13129
- }
13130
- function isTrivia(text2) {
13131
- let i = 0;
13132
- while (i < text2.length) {
13133
- const ch = text2[i];
13134
- if (ch === void 0 || /\s/.test(ch) || ch === ";") {
13135
- i++;
13136
- continue;
13137
- }
13138
- if (startsComment(text2, i)) {
13139
- i = skipComment(text2, i);
13140
- continue;
13141
- }
13142
- return false;
13778
+ };
13779
+ function subjectOf(e) {
13780
+ switch (e.keyword) {
13781
+ case "required":
13782
+ return e.params.missingProperty;
13783
+ case "additionalProperties":
13784
+ return e.params.additionalProperty;
13785
+ case "format":
13786
+ return e.params.format;
13787
+ case "type":
13788
+ return e.params.type;
13789
+ default:
13790
+ return void 0;
13143
13791
  }
13144
- return true;
13145
- }
13146
- var sqliteModule;
13147
- function loadSqlite() {
13148
- sqliteModule ??= (async () => {
13149
- const original = process.emitWarning.bind(process);
13150
- const filtered = (warning, ...rest) => {
13151
- const text2 = typeof warning === "string" ? warning : warning.message;
13152
- if (text2.includes("SQLite is an experimental feature")) return;
13153
- Reflect.apply(original, process, [warning, ...rest]);
13154
- };
13155
- process.emitWarning = filtered;
13156
- try {
13157
- return await import("node:sqlite");
13158
- } finally {
13159
- process.emitWarning = original;
13160
- }
13161
- })();
13162
- return sqliteModule;
13163
- }
13164
-
13165
- // src/meta/core/collections.ts
13166
- function memberOf(collections, configDir, base, label) {
13167
- if (collections.length === 0) return [];
13168
- if (label === STDIN_LABEL) return [];
13169
- const rel = relative10(configDir, resolvePath2(base, label)).split(sep5).join("/");
13170
- if (isAbsolute6(rel)) return [];
13171
- return collections.filter((c) => isMember(c, rel)).map((c) => c.name);
13172
13792
  }
13173
- function retainMembers(files, membersFor) {
13174
- return files.filter((label) => membersFor(label).length > 0);
13175
- }
13176
- function collectionNames(collections) {
13177
- return (collections ?? []).map((c) => c.name);
13178
- }
13179
- function collectCollections(entries, params) {
13180
- const declared = params.collections ?? [];
13181
- if (declared.length === 0) return [];
13182
- const base = params.base ?? ".";
13183
- const configDir = params.configDir ?? base;
13184
- const members = /* @__PURE__ */ new Map();
13185
- for (const entry of entries) {
13186
- for (const name2 of memberOf(declared, configDir, base, entry.label)) {
13187
- const list2 = members.get(name2);
13188
- if (list2) list2.push(entry.label);
13189
- else members.set(name2, [entry.label]);
13190
- }
13793
+ function toFieldError(schema, e, lineFor, colFor, locate) {
13794
+ const defined = e;
13795
+ const instancePath = e.instancePath;
13796
+ const subject = subjectOf(defined);
13797
+ let message = e.message ?? "is invalid";
13798
+ let wantsColumn = true;
13799
+ if (defined.keyword === "required") {
13800
+ message = `must have required property '${defined.params.missingProperty}'`;
13801
+ wantsColumn = false;
13802
+ } else if (defined.keyword === "additionalProperties") {
13803
+ message = `must NOT have additional property '${defined.params.additionalProperty}'`;
13804
+ wantsColumn = false;
13191
13805
  }
13192
- return declared.map((c) => ({ name: c.name, members: members.get(c.name) ?? [] }));
13193
- }
13194
- function quoteSqlString(value) {
13195
- return `'${value.replaceAll("'", "''")}'`;
13196
- }
13197
- function createCollectionViews(db, collections) {
13198
- for (const c of collections) {
13199
- const where = c.members.length === 0 ? "0" : `_path IN (${c.members.map(quoteSqlString).join(", ")})`;
13200
- db.exec(
13201
- `CREATE VIEW ${quoteIdent(c.name)} AS SELECT * FROM docs WHERE ${where}`
13202
- );
13806
+ const located = defined.keyword === "additionalProperties" ? locate?.(
13807
+ `${instancePath}/${escapePointerSegment(defined.params.additionalProperty)}`
13808
+ ) ?? locate?.(instancePath) : locate?.(instancePath);
13809
+ if (located) {
13810
+ return {
13811
+ schema,
13812
+ instancePath,
13813
+ message,
13814
+ keyword: e.keyword,
13815
+ ...subject != null ? { subject } : {},
13816
+ file: located.file,
13817
+ ...located.line != null ? { line: located.line } : {},
13818
+ ...wantsColumn && located.col != null ? { col: located.col } : {}
13819
+ };
13203
13820
  }
13821
+ const line = lineFor(instancePath);
13822
+ const col = wantsColumn ? colFor?.(instancePath) : void 0;
13823
+ return {
13824
+ schema,
13825
+ instancePath,
13826
+ message,
13827
+ keyword: e.keyword,
13828
+ ...subject != null ? { subject } : {},
13829
+ ...line != null ? { line } : {},
13830
+ ...col != null ? { col } : {}
13831
+ };
13204
13832
  }
13205
13833
 
13206
13834
  // src/meta/core/derive/cache.ts
13207
13835
  import { createHash as createHash4 } from "node:crypto";
13208
- import { mkdirSync, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync } from "node:fs";
13836
+ import { mkdirSync, readFileSync as readFileSync3, renameSync, rmSync, writeFileSync } from "node:fs";
13209
13837
  import { join as join8 } from "node:path";
13210
13838
  var REVIEW_CACHE_DIR = ".manni/meta/review-cache";
13211
13839
  var REVIEW_CACHE_VERSION = 1;
@@ -13225,7 +13853,7 @@ var ReviewCache = class {
13225
13853
  if (!this.enabled) return void 0;
13226
13854
  let raw;
13227
13855
  try {
13228
- raw = JSON.parse(readFileSync2(this.entryPath(key), "utf8"));
13856
+ raw = JSON.parse(readFileSync3(this.entryPath(key), "utf8"));
13229
13857
  } catch {
13230
13858
  return void 0;
13231
13859
  }
@@ -13789,12 +14417,12 @@ function utcDate(iso) {
13789
14417
  }
13790
14418
 
13791
14419
  // src/meta/core/derive/index.ts
13792
- import { dirname as dirname9, resolve as resolve15 } from "node:path";
14420
+ import { dirname as dirname9, resolve as resolve16 } from "node:path";
13793
14421
 
13794
14422
  // src/meta/core/derive/codeowners.ts
13795
14423
  import { existsSync as existsSync3 } from "node:fs";
13796
- import { readFile as readFile8 } from "node:fs/promises";
13797
- import { dirname as dirname7, relative as relative11, resolve as resolve14, sep as sep6 } from "node:path";
14424
+ import { readFile as readFile7 } from "node:fs/promises";
14425
+ import { dirname as dirname7, relative as relative11, resolve as resolve15, sep as sep6 } from "node:path";
13798
14426
  import picomatch4 from "picomatch";
13799
14427
  var CODEOWNERS_LOCATIONS = [
13800
14428
  ".github/CODEOWNERS",
@@ -13804,11 +14432,11 @@ var CODEOWNERS_LOCATIONS = [
13804
14432
  ];
13805
14433
  function findCodeowners(root, explicit) {
13806
14434
  if (explicit !== void 0) {
13807
- const path = resolve14(root, explicit);
14435
+ const path = resolve15(root, explicit);
13808
14436
  return existsSync3(path) ? path : null;
13809
14437
  }
13810
14438
  for (const loc of CODEOWNERS_LOCATIONS) {
13811
- const path = resolve14(root, ...loc.split("/"));
14439
+ const path = resolve15(root, ...loc.split("/"));
13812
14440
  if (existsSync3(path)) return path;
13813
14441
  }
13814
14442
  return null;
@@ -13923,7 +14551,7 @@ function toGlobs(pattern) {
13923
14551
  var NOT_FOUND_REASON = `no CODEOWNERS file found (looked for ${CODEOWNERS_LOCATIONS.join(", ")})`;
13924
14552
  async function deriveFromCodeowners(inputs, opts) {
13925
14553
  const records = /* @__PURE__ */ new Map();
13926
- const explicitPath = opts.explicit === void 0 ? void 0 : resolve14(opts.configDir ?? process.cwd(), opts.explicit);
14554
+ const explicitPath = opts.explicit === void 0 ? void 0 : resolve15(opts.configDir ?? process.cwd(), opts.explicit);
13927
14555
  if (explicitPath !== void 0 && !existsSync3(explicitPath)) {
13928
14556
  return {
13929
14557
  status: { available: false, reason: `CODEOWNERS not found at ${explicitPath}` },
@@ -13957,7 +14585,7 @@ async function deriveFromCodeowners(inputs, opts) {
13957
14585
  const key = `${path}\0${label}`;
13958
14586
  let file = parsed2.get(key);
13959
14587
  if (!file) {
13960
- file = readFile8(path, "utf8").then(
14588
+ file = readFile7(path, "utf8").then(
13961
14589
  (text2) => parseCodeowners(text2, path, label)
13962
14590
  );
13963
14591
  parsed2.set(key, file);
@@ -15667,7 +16295,7 @@ async function deriveReviewsByRoot(inputs, git2, rootFor, ctx, requested) {
15667
16295
  if (byRoot.size === 0) {
15668
16296
  return unavailable3(requested, "no git repository contains the documents");
15669
16297
  }
15670
- const cacheDir = resolve15(ctx.configDir ?? ctx.cwd, REVIEW_CACHE_DIR);
16298
+ const cacheDir = resolve16(ctx.configDir ?? ctx.cwd, REVIEW_CACHE_DIR);
15671
16299
  for (const [root, bucket] of byRoot) {
15672
16300
  const client = ctx.reviews ?? await createReviewClient(root, { cwd: ctx.cwd, cache: ctx.cache, cacheDir, requested });
15673
16301
  const identity = await client.detect();
@@ -15845,7 +16473,7 @@ async function deriveForTable(inputs, ctx, hint, fields) {
15845
16473
  }
15846
16474
 
15847
16475
  // src/meta/commands/validate.ts
15848
- import { readFile as readFile10 } from "node:fs/promises";
16476
+ import { readFile as readFile9 } from "node:fs/promises";
15849
16477
 
15850
16478
  // src/meta/core/page-marks.ts
15851
16479
  function pageMarks(opts) {
@@ -15881,17 +16509,20 @@ function lazyValidator(opts) {
15881
16509
  let built;
15882
16510
  return () => opts.validator instanceof Validator ? opts.validator : built ??= opts.validator();
15883
16511
  }
16512
+ function pageSchemaSet(opts, label, probe, memberOf2) {
16513
+ return resolveSchemaSetWithSource({
16514
+ filePath: label,
16515
+ fileSchema: probe[FILE_SCHEMA_KEY],
16516
+ ...opts.cliSchemas !== void 0 ? { cliSchemas: [...opts.cliSchemas] } : {},
16517
+ config: opts.config,
16518
+ memberOf: opts.memberOf?.(label) ?? memberOf2,
16519
+ fileBase: opts.cwd,
16520
+ trustRoot: opts.trustRoot
16521
+ }).schemas;
16522
+ }
15884
16523
  function pageRefs(opts, label, probe, memberOf2) {
15885
16524
  try {
15886
- return resolveSchemaSetWithSource({
15887
- filePath: label,
15888
- fileSchema: probe[FILE_SCHEMA_KEY],
15889
- ...opts.cliSchemas !== void 0 ? { cliSchemas: [...opts.cliSchemas] } : {},
15890
- config: opts.config,
15891
- memberOf: opts.memberOf?.(label) ?? memberOf2,
15892
- fileBase: opts.cwd,
15893
- trustRoot: opts.trustRoot
15894
- }).schemas;
16525
+ return pageSchemaSet(opts, label, probe, memberOf2);
15895
16526
  } catch (err) {
15896
16527
  if (err instanceof DocmetaError) return void 0;
15897
16528
  throw err;
@@ -15922,8 +16553,8 @@ async function configMarks(configPath, cwd) {
15922
16553
  }
15923
16554
 
15924
16555
  // src/meta/core/relocation.ts
15925
- import { access, readFile as readFile9, stat as stat7, unlink } from "node:fs/promises";
15926
- import { basename as basename5, extname as extname3, isAbsolute as isAbsolute8, join as join9, relative as relative13, resolve as resolve16, sep as sep8 } from "node:path";
16556
+ import { access, readFile as readFile8, stat as stat7, unlink } from "node:fs/promises";
16557
+ import { basename as basename5, extname as extname3, isAbsolute as isAbsolute8, join as join9, relative as relative13, resolve as resolve17, sep as sep8 } from "node:path";
15927
16558
  import picomatch6 from "picomatch";
15928
16559
  import {
15929
16560
  LineCounter as LineCounter5,
@@ -16327,7 +16958,7 @@ var Model = class {
16327
16958
  c.externalMetadata.forEach((m, index) => {
16328
16959
  const url = classifyRef(m.file).kind === "url";
16329
16960
  const perPage = !url && hasPagePlaceholder(m.file);
16330
- const absPath = url || perPage ? void 0 : isAbsolute8(m.file) ? m.file : resolve16(configDir, m.file);
16961
+ const absPath = url || perPage ? void 0 : isAbsolute8(m.file) ? m.file : resolve17(configDir, m.file);
16331
16962
  this.manifests.push({
16332
16963
  collection: c.name,
16333
16964
  index,
@@ -16519,7 +17150,7 @@ var Model = class {
16519
17150
  /** A label relative to the config directory, posix; undefined outside it. */
16520
17151
  relative(label) {
16521
17152
  if (label === "<stdin>") return void 0;
16522
- const rel = toPosix3(relative13(this.configDir, resolve16(this.ctx.base, label)));
17153
+ const rel = toPosix3(relative13(this.configDir, resolve17(this.ctx.base, label)));
16523
17154
  return isAbsolute8(rel) || rel.startsWith("../") ? void 0 : rel;
16524
17155
  }
16525
17156
  };
@@ -16534,11 +17165,11 @@ async function collectionPath(target, cwd, configDir) {
16534
17165
  const posix7 = target.replace(/\\/g, "/");
16535
17166
  const scanned = picomatch6.scan(posix7);
16536
17167
  if (scanned.isGlob) {
16537
- const rel2 = toPosix3(relative13(configDir, resolve16(cwd, scanned.base === "" ? "." : scanned.base)));
17168
+ const rel2 = toPosix3(relative13(configDir, resolve17(cwd, scanned.base === "" ? "." : scanned.base)));
16538
17169
  if (isAbsolute8(rel2) || rel2 === ".." || rel2.startsWith("../")) return void 0;
16539
17170
  return rel2 === "" ? scanned.glob : `${rel2}/${scanned.glob}`;
16540
17171
  }
16541
- const abs = resolve16(cwd, posix7);
17172
+ const abs = resolve17(cwd, posix7);
16542
17173
  const rel = toPosix3(relative13(configDir, abs));
16543
17174
  if (isAbsolute8(rel) || rel.startsWith("..")) return void 0;
16544
17175
  let directory = posix7.endsWith("/");
@@ -16562,7 +17193,7 @@ async function exists(absPath, file) {
16562
17193
  }
16563
17194
  }
16564
17195
  function plannedConfigDir(ctx) {
16565
- return ctx.configDir ?? findGitRoot(ctx.cwd) ?? resolve16(ctx.cwd);
17196
+ return ctx.configDir ?? findGitRoot(ctx.cwd) ?? resolve17(ctx.cwd);
16566
17197
  }
16567
17198
  async function keyHome(ctx, label, data, key) {
16568
17199
  const model = new Model(ctx, plannedConfigDir(ctx));
@@ -16574,7 +17205,7 @@ async function keyHome(ctx, label, data, key) {
16574
17205
  ) : void 0;
16575
17206
  const owner = model.ownerOf(key, members, marked);
16576
17207
  if (owner !== void 0) {
16577
- const at2 = model.fileFor(owner, resolve16(ctx.base, label));
17208
+ const at2 = model.fileFor(owner, resolve17(ctx.base, label));
16578
17209
  if (owner.url || at2.abs === void 0) {
16579
17210
  return { kind: "url", collection: owner.collection, file: owner.file };
16580
17211
  }
@@ -16600,7 +17231,7 @@ function marksOf(ctx) {
16600
17231
  });
16601
17232
  }
16602
17233
  function entryFor(m, label, data, configDir, base, key) {
16603
- if (m.join === PATH_JOIN2) return toPosix3(relative13(configDir, resolve16(base, label)));
17234
+ if (m.join === PATH_JOIN2) return toPosix3(relative13(configDir, resolve17(base, label)));
16604
17235
  let raw = data[m.join];
16605
17236
  if (isEncryptedValue(raw)) {
16606
17237
  const k = key();
@@ -16618,7 +17249,7 @@ function entryFor(m, label, data, configDir, base, key) {
16618
17249
  return void 0;
16619
17250
  }
16620
17251
  async function relocationPages(ctx, request) {
16621
- const pages2 = new Set(request.files.map((label) => resolve16(ctx.base, label)));
17252
+ const pages2 = new Set(request.files.map((label) => resolve17(ctx.base, label)));
16622
17253
  const root = ctx.configDir ?? ctx.cwd;
16623
17254
  for (const collection of ctx.declaredCollections) {
16624
17255
  const perPage = collection.externalMetadata.some(
@@ -16634,7 +17265,7 @@ async function relocationPages(ctx, request) {
16634
17265
  ...ctx.config?.respectGitignore !== void 0 ? { configured: ctx.config.respectGitignore } : {}
16635
17266
  })
16636
17267
  });
16637
- for (const file of walked.files) pages2.add(resolve16(root, file));
17268
+ for (const file of walked.files) pages2.add(resolve17(root, file));
16638
17269
  }
16639
17270
  return [...pages2];
16640
17271
  }
@@ -16664,20 +17295,20 @@ async function planRelocation(ctx, request) {
16664
17295
  if (beyond) return void 0;
16665
17296
  throw new DocmetaError(`Unsupported file type "${extname3(label)}" for "${label}". Use --as to override.`);
16666
17297
  }
16667
- const absPath = resolve16(ctx.base, label);
17298
+ const absPath = resolve17(ctx.base, label);
16668
17299
  const elements = resolveElements(label, ctx.config, currentMembers(label));
16669
17300
  let content = "";
16670
17301
  let own2 = {};
16671
17302
  let unreadable2;
16672
17303
  if (beyond) {
16673
17304
  try {
16674
- content = await readFile9(absPath, "utf8");
17305
+ content = await readFile8(absPath, "utf8");
16675
17306
  own2 = extractor.extract(content, label, { elements }).data;
16676
17307
  } catch (err) {
16677
17308
  unreadable2 = (errorMessage(err).split("\n")[0] ?? "").replace(/:\s*$/, "");
16678
17309
  }
16679
17310
  } else {
16680
- content = await readFile9(absPath, "utf8");
17311
+ content = await readFile8(absPath, "utf8");
16681
17312
  try {
16682
17313
  own2 = extractor.extract(content, label, { elements }).data;
16683
17314
  } catch (err) {
@@ -16883,7 +17514,7 @@ async function planRelocation(ctx, request) {
16883
17514
  });
16884
17515
  const labels = dedupe2([
16885
17516
  ...request.files,
16886
- ...walked.files.map((f) => reported2(resolve16(configDir, f), ctx.base))
17517
+ ...walked.files.map((f) => reported2(resolve17(configDir, f), ctx.base))
16887
17518
  ]).filter((label) => model.members(label).includes(name2));
16888
17519
  for (const label of labels) {
16889
17520
  const page = await load(label, !runSet.has(label));
@@ -17118,7 +17749,7 @@ function assertNoStrandedEntries(m, index, pages2, configDir, fileOf) {
17118
17749
  }
17119
17750
  async function readManifestText(path, file, perPage) {
17120
17751
  try {
17121
- return await readFile9(path, "utf8");
17752
+ return await readFile8(path, "utf8");
17122
17753
  } catch (err) {
17123
17754
  if (perPage && isMissing(err)) return null;
17124
17755
  throw new DocmetaError(`Manifest ${file} could not be read: ${errorMessage(err)}`);
@@ -17148,7 +17779,7 @@ function keyLines2(text2, join12) {
17148
17779
  async function configTarget(ctx, configDir) {
17149
17780
  if (ctx.configPath !== void 0) {
17150
17781
  try {
17151
- const text2 = await readFile9(ctx.configPath, "utf8");
17782
+ const text2 = await readFile8(ctx.configPath, "utf8");
17152
17783
  return { path: ctx.configPath, text: text2, disk: text2 };
17153
17784
  } catch (err) {
17154
17785
  if (err.code !== "ENOENT") {
@@ -17159,10 +17790,10 @@ async function configTarget(ctx, configDir) {
17159
17790
  return { path: ctx.configPath, text: ctx.configFile?.text ?? null, disk: null };
17160
17791
  }
17161
17792
  }
17162
- for (const name2 of [...FAMILY_CONFIG_NAMES, ...MOOSE_CONFIG_NAMES]) {
17793
+ for (const name2 of FAMILY_CONFIG_NAMES) {
17163
17794
  const path = join9(configDir, name2);
17164
17795
  try {
17165
- const text2 = await readFile9(path, "utf8");
17796
+ const text2 = await readFile8(path, "utf8");
17166
17797
  return { path, text: text2, disk: text2 };
17167
17798
  } catch {
17168
17799
  }
@@ -17348,7 +17979,7 @@ async function applyRelocation(ctx, plan, io = {}) {
17348
17979
  for (const w of plan.writes) {
17349
17980
  let now;
17350
17981
  try {
17351
- now = await readFile9(w.path, "utf8");
17982
+ now = await readFile8(w.path, "utf8");
17352
17983
  } catch (err) {
17353
17984
  if (err.code !== "ENOENT") {
17354
17985
  throw new DocmetaError(`${label(w)} could not be read: ${errorMessage(err)}`);
@@ -17515,7 +18146,7 @@ function relocationOffers(plan) {
17515
18146
  }
17516
18147
 
17517
18148
  // src/meta/commands/validate.ts
17518
- import { resolve as resolve18, extname as extname4 } from "node:path";
18149
+ import { resolve as resolve19, extname as extname4 } from "node:path";
17519
18150
 
17520
18151
  // src/meta/core/checks.ts
17521
18152
  var CHECK_KEYWORD = "check";
@@ -17653,7 +18284,7 @@ async function runChecks(checks, entries, ctx = {}) {
17653
18284
  }
17654
18285
 
17655
18286
  // src/meta/core/derive/provenance-place.ts
17656
- import { isAbsolute as isAbsolute9, relative as relative14, resolve as resolve17, sep as sep9 } from "node:path";
18287
+ import { isAbsolute as isAbsolute9, relative as relative14, resolve as resolve18, sep as sep9 } from "node:path";
17657
18288
  var toPosix4 = (p) => p.split(sep9).join("/");
17658
18289
  function provenanceManifests(collections, configDir, base, field = PROVENANCE_FIELD) {
17659
18290
  const out = [];
@@ -17675,7 +18306,7 @@ function provenanceManifests(collections, configDir, base, field = PROVENANCE_FI
17675
18306
  });
17676
18307
  continue;
17677
18308
  }
17678
- const absPath = isAbsolute9(manifest.file) ? manifest.file : resolve17(configDir, manifest.file);
18309
+ const absPath = isAbsolute9(manifest.file) ? manifest.file : resolve18(configDir, manifest.file);
17679
18310
  const perPage = hasPagePlaceholder(manifest.file);
17680
18311
  out.push({
17681
18312
  collection: collection.name,
@@ -17709,7 +18340,7 @@ function provenancePlace(label, data, manifests, collections, configDir, base, m
17709
18340
  if (declared.implied?.url === true) throw new DocmetaError(urlRefusal(declared.collection, field));
17710
18341
  const manifest = declared.perPage ? ownManifest(declared, label, collections, configDir, base) : declared;
17711
18342
  if (manifest.join === PATH_JOIN2) {
17712
- const entry = toPosix4(relative14(configDir, resolve17(base, label)));
18343
+ const entry = toPosix4(relative14(configDir, resolve18(base, label)));
17713
18344
  return { manifest, absPath: manifest.absPath, entry, join: manifest.join };
17714
18345
  }
17715
18346
  const value = data[manifest.join];
@@ -17721,7 +18352,7 @@ function provenancePlace(label, data, manifests, collections, configDir, base, m
17721
18352
  return { manifest, absPath: manifest.absPath, entry: String(value), join: manifest.join };
17722
18353
  }
17723
18354
  function ownManifest(manifest, label, collections, configDir, base) {
17724
- const resolved = pageManifestPath(manifest.written, configDir, resolve17(base, label));
18355
+ const resolved = pageManifestPath(manifest.written, configDir, resolve18(base, label));
17725
18356
  if ("outside" in resolved) {
17726
18357
  const declared = collections.find((c) => c.name === manifest.collection)?.externalMetadata.find((m) => m.file === manifest.written);
17727
18358
  throw new DocmetaError(
@@ -17757,6 +18388,36 @@ function stampManifests(label, data, collections, configDir, base, marked, field
17757
18388
  return out;
17758
18389
  }
17759
18390
 
18391
+ // src/shared/cli-options.ts
18392
+ import { basename as basename6, relative as relative15 } from "node:path";
18393
+ function collect(value, prev) {
18394
+ return prev.concat([value]);
18395
+ }
18396
+ function splitList(value) {
18397
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
18398
+ }
18399
+ var STDIN_REQUIRES_AS = "Reading from stdin (`-`) requires --as <format> to choose an extractor.";
18400
+ async function readStdin() {
18401
+ const chunks = [];
18402
+ for await (const chunk of process.stdin) chunks.push(chunk);
18403
+ return Buffer.concat(chunks).toString("utf8");
18404
+ }
18405
+ function configOption(value) {
18406
+ if (value === false) return { noConfig: true };
18407
+ return typeof value === "string" ? { configPath: value } : {};
18408
+ }
18409
+ function explicitFalse(value) {
18410
+ return value === false ? false : void 0;
18411
+ }
18412
+ function reportConfig(toStdout, cwd) {
18413
+ return (info) => {
18414
+ const where = (relative15(cwd, info.dir) || ".").replace(/\\/g, "/");
18415
+ const line = `Using ${basename6(info.path)} (${where})
18416
+ `;
18417
+ (toStdout ? process.stdout : process.stderr).write(line);
18418
+ };
18419
+ }
18420
+
17760
18421
  // src/meta/commands/validate.ts
17761
18422
  function parseErrorResult(file, format, message, keyword) {
17762
18423
  const err = {
@@ -17840,7 +18501,7 @@ async function runValidate(opts) {
17840
18501
  base,
17841
18502
  offline: opts.offline ?? config?.offline ?? false,
17842
18503
  // A `{page}` manifest (0058) is read for exactly the pages this run reads.
17843
- pages: files.map((file) => resolve18(base, file))
18504
+ pages: files.map((file) => resolve19(base, file))
17844
18505
  });
17845
18506
  const validator = new Validator(
17846
18507
  schemaLoadOptions({
@@ -18011,7 +18672,7 @@ async function runValidate(opts) {
18011
18672
  const owned = declaredCollections.length === 0 ? {} : stampManifests(label, extracted.data, declaredCollections, configDir ?? cwd, base, merged.marked, STAMPED_DATE_FIELDS);
18012
18673
  deriveInputs.push({
18013
18674
  label,
18014
- absPath: resolve18(base, label),
18675
+ absPath: resolve19(base, label),
18015
18676
  content,
18016
18677
  extracted,
18017
18678
  ...place !== void 0 ? { provenanceManifest: place } : {},
@@ -18145,13 +18806,13 @@ async function runValidate(opts) {
18145
18806
  const content = opts.stdinContent ?? "";
18146
18807
  if (!forcedExtractor) {
18147
18808
  throw new DocmetaError(
18148
- "Reading from stdin (`-`) requires --as <format> to choose an extractor."
18809
+ STDIN_REQUIRES_AS
18149
18810
  );
18150
18811
  }
18151
18812
  await processOne(STDIN_LABEL, content, forcedExtractor.extensions[0] ?? "");
18152
18813
  }
18153
18814
  for (const file of files) {
18154
- const content = await readFile10(resolve18(base, file), "utf8");
18815
+ const content = await readFile9(resolve19(base, file), "utf8");
18155
18816
  await processOne(file, content, extname4(file));
18156
18817
  }
18157
18818
  if (unverified > 0) opts.onNotice?.(unverifiedWarning(unverified));
@@ -18297,8 +18958,8 @@ async function runValidate(opts) {
18297
18958
  }
18298
18959
 
18299
18960
  // src/meta/commands/get.ts
18300
- import { readFile as readFile11 } from "node:fs/promises";
18301
- import { resolve as resolve19, extname as extname5 } from "node:path";
18961
+ import { readFile as readFile10 } from "node:fs/promises";
18962
+ import { resolve as resolve20, extname as extname5 } from "node:path";
18302
18963
  async function runGet(opts) {
18303
18964
  const cwd = opts.cwd ?? process.cwd();
18304
18965
  if (opts.fields.length === 0) {
@@ -18359,7 +19020,7 @@ async function runGet(opts) {
18359
19020
  base,
18360
19021
  offline: opts.offline ?? config?.offline ?? false,
18361
19022
  // A `{page}` manifest (0058) is read for exactly the pages this run reads.
18362
- pages: files.map((file) => resolve19(base, file))
19023
+ pages: files.map((file) => resolve20(base, file))
18363
19024
  });
18364
19025
  const commands = commandsOf(config?.derive) ?? {};
18365
19026
  const deriving = opts.derived !== false;
@@ -18402,7 +19063,7 @@ async function runGet(opts) {
18402
19063
  const owned = declaredCollections.length === 0 ? {} : stampManifests(label, own2.data, declaredCollections, configDir ?? cwd, base, merged.marked, STAMPED_DATE_FIELDS);
18403
19064
  deriveInputs.push({
18404
19065
  label,
18405
- absPath: resolve19(base, label),
19066
+ absPath: resolve20(base, label),
18406
19067
  content,
18407
19068
  extracted: own2,
18408
19069
  ...place !== void 0 ? { provenanceManifest: place } : {},
@@ -18437,13 +19098,13 @@ async function runGet(opts) {
18437
19098
  if (usingStdin) {
18438
19099
  if (!forced) {
18439
19100
  throw new DocmetaError(
18440
- "Reading from stdin (`-`) requires --as <format> to choose an extractor."
19101
+ STDIN_REQUIRES_AS
18441
19102
  );
18442
19103
  }
18443
19104
  await readOne(STDIN_LABEL, opts.stdinContent ?? "", forced.extensions[0] ?? "");
18444
19105
  }
18445
19106
  for (const file of files) {
18446
- const content = await readFile11(resolve19(base, file), "utf8");
19107
+ const content = await readFile10(resolve20(base, file), "utf8");
18447
19108
  await readOne(file, content, extname5(file));
18448
19109
  }
18449
19110
  if (deriving) {
@@ -18569,8 +19230,8 @@ function parseJsonPointer(pointer) {
18569
19230
 
18570
19231
  // src/meta/commands/schemas.ts
18571
19232
  import { existsSync as existsSync4 } from "node:fs";
18572
- import { mkdir as mkdir3, readFile as readFile12 } from "node:fs/promises";
18573
- import { dirname as dirname10, extname as extname6, isAbsolute as isAbsolute10, join as join10, relative as relative15, resolve as resolve20, sep as sep10 } from "node:path";
19233
+ import { mkdir as mkdir3, readFile as readFile11 } from "node:fs/promises";
19234
+ import { dirname as dirname10, extname as extname6, isAbsolute as isAbsolute10, join as join10, relative as relative16, resolve as resolve21, sep as sep10 } from "node:path";
18574
19235
  import { parseDocument as parseDocument8, parse as parseYaml6, stringify as stringify4 } from "yaml";
18575
19236
  function getSchemasInfo(registered2) {
18576
19237
  return {
@@ -18582,7 +19243,7 @@ function getSchemasInfo(registered2) {
18582
19243
  var DEFAULT_VENDOR_DIR = "./schema";
18583
19244
  var DEFAULT_CONFIG_NAME = "manni.config.yaml";
18584
19245
  function posixRelative(from, to) {
18585
- return relative15(from, to).split(sep10).join("/");
19246
+ return relative16(from, to).split(sep10).join("/");
18586
19247
  }
18587
19248
  function vendorFileName(url) {
18588
19249
  let parsed2;
@@ -18668,9 +19329,9 @@ async function runVendorSchema(opts) {
18668
19329
  );
18669
19330
  }
18670
19331
  const loaded = await loadConfig(opts.configPath, cwd);
18671
- const configPath = loaded?.path ?? resolve20(cwd, DEFAULT_CONFIG_NAME);
19332
+ const configPath = loaded?.path ?? resolve21(cwd, DEFAULT_CONFIG_NAME);
18672
19333
  const configDir = loaded?.dir ?? cwd;
18673
- const absDir = resolve20(cwd, opts.dir ?? DEFAULT_VENDOR_DIR);
19334
+ const absDir = resolve21(cwd, opts.dir ?? DEFAULT_VENDOR_DIR);
18674
19335
  const absFile = join10(absDir, vendorFileName(url));
18675
19336
  await assertNotIgnored(absFile, absDir, cwd, opts.onNotice);
18676
19337
  const fromConfig = posixRelative(configDir, absFile);
@@ -18689,7 +19350,7 @@ async function runVendorSchema(opts) {
18689
19350
  }
18690
19351
  let unchanged = false;
18691
19352
  if (existsSync4(absFile)) {
18692
- const current = await readFile12(absFile);
19353
+ const current = await readFile11(absFile);
18693
19354
  unchanged = bytes.equals(current);
18694
19355
  if (!unchanged && !folded.replaced) {
18695
19356
  throw new DocmetaError(
@@ -18699,7 +19360,7 @@ async function runVendorSchema(opts) {
18699
19360
  }
18700
19361
  await mkdir3(absDir, { recursive: true });
18701
19362
  await writeFileAtomic(absFile, bytes);
18702
- const existing = loaded ? await readFile12(configPath, "utf8") : null;
19363
+ const existing = loaded ? await readFile11(configPath, "utf8") : null;
18703
19364
  const section = loaded ? loaded.section : META_SECTION;
18704
19365
  const text2 = renderConfig(existing, folded.entries, section);
18705
19366
  reparseConfig(text2, posixRelative(cwd, configPath) || DEFAULT_CONFIG_NAME, section);
@@ -18841,12 +19502,12 @@ async function runInferSchema(opts) {
18841
19502
  }
18842
19503
  if (usingStdin && !forced) {
18843
19504
  throw new DocmetaError(
18844
- "Reading from stdin (`-`) requires --as <format> to choose an extractor."
19505
+ STDIN_REQUIRES_AS
18845
19506
  );
18846
19507
  }
18847
19508
  let absOut;
18848
19509
  if (opts.out !== void 0) {
18849
- absOut = resolve20(cwd, opts.out);
19510
+ absOut = resolve21(cwd, opts.out);
18850
19511
  await assertNotIgnored(
18851
19512
  absOut,
18852
19513
  dirname10(absOut),
@@ -18892,7 +19553,7 @@ async function runInferSchema(opts) {
18892
19553
  configDir: configDir ?? cwd,
18893
19554
  base,
18894
19555
  offline: opts.offline ?? config?.offline ?? false,
18895
- pages: files.map((file) => resolve20(base, file))
19556
+ pages: files.map((file) => resolve21(base, file))
18896
19557
  });
18897
19558
  const stats = /* @__PURE__ */ new Map();
18898
19559
  const unreadable2 = [];
@@ -18951,7 +19612,7 @@ async function runInferSchema(opts) {
18951
19612
  await scanOne(STDIN_LABEL, opts.stdinContent ?? "", forced.extensions[0] ?? "");
18952
19613
  }
18953
19614
  for (const file of files) {
18954
- const content = await readFile12(resolve20(base, file), "utf8");
19615
+ const content = await readFile11(resolve21(base, file), "utf8");
18955
19616
  await scanOne(file, content, extname6(file));
18956
19617
  }
18957
19618
  for (const u of unreadable2) {
@@ -19012,8 +19673,8 @@ async function runInferSchema(opts) {
19012
19673
  // src/meta/commands/query.ts
19013
19674
  import { randomBytes as randomBytes2 } from "node:crypto";
19014
19675
  import { existsSync as existsSync5 } from "node:fs";
19015
- import { mkdir as mkdir4, open as open2, readFile as readFile13, rename as rename3, rm as rm3 } from "node:fs/promises";
19016
- import { dirname as dirname11, isAbsolute as isAbsolute11, resolve as resolve21, extname as extname7, sep as sep11 } from "node:path";
19676
+ import { mkdir as mkdir4, open as open2, readFile as readFile12, rename as rename3, rm as rm3 } from "node:fs/promises";
19677
+ import { dirname as dirname11, isAbsolute as isAbsolute11, resolve as resolve22, extname as extname7, sep as sep11 } from "node:path";
19017
19678
 
19018
19679
  // src/shared/prompt.ts
19019
19680
  import { createInterface } from "node:readline";
@@ -19272,7 +19933,7 @@ async function runQuery(opts) {
19272
19933
  base,
19273
19934
  offline: opts.offline ?? config?.offline ?? false,
19274
19935
  // A `{page}` manifest (0058) is read for exactly the pages this run reads.
19275
- pages: files.map((file) => resolve21(base, file))
19936
+ pages: files.map((file) => resolve22(base, file))
19276
19937
  });
19277
19938
  const configuredKey = lazyKey(configFile, opts.env);
19278
19939
  let ensuredKey;
@@ -19358,7 +20019,7 @@ async function runQuery(opts) {
19358
20019
  if (usingStdin) {
19359
20020
  if (!forced) {
19360
20021
  throw new DocmetaError(
19361
- "Reading from stdin (`-`) requires --as <format> to choose an extractor."
20022
+ STDIN_REQUIRES_AS
19362
20023
  );
19363
20024
  }
19364
20025
  const ext = forced.extensions[0];
@@ -19370,10 +20031,10 @@ async function runQuery(opts) {
19370
20031
  await readOne(STDIN_LABEL, opts.stdinContent ?? "", ext);
19371
20032
  }
19372
20033
  for (const file of files) {
19373
- const content = await readFile13(resolve21(base, file), "utf8");
20034
+ const content = await readFile12(resolve22(base, file), "utf8");
19374
20035
  await readOne(file, content, extname7(file));
19375
20036
  }
19376
- const db = opts.db === void 0 ? void 0 : { resolved: resolve21(cwd, opts.db), display: opts.db };
20037
+ const db = opts.db === void 0 ? void 0 : { resolved: resolve22(cwd, opts.db), display: opts.db };
19377
20038
  const run2 = await runSql(sql, entries, {
19378
20039
  target: db,
19379
20040
  params: boundParams,
@@ -19516,7 +20177,7 @@ async function runSql(sql, entries, ctx) {
19516
20177
  const owned = ctx.declaredCollections.length === 0 ? {} : stampManifests(e.label, e.own.data, ctx.declaredCollections, manifestRoot, ctx.base, e.marked, STAMPED_DATE_FIELDS);
19517
20178
  return {
19518
20179
  label: e.label,
19519
- absPath: resolve21(ctx.base, e.label),
20180
+ absPath: resolve22(ctx.base, e.label),
19520
20181
  content: e.content,
19521
20182
  extracted: e.own,
19522
20183
  ...place !== void 0 ? { provenanceManifest: place } : {},
@@ -19988,14 +20649,14 @@ function assertSchemaWriteWithin(ctx, abs) {
19988
20649
  );
19989
20650
  }
19990
20651
  function foldPath(p) {
19991
- const abs = resolve21(p);
20652
+ const abs = resolve22(p);
19992
20653
  return process.platform === "win32" ? abs.toLowerCase() : abs;
19993
20654
  }
19994
20655
  function filePinsByPath(ctx) {
19995
20656
  const byPath = /* @__PURE__ */ new Map();
19996
20657
  for (const [ref, pin] of collectSchemaPins(ctx.config)) {
19997
20658
  if (classifyRef(ref).kind !== "file") continue;
19998
- byPath.set(foldPath(resolve21(ctx.cwd, ref)), pin);
20659
+ byPath.set(foldPath(resolve22(ctx.cwd, ref)), pin);
19999
20660
  }
20000
20661
  return byPath;
20001
20662
  }
@@ -20024,13 +20685,13 @@ async function loadSetMembers(refs, ctx) {
20024
20685
  });
20025
20686
  continue;
20026
20687
  }
20027
- const abs = registered2?.path ?? resolve21(ctx.cwd, ref);
20688
+ const abs = registered2?.path ?? resolve22(ctx.cwd, ref);
20028
20689
  const canon = foldPath(abs);
20029
20690
  if (seenFiles.has(canon)) continue;
20030
20691
  seenFiles.add(canon);
20031
20692
  let text2;
20032
20693
  try {
20033
- text2 = await readFile13(abs, "utf8");
20694
+ text2 = await readFile12(abs, "utf8");
20034
20695
  } catch {
20035
20696
  throw new DocmetaError(
20036
20697
  `Schema file not found: "${ref}" (looked at ${abs}).`
@@ -20231,7 +20892,7 @@ async function planSchemaMutation(ops, entries, ctx) {
20231
20892
  const [, name2, ver] = target.builtinId.split(":");
20232
20893
  const safe = (s) => s.replace(/[^A-Za-z0-9._-]/g, "-");
20233
20894
  const forkName = `${safe(name2 ?? "schema")}-${safe(ver ?? "0")}.local.json`;
20234
- schemaAbs = resolve21(ctx.configDir ?? ctx.cwd, "schemas", forkName);
20895
+ schemaAbs = resolve22(ctx.configDir ?? ctx.cwd, "schemas", forkName);
20235
20896
  assertSchemaWriteWithin(ctx, schemaAbs);
20236
20897
  if (existsSync5(schemaAbs)) {
20237
20898
  throw new DocmetaError(
@@ -20387,7 +21048,7 @@ async function planConfigEdit(ctx, edit, runRefs) {
20387
21048
  if (configPath === void 0 || configDir === void 0) {
20388
21049
  return { changes: [], repointed: false };
20389
21050
  }
20390
- const text2 = await readFile13(configPath, "utf8");
21051
+ const text2 = await readFile12(configPath, "utf8");
20391
21052
  const doc = parseDocument10(stripBom2(text2));
20392
21053
  const display = displayPath(ctx, configPath);
20393
21054
  const rawRefOf = (item) => {
@@ -20398,7 +21059,7 @@ async function planConfigEdit(ctx, edit, runRefs) {
20398
21059
  }
20399
21060
  return void 0;
20400
21061
  };
20401
- const rebaseRaw = (raw) => classifyRef(raw).kind === "file" && !isAbsolute11(raw) ? resolve21(configDir, raw) : raw;
21062
+ const rebaseRaw = (raw) => classifyRef(raw).kind === "file" && !isAbsolute11(raw) ? resolve22(configDir, raw) : raw;
20402
21063
  const wantSet = runRefs.map(rebaseRaw).sort().join("\n");
20403
21064
  const seqResolvesToRunSet = (node) => {
20404
21065
  if (!isSeq5(node)) return false;
@@ -20443,7 +21104,7 @@ async function planConfigEdit(ctx, edit, runRefs) {
20443
21104
  if (!isMap10(item)) continue;
20444
21105
  const raw = rawRefOf(item);
20445
21106
  if (raw === void 0 || classifyRef(raw).kind !== "file") continue;
20446
- const abs = isAbsolute11(raw) ? raw : resolve21(configDir, raw);
21107
+ const abs = isAbsolute11(raw) ? raw : resolve22(configDir, raw);
20447
21108
  if (foldPath(abs) !== wantPath) continue;
20448
21109
  pinFrom = item.get("integrity");
20449
21110
  item.set("integrity", edit.integrity.value);
@@ -20672,7 +21333,7 @@ function buildChanges(diff, entries, sentinel, ctx, renameHints = [], booleanAdd
20672
21333
  `"${file}": no writable format for that extension.`
20673
21334
  );
20674
21335
  }
20675
- if (existsSync5(resolve21(ctx.base, file))) {
21336
+ if (existsSync5(resolve22(ctx.base, file))) {
20676
21337
  throw new DocmetaError(
20677
21338
  `"${file}" already exists; INSERT creates new files only.`
20678
21339
  );
@@ -20691,7 +21352,7 @@ function buildChanges(diff, entries, sentinel, ctx, renameHints = [], booleanAdd
20691
21352
  `"${from}" -> "${to}": a rename may not change the extension.`
20692
21353
  );
20693
21354
  }
20694
- if (existsSync5(resolve21(ctx.base, to))) {
21355
+ if (existsSync5(resolve22(ctx.base, to))) {
20695
21356
  throw new DocmetaError(
20696
21357
  `"${to}" already exists; the rename would overwrite it.`
20697
21358
  );
@@ -20725,9 +21386,9 @@ function validateNewPath(p, base) {
20725
21386
  if (p.split(/[\\/]/).some((segment) => segment === "..")) {
20726
21387
  refuse("it traverses upward");
20727
21388
  }
20728
- const abs = resolve21(base, p);
20729
- if (abs === resolve21(base)) refuse("it points at the corpus root, not a file");
20730
- if (!abs.startsWith(resolve21(base) + sep11)) refuse("it escapes the corpus");
21389
+ const abs = resolve22(base, p);
21390
+ if (abs === resolve22(base)) refuse("it points at the corpus root, not a file");
21391
+ if (!abs.startsWith(resolve22(base) + sep11)) refuse("it escapes the corpus");
20731
21392
  }
20732
21393
  function refuseJoinFieldChanges(changes, entries, ctx) {
20733
21394
  const index = ctx.externalMetadata;
@@ -20802,7 +21463,7 @@ function runPages(changes, entries, base) {
20802
21463
  const labels = new Set(entries.map((e) => e.label));
20803
21464
  for (const c of changes) if (!("schema" in c) && !("config" in c)) labels.add(c.file);
20804
21465
  labels.delete(STDIN_LABEL);
20805
- return [...labels].map((label) => resolve21(base, label));
21466
+ return [...labels].map((label) => resolve22(base, label));
20806
21467
  }
20807
21468
  async function planManifestEdits(changes, entries, ctx, renameHints) {
20808
21469
  for (const c of changes) {
@@ -20854,7 +21515,7 @@ async function planManifestEdits(changes, entries, ctx, renameHints) {
20854
21515
  let text2;
20855
21516
  let expected;
20856
21517
  try {
20857
- text2 = await readFile13(home.absPath, "utf8");
21518
+ text2 = await readFile12(home.absPath, "utf8");
20858
21519
  expected = text2;
20859
21520
  } catch (err) {
20860
21521
  if (!home.perPage || !isMissing(err)) {
@@ -20890,7 +21551,7 @@ async function planManifestEdits(changes, entries, ctx, renameHints) {
20890
21551
  );
20891
21552
  const index = await leftOut;
20892
21553
  if (index === null) return void 0;
20893
- const abs = resolve21(ctx.base, file);
21554
+ const abs = resolve22(ctx.base, file);
20894
21555
  const named = index.entries.find(
20895
21556
  (e) => e.join === PATH_JOIN2 && e.abs === abs && excluded.includes(e.collection)
20896
21557
  );
@@ -20937,7 +21598,7 @@ async function planManifestEdits(changes, entries, ctx, renameHints) {
20937
21598
  continue;
20938
21599
  }
20939
21600
  if ("renamed" in c) {
20940
- const supplied = ctx.externalMetadata?.byPath.get(resolve21(ctx.base, c.file));
21601
+ const supplied = ctx.externalMetadata?.byPath.get(resolve22(ctx.base, c.file));
20941
21602
  const seen = /* @__PURE__ */ new Set();
20942
21603
  for (const [key, sv] of supplied ?? []) {
20943
21604
  if (seen.has(sv.file) || !ctx.memberships(c.file).includes(sv.collection)) continue;
@@ -21245,14 +21906,14 @@ async function applyChanges(changes, entries, ctx, schemaPlan, manifestWrites =
21245
21906
  const pendingWrites = [];
21246
21907
  const pendingRenames = [];
21247
21908
  for (const [label, ops] of grouped) {
21248
- const path = resolve21(ctx.base, label);
21909
+ const path = resolve22(ctx.base, label);
21249
21910
  if (ops.renamedTo !== void 0) {
21250
21911
  if (!existsSync5(path)) {
21251
21912
  throw new DocmetaError(
21252
21913
  `"${label}" is gone from disk since it was read; re-run the query.`
21253
21914
  );
21254
21915
  }
21255
- const toDisk = resolve21(ctx.base, ops.renamedTo);
21916
+ const toDisk = resolve22(ctx.base, ops.renamedTo);
21256
21917
  if (existsSync5(toDisk)) {
21257
21918
  throw new DocmetaError(
21258
21919
  `"${ops.renamedTo}" appeared on disk since the plan; re-run the query.`
@@ -21290,7 +21951,7 @@ async function applyChanges(changes, entries, ctx, schemaPlan, manifestWrites =
21290
21951
  }
21291
21952
  const entry = byLabel.get(label);
21292
21953
  if (!entry) throw new DocmetaError(`No loaded entry for "${label}".`);
21293
- const content = await readFile13(path, "utf8");
21954
+ const content = await readFile12(path, "utf8");
21294
21955
  const members = ctx.memberships(label);
21295
21956
  const current = (await mergeWithMarks(
21296
21957
  label,
@@ -21362,7 +22023,7 @@ async function applyChanges(changes, entries, ctx, schemaPlan, manifestWrites =
21362
22023
  }
21363
22024
  let now;
21364
22025
  try {
21365
- now = await readFile13(w.path, "utf8");
22026
+ now = await readFile12(w.path, "utf8");
21366
22027
  } catch {
21367
22028
  now = void 0;
21368
22029
  }
@@ -21375,7 +22036,7 @@ async function applyChanges(changes, entries, ctx, schemaPlan, manifestWrites =
21375
22036
  for (const m of manifestWrites) {
21376
22037
  let now;
21377
22038
  try {
21378
- now = await readFile13(m.path, "utf8");
22039
+ now = await readFile12(m.path, "utf8");
21379
22040
  } catch {
21380
22041
  now = null;
21381
22042
  }
@@ -21508,19 +22169,17 @@ function skipExpression(sql, from) {
21508
22169
  }
21509
22170
 
21510
22171
  // src/meta/commands/fill.ts
21511
- import { readFile as readFile16 } from "node:fs/promises";
21512
- import { resolve as resolve23, extname as extname9, join as join11 } from "node:path";
22172
+ import { readFile as readFile15 } from "node:fs/promises";
22173
+ import { resolve as resolve24, extname as extname9, join as join11 } from "node:path";
21513
22174
  import {
21514
22175
  completeValidatedJSON,
21515
22176
  makeProvider,
21516
- resolveProviderIdentityAsync,
21517
- DEFAULT_MODELS,
21518
22177
  JsonCache,
21519
22178
  buildCacheKey,
21520
22179
  sha256,
21521
22180
  pricingFor,
21522
22181
  costOfUsage,
21523
- InferenceError
22182
+ InferenceError as InferenceError2
21524
22183
  } from "@hawkeyexl/inference";
21525
22184
 
21526
22185
  // src/meta/commands/fill-projection.ts
@@ -21930,8 +22589,8 @@ function single(alternatives2) {
21930
22589
  }
21931
22590
 
21932
22591
  // src/meta/commands/fill-hints.ts
21933
- import { readFile as readFile14 } from "node:fs/promises";
21934
- import { extname as extname8, resolve as resolve22 } from "node:path";
22592
+ import { readFile as readFile13 } from "node:fs/promises";
22593
+ import { extname as extname8, resolve as resolve23 } from "node:path";
21935
22594
 
21936
22595
  // src/shared/reference-fields.ts
21937
22596
  var CONCEPT_FIELDS = [["concepts"], ["graph", "concepts"]];
@@ -22017,7 +22676,7 @@ function pageIdReader(opts) {
22017
22676
  return held;
22018
22677
  };
22019
22678
  return async (file, names) => {
22020
- const self = resolve22(file);
22679
+ const self = resolve23(file);
22021
22680
  const ids = /* @__PURE__ */ new Set();
22022
22681
  for (const name2 of names) {
22023
22682
  const collection = opts.collections.find((c) => c.name === name2);
@@ -22041,10 +22700,10 @@ async function readIds(collection, opts) {
22041
22700
  if (!isMember(collection, file)) continue;
22042
22701
  const extractor = extractorForExtension(extname8(file));
22043
22702
  if (extractor === void 0) continue;
22044
- const path = resolve22(opts.configDir, file);
22703
+ const path = resolve23(opts.configDir, file);
22045
22704
  let id;
22046
22705
  try {
22047
- id = extractor.extract(await readFile14(path, "utf8"), file).data.id;
22706
+ id = extractor.extract(await readFile13(path, "utf8"), file).data.id;
22048
22707
  } catch {
22049
22708
  continue;
22050
22709
  }
@@ -22063,7 +22722,7 @@ function termLabelSource() {
22063
22722
  }
22064
22723
 
22065
22724
  // src/meta/core/manifest-writes.ts
22066
- import { readFile as readFile15 } from "node:fs/promises";
22725
+ import { readFile as readFile14 } from "node:fs/promises";
22067
22726
  var manifestCodec = {
22068
22727
  apply(text2, op) {
22069
22728
  const where = { entry: op.entry, key: op.key, join: op.join, file: op.file };
@@ -22079,7 +22738,7 @@ function applyManifestOp(text2, op) {
22079
22738
  async function holdManifestFile(path, file, perPage = false) {
22080
22739
  let before;
22081
22740
  try {
22082
- before = await readFile15(path, "utf8");
22741
+ before = await readFile14(path, "utf8");
22083
22742
  } catch (err) {
22084
22743
  if (perPage && isMissing(err)) return heldManifest(path, file, "", true);
22085
22744
  throw new DocmetaError(`Manifest ${file} could not be read: ${errorMessage(err)}`);
@@ -22096,7 +22755,7 @@ async function settleManifest(held, io = {}) {
22096
22755
  codec: manifestCodec,
22097
22756
  // A per-page manifest (proposal 0058) may be the first file in its directory.
22098
22757
  write: io.write ?? ((at2, text2) => writeFileAtomic(at2, text2, { createParents: true })),
22099
- read: io.read ?? ((at2) => readFile15(at2, "utf8")),
22758
+ read: io.read ?? ((at2) => readFile14(at2, "utf8")),
22100
22759
  toError: (message) => new DocmetaError(message)
22101
22760
  });
22102
22761
  rebaseline(held);
@@ -22253,12 +22912,56 @@ function buildUserPrompt(params, before = []) {
22253
22912
  ].join("\n");
22254
22913
  }
22255
22914
 
22915
+ // src/meta/core/meta-provenance.ts
22916
+ function isPlainRecord(value) {
22917
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22918
+ }
22919
+ function mergeMetaProvenance(held, model, key, proposed) {
22920
+ if (held !== void 0 && !Array.isArray(held)) return void 0;
22921
+ const list2 = held === void 0 ? [] : [...held];
22922
+ const at2 = list2.findIndex((e) => isPlainRecord(e) && e["generated-by"] === model);
22923
+ const prior = list2[at2];
22924
+ const kept = isPlainRecord(prior) ? prior : {};
22925
+ const priorNames = kept[key];
22926
+ const names = Array.isArray(priorNames) ? priorNames.filter((p) => typeof p === "string") : [];
22927
+ const confidence = {};
22928
+ if (isPlainRecord(kept.confidence)) {
22929
+ for (const [k, v] of Object.entries(kept.confidence)) {
22930
+ if (typeof v === "number") confidence[k] = v;
22931
+ }
22932
+ }
22933
+ for (const p of proposed) {
22934
+ if (!names.includes(p.name)) names.push(p.name);
22935
+ confidence[p.name] = p.confidence;
22936
+ }
22937
+ const entry = {
22938
+ ...kept,
22939
+ "generated-by": model,
22940
+ [key]: names,
22941
+ confidence
22942
+ };
22943
+ if (at2 === -1) list2.push(entry);
22944
+ else list2[at2] = entry;
22945
+ return { list: list2, entry, names };
22946
+ }
22947
+ function metaProvenanceEntries(value) {
22948
+ if (!Array.isArray(value)) return [];
22949
+ const strings = (list2) => Array.isArray(list2) ? list2.filter((s) => typeof s === "string") : [];
22950
+ const out = [];
22951
+ for (const item of value) {
22952
+ if (!isPlainRecord(item)) continue;
22953
+ const machine = item["generated-by"];
22954
+ if (typeof machine !== "string" || machine === "") continue;
22955
+ out.push({ "generated-by": machine, fields: strings(item.fields), evals: strings(item.evals) });
22956
+ }
22957
+ return out;
22958
+ }
22959
+
22256
22960
  // src/meta/commands/fill.ts
22257
22961
  var META_PROVENANCE_KEY = "meta-provenance";
22258
22962
  var PROVENANCE_KEYS = /* @__PURE__ */ new Set(["provenance", META_PROVENANCE_KEY]);
22259
22963
  var DEFAULT_THRESHOLD = 0.7;
22260
22964
  var DEFAULT_CONCURRENCY = 4;
22261
- var DEFAULT_PROVIDER = "auto";
22262
22965
  var CACHE_DIR = ".manni/meta/cache";
22263
22966
  function looksLikeOverflow(message) {
22264
22967
  return /context|too long|too large|token limit|maximum.*tokens|exceeds/i.test(
@@ -22310,7 +23013,7 @@ async function runFill(opts) {
22310
23013
  }
22311
23014
  if (usingStdin && !forcedExtractor) {
22312
23015
  throw new DocmetaError(
22313
- "Reading from stdin (`-`) requires --as <format> to choose an extractor."
23016
+ STDIN_REQUIRES_AS
22314
23017
  );
22315
23018
  }
22316
23019
  const threshold = requireNumber(
@@ -22339,13 +23042,19 @@ async function runFill(opts) {
22339
23042
  const dryRun = Boolean(opts.dryRun);
22340
23043
  const only = opts.fields != null ? new Set(opts.fields) : void 0;
22341
23044
  const managed = new Set(managedFields(config?.derive));
22342
- const requestedProvider = opts.provider ?? config?.fill?.provider ?? DEFAULT_PROVIDER;
22343
- const providerName = opts.local === true && requestedProvider === "auto" ? "llama-cpp" : requestedProvider;
22344
- const model = opts.model ?? config?.fill?.model;
22345
- const spec = { provider: providerName, model: model ?? null };
22346
- assertKnownProvider(providerName);
22347
- assertModelHasProvider(providerName, model);
22348
- if (opts.local === true) assertLocalProvider(providerName);
23045
+ const flags = { provider: opts.provider, model: opts.model, local: opts.local };
23046
+ assertLocalFlag(flags, toDocmetaError);
23047
+ const family = configFile?.providers ?? {};
23048
+ const selection = selectProvider(flags, [
23049
+ { provider: config?.fill?.provider, model: config?.fill?.model, origin: "fill.provider" },
23050
+ { provider: family.provider, model: family.model, origin: "providers.provider" }
23051
+ ]);
23052
+ const { provider: providerName, model } = selection;
23053
+ assertKnownProvider2(providerName);
23054
+ assertModelHasProvider2(providerName, model);
23055
+ const replaced = localNotice(selection);
23056
+ if (replaced !== void 0) opts.onNotice?.(replaced);
23057
+ const spec = providerSpecFor(family, { provider: providerName, model: model ?? null });
22349
23058
  const fileInputs = inputs.filter((i) => i !== STDIN_TOKEN);
22350
23059
  const allowEmpty = opts.allowEmpty ?? config?.allowEmpty;
22351
23060
  const fillExts = opts.exts ?? forcedExtractor?.extensions;
@@ -22381,14 +23090,9 @@ async function runFill(opts) {
22381
23090
  identity = { provider: injected.provider(), model: injected.modelName() };
22382
23091
  construct = () => injected;
22383
23092
  } else {
22384
- const resolved = await resolveIdentity(spec);
22385
- if (opts.local === true) assertLocalProvider(resolved.provider);
23093
+ const resolved = await resolveIdentity2(spec);
22386
23094
  identity = resolved;
22387
- const concrete = {
22388
- ...spec,
22389
- provider: resolved.provider,
22390
- model: resolved.model
22391
- };
23095
+ const concrete = providerSpecFor(family, resolved);
22392
23096
  construct = () => makeProvider(concrete);
22393
23097
  }
22394
23098
  let provider;
@@ -22398,7 +23102,7 @@ async function runFill(opts) {
22398
23102
  provider = construct();
22399
23103
  } catch (err) {
22400
23104
  throw new DocmetaError(
22401
- err instanceof InferenceError ? err.message : `Could not construct the "${providerName}" provider: ${errorMessage(err)}`
23105
+ err instanceof InferenceError2 ? err.message : `Could not construct the "${providerName}" provider: ${errorMessage(err)}`
22402
23106
  );
22403
23107
  }
22404
23108
  }
@@ -22432,7 +23136,7 @@ async function runFill(opts) {
22432
23136
  base,
22433
23137
  offline: opts.offline ?? config?.offline ?? false,
22434
23138
  // A `{page}` manifest (0058) is read for exactly the pages this run fills.
22435
- pages: files.map((file) => resolve23(base, file))
23139
+ pages: files.map((file) => resolve24(base, file))
22436
23140
  });
22437
23141
  const mergeMembersFor = (label) => memberOf(runConfig.declaredCollections, configDir ?? cwd, base, label);
22438
23142
  let externalMetadata = await loadManifests();
@@ -22465,17 +23169,17 @@ async function runFill(opts) {
22465
23169
  const keys = new Set(candidates.map((c) => c.key));
22466
23170
  const wants = (fields) => fields.some(([key]) => key !== void 0 && keys.has(key));
22467
23171
  const termIndex = wants(TERM_FIELDS) ? await (termbase ??= readTermbase()) : void 0;
22468
- const pages2 = wants(PAGE_FIELDS) ? await pageIds(resolve23(base, label), mergeMembersFor(label)) : [];
23172
+ const pages2 = wants(PAGE_FIELDS) ? await pageIds(resolve24(base, label), mergeMembersFor(label)) : [];
22469
23173
  const hints = fieldHints(candidates, {
22470
23174
  terms: termIndex === void 0 ? void 0 : offered(termIndex.labels),
22471
23175
  pages: offered(pages2)
22472
23176
  });
22473
23177
  return { hints, termIndex };
22474
23178
  };
22475
- const noticed = /* @__PURE__ */ new Set();
22476
- const noticeOnce = opts.onNotice === void 0 ? void 0 : (message) => {
22477
- if (noticed.has(message)) return;
22478
- noticed.add(message);
23179
+ const noticed2 = /* @__PURE__ */ new Set();
23180
+ const noticeOnce2 = opts.onNotice === void 0 ? void 0 : (message) => {
23181
+ if (noticed2.has(message)) return;
23182
+ noticed2.add(message);
22479
23183
  opts.onNotice?.(message);
22480
23184
  };
22481
23185
  const ctx = relocationContext(runConfig, {
@@ -22487,7 +23191,7 @@ async function runFill(opts) {
22487
23191
  ...opts.as !== void 0 ? { as: opts.as } : {},
22488
23192
  ...opts.respectGitignore !== void 0 ? { respectGitignore: opts.respectGitignore } : {},
22489
23193
  ...opts.env !== void 0 ? { env: opts.env } : {},
22490
- ...noticeOnce !== void 0 ? { onNotice: noticeOnce } : {}
23194
+ ...noticeOnce2 !== void 0 ? { onNotice: noticeOnce2 } : {}
22491
23195
  });
22492
23196
  const heldManifests = /* @__PURE__ */ new Map();
22493
23197
  const holding = /* @__PURE__ */ new Map();
@@ -22505,7 +23209,7 @@ async function runFill(opts) {
22505
23209
  };
22506
23210
  const restorePage = async (label, before) => {
22507
23211
  try {
22508
- await writeFileAtomic(resolve23(base, label), before);
23212
+ await writeFileAtomic(resolve24(base, label), before);
22509
23213
  return `${label} was restored.`;
22510
23214
  } catch (err) {
22511
23215
  return `${label} could not be restored: ${errorMessage(err)}. Restore it from version control.`;
@@ -22599,7 +23303,7 @@ async function runFill(opts) {
22599
23303
  // gets written — if anything, a stronger reason to guard it.
22600
23304
  fileBase: cwd,
22601
23305
  trustRoot,
22602
- onNotice: noticeOnce
23306
+ onNotice: noticeOnce2
22603
23307
  });
22604
23308
  } catch (err) {
22605
23309
  return errorResult(label, extractor.name, errorMessage(err));
@@ -22655,7 +23359,7 @@ async function runFill(opts) {
22655
23359
  }));
22656
23360
  const candidates = proposed.filter((c) => !managedHere.has(c.key));
22657
23361
  const destinations = /* @__PURE__ */ new Map();
22658
- const flags = [];
23362
+ const flags2 = [];
22659
23363
  let metaHome;
22660
23364
  if (label !== "<stdin>" && candidates.length > 0) {
22661
23365
  const located = { ...view.data };
@@ -22685,7 +23389,7 @@ async function runFill(opts) {
22685
23389
  if (!meta) destinations.set(key, { ...home, entry: home.entry });
22686
23390
  continue;
22687
23391
  }
22688
- if (prefs.get(key)?.location === "external") flags.push({ label, key, home: home.home });
23392
+ if (prefs.get(key)?.location === "external") flags2.push({ label, key, home: home.home });
22689
23393
  }
22690
23394
  }
22691
23395
  const inPlace = view.plain.flatMap((pointer) => {
@@ -22739,7 +23443,7 @@ async function runFill(opts) {
22739
23443
  sealedCandidates,
22740
23444
  destinations,
22741
23445
  metaHome,
22742
- flags
23446
+ flags: flags2
22743
23447
  };
22744
23448
  };
22745
23449
  const finishOne = async (ready) => {
@@ -23039,7 +23743,12 @@ async function runFill(opts) {
23039
23743
  if (writable.length > 0) {
23040
23744
  const metaManifest = metaHome?.kind === "manifest" ? metaHome : void 0;
23041
23745
  const metaEntry = metaManifest?.entry;
23042
- const mergedProvenance = metaHome?.kind === "url" || metaManifest !== void 0 && metaEntry === void 0 ? void 0 : mergeMetaProvenance(extracted.data[META_PROVENANCE_KEY], identity.model, writable);
23746
+ const mergedProvenance = metaHome?.kind === "url" || metaManifest !== void 0 && metaEntry === void 0 ? void 0 : mergeMetaProvenance(
23747
+ extracted.data[META_PROVENANCE_KEY],
23748
+ identity.model,
23749
+ "fields",
23750
+ writable.map((f) => ({ name: f.field, confidence: f.confidence }))
23751
+ );
23043
23752
  if (metaHome?.kind === "url") {
23044
23753
  metaProvenance = { written: false, skipReason: "manifest-owned", manifest: metaHome.file };
23045
23754
  } else if (metaManifest !== void 0 && metaEntry === void 0) {
@@ -23054,12 +23763,12 @@ async function runFill(opts) {
23054
23763
  metaWrite = { value: mergedProvenance.list, home: { ...metaManifest, entry: metaEntry } };
23055
23764
  metaProvenance = {
23056
23765
  written: true,
23057
- entry: mergedProvenance.entry,
23766
+ entry: { ...mergedProvenance.entry, fields: mergedProvenance.names },
23058
23767
  destination: metaManifest.file
23059
23768
  };
23060
23769
  } else {
23061
23770
  patch[META_PROVENANCE_KEY] = mergedProvenance.list;
23062
- metaProvenance = { written: true, entry: mergedProvenance.entry };
23771
+ metaProvenance = { written: true, entry: { ...mergedProvenance.entry, fields: mergedProvenance.names } };
23063
23772
  }
23064
23773
  }
23065
23774
  const entryWrites = [];
@@ -23128,7 +23837,7 @@ async function runFill(opts) {
23128
23837
  const changed = next !== content || manifestChanged;
23129
23838
  const pageWritten = next !== content && !dryRun && label !== "<stdin>";
23130
23839
  if (pageWritten) {
23131
- await writeFileAtomic(resolve23(base, label), next);
23840
+ await writeFileAtomic(resolve24(base, label), next);
23132
23841
  }
23133
23842
  if (manifestChanged) {
23134
23843
  const perManifest = /* @__PURE__ */ new Map();
@@ -23160,7 +23869,7 @@ async function runFill(opts) {
23160
23869
  const prepareAll = async () => ({
23161
23870
  stdin: usingStdin ? await prepareOne("<stdin>", opts.stdinContent ?? "", forcedExtractor?.extensions[0] ?? "") : void 0,
23162
23871
  files: await mapConcurrent(files, concurrency, async (file) => {
23163
- const content = await readFile16(resolve23(base, file), "utf8");
23872
+ const content = await readFile15(resolve24(base, file), "utf8");
23164
23873
  return prepareOne(file, content, extname9(file));
23165
23874
  })
23166
23875
  });
@@ -23213,31 +23922,6 @@ function topKey(instancePath) {
23213
23922
  return seg.replace(/~1/g, "/").replace(/~0/g, "~");
23214
23923
  }
23215
23924
  var keyOf = (f) => topKey(f.field);
23216
- function isPlainRecord(value) {
23217
- return typeof value === "object" && value !== null && !Array.isArray(value);
23218
- }
23219
- function mergeMetaProvenance(held, model, written) {
23220
- if (held !== void 0 && !Array.isArray(held)) return void 0;
23221
- const list2 = held === void 0 ? [] : [...held];
23222
- const at2 = list2.findIndex((e) => isPlainRecord(e) && e["generated-by"] === model);
23223
- const prior = list2[at2];
23224
- const kept = isPlainRecord(prior) ? prior : {};
23225
- const fields = Array.isArray(kept.fields) ? kept.fields.filter((p) => typeof p === "string") : [];
23226
- const confidence = {};
23227
- if (isPlainRecord(kept.confidence)) {
23228
- for (const [k, v] of Object.entries(kept.confidence)) {
23229
- if (typeof v === "number") confidence[k] = v;
23230
- }
23231
- }
23232
- for (const f of written) {
23233
- if (!fields.includes(f.field)) fields.push(f.field);
23234
- confidence[f.field] = f.confidence;
23235
- }
23236
- const entry = { ...kept, "generated-by": model, fields, confidence };
23237
- if (at2 === -1) list2.push(entry);
23238
- else list2[at2] = entry;
23239
- return { list: list2, entry };
23240
- }
23241
23925
  async function addsFindings(validate, without, list2) {
23242
23926
  const identity = (e) => JSON.stringify([e.schema, e.instancePath, e.keyword, e.subject ?? null, e.message]);
23243
23927
  const before = /* @__PURE__ */ new Map();
@@ -23550,34 +24234,15 @@ function requireNumber(value, name2, bounds) {
23550
24234
  }
23551
24235
  return value;
23552
24236
  }
23553
- var PROVIDERS = /* @__PURE__ */ new Set([...Object.keys(DEFAULT_MODELS), "auto"]);
23554
- function assertLocalProvider(name2) {
23555
- if (name2 === "llama-cpp" || name2 === "mock") return;
23556
- if (name2 === "auto") return;
23557
- throw new DocmetaError(
23558
- name2 === "claude-cli" ? `--local cannot use "claude-cli": the CLI runs on this machine but its inference does not. Use --provider llama-cpp.` : `--local cannot use "${name2}", which sends document content to a hosted API. Use --provider llama-cpp, or drop --local.`
23559
- );
23560
- }
23561
- function assertKnownProvider(name2) {
23562
- if (PROVIDERS.has(name2)) return;
23563
- throw new DocmetaError(
23564
- `Unknown provider "${name2}". Available: ${[...PROVIDERS].join(", ")}.`
23565
- );
24237
+ var toDocmetaError = (message) => new DocmetaError(message);
24238
+ function assertKnownProvider2(name2) {
24239
+ assertKnownProvider(name2, toDocmetaError);
23566
24240
  }
23567
- function assertModelHasProvider(name2, model) {
23568
- if (name2 !== "auto" || model == null) return;
23569
- throw new DocmetaError(
23570
- `Model "${model}" was given without a provider: a model name does not say which provider owns it. Set --provider or fill.provider to one of ${Object.keys(DEFAULT_MODELS).join(", ")}, or drop the model to take the detected provider's default.`
23571
- );
24241
+ function assertModelHasProvider2(name2, model) {
24242
+ assertModelHasProvider(name2, model, "fill.provider", toDocmetaError);
23572
24243
  }
23573
- async function resolveIdentity(spec) {
23574
- try {
23575
- return await resolveProviderIdentityAsync(spec);
23576
- } catch (err) {
23577
- throw new DocmetaError(
23578
- err instanceof InferenceError ? err.message : `Could not resolve provider "${spec.provider}": ${errorMessage(err)}`
23579
- );
23580
- }
24244
+ function resolveIdentity2(spec) {
24245
+ return resolveIdentity(spec, toDocmetaError);
23581
24246
  }
23582
24247
  function summarize(results, costUsd, cached) {
23583
24248
  let written = 0;
@@ -23622,8 +24287,8 @@ async function mapConcurrent(items2, limit, fn) {
23622
24287
  }
23623
24288
 
23624
24289
  // src/meta/commands/derive.ts
23625
- import { readFile as readFile17 } from "node:fs/promises";
23626
- import { extname as extname10, relative as relative16, resolve as resolve24, sep as sep12 } from "node:path";
24290
+ import { readFile as readFile16 } from "node:fs/promises";
24291
+ import { extname as extname10, relative as relative17, resolve as resolve25, sep as sep12 } from "node:path";
23627
24292
  var GENERATED_BY_ENV = "MANNI_GENERATED_BY";
23628
24293
  var SOURCE_HINT = "narrow --sources or --fields";
23629
24294
  async function runDerive(opts) {
@@ -23741,7 +24406,7 @@ async function runDerive(opts) {
23741
24406
  const files = fromCollections ? retainMembers(walked, membersFor) : walked;
23742
24407
  const rangesByPage = /* @__PURE__ */ new Map();
23743
24408
  for (const t of targets) {
23744
- const label = relative16(base, resolve24(base, t.page)).split(sep12).join("/");
24409
+ const label = relative17(base, resolve25(base, t.page)).split(sep12).join("/");
23745
24410
  if (!files.includes(label)) {
23746
24411
  throw new DocmetaError(
23747
24412
  `${t.target} does not name one file that derive reads; a range names lines of one file.`
@@ -23766,7 +24431,7 @@ async function runDerive(opts) {
23766
24431
  const loaded2 = /* @__PURE__ */ new Map();
23767
24432
  const errors2 = /* @__PURE__ */ new Map();
23768
24433
  for (const label of files) {
23769
- const absPath = resolve24(base, label);
24434
+ const absPath = resolve25(base, label);
23770
24435
  const extractor = forcedExtractor ?? extractorForExtension(extname10(label));
23771
24436
  if (!extractor) {
23772
24437
  errors2.set(label, {
@@ -23775,7 +24440,7 @@ async function runDerive(opts) {
23775
24440
  });
23776
24441
  continue;
23777
24442
  }
23778
- const content = await readFile17(absPath, "utf8");
24443
+ const content = await readFile16(absPath, "utf8");
23779
24444
  const elements = resolveElements(label, config, membersFor(label));
23780
24445
  try {
23781
24446
  loaded2.set(label, {
@@ -23818,7 +24483,7 @@ async function runDerive(opts) {
23818
24483
  runFields = union2;
23819
24484
  }
23820
24485
  const provenanceRoot = configDir ?? cwd;
23821
- const pages2 = files.map((label) => resolve24(base, label));
24486
+ const pages2 = files.map((label) => resolve25(base, label));
23822
24487
  const manifests = runFields.includes(PROVENANCE_FIELD) ? provenanceManifests(declaredCollections, provenanceRoot, base) : [];
23823
24488
  const provenanceCollections = declaredCollections.map((c) => ({
23824
24489
  ...c,
@@ -23910,10 +24575,10 @@ async function runDerive(opts) {
23910
24575
  const members = memberOf(prepared.provenanceCollections, root, base, doc.label);
23911
24576
  return (await mergeWithMarks(doc.label, doc.extracted, prepared.provenanceIndex, members, base, { marks })).extracted.data[PROVENANCE_FIELD];
23912
24577
  };
23913
- const noticed = /* @__PURE__ */ new Set();
23914
- const noticeOnce = opts.onNotice === void 0 ? void 0 : (message) => {
23915
- if (noticed.has(message)) return;
23916
- noticed.add(message);
24578
+ const noticed2 = /* @__PURE__ */ new Set();
24579
+ const noticeOnce2 = opts.onNotice === void 0 ? void 0 : (message) => {
24580
+ if (noticed2.has(message)) return;
24581
+ noticed2.add(message);
23917
24582
  opts.onNotice?.(message);
23918
24583
  };
23919
24584
  const markFields = async (doc, record) => {
@@ -23944,7 +24609,7 @@ async function runDerive(opts) {
23944
24609
  // `$schema`, since that schema decides what gets encrypted.
23945
24610
  fileBase: cwd,
23946
24611
  trustRoot,
23947
- onNotice: noticeOnce
24612
+ onNotice: noticeOnce2
23948
24613
  });
23949
24614
  } catch (err) {
23950
24615
  return { error: errorMessage(err) };
@@ -24005,7 +24670,7 @@ async function runDerive(opts) {
24005
24670
  validator,
24006
24671
  ...opts.noConfig === true ? { noConfig: true } : {},
24007
24672
  ...opts.env !== void 0 ? { env: opts.env } : {},
24008
- ...noticeOnce !== void 0 ? { onNotice: noticeOnce } : {}
24673
+ ...noticeOnce2 !== void 0 ? { onNotice: noticeOnce2 } : {}
24009
24674
  });
24010
24675
  const externalWrites = async () => {
24011
24676
  const out = [];
@@ -24988,15 +25653,28 @@ export {
24988
25653
  generateEncryptionKey,
24989
25654
  encryptValue,
24990
25655
  decryptValue,
25656
+ ToolError,
25657
+ errorMessage,
25658
+ assertKnownProvider,
25659
+ assertModelHasProvider,
25660
+ PROVIDERS_KEY,
25661
+ parseProviders,
25662
+ LOCAL_FLAG_HELP,
25663
+ assertLocalFlag,
25664
+ selectProvider,
25665
+ localNotice,
25666
+ providerSpecFor,
25667
+ resolveIdentity,
24991
25668
  findGitRoot,
24992
25669
  valeConfigPath,
24993
25670
  ditaOtHome,
24994
25671
  programName,
24995
25672
  warn,
24996
25673
  notice,
24997
- ToolError,
24998
- errorMessage,
25674
+ noticeOnce,
24999
25675
  FAMILY_CONFIG_NAMES,
25676
+ findConfigFileSync,
25677
+ readConfigFileSync,
25000
25678
  findConfigFile,
25001
25679
  readConfigFile,
25002
25680
  findFamilyConfigFile,
@@ -25005,6 +25683,13 @@ export {
25005
25683
  runIfMain,
25006
25684
  escapeWorkflowCommandMessage,
25007
25685
  escapeWorkflowCommandProperty,
25686
+ collect,
25687
+ splitList,
25688
+ STDIN_REQUIRES_AS,
25689
+ readStdin,
25690
+ configOption,
25691
+ explicitFalse,
25692
+ reportConfig,
25008
25693
  INTEGRITY_SHAPE,
25009
25694
  integrityOf,
25010
25695
  isIntegrity,
@@ -25058,7 +25743,9 @@ export {
25058
25743
  DEFAULT_TTL_HOURS,
25059
25744
  schemaCacheDir,
25060
25745
  SchemaCache,
25061
- __default34 as __default,
25746
+ __default31 as __default,
25747
+ __default33 as __default2,
25748
+ __default34 as __default3,
25062
25749
  listBuiltins,
25063
25750
  classifyRef,
25064
25751
  fetchSchemaBytes,
@@ -25082,7 +25769,10 @@ export {
25082
25769
  staleFindings,
25083
25770
  LEGACY_CONFIG_NAMES,
25084
25771
  parseConfig,
25772
+ urlManifestMessage,
25773
+ schemaTrustRoot,
25085
25774
  loadConfig,
25775
+ loadMetaSection,
25086
25776
  resolveRunConfig,
25087
25777
  renderSarif,
25088
25778
  renderJunit,
@@ -25108,6 +25798,7 @@ export {
25108
25798
  EXTERNAL_METADATA_FETCH_MAX_BYTES,
25109
25799
  fetchExternalMetadata,
25110
25800
  META_CONTEXT,
25801
+ lazyKey,
25111
25802
  withValueAt,
25112
25803
  EXTERNAL_OWNED_SCHEMA,
25113
25804
  EXTERNAL_DUPLICATE_SCHEMA,
@@ -25124,19 +25815,28 @@ export {
25124
25815
  spliceManifestValue,
25125
25816
  readManifestValue,
25126
25817
  removeManifestKey,
25818
+ isParamName,
25819
+ memberOf,
25127
25820
  LOCATION_KEYWORD,
25128
25821
  LOCATION_EXTERNAL_SCHEMA,
25129
25822
  LOCATION_PAGE_SCHEMA,
25823
+ GRAPH_OUTPUT_KEYWORD,
25130
25824
  Validator,
25825
+ pageMarks,
25826
+ pageSchemaSet,
25827
+ marksValidator,
25131
25828
  configMarks,
25132
- isParamName,
25133
- memberOf,
25134
25829
  relocateFailed,
25135
25830
  relocationContext,
25136
25831
  keyHome,
25137
25832
  planRelocation,
25138
25833
  applyRelocation,
25139
25834
  offerPrompt,
25835
+ offerExternalHomes,
25836
+ externalWriteWarnings,
25837
+ mergeMetaProvenance,
25838
+ metaProvenanceEntries,
25839
+ provenanceEntries,
25140
25840
  REVIEW_CACHE_DIR,
25141
25841
  REVIEW_CACHE_VERSION,
25142
25842
  ReviewCache,
@@ -25195,4 +25895,4 @@ export {
25195
25895
  renderQuery,
25196
25896
  renderQueryCsv
25197
25897
  };
25198
- //# sourceMappingURL=chunk-TQG3WCJY.js.map
25898
+ //# sourceMappingURL=chunk-TH565O2N.js.map