@ai-matrx/kit 0.12.1 → 0.13.2
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 +31 -0
- package/README.md +3 -0
- package/dist/content-transfer.cjs +1356 -0
- package/dist/content-transfer.cjs.map +1 -0
- package/dist/content-transfer.d.cts +356 -0
- package/dist/content-transfer.d.ts +356 -0
- package/dist/content-transfer.js +1323 -0
- package/dist/content-transfer.js.map +1 -0
- package/dist/index.cjs +1207 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +356 -1
- package/dist/index.d.ts +356 -1
- package/dist/index.js +1207 -3
- package/dist/index.js.map +1 -1
- package/package.json +14 -2
|
@@ -0,0 +1,1356 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/content-transfer.ts
|
|
31
|
+
var content_transfer_exports = {};
|
|
32
|
+
__export(content_transfer_exports, {
|
|
33
|
+
ContentTransferError: () => ContentTransferError,
|
|
34
|
+
applySectionSelection: () => applySectionSelection,
|
|
35
|
+
applyTransferLimits: () => applyTransferLimits,
|
|
36
|
+
buildAgentPayload: () => buildAgentPayload,
|
|
37
|
+
capture: () => capture,
|
|
38
|
+
collectAllMatching: () => collectAllMatching,
|
|
39
|
+
createBrowserTransport: () => createBrowserTransport,
|
|
40
|
+
createDraft: () => createDraft,
|
|
41
|
+
createSurfaceTransferHandle: () => createSurfaceTransferHandle,
|
|
42
|
+
directSource: () => directSource,
|
|
43
|
+
editDraft: () => editDraft,
|
|
44
|
+
fenceJsonBlock: () => fenceJsonBlock,
|
|
45
|
+
normalizeTransferJson: () => normalizeTransferJson,
|
|
46
|
+
projectSnapshot: () => projectSnapshot,
|
|
47
|
+
readTransferCell: () => readTransferCell,
|
|
48
|
+
reduceJson: () => reduceJson,
|
|
49
|
+
refreshDraft: () => refreshDraft,
|
|
50
|
+
renderMarkdownHtml: () => renderMarkdownHtml,
|
|
51
|
+
resetDraft: () => resetDraft,
|
|
52
|
+
resolveTransferPreferences: () => resolveTransferPreferences,
|
|
53
|
+
resolveTransferRegistry: () => resolveTransferRegistry,
|
|
54
|
+
sealTransferArtifact: () => sealTransferArtifact,
|
|
55
|
+
serialize: () => serialize,
|
|
56
|
+
serializeMarkdownRich: () => serializeMarkdownRich,
|
|
57
|
+
sourceChanged: () => sourceChanged,
|
|
58
|
+
transformRows: () => transformRows,
|
|
59
|
+
undoDraft: () => undoDraft
|
|
60
|
+
});
|
|
61
|
+
module.exports = __toCommonJS(content_transfer_exports);
|
|
62
|
+
|
|
63
|
+
// src/json-format/json-value.ts
|
|
64
|
+
function isJsonObject(value) {
|
|
65
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
66
|
+
}
|
|
67
|
+
function isJsonArray(value) {
|
|
68
|
+
return Array.isArray(value);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/json-format/format.ts
|
|
72
|
+
var DEFAULT_JSON_INDENT = 2;
|
|
73
|
+
var DEFAULT_JSON_WIDTH = 100;
|
|
74
|
+
function orderedKeys(obj, sortKeys) {
|
|
75
|
+
const keys = Object.keys(obj).filter((k) => obj[k] !== void 0);
|
|
76
|
+
return sortKeys ? [...keys].sort((a, b) => a.localeCompare(b)) : keys;
|
|
77
|
+
}
|
|
78
|
+
function writeScalar(value) {
|
|
79
|
+
return JSON.stringify(value) ?? "null";
|
|
80
|
+
}
|
|
81
|
+
function flatten(value, cfg) {
|
|
82
|
+
if (isJsonArray(value)) {
|
|
83
|
+
if (value.length === 0) return "[]";
|
|
84
|
+
const parts = value.map((v) => flatten(v ?? null, cfg));
|
|
85
|
+
return cfg.spaced ? `[${parts.join(", ")}]` : `[${parts.join(",")}]`;
|
|
86
|
+
}
|
|
87
|
+
if (isJsonObject(value)) {
|
|
88
|
+
const keys = orderedKeys(value, cfg.sortKeys);
|
|
89
|
+
if (keys.length === 0) return "{}";
|
|
90
|
+
const colon = cfg.spaced ? ": " : ":";
|
|
91
|
+
const parts = keys.map(
|
|
92
|
+
(k) => `${JSON.stringify(k)}${colon}${flatten(value[k] ?? null, cfg)}`
|
|
93
|
+
);
|
|
94
|
+
return cfg.spaced ? `{ ${parts.join(", ")} }` : `{${parts.join(",")}}`;
|
|
95
|
+
}
|
|
96
|
+
return writeScalar(value);
|
|
97
|
+
}
|
|
98
|
+
function layoutEntries(entries, pad, cfg) {
|
|
99
|
+
if (!cfg.pack) return entries.map((e) => pad + e);
|
|
100
|
+
const lines = [];
|
|
101
|
+
let current = "";
|
|
102
|
+
for (let i = 0; i < entries.length; i++) {
|
|
103
|
+
const entry = entries[i] ?? "";
|
|
104
|
+
const isLast = i === entries.length - 1;
|
|
105
|
+
const piece = isLast ? entry : `${entry},`;
|
|
106
|
+
if (entry.includes("\n")) {
|
|
107
|
+
if (current !== "") {
|
|
108
|
+
lines.push(pad + current);
|
|
109
|
+
current = "";
|
|
110
|
+
}
|
|
111
|
+
lines.push(pad + piece);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (current === "") {
|
|
115
|
+
current = piece;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const merged = `${current} ${piece}`;
|
|
119
|
+
if (pad.length + merged.length <= cfg.width) {
|
|
120
|
+
current = merged;
|
|
121
|
+
} else {
|
|
122
|
+
lines.push(pad + current);
|
|
123
|
+
current = piece;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (current !== "") lines.push(pad + current);
|
|
127
|
+
return lines;
|
|
128
|
+
}
|
|
129
|
+
function renderNode(value, level, used, cfg) {
|
|
130
|
+
const isContainer = isJsonArray(value) || isJsonObject(value);
|
|
131
|
+
if (!isContainer) return writeScalar(value);
|
|
132
|
+
const flat = flatten(value, cfg);
|
|
133
|
+
if (flat === "[]" || flat === "{}") return flat;
|
|
134
|
+
if (cfg.width >= 0 && used + flat.length <= cfg.width) return flat;
|
|
135
|
+
const pad = " ".repeat((level + 1) * cfg.indent);
|
|
136
|
+
const closePad = " ".repeat(level * cfg.indent);
|
|
137
|
+
if (isJsonArray(value)) {
|
|
138
|
+
const entries2 = value.map(
|
|
139
|
+
(v) => renderNode(v ?? null, level + 1, pad.length, cfg)
|
|
140
|
+
);
|
|
141
|
+
const body2 = layoutEntries(entries2, pad, cfg);
|
|
142
|
+
const joined2 = cfg.pack ? body2.join("\n") : body2.join(",\n");
|
|
143
|
+
return `[
|
|
144
|
+
${joined2}
|
|
145
|
+
${closePad}]`;
|
|
146
|
+
}
|
|
147
|
+
const keys = orderedKeys(value, cfg.sortKeys);
|
|
148
|
+
const entries = keys.map((k) => {
|
|
149
|
+
const prefix = `${JSON.stringify(k)}: `;
|
|
150
|
+
const rendered = renderNode(
|
|
151
|
+
value[k] ?? null,
|
|
152
|
+
level + 1,
|
|
153
|
+
pad.length + prefix.length,
|
|
154
|
+
cfg
|
|
155
|
+
);
|
|
156
|
+
return prefix + rendered;
|
|
157
|
+
});
|
|
158
|
+
const body = layoutEntries(entries, pad, cfg);
|
|
159
|
+
const joined = cfg.pack ? body.join("\n") : body.join(",\n");
|
|
160
|
+
return `{
|
|
161
|
+
${joined}
|
|
162
|
+
${closePad}}`;
|
|
163
|
+
}
|
|
164
|
+
function stringifyJson(value, options) {
|
|
165
|
+
const indent = options.indent ?? DEFAULT_JSON_INDENT;
|
|
166
|
+
const sortKeys = options.sortKeys ?? false;
|
|
167
|
+
if (options.style === "minify") {
|
|
168
|
+
return flatten(value, { indent, width: -1, pack: false, spaced: false, sortKeys });
|
|
169
|
+
}
|
|
170
|
+
const cfg = {
|
|
171
|
+
indent,
|
|
172
|
+
width: options.style === "pretty" ? -1 : options.width ?? DEFAULT_JSON_WIDTH,
|
|
173
|
+
pack: options.style === "compact",
|
|
174
|
+
spaced: true,
|
|
175
|
+
sortKeys
|
|
176
|
+
};
|
|
177
|
+
return renderNode(value, 0, 0, cfg);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/html-escape.ts
|
|
181
|
+
var HTML_ESCAPES = {
|
|
182
|
+
"&": "&",
|
|
183
|
+
"<": "<",
|
|
184
|
+
">": ">",
|
|
185
|
+
'"': """,
|
|
186
|
+
"'": "'"
|
|
187
|
+
};
|
|
188
|
+
var HTML_ESCAPE_RE = /[&<>"']/g;
|
|
189
|
+
function escapeHtml(value) {
|
|
190
|
+
if (typeof value !== "string") return "";
|
|
191
|
+
return value.replace(HTML_ESCAPE_RE, (char) => HTML_ESCAPES[char] ?? char);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/content-transfer/agent-payload.ts
|
|
195
|
+
var escapeXml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
196
|
+
var xmlName = (name) => {
|
|
197
|
+
if (!/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(name)) throw new Error(`Invalid AI envelope name: ${name}`);
|
|
198
|
+
return name;
|
|
199
|
+
};
|
|
200
|
+
var presentEntries = (values) => Object.entries(values ?? {}).filter(([, value]) => value !== null && value !== void 0 && value !== "");
|
|
201
|
+
function fenceJsonBlock(json) {
|
|
202
|
+
const runs = json.match(/`+/g) ?? [];
|
|
203
|
+
const fence = "`".repeat(Math.max(3, ...runs.map((run) => run.length + 1)));
|
|
204
|
+
return `${fence}json
|
|
205
|
+
${json.trimEnd()}
|
|
206
|
+
${fence}`;
|
|
207
|
+
}
|
|
208
|
+
function buildAgentPayload(input, environment = {}) {
|
|
209
|
+
const kind = xmlName(input.kind);
|
|
210
|
+
const url = environment.url ?? (typeof window === "undefined" ? "" : window.location.href);
|
|
211
|
+
const route = environment.route ?? (typeof window === "undefined" ? "" : window.location.pathname);
|
|
212
|
+
const attrs = presentEntries(input.attributes).map(([key, value]) => ` ${xmlName(key)}="${escapeXml(String(value))}"`).join("");
|
|
213
|
+
const entries = [
|
|
214
|
+
["location", input.location],
|
|
215
|
+
...url ? [["url", url]] : [],
|
|
216
|
+
...route ? [["route", route]] : [],
|
|
217
|
+
["copied", input.description],
|
|
218
|
+
["copied-at", environment.capturedAt ?? (/* @__PURE__ */ new Date()).toISOString()],
|
|
219
|
+
...presentEntries(input.context).map(([key, value]) => [xmlName(key), String(value)])
|
|
220
|
+
];
|
|
221
|
+
const json = JSON.stringify(input.data, null, 2);
|
|
222
|
+
if (json === void 0) throw new Error("AI envelope data must be JSON serializable.");
|
|
223
|
+
const safeJson = json.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
|
|
224
|
+
const context = entries.map(([key, value]) => `<${key}>${escapeXml(value)}</${key}>`).join("\n");
|
|
225
|
+
const summary = input.summary ? `<summary>
|
|
226
|
+
${escapeXml(input.summary)}
|
|
227
|
+
</summary>
|
|
228
|
+
` : "";
|
|
229
|
+
return `<${kind}${attrs}>
|
|
230
|
+
<context>
|
|
231
|
+
${context}
|
|
232
|
+
</context>
|
|
233
|
+
${summary}<data format="json">
|
|
234
|
+
${fenceJsonBlock(safeJson)}
|
|
235
|
+
</data>
|
|
236
|
+
</${kind}>`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/content-transfer.ts
|
|
240
|
+
var ContentTransferError = class extends Error {
|
|
241
|
+
code;
|
|
242
|
+
path;
|
|
243
|
+
constructor(code, message, path) {
|
|
244
|
+
super(message);
|
|
245
|
+
this.code = code;
|
|
246
|
+
this.path = path;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
var bound = /* @__PURE__ */ Symbol("bound-all-matching");
|
|
250
|
+
var stop = (s) => {
|
|
251
|
+
if (s.aborted) throw new DOMException("Cancelled", "AbortError");
|
|
252
|
+
};
|
|
253
|
+
var enc = (x) => x.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
254
|
+
var dec = (x) => x.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
255
|
+
function clone(v, path = "", seen = /* @__PURE__ */ new WeakSet(), policy = {}) {
|
|
256
|
+
if (v === null || typeof v === "string" || typeof v === "boolean") return v;
|
|
257
|
+
if (typeof v === "number") {
|
|
258
|
+
if (!Number.isFinite(v))
|
|
259
|
+
throw new ContentTransferError(
|
|
260
|
+
"unsupported-value",
|
|
261
|
+
"Only finite numbers are transferable",
|
|
262
|
+
path
|
|
263
|
+
);
|
|
264
|
+
return v;
|
|
265
|
+
}
|
|
266
|
+
if (typeof v !== "object")
|
|
267
|
+
throw new ContentTransferError(
|
|
268
|
+
"unsupported-value",
|
|
269
|
+
"Unsupported value",
|
|
270
|
+
path
|
|
271
|
+
);
|
|
272
|
+
if (seen.has(v))
|
|
273
|
+
throw new ContentTransferError(
|
|
274
|
+
"circular-value",
|
|
275
|
+
"Circular values cannot be transferred",
|
|
276
|
+
path
|
|
277
|
+
);
|
|
278
|
+
if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype && Object.getPrototypeOf(v) !== null)
|
|
279
|
+
throw new ContentTransferError(
|
|
280
|
+
"unsupported-value",
|
|
281
|
+
"Only plain JSON objects are transferable",
|
|
282
|
+
path
|
|
283
|
+
);
|
|
284
|
+
seen.add(v);
|
|
285
|
+
if (Array.isArray(v)) {
|
|
286
|
+
for (let index = 0; index < v.length; index += 1)
|
|
287
|
+
if (!Object.hasOwn(v, index))
|
|
288
|
+
throw new ContentTransferError(
|
|
289
|
+
"unsupported-value",
|
|
290
|
+
"Sparse arrays cannot be transferred",
|
|
291
|
+
`${path}/${index}`
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
const out = Array.isArray(v) ? v.map((x, i) => clone(x, `${path}/${i}`, seen, policy)) : Object.fromEntries(
|
|
295
|
+
Object.entries(v).filter(([, x]) => !policy.omitUndefinedObjectProperties || x !== void 0).map(([k, x]) => [
|
|
296
|
+
k,
|
|
297
|
+
clone(x, `${path}/${enc(k)}`, seen, policy)
|
|
298
|
+
])
|
|
299
|
+
);
|
|
300
|
+
seen.delete(v);
|
|
301
|
+
return out;
|
|
302
|
+
}
|
|
303
|
+
var maxSurfacePointerSegmentLength = 64;
|
|
304
|
+
var maxSurfacePointerLength = 192;
|
|
305
|
+
var truncateSurfacePointer = (value, maxLength) => {
|
|
306
|
+
const characters = Array.from(value);
|
|
307
|
+
return characters.length > maxLength ? `${characters.slice(0, maxLength - 1).join("")}\u2026` : value;
|
|
308
|
+
};
|
|
309
|
+
function displaySurfacePointer(path) {
|
|
310
|
+
if (!path) return "/";
|
|
311
|
+
const safe = path.split("/").slice(1).map((segment) => {
|
|
312
|
+
const controlSafe = segment.replace(
|
|
313
|
+
/[\u0000-\u001F\u007F-\u009F]/g,
|
|
314
|
+
(character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`
|
|
315
|
+
);
|
|
316
|
+
return truncateSurfacePointer(controlSafe, maxSurfacePointerSegmentLength);
|
|
317
|
+
}).join("/");
|
|
318
|
+
const pointer = `/${safe}`;
|
|
319
|
+
return truncateSurfacePointer(pointer, maxSurfacePointerLength);
|
|
320
|
+
}
|
|
321
|
+
function cloneSurfaceValue(value, path) {
|
|
322
|
+
try {
|
|
323
|
+
return clone(value, path, /* @__PURE__ */ new WeakSet(), {
|
|
324
|
+
omitUndefinedObjectProperties: true
|
|
325
|
+
});
|
|
326
|
+
} catch (error) {
|
|
327
|
+
if (error instanceof ContentTransferError)
|
|
328
|
+
throw new ContentTransferError(
|
|
329
|
+
error.code,
|
|
330
|
+
`Surface capture rejected value at ${displaySurfacePointer(error.path)}: ${error.message}`,
|
|
331
|
+
error.path
|
|
332
|
+
);
|
|
333
|
+
throw error;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function normalizeTransferJson(value) {
|
|
337
|
+
return clone(value);
|
|
338
|
+
}
|
|
339
|
+
function deepFreeze(v) {
|
|
340
|
+
if (ArrayBuffer.isView(v)) return v;
|
|
341
|
+
if (v && typeof v === "object" && !Object.isFrozen(v)) {
|
|
342
|
+
Object.freeze(v);
|
|
343
|
+
for (const x of Object.values(v)) deepFreeze(x);
|
|
344
|
+
}
|
|
345
|
+
return v;
|
|
346
|
+
}
|
|
347
|
+
function at(v, path) {
|
|
348
|
+
validatePointer(path);
|
|
349
|
+
if (path === "") return v;
|
|
350
|
+
if (!path.startsWith("/"))
|
|
351
|
+
throw new ContentTransferError(
|
|
352
|
+
"invalid-path",
|
|
353
|
+
"Expected RFC 6901 pointer",
|
|
354
|
+
path
|
|
355
|
+
);
|
|
356
|
+
let cur = v;
|
|
357
|
+
for (const part of path.slice(1).split("/").map(dec)) {
|
|
358
|
+
if (Array.isArray(cur)) cur = cur[Number(part)];
|
|
359
|
+
else if (cur && typeof cur === "object")
|
|
360
|
+
cur = Object.hasOwn(cur, part) ? cur[part] : void 0;
|
|
361
|
+
else return void 0;
|
|
362
|
+
}
|
|
363
|
+
return cur;
|
|
364
|
+
}
|
|
365
|
+
function validatePointer(path) {
|
|
366
|
+
if (path !== "" && !path.startsWith("/") || /~(?![01])/u.test(path))
|
|
367
|
+
throw new ContentTransferError(
|
|
368
|
+
"invalid-path",
|
|
369
|
+
"Expected an RFC 6901 JSON pointer",
|
|
370
|
+
path
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
function readTransferCell(row, column) {
|
|
374
|
+
return at(row, column.path);
|
|
375
|
+
}
|
|
376
|
+
function without(v, paths, omit, path = "") {
|
|
377
|
+
if (paths.includes(path)) {
|
|
378
|
+
omit.push({ path, reason: "excluded" });
|
|
379
|
+
return void 0;
|
|
380
|
+
}
|
|
381
|
+
if (Array.isArray(v))
|
|
382
|
+
return v.map((x, i) => without(x, paths, omit, `${path}/${i}`)).filter((x) => x !== void 0);
|
|
383
|
+
if (v && typeof v === "object") {
|
|
384
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
385
|
+
for (const [k, x] of Object.entries(v)) {
|
|
386
|
+
const y = without(x, paths, omit, `${path}/${enc(k)}`);
|
|
387
|
+
if (y !== void 0) out[k] = y;
|
|
388
|
+
}
|
|
389
|
+
return out;
|
|
390
|
+
}
|
|
391
|
+
return v;
|
|
392
|
+
}
|
|
393
|
+
function projectValue(v, rules, target, omit) {
|
|
394
|
+
rules.forEach((rule) => validatePointer(rule.path));
|
|
395
|
+
return without(
|
|
396
|
+
clone(v),
|
|
397
|
+
rules.filter(
|
|
398
|
+
(r) => r.target === target && (!r.exportable || r.classification !== "ordinary")
|
|
399
|
+
).map((r) => r.path),
|
|
400
|
+
omit
|
|
401
|
+
) ?? null;
|
|
402
|
+
}
|
|
403
|
+
function normalize(p, projection, omit) {
|
|
404
|
+
if (p.kind === "text" || p.kind === "markdown") {
|
|
405
|
+
if (typeof p.text !== "string")
|
|
406
|
+
throw new ContentTransferError(
|
|
407
|
+
"unsupported-value",
|
|
408
|
+
"Text payload must contain a string",
|
|
409
|
+
"/text"
|
|
410
|
+
);
|
|
411
|
+
const value = projectValue(p.text, projection.rules, "payload", omit);
|
|
412
|
+
return { kind: p.kind, text: typeof value === "string" ? value : "" };
|
|
413
|
+
}
|
|
414
|
+
if (p.kind === "json")
|
|
415
|
+
return {
|
|
416
|
+
kind: "json",
|
|
417
|
+
value: projectValue(p.value, projection.rules, "payload", omit)
|
|
418
|
+
};
|
|
419
|
+
if (p.kind === "registered")
|
|
420
|
+
return {
|
|
421
|
+
kind: "registered",
|
|
422
|
+
format: p.format,
|
|
423
|
+
value: projectValue(p.value, projection.rules, "payload", omit)
|
|
424
|
+
};
|
|
425
|
+
const rowsPayload = p;
|
|
426
|
+
if (p.kind !== "rows")
|
|
427
|
+
throw new ContentTransferError(
|
|
428
|
+
"invalid-payload",
|
|
429
|
+
"Unsupported payload kind"
|
|
430
|
+
);
|
|
431
|
+
const columns = rowsPayload.columns.map((c) => ({ ...c }));
|
|
432
|
+
resolveTransferRegistry(columns);
|
|
433
|
+
columns.forEach((column) => validatePointer(column.path));
|
|
434
|
+
const rules = [
|
|
435
|
+
...projection.rules,
|
|
436
|
+
...columns.filter(
|
|
437
|
+
(c) => c.exportable === false || c.classification === "secret" || c.classification === "credential"
|
|
438
|
+
).map((c) => ({
|
|
439
|
+
target: "row",
|
|
440
|
+
path: c.path,
|
|
441
|
+
exportable: c.exportable ?? true,
|
|
442
|
+
classification: c.classification ?? "ordinary"
|
|
443
|
+
}))
|
|
444
|
+
];
|
|
445
|
+
return {
|
|
446
|
+
kind: "rows",
|
|
447
|
+
columns,
|
|
448
|
+
rows: rowsPayload.rows.map((row, index) => {
|
|
449
|
+
if (!row || typeof row !== "object" || Array.isArray(row))
|
|
450
|
+
throw new ContentTransferError(
|
|
451
|
+
"invalid-row",
|
|
452
|
+
"A table row must be a JSON object",
|
|
453
|
+
`/rows/${index}`
|
|
454
|
+
);
|
|
455
|
+
const projected = projectValue(row, rules, "row", omit);
|
|
456
|
+
return projected === null ? {} : projected;
|
|
457
|
+
})
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function projectSnapshot(raw, projection) {
|
|
461
|
+
const omissions = raw.omissions.map((o) => ({ ...o }));
|
|
462
|
+
const normalized = normalize(raw.payload, projection, omissions);
|
|
463
|
+
const payload = raw.limits ? limitPayload(normalized, raw.limits, omissions) : normalized;
|
|
464
|
+
const included = payload.kind === "rows" ? payload.rows.length : raw.coverage.included;
|
|
465
|
+
return deepFreeze({
|
|
466
|
+
...raw,
|
|
467
|
+
payload,
|
|
468
|
+
coverage: {
|
|
469
|
+
...raw.coverage,
|
|
470
|
+
included,
|
|
471
|
+
...included < raw.coverage.included ? { status: "partial", reason: "Source export limit applied" } : {}
|
|
472
|
+
},
|
|
473
|
+
omissions,
|
|
474
|
+
sections: raw.sections.filter(
|
|
475
|
+
(section) => !projection.rules.some(
|
|
476
|
+
(rule) => rule.target === "payload" && (!rule.exportable || rule.classification !== "ordinary") && (section.path === rule.path || section.path.startsWith(`${rule.path}/`))
|
|
477
|
+
)
|
|
478
|
+
).map((x) => ({ ...x })),
|
|
479
|
+
...raw.limits ? { limits: { ...raw.limits } } : {}
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
var uid = () => globalThis.crypto?.randomUUID?.() ?? `capture-${Date.now()}-${Math.random()}`;
|
|
483
|
+
function directSource(payload, options = {}) {
|
|
484
|
+
const id = options.id ?? uid();
|
|
485
|
+
return {
|
|
486
|
+
projection: options.projection ?? { rules: [] },
|
|
487
|
+
snapshot: {
|
|
488
|
+
id,
|
|
489
|
+
sourceId: options.sourceId ?? id,
|
|
490
|
+
revision: options.revision ?? uid(),
|
|
491
|
+
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
492
|
+
label: options.label ?? "Content",
|
|
493
|
+
payload,
|
|
494
|
+
coverage: options.coverage ?? {
|
|
495
|
+
status: "complete",
|
|
496
|
+
included: payload.kind === "rows" ? payload.rows.length : 1,
|
|
497
|
+
total: payload.kind === "rows" ? payload.rows.length : 1
|
|
498
|
+
},
|
|
499
|
+
unsavedChanges: options.unsavedChanges ?? false,
|
|
500
|
+
omissions: [],
|
|
501
|
+
sections: options.sections ?? [],
|
|
502
|
+
...options.limits ? { limits: { ...options.limits } } : {}
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
async function capture(source, signal, scope = "target") {
|
|
507
|
+
stop(signal);
|
|
508
|
+
const captured = "kind" in source ? directSource(source) : await source.capture({ scope, signal });
|
|
509
|
+
stop(signal);
|
|
510
|
+
const all = captured.allMatching;
|
|
511
|
+
if (all && (all.query.sourceId !== captured.snapshot.sourceId || all.query.revision !== captured.snapshot.revision || all.query.order.length === 0))
|
|
512
|
+
throw new ContentTransferError(
|
|
513
|
+
"query-mismatch",
|
|
514
|
+
"All-matching query must be captured with stable ordering"
|
|
515
|
+
);
|
|
516
|
+
const snapshot = projectSnapshot(captured.snapshot, captured.projection);
|
|
517
|
+
if (!all) return { snapshot };
|
|
518
|
+
return {
|
|
519
|
+
snapshot,
|
|
520
|
+
allMatching: deepFreeze({
|
|
521
|
+
[bound]: true,
|
|
522
|
+
source: {
|
|
523
|
+
...all,
|
|
524
|
+
query: {
|
|
525
|
+
...all.query,
|
|
526
|
+
query: clone(all.query.query),
|
|
527
|
+
order: all.query.order.map((x) => ({ ...x }))
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
projection: { rules: captured.projection.rules.map((x) => ({ ...x })) },
|
|
531
|
+
columns: snapshot.payload.kind === "rows" ? snapshot.payload.columns : [],
|
|
532
|
+
...snapshot.limits ? { limits: { ...snapshot.limits } } : {}
|
|
533
|
+
})
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
function createDraft(s) {
|
|
537
|
+
return deepFreeze({
|
|
538
|
+
snapshotId: s.id,
|
|
539
|
+
sourceId: s.sourceId,
|
|
540
|
+
sourceRevision: s.revision,
|
|
541
|
+
revision: 0,
|
|
542
|
+
payload: normalize(s.payload, { rules: [] }, []),
|
|
543
|
+
omissions: [...s.omissions],
|
|
544
|
+
manuallyEdited: false,
|
|
545
|
+
provenance: [{ kind: "original", description: "Captured source snapshot" }]
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
function revise(d, p, o, description, manual = d.manuallyEdited, kind = "deterministic", baseOmissions = d.omissions) {
|
|
549
|
+
const { history: _history, ...previous } = d;
|
|
550
|
+
return deepFreeze({
|
|
551
|
+
...d,
|
|
552
|
+
revision: d.revision + 1,
|
|
553
|
+
payload: normalize(p, { rules: [] }, []),
|
|
554
|
+
omissions: [...baseOmissions, ...o],
|
|
555
|
+
manuallyEdited: manual,
|
|
556
|
+
provenance: [...d.provenance, { kind, description }],
|
|
557
|
+
history: [...d.history ?? [], previous]
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
var editDraft = (d, p) => revise(d, p, [], "Manual edit", true, "manual");
|
|
561
|
+
var resetDraft = (s) => createDraft(s);
|
|
562
|
+
var sourceChanged = (d, s) => d.sourceId !== s.sourceId || d.sourceRevision !== s.revision;
|
|
563
|
+
function undoDraft(d) {
|
|
564
|
+
const previous = d.history?.at(-1);
|
|
565
|
+
if (!previous) return d;
|
|
566
|
+
return deepFreeze({
|
|
567
|
+
...previous,
|
|
568
|
+
revision: d.revision + 1,
|
|
569
|
+
history: d.history.slice(0, -1)
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
function refreshDraft(draft, snapshot, choice) {
|
|
573
|
+
return choice === "keep" ? draft : createDraft(snapshot);
|
|
574
|
+
}
|
|
575
|
+
function applySectionSelection(d, s, ids) {
|
|
576
|
+
if (d.snapshotId !== s.id || d.sourceId !== s.sourceId || d.sourceRevision !== s.revision)
|
|
577
|
+
throw new ContentTransferError(
|
|
578
|
+
"snapshot-mismatch",
|
|
579
|
+
"Section selection requires the draft's captured source snapshot"
|
|
580
|
+
);
|
|
581
|
+
const selected = new Set(ids), omit = [], paths = s.sections.filter((x) => !selected.has(x.id)).map((x) => {
|
|
582
|
+
omit.push({ path: x.path, reason: "excluded" });
|
|
583
|
+
return x.path;
|
|
584
|
+
}), baseOmissions = s.omissions;
|
|
585
|
+
if (s.payload.kind === "json")
|
|
586
|
+
return revise(
|
|
587
|
+
d,
|
|
588
|
+
{ kind: "json", value: without(s.payload.value, paths, []) ?? null },
|
|
589
|
+
omit,
|
|
590
|
+
"Selected sections",
|
|
591
|
+
d.manuallyEdited,
|
|
592
|
+
"deterministic",
|
|
593
|
+
baseOmissions
|
|
594
|
+
);
|
|
595
|
+
if (s.payload.kind === "registered")
|
|
596
|
+
return revise(
|
|
597
|
+
d,
|
|
598
|
+
{
|
|
599
|
+
kind: "registered",
|
|
600
|
+
format: s.payload.format,
|
|
601
|
+
value: without(s.payload.value, paths, []) ?? null
|
|
602
|
+
},
|
|
603
|
+
omit,
|
|
604
|
+
"Selected sections",
|
|
605
|
+
d.manuallyEdited,
|
|
606
|
+
"deterministic",
|
|
607
|
+
baseOmissions
|
|
608
|
+
);
|
|
609
|
+
return revise(
|
|
610
|
+
d,
|
|
611
|
+
s.payload,
|
|
612
|
+
omit,
|
|
613
|
+
"Selected sections",
|
|
614
|
+
d.manuallyEdited,
|
|
615
|
+
"deterministic",
|
|
616
|
+
baseOmissions
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
function transformRows(d, o) {
|
|
620
|
+
if (d.payload.kind !== "rows") return d;
|
|
621
|
+
if (o.top != null) assertLimit(o.top, "maxRows");
|
|
622
|
+
const cols = o.visibleColumns ? o.visibleColumns.map(
|
|
623
|
+
(id) => d.payload.kind === "rows" ? d.payload.columns.find((c) => c.id === id) : void 0
|
|
624
|
+
).filter((c) => Boolean(c)) : d.payload.columns.filter((c) => c.visible);
|
|
625
|
+
const get = readTransferCell;
|
|
626
|
+
let rows = [...d.payload.rows], omit = [];
|
|
627
|
+
const before = rows.length;
|
|
628
|
+
if (o.selectedIndices !== void 0) {
|
|
629
|
+
const selected = /* @__PURE__ */ new Set();
|
|
630
|
+
for (const index of o.selectedIndices) {
|
|
631
|
+
if (!Number.isSafeInteger(index) || index < 0 || index >= d.payload.rows.length)
|
|
632
|
+
throw new ContentTransferError(
|
|
633
|
+
"invalid-selection-index",
|
|
634
|
+
"Selected row indexes must be non-negative positions in the input draft rows"
|
|
635
|
+
);
|
|
636
|
+
selected.add(index);
|
|
637
|
+
}
|
|
638
|
+
rows = d.payload.rows.filter((_, index) => selected.has(index));
|
|
639
|
+
}
|
|
640
|
+
if (o.selectedIds && o.idColumn) {
|
|
641
|
+
const set = new Set(o.selectedIds);
|
|
642
|
+
const idColumn = d.payload.columns.find((c) => c.id === o.idColumn);
|
|
643
|
+
rows = rows.filter(
|
|
644
|
+
(r) => set.has(String(idColumn ? get(r, idColumn) : r[o.idColumn]))
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
if (o.search) {
|
|
648
|
+
const q = o.search.toLowerCase();
|
|
649
|
+
rows = rows.filter(
|
|
650
|
+
(r) => cols.some(
|
|
651
|
+
(c) => String(get(r, c) ?? "").toLowerCase().includes(q)
|
|
652
|
+
)
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
for (const [id, q] of Object.entries(o.filters ?? {})) {
|
|
656
|
+
const c = cols.find((x) => x.id === id);
|
|
657
|
+
if (c)
|
|
658
|
+
rows = rows.filter(
|
|
659
|
+
(r) => String(get(r, c) ?? "").toLowerCase().includes(q.toLowerCase())
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
if (rows.length !== before)
|
|
663
|
+
omit.push({
|
|
664
|
+
path: "/rows",
|
|
665
|
+
reason: "filtered",
|
|
666
|
+
count: before - rows.length
|
|
667
|
+
});
|
|
668
|
+
if (o.sort) {
|
|
669
|
+
const c = cols.find((x) => x.id === o.sort.column);
|
|
670
|
+
if (c)
|
|
671
|
+
rows.sort((a, b) => {
|
|
672
|
+
const left = get(a, c);
|
|
673
|
+
const right = get(b, c);
|
|
674
|
+
const compare = typeof left === "number" && typeof right === "number" ? left - right : String(left ?? "").localeCompare(String(right ?? ""));
|
|
675
|
+
return compare * (o.sort.direction === "asc" ? 1 : -1);
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
if (o.top != null && rows.length > o.top) {
|
|
679
|
+
omit.push({
|
|
680
|
+
path: "/rows",
|
|
681
|
+
reason: "truncated",
|
|
682
|
+
count: rows.length - o.top
|
|
683
|
+
});
|
|
684
|
+
rows = rows.slice(0, o.top);
|
|
685
|
+
}
|
|
686
|
+
if (o.structuredProjection === "visible") {
|
|
687
|
+
rows = rows.map(
|
|
688
|
+
(r) => Object.fromEntries(cols.map((c) => [c.id, get(r, c) ?? null]))
|
|
689
|
+
);
|
|
690
|
+
omit.push({
|
|
691
|
+
path: "/columns",
|
|
692
|
+
reason: "excluded",
|
|
693
|
+
count: d.payload.columns.length - cols.length
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
return revise(
|
|
697
|
+
d,
|
|
698
|
+
{
|
|
699
|
+
kind: "rows",
|
|
700
|
+
rows,
|
|
701
|
+
columns: cols.map((c) => ({
|
|
702
|
+
...c,
|
|
703
|
+
visible: true,
|
|
704
|
+
...o.structuredProjection === "visible" ? { path: `/${enc(c.id)}` } : {}
|
|
705
|
+
}))
|
|
706
|
+
},
|
|
707
|
+
omit,
|
|
708
|
+
"Prepared row subset"
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
function reduceJson(draft, options) {
|
|
712
|
+
if (draft.payload.kind !== "json" && draft.payload.kind !== "registered")
|
|
713
|
+
return draft;
|
|
714
|
+
const omissions = [];
|
|
715
|
+
options.exclude?.forEach(validatePointer);
|
|
716
|
+
const filtered = without(clone(draft.payload.value), options.exclude ?? [], omissions) ?? null;
|
|
717
|
+
const payload = limitPayload(
|
|
718
|
+
{ ...draft.payload, value: filtered },
|
|
719
|
+
options,
|
|
720
|
+
omissions
|
|
721
|
+
);
|
|
722
|
+
return revise(draft, payload, omissions, "Reduced JSON");
|
|
723
|
+
}
|
|
724
|
+
async function collectAllMatching(c, signal) {
|
|
725
|
+
const rows = [], omissions = [], ids = /* @__PURE__ */ new Set(), cursors = /* @__PURE__ */ new Set();
|
|
726
|
+
let cursor = null, token, last = {
|
|
727
|
+
status: "unknown",
|
|
728
|
+
included: 0,
|
|
729
|
+
total: null
|
|
730
|
+
};
|
|
731
|
+
let certifiedTotal;
|
|
732
|
+
const maxRows = c.limits?.maxRows;
|
|
733
|
+
if (maxRows != null) assertLimit(maxRows, "maxRows");
|
|
734
|
+
const finishAtLimit = (total, knownOmitted) => ({
|
|
735
|
+
rows: deepFreeze(rows),
|
|
736
|
+
omissions: deepFreeze([
|
|
737
|
+
...omissions,
|
|
738
|
+
{
|
|
739
|
+
path: "/rows",
|
|
740
|
+
reason: "truncated",
|
|
741
|
+
...knownOmitted === void 0 || knownOmitted <= 0 ? {} : { count: knownOmitted }
|
|
742
|
+
}
|
|
743
|
+
]),
|
|
744
|
+
coverage: deepFreeze({
|
|
745
|
+
status: "partial",
|
|
746
|
+
included: rows.length,
|
|
747
|
+
total,
|
|
748
|
+
reason: "Source export limit applied"
|
|
749
|
+
})
|
|
750
|
+
});
|
|
751
|
+
try {
|
|
752
|
+
if (maxRows === 0) return finishAtLimit(null);
|
|
753
|
+
do {
|
|
754
|
+
stop(signal);
|
|
755
|
+
const page = await c.source.page({
|
|
756
|
+
query: c.source.query,
|
|
757
|
+
cursor,
|
|
758
|
+
consistencyToken: token ?? null,
|
|
759
|
+
signal
|
|
760
|
+
});
|
|
761
|
+
stop(signal);
|
|
762
|
+
if (token === void 0) token = page.consistencyToken;
|
|
763
|
+
else if (page.consistencyToken !== token)
|
|
764
|
+
throw new ContentTransferError(
|
|
765
|
+
"consistency-token-changed",
|
|
766
|
+
"All-matching export changed during collection"
|
|
767
|
+
);
|
|
768
|
+
if (page.coverage.included !== page.rows.length)
|
|
769
|
+
throw new ContentTransferError(
|
|
770
|
+
"invalid-page-coverage",
|
|
771
|
+
"Page count does not match returned rows"
|
|
772
|
+
);
|
|
773
|
+
if (page.coverage.total !== null && (!Number.isSafeInteger(page.coverage.total) || page.coverage.total < 0))
|
|
774
|
+
throw new ContentTransferError(
|
|
775
|
+
"invalid-page-coverage",
|
|
776
|
+
"Page total must be a non-negative integer or null"
|
|
777
|
+
);
|
|
778
|
+
if (certifiedTotal === void 0) certifiedTotal = page.coverage.total;
|
|
779
|
+
else if (page.coverage.total !== certifiedTotal)
|
|
780
|
+
throw new ContentTransferError(
|
|
781
|
+
"inconsistent-total",
|
|
782
|
+
"All-matching source changed its reported total during collection"
|
|
783
|
+
);
|
|
784
|
+
const projected = normalize(
|
|
785
|
+
{ kind: "rows", rows: page.rows, columns: [...c.columns] },
|
|
786
|
+
c.projection,
|
|
787
|
+
omissions
|
|
788
|
+
).rows;
|
|
789
|
+
let uniqueBeyondLimit = 0;
|
|
790
|
+
for (const row of projected) {
|
|
791
|
+
const id = c.source.getRowId(row);
|
|
792
|
+
if (!id)
|
|
793
|
+
throw new ContentTransferError(
|
|
794
|
+
"invalid-row-id",
|
|
795
|
+
"All-matching source returned empty projected ID"
|
|
796
|
+
);
|
|
797
|
+
if (!ids.has(id)) {
|
|
798
|
+
ids.add(id);
|
|
799
|
+
if (maxRows == null || rows.length < maxRows)
|
|
800
|
+
rows.push(
|
|
801
|
+
limitPayload(
|
|
802
|
+
{ kind: "rows", rows: [row], columns: [...c.columns] },
|
|
803
|
+
{ ...c.limits, maxRows: null },
|
|
804
|
+
omissions
|
|
805
|
+
).rows[0]
|
|
806
|
+
);
|
|
807
|
+
else uniqueBeyondLimit += 1;
|
|
808
|
+
} else omissions.push({ path: `/rows/${enc(id)}`, reason: "duplicate" });
|
|
809
|
+
}
|
|
810
|
+
if (page.coverage.total !== null && page.coverage.total < ids.size)
|
|
811
|
+
throw new ContentTransferError(
|
|
812
|
+
"invalid-page-coverage",
|
|
813
|
+
"Page total is smaller than the accumulated unique row count"
|
|
814
|
+
);
|
|
815
|
+
last = page.coverage;
|
|
816
|
+
if (maxRows != null && rows.length >= maxRows && (uniqueBeyondLimit > 0 || page.nextCursor !== null))
|
|
817
|
+
return finishAtLimit(
|
|
818
|
+
page.coverage.total,
|
|
819
|
+
page.nextCursor === null ? uniqueBeyondLimit : page.coverage.total != null && page.coverage.total > rows.length ? page.coverage.total - rows.length : void 0
|
|
820
|
+
);
|
|
821
|
+
cursor = page.nextCursor;
|
|
822
|
+
if (cursor !== null && (!cursor || cursors.has(cursor)))
|
|
823
|
+
throw new ContentTransferError(
|
|
824
|
+
"non-advancing-cursor",
|
|
825
|
+
"All-matching source repeated a cursor"
|
|
826
|
+
);
|
|
827
|
+
if (cursor) cursors.add(cursor);
|
|
828
|
+
} while (cursor !== null);
|
|
829
|
+
const complete = c.source.stableSnapshot === true && last.status === "complete" && last.total === rows.length && token !== void 0;
|
|
830
|
+
return {
|
|
831
|
+
rows: deepFreeze(rows),
|
|
832
|
+
omissions: deepFreeze(omissions),
|
|
833
|
+
coverage: deepFreeze(
|
|
834
|
+
complete ? { ...last, included: rows.length } : {
|
|
835
|
+
...last,
|
|
836
|
+
status: "partial",
|
|
837
|
+
included: rows.length,
|
|
838
|
+
reason: last.reason ?? "Source did not certify a stable complete snapshot"
|
|
839
|
+
}
|
|
840
|
+
)
|
|
841
|
+
};
|
|
842
|
+
} catch (e) {
|
|
843
|
+
const error = e instanceof ContentTransferError ? e : new ContentTransferError(
|
|
844
|
+
signal.aborted ? "cancelled" : "page-failed",
|
|
845
|
+
e instanceof Error ? e.message : "Page collection failed"
|
|
846
|
+
);
|
|
847
|
+
return {
|
|
848
|
+
rows: deepFreeze(rows),
|
|
849
|
+
omissions: deepFreeze(omissions),
|
|
850
|
+
coverage: deepFreeze({
|
|
851
|
+
status: "partial",
|
|
852
|
+
included: rows.length,
|
|
853
|
+
total: last.total,
|
|
854
|
+
reason: error.message
|
|
855
|
+
}),
|
|
856
|
+
error
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
var textValue = (value) => value === void 0 ? "" : typeof value === "string" ? value : stringifyJson(value, { style: "compact" });
|
|
861
|
+
function sealTransferArtifact(artifact) {
|
|
862
|
+
if (!artifact.file)
|
|
863
|
+
return deepFreeze({
|
|
864
|
+
...artifact,
|
|
865
|
+
omissions: artifact.omissions.map((o) => ({ ...o }))
|
|
866
|
+
});
|
|
867
|
+
const bytes = artifact.file.bytes.slice();
|
|
868
|
+
const file = {
|
|
869
|
+
filename: artifact.file.filename,
|
|
870
|
+
mime: artifact.file.mime,
|
|
871
|
+
get bytes() {
|
|
872
|
+
return bytes.slice();
|
|
873
|
+
}
|
|
874
|
+
};
|
|
875
|
+
return deepFreeze({
|
|
876
|
+
...artifact,
|
|
877
|
+
file,
|
|
878
|
+
omissions: artifact.omissions.map((o) => ({ ...o }))
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
function serialize(draft, format, options = {}) {
|
|
882
|
+
const payload = draft.payload;
|
|
883
|
+
const omissions = draft.omissions.map((o) => ({ ...o }));
|
|
884
|
+
let plainText = "", html;
|
|
885
|
+
let mime = "text/plain;charset=utf-8", extension = "txt";
|
|
886
|
+
const columnCell = (row, column) => textValue(readTransferCell(row, column));
|
|
887
|
+
if ((format === "csv" || format === "tsv") && payload.kind !== "rows") {
|
|
888
|
+
throw new ContentTransferError(
|
|
889
|
+
"unsupported-format",
|
|
890
|
+
`${format.toUpperCase()} requires tabular data`
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
if (format === "json" || format === "compact-json") {
|
|
894
|
+
const value = payload.kind === "rows" ? payload.rows : payload.kind === "text" || payload.kind === "markdown" ? payload.text : payload.value;
|
|
895
|
+
plainText = stringifyJson(value, {
|
|
896
|
+
style: format === "json" ? "pretty" : "compact"
|
|
897
|
+
});
|
|
898
|
+
mime = "application/json;charset=utf-8";
|
|
899
|
+
extension = "json";
|
|
900
|
+
} else if (payload.kind === "rows") {
|
|
901
|
+
const columns = payload.columns.filter(
|
|
902
|
+
(c) => c.visible && c.exportable !== false && c.classification !== "secret" && c.classification !== "credential"
|
|
903
|
+
);
|
|
904
|
+
if (format === "markdown") {
|
|
905
|
+
const escapeCell = (value) => escapeHtml(value).replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\r\n|\r|\n/g, "<br>");
|
|
906
|
+
plainText = [
|
|
907
|
+
`| ${columns.map((c) => escapeCell(c.label)).join(" | ")} |`,
|
|
908
|
+
`| ${columns.map(() => "---").join(" | ")} |`,
|
|
909
|
+
...payload.rows.map(
|
|
910
|
+
(row) => `| ${columns.map((c) => escapeCell(columnCell(row, c))).join(" | ")} |`
|
|
911
|
+
)
|
|
912
|
+
].join("\n");
|
|
913
|
+
mime = "text/markdown;charset=utf-8";
|
|
914
|
+
extension = "md";
|
|
915
|
+
} else {
|
|
916
|
+
const delimiter = format === "csv" ? "," : " ";
|
|
917
|
+
const safe = options.spreadsheetSafe ?? format === "tsv";
|
|
918
|
+
const encode = (value, path, isString = true) => {
|
|
919
|
+
let text = value;
|
|
920
|
+
if (safe && isString && /^[\t\r\n ]*[=+\-@]/.test(text)) {
|
|
921
|
+
text = `'${text}`;
|
|
922
|
+
omissions.push({ path, reason: "escaped", count: 1 });
|
|
923
|
+
}
|
|
924
|
+
return text.includes(delimiter) || /["\n\r]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
|
|
925
|
+
};
|
|
926
|
+
plainText = [
|
|
927
|
+
columns.map((c) => encode(c.label, `/columns/${enc(c.id)}`)).join(delimiter),
|
|
928
|
+
...payload.rows.map(
|
|
929
|
+
(row, index) => columns.map(
|
|
930
|
+
(c) => encode(
|
|
931
|
+
columnCell(row, c),
|
|
932
|
+
`/rows/${index}${c.path}`,
|
|
933
|
+
typeof readTransferCell(row, c) === "string"
|
|
934
|
+
)
|
|
935
|
+
).join(delimiter)
|
|
936
|
+
)
|
|
937
|
+
].join("\n");
|
|
938
|
+
if (format === "csv") {
|
|
939
|
+
mime = "text/csv;charset=utf-8";
|
|
940
|
+
extension = "csv";
|
|
941
|
+
}
|
|
942
|
+
if (format === "tsv") {
|
|
943
|
+
mime = "text/tab-separated-values;charset=utf-8";
|
|
944
|
+
extension = "tsv";
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
if (format === "html")
|
|
948
|
+
html = `<table><thead><tr>${columns.map((c) => `<th>${escapeHtml(c.label)}</th>`).join("")}</tr></thead><tbody>${payload.rows.map((row) => `<tr>${columns.map((c) => `<td>${escapeHtml(columnCell(row, c)).replace(/\r\n|\r|\n/g, "<br>")}</td>`).join("")}</tr>`).join("")}</tbody></table>`;
|
|
949
|
+
} else {
|
|
950
|
+
plainText = payload.kind === "text" || payload.kind === "markdown" ? payload.text : stringifyJson(payload.value, { style: "pretty" });
|
|
951
|
+
if (format === "markdown") {
|
|
952
|
+
mime = "text/markdown;charset=utf-8";
|
|
953
|
+
extension = "md";
|
|
954
|
+
}
|
|
955
|
+
if (format === "html") html = `<pre>${escapeHtml(plainText)}</pre>`;
|
|
956
|
+
}
|
|
957
|
+
if (format === "html") {
|
|
958
|
+
mime = "text/html;charset=utf-8";
|
|
959
|
+
extension = "html";
|
|
960
|
+
}
|
|
961
|
+
return sealTransferArtifact({
|
|
962
|
+
snapshotId: draft.snapshotId,
|
|
963
|
+
draftRevision: draft.revision,
|
|
964
|
+
format,
|
|
965
|
+
plainText,
|
|
966
|
+
...html === void 0 ? {} : { html },
|
|
967
|
+
file: {
|
|
968
|
+
filename: options.filename ?? `export.${extension}`,
|
|
969
|
+
mime,
|
|
970
|
+
bytes: new TextEncoder().encode(
|
|
971
|
+
format === "html" ? html ?? plainText : plainText
|
|
972
|
+
)
|
|
973
|
+
},
|
|
974
|
+
omissions
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
async function renderMarkdownHtml(markdown) {
|
|
978
|
+
const [{ marked }, purify] = await Promise.all([
|
|
979
|
+
import("marked"),
|
|
980
|
+
import("dompurify")
|
|
981
|
+
]);
|
|
982
|
+
const html = await marked.parse(markdown);
|
|
983
|
+
const api = purify.default;
|
|
984
|
+
if (typeof api === "function" && typeof window !== "undefined") {
|
|
985
|
+
const purifier = api(window);
|
|
986
|
+
if (typeof purifier?.sanitize === "function") return purifier.sanitize(html);
|
|
987
|
+
}
|
|
988
|
+
if (typeof api.sanitize === "function")
|
|
989
|
+
return api.sanitize(html);
|
|
990
|
+
throw new ContentTransferError(
|
|
991
|
+
"rich-html-unavailable",
|
|
992
|
+
"Rich Markdown HTML requires a DOMPurify DOM adapter"
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
async function serializeMarkdownRich(d, filename = "export.html") {
|
|
996
|
+
if (d.payload.kind !== "markdown")
|
|
997
|
+
throw new ContentTransferError(
|
|
998
|
+
"unsupported-format",
|
|
999
|
+
"Rich Markdown requires Markdown payload"
|
|
1000
|
+
);
|
|
1001
|
+
const html = await renderMarkdownHtml(d.payload.text);
|
|
1002
|
+
return sealTransferArtifact({
|
|
1003
|
+
snapshotId: d.snapshotId,
|
|
1004
|
+
draftRevision: d.revision,
|
|
1005
|
+
format: "html",
|
|
1006
|
+
plainText: d.payload.text,
|
|
1007
|
+
html,
|
|
1008
|
+
file: {
|
|
1009
|
+
filename,
|
|
1010
|
+
mime: "text/html;charset=utf-8",
|
|
1011
|
+
bytes: new TextEncoder().encode(html)
|
|
1012
|
+
},
|
|
1013
|
+
omissions: [...d.omissions]
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
function createBrowserTransport() {
|
|
1017
|
+
return {
|
|
1018
|
+
async copy(a, signal) {
|
|
1019
|
+
const cancelled = () => signal.aborted ? { status: "cancelled" } : void 0;
|
|
1020
|
+
try {
|
|
1021
|
+
if (cancelled()) return cancelled();
|
|
1022
|
+
const c = globalThis.navigator?.clipboard;
|
|
1023
|
+
if (!c?.write && !c?.writeText)
|
|
1024
|
+
return {
|
|
1025
|
+
status: "error",
|
|
1026
|
+
code: "clipboard-unavailable",
|
|
1027
|
+
message: "Clipboard writing is unavailable",
|
|
1028
|
+
retryable: false
|
|
1029
|
+
};
|
|
1030
|
+
if (a.html && c.write && typeof globalThis.ClipboardItem === "function") {
|
|
1031
|
+
try {
|
|
1032
|
+
await c.write([
|
|
1033
|
+
new ClipboardItem({
|
|
1034
|
+
"text/plain": new Blob([a.plainText], { type: "text/plain" }),
|
|
1035
|
+
"text/html": new Blob([a.html], { type: "text/html" })
|
|
1036
|
+
})
|
|
1037
|
+
]);
|
|
1038
|
+
if (cancelled()) return cancelled();
|
|
1039
|
+
return {
|
|
1040
|
+
status: "success",
|
|
1041
|
+
delivered: "clipboard",
|
|
1042
|
+
mimeTypes: ["text/plain", "text/html"]
|
|
1043
|
+
};
|
|
1044
|
+
} catch {
|
|
1045
|
+
if (cancelled()) return cancelled();
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
if (c.writeText) {
|
|
1049
|
+
await c.writeText(a.plainText);
|
|
1050
|
+
if (cancelled()) return cancelled();
|
|
1051
|
+
return a.html ? {
|
|
1052
|
+
status: "degraded",
|
|
1053
|
+
delivered: "plain-text",
|
|
1054
|
+
reason: "Rich clipboard write was unavailable or rejected"
|
|
1055
|
+
} : {
|
|
1056
|
+
status: "success",
|
|
1057
|
+
delivered: "clipboard",
|
|
1058
|
+
mimeTypes: ["text/plain"]
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
return {
|
|
1062
|
+
status: "error",
|
|
1063
|
+
code: "clipboard-rejected",
|
|
1064
|
+
message: "Clipboard rejected the write",
|
|
1065
|
+
retryable: true
|
|
1066
|
+
};
|
|
1067
|
+
} catch (e) {
|
|
1068
|
+
return cancelled() ?? {
|
|
1069
|
+
status: "error",
|
|
1070
|
+
code: "clipboard-rejected",
|
|
1071
|
+
message: e instanceof Error ? e.message : "Clipboard rejected the write",
|
|
1072
|
+
retryable: true
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
},
|
|
1076
|
+
async download(a, signal) {
|
|
1077
|
+
if (signal.aborted) return { status: "cancelled" };
|
|
1078
|
+
if (!a.file || typeof document === "undefined" || !URL?.createObjectURL)
|
|
1079
|
+
return {
|
|
1080
|
+
status: "error",
|
|
1081
|
+
code: "download-unavailable",
|
|
1082
|
+
message: "Download is unavailable",
|
|
1083
|
+
retryable: false
|
|
1084
|
+
};
|
|
1085
|
+
let url;
|
|
1086
|
+
try {
|
|
1087
|
+
url = URL.createObjectURL(
|
|
1088
|
+
new Blob([a.file.bytes.slice().buffer], {
|
|
1089
|
+
type: a.file.mime
|
|
1090
|
+
})
|
|
1091
|
+
);
|
|
1092
|
+
const link = document.createElement("a");
|
|
1093
|
+
link.href = url;
|
|
1094
|
+
link.download = a.file.filename;
|
|
1095
|
+
link.click();
|
|
1096
|
+
return signal.aborted ? { status: "cancelled" } : {
|
|
1097
|
+
status: "success",
|
|
1098
|
+
delivered: "download-started",
|
|
1099
|
+
mimeTypes: [a.file.mime]
|
|
1100
|
+
};
|
|
1101
|
+
} catch (e) {
|
|
1102
|
+
return signal.aborted ? { status: "cancelled" } : {
|
|
1103
|
+
status: "error",
|
|
1104
|
+
code: "download-failed",
|
|
1105
|
+
message: e instanceof Error ? e.message : "Download failed",
|
|
1106
|
+
retryable: true
|
|
1107
|
+
};
|
|
1108
|
+
} finally {
|
|
1109
|
+
if (url) setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
function assertLimit(value, name) {
|
|
1115
|
+
if (value != null && (!Number.isSafeInteger(value) || value < 0)) {
|
|
1116
|
+
throw new ContentTransferError(
|
|
1117
|
+
"invalid-limit",
|
|
1118
|
+
`${name} must be a non-negative safe integer or null`
|
|
1119
|
+
);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
var limitKeys = [
|
|
1123
|
+
"maxRows",
|
|
1124
|
+
"maxStringChars",
|
|
1125
|
+
"maxDepth",
|
|
1126
|
+
"maxArrayItems",
|
|
1127
|
+
"targetTokens"
|
|
1128
|
+
];
|
|
1129
|
+
function resolveTransferPreferences(provider = {}, menu = {}, sourceLimits = {}) {
|
|
1130
|
+
const limits = {
|
|
1131
|
+
maxRows: null,
|
|
1132
|
+
maxStringChars: null,
|
|
1133
|
+
maxDepth: null,
|
|
1134
|
+
maxArrayItems: null,
|
|
1135
|
+
targetTokens: null
|
|
1136
|
+
};
|
|
1137
|
+
for (const key of limitKeys) {
|
|
1138
|
+
const candidates = [
|
|
1139
|
+
provider.limits?.[key],
|
|
1140
|
+
menu.limits?.[key],
|
|
1141
|
+
sourceLimits[key]
|
|
1142
|
+
];
|
|
1143
|
+
candidates.forEach((value) => assertLimit(value, key));
|
|
1144
|
+
const finite = candidates.filter((value) => value != null);
|
|
1145
|
+
limits[key] = finite.length ? Math.min(...finite) : null;
|
|
1146
|
+
}
|
|
1147
|
+
return deepFreeze({
|
|
1148
|
+
initialFormat: menu.initialFormat ?? provider.initialFormat ?? "plain",
|
|
1149
|
+
initialPreset: menu.initialPreset ?? provider.initialPreset ?? "full",
|
|
1150
|
+
limits
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
function cloneTransferRegistration(value, path = "registration", seen = /* @__PURE__ */ new WeakSet()) {
|
|
1154
|
+
if (value === null || typeof value !== "object" && typeof value !== "function")
|
|
1155
|
+
return value;
|
|
1156
|
+
if (typeof value === "function") return value;
|
|
1157
|
+
if (seen.has(value))
|
|
1158
|
+
throw new ContentTransferError(
|
|
1159
|
+
"unsupported-registration-metadata",
|
|
1160
|
+
"Circular transfer registration metadata is unsupported",
|
|
1161
|
+
path
|
|
1162
|
+
);
|
|
1163
|
+
seen.add(value);
|
|
1164
|
+
if (Array.isArray(value)) {
|
|
1165
|
+
const copy2 = value.map(
|
|
1166
|
+
(item, index) => cloneTransferRegistration(item, `${path}/${index}`, seen)
|
|
1167
|
+
);
|
|
1168
|
+
seen.delete(value);
|
|
1169
|
+
return Object.freeze(copy2);
|
|
1170
|
+
}
|
|
1171
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1172
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
1173
|
+
throw new ContentTransferError(
|
|
1174
|
+
"unsupported-registration-metadata",
|
|
1175
|
+
"Transfer registration metadata must use plain objects and arrays",
|
|
1176
|
+
path
|
|
1177
|
+
);
|
|
1178
|
+
const copy = Object.create(prototype);
|
|
1179
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
1180
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1181
|
+
if (!("value" in descriptor))
|
|
1182
|
+
throw new ContentTransferError(
|
|
1183
|
+
"unsupported-registration-metadata",
|
|
1184
|
+
"Transfer registration metadata cannot use accessors",
|
|
1185
|
+
`${path}/${String(key)}`
|
|
1186
|
+
);
|
|
1187
|
+
Object.defineProperty(copy, key, {
|
|
1188
|
+
value: cloneTransferRegistration(
|
|
1189
|
+
descriptor.value,
|
|
1190
|
+
`${path}/${String(key)}`,
|
|
1191
|
+
seen
|
|
1192
|
+
),
|
|
1193
|
+
enumerable: descriptor.enumerable === true,
|
|
1194
|
+
writable: false,
|
|
1195
|
+
configurable: false
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
seen.delete(value);
|
|
1199
|
+
return Object.freeze(copy);
|
|
1200
|
+
}
|
|
1201
|
+
function resolveTransferRegistry(...groups) {
|
|
1202
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1203
|
+
const result = [];
|
|
1204
|
+
for (const group of groups)
|
|
1205
|
+
for (const item of group) {
|
|
1206
|
+
const copy = cloneTransferRegistration(item);
|
|
1207
|
+
if (!copy.id.trim() || ids.has(copy.id))
|
|
1208
|
+
throw new ContentTransferError(
|
|
1209
|
+
"duplicate-registration",
|
|
1210
|
+
`Transfer id ${JSON.stringify(copy.id)} is empty or registered more than once`
|
|
1211
|
+
);
|
|
1212
|
+
ids.add(copy.id);
|
|
1213
|
+
result.push(copy);
|
|
1214
|
+
}
|
|
1215
|
+
return Object.freeze(result);
|
|
1216
|
+
}
|
|
1217
|
+
function createSurfaceTransferHandle(options) {
|
|
1218
|
+
const declarations = resolveTransferRegistry(
|
|
1219
|
+
options.manifest.values.map((value) => ({ ...value, id: value.name }))
|
|
1220
|
+
);
|
|
1221
|
+
return {
|
|
1222
|
+
instanceId: options.instanceId,
|
|
1223
|
+
surfaceName: options.manifest.surfaceName,
|
|
1224
|
+
isMounted: options.isMounted,
|
|
1225
|
+
async capture(signal) {
|
|
1226
|
+
stop(signal);
|
|
1227
|
+
if (!options.isMounted())
|
|
1228
|
+
throw new ContentTransferError(
|
|
1229
|
+
"surface-unmounted",
|
|
1230
|
+
"The captured surface is no longer mounted"
|
|
1231
|
+
);
|
|
1232
|
+
const revisionBefore = options.getRevision?.();
|
|
1233
|
+
const scope = await options.getScope();
|
|
1234
|
+
stop(signal);
|
|
1235
|
+
if (!options.isMounted())
|
|
1236
|
+
throw new ContentTransferError(
|
|
1237
|
+
"surface-unmounted",
|
|
1238
|
+
"The surface closed during capture"
|
|
1239
|
+
);
|
|
1240
|
+
if (revisionBefore !== void 0 && options.getRevision?.() !== revisionBefore)
|
|
1241
|
+
throw new ContentTransferError(
|
|
1242
|
+
"surface-changed",
|
|
1243
|
+
"The surface changed during capture; capture again"
|
|
1244
|
+
);
|
|
1245
|
+
const value = {};
|
|
1246
|
+
for (const declaration of declarations) {
|
|
1247
|
+
if (Object.hasOwn(scope, declaration.name) && scope[declaration.name] !== void 0) {
|
|
1248
|
+
Object.defineProperty(value, declaration.name, {
|
|
1249
|
+
value: cloneSurfaceValue(
|
|
1250
|
+
scope[declaration.name],
|
|
1251
|
+
`/${enc(declaration.name)}`
|
|
1252
|
+
),
|
|
1253
|
+
enumerable: true,
|
|
1254
|
+
writable: true,
|
|
1255
|
+
configurable: true
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
return directSource(
|
|
1260
|
+
{ kind: "json", value },
|
|
1261
|
+
{
|
|
1262
|
+
sourceId: options.instanceId,
|
|
1263
|
+
revision: revisionBefore ?? uid(),
|
|
1264
|
+
label: options.manifest.label ?? options.manifest.surfaceName,
|
|
1265
|
+
unsavedChanges: options.unsavedChanges?.() ?? false,
|
|
1266
|
+
sections: declarations.filter((d) => Object.hasOwn(value, d.name)).map((d) => ({
|
|
1267
|
+
id: d.name,
|
|
1268
|
+
label: d.label,
|
|
1269
|
+
path: `/${enc(d.name)}`,
|
|
1270
|
+
includedByDefault: d.includedByDefault ?? true
|
|
1271
|
+
})),
|
|
1272
|
+
projection: {
|
|
1273
|
+
rules: [
|
|
1274
|
+
...options.projection?.rules ?? [],
|
|
1275
|
+
...declarations.map((d) => ({
|
|
1276
|
+
target: "payload",
|
|
1277
|
+
path: `/${enc(d.name)}`,
|
|
1278
|
+
exportable: d.exportable ?? true,
|
|
1279
|
+
classification: d.classification ?? "ordinary"
|
|
1280
|
+
}))
|
|
1281
|
+
]
|
|
1282
|
+
},
|
|
1283
|
+
...options.limits ? { limits: options.limits } : {}
|
|
1284
|
+
}
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
function limitJson(value, limits, omissions, path = "", depth = 0) {
|
|
1290
|
+
if (limits.maxDepth != null && depth > limits.maxDepth) {
|
|
1291
|
+
omissions.push({ path, reason: "truncated" });
|
|
1292
|
+
return null;
|
|
1293
|
+
}
|
|
1294
|
+
if (typeof value === "string" && limits.maxStringChars != null && Array.from(value).length > limits.maxStringChars) {
|
|
1295
|
+
const chars = Array.from(value);
|
|
1296
|
+
omissions.push({
|
|
1297
|
+
path,
|
|
1298
|
+
reason: "truncated",
|
|
1299
|
+
count: chars.length - limits.maxStringChars
|
|
1300
|
+
});
|
|
1301
|
+
return chars.slice(0, limits.maxStringChars).join("");
|
|
1302
|
+
}
|
|
1303
|
+
if (Array.isArray(value)) {
|
|
1304
|
+
const count = Math.min(value.length, limits.maxArrayItems ?? value.length);
|
|
1305
|
+
if (count < value.length)
|
|
1306
|
+
omissions.push({
|
|
1307
|
+
path,
|
|
1308
|
+
reason: "truncated",
|
|
1309
|
+
count: value.length - count
|
|
1310
|
+
});
|
|
1311
|
+
return value.slice(0, count).map(
|
|
1312
|
+
(item, index) => limitJson(item, limits, omissions, `${path}/${index}`, depth + 1)
|
|
1313
|
+
);
|
|
1314
|
+
}
|
|
1315
|
+
if (value && typeof value === "object")
|
|
1316
|
+
return Object.fromEntries(
|
|
1317
|
+
Object.entries(value).map(([key, item]) => [
|
|
1318
|
+
key,
|
|
1319
|
+
limitJson(item, limits, omissions, `${path}/${enc(key)}`, depth + 1)
|
|
1320
|
+
])
|
|
1321
|
+
);
|
|
1322
|
+
return value;
|
|
1323
|
+
}
|
|
1324
|
+
function limitPayload(payload, limits, omissions) {
|
|
1325
|
+
limitKeys.forEach((key) => assertLimit(limits[key], key));
|
|
1326
|
+
if (payload.kind === "rows") {
|
|
1327
|
+
const count = Math.min(
|
|
1328
|
+
payload.rows.length,
|
|
1329
|
+
limits.maxRows ?? payload.rows.length
|
|
1330
|
+
);
|
|
1331
|
+
if (count < payload.rows.length)
|
|
1332
|
+
omissions.push({
|
|
1333
|
+
path: "/rows",
|
|
1334
|
+
reason: "truncated",
|
|
1335
|
+
count: payload.rows.length - count
|
|
1336
|
+
});
|
|
1337
|
+
return {
|
|
1338
|
+
...payload,
|
|
1339
|
+
rows: payload.rows.slice(0, count).map(
|
|
1340
|
+
(row, index) => limitJson(row, limits, omissions, `/rows/${index}`)
|
|
1341
|
+
)
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
if (payload.kind === "json" || payload.kind === "registered")
|
|
1345
|
+
return { ...payload, value: limitJson(payload.value, limits, omissions) };
|
|
1346
|
+
return {
|
|
1347
|
+
...payload,
|
|
1348
|
+
text: limitJson(payload.text, limits, omissions)
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
function applyTransferLimits(draft, limits) {
|
|
1352
|
+
const omissions = [];
|
|
1353
|
+
const payload = limitPayload(draft.payload, limits, omissions);
|
|
1354
|
+
return omissions.length ? revise(draft, payload, omissions, "Applied preparation limits") : draft;
|
|
1355
|
+
}
|
|
1356
|
+
//# sourceMappingURL=content-transfer.cjs.map
|