@hawkeyexl/manni 4.0.3 → 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.3",
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,721 +12724,721 @@ 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;
12587
- return own2[LOCATION_KEYWORD] === "external";
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
+ }
12588
12802
  }
12589
- function isRecord2(value) {
12590
- return typeof value === "object" && value !== null && !Array.isArray(value);
12803
+ function startsStringOrIdent(sql, i) {
12804
+ const ch = sql[i];
12805
+ return ch === "'" || ch === '"' || ch === "`" || ch === "[";
12591
12806
  }
12592
- function definiteLocationConflict(root) {
12593
- const marks = /* @__PURE__ */ new Map();
12594
- const localRef = (node, embedded) => {
12595
- const ref = node["$ref"];
12596
- if (embedded || typeof ref !== "string" || !ref.startsWith("#")) return void 0;
12597
- const pointer = ref.slice(1);
12598
- if (pointer === "") return root;
12599
- if (!pointer.startsWith("/")) return void 0;
12600
- let at2 = root;
12601
- for (const raw of pointer.slice(1).split("/")) {
12602
- let segment;
12603
- try {
12604
- segment = decodeURIComponent(raw).replace(/~1/g, "/").replace(/~0/g, "~");
12605
- } catch {
12606
- return void 0;
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;
12607
12824
  }
12608
- if (Array.isArray(at2)) at2 = at2[Number(segment)];
12609
- else if (isRecord2(at2)) at2 = at2[segment];
12610
- else return void 0;
12611
- }
12612
- return at2;
12613
- };
12614
- const isEmbedded = (node, embedded) => embedded || node !== root && typeof node["$id"] === "string";
12615
- const allOf = (node) => Array.isArray(node["allOf"]) ? node["allOf"] : [];
12616
- const property = (key, node, embedded, visited) => {
12617
- if (!isRecord2(node) || visited.has(node)) return;
12618
- visited.add(node);
12619
- const inner = isEmbedded(node, embedded);
12620
- const mark = node[LOCATION_KEYWORD];
12621
- if (isFieldLocation(mark)) {
12622
- const seen = marks.get(key);
12623
- if (seen) seen.add(mark);
12624
- else marks.set(key, /* @__PURE__ */ new Set([mark]));
12625
- }
12626
- for (const entry of allOf(node)) property(key, entry, inner, visited);
12627
- property(key, localRef(node, inner), inner, visited);
12628
- };
12629
- const object = (node, embedded, visited) => {
12630
- if (!isRecord2(node) || visited.has(node)) return;
12631
- visited.add(node);
12632
- const inner = isEmbedded(node, embedded);
12633
- const properties = node["properties"];
12634
- if (isRecord2(properties)) {
12635
- for (const [key, sub] of Object.entries(properties)) property(key, sub, inner, /* @__PURE__ */ new Set());
12825
+ return j + 1;
12636
12826
  }
12637
- for (const entry of allOf(node)) object(entry, inner, visited);
12638
- object(localRef(node, inner), inner, visited);
12639
- };
12640
- object(root, false, /* @__PURE__ */ new Set());
12641
- for (const [key, seen] of marks) if (seen.size > 1) return key;
12642
- return void 0;
12827
+ j++;
12828
+ }
12829
+ return j;
12643
12830
  }
12644
- function registerBuiltins(ajv, dialect) {
12645
- for (const { id, url, legacyUrl, schema } of publishedBuiltins()) {
12646
- ajv.addSchema(schema, url, void 0, false);
12647
- ajv.addSchema(schema, legacyUrl, void 0, false);
12648
- if (dialect === "draft4") ajv.addSchema(schema, id, void 0, false);
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;
12649
12836
  }
12837
+ const end = sql.indexOf("*/", i + 2);
12838
+ return end === -1 ? sql.length : end + 2;
12650
12839
  }
12651
- function registerRegistered(ajv, dialect, registered2) {
12652
- for (const { id, schema } of registered2?.values() ?? []) {
12653
- ajv.addSchema(schema, dialect === "draft4" ? id : void 0, void 0, false);
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
+ }
12654
12852
  }
12853
+ return -1;
12655
12854
  }
12656
- function compileWithFormats(schema, registered2, options = {}) {
12657
- const ajv = buildAjv("2020", options.coerce === true ? "array" : false);
12658
- registerRegistered(ajv, "2020", registered2);
12659
- return ajv.compile(schema);
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;
12660
12858
  }
12661
- var formatRegistry;
12662
- function formatsAjv() {
12663
- if (!formatRegistry) {
12664
- formatRegistry = new Ajv2020({ strict: false });
12665
- addFormats(formatRegistry);
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
+ }
12666
12896
  }
12667
- return formatRegistry;
12897
+ return tokens2;
12668
12898
  }
12669
- var stringFormatsMemo;
12670
- function stringFormatNames() {
12671
- if (!stringFormatsMemo) {
12672
- const names = /* @__PURE__ */ new Set();
12673
- const registered2 = formatsAjv().formats;
12674
- for (const [name2, def] of Object.entries(registered2)) {
12675
- const enforcesStrings = def instanceof RegExp || typeof def === "function" || typeof def === "object" && def !== null && (def.type === void 0 || def.type === "string");
12676
- if (enforcesStrings) names.add(name2);
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;
12677
12906
  }
12678
- stringFormatsMemo = names;
12907
+ return sql.slice(i);
12679
12908
  }
12680
- return stringFormatsMemo;
12681
12909
  }
12682
- var formatCheckers = /* @__PURE__ */ new Map();
12683
- function validatesFormat(name2, value) {
12684
- let fn = formatCheckers.get(name2);
12685
- if (!fn) {
12686
- fn = formatsAjv().compile({ type: "string", format: name2 });
12687
- formatCheckers.set(name2, fn);
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;
12688
12923
  }
12689
- return fn(value);
12924
+ return true;
12690
12925
  }
12691
- var Validator = class {
12692
- /**
12693
- * How this validator's schemas are loaded: the disk cache location, its TTL,
12694
- * and `--offline`. Held per instance rather than read from module state, so
12695
- * two differently-configured validations in one process each get their own
12696
- * settings.
12697
- *
12698
- * That is not full isolation, and the difference matters to a library
12699
- * caller: `schema-registry` keeps a process-wide memo of fetched schemas, so
12700
- * one validator's successful fetch is visible to another. `offline` is
12701
- * excluded from that sharing on purpose — an offline validator will not be
12702
- * served something this process pulled over the network — but the memo is
12703
- * still shared, so a URL fetched once is not re-fetched per instance.
12704
- */
12705
- constructor(schemaOptions = {}) {
12706
- this.schemaOptions = schemaOptions;
12707
- }
12708
- schemaOptions;
12709
- ajvByDialect = /* @__PURE__ */ new Map();
12710
- /**
12711
- * Keyed on the in-flight *promise*, not the resolved validator. Caching the
12712
- * result made this a check-then-act race: `fill` walks files through a worker
12713
- * pool, so every worker missed the cache while the first `loadSchema` was
12714
- * still pending and they all then compiled the same schema into the one
12715
- * shared per-dialect Ajv. Ajv registers a schema's `$id` on the first compile
12716
- * and rejects the second with "schema with key or id ... already exists",
12717
- * which took down any multi-file run against an $id-bearing schema. Storing
12718
- * the promise before the first await lets the losers await the one compile.
12719
- */
12720
- cache = /* @__PURE__ */ new Map();
12721
- ajvFor(dialect) {
12722
- let ajv = this.ajvByDialect.get(dialect);
12723
- if (!ajv) {
12724
- ajv = buildAjv(dialect);
12725
- registerRegistered(ajv, dialect, this.schemaOptions.registered);
12726
- this.ajvByDialect.set(dialect, ajv);
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;
12727
12940
  }
12728
- return ajv;
12729
- }
12730
- /**
12731
- * Synchronous by design: the `cache.set` has to happen in the same tick as
12732
- * the miss, or a second caller can slip in before the entry exists.
12733
- */
12734
- compile(ref) {
12735
- const cached = this.cache.get(ref);
12736
- if (cached) return cached;
12737
- const pending = this.compileUncached(ref);
12738
- this.cache.set(ref, pending);
12739
- pending.catch(() => {
12740
- if (this.cache.get(ref) === pending) this.cache.delete(ref);
12741
- });
12742
- return pending;
12743
- }
12744
- async compileUncached(ref) {
12745
- const schema = await loadSchema(ref, this.schemaOptions);
12746
- if (marksSchemaExternal(schema)) {
12747
- throw new DocmetaError(
12748
- `${ref}: "${FILE_SCHEMA_KEY}" cannot be stored in external metadata.`
12749
- );
12750
- }
12751
- const contradicted = definiteLocationConflict(schema);
12752
- if (contradicted !== void 0) {
12753
- throw new DocmetaError(
12754
- `${ref}: "${LOCATION_KEYWORD}" says both "page" and "external" for "${contradicted}".`
12755
- );
12756
- }
12757
- try {
12758
- const ajv = this.ajvFor(dialectOf(schema));
12759
- const id = typeof schema["$id"] === "string" ? schema["$id"] : void 0;
12760
- const registered2 = id != null ? ajv.getSchema(id) : void 0;
12761
- if (registered2) {
12762
- if (registered2.schema === schema) return registered2;
12763
- const { $id: _taken, ...anonymous } = schema;
12764
- return ajv.compile(anonymous);
12765
- }
12766
- return ajv.compile(schema);
12767
- } catch (err) {
12768
- if (isMarkShapeError(err)) {
12769
- throw new DocmetaError(
12770
- `${ref}: "${ENCRYPT_KEYWORD}" must be true or false.`
12771
- );
12772
- }
12773
- if (isLocationShapeError(err)) {
12774
- throw new DocmetaError(
12775
- `${ref}: "${LOCATION_KEYWORD}" must be "page" or "external".`
12776
- );
12777
- }
12778
- throw new DocmetaError(
12779
- `Schema "${ref}" failed to compile: ${errorMessage(err)}`
12780
- );
12781
- }
12782
- }
12783
- /**
12784
- * The instance pointers of `data` that carry `x-manni-encrypt: true` under
12785
- * any schema in `refs`, in the order the validator evaluated them.
12786
- *
12787
- * A first pass with the recording keyword, so a mark counts exactly where
12788
- * validation would evaluate it. Only present values are marked: Ajv applies
12789
- * a property's subschema to a property that exists. A branch Ajv does not
12790
- * take (`then` behind a failing `if`, an `anyOf` branch after a passing
12791
- * one) contributes nothing; a branch it takes contributes its marks
12792
- * whether or not the branch passes, because a ciphertext routinely fails
12793
- * the rest of its own subschema until it is decrypted.
12794
- */
12795
- async markedPointers(data, refs) {
12796
- const { [FILE_SCHEMA_KEY]: _omit, ...subject } = data;
12797
- void _omit;
12798
- const marks = /* @__PURE__ */ new Set();
12799
- for (const ref of refs) {
12800
- const fn = await this.compile(ref);
12801
- markRecorder = marks;
12802
- try {
12803
- fn(subject);
12804
- } finally {
12805
- markRecorder = void 0;
12806
- }
12807
- }
12808
- return marks;
12809
- }
12810
- /**
12811
- * The location each top-level key of `data` prefers under `refs`: `page` or
12812
- * `external`, and the ref whose `x-manni-location` mark decided it. A key no
12813
- * schema marks has no entry.
12814
- *
12815
- * A first pass with the recording keyword, as `markedPointers` runs, so a
12816
- * mark counts exactly where validation would evaluate it, and a nested mark
12817
- * is ignored. Only present values are marked: Ajv applies a property's
12818
- * subschema to a property that exists. A caller asking where a key it is
12819
- * about to write belongs passes `data` that already holds the candidate
12820
- * value. `$schema` is stripped first, as validation does.
12821
- *
12822
- * Refs are evaluated in order and a later ref's mark wins. A ref that says
12823
- * both values for one key unconditionally is refused when it compiles; one
12824
- * whose evaluated branches say both gives that key no preference.
12825
- */
12826
- async locationPreferences(data, refs) {
12827
- const { [FILE_SCHEMA_KEY]: _omit, ...subject } = data;
12828
- void _omit;
12829
- const preferences = /* @__PURE__ */ new Map();
12830
- for (const ref of refs) {
12831
- const fn = await this.compile(ref);
12832
- const seen = /* @__PURE__ */ new Map();
12833
- locationRecorder = seen;
12834
- try {
12835
- fn(subject);
12836
- } finally {
12837
- locationRecorder = void 0;
12838
- }
12839
- for (const [key, locations] of seen) {
12840
- const [location, ...others] = [...locations];
12841
- if (location === void 0 || others.length > 0) continue;
12842
- preferences.set(key, { location, schema: ref });
12843
- }
12844
- }
12845
- return preferences;
12846
- }
12847
- /**
12848
- * The top-level property names the schemas in `refs` claim: the keys of
12849
- * each schema's own `properties`, each once. What a page's schema set
12850
- * claims decides which merge-safe fields a `derive:` block with no `fields`
12851
- * manages on it (proposal 0069). A property reached only through `allOf`,
12852
- * `$ref` or a conditional is not counted, so a schema that should manage a
12853
- * field names it in its root `properties`. A schema that fails to load or
12854
- * compile throws, as it would for `validate`.
12855
- */
12856
- async claimedProperties(refs) {
12857
- const claimed = /* @__PURE__ */ new Set();
12858
- for (const ref of refs) {
12859
- const schema = (await this.compile(ref)).schema;
12860
- if (typeof schema !== "object" || schema === null) continue;
12861
- const properties = schema["properties"];
12862
- if (typeof properties !== "object" || properties === null || Array.isArray(properties)) continue;
12863
- for (const key of Object.keys(properties)) claimed.add(key);
12864
- }
12865
- return claimed;
12866
- }
12867
- /**
12868
- * Validate `data` against every schema in `refs`. Returns all violations,
12869
- * each tagged with the schema that produced it and a source line via
12870
- * `lineFor`.
12871
- *
12872
- * `colFor` is optional and additive: this signature is public, so a fourth
12873
- * *required* parameter — or a widened third one — would be a consumer break.
12874
- * Callers with an extractor that supplies no column pass nothing and get the
12875
- * previous behavior exactly.
12876
- */
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
- }
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]);
12888
12970
  }
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
12971
  }
