@ai-matrx/content-ir 0.21.1 → 0.22.1
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/CHANGELOG.md +42 -0
- package/dist/index.cjs +4 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -13
- package/dist/index.js.map +1 -1
- package/dist/source.cjs +4 -13
- package/dist/source.cjs.map +1 -1
- package/dist/source.d.cts +0 -12
- package/dist/source.d.ts +0 -12
- package/dist/source.js +4 -13
- package/dist/source.js.map +1 -1
- package/dist/surfaces.cjs +3437 -0
- package/dist/surfaces.cjs.map +1 -0
- package/dist/surfaces.d.cts +870 -0
- package/dist/surfaces.d.ts +870 -0
- package/dist/surfaces.js +3354 -0
- package/dist/surfaces.js.map +1 -0
- package/package.json +14 -1
|
@@ -0,0 +1,3437 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// source/fence-nesting.ts
|
|
4
|
+
var NESTING_FENCE_LANGUAGES = /* @__PURE__ */ new Set([
|
|
5
|
+
"markdown",
|
|
6
|
+
"md",
|
|
7
|
+
"mdx"
|
|
8
|
+
]);
|
|
9
|
+
function fenceNestsInnerFences(language) {
|
|
10
|
+
return !!language && NESTING_FENCE_LANGUAGES.has(language.toLowerCase());
|
|
11
|
+
}
|
|
12
|
+
var FENCE_WHITESPACE = " \n\r\f\v";
|
|
13
|
+
function isFenceWhitespace(ch) {
|
|
14
|
+
return ch !== void 0 && ch !== "" && FENCE_WHITESPACE.includes(ch);
|
|
15
|
+
}
|
|
16
|
+
function trimFenceLine(line) {
|
|
17
|
+
let start = 0;
|
|
18
|
+
let end = line.length;
|
|
19
|
+
while (start < end && isFenceWhitespace(line[start])) start++;
|
|
20
|
+
while (end > start && isFenceWhitespace(line[end - 1])) end--;
|
|
21
|
+
return start === 0 && end === line.length ? line : line.slice(start, end);
|
|
22
|
+
}
|
|
23
|
+
var FENCE_OPENER_WHITESPACE = " \n\v\f\r \xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF";
|
|
24
|
+
function isOpenerWhitespace(ch) {
|
|
25
|
+
return ch !== void 0 && ch !== "" && FENCE_OPENER_WHITESPACE.includes(ch);
|
|
26
|
+
}
|
|
27
|
+
function parseFenceOpener(line) {
|
|
28
|
+
let start = 0;
|
|
29
|
+
let end = line.length;
|
|
30
|
+
while (start < end && isOpenerWhitespace(line[start])) start++;
|
|
31
|
+
while (end > start && isOpenerWhitespace(line[end - 1])) end--;
|
|
32
|
+
let ticks = 0;
|
|
33
|
+
while (start + ticks < end && line[start + ticks] === "`") ticks++;
|
|
34
|
+
if (ticks < 3) return null;
|
|
35
|
+
if (line.slice(start + ticks, end).includes("`")) return null;
|
|
36
|
+
let infoStart = start + ticks;
|
|
37
|
+
while (infoStart < end && isOpenerWhitespace(line[infoStart])) infoStart++;
|
|
38
|
+
let langEnd = infoStart;
|
|
39
|
+
while (langEnd < end && !isOpenerWhitespace(line[langEnd])) langEnd++;
|
|
40
|
+
return { ticks, lang: line.slice(infoStart, langEnd) };
|
|
41
|
+
}
|
|
42
|
+
function classifyInnerFenceLine(trimmed, openTicks, nests, nestedDepth) {
|
|
43
|
+
let ticks = 0;
|
|
44
|
+
while (ticks < trimmed.length && trimmed[ticks] === "`") ticks++;
|
|
45
|
+
if (ticks < 3) return "content";
|
|
46
|
+
const info = trimFenceLine(trimmed.slice(ticks));
|
|
47
|
+
if (info === "") {
|
|
48
|
+
if (ticks < openTicks) return "content";
|
|
49
|
+
if (nests && nestedDepth > 0) return "close-nested";
|
|
50
|
+
return "close-outer";
|
|
51
|
+
}
|
|
52
|
+
if (nests && ticks >= openTicks && !info.includes("`")) return "open-nested";
|
|
53
|
+
return "content";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// source/code-ranges.ts
|
|
57
|
+
function fenceOpenerOf(content) {
|
|
58
|
+
const t = content.trimStart();
|
|
59
|
+
if (t.startsWith("```")) {
|
|
60
|
+
const parsed = parseFenceOpener(t);
|
|
61
|
+
if (!parsed) return null;
|
|
62
|
+
return { char: "`", ticks: parsed.ticks, lang: parsed.lang };
|
|
63
|
+
}
|
|
64
|
+
if (content.length - t.length <= 3 && t.startsWith("~~~")) {
|
|
65
|
+
const open = /^(~{3,})(.*)$/.exec(t);
|
|
66
|
+
if (!open) return null;
|
|
67
|
+
return { char: "~", ticks: open[1].length, lang: open[2].trim().split(/\s/)[0] ?? "" };
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
var LINE_START = { inString: false, escaped: false };
|
|
72
|
+
var FenceReader = class {
|
|
73
|
+
opener;
|
|
74
|
+
depth = 0;
|
|
75
|
+
/** A nested fence was opened at some point. */
|
|
76
|
+
sawNested = false;
|
|
77
|
+
isJson;
|
|
78
|
+
nesting;
|
|
79
|
+
tildeCloser;
|
|
80
|
+
constructor(opener, options = {}) {
|
|
81
|
+
this.opener = opener;
|
|
82
|
+
this.isJson = opener.char === "`" && opener.lang === "json";
|
|
83
|
+
this.nesting = opener.char === "`" && (options.nesting ?? fenceNestsInnerFences(opener.lang));
|
|
84
|
+
this.tildeCloser = opener.char === "~" ? new RegExp(`^ {0,3}~{${opener.ticks},}[ \\t]*$`) : null;
|
|
85
|
+
}
|
|
86
|
+
/** How many nested fences are open right now. */
|
|
87
|
+
get nestedDepth() {
|
|
88
|
+
return this.depth;
|
|
89
|
+
}
|
|
90
|
+
feed(line) {
|
|
91
|
+
if (this.tildeCloser) return this.tildeCloser.test(line);
|
|
92
|
+
const trimmedLine = trimFenceLine(line);
|
|
93
|
+
const at = line.indexOf("```");
|
|
94
|
+
if (at === -1) return false;
|
|
95
|
+
if (this.isJson && jsonStringState(line, 0, at, LINE_START).inString) return false;
|
|
96
|
+
if (trimmedLine.startsWith("```")) {
|
|
97
|
+
const kind = classifyInnerFenceLine(trimmedLine, this.opener.ticks, this.nesting, this.depth);
|
|
98
|
+
if (kind === "close-outer") return true;
|
|
99
|
+
if (kind === "open-nested") {
|
|
100
|
+
this.depth++;
|
|
101
|
+
this.sawNested = true;
|
|
102
|
+
}
|
|
103
|
+
if (kind === "close-nested") this.depth--;
|
|
104
|
+
} else {
|
|
105
|
+
const closeTicks = backtickRunLength(line, at);
|
|
106
|
+
const after = trimFenceLine(line.slice(at + closeTicks));
|
|
107
|
+
if (closeTicks >= this.opener.ticks && after === "" && this.depth === 0) return true;
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
function closeFence(src, open, opener) {
|
|
113
|
+
const first = readFence(src, open, new FenceReader(opener));
|
|
114
|
+
if (first.closed || !first.reader.sawNested || first.reader.nestedDepth <= 0) return { line: first.line, closed: first.closed };
|
|
115
|
+
if (src.streaming) return { line: first.line, closed: false };
|
|
116
|
+
const strict = readFence(src, open, new FenceReader(opener, { nesting: false }));
|
|
117
|
+
return { line: strict.line, closed: strict.closed };
|
|
118
|
+
}
|
|
119
|
+
function readFence(src, open, reader) {
|
|
120
|
+
for (let k = open + 1; k < src.count; k++) {
|
|
121
|
+
if (src.openTail?.(k)) continue;
|
|
122
|
+
const line = src.line(k);
|
|
123
|
+
if (line === null) return { line: k - 1, closed: false, reader };
|
|
124
|
+
if (reader.feed(line)) return { line: k, closed: true, reader };
|
|
125
|
+
}
|
|
126
|
+
return { line: src.count - 1, closed: false, reader };
|
|
127
|
+
}
|
|
128
|
+
function jsonStringState(line, from, upTo, state2) {
|
|
129
|
+
let { inString, escaped } = state2;
|
|
130
|
+
for (let idx = from; idx < upTo; idx++) {
|
|
131
|
+
const ch = line[idx];
|
|
132
|
+
if (escaped) {
|
|
133
|
+
escaped = false;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (ch === "\\" && inString) {
|
|
137
|
+
escaped = true;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (ch === '"') inString = !inString;
|
|
141
|
+
}
|
|
142
|
+
return { inString, escaped };
|
|
143
|
+
}
|
|
144
|
+
function backtickRunLength(str, pos) {
|
|
145
|
+
let n = 0;
|
|
146
|
+
while (pos + n < str.length && str[pos + n] === "`") n++;
|
|
147
|
+
return n;
|
|
148
|
+
}
|
|
149
|
+
function quotePrefix(line) {
|
|
150
|
+
let depth = 0;
|
|
151
|
+
let offset = 0;
|
|
152
|
+
for (; ; ) {
|
|
153
|
+
const m = /^ {0,3}> ?/.exec(line.slice(offset));
|
|
154
|
+
if (!m) return { depth, offset };
|
|
155
|
+
depth++;
|
|
156
|
+
offset += m[0].length;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function stripQuotes(line, depth) {
|
|
160
|
+
let offset = 0;
|
|
161
|
+
for (let d = 0; d < depth; d++) {
|
|
162
|
+
const m = /^ {0,3}> ?/.exec(line.slice(offset));
|
|
163
|
+
if (!m) return null;
|
|
164
|
+
offset += m[0].length;
|
|
165
|
+
}
|
|
166
|
+
return line.slice(offset);
|
|
167
|
+
}
|
|
168
|
+
var LIST_ITEM = /^( *)([-*+]|\d{1,9}[.)])( +|\t|$)/;
|
|
169
|
+
function leadingSpaces(s) {
|
|
170
|
+
let i = 0;
|
|
171
|
+
while (i < s.length && s[i] === " ") i++;
|
|
172
|
+
return i;
|
|
173
|
+
}
|
|
174
|
+
function scan(text) {
|
|
175
|
+
const starts = [];
|
|
176
|
+
const ends = [];
|
|
177
|
+
for (let pos = 0; ; ) {
|
|
178
|
+
const nl = text.indexOf("\n", pos);
|
|
179
|
+
const lineEnd = nl === -1 ? text.length : nl;
|
|
180
|
+
starts.push(pos);
|
|
181
|
+
ends.push(lineEnd > pos && text.charCodeAt(lineEnd - 1) === 13 ? lineEnd - 1 : lineEnd);
|
|
182
|
+
if (nl === -1) break;
|
|
183
|
+
pos = nl + 1;
|
|
184
|
+
}
|
|
185
|
+
const count = starts.length;
|
|
186
|
+
const raw = (k) => text.slice(starts[k], ends[k]);
|
|
187
|
+
const out = { ranges: [], fences: [], paragraphs: [], endsInFence: false, pendingPara: -1 };
|
|
188
|
+
const rawLines = { count, line: raw };
|
|
189
|
+
let segment = 0;
|
|
190
|
+
for (let k = 0; k < count; k++) {
|
|
191
|
+
const opener = fenceOpenerOf(raw(k));
|
|
192
|
+
if (!opener || opener.char !== "`") continue;
|
|
193
|
+
prose(text, starts, ends, raw, segment, k, out);
|
|
194
|
+
const end = closeFence(rawLines, k, opener);
|
|
195
|
+
out.ranges.push({ start: starts[k], end: ends[end.line], kind: "fence" });
|
|
196
|
+
out.fences.push([k, end.line, end.closed]);
|
|
197
|
+
out.pendingPara = -1;
|
|
198
|
+
if (!end.closed && end.line === count - 1) out.endsInFence = true;
|
|
199
|
+
k = end.line;
|
|
200
|
+
segment = k + 1;
|
|
201
|
+
}
|
|
202
|
+
prose(text, starts, ends, raw, segment, count, out);
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
function prose(text, starts, ends, raw, from, to, out) {
|
|
206
|
+
let para = -1;
|
|
207
|
+
let quoteDepth2 = 0;
|
|
208
|
+
let lists = [];
|
|
209
|
+
const closePara = (at) => {
|
|
210
|
+
if (para !== -1) out.paragraphs.push([para, at]);
|
|
211
|
+
para = -1;
|
|
212
|
+
};
|
|
213
|
+
for (let k = from; k < to; k++) {
|
|
214
|
+
const line = raw(k);
|
|
215
|
+
const { depth, offset } = quotePrefix(line);
|
|
216
|
+
if (depth !== quoteDepth2) {
|
|
217
|
+
quoteDepth2 = depth;
|
|
218
|
+
lists = [];
|
|
219
|
+
}
|
|
220
|
+
let content = line.slice(offset);
|
|
221
|
+
if (content.trim() === "") {
|
|
222
|
+
closePara(starts[k]);
|
|
223
|
+
if (!(k === starts.length - 1 && line === "")) out.pendingPara = -1;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const indent = leadingSpaces(content);
|
|
227
|
+
while (lists.length && indent < lists[lists.length - 1]) lists.pop();
|
|
228
|
+
const item = LIST_ITEM.exec(content);
|
|
229
|
+
let column = lists.length ? lists[lists.length - 1] : 0;
|
|
230
|
+
if (item) {
|
|
231
|
+
const gap = item[3].length;
|
|
232
|
+
column = item[1].length + item[2].length + (gap >= 1 && gap <= 4 ? gap : 1);
|
|
233
|
+
lists.push(column);
|
|
234
|
+
content = content.slice(Math.min(column, content.length));
|
|
235
|
+
} else {
|
|
236
|
+
content = content.slice(Math.min(indent, column));
|
|
237
|
+
}
|
|
238
|
+
const opener = fenceOpenerOf(content);
|
|
239
|
+
if (!opener) {
|
|
240
|
+
if (para === -1) para = starts[k];
|
|
241
|
+
if (out.pendingPara === -1) out.pendingPara = starts[k];
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
closePara(starts[k]);
|
|
245
|
+
out.pendingPara = -1;
|
|
246
|
+
const col = column;
|
|
247
|
+
const src = {
|
|
248
|
+
count: to,
|
|
249
|
+
line: (j) => {
|
|
250
|
+
const inner = stripQuotes(raw(j), depth);
|
|
251
|
+
if (inner === null) return null;
|
|
252
|
+
if (opener.char === "~" && col > 0) {
|
|
253
|
+
const ind = leadingSpaces(inner);
|
|
254
|
+
if (inner.trim() !== "" && ind < col) return null;
|
|
255
|
+
return inner.slice(Math.min(ind, col));
|
|
256
|
+
}
|
|
257
|
+
return inner;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
const end = closeFence(src, k, opener);
|
|
261
|
+
out.ranges.push({ start: starts[k], end: ends[end.line], kind: "fence" });
|
|
262
|
+
out.fences.push([k, end.line, end.closed]);
|
|
263
|
+
if (!end.closed && end.line === starts.length - 1) out.endsInFence = true;
|
|
264
|
+
k = end.line;
|
|
265
|
+
}
|
|
266
|
+
closePara(to < starts.length ? starts[to] : text.length);
|
|
267
|
+
}
|
|
268
|
+
var BacktickRuns = class {
|
|
269
|
+
/** Every run, in order: [start, length]. */
|
|
270
|
+
all = [];
|
|
271
|
+
byLength = /* @__PURE__ */ new Map();
|
|
272
|
+
indexOf = /* @__PURE__ */ new Map();
|
|
273
|
+
constructor(text, from, to) {
|
|
274
|
+
for (let k = from; k < to; ) {
|
|
275
|
+
if (text.charCodeAt(k) !== 96) {
|
|
276
|
+
k++;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
let len = 1;
|
|
280
|
+
while (k + len < to && text.charCodeAt(k + len) === 96) len++;
|
|
281
|
+
this.indexOf.set(k, this.all.length);
|
|
282
|
+
this.all.push([k, len]);
|
|
283
|
+
const list = this.byLength.get(len);
|
|
284
|
+
if (list) list.push(k);
|
|
285
|
+
else this.byLength.set(len, [k]);
|
|
286
|
+
k += len;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/** Start of the first run of exactly `n` backticks at or after `after`, or -1. */
|
|
290
|
+
next(n, after) {
|
|
291
|
+
const list = this.byLength.get(n);
|
|
292
|
+
if (!list) return -1;
|
|
293
|
+
let lo = 0;
|
|
294
|
+
let hi = list.length;
|
|
295
|
+
while (lo < hi) {
|
|
296
|
+
const mid = lo + hi >>> 1;
|
|
297
|
+
if (list[mid] < after) lo = mid + 1;
|
|
298
|
+
else hi = mid;
|
|
299
|
+
}
|
|
300
|
+
return list[lo] ?? -1;
|
|
301
|
+
}
|
|
302
|
+
/** Index of the run starting at `start` in `all`. */
|
|
303
|
+
index(start) {
|
|
304
|
+
return this.indexOf.get(start) ?? -1;
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
function codeSpans(text, from, to, out) {
|
|
308
|
+
let runs = null;
|
|
309
|
+
for (let i = from; i < to; ) {
|
|
310
|
+
const ch = text.charCodeAt(i);
|
|
311
|
+
if (ch === 92) {
|
|
312
|
+
i += 2;
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (ch !== 96) {
|
|
316
|
+
i++;
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const n = backtickRunLength(text, i);
|
|
320
|
+
const len = Math.min(n, to - i);
|
|
321
|
+
runs ??= new BacktickRuns(text, from, to);
|
|
322
|
+
const close = runs.next(len, i + len);
|
|
323
|
+
if (close === -1) {
|
|
324
|
+
i += len;
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
out.push({ start: i, end: close + len, kind: "span" });
|
|
328
|
+
i = close + len;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function findCodeRanges(text) {
|
|
332
|
+
const s = scan(text);
|
|
333
|
+
const ranges = [...s.ranges];
|
|
334
|
+
for (const [from, to] of s.paragraphs) codeSpans(text, from, to, ranges);
|
|
335
|
+
return ranges.sort((a, b) => a.start - b.start);
|
|
336
|
+
}
|
|
337
|
+
function fenceParts(raw) {
|
|
338
|
+
const lines = raw.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
339
|
+
const { offset } = quotePrefix(lines[0] ?? "");
|
|
340
|
+
const opener = fenceOpenerOf((lines[0] ?? "").slice(offset).replace(/^ *(?:[-*+]|\d{1,9}[.)])(?= |\t|$) ?/, ""));
|
|
341
|
+
if (!opener) return null;
|
|
342
|
+
const rest = lines.slice(1);
|
|
343
|
+
const closed = rest.length > 0 && new FenceReader(opener, { nesting: false }).feed(rest[rest.length - 1]);
|
|
344
|
+
if (!closed) return { opener, body: rest.join("\n"), closed };
|
|
345
|
+
const last = rest[rest.length - 1];
|
|
346
|
+
const before = trimFenceLine(last).startsWith("```") ? "" : last.slice(0, last.indexOf("```"));
|
|
347
|
+
return { opener, body: [...rest.slice(0, -1), ...before.trim() ? [before] : []].join("\n"), closed };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// source/xml-tag.ts
|
|
351
|
+
var XML_NAME_START = /[A-Za-z_]/;
|
|
352
|
+
var XML_NAME_CHARACTER = /[\w.:-]/;
|
|
353
|
+
var WHITESPACE = /\s/;
|
|
354
|
+
function readXmlTag(content, start, find = (needle, from) => content.indexOf(needle, from)) {
|
|
355
|
+
if (content[start] !== "<") return null;
|
|
356
|
+
let cursor = start + 1;
|
|
357
|
+
const isClosing = content[cursor] === "/";
|
|
358
|
+
if (isClosing) cursor++;
|
|
359
|
+
if (!XML_NAME_START.test(content[cursor] ?? "")) return null;
|
|
360
|
+
const nameStart = cursor;
|
|
361
|
+
while (XML_NAME_CHARACTER.test(content[cursor] ?? "")) cursor++;
|
|
362
|
+
const tagName = content.slice(nameStart, cursor);
|
|
363
|
+
const attributes = [];
|
|
364
|
+
if (isClosing) {
|
|
365
|
+
while (WHITESPACE.test(content[cursor] ?? "")) cursor++;
|
|
366
|
+
if (content[cursor] !== ">") return null;
|
|
367
|
+
return {
|
|
368
|
+
tagName,
|
|
369
|
+
raw: content.slice(start, cursor + 1),
|
|
370
|
+
isClosing,
|
|
371
|
+
isSelfClosing: false,
|
|
372
|
+
attributes
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
while (cursor < content.length) {
|
|
376
|
+
while (WHITESPACE.test(content[cursor] ?? "")) cursor++;
|
|
377
|
+
if (content[cursor] === ">")
|
|
378
|
+
return {
|
|
379
|
+
tagName,
|
|
380
|
+
raw: content.slice(start, cursor + 1),
|
|
381
|
+
isClosing,
|
|
382
|
+
isSelfClosing: false,
|
|
383
|
+
attributes
|
|
384
|
+
};
|
|
385
|
+
if (content[cursor] === "/") {
|
|
386
|
+
cursor++;
|
|
387
|
+
while (WHITESPACE.test(content[cursor] ?? "")) cursor++;
|
|
388
|
+
if (content[cursor] !== ">") return null;
|
|
389
|
+
return {
|
|
390
|
+
tagName,
|
|
391
|
+
raw: content.slice(start, cursor + 1),
|
|
392
|
+
isClosing,
|
|
393
|
+
isSelfClosing: true,
|
|
394
|
+
attributes
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
if (!XML_NAME_START.test(content[cursor] ?? "")) return null;
|
|
398
|
+
const attributeStart = cursor;
|
|
399
|
+
while (XML_NAME_CHARACTER.test(content[cursor] ?? "")) cursor++;
|
|
400
|
+
const name = content.slice(attributeStart, cursor);
|
|
401
|
+
while (WHITESPACE.test(content[cursor] ?? "")) cursor++;
|
|
402
|
+
if (content[cursor] !== "=") return null;
|
|
403
|
+
cursor++;
|
|
404
|
+
while (WHITESPACE.test(content[cursor] ?? "")) cursor++;
|
|
405
|
+
const quote = content[cursor];
|
|
406
|
+
if (quote !== '"' && quote !== "'") return null;
|
|
407
|
+
const valueStart = ++cursor;
|
|
408
|
+
const valueEnd = find(quote, valueStart);
|
|
409
|
+
if (valueEnd === -1) return null;
|
|
410
|
+
cursor = valueEnd;
|
|
411
|
+
attributes.push({ name, value: content.slice(valueStart, cursor) });
|
|
412
|
+
cursor++;
|
|
413
|
+
}
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// surfaces/json-kind-signal.ts
|
|
418
|
+
var UNDECIDED_LIMIT_CHARS = 2e3;
|
|
419
|
+
var KIND_KEY_BODY = [..."__kind"].map((ch) => {
|
|
420
|
+
const hex = ch.charCodeAt(0).toString(16).padStart(4, "0");
|
|
421
|
+
const escaped = [...hex].map((d) => /[a-f]/.test(d) ? `[${d}${d.toUpperCase()}]` : d).join("");
|
|
422
|
+
return `(?:${ch}|\\\\u${escaped})`;
|
|
423
|
+
}).join("");
|
|
424
|
+
var KIND_KEY = new RegExp(String.raw`(?<!\\)"${KIND_KEY_BODY}"\s*:`);
|
|
425
|
+
var JSON5_KIND_KEY = /(?:^|[{,]\s*|\n\s*)(?:__kind|'__kind')\s*:/;
|
|
426
|
+
var JSON5_KIND_SLUG = /(?:^|[{,]\s*|\n\s*)(?:__kind|'__kind'|"__kind")\s*:\s*("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/;
|
|
427
|
+
var MARKDOWN_KIND_KEY = /(?<!\\)"(?:\\_\\_|\\__|_\\_)kind"\s*:/;
|
|
428
|
+
var ESCAPED_KIND_KEY = /\\+"__kind\\+"\s*:/;
|
|
429
|
+
var ESCAPED_KIND_SLUG = /\\+"__kind\\+"\s*:\s*\\+"([A-Za-z0-9_.:-]+)\\+"/;
|
|
430
|
+
var ALL_KIND_SPELLINGS = Object.freeze({
|
|
431
|
+
markdown: true,
|
|
432
|
+
escaped: true,
|
|
433
|
+
python: true,
|
|
434
|
+
smart: true,
|
|
435
|
+
entity: true,
|
|
436
|
+
js: true
|
|
437
|
+
});
|
|
438
|
+
var JSON_KIND_SPELLINGS = Object.freeze({ markdown: true });
|
|
439
|
+
function hasJsonKindKey(source) {
|
|
440
|
+
return hasKindKey(source, JSON_KIND_SPELLINGS);
|
|
441
|
+
}
|
|
442
|
+
function hasDetectionOnlyKindKey(source) {
|
|
443
|
+
return !hasJsonKindKey(source) && (hasKindKey(source, ALL_KIND_SPELLINGS) || hasExoticKindKey(source));
|
|
444
|
+
}
|
|
445
|
+
var ZERO_WIDTH = /[\u200B-\u200D\u2060\uFEFF\u00AD]/g;
|
|
446
|
+
var HAS_ZERO_WIDTH = /[\u200B-\u200D\u2060\uFEFF\u00AD]/;
|
|
447
|
+
function withoutZeroWidth(text) {
|
|
448
|
+
return HAS_ZERO_WIDTH.test(text) ? text.replace(ZERO_WIDTH, "") : text;
|
|
449
|
+
}
|
|
450
|
+
var PYTHON_KIND_KEY = /[{,]\s*'__kind'\s*:\s*['"]/;
|
|
451
|
+
var PYTHON_KIND_SLUG = /[{,]\s*'__kind'\s*:\s*(?:'([A-Za-z0-9_.:-]+)'|"([A-Za-z0-9_.:-]+)")/;
|
|
452
|
+
var SMART_DOUBLE = "\u201C\u201D\u201E\u201F\u2033";
|
|
453
|
+
var SMART_SINGLE = "\u2018\u2019\u201A\u201B\u2032";
|
|
454
|
+
var SMART_KIND_KEY = new RegExp(`[${SMART_DOUBLE}${SMART_SINGLE}]__kind[${SMART_DOUBLE}${SMART_SINGLE}"']\\s*:`);
|
|
455
|
+
var ENTITY_QUOTE = "&(?:[qQ][uU][oO][tT]|[aA][pP][oO][sS]|#0*3[49]|#[xX]0*2[27]);";
|
|
456
|
+
var ENTITY_KIND_KEY = new RegExp(`${ENTITY_QUOTE}__kind${ENTITY_QUOTE}\\s*:`);
|
|
457
|
+
function isJson5Language(lang) {
|
|
458
|
+
return (lang ?? "").trim().toLowerCase() === "json5";
|
|
459
|
+
}
|
|
460
|
+
function hasKindKey(source, options = {}) {
|
|
461
|
+
const text = withoutZeroWidth(source);
|
|
462
|
+
return KIND_KEY.test(text) || options.python === true && PYTHON_KIND_KEY.test(text) || options.json5 === true && JSON5_KIND_KEY.test(text) || options.markdown === true && MARKDOWN_KIND_KEY.test(text) || options.escaped === true && ESCAPED_KIND_KEY.test(text) || options.smart === true && SMART_KIND_KEY.test(text) || options.entity === true && ENTITY_KIND_KEY.test(text) || options.js === true && JS_KIND_KEY.test(text) || // Every realistic spelling COMBINED in one key (escaped + zero-width,
|
|
463
|
+
// repr + zero-width, `"\__kind"`, `{\"\\_\\_kind\"…}` — L-1, round 9).
|
|
464
|
+
options.escaped === true && options.python === true && options.smart === true && options.markdown === true && hasSpelledKey(source);
|
|
465
|
+
}
|
|
466
|
+
var JS_KIND_KEY = /[{,]\s*__kind\s*:\s*['"]/;
|
|
467
|
+
function hasExoticKindKey(text) {
|
|
468
|
+
if (!/kind|&#|&|\uFF3F/i.test(text)) return false;
|
|
469
|
+
let plain = text.replace(EXOTIC_INVISIBLE, "").replace(/\uFF02/g, '"').replace(/\uFF07/g, "'").replace(/\uFF3F/g, "_");
|
|
470
|
+
for (let pass = 0; pass < 3 && plain.includes("&"); pass++) plain = plain.replace(LOOSE_ENTITY, looseEntity);
|
|
471
|
+
return plain !== text && hasKindKey(plain, ALL_KIND_SPELLINGS);
|
|
472
|
+
}
|
|
473
|
+
var EXOTIC_INVISIBLE = /[\p{Cf}\u034F\u115F\u1160\u17B4\u17B5\u180E\u3164\uFFA0]/gu;
|
|
474
|
+
var LOOSE_ENTITY = /&(#[xX][0-9a-fA-F]{1,8}|#\d{1,8}|[A-Za-z]{2,8});/g;
|
|
475
|
+
var LOOSE_NAMED = { quot: '"', apos: "'", amp: "&", lt: "<", gt: ">", lowbar: "_" };
|
|
476
|
+
function looseEntity(whole, body) {
|
|
477
|
+
if (body.startsWith("#")) {
|
|
478
|
+
const code = body[1] === "x" || body[1] === "X" ? Number.parseInt(body.slice(2), 16) : Number.parseInt(body.slice(1), 10);
|
|
479
|
+
return Number.isFinite(code) && code > 0 && code < 1114112 ? String.fromCodePoint(code) : whole;
|
|
480
|
+
}
|
|
481
|
+
return LOOSE_NAMED[body.toLowerCase()] ?? whole;
|
|
482
|
+
}
|
|
483
|
+
function hasKindKeyAnySpelling(source) {
|
|
484
|
+
return hasKindKey(source, ALL_KIND_SPELLINGS);
|
|
485
|
+
}
|
|
486
|
+
var KIND_SLUG = new RegExp(
|
|
487
|
+
String.raw`(?<!\\)"${KIND_KEY_BODY}"\s*:\s*("(?:[^"\\]|\\.)*")`
|
|
488
|
+
);
|
|
489
|
+
var SLUG_TEXT = /^[A-Za-z0-9_.:-]+$/;
|
|
490
|
+
function isKindSlug(value) {
|
|
491
|
+
return typeof value === "string" && SLUG_TEXT.test(value);
|
|
492
|
+
}
|
|
493
|
+
function firstKindSlug(source, options = {}) {
|
|
494
|
+
const text = withoutZeroWidth(source);
|
|
495
|
+
let literal = KIND_SLUG.exec(text)?.[1];
|
|
496
|
+
if (!literal && (options.json5 || options.js)) {
|
|
497
|
+
const json5 = JSON5_KIND_SLUG.exec(text)?.[1];
|
|
498
|
+
literal = json5?.startsWith("'") ? JSON.stringify(json5.slice(1, -1)) : json5;
|
|
499
|
+
}
|
|
500
|
+
if (!literal && options.escaped) {
|
|
501
|
+
const escaped = ESCAPED_KIND_SLUG.exec(text)?.[1];
|
|
502
|
+
if (escaped) return escaped;
|
|
503
|
+
}
|
|
504
|
+
if (!literal && options.python) {
|
|
505
|
+
const python = PYTHON_KIND_SLUG.exec(text);
|
|
506
|
+
if (python) return python[1] ?? python[2] ?? null;
|
|
507
|
+
}
|
|
508
|
+
if (!literal && (options.smart || options.entity || options.markdown)) {
|
|
509
|
+
const canonical = normalizeKindSpellings(text);
|
|
510
|
+
if (canonical !== text) return firstKindSlug(canonical, { ...options, smart: false, entity: false, markdown: false });
|
|
511
|
+
}
|
|
512
|
+
if (!literal) return null;
|
|
513
|
+
let slug;
|
|
514
|
+
try {
|
|
515
|
+
slug = JSON.parse(literal);
|
|
516
|
+
} catch {
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
return typeof slug === "string" && SLUG_TEXT.test(slug) ? slug : null;
|
|
520
|
+
}
|
|
521
|
+
var PARTIAL_KIND_KEY_TAIL = /"__k(?:i(?:n(?:d(?:"\s*)?)?)?)?$/;
|
|
522
|
+
function endsInPartialKindKey(text) {
|
|
523
|
+
const tail = PARTIAL_KIND_KEY_TAIL.exec(text);
|
|
524
|
+
if (!tail) return false;
|
|
525
|
+
return isObjectKeyPosition(text, tail.index);
|
|
526
|
+
}
|
|
527
|
+
function isObjectKeyPosition(text, quoteAt) {
|
|
528
|
+
const stack = [];
|
|
529
|
+
let expectKey = false;
|
|
530
|
+
let inString = false;
|
|
531
|
+
let escaped = false;
|
|
532
|
+
for (let i = 0; i < quoteAt; i++) {
|
|
533
|
+
const ch = text[i];
|
|
534
|
+
if (inString) {
|
|
535
|
+
if (escaped) escaped = false;
|
|
536
|
+
else if (ch === "\\") escaped = true;
|
|
537
|
+
else if (ch === '"') inString = false;
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
if (ch === '"') {
|
|
541
|
+
inString = true;
|
|
542
|
+
expectKey = false;
|
|
543
|
+
} else if (ch === "{" || ch === "[") {
|
|
544
|
+
stack.push(ch);
|
|
545
|
+
expectKey = ch === "{";
|
|
546
|
+
} else if (ch === "}" || ch === "]") {
|
|
547
|
+
stack.pop();
|
|
548
|
+
expectKey = false;
|
|
549
|
+
} else if (ch === ",") {
|
|
550
|
+
expectKey = stack[stack.length - 1] === "{";
|
|
551
|
+
} else if (ch === ":") {
|
|
552
|
+
expectKey = false;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return !inString && expectKey;
|
|
556
|
+
}
|
|
557
|
+
function withoutLeadingJsonComments(text) {
|
|
558
|
+
let i = 0;
|
|
559
|
+
for (; ; ) {
|
|
560
|
+
i = skipWs(text, i);
|
|
561
|
+
if (text.startsWith("//", i)) {
|
|
562
|
+
const end = text.indexOf("\n", i);
|
|
563
|
+
if (end === -1) return "";
|
|
564
|
+
i = end + 1;
|
|
565
|
+
} else if (text.startsWith("/*", i)) {
|
|
566
|
+
const end = text.indexOf("*/", i + 2);
|
|
567
|
+
if (end === -1) return "";
|
|
568
|
+
i = end + 2;
|
|
569
|
+
} else {
|
|
570
|
+
return i === 0 ? text : text.slice(i);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
function jsonKindSignal(text, options = {}) {
|
|
575
|
+
const source = withoutLeadingJsonComments(text ?? "");
|
|
576
|
+
if (hasKindKey(source, options)) return "kind";
|
|
577
|
+
let i = skipWs(source, 0);
|
|
578
|
+
if (i >= source.length) return "undecided";
|
|
579
|
+
if (source[i] === "[") {
|
|
580
|
+
i = skipWs(source, i + 1);
|
|
581
|
+
if (i >= source.length) return capped(source);
|
|
582
|
+
if (source[i] !== "{") return notKindUnlessPartialKey(source);
|
|
583
|
+
}
|
|
584
|
+
if (source[i] !== "{") return notKindUnlessPartialKey(source);
|
|
585
|
+
i = skipWs(source, i + 1);
|
|
586
|
+
if (i >= source.length) return capped(source);
|
|
587
|
+
if (source[i] !== '"') return notKindUnlessPartialKey(source);
|
|
588
|
+
let j = i + 1;
|
|
589
|
+
while (j < source.length) {
|
|
590
|
+
const ch = source[j];
|
|
591
|
+
if (ch === "\\") {
|
|
592
|
+
j += 2;
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
if (ch === '"') {
|
|
596
|
+
return withoutZeroWidth(decodedKey(source.slice(i, j + 1))) === "__kind" ? "kind" : notKindUnlessPartialKey(source);
|
|
597
|
+
}
|
|
598
|
+
j++;
|
|
599
|
+
}
|
|
600
|
+
return capped(source);
|
|
601
|
+
}
|
|
602
|
+
function notKindUnlessPartialKey(source) {
|
|
603
|
+
return endsInPartialKindKey(source) ? "undecided" : "not_kind";
|
|
604
|
+
}
|
|
605
|
+
function decodedKey(literal) {
|
|
606
|
+
try {
|
|
607
|
+
const key = JSON.parse(literal);
|
|
608
|
+
return typeof key === "string" ? key : literal.slice(1, -1);
|
|
609
|
+
} catch {
|
|
610
|
+
return literal.slice(1, -1);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
function capped(source) {
|
|
614
|
+
return source.length > UNDECIDED_LIMIT_CHARS ? "not_kind" : "undecided";
|
|
615
|
+
}
|
|
616
|
+
function skipWs(source, from) {
|
|
617
|
+
let i = from;
|
|
618
|
+
while (i < source.length && /\s/.test(source[i] ?? "")) i++;
|
|
619
|
+
return i;
|
|
620
|
+
}
|
|
621
|
+
function json5AsJson(text) {
|
|
622
|
+
let out = "";
|
|
623
|
+
let i = 0;
|
|
624
|
+
while (i < text.length) {
|
|
625
|
+
const ch = text[i];
|
|
626
|
+
if (ch === '"' || ch === "'") {
|
|
627
|
+
let j = i + 1;
|
|
628
|
+
let body = "";
|
|
629
|
+
while (j < text.length && text[j] !== ch) {
|
|
630
|
+
if (text[j] === "\\") {
|
|
631
|
+
body += text[j] + (text[j + 1] ?? "");
|
|
632
|
+
j += 2;
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
body += ch === "'" && text[j] === '"' ? '\\"' : text[j];
|
|
636
|
+
j++;
|
|
637
|
+
}
|
|
638
|
+
out += `"${body}"`;
|
|
639
|
+
i = j + 1;
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
if (text.startsWith("//", i)) {
|
|
643
|
+
const end = text.indexOf("\n", i);
|
|
644
|
+
i = end === -1 ? text.length : end;
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
if (text.startsWith("/*", i)) {
|
|
648
|
+
const end = text.indexOf("*/", i + 2);
|
|
649
|
+
i = end === -1 ? text.length : end + 2;
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
const key = /^[A-Za-z_$][\w$]*(?=\s*:)/.exec(text.slice(i));
|
|
653
|
+
if (key && /[{,]\s*$/.test(out)) {
|
|
654
|
+
out += `"${key[0]}"`;
|
|
655
|
+
i += key[0].length;
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
out += ch;
|
|
659
|
+
i++;
|
|
660
|
+
}
|
|
661
|
+
out = out.replace(/,(\s*[}\]])/g, "$1");
|
|
662
|
+
try {
|
|
663
|
+
JSON.parse(out);
|
|
664
|
+
return out;
|
|
665
|
+
} catch {
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
function pythonReprAsJson(text) {
|
|
670
|
+
let out = "";
|
|
671
|
+
let i = 0;
|
|
672
|
+
while (i < text.length) {
|
|
673
|
+
const ch = text[i];
|
|
674
|
+
if (ch === "'" || ch === '"') {
|
|
675
|
+
let j = i + 1;
|
|
676
|
+
let body = "";
|
|
677
|
+
while (j < text.length && text[j] !== ch) {
|
|
678
|
+
if (text[j] === "\\") {
|
|
679
|
+
const next = text[j + 1] ?? "";
|
|
680
|
+
body += next === "'" ? "'" : `\\${next}`;
|
|
681
|
+
j += 2;
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
body += text[j] === '"' ? '\\"' : text[j];
|
|
685
|
+
j++;
|
|
686
|
+
}
|
|
687
|
+
if (j >= text.length) return null;
|
|
688
|
+
out += `"${body}"`;
|
|
689
|
+
i = j + 1;
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
const word = /^(?:True|False|None)\b/.exec(text.slice(i, i + 5));
|
|
693
|
+
if (word) {
|
|
694
|
+
out += word[0] === "True" ? "true" : word[0] === "False" ? "false" : "null";
|
|
695
|
+
i += word[0].length;
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
out += ch;
|
|
699
|
+
i++;
|
|
700
|
+
}
|
|
701
|
+
try {
|
|
702
|
+
JSON.parse(out);
|
|
703
|
+
return out;
|
|
704
|
+
} catch {
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
function hasPythonKindKey(text) {
|
|
709
|
+
return PYTHON_KIND_KEY.test(withoutZeroWidth(text));
|
|
710
|
+
}
|
|
711
|
+
function pythonBalancedEnd(text, start) {
|
|
712
|
+
let depth = 0;
|
|
713
|
+
let quote = null;
|
|
714
|
+
for (let i = start; i < text.length; i++) {
|
|
715
|
+
const ch = text[i];
|
|
716
|
+
if (quote) {
|
|
717
|
+
if (ch === "\\") i++;
|
|
718
|
+
else if (ch === quote) quote = null;
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
if (ch === "'" || ch === '"') quote = ch;
|
|
722
|
+
else if (ch === "{" || ch === "[") depth++;
|
|
723
|
+
else if (ch === "}" || ch === "]") {
|
|
724
|
+
depth--;
|
|
725
|
+
if (depth === 0) return i + 1;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
return null;
|
|
729
|
+
}
|
|
730
|
+
function normalizeKindSpellings(source) {
|
|
731
|
+
if (!mayHoldKindKey(source)) return source;
|
|
732
|
+
if (/^[{[]/.test(source.trimStart())) {
|
|
733
|
+
try {
|
|
734
|
+
JSON.parse(source);
|
|
735
|
+
return withoutZeroWidthInKeys(source);
|
|
736
|
+
} catch {
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
let out = "";
|
|
740
|
+
let cursor = 0;
|
|
741
|
+
for (const region of scanKindSpellingRegions(source)) {
|
|
742
|
+
const rewritten = canonicalRegionText(source, region);
|
|
743
|
+
if (rewritten === null) continue;
|
|
744
|
+
out += source.slice(cursor, region.rewriteStart) + rewritten;
|
|
745
|
+
cursor = region.rewriteEnd;
|
|
746
|
+
}
|
|
747
|
+
return cursor === 0 ? source : out + source.slice(cursor);
|
|
748
|
+
}
|
|
749
|
+
function canonicalRegionText(source, region) {
|
|
750
|
+
if (region.family === "lifted") {
|
|
751
|
+
if (region.markdown) return region.status === "broken" ? closedJson(region) ?? region.decoded : region.decoded;
|
|
752
|
+
const key = source.slice(region.rewriteStart, region.rewriteEnd);
|
|
753
|
+
return region.keyText === void 0 || key === region.keyText ? null : region.keyText;
|
|
754
|
+
}
|
|
755
|
+
if (region.status === "complete") {
|
|
756
|
+
const json = regionJson(region);
|
|
757
|
+
if (json !== null) return json;
|
|
758
|
+
return region.family === "python" || region.family === "js" ? null : region.decoded;
|
|
759
|
+
}
|
|
760
|
+
if (region.status === "broken") return closedJson(region) ?? region.decoded;
|
|
761
|
+
return region.family === "python" || region.family === "js" ? null : region.decoded;
|
|
762
|
+
}
|
|
763
|
+
var ZW = "[\\u200B-\\u200D\\u2060\\uFEFF\\u00AD]";
|
|
764
|
+
var KEY_UNDERSCORE = String.raw`(?:\\*_|\\u005[fF]|�*95;|&#[xX]0*5[fF];|_|_)`;
|
|
765
|
+
var KEY_CORE = `${ZW}*${KEY_UNDERSCORE}${ZW}*${KEY_UNDERSCORE}${ZW}*k${ZW}*i${ZW}*n${ZW}*d${ZW}*`;
|
|
766
|
+
var SMART_ANY = `[${SMART_DOUBLE}${SMART_SINGLE}]`;
|
|
767
|
+
var SPELLED_KEY_SOURCE = [
|
|
768
|
+
String.raw`(\\*)"(${KEY_CORE})\\*"\s*:`,
|
|
769
|
+
String.raw`(?<=[{,]\s*)'(${KEY_CORE})'\s*:(?=\s*['"])`,
|
|
770
|
+
`${SMART_ANY}(${KEY_CORE})[${SMART_DOUBLE}${SMART_SINGLE}"']\\s*:`,
|
|
771
|
+
`${ENTITY_QUOTE}(${KEY_CORE})${ENTITY_QUOTE}\\s*:`,
|
|
772
|
+
String.raw`(?<=[{,]\s*)(${KEY_CORE})\s*:(?=\s*['"])`
|
|
773
|
+
].join("|");
|
|
774
|
+
var SPELLED_KEY = new RegExp(SPELLED_KEY_SOURCE);
|
|
775
|
+
var SPELLED_KEY_G = new RegExp(SPELLED_KEY_SOURCE, "g");
|
|
776
|
+
function hasSpelledKey(text) {
|
|
777
|
+
return mayHoldKindKey(text) && SPELLED_KEY.test(text);
|
|
778
|
+
}
|
|
779
|
+
function mayHoldKindKey(text) {
|
|
780
|
+
return text.includes("kind") || text.includes("_") && HAS_ZERO_WIDTH.test(text);
|
|
781
|
+
}
|
|
782
|
+
var MAX_REGIONS_PER_CALL = 2e3;
|
|
783
|
+
var KEY_LOOKBEHIND = 64;
|
|
784
|
+
function nextKindHit(text, from, zeroWidth) {
|
|
785
|
+
if (!zeroWidth) return text.indexOf("kind", from);
|
|
786
|
+
KIND_LETTERS_G.lastIndex = from;
|
|
787
|
+
const m = KIND_LETTERS_G.exec(text);
|
|
788
|
+
return m ? m.index : -1;
|
|
789
|
+
}
|
|
790
|
+
var KIND_LETTERS_G = new RegExp(`k${ZW}*i${ZW}*n${ZW}*d`, "g");
|
|
791
|
+
function keyFamily(m) {
|
|
792
|
+
if (m[3] !== void 0) return { family: "python", core: m[3], levels: 0 };
|
|
793
|
+
if (m[4] !== void 0) return { family: "smart", core: m[4], levels: 0 };
|
|
794
|
+
if (m[5] !== void 0) return { family: "entity", core: m[5], levels: 0 };
|
|
795
|
+
if (m[6] !== void 0) return { family: "js", core: m[6], levels: 0 };
|
|
796
|
+
const backslashes = m[1].length;
|
|
797
|
+
const core = m[2];
|
|
798
|
+
if (core.includes("&")) return { family: "entity", core, levels: 0 };
|
|
799
|
+
if (backslashes > 0) {
|
|
800
|
+
return { family: "escaped", core, levels: Math.min(4, Math.ceil(Math.log2(backslashes + 1))) };
|
|
801
|
+
}
|
|
802
|
+
const plain = core.replace(ZERO_WIDTH, "");
|
|
803
|
+
if (plain === "__kind" || /^\\u005[fF]_kind$/.test(plain)) return { family: "lifted", core, levels: 0 };
|
|
804
|
+
if (plain === "\\_\\_kind") return { family: "lifted", core, levels: 0 };
|
|
805
|
+
return { family: "markdown", core, levels: 0 };
|
|
806
|
+
}
|
|
807
|
+
function scanKindSpellingRegions(text, options = {}) {
|
|
808
|
+
const jsonOnly = options.families !== "all";
|
|
809
|
+
const from = options.from ?? 0;
|
|
810
|
+
const regions = [];
|
|
811
|
+
if (!mayHoldKindKey(text)) return regions;
|
|
812
|
+
const zeroWidth = HAS_ZERO_WIDTH.test(text);
|
|
813
|
+
let quoted = null;
|
|
814
|
+
const inQuoted = (at) => {
|
|
815
|
+
quoted ??= quotedSourceRanges(text);
|
|
816
|
+
let lo = 0;
|
|
817
|
+
let hi = quoted.length - 1;
|
|
818
|
+
while (lo <= hi) {
|
|
819
|
+
const mid = lo + hi >> 1;
|
|
820
|
+
const [a, b] = quoted[mid];
|
|
821
|
+
if (at < a) hi = mid - 1;
|
|
822
|
+
else if (at >= b) lo = mid + 1;
|
|
823
|
+
else return true;
|
|
824
|
+
}
|
|
825
|
+
return false;
|
|
826
|
+
};
|
|
827
|
+
let cursor = from;
|
|
828
|
+
let braceScan = from;
|
|
829
|
+
let lastBrace = -1;
|
|
830
|
+
let deadBraceFloor = from;
|
|
831
|
+
const reach = { left: REACH_BUDGET_FACTOR * text.length + REACH_BUDGET_FLOOR };
|
|
832
|
+
let hit = nextKindHit(text, from, zeroWidth);
|
|
833
|
+
while (hit >= 0 && regions.length < MAX_REGIONS_PER_CALL) {
|
|
834
|
+
const windowStart = Math.max(cursor, hit - KEY_LOOKBEHIND);
|
|
835
|
+
const windowEnd = Math.min(text.length, hit + KEY_LOOKBEHIND);
|
|
836
|
+
SPELLED_KEY_G.lastIndex = 0;
|
|
837
|
+
const window = text.slice(windowStart, windowEnd);
|
|
838
|
+
let m = null;
|
|
839
|
+
for (let candidate = SPELLED_KEY_G.exec(window); candidate; candidate = SPELLED_KEY_G.exec(window)) {
|
|
840
|
+
if (windowStart + candidate.index + candidate[0].length > hit) {
|
|
841
|
+
m = candidate;
|
|
842
|
+
break;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
if (!m) {
|
|
846
|
+
hit = nextKindHit(text, hit + 1, zeroWidth);
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
const keyAt = windowStart + m.index;
|
|
850
|
+
const keyEnd = keyAt + m[0].length;
|
|
851
|
+
const next = () => nextKindHit(text, keyEnd, zeroWidth);
|
|
852
|
+
while (braceScan < keyAt) {
|
|
853
|
+
if (text.charCodeAt(braceScan) === 123) lastBrace = braceScan;
|
|
854
|
+
braceScan++;
|
|
855
|
+
}
|
|
856
|
+
const brace = lastBrace >= Math.max(cursor, deadBraceFloor) ? lastBrace : -1;
|
|
857
|
+
if (brace < 0 || inQuoted(keyAt) || inQuoted(brace)) {
|
|
858
|
+
hit = next();
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
const { family, core, levels } = keyFamily(m);
|
|
862
|
+
if (jsonOnly && family !== "lifted" && family !== "markdown") {
|
|
863
|
+
hit = next();
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
const markdown = /\\_/.test(core) || family === "escaped" && /\\{2}_/.test(core);
|
|
867
|
+
const plan = decodePlan(family, levels, markdown);
|
|
868
|
+
const region = boundRegion(text, brace, keyAt, plan, reach);
|
|
869
|
+
if (!region) {
|
|
870
|
+
deadBraceFloor = brace + 1;
|
|
871
|
+
hit = next();
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
if (family === "lifted" && !markdown) {
|
|
875
|
+
regions.push({
|
|
876
|
+
start: brace,
|
|
877
|
+
end: region.end,
|
|
878
|
+
rewriteStart: keyAt,
|
|
879
|
+
rewriteEnd: keyEnd,
|
|
880
|
+
keyText: m[0].replace(ZERO_WIDTH, ""),
|
|
881
|
+
family,
|
|
882
|
+
status: region.status,
|
|
883
|
+
decoded: withoutZeroWidthInKeys(region.decoded),
|
|
884
|
+
markdown: false
|
|
885
|
+
});
|
|
886
|
+
} else {
|
|
887
|
+
regions.push({
|
|
888
|
+
start: brace,
|
|
889
|
+
end: region.end,
|
|
890
|
+
rewriteStart: brace,
|
|
891
|
+
rewriteEnd: region.end,
|
|
892
|
+
family,
|
|
893
|
+
status: region.status,
|
|
894
|
+
decoded: withoutZeroWidthInKeys(region.decoded),
|
|
895
|
+
markdown
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
cursor = region.end;
|
|
899
|
+
if (region.status === "prefix") break;
|
|
900
|
+
hit = nextKindHit(text, cursor, zeroWidth);
|
|
901
|
+
}
|
|
902
|
+
return regions;
|
|
903
|
+
}
|
|
904
|
+
var ZW_KEY_IN_DECODED = new RegExp(`(["'])${ZW}*_${ZW}*_${ZW}*k${ZW}*i${ZW}*n${ZW}*d${ZW}*\\1(?=\\s*:)`, "g");
|
|
905
|
+
function withoutZeroWidthInKeys(decoded) {
|
|
906
|
+
return HAS_ZERO_WIDTH.test(decoded) ? decoded.replace(ZW_KEY_IN_DECODED, (key) => key.replace(ZERO_WIDTH, "")) : decoded;
|
|
907
|
+
}
|
|
908
|
+
function decodePlan(family, levels, markdown) {
|
|
909
|
+
const steps = [];
|
|
910
|
+
if (family === "entity") steps.push(entityStep);
|
|
911
|
+
if (family === "smart") steps.push(smartStep);
|
|
912
|
+
for (let level = 0; level < levels; level++) steps.push(escapeStep);
|
|
913
|
+
if (markdown) steps.push(markdownStep);
|
|
914
|
+
const grammar = family === "js" ? "js" : family === "python" || family === "smart" || family === "entity" ? "python" : "json";
|
|
915
|
+
return { steps, grammar };
|
|
916
|
+
}
|
|
917
|
+
function decodeWith(raw, step) {
|
|
918
|
+
let text = "";
|
|
919
|
+
const map = [];
|
|
920
|
+
for (let i = 0; i < raw.length; ) {
|
|
921
|
+
const [out, consumed] = step(raw, i);
|
|
922
|
+
for (let k = 0; k < out.length; k++) map.push(i);
|
|
923
|
+
text += out;
|
|
924
|
+
i += Math.max(1, consumed);
|
|
925
|
+
}
|
|
926
|
+
map.push(raw.length);
|
|
927
|
+
return { text, map };
|
|
928
|
+
}
|
|
929
|
+
function decodeChain(raw, steps) {
|
|
930
|
+
if (steps.length === 0) {
|
|
931
|
+
const map = new Array(raw.length + 1);
|
|
932
|
+
for (let i = 0; i <= raw.length; i++) map[i] = i;
|
|
933
|
+
return { text: raw, map };
|
|
934
|
+
}
|
|
935
|
+
let current = decodeWith(raw, steps[0]);
|
|
936
|
+
for (let s = 1; s < steps.length; s++) {
|
|
937
|
+
const next = decodeWith(current.text, steps[s]);
|
|
938
|
+
const prior = current.map;
|
|
939
|
+
current = { text: next.text, map: next.map.map((at) => prior[at]) };
|
|
940
|
+
}
|
|
941
|
+
return current;
|
|
942
|
+
}
|
|
943
|
+
var ENTITY_STEP_RE = /^&(?:(quot|apos|amp|lt|gt|lowbar|UnderBar)|#(\d{1,6})|#[xX]([0-9a-fA-F]{1,6}));/i;
|
|
944
|
+
var NAMED_ENTITIES = { quot: '"', apos: "'", amp: "&", lt: "<", gt: ">", lowbar: "_", underbar: "_" };
|
|
945
|
+
var ESCAPE_CHARS = { '"': '"', "\\": "\\", "/": "/", n: "\n", t: " ", r: "\r", b: "", f: "" };
|
|
946
|
+
var entityStep = (raw, i) => {
|
|
947
|
+
if (raw[i] !== "&") return [raw[i], 1];
|
|
948
|
+
const m = ENTITY_STEP_RE.exec(raw.slice(i, i + 12));
|
|
949
|
+
if (!m) return ["&", 1];
|
|
950
|
+
const ch = m[1] ? NAMED_ENTITIES[m[1].toLowerCase()] : String.fromCodePoint(Number.parseInt(m[2] ?? m[3], m[2] ? 10 : 16));
|
|
951
|
+
return [ch, m[0].length];
|
|
952
|
+
};
|
|
953
|
+
var smartStep = (raw, i) => {
|
|
954
|
+
const ch = raw[i];
|
|
955
|
+
if (SMART_DOUBLE.includes(ch)) return ['"', 1];
|
|
956
|
+
if (SMART_SINGLE.includes(ch)) return ["'", 1];
|
|
957
|
+
return [ch, 1];
|
|
958
|
+
};
|
|
959
|
+
var escapeStep = (raw, i) => {
|
|
960
|
+
if (raw[i] !== "\\") return [raw[i], 1];
|
|
961
|
+
const next = raw[i + 1] ?? "";
|
|
962
|
+
if (next in ESCAPE_CHARS) return [ESCAPE_CHARS[next], 2];
|
|
963
|
+
if (next === "u" && /^[0-9a-fA-F]{4}$/.test(raw.slice(i + 2, i + 6))) return [raw.slice(i, i + 6), 6];
|
|
964
|
+
return [raw.slice(i, i + 2), 2];
|
|
965
|
+
};
|
|
966
|
+
var markdownStep = (raw, i) => raw[i] === "\\" && raw[i + 1] === "_" ? ["_", 2] : [raw[i], 1];
|
|
967
|
+
var FIRST_WINDOW = 128;
|
|
968
|
+
var REACH_BUDGET_FACTOR = 4;
|
|
969
|
+
var REACH_BUDGET_FLOOR = 65536;
|
|
970
|
+
function boundRegion(text, brace, keyAt, plan, reach = { left: Number.POSITIVE_INFINITY }) {
|
|
971
|
+
for (let width = FIRST_WINDOW; ; width *= 4) {
|
|
972
|
+
const hi = Math.min(text.length, brace + width);
|
|
973
|
+
const atEnd = hi === text.length;
|
|
974
|
+
const decoded = decodeChain(text.slice(brace, hi), plan.steps);
|
|
975
|
+
const keyDecoded = firstAtOrAfter(decoded.map, keyAt - brace);
|
|
976
|
+
const verdict = kindGrammar(decoded.text, plan.grammar);
|
|
977
|
+
if (verdict.status === "complete") {
|
|
978
|
+
if (verdict.end <= keyDecoded) return null;
|
|
979
|
+
return { end: brace + decoded.map[verdict.end], status: "complete", decoded: decoded.text.slice(0, verdict.end) };
|
|
980
|
+
}
|
|
981
|
+
if (verdict.status === "broken") {
|
|
982
|
+
if (!atEnd && decoded.map[verdict.at] >= hi - brace - 16) continue;
|
|
983
|
+
if (!verdict.prose) {
|
|
984
|
+
const end = balancedEnd(decoded.text, plan.grammar !== "json");
|
|
985
|
+
if (end === null && !atEnd) {
|
|
986
|
+
const past = hi - brace - verdict.at;
|
|
987
|
+
if (reach.left > past) {
|
|
988
|
+
reach.left -= past;
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
if (end !== null && end > keyDecoded && lenientlyParses(decoded.text.slice(0, end), plan.grammar)) {
|
|
993
|
+
return { end: brace + decoded.map[end], status: "complete", decoded: decoded.text.slice(0, end) };
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
if (atEnd && ARRIVING_SPELLING_TAIL.test(text.slice(brace + decoded.map[verdict.at]))) {
|
|
997
|
+
if (verdict.at <= keyDecoded) return null;
|
|
998
|
+
return { end: text.length, status: "prefix", decoded: decoded.text };
|
|
999
|
+
}
|
|
1000
|
+
if (verdict.cut <= keyDecoded) return null;
|
|
1001
|
+
return { end: brace + decoded.map[verdict.cut], status: "broken", decoded: decoded.text.slice(0, verdict.cut) };
|
|
1002
|
+
}
|
|
1003
|
+
if (!atEnd) continue;
|
|
1004
|
+
return { end: text.length, status: "prefix", decoded: decoded.text };
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
var BARE_REGION_GRAMMAR_BUDGET = 16384;
|
|
1008
|
+
function bareRegionBreaksIntoProse(text, options = {}) {
|
|
1009
|
+
let region = text;
|
|
1010
|
+
if (region.length > BARE_REGION_GRAMMAR_BUDGET) {
|
|
1011
|
+
if (!options.firstBudget) return false;
|
|
1012
|
+
region = region.slice(0, BARE_REGION_GRAMMAR_BUDGET);
|
|
1013
|
+
}
|
|
1014
|
+
const start = region.search(/\S/);
|
|
1015
|
+
if (start < 0) return false;
|
|
1016
|
+
const verdict = kindGrammar(region, "json", start);
|
|
1017
|
+
return verdict.status === "broken" && verdict.prose;
|
|
1018
|
+
}
|
|
1019
|
+
function lenientlyParses(span, grammar) {
|
|
1020
|
+
const body = span.replace(/,(\s*[}\]])/g, "$1");
|
|
1021
|
+
if (grammar !== "json") return true;
|
|
1022
|
+
try {
|
|
1023
|
+
JSON.parse(body);
|
|
1024
|
+
return true;
|
|
1025
|
+
} catch {
|
|
1026
|
+
return false;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
function balancedEnd(text, singleQuotes) {
|
|
1030
|
+
let depth = 0;
|
|
1031
|
+
let quote = "";
|
|
1032
|
+
for (let i = 0; i < text.length; i++) {
|
|
1033
|
+
const ch = text[i];
|
|
1034
|
+
if (quote) {
|
|
1035
|
+
if (ch === "\\") i++;
|
|
1036
|
+
else if (ch === quote) quote = "";
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
if (ch === '"' || singleQuotes && ch === "'") quote = ch;
|
|
1040
|
+
else if (ch === "{" || ch === "[") depth++;
|
|
1041
|
+
else if (ch === "}" || ch === "]") {
|
|
1042
|
+
depth--;
|
|
1043
|
+
if (depth === 0) return i + 1;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
return null;
|
|
1047
|
+
}
|
|
1048
|
+
function kindObjectProseBreak(text) {
|
|
1049
|
+
const key = text.search(LITERAL_KIND_KEY);
|
|
1050
|
+
if (key < 0) return null;
|
|
1051
|
+
const verdict = kindGrammar(text);
|
|
1052
|
+
return verdict.status === "broken" && verdict.prose && verdict.cut > key ? verdict.cut : null;
|
|
1053
|
+
}
|
|
1054
|
+
var LITERAL_KIND_KEY = new RegExp(String.raw`(?<!\\)"${KIND_KEY_BODY}"\s*:`);
|
|
1055
|
+
var ARRIVING_SPELLING_TAIL = /^(?:\\+"?|&#?[A-Za-z0-9]{0,6})$/;
|
|
1056
|
+
function firstAtOrAfter(map, raw) {
|
|
1057
|
+
let lo = 0;
|
|
1058
|
+
let hi = map.length - 1;
|
|
1059
|
+
while (lo < hi) {
|
|
1060
|
+
const mid = lo + hi >> 1;
|
|
1061
|
+
if (map[mid] < raw) lo = mid + 1;
|
|
1062
|
+
else hi = mid;
|
|
1063
|
+
}
|
|
1064
|
+
return lo;
|
|
1065
|
+
}
|
|
1066
|
+
function kindGrammar(s, dialect = "json", from = 0) {
|
|
1067
|
+
const closers = [];
|
|
1068
|
+
const singleQuotes = dialect !== "json";
|
|
1069
|
+
const bareKeys = dialect === "js";
|
|
1070
|
+
const trailingCommas = dialect !== "json";
|
|
1071
|
+
let expect = 0;
|
|
1072
|
+
let lastGood = from;
|
|
1073
|
+
const n = s.length;
|
|
1074
|
+
const broken = (at) => ({
|
|
1075
|
+
status: "broken",
|
|
1076
|
+
at,
|
|
1077
|
+
cut: lastGood,
|
|
1078
|
+
prose: !/^[{}[\]:,"'0-9.+-]$/.test(s[at] ?? "")
|
|
1079
|
+
});
|
|
1080
|
+
let i = from;
|
|
1081
|
+
while (i < n) {
|
|
1082
|
+
const c = s.charCodeAt(i);
|
|
1083
|
+
if (c === 32 || c === 9 || c === 10 || c === 13) {
|
|
1084
|
+
i++;
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
if (expect === 4 || expect === 1 || expect === 2) {
|
|
1088
|
+
if (c === 125 || c === 93) {
|
|
1089
|
+
if (closers.length === 0 || closers[closers.length - 1] !== c) return broken(i);
|
|
1090
|
+
if (expect === 4 || trailingCommas || lastCharWasOpener(s, i)) {
|
|
1091
|
+
closers.pop();
|
|
1092
|
+
i++;
|
|
1093
|
+
lastGood = i;
|
|
1094
|
+
if (closers.length === 0) return { status: "complete", end: i };
|
|
1095
|
+
expect = 4;
|
|
1096
|
+
continue;
|
|
1097
|
+
}
|
|
1098
|
+
return broken(i);
|
|
1099
|
+
}
|
|
1100
|
+
if (expect === 4) {
|
|
1101
|
+
if (c !== 44) return broken(i);
|
|
1102
|
+
i++;
|
|
1103
|
+
lastGood = i;
|
|
1104
|
+
expect = closers[closers.length - 1] === 125 ? 2 : 1;
|
|
1105
|
+
continue;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
if (expect === 3) {
|
|
1109
|
+
if (c !== 58) return broken(i);
|
|
1110
|
+
i++;
|
|
1111
|
+
expect = 0;
|
|
1112
|
+
continue;
|
|
1113
|
+
}
|
|
1114
|
+
const keyPosition = expect === 2;
|
|
1115
|
+
if (c === 34 || singleQuotes && c === 39) {
|
|
1116
|
+
let j = i + 1;
|
|
1117
|
+
for (; ; ) {
|
|
1118
|
+
if (j >= n) return { status: "prefix" };
|
|
1119
|
+
const d = s.charCodeAt(j);
|
|
1120
|
+
if (d === 92) {
|
|
1121
|
+
j += 2;
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
1124
|
+
if (d === 10 || d === 13) return broken(j);
|
|
1125
|
+
if (d === c) break;
|
|
1126
|
+
j++;
|
|
1127
|
+
}
|
|
1128
|
+
i = j + 1;
|
|
1129
|
+
if (keyPosition) expect = 3;
|
|
1130
|
+
else {
|
|
1131
|
+
expect = 4;
|
|
1132
|
+
lastGood = i;
|
|
1133
|
+
}
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
if (keyPosition) {
|
|
1137
|
+
if (!bareKeys || !isIdentStart(c)) return broken(i);
|
|
1138
|
+
let j = i + 1;
|
|
1139
|
+
while (j < n && isIdentPart(s.charCodeAt(j))) j++;
|
|
1140
|
+
if (j >= n) return { status: "prefix" };
|
|
1141
|
+
i = j;
|
|
1142
|
+
expect = 3;
|
|
1143
|
+
continue;
|
|
1144
|
+
}
|
|
1145
|
+
if (c === 123 || c === 91) {
|
|
1146
|
+
closers.push(c === 123 ? 125 : 93);
|
|
1147
|
+
i++;
|
|
1148
|
+
lastGood = i;
|
|
1149
|
+
expect = c === 123 ? 2 : 1;
|
|
1150
|
+
continue;
|
|
1151
|
+
}
|
|
1152
|
+
if (c === 45 || c >= 48 && c <= 57) {
|
|
1153
|
+
let j = i + 1;
|
|
1154
|
+
while (j < n && /[0-9eE.+-]/.test(s[j])) j++;
|
|
1155
|
+
if (j >= n) return { status: "prefix" };
|
|
1156
|
+
i = j;
|
|
1157
|
+
lastGood = i;
|
|
1158
|
+
expect = 4;
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
if (isIdentStart(c)) {
|
|
1162
|
+
let j = i + 1;
|
|
1163
|
+
while (j < n && isIdentPart(s.charCodeAt(j))) j++;
|
|
1164
|
+
const word = s.slice(i, j);
|
|
1165
|
+
if (j >= n) return VALUE_WORDS[dialect].some((w) => w.startsWith(word)) ? { status: "prefix" } : broken(i);
|
|
1166
|
+
if (!VALUE_WORDS[dialect].includes(word)) return broken(i);
|
|
1167
|
+
i = j;
|
|
1168
|
+
lastGood = i;
|
|
1169
|
+
expect = 4;
|
|
1170
|
+
continue;
|
|
1171
|
+
}
|
|
1172
|
+
return broken(i);
|
|
1173
|
+
}
|
|
1174
|
+
return { status: "prefix" };
|
|
1175
|
+
}
|
|
1176
|
+
var VALUE_WORDS = {
|
|
1177
|
+
json: ["true", "false", "null"],
|
|
1178
|
+
python: ["True", "False", "None", "true", "false", "null"],
|
|
1179
|
+
js: ["true", "false", "null", "undefined", "NaN", "Infinity"]
|
|
1180
|
+
};
|
|
1181
|
+
function isIdentStart(c) {
|
|
1182
|
+
return c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95 || c === 36;
|
|
1183
|
+
}
|
|
1184
|
+
function isIdentPart(c) {
|
|
1185
|
+
return isIdentStart(c) || c >= 48 && c <= 57;
|
|
1186
|
+
}
|
|
1187
|
+
function lastCharWasOpener(s, at) {
|
|
1188
|
+
for (let k = at - 1; k >= 0; k--) {
|
|
1189
|
+
const c = s.charCodeAt(k);
|
|
1190
|
+
if (c === 32 || c === 9 || c === 10 || c === 13) continue;
|
|
1191
|
+
return c === 123 || c === 91;
|
|
1192
|
+
}
|
|
1193
|
+
return false;
|
|
1194
|
+
}
|
|
1195
|
+
var JSON_ONLY_FAMILIES = /* @__PURE__ */ new Set(["lifted", "escaped", "markdown"]);
|
|
1196
|
+
function regionJson(region) {
|
|
1197
|
+
const text = region.decoded;
|
|
1198
|
+
if (region.family === "js") return json5AsJson(text);
|
|
1199
|
+
if (region.family !== "python") {
|
|
1200
|
+
try {
|
|
1201
|
+
JSON.parse(text);
|
|
1202
|
+
return text;
|
|
1203
|
+
} catch {
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
return pythonReprAsJson(text);
|
|
1207
|
+
}
|
|
1208
|
+
function closedJson(region) {
|
|
1209
|
+
let body = region.decoded.replace(/\s+$/, "");
|
|
1210
|
+
body = body.replace(/,?\s*(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|[A-Za-z_$][\w$]*)\s*:\s*$/, "");
|
|
1211
|
+
body = body.replace(/,\s*$/, "");
|
|
1212
|
+
const closers = [];
|
|
1213
|
+
let quote = "";
|
|
1214
|
+
for (let i = 0; i < body.length; i++) {
|
|
1215
|
+
const ch = body[i];
|
|
1216
|
+
if (quote) {
|
|
1217
|
+
if (ch === "\\") i++;
|
|
1218
|
+
else if (ch === quote) quote = "";
|
|
1219
|
+
continue;
|
|
1220
|
+
}
|
|
1221
|
+
if (ch === '"' || ch === "'" && !JSON_ONLY_FAMILIES.has(region.family)) quote = ch;
|
|
1222
|
+
else if (ch === "{") closers.push("}");
|
|
1223
|
+
else if (ch === "[") closers.push("]");
|
|
1224
|
+
else if (ch === "}" || ch === "]") closers.pop();
|
|
1225
|
+
}
|
|
1226
|
+
if (quote) return null;
|
|
1227
|
+
return regionJson({ family: region.family, decoded: body + closers.reverse().join("") });
|
|
1228
|
+
}
|
|
1229
|
+
function valueCarriesKind(value) {
|
|
1230
|
+
return carriesKind(value, 0, /* @__PURE__ */ new Set());
|
|
1231
|
+
}
|
|
1232
|
+
var VALUE_SEARCH_DEPTH = 64;
|
|
1233
|
+
function carriesKind(value, depth, seen) {
|
|
1234
|
+
if (typeof value === "string") return textCarriesKind(value);
|
|
1235
|
+
if (value === null || typeof value !== "object") return false;
|
|
1236
|
+
if (depth > VALUE_SEARCH_DEPTH || seen.has(value)) return false;
|
|
1237
|
+
seen.add(value);
|
|
1238
|
+
if (Array.isArray(value)) {
|
|
1239
|
+
return value.some((item) => carriesKind(item, depth + 1, seen));
|
|
1240
|
+
}
|
|
1241
|
+
const record = value;
|
|
1242
|
+
if (typeof record.__kind === "string" && record.__kind.trim()) return true;
|
|
1243
|
+
for (const item of Object.values(record)) {
|
|
1244
|
+
if (carriesKind(item, depth + 1, seen)) return true;
|
|
1245
|
+
}
|
|
1246
|
+
return false;
|
|
1247
|
+
}
|
|
1248
|
+
function textCarriesKind(text) {
|
|
1249
|
+
if (isKindJsonText(text) || markdownCarriesKind(text)) return true;
|
|
1250
|
+
if (!mayHoldKindKey(text) || !/^[{[]/.test(text.trimStart())) return false;
|
|
1251
|
+
let parsed;
|
|
1252
|
+
try {
|
|
1253
|
+
parsed = JSON.parse(text);
|
|
1254
|
+
} catch {
|
|
1255
|
+
return false;
|
|
1256
|
+
}
|
|
1257
|
+
return typeof parsed === "object" && parsed !== null && valueCarriesKind(parsed);
|
|
1258
|
+
}
|
|
1259
|
+
function isKindJsonText(text) {
|
|
1260
|
+
const first = text.trimStart()[0];
|
|
1261
|
+
return (first === "{" || first === "[") && hasKindKey(text);
|
|
1262
|
+
}
|
|
1263
|
+
function rootKindSlug(value) {
|
|
1264
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
1265
|
+
const kind = value.__kind;
|
|
1266
|
+
return typeof kind === "string" && kind.trim() ? kind : null;
|
|
1267
|
+
}
|
|
1268
|
+
var JSON_FENCE_LANGS = /* @__PURE__ */ new Set(["", "json", "jsonc", "json5"]);
|
|
1269
|
+
function isJsonFenceLanguage(lang) {
|
|
1270
|
+
return JSON_FENCE_LANGS.has((lang ?? "").trim().toLowerCase());
|
|
1271
|
+
}
|
|
1272
|
+
function quotedSourceRanges(text) {
|
|
1273
|
+
const ranges = [];
|
|
1274
|
+
for (const range of findCodeRanges(text)) {
|
|
1275
|
+
if (range.kind === "span") {
|
|
1276
|
+
ranges.push([range.start, range.end]);
|
|
1277
|
+
continue;
|
|
1278
|
+
}
|
|
1279
|
+
const raw = text.slice(range.start, range.end);
|
|
1280
|
+
const newline = raw.indexOf("\n");
|
|
1281
|
+
const firstLine = (newline === -1 ? raw : raw.slice(0, newline)).replace(/^ {0,3}(?:> ?)*/, "");
|
|
1282
|
+
const opener = fenceOpenerOf(firstLine.trimStart());
|
|
1283
|
+
if (!opener || !isJsonFenceLanguage(opener.lang)) {
|
|
1284
|
+
ranges.push([range.start, range.end]);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
return ranges;
|
|
1288
|
+
}
|
|
1289
|
+
function isQuotedSourceXmlBlock(block) {
|
|
1290
|
+
return block.metadata?.genericXmlContainer !== true;
|
|
1291
|
+
}
|
|
1292
|
+
function frontMatterEnd(source) {
|
|
1293
|
+
const body = source.charCodeAt(0) === 65279 ? 1 : 0;
|
|
1294
|
+
const firstBreak = source.indexOf("\n", body);
|
|
1295
|
+
if (firstBreak < 0) return 0;
|
|
1296
|
+
const opener = source.slice(body, firstBreak).replace(/\r$/, "");
|
|
1297
|
+
if (opener !== "---" && opener !== "+++") return 0;
|
|
1298
|
+
for (let pos = firstBreak + 1; pos < source.length; ) {
|
|
1299
|
+
const next = source.indexOf("\n", pos);
|
|
1300
|
+
const end = next < 0 ? source.length : next;
|
|
1301
|
+
const line = source.slice(pos, end).replace(/\r$/, "");
|
|
1302
|
+
if (line === opener || opener === "---" && line === "...") return end;
|
|
1303
|
+
if (next < 0) break;
|
|
1304
|
+
pos = next + 1;
|
|
1305
|
+
}
|
|
1306
|
+
return 0;
|
|
1307
|
+
}
|
|
1308
|
+
function markdownCarriesKind(source) {
|
|
1309
|
+
const text = source.slice(frontMatterEnd(source));
|
|
1310
|
+
if (!hasKindKey(text, JSON_KIND_SPELLINGS)) return false;
|
|
1311
|
+
if (isKindJsonText(text)) return true;
|
|
1312
|
+
const quoted = quotedSourceRanges(text);
|
|
1313
|
+
if (quoted.length === 0) return true;
|
|
1314
|
+
let outside = "";
|
|
1315
|
+
let cursor = 0;
|
|
1316
|
+
for (const [start, end] of quoted) {
|
|
1317
|
+
if (start < cursor) continue;
|
|
1318
|
+
outside += text.slice(cursor, start) + " ";
|
|
1319
|
+
cursor = end;
|
|
1320
|
+
}
|
|
1321
|
+
outside += text.slice(cursor);
|
|
1322
|
+
return hasKindKey(outside, JSON_KIND_SPELLINGS);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// surfaces/embedded-kind-json.ts
|
|
1326
|
+
function matchingJsonObjectEnd(source, start) {
|
|
1327
|
+
if (source[start] !== "{" && source[start] !== "[") return null;
|
|
1328
|
+
const stack = [source[start]];
|
|
1329
|
+
let inString = false;
|
|
1330
|
+
let escaped = false;
|
|
1331
|
+
for (let index = start + 1; index < source.length; index++) {
|
|
1332
|
+
const char = source[index];
|
|
1333
|
+
if (inString) {
|
|
1334
|
+
if (escaped) {
|
|
1335
|
+
escaped = false;
|
|
1336
|
+
} else if (char === "\\") {
|
|
1337
|
+
escaped = true;
|
|
1338
|
+
} else if (char === '"') {
|
|
1339
|
+
inString = false;
|
|
1340
|
+
}
|
|
1341
|
+
continue;
|
|
1342
|
+
}
|
|
1343
|
+
if (char === '"') {
|
|
1344
|
+
inString = true;
|
|
1345
|
+
continue;
|
|
1346
|
+
}
|
|
1347
|
+
if (char === "{" || char === "[") {
|
|
1348
|
+
stack.push(char);
|
|
1349
|
+
continue;
|
|
1350
|
+
}
|
|
1351
|
+
if (char !== "}" && char !== "]") continue;
|
|
1352
|
+
const opening = stack.pop();
|
|
1353
|
+
if (char === "}" && opening !== "{" || char === "]" && opening !== "[") {
|
|
1354
|
+
return null;
|
|
1355
|
+
}
|
|
1356
|
+
if (stack.length === 0) return index + 1;
|
|
1357
|
+
}
|
|
1358
|
+
return null;
|
|
1359
|
+
}
|
|
1360
|
+
function balancedEnds(text) {
|
|
1361
|
+
const ends = new Int32Array(text.length).fill(-1);
|
|
1362
|
+
const stack = [];
|
|
1363
|
+
for (let start = text.length - 1; start >= 0; start--) {
|
|
1364
|
+
const open = text[start];
|
|
1365
|
+
if (open !== "{" && open !== "[") continue;
|
|
1366
|
+
stack.length = 0;
|
|
1367
|
+
stack.push(open === "{" ? "}" : "]");
|
|
1368
|
+
let inString = false;
|
|
1369
|
+
for (let i = start + 1; i < text.length; i++) {
|
|
1370
|
+
const ch = text[i];
|
|
1371
|
+
if (inString) {
|
|
1372
|
+
if (ch === "\\") i++;
|
|
1373
|
+
else if (ch === '"') inString = false;
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
if (ch === '"') inString = true;
|
|
1377
|
+
else if (ch === "{" || ch === "[") {
|
|
1378
|
+
const end = ends[i];
|
|
1379
|
+
if (end < 0) break;
|
|
1380
|
+
i = end - 1;
|
|
1381
|
+
} else if (ch === "}" || ch === "]") {
|
|
1382
|
+
if (stack.pop() !== ch) break;
|
|
1383
|
+
ends[start] = i + 1;
|
|
1384
|
+
break;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
return ends;
|
|
1389
|
+
}
|
|
1390
|
+
function lazyJsonEnds(source) {
|
|
1391
|
+
let table;
|
|
1392
|
+
return (start) => {
|
|
1393
|
+
if (source[start] !== "{" && source[start] !== "[") return null;
|
|
1394
|
+
table ??= balancedEnds(source);
|
|
1395
|
+
const end = table[start];
|
|
1396
|
+
return end < 0 ? null : end;
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
function skipWhitespace(source, cursor) {
|
|
1400
|
+
while (/\s/.test(source[cursor] ?? "")) cursor++;
|
|
1401
|
+
return cursor;
|
|
1402
|
+
}
|
|
1403
|
+
function jsonStringEnd(source, start) {
|
|
1404
|
+
if (source[start] !== '"') return null;
|
|
1405
|
+
let escaped = false;
|
|
1406
|
+
for (let cursor = start + 1; cursor < source.length; cursor++) {
|
|
1407
|
+
const char = source[cursor];
|
|
1408
|
+
if (escaped) {
|
|
1409
|
+
escaped = false;
|
|
1410
|
+
} else if (char === "\\") {
|
|
1411
|
+
escaped = true;
|
|
1412
|
+
} else if (char === '"') {
|
|
1413
|
+
return cursor + 1;
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
return null;
|
|
1417
|
+
}
|
|
1418
|
+
function jsonValueEnd(source, start) {
|
|
1419
|
+
const char = source[start];
|
|
1420
|
+
if (char === '"') return jsonStringEnd(source, start);
|
|
1421
|
+
if (char === "{" || char === "[") return matchingJsonObjectEnd(source, start);
|
|
1422
|
+
const end = source.slice(start).search(/[\s,}\]]/);
|
|
1423
|
+
const tokenEnd = end === -1 ? source.length : start + end;
|
|
1424
|
+
const token = source.slice(start, tokenEnd);
|
|
1425
|
+
return /^(?:true|false|null|-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)$/.test(
|
|
1426
|
+
token
|
|
1427
|
+
) ? tokenEnd : null;
|
|
1428
|
+
}
|
|
1429
|
+
function malformedKindOwnerEnd(source, start, boundary = source.length) {
|
|
1430
|
+
let cursor = skipWhitespace(source, start + 1);
|
|
1431
|
+
let kindFound = false;
|
|
1432
|
+
const ownerEnd = () => kindFound ? boundary : null;
|
|
1433
|
+
while (cursor < boundary) {
|
|
1434
|
+
const keyStart = cursor;
|
|
1435
|
+
const keyEnd = jsonStringEnd(source, keyStart);
|
|
1436
|
+
if (keyEnd === null || keyEnd > boundary) return ownerEnd();
|
|
1437
|
+
let key;
|
|
1438
|
+
try {
|
|
1439
|
+
key = JSON.parse(source.slice(keyStart, keyEnd));
|
|
1440
|
+
} catch {
|
|
1441
|
+
return ownerEnd();
|
|
1442
|
+
}
|
|
1443
|
+
cursor = skipWhitespace(source, keyEnd);
|
|
1444
|
+
if (cursor >= boundary || source[cursor] !== ":") return ownerEnd();
|
|
1445
|
+
cursor = skipWhitespace(source, cursor + 1);
|
|
1446
|
+
const valueStart = cursor;
|
|
1447
|
+
const valueEnd = jsonValueEnd(source, valueStart);
|
|
1448
|
+
if (valueEnd === null || valueEnd > boundary) return ownerEnd();
|
|
1449
|
+
if (key === "__kind") {
|
|
1450
|
+
try {
|
|
1451
|
+
const value = JSON.parse(source.slice(valueStart, valueEnd));
|
|
1452
|
+
kindFound ||= typeof value === "string" && value.trim().length > 0;
|
|
1453
|
+
} catch {
|
|
1454
|
+
return ownerEnd();
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
cursor = skipWhitespace(source, valueEnd);
|
|
1458
|
+
if (source[cursor] === "}") return kindFound ? cursor + 1 : null;
|
|
1459
|
+
if (cursor >= boundary || source[cursor] !== ",") return ownerEnd();
|
|
1460
|
+
cursor = skipWhitespace(source, cursor + 1);
|
|
1461
|
+
}
|
|
1462
|
+
return ownerEnd();
|
|
1463
|
+
}
|
|
1464
|
+
function jsonStringRanges(source, start, end) {
|
|
1465
|
+
const ranges = [];
|
|
1466
|
+
for (let cursor = start; cursor < end; cursor++) {
|
|
1467
|
+
if (source[cursor] !== '"') continue;
|
|
1468
|
+
const stringEnd = jsonStringEnd(source, cursor);
|
|
1469
|
+
if (stringEnd === null || stringEnd > end) break;
|
|
1470
|
+
ranges.push([cursor, stringEnd]);
|
|
1471
|
+
cursor = stringEnd - 1;
|
|
1472
|
+
}
|
|
1473
|
+
return ranges;
|
|
1474
|
+
}
|
|
1475
|
+
function declaredKind(candidate) {
|
|
1476
|
+
try {
|
|
1477
|
+
const parsed = JSON.parse(candidate);
|
|
1478
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1479
|
+
return null;
|
|
1480
|
+
}
|
|
1481
|
+
const kind = parsed.__kind;
|
|
1482
|
+
return typeof kind === "string" && kind.trim() ? kind : null;
|
|
1483
|
+
} catch {
|
|
1484
|
+
return null;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
function literalRanges(source, liftJsonFences = false) {
|
|
1488
|
+
const ranges = [];
|
|
1489
|
+
const code = liftJsonFences ? findCodeRanges(source).filter((range) => !isJsonFenceRange(source, range)) : findCodeRanges(source);
|
|
1490
|
+
let next = 0;
|
|
1491
|
+
let cursor = 0;
|
|
1492
|
+
while (cursor < source.length) {
|
|
1493
|
+
while (next < code.length && code[next].start < cursor) next++;
|
|
1494
|
+
const range = code[next];
|
|
1495
|
+
if (range && range.start === cursor) {
|
|
1496
|
+
ranges.push([range.start, range.end]);
|
|
1497
|
+
cursor = range.end;
|
|
1498
|
+
next++;
|
|
1499
|
+
continue;
|
|
1500
|
+
}
|
|
1501
|
+
if (source.startsWith("<!--", cursor)) {
|
|
1502
|
+
const end = source.indexOf("-->", cursor + 4);
|
|
1503
|
+
const rangeEnd = end === -1 ? source.length : end + 3;
|
|
1504
|
+
ranges.push([cursor, rangeEnd]);
|
|
1505
|
+
cursor = rangeEnd;
|
|
1506
|
+
continue;
|
|
1507
|
+
}
|
|
1508
|
+
if (source.startsWith("<![CDATA[", cursor)) {
|
|
1509
|
+
const end = source.indexOf("]]>", cursor + 9);
|
|
1510
|
+
const rangeEnd = end === -1 ? source.length : end + 3;
|
|
1511
|
+
ranges.push([cursor, rangeEnd]);
|
|
1512
|
+
cursor = rangeEnd;
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1515
|
+
if (source[cursor] === "<") {
|
|
1516
|
+
const tag = readXmlTag(source, cursor);
|
|
1517
|
+
if (tag) {
|
|
1518
|
+
const end = cursor + tag.raw.length;
|
|
1519
|
+
ranges.push([cursor, end]);
|
|
1520
|
+
cursor = end;
|
|
1521
|
+
continue;
|
|
1522
|
+
}
|
|
1523
|
+
if (/^<[A-Za-z_][\w.:-]*\s/.test(source.slice(cursor, cursor + 64)) && !tagCloses(source, cursor)) {
|
|
1524
|
+
ranges.push([cursor, source.length]);
|
|
1525
|
+
break;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
cursor++;
|
|
1529
|
+
}
|
|
1530
|
+
return ranges;
|
|
1531
|
+
}
|
|
1532
|
+
function tagCloses(source, start) {
|
|
1533
|
+
let quote = null;
|
|
1534
|
+
for (let i = start + 1; i < source.length; i++) {
|
|
1535
|
+
const ch = source[i];
|
|
1536
|
+
if (quote) {
|
|
1537
|
+
if (ch === quote) quote = null;
|
|
1538
|
+
} else if (ch === "'" || ch === '"') quote = ch;
|
|
1539
|
+
else if (ch === ">") return true;
|
|
1540
|
+
}
|
|
1541
|
+
return false;
|
|
1542
|
+
}
|
|
1543
|
+
function isJsonFenceRange(source, range) {
|
|
1544
|
+
if (range.kind !== "fence") return false;
|
|
1545
|
+
const raw = source.slice(range.start, range.end);
|
|
1546
|
+
const newline = raw.indexOf("\n");
|
|
1547
|
+
const opener = fenceOpenerOf((newline === -1 ? raw : raw.slice(0, newline)).trimStart());
|
|
1548
|
+
return opener !== null && isJsonFenceLanguage(opener.lang);
|
|
1549
|
+
}
|
|
1550
|
+
function jsonFenceChromeSpans(source, regions) {
|
|
1551
|
+
const spans = [];
|
|
1552
|
+
for (const range of findCodeRanges(source)) {
|
|
1553
|
+
if (!isJsonFenceRange(source, range)) continue;
|
|
1554
|
+
const inside2 = regions.filter((r) => r.start >= range.start && r.end <= range.end);
|
|
1555
|
+
if (inside2.length !== 1) continue;
|
|
1556
|
+
const region = inside2[0];
|
|
1557
|
+
const head = source.slice(range.start, region.start);
|
|
1558
|
+
const tail = source.slice(region.end, range.end);
|
|
1559
|
+
if (!/^[^\n]*\n\s*$/.test(head) || !/^\s*(?:[`~]{3,}[ \t]*)?$/.test(tail)) continue;
|
|
1560
|
+
spans.push([range.start, region.start]);
|
|
1561
|
+
if (region.end < range.end) spans.push([region.end, range.end]);
|
|
1562
|
+
}
|
|
1563
|
+
return spans;
|
|
1564
|
+
}
|
|
1565
|
+
function findEmbeddedKindJsonRegions(source, options = {}) {
|
|
1566
|
+
const regions = [];
|
|
1567
|
+
const endOf = lazyJsonEnds(source);
|
|
1568
|
+
const excluded = [
|
|
1569
|
+
...options.excludeLiteralContexts ? literalRanges(source, options.liftJsonFences === true) : [],
|
|
1570
|
+
...options.excludeQuotedSource ? quotedSourceRanges(source) : []
|
|
1571
|
+
].sort((a, b) => a[0] - b[0]);
|
|
1572
|
+
const jsonStrings = [];
|
|
1573
|
+
let excludedIndex = 0;
|
|
1574
|
+
let jsonStringIndex = 0;
|
|
1575
|
+
for (let start = frontMatterEnd(source); start < source.length; start++) {
|
|
1576
|
+
while (excludedIndex < excluded.length && excluded[excludedIndex][1] <= start) {
|
|
1577
|
+
excludedIndex++;
|
|
1578
|
+
}
|
|
1579
|
+
while (jsonStringIndex < jsonStrings.length && jsonStrings[jsonStringIndex][1] <= start) {
|
|
1580
|
+
jsonStringIndex++;
|
|
1581
|
+
}
|
|
1582
|
+
if (excludedIndex < excluded.length && start >= excluded[excludedIndex][0] && start < excluded[excludedIndex][1])
|
|
1583
|
+
continue;
|
|
1584
|
+
if (jsonStringIndex < jsonStrings.length && start >= jsonStrings[jsonStringIndex][0] && start < jsonStrings[jsonStringIndex][1])
|
|
1585
|
+
continue;
|
|
1586
|
+
if (source[start] !== "{") continue;
|
|
1587
|
+
const end = endOf(start);
|
|
1588
|
+
if (end === null) {
|
|
1589
|
+
const verdict = kindGrammar(source, "json", start);
|
|
1590
|
+
const bound = verdict.status === "broken" ? verdict.cut : source.length;
|
|
1591
|
+
const ownerEnd = bound > start ? malformedKindOwnerEnd(source, start, bound) : null;
|
|
1592
|
+
if (ownerEnd !== null) start = ownerEnd - 1;
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1595
|
+
const content = source.slice(start, end);
|
|
1596
|
+
const kind = declaredKind(content);
|
|
1597
|
+
if (!kind) {
|
|
1598
|
+
const ownerEnd = malformedKindOwnerEnd(source, start, end);
|
|
1599
|
+
if (ownerEnd !== null) {
|
|
1600
|
+
start = ownerEnd - 1;
|
|
1601
|
+
continue;
|
|
1602
|
+
}
|
|
1603
|
+
for (const range of jsonStringRanges(source, start, end)) jsonStrings.push(range);
|
|
1604
|
+
continue;
|
|
1605
|
+
}
|
|
1606
|
+
regions.push({ start, end, content, kind });
|
|
1607
|
+
start = end - 1;
|
|
1608
|
+
}
|
|
1609
|
+
return regions;
|
|
1610
|
+
}
|
|
1611
|
+
function kindArrayChromeSpans(source, regions) {
|
|
1612
|
+
const spans = [];
|
|
1613
|
+
let first = 0;
|
|
1614
|
+
while (first < regions.length) {
|
|
1615
|
+
let last = first;
|
|
1616
|
+
while (last + 1 < regions.length && /^\s*,\s*$/.test(source.slice(regions[last].end, regions[last + 1].start))) {
|
|
1617
|
+
last++;
|
|
1618
|
+
}
|
|
1619
|
+
const before = source.slice(0, regions[first].start);
|
|
1620
|
+
const open = /\[\s*$/.exec(before);
|
|
1621
|
+
const after = source.slice(regions[last].end);
|
|
1622
|
+
const close = /^\s*\]/.exec(after);
|
|
1623
|
+
if (open && close) {
|
|
1624
|
+
spans.push([open.index, regions[first].start]);
|
|
1625
|
+
for (let k = first; k < last; k++) {
|
|
1626
|
+
spans.push([regions[k].end, regions[k + 1].start]);
|
|
1627
|
+
}
|
|
1628
|
+
spans.push([regions[last].end, regions[last].end + close[0].length]);
|
|
1629
|
+
}
|
|
1630
|
+
first = last + 1;
|
|
1631
|
+
}
|
|
1632
|
+
return spans;
|
|
1633
|
+
}
|
|
1634
|
+
function isKindValue(value) {
|
|
1635
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1636
|
+
return false;
|
|
1637
|
+
}
|
|
1638
|
+
const kind = value.__kind;
|
|
1639
|
+
return typeof kind === "string" && kind.trim().length > 0;
|
|
1640
|
+
}
|
|
1641
|
+
function withoutKinds(value) {
|
|
1642
|
+
if (Array.isArray(value)) {
|
|
1643
|
+
return value.filter((item) => !isKindValue(item)).map(withoutKinds);
|
|
1644
|
+
}
|
|
1645
|
+
if (typeof value === "object" && value !== null) {
|
|
1646
|
+
const out = {};
|
|
1647
|
+
for (const [key, item] of Object.entries(value)) {
|
|
1648
|
+
if (!isKindValue(item)) out[key] = withoutKinds(item);
|
|
1649
|
+
}
|
|
1650
|
+
return out;
|
|
1651
|
+
}
|
|
1652
|
+
return value;
|
|
1653
|
+
}
|
|
1654
|
+
function carriesData(value) {
|
|
1655
|
+
if (Array.isArray(value)) return value.some(carriesData);
|
|
1656
|
+
if (typeof value === "object" && value !== null) {
|
|
1657
|
+
return Object.values(value).some(carriesData);
|
|
1658
|
+
}
|
|
1659
|
+
return true;
|
|
1660
|
+
}
|
|
1661
|
+
function isPureJsonStructure(span) {
|
|
1662
|
+
return /^[\s{}[\],:]*$/.test(
|
|
1663
|
+
span.replace(/"(?:[^"\\]|\\.)*"\s*:/g, "")
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
function wrapperPieces(source, regions) {
|
|
1667
|
+
const trimmed = source.trim();
|
|
1668
|
+
if (!/^[[{]/.test(trimmed)) return null;
|
|
1669
|
+
let parsed;
|
|
1670
|
+
try {
|
|
1671
|
+
parsed = JSON.parse(trimmed);
|
|
1672
|
+
} catch {
|
|
1673
|
+
return null;
|
|
1674
|
+
}
|
|
1675
|
+
if (typeof parsed !== "object" || parsed === null || isKindValue(parsed)) {
|
|
1676
|
+
return null;
|
|
1677
|
+
}
|
|
1678
|
+
const residual = withoutKinds(parsed);
|
|
1679
|
+
const residualJson = carriesData(residual) ? JSON.stringify(residual, null, 2) : null;
|
|
1680
|
+
const pieces = [];
|
|
1681
|
+
let residualPlaced = residualJson === null;
|
|
1682
|
+
const pushSpan = (content) => {
|
|
1683
|
+
if (!content) return;
|
|
1684
|
+
if (!residualPlaced && !isPureJsonStructure(content)) {
|
|
1685
|
+
pieces.push({ type: "residual", content, json: residualJson });
|
|
1686
|
+
residualPlaced = true;
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
pieces.push({ type: "chrome", content });
|
|
1690
|
+
};
|
|
1691
|
+
let cursor = 0;
|
|
1692
|
+
for (const region of regions) {
|
|
1693
|
+
pushSpan(source.slice(cursor, region.start));
|
|
1694
|
+
pieces.push({ type: "kind", content: region.content, kind: region.kind });
|
|
1695
|
+
cursor = region.end;
|
|
1696
|
+
}
|
|
1697
|
+
pushSpan(source.slice(cursor));
|
|
1698
|
+
if (!residualPlaced) {
|
|
1699
|
+
pieces.push({ type: "residual", content: "", json: residualJson });
|
|
1700
|
+
}
|
|
1701
|
+
return pieces;
|
|
1702
|
+
}
|
|
1703
|
+
function splitAroundEmbeddedKindJson(source, options = {}) {
|
|
1704
|
+
const regions = findEmbeddedKindJsonRegions(source, options);
|
|
1705
|
+
if (regions.length === 0) return [{ type: "container", content: source }];
|
|
1706
|
+
const wrapped = wrapperPieces(source, regions);
|
|
1707
|
+
if (wrapped) return wrapped;
|
|
1708
|
+
const marks = [
|
|
1709
|
+
...regions.map((r) => ({ start: r.start, end: r.end, type: "kind", kind: r.kind })),
|
|
1710
|
+
...[
|
|
1711
|
+
...kindArrayChromeSpans(source, regions),
|
|
1712
|
+
...options.excludeLiteralContexts && options.liftJsonFences ? jsonFenceChromeSpans(source, regions) : []
|
|
1713
|
+
].map(([start, end]) => ({
|
|
1714
|
+
start,
|
|
1715
|
+
end,
|
|
1716
|
+
type: "chrome"
|
|
1717
|
+
}))
|
|
1718
|
+
].sort((a, b) => a.start - b.start);
|
|
1719
|
+
const pieces = [];
|
|
1720
|
+
let cursor = 0;
|
|
1721
|
+
for (const mark of marks) {
|
|
1722
|
+
if (mark.start > cursor) {
|
|
1723
|
+
pieces.push({
|
|
1724
|
+
type: "container",
|
|
1725
|
+
content: source.slice(cursor, mark.start)
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
if (mark.end > mark.start) {
|
|
1729
|
+
pieces.push(
|
|
1730
|
+
mark.type === "kind" ? { type: "kind", content: source.slice(mark.start, mark.end), kind: mark.kind } : { type: "chrome", content: source.slice(mark.start, mark.end) }
|
|
1731
|
+
);
|
|
1732
|
+
}
|
|
1733
|
+
cursor = Math.max(cursor, mark.end);
|
|
1734
|
+
}
|
|
1735
|
+
if (cursor < source.length) {
|
|
1736
|
+
pieces.push({ type: "container", content: source.slice(cursor) });
|
|
1737
|
+
}
|
|
1738
|
+
return pieces;
|
|
1739
|
+
}
|
|
1740
|
+
function normalizeRecoveredProsePiece(content, followsKind) {
|
|
1741
|
+
const start = followsKind ? content.replace(/^[ \t]*(?:\r?\n)?/, "") : content;
|
|
1742
|
+
return start.trimEnd();
|
|
1743
|
+
}
|
|
1744
|
+
function normalizeRecoveredContainerPiece(content, containerType, followsKind) {
|
|
1745
|
+
if (containerType === "text") {
|
|
1746
|
+
return normalizeRecoveredProsePiece(content, followsKind);
|
|
1747
|
+
}
|
|
1748
|
+
if (containerType === "code") return content;
|
|
1749
|
+
return content.trim();
|
|
1750
|
+
}
|
|
1751
|
+
function findKindCarryingJsonValues(source, options = {}) {
|
|
1752
|
+
const kinds = findEmbeddedKindJsonRegions(source, options);
|
|
1753
|
+
if (kinds.length === 0) return [];
|
|
1754
|
+
const excluded = options.excludeLiteralContexts ? literalRanges(source) : [];
|
|
1755
|
+
const inLiteral = (index) => excluded.some(([start, end]) => index >= start && index < end);
|
|
1756
|
+
const ends = balancedEnds(source);
|
|
1757
|
+
const regions = [];
|
|
1758
|
+
let floor = frontMatterEnd(source);
|
|
1759
|
+
for (const region of kinds) {
|
|
1760
|
+
if (region.start < floor) continue;
|
|
1761
|
+
let best = { start: region.start, end: region.end, value: JSON.parse(region.content) };
|
|
1762
|
+
for (let open = floor; open < region.start; open++) {
|
|
1763
|
+
const char = source[open];
|
|
1764
|
+
if (char !== "{" && char !== "[" || inLiteral(open)) continue;
|
|
1765
|
+
const found = ends[open];
|
|
1766
|
+
if (found < 0 || found < region.end) continue;
|
|
1767
|
+
const end = found;
|
|
1768
|
+
try {
|
|
1769
|
+
best = { start: open, end, value: JSON.parse(source.slice(open, end)) };
|
|
1770
|
+
break;
|
|
1771
|
+
} catch {
|
|
1772
|
+
continue;
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
regions.push({ ...best, content: source.slice(best.start, best.end) });
|
|
1776
|
+
floor = best.end;
|
|
1777
|
+
}
|
|
1778
|
+
return regions;
|
|
1779
|
+
}
|
|
1780
|
+
var KIND_KEY_TEXT = /(?<!\\)"__kind"\s*:/;
|
|
1781
|
+
var KIND_KEY_TEXT_G = /(?<!\\)"__kind"\s*:/g;
|
|
1782
|
+
function findBrokenKindJsonRegions(source, options = {}) {
|
|
1783
|
+
const broken = [];
|
|
1784
|
+
if (!KIND_KEY_TEXT.test(source)) return broken;
|
|
1785
|
+
const endOf = lazyJsonEnds(source);
|
|
1786
|
+
const keys = [];
|
|
1787
|
+
KIND_KEY_TEXT_G.lastIndex = 0;
|
|
1788
|
+
for (let m = KIND_KEY_TEXT_G.exec(source); m; m = KIND_KEY_TEXT_G.exec(source)) keys.push(m.index);
|
|
1789
|
+
let keyIndex = 0;
|
|
1790
|
+
const excluded = options.excludeLiteralContexts ? literalRanges(source) : [];
|
|
1791
|
+
let excludedIndex = 0;
|
|
1792
|
+
for (let start = frontMatterEnd(source); start < source.length; start++) {
|
|
1793
|
+
while (excludedIndex < excluded.length && excluded[excludedIndex][1] <= start) {
|
|
1794
|
+
excludedIndex++;
|
|
1795
|
+
}
|
|
1796
|
+
if (excludedIndex < excluded.length && start >= excluded[excludedIndex][0] && start < excluded[excludedIndex][1]) {
|
|
1797
|
+
start = excluded[excludedIndex][1] - 1;
|
|
1798
|
+
continue;
|
|
1799
|
+
}
|
|
1800
|
+
const char = source[start];
|
|
1801
|
+
if (char !== "{" && char !== "[") continue;
|
|
1802
|
+
while (keyIndex < keys.length && keys[keyIndex] < start) keyIndex++;
|
|
1803
|
+
if (keyIndex >= keys.length) break;
|
|
1804
|
+
const key = keys[keyIndex];
|
|
1805
|
+
const verdict = kindGrammar(source, "json", start);
|
|
1806
|
+
if (verdict.status === "complete") {
|
|
1807
|
+
try {
|
|
1808
|
+
JSON.parse(source.slice(start, verdict.end));
|
|
1809
|
+
start = verdict.end - 1;
|
|
1810
|
+
} catch {
|
|
1811
|
+
}
|
|
1812
|
+
continue;
|
|
1813
|
+
}
|
|
1814
|
+
if (verdict.status === "prefix") {
|
|
1815
|
+
broken.push({ start, end: source.length, content: source.slice(start) });
|
|
1816
|
+
break;
|
|
1817
|
+
}
|
|
1818
|
+
if (verdict.at <= key) continue;
|
|
1819
|
+
const end = endOf(start);
|
|
1820
|
+
if (end !== null && lenientJson(source.slice(start, end))) {
|
|
1821
|
+
broken.push({ start, end, content: source.slice(start, end) });
|
|
1822
|
+
start = end - 1;
|
|
1823
|
+
continue;
|
|
1824
|
+
}
|
|
1825
|
+
if (verdict.cut <= key) continue;
|
|
1826
|
+
broken.push({ start, end: verdict.cut, content: source.slice(start, verdict.cut) });
|
|
1827
|
+
start = verdict.cut - 1;
|
|
1828
|
+
}
|
|
1829
|
+
return broken;
|
|
1830
|
+
}
|
|
1831
|
+
function lenientJson(span) {
|
|
1832
|
+
try {
|
|
1833
|
+
JSON.parse(span.replace(/,(\s*[}\]])/g, "$1"));
|
|
1834
|
+
return true;
|
|
1835
|
+
} catch {
|
|
1836
|
+
return false;
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
// core/kind-schema.types.ts
|
|
1841
|
+
var KIND_KEY2 = "__kind";
|
|
1842
|
+
function readObjectKind(value) {
|
|
1843
|
+
const kind = value[KIND_KEY2];
|
|
1844
|
+
return typeof kind === "string" ? kind : null;
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
// surfaces/instance-title.ts
|
|
1848
|
+
var INSTANCE_TITLE_KEYS = [
|
|
1849
|
+
"title",
|
|
1850
|
+
"name",
|
|
1851
|
+
"label",
|
|
1852
|
+
"heading",
|
|
1853
|
+
"subject",
|
|
1854
|
+
"customer"
|
|
1855
|
+
];
|
|
1856
|
+
function isRecord(value) {
|
|
1857
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1858
|
+
}
|
|
1859
|
+
function kindTitleKeyFromMetadata(metadata) {
|
|
1860
|
+
if (!isRecord(metadata)) return null;
|
|
1861
|
+
const value = metadata["title_key"];
|
|
1862
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
1863
|
+
}
|
|
1864
|
+
function scalarTitle(value) {
|
|
1865
|
+
if (typeof value === "string") return value.trim() || null;
|
|
1866
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
1867
|
+
if (typeof value === "number") return Number.isFinite(value) ? String(value) : null;
|
|
1868
|
+
return null;
|
|
1869
|
+
}
|
|
1870
|
+
function deriveInstanceTitle(data, explicit, titleKey) {
|
|
1871
|
+
if (explicit && explicit.trim()) return explicit.trim();
|
|
1872
|
+
if (titleKey) {
|
|
1873
|
+
const override = scalarTitle(data[titleKey]);
|
|
1874
|
+
if (override) return override;
|
|
1875
|
+
}
|
|
1876
|
+
for (const key of INSTANCE_TITLE_KEYS) {
|
|
1877
|
+
const value = data[key];
|
|
1878
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
1879
|
+
}
|
|
1880
|
+
return null;
|
|
1881
|
+
}
|
|
1882
|
+
var IDENTIFIER_ACRONYMS = [
|
|
1883
|
+
"ID",
|
|
1884
|
+
"URL",
|
|
1885
|
+
"PDF",
|
|
1886
|
+
"HTML",
|
|
1887
|
+
"JSON",
|
|
1888
|
+
"API",
|
|
1889
|
+
"AI",
|
|
1890
|
+
"UI",
|
|
1891
|
+
"CSV",
|
|
1892
|
+
"SMS",
|
|
1893
|
+
"SEO",
|
|
1894
|
+
"LLM",
|
|
1895
|
+
"MCP",
|
|
1896
|
+
"UUID",
|
|
1897
|
+
"ISO"
|
|
1898
|
+
];
|
|
1899
|
+
var IDENTIFIER_ACRONYM_SET = new Set(IDENTIFIER_ACRONYMS.map((a) => a.toLowerCase()));
|
|
1900
|
+
function humanizeIdentifier(identifier) {
|
|
1901
|
+
const text = (identifier ?? "").trim();
|
|
1902
|
+
if (text === "") return "";
|
|
1903
|
+
if (!text.includes("_") && /\s/.test(text)) {
|
|
1904
|
+
return /^\p{Ll}/u.test(text) ? text.charAt(0).toUpperCase() + text.slice(1) : text;
|
|
1905
|
+
}
|
|
1906
|
+
return text.replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)(?![A-Z]s(?![a-z]))([A-Z][a-z])/g, "$1 $2").split(/[_\-.\s]+/).filter((part) => part.length > 0).map(identifierWord).join(" ");
|
|
1907
|
+
}
|
|
1908
|
+
function identifierWord(part) {
|
|
1909
|
+
const lower = part.toLowerCase();
|
|
1910
|
+
if (IDENTIFIER_ACRONYM_SET.has(lower)) return part.toUpperCase();
|
|
1911
|
+
if (lower.length > 1 && lower.endsWith("s") && IDENTIFIER_ACRONYM_SET.has(lower.slice(0, -1))) {
|
|
1912
|
+
return `${lower.slice(0, -1).toUpperCase()}s`;
|
|
1913
|
+
}
|
|
1914
|
+
return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
// surfaces/kind-markdown-utils.ts
|
|
1918
|
+
function isRecordValue(value) {
|
|
1919
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1920
|
+
}
|
|
1921
|
+
function isScalar(value) {
|
|
1922
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
1923
|
+
}
|
|
1924
|
+
function formatInlineValue(value) {
|
|
1925
|
+
if (value === null || value === void 0) return "\u2014";
|
|
1926
|
+
if (isScalar(value)) return String(value);
|
|
1927
|
+
if (Array.isArray(value) && value.every(isScalar)) {
|
|
1928
|
+
return value.map(String).join(", ");
|
|
1929
|
+
}
|
|
1930
|
+
try {
|
|
1931
|
+
return `\`${JSON.stringify(stripKindForDisplay(value))}\``;
|
|
1932
|
+
} catch {
|
|
1933
|
+
return String(value);
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
function stripKindForDisplay(value) {
|
|
1937
|
+
if (Array.isArray(value)) return value.map(stripKindForDisplay);
|
|
1938
|
+
if (isRecordValue(value)) {
|
|
1939
|
+
const out = {};
|
|
1940
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1941
|
+
if (key === KIND_KEY2) continue;
|
|
1942
|
+
out[key] = stripKindForDisplay(child);
|
|
1943
|
+
}
|
|
1944
|
+
return out;
|
|
1945
|
+
}
|
|
1946
|
+
return value;
|
|
1947
|
+
}
|
|
1948
|
+
function collectExtras(value, knownKeys) {
|
|
1949
|
+
const known = new Set(knownKeys);
|
|
1950
|
+
known.add(KIND_KEY2);
|
|
1951
|
+
known.add("additionalDetails");
|
|
1952
|
+
const extras = {};
|
|
1953
|
+
const details = value.additionalDetails;
|
|
1954
|
+
if (isRecordValue(details)) {
|
|
1955
|
+
for (const [key, child] of Object.entries(details)) {
|
|
1956
|
+
if (key === KIND_KEY2 || child === null || child === void 0) continue;
|
|
1957
|
+
extras[key] = child;
|
|
1958
|
+
}
|
|
1959
|
+
} else if (details !== null && details !== void 0) {
|
|
1960
|
+
extras.additionalDetails = details;
|
|
1961
|
+
}
|
|
1962
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1963
|
+
if (known.has(key) || child === null || child === void 0) continue;
|
|
1964
|
+
extras[key] = child;
|
|
1965
|
+
}
|
|
1966
|
+
return extras;
|
|
1967
|
+
}
|
|
1968
|
+
function plainFieldLabel(key) {
|
|
1969
|
+
const words = key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim().toLowerCase();
|
|
1970
|
+
if (!words) return key;
|
|
1971
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
1972
|
+
}
|
|
1973
|
+
function extrasList(extras) {
|
|
1974
|
+
const entries = Object.entries(extras);
|
|
1975
|
+
if (entries.length === 0) return null;
|
|
1976
|
+
return entries.map(([key, value]) => `- **${key}:** ${formatInlineValue(value)}`).join("\n");
|
|
1977
|
+
}
|
|
1978
|
+
function additionalDetailsSection(extras, headingLevel = "##") {
|
|
1979
|
+
const list = extrasList(extras);
|
|
1980
|
+
if (!list) return null;
|
|
1981
|
+
return `${headingLevel} Additional details
|
|
1982
|
+
|
|
1983
|
+
${list}`;
|
|
1984
|
+
}
|
|
1985
|
+
function joinBlocks(blocks) {
|
|
1986
|
+
return blocks.map((block) => typeof block === "string" ? block.trim() : "").filter((block) => block.length > 0).join("\n\n");
|
|
1987
|
+
}
|
|
1988
|
+
function humanizeKind(kind) {
|
|
1989
|
+
return humanizeIdentifier(kind) || "Artifact";
|
|
1990
|
+
}
|
|
1991
|
+
function isKindValue2(value) {
|
|
1992
|
+
return isRecordValue(value) && typeof value[KIND_KEY2] === "string" && value[KIND_KEY2].trim().length > 0;
|
|
1993
|
+
}
|
|
1994
|
+
function scalarText(value) {
|
|
1995
|
+
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
1996
|
+
return String(value);
|
|
1997
|
+
}
|
|
1998
|
+
function cellText(value) {
|
|
1999
|
+
return scalarText(value).replace(/\r?\n/g, " ").replace(/\|/g, "\\|");
|
|
2000
|
+
}
|
|
2001
|
+
function visibleEntries(value) {
|
|
2002
|
+
return Object.entries(value).filter(
|
|
2003
|
+
([key, child]) => key !== KIND_KEY2 && child !== null && child !== void 0 && !(typeof child === "string" && child.trim() === "") && !(Array.isArray(child) && child.length === 0)
|
|
2004
|
+
);
|
|
2005
|
+
}
|
|
2006
|
+
function uniformTable(items) {
|
|
2007
|
+
if (items.length === 0) return null;
|
|
2008
|
+
let columns = null;
|
|
2009
|
+
for (const item of items) {
|
|
2010
|
+
if (!isRecordValue(item) || isKindValue2(item)) return null;
|
|
2011
|
+
const keys = Object.keys(item).filter((key) => key !== KIND_KEY2);
|
|
2012
|
+
if (keys.length === 0) return null;
|
|
2013
|
+
if (!keys.every((key) => item[key] === null || isScalar(item[key]))) return null;
|
|
2014
|
+
if (columns === null) columns = keys;
|
|
2015
|
+
else if (keys.length !== columns.length || !keys.every((k, i) => k === columns[i]))
|
|
2016
|
+
return null;
|
|
2017
|
+
}
|
|
2018
|
+
const cols = columns;
|
|
2019
|
+
const head = `| ${cols.map(plainFieldLabel).join(" | ")} |`;
|
|
2020
|
+
const rule = `| ${cols.map(() => "---").join(" | ")} |`;
|
|
2021
|
+
const rows = items.map(
|
|
2022
|
+
(item) => `| ${cols.map((key) => {
|
|
2023
|
+
const cell = item[key];
|
|
2024
|
+
return cell === null || cell === void 0 ? "\u2014" : cellText(cell);
|
|
2025
|
+
}).join(" | ")} |`
|
|
2026
|
+
);
|
|
2027
|
+
return [head, rule, ...rows].join("\n");
|
|
2028
|
+
}
|
|
2029
|
+
function indentBlock(text, indent) {
|
|
2030
|
+
return text.split("\n").map((line) => line.trim() ? indent + line : line).join("\n");
|
|
2031
|
+
}
|
|
2032
|
+
function listLines(value, indent, nested) {
|
|
2033
|
+
if (Array.isArray(value)) {
|
|
2034
|
+
const lines = [];
|
|
2035
|
+
for (const item of value) {
|
|
2036
|
+
if (item === null || item === void 0) continue;
|
|
2037
|
+
if (isScalar(item)) lines.push(`${indent}- ${scalarText(item)}`);
|
|
2038
|
+
else if (isKindValue2(item)) {
|
|
2039
|
+
lines.push(`${indent}-`);
|
|
2040
|
+
lines.push(indentBlock(nested(item), indent + " "));
|
|
2041
|
+
} else if (Array.isArray(item)) {
|
|
2042
|
+
lines.push(`${indent}-`);
|
|
2043
|
+
lines.push(...listLines(item, indent + " ", nested));
|
|
2044
|
+
} else if (isRecordValue(item)) {
|
|
2045
|
+
const fields = listLines(item, indent + " ", nested);
|
|
2046
|
+
if (fields.length > 0) {
|
|
2047
|
+
lines.push(`${indent}- ${fields[0].trimStart().replace(/^- /, "")}`);
|
|
2048
|
+
lines.push(...fields.slice(1));
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
return lines;
|
|
2053
|
+
}
|
|
2054
|
+
if (isRecordValue(value)) {
|
|
2055
|
+
if (isKindValue2(value)) return [indentBlock(nested(value), indent)];
|
|
2056
|
+
const lines = [];
|
|
2057
|
+
for (const [key, child] of visibleEntries(value)) {
|
|
2058
|
+
const label = `**${plainFieldLabel(key)}:**`;
|
|
2059
|
+
if (isScalar(child)) lines.push(`${indent}- ${label} ${scalarText(child)}`);
|
|
2060
|
+
else if (Array.isArray(child) && child.every(isScalar))
|
|
2061
|
+
lines.push(`${indent}- ${label} ${child.map(scalarText).join(", ")}`);
|
|
2062
|
+
else {
|
|
2063
|
+
lines.push(`${indent}- ${label}`);
|
|
2064
|
+
lines.push(...listLines(child, indent + " ", nested));
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
return lines;
|
|
2068
|
+
}
|
|
2069
|
+
return isScalar(value) ? [`${indent}- ${scalarText(value)}`] : [];
|
|
2070
|
+
}
|
|
2071
|
+
function headingFor(depth) {
|
|
2072
|
+
return "#".repeat(Math.min(Math.max(depth, 1), 6));
|
|
2073
|
+
}
|
|
2074
|
+
function recordBody(value, depth, nested, skipKey) {
|
|
2075
|
+
const scalars = [];
|
|
2076
|
+
const sections = [];
|
|
2077
|
+
for (const [key, child] of visibleEntries(value)) {
|
|
2078
|
+
if (key === skipKey) continue;
|
|
2079
|
+
const label = plainFieldLabel(key);
|
|
2080
|
+
if (isScalar(child)) {
|
|
2081
|
+
scalars.push(`- **${label}:** ${scalarText(child)}`);
|
|
2082
|
+
} else if (Array.isArray(child) && child.every(isScalar)) {
|
|
2083
|
+
scalars.push(`- **${label}:** ${child.map(scalarText).join(", ")}`);
|
|
2084
|
+
} else if (isKindValue2(child)) {
|
|
2085
|
+
sections.push(joinBlocks([`${headingFor(depth + 1)} ${label}`, nested(child)]));
|
|
2086
|
+
} else if (Array.isArray(child)) {
|
|
2087
|
+
sections.push(
|
|
2088
|
+
joinBlocks([
|
|
2089
|
+
`${headingFor(depth + 1)} ${label}`,
|
|
2090
|
+
uniformTable(child) ?? listLines(child, "", nested).join("\n")
|
|
2091
|
+
])
|
|
2092
|
+
);
|
|
2093
|
+
} else if (isRecordValue(child)) {
|
|
2094
|
+
sections.push(
|
|
2095
|
+
joinBlocks([
|
|
2096
|
+
`${headingFor(depth + 1)} ${label}`,
|
|
2097
|
+
...recordBody(child, depth + 1, nested, null)
|
|
2098
|
+
])
|
|
2099
|
+
);
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
return [scalars.length ? scalars.join("\n") : null, ...sections].filter(
|
|
2103
|
+
(block) => block !== null
|
|
2104
|
+
);
|
|
2105
|
+
}
|
|
2106
|
+
function titleSourceKey(value, title) {
|
|
2107
|
+
if (!title) return null;
|
|
2108
|
+
for (const key of INSTANCE_TITLE_KEYS) {
|
|
2109
|
+
const v = value[key];
|
|
2110
|
+
if (typeof v === "string" && v.trim() === title) return key;
|
|
2111
|
+
}
|
|
2112
|
+
return null;
|
|
2113
|
+
}
|
|
2114
|
+
function genericKindMarkdown(kind, value, nested) {
|
|
2115
|
+
const renderNested = nested ?? ((child) => genericKindMarkdown(String(child[KIND_KEY2] ?? "artifact"), child));
|
|
2116
|
+
const title = deriveInstanceTitle(value);
|
|
2117
|
+
return joinBlocks([
|
|
2118
|
+
`# ${title ?? humanizeKind(kind)}`,
|
|
2119
|
+
`*${humanizeKind(kind)}*`,
|
|
2120
|
+
...recordBody(value, 1, renderNested, titleSourceKey(value, title))
|
|
2121
|
+
]);
|
|
2122
|
+
}
|
|
2123
|
+
function plainValueMarkdown(value, nested) {
|
|
2124
|
+
const renderNested = nested ?? ((child) => genericKindMarkdown(String(child[KIND_KEY2] ?? "artifact"), child));
|
|
2125
|
+
if (value === null || value === void 0) return "";
|
|
2126
|
+
if (isKindValue2(value)) return renderNested(value);
|
|
2127
|
+
if (Array.isArray(value)) {
|
|
2128
|
+
return uniformTable(value) ?? listLines(value, "", renderNested).join("\n");
|
|
2129
|
+
}
|
|
2130
|
+
if (isRecordValue(value)) return joinBlocks(recordBody(value, 1, renderNested, null));
|
|
2131
|
+
return isScalar(value) ? scalarText(value) : String(value);
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
// surfaces/kind-value-markdown.ts
|
|
2135
|
+
var STATE_KEY = /* @__PURE__ */ Symbol.for("ai-matrx.content-ir.kind-value-markdown");
|
|
2136
|
+
function state() {
|
|
2137
|
+
const g = globalThis;
|
|
2138
|
+
let s = g[STATE_KEY];
|
|
2139
|
+
if (!s) {
|
|
2140
|
+
s = { impl: null, announced: false };
|
|
2141
|
+
g[STATE_KEY] = s;
|
|
2142
|
+
}
|
|
2143
|
+
return s;
|
|
2144
|
+
}
|
|
2145
|
+
function registerKindValueMarkdown(impl) {
|
|
2146
|
+
state().impl = impl;
|
|
2147
|
+
}
|
|
2148
|
+
function kindValueToMarkdown(value, fallbackKind = "artifact") {
|
|
2149
|
+
const s = state();
|
|
2150
|
+
if (s.impl) return s.impl(value, fallbackKind);
|
|
2151
|
+
if (!s.announced) {
|
|
2152
|
+
s.announced = true;
|
|
2153
|
+
console.warn(
|
|
2154
|
+
`[@ai-matrx/content-ir] No "kindValueMarkdown" registered, so kind values render through the generic readable form, not the host's kind registry.`
|
|
2155
|
+
);
|
|
2156
|
+
}
|
|
2157
|
+
const nested = (child) => kindValueToMarkdown(child);
|
|
2158
|
+
return genericKindMarkdown(readObjectKind(value) ?? fallbackKind, value, nested);
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
// surfaces/kind-text-to-markdown.ts
|
|
2162
|
+
function isRecord2(value) {
|
|
2163
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2164
|
+
}
|
|
2165
|
+
function isKindObject(value) {
|
|
2166
|
+
return isRecord2(value) && typeof value.__kind === "string" && value.__kind.trim().length > 0;
|
|
2167
|
+
}
|
|
2168
|
+
function unfinishedKindLabel(kind) {
|
|
2169
|
+
return kind ? `${humanizeKind(kind)} did not finish` : "Result did not finish";
|
|
2170
|
+
}
|
|
2171
|
+
var UNREADABLE_KIND_NOTE = "Structured output could not be read";
|
|
2172
|
+
var KIND_SLUG_TEXT = /^[A-Za-z0-9_.:-]+$/;
|
|
2173
|
+
function carriesBrokenKind(value, depth = 0) {
|
|
2174
|
+
if (depth > 64 || value === null || typeof value !== "object") return false;
|
|
2175
|
+
if (Array.isArray(value)) return value.some((item) => carriesBrokenKind(item, depth + 1));
|
|
2176
|
+
const record = value;
|
|
2177
|
+
if (Object.prototype.hasOwnProperty.call(record, "__kind")) {
|
|
2178
|
+
const kind = record.__kind;
|
|
2179
|
+
const shape = typeof kind === "object" && kind !== null && !Array.isArray(kind);
|
|
2180
|
+
if (!shape && (typeof kind !== "string" || !KIND_SLUG_TEXT.test(kind))) return true;
|
|
2181
|
+
}
|
|
2182
|
+
return Object.values(record).some((item) => carriesBrokenKind(item, depth + 1));
|
|
2183
|
+
}
|
|
2184
|
+
function rangeCursor(ranges) {
|
|
2185
|
+
const sorted = [...ranges].sort((a, b) => a[0] - b[0]);
|
|
2186
|
+
let k = 0;
|
|
2187
|
+
return (at) => {
|
|
2188
|
+
while (k < sorted.length && sorted[k][1] <= at) k++;
|
|
2189
|
+
for (let j = k; j < sorted.length && sorted[j][0] <= at; j++) if (at < sorted[j][1]) return sorted[j];
|
|
2190
|
+
return void 0;
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
function quotedCodeRanges(text) {
|
|
2194
|
+
return findCodeRanges(text).filter((range) => {
|
|
2195
|
+
if (range.kind !== "fence") return true;
|
|
2196
|
+
const parts = fenceParts(text.slice(range.start, range.end));
|
|
2197
|
+
return !parts || !JSON_FENCE_LANGS2.has(parts.opener.lang.toLowerCase());
|
|
2198
|
+
}).map((range) => [range.start, range.end]);
|
|
2199
|
+
}
|
|
2200
|
+
function noteUnreadableKinds(text) {
|
|
2201
|
+
if (!hasKindKey(text)) return text;
|
|
2202
|
+
const quoted = quotedCodeRanges(text);
|
|
2203
|
+
const fences = findCodeRanges(text).filter((range) => range.kind === "fence");
|
|
2204
|
+
const spans = [];
|
|
2205
|
+
const ends = balancedEnds(text);
|
|
2206
|
+
const quotedAt = rangeCursor(quoted);
|
|
2207
|
+
for (let i = 0; i < text.length; i++) {
|
|
2208
|
+
const ch = text[i];
|
|
2209
|
+
if (ch !== "{" && ch !== "[") continue;
|
|
2210
|
+
const range = quotedAt(i);
|
|
2211
|
+
if (range) {
|
|
2212
|
+
i = range[1] - 1;
|
|
2213
|
+
continue;
|
|
2214
|
+
}
|
|
2215
|
+
const end = ends[i];
|
|
2216
|
+
if (end < 0) continue;
|
|
2217
|
+
let value;
|
|
2218
|
+
try {
|
|
2219
|
+
value = JSON.parse(text.slice(i, end));
|
|
2220
|
+
} catch {
|
|
2221
|
+
continue;
|
|
2222
|
+
}
|
|
2223
|
+
if (carriesBrokenKind(value)) spans.push([i, end]);
|
|
2224
|
+
i = end - 1;
|
|
2225
|
+
}
|
|
2226
|
+
let kept = text.length;
|
|
2227
|
+
let tail = "";
|
|
2228
|
+
let tailHead = "";
|
|
2229
|
+
for (let k = spans.length - 1; k >= 0; k--) {
|
|
2230
|
+
let [start, end] = spans[k];
|
|
2231
|
+
const fence = fences.find((range) => start >= range.start && end <= range.end);
|
|
2232
|
+
if (fence) {
|
|
2233
|
+
const parts = fenceParts(text.slice(fence.start, fence.end));
|
|
2234
|
+
const inner = parts ? text.slice(fence.start, fence.end).split("\n").slice(1, parts.closed ? -1 : void 0).join("\n").trim() : "";
|
|
2235
|
+
if (parts && inner === text.slice(start, end).trim()) {
|
|
2236
|
+
start = fence.start;
|
|
2237
|
+
end = fence.end;
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
const before = closeBeforeAt(text, start);
|
|
2241
|
+
const following = end <= kept ? text.slice(end, kept) : "";
|
|
2242
|
+
const after = end <= kept ? openAfterJoined(following, tail, tailHead) : openAfter(tail.slice(end - kept));
|
|
2243
|
+
kept = before.cut;
|
|
2244
|
+
tailHead = before.add + UNREADABLE_KIND_NOTE;
|
|
2245
|
+
tail = tailHead + after;
|
|
2246
|
+
}
|
|
2247
|
+
return text.slice(0, kept) + tail;
|
|
2248
|
+
}
|
|
2249
|
+
function liftKindStrings(value, depth = 0) {
|
|
2250
|
+
if (depth > 64) return value;
|
|
2251
|
+
if (typeof value === "string") {
|
|
2252
|
+
if (!isKindJsonText(value)) return value;
|
|
2253
|
+
try {
|
|
2254
|
+
const parsed = JSON.parse(value);
|
|
2255
|
+
return valueCarriesKind(parsed) ? parsed : value;
|
|
2256
|
+
} catch {
|
|
2257
|
+
return value;
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
if (Array.isArray(value)) return value.map((item) => liftKindStrings(item, depth + 1));
|
|
2261
|
+
if (isRecord2(value)) {
|
|
2262
|
+
const out = {};
|
|
2263
|
+
for (const [key, item] of Object.entries(value)) out[key] = liftKindStrings(item, depth + 1);
|
|
2264
|
+
return out;
|
|
2265
|
+
}
|
|
2266
|
+
return value;
|
|
2267
|
+
}
|
|
2268
|
+
function valueMarkdown(raw) {
|
|
2269
|
+
const value = liftKindStrings(raw);
|
|
2270
|
+
const nested = (child) => kindValueToMarkdown(child);
|
|
2271
|
+
if (isKindObject(value)) return kindValueToMarkdown(value);
|
|
2272
|
+
if (Array.isArray(value)) {
|
|
2273
|
+
const blocks = [];
|
|
2274
|
+
let run = [];
|
|
2275
|
+
const flush = () => {
|
|
2276
|
+
if (run.length) blocks.push(plainValueMarkdown(run, nested));
|
|
2277
|
+
run = [];
|
|
2278
|
+
};
|
|
2279
|
+
for (const item of value) {
|
|
2280
|
+
if (isKindObject(item)) {
|
|
2281
|
+
flush();
|
|
2282
|
+
blocks.push(kindValueToMarkdown(item));
|
|
2283
|
+
} else if (Array.isArray(item) || isRecord2(item)) {
|
|
2284
|
+
if (valueCarriesKind(item)) {
|
|
2285
|
+
flush();
|
|
2286
|
+
blocks.push(valueMarkdown(item));
|
|
2287
|
+
} else run.push(item);
|
|
2288
|
+
} else run.push(item);
|
|
2289
|
+
}
|
|
2290
|
+
flush();
|
|
2291
|
+
return blocks.filter((block) => block.trim()).join("\n\n");
|
|
2292
|
+
}
|
|
2293
|
+
return plainValueMarkdown(value, nested);
|
|
2294
|
+
}
|
|
2295
|
+
function jsonKindValueMarkdown(text) {
|
|
2296
|
+
const trimmed = text.trim();
|
|
2297
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null;
|
|
2298
|
+
let parsed;
|
|
2299
|
+
try {
|
|
2300
|
+
parsed = JSON.parse(trimmed);
|
|
2301
|
+
} catch {
|
|
2302
|
+
return null;
|
|
2303
|
+
}
|
|
2304
|
+
return valueCarriesKind(parsed) ? valueMarkdown(parsed) : null;
|
|
2305
|
+
}
|
|
2306
|
+
function closeBefore(text) {
|
|
2307
|
+
const { cut, add } = closeBeforeAt(text, text.length);
|
|
2308
|
+
return text.slice(0, cut) + add;
|
|
2309
|
+
}
|
|
2310
|
+
var isLineSpace = (ch) => ch === " " || ch === " " || ch === "\r";
|
|
2311
|
+
function closeBeforeAt(text, at) {
|
|
2312
|
+
let cut = at;
|
|
2313
|
+
while (cut > 0 && isLineSpace(text[cut - 1])) cut--;
|
|
2314
|
+
if (cut === 0) return { cut, add: "" };
|
|
2315
|
+
if (text[cut - 1] !== "\n") return { cut, add: "\n\n" };
|
|
2316
|
+
return { cut, add: cut > 1 && text[cut - 2] === "\n" ? "" : "\n" };
|
|
2317
|
+
}
|
|
2318
|
+
function openAfterJoined(head, tail, tailHead) {
|
|
2319
|
+
let from = 0;
|
|
2320
|
+
while (from < head.length && isLineSpace(head[from])) from++;
|
|
2321
|
+
const rest = head.slice(from);
|
|
2322
|
+
const lead = rest + tailHead.slice(0, 4);
|
|
2323
|
+
if (!lead) return "";
|
|
2324
|
+
const joined = rest + tail;
|
|
2325
|
+
if (lead.startsWith("\n\n") || lead.startsWith("\r\n\r\n")) return joined;
|
|
2326
|
+
if (lead.startsWith("\n")) return `
|
|
2327
|
+
${joined}`;
|
|
2328
|
+
return `
|
|
2329
|
+
|
|
2330
|
+
${joined}`;
|
|
2331
|
+
}
|
|
2332
|
+
function openAfter(text) {
|
|
2333
|
+
const out = text.replace(/^[ \t\r]+/, "");
|
|
2334
|
+
if (!out) return out;
|
|
2335
|
+
if (out.startsWith("\n\n") || out.startsWith("\r\n\r\n")) return out;
|
|
2336
|
+
if (out.startsWith("\n")) return `
|
|
2337
|
+
${out}`;
|
|
2338
|
+
return `
|
|
2339
|
+
|
|
2340
|
+
${out}`;
|
|
2341
|
+
}
|
|
2342
|
+
function convertRegions(text, excludeLiteralContexts, options, leftover = (piece) => piece) {
|
|
2343
|
+
const spans = findKindCarryingJsonValues(text, { excludeLiteralContexts }).map(
|
|
2344
|
+
(region) => ({ start: region.start, end: region.end, md: valueMarkdown(region.value) })
|
|
2345
|
+
);
|
|
2346
|
+
if (options.broken === "note") {
|
|
2347
|
+
for (const region of findBrokenKindJsonRegions(text, { excludeLiteralContexts })) {
|
|
2348
|
+
spans.push({
|
|
2349
|
+
start: region.start,
|
|
2350
|
+
end: region.end,
|
|
2351
|
+
md: unfinishedKindLabel(firstKindSlug(region.content))
|
|
2352
|
+
});
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
if (spans.length === 0) return null;
|
|
2356
|
+
spans.sort((a, b) => a.start - b.start);
|
|
2357
|
+
const parts = [];
|
|
2358
|
+
const push = (piece) => {
|
|
2359
|
+
if (piece) parts.push(piece);
|
|
2360
|
+
};
|
|
2361
|
+
const closeBeforeParts = () => {
|
|
2362
|
+
while (parts.length > 0) {
|
|
2363
|
+
const last2 = parts[parts.length - 1];
|
|
2364
|
+
const trimmed = last2.replace(/[ \t\r]+$/, "");
|
|
2365
|
+
if (trimmed) {
|
|
2366
|
+
parts[parts.length - 1] = trimmed;
|
|
2367
|
+
break;
|
|
2368
|
+
}
|
|
2369
|
+
parts.pop();
|
|
2370
|
+
}
|
|
2371
|
+
if (parts.length === 0) return;
|
|
2372
|
+
const last = parts[parts.length - 1];
|
|
2373
|
+
if (last[last.length - 1] !== "\n") {
|
|
2374
|
+
parts.push("\n\n");
|
|
2375
|
+
return;
|
|
2376
|
+
}
|
|
2377
|
+
const before = last.length > 1 ? last[last.length - 2] : parts.length > 1 ? parts[parts.length - 2].slice(-1) : "";
|
|
2378
|
+
if (before !== "\n") parts.push("\n");
|
|
2379
|
+
};
|
|
2380
|
+
let cursor = 0;
|
|
2381
|
+
let afterBlock = false;
|
|
2382
|
+
for (const span of spans) {
|
|
2383
|
+
if (span.start < cursor) continue;
|
|
2384
|
+
const between = leftover(text.slice(cursor, span.start));
|
|
2385
|
+
push(afterBlock ? openAfter(between) : between);
|
|
2386
|
+
closeBeforeParts();
|
|
2387
|
+
push(span.md);
|
|
2388
|
+
cursor = span.end;
|
|
2389
|
+
afterBlock = true;
|
|
2390
|
+
}
|
|
2391
|
+
const tail = leftover(text.slice(cursor));
|
|
2392
|
+
push(afterBlock ? openAfter(tail) : tail);
|
|
2393
|
+
return parts.join("");
|
|
2394
|
+
}
|
|
2395
|
+
var JSON_FENCE_LANGS2 = /* @__PURE__ */ new Set(["", "json", "jsonc", "json5"]);
|
|
2396
|
+
var QUOTE_PREFIX = /^(?: {0,3}> ?)*/;
|
|
2397
|
+
function convertKindFences(text, options) {
|
|
2398
|
+
const fences = findCodeRanges(text).filter((range) => range.kind === "fence");
|
|
2399
|
+
let out = text;
|
|
2400
|
+
for (let i = fences.length - 1; i >= 0; i--) {
|
|
2401
|
+
const range = fences[i];
|
|
2402
|
+
const raw = text.slice(range.start, range.end);
|
|
2403
|
+
if (!hasKindKey(raw)) continue;
|
|
2404
|
+
const parts = fenceParts(raw);
|
|
2405
|
+
if (!parts || !JSON_FENCE_LANGS2.has(parts.opener.lang.toLowerCase())) continue;
|
|
2406
|
+
const lines = raw.split("\n").map((line) => line.replace(/\r$/, ""));
|
|
2407
|
+
const prefix = QUOTE_PREFIX.exec(lines[0] ?? "")?.[0] ?? "";
|
|
2408
|
+
const strip = (line) => prefix ? line.replace(QUOTE_PREFIX, "") : line;
|
|
2409
|
+
const opener = strip(lines[0] ?? "").trim();
|
|
2410
|
+
const fence = parts.opener.char.repeat(parts.opener.ticks);
|
|
2411
|
+
const body = lines.slice(1, parts.closed ? -1 : void 0).map(strip).join("\n");
|
|
2412
|
+
const md = jsonKindValueMarkdown(body) ?? convertRegions(
|
|
2413
|
+
body,
|
|
2414
|
+
false,
|
|
2415
|
+
options,
|
|
2416
|
+
(piece) => /^[\s,[\]{}]*$/.test(piece) ? "" : `${opener}
|
|
2417
|
+
${piece.trim()}
|
|
2418
|
+
${fence}`
|
|
2419
|
+
);
|
|
2420
|
+
if (md === null) continue;
|
|
2421
|
+
const block = prefix ? md.split("\n").map((line) => line ? `${prefix}${line}` : prefix.trimEnd()).join("\n") : md;
|
|
2422
|
+
out = closeBefore(out.slice(0, range.start)) + block + openAfter(out.slice(range.end));
|
|
2423
|
+
}
|
|
2424
|
+
return out;
|
|
2425
|
+
}
|
|
2426
|
+
function convertKindText(text, options) {
|
|
2427
|
+
text = noteUnreadableKinds(convertFrontMatterKind(text, options));
|
|
2428
|
+
const whole = jsonKindValueMarkdown(text);
|
|
2429
|
+
if (whole !== null) return whole;
|
|
2430
|
+
let out = convertKindFences(text, options);
|
|
2431
|
+
if (!hasKindKey(out)) return out;
|
|
2432
|
+
out = convertRegions(out, true, options) ?? out;
|
|
2433
|
+
out = convertCommentKinds(out, options);
|
|
2434
|
+
return noteEscapedCutOffKind(out, options);
|
|
2435
|
+
}
|
|
2436
|
+
function convertFrontMatterKind(text, options) {
|
|
2437
|
+
const end = frontMatterEnd(text);
|
|
2438
|
+
if (end === 0 || !hasKindKey(text.slice(0, end))) return text;
|
|
2439
|
+
const block = text.slice(0, end);
|
|
2440
|
+
const lines = block.split("\n");
|
|
2441
|
+
const body = lines.slice(1, -1).join("\n").replace(/\r$/, "");
|
|
2442
|
+
const md = convertRegions(
|
|
2443
|
+
body,
|
|
2444
|
+
false,
|
|
2445
|
+
options,
|
|
2446
|
+
(piece) => /^[\s,[\]{}]*$/.test(piece) ? "" : piece.trim()
|
|
2447
|
+
);
|
|
2448
|
+
if (md === null) return text;
|
|
2449
|
+
return md + openAfter(text.slice(end));
|
|
2450
|
+
}
|
|
2451
|
+
function convertCommentKinds(text, options) {
|
|
2452
|
+
const quoted = quotedCodeRanges(text);
|
|
2453
|
+
const comments = [...text.matchAll(/<!--[\s\S]*?(?:-->|$)/g)].filter(
|
|
2454
|
+
(m) => hasKindKey(m[0]) && !quoted.some(([a, b]) => m.index >= a && m.index < b)
|
|
2455
|
+
);
|
|
2456
|
+
let out = text;
|
|
2457
|
+
for (let i = comments.length - 1; i >= 0; i--) {
|
|
2458
|
+
const m = comments[i];
|
|
2459
|
+
const body = m[0].replace(/^<!--/, "").replace(/-->$/, "");
|
|
2460
|
+
const md = convertRegions(
|
|
2461
|
+
body,
|
|
2462
|
+
false,
|
|
2463
|
+
options,
|
|
2464
|
+
(piece) => piece.trim() ? `<!-- ${piece.trim()} -->` : ""
|
|
2465
|
+
);
|
|
2466
|
+
if (md === null) continue;
|
|
2467
|
+
const start = m.index;
|
|
2468
|
+
const end = start + m[0].length;
|
|
2469
|
+
out = closeBefore(out.slice(0, start)) + md + openAfter(out.slice(end));
|
|
2470
|
+
}
|
|
2471
|
+
return out;
|
|
2472
|
+
}
|
|
2473
|
+
var KIND_KEY_PLAIN = /(?<!\\)"__kind"\s*:/;
|
|
2474
|
+
function noteEscapedCutOffKind(text, options) {
|
|
2475
|
+
if (options.broken !== "note" || !hasKindKey(text) || KIND_KEY_PLAIN.test(text)) return text;
|
|
2476
|
+
const quotedAt = rangeCursor(quotedCodeRanges(text));
|
|
2477
|
+
const ends = balancedEnds(text);
|
|
2478
|
+
for (let i = 0; i < text.length; i++) {
|
|
2479
|
+
const ch = text[i];
|
|
2480
|
+
if (ch !== "{" && ch !== "[") continue;
|
|
2481
|
+
const range = quotedAt(i);
|
|
2482
|
+
if (range) {
|
|
2483
|
+
i = range[1] - 1;
|
|
2484
|
+
continue;
|
|
2485
|
+
}
|
|
2486
|
+
const end = ends[i];
|
|
2487
|
+
if (end >= 0) {
|
|
2488
|
+
i = end - 1;
|
|
2489
|
+
continue;
|
|
2490
|
+
}
|
|
2491
|
+
const tail = text.slice(i);
|
|
2492
|
+
if (/^[{[]\s*["{[]/.test(tail) && hasKindKey(tail)) {
|
|
2493
|
+
return closeBefore(text.slice(0, i)).trimEnd() + (i > 0 ? "\n\n" : "") + unfinishedKindLabel(firstKindSlug(tail));
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
return text;
|
|
2497
|
+
}
|
|
2498
|
+
function kindTextToMarkdown(raw) {
|
|
2499
|
+
if (!raw) return "";
|
|
2500
|
+
const text = normalizeKindSpellings(raw);
|
|
2501
|
+
if (!hasKindKey(text)) return text;
|
|
2502
|
+
return convertKindText(text, { broken: "note" });
|
|
2503
|
+
}
|
|
2504
|
+
function owningObjectStart(text, keyIndex) {
|
|
2505
|
+
const open = [];
|
|
2506
|
+
let inString = false;
|
|
2507
|
+
for (let i = 0; i < keyIndex; i++) {
|
|
2508
|
+
const ch = text[i];
|
|
2509
|
+
if (inString) {
|
|
2510
|
+
if (ch === "\\") i++;
|
|
2511
|
+
else if (ch === '"') inString = false;
|
|
2512
|
+
continue;
|
|
2513
|
+
}
|
|
2514
|
+
if (ch === '"') inString = true;
|
|
2515
|
+
else if (ch === "{") open.push(i);
|
|
2516
|
+
else if (ch === "}") open.pop();
|
|
2517
|
+
}
|
|
2518
|
+
return open[0] ?? -1;
|
|
2519
|
+
}
|
|
2520
|
+
function kindKeyIndex(text) {
|
|
2521
|
+
if (!hasKindKey(text)) return -1;
|
|
2522
|
+
let lo = 0;
|
|
2523
|
+
let hi = text.length;
|
|
2524
|
+
while (lo < hi) {
|
|
2525
|
+
const mid = lo + hi >> 1;
|
|
2526
|
+
if (hasKindKey(text.slice(0, mid))) hi = mid;
|
|
2527
|
+
else lo = mid + 1;
|
|
2528
|
+
}
|
|
2529
|
+
let i = lo - 1;
|
|
2530
|
+
while (i > 0 && /\s/.test(text[i - 1])) i--;
|
|
2531
|
+
const closing = i - 1;
|
|
2532
|
+
return text.lastIndexOf('"', closing - 1);
|
|
2533
|
+
}
|
|
2534
|
+
function kindTextPreview(raw) {
|
|
2535
|
+
const text = raw ? normalizeKindSpellings(raw) : raw;
|
|
2536
|
+
const md = !text ? "" : hasKindKey(text) ? convertKindText(text, { broken: "keep" }) : text;
|
|
2537
|
+
const keyIndex = kindKeyIndex(md);
|
|
2538
|
+
if (keyIndex < 0) return { text: md, pendingKind: null, pendingUnnamed: false };
|
|
2539
|
+
const start = owningObjectStart(md, keyIndex);
|
|
2540
|
+
const cutAt = start < 0 ? keyIndex : start;
|
|
2541
|
+
const head = md.slice(0, cutAt).replace(/(^|\n)[ \t]*(`{3,}|~{3,})[^\n]*\n?[ \t]*$/, "$1");
|
|
2542
|
+
const slug = firstKindSlug(md.slice(keyIndex));
|
|
2543
|
+
return { text: head.trimEnd(), pendingKind: slug, pendingUnnamed: slug === null };
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
// surfaces/kind-one-line.ts
|
|
2547
|
+
var UNNAMED = "Structured output";
|
|
2548
|
+
function kindName(slug) {
|
|
2549
|
+
return slug && humanizeIdentifier(slug) || UNNAMED;
|
|
2550
|
+
}
|
|
2551
|
+
function kindOneLine(value, options = {}) {
|
|
2552
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return UNNAMED;
|
|
2553
|
+
const record = value;
|
|
2554
|
+
const slug = typeof record.__kind === "string" ? record.__kind : null;
|
|
2555
|
+
const title = deriveInstanceTitle(record);
|
|
2556
|
+
const name = kindName(slug);
|
|
2557
|
+
if (!(title && title !== name)) return name;
|
|
2558
|
+
const clean = title.replace(/[*_`[\]]/g, "");
|
|
2559
|
+
return options.plain ? `${clean} \xB7 ${name}` : `**${clean}** \xB7 ${name}`;
|
|
2560
|
+
}
|
|
2561
|
+
function inside(ranges, at) {
|
|
2562
|
+
return ranges.some(([start, end]) => at >= start && at < end);
|
|
2563
|
+
}
|
|
2564
|
+
function inlineKindText(raw, options = {}) {
|
|
2565
|
+
const source = raw ? normalizeKindSpellings(raw) : raw;
|
|
2566
|
+
if (!source || !hasKindKey(source)) return source;
|
|
2567
|
+
const spans = [];
|
|
2568
|
+
for (const region of findEmbeddedKindJsonRegions(source, { excludeQuotedSource: true })) {
|
|
2569
|
+
let value = null;
|
|
2570
|
+
try {
|
|
2571
|
+
value = JSON.parse(region.content);
|
|
2572
|
+
} catch {
|
|
2573
|
+
value = { __kind: region.kind };
|
|
2574
|
+
}
|
|
2575
|
+
spans.push({ start: region.start, end: region.end, line: kindOneLine(value, options) });
|
|
2576
|
+
}
|
|
2577
|
+
const quoted = quotedSourceRanges(source);
|
|
2578
|
+
for (const region of findBrokenKindJsonRegions(source)) {
|
|
2579
|
+
if (inside(quoted, region.start)) continue;
|
|
2580
|
+
spans.push({ start: region.start, end: region.end, line: kindName(firstKindSlug(region.content)) });
|
|
2581
|
+
}
|
|
2582
|
+
spans.sort((a, b) => a.start - b.start);
|
|
2583
|
+
let out = "";
|
|
2584
|
+
let cursor = 0;
|
|
2585
|
+
for (const span of spans) {
|
|
2586
|
+
if (span.start < cursor) continue;
|
|
2587
|
+
out += source.slice(cursor, span.start) + span.line;
|
|
2588
|
+
cursor = span.end;
|
|
2589
|
+
}
|
|
2590
|
+
out += source.slice(cursor);
|
|
2591
|
+
if (hasKindKey(out)) {
|
|
2592
|
+
const key = out.search(/(?<!\\)"(?:__kind|\\u005[fF]_kind)"\s*:/);
|
|
2593
|
+
const brace = key >= 0 ? out.lastIndexOf("{", key) : -1;
|
|
2594
|
+
if (brace >= 0 && !inside(quotedSourceRanges(out), brace)) {
|
|
2595
|
+
const verdict = kindGrammar(out, "json", brace);
|
|
2596
|
+
const end = verdict.status === "prefix" ? out.length : verdict.status === "broken" && verdict.at > key ? verdict.cut : -1;
|
|
2597
|
+
if (end > key) out = out.slice(0, brace) + kindName(firstKindSlug(out.slice(key, end))) + out.slice(end);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
return unreadableKindsAsNote(out);
|
|
2601
|
+
}
|
|
2602
|
+
function holdsUnreadableKind(json) {
|
|
2603
|
+
let value;
|
|
2604
|
+
try {
|
|
2605
|
+
value = JSON.parse(json);
|
|
2606
|
+
} catch {
|
|
2607
|
+
return false;
|
|
2608
|
+
}
|
|
2609
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2610
|
+
if (!Object.prototype.hasOwnProperty.call(value, "__kind")) return false;
|
|
2611
|
+
const kind = value.__kind;
|
|
2612
|
+
if (typeof kind === "string") return false;
|
|
2613
|
+
return !(typeof kind === "object" && kind !== null && !Array.isArray(kind));
|
|
2614
|
+
}
|
|
2615
|
+
function unreadableKindsAsNote(text) {
|
|
2616
|
+
if (!text || !mayHoldKindKey(text) || !hasJsonKindKey(text)) return text;
|
|
2617
|
+
let out = "";
|
|
2618
|
+
let cursor = 0;
|
|
2619
|
+
for (const region of scanKindSpellingRegions(text)) {
|
|
2620
|
+
if (region.status !== "complete") continue;
|
|
2621
|
+
const json = regionJson(region);
|
|
2622
|
+
if (json === null || !holdsUnreadableKind(json)) continue;
|
|
2623
|
+
out += text.slice(cursor, region.start) + UNREADABLE_KIND_NOTE;
|
|
2624
|
+
cursor = region.end;
|
|
2625
|
+
}
|
|
2626
|
+
return cursor === 0 ? text : out + text.slice(cursor);
|
|
2627
|
+
}
|
|
2628
|
+
function catalogProseText(text) {
|
|
2629
|
+
if (!text) return "";
|
|
2630
|
+
return inlineKindText(text, { plain: true });
|
|
2631
|
+
}
|
|
2632
|
+
function regionOneLine(region) {
|
|
2633
|
+
const json = region.status === "complete" ? regionJson(region) : region.status === "broken" ? closedJson(region) : null;
|
|
2634
|
+
if (json !== null) {
|
|
2635
|
+
try {
|
|
2636
|
+
const value = JSON.parse(json);
|
|
2637
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) return kindOneLine(value);
|
|
2638
|
+
} catch {
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
return kindName(firstKindSlug(json ?? region.decoded));
|
|
2642
|
+
}
|
|
2643
|
+
var ONE_LINE_CACHE = /* @__PURE__ */ new Map();
|
|
2644
|
+
var ONE_LINE_CACHE_SIZE = 16;
|
|
2645
|
+
function spelledKindsAsOneLine(text) {
|
|
2646
|
+
if (!text || !mayHoldKindKey(text)) return text;
|
|
2647
|
+
const cached = ONE_LINE_CACHE.get(text);
|
|
2648
|
+
if (cached !== void 0) return cached;
|
|
2649
|
+
const out = computeSpelledKindsAsOneLine(text);
|
|
2650
|
+
if (ONE_LINE_CACHE.size >= ONE_LINE_CACHE_SIZE) ONE_LINE_CACHE.delete(ONE_LINE_CACHE.keys().next().value);
|
|
2651
|
+
ONE_LINE_CACHE.set(text, out);
|
|
2652
|
+
return out;
|
|
2653
|
+
}
|
|
2654
|
+
function computeSpelledKindsAsOneLine(text) {
|
|
2655
|
+
let out = "";
|
|
2656
|
+
let cursor = 0;
|
|
2657
|
+
for (const region of scanKindSpellingRegions(text)) {
|
|
2658
|
+
if (region.family === "lifted" && !region.markdown && region.status !== "broken") {
|
|
2659
|
+
const json = region.status === "complete" ? regionJson(region) : null;
|
|
2660
|
+
if (json === null || !holdsUnreadableKind(json)) continue;
|
|
2661
|
+
out += text.slice(cursor, region.start) + UNREADABLE_KIND_NOTE;
|
|
2662
|
+
cursor = region.end;
|
|
2663
|
+
continue;
|
|
2664
|
+
}
|
|
2665
|
+
out += text.slice(cursor, region.start) + regionOneLine(region);
|
|
2666
|
+
cursor = region.end;
|
|
2667
|
+
}
|
|
2668
|
+
return cursor === 0 ? text : out + text.slice(cursor);
|
|
2669
|
+
}
|
|
2670
|
+
var AS_CODE_CACHE = /* @__PURE__ */ new Map();
|
|
2671
|
+
function nonJsonKindsAsCode(text) {
|
|
2672
|
+
if (!text || !mayHoldKindKey(text)) return text;
|
|
2673
|
+
const cached = AS_CODE_CACHE.get(text);
|
|
2674
|
+
if (cached !== void 0) return cached;
|
|
2675
|
+
let out = "";
|
|
2676
|
+
let cursor = 0;
|
|
2677
|
+
let paragraphs = null;
|
|
2678
|
+
let p = 0;
|
|
2679
|
+
for (const region of scanKindSpellingRegions(text, { families: "all" })) {
|
|
2680
|
+
if (region.family === "lifted" || region.family === "markdown") continue;
|
|
2681
|
+
paragraphs ??= codeSpanParagraphs(text);
|
|
2682
|
+
while (p < paragraphs.length - 1 && paragraphs[p].end <= region.start) p++;
|
|
2683
|
+
const fence = safeCodeSpanFence(text, region.start, region.end, paragraphs[p]);
|
|
2684
|
+
if (fence === null) continue;
|
|
2685
|
+
out += text.slice(cursor, region.start) + fence + text.slice(region.start, region.end) + fence;
|
|
2686
|
+
cursor = region.end;
|
|
2687
|
+
}
|
|
2688
|
+
const result = cursor === 0 ? text : out + text.slice(cursor);
|
|
2689
|
+
if (AS_CODE_CACHE.size >= ONE_LINE_CACHE_SIZE) AS_CODE_CACHE.delete(AS_CODE_CACHE.keys().next().value);
|
|
2690
|
+
AS_CODE_CACHE.set(text, result);
|
|
2691
|
+
return result;
|
|
2692
|
+
}
|
|
2693
|
+
var HTML_TAG = /^ {0,3}<\/?[A-Za-z][A-Za-z0-9-]*(?:[\s/>]|$)/m;
|
|
2694
|
+
var TABLE_ROW = /^[ \t]*\|/m;
|
|
2695
|
+
function codeSpanParagraphs(text) {
|
|
2696
|
+
const out = [];
|
|
2697
|
+
const blank = /\n[ \t]*\n/g;
|
|
2698
|
+
let start = 0;
|
|
2699
|
+
for (; ; ) {
|
|
2700
|
+
const m = blank.exec(text);
|
|
2701
|
+
const end = m ? m.index + 1 : text.length;
|
|
2702
|
+
const body = text.slice(start, end);
|
|
2703
|
+
const runs = /* @__PURE__ */ new Set();
|
|
2704
|
+
for (let i = 0; i < body.length; ) {
|
|
2705
|
+
if (body[i] !== "`") {
|
|
2706
|
+
i++;
|
|
2707
|
+
continue;
|
|
2708
|
+
}
|
|
2709
|
+
let n = 0;
|
|
2710
|
+
while (body[i + n] === "`") n++;
|
|
2711
|
+
runs.add(n);
|
|
2712
|
+
i += n;
|
|
2713
|
+
}
|
|
2714
|
+
out.push({ start, end, html: HTML_TAG.test(body), table: TABLE_ROW.test(body), runs });
|
|
2715
|
+
if (!m) break;
|
|
2716
|
+
start = m.index + m[0].length - 1;
|
|
2717
|
+
}
|
|
2718
|
+
return out;
|
|
2719
|
+
}
|
|
2720
|
+
function safeCodeSpanFence(text, start, end, paragraph) {
|
|
2721
|
+
const source = text.slice(start, end);
|
|
2722
|
+
if (/\n[ \t]*\n/.test(source) || source.includes("`")) return null;
|
|
2723
|
+
if (paragraph.html || paragraph.table && source.includes("|")) return null;
|
|
2724
|
+
if (text[start - 1] === "`" || text[end] === "`") return null;
|
|
2725
|
+
let ticks = 1;
|
|
2726
|
+
while (paragraph.runs.has(ticks)) ticks++;
|
|
2727
|
+
return "`".repeat(ticks);
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
// surfaces/quoted-kind-lift.ts
|
|
2731
|
+
var JSON_FENCE_LANGS3 = /* @__PURE__ */ new Set(["", "json", "jsonc", "json5"]);
|
|
2732
|
+
var QUOTE_PREFIX2 = /^ {0,3}>(?: ?>)* ?/;
|
|
2733
|
+
function quoteDepth(prefix) {
|
|
2734
|
+
return (prefix.match(/>/g) ?? []).length;
|
|
2735
|
+
}
|
|
2736
|
+
function prefixOfDepth(line, depth) {
|
|
2737
|
+
const match = new RegExp(`^ {0,3}>(?: ?>){${depth - 1}} ?`).exec(line);
|
|
2738
|
+
return match ? match[0] : null;
|
|
2739
|
+
}
|
|
2740
|
+
function bracketDelta(line) {
|
|
2741
|
+
let delta = 0;
|
|
2742
|
+
let inString = false;
|
|
2743
|
+
let escaped = false;
|
|
2744
|
+
for (const ch of line) {
|
|
2745
|
+
if (inString) {
|
|
2746
|
+
if (escaped) escaped = false;
|
|
2747
|
+
else if (ch === "\\") escaped = true;
|
|
2748
|
+
else if (ch === '"') inString = false;
|
|
2749
|
+
continue;
|
|
2750
|
+
}
|
|
2751
|
+
if (ch === '"') inString = true;
|
|
2752
|
+
else if (ch === "{" || ch === "[") delta++;
|
|
2753
|
+
else if (ch === "}" || ch === "]") delta--;
|
|
2754
|
+
}
|
|
2755
|
+
return delta;
|
|
2756
|
+
}
|
|
2757
|
+
var QuotedKindLift = class {
|
|
2758
|
+
mode = { kind: "none" };
|
|
2759
|
+
/** The current line's bytes not yet handed on (held while undecided). */
|
|
2760
|
+
held = "";
|
|
2761
|
+
/** The current line's bytes so far (handed on or not), as received. */
|
|
2762
|
+
line = "";
|
|
2763
|
+
/** Whether the rest of the current line passes straight through. */
|
|
2764
|
+
passing = false;
|
|
2765
|
+
/** Prefix stripped from the current line's handed-on bytes (passing lines). */
|
|
2766
|
+
stripping = 0;
|
|
2767
|
+
push(text) {
|
|
2768
|
+
let out = "";
|
|
2769
|
+
for (const ch of text) out += this.pushChar(ch);
|
|
2770
|
+
return out;
|
|
2771
|
+
}
|
|
2772
|
+
flush() {
|
|
2773
|
+
let out = "";
|
|
2774
|
+
if (this.mode.kind === "candidate") {
|
|
2775
|
+
out += this.mode.raw.join("\n") + (this.mode.raw.length ? "\n" : "");
|
|
2776
|
+
this.mode = { kind: "none" };
|
|
2777
|
+
}
|
|
2778
|
+
out += this.held;
|
|
2779
|
+
this.held = "";
|
|
2780
|
+
this.line = "";
|
|
2781
|
+
this.passing = false;
|
|
2782
|
+
this.stripping = 0;
|
|
2783
|
+
return out;
|
|
2784
|
+
}
|
|
2785
|
+
pushChar(ch) {
|
|
2786
|
+
if (ch === "\n") return this.endLine();
|
|
2787
|
+
this.line += ch;
|
|
2788
|
+
if (this.passing) {
|
|
2789
|
+
if (this.stripping > 0) {
|
|
2790
|
+
this.stripping--;
|
|
2791
|
+
return "";
|
|
2792
|
+
}
|
|
2793
|
+
return ch;
|
|
2794
|
+
}
|
|
2795
|
+
this.held += ch;
|
|
2796
|
+
return this.decideMidLine();
|
|
2797
|
+
}
|
|
2798
|
+
/** Try to decide the current line before it ends; returns bytes released. */
|
|
2799
|
+
decideMidLine() {
|
|
2800
|
+
const mode = this.mode;
|
|
2801
|
+
if (mode.kind === "source_fence") {
|
|
2802
|
+
return this.release(this.held, 0);
|
|
2803
|
+
}
|
|
2804
|
+
if (mode.kind === "lifted_fence" || mode.kind === "lifted_bare") {
|
|
2805
|
+
const prefix2 = prefixOfDepth(this.held, mode.depth);
|
|
2806
|
+
if (prefix2 !== null && (this.held.length > prefix2.length || /> $/.test(prefix2))) {
|
|
2807
|
+
const rest2 = this.held.slice(prefix2.length);
|
|
2808
|
+
if (mode.kind === "lifted_fence" && /^\s*[`~]/.test(rest2)) return "";
|
|
2809
|
+
return this.release(rest2, 0);
|
|
2810
|
+
}
|
|
2811
|
+
if (!/^ {0,3}(?:> ?)*$/.test(this.held)) {
|
|
2812
|
+
return this.release(this.held, 0);
|
|
2813
|
+
}
|
|
2814
|
+
return "";
|
|
2815
|
+
}
|
|
2816
|
+
if (mode.kind === "candidate") return "";
|
|
2817
|
+
if (!/^ {0,3}(?:>|$)/.test(this.held) && !/^ {0,3}$/.test(this.held)) {
|
|
2818
|
+
return this.release(this.held, 0);
|
|
2819
|
+
}
|
|
2820
|
+
const prefix = QUOTE_PREFIX2.exec(this.held)?.[0];
|
|
2821
|
+
if (!prefix) return "";
|
|
2822
|
+
const rest = this.held.slice(prefix.length);
|
|
2823
|
+
if (!rest.trim()) return "";
|
|
2824
|
+
const first = rest.trimStart()[0];
|
|
2825
|
+
if (first === "`" || first === "~") return "";
|
|
2826
|
+
if (first === "{" || first === "[") {
|
|
2827
|
+
if (jsonKindSignal(rest) === "kind") {
|
|
2828
|
+
this.mode = { kind: "lifted_bare", depth: quoteDepth(prefix), balance: 0 };
|
|
2829
|
+
return this.release(rest, 0);
|
|
2830
|
+
}
|
|
2831
|
+
return "";
|
|
2832
|
+
}
|
|
2833
|
+
return this.release(this.held, 0);
|
|
2834
|
+
}
|
|
2835
|
+
/** Hand on `bytes` and let the rest of the line pass straight through. */
|
|
2836
|
+
release(bytes, strip) {
|
|
2837
|
+
this.held = "";
|
|
2838
|
+
this.passing = true;
|
|
2839
|
+
this.stripping = strip;
|
|
2840
|
+
return bytes;
|
|
2841
|
+
}
|
|
2842
|
+
endLine() {
|
|
2843
|
+
const line = this.line;
|
|
2844
|
+
const held = this.held;
|
|
2845
|
+
this.line = "";
|
|
2846
|
+
this.held = "";
|
|
2847
|
+
this.passing = false;
|
|
2848
|
+
this.stripping = 0;
|
|
2849
|
+
const mode = this.mode;
|
|
2850
|
+
if (mode.kind === "source_fence") {
|
|
2851
|
+
const content = mode.depth > 0 ? line.replace(QUOTE_PREFIX2, "") : line;
|
|
2852
|
+
if (mode.reader.feed(content)) this.mode = { kind: "none" };
|
|
2853
|
+
return held + "\n";
|
|
2854
|
+
}
|
|
2855
|
+
if (mode.kind === "lifted_fence") {
|
|
2856
|
+
const prefix = prefixOfDepth(line, mode.depth);
|
|
2857
|
+
const content = prefix !== null ? line.slice(prefix.length) : line;
|
|
2858
|
+
if (mode.reader.feed(content)) this.mode = { kind: "none" };
|
|
2859
|
+
const sent = line.length - held.length;
|
|
2860
|
+
const contentSent = Math.max(0, sent - (prefix?.length ?? 0));
|
|
2861
|
+
return content.slice(contentSent) + "\n";
|
|
2862
|
+
}
|
|
2863
|
+
if (mode.kind === "lifted_bare") {
|
|
2864
|
+
const prefix = prefixOfDepth(line, mode.depth);
|
|
2865
|
+
const content = prefix !== null ? line.slice(prefix.length) : line;
|
|
2866
|
+
mode.balance += bracketDelta(content);
|
|
2867
|
+
if (mode.balance <= 0) this.mode = { kind: "none" };
|
|
2868
|
+
const sent = line.length - held.length;
|
|
2869
|
+
const contentSent = Math.max(0, sent - (prefix?.length ?? 0));
|
|
2870
|
+
return content.slice(contentSent) + "\n";
|
|
2871
|
+
}
|
|
2872
|
+
if (mode.kind === "candidate") {
|
|
2873
|
+
const prefix = prefixOfDepth(line, mode.depth);
|
|
2874
|
+
if (prefix === null) {
|
|
2875
|
+
const out = mode.raw.join("\n") + "\n";
|
|
2876
|
+
this.mode = { kind: "none" };
|
|
2877
|
+
return out + this.lineFromNone(line);
|
|
2878
|
+
}
|
|
2879
|
+
mode.raw.push(line);
|
|
2880
|
+
mode.stripped.push(line.slice(prefix.length));
|
|
2881
|
+
return this.settleCandidate(mode);
|
|
2882
|
+
}
|
|
2883
|
+
return this.lineFromNone(line, held);
|
|
2884
|
+
}
|
|
2885
|
+
/**
|
|
2886
|
+
* A complete line met with no region open. `held` is what was not handed
|
|
2887
|
+
* on yet: the whole line when it was held to the end, otherwise "" (it was
|
|
2888
|
+
* released mid-line as prose or as an unquoted line).
|
|
2889
|
+
*/
|
|
2890
|
+
lineFromNone(line, held = line) {
|
|
2891
|
+
const prefix = QUOTE_PREFIX2.exec(line)?.[0];
|
|
2892
|
+
if (held.length < line.length || !prefix) {
|
|
2893
|
+
if (!prefix) {
|
|
2894
|
+
const opener = fenceOpenerOf(line);
|
|
2895
|
+
if (opener) {
|
|
2896
|
+
this.mode = { kind: "source_fence", reader: new FenceReader(opener), depth: 0 };
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
return held + "\n";
|
|
2900
|
+
}
|
|
2901
|
+
const rest = line.slice(prefix.length);
|
|
2902
|
+
const depth = quoteDepth(prefix);
|
|
2903
|
+
const trimmed = rest.trimStart();
|
|
2904
|
+
if (trimmed.startsWith("`") || trimmed.startsWith("~")) {
|
|
2905
|
+
const opener = fenceOpenerOf(rest);
|
|
2906
|
+
if (opener && JSON_FENCE_LANGS3.has(opener.lang.toLowerCase())) {
|
|
2907
|
+
this.mode = { kind: "lifted_fence", reader: new FenceReader(opener), depth };
|
|
2908
|
+
return rest + "\n";
|
|
2909
|
+
}
|
|
2910
|
+
if (opener) {
|
|
2911
|
+
this.mode = { kind: "source_fence", reader: new FenceReader(opener), depth };
|
|
2912
|
+
}
|
|
2913
|
+
return held + "\n";
|
|
2914
|
+
}
|
|
2915
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
2916
|
+
const candidate = { kind: "candidate", depth, raw: [line], stripped: [rest] };
|
|
2917
|
+
this.mode = candidate;
|
|
2918
|
+
return this.settleCandidate(candidate);
|
|
2919
|
+
}
|
|
2920
|
+
return held + "\n";
|
|
2921
|
+
}
|
|
2922
|
+
/** A held quoted JSON start, re-judged after each complete line. */
|
|
2923
|
+
settleCandidate(mode) {
|
|
2924
|
+
const signal = jsonKindSignal(mode.stripped.join("\n"));
|
|
2925
|
+
if (signal === "undecided") return "";
|
|
2926
|
+
if (signal === "not_kind") {
|
|
2927
|
+
this.mode = { kind: "none" };
|
|
2928
|
+
return mode.raw.join("\n") + "\n";
|
|
2929
|
+
}
|
|
2930
|
+
const balance = mode.stripped.reduce((sum, l) => sum + bracketDelta(l), 0);
|
|
2931
|
+
this.mode = balance > 0 ? { kind: "lifted_bare", depth: mode.depth, balance } : { kind: "none" };
|
|
2932
|
+
return mode.stripped.join("\n") + "\n";
|
|
2933
|
+
}
|
|
2934
|
+
};
|
|
2935
|
+
function liftQuotedKindRegions(source) {
|
|
2936
|
+
if (!source.includes(">")) return source;
|
|
2937
|
+
const lift = new QuotedKindLift();
|
|
2938
|
+
return lift.push(source) + lift.flush();
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2941
|
+
// surfaces/markdown-escaped-kind.ts
|
|
2942
|
+
var TARGET = '"\\_\\_kind"';
|
|
2943
|
+
var KEY = '"__kind"';
|
|
2944
|
+
var HAS_ZERO_WIDTH2 = /[\u200B-\u200D\u2060\uFEFF\u00AD]/;
|
|
2945
|
+
var ZeroWidthKindKey = class {
|
|
2946
|
+
/** Bytes that may still become the key — held, never shown. */
|
|
2947
|
+
pending = "";
|
|
2948
|
+
push(text) {
|
|
2949
|
+
let out = "";
|
|
2950
|
+
for (const ch of text) out += this.pushChar(ch);
|
|
2951
|
+
return out;
|
|
2952
|
+
}
|
|
2953
|
+
flush() {
|
|
2954
|
+
const out = this.pending;
|
|
2955
|
+
this.pending = "";
|
|
2956
|
+
return out;
|
|
2957
|
+
}
|
|
2958
|
+
pushChar(ch) {
|
|
2959
|
+
let candidate = this.pending + ch;
|
|
2960
|
+
let out = "";
|
|
2961
|
+
while (candidate && !(candidate.startsWith('"') && KEY.startsWith(withoutZeroWidth(candidate)))) {
|
|
2962
|
+
const next = candidate.indexOf('"', 1);
|
|
2963
|
+
if (next < 0) {
|
|
2964
|
+
out += candidate;
|
|
2965
|
+
candidate = "";
|
|
2966
|
+
} else {
|
|
2967
|
+
out += candidate.slice(0, next);
|
|
2968
|
+
candidate = candidate.slice(next);
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
if (withoutZeroWidth(candidate) === KEY) {
|
|
2972
|
+
this.pending = "";
|
|
2973
|
+
return out + KEY;
|
|
2974
|
+
}
|
|
2975
|
+
this.pending = candidate;
|
|
2976
|
+
return out;
|
|
2977
|
+
}
|
|
2978
|
+
};
|
|
2979
|
+
var MarkdownEscapedKindJson = class {
|
|
2980
|
+
/** The zero-width key spelling is read first (round 8) — one transform, both hosts. */
|
|
2981
|
+
zeroWidth = new ZeroWidthKindKey();
|
|
2982
|
+
/** Bytes that may still become the escaped key — held, never shown. */
|
|
2983
|
+
pending = "";
|
|
2984
|
+
/** Inside the object holding a rewritten key: bracket depth (0 = outside). */
|
|
2985
|
+
depth = 0;
|
|
2986
|
+
inString = false;
|
|
2987
|
+
/** A held backslash inside the object (its fate depends on the next char). */
|
|
2988
|
+
escape = false;
|
|
2989
|
+
push(text) {
|
|
2990
|
+
return this.pushUnescape(this.zeroWidth.push(text));
|
|
2991
|
+
}
|
|
2992
|
+
pushUnescape(text) {
|
|
2993
|
+
let out = "";
|
|
2994
|
+
for (const ch of text) out += this.pushChar(ch);
|
|
2995
|
+
return out;
|
|
2996
|
+
}
|
|
2997
|
+
flush() {
|
|
2998
|
+
const held = this.pushUnescape(this.zeroWidth.flush());
|
|
2999
|
+
const out = held + this.pending + (this.escape ? "\\" : "");
|
|
3000
|
+
this.pending = "";
|
|
3001
|
+
this.escape = false;
|
|
3002
|
+
this.depth = 0;
|
|
3003
|
+
this.inString = false;
|
|
3004
|
+
return out;
|
|
3005
|
+
}
|
|
3006
|
+
pushChar(ch) {
|
|
3007
|
+
if (this.depth > 0) return this.insideObject(ch);
|
|
3008
|
+
let candidate = this.pending + ch;
|
|
3009
|
+
let out = "";
|
|
3010
|
+
while (candidate && !(candidate.startsWith('"') && TARGET.startsWith(withoutZeroWidth(candidate)))) {
|
|
3011
|
+
const next = candidate.indexOf('"', 1);
|
|
3012
|
+
if (next < 0) {
|
|
3013
|
+
out += candidate;
|
|
3014
|
+
candidate = "";
|
|
3015
|
+
} else {
|
|
3016
|
+
out += candidate.slice(0, next);
|
|
3017
|
+
candidate = candidate.slice(next);
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
if (withoutZeroWidth(candidate) === TARGET) {
|
|
3021
|
+
this.pending = "";
|
|
3022
|
+
this.depth = 1;
|
|
3023
|
+
this.inString = false;
|
|
3024
|
+
return out + KEY;
|
|
3025
|
+
}
|
|
3026
|
+
this.pending = candidate;
|
|
3027
|
+
return out;
|
|
3028
|
+
}
|
|
3029
|
+
insideObject(ch) {
|
|
3030
|
+
if (this.escape) {
|
|
3031
|
+
this.escape = false;
|
|
3032
|
+
return ch === "_" ? "_" : `\\${ch}`;
|
|
3033
|
+
}
|
|
3034
|
+
if (ch === "\\") {
|
|
3035
|
+
this.escape = true;
|
|
3036
|
+
return "";
|
|
3037
|
+
}
|
|
3038
|
+
if (this.inString) {
|
|
3039
|
+
if (ch === '"') this.inString = false;
|
|
3040
|
+
return ch;
|
|
3041
|
+
}
|
|
3042
|
+
if (ch === '"') this.inString = true;
|
|
3043
|
+
else if (ch === "{" || ch === "[") this.depth += 1;
|
|
3044
|
+
else if (ch === "}" || ch === "]") this.depth -= 1;
|
|
3045
|
+
return ch;
|
|
3046
|
+
}
|
|
3047
|
+
};
|
|
3048
|
+
function unescapeMarkdownKindJson(source) {
|
|
3049
|
+
if (!source.includes(TARGET) && !HAS_ZERO_WIDTH2.test(source)) return source;
|
|
3050
|
+
const transform = new MarkdownEscapedKindJson();
|
|
3051
|
+
return transform.push(source) + transform.flush();
|
|
3052
|
+
}
|
|
3053
|
+
function decodeDoubleEncodedKindText(source) {
|
|
3054
|
+
const trimmed = source.trim();
|
|
3055
|
+
if (!trimmed.startsWith('"') || !trimmed.endsWith('"') || !trimmed.includes('\\"__kind\\"')) return source;
|
|
3056
|
+
try {
|
|
3057
|
+
const inner = JSON.parse(trimmed);
|
|
3058
|
+
return typeof inner === "string" && isKindJsonText(inner.trim()) ? inner.trim() : source;
|
|
3059
|
+
} catch {
|
|
3060
|
+
return source;
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
|
|
3064
|
+
// surfaces/kind-image-alt.ts
|
|
3065
|
+
var HEAD_LIMIT = 2e3;
|
|
3066
|
+
var KindImageAltUnwrap = class {
|
|
3067
|
+
mode = "scan";
|
|
3068
|
+
/** Held: `!` / `. */
|
|
3069
|
+
held = "";
|
|
3070
|
+
depth = 0;
|
|
3071
|
+
inString = false;
|
|
3072
|
+
escape = false;
|
|
3073
|
+
push(text) {
|
|
3074
|
+
let out = "";
|
|
3075
|
+
for (const ch of text) out += this.pushChar(ch);
|
|
3076
|
+
return out;
|
|
3077
|
+
}
|
|
3078
|
+
flush() {
|
|
3079
|
+
const out = this.mode === "head" ? `![${this.held}` : this.held;
|
|
3080
|
+
this.mode = "scan";
|
|
3081
|
+
this.held = "";
|
|
3082
|
+
this.depth = 0;
|
|
3083
|
+
this.inString = false;
|
|
3084
|
+
this.escape = false;
|
|
3085
|
+
return out;
|
|
3086
|
+
}
|
|
3087
|
+
/** Track JSON structure; returns true when the object just closed. */
|
|
3088
|
+
track(ch) {
|
|
3089
|
+
if (this.escape) {
|
|
3090
|
+
this.escape = false;
|
|
3091
|
+
return false;
|
|
3092
|
+
}
|
|
3093
|
+
if (this.inString) {
|
|
3094
|
+
if (ch === "\\") this.escape = true;
|
|
3095
|
+
else if (ch === '"') this.inString = false;
|
|
3096
|
+
return false;
|
|
3097
|
+
}
|
|
3098
|
+
if (ch === '"') this.inString = true;
|
|
3099
|
+
else if (ch === "{" || ch === "[") this.depth += 1;
|
|
3100
|
+
else if (ch === "}" || ch === "]") {
|
|
3101
|
+
this.depth -= 1;
|
|
3102
|
+
return this.depth === 0;
|
|
3103
|
+
}
|
|
3104
|
+
return false;
|
|
3105
|
+
}
|
|
3106
|
+
pushChar(ch) {
|
|
3107
|
+
switch (this.mode) {
|
|
3108
|
+
case "scan": {
|
|
3109
|
+
if (this.held === "" && ch === "!") {
|
|
3110
|
+
this.held = "!";
|
|
3111
|
+
return "";
|
|
3112
|
+
}
|
|
3113
|
+
if (this.held === "!" && ch === "[") {
|
|
3114
|
+
this.held = "![";
|
|
3115
|
+
return "";
|
|
3116
|
+
}
|
|
3117
|
+
if (this.held === "![" && ch === "{") {
|
|
3118
|
+
this.mode = "head";
|
|
3119
|
+
this.held = "{";
|
|
3120
|
+
this.depth = 1;
|
|
3121
|
+
this.inString = false;
|
|
3122
|
+
this.escape = false;
|
|
3123
|
+
return "";
|
|
3124
|
+
}
|
|
3125
|
+
if (this.held === "") return ch;
|
|
3126
|
+
const out = this.held;
|
|
3127
|
+
this.held = "";
|
|
3128
|
+
return out + this.pushChar(ch);
|
|
3129
|
+
}
|
|
3130
|
+
case "head": {
|
|
3131
|
+
this.held += ch;
|
|
3132
|
+
const closed = this.track(ch);
|
|
3133
|
+
const signal = jsonKindSignal(this.held);
|
|
3134
|
+
if (signal === "kind") {
|
|
3135
|
+
const out = this.held;
|
|
3136
|
+
this.held = "";
|
|
3137
|
+
if (closed) {
|
|
3138
|
+
this.mode = "tail";
|
|
3139
|
+
return out;
|
|
3140
|
+
}
|
|
3141
|
+
this.mode = "body";
|
|
3142
|
+
return out;
|
|
3143
|
+
}
|
|
3144
|
+
if (signal === "not_kind" || closed || this.held.length > HEAD_LIMIT) {
|
|
3145
|
+
const out = `![${this.held}`;
|
|
3146
|
+
this.held = "";
|
|
3147
|
+
this.mode = "scan";
|
|
3148
|
+
return out;
|
|
3149
|
+
}
|
|
3150
|
+
return "";
|
|
3151
|
+
}
|
|
3152
|
+
case "body": {
|
|
3153
|
+
if (this.track(ch)) this.mode = "tail";
|
|
3154
|
+
return ch;
|
|
3155
|
+
}
|
|
3156
|
+
case "tail": {
|
|
3157
|
+
this.held += ch;
|
|
3158
|
+
if (/^\]\([^)\n]*\)$/.test(this.held)) {
|
|
3159
|
+
this.held = "";
|
|
3160
|
+
this.mode = "scan";
|
|
3161
|
+
return "";
|
|
3162
|
+
}
|
|
3163
|
+
if (/^\](?:\([^)\n]*)?$/.test(this.held)) return "";
|
|
3164
|
+
const out = this.held;
|
|
3165
|
+
this.held = "";
|
|
3166
|
+
this.mode = "scan";
|
|
3167
|
+
return out;
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
};
|
|
3172
|
+
function unwrapKindImageAlt(source) {
|
|
3173
|
+
if (!source.includes("![{")) return source;
|
|
3174
|
+
const transform = new KindImageAltUnwrap();
|
|
3175
|
+
return transform.push(source) + transform.flush();
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3178
|
+
// surfaces/kind-text-label.ts
|
|
3179
|
+
function clip(text, max) {
|
|
3180
|
+
return text.length > max ? `${text.slice(0, Math.max(0, max - 1)).trimEnd()}\u2026` : text;
|
|
3181
|
+
}
|
|
3182
|
+
function wholeKindLabel(text) {
|
|
3183
|
+
let body = text.trim();
|
|
3184
|
+
const fence = body.match(/^(?:`{3,}|~{3,})[a-zA-Z0-9]*\s*\n([\s\S]*?)\n?(?:`{3,}|~{3,})$/);
|
|
3185
|
+
if (fence) body = fence[1].trim();
|
|
3186
|
+
if (!body.startsWith("{")) return null;
|
|
3187
|
+
try {
|
|
3188
|
+
const parsed = JSON.parse(body);
|
|
3189
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
3190
|
+
const record = parsed;
|
|
3191
|
+
if (typeof record.__kind !== "string" || !record.__kind.trim()) return null;
|
|
3192
|
+
const name = humanizeKind(record.__kind.trim());
|
|
3193
|
+
const title = deriveInstanceTitle(record);
|
|
3194
|
+
return title ? `${name} \xB7 ${title}` : name;
|
|
3195
|
+
} catch {
|
|
3196
|
+
return null;
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
function firstReadableLine(markdown) {
|
|
3200
|
+
for (const raw of markdown.split("\n")) {
|
|
3201
|
+
const line = raw.replace(/^\s*(?:#{1,6}\s+|>\s*|[-*+]\s+|\d+[.)]\s+)/, "").replace(/<\/?[A-Za-z][\w:-]*(\s[^<>]*)?\/?>/g, "").replace(/[*_`]+/g, "").replace(/\s+/g, " ").trim();
|
|
3202
|
+
if (line) return line;
|
|
3203
|
+
}
|
|
3204
|
+
return "";
|
|
3205
|
+
}
|
|
3206
|
+
function kindTextLabel(raw, max = 90) {
|
|
3207
|
+
if (!raw) return "";
|
|
3208
|
+
const text = normalizeKindSpellings(raw);
|
|
3209
|
+
if (!hasKindKey(text)) return clip(text.replace(/\s+/g, " ").trim(), max);
|
|
3210
|
+
const whole = wholeKindLabel(text);
|
|
3211
|
+
if (whole) return clip(whole, max);
|
|
3212
|
+
const line = firstReadableLine(kindTextToMarkdown(text));
|
|
3213
|
+
if (line) return clip(line, max);
|
|
3214
|
+
const slug = firstKindSlug(text);
|
|
3215
|
+
return clip(slug ? humanizeKind(slug) : "Structured output", max);
|
|
3216
|
+
}
|
|
3217
|
+
function conversationTitleText(title) {
|
|
3218
|
+
if (title == null) return null;
|
|
3219
|
+
return hasJsonKindKey(title) ? kindTextLabel(title, 200) : title;
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
// surfaces/legacy-quiz-envelope.ts
|
|
3223
|
+
function isRecord3(value) {
|
|
3224
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3225
|
+
}
|
|
3226
|
+
function canonicalizeCompletedLegacyQuizEnvelope(envelope, sourceText) {
|
|
3227
|
+
if (envelope.root.kind !== "quiz_set" || envelope.root.status !== "complete") {
|
|
3228
|
+
return envelope;
|
|
3229
|
+
}
|
|
3230
|
+
let source;
|
|
3231
|
+
try {
|
|
3232
|
+
source = JSON.parse(sourceText);
|
|
3233
|
+
} catch {
|
|
3234
|
+
return envelope;
|
|
3235
|
+
}
|
|
3236
|
+
if (!isRecord3(source)) return envelope;
|
|
3237
|
+
const hasLegacyAliases = typeof source.quiz_title === "string" || typeof source.quizTitle === "string" || Array.isArray(source.multiple_choice) || Array.isArray(source.multipleChoice);
|
|
3238
|
+
if (source.__kind !== void 0 || !hasLegacyAliases) return envelope;
|
|
3239
|
+
const title = typeof source.quiz_title === "string" ? source.quiz_title : typeof source.quizTitle === "string" ? source.quizTitle : null;
|
|
3240
|
+
const sourceQuestions = source.questions ?? source.multiple_choice ?? source.multipleChoice;
|
|
3241
|
+
if (title === null || !Array.isArray(sourceQuestions)) return envelope;
|
|
3242
|
+
const questions = sourceQuestions.flatMap((candidate) => {
|
|
3243
|
+
if (!isRecord3(candidate)) return [];
|
|
3244
|
+
const { __kind: _legacyKind, ...legacyQuestion } = candidate;
|
|
3245
|
+
const options = Array.isArray(candidate.options) ? candidate.options : [];
|
|
3246
|
+
const answer = candidate.correct_answer ?? candidate.correctAnswer ?? "";
|
|
3247
|
+
const correctAnswer = typeof answer === "number" && Number.isInteger(answer) && answer >= 0 && answer < options.length ? options[answer] : answer;
|
|
3248
|
+
return [{
|
|
3249
|
+
...legacyQuestion,
|
|
3250
|
+
type: typeof candidate.type === "string" ? candidate.type : "multiple_choice",
|
|
3251
|
+
question: typeof candidate.question === "string" ? candidate.question : "",
|
|
3252
|
+
options,
|
|
3253
|
+
correct_answer: typeof correctAnswer === "string" ? correctAnswer : String(correctAnswer),
|
|
3254
|
+
explanation: typeof candidate.explanation === "string" ? candidate.explanation : null
|
|
3255
|
+
}];
|
|
3256
|
+
});
|
|
3257
|
+
return {
|
|
3258
|
+
...envelope,
|
|
3259
|
+
root: {
|
|
3260
|
+
...envelope.root,
|
|
3261
|
+
value: { ...envelope.root.value, title, questions }
|
|
3262
|
+
}
|
|
3263
|
+
};
|
|
3264
|
+
}
|
|
3265
|
+
|
|
3266
|
+
// core/ir-types.ts
|
|
3267
|
+
function isEmptyResidue(residue) {
|
|
3268
|
+
return (residue.extra === null || Object.keys(residue.extra).length === 0) && (residue.optionalMissing === null || residue.optionalMissing.length === 0) && (residue.notices === null || residue.notices.length === 0);
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
// core/envelope-value.ts
|
|
3272
|
+
function isRecord4(value) {
|
|
3273
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3274
|
+
}
|
|
3275
|
+
function reconstructRegionValue(envelope) {
|
|
3276
|
+
const cloned = structuredClone(envelope.root.value);
|
|
3277
|
+
const applyExtras = (target, extras) => {
|
|
3278
|
+
if (!extras) return;
|
|
3279
|
+
for (const [key, value] of Object.entries(extras)) {
|
|
3280
|
+
target[key] = structuredClone(value);
|
|
3281
|
+
}
|
|
3282
|
+
};
|
|
3283
|
+
applyExtras(cloned, envelope.root.residue?.extra);
|
|
3284
|
+
for (const [pathKey, meta] of Object.entries(envelope.nodeIndex ?? {})) {
|
|
3285
|
+
const residue = meta.residue;
|
|
3286
|
+
if (!residue || isEmptyResidue(residue) || !residue.extra) continue;
|
|
3287
|
+
const segments = pathKey.split(".");
|
|
3288
|
+
let cursor = cloned;
|
|
3289
|
+
for (const segment of segments) {
|
|
3290
|
+
if (Array.isArray(cursor)) {
|
|
3291
|
+
cursor = cursor[Number(segment)];
|
|
3292
|
+
} else if (isRecord4(cursor)) {
|
|
3293
|
+
cursor = cursor[segment];
|
|
3294
|
+
} else {
|
|
3295
|
+
cursor = void 0;
|
|
3296
|
+
break;
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
if (isRecord4(cursor)) {
|
|
3300
|
+
applyExtras(cursor, residue.extra);
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
return cloned;
|
|
3304
|
+
}
|
|
3305
|
+
function stripKindDeep(value) {
|
|
3306
|
+
if (Array.isArray(value)) {
|
|
3307
|
+
return value.map(stripKindDeep);
|
|
3308
|
+
}
|
|
3309
|
+
if (isRecord4(value)) {
|
|
3310
|
+
const out = {};
|
|
3311
|
+
for (const [key, child] of Object.entries(value)) {
|
|
3312
|
+
if (key === KIND_KEY2) continue;
|
|
3313
|
+
out[key] = stripKindDeep(child);
|
|
3314
|
+
}
|
|
3315
|
+
return out;
|
|
3316
|
+
}
|
|
3317
|
+
return value;
|
|
3318
|
+
}
|
|
3319
|
+
|
|
3320
|
+
// surfaces/envelope-parity.ts
|
|
3321
|
+
function isRecord5(value) {
|
|
3322
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3323
|
+
}
|
|
3324
|
+
function deepEqual(left, right) {
|
|
3325
|
+
if (Object.is(left, right)) return true;
|
|
3326
|
+
if (Array.isArray(left) && Array.isArray(right)) {
|
|
3327
|
+
if (left.length !== right.length) return false;
|
|
3328
|
+
for (let i = 0; i < left.length; i++) {
|
|
3329
|
+
if (!deepEqual(left[i], right[i])) return false;
|
|
3330
|
+
}
|
|
3331
|
+
return true;
|
|
3332
|
+
}
|
|
3333
|
+
if (isRecord5(left) && isRecord5(right)) {
|
|
3334
|
+
const leftKeys = Object.keys(left);
|
|
3335
|
+
const rightKeys = Object.keys(right);
|
|
3336
|
+
if (leftKeys.length !== rightKeys.length) return false;
|
|
3337
|
+
for (const key of leftKeys) {
|
|
3338
|
+
if (!(key in right)) return false;
|
|
3339
|
+
if (!deepEqual(left[key], right[key])) return false;
|
|
3340
|
+
}
|
|
3341
|
+
return true;
|
|
3342
|
+
}
|
|
3343
|
+
return false;
|
|
3344
|
+
}
|
|
3345
|
+
function envelopeMatchesParsedSource(envelope, parsedSource) {
|
|
3346
|
+
if (!isRecord5(parsedSource)) return false;
|
|
3347
|
+
return deepEqual(stripKindDeep(reconstructRegionValue(envelope)), stripKindDeep(parsedSource));
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
// surfaces/decision-answers-ids.ts
|
|
3351
|
+
var DECISION_ANSWERS_KIND = "decision_answers";
|
|
3352
|
+
var DECISION_ANSWERS_BLOCK_TYPE = "decision_answers";
|
|
3353
|
+
|
|
3354
|
+
exports.ALL_KIND_SPELLINGS = ALL_KIND_SPELLINGS;
|
|
3355
|
+
exports.BARE_REGION_GRAMMAR_BUDGET = BARE_REGION_GRAMMAR_BUDGET;
|
|
3356
|
+
exports.DECISION_ANSWERS_BLOCK_TYPE = DECISION_ANSWERS_BLOCK_TYPE;
|
|
3357
|
+
exports.DECISION_ANSWERS_KIND = DECISION_ANSWERS_KIND;
|
|
3358
|
+
exports.INSTANCE_TITLE_KEYS = INSTANCE_TITLE_KEYS;
|
|
3359
|
+
exports.JSON_KIND_SPELLINGS = JSON_KIND_SPELLINGS;
|
|
3360
|
+
exports.KindImageAltUnwrap = KindImageAltUnwrap;
|
|
3361
|
+
exports.MarkdownEscapedKindJson = MarkdownEscapedKindJson;
|
|
3362
|
+
exports.QuotedKindLift = QuotedKindLift;
|
|
3363
|
+
exports.UNREADABLE_KIND_NOTE = UNREADABLE_KIND_NOTE;
|
|
3364
|
+
exports.ZeroWidthKindKey = ZeroWidthKindKey;
|
|
3365
|
+
exports.additionalDetailsSection = additionalDetailsSection;
|
|
3366
|
+
exports.balancedEnds = balancedEnds;
|
|
3367
|
+
exports.bareRegionBreaksIntoProse = bareRegionBreaksIntoProse;
|
|
3368
|
+
exports.canonicalizeCompletedLegacyQuizEnvelope = canonicalizeCompletedLegacyQuizEnvelope;
|
|
3369
|
+
exports.catalogProseText = catalogProseText;
|
|
3370
|
+
exports.closedJson = closedJson;
|
|
3371
|
+
exports.collectExtras = collectExtras;
|
|
3372
|
+
exports.conversationTitleText = conversationTitleText;
|
|
3373
|
+
exports.decodeDoubleEncodedKindText = decodeDoubleEncodedKindText;
|
|
3374
|
+
exports.deriveInstanceTitle = deriveInstanceTitle;
|
|
3375
|
+
exports.endsInPartialKindKey = endsInPartialKindKey;
|
|
3376
|
+
exports.envelopeMatchesParsedSource = envelopeMatchesParsedSource;
|
|
3377
|
+
exports.extrasList = extrasList;
|
|
3378
|
+
exports.findBrokenKindJsonRegions = findBrokenKindJsonRegions;
|
|
3379
|
+
exports.findEmbeddedKindJsonRegions = findEmbeddedKindJsonRegions;
|
|
3380
|
+
exports.findKindCarryingJsonValues = findKindCarryingJsonValues;
|
|
3381
|
+
exports.firstKindSlug = firstKindSlug;
|
|
3382
|
+
exports.formatInlineValue = formatInlineValue;
|
|
3383
|
+
exports.frontMatterEnd = frontMatterEnd;
|
|
3384
|
+
exports.genericKindMarkdown = genericKindMarkdown;
|
|
3385
|
+
exports.hasDetectionOnlyKindKey = hasDetectionOnlyKindKey;
|
|
3386
|
+
exports.hasExoticKindKey = hasExoticKindKey;
|
|
3387
|
+
exports.hasJsonKindKey = hasJsonKindKey;
|
|
3388
|
+
exports.hasKindKey = hasKindKey;
|
|
3389
|
+
exports.hasKindKeyAnySpelling = hasKindKeyAnySpelling;
|
|
3390
|
+
exports.hasPythonKindKey = hasPythonKindKey;
|
|
3391
|
+
exports.humanizeKind = humanizeKind;
|
|
3392
|
+
exports.inlineKindText = inlineKindText;
|
|
3393
|
+
exports.isJson5Language = isJson5Language;
|
|
3394
|
+
exports.isJsonFenceLanguage = isJsonFenceLanguage;
|
|
3395
|
+
exports.isKindJsonText = isKindJsonText;
|
|
3396
|
+
exports.isKindSlug = isKindSlug;
|
|
3397
|
+
exports.isQuotedSourceXmlBlock = isQuotedSourceXmlBlock;
|
|
3398
|
+
exports.isRecordValue = isRecordValue;
|
|
3399
|
+
exports.joinBlocks = joinBlocks;
|
|
3400
|
+
exports.json5AsJson = json5AsJson;
|
|
3401
|
+
exports.jsonKindSignal = jsonKindSignal;
|
|
3402
|
+
exports.kindGrammar = kindGrammar;
|
|
3403
|
+
exports.kindObjectProseBreak = kindObjectProseBreak;
|
|
3404
|
+
exports.kindOneLine = kindOneLine;
|
|
3405
|
+
exports.kindTextLabel = kindTextLabel;
|
|
3406
|
+
exports.kindTextPreview = kindTextPreview;
|
|
3407
|
+
exports.kindTextToMarkdown = kindTextToMarkdown;
|
|
3408
|
+
exports.kindTitleKeyFromMetadata = kindTitleKeyFromMetadata;
|
|
3409
|
+
exports.kindValueToMarkdown = kindValueToMarkdown;
|
|
3410
|
+
exports.liftQuotedKindRegions = liftQuotedKindRegions;
|
|
3411
|
+
exports.markdownCarriesKind = markdownCarriesKind;
|
|
3412
|
+
exports.mayHoldKindKey = mayHoldKindKey;
|
|
3413
|
+
exports.nonJsonKindsAsCode = nonJsonKindsAsCode;
|
|
3414
|
+
exports.normalizeKindSpellings = normalizeKindSpellings;
|
|
3415
|
+
exports.normalizeRecoveredContainerPiece = normalizeRecoveredContainerPiece;
|
|
3416
|
+
exports.normalizeRecoveredProsePiece = normalizeRecoveredProsePiece;
|
|
3417
|
+
exports.plainFieldLabel = plainFieldLabel;
|
|
3418
|
+
exports.plainValueMarkdown = plainValueMarkdown;
|
|
3419
|
+
exports.pythonBalancedEnd = pythonBalancedEnd;
|
|
3420
|
+
exports.pythonReprAsJson = pythonReprAsJson;
|
|
3421
|
+
exports.quotedSourceRanges = quotedSourceRanges;
|
|
3422
|
+
exports.regionJson = regionJson;
|
|
3423
|
+
exports.registerKindValueMarkdown = registerKindValueMarkdown;
|
|
3424
|
+
exports.rootKindSlug = rootKindSlug;
|
|
3425
|
+
exports.scanKindSpellingRegions = scanKindSpellingRegions;
|
|
3426
|
+
exports.spelledKindsAsOneLine = spelledKindsAsOneLine;
|
|
3427
|
+
exports.splitAroundEmbeddedKindJson = splitAroundEmbeddedKindJson;
|
|
3428
|
+
exports.textCarriesKind = textCarriesKind;
|
|
3429
|
+
exports.unescapeMarkdownKindJson = unescapeMarkdownKindJson;
|
|
3430
|
+
exports.unfinishedKindLabel = unfinishedKindLabel;
|
|
3431
|
+
exports.unreadableKindsAsNote = unreadableKindsAsNote;
|
|
3432
|
+
exports.unwrapKindImageAlt = unwrapKindImageAlt;
|
|
3433
|
+
exports.valueCarriesKind = valueCarriesKind;
|
|
3434
|
+
exports.withoutLeadingJsonComments = withoutLeadingJsonComments;
|
|
3435
|
+
exports.withoutZeroWidth = withoutZeroWidth;
|
|
3436
|
+
//# sourceMappingURL=surfaces.cjs.map
|
|
3437
|
+
//# sourceMappingURL=surfaces.cjs.map
|