@hawkeyexl/manni 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-2OALUJV2.js → chunk-7M2YOXVG.js} +1224 -537
- package/dist/chunk-7M2YOXVG.js.map +1 -0
- package/dist/{chunk-OXH2ZM7E.js → chunk-BKJOQHWN.js} +2 -2
- package/dist/cli.js +2 -2
- package/dist/docmeta.js +2 -2
- package/dist/index.d.ts +228 -2
- package/dist/index.js +31 -1
- package/package.json +1 -1
- package/dist/chunk-2OALUJV2.js.map +0 -1
- /package/dist/{chunk-OXH2ZM7E.js.map → chunk-BKJOQHWN.js.map} +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@hawkeyexl/manni",
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.3.0",
|
|
7
7
|
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.",
|
|
8
8
|
type: "module",
|
|
9
9
|
bin: {
|
|
@@ -154,8 +154,8 @@ var RENAME_ATTEMPTS = 3;
|
|
|
154
154
|
function errorCode(err) {
|
|
155
155
|
return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
|
|
156
156
|
}
|
|
157
|
-
var wait = (ms) => new Promise((
|
|
158
|
-
setTimeout(
|
|
157
|
+
var wait = (ms) => new Promise((resolve14) => {
|
|
158
|
+
setTimeout(resolve14, ms);
|
|
159
159
|
});
|
|
160
160
|
async function writeFileAtomic(path, contents) {
|
|
161
161
|
const tmp = join(
|
|
@@ -2611,11 +2611,17 @@ var BUILTINS = /* @__PURE__ */ new Map([
|
|
|
2611
2611
|
["anthropic:claude-subagent:2.1", __default23]
|
|
2612
2612
|
]);
|
|
2613
2613
|
function assertPublishableBuiltinId(id) {
|
|
2614
|
-
|
|
2614
|
+
const first = id.split(":")[0];
|
|
2615
|
+
if (first === "check") {
|
|
2615
2616
|
throw new Error(
|
|
2616
2617
|
`Built-in id "${id}" is not publishable: the "check" first segment is reserved for corpus check identities (check:<name>, proposal 0026).`
|
|
2617
2618
|
);
|
|
2618
2619
|
}
|
|
2620
|
+
if (first === "sidecar") {
|
|
2621
|
+
throw new Error(
|
|
2622
|
+
`Built-in id "${id}" is not publishable: the "sidecar" first segment is reserved for sidecar finding identities (sidecar:owned, proposal 0037).`
|
|
2623
|
+
);
|
|
2624
|
+
}
|
|
2619
2625
|
}
|
|
2620
2626
|
for (const id of BUILTINS.keys()) assertPublishableBuiltinId(id);
|
|
2621
2627
|
function listBuiltins() {
|
|
@@ -2743,8 +2749,8 @@ var SCHEMA_KEYS = [
|
|
|
2743
2749
|
"multipleOf"
|
|
2744
2750
|
];
|
|
2745
2751
|
var RETRY_DELAY_MS = 500;
|
|
2746
|
-
var wait2 = (ms) => new Promise((
|
|
2747
|
-
setTimeout(
|
|
2752
|
+
var wait2 = (ms) => new Promise((resolve14) => {
|
|
2753
|
+
setTimeout(resolve14, ms);
|
|
2748
2754
|
});
|
|
2749
2755
|
var RetryableFetchError = class extends DocmetaError {
|
|
2750
2756
|
};
|
|
@@ -2996,6 +3002,277 @@ async function loadSchema(ref, options = {}) {
|
|
|
2996
3002
|
}
|
|
2997
3003
|
}
|
|
2998
3004
|
|
|
3005
|
+
// src/meta/core/resolve-schema.ts
|
|
3006
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
3007
|
+
import picomatch from "picomatch";
|
|
3008
|
+
var DEFAULT_SCHEMAS = Object.freeze([
|
|
3009
|
+
"google:okf:0.1",
|
|
3010
|
+
"passo-uno:seven-action:1.0"
|
|
3011
|
+
]);
|
|
3012
|
+
var FILE_SCHEMA_KEY = "$schema";
|
|
3013
|
+
function schemaEntryRef(entry) {
|
|
3014
|
+
return typeof entry === "string" ? entry : entry.ref;
|
|
3015
|
+
}
|
|
3016
|
+
function collectSchemaPins(config) {
|
|
3017
|
+
const pins = /* @__PURE__ */ new Map();
|
|
3018
|
+
for (const entry of config?.schemas ?? []) {
|
|
3019
|
+
if (typeof entry === "string") continue;
|
|
3020
|
+
if (entry.source === void 0 && entry.integrity === void 0) continue;
|
|
3021
|
+
pins.set(entry.ref, {
|
|
3022
|
+
...entry.source !== void 0 ? { source: entry.source } : {},
|
|
3023
|
+
...entry.integrity !== void 0 ? { integrity: entry.integrity } : {}
|
|
3024
|
+
});
|
|
3025
|
+
}
|
|
3026
|
+
return pins;
|
|
3027
|
+
}
|
|
3028
|
+
function rebaseConfigSchemaRefs(config, configDir, cwd) {
|
|
3029
|
+
if (resolve(configDir) === resolve(cwd)) return config;
|
|
3030
|
+
const rebase = (ref) => classifyRef(ref).kind === "file" && !isAbsolute(ref) ? resolve(configDir, ref) : ref;
|
|
3031
|
+
const rebaseEntry = (entry) => typeof entry === "string" ? rebase(entry) : {
|
|
3032
|
+
...entry,
|
|
3033
|
+
ref: rebase(entry.ref),
|
|
3034
|
+
...entry.source !== void 0 ? { source: rebase(entry.source) } : {}
|
|
3035
|
+
};
|
|
3036
|
+
return {
|
|
3037
|
+
...config,
|
|
3038
|
+
...config.schemas ? { schemas: config.schemas.map(rebaseEntry) } : {},
|
|
3039
|
+
...config.overrides ? {
|
|
3040
|
+
overrides: config.overrides.map((o) => ({
|
|
3041
|
+
...o,
|
|
3042
|
+
schemas: o.schemas.map(rebase)
|
|
3043
|
+
}))
|
|
3044
|
+
} : {}
|
|
3045
|
+
};
|
|
3046
|
+
}
|
|
3047
|
+
var matcherCache = /* @__PURE__ */ new Map();
|
|
3048
|
+
function overrideGlobs(files) {
|
|
3049
|
+
return typeof files === "string" ? [files] : files;
|
|
3050
|
+
}
|
|
3051
|
+
function matchesFileGlob(glob, filePath) {
|
|
3052
|
+
return matches(glob, filePath);
|
|
3053
|
+
}
|
|
3054
|
+
function matches(glob, filePath) {
|
|
3055
|
+
const key = JSON.stringify(glob);
|
|
3056
|
+
let m = matcherCache.get(key);
|
|
3057
|
+
if (!m) {
|
|
3058
|
+
m = picomatch(glob, { dot: true });
|
|
3059
|
+
matcherCache.set(key, m);
|
|
3060
|
+
}
|
|
3061
|
+
return m(filePath.replace(/\\/g, "/"));
|
|
3062
|
+
}
|
|
3063
|
+
function coerceFileSchema(value) {
|
|
3064
|
+
if (value === void 0 || value === null) return void 0;
|
|
3065
|
+
if (typeof value === "string") return [value];
|
|
3066
|
+
if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
|
|
3067
|
+
return value;
|
|
3068
|
+
}
|
|
3069
|
+
throw new Error(
|
|
3070
|
+
`Invalid "${FILE_SCHEMA_KEY}": must be a string or a list of strings.`
|
|
3071
|
+
);
|
|
3072
|
+
}
|
|
3073
|
+
function dedupe(refs) {
|
|
3074
|
+
return [...new Set(refs)];
|
|
3075
|
+
}
|
|
3076
|
+
function hostAllowed(url, hosts) {
|
|
3077
|
+
const hostname = url.hostname.toLowerCase();
|
|
3078
|
+
const host = url.host.toLowerCase();
|
|
3079
|
+
return hosts.some((h) => {
|
|
3080
|
+
const want = h.trim().toLowerCase();
|
|
3081
|
+
return want === hostname || want === host;
|
|
3082
|
+
});
|
|
3083
|
+
}
|
|
3084
|
+
function assertDocumentRefAllowed(ref, mode, params) {
|
|
3085
|
+
const { kind } = classifyRef(ref);
|
|
3086
|
+
if (kind === "builtin" || isPublishedBuiltinUrl(ref)) return;
|
|
3087
|
+
if (kind === "url") {
|
|
3088
|
+
if (mode === "local") {
|
|
3089
|
+
throw new DocmetaError(
|
|
3090
|
+
`Refusing the "${FILE_SCHEMA_KEY}" URL "${ref}": schemaTrust.documentRefs is "local", so a document may name a built-in id or a schema file inside the repository, but not a URL. Vendor it with \`manni meta schemas vendor ${ref}\` and reference the local copy, or put the URL in \`schemas:\` where an operator controls it.`
|
|
3091
|
+
);
|
|
3092
|
+
}
|
|
3093
|
+
const hosts = params.config?.schemaTrust?.hosts;
|
|
3094
|
+
if (!hosts || hosts.length === 0) return;
|
|
3095
|
+
let url;
|
|
3096
|
+
try {
|
|
3097
|
+
url = new URL(ref);
|
|
3098
|
+
} catch {
|
|
3099
|
+
throw new DocmetaError(
|
|
3100
|
+
`Refusing the "${FILE_SCHEMA_KEY}" URL "${ref}": it cannot be parsed as a URL, so it cannot be checked against schemaTrust.hosts.`
|
|
3101
|
+
);
|
|
3102
|
+
}
|
|
3103
|
+
if (!hostAllowed(url, hosts)) {
|
|
3104
|
+
throw new DocmetaError(
|
|
3105
|
+
`Refusing the "${FILE_SCHEMA_KEY}" URL "${ref}": host "${url.host}" is not in schemaTrust.hosts (${hosts.join(", ")}). Add the host there, or reference a schema the config already names.`
|
|
3106
|
+
);
|
|
3107
|
+
}
|
|
3108
|
+
return;
|
|
3109
|
+
}
|
|
3110
|
+
const root = params.trustRoot;
|
|
3111
|
+
if (!root) return;
|
|
3112
|
+
const abs = resolve(params.fileBase ?? process.cwd(), ref);
|
|
3113
|
+
const within = relative(root.dir, abs);
|
|
3114
|
+
if (within !== "" && !within.startsWith("..") && !isAbsolute(within)) return;
|
|
3115
|
+
const boundary = root.source === "git" ? `the repository root (${root.dir})` : root.source === "config" ? `${root.dir} \u2014 no git repository was found, so the config file's own directory is the boundary` : `${root.dir} \u2014 no git repository and no config file were found, so the directory the command was run from is the boundary`;
|
|
3116
|
+
const scope = root.source === "git" ? "the repository" : "the project";
|
|
3117
|
+
throw new DocmetaError(
|
|
3118
|
+
`Refusing the "${FILE_SCHEMA_KEY}" path "${ref}": it resolves outside ${boundary}. A document may only name a schema inside ${scope}; a path in \`schemas:\` or on \`--schema\` is not restricted this way.`
|
|
3119
|
+
);
|
|
3120
|
+
}
|
|
3121
|
+
function resolveSchemaSetWithSource(params) {
|
|
3122
|
+
const { filePath, fileSchema, cliSchemas, config } = params;
|
|
3123
|
+
if (cliSchemas && cliSchemas.length > 0) {
|
|
3124
|
+
return { schemas: dedupe(cliSchemas), source: "cli" };
|
|
3125
|
+
}
|
|
3126
|
+
const fromFile = coerceFileSchema(fileSchema);
|
|
3127
|
+
if (fromFile && fromFile.length > 0) {
|
|
3128
|
+
const mode = config?.schemaTrust?.documentRefs ?? "any";
|
|
3129
|
+
if (mode === "none") {
|
|
3130
|
+
params.onNotice?.(
|
|
3131
|
+
`${filePath}: "${FILE_SCHEMA_KEY}" is ignored (${fromFile.join(", ")}) \u2014 schemaTrust.documentRefs is "none", so the schema set comes from the config instead. Remove the key from the document, or set schemaTrust.documentRefs to "local" or "any" to honor it.`
|
|
3132
|
+
);
|
|
3133
|
+
} else {
|
|
3134
|
+
for (const ref of fromFile) assertDocumentRefAllowed(ref, mode, params);
|
|
3135
|
+
return { schemas: dedupe(fromFile), source: "document" };
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
if (config?.overrides) {
|
|
3139
|
+
for (const [i, ov] of config.overrides.entries()) {
|
|
3140
|
+
if (matches(ov.files, filePath) && ov.schemas.length > 0) {
|
|
3141
|
+
return { schemas: dedupe(ov.schemas), source: "override", overrideIndex: i };
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
if (config?.schemas && config.schemas.length > 0) {
|
|
3146
|
+
return {
|
|
3147
|
+
schemas: dedupe(config.schemas.map(schemaEntryRef)),
|
|
3148
|
+
source: "config"
|
|
3149
|
+
};
|
|
3150
|
+
}
|
|
3151
|
+
return { schemas: [...DEFAULT_SCHEMAS], source: "default" };
|
|
3152
|
+
}
|
|
3153
|
+
function resolveSchemaSet(params) {
|
|
3154
|
+
return resolveSchemaSetWithSource(params).schemas;
|
|
3155
|
+
}
|
|
3156
|
+
function resolveElements(filePath, config) {
|
|
3157
|
+
const out = [];
|
|
3158
|
+
if (config?.elements) out.push(...config.elements);
|
|
3159
|
+
for (const ov of config?.overrides ?? []) {
|
|
3160
|
+
if (ov.elements && matches(ov.files, filePath)) out.push(...ov.elements);
|
|
3161
|
+
}
|
|
3162
|
+
return dedupe(out);
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
// src/meta/core/sidecar-fetch.ts
|
|
3166
|
+
var SIDECAR_FETCH_TIMEOUT_MS = 1e4;
|
|
3167
|
+
var SIDECAR_FETCH_MAX_BYTES = 5 * 1024 * 1024;
|
|
3168
|
+
var MAX_REDIRECTS = 5;
|
|
3169
|
+
var RETRY_DELAY_MS2 = 500;
|
|
3170
|
+
function sidecarUrlProblem(url) {
|
|
3171
|
+
let parsed2;
|
|
3172
|
+
try {
|
|
3173
|
+
parsed2 = new URL(url);
|
|
3174
|
+
} catch {
|
|
3175
|
+
return "is not a valid URL";
|
|
3176
|
+
}
|
|
3177
|
+
if (parsed2.username !== "" || parsed2.password !== "") {
|
|
3178
|
+
return 'carries a credential in the URL; put the token in an environment variable and name it with "tokenEnv"';
|
|
3179
|
+
}
|
|
3180
|
+
if (parsed2.protocol === "https:") return null;
|
|
3181
|
+
if (parsed2.protocol === "http:" && isLoopback(parsed2.hostname)) return null;
|
|
3182
|
+
if (parsed2.protocol === "http:") {
|
|
3183
|
+
return "is plain http://; a bearer token over plaintext is a leak, and a public manifest is served over https too";
|
|
3184
|
+
}
|
|
3185
|
+
return `has the unsupported scheme "${parsed2.protocol}"`;
|
|
3186
|
+
}
|
|
3187
|
+
function isLoopback(host) {
|
|
3188
|
+
return host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host === "::1" || host.startsWith("127.");
|
|
3189
|
+
}
|
|
3190
|
+
async function fetchSidecar(url, opts = {}) {
|
|
3191
|
+
if (opts.offline) {
|
|
3192
|
+
throw new DocmetaError(
|
|
3193
|
+
`Sidecar manifest ${url} is remote and the run is offline. Vendor it to a path, or drop --offline.`
|
|
3194
|
+
);
|
|
3195
|
+
}
|
|
3196
|
+
const timeoutMs = opts.timeoutMs ?? SIDECAR_FETCH_TIMEOUT_MS;
|
|
3197
|
+
const maxBytes = opts.maxBytes ?? SIDECAR_FETCH_MAX_BYTES;
|
|
3198
|
+
const env = opts.env ?? process.env;
|
|
3199
|
+
let token;
|
|
3200
|
+
if (opts.tokenEnv !== void 0) {
|
|
3201
|
+
token = env[opts.tokenEnv];
|
|
3202
|
+
if (token === void 0 || token === "") {
|
|
3203
|
+
throw new DocmetaError(
|
|
3204
|
+
`Sidecar manifest ${url}: the environment variable ${opts.tokenEnv} named by "tokenEnv" is not set.`
|
|
3205
|
+
);
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
let res = await requestWithRedirects(url, token, timeoutMs);
|
|
3209
|
+
if (res instanceof Error || res.status >= 500) {
|
|
3210
|
+
await new Promise((r) => setTimeout(r, RETRY_DELAY_MS2));
|
|
3211
|
+
res = await requestWithRedirects(url, token, timeoutMs);
|
|
3212
|
+
}
|
|
3213
|
+
if (res instanceof Error) {
|
|
3214
|
+
throw new DocmetaError(
|
|
3215
|
+
`Sidecar manifest ${url} could not be fetched: ${res.message}`
|
|
3216
|
+
);
|
|
3217
|
+
}
|
|
3218
|
+
if (!res.ok) {
|
|
3219
|
+
const hint = res.status === 404 && token === void 0 ? ' (a private file answers 404 without a token; set "tokenEnv")' : res.status === 404 ? " (a private file answers 404 without a valid token)" : "";
|
|
3220
|
+
throw new DocmetaError(
|
|
3221
|
+
`Sidecar manifest ${url} could not be fetched: HTTP ${res.status}${hint}.`
|
|
3222
|
+
);
|
|
3223
|
+
}
|
|
3224
|
+
return readCapped(url, res, maxBytes);
|
|
3225
|
+
}
|
|
3226
|
+
async function requestWithRedirects(url, token, timeoutMs) {
|
|
3227
|
+
const origin = new URL(url).origin;
|
|
3228
|
+
let current = url;
|
|
3229
|
+
for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
|
|
3230
|
+
const sameOrigin = new URL(current).origin === origin;
|
|
3231
|
+
const headers = { accept: "text/plain, application/x-yaml, */*" };
|
|
3232
|
+
if (token !== void 0 && sameOrigin) headers.authorization = `Bearer ${token}`;
|
|
3233
|
+
let res;
|
|
3234
|
+
try {
|
|
3235
|
+
res = await fetch(current, {
|
|
3236
|
+
headers,
|
|
3237
|
+
redirect: "manual",
|
|
3238
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
3239
|
+
});
|
|
3240
|
+
} catch (err) {
|
|
3241
|
+
const isAbort2 = err instanceof Error && err.name === "TimeoutError";
|
|
3242
|
+
return isAbort2 ? new Error(`timed out after ${timeoutMs}ms`) : new Error(err instanceof Error ? err.message : String(err));
|
|
3243
|
+
}
|
|
3244
|
+
if (res.status >= 300 && res.status < 400) {
|
|
3245
|
+
const location = res.headers.get("location");
|
|
3246
|
+
if (location === null) return res;
|
|
3247
|
+
await res.body?.cancel();
|
|
3248
|
+
current = new URL(location, current).toString();
|
|
3249
|
+
continue;
|
|
3250
|
+
}
|
|
3251
|
+
return res;
|
|
3252
|
+
}
|
|
3253
|
+
return new Error(`more than ${MAX_REDIRECTS} redirects`);
|
|
3254
|
+
}
|
|
3255
|
+
async function readCapped(url, res, maxBytes) {
|
|
3256
|
+
const body = res.body;
|
|
3257
|
+
if (!body) return "";
|
|
3258
|
+
const reader = body.getReader();
|
|
3259
|
+
const chunks = [];
|
|
3260
|
+
let total = 0;
|
|
3261
|
+
for (; ; ) {
|
|
3262
|
+
const { done, value } = await reader.read();
|
|
3263
|
+
if (done || value === void 0) break;
|
|
3264
|
+
total += value.byteLength;
|
|
3265
|
+
if (total > maxBytes) {
|
|
3266
|
+
await reader.cancel();
|
|
3267
|
+
throw new DocmetaError(
|
|
3268
|
+
`Sidecar manifest ${url} is too large: the response exceeds the ${maxBytes}-byte limit.`
|
|
3269
|
+
);
|
|
3270
|
+
}
|
|
3271
|
+
chunks.push(value);
|
|
3272
|
+
}
|
|
3273
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
3274
|
+
}
|
|
3275
|
+
|
|
2999
3276
|
// src/meta/extractors/frontmatter.ts
|
|
3000
3277
|
import { parseDocument, LineCounter, isMap, isSeq, isScalar } from "yaml";
|
|
3001
3278
|
import { parse as parseToml } from "smol-toml";
|
|
@@ -3755,8 +4032,8 @@ function ditaShape(content, root, filePath) {
|
|
|
3755
4032
|
const lower = filePath?.toLowerCase() ?? "";
|
|
3756
4033
|
const byExtension2 = (lower.endsWith(".dita") || lower.endsWith(".ditamap")) && DITA_ROOTS.has(name2);
|
|
3757
4034
|
if (!byDoctype && !byClass && !byArch && !byExtension2) return void 0;
|
|
3758
|
-
const
|
|
3759
|
-
return
|
|
4035
|
+
const isMap5 = /^\s*[-+]\s+map\//.test(cls) || MAP_ROOTS.has(name2) || lower.endsWith(".ditamap");
|
|
4036
|
+
return isMap5 ? { kind: "map", container: "topicmeta", preamble: /* @__PURE__ */ new Set(["title"]) } : {
|
|
3760
4037
|
kind: "topic",
|
|
3761
4038
|
container: "metadata",
|
|
3762
4039
|
preamble: /* @__PURE__ */ new Set([
|
|
@@ -5181,202 +5458,477 @@ function listFormats() {
|
|
|
5181
5458
|
}));
|
|
5182
5459
|
}
|
|
5183
5460
|
|
|
5184
|
-
// src/meta/core/
|
|
5185
|
-
import {
|
|
5186
|
-
import
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5461
|
+
// src/meta/core/sidecars.ts
|
|
5462
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
5463
|
+
import { isAbsolute as isAbsolute2, relative as relative4, resolve as resolve5, sep as sep2 } from "node:path";
|
|
5464
|
+
import {
|
|
5465
|
+
LineCounter as LineCounter2,
|
|
5466
|
+
isMap as isMap3,
|
|
5467
|
+
isNode,
|
|
5468
|
+
isScalar as isScalar2,
|
|
5469
|
+
parseDocument as parseDocument3
|
|
5470
|
+
} from "yaml";
|
|
5471
|
+
|
|
5472
|
+
// src/meta/core/load-files.ts
|
|
5473
|
+
import { stat as stat3 } from "node:fs/promises";
|
|
5474
|
+
import { extname, relative as relative3, resolve as resolve4 } from "node:path";
|
|
5475
|
+
import fg from "fast-glob";
|
|
5476
|
+
import picomatch2 from "picomatch";
|
|
5477
|
+
|
|
5478
|
+
// src/meta/core/gitignore.ts
|
|
5479
|
+
import { spawn } from "node:child_process";
|
|
5480
|
+
import { dirname as dirname3, relative as relative2, resolve as resolve3, sep } from "node:path";
|
|
5481
|
+
|
|
5482
|
+
// src/shared/git-root.ts
|
|
5483
|
+
import { existsSync } from "node:fs";
|
|
5484
|
+
import { dirname as dirname2, join as join3, resolve as resolve2 } from "node:path";
|
|
5485
|
+
function findGitRoot(cwd) {
|
|
5486
|
+
let dir = resolve2(cwd);
|
|
5487
|
+
for (; ; ) {
|
|
5488
|
+
if (existsSync(join3(dir, ".git"))) return dir;
|
|
5489
|
+
const parent = dirname2(dir);
|
|
5490
|
+
if (parent === dir) return null;
|
|
5491
|
+
dir = parent;
|
|
5204
5492
|
}
|
|
5205
|
-
return pins;
|
|
5206
5493
|
}
|
|
5207
|
-
function
|
|
5208
|
-
|
|
5209
|
-
const
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
...o,
|
|
5221
|
-
schemas: o.schemas.map(rebase)
|
|
5222
|
-
}))
|
|
5223
|
-
} : {}
|
|
5224
|
-
};
|
|
5494
|
+
function searchPath(cwd) {
|
|
5495
|
+
const start = resolve2(cwd);
|
|
5496
|
+
const root = findGitRoot(start);
|
|
5497
|
+
if (root === null) return [start];
|
|
5498
|
+
const chain = [];
|
|
5499
|
+
let dir = start;
|
|
5500
|
+
for (; ; ) {
|
|
5501
|
+
chain.push(dir);
|
|
5502
|
+
if (dir === root) return chain;
|
|
5503
|
+
const parent = dirname2(dir);
|
|
5504
|
+
if (parent === dir) return chain;
|
|
5505
|
+
dir = parent;
|
|
5506
|
+
}
|
|
5225
5507
|
}
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5508
|
+
|
|
5509
|
+
// src/meta/core/gitignore.ts
|
|
5510
|
+
var GITIGNORE_UNAVAILABLE = ".gitignore filtering was requested but git could not answer here (no repository, or no git on PATH); no files were skipped.";
|
|
5511
|
+
var unavailable = () => ({
|
|
5512
|
+
ignored: /* @__PURE__ */ new Set(),
|
|
5513
|
+
available: false
|
|
5514
|
+
});
|
|
5515
|
+
async function gitIgnored(paths, cwd) {
|
|
5516
|
+
if (paths.length === 0) {
|
|
5517
|
+
return { ignored: /* @__PURE__ */ new Set(), available: true };
|
|
5518
|
+
}
|
|
5519
|
+
const rootOf = /* @__PURE__ */ new Map();
|
|
5520
|
+
const byRoot = /* @__PURE__ */ new Map();
|
|
5521
|
+
let inSomeRepo = false;
|
|
5522
|
+
for (const spelled of paths) {
|
|
5523
|
+
const abs = resolve3(cwd, spelled);
|
|
5524
|
+
const dir = dirname3(abs);
|
|
5525
|
+
let root = rootOf.get(dir);
|
|
5526
|
+
if (root === void 0) {
|
|
5527
|
+
root = findGitRoot(dir);
|
|
5528
|
+
rootOf.set(dir, root);
|
|
5529
|
+
}
|
|
5530
|
+
if (root === null) continue;
|
|
5531
|
+
inSomeRepo = true;
|
|
5532
|
+
const rel = relative2(root, abs).split(sep).join("/");
|
|
5533
|
+
const bucket = byRoot.get(root);
|
|
5534
|
+
if (bucket) bucket.push({ rel, spelled });
|
|
5535
|
+
else byRoot.set(root, [{ rel, spelled }]);
|
|
5536
|
+
}
|
|
5537
|
+
if (!inSomeRepo) return unavailable();
|
|
5538
|
+
const ignored = /* @__PURE__ */ new Set();
|
|
5539
|
+
let answered = 0;
|
|
5540
|
+
for (const [root, bucket] of byRoot) {
|
|
5541
|
+
const answer = await checkIgnore(
|
|
5542
|
+
bucket.map((b) => b.rel),
|
|
5543
|
+
root
|
|
5544
|
+
);
|
|
5545
|
+
if (!answer.available) continue;
|
|
5546
|
+
answered += 1;
|
|
5547
|
+
for (const b of bucket) {
|
|
5548
|
+
if (answer.ignored.has(b.rel)) ignored.add(b.spelled);
|
|
5549
|
+
}
|
|
5550
|
+
}
|
|
5551
|
+
return answered > 0 ? { ignored, available: true } : unavailable();
|
|
5229
5552
|
}
|
|
5230
|
-
function
|
|
5231
|
-
return
|
|
5553
|
+
function checkIgnore(paths, cwd) {
|
|
5554
|
+
return new Promise((settle) => {
|
|
5555
|
+
let done = false;
|
|
5556
|
+
const finish = (answer) => {
|
|
5557
|
+
if (done) return;
|
|
5558
|
+
done = true;
|
|
5559
|
+
settle(answer);
|
|
5560
|
+
};
|
|
5561
|
+
let child;
|
|
5562
|
+
try {
|
|
5563
|
+
child = spawn("git", ["check-ignore", "--stdin", "-z"], {
|
|
5564
|
+
cwd,
|
|
5565
|
+
windowsHide: true
|
|
5566
|
+
});
|
|
5567
|
+
} catch {
|
|
5568
|
+
finish(unavailable());
|
|
5569
|
+
return;
|
|
5570
|
+
}
|
|
5571
|
+
let out = "";
|
|
5572
|
+
child.stdout.setEncoding("utf8");
|
|
5573
|
+
child.stdout.on("data", (chunk) => {
|
|
5574
|
+
out += chunk;
|
|
5575
|
+
});
|
|
5576
|
+
child.stderr.resume();
|
|
5577
|
+
child.on("error", () => {
|
|
5578
|
+
finish(unavailable());
|
|
5579
|
+
});
|
|
5580
|
+
child.stdin.on("error", () => {
|
|
5581
|
+
});
|
|
5582
|
+
child.on("close", (code) => {
|
|
5583
|
+
if (code !== 0 && code !== 1) {
|
|
5584
|
+
finish(unavailable());
|
|
5585
|
+
return;
|
|
5586
|
+
}
|
|
5587
|
+
finish({
|
|
5588
|
+
ignored: new Set(out.split("\0").filter((p) => p !== "")),
|
|
5589
|
+
available: true
|
|
5590
|
+
});
|
|
5591
|
+
});
|
|
5592
|
+
child.stdin.end(paths.join("\0"));
|
|
5593
|
+
});
|
|
5232
5594
|
}
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
return m(filePath.replace(/\\/g, "/"));
|
|
5595
|
+
|
|
5596
|
+
// src/meta/core/load-files.ts
|
|
5597
|
+
var DEFAULT_IGNORE = ["**/node_modules/**", "**/.git/**"];
|
|
5598
|
+
var STDIN_TOKEN = "-";
|
|
5599
|
+
var STDIN_LABEL = "<stdin>";
|
|
5600
|
+
function toPosix(p) {
|
|
5601
|
+
return p.replace(/\\/g, "/");
|
|
5241
5602
|
}
|
|
5242
|
-
function
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
return
|
|
5603
|
+
async function statOrNull(p) {
|
|
5604
|
+
try {
|
|
5605
|
+
return await stat3(p);
|
|
5606
|
+
} catch {
|
|
5607
|
+
return null;
|
|
5247
5608
|
}
|
|
5248
|
-
throw new Error(
|
|
5249
|
-
`Invalid "${FILE_SCHEMA_KEY}": must be a string or a list of strings.`
|
|
5250
|
-
);
|
|
5251
|
-
}
|
|
5252
|
-
function dedupe(refs) {
|
|
5253
|
-
return [...new Set(refs)];
|
|
5254
5609
|
}
|
|
5255
|
-
function
|
|
5256
|
-
const
|
|
5257
|
-
const
|
|
5258
|
-
return
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5610
|
+
function gitignoreOptions(opts) {
|
|
5611
|
+
const asked = opts.flag === true || opts.flag === void 0 && opts.configured === true;
|
|
5612
|
+
const notice = opts.onNotice;
|
|
5613
|
+
return {
|
|
5614
|
+
respectGitignore: opts.flag ?? opts.configured ?? true,
|
|
5615
|
+
...asked && notice ? {
|
|
5616
|
+
onGitignoreUnavailable: () => {
|
|
5617
|
+
notice(GITIGNORE_UNAVAILABLE);
|
|
5618
|
+
}
|
|
5619
|
+
} : {}
|
|
5620
|
+
};
|
|
5262
5621
|
}
|
|
5263
|
-
function
|
|
5264
|
-
const
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5622
|
+
async function resolveTargetSet(opts) {
|
|
5623
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
5624
|
+
const exts = (opts.exts ?? supportedExtensions()).map(
|
|
5625
|
+
(e) => e.toLowerCase().startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`
|
|
5626
|
+
);
|
|
5627
|
+
const ignore = [...DEFAULT_IGNORE, ...opts.exclude ?? []];
|
|
5628
|
+
const named = /* @__PURE__ */ new Set();
|
|
5629
|
+
const walked = /* @__PURE__ */ new Set();
|
|
5630
|
+
const keepByExt = (file) => exts.includes(extname(file).toLowerCase());
|
|
5631
|
+
const missing = [];
|
|
5632
|
+
for (const input of opts.inputs) {
|
|
5633
|
+
if (input === STDIN_TOKEN) continue;
|
|
5634
|
+
const posixInput = toPosix(input);
|
|
5635
|
+
const abs = resolve4(cwd, posixInput);
|
|
5636
|
+
const st = await statOrNull(abs);
|
|
5637
|
+
if (st?.isFile()) {
|
|
5638
|
+
named.add(toPosix(relative3(cwd, abs)));
|
|
5639
|
+
continue;
|
|
5271
5640
|
}
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5641
|
+
if (st?.isDirectory()) {
|
|
5642
|
+
const found2 = await fg(`${posixInput}/**/*`, {
|
|
5643
|
+
cwd,
|
|
5644
|
+
ignore,
|
|
5645
|
+
onlyFiles: true,
|
|
5646
|
+
dot: false
|
|
5647
|
+
});
|
|
5648
|
+
for (const f of found2) if (keepByExt(f)) walked.add(f);
|
|
5649
|
+
continue;
|
|
5281
5650
|
}
|
|
5282
|
-
if (!
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
);
|
|
5651
|
+
if (!picomatch2.scan(posixInput).isGlob) {
|
|
5652
|
+
missing.push(posixInput);
|
|
5653
|
+
continue;
|
|
5286
5654
|
}
|
|
5287
|
-
|
|
5655
|
+
const found = await fg(posixInput, {
|
|
5656
|
+
cwd,
|
|
5657
|
+
ignore,
|
|
5658
|
+
onlyFiles: true,
|
|
5659
|
+
dot: false
|
|
5660
|
+
});
|
|
5661
|
+
for (const f of found) if (keepByExt(f)) walked.add(f);
|
|
5662
|
+
}
|
|
5663
|
+
if (missing.length > 0 && !opts.allowEmpty) {
|
|
5664
|
+
const names = missing.map((m) => `"${m}"`).join(", ");
|
|
5665
|
+
throw new DocmetaError(
|
|
5666
|
+
missing.length === 1 ? `File not found: ${names}.` : `Files not found: ${names}.`
|
|
5667
|
+
);
|
|
5668
|
+
}
|
|
5669
|
+
let gitignoreSkipped = 0;
|
|
5670
|
+
if (opts.respectGitignore !== false && walked.size > 0) {
|
|
5671
|
+
const candidates = [...walked].filter((f) => !named.has(f));
|
|
5672
|
+
const { ignored, available } = await gitIgnored(candidates, cwd);
|
|
5673
|
+
if (available) {
|
|
5674
|
+
for (const f of ignored) {
|
|
5675
|
+
if (walked.delete(f)) gitignoreSkipped += 1;
|
|
5676
|
+
}
|
|
5677
|
+
} else {
|
|
5678
|
+
opts.onGitignoreUnavailable?.();
|
|
5679
|
+
}
|
|
5680
|
+
}
|
|
5681
|
+
return {
|
|
5682
|
+
files: [.../* @__PURE__ */ new Set([...named, ...walked])].sort(),
|
|
5683
|
+
gitignoreSkipped
|
|
5684
|
+
};
|
|
5685
|
+
}
|
|
5686
|
+
function assertNonEmpty(p) {
|
|
5687
|
+
if (p.allowEmpty || p.usingStdin || p.files.length > 0) return;
|
|
5688
|
+
const tried = p.inputs.map((i) => `"${i}"`).join(", ");
|
|
5689
|
+
const notes = [];
|
|
5690
|
+
if (p.exts && p.exts.length > 0) {
|
|
5691
|
+
notes.push(`extensions: ${p.exts.join(", ")}`);
|
|
5692
|
+
}
|
|
5693
|
+
if (p.exclude && p.exclude.length > 0) {
|
|
5694
|
+
notes.push(`excludes: ${p.exclude.map((e) => `"${e}"`).join(", ")}`);
|
|
5695
|
+
}
|
|
5696
|
+
if (p.gitignoreSkipped && p.gitignoreSkipped > 0) {
|
|
5697
|
+
notes.push(
|
|
5698
|
+
`.gitignore skipped ${p.gitignoreSkipped} (pass --no-gitignore to check them)`
|
|
5699
|
+
);
|
|
5288
5700
|
}
|
|
5289
|
-
const root = params.trustRoot;
|
|
5290
|
-
if (!root) return;
|
|
5291
|
-
const abs = resolve(params.fileBase ?? process.cwd(), ref);
|
|
5292
|
-
const within = relative(root.dir, abs);
|
|
5293
|
-
if (within !== "" && !within.startsWith("..") && !isAbsolute(within)) return;
|
|
5294
|
-
const boundary = root.source === "git" ? `the repository root (${root.dir})` : root.source === "config" ? `${root.dir} \u2014 no git repository was found, so the config file's own directory is the boundary` : `${root.dir} \u2014 no git repository and no config file were found, so the directory the command was run from is the boundary`;
|
|
5295
|
-
const scope = root.source === "git" ? "the repository" : "the project";
|
|
5296
5701
|
throw new DocmetaError(
|
|
5297
|
-
|
|
5702
|
+
[
|
|
5703
|
+
`No files matched. Patterns tried: ${tried}.`,
|
|
5704
|
+
...notes.length > 0 ? [`Filters applied \u2014 ${notes.join("; ")}.`] : [],
|
|
5705
|
+
`Nothing was ${p.action}, so this is an error rather than a pass.`,
|
|
5706
|
+
`Pass --allow-empty (or set allowEmpty: true) if matching nothing is expected.`
|
|
5707
|
+
].join("\n")
|
|
5298
5708
|
);
|
|
5299
5709
|
}
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5710
|
+
|
|
5711
|
+
// src/meta/core/sidecars.ts
|
|
5712
|
+
var SIDECAR_OWNED_SCHEMA = "sidecar:owned";
|
|
5713
|
+
var SIDECAR_DUPLICATE_SCHEMA = "sidecar:duplicate";
|
|
5714
|
+
var SIDECAR_KEYWORD = "sidecar";
|
|
5715
|
+
var PATH_JOIN = "path";
|
|
5716
|
+
var posix = (p) => p.split(sep2).join("/");
|
|
5717
|
+
function reportedPath(abs, base) {
|
|
5718
|
+
const rel = relative4(base, abs);
|
|
5719
|
+
return rel === "" ? "." : posix(rel);
|
|
5720
|
+
}
|
|
5721
|
+
function sidecarJoin(sidecar) {
|
|
5722
|
+
return sidecar.join ?? PATH_JOIN;
|
|
5723
|
+
}
|
|
5724
|
+
async function loadSidecars(config, opts) {
|
|
5725
|
+
const configured = config?.sidecars;
|
|
5726
|
+
if (!configured || configured.length === 0) return null;
|
|
5727
|
+
const owners = /* @__PURE__ */ new Map();
|
|
5728
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
5729
|
+
const byField = /* @__PURE__ */ new Map();
|
|
5730
|
+
const entries = [];
|
|
5731
|
+
for (const sidecar of configured) {
|
|
5732
|
+
let text;
|
|
5733
|
+
let file;
|
|
5734
|
+
if (classifyRef(sidecar.file).kind === "url") {
|
|
5735
|
+
file = sidecar.file;
|
|
5736
|
+
text = await fetchSidecar(sidecar.file, {
|
|
5737
|
+
...sidecar.tokenEnv !== void 0 ? { tokenEnv: sidecar.tokenEnv } : {},
|
|
5738
|
+
...opts.offline !== void 0 ? { offline: opts.offline } : {},
|
|
5739
|
+
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
|
|
5740
|
+
...opts.env !== void 0 ? { env: opts.env } : {}
|
|
5741
|
+
});
|
|
5312
5742
|
} else {
|
|
5313
|
-
|
|
5314
|
-
|
|
5743
|
+
const abs = isAbsolute2(sidecar.file) ? sidecar.file : resolve5(opts.configDir, sidecar.file);
|
|
5744
|
+
file = reportedPath(abs, opts.base);
|
|
5745
|
+
text = await readManifest(abs, file);
|
|
5746
|
+
}
|
|
5747
|
+
for (const key of sidecar.keys) owners.set(key, file);
|
|
5748
|
+
const join7 = sidecarJoin(sidecar);
|
|
5749
|
+
let target;
|
|
5750
|
+
if (join7 === PATH_JOIN) {
|
|
5751
|
+
target = byPath;
|
|
5752
|
+
} else {
|
|
5753
|
+
const existing = byField.get(join7);
|
|
5754
|
+
if (existing) target = existing;
|
|
5755
|
+
else {
|
|
5756
|
+
target = /* @__PURE__ */ new Map();
|
|
5757
|
+
byField.set(join7, target);
|
|
5758
|
+
}
|
|
5315
5759
|
}
|
|
5760
|
+
parseManifest(sidecar, text, file, opts.configDir, join7, target, entries);
|
|
5316
5761
|
}
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5762
|
+
return { owners, byPath, byField, entries };
|
|
5763
|
+
}
|
|
5764
|
+
async function readManifest(abs, file) {
|
|
5765
|
+
try {
|
|
5766
|
+
return await readFile3(abs, "utf8");
|
|
5767
|
+
} catch (err) {
|
|
5768
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
5769
|
+
throw new DocmetaError(
|
|
5770
|
+
`Sidecar manifest ${file} could not be read: ${reason}`
|
|
5771
|
+
);
|
|
5772
|
+
}
|
|
5773
|
+
}
|
|
5774
|
+
function parseManifest(sidecar, text, file, configDir, join7, target, entries) {
|
|
5775
|
+
const lc = new LineCounter2();
|
|
5776
|
+
const doc = parseDocument3(text, { lineCounter: lc, uniqueKeys: false });
|
|
5777
|
+
const problem = doc.errors[0];
|
|
5778
|
+
if (problem) {
|
|
5779
|
+
throw new DocmetaError(
|
|
5780
|
+
`Sidecar manifest ${file} is not valid YAML: ${problem.message}`
|
|
5781
|
+
);
|
|
5782
|
+
}
|
|
5783
|
+
const root = doc.contents;
|
|
5784
|
+
if (root === null || isScalar2(root) && root.value === null) return;
|
|
5785
|
+
if (!isMap3(root)) {
|
|
5786
|
+
throw new DocmetaError(
|
|
5787
|
+
`Sidecar manifest ${file}: the manifest must be a mapping from document ${join7 === PATH_JOIN ? "path" : `"${join7}"`} to owned keys.`
|
|
5788
|
+
);
|
|
5789
|
+
}
|
|
5790
|
+
const owned = new Set(sidecar.keys);
|
|
5791
|
+
const lineAt = (node) => {
|
|
5792
|
+
const range = isNode(node) ? node.range : void 0;
|
|
5793
|
+
return range ? lc.linePos(range[0]).line : void 0;
|
|
5794
|
+
};
|
|
5795
|
+
const seen = /* @__PURE__ */ new Map();
|
|
5796
|
+
for (const pair of root.items) {
|
|
5797
|
+
const spelled = isScalar2(pair.key) ? String(pair.key.value) : String(pair.key);
|
|
5798
|
+
const entryLine = lineAt(pair.key);
|
|
5799
|
+
const where = entryLine === void 0 ? file : `${file}:${entryLine}`;
|
|
5800
|
+
if (seen.has(spelled)) {
|
|
5801
|
+
const first = seen.get(spelled);
|
|
5802
|
+
throw new DocmetaError(
|
|
5803
|
+
`Sidecar manifest ${where}: "${spelled}" is named twice (first at line ${first ?? "?"}). Merge the two entries into one.`
|
|
5804
|
+
);
|
|
5805
|
+
}
|
|
5806
|
+
seen.set(spelled, entryLine);
|
|
5807
|
+
if (!isMap3(pair.value)) {
|
|
5808
|
+
throw new DocmetaError(
|
|
5809
|
+
`Sidecar manifest ${where}: "${spelled}" must be a mapping of owned keys to values.`
|
|
5810
|
+
);
|
|
5811
|
+
}
|
|
5812
|
+
const indexKey = join7 === PATH_JOIN ? resolve5(configDir, spelled) : spelled;
|
|
5813
|
+
const values = target.get(indexKey) ?? /* @__PURE__ */ new Map();
|
|
5814
|
+
for (const kv of pair.value.items) {
|
|
5815
|
+
const key = isScalar2(kv.key) ? String(kv.key.value) : String(kv.key);
|
|
5816
|
+
if (key === FILE_SCHEMA_KEY) {
|
|
5817
|
+
throw new DocmetaError(
|
|
5818
|
+
`Sidecar manifest ${where}: "${spelled}" sets "${FILE_SCHEMA_KEY}" \u2014 a sidecar never chooses the schema a document is judged by; use "overrides" in the config.`
|
|
5819
|
+
);
|
|
5321
5820
|
}
|
|
5821
|
+
if (!owned.has(key)) {
|
|
5822
|
+
throw new DocmetaError(
|
|
5823
|
+
`Sidecar manifest ${where}: "${spelled}" sets "${key}", which this sidecar does not own. Add it to the sidecar's "keys", or remove it from the entry.`
|
|
5824
|
+
);
|
|
5825
|
+
}
|
|
5826
|
+
const value = kv.value === null ? null : kv.value.toJS(doc, { maxAliasCount: 100 });
|
|
5827
|
+
const line = lineAt(kv.key);
|
|
5828
|
+
values.set(key, { value, file, ...line === void 0 ? {} : { line } });
|
|
5322
5829
|
}
|
|
5830
|
+
target.set(indexKey, values);
|
|
5831
|
+
entries.push({
|
|
5832
|
+
join: join7,
|
|
5833
|
+
...join7 === PATH_JOIN ? { abs: indexKey } : {},
|
|
5834
|
+
spelled,
|
|
5835
|
+
file,
|
|
5836
|
+
...entryLine === void 0 ? {} : { line: entryLine }
|
|
5837
|
+
});
|
|
5323
5838
|
}
|
|
5324
|
-
if (config?.schemas && config.schemas.length > 0) {
|
|
5325
|
-
return {
|
|
5326
|
-
schemas: dedupe(config.schemas.map(schemaEntryRef)),
|
|
5327
|
-
source: "config"
|
|
5328
|
-
};
|
|
5329
|
-
}
|
|
5330
|
-
return { schemas: [...DEFAULT_SCHEMAS], source: "default" };
|
|
5331
5839
|
}
|
|
5332
|
-
function
|
|
5333
|
-
|
|
5840
|
+
function joinValue(raw) {
|
|
5841
|
+
if (typeof raw === "string") return raw;
|
|
5842
|
+
if (typeof raw === "number" || typeof raw === "bigint") return String(raw);
|
|
5843
|
+
if (typeof raw === "boolean") return String(raw);
|
|
5844
|
+
return void 0;
|
|
5334
5845
|
}
|
|
5335
|
-
function
|
|
5336
|
-
const
|
|
5337
|
-
if (
|
|
5338
|
-
|
|
5339
|
-
|
|
5846
|
+
function mergeSidecars(label, extracted, index, base) {
|
|
5847
|
+
const none = () => void 0;
|
|
5848
|
+
if (!index) return { extracted, collisions: [], joins: [], locate: none };
|
|
5849
|
+
const collisions = [];
|
|
5850
|
+
for (const key of Object.keys(extracted.data)) {
|
|
5851
|
+
const file = index.owners.get(key);
|
|
5852
|
+
if (file !== void 0) collisions.push({ key, file });
|
|
5853
|
+
}
|
|
5854
|
+
const sources = [];
|
|
5855
|
+
const joins = [];
|
|
5856
|
+
if (label !== STDIN_LABEL) {
|
|
5857
|
+
const byPath = index.byPath.get(resolve5(base, label));
|
|
5858
|
+
if (byPath) sources.push(byPath);
|
|
5859
|
+
}
|
|
5860
|
+
for (const [field, byValue] of index.byField) {
|
|
5861
|
+
const value = joinValue(extracted.data[field]);
|
|
5862
|
+
if (value === void 0) continue;
|
|
5863
|
+
const hit = byValue.get(value);
|
|
5864
|
+
if (!hit) continue;
|
|
5865
|
+
sources.push(hit);
|
|
5866
|
+
const first = hit.values().next().value;
|
|
5867
|
+
joins.push({ field, value, file: first?.file ?? "manifest" });
|
|
5340
5868
|
}
|
|
5341
|
-
|
|
5869
|
+
if (sources.length === 0) {
|
|
5870
|
+
return { extracted, collisions, joins, locate: none };
|
|
5871
|
+
}
|
|
5872
|
+
const data = { ...extracted.data };
|
|
5873
|
+
const merged = /* @__PURE__ */ new Map();
|
|
5874
|
+
for (const supplied of sources) {
|
|
5875
|
+
for (const [key, sv] of supplied) {
|
|
5876
|
+
if (key in extracted.data) continue;
|
|
5877
|
+
data[key] = sv.value;
|
|
5878
|
+
merged.set(key, sv);
|
|
5879
|
+
}
|
|
5880
|
+
}
|
|
5881
|
+
const locate = (pointer) => {
|
|
5882
|
+
const top = topLevelKey(pointer);
|
|
5883
|
+
if (top === void 0) return void 0;
|
|
5884
|
+
const sv = merged.get(top);
|
|
5885
|
+
if (!sv) return void 0;
|
|
5886
|
+
return { file: sv.file, ...sv.line === void 0 ? {} : { line: sv.line } };
|
|
5887
|
+
};
|
|
5888
|
+
return { extracted: { ...extracted, data }, collisions, joins, locate };
|
|
5889
|
+
}
|
|
5890
|
+
function topLevelKey(pointer) {
|
|
5891
|
+
if (pointer === "") return void 0;
|
|
5892
|
+
if (!pointer.startsWith("/")) return pointer;
|
|
5893
|
+
const seg = pointer.slice(1).split("/")[0] ?? "";
|
|
5894
|
+
return seg.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
5895
|
+
}
|
|
5896
|
+
function sidecarPointer(key) {
|
|
5897
|
+
return `/${escapePointerSegment(key)}`;
|
|
5898
|
+
}
|
|
5899
|
+
function orphanEntries(index, loaded, base) {
|
|
5900
|
+
if (!index) return [];
|
|
5901
|
+
const have = new Set(loaded.map((l) => resolve5(base, l)));
|
|
5902
|
+
return index.entries.filter(
|
|
5903
|
+
(e) => e.join === PATH_JOIN && e.abs !== void 0 && !have.has(e.abs)
|
|
5904
|
+
);
|
|
5905
|
+
}
|
|
5906
|
+
function orphanJoins(index, matched) {
|
|
5907
|
+
if (!index) return [];
|
|
5908
|
+
return index.entries.filter(
|
|
5909
|
+
(e) => e.join !== PATH_JOIN && !(matched.get(e.join)?.has(e.spelled) ?? false)
|
|
5910
|
+
);
|
|
5911
|
+
}
|
|
5912
|
+
function orphanError(orphans) {
|
|
5913
|
+
const first = orphans[0];
|
|
5914
|
+
if (!first) throw new Error("orphanError called with no orphans");
|
|
5915
|
+
const where = first.line === void 0 ? first.file : `${first.file}:${first.line}`;
|
|
5916
|
+
const more = orphans.length > 1 ? ` (${orphans.length - 1} more)` : "";
|
|
5917
|
+
const what = first.join === PATH_JOIN ? `names "${first.spelled}", which this run did not load` : `names ${first.join} "${first.spelled}", which no loaded document carries`;
|
|
5918
|
+
return new DocmetaError(
|
|
5919
|
+
`Sidecar manifest ${where} ${what}${more}. Fix the entry, or remove it.`
|
|
5920
|
+
);
|
|
5342
5921
|
}
|
|
5343
5922
|
|
|
5344
5923
|
// src/meta/core/config.ts
|
|
5345
|
-
import { resolve as
|
|
5924
|
+
import { resolve as resolve7 } from "node:path";
|
|
5346
5925
|
import { parse as parseYaml2 } from "yaml";
|
|
5347
5926
|
|
|
5348
5927
|
// src/shared/config-file.ts
|
|
5349
|
-
import { readFile as
|
|
5350
|
-
import { dirname as
|
|
5928
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
5929
|
+
import { dirname as dirname4, join as join4, relative as relative5, resolve as resolve6 } from "node:path";
|
|
5351
5930
|
import { parse as parseYaml } from "yaml";
|
|
5352
5931
|
|
|
5353
|
-
// src/shared/git-root.ts
|
|
5354
|
-
import { existsSync } from "node:fs";
|
|
5355
|
-
import { dirname as dirname2, join as join3, resolve as resolve2 } from "node:path";
|
|
5356
|
-
function findGitRoot(cwd) {
|
|
5357
|
-
let dir = resolve2(cwd);
|
|
5358
|
-
for (; ; ) {
|
|
5359
|
-
if (existsSync(join3(dir, ".git"))) return dir;
|
|
5360
|
-
const parent = dirname2(dir);
|
|
5361
|
-
if (parent === dir) return null;
|
|
5362
|
-
dir = parent;
|
|
5363
|
-
}
|
|
5364
|
-
}
|
|
5365
|
-
function searchPath(cwd) {
|
|
5366
|
-
const start = resolve2(cwd);
|
|
5367
|
-
const root = findGitRoot(start);
|
|
5368
|
-
if (root === null) return [start];
|
|
5369
|
-
const chain = [];
|
|
5370
|
-
let dir = start;
|
|
5371
|
-
for (; ; ) {
|
|
5372
|
-
chain.push(dir);
|
|
5373
|
-
if (dir === root) return chain;
|
|
5374
|
-
const parent = dirname2(dir);
|
|
5375
|
-
if (parent === dir) return chain;
|
|
5376
|
-
dir = parent;
|
|
5377
|
-
}
|
|
5378
|
-
}
|
|
5379
|
-
|
|
5380
5932
|
// src/shared/warn.ts
|
|
5381
5933
|
var said = /* @__PURE__ */ new Set();
|
|
5382
5934
|
function warn(message) {
|
|
@@ -5398,7 +5950,7 @@ var MOOSE_CONFIG_NAMES = [
|
|
|
5398
5950
|
async function readDocument(path, source, opts) {
|
|
5399
5951
|
let text;
|
|
5400
5952
|
try {
|
|
5401
|
-
text = await
|
|
5953
|
+
text = await readFile4(path, "utf8");
|
|
5402
5954
|
} catch {
|
|
5403
5955
|
return null;
|
|
5404
5956
|
}
|
|
@@ -5425,10 +5977,10 @@ function slice(document, opts) {
|
|
|
5425
5977
|
return null;
|
|
5426
5978
|
}
|
|
5427
5979
|
function relativeSource(cwd, path) {
|
|
5428
|
-
return
|
|
5980
|
+
return relative5(cwd, path).replace(/\\/g, "/");
|
|
5429
5981
|
}
|
|
5430
5982
|
async function findConfigFile(cwd, opts) {
|
|
5431
|
-
const start =
|
|
5983
|
+
const start = resolve6(cwd);
|
|
5432
5984
|
for (const dir of searchPath(start)) {
|
|
5433
5985
|
const family = [
|
|
5434
5986
|
{ names: FAMILY_CONFIG_NAMES, kind: "manni" },
|
|
@@ -5472,7 +6024,7 @@ async function findConfigFile(cwd, opts) {
|
|
|
5472
6024
|
return null;
|
|
5473
6025
|
}
|
|
5474
6026
|
async function readConfigFile(explicitPath, cwd, opts) {
|
|
5475
|
-
const path =
|
|
6027
|
+
const path = resolve6(cwd, explicitPath);
|
|
5476
6028
|
const document = await readDocument(path, explicitPath, opts);
|
|
5477
6029
|
if (document === null) {
|
|
5478
6030
|
throw opts.toError(`Config file not found: "${explicitPath}".`);
|
|
@@ -5483,7 +6035,7 @@ async function readConfigFile(explicitPath, cwd, opts) {
|
|
|
5483
6035
|
};
|
|
5484
6036
|
return {
|
|
5485
6037
|
path,
|
|
5486
|
-
dir:
|
|
6038
|
+
dir: dirname4(path),
|
|
5487
6039
|
source: explicitPath,
|
|
5488
6040
|
text: document.text,
|
|
5489
6041
|
kind: "explicit",
|
|
@@ -5509,6 +6061,7 @@ function asElementPaths(value, where, source) {
|
|
|
5509
6061
|
}
|
|
5510
6062
|
var OVERRIDE_KEYS = ["name", "files", "schemas", "elements"];
|
|
5511
6063
|
var CHECK_KEYS = ["name", "query"];
|
|
6064
|
+
var SIDECAR_KEYS = ["file", "keys", "tokenEnv", "join"];
|
|
5512
6065
|
var CHECK_NAME = /^[a-z0-9][a-z0-9._-]*$/;
|
|
5513
6066
|
var RESERVED_CHECK_NAMES = /* @__PURE__ */ new Set(["query"]);
|
|
5514
6067
|
var FILL_KEYS = [
|
|
@@ -5525,6 +6078,7 @@ var CONFIG_KEYS = [
|
|
|
5525
6078
|
"schemas",
|
|
5526
6079
|
"overrides",
|
|
5527
6080
|
"checks",
|
|
6081
|
+
"sidecars",
|
|
5528
6082
|
"baseline",
|
|
5529
6083
|
"allowEmpty",
|
|
5530
6084
|
"respectGitignore",
|
|
@@ -5668,6 +6222,93 @@ function withSection(message, source, section) {
|
|
|
5668
6222
|
if (/^[a-zA-Z]+(\[|\.| —)/.test(rest)) return `${head}${section}.${rest}`;
|
|
5669
6223
|
return message;
|
|
5670
6224
|
}
|
|
6225
|
+
function parseSidecars(raw, source) {
|
|
6226
|
+
if (!Array.isArray(raw)) {
|
|
6227
|
+
throw new DocmetaError(`${source}: "sidecars" must be a list.`);
|
|
6228
|
+
}
|
|
6229
|
+
const owners = /* @__PURE__ */ new Map();
|
|
6230
|
+
return raw.map((entry, i) => {
|
|
6231
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
6232
|
+
throw new DocmetaError(`${source}: sidecars[${i}] must be a mapping.`);
|
|
6233
|
+
}
|
|
6234
|
+
const e = entry;
|
|
6235
|
+
rejectUnknownKeys(e, SIDECAR_KEYS, `sidecars[${i}]`, source);
|
|
6236
|
+
if (typeof e.file !== "string" || e.file.trim() === "") {
|
|
6237
|
+
throw new DocmetaError(
|
|
6238
|
+
`${source}: sidecars[${i}].file must be a non-empty string naming the manifest, relative to the config file.`
|
|
6239
|
+
);
|
|
6240
|
+
}
|
|
6241
|
+
if (!Array.isArray(e.keys) || e.keys.length === 0 || e.keys.some((k) => typeof k !== "string" || k.trim() === "")) {
|
|
6242
|
+
throw new DocmetaError(
|
|
6243
|
+
`${source}: sidecars[${i}].keys must be a non-empty list of key names.`
|
|
6244
|
+
);
|
|
6245
|
+
}
|
|
6246
|
+
const isUrl = classifyRef(e.file).kind === "url";
|
|
6247
|
+
if (isUrl) {
|
|
6248
|
+
const problem = sidecarUrlProblem(e.file);
|
|
6249
|
+
if (problem !== null) {
|
|
6250
|
+
throw new DocmetaError(`${source}: sidecars[${i}].file ${problem}.`);
|
|
6251
|
+
}
|
|
6252
|
+
}
|
|
6253
|
+
if (e.tokenEnv !== void 0) {
|
|
6254
|
+
if (typeof e.tokenEnv !== "string" || e.tokenEnv.trim() === "") {
|
|
6255
|
+
throw new DocmetaError(
|
|
6256
|
+
`${source}: sidecars[${i}].tokenEnv must be the name of an environment variable.`
|
|
6257
|
+
);
|
|
6258
|
+
}
|
|
6259
|
+
if (!isUrl) {
|
|
6260
|
+
throw new DocmetaError(
|
|
6261
|
+
`${source}: sidecars[${i}].tokenEnv is set, but "file" is a path, so no request is made and the token would go nowhere. Remove it, or make "file" a URL.`
|
|
6262
|
+
);
|
|
6263
|
+
}
|
|
6264
|
+
}
|
|
6265
|
+
const keys = e.keys;
|
|
6266
|
+
if (e.join !== void 0) {
|
|
6267
|
+
if (typeof e.join !== "string" || e.join.trim() === "") {
|
|
6268
|
+
throw new DocmetaError(
|
|
6269
|
+
`${source}: sidecars[${i}].join must be "path" or the name of a top-level frontmatter field.`
|
|
6270
|
+
);
|
|
6271
|
+
}
|
|
6272
|
+
if (e.join === FILE_SCHEMA_KEY) {
|
|
6273
|
+
throw new DocmetaError(
|
|
6274
|
+
`${source}: sidecars[${i}].join may not be "${FILE_SCHEMA_KEY}".`
|
|
6275
|
+
);
|
|
6276
|
+
}
|
|
6277
|
+
if (keys.includes(e.join)) {
|
|
6278
|
+
throw new DocmetaError(
|
|
6279
|
+
`${source}: sidecars[${i}].join names "${e.join}", which the same entry owns \u2014 the value that selects an entry cannot come from the entry.`
|
|
6280
|
+
);
|
|
6281
|
+
}
|
|
6282
|
+
}
|
|
6283
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6284
|
+
for (const key of keys) {
|
|
6285
|
+
if (key === FILE_SCHEMA_KEY) {
|
|
6286
|
+
throw new DocmetaError(
|
|
6287
|
+
`${source}: sidecars[${i}].keys may not include "${FILE_SCHEMA_KEY}" \u2014 a sidecar never chooses the schema a document is judged by; use "overrides".`
|
|
6288
|
+
);
|
|
6289
|
+
}
|
|
6290
|
+
if (seen.has(key)) {
|
|
6291
|
+
throw new DocmetaError(
|
|
6292
|
+
`${source}: sidecars[${i}].keys lists "${key}" twice.`
|
|
6293
|
+
);
|
|
6294
|
+
}
|
|
6295
|
+
seen.add(key);
|
|
6296
|
+
const owner = owners.get(key);
|
|
6297
|
+
if (owner !== void 0) {
|
|
6298
|
+
throw new DocmetaError(
|
|
6299
|
+
`${source}: sidecars[${i}].keys claims "${key}", which sidecars[${owner}] already owns \u2014 a key has exactly one sidecar.`
|
|
6300
|
+
);
|
|
6301
|
+
}
|
|
6302
|
+
owners.set(key, i);
|
|
6303
|
+
}
|
|
6304
|
+
return {
|
|
6305
|
+
file: e.file,
|
|
6306
|
+
keys,
|
|
6307
|
+
...typeof e.tokenEnv === "string" ? { tokenEnv: e.tokenEnv } : {},
|
|
6308
|
+
...typeof e.join === "string" ? { join: e.join } : {}
|
|
6309
|
+
};
|
|
6310
|
+
});
|
|
6311
|
+
}
|
|
5671
6312
|
function parseConfigDocument(raw, source) {
|
|
5672
6313
|
if (raw == null) return {};
|
|
5673
6314
|
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
@@ -5741,6 +6382,9 @@ function parseConfigDocument(raw, source) {
|
|
|
5741
6382
|
if (obj.checks !== void 0) {
|
|
5742
6383
|
config.checks = parseChecks(obj.checks, source);
|
|
5743
6384
|
}
|
|
6385
|
+
if (obj.sidecars !== void 0) {
|
|
6386
|
+
config.sidecars = parseSidecars(obj.sidecars, source);
|
|
6387
|
+
}
|
|
5744
6388
|
if (obj.elements !== void 0) {
|
|
5745
6389
|
config.elements = asElementPaths(obj.elements, "elements", source);
|
|
5746
6390
|
}
|
|
@@ -5909,8 +6553,8 @@ function parseFill(value, source) {
|
|
|
5909
6553
|
function schemaTrustRoot(cwd, configDir) {
|
|
5910
6554
|
const git = findGitRoot(cwd);
|
|
5911
6555
|
if (git !== null) return { dir: git, source: "git" };
|
|
5912
|
-
if (configDir !== void 0) return { dir:
|
|
5913
|
-
return { dir:
|
|
6556
|
+
if (configDir !== void 0) return { dir: resolve7(configDir), source: "config" };
|
|
6557
|
+
return { dir: resolve7(cwd), source: "cwd" };
|
|
5914
6558
|
}
|
|
5915
6559
|
async function loadConfig(explicitPath, cwd = process.cwd()) {
|
|
5916
6560
|
const file = explicitPath ? await readConfigFile(explicitPath, cwd, CONFIG_FILE) : await findConfigFile(cwd, CONFIG_FILE);
|
|
@@ -6091,7 +6735,7 @@ var Validator = class {
|
|
|
6091
6735
|
* Callers with an extractor that supplies no column pass nothing and get the
|
|
6092
6736
|
* previous behavior exactly.
|
|
6093
6737
|
*/
|
|
6094
|
-
async validate(data, refs, lineFor, colFor) {
|
|
6738
|
+
async validate(data, refs, lineFor, colFor, locate) {
|
|
6095
6739
|
const { [FILE_SCHEMA_KEY]: _omit, ...subject } = data;
|
|
6096
6740
|
void _omit;
|
|
6097
6741
|
const errors = [];
|
|
@@ -6100,7 +6744,7 @@ var Validator = class {
|
|
|
6100
6744
|
const ok = fn(subject);
|
|
6101
6745
|
if (ok) continue;
|
|
6102
6746
|
for (const e of fn.errors ?? []) {
|
|
6103
|
-
errors.push(toFieldError(ref, e, lineFor, colFor));
|
|
6747
|
+
errors.push(toFieldError(ref, e, lineFor, colFor, locate));
|
|
6104
6748
|
}
|
|
6105
6749
|
}
|
|
6106
6750
|
return errors;
|
|
@@ -6120,7 +6764,7 @@ function subjectOf(e) {
|
|
|
6120
6764
|
return void 0;
|
|
6121
6765
|
}
|
|
6122
6766
|
}
|
|
6123
|
-
function toFieldError(schema, e, lineFor, colFor) {
|
|
6767
|
+
function toFieldError(schema, e, lineFor, colFor, locate) {
|
|
6124
6768
|
const defined = e;
|
|
6125
6769
|
const instancePath = e.instancePath;
|
|
6126
6770
|
const subject = subjectOf(defined);
|
|
@@ -6133,6 +6777,21 @@ function toFieldError(schema, e, lineFor, colFor) {
|
|
|
6133
6777
|
message = `must NOT have additional property '${defined.params.additionalProperty}'`;
|
|
6134
6778
|
wantsColumn = false;
|
|
6135
6779
|
}
|
|
6780
|
+
const located = locate?.(instancePath) ?? (defined.keyword === "additionalProperties" ? locate?.(
|
|
6781
|
+
`${instancePath}/${escapePointerSegment(defined.params.additionalProperty)}`
|
|
6782
|
+
) : void 0);
|
|
6783
|
+
if (located) {
|
|
6784
|
+
return {
|
|
6785
|
+
schema,
|
|
6786
|
+
instancePath,
|
|
6787
|
+
message,
|
|
6788
|
+
keyword: e.keyword,
|
|
6789
|
+
...subject != null ? { subject } : {},
|
|
6790
|
+
file: located.file,
|
|
6791
|
+
...located.line != null ? { line: located.line } : {},
|
|
6792
|
+
...wantsColumn && located.col != null ? { col: located.col } : {}
|
|
6793
|
+
};
|
|
6794
|
+
}
|
|
6136
6795
|
const line = lineFor(instancePath);
|
|
6137
6796
|
const col = wantsColumn ? colFor?.(instancePath) : void 0;
|
|
6138
6797
|
return {
|
|
@@ -6148,13 +6807,13 @@ function toFieldError(schema, e, lineFor, colFor) {
|
|
|
6148
6807
|
|
|
6149
6808
|
// src/meta/commands/validate.ts
|
|
6150
6809
|
import { existsSync as existsSync2 } from "node:fs";
|
|
6151
|
-
import { readFile as
|
|
6152
|
-
import { resolve as
|
|
6810
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
6811
|
+
import { resolve as resolve9, extname as extname2 } from "node:path";
|
|
6153
6812
|
|
|
6154
6813
|
// src/meta/core/baseline.ts
|
|
6155
6814
|
import { createHash as createHash3 } from "node:crypto";
|
|
6156
|
-
import { readFile as
|
|
6157
|
-
import { relative as
|
|
6815
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
6816
|
+
import { relative as relative6, resolve as resolve8 } from "node:path";
|
|
6158
6817
|
var DEFAULT_BASELINE_PATH = ".manni-baseline.json";
|
|
6159
6818
|
var LEGACY_BASELINE_PATH = ".docmeta-baseline.json";
|
|
6160
6819
|
var BASELINE_VERSION = 1;
|
|
@@ -6162,13 +6821,13 @@ var FINGERPRINT_RE = /^[0-9a-f]{16}$/;
|
|
|
6162
6821
|
function canonicalFilePath(file, ctx) {
|
|
6163
6822
|
if (!ctx) return file;
|
|
6164
6823
|
const from = ctx.runBase ?? ctx.base;
|
|
6165
|
-
return
|
|
6824
|
+
return relative6(ctx.base, resolve8(from, file)).replace(/\\/g, "/");
|
|
6166
6825
|
}
|
|
6167
6826
|
function canonicalSchemaRef(ref, ctx) {
|
|
6168
6827
|
if (!ctx) return ref;
|
|
6169
6828
|
if (classifyRef(ref).kind !== "file") return ref;
|
|
6170
|
-
const abs =
|
|
6171
|
-
return
|
|
6829
|
+
const abs = resolve8(ctx.cwd, ref);
|
|
6830
|
+
return relative6(ctx.base, abs).replace(/\\/g, "/");
|
|
6172
6831
|
}
|
|
6173
6832
|
function fingerprint(e, ctx) {
|
|
6174
6833
|
const parts = [
|
|
@@ -6235,265 +6894,91 @@ function parseBaseline(text, source) {
|
|
|
6235
6894
|
);
|
|
6236
6895
|
}
|
|
6237
6896
|
entries[file] = value;
|
|
6238
|
-
}
|
|
6239
|
-
return { version: BASELINE_VERSION, generatedWith, entries };
|
|
6240
|
-
}
|
|
6241
|
-
async function readBaseline(absPath, source) {
|
|
6242
|
-
let text;
|
|
6243
|
-
try {
|
|
6244
|
-
text = await readFile4(absPath, "utf8");
|
|
6245
|
-
} catch (err) {
|
|
6246
|
-
if (err.code === "ENOENT") return null;
|
|
6247
|
-
throw new DocmetaError(
|
|
6248
|
-
`Baseline "${source}" could not be read: ${err.message}`
|
|
6249
|
-
);
|
|
6250
|
-
}
|
|
6251
|
-
return parseBaseline(text, source);
|
|
6252
|
-
}
|
|
6253
|
-
async function writeBaselineFile(absPath, baseline, source) {
|
|
6254
|
-
try {
|
|
6255
|
-
await writeFileAtomic(absPath, serializeBaseline(baseline));
|
|
6256
|
-
} catch (err) {
|
|
6257
|
-
throw new DocmetaError(
|
|
6258
|
-
`Baseline "${source}" could not be written: ${err.message}`
|
|
6259
|
-
);
|
|
6260
|
-
}
|
|
6261
|
-
}
|
|
6262
|
-
function buildBaseline(results, generatedWith, ctx) {
|
|
6263
|
-
const entries = /* @__PURE__ */ Object.create(null);
|
|
6264
|
-
for (const r of results) {
|
|
6265
|
-
if (r.errors.length === 0) continue;
|
|
6266
|
-
const prints = [...new Set(r.errors.map((e) => fingerprint(e, ctx)))].sort();
|
|
6267
|
-
entries[canonicalFilePath(r.file, ctx)] = prints;
|
|
6268
|
-
}
|
|
6269
|
-
return { version: BASELINE_VERSION, generatedWith, entries };
|
|
6270
|
-
}
|
|
6271
|
-
function applyBaseline(results, baseline, ctx) {
|
|
6272
|
-
let recorded = 0;
|
|
6273
|
-
let suppressed = 0;
|
|
6274
|
-
let stale = 0;
|
|
6275
|
-
const applied = results.map((r) => {
|
|
6276
|
-
const known = baseline.entries[canonicalFilePath(r.file, ctx)];
|
|
6277
|
-
if (!known) return r;
|
|
6278
|
-
const recordedHere = new Set(known);
|
|
6279
|
-
recorded += recordedHere.size;
|
|
6280
|
-
const seen = /* @__PURE__ */ new Set();
|
|
6281
|
-
const fresh = [];
|
|
6282
|
-
for (const e of r.errors) {
|
|
6283
|
-
const print = fingerprint(e, ctx);
|
|
6284
|
-
if (recordedHere.has(print)) {
|
|
6285
|
-
seen.add(print);
|
|
6286
|
-
suppressed += 1;
|
|
6287
|
-
} else {
|
|
6288
|
-
fresh.push(e);
|
|
6289
|
-
}
|
|
6290
|
-
}
|
|
6291
|
-
stale += recordedHere.size - seen.size;
|
|
6292
|
-
const baselined = r.errors.length - fresh.length;
|
|
6293
|
-
return {
|
|
6294
|
-
...r,
|
|
6295
|
-
ok: fresh.length === 0,
|
|
6296
|
-
errors: fresh,
|
|
6297
|
-
...baselined > 0 ? { baselined } : {}
|
|
6298
|
-
};
|
|
6299
|
-
});
|
|
6300
|
-
return { results: applied, recorded, suppressed, stale };
|
|
6301
|
-
}
|
|
6302
|
-
function diffBaselines(previous, next) {
|
|
6303
|
-
const flatten = (b) => {
|
|
6304
|
-
const out = /* @__PURE__ */ new Set();
|
|
6305
|
-
if (!b) return out;
|
|
6306
|
-
for (const [file, prints] of Object.entries(b.entries)) {
|
|
6307
|
-
for (const p of prints) out.add(`${file}\0${p}`);
|
|
6308
|
-
}
|
|
6309
|
-
return out;
|
|
6310
|
-
};
|
|
6311
|
-
const before = flatten(previous);
|
|
6312
|
-
const after = flatten(next);
|
|
6313
|
-
let added = 0;
|
|
6314
|
-
let removed = 0;
|
|
6315
|
-
for (const k of after) if (!before.has(k)) added += 1;
|
|
6316
|
-
for (const k of before) if (!after.has(k)) removed += 1;
|
|
6317
|
-
return { added, removed };
|
|
6318
|
-
}
|
|
6319
|
-
function countFingerprints(baseline) {
|
|
6320
|
-
let n = 0;
|
|
6321
|
-
for (const prints of Object.values(baseline.entries)) n += prints.length;
|
|
6322
|
-
return n;
|
|
6323
|
-
}
|
|
6324
|
-
|
|
6325
|
-
// src/meta/core/load-files.ts
|
|
6326
|
-
import { stat as stat3 } from "node:fs/promises";
|
|
6327
|
-
import { extname, relative as relative4, resolve as resolve6 } from "node:path";
|
|
6328
|
-
import fg from "fast-glob";
|
|
6329
|
-
import picomatch2 from "picomatch";
|
|
6330
|
-
|
|
6331
|
-
// src/meta/core/gitignore.ts
|
|
6332
|
-
import { spawn } from "node:child_process";
|
|
6333
|
-
var GITIGNORE_UNAVAILABLE = ".gitignore filtering was requested but git could not answer here (no repository, or no git on PATH); no files were skipped.";
|
|
6334
|
-
var unavailable = () => ({
|
|
6335
|
-
ignored: /* @__PURE__ */ new Set(),
|
|
6336
|
-
available: false
|
|
6337
|
-
});
|
|
6338
|
-
function gitIgnored(paths, cwd) {
|
|
6339
|
-
if (paths.length === 0) {
|
|
6340
|
-
return Promise.resolve({ ignored: /* @__PURE__ */ new Set(), available: true });
|
|
6341
|
-
}
|
|
6342
|
-
return new Promise((settle) => {
|
|
6343
|
-
let done = false;
|
|
6344
|
-
const finish = (answer) => {
|
|
6345
|
-
if (done) return;
|
|
6346
|
-
done = true;
|
|
6347
|
-
settle(answer);
|
|
6348
|
-
};
|
|
6349
|
-
let child;
|
|
6350
|
-
try {
|
|
6351
|
-
child = spawn("git", ["check-ignore", "--stdin", "-z"], {
|
|
6352
|
-
cwd,
|
|
6353
|
-
windowsHide: true
|
|
6354
|
-
});
|
|
6355
|
-
} catch {
|
|
6356
|
-
finish(unavailable());
|
|
6357
|
-
return;
|
|
6358
|
-
}
|
|
6359
|
-
let out = "";
|
|
6360
|
-
child.stdout.setEncoding("utf8");
|
|
6361
|
-
child.stdout.on("data", (chunk) => {
|
|
6362
|
-
out += chunk;
|
|
6363
|
-
});
|
|
6364
|
-
child.stderr.resume();
|
|
6365
|
-
child.on("error", () => {
|
|
6366
|
-
finish(unavailable());
|
|
6367
|
-
});
|
|
6368
|
-
child.stdin.on("error", () => {
|
|
6369
|
-
});
|
|
6370
|
-
child.on("close", (code) => {
|
|
6371
|
-
if (code !== 0 && code !== 1) {
|
|
6372
|
-
finish(unavailable());
|
|
6373
|
-
return;
|
|
6374
|
-
}
|
|
6375
|
-
finish({
|
|
6376
|
-
ignored: new Set(out.split("\0").filter((p) => p !== "")),
|
|
6377
|
-
available: true
|
|
6378
|
-
});
|
|
6379
|
-
});
|
|
6380
|
-
child.stdin.end(paths.join("\0"));
|
|
6381
|
-
});
|
|
6382
|
-
}
|
|
6383
|
-
|
|
6384
|
-
// src/meta/core/load-files.ts
|
|
6385
|
-
var DEFAULT_IGNORE = ["**/node_modules/**", "**/.git/**"];
|
|
6386
|
-
var STDIN_TOKEN = "-";
|
|
6387
|
-
var STDIN_LABEL = "<stdin>";
|
|
6388
|
-
function toPosix(p) {
|
|
6389
|
-
return p.replace(/\\/g, "/");
|
|
6897
|
+
}
|
|
6898
|
+
return { version: BASELINE_VERSION, generatedWith, entries };
|
|
6390
6899
|
}
|
|
6391
|
-
async function
|
|
6900
|
+
async function readBaseline(absPath, source) {
|
|
6901
|
+
let text;
|
|
6392
6902
|
try {
|
|
6393
|
-
|
|
6394
|
-
} catch {
|
|
6395
|
-
return null;
|
|
6903
|
+
text = await readFile5(absPath, "utf8");
|
|
6904
|
+
} catch (err) {
|
|
6905
|
+
if (err.code === "ENOENT") return null;
|
|
6906
|
+
throw new DocmetaError(
|
|
6907
|
+
`Baseline "${source}" could not be read: ${err.message}`
|
|
6908
|
+
);
|
|
6396
6909
|
}
|
|
6910
|
+
return parseBaseline(text, source);
|
|
6397
6911
|
}
|
|
6398
|
-
function
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
respectGitignore: opts.flag ?? opts.configured ?? true,
|
|
6403
|
-
...asked && notice ? {
|
|
6404
|
-
onGitignoreUnavailable: () => {
|
|
6405
|
-
notice(GITIGNORE_UNAVAILABLE);
|
|
6406
|
-
}
|
|
6407
|
-
} : {}
|
|
6408
|
-
};
|
|
6409
|
-
}
|
|
6410
|
-
async function resolveTargetSet(opts) {
|
|
6411
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
6412
|
-
const exts = (opts.exts ?? supportedExtensions()).map(
|
|
6413
|
-
(e) => e.toLowerCase().startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`
|
|
6414
|
-
);
|
|
6415
|
-
const ignore = [...DEFAULT_IGNORE, ...opts.exclude ?? []];
|
|
6416
|
-
const named = /* @__PURE__ */ new Set();
|
|
6417
|
-
const walked = /* @__PURE__ */ new Set();
|
|
6418
|
-
const keepByExt = (file) => exts.includes(extname(file).toLowerCase());
|
|
6419
|
-
const missing = [];
|
|
6420
|
-
for (const input of opts.inputs) {
|
|
6421
|
-
if (input === STDIN_TOKEN) continue;
|
|
6422
|
-
const posixInput = toPosix(input);
|
|
6423
|
-
const abs = resolve6(cwd, posixInput);
|
|
6424
|
-
const st = await statOrNull(abs);
|
|
6425
|
-
if (st?.isFile()) {
|
|
6426
|
-
named.add(toPosix(relative4(cwd, abs)));
|
|
6427
|
-
continue;
|
|
6428
|
-
}
|
|
6429
|
-
if (st?.isDirectory()) {
|
|
6430
|
-
const found2 = await fg(`${posixInput}/**/*`, {
|
|
6431
|
-
cwd,
|
|
6432
|
-
ignore,
|
|
6433
|
-
onlyFiles: true,
|
|
6434
|
-
dot: false
|
|
6435
|
-
});
|
|
6436
|
-
for (const f of found2) if (keepByExt(f)) walked.add(f);
|
|
6437
|
-
continue;
|
|
6438
|
-
}
|
|
6439
|
-
if (!picomatch2.scan(posixInput).isGlob) {
|
|
6440
|
-
missing.push(posixInput);
|
|
6441
|
-
continue;
|
|
6442
|
-
}
|
|
6443
|
-
const found = await fg(posixInput, {
|
|
6444
|
-
cwd,
|
|
6445
|
-
ignore,
|
|
6446
|
-
onlyFiles: true,
|
|
6447
|
-
dot: false
|
|
6448
|
-
});
|
|
6449
|
-
for (const f of found) if (keepByExt(f)) walked.add(f);
|
|
6450
|
-
}
|
|
6451
|
-
if (missing.length > 0 && !opts.allowEmpty) {
|
|
6452
|
-
const names = missing.map((m) => `"${m}"`).join(", ");
|
|
6912
|
+
async function writeBaselineFile(absPath, baseline, source) {
|
|
6913
|
+
try {
|
|
6914
|
+
await writeFileAtomic(absPath, serializeBaseline(baseline));
|
|
6915
|
+
} catch (err) {
|
|
6453
6916
|
throw new DocmetaError(
|
|
6454
|
-
|
|
6917
|
+
`Baseline "${source}" could not be written: ${err.message}`
|
|
6455
6918
|
);
|
|
6456
6919
|
}
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
if (
|
|
6462
|
-
|
|
6463
|
-
|
|
6920
|
+
}
|
|
6921
|
+
function buildBaseline(results, generatedWith, ctx) {
|
|
6922
|
+
const entries = /* @__PURE__ */ Object.create(null);
|
|
6923
|
+
for (const r of results) {
|
|
6924
|
+
if (r.errors.length === 0) continue;
|
|
6925
|
+
const prints = [...new Set(r.errors.map((e) => fingerprint(e, ctx)))].sort();
|
|
6926
|
+
entries[canonicalFilePath(r.file, ctx)] = prints;
|
|
6927
|
+
}
|
|
6928
|
+
return { version: BASELINE_VERSION, generatedWith, entries };
|
|
6929
|
+
}
|
|
6930
|
+
function applyBaseline(results, baseline, ctx) {
|
|
6931
|
+
let recorded = 0;
|
|
6932
|
+
let suppressed = 0;
|
|
6933
|
+
let stale = 0;
|
|
6934
|
+
const applied = results.map((r) => {
|
|
6935
|
+
const known = baseline.entries[canonicalFilePath(r.file, ctx)];
|
|
6936
|
+
if (!known) return r;
|
|
6937
|
+
const recordedHere = new Set(known);
|
|
6938
|
+
recorded += recordedHere.size;
|
|
6939
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6940
|
+
const fresh = [];
|
|
6941
|
+
for (const e of r.errors) {
|
|
6942
|
+
const print = fingerprint(e, ctx);
|
|
6943
|
+
if (recordedHere.has(print)) {
|
|
6944
|
+
seen.add(print);
|
|
6945
|
+
suppressed += 1;
|
|
6946
|
+
} else {
|
|
6947
|
+
fresh.push(e);
|
|
6464
6948
|
}
|
|
6465
|
-
} else {
|
|
6466
|
-
opts.onGitignoreUnavailable?.();
|
|
6467
6949
|
}
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6950
|
+
stale += recordedHere.size - seen.size;
|
|
6951
|
+
const baselined = r.errors.length - fresh.length;
|
|
6952
|
+
return {
|
|
6953
|
+
...r,
|
|
6954
|
+
ok: fresh.length === 0,
|
|
6955
|
+
errors: fresh,
|
|
6956
|
+
...baselined > 0 ? { baselined } : {}
|
|
6957
|
+
};
|
|
6958
|
+
});
|
|
6959
|
+
return { results: applied, recorded, suppressed, stale };
|
|
6960
|
+
}
|
|
6961
|
+
function diffBaselines(previous, next) {
|
|
6962
|
+
const flatten = (b) => {
|
|
6963
|
+
const out = /* @__PURE__ */ new Set();
|
|
6964
|
+
if (!b) return out;
|
|
6965
|
+
for (const [file, prints] of Object.entries(b.entries)) {
|
|
6966
|
+
for (const p of prints) out.add(`${file}\0${p}`);
|
|
6967
|
+
}
|
|
6968
|
+
return out;
|
|
6472
6969
|
};
|
|
6970
|
+
const before = flatten(previous);
|
|
6971
|
+
const after = flatten(next);
|
|
6972
|
+
let added = 0;
|
|
6973
|
+
let removed = 0;
|
|
6974
|
+
for (const k of after) if (!before.has(k)) added += 1;
|
|
6975
|
+
for (const k of before) if (!after.has(k)) removed += 1;
|
|
6976
|
+
return { added, removed };
|
|
6473
6977
|
}
|
|
6474
|
-
function
|
|
6475
|
-
|
|
6476
|
-
const
|
|
6477
|
-
|
|
6478
|
-
if (p.exts && p.exts.length > 0) {
|
|
6479
|
-
notes.push(`extensions: ${p.exts.join(", ")}`);
|
|
6480
|
-
}
|
|
6481
|
-
if (p.exclude && p.exclude.length > 0) {
|
|
6482
|
-
notes.push(`excludes: ${p.exclude.map((e) => `"${e}"`).join(", ")}`);
|
|
6483
|
-
}
|
|
6484
|
-
if (p.gitignoreSkipped && p.gitignoreSkipped > 0) {
|
|
6485
|
-
notes.push(
|
|
6486
|
-
`.gitignore skipped ${p.gitignoreSkipped} (pass --no-gitignore to check them)`
|
|
6487
|
-
);
|
|
6488
|
-
}
|
|
6489
|
-
throw new DocmetaError(
|
|
6490
|
-
[
|
|
6491
|
-
`No files matched. Patterns tried: ${tried}.`,
|
|
6492
|
-
...notes.length > 0 ? [`Filters applied \u2014 ${notes.join("; ")}.`] : [],
|
|
6493
|
-
`Nothing was ${p.action}, so this is an error rather than a pass.`,
|
|
6494
|
-
`Pass --allow-empty (or set allowEmpty: true) if matching nothing is expected.`
|
|
6495
|
-
].join("\n")
|
|
6496
|
-
);
|
|
6978
|
+
function countFingerprints(baseline) {
|
|
6979
|
+
let n = 0;
|
|
6980
|
+
for (const prints of Object.values(baseline.entries)) n += prints.length;
|
|
6981
|
+
return n;
|
|
6497
6982
|
}
|
|
6498
6983
|
|
|
6499
6984
|
// src/meta/core/projection.ts
|
|
@@ -6900,16 +7385,16 @@ function parseErrorResult(file, format, message, keyword) {
|
|
|
6900
7385
|
}
|
|
6901
7386
|
function resolveBaselineRequest(opts, configured, configDir, cwd) {
|
|
6902
7387
|
const named = (label) => ({
|
|
6903
|
-
absPath:
|
|
7388
|
+
absPath: resolve9(cwd, label),
|
|
6904
7389
|
label
|
|
6905
7390
|
});
|
|
6906
7391
|
const implied = () => {
|
|
6907
7392
|
const base = configDir ?? cwd;
|
|
6908
7393
|
if (configured !== void 0) {
|
|
6909
|
-
return { absPath:
|
|
7394
|
+
return { absPath: resolve9(base, configured), label: configured };
|
|
6910
7395
|
}
|
|
6911
|
-
const current =
|
|
6912
|
-
const legacy =
|
|
7396
|
+
const current = resolve9(base, DEFAULT_BASELINE_PATH);
|
|
7397
|
+
const legacy = resolve9(base, LEGACY_BASELINE_PATH);
|
|
6913
7398
|
if (!existsSync2(current) && existsSync2(legacy)) {
|
|
6914
7399
|
warn(
|
|
6915
7400
|
`"${LEGACY_BASELINE_PATH}" is the pre-rename baseline file name. Rename it to "${DEFAULT_BASELINE_PATH}".`
|
|
@@ -6977,6 +7462,11 @@ async function runValidate(opts) {
|
|
|
6977
7462
|
action: "validated"
|
|
6978
7463
|
});
|
|
6979
7464
|
const trustRoot = schemaTrustRoot(cwd, configDir);
|
|
7465
|
+
const sidecars = await loadSidecars(config, {
|
|
7466
|
+
configDir: configDir ?? cwd,
|
|
7467
|
+
base,
|
|
7468
|
+
offline: opts.offline ?? config?.offline ?? false
|
|
7469
|
+
});
|
|
6980
7470
|
const validator = new Validator(
|
|
6981
7471
|
schemaLoadOptions({
|
|
6982
7472
|
// The config's directory when a config governs the run, so one project
|
|
@@ -6992,9 +7482,14 @@ async function runValidate(opts) {
|
|
|
6992
7482
|
})
|
|
6993
7483
|
);
|
|
6994
7484
|
const results = [];
|
|
7485
|
+
const joinHits = /* @__PURE__ */ new Map();
|
|
6995
7486
|
const scoped = opts.inputs.length > 0 || // redundant with inputs.length (stdin is an input) — kept as belt-and-suspenders
|
|
6996
7487
|
usingStdin || opts.as !== void 0 || opts.exts !== void 0 || opts.exclude !== void 0 && opts.exclude.length > 0 || opts.respectGitignore !== void 0 || (opts.cliSchemas?.length ?? 0) > 0;
|
|
6997
7488
|
const configuredChecks = config?.checks ?? [];
|
|
7489
|
+
if (!scoped) {
|
|
7490
|
+
const orphans = orphanEntries(sidecars, files, base);
|
|
7491
|
+
if (orphans.length > 0) throw orphanError(orphans);
|
|
7492
|
+
}
|
|
6998
7493
|
const checksWillRun = configuredChecks.length > 0 && opts.checks !== false && !scoped;
|
|
6999
7494
|
const checkEntries = [];
|
|
7000
7495
|
const checkResolved = /* @__PURE__ */ new Map();
|
|
@@ -7017,6 +7512,16 @@ async function runValidate(opts) {
|
|
|
7017
7512
|
);
|
|
7018
7513
|
return;
|
|
7019
7514
|
}
|
|
7515
|
+
const merged = mergeSidecars(label, extracted, sidecars, base);
|
|
7516
|
+
extracted = merged.extracted;
|
|
7517
|
+
for (const j of merged.joins) {
|
|
7518
|
+
const byValue = joinHits.get(j.field) ?? /* @__PURE__ */ new Map();
|
|
7519
|
+
const line = extracted.lineFor(j.field);
|
|
7520
|
+
const hits = byValue.get(j.value) ?? [];
|
|
7521
|
+
hits.push({ label, file: j.file, ...line != null ? { line } : {} });
|
|
7522
|
+
byValue.set(j.value, hits);
|
|
7523
|
+
joinHits.set(j.field, byValue);
|
|
7524
|
+
}
|
|
7020
7525
|
if (checksWillRun) checkEntries.push({ label, extracted });
|
|
7021
7526
|
let resolved;
|
|
7022
7527
|
try {
|
|
@@ -7046,7 +7551,8 @@ async function runValidate(opts) {
|
|
|
7046
7551
|
extracted.data,
|
|
7047
7552
|
schemaSet,
|
|
7048
7553
|
extracted.lineFor,
|
|
7049
|
-
extracted.colFor
|
|
7554
|
+
extracted.colFor,
|
|
7555
|
+
merged.locate
|
|
7050
7556
|
);
|
|
7051
7557
|
} catch (err) {
|
|
7052
7558
|
if (!(err instanceof DocmetaError) || resolved.source !== "document") {
|
|
@@ -7057,6 +7563,17 @@ async function runValidate(opts) {
|
|
|
7057
7563
|
);
|
|
7058
7564
|
return;
|
|
7059
7565
|
}
|
|
7566
|
+
for (const { key, file } of merged.collisions) {
|
|
7567
|
+
const line = extracted.lineFor(key);
|
|
7568
|
+
errors.push({
|
|
7569
|
+
schema: SIDECAR_OWNED_SCHEMA,
|
|
7570
|
+
keyword: SIDECAR_KEYWORD,
|
|
7571
|
+
subject: key,
|
|
7572
|
+
instancePath: sidecarPointer(key),
|
|
7573
|
+
message: `"${key}" is owned by sidecar ${file}; remove it from the document`,
|
|
7574
|
+
...line != null ? { line } : {}
|
|
7575
|
+
});
|
|
7576
|
+
}
|
|
7060
7577
|
results.push({
|
|
7061
7578
|
file: label,
|
|
7062
7579
|
format: extractor.name,
|
|
@@ -7075,9 +7592,37 @@ async function runValidate(opts) {
|
|
|
7075
7592
|
await processOne(STDIN_LABEL, content, forcedExtractor.extensions[0] ?? "");
|
|
7076
7593
|
}
|
|
7077
7594
|
for (const file of files) {
|
|
7078
|
-
const content = await
|
|
7595
|
+
const content = await readFile6(resolve9(base, file), "utf8");
|
|
7079
7596
|
await processOne(file, content, extname2(file));
|
|
7080
7597
|
}
|
|
7598
|
+
if (sidecars) {
|
|
7599
|
+
const byLabel = new Map(results.map((r) => [r.file, r]));
|
|
7600
|
+
const matched = /* @__PURE__ */ new Map();
|
|
7601
|
+
for (const [field, byValue] of joinHits) {
|
|
7602
|
+
matched.set(field, new Set(byValue.keys()));
|
|
7603
|
+
for (const [value, hits] of byValue) {
|
|
7604
|
+
if (hits.length < 2) continue;
|
|
7605
|
+
for (const hit of hits) {
|
|
7606
|
+
const others = hits.filter((h) => h.label !== hit.label).map((h) => h.label).join(", ");
|
|
7607
|
+
const result = byLabel.get(hit.label);
|
|
7608
|
+
if (!result) continue;
|
|
7609
|
+
result.errors.push({
|
|
7610
|
+
schema: SIDECAR_DUPLICATE_SCHEMA,
|
|
7611
|
+
keyword: SIDECAR_KEYWORD,
|
|
7612
|
+
subject: value,
|
|
7613
|
+
instancePath: sidecarPointer(field),
|
|
7614
|
+
message: `${hits.length} documents carry ${field} "${value}"; ${hit.file} cannot tell them apart (${others})`,
|
|
7615
|
+
...hit.line != null ? { line: hit.line } : {}
|
|
7616
|
+
});
|
|
7617
|
+
result.ok = false;
|
|
7618
|
+
}
|
|
7619
|
+
}
|
|
7620
|
+
}
|
|
7621
|
+
if (!scoped) {
|
|
7622
|
+
const orphans = orphanJoins(sidecars, matched);
|
|
7623
|
+
if (orphans.length > 0) throw orphanError(orphans);
|
|
7624
|
+
}
|
|
7625
|
+
}
|
|
7081
7626
|
if (configuredChecks.length > 0 && opts.checks !== false) {
|
|
7082
7627
|
if (scoped) {
|
|
7083
7628
|
opts.onNotice?.("corpus checks skipped: run is scoped");
|
|
@@ -7167,14 +7712,14 @@ async function settleBaseline(results, request, ctx) {
|
|
|
7167
7712
|
}
|
|
7168
7713
|
|
|
7169
7714
|
// src/meta/commands/get.ts
|
|
7170
|
-
import { readFile as
|
|
7171
|
-
import { resolve as
|
|
7715
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
7716
|
+
import { resolve as resolve10, extname as extname3 } from "node:path";
|
|
7172
7717
|
async function runGet(opts) {
|
|
7173
7718
|
const cwd = opts.cwd ?? process.cwd();
|
|
7174
7719
|
if (opts.fields.length === 0) {
|
|
7175
7720
|
throw new DocmetaError("Specify at least one field to get.");
|
|
7176
7721
|
}
|
|
7177
|
-
const { config, inputs, base } = await resolveRunConfig({
|
|
7722
|
+
const { config, inputs, base, configDir } = await resolveRunConfig({
|
|
7178
7723
|
cwd,
|
|
7179
7724
|
configPath: opts.configPath,
|
|
7180
7725
|
noConfig: opts.noConfig,
|
|
@@ -7220,6 +7765,11 @@ async function runGet(opts) {
|
|
|
7220
7765
|
action: "read"
|
|
7221
7766
|
});
|
|
7222
7767
|
const out = [];
|
|
7768
|
+
const sidecars = await loadSidecars(config, {
|
|
7769
|
+
configDir: configDir ?? cwd,
|
|
7770
|
+
base,
|
|
7771
|
+
offline: opts.offline ?? config?.offline ?? false
|
|
7772
|
+
});
|
|
7223
7773
|
const readOne = (label, content, extension) => {
|
|
7224
7774
|
const extractor = forced ?? extractorForExtension(extension);
|
|
7225
7775
|
if (!extractor) {
|
|
@@ -7229,9 +7779,14 @@ async function runGet(opts) {
|
|
|
7229
7779
|
}
|
|
7230
7780
|
let extracted;
|
|
7231
7781
|
try {
|
|
7232
|
-
extracted =
|
|
7233
|
-
|
|
7234
|
-
|
|
7782
|
+
extracted = mergeSidecars(
|
|
7783
|
+
label,
|
|
7784
|
+
extractor.extract(content, label, {
|
|
7785
|
+
elements: resolveElements(label, config)
|
|
7786
|
+
}),
|
|
7787
|
+
sidecars,
|
|
7788
|
+
base
|
|
7789
|
+
).extracted;
|
|
7235
7790
|
} catch (err) {
|
|
7236
7791
|
if (err instanceof DocmetaError) throw err;
|
|
7237
7792
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -7251,7 +7806,7 @@ async function runGet(opts) {
|
|
|
7251
7806
|
readOne("<stdin>", opts.stdinContent ?? "", forced.extensions[0] ?? "");
|
|
7252
7807
|
}
|
|
7253
7808
|
for (const file of files) {
|
|
7254
|
-
const content = await
|
|
7809
|
+
const content = await readFile7(resolve10(base, file), "utf8");
|
|
7255
7810
|
readOne(file, content, extname3(file));
|
|
7256
7811
|
}
|
|
7257
7812
|
return out;
|
|
@@ -7288,16 +7843,16 @@ function parseJsonPointer(pointer) {
|
|
|
7288
7843
|
|
|
7289
7844
|
// src/meta/commands/schemas.ts
|
|
7290
7845
|
import { existsSync as existsSync3 } from "node:fs";
|
|
7291
|
-
import { mkdir as mkdir2, readFile as
|
|
7292
|
-
import { dirname as
|
|
7293
|
-
import { parseDocument as
|
|
7846
|
+
import { mkdir as mkdir2, readFile as readFile8 } from "node:fs/promises";
|
|
7847
|
+
import { dirname as dirname5, extname as extname4, isAbsolute as isAbsolute3, join as join5, relative as relative7, resolve as resolve11, sep as sep3 } from "node:path";
|
|
7848
|
+
import { parseDocument as parseDocument4, parse as parseYaml3, stringify } from "yaml";
|
|
7294
7849
|
function getSchemasInfo() {
|
|
7295
7850
|
return { builtins: listBuiltins(), formats: listFormats() };
|
|
7296
7851
|
}
|
|
7297
7852
|
var DEFAULT_VENDOR_DIR = "./schema";
|
|
7298
7853
|
var DEFAULT_CONFIG_NAME = "manni.config.yaml";
|
|
7299
7854
|
function posixRelative(from, to) {
|
|
7300
|
-
return
|
|
7855
|
+
return relative7(from, to).split(sep3).join("/");
|
|
7301
7856
|
}
|
|
7302
7857
|
function vendorFileName(url) {
|
|
7303
7858
|
let parsed2;
|
|
@@ -7362,7 +7917,7 @@ function renderConfig(existing, entries, section) {
|
|
|
7362
7917
|
if (existing === null) {
|
|
7363
7918
|
return stringify({ [META_SECTION]: { schemas: entries } });
|
|
7364
7919
|
}
|
|
7365
|
-
const doc =
|
|
7920
|
+
const doc = parseDocument4(existing);
|
|
7366
7921
|
doc.setIn(section === void 0 ? ["schemas"] : [section, "schemas"], entries);
|
|
7367
7922
|
return doc.toString();
|
|
7368
7923
|
}
|
|
@@ -7383,13 +7938,13 @@ async function runVendorSchema(opts) {
|
|
|
7383
7938
|
);
|
|
7384
7939
|
}
|
|
7385
7940
|
const loaded = await loadConfig(opts.configPath, cwd);
|
|
7386
|
-
const configPath = loaded?.path ??
|
|
7941
|
+
const configPath = loaded?.path ?? resolve11(cwd, DEFAULT_CONFIG_NAME);
|
|
7387
7942
|
const configDir = loaded?.dir ?? cwd;
|
|
7388
|
-
const absDir =
|
|
7943
|
+
const absDir = resolve11(cwd, opts.dir ?? DEFAULT_VENDOR_DIR);
|
|
7389
7944
|
const absFile = join5(absDir, vendorFileName(url));
|
|
7390
7945
|
await assertNotIgnored(absFile, absDir, cwd, opts.onNotice);
|
|
7391
7946
|
const fromConfig = posixRelative(configDir, absFile);
|
|
7392
|
-
const ref =
|
|
7947
|
+
const ref = isAbsolute3(fromConfig) || fromConfig === "" ? absFile : fromConfig.startsWith(".") ? fromConfig : `./${fromConfig}`;
|
|
7393
7948
|
const { bytes } = await fetchSchemaBytes(url, {
|
|
7394
7949
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
|
|
7395
7950
|
...opts.maxBytes !== void 0 ? { maxBytes: opts.maxBytes } : {}
|
|
@@ -7404,7 +7959,7 @@ async function runVendorSchema(opts) {
|
|
|
7404
7959
|
}
|
|
7405
7960
|
let unchanged = false;
|
|
7406
7961
|
if (existsSync3(absFile)) {
|
|
7407
|
-
const current = await
|
|
7962
|
+
const current = await readFile8(absFile);
|
|
7408
7963
|
unchanged = bytes.equals(current);
|
|
7409
7964
|
if (!unchanged && !folded.replaced) {
|
|
7410
7965
|
throw new DocmetaError(
|
|
@@ -7414,7 +7969,7 @@ async function runVendorSchema(opts) {
|
|
|
7414
7969
|
}
|
|
7415
7970
|
await mkdir2(absDir, { recursive: true });
|
|
7416
7971
|
await writeFileAtomic(absFile, bytes);
|
|
7417
|
-
const existing = loaded ? await
|
|
7972
|
+
const existing = loaded ? await readFile8(configPath, "utf8") : null;
|
|
7418
7973
|
const section = loaded ? loaded.section : META_SECTION;
|
|
7419
7974
|
const text = renderConfig(existing, folded.entries, section);
|
|
7420
7975
|
reparseConfig(text, posixRelative(cwd, configPath) || DEFAULT_CONFIG_NAME, section);
|
|
@@ -7533,7 +8088,7 @@ async function runInferSchema(opts) {
|
|
|
7533
8088
|
`--min-coverage must be a percentage between 0 and 100, got ${minCoverage}.`
|
|
7534
8089
|
);
|
|
7535
8090
|
}
|
|
7536
|
-
const { config, inputs, base } = await resolveRunConfig({
|
|
8091
|
+
const { config, inputs, base, configDir } = await resolveRunConfig({
|
|
7537
8092
|
cwd,
|
|
7538
8093
|
configPath: opts.configPath,
|
|
7539
8094
|
noConfig: opts.noConfig,
|
|
@@ -7541,6 +8096,11 @@ async function runInferSchema(opts) {
|
|
|
7541
8096
|
onConfigLoaded: opts.onConfigLoaded
|
|
7542
8097
|
});
|
|
7543
8098
|
const usingStdin = inputs.includes(STDIN_TOKEN);
|
|
8099
|
+
const sidecars = await loadSidecars(config, {
|
|
8100
|
+
configDir: configDir ?? cwd,
|
|
8101
|
+
base,
|
|
8102
|
+
offline: opts.offline ?? config?.offline ?? false
|
|
8103
|
+
});
|
|
7544
8104
|
if (inputs.length === 0) {
|
|
7545
8105
|
throw new DocmetaError(
|
|
7546
8106
|
"No files to scan. Pass paths/globs, or add `paths:` under `meta:` in manni.config.yaml."
|
|
@@ -7559,10 +8119,10 @@ async function runInferSchema(opts) {
|
|
|
7559
8119
|
}
|
|
7560
8120
|
let absOut;
|
|
7561
8121
|
if (opts.out !== void 0) {
|
|
7562
|
-
absOut =
|
|
8122
|
+
absOut = resolve11(cwd, opts.out);
|
|
7563
8123
|
await assertNotIgnored(
|
|
7564
8124
|
absOut,
|
|
7565
|
-
|
|
8125
|
+
dirname5(absOut),
|
|
7566
8126
|
cwd,
|
|
7567
8127
|
opts.onNotice,
|
|
7568
8128
|
DRAFT_IGNORE_TEXT
|
|
@@ -7613,9 +8173,14 @@ async function runInferSchema(opts) {
|
|
|
7613
8173
|
filesScanned += 1;
|
|
7614
8174
|
let extracted;
|
|
7615
8175
|
try {
|
|
7616
|
-
extracted =
|
|
7617
|
-
|
|
7618
|
-
|
|
8176
|
+
extracted = mergeSidecars(
|
|
8177
|
+
label,
|
|
8178
|
+
extractor.extract(content, label, {
|
|
8179
|
+
elements: resolveElements(label, config)
|
|
8180
|
+
}),
|
|
8181
|
+
sidecars,
|
|
8182
|
+
base
|
|
8183
|
+
).extracted;
|
|
7619
8184
|
} catch (err) {
|
|
7620
8185
|
unreadable.push({
|
|
7621
8186
|
file: label,
|
|
@@ -7637,7 +8202,7 @@ async function runInferSchema(opts) {
|
|
|
7637
8202
|
scanOne(STDIN_LABEL, opts.stdinContent ?? "", forced.extensions[0] ?? "");
|
|
7638
8203
|
}
|
|
7639
8204
|
for (const file of files) {
|
|
7640
|
-
const content = await
|
|
8205
|
+
const content = await readFile8(resolve11(base, file), "utf8");
|
|
7641
8206
|
scanOne(file, content, extname4(file));
|
|
7642
8207
|
}
|
|
7643
8208
|
for (const u of unreadable) {
|
|
@@ -7679,7 +8244,7 @@ async function runInferSchema(opts) {
|
|
|
7679
8244
|
};
|
|
7680
8245
|
const keys = kept.map(({ sawEmptyString: _drop, ...rest }) => rest);
|
|
7681
8246
|
if (absOut !== void 0) {
|
|
7682
|
-
await mkdir2(
|
|
8247
|
+
await mkdir2(dirname5(absOut), { recursive: true });
|
|
7683
8248
|
await writeFileAtomic(absOut, `${JSON.stringify(draft, null, 2)}
|
|
7684
8249
|
`);
|
|
7685
8250
|
}
|
|
@@ -7698,9 +8263,9 @@ async function runInferSchema(opts) {
|
|
|
7698
8263
|
// src/meta/commands/query.ts
|
|
7699
8264
|
import { randomBytes } from "node:crypto";
|
|
7700
8265
|
import { existsSync as existsSync4 } from "node:fs";
|
|
7701
|
-
import { mkdir as mkdir3, open, readFile as
|
|
7702
|
-
import { dirname as
|
|
7703
|
-
import { parseDocument as
|
|
8266
|
+
import { mkdir as mkdir3, open, readFile as readFile9, rename as rename2, rm as rm2 } from "node:fs/promises";
|
|
8267
|
+
import { dirname as dirname6, isAbsolute as isAbsolute4, resolve as resolve12, extname as extname5, sep as sep4 } from "node:path";
|
|
8268
|
+
import { parseDocument as parseDocument5, isMap as isMap4, isSeq as isSeq2, isScalar as isScalar3 } from "yaml";
|
|
7704
8269
|
async function runQuery(opts) {
|
|
7705
8270
|
const cwd = opts.cwd ?? process.cwd();
|
|
7706
8271
|
const sql = opts.sql.trim();
|
|
@@ -7780,6 +8345,11 @@ async function runQuery(opts) {
|
|
|
7780
8345
|
action: "queried"
|
|
7781
8346
|
});
|
|
7782
8347
|
const entries = [];
|
|
8348
|
+
const sidecars = await loadSidecars(config, {
|
|
8349
|
+
configDir: configDir ?? cwd,
|
|
8350
|
+
base,
|
|
8351
|
+
offline: opts.offline ?? config?.offline ?? false
|
|
8352
|
+
});
|
|
7783
8353
|
const readOne = (label, content, extension) => {
|
|
7784
8354
|
const extractor = forced ?? extractorForExtension(extension);
|
|
7785
8355
|
if (!extractor) {
|
|
@@ -7787,9 +8357,14 @@ async function runQuery(opts) {
|
|
|
7787
8357
|
`Unsupported file type "${extension}" for "${label}". Supported: ${supportedExtensions().join(", ")}. Use --as to override.`
|
|
7788
8358
|
);
|
|
7789
8359
|
}
|
|
7790
|
-
const extracted =
|
|
7791
|
-
|
|
7792
|
-
|
|
8360
|
+
const extracted = mergeSidecars(
|
|
8361
|
+
label,
|
|
8362
|
+
extractor.extract(content, label, {
|
|
8363
|
+
elements: resolveElements(label, config)
|
|
8364
|
+
}),
|
|
8365
|
+
sidecars,
|
|
8366
|
+
base
|
|
8367
|
+
).extracted;
|
|
7793
8368
|
entries.push({ label, extracted, extractor });
|
|
7794
8369
|
};
|
|
7795
8370
|
if (usingStdin) {
|
|
@@ -7807,10 +8382,10 @@ async function runQuery(opts) {
|
|
|
7807
8382
|
readOne(STDIN_LABEL, opts.stdinContent ?? "", ext);
|
|
7808
8383
|
}
|
|
7809
8384
|
for (const file of files) {
|
|
7810
|
-
const content = await
|
|
8385
|
+
const content = await readFile9(resolve12(base, file), "utf8");
|
|
7811
8386
|
readOne(file, content, extname5(file));
|
|
7812
8387
|
}
|
|
7813
|
-
const db = opts.db === void 0 ? void 0 : { resolved:
|
|
8388
|
+
const db = opts.db === void 0 ? void 0 : { resolved: resolve12(cwd, opts.db), display: opts.db };
|
|
7814
8389
|
const run = await runSql(sql, entries, {
|
|
7815
8390
|
target: db,
|
|
7816
8391
|
params: boundParams,
|
|
@@ -7822,6 +8397,7 @@ async function runQuery(opts) {
|
|
|
7822
8397
|
configDir,
|
|
7823
8398
|
configPath,
|
|
7824
8399
|
trustRoot: schemaTrustRoot(cwd, configDir),
|
|
8400
|
+
sidecars,
|
|
7825
8401
|
onNotice: opts.onNotice
|
|
7826
8402
|
});
|
|
7827
8403
|
return { ...run, frame: { cwd, base: configDir ?? cwd, runBase: base } };
|
|
@@ -7885,7 +8461,7 @@ async function runSql(sql, entries, ctx) {
|
|
|
7885
8461
|
);
|
|
7886
8462
|
const { DatabaseSync } = await loadSqlite();
|
|
7887
8463
|
if (target) {
|
|
7888
|
-
await mkdir3(
|
|
8464
|
+
await mkdir3(dirname6(target.resolved), { recursive: true });
|
|
7889
8465
|
await prepareDbTarget(target.resolved, target.display);
|
|
7890
8466
|
}
|
|
7891
8467
|
let db;
|
|
@@ -8337,20 +8913,20 @@ function displayPath(ctx, abs) {
|
|
|
8337
8913
|
}
|
|
8338
8914
|
function assertSchemaWriteWithin(ctx, abs) {
|
|
8339
8915
|
const within = posixRelative(ctx.trustRoot.dir, abs);
|
|
8340
|
-
if (within !== "" && !within.startsWith("..") && !
|
|
8916
|
+
if (within !== "" && !within.startsWith("..") && !isAbsolute4(within)) return;
|
|
8341
8917
|
throw new DocmetaError(
|
|
8342
8918
|
`The resolved schema "${abs}" lives outside ${ctx.trustRoot.dir}; DDL edits schemas inside the repository. Evolve it where it lives, or vendor a copy in.`
|
|
8343
8919
|
);
|
|
8344
8920
|
}
|
|
8345
8921
|
function foldPath(p) {
|
|
8346
|
-
const abs =
|
|
8922
|
+
const abs = resolve12(p);
|
|
8347
8923
|
return process.platform === "win32" ? abs.toLowerCase() : abs;
|
|
8348
8924
|
}
|
|
8349
8925
|
function filePinsByPath(ctx) {
|
|
8350
8926
|
const byPath = /* @__PURE__ */ new Map();
|
|
8351
8927
|
for (const [ref, pin] of collectSchemaPins(ctx.config)) {
|
|
8352
8928
|
if (classifyRef(ref).kind !== "file") continue;
|
|
8353
|
-
byPath.set(foldPath(
|
|
8929
|
+
byPath.set(foldPath(resolve12(ctx.cwd, ref)), pin);
|
|
8354
8930
|
}
|
|
8355
8931
|
return byPath;
|
|
8356
8932
|
}
|
|
@@ -8378,13 +8954,13 @@ async function loadSetMembers(refs, ctx) {
|
|
|
8378
8954
|
});
|
|
8379
8955
|
continue;
|
|
8380
8956
|
}
|
|
8381
|
-
const abs =
|
|
8957
|
+
const abs = resolve12(ctx.cwd, ref);
|
|
8382
8958
|
const canon = foldPath(abs);
|
|
8383
8959
|
if (seenFiles.has(canon)) continue;
|
|
8384
8960
|
seenFiles.add(canon);
|
|
8385
8961
|
let text;
|
|
8386
8962
|
try {
|
|
8387
|
-
text = await
|
|
8963
|
+
text = await readFile9(abs, "utf8");
|
|
8388
8964
|
} catch {
|
|
8389
8965
|
throw new DocmetaError(
|
|
8390
8966
|
`Schema file not found: "${ref}" (looked at ${abs}).`
|
|
@@ -8578,7 +9154,7 @@ async function planSchemaMutation(ops, entries, ctx) {
|
|
|
8578
9154
|
const [, name2, ver] = target.builtinId.split(":");
|
|
8579
9155
|
const safe = (s) => s.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
8580
9156
|
const forkName = `${safe(name2 ?? "schema")}-${safe(ver ?? "0")}.local.json`;
|
|
8581
|
-
schemaAbs =
|
|
9157
|
+
schemaAbs = resolve12(ctx.configDir ?? ctx.cwd, "schemas", forkName);
|
|
8582
9158
|
assertSchemaWriteWithin(ctx, schemaAbs);
|
|
8583
9159
|
if (existsSync4(schemaAbs)) {
|
|
8584
9160
|
throw new DocmetaError(
|
|
@@ -8587,7 +9163,7 @@ async function planSchemaMutation(ops, entries, ctx) {
|
|
|
8587
9163
|
}
|
|
8588
9164
|
await assertNotIgnored(
|
|
8589
9165
|
schemaAbs,
|
|
8590
|
-
|
|
9166
|
+
dirname6(schemaAbs),
|
|
8591
9167
|
ctx.cwd,
|
|
8592
9168
|
ctx.onNotice,
|
|
8593
9169
|
FORK_IGNORE_TEXT
|
|
@@ -8734,18 +9310,18 @@ async function planConfigEdit(ctx, edit, runRefs) {
|
|
|
8734
9310
|
if (configPath === void 0 || configDir === void 0) {
|
|
8735
9311
|
return { changes: [], repointed: false };
|
|
8736
9312
|
}
|
|
8737
|
-
const text = await
|
|
8738
|
-
const doc =
|
|
9313
|
+
const text = await readFile9(configPath, "utf8");
|
|
9314
|
+
const doc = parseDocument5(stripBom(text));
|
|
8739
9315
|
const display = displayPath(ctx, configPath);
|
|
8740
9316
|
const rawRefOf = (item) => {
|
|
8741
|
-
if (
|
|
8742
|
-
if (
|
|
9317
|
+
if (isScalar3(item) && typeof item.value === "string") return item.value;
|
|
9318
|
+
if (isMap4(item)) {
|
|
8743
9319
|
const ref = item.get("ref");
|
|
8744
9320
|
if (typeof ref === "string") return ref;
|
|
8745
9321
|
}
|
|
8746
9322
|
return void 0;
|
|
8747
9323
|
};
|
|
8748
|
-
const rebaseRaw = (raw) => classifyRef(raw).kind === "file" && !
|
|
9324
|
+
const rebaseRaw = (raw) => classifyRef(raw).kind === "file" && !isAbsolute4(raw) ? resolve12(configDir, raw) : raw;
|
|
8749
9325
|
const wantSet = runRefs.map(rebaseRaw).sort().join("\n");
|
|
8750
9326
|
const seqResolvesToRunSet = (node) => {
|
|
8751
9327
|
if (!isSeq2(node)) return false;
|
|
@@ -8761,10 +9337,10 @@ async function planConfigEdit(ctx, edit, runRefs) {
|
|
|
8761
9337
|
for (const item of node.items) {
|
|
8762
9338
|
const raw = rawRefOf(item);
|
|
8763
9339
|
if (raw === void 0 || !wants(raw)) continue;
|
|
8764
|
-
if (
|
|
9340
|
+
if (isScalar3(item)) {
|
|
8765
9341
|
item.value = spec.newRef;
|
|
8766
9342
|
n += 1;
|
|
8767
|
-
} else if (
|
|
9343
|
+
} else if (isMap4(item)) {
|
|
8768
9344
|
item.set("ref", spec.newRef);
|
|
8769
9345
|
n += 1;
|
|
8770
9346
|
}
|
|
@@ -8776,7 +9352,7 @@ async function planConfigEdit(ctx, edit, runRefs) {
|
|
|
8776
9352
|
const overrides = doc.get("overrides", true);
|
|
8777
9353
|
if (isSeq2(overrides)) {
|
|
8778
9354
|
for (const entry of overrides.items) {
|
|
8779
|
-
if (
|
|
9355
|
+
if (isMap4(entry)) repointCount += repoint(entry.get("schemas", true));
|
|
8780
9356
|
}
|
|
8781
9357
|
}
|
|
8782
9358
|
const repointed = repointCount > 0;
|
|
@@ -8785,10 +9361,10 @@ async function planConfigEdit(ctx, edit, runRefs) {
|
|
|
8785
9361
|
if (edit.integrity && isSeq2(topSeq)) {
|
|
8786
9362
|
const wantPath = foldPath(edit.integrity.path);
|
|
8787
9363
|
for (const item of topSeq.items) {
|
|
8788
|
-
if (!
|
|
9364
|
+
if (!isMap4(item)) continue;
|
|
8789
9365
|
const raw = rawRefOf(item);
|
|
8790
9366
|
if (raw === void 0 || classifyRef(raw).kind !== "file") continue;
|
|
8791
|
-
const abs =
|
|
9367
|
+
const abs = isAbsolute4(raw) ? raw : resolve12(configDir, raw);
|
|
8792
9368
|
if (foldPath(abs) !== wantPath) continue;
|
|
8793
9369
|
pinFrom = item.get("integrity");
|
|
8794
9370
|
item.set("integrity", edit.integrity.value);
|
|
@@ -9009,7 +9585,7 @@ function buildChanges(diff, entries, sentinel, ctx, renameHints = [], booleanAdd
|
|
|
9009
9585
|
`"${file}": no writable format for that extension.`
|
|
9010
9586
|
);
|
|
9011
9587
|
}
|
|
9012
|
-
if (existsSync4(
|
|
9588
|
+
if (existsSync4(resolve12(ctx.base, file))) {
|
|
9013
9589
|
throw new DocmetaError(
|
|
9014
9590
|
`"${file}" already exists; INSERT creates new files only.`
|
|
9015
9591
|
);
|
|
@@ -9028,26 +9604,81 @@ function buildChanges(diff, entries, sentinel, ctx, renameHints = [], booleanAdd
|
|
|
9028
9604
|
`"${from}" -> "${to}": a rename may not change the extension.`
|
|
9029
9605
|
);
|
|
9030
9606
|
}
|
|
9031
|
-
if (existsSync4(
|
|
9607
|
+
if (existsSync4(resolve12(ctx.base, to))) {
|
|
9032
9608
|
throw new DocmetaError(
|
|
9033
9609
|
`"${to}" already exists; the rename would overwrite it.`
|
|
9034
9610
|
);
|
|
9035
9611
|
}
|
|
9036
9612
|
changes.push({ file: from, renamed: to, written: false });
|
|
9037
9613
|
}
|
|
9614
|
+
refuseSidecarWrites(changes, entries, ctx);
|
|
9038
9615
|
return changes;
|
|
9039
9616
|
}
|
|
9040
9617
|
function validateNewPath(p, base) {
|
|
9041
9618
|
const refuse = (why) => {
|
|
9042
9619
|
throw new DocmetaError(`"${p}" is not a usable path: ${why}.`);
|
|
9043
9620
|
};
|
|
9044
|
-
if (
|
|
9621
|
+
if (isAbsolute4(p)) refuse("it is absolute");
|
|
9045
9622
|
if (p.split(/[\\/]/).some((segment) => segment === "..")) {
|
|
9046
9623
|
refuse("it traverses upward");
|
|
9047
9624
|
}
|
|
9048
|
-
const abs =
|
|
9049
|
-
if (abs ===
|
|
9050
|
-
if (!abs.startsWith(
|
|
9625
|
+
const abs = resolve12(base, p);
|
|
9626
|
+
if (abs === resolve12(base)) refuse("it points at the corpus root, not a file");
|
|
9627
|
+
if (!abs.startsWith(resolve12(base) + sep4)) refuse("it escapes the corpus");
|
|
9628
|
+
}
|
|
9629
|
+
function refuseSidecarWrites(changes, entries, ctx) {
|
|
9630
|
+
const index = ctx.sidecars;
|
|
9631
|
+
if (!index) return;
|
|
9632
|
+
const owned = (key) => index.owners.get(key);
|
|
9633
|
+
const refuse = (file, key, owner) => {
|
|
9634
|
+
throw new DocmetaError(
|
|
9635
|
+
`"${file}": "${key}" is owned by sidecar ${owner}; edit the sidecar file instead.`
|
|
9636
|
+
);
|
|
9637
|
+
};
|
|
9638
|
+
const data = new Map(entries.map((e) => [e.label, e.extracted.data]));
|
|
9639
|
+
for (const c of changes) {
|
|
9640
|
+
if ("schema" in c || "config" in c) continue;
|
|
9641
|
+
if ("cleared" in c) {
|
|
9642
|
+
for (const key of Object.keys(data.get(c.file) ?? {})) {
|
|
9643
|
+
const owner = owned(key);
|
|
9644
|
+
if (owner !== void 0) refuse(c.file, key, owner);
|
|
9645
|
+
}
|
|
9646
|
+
continue;
|
|
9647
|
+
}
|
|
9648
|
+
if ("created" in c) {
|
|
9649
|
+
for (const key of Object.keys(c.to)) {
|
|
9650
|
+
const owner = owned(key);
|
|
9651
|
+
if (owner !== void 0) refuse(c.file, key, owner);
|
|
9652
|
+
}
|
|
9653
|
+
continue;
|
|
9654
|
+
}
|
|
9655
|
+
if ("renamed" in c) {
|
|
9656
|
+
const entry = index.byPath.get(resolve12(ctx.base, c.file));
|
|
9657
|
+
if (entry !== void 0 && entry.size > 0) {
|
|
9658
|
+
const manifest = [...entry.values()][0]?.file ?? "manifest";
|
|
9659
|
+
throw new DocmetaError(
|
|
9660
|
+
`"${c.file}": sidecar ${manifest} names it; rename the manifest entry first.`
|
|
9661
|
+
);
|
|
9662
|
+
}
|
|
9663
|
+
continue;
|
|
9664
|
+
}
|
|
9665
|
+
const keys = "renamedFrom" in c ? [c.key, c.renamedFrom] : [c.key];
|
|
9666
|
+
for (const key of keys) {
|
|
9667
|
+
const owner = owned(key);
|
|
9668
|
+
if (owner !== void 0) refuse(c.file, key, owner);
|
|
9669
|
+
const byValue = index.byField.get(key);
|
|
9670
|
+
if (byValue === void 0) continue;
|
|
9671
|
+
const raw = data.get(c.file)?.[key];
|
|
9672
|
+
const value = typeof raw === "string" ? raw : typeof raw === "number" || typeof raw === "boolean" ? String(raw) : void 0;
|
|
9673
|
+
const entry = value === void 0 ? void 0 : byValue.get(value);
|
|
9674
|
+
if (entry !== void 0) {
|
|
9675
|
+
const manifest = entry.values().next().value?.file ?? "manifest";
|
|
9676
|
+
throw new DocmetaError(
|
|
9677
|
+
`"${c.file}": "${key}" is the field sidecar ${manifest} joins on, and this document has an entry; change the manifest first.`
|
|
9678
|
+
);
|
|
9679
|
+
}
|
|
9680
|
+
}
|
|
9681
|
+
}
|
|
9051
9682
|
}
|
|
9052
9683
|
async function applyChanges(changes, entries, ctx, schemaPlan) {
|
|
9053
9684
|
const schemaWrites = schemaPlan?.writes ?? [];
|
|
@@ -9075,14 +9706,14 @@ async function applyChanges(changes, entries, ctx, schemaPlan) {
|
|
|
9075
9706
|
const pendingWrites = [];
|
|
9076
9707
|
const pendingRenames = [];
|
|
9077
9708
|
for (const [label, ops] of grouped) {
|
|
9078
|
-
const path =
|
|
9709
|
+
const path = resolve12(ctx.base, label);
|
|
9079
9710
|
if (ops.renamedTo !== void 0) {
|
|
9080
9711
|
if (!existsSync4(path)) {
|
|
9081
9712
|
throw new DocmetaError(
|
|
9082
9713
|
`"${label}" is gone from disk since it was read; re-run the query.`
|
|
9083
9714
|
);
|
|
9084
9715
|
}
|
|
9085
|
-
const toDisk =
|
|
9716
|
+
const toDisk = resolve12(ctx.base, ops.renamedTo);
|
|
9086
9717
|
if (existsSync4(toDisk)) {
|
|
9087
9718
|
throw new DocmetaError(
|
|
9088
9719
|
`"${ops.renamedTo}" appeared on disk since the plan; re-run the query.`
|
|
@@ -9115,10 +9746,15 @@ async function applyChanges(changes, entries, ctx, schemaPlan) {
|
|
|
9115
9746
|
}
|
|
9116
9747
|
const entry = byLabel.get(label);
|
|
9117
9748
|
if (!entry) throw new DocmetaError(`No loaded entry for "${label}".`);
|
|
9118
|
-
const content = await
|
|
9119
|
-
const current =
|
|
9120
|
-
|
|
9121
|
-
|
|
9749
|
+
const content = await readFile9(path, "utf8");
|
|
9750
|
+
const current = mergeSidecars(
|
|
9751
|
+
label,
|
|
9752
|
+
entry.extractor.extract(content, label, {
|
|
9753
|
+
elements: resolveElements(label, ctx.config)
|
|
9754
|
+
}),
|
|
9755
|
+
ctx.sidecars,
|
|
9756
|
+
ctx.base
|
|
9757
|
+
).extracted;
|
|
9122
9758
|
if (ops.cleared) {
|
|
9123
9759
|
if (!deepEqual(current.data, entry.extracted.data)) {
|
|
9124
9760
|
throw new DocmetaError(
|
|
@@ -9152,9 +9788,14 @@ async function applyChanges(changes, entries, ctx, schemaPlan) {
|
|
|
9152
9788
|
deletions: ops.deletions
|
|
9153
9789
|
});
|
|
9154
9790
|
if (ops.deletions.length > 0) {
|
|
9155
|
-
const check =
|
|
9156
|
-
|
|
9157
|
-
|
|
9791
|
+
const check = mergeSidecars(
|
|
9792
|
+
label,
|
|
9793
|
+
entry.extractor.extract(applied, label, {
|
|
9794
|
+
elements: resolveElements(label, ctx.config)
|
|
9795
|
+
}),
|
|
9796
|
+
ctx.sidecars,
|
|
9797
|
+
ctx.base
|
|
9798
|
+
).extracted;
|
|
9158
9799
|
for (const key of ops.deletions) {
|
|
9159
9800
|
if (check.data[key] !== void 0) {
|
|
9160
9801
|
throw new DocmetaError(
|
|
@@ -9177,7 +9818,7 @@ async function applyChanges(changes, entries, ctx, schemaPlan) {
|
|
|
9177
9818
|
}
|
|
9178
9819
|
let now;
|
|
9179
9820
|
try {
|
|
9180
|
-
now = await
|
|
9821
|
+
now = await readFile9(w.path, "utf8");
|
|
9181
9822
|
} catch {
|
|
9182
9823
|
now = void 0;
|
|
9183
9824
|
}
|
|
@@ -9188,10 +9829,10 @@ async function applyChanges(changes, entries, ctx, schemaPlan) {
|
|
|
9188
9829
|
}
|
|
9189
9830
|
}
|
|
9190
9831
|
const dirs = /* @__PURE__ */ new Set();
|
|
9191
|
-
for (const w of schemaWrites) dirs.add(
|
|
9192
|
-
for (const r of pendingRenames) dirs.add(
|
|
9832
|
+
for (const w of schemaWrites) dirs.add(dirname6(w.path));
|
|
9833
|
+
for (const r of pendingRenames) dirs.add(dirname6(r.to));
|
|
9193
9834
|
for (const p of pendingWrites) {
|
|
9194
|
-
if (p.ensureDir) dirs.add(
|
|
9835
|
+
if (p.ensureDir) dirs.add(dirname6(p.path));
|
|
9195
9836
|
}
|
|
9196
9837
|
for (const d of dirs) await mkdir3(d, { recursive: true });
|
|
9197
9838
|
const writeSchema = async () => {
|
|
@@ -9306,8 +9947,8 @@ function skipExpression(sql, from) {
|
|
|
9306
9947
|
}
|
|
9307
9948
|
|
|
9308
9949
|
// src/meta/commands/fill.ts
|
|
9309
|
-
import { readFile as
|
|
9310
|
-
import { resolve as
|
|
9950
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
9951
|
+
import { resolve as resolve13, extname as extname6, join as join6 } from "node:path";
|
|
9311
9952
|
import {
|
|
9312
9953
|
completeValidatedJSON,
|
|
9313
9954
|
makeProvider,
|
|
@@ -9580,6 +10221,11 @@ async function runFill(opts) {
|
|
|
9580
10221
|
let turnsUsed = 0;
|
|
9581
10222
|
let inFlight = 0;
|
|
9582
10223
|
const turnsExhausted = () => maxTurns != null && turnsUsed + inFlight >= maxTurns;
|
|
10224
|
+
const sidecars = await loadSidecars(config, {
|
|
10225
|
+
configDir: configDir ?? cwd,
|
|
10226
|
+
base,
|
|
10227
|
+
offline: opts.offline ?? config?.offline ?? false
|
|
10228
|
+
});
|
|
9583
10229
|
const processOne = async (label, content, extension) => {
|
|
9584
10230
|
const extractor = forcedExtractor ?? extractorForExtension(extension);
|
|
9585
10231
|
if (!extractor) {
|
|
@@ -9598,8 +10244,15 @@ async function runFill(opts) {
|
|
|
9598
10244
|
}
|
|
9599
10245
|
const elements = resolveElements(label, config);
|
|
9600
10246
|
let extracted;
|
|
10247
|
+
let merged;
|
|
9601
10248
|
try {
|
|
9602
|
-
|
|
10249
|
+
merged = mergeSidecars(
|
|
10250
|
+
label,
|
|
10251
|
+
extractor.extract(content, label, { elements }),
|
|
10252
|
+
sidecars,
|
|
10253
|
+
base
|
|
10254
|
+
);
|
|
10255
|
+
extracted = merged.extracted;
|
|
9603
10256
|
} catch (err) {
|
|
9604
10257
|
return errorResult(label, extractor.name, err.message);
|
|
9605
10258
|
}
|
|
@@ -9631,7 +10284,8 @@ async function runFill(opts) {
|
|
|
9631
10284
|
extracted.data,
|
|
9632
10285
|
schemaSet,
|
|
9633
10286
|
extracted.lineFor,
|
|
9634
|
-
extracted.colFor
|
|
10287
|
+
extracted.colFor,
|
|
10288
|
+
merged.locate
|
|
9635
10289
|
);
|
|
9636
10290
|
} catch (err) {
|
|
9637
10291
|
if (!(err instanceof DocmetaError) || resolved.source !== "document") {
|
|
@@ -9645,6 +10299,19 @@ async function runFill(opts) {
|
|
|
9645
10299
|
existingErrors,
|
|
9646
10300
|
only
|
|
9647
10301
|
);
|
|
10302
|
+
const ownedCandidates = sidecars ? candidates.flatMap((c) => {
|
|
10303
|
+
const file = sidecars.owners.get(c.key);
|
|
10304
|
+
return file === void 0 ? [] : [{ key: c.key, file }];
|
|
10305
|
+
}) : [];
|
|
10306
|
+
const firstOwned = ownedCandidates[0];
|
|
10307
|
+
if (firstOwned) {
|
|
10308
|
+
return errorResult(
|
|
10309
|
+
label,
|
|
10310
|
+
extractor.name,
|
|
10311
|
+
`"${firstOwned.key}" is owned by sidecar ${firstOwned.file}; manni meta fill cannot write a sidecar key. Add it to the manifest instead.`,
|
|
10312
|
+
schemaSet
|
|
10313
|
+
);
|
|
10314
|
+
}
|
|
9648
10315
|
if (candidates.length === 0) {
|
|
9649
10316
|
return {
|
|
9650
10317
|
file: label,
|
|
@@ -9785,9 +10452,9 @@ async function runFill(opts) {
|
|
|
9785
10452
|
const fields = gate(candidates, proposals, threshold);
|
|
9786
10453
|
const accepted = fields.filter((f) => f.written);
|
|
9787
10454
|
if (accepted.length > 0) {
|
|
9788
|
-
const
|
|
10455
|
+
const merged2 = { ...extracted.data, ...patchOf(accepted) };
|
|
9789
10456
|
const after = await validator.validate(
|
|
9790
|
-
|
|
10457
|
+
merged2,
|
|
9791
10458
|
schemaSet,
|
|
9792
10459
|
extracted.lineFor,
|
|
9793
10460
|
extracted.colFor
|
|
@@ -9831,7 +10498,7 @@ async function runFill(opts) {
|
|
|
9831
10498
|
}
|
|
9832
10499
|
const changed = next !== content;
|
|
9833
10500
|
if (changed && !dryRun && label !== "<stdin>") {
|
|
9834
|
-
await writeFileAtomic(
|
|
10501
|
+
await writeFileAtomic(resolve13(base, label), next);
|
|
9835
10502
|
}
|
|
9836
10503
|
return {
|
|
9837
10504
|
file: label,
|
|
@@ -9853,7 +10520,7 @@ async function runFill(opts) {
|
|
|
9853
10520
|
);
|
|
9854
10521
|
}
|
|
9855
10522
|
const fileResults = await mapConcurrent(files, concurrency, async (file) => {
|
|
9856
|
-
const content = await
|
|
10523
|
+
const content = await readFile10(resolve13(base, file), "utf8");
|
|
9857
10524
|
return processOne(file, content, extname6(file));
|
|
9858
10525
|
});
|
|
9859
10526
|
results.push(...fileResults);
|
|
@@ -10171,15 +10838,19 @@ async function mapConcurrent(items, limit, fn) {
|
|
|
10171
10838
|
}
|
|
10172
10839
|
|
|
10173
10840
|
// src/meta/reporters/sarif.ts
|
|
10174
|
-
import { isAbsolute as
|
|
10841
|
+
import { isAbsolute as isAbsolute5 } from "node:path";
|
|
10175
10842
|
|
|
10176
10843
|
// src/meta/reporters/rule-id.ts
|
|
10177
10844
|
var SYNTHETIC_SCHEMA = "(parse)";
|
|
10178
10845
|
var PARSE_ERROR_RULE = "manni/parse-error";
|
|
10179
10846
|
var SCHEMA_ERROR_RULE = "manni/schema-error";
|
|
10847
|
+
var SIDECAR_OWNED_RULE = "sidecar:owned/sidecar";
|
|
10848
|
+
var SIDECAR_DUPLICATE_RULE = "sidecar:duplicate/sidecar";
|
|
10180
10849
|
var RESERVED_RULES = {
|
|
10181
10850
|
[PARSE_ERROR_RULE]: "The document's metadata block could not be parsed.",
|
|
10182
|
-
[SCHEMA_ERROR_RULE]: "No schema set could be resolved for the document."
|
|
10851
|
+
[SCHEMA_ERROR_RULE]: "No schema set could be resolved for the document.",
|
|
10852
|
+
[SIDECAR_OWNED_RULE]: "The document carries a key a sidecar manifest owns; the manifest is the only place that key may be set.",
|
|
10853
|
+
[SIDECAR_DUPLICATE_RULE]: "Two documents carry the same value of a sidecar's join field, so one manifest entry matched both."
|
|
10183
10854
|
};
|
|
10184
10855
|
function ruleIdFor(e, frame) {
|
|
10185
10856
|
if (e.schema === SYNTHETIC_SCHEMA) {
|
|
@@ -10207,7 +10878,7 @@ function uriFrame(frame) {
|
|
|
10207
10878
|
}
|
|
10208
10879
|
function artifactUri(file, frame) {
|
|
10209
10880
|
const uri = canonicalFilePath(file, frame);
|
|
10210
|
-
if (uri === "" || uri.startsWith("../") ||
|
|
10881
|
+
if (uri === "" || uri.startsWith("../") || isAbsolute5(uri)) return null;
|
|
10211
10882
|
return uri;
|
|
10212
10883
|
}
|
|
10213
10884
|
function renderSarif(results, opts = {}) {
|
|
@@ -10221,12 +10892,13 @@ function renderSarif(results, opts = {}) {
|
|
|
10221
10892
|
for (const r of results) {
|
|
10222
10893
|
if (r.errors.length === 0) continue;
|
|
10223
10894
|
if (r.file === STDIN_LABEL) continue;
|
|
10224
|
-
const
|
|
10225
|
-
if (uri === null) {
|
|
10226
|
-
dropped += r.errors.length;
|
|
10227
|
-
continue;
|
|
10228
|
-
}
|
|
10895
|
+
const docUri = artifactUri(r.file, pathFrame);
|
|
10229
10896
|
for (const e of r.errors) {
|
|
10897
|
+
const uri = e.file != null ? artifactUri(e.file, pathFrame) : docUri;
|
|
10898
|
+
if (uri === null) {
|
|
10899
|
+
dropped += 1;
|
|
10900
|
+
continue;
|
|
10901
|
+
}
|
|
10230
10902
|
const ruleId = ruleIdFor(e, frame);
|
|
10231
10903
|
if (!rules.has(ruleId)) {
|
|
10232
10904
|
rules.set(ruleId, {
|
|
@@ -10334,7 +11006,7 @@ function renderJunit(results, opts = {}) {
|
|
|
10334
11006
|
}
|
|
10335
11007
|
lines.push(`${open2}>`);
|
|
10336
11008
|
for (const e of r.errors) {
|
|
10337
|
-
const where = e.line != null ? ` (line ${e.line})` : "";
|
|
11009
|
+
const where = e.file != null ? ` (${e.file}${e.line != null ? `:${e.line}` : ""})` : e.line != null ? ` (line ${e.line})` : "";
|
|
10338
11010
|
lines.push(
|
|
10339
11011
|
` <failure${attr("type", ruleIdFor(e, opts.frame))}${attr(
|
|
10340
11012
|
"message",
|
|
@@ -10443,7 +11115,7 @@ function renderPretty(results, summary, opts = {}) {
|
|
|
10443
11115
|
}
|
|
10444
11116
|
lines.push(`${c.red("\u2717")} ${r.file}${forgiven}`);
|
|
10445
11117
|
for (const e of r.errors) {
|
|
10446
|
-
const loc = e.line != null ? c.dim(` (line ${e.line})`) : "";
|
|
11118
|
+
const loc = e.file != null ? c.dim(` (${e.file}${e.line != null ? `:${e.line}` : ""})`) : e.line != null ? c.dim(` (line ${e.line})`) : "";
|
|
10447
11119
|
lines.push(
|
|
10448
11120
|
` ${c.cyan(fieldLabel(e.instancePath))} ${e.message}${loc} ${c.dim(
|
|
10449
11121
|
`[${e.schema}]`
|
|
@@ -10467,7 +11139,7 @@ function renderGithub(results) {
|
|
|
10467
11139
|
const lines = [];
|
|
10468
11140
|
for (const r of results) {
|
|
10469
11141
|
for (const e of r.errors) {
|
|
10470
|
-
const params = [`file=${escapeWorkflowCommandProperty(r.file)}`];
|
|
11142
|
+
const params = [`file=${escapeWorkflowCommandProperty(e.file ?? r.file)}`];
|
|
10471
11143
|
if (e.line != null) params.push(`line=${e.line}`);
|
|
10472
11144
|
if (e.col != null) params.push(`col=${e.col}`);
|
|
10473
11145
|
const msg = escapeWorkflowCommandMessage(
|
|
@@ -10766,17 +11438,32 @@ export {
|
|
|
10766
11438
|
fetchSchemaBytes,
|
|
10767
11439
|
schemaLoadOptions,
|
|
10768
11440
|
loadSchema,
|
|
11441
|
+
DEFAULT_SCHEMAS,
|
|
11442
|
+
schemaEntryRef,
|
|
11443
|
+
collectSchemaPins,
|
|
11444
|
+
rebaseConfigSchemaRefs,
|
|
11445
|
+
resolveSchemaSet,
|
|
11446
|
+
SIDECAR_FETCH_TIMEOUT_MS,
|
|
11447
|
+
SIDECAR_FETCH_MAX_BYTES,
|
|
11448
|
+
sidecarUrlProblem,
|
|
11449
|
+
fetchSidecar,
|
|
10769
11450
|
locateFrontmatter,
|
|
10770
11451
|
frontmatterInnerText,
|
|
10771
11452
|
extractFrontmatter,
|
|
10772
11453
|
applyFrontmatter,
|
|
10773
11454
|
extractorForExtension,
|
|
10774
11455
|
supportedExtensions,
|
|
10775
|
-
|
|
10776
|
-
|
|
10777
|
-
|
|
10778
|
-
|
|
10779
|
-
|
|
11456
|
+
SIDECAR_OWNED_SCHEMA,
|
|
11457
|
+
SIDECAR_DUPLICATE_SCHEMA,
|
|
11458
|
+
SIDECAR_KEYWORD,
|
|
11459
|
+
PATH_JOIN,
|
|
11460
|
+
sidecarJoin,
|
|
11461
|
+
loadSidecars,
|
|
11462
|
+
mergeSidecars,
|
|
11463
|
+
sidecarPointer,
|
|
11464
|
+
orphanEntries,
|
|
11465
|
+
orphanJoins,
|
|
11466
|
+
orphanError,
|
|
10780
11467
|
parseConfig,
|
|
10781
11468
|
loadConfig,
|
|
10782
11469
|
resolveRunConfig,
|
|
@@ -10817,4 +11504,4 @@ export {
|
|
|
10817
11504
|
renderQuery,
|
|
10818
11505
|
renderQueryCsv
|
|
10819
11506
|
};
|
|
10820
|
-
//# sourceMappingURL=chunk-
|
|
11507
|
+
//# sourceMappingURL=chunk-7M2YOXVG.js.map
|