12972
+ return declared.map((c) => ({ name: c.name, members: members.get(c.name) ?? [] }));
12905
12973
  }
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
- };
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
+ );
12933
12983
  }
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
12984
  }
12946
12985
 
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('"', '""')}"`;
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";
12955
12993
  }
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);
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";
12968
13015
  }
13016
+ if (meta.includes("draft-04") || meta.includes("draft/4")) return "draft4";
13017
+ return "2020";
12969
13018
  }
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();
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;
12981
13028
  }
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
- );
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;
13000
13040
  }
13001
- db.exec("COMMIT");
13002
- committed = true;
13003
- } finally {
13004
- if (!committed) db.exec("ROLLBACK");
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;
13005
13050
  }
13051
+ return instancePath.slice(1).replace(/~1/g, "/").replace(/~0/g, "~");
13006
13052
  }
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;
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
+ }
13012
13069
  });
13013
13070
  }
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
- );
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;
13079
+ return own2[LOCATION_KEYWORD] === "external";
13080
+ }
13081
+ function isRecord2(value) {
13082
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13083
+ }
13084
+ function definiteLocationConflict(root) {
13085
+ const marks = /* @__PURE__ */ new Map();
13086
+ const localRef = (node, embedded) => {
13087
+ const ref = node["$ref"];
13088
+ if (embedded || typeof ref !== "string" || !ref.startsWith("#")) return void 0;
13089
+ const pointer = ref.slice(1);
13090
+ if (pointer === "") return root;
13091
+ if (!pointer.startsWith("/")) return void 0;
13092
+ let at2 = root;
13093
+ for (const raw of pointer.slice(1).split("/")) {
13094
+ let segment;
13095
+ try {
13096
+ segment = decodeURIComponent(raw).replace(/~1/g, "/").replace(/~0/g, "~");
13097
+ } catch {
13098
+ return void 0;
13099
+ }
13100
+ if (Array.isArray(at2)) at2 = at2[Number(segment)];
13101
+ else if (isRecord2(at2)) at2 = at2[segment];
13102
+ else return void 0;
13103
+ }
13104
+ return at2;
13105
+ };
13106
+ const isEmbedded = (node, embedded) => embedded || node !== root && typeof node["$id"] === "string";
13107
+ const allOf = (node) => Array.isArray(node["allOf"]) ? node["allOf"] : [];
13108
+ const property = (key, node, embedded, visited) => {
13109
+ if (!isRecord2(node) || visited.has(node)) return;
13110
+ visited.add(node);
13111
+ const inner = isEmbedded(node, embedded);
13112
+ const mark = node[LOCATION_KEYWORD];
13113
+ if (isFieldLocation(mark)) {
13114
+ const seen = marks.get(key);
13115
+ if (seen) seen.add(mark);
13116
+ else marks.set(key, /* @__PURE__ */ new Set([mark]));
13117
+ }
13118
+ for (const entry of allOf(node)) property(key, entry, inner, visited);
13119
+ property(key, localRef(node, inner), inner, visited);
13120
+ };
13121
+ const object = (node, embedded, visited) => {
13122
+ if (!isRecord2(node) || visited.has(node)) return;
13123
+ visited.add(node);
13124
+ const inner = isEmbedded(node, embedded);
13125
+ const properties = node["properties"];
13126
+ if (isRecord2(properties)) {
13127
+ for (const [key, sub] of Object.entries(properties)) property(key, sub, inner, /* @__PURE__ */ new Set());
13128
+ }
13129
+ for (const entry of allOf(node)) object(entry, inner, visited);
13130
+ object(localRef(node, inner), inner, visited);
13131
+ };
13132
+ object(root, false, /* @__PURE__ */ new Set());
13133
+ for (const [key, seen] of marks) if (seen.size > 1) return key;
13134
+ return void 0;
13135
+ }
13136
+ function registerBuiltins(ajv, dialect) {
13137
+ for (const { id, url, legacyUrl, schema } of publishedBuiltins()) {
13138
+ ajv.addSchema(schema, url, void 0, false);
13139
+ ajv.addSchema(schema, legacyUrl, void 0, false);
13140
+ if (dialect === "draft4") ajv.addSchema(schema, id, void 0, false);
13021
13141
  }
13022
13142
  }
