relic-mcp 0.14.4 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +67 -1
- package/dist/cover/cover.js +211 -40
- package/dist/cover/frame.html +104 -6
- package/dist/cover/pdf.worker.mjs +73 -3
- package/dist/cover/styles.css +698 -27
- package/dist/native-secure-open.d.ts +1 -1
- package/dist/native-secure-open.js +8 -9
- package/dist/program/index.html +4 -0
- package/dist/program/index.js +35403 -0
- package/dist/relic-mcp.js +7538 -1919
- package/mcp-servers.json +2 -2
- package/native/build.mjs +164 -0
- package/native/secure-enclave/relic-se.swift +243 -0
- package/native/secure-open.c +39 -39
- package/native/secure-open.d.ts +1 -1
- package/package.json +2 -2
- package/prebuilds/darwin-arm64/build-info.json +2 -2
- package/prebuilds/darwin-arm64/relic-se +0 -0
- package/prebuilds/darwin-arm64/relic-se-build-info.json +44 -0
- package/prebuilds/darwin-arm64/secure_open.node +0 -0
- package/prebuilds/darwin-x64/build-info.json +2 -2
- package/prebuilds/darwin-x64/relic-se +0 -0
- package/prebuilds/darwin-x64/relic-se-build-info.json +44 -0
- package/prebuilds/darwin-x64/secure_open.node +0 -0
- package/prebuilds/linux-arm64-gnu/build-info.json +2 -2
- package/prebuilds/linux-arm64-gnu/secure_open.node +0 -0
- package/prebuilds/linux-x64-gnu/build-info.json +2 -2
- package/prebuilds/linux-x64-gnu/secure_open.node +0 -0
- package/scripts/prove-packed-native.mjs +42 -3
- package/skills/relic/SKILL.md +39 -0
- package/src/comments.ts +138 -24
- package/src/cover/chrome.ts +46 -18
- package/src/cover/inputs.ts +1 -1
- package/src/cover/render.ts +9 -0
- package/src/decisions.ts +13 -7
- package/src/device.ts +1196 -0
- package/src/files.ts +4 -3
- package/src/index.ts +27 -0
- package/src/inventory.ts +1 -1
- package/src/native-secure-open.ts +13 -13
- package/src/otio-bundle.ts +433 -152
- package/src/private.ts +1322 -0
- package/src/program/browser.ts +124 -0
- package/src/program/build.ts +1339 -0
- package/src/program/confine.ts +162 -0
- package/src/program/jail-probe.ts +192 -0
- package/src/program/policy.ts +128 -0
- package/src/program/ramdisk.ts +371 -0
- package/src/program/render.ts +793 -0
- package/src/program/report.ts +92 -0
- package/src/publish.ts +202 -33
- package/src/republish.ts +69 -6
- package/src/resolve-anchor.ts +9 -6
- package/src/secure-enclave.ts +228 -0
- package/src/server.ts +588 -12
- package/src/share.ts +35 -1
- package/src/state.ts +201 -47
- package/src/upload.ts +140 -0
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
{
|
|
11
11
|
"name": "relic",
|
|
12
12
|
"description": "Publish a file from your machine as an encrypted, shareable link. The agent encrypts locally, uploads only ciphertext, and hands back a URL whose fragment holds the key, so the service stores something it cannot read.",
|
|
13
|
-
"version": "0.
|
|
13
|
+
"version": "0.16.0",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"author": {
|
|
16
16
|
"name": "The Bushido Collective",
|
|
@@ -21,6 +21,6 @@
|
|
|
21
21
|
}
|
|
22
22
|
],
|
|
23
23
|
"metadata": {
|
|
24
|
-
"version": "0.
|
|
24
|
+
"version": "0.16.0"
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "relic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Publish a file from your machine as an encrypted, shareable link. The agent encrypts locally, uploads only ciphertext, and hands back a URL whose fragment holds the key, so the service stores something it cannot read.",
|
|
5
5
|
"mcpServers": "./mcp-servers.json",
|
|
6
6
|
"author": {
|
package/README.md
CHANGED
|
@@ -100,7 +100,10 @@ created `0600` inside a `0700` directory, and redirectable with
|
|
|
100
100
|
Copying the state file to another machine moves the ability with it.
|
|
101
101
|
- **The file holds the key and the token.** They are never printed, logged,
|
|
102
102
|
or returned by any tool result. The service stores only a SHA-256 of the
|
|
103
|
-
token, so it cannot reconstruct either one.
|
|
103
|
+
token, so it cannot reconstruct either one. On this machine the version keys
|
|
104
|
+
are plaintext in the file. Other users cannot read it (`0600`), but any
|
|
105
|
+
process running as this user can, so the file is protected only from other
|
|
106
|
+
users and from the service.
|
|
104
107
|
- **Losing the file changes nothing for existing links**; it only ends that
|
|
105
108
|
machine's ability to update those relics.
|
|
106
109
|
- **A takedown is permanent.** A removed relic answers `relic_removed`
|
|
@@ -210,6 +213,66 @@ flags (`--host-resolver-rules=MAP * ~NOTFOUND`,
|
|
|
210
213
|
`--webrtc-ip-handling-policy=disable_non_proxied_udp`, `--disable-quic`) so no
|
|
211
214
|
relic plaintext leaves the machine during rendering.
|
|
212
215
|
|
|
216
|
+
### OTIO programs
|
|
217
|
+
|
|
218
|
+
Publishing or republishing an `.otioz` archive, or a `.otiod` directory packaged
|
|
219
|
+
into one, also renders each OpenTimelineIO timeline to an H.264/AAC MP4 in the
|
|
220
|
+
same local headless browser shell. The render runs in memory: the browser context is
|
|
221
|
+
off-the-record, the HTTP cache is disabled, and the page can reach only the
|
|
222
|
+
archive's own media through the virtual origin
|
|
223
|
+
(`https://program.relic.invalid`). The MP4s and a `relic/program.json` manifest
|
|
224
|
+
go into the archive before encryption, so the program is encrypted like the
|
|
225
|
+
source. `content.otio` and every media entry keep their bytes.
|
|
226
|
+
|
|
227
|
+
A timeline that cannot be rendered is published focus-only, and the publish
|
|
228
|
+
result says why for each one: no browser, no H.264/AAC encoder, an unsupported
|
|
229
|
+
effect, codec, transition, schema, or timing, missing media, or the size cap.
|
|
230
|
+
The result's `program` field reports the status and every timeline. A bare
|
|
231
|
+
`.otio` file has no archive to carry a program and publishes as given. No
|
|
232
|
+
browser or encoder is installed or downloaded, and nothing is sent to a remote
|
|
233
|
+
renderer.
|
|
234
|
+
|
|
235
|
+
Programs render only on macOS in this version. On Linux no browser starts, and
|
|
236
|
+
every OTIO timeline is published focus-only with the reason "program rendering is
|
|
237
|
+
confined only on macOS in this version, so this timeline has no program".
|
|
238
|
+
|
|
239
|
+
On macOS each publish creates a private RAM volume and runs the headless shell
|
|
240
|
+
under one deny-default macOS sandbox profile that the kernel applies to every
|
|
241
|
+
process the shell starts. Chromium's own sandbox is off, since it cannot start
|
|
242
|
+
inside that profile, so the profile is the containment. The profile allows
|
|
243
|
+
writes only to the RAM volume; reads only of the system libraries, fonts, and
|
|
244
|
+
the shell's own directory; no network; no home directory; and executing only the
|
|
245
|
+
shell. GPU and hardware video decoding are disabled. Before every launch a probe
|
|
246
|
+
runs unconfined,
|
|
247
|
+
which must succeed, and then confined, where each outside write, read, exec and
|
|
248
|
+
connection must be refused and the inside write must succeed. Any deviation, or
|
|
249
|
+
a volume that cannot be created, means no browser starts and every timeline is
|
|
250
|
+
focus-only with that reason. The browser gets a minimal environment: `HOME`,
|
|
251
|
+
temporary and cache directories inside the volume, and a fixed `PATH`. Nothing
|
|
252
|
+
else is inherited.
|
|
253
|
+
|
|
254
|
+
After rendering, the process unmounts and detaches its volume and removes only
|
|
255
|
+
the device and directory it created. SIGINT and SIGTERM abort active renders and
|
|
256
|
+
wait for browser and volume cleanup before exit. SIGKILL, power loss, and other
|
|
257
|
+
forced termination cannot run cleanup; the RAM mount may persist until system
|
|
258
|
+
cleanup or reboot. If storage this process made cannot be released, the publish
|
|
259
|
+
is refused. Reasons in the archive are fixed sentences,
|
|
260
|
+
never local paths or environment values. The archive always carries its
|
|
261
|
+
manifest, so a reader never falls back to a full composite the publisher did not
|
|
262
|
+
select. An archive that already contains `relic/` entries, a bad
|
|
263
|
+
`RELIC_PROGRAM_MAX_BYTES`, an unsafe entry name, or a manifest that cannot fit
|
|
264
|
+
the cap is refused with an exact reason rather than published without one.
|
|
265
|
+
|
|
266
|
+
What the sandbox does not cover: with Chromium's sandbox off, one profile covers
|
|
267
|
+
every process the shell starts, so an exploit in a media decoder reaches
|
|
268
|
+
everything that profile allows, though nothing beyond it. Seatbelt does not filter
|
|
269
|
+
every system call, and its profile language is private and deprecated, so a
|
|
270
|
+
future macOS release may change it; the per-launch probe then fails closed. The
|
|
271
|
+
macOS crash reporter sits outside the profile and can write crash details,
|
|
272
|
+
including strings a compromised process chose, to `~/Library/Logs/DiagnosticReports`.
|
|
273
|
+
No claim is made about swap. Anything readable inside the volume, such as the
|
|
274
|
+
host name, can appear in the published MP4.
|
|
275
|
+
|
|
213
276
|
## Tools
|
|
214
277
|
|
|
215
278
|
| Tool | Input | Notes |
|
|
@@ -238,6 +301,9 @@ relic plaintext leaves the machine during rendering.
|
|
|
238
301
|
| `RELIC_MCP_PORT`, `RELIC_MCP_HOST` | HTTP bind. Defaults to `127.0.0.1:7333`. |
|
|
239
302
|
| `RELIC_MCP_ALLOWED_ORIGINS` | Comma-separated `Origin` allowlist for HTTP. |
|
|
240
303
|
| `RELIC_CHROME_PATH` | Explicit path to Chrome, Chromium, or Edge binary for cover rendering. Set to `none` or `disabled` to skip covers. |
|
|
304
|
+
| `RELIC_OTIO_CHROME_PATH` | Path to a `chrome-headless-shell` build whose file layout matches the r1228 headless shell, for OTIO program rendering on macOS. Unset, the newest one under `~/.omp/owned-shells` is used. Any other file or layout is refused. Full Chrome is never used for programs. Set to `none` or `disabled` to publish OTIO without programs. |
|
|
305
|
+
| `RELIC_PROGRAM_MAX_BYTES` | Lowers the program budget for the whole archive, never raises it. Must be a positive integer. A timeline that does not fit is focus-only with the reason `size-cap-exceeded`. |
|
|
306
|
+
| `RELIC_PROGRAM_ENCODER_OFF` | `1` makes the page report its H.264/AAC encoder as absent. Used by proofs, to exercise the focus-only path. |
|
|
241
307
|
| `RELIC_FFMPEG_PATH` | Path to ffmpeg binary for generating video frame and audio waveform/artwork cover images. |
|
|
242
308
|
|
|
243
309
|
## Protocol
|
package/dist/cover/cover.js
CHANGED
|
@@ -1202,6 +1202,68 @@ var require_build = __commonJS(function(exports) {
|
|
|
1202
1202
|
exports.default = LinesAndColumns;
|
|
1203
1203
|
});
|
|
1204
1204
|
|
|
1205
|
+
// ../relic-viewer/src/content.ts
|
|
1206
|
+
var CONTENT_BATCH_BYTES = 8 * 1024 * 1024;
|
|
1207
|
+
var CONTENT_HEAD_BYTES = 65536;
|
|
1208
|
+
var CONTENT_TAIL_BYTES = 65557;
|
|
1209
|
+
var MAX_CONTENT_READ_BYTES = 16 * 1024 * 1024;
|
|
1210
|
+
var DOCUMENT_RENDER_LIMIT_BYTES = 100 * 1024 * 1024;
|
|
1211
|
+
|
|
1212
|
+
class ContentStoreError extends Error {
|
|
1213
|
+
constructor(cause) {
|
|
1214
|
+
super("The browser could not hold the decrypted content.", { cause });
|
|
1215
|
+
this.name = "ContentStoreError";
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
function sliceBlob(blob, start, end, type) {
|
|
1219
|
+
try {
|
|
1220
|
+
return blob.slice(start, end, type);
|
|
1221
|
+
} catch (cause) {
|
|
1222
|
+
throw new ContentStoreError(cause);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
async function readBlob(blob) {
|
|
1226
|
+
try {
|
|
1227
|
+
const bytes = new Uint8Array(await blob.arrayBuffer());
|
|
1228
|
+
if (bytes.length !== blob.size)
|
|
1229
|
+
throw new Error("short Blob read");
|
|
1230
|
+
return bytes;
|
|
1231
|
+
} catch (cause) {
|
|
1232
|
+
throw new ContentStoreError(cause);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
function contentFromParts(base, head, tail) {
|
|
1236
|
+
return {
|
|
1237
|
+
size: base.size,
|
|
1238
|
+
head,
|
|
1239
|
+
tail,
|
|
1240
|
+
blob(type) {
|
|
1241
|
+
return sliceBlob(base, 0, base.size, type);
|
|
1242
|
+
},
|
|
1243
|
+
async read(start, end) {
|
|
1244
|
+
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start || end > base.size || end - start > MAX_CONTENT_READ_BYTES) {
|
|
1245
|
+
throw new RangeError("Invalid or oversized content read");
|
|
1246
|
+
}
|
|
1247
|
+
return readBlob(sliceBlob(base, start, end));
|
|
1248
|
+
},
|
|
1249
|
+
async readWhole(limit) {
|
|
1250
|
+
if (!Number.isSafeInteger(limit) || limit < 0 || limit > DOCUMENT_RENDER_LIMIT_BYTES) {
|
|
1251
|
+
throw new RangeError("Invalid whole-content read limit");
|
|
1252
|
+
}
|
|
1253
|
+
if (base.size > limit)
|
|
1254
|
+
return;
|
|
1255
|
+
return readBlob(base);
|
|
1256
|
+
}
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
async function contentFromBlob(blob) {
|
|
1260
|
+
const [head, tail] = await Promise.all([
|
|
1261
|
+
readBlob(sliceBlob(blob, 0, Math.min(blob.size, CONTENT_HEAD_BYTES))),
|
|
1262
|
+
readBlob(sliceBlob(blob, Math.max(0, blob.size - CONTENT_TAIL_BYTES), blob.size))
|
|
1263
|
+
]);
|
|
1264
|
+
return contentFromParts(blob, head, tail);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1205
1267
|
// ../relic-viewer/src/markdown.ts
|
|
1206
1268
|
function escapeHtml(value) {
|
|
1207
1269
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
@@ -1240,7 +1302,7 @@ function inline(source) {
|
|
|
1240
1302
|
});
|
|
1241
1303
|
return out.split(SENTINEL).join("");
|
|
1242
1304
|
}
|
|
1243
|
-
function
|
|
1305
|
+
function parseMarkdown(source) {
|
|
1244
1306
|
const lines = source.replace(/\r\n?/g, `
|
|
1245
1307
|
`).split(`
|
|
1246
1308
|
`);
|
|
@@ -1250,7 +1312,7 @@ function renderMarkdown(source) {
|
|
|
1250
1312
|
const flushParagraph = () => {
|
|
1251
1313
|
if (paragraph.length === 0)
|
|
1252
1314
|
return;
|
|
1253
|
-
out.push(
|
|
1315
|
+
out.push({ kind: "paragraph", text: paragraph.join(" ") });
|
|
1254
1316
|
paragraph = [];
|
|
1255
1317
|
};
|
|
1256
1318
|
while (index < lines.length) {
|
|
@@ -1266,22 +1328,24 @@ function renderMarkdown(source) {
|
|
|
1266
1328
|
index++;
|
|
1267
1329
|
}
|
|
1268
1330
|
index++;
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
`))}</code></pre>`);
|
|
1331
|
+
out.push({ kind: "code", language, text: body.join(`
|
|
1332
|
+
`) });
|
|
1272
1333
|
continue;
|
|
1273
1334
|
}
|
|
1274
1335
|
const heading = /^(#{1,6})\s+(.*)$/.exec(line);
|
|
1275
1336
|
if (heading !== null) {
|
|
1276
1337
|
flushParagraph();
|
|
1277
|
-
|
|
1278
|
-
|
|
1338
|
+
out.push({
|
|
1339
|
+
kind: "heading",
|
|
1340
|
+
level: heading[1].length,
|
|
1341
|
+
text: heading[2]
|
|
1342
|
+
});
|
|
1279
1343
|
index++;
|
|
1280
1344
|
continue;
|
|
1281
1345
|
}
|
|
1282
1346
|
if (/^\s*(-\s*-\s*-|\*\s*\*\s*\*|_\s*_\s*_)[-*_\s]*$/.test(line)) {
|
|
1283
1347
|
flushParagraph();
|
|
1284
|
-
out.push("
|
|
1348
|
+
out.push({ kind: "rule" });
|
|
1285
1349
|
index++;
|
|
1286
1350
|
continue;
|
|
1287
1351
|
}
|
|
@@ -1292,8 +1356,8 @@ function renderMarkdown(source) {
|
|
|
1292
1356
|
quoted.push(lines[index].replace(/^\s*>\s?/, ""));
|
|
1293
1357
|
index++;
|
|
1294
1358
|
}
|
|
1295
|
-
out.push(
|
|
1296
|
-
`))}
|
|
1359
|
+
out.push({ kind: "quote", blocks: parseMarkdown(quoted.join(`
|
|
1360
|
+
`)) });
|
|
1297
1361
|
continue;
|
|
1298
1362
|
}
|
|
1299
1363
|
if (/^\s*([-*+]|\d+\.)\s+/.test(line)) {
|
|
@@ -1311,9 +1375,7 @@ function renderMarkdown(source) {
|
|
|
1311
1375
|
}
|
|
1312
1376
|
index++;
|
|
1313
1377
|
}
|
|
1314
|
-
|
|
1315
|
-
const body = items.map((item) => `<li>${inline(item)}</li>`).join("");
|
|
1316
|
-
out.push(`<${tag}>${body}</${tag}>`);
|
|
1378
|
+
out.push({ kind: "list", ordered, items });
|
|
1317
1379
|
continue;
|
|
1318
1380
|
}
|
|
1319
1381
|
if (line.includes("|") && isTableSeparator(lines[index + 1])) {
|
|
@@ -1325,7 +1387,7 @@ function renderMarkdown(source) {
|
|
|
1325
1387
|
rows.push(splitRow(lines[index]));
|
|
1326
1388
|
index++;
|
|
1327
1389
|
}
|
|
1328
|
-
out.push(
|
|
1390
|
+
out.push({ kind: "table", header, rows });
|
|
1329
1391
|
continue;
|
|
1330
1392
|
}
|
|
1331
1393
|
if (line.trim().length === 0) {
|
|
@@ -1337,7 +1399,34 @@ function renderMarkdown(source) {
|
|
|
1337
1399
|
index++;
|
|
1338
1400
|
}
|
|
1339
1401
|
flushParagraph();
|
|
1340
|
-
return out
|
|
1402
|
+
return out;
|
|
1403
|
+
}
|
|
1404
|
+
function renderBlock(block) {
|
|
1405
|
+
switch (block.kind) {
|
|
1406
|
+
case "heading":
|
|
1407
|
+
return `<h${block.level}>${inline(block.text)}</h${block.level}>`;
|
|
1408
|
+
case "paragraph":
|
|
1409
|
+
return `<p>${inline(block.text)}</p>`;
|
|
1410
|
+
case "code": {
|
|
1411
|
+
const attribute = block.language.length > 0 ? ` data-language="${escapeHtml(block.language)}"` : "";
|
|
1412
|
+
return `<pre${attribute}><code>${escapeHtml(block.text)}</code></pre>`;
|
|
1413
|
+
}
|
|
1414
|
+
case "rule":
|
|
1415
|
+
return "<hr>";
|
|
1416
|
+
case "quote":
|
|
1417
|
+
return `<blockquote>${block.blocks.map(renderBlock).join(`
|
|
1418
|
+
`)}</blockquote>`;
|
|
1419
|
+
case "list": {
|
|
1420
|
+
const tag = block.ordered ? "ol" : "ul";
|
|
1421
|
+
const body = block.items.map((item) => `<li>${inline(item)}</li>`);
|
|
1422
|
+
return `<${tag}>${body.join("")}</${tag}>`;
|
|
1423
|
+
}
|
|
1424
|
+
case "table":
|
|
1425
|
+
return '<div class="md-scroll"><table><thead><tr>' + block.header.map((cell) => `<th>${inline(cell)}</th>`).join("") + "</tr></thead><tbody>" + block.rows.map((row) => `<tr>${row.map((cell) => `<td>${inline(cell)}</td>`).join("")}</tr>`).join("") + "</tbody></table></div>";
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
function renderMarkdown(source) {
|
|
1429
|
+
return parseMarkdown(source).map(renderBlock).join(`
|
|
1341
1430
|
`);
|
|
1342
1431
|
}
|
|
1343
1432
|
function isTableSeparator(line) {
|
|
@@ -44726,6 +44815,81 @@ globalThis.pdfjsLib = {
|
|
|
44726
44815
|
XfaLayer
|
|
44727
44816
|
};
|
|
44728
44817
|
|
|
44818
|
+
// ../relic-viewer/src/pdf.ts
|
|
44819
|
+
var PDF_INITIAL_BYTES = 1024 * 1024;
|
|
44820
|
+
var PDF_RANGE_CHUNK_SIZE = 65536;
|
|
44821
|
+
|
|
44822
|
+
class RelicPdfRangeTransport extends PDFDataRangeTransport {
|
|
44823
|
+
content;
|
|
44824
|
+
onError;
|
|
44825
|
+
aborted = false;
|
|
44826
|
+
failed = false;
|
|
44827
|
+
constructor(content, initialData, onError) {
|
|
44828
|
+
super(content.size, initialData);
|
|
44829
|
+
this.content = content;
|
|
44830
|
+
this.onError = onError;
|
|
44831
|
+
}
|
|
44832
|
+
requestDataRange(begin, end) {
|
|
44833
|
+
if (this.aborted || this.failed)
|
|
44834
|
+
return;
|
|
44835
|
+
this.readRange(begin, end).then((bytes) => {
|
|
44836
|
+
if (!this.aborted && !this.failed)
|
|
44837
|
+
this.onDataRange(begin, bytes);
|
|
44838
|
+
}).catch((error) => {
|
|
44839
|
+
if (this.aborted || this.failed)
|
|
44840
|
+
return;
|
|
44841
|
+
this.failed = true;
|
|
44842
|
+
this.onError(error);
|
|
44843
|
+
});
|
|
44844
|
+
}
|
|
44845
|
+
abort() {
|
|
44846
|
+
this.aborted = true;
|
|
44847
|
+
}
|
|
44848
|
+
async readRange(begin, end) {
|
|
44849
|
+
if (!Number.isSafeInteger(begin) || !Number.isSafeInteger(end) || begin < 0 || end < begin || end > this.content.size) {
|
|
44850
|
+
throw new RangeError("Invalid PDF range");
|
|
44851
|
+
}
|
|
44852
|
+
const length = end - begin;
|
|
44853
|
+
if (length <= MAX_CONTENT_READ_BYTES) {
|
|
44854
|
+
const bytes = await this.content.read(begin, end);
|
|
44855
|
+
if (bytes.byteLength !== length)
|
|
44856
|
+
throw new Error("Short PDF range read");
|
|
44857
|
+
return bytes;
|
|
44858
|
+
}
|
|
44859
|
+
const bytes = new Uint8Array(length);
|
|
44860
|
+
for (let start = begin;start < end; start += MAX_CONTENT_READ_BYTES) {
|
|
44861
|
+
if (this.aborted)
|
|
44862
|
+
return new Uint8Array(0);
|
|
44863
|
+
const stop = Math.min(start + MAX_CONTENT_READ_BYTES, end);
|
|
44864
|
+
const chunk = await this.content.read(start, stop);
|
|
44865
|
+
if (chunk.byteLength !== stop - start)
|
|
44866
|
+
throw new Error("Short PDF range read");
|
|
44867
|
+
bytes.set(chunk, start - begin);
|
|
44868
|
+
}
|
|
44869
|
+
return bytes;
|
|
44870
|
+
}
|
|
44871
|
+
}
|
|
44872
|
+
async function createPdfLoadingTask(content, signal) {
|
|
44873
|
+
const initialData = await content.read(0, Math.min(content.size, PDF_INITIAL_BYTES));
|
|
44874
|
+
if (signal?.aborted)
|
|
44875
|
+
throw new Error("PDF loading cancelled");
|
|
44876
|
+
if (initialData.byteLength !== Math.min(content.size, PDF_INITIAL_BYTES))
|
|
44877
|
+
throw new Error("Short PDF initial read");
|
|
44878
|
+
const { promise: failure, reject: rejectFailure } = Promise.withResolvers();
|
|
44879
|
+
failure.catch(() => {
|
|
44880
|
+
return;
|
|
44881
|
+
});
|
|
44882
|
+
const range = new RelicPdfRangeTransport(content, initialData, rejectFailure);
|
|
44883
|
+
const task = getDocument({
|
|
44884
|
+
range,
|
|
44885
|
+
length: content.size,
|
|
44886
|
+
rangeChunkSize: PDF_RANGE_CHUNK_SIZE,
|
|
44887
|
+
disableAutoFetch: true,
|
|
44888
|
+
disableStream: true
|
|
44889
|
+
});
|
|
44890
|
+
return { task, failure };
|
|
44891
|
+
}
|
|
44892
|
+
|
|
44729
44893
|
// ../relic-viewer/src/cover/pdf-trim.ts
|
|
44730
44894
|
var PDF_GUTTER_PX = 20;
|
|
44731
44895
|
var PDF_CANVAS_WIDTH = 600;
|
|
@@ -44778,21 +44942,21 @@ function trimPdfPage(ink, pageWidth) {
|
|
|
44778
44942
|
// ../relic-viewer/src/cover/pdf.ts
|
|
44779
44943
|
var PDF_CANVAS_HEIGHT = 315;
|
|
44780
44944
|
var PDF_DEVICE_SCALE = 2;
|
|
44781
|
-
var workerInitialized = false;
|
|
44782
44945
|
function ensurePdfWorker() {
|
|
44783
|
-
|
|
44784
|
-
GlobalWorkerOptions.workerPort = new Worker("/pdf.worker.mjs", {
|
|
44785
|
-
type: "module"
|
|
44786
|
-
});
|
|
44787
|
-
workerInitialized = true;
|
|
44788
|
-
}
|
|
44946
|
+
GlobalWorkerOptions.workerSrc = "/pdf.worker.mjs";
|
|
44789
44947
|
}
|
|
44790
|
-
async function renderPdfCover(
|
|
44948
|
+
async function renderPdfCover(content, root) {
|
|
44791
44949
|
ensurePdfWorker();
|
|
44792
|
-
let pdfDoc;
|
|
44793
44950
|
try {
|
|
44794
|
-
const
|
|
44795
|
-
|
|
44951
|
+
const { task, failure } = await createPdfLoadingTask(content);
|
|
44952
|
+
try {
|
|
44953
|
+
const pdfDoc = await Promise.race([task.promise, failure]);
|
|
44954
|
+
return await renderPdfCoverDocument(pdfDoc, root, failure);
|
|
44955
|
+
} finally {
|
|
44956
|
+
await task.destroy().catch(() => {
|
|
44957
|
+
return;
|
|
44958
|
+
});
|
|
44959
|
+
}
|
|
44796
44960
|
} catch (err) {
|
|
44797
44961
|
return {
|
|
44798
44962
|
ok: false,
|
|
@@ -44800,9 +44964,11 @@ async function renderPdfCover(bytes, root) {
|
|
|
44800
44964
|
detail: err instanceof Error ? err.message : String(err)
|
|
44801
44965
|
};
|
|
44802
44966
|
}
|
|
44967
|
+
}
|
|
44968
|
+
async function renderPdfCoverDocument(pdfDoc, root, failure) {
|
|
44803
44969
|
let page;
|
|
44804
44970
|
try {
|
|
44805
|
-
page = await pdfDoc.getPage(1);
|
|
44971
|
+
page = await Promise.race([pdfDoc.getPage(1), failure]);
|
|
44806
44972
|
} catch (err) {
|
|
44807
44973
|
return {
|
|
44808
44974
|
ok: false,
|
|
@@ -44827,11 +44993,14 @@ async function renderPdfCover(bytes, root) {
|
|
|
44827
44993
|
};
|
|
44828
44994
|
}
|
|
44829
44995
|
try {
|
|
44830
|
-
await
|
|
44831
|
-
|
|
44832
|
-
|
|
44833
|
-
|
|
44834
|
-
|
|
44996
|
+
await Promise.race([
|
|
44997
|
+
page.render({
|
|
44998
|
+
canvasContext: measureCtx,
|
|
44999
|
+
viewport: refViewport,
|
|
45000
|
+
canvas: measureCanvas
|
|
45001
|
+
}).promise,
|
|
45002
|
+
failure
|
|
45003
|
+
]);
|
|
44835
45004
|
} catch (err) {
|
|
44836
45005
|
return {
|
|
44837
45006
|
ok: false,
|
|
@@ -44864,11 +45033,14 @@ async function renderPdfCover(bytes, root) {
|
|
|
44864
45033
|
};
|
|
44865
45034
|
}
|
|
44866
45035
|
try {
|
|
44867
|
-
await
|
|
44868
|
-
|
|
44869
|
-
|
|
44870
|
-
|
|
44871
|
-
|
|
45036
|
+
await Promise.race([
|
|
45037
|
+
page.render({
|
|
45038
|
+
canvasContext: renderCtx,
|
|
45039
|
+
viewport: finalViewport,
|
|
45040
|
+
canvas: renderCanvas
|
|
45041
|
+
}).promise,
|
|
45042
|
+
failure
|
|
45043
|
+
]);
|
|
44872
45044
|
} catch (err) {
|
|
44873
45045
|
return {
|
|
44874
45046
|
ok: false,
|
|
@@ -44896,7 +45068,7 @@ async function fetchInputBlob(input) {
|
|
|
44896
45068
|
if (!res.ok) {
|
|
44897
45069
|
throw new Error(`Failed to fetch input part ${i}: ${res.status}`);
|
|
44898
45070
|
}
|
|
44899
|
-
parts.push(await res.
|
|
45071
|
+
parts.push(await res.blob());
|
|
44900
45072
|
}
|
|
44901
45073
|
return new Blob(parts, { type: input.mimetype });
|
|
44902
45074
|
}
|
|
@@ -45084,8 +45256,7 @@ async function renderKind(request, root) {
|
|
|
45084
45256
|
}
|
|
45085
45257
|
}
|
|
45086
45258
|
case "pdf": {
|
|
45087
|
-
|
|
45088
|
-
return renderPdfCover(new Uint8Array(arrayBuffer), root);
|
|
45259
|
+
return renderPdfCover(await contentFromBlob(blob), root);
|
|
45089
45260
|
}
|
|
45090
45261
|
}
|
|
45091
45262
|
}
|
package/dist/cover/frame.html
CHANGED
|
@@ -11390,15 +11390,91 @@ var MAX_HEADER_BYTES = 2 + (2 + MAX_FILENAME_BYTES + 2 + MAX_MIMETYPE_BYTES + 8
|
|
|
11390
11390
|
var encoder = new TextEncoder;
|
|
11391
11391
|
var decoder = new TextDecoder("utf-8", { fatal: false });
|
|
11392
11392
|
|
|
11393
|
+
// ../relic-format/src/limits.ts
|
|
11394
|
+
var PLAINTEXT_CAP_BYTES = 500 * 1024 * 1024;
|
|
11395
|
+
|
|
11393
11396
|
// ../relic-format/src/rfc8188.ts
|
|
11394
11397
|
var TAG_BYTES = 16;
|
|
11395
11398
|
var DELIMITER_BYTES = 1;
|
|
11396
11399
|
var RECORD_OVERHEAD = TAG_BYTES + DELIMITER_BYTES;
|
|
11397
11400
|
var CEK_INFO = new TextEncoder().encode("Content-Encoding: aes128gcm\x00");
|
|
11398
11401
|
var NONCE_INFO = new TextEncoder().encode("Content-Encoding: nonce\x00");
|
|
11399
|
-
|
|
11400
|
-
|
|
11401
|
-
|
|
11402
|
+
// ../relic-format/src/crc32c.ts
|
|
11403
|
+
var CRC32C_TABLE = new Uint32Array(256);
|
|
11404
|
+
for (let index = 0;index < 256; index++) {
|
|
11405
|
+
let value = index;
|
|
11406
|
+
for (let bit = 0;bit < 8; bit++) {
|
|
11407
|
+
value = value & 1 ? value >>> 1 ^ 2197175160 : value >>> 1;
|
|
11408
|
+
}
|
|
11409
|
+
CRC32C_TABLE[index] = value >>> 0;
|
|
11410
|
+
}
|
|
11411
|
+
// ../relic-format/src/sha256.ts
|
|
11412
|
+
var K = new Uint32Array([
|
|
11413
|
+
1116352408,
|
|
11414
|
+
1899447441,
|
|
11415
|
+
3049323471,
|
|
11416
|
+
3921009573,
|
|
11417
|
+
961987163,
|
|
11418
|
+
1508970993,
|
|
11419
|
+
2453635748,
|
|
11420
|
+
2870763221,
|
|
11421
|
+
3624381080,
|
|
11422
|
+
310598401,
|
|
11423
|
+
607225278,
|
|
11424
|
+
1426881987,
|
|
11425
|
+
1925078388,
|
|
11426
|
+
2162078206,
|
|
11427
|
+
2614888103,
|
|
11428
|
+
3248222580,
|
|
11429
|
+
3835390401,
|
|
11430
|
+
4022224774,
|
|
11431
|
+
264347078,
|
|
11432
|
+
604807628,
|
|
11433
|
+
770255983,
|
|
11434
|
+
1249150122,
|
|
11435
|
+
1555081692,
|
|
11436
|
+
1996064986,
|
|
11437
|
+
2554220882,
|
|
11438
|
+
2821834349,
|
|
11439
|
+
2952996808,
|
|
11440
|
+
3210313671,
|
|
11441
|
+
3336571891,
|
|
11442
|
+
3584528711,
|
|
11443
|
+
113926993,
|
|
11444
|
+
338241895,
|
|
11445
|
+
666307205,
|
|
11446
|
+
773529912,
|
|
11447
|
+
1294757372,
|
|
11448
|
+
1396182291,
|
|
11449
|
+
1695183700,
|
|
11450
|
+
1986661051,
|
|
11451
|
+
2177026350,
|
|
11452
|
+
2456956037,
|
|
11453
|
+
2730485921,
|
|
11454
|
+
2820302411,
|
|
11455
|
+
3259730800,
|
|
11456
|
+
3345764771,
|
|
11457
|
+
3516065817,
|
|
11458
|
+
3600352804,
|
|
11459
|
+
4094571909,
|
|
11460
|
+
275423344,
|
|
11461
|
+
430227734,
|
|
11462
|
+
506948616,
|
|
11463
|
+
659060556,
|
|
11464
|
+
883997877,
|
|
11465
|
+
958139571,
|
|
11466
|
+
1322822218,
|
|
11467
|
+
1537002063,
|
|
11468
|
+
1747873779,
|
|
11469
|
+
1955562222,
|
|
11470
|
+
2024104815,
|
|
11471
|
+
2227730452,
|
|
11472
|
+
2361852424,
|
|
11473
|
+
2428436474,
|
|
11474
|
+
2756734187,
|
|
11475
|
+
3204031479,
|
|
11476
|
+
3329325298
|
|
11477
|
+
]);
|
|
11402
11478
|
// ../relic-format/src/id.ts
|
|
11403
11479
|
var RESERVED_SEGMENTS = [
|
|
11404
11480
|
"abuse",
|
|
@@ -11431,7 +11507,10 @@ function foldAliases(lowered) {
|
|
|
11431
11507
|
return out;
|
|
11432
11508
|
}
|
|
11433
11509
|
// ../relic-format/src/otio-zip.ts
|
|
11434
|
-
var
|
|
11510
|
+
var MAX_INFLATED_ZIP_BYTES = 100 * 1024 * 1024;
|
|
11511
|
+
var OTIO_DOCUMENT_MAX_BYTES = 100 * 1024 * 1024;
|
|
11512
|
+
var MAX_ZIP_DIRECTORY_BYTES = 16 * 1024 * 1024;
|
|
11513
|
+
var ZIP_READ_CHUNK_BYTES = 8 * 1024 * 1024;
|
|
11435
11514
|
var MAX_OTIO_BUNDLE_MEDIA_ENTRIES = 65535 - 3;
|
|
11436
11515
|
var CRC_TABLE = new Uint32Array(256);
|
|
11437
11516
|
for (let i = 0;i < 256; i++) {
|
|
@@ -11453,6 +11532,12 @@ var documentSequences = new WeakMap;
|
|
|
11453
11532
|
|
|
11454
11533
|
// ../relic-format/src/otio-identity.ts
|
|
11455
11534
|
var mediaWitnessCache = new WeakMap;
|
|
11535
|
+
// ../relic-format/src/otio-program.ts
|
|
11536
|
+
var MAX_OTIO_PROGRAM_MANIFEST_BYTES = 16 * 1024 * 1024;
|
|
11537
|
+
var OTIO_PROGRAM_MAX_BYTES = 100 * 1024 * 1024;
|
|
11538
|
+
var UTF8 = new TextEncoder;
|
|
11539
|
+
var MAX_FRAMES = Number.MAX_SAFE_INTEGER;
|
|
11540
|
+
var MAX_AUDIO_SAMPLES = Number.MAX_SAFE_INTEGER;
|
|
11456
11541
|
// ../relic-format/src/share.ts
|
|
11457
11542
|
var SHARE_PROOF_INFO = new TextEncoder().encode("relic/share-proof/v1");
|
|
11458
11543
|
// ../relic-viewer/src/sandbox.ts
|
|
@@ -11802,12 +11887,25 @@ function createScrollportWatch(onChange) {
|
|
|
11802
11887
|
}
|
|
11803
11888
|
function concealMarks(marks, watch, live = true) {
|
|
11804
11889
|
watch.sync(marks.map((mark) => mark.node));
|
|
11805
|
-
|
|
11890
|
+
if (live) {
|
|
11891
|
+
for (const { node, subject } of marks) {
|
|
11892
|
+
if (subject === node && node.getAttribute(CONCEALED_ATTRIBUTE) === "hidden")
|
|
11893
|
+
node.removeAttribute(CONCEALED_ATTRIBUTE);
|
|
11894
|
+
}
|
|
11895
|
+
}
|
|
11896
|
+
const measured = [];
|
|
11806
11897
|
for (const { node, subject } of marks) {
|
|
11807
11898
|
const id = node.getAttribute("data-comment-id");
|
|
11808
11899
|
if (id === null || id === "")
|
|
11809
11900
|
continue;
|
|
11810
|
-
|
|
11901
|
+
measured.push({
|
|
11902
|
+
node,
|
|
11903
|
+
id,
|
|
11904
|
+
concealment: !live || !isRendered(subject) ? "hidden" : !watch.inView(node) ? "offscreen" : "shown"
|
|
11905
|
+
});
|
|
11906
|
+
}
|
|
11907
|
+
const merged = new Map;
|
|
11908
|
+
for (const { node, id, concealment } of measured) {
|
|
11811
11909
|
setConcealed(node, concealment === "shown" ? undefined : concealment);
|
|
11812
11910
|
const previous = merged.get(id);
|
|
11813
11911
|
merged.set(id, mergeReason(previous, concealment));
|