@hawkeyexl/manni 4.0.2 → 4.1.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.2",
11
+ version: "4.1.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: {
@@ -63,6 +63,7 @@ var package_default = {
63
63
  "docs:check-action": "node scripts/check-action-reference.mjs",
64
64
  "docs:check-api": "node scripts/check-api-reference.mjs",
65
65
  "docs:check-links": "node scripts/check-doc-links.mjs",
66
+ "docs:check-docevals": "node dist/cli.js docevals run --deterministic-only --no-generate",
66
67
  "docs:check-tables": "node scripts/check-table-rows.mjs",
67
68
  "docs:check-versions": "node scripts/check-versions.mjs",
68
69
  "docs:sync-versions": "node scripts/sync-versions.mjs",
@@ -92,7 +93,7 @@ var package_default = {
92
93
  "@apidevtools/json-schema-ref-parser": "^11.9.3",
93
94
  "@asciidoctor/core": "^3.0.4",
94
95
  "@axe-core/playwright": "^4.13.0",
95
- "@hawkeyexl/inference": "^0.3.2",
96
+ "@hawkeyexl/inference": "^0.4.0",
96
97
  "@xmldom/xmldom": "^0.9.12",
97
98
  ajv: "^8.17.1",
98
99
  "ajv-draft-04": "^1.0.0",
@@ -137,6 +138,14 @@ var package_default = {
137
138
  typescript: "^6.0.3",
138
139
  "typescript-eslint": "^8.68.0",
139
140
  vitest: "^4.1.11"
141
+ },
142
+ peerDependencies: {
143
+ "node-llama-cpp": "^3.19.0"
144
+ },
145
+ peerDependenciesMeta: {
146
+ "node-llama-cpp": {
147
+ optional: true
148
+ }
140
149
  }
141
150
  };
142
151
 
@@ -3232,8 +3241,8 @@ var RENAME_ATTEMPTS = 3;
3232
3241
  function errorCode(err) {
3233
3242
  return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
3234
3243
  }
3235
- var wait = (ms) => new Promise((resolve25) => {
3236
- setTimeout(resolve25, ms);
3244
+ var wait = (ms) => new Promise((resolve26) => {
3245
+ setTimeout(resolve26, ms);
3237
3246
  });
3238
3247
  async function writeFileAtomic(path, contents, options = {}) {
3239
3248
  const tmp = join(
@@ -8578,8 +8587,8 @@ var SCHEMA_KEYS = [
8578
8587
  "multipleOf"
8579
8588
  ];
8580
8589
  var RETRY_DELAY_MS = 500;
8581
- var wait2 = (ms) => new Promise((resolve25) => {
8582
- setTimeout(resolve25, ms);
8590
+ var wait2 = (ms) => new Promise((resolve26) => {
8591
+ setTimeout(resolve26, ms);
8583
8592
  });
8584
8593
  var RetryableFetchError = class extends DocmetaError {
8585
8594
  };
@@ -9146,12 +9155,12 @@ function isPlainObject(v) {
9146
9155
  }
9147
9156
 
9148
9157
  // src/meta/core/config.ts
9149
- import { resolve as resolve8 } from "node:path";
9158
+ import { resolve as resolve9 } from "node:path";
9150
9159
  import { parse as parseYaml2 } from "yaml";
9151
9160
 
9152
9161
  // 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";
9162
+ import { readFileSync as readFileSync2 } from "node:fs";
9163
+ import { basename as basename2, dirname as dirname3, join as join4, relative as relative3, resolve as resolve7 } from "node:path";
9155
9164
  import { parse as parseYaml } from "yaml";
9156
9165
 
9157
9166
  // src/shared/encryption.ts
@@ -9280,11 +9289,218 @@ function keyedPin(text2, key) {
9280
9289
  return `${KEYED_PIN_PREFIX}${mac}`;
9281
9290
  }
9282
9291
 
9292
+ // src/shared/providers.ts
9293
+ import { resolve as resolve4 } from "node:path";
9294
+ import {
9295
+ DEFAULT_MODELS,
9296
+ DEFAULT_OPENAI_BASE_URL,
9297
+ InferenceError,
9298
+ resolveProviderIdentityAsync
9299
+ } from "@hawkeyexl/inference";
9300
+ var DEFAULT_PROVIDER = "auto";
9301
+ var PROVIDERS = /* @__PURE__ */ new Set([
9302
+ ...Object.keys(DEFAULT_MODELS),
9303
+ DEFAULT_PROVIDER
9304
+ ]);
9305
+ var NAMEABLE_PROVIDERS = Object.keys(DEFAULT_MODELS).filter(
9306
+ (name2) => name2 !== "mock"
9307
+ );
9308
+ function assertKnownProvider(name2, toError) {
9309
+ if (PROVIDERS.has(name2)) return;
9310
+ throw toError(
9311
+ `Unknown provider "${name2}". Available: ${[...NAMEABLE_PROVIDERS, DEFAULT_PROVIDER].join(", ")}.`
9312
+ );
9313
+ }
9314
+ function assertModelHasProvider(name2, model, configKey, toError) {
9315
+ if (name2 !== DEFAULT_PROVIDER || model == null) return;
9316
+ throw toError(
9317
+ `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.`
9318
+ );
9319
+ }
9320
+ var PROVIDERS_KEY = "providers";
9321
+ var CONNECTION_KEYS = {
9322
+ anthropic: ["apiKeyEnv"],
9323
+ openai: ["baseUrl", "apiKeyEnv"],
9324
+ "claude-cli": ["command"],
9325
+ "llama-cpp": ["modelsDir", "thoughtTokens"]
9326
+ };
9327
+ var CONNECTION_NAMES = Object.keys(CONNECTION_KEYS);
9328
+ var TOP_LEVEL_KEYS = ["provider", "model", ...CONNECTION_NAMES];
9329
+ function isMapping2(value) {
9330
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9331
+ }
9332
+ function rejectUnknown(raw, allowed, label, source, toError) {
9333
+ for (const key of Object.keys(raw)) {
9334
+ if (!allowed.includes(key)) {
9335
+ throw toError(
9336
+ `${source}: "${label}" has unknown key "${key}". Supported keys: ${allowed.join(", ")}.`
9337
+ );
9338
+ }
9339
+ }
9340
+ }
9341
+ function parseProviders(value, source, dir, toError) {
9342
+ if (value == null) return {};
9343
+ if (!isMapping2(value)) {
9344
+ throw toError(`${source}: "${PROVIDERS_KEY}" must be a mapping.`);
9345
+ }
9346
+ rejectUnknown(value, TOP_LEVEL_KEYS, PROVIDERS_KEY, source, toError);
9347
+ const string = (raw, key, label) => {
9348
+ const v = raw[key];
9349
+ if (v === void 0) return void 0;
9350
+ if (typeof v !== "string") {
9351
+ throw toError(`${source}: "${label}" must be a string.`);
9352
+ }
9353
+ return v;
9354
+ };
9355
+ const parsed2 = {};
9356
+ const provider = string(value, "provider", `${PROVIDERS_KEY}.provider`);
9357
+ if (provider !== void 0) {
9358
+ assertKnownProvider(provider, (message) => toError(`${source}: ${message}`));
9359
+ parsed2.provider = provider;
9360
+ }
9361
+ const model = string(value, "model", `${PROVIDERS_KEY}.model`);
9362
+ if (model !== void 0) {
9363
+ if (provider === void 0 || provider === DEFAULT_PROVIDER) {
9364
+ throw toError(
9365
+ `${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.`
9366
+ );
9367
+ }
9368
+ parsed2.model = model;
9369
+ }
9370
+ for (const name2 of CONNECTION_NAMES) {
9371
+ const section = value[name2];
9372
+ if (section == null) continue;
9373
+ const label = `${PROVIDERS_KEY}.${name2}`;
9374
+ if (!isMapping2(section)) {
9375
+ throw toError(`${source}: "${label}" must be a mapping.`);
9376
+ }
9377
+ rejectUnknown(section, CONNECTION_KEYS[name2], label, source, toError);
9378
+ const field = (key) => string(section, key, `${label}.${key}`);
9379
+ switch (name2) {
9380
+ case "anthropic": {
9381
+ const apiKeyEnv = field("apiKeyEnv");
9382
+ parsed2.anthropic = apiKeyEnv === void 0 ? {} : { apiKeyEnv };
9383
+ break;
9384
+ }
9385
+ case "openai": {
9386
+ const baseUrl = field("baseUrl");
9387
+ const apiKeyEnv = field("apiKeyEnv");
9388
+ parsed2.openai = {
9389
+ ...baseUrl !== void 0 ? { baseUrl } : {},
9390
+ ...apiKeyEnv !== void 0 ? { apiKeyEnv } : {}
9391
+ };
9392
+ break;
9393
+ }
9394
+ case "claude-cli": {
9395
+ const command = field("command");
9396
+ parsed2["claude-cli"] = command === void 0 ? {} : { command };
9397
+ break;
9398
+ }
9399
+ case "llama-cpp": {
9400
+ const modelsDir = field("modelsDir");
9401
+ const thoughtTokens = section["thoughtTokens"];
9402
+ if (thoughtTokens !== void 0 && (typeof thoughtTokens !== "number" || !Number.isInteger(thoughtTokens) || thoughtTokens < 0)) {
9403
+ throw toError(
9404
+ `${source}: "${label}.thoughtTokens" must be a whole number of 0 or more, got ${JSON.stringify(thoughtTokens)}.`
9405
+ );
9406
+ }
9407
+ parsed2["llama-cpp"] = {
9408
+ ...modelsDir !== void 0 ? { modelsDir: resolve4(dir, modelsDir) } : {},
9409
+ ...thoughtTokens !== void 0 ? { thoughtTokens } : {}
9410
+ };
9411
+ break;
9412
+ }
9413
+ }
9414
+ }
9415
+ return parsed2;
9416
+ }
9417
+ var LOCAL_PROVIDER = "llama-cpp";
9418
+ var LOCAL_FLAG_HELP = "run inference on this machine (llama-cpp); overrides any configured or eval-level provider";
9419
+ var LOCAL_COMPATIBLE = /* @__PURE__ */ new Set([LOCAL_PROVIDER, DEFAULT_PROVIDER, "mock"]);
9420
+ function assertLocalFlag(flag, toError) {
9421
+ if (flag.local !== true || flag.provider === void 0) return;
9422
+ assertKnownProvider(flag.provider, toError);
9423
+ if (LOCAL_COMPATIBLE.has(flag.provider)) return;
9424
+ throw toError(
9425
+ `--local and --provider ${flag.provider} contradict each other: --local runs inference on this machine with ${LOCAL_PROVIDER}. Drop one of them.`
9426
+ );
9427
+ }
9428
+ function selectProvider(flag, levels) {
9429
+ if (flag.local === true) return selectLocal(flag, levels);
9430
+ const provider = flag.provider ?? levels.find((level) => level.provider !== void 0)?.provider ?? DEFAULT_PROVIDER;
9431
+ const model = flag.model ?? levels.find(
9432
+ (level) => level.model !== void 0 && (level.provider === void 0 || level.provider === provider)
9433
+ )?.model;
9434
+ return { provider, model };
9435
+ }
9436
+ function selectLocal(flag, levels) {
9437
+ const provider = flag.provider === "mock" ? "mock" : LOCAL_PROVIDER;
9438
+ const model = flag.model ?? levels.find((level) => level.model !== void 0 && level.provider === provider)?.model;
9439
+ const chosen = flag.provider === void 0 ? levels.find((level) => level.provider !== void 0) : void 0;
9440
+ 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;
9441
+ return replaced === void 0 ? { provider, model } : { provider, model, replaced };
9442
+ }
9443
+ function localNotice(selection) {
9444
+ const { replaced } = selection;
9445
+ if (replaced === void 0) return void 0;
9446
+ return `--local: using ${LOCAL_PROVIDER} instead of "${replaced.provider}" from ${replaced.source}.`;
9447
+ }
9448
+ function providerSpecFor(connections, selection) {
9449
+ const { provider, model } = selection;
9450
+ const local = connections["llama-cpp"] ?? {};
9451
+ const llamaCpp = {
9452
+ thoughtTokens: local.thoughtTokens ?? 0,
9453
+ ...local.modelsDir !== void 0 ? { modelsDirectory: local.modelsDir } : {}
9454
+ };
9455
+ switch (provider) {
9456
+ case "anthropic":
9457
+ return {
9458
+ provider,
9459
+ model,
9460
+ apiKeyEnv: connections.anthropic?.apiKeyEnv ?? "ANTHROPIC_API_KEY"
9461
+ };
9462
+ case "openai":
9463
+ return {
9464
+ provider,
9465
+ model,
9466
+ apiKeyEnv: connections.openai?.apiKeyEnv ?? "OPENAI_API_KEY",
9467
+ baseUrl: connections.openai?.baseUrl ?? DEFAULT_OPENAI_BASE_URL
9468
+ };
9469
+ case "claude-cli":
9470
+ return { provider, model, command: connections["claude-cli"]?.command ?? "claude" };
9471
+ case "llama-cpp":
9472
+ return { provider, model, llamaCpp };
9473
+ case "mock":
9474
+ return { provider, model };
9475
+ case "auto": {
9476
+ const baseUrl = connections.openai?.baseUrl;
9477
+ const command = connections["claude-cli"]?.command;
9478
+ const configuredLocal = local.thoughtTokens !== void 0 || local.modelsDir !== void 0;
9479
+ return {
9480
+ provider,
9481
+ model,
9482
+ ...baseUrl !== void 0 ? { baseUrl } : {},
9483
+ ...command !== void 0 ? { command } : {},
9484
+ ...configuredLocal ? { llamaCpp } : {}
9485
+ };
9486
+ }
9487
+ }
9488
+ }
9489
+ async function resolveIdentity(spec, toError) {
9490
+ try {
9491
+ return await resolveProviderIdentityAsync(spec);
9492
+ } catch (err) {
9493
+ throw toError(
9494
+ err instanceof InferenceError ? err.message : `Could not resolve provider "${String(spec.provider)}": ${errorMessage(err)}`
9495
+ );
9496
+ }
9497
+ }
9498
+
9283
9499
  // src/shared/git-root.ts
9284
9500
  import { existsSync } from "node:fs";
9285
- import { dirname as dirname2, join as join3, resolve as resolve4 } from "node:path";
9501
+ import { dirname as dirname2, join as join3, resolve as resolve5 } from "node:path";
9286
9502
  function findGitRoot(cwd) {
9287
- let dir = resolve4(cwd);
9503
+ let dir = resolve5(cwd);
9288
9504
  for (; ; ) {
9289
9505
  if (existsSync(join3(dir, ".git"))) return dir;
9290
9506
  const parent = dirname2(dir);
@@ -9293,7 +9509,7 @@ function findGitRoot(cwd) {
9293
9509
  }
9294
9510
  }
9295
9511
  function searchPath(cwd) {
9296
- const start = resolve4(cwd);
9512
+ const start = resolve5(cwd);
9297
9513
  const root = findGitRoot(start);
9298
9514
  if (root === null) return [start];
9299
9515
  const chain = [];
@@ -9308,11 +9524,11 @@ function searchPath(cwd) {
9308
9524
  }
9309
9525
 
9310
9526
  // src/shared/tools.ts
9311
- import { isAbsolute as isAbsolute3, resolve as resolve5 } from "node:path";
9527
+ import { isAbsolute as isAbsolute3, resolve as resolve6 } from "node:path";
9312
9528
  var TOOL_KEYS = ["vale", "dita-ot"];
9313
9529
  var VALE_KEYS = ["config"];
9314
9530
  var DITA_OT_KEYS = ["home"];
9315
- function isMapping2(value) {
9531
+ function isMapping3(value) {
9316
9532
  return typeof value === "object" && value !== null && !Array.isArray(value);
9317
9533
  }
9318
9534
  function rejectUnknownKeys2(raw, allowed, where, source, toError) {
@@ -9326,7 +9542,7 @@ function rejectUnknownKeys2(raw, allowed, where, source, toError) {
9326
9542
  }
9327
9543
  function parseTools(raw, source, toError) {
9328
9544
  if (raw === null || raw === void 0) return {};
9329
- if (!isMapping2(raw)) {
9545
+ if (!isMapping3(raw)) {
9330
9546
  throw toError(`${source}: "tools" must be a mapping.`);
9331
9547
  }
9332
9548
  rejectUnknownKeys2(raw, TOOL_KEYS, "tools", source, toError);
@@ -9340,7 +9556,7 @@ function parseTools(raw, source, toError) {
9340
9556
  return tools;
9341
9557
  }
9342
9558
  function parseVale(raw, source, toError) {
9343
- if (!isMapping2(raw)) {
9559
+ if (!isMapping3(raw)) {
9344
9560
  throw toError(`${source}: tools.vale must be a mapping.`);
9345
9561
  }
9346
9562
  rejectUnknownKeys2(raw, VALE_KEYS, "tools.vale", source, toError);
@@ -9355,7 +9571,7 @@ function parseVale(raw, source, toError) {
9355
9571
  return vale;
9356
9572
  }
9357
9573
  function parseDitaOt(raw, source, toError) {
9358
- if (!isMapping2(raw)) {
9574
+ if (!isMapping3(raw)) {
9359
9575
  throw toError(`${source}: tools.dita-ot must be a mapping.`);
9360
9576
  }
9361
9577
  rejectUnknownKeys2(raw, DITA_OT_KEYS, "tools.dita-ot", source, toError);
@@ -9372,12 +9588,12 @@ function parseDitaOt(raw, source, toError) {
9372
9588
  function valeConfigPath(tools, configDir) {
9373
9589
  const config = tools.vale?.config;
9374
9590
  if (config === void 0) return void 0;
9375
- return isAbsolute3(config) ? config : resolve5(configDir, config);
9591
+ return isAbsolute3(config) ? config : resolve6(configDir, config);
9376
9592
  }
9377
9593
  function ditaOtHome(tools, configDir) {
9378
9594
  const home = tools["dita-ot"]?.home;
9379
9595
  if (home === void 0) return void 0;
9380
- return isAbsolute3(home) ? home : resolve5(configDir, home);
9596
+ return isAbsolute3(home) ? home : resolve6(configDir, home);
9381
9597
  }
9382
9598
 
9383
9599
  // src/shared/warn.ts
@@ -9392,22 +9608,24 @@ function notice(message) {
9392
9608
  process.stderr.write(`${programName()}: ${message}
9393
9609
  `);
9394
9610
  }
9611
+ var noticed = /* @__PURE__ */ new Set();
9612
+ function noticeOnce(message) {
9613
+ if (noticed.has(message)) return;
9614
+ noticed.add(message);
9615
+ notice(message);
9616
+ }
9395
9617
 
9396
9618
  // src/shared/config-file.ts
9397
9619
  var FAMILY_CONFIG_NAMES = [
9398
9620
  "manni.config.yaml",
9399
9621
  "manni.config.yml"
9400
9622
  ];
9401
- var MOOSE_CONFIG_NAMES = [
9402
- "moose.config.yaml",
9403
- "moose.config.yml"
9404
- ];
9405
9623
  var ENCRYPTION_KEY_FIELD = "encryptionKey";
9406
9624
  var ENCRYPTION_KEY_PREVIOUS_FIELD = "encryptionKeyPrevious";
9407
- async function readDocument(path, source, toError) {
9625
+ function readDocument(path, source, toError) {
9408
9626
  let text2;
9409
9627
  try {
9410
- text2 = await readFile3(path, "utf8");
9628
+ text2 = readFileSync2(path, "utf8");
9411
9629
  } catch {
9412
9630
  return null;
9413
9631
  }
@@ -9428,7 +9646,12 @@ function parseMapping(text2, source, toError) {
9428
9646
  }
9429
9647
  var COLLECTIONS_KEY = "collections";
9430
9648
  var TOOLS_KEY = "tools";
9431
- var FAMILY_KEYS = [COLLECTIONS_KEY, TOOLS_KEY, ENCRYPTION_KEY_FIELD];
9649
+ var FAMILY_KEYS = [
9650
+ COLLECTIONS_KEY,
9651
+ TOOLS_KEY,
9652
+ ENCRYPTION_KEY_FIELD,
9653
+ PROVIDERS_KEY
9654
+ ];
9432
9655
  function slice(document, opts) {
9433
9656
  const { doc } = document;
9434
9657
  if (doc !== null && Object.hasOwn(doc, opts.section)) {
@@ -9473,6 +9696,11 @@ function encryptionKeyOf(document, source, toError) {
9473
9696
  }
9474
9697
  return keys;
9475
9698
  }
9699
+ function providersOf(document, source, dir, toError) {
9700
+ const { doc } = document;
9701
+ if (doc === null || !Object.hasOwn(doc, PROVIDERS_KEY)) return {};
9702
+ return { providers: parseProviders(doc[PROVIDERS_KEY], source, dir, toError) };
9703
+ }
9476
9704
  function tolerantKeysOf(document) {
9477
9705
  const key = document.doc?.[ENCRYPTION_KEY_FIELD];
9478
9706
  const previous = document.doc?.[ENCRYPTION_KEY_PREVIOUS_FIELD];
@@ -9484,42 +9712,33 @@ function tolerantKeysOf(document) {
9484
9712
  function relativeSource(cwd, path) {
9485
9713
  return relative3(cwd, path).replace(/\\/g, "/");
9486
9714
  }
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);
9715
+ function findConfigFileSync(cwd, opts) {
9716
+ const start = resolve7(cwd);
9496
9717
  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
- }
9718
+ for (const name2 of FAMILY_CONFIG_NAMES) {
9719
+ const path = join4(dir, name2);
9720
+ const source = relativeSource(start, path);
9721
+ const document = readDocument(path, source, opts.toError);
9722
+ if (document === null) continue;
9723
+ const found = slice(document, opts);
9724
+ if (found === null) continue;
9725
+ return {
9726
+ path,
9727
+ dir,
9728
+ source,
9729
+ text: document.text,
9730
+ kind: "manni",
9731
+ collections: collectionsOf(document, source, opts.toError),
9732
+ tools: toolsOf(document, source, opts.toError),
9733
+ ...encryptionKeyOf(document, source, opts.toError),
9734
+ ...providersOf(document, source, dir, opts.toError),
9735
+ ...found
9736
+ };
9518
9737
  }
9519
9738
  for (const name2 of opts.legacyNames) {
9520
9739
  const path = join4(dir, name2);
9521
9740
  const source = relativeSource(start, path);
9522
- const document = await readDocument(path, source, opts.toError);
9741
+ const document = readDocument(path, source, opts.toError);
9523
9742
  if (document === null) continue;
9524
9743
  warn(
9525
9744
  `"${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 +9758,9 @@ async function findConfigFile(cwd, opts) {
9539
9758
  }
9540
9759
  return null;
9541
9760
  }
9542
- async function readConfigFile(explicitPath, cwd, opts) {
9543
- const path = resolve6(cwd, explicitPath);
9544
- const document = await readDocument(path, explicitPath, opts.toError);
9761
+ function readConfigFileSync(explicitPath, cwd, opts) {
9762
+ const path = resolve7(cwd, explicitPath);
9763
+ const document = readDocument(path, explicitPath, opts.toError);
9545
9764
  if (document === null) {
9546
9765
  throw opts.toError(`Config file not found: "${explicitPath}".`);
9547
9766
  }
@@ -9560,44 +9779,58 @@ async function readConfigFile(explicitPath, cwd, opts) {
9560
9779
  // An unwrapped document carries no family key (else it would be wrapped),
9561
9780
  // so this only ever reads a family file's key.
9562
9781
  ...encryptionKeyOf(document, explicitPath, opts.toError),
9782
+ ...providersOf(document, explicitPath, dirname3(path), opts.toError),
9563
9783
  ...found
9564
9784
  };
9565
9785
  }
9566
- async function findFamilyConfigFile(cwd, toError) {
9567
- const start = resolve6(cwd);
9786
+ function findConfigFile(cwd, opts) {
9787
+ return Promise.resolve().then(() => findConfigFileSync(cwd, opts));
9788
+ }
9789
+ function readConfigFile(explicitPath, cwd, opts) {
9790
+ return Promise.resolve().then(
9791
+ () => readConfigFileSync(explicitPath, cwd, opts)
9792
+ );
9793
+ }
9794
+ function findFamilyConfigFile(cwd, toError) {
9795
+ return Promise.resolve().then(() => findFamilyConfigFileSync(cwd, toError));
9796
+ }
9797
+ function findFamilyConfigFileSync(cwd, toError) {
9798
+ const start = resolve7(cwd);
9568
9799
  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
- }
9800
+ for (const name2 of FAMILY_CONFIG_NAMES) {
9801
+ const path = join4(dir, name2);
9802
+ const source = relativeSource(start, path);
9803
+ const document = readDocument(path, source, toError);
9804
+ if (document === null) continue;
9805
+ return {
9806
+ path,
9807
+ dir,
9808
+ source,
9809
+ text: document.text,
9810
+ value: null,
9811
+ wrapped: true,
9812
+ kind: "manni",
9813
+ collections: collectionsOf(document, source, toError),
9814
+ tools: toolsOf(document, source, toError),
9815
+ ...tolerantKeysOf(document)
9816
+ };
9589
9817
  }
9590
9818
  }
9591
9819
  return null;
9592
9820
  }
9593
- async function readFamilyConfigFile(explicitPath, cwd, toError) {
9594
- const path = resolve6(cwd, explicitPath);
9595
- const document = await readDocument(path, explicitPath, toError);
9821
+ function readFamilyConfigFile(explicitPath, cwd, toError) {
9822
+ return Promise.resolve().then(
9823
+ () => readFamilyConfigFileSync(explicitPath, cwd, toError)
9824
+ );
9825
+ }
9826
+ function readFamilyConfigFileSync(explicitPath, cwd, toError) {
9827
+ const path = resolve7(cwd, explicitPath);
9828
+ const document = readDocument(path, explicitPath, toError);
9596
9829
  if (document === null) {
9597
9830
  throw toError(`Config file not found: "${explicitPath}".`);
9598
9831
  }
9599
9832
  const { doc } = document;
9600
- const named = [...FAMILY_CONFIG_NAMES, ...MOOSE_CONFIG_NAMES].includes(basename2(path));
9833
+ const named = FAMILY_CONFIG_NAMES.includes(basename2(path));
9601
9834
  const family = doc === null || named || FAMILY_KEYS.some((key) => Object.hasOwn(doc, key));
9602
9835
  return {
9603
9836
  path,
@@ -9614,8 +9847,8 @@ async function readFamilyConfigFile(explicitPath, cwd, toError) {
9614
9847
  }
9615
9848
 
9616
9849
  // 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";
9850
+ import { readFile as readFile3, readdir, stat as stat4 } from "node:fs/promises";
9851
+ import { join as join5, relative as relative4, resolve as resolve8, sep as sep2 } from "node:path";
9619
9852
  var RESERVED_VENDORS = [
9620
9853
  "manni",
9621
9854
  "check",
@@ -9671,7 +9904,7 @@ function shown(configDir, path) {
9671
9904
  async function loadRegisteredSchemas(register2, configDir) {
9672
9905
  const files = [];
9673
9906
  for (const [i, entry] of register2.entries()) {
9674
- const path = resolve7(configDir, entry);
9907
+ const path = resolve8(configDir, entry);
9675
9908
  let entryStats;
9676
9909
  try {
9677
9910
  entryStats = await stat4(path);
@@ -9701,7 +9934,7 @@ async function loadRegisteredSchemas(register2, configDir) {
9701
9934
  const file = shown(configDir, path);
9702
9935
  let parsed2;
9703
9936
  try {
9704
- parsed2 = JSON.parse(stripBom2(await readFile4(path, "utf8")));
9937
+ parsed2 = JSON.parse(stripBom2(await readFile3(path, "utf8")));
9705
9938
  } catch {
9706
9939
  throw new DocmetaError(`register: ${file} is not valid JSON.`);
9707
9940
  }
@@ -10470,8 +10703,8 @@ function parseFill(value, source) {
10470
10703
  function schemaTrustRoot(cwd, configDir) {
10471
10704
  const git2 = findGitRoot(cwd);
10472
10705
  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" };
10706
+ if (configDir !== void 0) return { dir: resolve9(configDir), source: "config" };
10707
+ return { dir: resolve9(cwd), source: "cwd" };
10475
10708
  }
10476
10709
  async function loadConfig(explicitPath, cwd = process.cwd()) {
10477
10710
  const file = explicitPath ? await readConfigFile(explicitPath, cwd, CONFIG_FILE) : await findConfigFile(cwd, CONFIG_FILE);
@@ -10550,18 +10783,18 @@ import { isAbsolute as isAbsolute4 } from "node:path";
10550
10783
  // src/meta/core/baseline.ts
10551
10784
  import { createHash as createHash3 } from "node:crypto";
10552
10785
  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";
10786
+ import { readFile as readFile4 } from "node:fs/promises";
10787
+ import { relative as relative7, resolve as resolve12 } from "node:path";
10555
10788
 
10556
10789
  // src/meta/core/load-files.ts
10557
10790
  import { stat as stat5 } from "node:fs/promises";
10558
- import { extname, relative as relative6, resolve as resolve10 } from "node:path";
10791
+ import { extname, relative as relative6, resolve as resolve11 } from "node:path";
10559
10792
  import fg2 from "fast-glob";
10560
10793
  import picomatch3 from "picomatch";
10561
10794
 
10562
10795
  // src/meta/core/gitignore.ts
10563
10796
  import { spawn } from "node:child_process";
10564
- import { dirname as dirname4, relative as relative5, resolve as resolve9, sep as sep3 } from "node:path";
10797
+ import { dirname as dirname4, relative as relative5, resolve as resolve10, sep as sep3 } from "node:path";
10565
10798
  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
10799
  var unavailable = () => ({
10567
10800
  ignored: /* @__PURE__ */ new Set(),
@@ -10575,7 +10808,7 @@ async function gitIgnored(paths, cwd) {
10575
10808
  const byRoot = /* @__PURE__ */ new Map();
10576
10809
  let inSomeRepo = false;
10577
10810
  for (const spelled of paths) {
10578
- const abs = resolve9(cwd, spelled);
10811
+ const abs = resolve10(cwd, spelled);
10579
10812
  const dir = dirname4(abs);
10580
10813
  let root = rootOf.get(dir);
10581
10814
  if (root === void 0) {
@@ -10693,7 +10926,7 @@ async function walkDirectory(cwd, abs, ignore, framed) {
10693
10926
  });
10694
10927
  const files = [];
10695
10928
  for (const file of found) {
10696
- const rel = toPosix(relative6(cwd, resolve10(root, file)));
10929
+ const rel = toPosix(relative6(cwd, resolve11(root, file)));
10697
10930
  if (!ignoredByGlobs(rel, framed)) files.push(rel);
10698
10931
  }
10699
10932
  return files;
@@ -10724,7 +10957,7 @@ async function resolveTargetSet(opts) {
10724
10957
  for (const input of opts.inputs) {
10725
10958
  if (input === STDIN_TOKEN) continue;
10726
10959
  const posixInput = toPosix(input);
10727
- const abs = resolve10(cwd, posixInput);
10960
+ const abs = resolve11(cwd, posixInput);
10728
10961
  const st = await statOrNull(abs);
10729
10962
  if (st?.isFile()) {
10730
10963
  named.add(toPosix(relative6(cwd, abs)));
@@ -10808,12 +11041,12 @@ var FINGERPRINT_RE = /^[0-9a-f]{16}$/;
10808
11041
  function canonicalFilePath(file, ctx) {
10809
11042
  if (!ctx) return file;
10810
11043
  const from = ctx.runBase ?? ctx.base;
10811
- return relative7(ctx.base, resolve11(from, file)).replace(/\\/g, "/");
11044
+ return relative7(ctx.base, resolve12(from, file)).replace(/\\/g, "/");
10812
11045
  }
10813
11046
  function canonicalSchemaRef(ref, ctx) {
10814
11047
  if (!ctx) return ref;
10815
11048
  if (classifyRef(ref).kind !== "file") return ref;
10816
- const abs = resolve11(ctx.cwd, ref);
11049
+ const abs = resolve12(ctx.cwd, ref);
10817
11050
  return relative7(ctx.base, abs).replace(/\\/g, "/");
10818
11051
  }
10819
11052
  function fingerprint(e, ctx) {
@@ -10887,7 +11120,7 @@ function parseBaseline(text2, source) {
10887
11120
  async function readBaseline(absPath, source) {
10888
11121
  let text2;
10889
11122
  try {
10890
- text2 = await readFile5(absPath, "utf8");
11123
+ text2 = await readFile4(absPath, "utf8");
10891
11124
  } catch (err) {
10892
11125
  if (err.code === "ENOENT") return null;
10893
11126
  throw new DocmetaError(
@@ -10971,17 +11204,17 @@ function countFingerprints(baseline) {
10971
11204
  }
10972
11205
  function resolveBaselineRequest(opts, configured, configDir, cwd, defaults) {
10973
11206
  const named = (label) => ({
10974
- absPath: resolve11(cwd, label),
11207
+ absPath: resolve12(cwd, label),
10975
11208
  label
10976
11209
  });
10977
11210
  const implied = () => {
10978
11211
  const base = configDir ?? cwd;
10979
11212
  if (configured !== void 0) {
10980
- return { absPath: resolve11(base, configured), label: configured };
11213
+ return { absPath: resolve12(base, configured), label: configured };
10981
11214
  }
10982
- const current = resolve11(base, defaults.current);
11215
+ const current = resolve12(base, defaults.current);
10983
11216
  if (defaults.legacy !== void 0) {
10984
- const legacy = resolve11(base, defaults.legacy);
11217
+ const legacy = resolve12(base, defaults.legacy);
10985
11218
  if (!existsSync2(current) && existsSync2(legacy)) {
10986
11219
  warn(
10987
11220
  `"${defaults.legacy}" is the pre-rename baseline file name. Rename it to "${defaults.current}".`
@@ -11487,8 +11720,8 @@ async function readCapped(url, res, maxBytes) {
11487
11720
  }
11488
11721
 
11489
11722
  // 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";
11723
+ import { readFile as readFile6 } from "node:fs/promises";
11724
+ import { isAbsolute as isAbsolute5, relative as relative9, resolve as resolve14, sep as sep4 } from "node:path";
11492
11725
  import {
11493
11726
  LineCounter as LineCounter3,
11494
11727
  isMap as isMap6,
@@ -11570,8 +11803,8 @@ function isMissing(error) {
11570
11803
 
11571
11804
  // src/shared/encryption-key.ts
11572
11805
  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";
11806
+ import { readFile as readFile5 } from "node:fs/promises";
11807
+ import { basename as basename4, dirname as dirname6, join as join7, relative as relative8, resolve as resolve13 } from "node:path";
11575
11808
  import { isDeepStrictEqual } from "node:util";
11576
11809
  import { isMap as isMap5, parseDocument as parseDocument4, parse as parseYaml3, stringify as stringify2 } from "yaml";
11577
11810
 
@@ -11583,8 +11816,8 @@ var RENAME_ATTEMPTS2 = 3;
11583
11816
  function errorCode2(err) {
11584
11817
  return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
11585
11818
  }
11586
- var wait3 = (ms) => new Promise((resolve25) => {
11587
- setTimeout(resolve25, ms);
11819
+ var wait3 = (ms) => new Promise((resolve26) => {
11820
+ setTimeout(resolve26, ms);
11588
11821
  });
11589
11822
  async function writeTextAtomic(path, text2) {
11590
11823
  const tmp = join6(
@@ -11633,14 +11866,14 @@ function resolveEncryptionKey(opts) {
11633
11866
  return { key: void 0, source: "none" };
11634
11867
  }
11635
11868
  function relativeSource2(cwd, path) {
11636
- return relative8(resolve12(cwd), path).replace(/\\/g, "/");
11869
+ return relative8(resolve13(cwd), path).replace(/\\/g, "/");
11637
11870
  }
11638
11871
  function errorCode3(err) {
11639
11872
  return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
11640
11873
  }
11641
11874
  async function readIfPresent(path) {
11642
11875
  try {
11643
- return await readFile6(path, "utf8");
11876
+ return await readFile5(path, "utf8");
11644
11877
  } catch (err) {
11645
11878
  if (errorCode3(err) === "ENOENT") return null;
11646
11879
  throw err;
@@ -11656,8 +11889,8 @@ async function fileTarget(file, toError) {
11656
11889
  return { path: file.path, source: file.source, text: text2 ?? file.text };
11657
11890
  }
11658
11891
  async function defaultTarget(cwd) {
11659
- const root = findGitRoot(cwd) ?? resolve12(cwd);
11660
- for (const name2 of [...FAMILY_CONFIG_NAMES, ...MOOSE_CONFIG_NAMES]) {
11892
+ const root = findGitRoot(cwd) ?? resolve13(cwd);
11893
+ for (const name2 of FAMILY_CONFIG_NAMES) {
11661
11894
  const path2 = join7(root, name2);
11662
11895
  const text2 = await readIfPresent(path2);
11663
11896
  if (text2 !== null) return { path: path2, source: relativeSource2(cwd, path2), text: text2 };
@@ -11679,20 +11912,20 @@ function withKey(text2, key, previous, source, toError) {
11679
11912
  if (previous === null) doc.delete(ENCRYPTION_KEY_PREVIOUS_FIELD);
11680
11913
  return doc.toString();
11681
11914
  }
11682
- function isMapping3(value) {
11915
+ function isMapping4(value) {
11683
11916
  return typeof value === "object" && value !== null && !Array.isArray(value);
11684
11917
  }
11685
11918
  function verify4(before, after, key, previous, source, toError) {
11686
11919
  const old = before === null ? null : parseYaml3(before);
11687
11920
  const expected = Object.fromEntries(
11688
11921
  Object.entries({
11689
- ...isMapping3(old) ? old : {},
11922
+ ...isMapping4(old) ? old : {},
11690
11923
  [ENCRYPTION_KEY_FIELD]: key,
11691
11924
  ...typeof previous === "string" ? { [ENCRYPTION_KEY_PREVIOUS_FIELD]: previous } : {}
11692
11925
  }).filter(([name2]) => previous !== null || name2 !== ENCRYPTION_KEY_PREVIOUS_FIELD)
11693
11926
  );
11694
11927
  const reread = parseYaml3(after);
11695
- if (!isMapping3(reread) || !isDeepStrictEqual(reread, expected)) {
11928
+ if (!isMapping4(reread) || !isDeepStrictEqual(reread, expected)) {
11696
11929
  throw toError(
11697
11930
  `Could not write ${ENCRYPTION_KEY_FIELD} to ${source}; add the key by hand.`
11698
11931
  );
@@ -11992,7 +12225,7 @@ async function loadExternalMetadata(collections, opts) {
11992
12225
  });
11993
12226
  parsed2 = parseManifest(manifest, collection, text2, file, opts.configDir, join12, ownership(declared, manifest));
11994
12227
  } else {
11995
- const abs = isAbsolute5(manifest.file) ? manifest.file : resolve13(opts.configDir, manifest.file);
12228
+ const abs = isAbsolute5(manifest.file) ? manifest.file : resolve14(opts.configDir, manifest.file);
11996
12229
  const label = reportedPath(abs, opts.base);
11997
12230
  file = label;
11998
12231
  parsed2 = await parsedManifests.parse(
@@ -12154,7 +12387,7 @@ function outsideRefusal(manifest, collection, pageRel) {
12154
12387
  }
12155
12388
  async function readPageManifest(abs, file) {
12156
12389
  try {
12157
- return await readFile7(abs, "utf8");
12390
+ return await readFile6(abs, "utf8");
12158
12391
  } catch (err) {
12159
12392
  if (isMissing(err)) return "";
12160
12393
  throw unreadable(file, err);
@@ -12162,7 +12395,7 @@ async function readPageManifest(abs, file) {
12162
12395
  }
12163
12396
  async function readManifest(abs, file) {
12164
12397
  try {
12165
- return await readFile7(abs, "utf8");
12398
+ return await readFile6(abs, "utf8");
12166
12399
  } catch (err) {
12167
12400
  throw unreadable(file, err);
12168
12401
  }
@@ -12211,7 +12444,7 @@ function parseManifest(manifest, collection, text2, file, configDir, join12, own
12211
12444
  `Manifest ${where}: "${spelled}" must be a mapping of owned keys to values.`
12212
12445
  );
12213
12446
  }
12214
- const indexKey = join12 === PATH_JOIN2 ? resolve13(configDir, spelled) : spelled;
12447
+ const indexKey = join12 === PATH_JOIN2 ? resolve14(configDir, spelled) : spelled;
12215
12448
  const values = target.get(indexKey) ?? /* @__PURE__ */ new Map();
12216
12449
  for (const kv of pair.value.items) {
12217
12450
  const key = isScalar5(kv.key) ? String(kv.key.value) : String(kv.key);
@@ -12299,10 +12532,10 @@ function mergeWith(label, extracted, index, memberOf2, base, encryptionKey, mark
12299
12532
  const entryOf = /* @__PURE__ */ new Map();
12300
12533
  const joins = [];
12301
12534
  if (label !== STDIN_LABEL) {
12302
- const byPath = index.byPath.get(resolve13(base, label));
12535
+ const byPath = index.byPath.get(resolve14(base, label));
12303
12536
  if (byPath) {
12304
12537
  sources.push(byPath);
12305
- const abs = resolve13(base, label);
12538
+ const abs = resolve14(base, label);
12306
12539
  entryOf.set(byPath, (sv) => index.entries.find((e) => e.file === sv.file && e.abs === abs));
12307
12540
  }
12308
12541
  }
@@ -12427,7 +12660,7 @@ function externalMetadataPointer(key) {
12427
12660
  }
12428
12661
  function orphanEntries(index, loaded, base, collections) {
12429
12662
  if (!index) return [];
12430
- const have = new Set(loaded.map((l) => resolve13(base, l)));
12663
+ const have = new Set(loaded.map((l) => resolve14(base, l)));
12431
12664
  const orphans = index.entries.filter(
12432
12665
  (e) => inCollections(e, collections) && e.join === PATH_JOIN2 && e.abs !== void 0 && !have.has(e.abs)
12433
12666
  );
@@ -12491,99 +12724,358 @@ function pageOwners(index, key, memberOf2, marked) {
12491
12724
  ];
12492
12725
  }
12493
12726
 
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
- }
12727
+ // src/meta/core/collections.ts
12728
+ import { isAbsolute as isAbsolute6, relative as relative10, resolve as resolvePath2, sep as sep5 } from "node:path";
12502
12729
 
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";
12523
- }
12524
- if (meta.includes("draft-04") || meta.includes("draft/4")) return "draft4";
12525
- return "2020";
12730
+ // src/meta/core/projection.ts
12731
+ var SYSTEM_COLUMNS = ["_path", "_format", "_present", "_data"];
12732
+ var RESERVED = new Set(SYSTEM_COLUMNS);
12733
+ function quoteIdent(name2) {
12734
+ return `"${name2.replaceAll('"', '""')}"`;
12526
12735
  }
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;
12736
+ function bindValue(value) {
12737
+ if (value === void 0 || value === null) return null;
12738
+ switch (typeof value) {
12739
+ case "boolean":
12740
+ return value ? 1 : 0;
12741
+ case "number":
12742
+ return Number.isFinite(value) ? value : null;
12743
+ case "bigint":
12744
+ case "string":
12745
+ return value;
12746
+ default:
12747
+ return JSON.stringify(value);
12748
+ }
12536
12749
  }
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;
12750
+ function corpusDataColumns(entries, extraKeys = []) {
12751
+ const keys = /* @__PURE__ */ new Set();
12752
+ for (const { extracted } of entries) {
12753
+ for (const key of Object.keys(extracted.data)) {
12754
+ if (key !== "" && !RESERVED.has(key)) keys.add(key);
12548
12755
  }
12549
- });
12550
- }
12551
- function isMarkShapeError(err) {
12552
- return err instanceof Error && err.message.includes(`"${ENCRYPT_KEYWORD}"`);
12553
- }
12554
- var locationRecorder;
12555
- function topLevelKey(instancePath) {
12556
- if (!instancePath.startsWith("/") || instancePath.indexOf("/", 1) !== -1) {
12557
- return void 0;
12558
12756
  }
12559
- return instancePath.slice(1).replace(/~1/g, "/").replace(/~0/g, "~");
12757
+ for (const key of extraKeys) {
12758
+ if (key !== "" && !RESERVED.has(key)) keys.add(key);
12759
+ }
12760
+ return [...keys].sort();
12560
12761
  }
12561
- function registerLocationKeyword(ajv) {
12562
- ajv.addKeyword({
12563
- keyword: LOCATION_KEYWORD,
12564
- metaSchema: { type: "string", enum: ["page", "external"] },
12565
- errors: false,
12566
- validate: (schema, _data, _parent, cxt) => {
12567
- if (locationRecorder !== void 0 && isFieldLocation(schema)) {
12568
- const key = topLevelKey(cxt?.instancePath ?? "");
12569
- if (key !== void 0) {
12570
- const seen = locationRecorder.get(key);
12571
- if (seen) seen.add(schema);
12572
- else locationRecorder.set(key, /* @__PURE__ */ new Set([schema]));
12573
- }
12574
- }
12575
- return true;
12762
+ function createDocsTable(db, entries, dataColumns) {
12763
+ db.exec(
12764
+ `CREATE TABLE docs ("_path" TEXT PRIMARY KEY, "_format" TEXT, "_present" INTEGER, "_data" TEXT${dataColumns.map((c) => `, ${quoteIdent(c)}`).join("")})`
12765
+ );
12766
+ const insert = db.prepare(
12767
+ `INSERT INTO docs VALUES (${["?", "?", "?", "?", ...dataColumns.map(() => "?")].join(", ")})`
12768
+ );
12769
+ db.exec("BEGIN");
12770
+ let committed = false;
12771
+ try {
12772
+ for (const { label, extracted } of entries) {
12773
+ insert.run(
12774
+ label,
12775
+ extracted.format,
12776
+ extracted.present ? 1 : 0,
12777
+ JSON.stringify(extracted.data),
12778
+ ...dataColumns.map((c) => bindValue(extracted.data[c]))
12779
+ );
12576
12780
  }
12577
- });
12781
+ db.exec("COMMIT");
12782
+ committed = true;
12783
+ } finally {
12784
+ if (!committed) db.exec("ROLLBACK");
12785
+ }
12578
12786
  }
12579
- function isLocationShapeError(err) {
12580
- return err instanceof Error && err.message.includes(`"${LOCATION_KEYWORD}"`);
12787
+ function registerLineFor(db, entries) {
12788
+ const byLabel = new Map(entries.map((e) => [e.label, e.extracted]));
12789
+ db.function("lineFor", { deterministic: true }, (path, key) => {
12790
+ if (typeof path !== "string" || typeof key !== "string") return null;
12791
+ return byLabel.get(path)?.lineFor(key) ?? null;
12792
+ });
12581
12793
  }
12582
- function marksSchemaExternal(schema) {
12583
- const properties = schema["properties"];
12584
- if (typeof properties !== "object" || properties === null) return false;
12585
- const own2 = properties[FILE_SCHEMA_KEY];
12586
- if (typeof own2 !== "object" || own2 === null) return false;
12794
+ function assertSingleStatement(sql) {
12795
+ const cut = topLevelSemicolon(sql);
12796
+ if (cut === -1) return;
12797
+ if (!isTrivia(sql.slice(cut + 1))) {
12798
+ throw new DocmetaError(
12799
+ "Run a single SQL statement per query; text after the first `;` would be silently ignored."
12800
+ );
12801
+ }
12802
+ }
12803
+ function startsStringOrIdent(sql, i) {
12804
+ const ch = sql[i];
12805
+ return ch === "'" || ch === '"' || ch === "`" || ch === "[";
12806
+ }
12807
+ function startsComment(sql, i) {
12808
+ const ch = sql[i];
12809
+ return ch === "-" && sql[i + 1] === "-" || ch === "/" && sql[i + 1] === "*";
12810
+ }
12811
+ function skipStringOrIdent(sql, i) {
12812
+ const quote = sql[i];
12813
+ if (quote === "[") {
12814
+ let j2 = i + 1;
12815
+ while (j2 < sql.length && sql[j2] !== "]") j2++;
12816
+ return j2 + 1;
12817
+ }
12818
+ let j = i + 1;
12819
+ while (j < sql.length) {
12820
+ if (sql[j] === quote) {
12821
+ if (sql[j + 1] === quote) {
12822
+ j += 2;
12823
+ continue;
12824
+ }
12825
+ return j + 1;
12826
+ }
12827
+ j++;
12828
+ }
12829
+ return j;
12830
+ }
12831
+ function skipComment(sql, i) {
12832
+ if (sql[i] === "-") {
12833
+ let j = i + 2;
12834
+ while (j < sql.length && sql[j] !== "\n") j++;
12835
+ return j < sql.length ? j + 1 : j;
12836
+ }
12837
+ const end = sql.indexOf("*/", i + 2);
12838
+ return end === -1 ? sql.length : end + 2;
12839
+ }
12840
+ function topLevelSemicolon(sql) {
12841
+ let i = 0;
12842
+ while (i < sql.length) {
12843
+ if (startsStringOrIdent(sql, i)) {
12844
+ i = skipStringOrIdent(sql, i);
12845
+ } else if (startsComment(sql, i)) {
12846
+ i = skipComment(sql, i);
12847
+ } else if (sql[i] === ";") {
12848
+ return i;
12849
+ } else {
12850
+ i++;
12851
+ }
12852
+ }
12853
+ return -1;
12854
+ }
12855
+ var PARAM_NAME_HEAD = /^[A-Za-z_][A-Za-z0-9_]*/;
12856
+ function isParamName(name2) {
12857
+ return PARAM_NAME_HEAD.exec(name2)?.[0] === name2;
12858
+ }
12859
+ function collectNamedParameters(sql) {
12860
+ const tokens2 = [];
12861
+ const seen = /* @__PURE__ */ new Set();
12862
+ const prefixOf = /* @__PURE__ */ new Map();
12863
+ let i = 0;
12864
+ while (i < sql.length) {
12865
+ const ch = sql[i];
12866
+ if (startsStringOrIdent(sql, i)) {
12867
+ i = skipStringOrIdent(sql, i);
12868
+ } else if (startsComment(sql, i)) {
12869
+ i = skipComment(sql, i);
12870
+ } else if (ch === "$" || ch === ":" || ch === "@") {
12871
+ if (ch === ":" && sql[i + 1] === ":") {
12872
+ i += 2;
12873
+ continue;
12874
+ }
12875
+ const m = PARAM_NAME_HEAD.exec(sql.slice(i + 1));
12876
+ if (!m) {
12877
+ i++;
12878
+ continue;
12879
+ }
12880
+ const token = ch + m[0];
12881
+ const priorPrefix = prefixOf.get(m[0]);
12882
+ if (priorPrefix !== void 0 && priorPrefix !== ch) {
12883
+ throw new DocmetaError(
12884
+ `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.`
12885
+ );
12886
+ }
12887
+ prefixOf.set(m[0], ch);
12888
+ if (!seen.has(token)) {
12889
+ seen.add(token);
12890
+ tokens2.push(token);
12891
+ }
12892
+ i += 1 + m[0].length;
12893
+ } else {
12894
+ i++;
12895
+ }
12896
+ }
12897
+ return tokens2;
12898
+ }
12899
+ function stripLeadingTrivia(sql) {
12900
+ let i = 0;
12901
+ for (; ; ) {
12902
+ while (i < sql.length && /\s/.test(sql[i] ?? "")) i++;
12903
+ if (startsComment(sql, i)) {
12904
+ i = skipComment(sql, i);
12905
+ continue;
12906
+ }
12907
+ return sql.slice(i);
12908
+ }
12909
+ }
12910
+ function isTrivia(text2) {
12911
+ let i = 0;
12912
+ while (i < text2.length) {
12913
+ const ch = text2[i];
12914
+ if (ch === void 0 || /\s/.test(ch) || ch === ";") {
12915
+ i++;
12916
+ continue;
12917
+ }
12918
+ if (startsComment(text2, i)) {
12919
+ i = skipComment(text2, i);
12920
+ continue;
12921
+ }
12922
+ return false;
12923
+ }
12924
+ return true;
12925
+ }
12926
+ var sqliteModule;
12927
+ function loadSqlite() {
12928
+ sqliteModule ??= (async () => {
12929
+ const original = process.emitWarning.bind(process);
12930
+ const filtered = (warning, ...rest) => {
12931
+ const text2 = typeof warning === "string" ? warning : warning.message;
12932
+ if (text2.includes("SQLite is an experimental feature")) return;
12933
+ Reflect.apply(original, process, [warning, ...rest]);
12934
+ };
12935
+ process.emitWarning = filtered;
12936
+ try {
12937
+ return await import("node:sqlite");
12938
+ } finally {
12939
+ process.emitWarning = original;
12940
+ }
12941
+ })();
12942
+ return sqliteModule;
12943
+ }
12944
+
12945
+ // src/meta/core/collections.ts
12946
+ function memberOf(collections, configDir, base, label) {
12947
+ if (collections.length === 0) return [];
12948
+ if (label === STDIN_LABEL) return [];
12949
+ const rel = relative10(configDir, resolvePath2(base, label)).split(sep5).join("/");
12950
+ if (isAbsolute6(rel)) return [];
12951
+ return collections.filter((c) => isMember(c, rel)).map((c) => c.name);
12952
+ }
12953
+ function retainMembers(files, membersFor) {
12954
+ return files.filter((label) => membersFor(label).length > 0);
12955
+ }
12956
+ function collectionNames(collections) {
12957
+ return (collections ?? []).map((c) => c.name);
12958
+ }
12959
+ function collectCollections(entries, params) {
12960
+ const declared = params.collections ?? [];
12961
+ if (declared.length === 0) return [];
12962
+ const base = params.base ?? ".";
12963
+ const configDir = params.configDir ?? base;
12964
+ const members = /* @__PURE__ */ new Map();
12965
+ for (const entry of entries) {
12966
+ for (const name2 of memberOf(declared, configDir, base, entry.label)) {
12967
+ const list2 = members.get(name2);
12968
+ if (list2) list2.push(entry.label);
12969
+ else members.set(name2, [entry.label]);
12970
+ }
12971
+ }
12972
+ return declared.map((c) => ({ name: c.name, members: members.get(c.name) ?? [] }));
12973
+ }
12974
+ function quoteSqlString(value) {
12975
+ return `'${value.replaceAll("'", "''")}'`;
12976
+ }
12977
+ function createCollectionViews(db, collections) {
12978
+ for (const c of collections) {
12979
+ const where = c.members.length === 0 ? "0" : `_path IN (${c.members.map(quoteSqlString).join(", ")})`;
12980
+ db.exec(
12981
+ `CREATE VIEW ${quoteIdent(c.name)} AS SELECT * FROM docs WHERE ${where}`
12982
+ );
12983
+ }
12984
+ }
12985
+
12986
+ // src/meta/core/location.ts
12987
+ var LOCATION_KEYWORD = "x-manni-location";
12988
+ var LOCATION_EXTERNAL_SCHEMA = "location:external";
12989
+ var LOCATION_PAGE_SCHEMA = "location:page";
12990
+ var LOCATION_FINDING_KEYWORD = "location";
12991
+ function isFieldLocation(value) {
12992
+ return value === "page" || value === "external";
12993
+ }
12994
+
12995
+ // src/meta/core/validator.ts
12996
+ import * as AjvDraft07Ns from "ajv";
12997
+ import * as Ajv2019Ns from "ajv/dist/2019.js";
12998
+ import * as Ajv2020Ns from "ajv/dist/2020.js";
12999
+ import * as AjvDraft04Ns from "ajv-draft-04";
13000
+ import * as addFormatsNs from "ajv-formats";
13001
+ import { createRequire } from "node:module";
13002
+ var draft06MetaSchema = createRequire(import.meta.url)(
13003
+ "ajv/dist/refs/json-schema-draft-06.json"
13004
+ );
13005
+ var AjvDraft07 = AjvDraft07Ns.default;
13006
+ var Ajv2019 = Ajv2019Ns.default;
13007
+ var Ajv2020 = Ajv2020Ns.default;
13008
+ var AjvDraft04 = AjvDraft04Ns.default;
13009
+ var addFormats = addFormatsNs.default;
13010
+ function dialectOf(schema) {
13011
+ const meta = typeof schema.$schema === "string" ? schema.$schema : "";
13012
+ if (meta.includes("2019-09")) return "2019";
13013
+ if (meta.includes("draft-07") || meta.includes("draft/7") || meta.includes("draft-06") || meta.includes("draft/6")) {
13014
+ return "draft7";
13015
+ }
13016
+ if (meta.includes("draft-04") || meta.includes("draft/4")) return "draft4";
13017
+ return "2020";
13018
+ }
13019
+ function buildAjv(dialect, coerceTypes = false) {
13020
+ const opts = { allErrors: true, strict: false, coerceTypes };
13021
+ const ajv = dialect === "2019" ? new Ajv2019(opts) : dialect === "draft7" ? new AjvDraft07(opts) : dialect === "draft4" ? new AjvDraft04(opts) : new Ajv2020(opts);
13022
+ addFormats(ajv);
13023
+ if (dialect === "draft7") ajv.addMetaSchema(draft06MetaSchema);
13024
+ registerEncryptKeyword(ajv);
13025
+ registerLocationKeyword(ajv);
13026
+ registerBuiltins(ajv, dialect);
13027
+ return ajv;
13028
+ }
13029
+ var markRecorder;
13030
+ function registerEncryptKeyword(ajv) {
13031
+ ajv.addKeyword({
13032
+ keyword: ENCRYPT_KEYWORD,
13033
+ metaSchema: { type: "boolean" },
13034
+ errors: false,
13035
+ validate: (schema, _data, _parent, cxt) => {
13036
+ if (schema === true && markRecorder !== void 0) {
13037
+ markRecorder.add(cxt?.instancePath ?? "");
13038
+ }
13039
+ return true;
13040
+ }
13041
+ });
13042
+ }
13043
+ function isMarkShapeError(err) {
13044
+ return err instanceof Error && err.message.includes(`"${ENCRYPT_KEYWORD}"`);
13045
+ }
13046
+ var locationRecorder;
13047
+ function topLevelKey(instancePath) {
13048
+ if (!instancePath.startsWith("/") || instancePath.indexOf("/", 1) !== -1) {
13049
+ return void 0;
13050
+ }
13051
+ return instancePath.slice(1).replace(/~1/g, "/").replace(/~0/g, "~");
13052
+ }
13053
+ function registerLocationKeyword(ajv) {
13054
+ ajv.addKeyword({
13055
+ keyword: LOCATION_KEYWORD,
13056
+ metaSchema: { type: "string", enum: ["page", "external"] },
13057
+ errors: false,
13058
+ validate: (schema, _data, _parent, cxt) => {
13059
+ if (locationRecorder !== void 0 && isFieldLocation(schema)) {
13060
+ const key = topLevelKey(cxt?.instancePath ?? "");
13061
+ if (key !== void 0) {
13062
+ const seen = locationRecorder.get(key);
13063
+ if (seen) seen.add(schema);
13064
+ else locationRecorder.set(key, /* @__PURE__ */ new Set([schema]));
13065
+ }
13066
+ }
13067
+ return true;
13068
+ }
13069
+ });
13070
+ }
13071
+ function isLocationShapeError(err) {
13072
+ return err instanceof Error && err.message.includes(`"${LOCATION_KEYWORD}"`);
13073
+ }
13074
+ function marksSchemaExternal(schema) {
13075
+ const properties = schema["properties"];
13076
+ if (typeof properties !== "object" || properties === null) return false;
13077
+ const own2 = properties[FILE_SCHEMA_KEY];
13078
+ if (typeof own2 !== "object" || own2 === null) return false;
12587
13079
  return own2[LOCATION_KEYWORD] === "external";
12588
13080
  }
12589
13081
  function isRecord2(value) {
@@ -12874,338 +13366,79 @@ var Validator = class {
12874
13366
  * Callers with an extractor that supplies no column pass nothing and get the
12875
13367
  * previous behavior exactly.
12876
13368
  */
12877
- async validate(data, refs, lineFor, colFor, locate) {
12878
- const { [FILE_SCHEMA_KEY]: _omit, ...subject } = data;
12879
- void _omit;
12880
- const errors = [];
12881
- for (const ref of refs) {
12882
- 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
- );
13106
- }
13107
- prefixOf.set(m[0], ch);
13108
- if (!seen.has(token)) {
13109
- seen.add(token);
13110
- tokens2.push(token);
13369
+ async validate(data, refs, lineFor, colFor, locate) {
13370
+ const { [FILE_SCHEMA_KEY]: _omit, ...subject } = data;
13371
+ void _omit;
13372
+ const errors = [];
13373
+ for (const ref of refs) {
13374
+ const fn = await this.compile(ref);
13375
+ const ok = fn(subject);
13376
+ if (ok) continue;
13377
+ for (const e of fn.errors ?? []) {
13378
+ errors.push(toFieldError(ref, e, lineFor, colFor, locate));
13111
13379
  }
13112
- i += 1 + m[0].length;
13113
- } else {
13114
- i++;
13115
13380
  }
13381
+ return errors;
13116
13382
  }
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;
13126
- }
13127
- return sql.slice(i);
13383
+ };
13384
+ function subjectOf(e) {
13385
+ switch (e.keyword) {
13386
+ case "required":
13387
+ return e.params.missingProperty;
13388
+ case "additionalProperties":
13389
+ return e.params.additionalProperty;
13390
+ case "format":
13391
+ return e.params.format;
13392
+ case "type":
13393
+ return e.params.type;
13394
+ default:
13395
+ return void 0;
13128
13396
  }
13129
13397
  }
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;
13398
+ function toFieldError(schema, e, lineFor, colFor, locate) {
13399
+ const defined = e;
13400
+ const instancePath = e.instancePath;
13401
+ const subject = subjectOf(defined);
13402
+ let message = e.message ?? "is invalid";
13403
+ let wantsColumn = true;
13404
+ if (defined.keyword === "required") {
13405
+ message = `must have required property '${defined.params.missingProperty}'`;
13406
+ wantsColumn = false;
13407
+ } else if (defined.keyword === "additionalProperties") {
13408
+ message = `must NOT have additional property '${defined.params.additionalProperty}'`;
13409
+ wantsColumn = false;
13143
13410
  }
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]);
13411
+ const located = defined.keyword === "additionalProperties" ? locate?.(
13412
+ `${instancePath}/${escapePointerSegment(defined.params.additionalProperty)}`
13413
+ ) ?? locate?.(instancePath) : locate?.(instancePath);
13414
+ if (located) {
13415
+ return {
13416
+ schema,
13417
+ instancePath,
13418
+ message,
13419
+ keyword: e.keyword,
13420
+ ...subject != null ? { subject } : {},
13421
+ file: located.file,
13422
+ ...located.line != null ? { line: located.line } : {},
13423
+ ...wantsColumn && located.col != null ? { col: located.col } : {}
13154
13424
  };
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
- }
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
- }
13191
- }
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
- );
13203
13425
  }
13426
+ const line = lineFor(instancePath);
13427
+ const col = wantsColumn ? colFor?.(instancePath) : void 0;
13428
+ return {
13429
+ schema,
13430
+ instancePath,
13431
+ message,
13432
+ keyword: e.keyword,
13433
+ ...subject != null ? { subject } : {},
13434
+ ...line != null ? { line } : {},
13435
+ ...col != null ? { col } : {}
13436
+ };
13204
13437
  }
13205
13438
 
13206
13439
  // src/meta/core/derive/cache.ts
13207
13440
  import { createHash as createHash4 } from "node:crypto";
13208
- import { mkdirSync, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync } from "node:fs";
13441
+ import { mkdirSync, readFileSync as readFileSync3, renameSync, rmSync, writeFileSync } from "node:fs";
13209
13442
  import { join as join8 } from "node:path";
13210
13443
  var REVIEW_CACHE_DIR = ".manni/meta/review-cache";
13211
13444
  var REVIEW_CACHE_VERSION = 1;
@@ -13225,7 +13458,7 @@ var ReviewCache = class {
13225
13458
  if (!this.enabled) return void 0;
13226
13459
  let raw;
13227
13460
  try {
13228
- raw = JSON.parse(readFileSync2(this.entryPath(key), "utf8"));
13461
+ raw = JSON.parse(readFileSync3(this.entryPath(key), "utf8"));
13229
13462
  } catch {
13230
13463
  return void 0;
13231
13464
  }
@@ -13789,12 +14022,12 @@ function utcDate(iso) {
13789
14022
  }
13790
14023
 
13791
14024
  // src/meta/core/derive/index.ts
13792
- import { dirname as dirname9, resolve as resolve15 } from "node:path";
14025
+ import { dirname as dirname9, resolve as resolve16 } from "node:path";
13793
14026
 
13794
14027
  // src/meta/core/derive/codeowners.ts
13795
14028
  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";
14029
+ import { readFile as readFile7 } from "node:fs/promises";
14030
+ import { dirname as dirname7, relative as relative11, resolve as resolve15, sep as sep6 } from "node:path";
13798
14031
  import picomatch4 from "picomatch";
13799
14032
  var CODEOWNERS_LOCATIONS = [
13800
14033
  ".github/CODEOWNERS",
@@ -13804,11 +14037,11 @@ var CODEOWNERS_LOCATIONS = [
13804
14037
  ];
13805
14038
  function findCodeowners(root, explicit) {
13806
14039
  if (explicit !== void 0) {
13807
- const path = resolve14(root, explicit);
14040
+ const path = resolve15(root, explicit);
13808
14041
  return existsSync3(path) ? path : null;
13809
14042
  }
13810
14043
  for (const loc of CODEOWNERS_LOCATIONS) {
13811
- const path = resolve14(root, ...loc.split("/"));
14044
+ const path = resolve15(root, ...loc.split("/"));
13812
14045
  if (existsSync3(path)) return path;
13813
14046
  }
13814
14047
  return null;
@@ -13923,7 +14156,7 @@ function toGlobs(pattern) {
13923
14156
  var NOT_FOUND_REASON = `no CODEOWNERS file found (looked for ${CODEOWNERS_LOCATIONS.join(", ")})`;
13924
14157
  async function deriveFromCodeowners(inputs, opts) {
13925
14158
  const records = /* @__PURE__ */ new Map();
13926
- const explicitPath = opts.explicit === void 0 ? void 0 : resolve14(opts.configDir ?? process.cwd(), opts.explicit);
14159
+ const explicitPath = opts.explicit === void 0 ? void 0 : resolve15(opts.configDir ?? process.cwd(), opts.explicit);
13927
14160
  if (explicitPath !== void 0 && !existsSync3(explicitPath)) {
13928
14161
  return {
13929
14162
  status: { available: false, reason: `CODEOWNERS not found at ${explicitPath}` },
@@ -13957,7 +14190,7 @@ async function deriveFromCodeowners(inputs, opts) {
13957
14190
  const key = `${path}\0${label}`;
13958
14191
  let file = parsed2.get(key);
13959
14192
  if (!file) {
13960
- file = readFile8(path, "utf8").then(
14193
+ file = readFile7(path, "utf8").then(
13961
14194
  (text2) => parseCodeowners(text2, path, label)
13962
14195
  );
13963
14196
  parsed2.set(key, file);
@@ -14684,15 +14917,24 @@ async function deriveFromGit(inputs, opts) {
14684
14917
  continue;
14685
14918
  }
14686
14919
  try {
14687
- const histories = bucket.length <= threshold ? await perFileHistories(run2, bucket) : await bulkHistories(run2, bucket);
14688
- if (histories === null) {
14920
+ const found = await histories(
14921
+ run2,
14922
+ bucket.map((e) => e.rel),
14923
+ threshold
14924
+ );
14925
+ if (found === null) {
14926
+ reasons.push(`git could not read the history at ${root}`);
14927
+ continue;
14928
+ }
14929
+ await readOtherParentBlobs(run2, found);
14930
+ const blobs = await fetchBlobs(run2, neededBlobs(found));
14931
+ if (!await followTakenBodies(run2, bucket, found, blobs, threshold)) {
14689
14932
  reasons.push(`git could not read the history at ${root}`);
14690
14933
  continue;
14691
14934
  }
14692
- const blobs = await fetchBlobs(run2, neededBlobs(histories));
14693
- const manifestStamps = await stampsInManifests(run2, root, bucket, histories, blobs);
14935
+ const manifestStamps = await stampsInManifests(run2, root, bucket, found, blobs);
14694
14936
  for (const entry of bucket) {
14695
- const history = histories.get(entry.rel) ?? [];
14937
+ const history = found.get(entry.rel) ?? [];
14696
14938
  const facts = judge(
14697
14939
  entry.input,
14698
14940
  root,
@@ -14753,7 +14995,7 @@ function groupByRoot(inputs) {
14753
14995
  }
14754
14996
  return byRoot;
14755
14997
  }
14756
- var RECORD_FORMAT = "--format=%x1e%H%x00%aI%x00%an%x00%ae%x00%(trailers:key=Co-authored-by,valueonly,unfold,separator=%x1f)%x00%(trailers:key=Reviewed-by,valueonly,unfold,separator=%x1f)%x00%(trailers:key=Generated-by,valueonly,unfold,separator=%x1f)";
14998
+ var RECORD_FORMAT = "--format=%x1e%H%x00%aI%x00%an%x00%ae%x00%(trailers:key=Co-authored-by,valueonly,unfold,separator=%x1f)%x00%(trailers:key=Reviewed-by,valueonly,unfold,separator=%x1f)%x00%(trailers:key=Generated-by,valueonly,unfold,separator=%x1f)%x00%P";
14757
14999
  var LOG_ARGS = [
14758
15000
  "-c",
14759
15001
  "core.quotePath=false",
@@ -14764,31 +15006,70 @@ var LOG_ARGS = [
14764
15006
  "--no-abbrev",
14765
15007
  RECORD_FORMAT
14766
15008
  ];
14767
- async function perFileHistories(run2, bucket) {
14768
- const histories = /* @__PURE__ */ new Map();
14769
- for (const entry of bucket) {
14770
- const out = await run2([...LOG_ARGS, "--follow", "--", pathspec(entry.rel)]);
15009
+ function histories(run2, rels, threshold, rev = "HEAD") {
15010
+ return rels.length <= threshold ? perFileHistories(run2, rels, rev) : bulkHistories(run2, rels, rev);
15011
+ }
15012
+ async function perFileHistories(run2, rels, rev) {
15013
+ const found = /* @__PURE__ */ new Map();
15014
+ for (const rel of rels) {
15015
+ const out = await run2([...LOG_ARGS, rev, "--follow", "--", pathspec(rel)]);
14771
15016
  if (out.tooLarge) throw new HistoryTooLarge();
14772
15017
  if (out.code !== 0) {
14773
- if (await isUnbornHead(run2)) return histories;
15018
+ if (await isUnbornHead(run2)) return found;
14774
15019
  return null;
14775
15020
  }
14776
- const attributed = attribute(parseLog(text(out)), [entry.rel]);
14777
- histories.set(entry.rel, attributed.get(entry.rel) ?? []);
15021
+ const attributed = attribute(parseLog(text(out)), [rel]);
15022
+ found.set(rel, attributed.get(rel) ?? []);
14778
15023
  }
14779
- return histories;
15024
+ return found;
14780
15025
  }
14781
- async function bulkHistories(run2, bucket) {
14782
- const out = await run2(LOG_ARGS);
15026
+ async function bulkHistories(run2, rels, rev) {
15027
+ const out = await run2([...LOG_ARGS, rev]);
14783
15028
  if (out.tooLarge) throw new HistoryTooLarge();
14784
15029
  if (out.code !== 0) {
14785
15030
  if (await isUnbornHead(run2)) return /* @__PURE__ */ new Map();
14786
15031
  return null;
14787
15032
  }
14788
- return attribute(
14789
- parseLog(text(out)),
14790
- bucket.map((e) => e.rel)
14791
- );
15033
+ return attribute(parseLog(text(out)), [...rels]);
15034
+ }
15035
+ async function followTakenBodies(run2, bucket, found, blobs, threshold) {
15036
+ for (; ; ) {
15037
+ const byParent = /* @__PURE__ */ new Map();
15038
+ for (const entry of bucket) {
15039
+ const history = found.get(entry.rel) ?? [];
15040
+ const taken = takenMerge(entry.input.extracted.fenced, history, blobs);
15041
+ if (taken === void 0) continue;
15042
+ const group = byParent.get(taken.parent) ?? [];
15043
+ group.push({ rel: entry.rel, keep: history.slice(0, taken.index), path: taken.path });
15044
+ byParent.set(taken.parent, group);
15045
+ }
15046
+ if (byParent.size === 0) return true;
15047
+ const walked = /* @__PURE__ */ new Map();
15048
+ for (const [parent, group] of byParent) {
15049
+ const paths = [...new Set(group.map((g) => g.path))];
15050
+ const side = await histories(run2, paths, threshold, parent);
15051
+ if (side === null) return false;
15052
+ for (const g of group) {
15053
+ const tail = side.get(g.path) ?? [];
15054
+ walked.set(g.rel, tail);
15055
+ found.set(g.rel, [...g.keep, ...tail]);
15056
+ }
15057
+ }
15058
+ await readOtherParentBlobs(run2, walked);
15059
+ const missing = new Set([...neededBlobs(walked)].filter((sha) => !blobs.has(sha)));
15060
+ for (const [sha, content] of await fetchBlobs(run2, missing)) blobs.set(sha, content);
15061
+ }
15062
+ }
15063
+ function takenMerge(fenced, history, blobs) {
15064
+ const blobBody = (sha) => sha === null ? null : bodyOf(blobs.get(sha) ?? "", fenced);
15065
+ const index = history.findIndex((h) => !sameBody(blobBody(h.newBlob), blobBody(h.oldBlob)));
15066
+ const newest = history[index];
15067
+ if (newest === void 0) return void 0;
15068
+ const body = blobBody(newest.newBlob);
15069
+ const k = newest.otherParentBlobs.findIndex((b) => b !== null && sameBody(body, blobBody(b)));
15070
+ const parent = newest.otherParents[k];
15071
+ if (k === -1 || parent === void 0) return void 0;
15072
+ return { index, parent, path: newest.pathAtCommit };
14792
15073
  }
14793
15074
  function pathspec(rel) {
14794
15075
  return /[*?[\]]/.test(rel) ? `:(literal)${rel}` : rel;
@@ -14821,6 +15102,7 @@ function parseLog(text2) {
14821
15102
  coAuthors: splitTrailers(fields[4]),
14822
15103
  reviewedBy: splitTrailers(fields[5]),
14823
15104
  generatedBy: splitTrailers(fields[6]),
15105
+ parents: (fields[7] ?? "").trim().split(" ").filter((p) => p !== ""),
14824
15106
  raw
14825
15107
  });
14826
15108
  }
@@ -14891,10 +15173,10 @@ function unquotePath(p) {
14891
15173
  return Buffer.from(bytes).toString("utf8");
14892
15174
  }
14893
15175
  function attribute(records, wanted) {
14894
- const histories = /* @__PURE__ */ new Map();
15176
+ const histories2 = /* @__PURE__ */ new Map();
14895
15177
  const tracked = /* @__PURE__ */ new Map();
14896
15178
  for (const rel of wanted) {
14897
- histories.set(rel, []);
15179
+ histories2.set(rel, []);
14898
15180
  tracked.set(rel, rel);
14899
15181
  }
14900
15182
  for (const record of records) {
@@ -14904,7 +15186,7 @@ function attribute(records, wanted) {
14904
15186
  for (const [rel, current] of tracked) {
14905
15187
  const entry = byNewPath.get(current);
14906
15188
  if (!entry) continue;
14907
- histories.get(rel)?.push({
15189
+ histories2.get(rel)?.push({
14908
15190
  sha: record.sha,
14909
15191
  authorDate: record.authorDate,
14910
15192
  authorName: record.authorName,
@@ -14914,23 +15196,52 @@ function attribute(records, wanted) {
14914
15196
  generatedBy: record.generatedBy,
14915
15197
  oldBlob: entry.oldBlob === NULL_SHA ? null : entry.oldBlob,
14916
15198
  newBlob: entry.newBlob === NULL_SHA ? null : entry.newBlob,
14917
- pathAtCommit: entry.newPath
15199
+ pathAtCommit: entry.newPath,
15200
+ otherParents: record.parents.slice(1),
15201
+ otherParentBlobs: []
14918
15202
  });
14919
15203
  if (entry.oldPath !== entry.newPath) tracked.set(rel, entry.oldPath);
14920
15204
  }
14921
15205
  }
14922
- return histories;
15206
+ return histories2;
14923
15207
  }
14924
- function neededBlobs(histories) {
15208
+ function neededBlobs(histories2) {
14925
15209
  const shas = /* @__PURE__ */ new Set();
14926
- for (const history of histories.values()) {
15210
+ for (const history of histories2.values()) {
14927
15211
  for (const h of history) {
14928
15212
  if (h.oldBlob !== null) shas.add(h.oldBlob);
14929
15213
  if (h.newBlob !== null) shas.add(h.newBlob);
15214
+ for (const b of h.otherParentBlobs) if (b !== null) shas.add(b);
14930
15215
  }
14931
15216
  }
14932
15217
  return shas;
14933
15218
  }
15219
+ async function readOtherParentBlobs(run2, histories2) {
15220
+ const merges = [];
15221
+ const specs = [];
15222
+ for (const history of histories2.values()) {
15223
+ for (const h of history) {
15224
+ if (h.otherParents.length === 0) continue;
15225
+ merges.push(h);
15226
+ for (const parent of h.otherParents) specs.push(`${parent}:${h.pathAtCommit}`);
15227
+ }
15228
+ }
15229
+ if (specs.length === 0) return;
15230
+ const out = await run2(["cat-file", "--batch-check"], `${specs.join("\n")}
15231
+ `);
15232
+ if (out.tooLarge) throw new HistoryTooLarge();
15233
+ if (out.code !== 0) throw new BlobsUnreadable(`exit ${String(out.code)}`);
15234
+ const lines = text(out).split("\n");
15235
+ let i = 0;
15236
+ for (const h of merges) {
15237
+ h.otherParentBlobs = h.otherParents.map(() => blobOfCheckLine(lines[i++]));
15238
+ }
15239
+ }
15240
+ function blobOfCheckLine(line) {
15241
+ const [sha, kind] = (line ?? "").split(" ");
15242
+ if (sha === void 0 || kind !== "blob" || !/^[0-9a-f]{40,64}$/.test(sha)) return null;
15243
+ return sha;
15244
+ }
14934
15245
  async function fetchBlobs(run2, shas) {
14935
15246
  const blobs = /* @__PURE__ */ new Map();
14936
15247
  if (shas.size === 0) return blobs;
@@ -14964,19 +15275,23 @@ var git = (value, evidence) => ({
14964
15275
  function factCommits(input, history, blobs) {
14965
15276
  const fenced = input.extracted.fenced;
14966
15277
  const blobBody = (sha) => sha === null ? null : bodyOf(blobs.get(sha) ?? "", fenced);
14967
- const bodyChanging = [...history].reverse().filter((h) => !sameBody(blobBody(h.newBlob), blobBody(h.oldBlob)));
15278
+ const bodyChanging = [...history].reverse().filter((h) => {
15279
+ const body = blobBody(h.newBlob);
15280
+ if (sameBody(body, blobBody(h.oldBlob))) return false;
15281
+ return !h.otherParentBlobs.some((b) => b !== null && sameBody(body, blobBody(b)));
15282
+ });
14968
15283
  return {
14969
15284
  bodyChanging,
14970
15285
  oldest: history[history.length - 1],
14971
15286
  newestBody: bodyChanging[bodyChanging.length - 1]
14972
15287
  };
14973
15288
  }
14974
- async function stampsInManifests(run2, root, bucket, histories, blobs) {
15289
+ async function stampsInManifests(run2, root, bucket, histories2, blobs) {
14975
15290
  const reads = [];
14976
15291
  for (const entry of bucket) {
14977
15292
  const refs = entry.input.stampManifests;
14978
15293
  if (refs === void 0) continue;
14979
- const { oldest, newestBody } = factCommits(entry.input, histories.get(entry.rel) ?? [], blobs);
15294
+ const { oldest, newestBody } = factCommits(entry.input, histories2.get(entry.rel) ?? [], blobs);
14980
15295
  for (const field of STAMPED_DATE_FIELDS) {
14981
15296
  const ref = refs[field];
14982
15297
  const at2 = field === "created" ? oldest : newestBody;
@@ -15585,7 +15900,7 @@ async function deriveReviewsByRoot(inputs, git2, rootFor, ctx, requested) {
15585
15900
  if (byRoot.size === 0) {
15586
15901
  return unavailable3(requested, "no git repository contains the documents");
15587
15902
  }
15588
- const cacheDir = resolve15(ctx.configDir ?? ctx.cwd, REVIEW_CACHE_DIR);
15903
+ const cacheDir = resolve16(ctx.configDir ?? ctx.cwd, REVIEW_CACHE_DIR);
15589
15904
  for (const [root, bucket] of byRoot) {
15590
15905
  const client = ctx.reviews ?? await createReviewClient(root, { cwd: ctx.cwd, cache: ctx.cache, cacheDir, requested });
15591
15906
  const identity = await client.detect();
@@ -15763,7 +16078,7 @@ async function deriveForTable(inputs, ctx, hint, fields) {
15763
16078
  }
15764
16079
 
15765
16080
  // src/meta/commands/validate.ts
15766
- import { readFile as readFile10 } from "node:fs/promises";
16081
+ import { readFile as readFile9 } from "node:fs/promises";
15767
16082
 
15768
16083
  // src/meta/core/page-marks.ts
15769
16084
  function pageMarks(opts) {
@@ -15840,8 +16155,8 @@ async function configMarks(configPath, cwd) {
15840
16155
  }
15841
16156
 
15842
16157
  // src/meta/core/relocation.ts
15843
- import { access, readFile as readFile9, stat as stat7, unlink } from "node:fs/promises";
15844
- 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";
16158
+ import { access, readFile as readFile8, stat as stat7, unlink } from "node:fs/promises";
16159
+ 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";
15845
16160
  import picomatch6 from "picomatch";
15846
16161
  import {
15847
16162
  LineCounter as LineCounter5,
@@ -16245,7 +16560,7 @@ var Model = class {
16245
16560
  c.externalMetadata.forEach((m, index) => {
16246
16561
  const url = classifyRef(m.file).kind === "url";
16247
16562
  const perPage = !url && hasPagePlaceholder(m.file);
16248
- const absPath = url || perPage ? void 0 : isAbsolute8(m.file) ? m.file : resolve16(configDir, m.file);
16563
+ const absPath = url || perPage ? void 0 : isAbsolute8(m.file) ? m.file : resolve17(configDir, m.file);
16249
16564
  this.manifests.push({
16250
16565
  collection: c.name,
16251
16566
  index,
@@ -16437,7 +16752,7 @@ var Model = class {
16437
16752
  /** A label relative to the config directory, posix; undefined outside it. */
16438
16753
  relative(label) {
16439
16754
  if (label === "<stdin>") return void 0;
16440
- const rel = toPosix3(relative13(this.configDir, resolve16(this.ctx.base, label)));
16755
+ const rel = toPosix3(relative13(this.configDir, resolve17(this.ctx.base, label)));
16441
16756
  return isAbsolute8(rel) || rel.startsWith("../") ? void 0 : rel;
16442
16757
  }
16443
16758
  };
@@ -16452,11 +16767,11 @@ async function collectionPath(target, cwd, configDir) {
16452
16767
  const posix7 = target.replace(/\\/g, "/");
16453
16768
  const scanned = picomatch6.scan(posix7);
16454
16769
  if (scanned.isGlob) {
16455
- const rel2 = toPosix3(relative13(configDir, resolve16(cwd, scanned.base === "" ? "." : scanned.base)));
16770
+ const rel2 = toPosix3(relative13(configDir, resolve17(cwd, scanned.base === "" ? "." : scanned.base)));
16456
16771
  if (isAbsolute8(rel2) || rel2 === ".." || rel2.startsWith("../")) return void 0;
16457
16772
  return rel2 === "" ? scanned.glob : `${rel2}/${scanned.glob}`;
16458
16773
  }
16459
- const abs = resolve16(cwd, posix7);
16774
+ const abs = resolve17(cwd, posix7);
16460
16775
  const rel = toPosix3(relative13(configDir, abs));
16461
16776
  if (isAbsolute8(rel) || rel.startsWith("..")) return void 0;
16462
16777
  let directory = posix7.endsWith("/");
@@ -16480,7 +16795,7 @@ async function exists(absPath, file) {
16480
16795
  }
16481
16796
  }
16482
16797
  function plannedConfigDir(ctx) {
16483
- return ctx.configDir ?? findGitRoot(ctx.cwd) ?? resolve16(ctx.cwd);
16798
+ return ctx.configDir ?? findGitRoot(ctx.cwd) ?? resolve17(ctx.cwd);
16484
16799
  }
16485
16800
  async function keyHome(ctx, label, data, key) {
16486
16801
  const model = new Model(ctx, plannedConfigDir(ctx));
@@ -16492,7 +16807,7 @@ async function keyHome(ctx, label, data, key) {
16492
16807
  ) : void 0;
16493
16808
  const owner = model.ownerOf(key, members, marked);
16494
16809
  if (owner !== void 0) {
16495
- const at2 = model.fileFor(owner, resolve16(ctx.base, label));
16810
+ const at2 = model.fileFor(owner, resolve17(ctx.base, label));
16496
16811
  if (owner.url || at2.abs === void 0) {
16497
16812
  return { kind: "url", collection: owner.collection, file: owner.file };
16498
16813
  }
@@ -16518,7 +16833,7 @@ function marksOf(ctx) {
16518
16833
  });
16519
16834
  }
16520
16835
  function entryFor(m, label, data, configDir, base, key) {
16521
- if (m.join === PATH_JOIN2) return toPosix3(relative13(configDir, resolve16(base, label)));
16836
+ if (m.join === PATH_JOIN2) return toPosix3(relative13(configDir, resolve17(base, label)));
16522
16837
  let raw = data[m.join];
16523
16838
  if (isEncryptedValue(raw)) {
16524
16839
  const k = key();
@@ -16536,7 +16851,7 @@ function entryFor(m, label, data, configDir, base, key) {
16536
16851
  return void 0;
16537
16852
  }
16538
16853
  async function relocationPages(ctx, request) {
16539
- const pages2 = new Set(request.files.map((label) => resolve16(ctx.base, label)));
16854
+ const pages2 = new Set(request.files.map((label) => resolve17(ctx.base, label)));
16540
16855
  const root = ctx.configDir ?? ctx.cwd;
16541
16856
  for (const collection of ctx.declaredCollections) {
16542
16857
  const perPage = collection.externalMetadata.some(
@@ -16552,7 +16867,7 @@ async function relocationPages(ctx, request) {
16552
16867
  ...ctx.config?.respectGitignore !== void 0 ? { configured: ctx.config.respectGitignore } : {}
16553
16868
  })
16554
16869
  });
16555
- for (const file of walked.files) pages2.add(resolve16(root, file));
16870
+ for (const file of walked.files) pages2.add(resolve17(root, file));
16556
16871
  }
16557
16872
  return [...pages2];
16558
16873
  }
@@ -16582,20 +16897,20 @@ async function planRelocation(ctx, request) {
16582
16897
  if (beyond) return void 0;
16583
16898
  throw new DocmetaError(`Unsupported file type "${extname3(label)}" for "${label}". Use --as to override.`);
16584
16899
  }
16585
- const absPath = resolve16(ctx.base, label);
16900
+ const absPath = resolve17(ctx.base, label);
16586
16901
  const elements = resolveElements(label, ctx.config, currentMembers(label));
16587
16902
  let content = "";
16588
16903
  let own2 = {};
16589
16904
  let unreadable2;
16590
16905
  if (beyond) {
16591
16906
  try {
16592
- content = await readFile9(absPath, "utf8");
16907
+ content = await readFile8(absPath, "utf8");
16593
16908
  own2 = extractor.extract(content, label, { elements }).data;
16594
16909
  } catch (err) {
16595
16910
  unreadable2 = (errorMessage(err).split("\n")[0] ?? "").replace(/:\s*$/, "");
16596
16911
  }
16597
16912
  } else {
16598
- content = await readFile9(absPath, "utf8");
16913
+ content = await readFile8(absPath, "utf8");
16599
16914
  try {
16600
16915
  own2 = extractor.extract(content, label, { elements }).data;
16601
16916
  } catch (err) {
@@ -16801,7 +17116,7 @@ async function planRelocation(ctx, request) {
16801
17116
  });
16802
17117
  const labels = dedupe2([
16803
17118
  ...request.files,
16804
- ...walked.files.map((f) => reported2(resolve16(configDir, f), ctx.base))
17119
+ ...walked.files.map((f) => reported2(resolve17(configDir, f), ctx.base))
16805
17120
  ]).filter((label) => model.members(label).includes(name2));
16806
17121
  for (const label of labels) {
16807
17122
  const page = await load(label, !runSet.has(label));
@@ -17036,7 +17351,7 @@ function assertNoStrandedEntries(m, index, pages2, configDir, fileOf) {
17036
17351
  }
17037
17352
  async function readManifestText(path, file, perPage) {
17038
17353
  try {
17039
- return await readFile9(path, "utf8");
17354
+ return await readFile8(path, "utf8");
17040
17355
  } catch (err) {
17041
17356
  if (perPage && isMissing(err)) return null;
17042
17357
  throw new DocmetaError(`Manifest ${file} could not be read: ${errorMessage(err)}`);
@@ -17066,7 +17381,7 @@ function keyLines2(text2, join12) {
17066
17381
  async function configTarget(ctx, configDir) {
17067
17382
  if (ctx.configPath !== void 0) {
17068
17383
  try {
17069
- const text2 = await readFile9(ctx.configPath, "utf8");
17384
+ const text2 = await readFile8(ctx.configPath, "utf8");
17070
17385
  return { path: ctx.configPath, text: text2, disk: text2 };
17071
17386
  } catch (err) {
17072
17387
  if (err.code !== "ENOENT") {
@@ -17077,10 +17392,10 @@ async function configTarget(ctx, configDir) {
17077
17392
  return { path: ctx.configPath, text: ctx.configFile?.text ?? null, disk: null };
17078
17393
  }
17079
17394
  }
17080
- for (const name2 of [...FAMILY_CONFIG_NAMES, ...MOOSE_CONFIG_NAMES]) {
17395
+ for (const name2 of FAMILY_CONFIG_NAMES) {
17081
17396
  const path = join9(configDir, name2);
17082
17397
  try {
17083
- const text2 = await readFile9(path, "utf8");
17398
+ const text2 = await readFile8(path, "utf8");
17084
17399
  return { path, text: text2, disk: text2 };
17085
17400
  } catch {
17086
17401
  }
@@ -17266,7 +17581,7 @@ async function applyRelocation(ctx, plan, io = {}) {
17266
17581
  for (const w of plan.writes) {
17267
17582
  let now;
17268
17583
  try {
17269
- now = await readFile9(w.path, "utf8");
17584
+ now = await readFile8(w.path, "utf8");
17270
17585
  } catch (err) {
17271
17586
  if (err.code !== "ENOENT") {
17272
17587
  throw new DocmetaError(`${label(w)} could not be read: ${errorMessage(err)}`);
@@ -17433,7 +17748,7 @@ function relocationOffers(plan) {
17433
17748
  }
17434
17749
 
17435
17750
  // src/meta/commands/validate.ts
17436
- import { resolve as resolve18, extname as extname4 } from "node:path";
17751
+ import { resolve as resolve19, extname as extname4 } from "node:path";
17437
17752
 
17438
17753
  // src/meta/core/checks.ts
17439
17754
  var CHECK_KEYWORD = "check";
@@ -17571,7 +17886,7 @@ async function runChecks(checks, entries, ctx = {}) {
17571
17886
  }
17572
17887
 
17573
17888
  // src/meta/core/derive/provenance-place.ts
17574
- import { isAbsolute as isAbsolute9, relative as relative14, resolve as resolve17, sep as sep9 } from "node:path";
17889
+ import { isAbsolute as isAbsolute9, relative as relative14, resolve as resolve18, sep as sep9 } from "node:path";
17575
17890
  var toPosix4 = (p) => p.split(sep9).join("/");
17576
17891
  function provenanceManifests(collections, configDir, base, field = PROVENANCE_FIELD) {
17577
17892
  const out = [];
@@ -17593,7 +17908,7 @@ function provenanceManifests(collections, configDir, base, field = PROVENANCE_FI
17593
17908
  });
17594
17909
  continue;
17595
17910
  }
17596
- const absPath = isAbsolute9(manifest.file) ? manifest.file : resolve17(configDir, manifest.file);
17911
+ const absPath = isAbsolute9(manifest.file) ? manifest.file : resolve18(configDir, manifest.file);
17597
17912
  const perPage = hasPagePlaceholder(manifest.file);
17598
17913
  out.push({
17599
17914
  collection: collection.name,
@@ -17627,7 +17942,7 @@ function provenancePlace(label, data, manifests, collections, configDir, base, m
17627
17942
  if (declared.implied?.url === true) throw new DocmetaError(urlRefusal(declared.collection, field));
17628
17943
  const manifest = declared.perPage ? ownManifest(declared, label, collections, configDir, base) : declared;
17629
17944
  if (manifest.join === PATH_JOIN2) {
17630
- const entry = toPosix4(relative14(configDir, resolve17(base, label)));
17945
+ const entry = toPosix4(relative14(configDir, resolve18(base, label)));
17631
17946
  return { manifest, absPath: manifest.absPath, entry, join: manifest.join };
17632
17947
  }
17633
17948
  const value = data[manifest.join];
@@ -17639,7 +17954,7 @@ function provenancePlace(label, data, manifests, collections, configDir, base, m
17639
17954
  return { manifest, absPath: manifest.absPath, entry: String(value), join: manifest.join };
17640
17955
  }
17641
17956
  function ownManifest(manifest, label, collections, configDir, base) {
17642
- const resolved = pageManifestPath(manifest.written, configDir, resolve17(base, label));
17957
+ const resolved = pageManifestPath(manifest.written, configDir, resolve18(base, label));
17643
17958
  if ("outside" in resolved) {
17644
17959
  const declared = collections.find((c) => c.name === manifest.collection)?.externalMetadata.find((m) => m.file === manifest.written);
17645
17960
  throw new DocmetaError(
@@ -17758,7 +18073,7 @@ async function runValidate(opts) {
17758
18073
  base,
17759
18074
  offline: opts.offline ?? config?.offline ?? false,
17760
18075
  // A `{page}` manifest (0058) is read for exactly the pages this run reads.
17761
- pages: files.map((file) => resolve18(base, file))
18076
+ pages: files.map((file) => resolve19(base, file))
17762
18077
  });
17763
18078
  const validator = new Validator(
17764
18079
  schemaLoadOptions({
@@ -17929,7 +18244,7 @@ async function runValidate(opts) {
17929
18244
  const owned = declaredCollections.length === 0 ? {} : stampManifests(label, extracted.data, declaredCollections, configDir ?? cwd, base, merged.marked, STAMPED_DATE_FIELDS);
17930
18245
  deriveInputs.push({
17931
18246
  label,
17932
- absPath: resolve18(base, label),
18247
+ absPath: resolve19(base, label),
17933
18248
  content,
17934
18249
  extracted,
17935
18250
  ...place !== void 0 ? { provenanceManifest: place } : {},
@@ -18069,7 +18384,7 @@ async function runValidate(opts) {
18069
18384
  await processOne(STDIN_LABEL, content, forcedExtractor.extensions[0] ?? "");
18070
18385
  }
18071
18386
  for (const file of files) {
18072
- const content = await readFile10(resolve18(base, file), "utf8");
18387
+ const content = await readFile9(resolve19(base, file), "utf8");
18073
18388
  await processOne(file, content, extname4(file));
18074
18389
  }
18075
18390
  if (unverified > 0) opts.onNotice?.(unverifiedWarning(unverified));
@@ -18215,8 +18530,8 @@ async function runValidate(opts) {
18215
18530
  }
18216
18531
 
18217
18532
  // src/meta/commands/get.ts
18218
- import { readFile as readFile11 } from "node:fs/promises";
18219
- import { resolve as resolve19, extname as extname5 } from "node:path";
18533
+ import { readFile as readFile10 } from "node:fs/promises";
18534
+ import { resolve as resolve20, extname as extname5 } from "node:path";
18220
18535
  async function runGet(opts) {
18221
18536
  const cwd = opts.cwd ?? process.cwd();
18222
18537
  if (opts.fields.length === 0) {
@@ -18277,7 +18592,7 @@ async function runGet(opts) {
18277
18592
  base,
18278
18593
  offline: opts.offline ?? config?.offline ?? false,
18279
18594
  // A `{page}` manifest (0058) is read for exactly the pages this run reads.
18280
- pages: files.map((file) => resolve19(base, file))
18595
+ pages: files.map((file) => resolve20(base, file))
18281
18596
  });
18282
18597
  const commands = commandsOf(config?.derive) ?? {};
18283
18598
  const deriving = opts.derived !== false;
@@ -18320,7 +18635,7 @@ async function runGet(opts) {
18320
18635
  const owned = declaredCollections.length === 0 ? {} : stampManifests(label, own2.data, declaredCollections, configDir ?? cwd, base, merged.marked, STAMPED_DATE_FIELDS);
18321
18636
  deriveInputs.push({
18322
18637
  label,
18323
- absPath: resolve19(base, label),
18638
+ absPath: resolve20(base, label),
18324
18639
  content,
18325
18640
  extracted: own2,
18326
18641
  ...place !== void 0 ? { provenanceManifest: place } : {},
@@ -18361,7 +18676,7 @@ async function runGet(opts) {
18361
18676
  await readOne(STDIN_LABEL, opts.stdinContent ?? "", forced.extensions[0] ?? "");
18362
18677
  }
18363
18678
  for (const file of files) {
18364
- const content = await readFile11(resolve19(base, file), "utf8");
18679
+ const content = await readFile10(resolve20(base, file), "utf8");
18365
18680
  await readOne(file, content, extname5(file));
18366
18681
  }
18367
18682
  if (deriving) {
@@ -18487,8 +18802,8 @@ function parseJsonPointer(pointer) {
18487
18802
 
18488
18803
  // src/meta/commands/schemas.ts
18489
18804
  import { existsSync as existsSync4 } from "node:fs";
18490
- import { mkdir as mkdir3, readFile as readFile12 } from "node:fs/promises";
18491
- 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";
18805
+ import { mkdir as mkdir3, readFile as readFile11 } from "node:fs/promises";
18806
+ import { dirname as dirname10, extname as extname6, isAbsolute as isAbsolute10, join as join10, relative as relative15, resolve as resolve21, sep as sep10 } from "node:path";
18492
18807
  import { parseDocument as parseDocument8, parse as parseYaml6, stringify as stringify4 } from "yaml";
18493
18808
  function getSchemasInfo(registered2) {
18494
18809
  return {
@@ -18586,9 +18901,9 @@ async function runVendorSchema(opts) {
18586
18901
  );
18587
18902
  }
18588
18903
  const loaded = await loadConfig(opts.configPath, cwd);
18589
- const configPath = loaded?.path ?? resolve20(cwd, DEFAULT_CONFIG_NAME);
18904
+ const configPath = loaded?.path ?? resolve21(cwd, DEFAULT_CONFIG_NAME);
18590
18905
  const configDir = loaded?.dir ?? cwd;
18591
- const absDir = resolve20(cwd, opts.dir ?? DEFAULT_VENDOR_DIR);
18906
+ const absDir = resolve21(cwd, opts.dir ?? DEFAULT_VENDOR_DIR);
18592
18907
  const absFile = join10(absDir, vendorFileName(url));
18593
18908
  await assertNotIgnored(absFile, absDir, cwd, opts.onNotice);
18594
18909
  const fromConfig = posixRelative(configDir, absFile);
@@ -18607,7 +18922,7 @@ async function runVendorSchema(opts) {
18607
18922
  }
18608
18923
  let unchanged = false;
18609
18924
  if (existsSync4(absFile)) {
18610
- const current = await readFile12(absFile);
18925
+ const current = await readFile11(absFile);
18611
18926
  unchanged = bytes.equals(current);
18612
18927
  if (!unchanged && !folded.replaced) {
18613
18928
  throw new DocmetaError(
@@ -18617,7 +18932,7 @@ async function runVendorSchema(opts) {
18617
18932
  }
18618
18933
  await mkdir3(absDir, { recursive: true });
18619
18934
  await writeFileAtomic(absFile, bytes);
18620
- const existing = loaded ? await readFile12(configPath, "utf8") : null;
18935
+ const existing = loaded ? await readFile11(configPath, "utf8") : null;
18621
18936
  const section = loaded ? loaded.section : META_SECTION;
18622
18937
  const text2 = renderConfig(existing, folded.entries, section);
18623
18938
  reparseConfig(text2, posixRelative(cwd, configPath) || DEFAULT_CONFIG_NAME, section);
@@ -18764,7 +19079,7 @@ async function runInferSchema(opts) {
18764
19079
  }
18765
19080
  let absOut;
18766
19081
  if (opts.out !== void 0) {
18767
- absOut = resolve20(cwd, opts.out);
19082
+ absOut = resolve21(cwd, opts.out);
18768
19083
  await assertNotIgnored(
18769
19084
  absOut,
18770
19085
  dirname10(absOut),
@@ -18810,7 +19125,7 @@ async function runInferSchema(opts) {
18810
19125
  configDir: configDir ?? cwd,
18811
19126
  base,
18812
19127
  offline: opts.offline ?? config?.offline ?? false,
18813
- pages: files.map((file) => resolve20(base, file))
19128
+ pages: files.map((file) => resolve21(base, file))
18814
19129
  });
18815
19130
  const stats = /* @__PURE__ */ new Map();
18816
19131
  const unreadable2 = [];
@@ -18869,7 +19184,7 @@ async function runInferSchema(opts) {
18869
19184
  await scanOne(STDIN_LABEL, opts.stdinContent ?? "", forced.extensions[0] ?? "");
18870
19185
  }
18871
19186
  for (const file of files) {
18872
- const content = await readFile12(resolve20(base, file), "utf8");
19187
+ const content = await readFile11(resolve21(base, file), "utf8");
18873
19188
  await scanOne(file, content, extname6(file));
18874
19189
  }
18875
19190
  for (const u of unreadable2) {
@@ -18930,8 +19245,8 @@ async function runInferSchema(opts) {
18930
19245
  // src/meta/commands/query.ts
18931
19246
  import { randomBytes as randomBytes2 } from "node:crypto";
18932
19247
  import { existsSync as existsSync5 } from "node:fs";
18933
- import { mkdir as mkdir4, open as open2, readFile as readFile13, rename as rename3, rm as rm3 } from "node:fs/promises";
18934
- import { dirname as dirname11, isAbsolute as isAbsolute11, resolve as resolve21, extname as extname7, sep as sep11 } from "node:path";
19248
+ import { mkdir as mkdir4, open as open2, readFile as readFile12, rename as rename3, rm as rm3 } from "node:fs/promises";
19249
+ import { dirname as dirname11, isAbsolute as isAbsolute11, resolve as resolve22, extname as extname7, sep as sep11 } from "node:path";
18935
19250
 
18936
19251
  // src/shared/prompt.ts
18937
19252
  import { createInterface } from "node:readline";
@@ -19190,7 +19505,7 @@ async function runQuery(opts) {
19190
19505
  base,
19191
19506
  offline: opts.offline ?? config?.offline ?? false,
19192
19507
  // A `{page}` manifest (0058) is read for exactly the pages this run reads.
19193
- pages: files.map((file) => resolve21(base, file))
19508
+ pages: files.map((file) => resolve22(base, file))
19194
19509
  });
19195
19510
  const configuredKey = lazyKey(configFile, opts.env);
19196
19511
  let ensuredKey;
@@ -19288,10 +19603,10 @@ async function runQuery(opts) {
19288
19603
  await readOne(STDIN_LABEL, opts.stdinContent ?? "", ext);
19289
19604
  }
19290
19605
  for (const file of files) {
19291
- const content = await readFile13(resolve21(base, file), "utf8");
19606
+ const content = await readFile12(resolve22(base, file), "utf8");
19292
19607
  await readOne(file, content, extname7(file));
19293
19608
  }
19294
- const db = opts.db === void 0 ? void 0 : { resolved: resolve21(cwd, opts.db), display: opts.db };
19609
+ const db = opts.db === void 0 ? void 0 : { resolved: resolve22(cwd, opts.db), display: opts.db };
19295
19610
  const run2 = await runSql(sql, entries, {
19296
19611
  target: db,
19297
19612
  params: boundParams,
@@ -19434,7 +19749,7 @@ async function runSql(sql, entries, ctx) {
19434
19749
  const owned = ctx.declaredCollections.length === 0 ? {} : stampManifests(e.label, e.own.data, ctx.declaredCollections, manifestRoot, ctx.base, e.marked, STAMPED_DATE_FIELDS);
19435
19750
  return {
19436
19751
  label: e.label,
19437
- absPath: resolve21(ctx.base, e.label),
19752
+ absPath: resolve22(ctx.base, e.label),
19438
19753
  content: e.content,
19439
19754
  extracted: e.own,
19440
19755
  ...place !== void 0 ? { provenanceManifest: place } : {},
@@ -19906,14 +20221,14 @@ function assertSchemaWriteWithin(ctx, abs) {
19906
20221
  );
19907
20222
  }
19908
20223
  function foldPath(p) {
19909
- const abs = resolve21(p);
20224
+ const abs = resolve22(p);
19910
20225
  return process.platform === "win32" ? abs.toLowerCase() : abs;
19911
20226
  }
19912
20227
  function filePinsByPath(ctx) {
19913
20228
  const byPath = /* @__PURE__ */ new Map();
19914
20229
  for (const [ref, pin] of collectSchemaPins(ctx.config)) {
19915
20230
  if (classifyRef(ref).kind !== "file") continue;
19916
- byPath.set(foldPath(resolve21(ctx.cwd, ref)), pin);
20231
+ byPath.set(foldPath(resolve22(ctx.cwd, ref)), pin);
19917
20232
  }
19918
20233
  return byPath;
19919
20234
  }
@@ -19942,13 +20257,13 @@ async function loadSetMembers(refs, ctx) {
19942
20257
  });
19943
20258
  continue;
19944
20259
  }
19945
- const abs = registered2?.path ?? resolve21(ctx.cwd, ref);
20260
+ const abs = registered2?.path ?? resolve22(ctx.cwd, ref);
19946
20261
  const canon = foldPath(abs);
19947
20262
  if (seenFiles.has(canon)) continue;
19948
20263
  seenFiles.add(canon);
19949
20264
  let text2;
19950
20265
  try {
19951
- text2 = await readFile13(abs, "utf8");
20266
+ text2 = await readFile12(abs, "utf8");
19952
20267
  } catch {
19953
20268
  throw new DocmetaError(
19954
20269
  `Schema file not found: "${ref}" (looked at ${abs}).`
@@ -20149,7 +20464,7 @@ async function planSchemaMutation(ops, entries, ctx) {
20149
20464
  const [, name2, ver] = target.builtinId.split(":");
20150
20465
  const safe = (s) => s.replace(/[^A-Za-z0-9._-]/g, "-");
20151
20466
  const forkName = `${safe(name2 ?? "schema")}-${safe(ver ?? "0")}.local.json`;
20152
- schemaAbs = resolve21(ctx.configDir ?? ctx.cwd, "schemas", forkName);
20467
+ schemaAbs = resolve22(ctx.configDir ?? ctx.cwd, "schemas", forkName);
20153
20468
  assertSchemaWriteWithin(ctx, schemaAbs);
20154
20469
  if (existsSync5(schemaAbs)) {
20155
20470
  throw new DocmetaError(
@@ -20305,7 +20620,7 @@ async function planConfigEdit(ctx, edit, runRefs) {
20305
20620
  if (configPath === void 0 || configDir === void 0) {
20306
20621
  return { changes: [], repointed: false };
20307
20622
  }
20308
- const text2 = await readFile13(configPath, "utf8");
20623
+ const text2 = await readFile12(configPath, "utf8");
20309
20624
  const doc = parseDocument10(stripBom2(text2));
20310
20625
  const display = displayPath(ctx, configPath);
20311
20626
  const rawRefOf = (item) => {
@@ -20316,7 +20631,7 @@ async function planConfigEdit(ctx, edit, runRefs) {
20316
20631
  }
20317
20632
  return void 0;
20318
20633
  };
20319
- const rebaseRaw = (raw) => classifyRef(raw).kind === "file" && !isAbsolute11(raw) ? resolve21(configDir, raw) : raw;
20634
+ const rebaseRaw = (raw) => classifyRef(raw).kind === "file" && !isAbsolute11(raw) ? resolve22(configDir, raw) : raw;
20320
20635
  const wantSet = runRefs.map(rebaseRaw).sort().join("\n");
20321
20636
  const seqResolvesToRunSet = (node) => {
20322
20637
  if (!isSeq5(node)) return false;
@@ -20361,7 +20676,7 @@ async function planConfigEdit(ctx, edit, runRefs) {
20361
20676
  if (!isMap10(item)) continue;
20362
20677
  const raw = rawRefOf(item);
20363
20678
  if (raw === void 0 || classifyRef(raw).kind !== "file") continue;
20364
- const abs = isAbsolute11(raw) ? raw : resolve21(configDir, raw);
20679
+ const abs = isAbsolute11(raw) ? raw : resolve22(configDir, raw);
20365
20680
  if (foldPath(abs) !== wantPath) continue;
20366
20681
  pinFrom = item.get("integrity");
20367
20682
  item.set("integrity", edit.integrity.value);
@@ -20590,7 +20905,7 @@ function buildChanges(diff, entries, sentinel, ctx, renameHints = [], booleanAdd
20590
20905
  `"${file}": no writable format for that extension.`
20591
20906
  );
20592
20907
  }
20593
- if (existsSync5(resolve21(ctx.base, file))) {
20908
+ if (existsSync5(resolve22(ctx.base, file))) {
20594
20909
  throw new DocmetaError(
20595
20910
  `"${file}" already exists; INSERT creates new files only.`
20596
20911
  );
@@ -20609,7 +20924,7 @@ function buildChanges(diff, entries, sentinel, ctx, renameHints = [], booleanAdd
20609
20924
  `"${from}" -> "${to}": a rename may not change the extension.`
20610
20925
  );
20611
20926
  }
20612
- if (existsSync5(resolve21(ctx.base, to))) {
20927
+ if (existsSync5(resolve22(ctx.base, to))) {
20613
20928
  throw new DocmetaError(
20614
20929
  `"${to}" already exists; the rename would overwrite it.`
20615
20930
  );
@@ -20643,9 +20958,9 @@ function validateNewPath(p, base) {
20643
20958
  if (p.split(/[\\/]/).some((segment) => segment === "..")) {
20644
20959
  refuse("it traverses upward");
20645
20960
  }
20646
- const abs = resolve21(base, p);
20647
- if (abs === resolve21(base)) refuse("it points at the corpus root, not a file");
20648
- if (!abs.startsWith(resolve21(base) + sep11)) refuse("it escapes the corpus");
20961
+ const abs = resolve22(base, p);
20962
+ if (abs === resolve22(base)) refuse("it points at the corpus root, not a file");
20963
+ if (!abs.startsWith(resolve22(base) + sep11)) refuse("it escapes the corpus");
20649
20964
  }
20650
20965
  function refuseJoinFieldChanges(changes, entries, ctx) {
20651
20966
  const index = ctx.externalMetadata;
@@ -20720,7 +21035,7 @@ function runPages(changes, entries, base) {
20720
21035
  const labels = new Set(entries.map((e) => e.label));
20721
21036
  for (const c of changes) if (!("schema" in c) && !("config" in c)) labels.add(c.file);
20722
21037
  labels.delete(STDIN_LABEL);
20723
- return [...labels].map((label) => resolve21(base, label));
21038
+ return [...labels].map((label) => resolve22(base, label));
20724
21039
  }
20725
21040
  async function planManifestEdits(changes, entries, ctx, renameHints) {
20726
21041
  for (const c of changes) {
@@ -20772,7 +21087,7 @@ async function planManifestEdits(changes, entries, ctx, renameHints) {
20772
21087
  let text2;
20773
21088
  let expected;
20774
21089
  try {
20775
- text2 = await readFile13(home.absPath, "utf8");
21090
+ text2 = await readFile12(home.absPath, "utf8");
20776
21091
  expected = text2;
20777
21092
  } catch (err) {
20778
21093
  if (!home.perPage || !isMissing(err)) {
@@ -20808,7 +21123,7 @@ async function planManifestEdits(changes, entries, ctx, renameHints) {
20808
21123
  );
20809
21124
  const index = await leftOut;
20810
21125
  if (index === null) return void 0;
20811
- const abs = resolve21(ctx.base, file);
21126
+ const abs = resolve22(ctx.base, file);
20812
21127
  const named = index.entries.find(
20813
21128
  (e) => e.join === PATH_JOIN2 && e.abs === abs && excluded.includes(e.collection)
20814
21129
  );
@@ -20855,7 +21170,7 @@ async function planManifestEdits(changes, entries, ctx, renameHints) {
20855
21170
  continue;
20856
21171
  }
20857
21172
  if ("renamed" in c) {
20858
- const supplied = ctx.externalMetadata?.byPath.get(resolve21(ctx.base, c.file));
21173
+ const supplied = ctx.externalMetadata?.byPath.get(resolve22(ctx.base, c.file));
20859
21174
  const seen = /* @__PURE__ */ new Set();
20860
21175
  for (const [key, sv] of supplied ?? []) {
20861
21176
  if (seen.has(sv.file) || !ctx.memberships(c.file).includes(sv.collection)) continue;
@@ -21163,14 +21478,14 @@ async function applyChanges(changes, entries, ctx, schemaPlan, manifestWrites =
21163
21478
  const pendingWrites = [];
21164
21479
  const pendingRenames = [];
21165
21480
  for (const [label, ops] of grouped) {
21166
- const path = resolve21(ctx.base, label);
21481
+ const path = resolve22(ctx.base, label);
21167
21482
  if (ops.renamedTo !== void 0) {
21168
21483
  if (!existsSync5(path)) {
21169
21484
  throw new DocmetaError(
21170
21485
  `"${label}" is gone from disk since it was read; re-run the query.`
21171
21486
  );
21172
21487
  }
21173
- const toDisk = resolve21(ctx.base, ops.renamedTo);
21488
+ const toDisk = resolve22(ctx.base, ops.renamedTo);
21174
21489
  if (existsSync5(toDisk)) {
21175
21490
  throw new DocmetaError(
21176
21491
  `"${ops.renamedTo}" appeared on disk since the plan; re-run the query.`
@@ -21208,7 +21523,7 @@ async function applyChanges(changes, entries, ctx, schemaPlan, manifestWrites =
21208
21523
  }
21209
21524
  const entry = byLabel.get(label);
21210
21525
  if (!entry) throw new DocmetaError(`No loaded entry for "${label}".`);
21211
- const content = await readFile13(path, "utf8");
21526
+ const content = await readFile12(path, "utf8");
21212
21527
  const members = ctx.memberships(label);
21213
21528
  const current = (await mergeWithMarks(
21214
21529
  label,
@@ -21280,7 +21595,7 @@ async function applyChanges(changes, entries, ctx, schemaPlan, manifestWrites =
21280
21595
  }
21281
21596
  let now;
21282
21597
  try {
21283
- now = await readFile13(w.path, "utf8");
21598
+ now = await readFile12(w.path, "utf8");
21284
21599
  } catch {
21285
21600
  now = void 0;
21286
21601
  }
@@ -21293,7 +21608,7 @@ async function applyChanges(changes, entries, ctx, schemaPlan, manifestWrites =
21293
21608
  for (const m of manifestWrites) {
21294
21609
  let now;
21295
21610
  try {
21296
- now = await readFile13(m.path, "utf8");
21611
+ now = await readFile12(m.path, "utf8");
21297
21612
  } catch {
21298
21613
  now = null;
21299
21614
  }
@@ -21426,19 +21741,17 @@ function skipExpression(sql, from) {
21426
21741
  }
21427
21742
 
21428
21743
  // src/meta/commands/fill.ts
21429
- import { readFile as readFile16 } from "node:fs/promises";
21430
- import { resolve as resolve23, extname as extname9, join as join11 } from "node:path";
21744
+ import { readFile as readFile15 } from "node:fs/promises";
21745
+ import { resolve as resolve24, extname as extname9, join as join11 } from "node:path";
21431
21746
  import {
21432
21747
  completeValidatedJSON,
21433
21748
  makeProvider,
21434
- resolveProviderIdentityAsync,
21435
- DEFAULT_MODELS,
21436
21749
  JsonCache,
21437
21750
  buildCacheKey,
21438
21751
  sha256,
21439
21752
  pricingFor,
21440
21753
  costOfUsage,
21441
- InferenceError
21754
+ InferenceError as InferenceError2
21442
21755
  } from "@hawkeyexl/inference";
21443
21756
 
21444
21757
  // src/meta/commands/fill-projection.ts
@@ -21848,8 +22161,8 @@ function single(alternatives2) {
21848
22161
  }
21849
22162
 
21850
22163
  // src/meta/commands/fill-hints.ts
21851
- import { readFile as readFile14 } from "node:fs/promises";
21852
- import { extname as extname8, resolve as resolve22 } from "node:path";
22164
+ import { readFile as readFile13 } from "node:fs/promises";
22165
+ import { extname as extname8, resolve as resolve23 } from "node:path";
21853
22166
 
21854
22167
  // src/shared/reference-fields.ts
21855
22168
  var CONCEPT_FIELDS = [["concepts"], ["graph", "concepts"]];
@@ -21935,7 +22248,7 @@ function pageIdReader(opts) {
21935
22248
  return held;
21936
22249
  };
21937
22250
  return async (file, names) => {
21938
- const self = resolve22(file);
22251
+ const self = resolve23(file);
21939
22252
  const ids = /* @__PURE__ */ new Set();
21940
22253
  for (const name2 of names) {
21941
22254
  const collection = opts.collections.find((c) => c.name === name2);
@@ -21959,10 +22272,10 @@ async function readIds(collection, opts) {
21959
22272
  if (!isMember(collection, file)) continue;
21960
22273
  const extractor = extractorForExtension(extname8(file));
21961
22274
  if (extractor === void 0) continue;
21962
- const path = resolve22(opts.configDir, file);
22275
+ const path = resolve23(opts.configDir, file);
21963
22276
  let id;
21964
22277
  try {
21965
- id = extractor.extract(await readFile14(path, "utf8"), file).data.id;
22278
+ id = extractor.extract(await readFile13(path, "utf8"), file).data.id;
21966
22279
  } catch {
21967
22280
  continue;
21968
22281
  }
@@ -21981,7 +22294,7 @@ function termLabelSource() {
21981
22294
  }
21982
22295
 
21983
22296
  // src/meta/core/manifest-writes.ts
21984
- import { readFile as readFile15 } from "node:fs/promises";
22297
+ import { readFile as readFile14 } from "node:fs/promises";
21985
22298
  var manifestCodec = {
21986
22299
  apply(text2, op) {
21987
22300
  const where = { entry: op.entry, key: op.key, join: op.join, file: op.file };
@@ -21997,7 +22310,7 @@ function applyManifestOp(text2, op) {
21997
22310
  async function holdManifestFile(path, file, perPage = false) {
21998
22311
  let before;
21999
22312
  try {
22000
- before = await readFile15(path, "utf8");
22313
+ before = await readFile14(path, "utf8");
22001
22314
  } catch (err) {
22002
22315
  if (perPage && isMissing(err)) return heldManifest(path, file, "", true);
22003
22316
  throw new DocmetaError(`Manifest ${file} could not be read: ${errorMessage(err)}`);
@@ -22014,7 +22327,7 @@ async function settleManifest(held, io = {}) {
22014
22327
  codec: manifestCodec,
22015
22328
  // A per-page manifest (proposal 0058) may be the first file in its directory.
22016
22329
  write: io.write ?? ((at2, text2) => writeFileAtomic(at2, text2, { createParents: true })),
22017
- read: io.read ?? ((at2) => readFile15(at2, "utf8")),
22330
+ read: io.read ?? ((at2) => readFile14(at2, "utf8")),
22018
22331
  toError: (message) => new DocmetaError(message)
22019
22332
  });
22020
22333
  rebaseline(held);
@@ -22171,12 +22484,56 @@ function buildUserPrompt(params, before = []) {
22171
22484
  ].join("\n");
22172
22485
  }
22173
22486
 
22487
+ // src/meta/core/meta-provenance.ts
22488
+ function isPlainRecord(value) {
22489
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22490
+ }
22491
+ function mergeMetaProvenance(held, model, key, proposed) {
22492
+ if (held !== void 0 && !Array.isArray(held)) return void 0;
22493
+ const list2 = held === void 0 ? [] : [...held];
22494
+ const at2 = list2.findIndex((e) => isPlainRecord(e) && e["generated-by"] === model);
22495
+ const prior = list2[at2];
22496
+ const kept = isPlainRecord(prior) ? prior : {};
22497
+ const priorNames = kept[key];
22498
+ const names = Array.isArray(priorNames) ? priorNames.filter((p) => typeof p === "string") : [];
22499
+ const confidence = {};
22500
+ if (isPlainRecord(kept.confidence)) {
22501
+ for (const [k, v] of Object.entries(kept.confidence)) {
22502
+ if (typeof v === "number") confidence[k] = v;
22503
+ }
22504
+ }
22505
+ for (const p of proposed) {
22506
+ if (!names.includes(p.name)) names.push(p.name);
22507
+ confidence[p.name] = p.confidence;
22508
+ }
22509
+ const entry = {
22510
+ ...kept,
22511
+ "generated-by": model,
22512
+ [key]: names,
22513
+ confidence
22514
+ };
22515
+ if (at2 === -1) list2.push(entry);
22516
+ else list2[at2] = entry;
22517
+ return { list: list2, entry, names };
22518
+ }
22519
+ function metaProvenanceEntries(value) {
22520
+ if (!Array.isArray(value)) return [];
22521
+ const strings = (list2) => Array.isArray(list2) ? list2.filter((s) => typeof s === "string") : [];
22522
+ const out = [];
22523
+ for (const item of value) {
22524
+ if (!isPlainRecord(item)) continue;
22525
+ const machine = item["generated-by"];
22526
+ if (typeof machine !== "string" || machine === "") continue;
22527
+ out.push({ "generated-by": machine, fields: strings(item.fields), evals: strings(item.evals) });
22528
+ }
22529
+ return out;
22530
+ }
22531
+
22174
22532
  // src/meta/commands/fill.ts
22175
22533
  var META_PROVENANCE_KEY = "meta-provenance";
22176
22534
  var PROVENANCE_KEYS = /* @__PURE__ */ new Set(["provenance", META_PROVENANCE_KEY]);
22177
22535
  var DEFAULT_THRESHOLD = 0.7;
22178
22536
  var DEFAULT_CONCURRENCY = 4;
22179
- var DEFAULT_PROVIDER = "auto";
22180
22537
  var CACHE_DIR = ".manni/meta/cache";
22181
22538
  function looksLikeOverflow(message) {
22182
22539
  return /context|too long|too large|token limit|maximum.*tokens|exceeds/i.test(
@@ -22257,13 +22614,19 @@ async function runFill(opts) {
22257
22614
  const dryRun = Boolean(opts.dryRun);
22258
22615
  const only = opts.fields != null ? new Set(opts.fields) : void 0;
22259
22616
  const managed = new Set(managedFields(config?.derive));
22260
- const requestedProvider = opts.provider ?? config?.fill?.provider ?? DEFAULT_PROVIDER;
22261
- const providerName = opts.local === true && requestedProvider === "auto" ? "llama-cpp" : requestedProvider;
22262
- const model = opts.model ?? config?.fill?.model;
22263
- const spec = { provider: providerName, model: model ?? null };
22264
- assertKnownProvider(providerName);
22265
- assertModelHasProvider(providerName, model);
22266
- if (opts.local === true) assertLocalProvider(providerName);
22617
+ const flags = { provider: opts.provider, model: opts.model, local: opts.local };
22618
+ assertLocalFlag(flags, toDocmetaError);
22619
+ const family = configFile?.providers ?? {};
22620
+ const selection = selectProvider(flags, [
22621
+ { provider: config?.fill?.provider, model: config?.fill?.model, origin: "fill.provider" },
22622
+ { provider: family.provider, model: family.model, origin: "providers.provider" }
22623
+ ]);
22624
+ const { provider: providerName, model } = selection;
22625
+ assertKnownProvider2(providerName);
22626
+ assertModelHasProvider2(providerName, model);
22627
+ const replaced = localNotice(selection);
22628
+ if (replaced !== void 0) opts.onNotice?.(replaced);
22629
+ const spec = providerSpecFor(family, { provider: providerName, model: model ?? null });
22267
22630
  const fileInputs = inputs.filter((i) => i !== STDIN_TOKEN);
22268
22631
  const allowEmpty = opts.allowEmpty ?? config?.allowEmpty;
22269
22632
  const fillExts = opts.exts ?? forcedExtractor?.extensions;
@@ -22299,14 +22662,9 @@ async function runFill(opts) {
22299
22662
  identity = { provider: injected.provider(), model: injected.modelName() };
22300
22663
  construct = () => injected;
22301
22664
  } else {
22302
- const resolved = await resolveIdentity(spec);
22303
- if (opts.local === true) assertLocalProvider(resolved.provider);
22665
+ const resolved = await resolveIdentity2(spec);
22304
22666
  identity = resolved;
22305
- const concrete = {
22306
- ...spec,
22307
- provider: resolved.provider,
22308
- model: resolved.model
22309
- };
22667
+ const concrete = providerSpecFor(family, resolved);
22310
22668
  construct = () => makeProvider(concrete);
22311
22669
  }
22312
22670
  let provider;
@@ -22316,7 +22674,7 @@ async function runFill(opts) {
22316
22674
  provider = construct();
22317
22675
  } catch (err) {
22318
22676
  throw new DocmetaError(
22319
- err instanceof InferenceError ? err.message : `Could not construct the "${providerName}" provider: ${errorMessage(err)}`
22677
+ err instanceof InferenceError2 ? err.message : `Could not construct the "${providerName}" provider: ${errorMessage(err)}`
22320
22678
  );
22321
22679
  }
22322
22680
  }
@@ -22350,7 +22708,7 @@ async function runFill(opts) {
22350
22708
  base,
22351
22709
  offline: opts.offline ?? config?.offline ?? false,
22352
22710
  // A `{page}` manifest (0058) is read for exactly the pages this run fills.
22353
- pages: files.map((file) => resolve23(base, file))
22711
+ pages: files.map((file) => resolve24(base, file))
22354
22712
  });
22355
22713
  const mergeMembersFor = (label) => memberOf(runConfig.declaredCollections, configDir ?? cwd, base, label);
22356
22714
  let externalMetadata = await loadManifests();
@@ -22383,17 +22741,17 @@ async function runFill(opts) {
22383
22741
  const keys = new Set(candidates.map((c) => c.key));
22384
22742
  const wants = (fields) => fields.some(([key]) => key !== void 0 && keys.has(key));
22385
22743
  const termIndex = wants(TERM_FIELDS) ? await (termbase ??= readTermbase()) : void 0;
22386
- const pages2 = wants(PAGE_FIELDS) ? await pageIds(resolve23(base, label), mergeMembersFor(label)) : [];
22744
+ const pages2 = wants(PAGE_FIELDS) ? await pageIds(resolve24(base, label), mergeMembersFor(label)) : [];
22387
22745
  const hints = fieldHints(candidates, {
22388
22746
  terms: termIndex === void 0 ? void 0 : offered(termIndex.labels),
22389
22747
  pages: offered(pages2)
22390
22748
  });
22391
22749
  return { hints, termIndex };
22392
22750
  };
22393
- const noticed = /* @__PURE__ */ new Set();
22394
- const noticeOnce = opts.onNotice === void 0 ? void 0 : (message) => {
22395
- if (noticed.has(message)) return;
22396
- noticed.add(message);
22751
+ const noticed2 = /* @__PURE__ */ new Set();
22752
+ const noticeOnce2 = opts.onNotice === void 0 ? void 0 : (message) => {
22753
+ if (noticed2.has(message)) return;
22754
+ noticed2.add(message);
22397
22755
  opts.onNotice?.(message);
22398
22756
  };
22399
22757
  const ctx = relocationContext(runConfig, {
@@ -22405,7 +22763,7 @@ async function runFill(opts) {
22405
22763
  ...opts.as !== void 0 ? { as: opts.as } : {},
22406
22764
  ...opts.respectGitignore !== void 0 ? { respectGitignore: opts.respectGitignore } : {},
22407
22765
  ...opts.env !== void 0 ? { env: opts.env } : {},
22408
- ...noticeOnce !== void 0 ? { onNotice: noticeOnce } : {}
22766
+ ...noticeOnce2 !== void 0 ? { onNotice: noticeOnce2 } : {}
22409
22767
  });
22410
22768
  const heldManifests = /* @__PURE__ */ new Map();
22411
22769
  const holding = /* @__PURE__ */ new Map();
@@ -22423,7 +22781,7 @@ async function runFill(opts) {
22423
22781
  };
22424
22782
  const restorePage = async (label, before) => {
22425
22783
  try {
22426
- await writeFileAtomic(resolve23(base, label), before);
22784
+ await writeFileAtomic(resolve24(base, label), before);
22427
22785
  return `${label} was restored.`;
22428
22786
  } catch (err) {
22429
22787
  return `${label} could not be restored: ${errorMessage(err)}. Restore it from version control.`;
@@ -22517,7 +22875,7 @@ async function runFill(opts) {
22517
22875
  // gets written — if anything, a stronger reason to guard it.
22518
22876
  fileBase: cwd,
22519
22877
  trustRoot,
22520
- onNotice: noticeOnce
22878
+ onNotice: noticeOnce2
22521
22879
  });
22522
22880
  } catch (err) {
22523
22881
  return errorResult(label, extractor.name, errorMessage(err));
@@ -22573,7 +22931,7 @@ async function runFill(opts) {
22573
22931
  }));
22574
22932
  const candidates = proposed.filter((c) => !managedHere.has(c.key));
22575
22933
  const destinations = /* @__PURE__ */ new Map();
22576
- const flags = [];
22934
+ const flags2 = [];
22577
22935
  let metaHome;
22578
22936
  if (label !== "<stdin>" && candidates.length > 0) {
22579
22937
  const located = { ...view.data };
@@ -22603,7 +22961,7 @@ async function runFill(opts) {
22603
22961
  if (!meta) destinations.set(key, { ...home, entry: home.entry });
22604
22962
  continue;
22605
22963
  }
22606
- if (prefs.get(key)?.location === "external") flags.push({ label, key, home: home.home });
22964
+ if (prefs.get(key)?.location === "external") flags2.push({ label, key, home: home.home });
22607
22965
  }
22608
22966
  }
22609
22967
  const inPlace = view.plain.flatMap((pointer) => {
@@ -22657,7 +23015,7 @@ async function runFill(opts) {
22657
23015
  sealedCandidates,
22658
23016
  destinations,
22659
23017
  metaHome,
22660
- flags
23018
+ flags: flags2
22661
23019
  };
22662
23020
  };
22663
23021
  const finishOne = async (ready) => {
@@ -22957,7 +23315,12 @@ async function runFill(opts) {
22957
23315
  if (writable.length > 0) {
22958
23316
  const metaManifest = metaHome?.kind === "manifest" ? metaHome : void 0;
22959
23317
  const metaEntry = metaManifest?.entry;
22960
- const mergedProvenance = metaHome?.kind === "url" || metaManifest !== void 0 && metaEntry === void 0 ? void 0 : mergeMetaProvenance(extracted.data[META_PROVENANCE_KEY], identity.model, writable);
23318
+ const mergedProvenance = metaHome?.kind === "url" || metaManifest !== void 0 && metaEntry === void 0 ? void 0 : mergeMetaProvenance(
23319
+ extracted.data[META_PROVENANCE_KEY],
23320
+ identity.model,
23321
+ "fields",
23322
+ writable.map((f) => ({ name: f.field, confidence: f.confidence }))
23323
+ );
22961
23324
  if (metaHome?.kind === "url") {
22962
23325
  metaProvenance = { written: false, skipReason: "manifest-owned", manifest: metaHome.file };
22963
23326
  } else if (metaManifest !== void 0 && metaEntry === void 0) {
@@ -22972,12 +23335,12 @@ async function runFill(opts) {
22972
23335
  metaWrite = { value: mergedProvenance.list, home: { ...metaManifest, entry: metaEntry } };
22973
23336
  metaProvenance = {
22974
23337
  written: true,
22975
- entry: mergedProvenance.entry,
23338
+ entry: { ...mergedProvenance.entry, fields: mergedProvenance.names },
22976
23339
  destination: metaManifest.file
22977
23340
  };
22978
23341
  } else {
22979
23342
  patch[META_PROVENANCE_KEY] = mergedProvenance.list;
22980
- metaProvenance = { written: true, entry: mergedProvenance.entry };
23343
+ metaProvenance = { written: true, entry: { ...mergedProvenance.entry, fields: mergedProvenance.names } };
22981
23344
  }
22982
23345
  }
22983
23346
  const entryWrites = [];
@@ -23046,7 +23409,7 @@ async function runFill(opts) {
23046
23409
  const changed = next !== content || manifestChanged;
23047
23410
  const pageWritten = next !== content && !dryRun && label !== "<stdin>";
23048
23411
  if (pageWritten) {
23049
- await writeFileAtomic(resolve23(base, label), next);
23412
+ await writeFileAtomic(resolve24(base, label), next);
23050
23413
  }
23051
23414
  if (manifestChanged) {
23052
23415
  const perManifest = /* @__PURE__ */ new Map();
@@ -23078,7 +23441,7 @@ async function runFill(opts) {
23078
23441
  const prepareAll = async () => ({
23079
23442
  stdin: usingStdin ? await prepareOne("<stdin>", opts.stdinContent ?? "", forcedExtractor?.extensions[0] ?? "") : void 0,
23080
23443
  files: await mapConcurrent(files, concurrency, async (file) => {
23081
- const content = await readFile16(resolve23(base, file), "utf8");
23444
+ const content = await readFile15(resolve24(base, file), "utf8");
23082
23445
  return prepareOne(file, content, extname9(file));
23083
23446
  })
23084
23447
  });
@@ -23131,31 +23494,6 @@ function topKey(instancePath) {
23131
23494
  return seg.replace(/~1/g, "/").replace(/~0/g, "~");
23132
23495
  }
23133
23496
  var keyOf = (f) => topKey(f.field);
23134
- function isPlainRecord(value) {
23135
- return typeof value === "object" && value !== null && !Array.isArray(value);
23136
- }
23137
- function mergeMetaProvenance(held, model, written) {
23138
- if (held !== void 0 && !Array.isArray(held)) return void 0;
23139
- const list2 = held === void 0 ? [] : [...held];
23140
- const at2 = list2.findIndex((e) => isPlainRecord(e) && e["generated-by"] === model);
23141
- const prior = list2[at2];
23142
- const kept = isPlainRecord(prior) ? prior : {};
23143
- const fields = Array.isArray(kept.fields) ? kept.fields.filter((p) => typeof p === "string") : [];
23144
- const confidence = {};
23145
- if (isPlainRecord(kept.confidence)) {
23146
- for (const [k, v] of Object.entries(kept.confidence)) {
23147
- if (typeof v === "number") confidence[k] = v;
23148
- }
23149
- }
23150
- for (const f of written) {
23151
- if (!fields.includes(f.field)) fields.push(f.field);
23152
- confidence[f.field] = f.confidence;
23153
- }
23154
- const entry = { ...kept, "generated-by": model, fields, confidence };
23155
- if (at2 === -1) list2.push(entry);
23156
- else list2[at2] = entry;
23157
- return { list: list2, entry };
23158
- }
23159
23497
  async function addsFindings(validate, without, list2) {
23160
23498
  const identity = (e) => JSON.stringify([e.schema, e.instancePath, e.keyword, e.subject ?? null, e.message]);
23161
23499
  const before = /* @__PURE__ */ new Map();
@@ -23468,34 +23806,15 @@ function requireNumber(value, name2, bounds) {
23468
23806
  }
23469
23807
  return value;
23470
23808
  }
23471
- var PROVIDERS = /* @__PURE__ */ new Set([...Object.keys(DEFAULT_MODELS), "auto"]);
23472
- function assertLocalProvider(name2) {
23473
- if (name2 === "llama-cpp" || name2 === "mock") return;
23474
- if (name2 === "auto") return;
23475
- throw new DocmetaError(
23476
- 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.`
23477
- );
23478
- }
23479
- function assertKnownProvider(name2) {
23480
- if (PROVIDERS.has(name2)) return;
23481
- throw new DocmetaError(
23482
- `Unknown provider "${name2}". Available: ${[...PROVIDERS].join(", ")}.`
23483
- );
23809
+ var toDocmetaError = (message) => new DocmetaError(message);
23810
+ function assertKnownProvider2(name2) {
23811
+ assertKnownProvider(name2, toDocmetaError);
23484
23812
  }
23485
- function assertModelHasProvider(name2, model) {
23486
- if (name2 !== "auto" || model == null) return;
23487
- throw new DocmetaError(
23488
- `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.`
23489
- );
23813
+ function assertModelHasProvider2(name2, model) {
23814
+ assertModelHasProvider(name2, model, "fill.provider", toDocmetaError);
23490
23815
  }
23491
- async function resolveIdentity(spec) {
23492
- try {
23493
- return await resolveProviderIdentityAsync(spec);
23494
- } catch (err) {
23495
- throw new DocmetaError(
23496
- err instanceof InferenceError ? err.message : `Could not resolve provider "${spec.provider}": ${errorMessage(err)}`
23497
- );
23498
- }
23816
+ function resolveIdentity2(spec) {
23817
+ return resolveIdentity(spec, toDocmetaError);
23499
23818
  }
23500
23819
  function summarize(results, costUsd, cached) {
23501
23820
  let written = 0;
@@ -23540,8 +23859,8 @@ async function mapConcurrent(items2, limit, fn) {
23540
23859
  }
23541
23860
 
23542
23861
  // src/meta/commands/derive.ts
23543
- import { readFile as readFile17 } from "node:fs/promises";
23544
- import { extname as extname10, relative as relative16, resolve as resolve24, sep as sep12 } from "node:path";
23862
+ import { readFile as readFile16 } from "node:fs/promises";
23863
+ import { extname as extname10, relative as relative16, resolve as resolve25, sep as sep12 } from "node:path";
23545
23864
  var GENERATED_BY_ENV = "MANNI_GENERATED_BY";
23546
23865
  var SOURCE_HINT = "narrow --sources or --fields";
23547
23866
  async function runDerive(opts) {
@@ -23659,7 +23978,7 @@ async function runDerive(opts) {
23659
23978
  const files = fromCollections ? retainMembers(walked, membersFor) : walked;
23660
23979
  const rangesByPage = /* @__PURE__ */ new Map();
23661
23980
  for (const t of targets) {
23662
- const label = relative16(base, resolve24(base, t.page)).split(sep12).join("/");
23981
+ const label = relative16(base, resolve25(base, t.page)).split(sep12).join("/");
23663
23982
  if (!files.includes(label)) {
23664
23983
  throw new DocmetaError(
23665
23984
  `${t.target} does not name one file that derive reads; a range names lines of one file.`
@@ -23684,7 +24003,7 @@ async function runDerive(opts) {
23684
24003
  const loaded2 = /* @__PURE__ */ new Map();
23685
24004
  const errors2 = /* @__PURE__ */ new Map();
23686
24005
  for (const label of files) {
23687
- const absPath = resolve24(base, label);
24006
+ const absPath = resolve25(base, label);
23688
24007
  const extractor = forcedExtractor ?? extractorForExtension(extname10(label));
23689
24008
  if (!extractor) {
23690
24009
  errors2.set(label, {
@@ -23693,7 +24012,7 @@ async function runDerive(opts) {
23693
24012
  });
23694
24013
  continue;
23695
24014
  }
23696
- const content = await readFile17(absPath, "utf8");
24015
+ const content = await readFile16(absPath, "utf8");
23697
24016
  const elements = resolveElements(label, config, membersFor(label));
23698
24017
  try {
23699
24018
  loaded2.set(label, {
@@ -23736,7 +24055,7 @@ async function runDerive(opts) {
23736
24055
  runFields = union2;
23737
24056
  }
23738
24057
  const provenanceRoot = configDir ?? cwd;
23739
- const pages2 = files.map((label) => resolve24(base, label));
24058
+ const pages2 = files.map((label) => resolve25(base, label));
23740
24059
  const manifests = runFields.includes(PROVENANCE_FIELD) ? provenanceManifests(declaredCollections, provenanceRoot, base) : [];
23741
24060
  const provenanceCollections = declaredCollections.map((c) => ({
23742
24061
  ...c,
@@ -23828,10 +24147,10 @@ async function runDerive(opts) {
23828
24147
  const members = memberOf(prepared.provenanceCollections, root, base, doc.label);
23829
24148
  return (await mergeWithMarks(doc.label, doc.extracted, prepared.provenanceIndex, members, base, { marks })).extracted.data[PROVENANCE_FIELD];
23830
24149
  };
23831
- const noticed = /* @__PURE__ */ new Set();
23832
- const noticeOnce = opts.onNotice === void 0 ? void 0 : (message) => {
23833
- if (noticed.has(message)) return;
23834
- noticed.add(message);
24150
+ const noticed2 = /* @__PURE__ */ new Set();
24151
+ const noticeOnce2 = opts.onNotice === void 0 ? void 0 : (message) => {
24152
+ if (noticed2.has(message)) return;
24153
+ noticed2.add(message);
23835
24154
  opts.onNotice?.(message);
23836
24155
  };
23837
24156
  const markFields = async (doc, record) => {
@@ -23862,7 +24181,7 @@ async function runDerive(opts) {
23862
24181
  // `$schema`, since that schema decides what gets encrypted.
23863
24182
  fileBase: cwd,
23864
24183
  trustRoot,
23865
- onNotice: noticeOnce
24184
+ onNotice: noticeOnce2
23866
24185
  });
23867
24186
  } catch (err) {
23868
24187
  return { error: errorMessage(err) };
@@ -23923,7 +24242,7 @@ async function runDerive(opts) {
23923
24242
  validator,
23924
24243
  ...opts.noConfig === true ? { noConfig: true } : {},
23925
24244
  ...opts.env !== void 0 ? { env: opts.env } : {},
23926
- ...noticeOnce !== void 0 ? { onNotice: noticeOnce } : {}
24245
+ ...noticeOnce2 !== void 0 ? { onNotice: noticeOnce2 } : {}
23927
24246
  });
23928
24247
  const externalWrites = async () => {
23929
24248
  const out = [];
@@ -24906,15 +25225,28 @@ export {
24906
25225
  generateEncryptionKey,
24907
25226
  encryptValue,
24908
25227
  decryptValue,
25228
+ ToolError,
25229
+ errorMessage,
25230
+ assertKnownProvider,
25231
+ assertModelHasProvider,
25232
+ PROVIDERS_KEY,
25233
+ parseProviders,
25234
+ LOCAL_FLAG_HELP,
25235
+ assertLocalFlag,
25236
+ selectProvider,
25237
+ localNotice,
25238
+ providerSpecFor,
25239
+ resolveIdentity,
24909
25240
  findGitRoot,
24910
25241
  valeConfigPath,
24911
25242
  ditaOtHome,
24912
25243
  programName,
24913
25244
  warn,
24914
25245
  notice,
24915
- ToolError,
24916
- errorMessage,
25246
+ noticeOnce,
24917
25247
  FAMILY_CONFIG_NAMES,
25248
+ findConfigFileSync,
25249
+ readConfigFileSync,
24918
25250
  findConfigFile,
24919
25251
  readConfigFile,
24920
25252
  findFamilyConfigFile,
@@ -24976,7 +25308,8 @@ export {
24976
25308
  DEFAULT_TTL_HOURS,
24977
25309
  schemaCacheDir,
24978
25310
  SchemaCache,
24979
- __default34 as __default,
25311
+ __default31 as __default,
25312
+ __default34 as __default2,
24980
25313
  listBuiltins,
24981
25314
  classifyRef,
24982
25315
  fetchSchemaBytes,
@@ -25000,6 +25333,7 @@ export {
25000
25333
  staleFindings,
25001
25334
  LEGACY_CONFIG_NAMES,
25002
25335
  parseConfig,
25336
+ schemaTrustRoot,
25003
25337
  loadConfig,
25004
25338
  resolveRunConfig,
25005
25339
  renderSarif,
@@ -25042,19 +25376,26 @@ export {
25042
25376
  spliceManifestValue,
25043
25377
  readManifestValue,
25044
25378
  removeManifestKey,
25379
+ isParamName,
25380
+ memberOf,
25045
25381
  LOCATION_KEYWORD,
25046
25382
  LOCATION_EXTERNAL_SCHEMA,
25047
25383
  LOCATION_PAGE_SCHEMA,
25048
25384
  Validator,
25385
+ pageMarks,
25386
+ marksValidator,
25049
25387
  configMarks,
25050
- isParamName,
25051
- memberOf,
25052
25388
  relocateFailed,
25053
25389
  relocationContext,
25054
25390
  keyHome,
25055
25391
  planRelocation,
25056
25392
  applyRelocation,
25057
25393
  offerPrompt,
25394
+ offerExternalHomes,
25395
+ externalWriteWarnings,
25396
+ mergeMetaProvenance,
25397
+ metaProvenanceEntries,
25398
+ provenanceEntries,
25058
25399
  REVIEW_CACHE_DIR,
25059
25400
  REVIEW_CACHE_VERSION,
25060
25401
  ReviewCache,
@@ -25113,4 +25454,4 @@ export {
25113
25454
  renderQuery,
25114
25455
  renderQueryCsv
25115
25456
  };
25116
- //# sourceMappingURL=chunk-TK5PYPFU.js.map
25457
+ //# sourceMappingURL=chunk-I57KBF45.js.map