13023
- function startsStringOrIdent(sql, i) {
13024
- const ch = sql[i];
13025
- return ch === "'" || ch === '"' || ch === "`" || ch === "[";
13143
+ function registerRegistered(ajv, dialect, registered2) {
13144
+ for (const { id, schema } of registered2?.values() ?? []) {
13145
+ ajv.addSchema(schema, dialect === "draft4" ? id : void 0, void 0, false);
13146
+ }
13026
13147
  }
13027
- function startsComment(sql, i) {
13028
- const ch = sql[i];
13029
- return ch === "-" && sql[i + 1] === "-" || ch === "/" && sql[i + 1] === "*";
13148
+ function compileWithFormats(schema, registered2, options = {}) {
13149
+ const ajv = buildAjv("2020", options.coerce === true ? "array" : false);
13150
+ registerRegistered(ajv, "2020", registered2);
13151
+ return ajv.compile(schema);
13030
13152
  }
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;
13153
+ var formatRegistry;
13154
+ function formatsAjv() {
13155
+ if (!formatRegistry) {
13156
+ formatRegistry = new Ajv2020({ strict: false });
13157
+ addFormats(formatRegistry);
13037
13158
  }
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;
13159
+ return formatRegistry;
13160
+ }
13161
+ var stringFormatsMemo;
13162
+ function stringFormatNames() {
13163
+ if (!stringFormatsMemo) {
13164
+ const names = /* @__PURE__ */ new Set();
13165
+ const registered2 = formatsAjv().formats;
13166
+ for (const [name2, def] of Object.entries(registered2)) {
13167
+ const enforcesStrings = def instanceof RegExp || typeof def === "function" || typeof def === "object" && def !== null && (def.type === void 0 || def.type === "string");
13168
+ if (enforcesStrings) names.add(name2);
13046
13169
  }
13047
- j++;
13170
+ stringFormatsMemo = names;
13048
13171
  }
13049
- return j;
13172
+ return stringFormatsMemo;
13050
13173
  }
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;
13174
+ var formatCheckers = /* @__PURE__ */ new Map();
13175
+ function validatesFormat(name2, value) {
13176
+ let fn = formatCheckers.get(name2);
13177
+ if (!fn) {
13178
+ fn = formatsAjv().compile({ type: "string", format: name2 });
13179
+ formatCheckers.set(name2, fn);
13056
13180
  }
13057
- const end = sql.indexOf("*/", i + 2);
13058
- return end === -1 ? sql.length : end + 2;
13181
+ return fn(value);
13059
13182
  }
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++;
13183
+ var Validator = class {
13184
+ /**
13185
+ * How this validator's schemas are loaded: the disk cache location, its TTL,
13186
+ * and `--offline`. Held per instance rather than read from module state, so
13187
+ * two differently-configured validations in one process each get their own
13188
+ * settings.
13189
+ *
13190
+ * That is not full isolation, and the difference matters to a library
13191
+ * caller: `schema-registry` keeps a process-wide memo of fetched schemas, so
13192
+ * one validator's successful fetch is visible to another. `offline` is
13193
+ * excluded from that sharing on purpose — an offline validator will not be
13194
+ * served something this process pulled over the network — but the memo is
13195
+ * still shared, so a URL fetched once is not re-fetched per instance.
13196
+ */
13197
+ constructor(schemaOptions = {}) {
13198
+ this.schemaOptions = schemaOptions;
13199
+ }
13200
+ schemaOptions;
13201
+ ajvByDialect = /* @__PURE__ */ new Map();
13202
+ /**
13203
+ * Keyed on the in-flight *promise*, not the resolved validator. Caching the
13204
+ * result made this a check-then-act race: `fill` walks files through a worker
13205
+ * pool, so every worker missed the cache while the first `loadSchema` was
13206
+ * still pending and they all then compiled the same schema into the one
13207
+ * shared per-dialect Ajv. Ajv registers a schema's `$id` on the first compile
13208
+ * and rejects the second with "schema with key or id ... already exists",
13209
+ * which took down any multi-file run against an $id-bearing schema. Storing
13210
+ * the promise before the first await lets the losers await the one compile.
13211
+ */
13212
+ cache = /* @__PURE__ */ new Map();
13213
+ ajvFor(dialect) {
13214
+ let ajv = this.ajvByDialect.get(dialect);
13215
+ if (!ajv) {
13216
+ ajv = buildAjv(dialect);
13217
+ registerRegistered(ajv, dialect, this.schemaOptions.registered);
13218
+ this.ajvByDialect.set(dialect, ajv);
13071
13219
  }
13220
+ return ajv;
13072
13221
  }
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;
13222
+ /**
13223
+ * Synchronous by design: the `cache.set` has to happen in the same tick as
13224
+ * the miss, or a second caller can slip in before the entry exists.
13225
+ */
13226
+ compile(ref) {
13227
+ const cached = this.cache.get(ref);
13228
+ if (cached) return cached;
13229
+ const pending = this.compileUncached(ref);
13230
+ this.cache.set(ref, pending);
13231
+ pending.catch(() => {
13232
+ if (this.cache.get(ref) === pending) this.cache.delete(ref);
13233
+ });
13234
+ return pending;
13235
+ }
13236
+ async compileUncached(ref) {
13237
+ const schema = await loadSchema(ref, this.schemaOptions);
13238
+ if (marksSchemaExternal(schema)) {
13239
+ throw new DocmetaError(
13240
+ `${ref}: "${FILE_SCHEMA_KEY}" cannot be stored in external metadata.`
13241
+ );
13242
+ }
13243
+ const contradicted = definiteLocationConflict(schema);
13244
+ if (contradicted !== void 0) {
13245
+ throw new DocmetaError(
13246
+ `${ref}: "${LOCATION_KEYWORD}" says both "page" and "external" for "${contradicted}".`
13247
+ );
13248
+ }
13249
+ try {
13250
+ const ajv = this.ajvFor(dialectOf(schema));
13251
+ const id = typeof schema["$id"] === "string" ? schema["$id"] : void 0;
13252
+ const registered2 = id != null ? ajv.getSchema(id) : void 0;
13253
+ if (registered2) {
13254
+ if (registered2.schema === schema) return registered2;
13255
+ const { $id: _taken, ...anonymous } = schema;
13256
+ return ajv.compile(anonymous);
13094
13257
  }
13095
- const m = PARAM_NAME_HEAD.exec(sql.slice(i + 1));
13096
- if (!m) {
13097
- i++;
13098
- continue;
13258
+ return ajv.compile(schema);
13259
+ } catch (err) {
13260
+ if (isMarkShapeError(err)) {
13261
+ throw new DocmetaError(
13262
+ `${ref}: "${ENCRYPT_KEYWORD}" must be true or false.`
13263
+ );
13099
13264
  }
13100
- const token = ch + m[0];
13101
- const priorPrefix = prefixOf.get(m[0]);
13102
- if (priorPrefix !== void 0 && priorPrefix !== ch) {
13265
+ if (isLocationShapeError(err)) {
13103
13266
  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.`
13267
+ `${ref}: "${LOCATION_KEYWORD}" must be "page" or "external".`
13105
13268
  );
13106
13269
  }
13107
- prefixOf.set(m[0], ch);
13108
- if (!seen.has(token)) {
13109
- seen.add(token);
13110
- tokens2.push(token);
13270
+ throw new DocmetaError(
13271
+ `Schema "${ref}" failed to compile: ${errorMessage(err)}`
13272
+ );
13273
+ }
13274
+ }
13275
+ /**
13276
+ * The instance pointers of `data` that carry `x-manni-encrypt: true` under
13277
+ * any schema in `refs`, in the order the validator evaluated them.
13278
+ *
13279
+ * A first pass with the recording keyword, so a mark counts exactly where
13280
+ * validation would evaluate it. Only present values are marked: Ajv applies
13281
+ * a property's subschema to a property that exists. A branch Ajv does not
13282
+ * take (`then` behind a failing `if`, an `anyOf` branch after a passing
13283
+ * one) contributes nothing; a branch it takes contributes its marks
13284
+ * whether or not the branch passes, because a ciphertext routinely fails
13285
+ * the rest of its own subschema until it is decrypted.
13286
+ */
13287
+ async markedPointers(data, refs) {
13288
+ const { [FILE_SCHEMA_KEY]: _omit, ...subject } = data;
13289
+ void _omit;
13290
+ const marks = /* @__PURE__ */ new Set();
13291
+ for (const ref of refs) {
13292
+ const fn = await this.compile(ref);
13293
+ markRecorder = marks;
13294
+ try {
13295
+ fn(subject);
13296
+ } finally {
13297
+ markRecorder = void 0;
13111
13298
  }
13112
- i += 1 + m[0].length;
13113
- } else {
13114
- i++;
13115
13299
  }
13300
+ return marks;
13116
13301
  }
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;
13302
+ /**
13303
+ * The location each top-level key of `data` prefers under `refs`: `page` or
13304
+ * `external`, and the ref whose `x-manni-location` mark decided it. A key no
13305
+ * schema marks has no entry.
13306
+ *
13307
+ * A first pass with the recording keyword, as `markedPointers` runs, so a
13308
+ * mark counts exactly where validation would evaluate it, and a nested mark
13309
+ * is ignored. Only present values are marked: Ajv applies a property's
13310
+ * subschema to a property that exists. A caller asking where a key it is
13311
+ * about to write belongs passes `data` that already holds the candidate
13312
+ * value. `$schema` is stripped first, as validation does.
13313
+ *
13314
+ * Refs are evaluated in order and a later ref's mark wins. A ref that says
13315
+ * both values for one key unconditionally is refused when it compiles; one
13316
+ * whose evaluated branches say both gives that key no preference.
13317
+ */
13318
+ async locationPreferences(data, refs) {
13319
+ const { [FILE_SCHEMA_KEY]: _omit, ...subject } = data;
13320
+ void _omit;
13321
+ const preferences = /* @__PURE__ */ new Map();
13322
+ for (const ref of refs) {
13323
+ const fn = await this.compile(ref);
13324
+ const seen = /* @__PURE__ */ new Map();
13325
+ locationRecorder = seen;
13326
+ try {
13327
+ fn(subject);
13328
+ } finally {
13329
+ locationRecorder = void 0;
13330
+ }
13331
+ for (const [key, locations] of seen) {
13332
+ const [location, ...others] = [...locations];
13333
+ if (location === void 0 || others.length > 0) continue;
13334
+ preferences.set(key, { location, schema: ref });
13335
+ }
13126
13336
  }
13127
- return sql.slice(i);
13337
+ return preferences;
13128
13338
  }
13129
- }
13130
- function isTrivia(text2) {
13131
- let i = 0;
13132
- while (i < text2.length) {
13133
- const ch = text2[i];
13134
- if (ch === void 0 || /\s/.test(ch) || ch === ";") {
13135
- i++;
13136
- continue;
13137
- }
13138
- if (startsComment(text2, i)) {
13139
- i = skipComment(text2, i);
13140
- continue;
13339
+ /**
13340
+ * The top-level property names the schemas in `refs` claim: the keys of
13341
+ * each schema's own `properties`, each once. What a page's schema set
13342
+ * claims decides which merge-safe fields a `derive:` block with no `fields`
13343
+ * manages on it (proposal 0069). A property reached only through `allOf`,
13344
+ * `$ref` or a conditional is not counted, so a schema that should manage a
13345
+ * field names it in its root `properties`. A schema that fails to load or
13346
+ * compile throws, as it would for `validate`.
13347
+ */
13348
+ async claimedProperties(refs) {
13349
+ const claimed = /* @__PURE__ */ new Set();
13350
+ for (const ref of refs) {
13351
+ const schema = (await this.compile(ref)).schema;
13352
+ if (typeof schema !== "object" || schema === null) continue;
13353
+ const properties = schema["properties"];
13354
+ if (typeof properties !== "object" || properties === null || Array.isArray(properties)) continue;
13355
+ for (const key of Object.keys(properties)) claimed.add(key);
13141
13356
  }
13142
- return false;
13357
+ return claimed;
13143
13358
  }
13144
- return true;
13145
- }
13146
- var sqliteModule;
13147
- function loadSqlite() {
13148
- sqliteModule ??= (async () => {
13149
- const original = process.emitWarning.bind(process);
13150
- const filtered = (warning, ...rest) => {
13151
- const text2 = typeof warning === "string" ? warning : warning.message;
13152
- if (text2.includes("SQLite is an experimental feature")) return;
13153
- Reflect.apply(original, process, [warning, ...rest]);
13154
- };
13155
- process.emitWarning = filtered;
13156
- try {
13157
- return await import("node:sqlite");
13158
- } finally {
13159
- process.emitWarning = original;
13160
- }
13161
- })();
13162
- return sqliteModule;
13163
- }
13164
-
13165
- // src/meta/core/collections.ts
13166
- function memberOf(collections, configDir, base, label) {
13167
- if (collections.length === 0) return [];
13168
- if (label === STDIN_LABEL) return [];
13169
- const rel = relative10(configDir, resolvePath2(base, label)).split(sep5).join("/");
13170
- if (isAbsolute6(rel)) return [];
13171
- return collections.filter((c) => isMember(c, rel)).map((c) => c.name);
13172
- }
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]);
13359
+ /**
13360
+ * Validate `data` against every schema in `refs`. Returns all violations,
13361
+ * each tagged with the schema that produced it and a source line via
13362
+ * `lineFor`.
13363
+ *
13364
+ * `colFor` is optional and additive: this signature is public, so a fourth
13365
+ * *required* parameter — or a widened third one — would be a consumer break.
13366
+ * Callers with an extractor that supplies no column pass nothing and get the
13367
+ * previous behavior exactly.
13368
+ */
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));
13379
+ }
13190
13380
  }
13381
+ return errors;
13382
+ }
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;
13191
13396
  }
13192
- return declared.map((c) => ({ name: c.name, members: members.get(c.name) ?? [] }));
13193
- }
13194
- function quoteSqlString(value) {
13195
- return `'${value.replaceAll("'", "''")}'`;
13196
13397
  }
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
- );
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;
13410
+ }
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 } : {}
13424
+ };
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);
@@ -15667,7 +15900,7 @@ async function deriveReviewsByRoot(inputs, git2, rootFor, ctx, requested) {
15667
15900
  if (byRoot.size === 0) {
15668
15901
  return unavailable3(requested, "no git repository contains the documents");
15669
15902
  }
15670
- const cacheDir = resolve15(ctx.configDir ?? ctx.cwd, REVIEW_CACHE_DIR);
15903
+ const cacheDir = resolve16(ctx.configDir ?? ctx.cwd, REVIEW_CACHE_DIR);
15671
15904
  for (const [root, bucket] of byRoot) {
15672
15905
  const client = ctx.reviews ?? await createReviewClient(root, { cwd: ctx.cwd, cache: ctx.cache, cacheDir, requested });
15673
15906
  const identity = await client.detect();
@@ -15845,7 +16078,7 @@ async function deriveForTable(inputs, ctx, hint, fields) {
15845
16078
  }
15846
16079
 
15847
16080
  // src/meta/commands/validate.ts
15848
- import { readFile as readFile10 } from "node:fs/promises";
16081
+ import { readFile as readFile9 } from "node:fs/promises";
15849
16082
 
15850
16083
  // src/meta/core/page-marks.ts
15851
16084
  function pageMarks(opts) {
@@ -15922,8 +16155,8 @@ async function configMarks(configPath, cwd) {
15922
16155
  }
15923
16156
 
15924
16157
  // src/meta/core/relocation.ts
15925
- import { access, readFile as readFile9, stat as stat7, unlink } from "node:fs/promises";
15926
- import { basename as basename5, extname as extname3, isAbsolute as isAbsolute8, join as join9, relative as relative13, resolve as resolve16, sep as sep8 } from "node:path";
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";
15927
16160
  import picomatch6 from "picomatch";
15928
16161
  import {
15929
16162
  LineCounter as LineCounter5,
@@ -16327,7 +16560,7 @@ var Model = class {
16327
16560
  c.externalMetadata.forEach((m, index) => {
16328
16561
  const url = classifyRef(m.file).kind === "url";
16329
16562
  const perPage = !url && hasPagePlaceholder(m.file);
16330
- 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);
16331
16564
  this.manifests.push({
16332
16565
  collection: c.name,
16333
16566
  index,
@@ -16519,7 +16752,7 @@ var Model = class {
16519
16752
  /** A label relative to the config directory, posix; undefined outside it. */
16520
16753
  relative(label) {
16521
16754
  if (label === "<stdin>") return void 0;
16522
- const rel = toPosix3(relative13(this.configDir, resolve16(this.ctx.base, label)));
16755
+ const rel = toPosix3(relative13(this.configDir, resolve17(this.ctx.base, label)));
16523
16756
  return isAbsolute8(rel) || rel.startsWith("../") ? void 0 : rel;
16524
16757
  }
16525
16758
  };
@@ -16534,11 +16767,11 @@ async function collectionPath(target, cwd, configDir) {
16534
16767
  const posix7 = target.replace(/\\/g, "/");
16535
16768
  const scanned = picomatch6.scan(posix7);
16536
16769
  if (scanned.isGlob) {
16537
- const rel2 = toPosix3(relative13(configDir, resolve16(cwd, scanned.base === "" ? "." : scanned.base)));
16770
+ const rel2 = toPosix3(relative13(configDir, resolve17(cwd, scanned.base === "" ? "." : scanned.base)));
16538
16771
  if (isAbsolute8(rel2) || rel2 === ".." || rel2.startsWith("../")) return void 0;
16539
16772
  return rel2 === "" ? scanned.glob : `${rel2}/${scanned.glob}`;
16540
16773
  }
16541
- const abs = resolve16(cwd, posix7);
16774
+ const abs = resolve17(cwd, posix7);
16542
16775
  const rel = toPosix3(relative13(configDir, abs));
16543
16776
  if (isAbsolute8(rel) || rel.startsWith("..")) return void 0;
16544
16777
  let directory = posix7.endsWith("/");
@@ -16562,7 +16795,7 @@ async function exists(absPath, file) {
16562
16795
  }
16563
16796
  }
16564
16797
  function plannedConfigDir(ctx) {
16565
- return ctx.configDir ?? findGitRoot(ctx.cwd) ?? resolve16(ctx.cwd);
16798
+ return ctx.configDir ?? findGitRoot(ctx.cwd) ?? resolve17(ctx.cwd);
16566
16799
  }
16567
16800
  async function keyHome(ctx, label, data, key) {
16568
16801
  const model = new Model(ctx, plannedConfigDir(ctx));
@@ -16574,7 +16807,7 @@ async function keyHome(ctx, label, data, key) {
16574
16807
  ) : void 0;
16575
16808
  const owner = model.ownerOf(key, members, marked);
16576
16809
  if (owner !== void 0) {
16577
- const at2 = model.fileFor(owner, resolve16(ctx.base, label));
16810
+ const at2 = model.fileFor(owner, resolve17(ctx.base, label));
16578
16811
  if (owner.url || at2.abs === void 0) {
16579
16812
  return { kind: "url", collection: owner.collection, file: owner.file };
16580
16813
  }
@@ -16600,7 +16833,7 @@ function marksOf(ctx) {
16600
16833
  });
16601
16834
  }
16602
16835
  function entryFor(m, label, data, configDir, base, key) {
16603
- 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)));
16604
16837
  let raw = data[m.join];
16605
16838
  if (isEncryptedValue(raw)) {
16606
16839
  const k = key();
@@ -16618,7 +16851,7 @@ function entryFor(m, label, data, configDir, base, key) {
16618
16851
  return void 0;
16619
16852
  }
16620
16853
  async function relocationPages(ctx, request) {
16621
- 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)));
16622
16855
  const root = ctx.configDir ?? ctx.cwd;
16623
16856
  for (const collection of ctx.declaredCollections) {
16624
16857
  const perPage = collection.externalMetadata.some(
@@ -16634,7 +16867,7 @@ async function relocationPages(ctx, request) {
16634
16867
  ...ctx.config?.respectGitignore !== void 0 ? { configured: ctx.config.respectGitignore } : {}
16635
16868
  })
16636
16869
  });
16637
- for (const file of walked.files) pages2.add(resolve16(root, file));
16870
+ for (const file of walked.files) pages2.add(resolve17(root, file));
16638
16871
  }
16639
16872
  return [...pages2];
16640
16873
  }
@@ -16664,20 +16897,20 @@ async function planRelocation(ctx, request) {
16664
16897
  if (beyond) return void 0;
16665
16898
  throw new DocmetaError(`Unsupported file type "${extname3(label)}" for "${label}". Use --as to override.`);
16666
16899
  }
16667
- const absPath = resolve16(ctx.base, label);
16900
+ const absPath = resolve17(ctx.base, label);
16668
16901
  const elements = resolveElements(label, ctx.config, currentMembers(label));
16669
16902
  let content = "";
16670
16903
  let own2 = {};
16671
16904
  let unreadable2;
16672
16905
  if (beyond) {
16673
16906
  try {
16674
- content = await readFile9(absPath, "utf8");
16907
+ content = await readFile8(absPath, "utf8");
16675
16908
  own2 = extractor.extract(content, label, { elements }).data;
16676
16909
  } catch (err) {
16677
16910
  unreadable2 = (errorMessage(err).split("\n")[0] ?? "").replace(/:\s*$/, "");
16678
16911
  }
16679
16912
  } else {
16680
- content = await readFile9(absPath, "utf8");
16913
+ content = await readFile8(absPath, "utf8");
16681
16914
  try {
16682
16915
  own2 = extractor.extract(content, label, { elements }).data;
16683
16916
  } catch (err) {
@@ -16883,7 +17116,7 @@ async function planRelocation(ctx, request) {
16883
17116
  });
16884
17117
  const labels = dedupe2([
16885
17118
  ...request.files,
16886
- ...walked.files.map((f) => reported2(resolve16(configDir, f), ctx.base))
17119
+ ...walked.files.map((f) => reported2(resolve17(configDir, f), ctx.base))
16887
17120
  ]).filter((label) => model.members(label).includes(name2));
16888
17121
  for (const label of labels) {
16889
17122
  const page = await load(label, !runSet.has(label));
@@ -17118,7 +17351,7 @@ function assertNoStrandedEntries(m, index, pages2, configDir, fileOf) {
17118
17351
  }
17119
17352
  async function readManifestText(path, file, perPage) {
17120
17353
  try {
17121
- return await readFile9(path, "utf8");
17354
+ return await readFile8(path, "utf8");
17122
17355
  } catch (err) {
17123
17356
  if (perPage && isMissing(err)) return null;
17124
17357
  throw new DocmetaError(`Manifest ${file} could not be read: ${errorMessage(err)}`);
@@ -17148,7 +17381,7 @@ function keyLines2(text2, join12) {
17148
17381
  async function configTarget(ctx, configDir) {
17149
17382
  if (ctx.configPath !== void 0) {
17150
17383
  try {
17151
- const text2 = await readFile9(ctx.configPath, "utf8");
17384
+ const text2 = await readFile8(ctx.configPath, "utf8");
17152
17385
  return { path: ctx.configPath, text: text2, disk: text2 };
17153
17386
  } catch (err) {
17154
17387
  if (err.code !== "ENOENT") {
@@ -17159,10 +17392,10 @@ async function configTarget(ctx, configDir) {
17159
17392
  return { path: ctx.configPath, text: ctx.configFile?.text ?? null, disk: null };
17160
17393
  }
17161
17394
  }
17162
- for (const name2 of [...FAMILY_CONFIG_NAMES, ...MOOSE_CONFIG_NAMES]) {
17395
+ for (const name2 of FAMILY_CONFIG_NAMES) {
17163
17396
  const path = join9(configDir, name2);
17164
17397
  try {
17165
- const text2 = await readFile9(path, "utf8");
17398
+ const text2 = await readFile8(path, "utf8");
17166
17399
  return { path, text: text2, disk: text2 };
17167
17400
  } catch {
17168
17401
  }
@@ -17348,7 +17581,7 @@ async function applyRelocation(ctx, plan, io = {}) {
17348
17581
  for (const w of plan.writes) {
17349
17582
  let now;
17350
17583
  try {
17351
- now = await readFile9(w.path, "utf8");
17584
+ now = await readFile8(w.path, "utf8");
17352
17585
  } catch (err) {
17353
17586
  if (err.code !== "ENOENT") {
17354
17587
  throw new DocmetaError(`${label(w)} could not be read: ${errorMessage(err)}`);
@@ -17515,7 +17748,7 @@ function relocationOffers(plan) {
17515
17748
  }
17516
17749
 
17517
17750
  // src/meta/commands/validate.ts
17518
- import { resolve as resolve18, extname as extname4 } from "node:path";
17751
+ import { resolve as resolve19, extname as extname4 } from "node:path";
17519
17752
 
17520
17753
  // src/meta/core/checks.ts
17521
17754
  var CHECK_KEYWORD = "check";
@@ -17653,7 +17886,7 @@ async function runChecks(checks, entries, ctx = {}) {
17653
17886
  }
17654
17887
 
17655
17888
  // src/meta/core/derive/provenance-place.ts
17656
- 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";
17657
17890
  var toPosix4 = (p) => p.split(sep9).join("/");
17658
17891
  function provenanceManifests(collections, configDir, base, field = PROVENANCE_FIELD) {
17659
17892
  const out = [];
@@ -17675,7 +17908,7 @@ function provenanceManifests(collections, configDir, base, field = PROVENANCE_FI
17675
17908
  });
17676
17909
  continue;
17677
17910
  }
17678
- const absPath = isAbsolute9(manifest.file) ? manifest.file : resolve17(configDir, manifest.file);
17911
+ const absPath = isAbsolute9(manifest.file) ? manifest.file : resolve18(configDir, manifest.file);
17679
17912
  const perPage = hasPagePlaceholder(manifest.file);
17680
17913
  out.push({
17681
17914
  collection: collection.name,
@@ -17709,7 +17942,7 @@ function provenancePlace(label, data, manifests, collections, configDir, base, m
17709
17942
  if (declared.implied?.url === true) throw new DocmetaError(urlRefusal(declared.collection, field));
17710
17943
  const manifest = declared.perPage ? ownManifest(declared, label, collections, configDir, base) : declared;
17711
17944
  if (manifest.join === PATH_JOIN2) {
17712
- const entry = toPosix4(relative14(configDir, resolve17(base, label)));
17945
+ const entry = toPosix4(relative14(configDir, resolve18(base, label)));
17713
17946
  return { manifest, absPath: manifest.absPath, entry, join: manifest.join };
17714
17947
  }
17715
17948
  const value = data[manifest.join];
@@ -17721,7 +17954,7 @@ function provenancePlace(label, data, manifests, collections, configDir, base, m
17721
17954
  return { manifest, absPath: manifest.absPath, entry: String(value), join: manifest.join };
17722
17955
  }
17723
17956
  function ownManifest(manifest, label, collections, configDir, base) {
17724
- const resolved = pageManifestPath(manifest.written, configDir, resolve17(base, label));
17957
+ const resolved = pageManifestPath(manifest.written, configDir, resolve18(base, label));
17725
17958
  if ("outside" in resolved) {
17726
17959
  const declared = collections.find((c) => c.name === manifest.collection)?.externalMetadata.find((m) => m.file === manifest.written);
17727
17960
  throw new DocmetaError(
@@ -17840,7 +18073,7 @@ async function runValidate(opts) {
17840
18073
  base,
17841
18074
  offline: opts.offline ?? config?.offline ?? false,
17842
18075
  // A `{page}` manifest (0058) is read for exactly the pages this run reads.
17843
- pages: files.map((file) => resolve18(base, file))
18076
+ pages: files.map((file) => resolve19(base, file))
17844
18077
  });
17845
18078
  const validator = new Validator(
17846
18079
  schemaLoadOptions({
@@ -18011,7 +18244,7 @@ async function runValidate(opts) {
18011
18244
  const owned = declaredCollections.length === 0 ? {} : stampManifests(label, extracted.data, declaredCollections, configDir ?? cwd, base, merged.marked, STAMPED_DATE_FIELDS);
18012
18245
  deriveInputs.push({
18013
18246
  label,
18014
- absPath: resolve18(base, label),
18247
+ absPath: resolve19(base, label),
18015
18248
  content,
18016
18249
  extracted,
18017
18250
  ...place !== void 0 ? { provenanceManifest: place } : {},
@@ -18151,7 +18384,7 @@ async function runValidate(opts) {
18151
18384
  await processOne(STDIN_LABEL, content, forcedExtractor.extensions[0] ?? "");
18152
18385
  }
18153
18386
  for (const file of files) {
18154
- const content = await readFile10(resolve18(base, file), "utf8");
18387
+ const content = await readFile9(resolve19(base, file), "utf8");
18155
18388
  await processOne(file, content, extname4(file));
18156
18389
  }
18157
18390
  if (unverified > 0) opts.onNotice?.(unverifiedWarning(unverified));
@@ -18297,8 +18530,8 @@ async function runValidate(opts) {
18297
18530
  }
18298
18531
 
18299
18532
  // src/meta/commands/get.ts
18300
- import { readFile as readFile11 } from "node:fs/promises";
18301
- import { resolve as resolve19, extname as extname5 } from "node:path";
18533
+ import { readFile as readFile10 } from "node:fs/promises";
18534
+ import { resolve as resolve20, extname as extname5 } from "node:path";
18302
18535
  async function runGet(opts) {
18303
18536
  const cwd = opts.cwd ?? process.cwd();
18304
18537
  if (opts.fields.length === 0) {
@@ -18359,7 +18592,7 @@ async function runGet(opts) {
18359
18592
  base,
18360
18593
  offline: opts.offline ?? config?.offline ?? false,
18361
18594
  // A `{page}` manifest (0058) is read for exactly the pages this run reads.
18362
- pages: files.map((file) => resolve19(base, file))
18595
+ pages: files.map((file) => resolve20(base, file))
18363
18596
  });
18364
18597
  const commands = commandsOf(config?.derive) ?? {};
18365
18598
  const deriving = opts.derived !== false;
@@ -18402,7 +18635,7 @@ async function runGet(opts) {
18402
18635
  const owned = declaredCollections.length === 0 ? {} : stampManifests(label, own2.data, declaredCollections, configDir ?? cwd, base, merged.marked, STAMPED_DATE_FIELDS);
18403
18636
  deriveInputs.push({
18404
18637
  label,
18405
- absPath: resolve19(base, label),
18638
+ absPath: resolve20(base, label),
18406
18639
  content,
18407
18640
  extracted: own2,
18408
18641
  ...place !== void 0 ? { provenanceManifest: place } : {},
@@ -18443,7 +18676,7 @@ async function runGet(opts) {
18443
18676
  await readOne(STDIN_LABEL, opts.stdinContent ?? "", forced.extensions[0] ?? "");
18444
18677
  }
18445
18678
  for (const file of files) {
18446
- const content = await readFile11(resolve19(base, file), "utf8");
18679
+ const content = await readFile10(resolve20(base, file), "utf8");
18447
18680
  await readOne(file, content, extname5(file));
18448
18681
  }
18449
18682
  if (deriving) {
@@ -18569,8 +18802,8 @@ function parseJsonPointer(pointer) {
18569
18802
 
18570
18803
  // src/meta/commands/schemas.ts
18571
18804
  import { existsSync as existsSync4 } from "node:fs";
18572
- import { mkdir as mkdir3, readFile as readFile12 } from "node:fs/promises";
18573
- import { dirname as dirname10, extname as extname6, isAbsolute as isAbsolute10, join as join10, relative as relative15, resolve as resolve20, sep as sep10 } from "node:path";
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";
18574
18807
  import { parseDocument as parseDocument8, parse as parseYaml6, stringify as stringify4 } from "yaml";
18575
18808
  function getSchemasInfo(registered2) {
18576
18809
  return {
@@ -18668,9 +18901,9 @@ async function runVendorSchema(opts) {
18668
18901
  );
18669
18902
  }
18670
18903
  const loaded = await loadConfig(opts.configPath, cwd);
18671
- const configPath = loaded?.path ?? resolve20(cwd, DEFAULT_CONFIG_NAME);
18904
+ const configPath = loaded?.path ?? resolve21(cwd, DEFAULT_CONFIG_NAME);
18672
18905
  const configDir = loaded?.dir ?? cwd;
18673
- const absDir = resolve20(cwd, opts.dir ?? DEFAULT_VENDOR_DIR);
18906
+ const absDir = resolve21(cwd, opts.dir ?? DEFAULT_VENDOR_DIR);
18674
18907
  const absFile = join10(absDir, vendorFileName(url));
18675
18908
  await assertNotIgnored(absFile, absDir, cwd, opts.onNotice);
18676
18909
  const fromConfig = posixRelative(configDir, absFile);
@@ -18689,7 +18922,7 @@ async function runVendorSchema(opts) {
18689
18922
  }
18690
18923
  let unchanged = false;
18691
18924
  if (existsSync4(absFile)) {
18692
- const current = await readFile12(absFile);
18925
+ const current = await readFile11(absFile);
18693
18926
  unchanged = bytes.equals(current);
18694
18927
  if (!unchanged && !folded.replaced) {
18695
18928
  throw new DocmetaError(
@@ -18699,7 +18932,7 @@ async function runVendorSchema(opts) {
18699
18932
  }
18700
18933
  await mkdir3(absDir, { recursive: true });
18701
18934
  await writeFileAtomic(absFile, bytes);
18702
- const existing = loaded ? await readFile12(configPath, "utf8") : null;
18935
+ const existing = loaded ? await readFile11(configPath, "utf8") : null;
18703
18936
  const section = loaded ? loaded.section : META_SECTION;
18704
18937
  const text2 = renderConfig(existing, folded.entries, section);
18705
18938
  reparseConfig(text2, posixRelative(cwd, configPath) || DEFAULT_CONFIG_NAME, section);
@@ -18846,7 +19079,7 @@ async function runInferSchema(opts) {
18846
19079
  }
18847
19080
  let absOut;
18848
19081
  if (opts.out !== void 0) {
18849
- absOut = resolve20(cwd, opts.out);
19082
+ absOut = resolve21(cwd, opts.out);
18850
19083
  await assertNotIgnored(
18851
19084
  absOut,
18852
19085
  dirname10(absOut),
@@ -18892,7 +19125,7 @@ async function runInferSchema(opts) {
18892
19125
  configDir: configDir ?? cwd,
18893
19126
  base,
18894
19127
  offline: opts.offline ?? config?.offline ?? false,
18895
- pages: files.map((file) => resolve20(base, file))
19128
+ pages: files.map((file) => resolve21(base, file))
18896
19129
  });
18897
19130
  const stats = /* @__PURE__ */ new Map();
18898
19131
  const unreadable2 = [];
@@ -18951,7 +19184,7 @@ async function runInferSchema(opts) {
18951
19184
  await scanOne(STDIN_LABEL, opts.stdinContent ?? "", forced.extensions[0] ?? "");
18952
19185
  }
18953
19186
  for (const file of files) {
18954
- const content = await readFile12(resolve20(base, file), "utf8");
19187
+ const content = await readFile11(resolve21(base, file), "utf8");
18955
19188
  await scanOne(file, content, extname6(file));
18956
19189
  }
18957
19190
  for (const u of unreadable2) {
@@ -19012,8 +19245,8 @@ async function runInferSchema(opts) {
19012
19245
  // src/meta/commands/query.ts
19013
19246
  import { randomBytes as randomBytes2 } from "node:crypto";
19014
19247
  import { existsSync as existsSync5 } from "node:fs";
19015
- import { mkdir as mkdir4, open as open2, readFile as readFile13, rename as rename3, rm as rm3 } from "node:fs/promises";
19016
- import { dirname as dirname11, isAbsolute as isAbsolute11, resolve as resolve21, extname as extname7, sep as sep11 } from "node:path";
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";
19017
19250
 
19018
19251
  // src/shared/prompt.ts
19019
19252
  import { createInterface } from "node:readline";
@@ -19272,7 +19505,7 @@ async function runQuery(opts) {
19272
19505
  base,
19273
19506
  offline: opts.offline ?? config?.offline ?? false,
19274
19507
  // A `{page}` manifest (0058) is read for exactly the pages this run reads.
19275
- pages: files.map((file) => resolve21(base, file))
19508
+ pages: files.map((file) => resolve22(base, file))
19276
19509
  });
19277
19510
  const configuredKey = lazyKey(configFile, opts.env);
19278
19511
  let ensuredKey;
@@ -19370,10 +19603,10 @@ async function runQuery(opts) {
19370
19603
  await readOne(STDIN_LABEL, opts.stdinContent ?? "", ext);
19371
19604
  }
19372
19605
  for (const file of files) {
19373
- const content = await readFile13(resolve21(base, file), "utf8");
19606
+ const content = await readFile12(resolve22(base, file), "utf8");
19374
19607
  await readOne(file, content, extname7(file));
19375
19608
  }
19376
- 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 };
19377
19610
  const run2 = await runSql(sql, entries, {
19378
19611
  target: db,
19379
19612
  params: boundParams,
@@ -19516,7 +19749,7 @@ async function runSql(sql, entries, ctx) {
19516
19749
  const owned = ctx.declaredCollections.length === 0 ? {} : stampManifests(e.label, e.own.data, ctx.declaredCollections, manifestRoot, ctx.base, e.marked, STAMPED_DATE_FIELDS);
19517
19750
  return {
19518
19751
  label: e.label,
19519
- absPath: resolve21(ctx.base, e.label),
19752
+ absPath: resolve22(ctx.base, e.label),
19520
19753
  content: e.content,
19521
19754
  extracted: e.own,
19522
19755
  ...place !== void 0 ? { provenanceManifest: place } : {},
@@ -19988,14 +20221,14 @@ function assertSchemaWriteWithin(ctx, abs) {
19988
20221
  );
19989
20222
  }
19990
20223
  function foldPath(p) {
19991
- const abs = resolve21(p);
20224
+ const abs = resolve22(p);
19992
20225
  return process.platform === "win32" ? abs.toLowerCase() : abs;
19993
20226
  }
19994
20227
  function filePinsByPath(ctx) {
19995
20228
  const byPath = /* @__PURE__ */ new Map();
19996
20229
  for (const [ref, pin] of collectSchemaPins(ctx.config)) {
19997
20230
  if (classifyRef(ref).kind !== "file") continue;
19998
- byPath.set(foldPath(resolve21(ctx.cwd, ref)), pin);
20231
+ byPath.set(foldPath(resolve22(ctx.cwd, ref)), pin);
19999
20232
  }
20000
20233
  return byPath;
20001
20234
  }
@@ -20024,13 +20257,13 @@ async function loadSetMembers(refs, ctx) {
20024
20257
  });
20025
20258
  continue;
20026
20259
  }
20027
- const abs = registered2?.path ?? resolve21(ctx.cwd, ref);
20260
+ const abs = registered2?.path ?? resolve22(ctx.cwd, ref);
20028
20261
  const canon = foldPath(abs);
20029
20262
  if (seenFiles.has(canon)) continue;
20030
20263
  seenFiles.add(canon);
20031
20264
  let text2;
20032
20265
  try {
20033
- text2 = await readFile13(abs, "utf8");
20266
+ text2 = await readFile12(abs, "utf8");
20034
20267
  } catch {
20035
20268
  throw new DocmetaError(
20036
20269
  `Schema file not found: "${ref}" (looked at ${abs}).`
@@ -20231,7 +20464,7 @@ async function planSchemaMutation(ops, entries, ctx) {
20231
20464
  const [, name2, ver] = target.builtinId.split(":");
20232
20465
  const safe = (s) => s.replace(/[^A-Za-z0-9._-]/g, "-");
20233
20466
  const forkName = `${safe(name2 ?? "schema")}-${safe(ver ?? "0")}.local.json`;
20234
- schemaAbs = resolve21(ctx.configDir ?? ctx.cwd, "schemas", forkName);
20467
+ schemaAbs = resolve22(ctx.configDir ?? ctx.cwd, "schemas", forkName);
20235
20468
  assertSchemaWriteWithin(ctx, schemaAbs);
20236
20469
  if (existsSync5(schemaAbs)) {
20237
20470
  throw new DocmetaError(
@@ -20387,7 +20620,7 @@ async function planConfigEdit(ctx, edit, runRefs) {
20387
20620
  if (configPath === void 0 || configDir === void 0) {
20388
20621
  return { changes: [], repointed: false };
20389
20622
  }
20390
- const text2 = await readFile13(configPath, "utf8");
20623
+ const text2 = await readFile12(configPath, "utf8");
20391
20624
  const doc = parseDocument10(stripBom2(text2));
20392
20625
  const display = displayPath(ctx, configPath);
20393
20626
  const rawRefOf = (item) => {
@@ -20398,7 +20631,7 @@ async function planConfigEdit(ctx, edit, runRefs) {
20398
20631
  }
20399
20632
  return void 0;
20400
20633
  };
20401
- 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;
20402
20635
  const wantSet = runRefs.map(rebaseRaw).sort().join("\n");
20403
20636
  const seqResolvesToRunSet = (node) => {
20404
20637
  if (!isSeq5(node)) return false;
@@ -20443,7 +20676,7 @@ async function planConfigEdit(ctx, edit, runRefs) {
20443
20676
  if (!isMap10(item)) continue;
20444
20677
  const raw = rawRefOf(item);
20445
20678
  if (raw === void 0 || classifyRef(raw).kind !== "file") continue;
20446
- const abs = isAbsolute11(raw) ? raw : resolve21(configDir, raw);
20679
+ const abs = isAbsolute11(raw) ? raw : resolve22(configDir, raw);
20447
20680
  if (foldPath(abs) !== wantPath) continue;
20448
20681
  pinFrom = item.get("integrity");
20449
20682
  item.set("integrity", edit.integrity.value);
@@ -20672,7 +20905,7 @@ function buildChanges(diff, entries, sentinel, ctx, renameHints = [], booleanAdd
20672
20905
  `"${file}": no writable format for that extension.`
20673
20906
  );
20674
20907
  }
20675
- if (existsSync5(resolve21(ctx.base, file))) {
20908
+ if (existsSync5(resolve22(ctx.base, file))) {
20676
20909
  throw new DocmetaError(
20677
20910
  `"${file}" already exists; INSERT creates new files only.`
20678
20911
  );
@@ -20691,7 +20924,7 @@ function buildChanges(diff, entries, sentinel, ctx, renameHints = [], booleanAdd
20691
20924
  `"${from}" -> "${to}": a rename may not change the extension.`
20692
20925
  );
20693
20926
  }
20694
- if (existsSync5(resolve21(ctx.base, to))) {
20927
+ if (existsSync5(resolve22(ctx.base, to))) {
20695
20928
  throw new DocmetaError(
20696
20929
  `"${to}" already exists; the rename would overwrite it.`
20697
20930
  );
@@ -20725,9 +20958,9 @@ function validateNewPath(p, base) {
20725
20958
  if (p.split(/[\\/]/).some((segment) => segment === "..")) {
20726
20959
  refuse("it traverses upward");
20727
20960
  }
20728
- const abs = resolve21(base, p);
20729
- if (abs === resolve21(base)) refuse("it points at the corpus root, not a file");
20730
- if (!abs.startsWith(resolve21(base) + sep11)) refuse("it escapes the corpus");
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");
20731
20964
  }
20732
20965
  function refuseJoinFieldChanges(changes, entries, ctx) {
20733
20966
  const index = ctx.externalMetadata;
@@ -20802,7 +21035,7 @@ function runPages(changes, entries, base) {
20802
21035
  const labels = new Set(entries.map((e) => e.label));
20803
21036
  for (const c of changes) if (!("schema" in c) && !("config" in c)) labels.add(c.file);
20804
21037
  labels.delete(STDIN_LABEL);
20805
- return [...labels].map((label) => resolve21(base, label));
21038
+ return [...labels].map((label) => resolve22(base, label));
20806
21039
  }
20807
21040
  async function planManifestEdits(changes, entries, ctx, renameHints) {
20808
21041
  for (const c of changes) {
@@ -20854,7 +21087,7 @@ async function planManifestEdits(changes, entries, ctx, renameHints) {
20854
21087
  let text2;
20855
21088
  let expected;
20856
21089
  try {
20857
- text2 = await readFile13(home.absPath, "utf8");
21090
+ text2 = await readFile12(home.absPath, "utf8");
20858
21091
  expected = text2;
20859
21092
  } catch (err) {
20860
21093
  if (!home.perPage || !isMissing(err)) {
@@ -20890,7 +21123,7 @@ async function planManifestEdits(changes, entries, ctx, renameHints) {
20890
21123
  );
20891
21124
  const index = await leftOut;
20892
21125
  if (index === null) return void 0;
20893
- const abs = resolve21(ctx.base, file);
21126
+ const abs = resolve22(ctx.base, file);
20894
21127
  const named = index.entries.find(
20895
21128
  (e) => e.join === PATH_JOIN2 && e.abs === abs && excluded.includes(e.collection)
20896
21129
  );
@@ -20937,7 +21170,7 @@ async function planManifestEdits(changes, entries, ctx, renameHints) {
20937
21170
  continue;
20938
21171
  }
20939
21172
  if ("renamed" in c) {
20940
- const supplied = ctx.externalMetadata?.byPath.get(resolve21(ctx.base, c.file));
21173
+ const supplied = ctx.externalMetadata?.byPath.get(resolve22(ctx.base, c.file));
20941
21174
  const seen = /* @__PURE__ */ new Set();
20942
21175
  for (const [key, sv] of supplied ?? []) {
20943
21176
  if (seen.has(sv.file) || !ctx.memberships(c.file).includes(sv.collection)) continue;
@@ -21245,14 +21478,14 @@ async function applyChanges(changes, entries, ctx, schemaPlan, manifestWrites =
21245
21478
  const pendingWrites = [];
21246
21479
  const pendingRenames = [];
21247
21480
  for (const [label, ops] of grouped) {
21248
- const path = resolve21(ctx.base, label);
21481
+ const path = resolve22(ctx.base, label);
21249
21482
  if (ops.renamedTo !== void 0) {
21250
21483
  if (!existsSync5(path)) {
21251
21484
  throw new DocmetaError(
21252
21485
  `"${label}" is gone from disk since it was read; re-run the query.`
21253
21486
  );
21254
21487
  }
21255
- const toDisk = resolve21(ctx.base, ops.renamedTo);
21488
+ const toDisk = resolve22(ctx.base, ops.renamedTo);
21256
21489
  if (existsSync5(toDisk)) {
21257
21490
  throw new DocmetaError(
21258
21491
  `"${ops.renamedTo}" appeared on disk since the plan; re-run the query.`
@@ -21290,7 +21523,7 @@ async function applyChanges(changes, entries, ctx, schemaPlan, manifestWrites =
21290
21523
  }
21291
21524
  const entry = byLabel.get(label);
21292
21525
  if (!entry) throw new DocmetaError(`No loaded entry for "${label}".`);
21293
- const content = await readFile13(path, "utf8");
21526
+ const content = await readFile12(path, "utf8");
21294
21527
  const members = ctx.memberships(label);
21295
21528
  const current = (await mergeWithMarks(
21296
21529
  label,
@@ -21362,7 +21595,7 @@ async function applyChanges(changes, entries, ctx, schemaPlan, manifestWrites =
21362
21595
  }
21363
21596
  let now;
21364
21597
  try {
21365
- now = await readFile13(w.path, "utf8");
21598
+ now = await readFile12(w.path, "utf8");
21366
21599
  } catch {
21367
21600
  now = void 0;
21368
21601
  }
@@ -21375,7 +21608,7 @@ async function applyChanges(changes, entries, ctx, schemaPlan, manifestWrites =
21375
21608
  for (const m of manifestWrites) {
21376
21609
  let now;
21377
21610
  try {
21378
- now = await readFile13(m.path, "utf8");
21611
+ now = await readFile12(m.path, "utf8");
21379
21612
  } catch {
21380
21613
  now = null;
21381
21614
  }
@@ -21508,19 +21741,17 @@ function skipExpression(sql, from) {
21508
21741
  }
21509
21742
 
21510
21743
  // src/meta/commands/fill.ts
21511
- import { readFile as readFile16 } from "node:fs/promises";
21512
- import { resolve as resolve23, extname as extname9, join as join11 } from "node:path";
21744
+ import { readFile as readFile15 } from "node:fs/promises";
21745
+ import { resolve as resolve24, extname as extname9, join as join11 } from "node:path";
21513
21746
  import {
21514
21747
  completeValidatedJSON,
21515
21748
  makeProvider,
21516
- resolveProviderIdentityAsync,
21517
- DEFAULT_MODELS,
21518
21749
  JsonCache,
21519
21750
  buildCacheKey,
21520
21751
  sha256,
21521
21752
  pricingFor,
21522
21753
  costOfUsage,
21523
- InferenceError
21754
+ InferenceError as InferenceError2
21524
21755
  } from "@hawkeyexl/inference";
21525
21756
 
21526
21757
  // src/meta/commands/fill-projection.ts
@@ -21930,8 +22161,8 @@ function single(alternatives2) {
21930
22161
  }
21931
22162
 
21932
22163
  // src/meta/commands/fill-hints.ts
21933
- import { readFile as readFile14 } from "node:fs/promises";
21934
- import { extname as extname8, resolve as resolve22 } from "node:path";
22164
+ import { readFile as readFile13 } from "node:fs/promises";
22165
+ import { extname as extname8, resolve as resolve23 } from "node:path";
21935
22166
 
21936
22167
  // src/shared/reference-fields.ts
21937
22168
  var CONCEPT_FIELDS = [["concepts"], ["graph", "concepts"]];
@@ -22017,7 +22248,7 @@ function pageIdReader(opts) {
22017
22248
  return held;
22018
22249
  };
22019
22250
  return async (file, names) => {
22020
- const self = resolve22(file);
22251
+ const self = resolve23(file);
22021
22252
  const ids = /* @__PURE__ */ new Set();
22022
22253
  for (const name2 of names) {
22023
22254
  const collection = opts.collections.find((c) => c.name === name2);
@@ -22041,10 +22272,10 @@ async function readIds(collection, opts) {
22041
22272
  if (!isMember(collection, file)) continue;
22042
22273
  const extractor = extractorForExtension(extname8(file));
22043
22274
  if (extractor === void 0) continue;
22044
- const path = resolve22(opts.configDir, file);
22275
+ const path = resolve23(opts.configDir, file);
22045
22276
  let id;
22046
22277
  try {
22047
- id = extractor.extract(await readFile14(path, "utf8"), file).data.id;
22278
+ id = extractor.extract(await readFile13(path, "utf8"), file).data.id;
22048
22279
  } catch {
22049
22280
  continue;
22050
22281
  }
@@ -22063,7 +22294,7 @@ function termLabelSource() {
22063
22294
  }
22064
22295
 
22065
22296
  // src/meta/core/manifest-writes.ts
22066
- import { readFile as readFile15 } from "node:fs/promises";
22297
+ import { readFile as readFile14 } from "node:fs/promises";
22067
22298
  var manifestCodec = {
22068
22299
  apply(text2, op) {
22069
22300
  const where = { entry: op.entry, key: op.key, join: op.join, file: op.file };
@@ -22079,7 +22310,7 @@ function applyManifestOp(text2, op) {
22079
22310
  async function holdManifestFile(path, file, perPage = false) {
22080
22311
  let before;
22081
22312
  try {
22082
- before = await readFile15(path, "utf8");
22313
+ before = await readFile14(path, "utf8");
22083
22314
  } catch (err) {
22084
22315
  if (perPage && isMissing(err)) return heldManifest(path, file, "", true);
22085
22316
  throw new DocmetaError(`Manifest ${file} could not be read: ${errorMessage(err)}`);
@@ -22096,7 +22327,7 @@ async function settleManifest(held, io = {}) {
22096
22327
  codec: manifestCodec,
22097
22328
  // A per-page manifest (proposal 0058) may be the first file in its directory.
22098
22329
  write: io.write ?? ((at2, text2) => writeFileAtomic(at2, text2, { createParents: true })),
22099
- read: io.read ?? ((at2) => readFile15(at2, "utf8")),
22330
+ read: io.read ?? ((at2) => readFile14(at2, "utf8")),
22100
22331
  toError: (message) => new DocmetaError(message)
22101
22332
  });
22102
22333
  rebaseline(held);
@@ -22253,12 +22484,56 @@ function buildUserPrompt(params, before = []) {
22253
22484
  ].join("\n");
22254
22485
  }
22255
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
+
22256
22532
  // src/meta/commands/fill.ts
22257
22533
  var META_PROVENANCE_KEY = "meta-provenance";
22258
22534
  var PROVENANCE_KEYS = /* @__PURE__ */ new Set(["provenance", META_PROVENANCE_KEY]);
22259
22535
  var DEFAULT_THRESHOLD = 0.7;
22260
22536
  var DEFAULT_CONCURRENCY = 4;
22261
- var DEFAULT_PROVIDER = "auto";
22262
22537
  var CACHE_DIR = ".manni/meta/cache";
22263
22538
  function looksLikeOverflow(message) {
22264
22539
  return /context|too long|too large|token limit|maximum.*tokens|exceeds/i.test(
@@ -22339,13 +22614,19 @@ async function runFill(opts) {
22339
22614
  const dryRun = Boolean(opts.dryRun);
22340
22615
  const only = opts.fields != null ? new Set(opts.fields) : void 0;
22341
22616
  const managed = new Set(managedFields(config?.derive));
22342
- const requestedProvider = opts.provider ?? config?.fill?.provider ?? DEFAULT_PROVIDER;
22343
- const providerName = opts.local === true && requestedProvider === "auto" ? "llama-cpp" : requestedProvider;
22344
- const model = opts.model ?? config?.fill?.model;
22345
- const spec = { provider: providerName, model: model ?? null };
22346
- assertKnownProvider(providerName);
22347
- assertModelHasProvider(providerName, model);
22348
- if (opts.local === true) assertLocalProvider(providerName);
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 });
22349
22630
  const fileInputs = inputs.filter((i) => i !== STDIN_TOKEN);
22350
22631
  const allowEmpty = opts.allowEmpty ?? config?.allowEmpty;
22351
22632
  const fillExts = opts.exts ?? forcedExtractor?.extensions;
@@ -22381,14 +22662,9 @@ async function runFill(opts) {
22381
22662
  identity = { provider: injected.provider(), model: injected.modelName() };
22382
22663
  construct = () => injected;
22383
22664
  } else {
22384
- const resolved = await resolveIdentity(spec);
22385
- if (opts.local === true) assertLocalProvider(resolved.provider);
22665
+ const resolved = await resolveIdentity2(spec);
22386
22666
  identity = resolved;
22387
- const concrete = {
22388
- ...spec,
22389
- provider: resolved.provider,
22390
- model: resolved.model
22391
- };
22667
+ const concrete = providerSpecFor(family, resolved);
22392
22668
  construct = () => makeProvider(concrete);
22393
22669
  }
22394
22670
  let provider;
@@ -22398,7 +22674,7 @@ async function runFill(opts) {
22398
22674
  provider = construct();
22399
22675
  } catch (err) {
22400
22676
  throw new DocmetaError(
22401
- 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)}`
22402
22678
  );
22403
22679
  }
22404
22680
  }
@@ -22432,7 +22708,7 @@ async function runFill(opts) {
22432
22708
  base,
22433
22709
  offline: opts.offline ?? config?.offline ?? false,
22434
22710
  // A `{page}` manifest (0058) is read for exactly the pages this run fills.
22435
- pages: files.map((file) => resolve23(base, file))
22711
+ pages: files.map((file) => resolve24(base, file))
22436
22712
  });
22437
22713
  const mergeMembersFor = (label) => memberOf(runConfig.declaredCollections, configDir ?? cwd, base, label);
22438
22714
  let externalMetadata = await loadManifests();
@@ -22465,17 +22741,17 @@ async function runFill(opts) {
22465
22741
  const keys = new Set(candidates.map((c) => c.key));
22466
22742
  const wants = (fields) => fields.some(([key]) => key !== void 0 && keys.has(key));
22467
22743
  const termIndex = wants(TERM_FIELDS) ? await (termbase ??= readTermbase()) : void 0;
22468
- 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)) : [];
22469
22745
  const hints = fieldHints(candidates, {
22470
22746
  terms: termIndex === void 0 ? void 0 : offered(termIndex.labels),
22471
22747
  pages: offered(pages2)
22472
22748
  });
22473
22749
  return { hints, termIndex };
22474
22750
  };
22475
- const noticed = /* @__PURE__ */ new Set();
22476
- const noticeOnce = opts.onNotice === void 0 ? void 0 : (message) => {
22477
- if (noticed.has(message)) return;
22478
- noticed.add(message);
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);
22479
22755
  opts.onNotice?.(message);
22480
22756
  };
22481
22757
  const ctx = relocationContext(runConfig, {
@@ -22487,7 +22763,7 @@ async function runFill(opts) {
22487
22763
  ...opts.as !== void 0 ? { as: opts.as } : {},
22488
22764
  ...opts.respectGitignore !== void 0 ? { respectGitignore: opts.respectGitignore } : {},
22489
22765
  ...opts.env !== void 0 ? { env: opts.env } : {},
22490
- ...noticeOnce !== void 0 ? { onNotice: noticeOnce } : {}
22766
+ ...noticeOnce2 !== void 0 ? { onNotice: noticeOnce2 } : {}
22491
22767
  });
22492
22768
  const heldManifests = /* @__PURE__ */ new Map();
22493
22769
  const holding = /* @__PURE__ */ new Map();
@@ -22505,7 +22781,7 @@ async function runFill(opts) {
22505
22781
  };
22506
22782
  const restorePage = async (label, before) => {
22507
22783
  try {
22508
- await writeFileAtomic(resolve23(base, label), before);
22784
+ await writeFileAtomic(resolve24(base, label), before);
22509
22785
  return `${label} was restored.`;
22510
22786
  } catch (err) {
22511
22787
  return `${label} could not be restored: ${errorMessage(err)}. Restore it from version control.`;
@@ -22599,7 +22875,7 @@ async function runFill(opts) {
22599
22875
  // gets written — if anything, a stronger reason to guard it.
22600
22876
  fileBase: cwd,
22601
22877
  trustRoot,
22602
- onNotice: noticeOnce
22878
+ onNotice: noticeOnce2
22603
22879
  });
22604
22880
  } catch (err) {
22605
22881
  return errorResult(label, extractor.name, errorMessage(err));
@@ -22655,7 +22931,7 @@ async function runFill(opts) {
22655
22931
  }));
22656
22932
  const candidates = proposed.filter((c) => !managedHere.has(c.key));
22657
22933
  const destinations = /* @__PURE__ */ new Map();
22658
- const flags = [];
22934
+ const flags2 = [];
22659
22935
  let metaHome;
22660
22936
  if (label !== "<stdin>" && candidates.length > 0) {
22661
22937
  const located = { ...view.data };
@@ -22685,7 +22961,7 @@ async function runFill(opts) {
22685
22961
  if (!meta) destinations.set(key, { ...home, entry: home.entry });
22686
22962
  continue;
22687
22963
  }
22688
- 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 });
22689
22965
  }
22690
22966
  }
22691
22967
  const inPlace = view.plain.flatMap((pointer) => {
@@ -22739,7 +23015,7 @@ async function runFill(opts) {
22739
23015
  sealedCandidates,
22740
23016
  destinations,
22741
23017
  metaHome,
22742
- flags
23018
+ flags: flags2
22743
23019
  };
22744
23020
  };
22745
23021
  const finishOne = async (ready) => {
@@ -23039,7 +23315,12 @@ async function runFill(opts) {
23039
23315
  if (writable.length > 0) {
23040
23316
  const metaManifest = metaHome?.kind === "manifest" ? metaHome : void 0;
23041
23317
  const metaEntry = metaManifest?.entry;
23042
- const mergedProvenance = metaHome?.kind === "url" || metaManifest !== void 0 && metaEntry === void 0 ? void 0 : mergeMetaProvenance(extracted.data[META_PROVENANCE_KEY], identity.model, writable);
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
+ );
23043
23324
  if (metaHome?.kind === "url") {
23044
23325
  metaProvenance = { written: false, skipReason: "manifest-owned", manifest: metaHome.file };
23045
23326
  } else if (metaManifest !== void 0 && metaEntry === void 0) {
@@ -23054,12 +23335,12 @@ async function runFill(opts) {
23054
23335
  metaWrite = { value: mergedProvenance.list, home: { ...metaManifest, entry: metaEntry } };
23055
23336
  metaProvenance = {
23056
23337
  written: true,
23057
- entry: mergedProvenance.entry,
23338
+ entry: { ...mergedProvenance.entry, fields: mergedProvenance.names },
23058
23339
  destination: metaManifest.file
23059
23340
  };
23060
23341
  } else {
23061
23342
  patch[META_PROVENANCE_KEY] = mergedProvenance.list;
23062
- metaProvenance = { written: true, entry: mergedProvenance.entry };
23343
+ metaProvenance = { written: true, entry: { ...mergedProvenance.entry, fields: mergedProvenance.names } };
23063
23344
  }
23064
23345
  }
23065
23346
  const entryWrites = [];
@@ -23128,7 +23409,7 @@ async function runFill(opts) {
23128
23409
  const changed = next !== content || manifestChanged;
23129
23410
  const pageWritten = next !== content && !dryRun && label !== "<stdin>";
23130
23411
  if (pageWritten) {
23131
- await writeFileAtomic(resolve23(base, label), next);
23412
+ await writeFileAtomic(resolve24(base, label), next);
23132
23413
  }
23133
23414
  if (manifestChanged) {
23134
23415
  const perManifest = /* @__PURE__ */ new Map();
@@ -23160,7 +23441,7 @@ async function runFill(opts) {
23160
23441
  const prepareAll = async () => ({
23161
23442
  stdin: usingStdin ? await prepareOne("<stdin>", opts.stdinContent ?? "", forcedExtractor?.extensions[0] ?? "") : void 0,
23162
23443
  files: await mapConcurrent(files, concurrency, async (file) => {
23163
- const content = await readFile16(resolve23(base, file), "utf8");
23444
+ const content = await readFile15(resolve24(base, file), "utf8");
23164
23445
  return prepareOne(file, content, extname9(file));
23165
23446
  })
23166
23447
  });
@@ -23213,31 +23494,6 @@ function topKey(instancePath) {
23213
23494
  return seg.replace(/~1/g, "/").replace(/~0/g, "~");
23214
23495
  }
23215
23496
  var keyOf = (f) => topKey(f.field);
23216
- function isPlainRecord(value) {
23217
- return typeof value === "object" && value !== null && !Array.isArray(value);
23218
- }
23219
- function mergeMetaProvenance(held, model, written) {
23220
- if (held !== void 0 && !Array.isArray(held)) return void 0;
23221
- const list2 = held === void 0 ? [] : [...held];
23222
- const at2 = list2.findIndex((e) => isPlainRecord(e) && e["generated-by"] === model);
23223
- const prior = list2[at2];
23224
- const kept = isPlainRecord(prior) ? prior : {};
23225
- const fields = Array.isArray(kept.fields) ? kept.fields.filter((p) => typeof p === "string") : [];
23226
- const confidence = {};
23227
- if (isPlainRecord(kept.confidence)) {
23228
- for (const [k, v] of Object.entries(kept.confidence)) {
23229
- if (typeof v === "number") confidence[k] = v;
23230
- }
23231
- }
23232
- for (const f of written) {
23233
- if (!fields.includes(f.field)) fields.push(f.field);
23234
- confidence[f.field] = f.confidence;
23235
- }
23236
- const entry = { ...kept, "generated-by": model, fields, confidence };
23237
- if (at2 === -1) list2.push(entry);
23238
- else list2[at2] = entry;
23239
- return { list: list2, entry };
23240
- }
23241
23497
  async function addsFindings(validate, without, list2) {
23242
23498
  const identity = (e) => JSON.stringify([e.schema, e.instancePath, e.keyword, e.subject ?? null, e.message]);
23243
23499
  const before = /* @__PURE__ */ new Map();
@@ -23550,34 +23806,15 @@ function requireNumber(value, name2, bounds) {
23550
23806
  }
23551
23807
  return value;
23552
23808
  }
23553
- var PROVIDERS = /* @__PURE__ */ new Set([...Object.keys(DEFAULT_MODELS), "auto"]);
23554
- function assertLocalProvider(name2) {
23555
- if (name2 === "llama-cpp" || name2 === "mock") return;
23556
- if (name2 === "auto") return;
23557
- throw new DocmetaError(
23558
- name2 === "claude-cli" ? `--local cannot use "claude-cli": the CLI runs on this machine but its inference does not. Use --provider llama-cpp.` : `--local cannot use "${name2}", which sends document content to a hosted API. Use --provider llama-cpp, or drop --local.`
23559
- );
23560
- }
23561
- function assertKnownProvider(name2) {
23562
- if (PROVIDERS.has(name2)) return;
23563
- throw new DocmetaError(
23564
- `Unknown provider "${name2}". Available: ${[...PROVIDERS].join(", ")}.`
23565
- );
23809
+ var toDocmetaError = (message) => new DocmetaError(message);
23810
+ function assertKnownProvider2(name2) {
23811
+ assertKnownProvider(name2, toDocmetaError);
23566
23812
  }
23567
- function assertModelHasProvider(name2, model) {
23568
- if (name2 !== "auto" || model == null) return;
23569
- throw new DocmetaError(
23570
- `Model "${model}" was given without a provider: a model name does not say which provider owns it. Set --provider or fill.provider to one of ${Object.keys(DEFAULT_MODELS).join(", ")}, or drop the model to take the detected provider's default.`
23571
- );
23813
+ function assertModelHasProvider2(name2, model) {
23814
+ assertModelHasProvider(name2, model, "fill.provider", toDocmetaError);
23572
23815
  }
23573
- async function resolveIdentity(spec) {
23574
- try {
23575
- return await resolveProviderIdentityAsync(spec);
23576
- } catch (err) {
23577
- throw new DocmetaError(
23578
- err instanceof InferenceError ? err.message : `Could not resolve provider "${spec.provider}": ${errorMessage(err)}`
23579
- );
23580
- }
23816
+ function resolveIdentity2(spec) {
23817
+ return resolveIdentity(spec, toDocmetaError);
23581
23818
  }
23582
23819
  function summarize(results, costUsd, cached) {
23583
23820
  let written = 0;
@@ -23622,8 +23859,8 @@ async function mapConcurrent(items2, limit, fn) {
23622
23859
  }
23623
23860
 
23624
23861
  // src/meta/commands/derive.ts
23625
- import { readFile as readFile17 } from "node:fs/promises";
23626
- import { extname as extname10, relative as relative16, resolve as resolve24, sep as sep12 } from "node:path";
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";
23627
23864
  var GENERATED_BY_ENV = "MANNI_GENERATED_BY";
23628
23865
  var SOURCE_HINT = "narrow --sources or --fields";
23629
23866
  async function runDerive(opts) {
@@ -23741,7 +23978,7 @@ async function runDerive(opts) {
23741
23978
  const files = fromCollections ? retainMembers(walked, membersFor) : walked;
23742
23979
  const rangesByPage = /* @__PURE__ */ new Map();
23743
23980
  for (const t of targets) {
23744
- const label = relative16(base, resolve24(base, t.page)).split(sep12).join("/");
23981
+ const label = relative16(base, resolve25(base, t.page)).split(sep12).join("/");
23745
23982
  if (!files.includes(label)) {
23746
23983
  throw new DocmetaError(
23747
23984
  `${t.target} does not name one file that derive reads; a range names lines of one file.`
@@ -23766,7 +24003,7 @@ async function runDerive(opts) {
23766
24003
  const loaded2 = /* @__PURE__ */ new Map();
23767
24004
  const errors2 = /* @__PURE__ */ new Map();
23768
24005
  for (const label of files) {
23769
- const absPath = resolve24(base, label);
24006
+ const absPath = resolve25(base, label);
23770
24007
  const extractor = forcedExtractor ?? extractorForExtension(extname10(label));
23771
24008
  if (!extractor) {
23772
24009
  errors2.set(label, {
@@ -23775,7 +24012,7 @@ async function runDerive(opts) {
23775
24012
  });
23776
24013
  continue;
23777
24014
  }
23778
- const content = await readFile17(absPath, "utf8");
24015
+ const content = await readFile16(absPath, "utf8");
23779
24016
  const elements = resolveElements(label, config, membersFor(label));
23780
24017
  try {
23781
24018
  loaded2.set(label, {
@@ -23818,7 +24055,7 @@ async function runDerive(opts) {
23818
24055
  runFields = union2;
23819
24056
  }
23820
24057
  const provenanceRoot = configDir ?? cwd;
23821
- const pages2 = files.map((label) => resolve24(base, label));
24058
+ const pages2 = files.map((label) => resolve25(base, label));
23822
24059
  const manifests = runFields.includes(PROVENANCE_FIELD) ? provenanceManifests(declaredCollections, provenanceRoot, base) : [];
23823
24060
  const provenanceCollections = declaredCollections.map((c) => ({
23824
24061
  ...c,
@@ -23910,10 +24147,10 @@ async function runDerive(opts) {
23910
24147
  const members = memberOf(prepared.provenanceCollections, root, base, doc.label);
23911
24148
  return (await mergeWithMarks(doc.label, doc.extracted, prepared.provenanceIndex, members, base, { marks })).extracted.data[PROVENANCE_FIELD];
23912
24149
  };
23913
- const noticed = /* @__PURE__ */ new Set();
23914
- const noticeOnce = opts.onNotice === void 0 ? void 0 : (message) => {
23915
- if (noticed.has(message)) return;
23916
- noticed.add(message);
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);
23917
24154
  opts.onNotice?.(message);
23918
24155
  };
23919
24156
  const markFields = async (doc, record) => {
@@ -23944,7 +24181,7 @@ async function runDerive(opts) {
23944
24181
  // `$schema`, since that schema decides what gets encrypted.
23945
24182
  fileBase: cwd,
23946
24183
  trustRoot,
23947
- onNotice: noticeOnce
24184
+ onNotice: noticeOnce2
23948
24185
  });
23949
24186
  } catch (err) {
23950
24187
  return { error: errorMessage(err) };
@@ -24005,7 +24242,7 @@ async function runDerive(opts) {
24005
24242
  validator,
24006
24243
  ...opts.noConfig === true ? { noConfig: true } : {},
24007
24244
  ...opts.env !== void 0 ? { env: opts.env } : {},
24008
- ...noticeOnce !== void 0 ? { onNotice: noticeOnce } : {}
24245
+ ...noticeOnce2 !== void 0 ? { onNotice: noticeOnce2 } : {}
24009
24246
  });
24010
24247
  const externalWrites = async () => {
24011
24248
  const out = [];
@@ -24988,15 +25225,28 @@ export {
24988
25225
  generateEncryptionKey,
24989
25226
  encryptValue,
24990
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,
24991
25240
  findGitRoot,
24992
25241
  valeConfigPath,
24993
25242
  ditaOtHome,
24994
25243
  programName,
24995
25244
  warn,
24996
25245
  notice,
24997
- ToolError,
24998
- errorMessage,
25246
+ noticeOnce,
24999
25247
  FAMILY_CONFIG_NAMES,
25248
+ findConfigFileSync,
25249
+ readConfigFileSync,
25000
25250
  findConfigFile,
25001
25251
  readConfigFile,
25002
25252
  findFamilyConfigFile,
@@ -25058,7 +25308,8 @@ export {
25058
25308
  DEFAULT_TTL_HOURS,
25059
25309
  schemaCacheDir,
25060
25310
  SchemaCache,
25061
- __default34 as __default,
25311
+ __default31 as __default,
25312
+ __default34 as __default2,
25062
25313
  listBuiltins,
25063
25314
  classifyRef,
25064
25315
  fetchSchemaBytes,
@@ -25082,6 +25333,7 @@ export {
25082
25333
  staleFindings,
25083
25334
  LEGACY_CONFIG_NAMES,
25084
25335
  parseConfig,
25336
+ schemaTrustRoot,
25085
25337
  loadConfig,
25086
25338
  resolveRunConfig,
25087
25339
  renderSarif,
@@ -25124,19 +25376,26 @@ export {
25124
25376
  spliceManifestValue,
25125
25377
  readManifestValue,
25126
25378
  removeManifestKey,
25379
+ isParamName,
25380
+ memberOf,
25127
25381
  LOCATION_KEYWORD,
25128
25382
  LOCATION_EXTERNAL_SCHEMA,
25129
25383
  LOCATION_PAGE_SCHEMA,
25130
25384
  Validator,
25385
+ pageMarks,
25386
+ marksValidator,
25131
25387
  configMarks,
25132
- isParamName,
25133
- memberOf,
25134
25388
  relocateFailed,
25135
25389
  relocationContext,
25136
25390
  keyHome,
25137
25391
  planRelocation,
25138
25392
  applyRelocation,
25139
25393
  offerPrompt,
25394
+ offerExternalHomes,
25395
+ externalWriteWarnings,
25396
+ mergeMetaProvenance,
25397
+ metaProvenanceEntries,
25398
+ provenanceEntries,
25140
25399
  REVIEW_CACHE_DIR,
25141
25400
  REVIEW_CACHE_VERSION,
25142
25401
  ReviewCache,
@@ -25195,4 +25454,4 @@ export {
25195
25454
  renderQuery,
25196
25455
  renderQueryCsv
25197
25456
  };
25198
- //# sourceMappingURL=chunk-TQG3WCJY.js.map
25457
+ //# sourceMappingURL=chunk-I57KBF45.js.map