@nyte-ai/host 0.0.0-stage → 0.0.19
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/LICENSE +21 -0
- package/README.md +6 -2
- package/THIRD-PARTY-NOTICES.md +866 -0
- package/dist/chunks/canvas-preview-0cmbkp2j.js +65 -0
- package/dist/chunks/canvas-preview-0nq75h9k.js +7 -0
- package/dist/chunks/canvas-preview-11nadadc.js +241 -0
- package/dist/chunks/canvas-preview-1sq7z9jq.js +17 -0
- package/dist/chunks/canvas-preview-2jwbe14k.js +130 -0
- package/dist/chunks/canvas-preview-2zdyh9ny.js +26 -0
- package/dist/chunks/canvas-preview-31w7xm8z.js +34 -0
- package/dist/chunks/canvas-preview-4b6sayhg.js +295 -0
- package/dist/chunks/canvas-preview-4e0pxa32.js +63 -0
- package/dist/chunks/canvas-preview-518dh8by.js +169 -0
- package/dist/chunks/canvas-preview-55s0y68g.js +387 -0
- package/dist/chunks/canvas-preview-5ekjx6sw.js +613 -0
- package/dist/chunks/canvas-preview-5mex725c.js +14 -0
- package/dist/chunks/canvas-preview-5z63am63.js +295 -0
- package/dist/chunks/canvas-preview-65xqjw7f.js +20 -0
- package/dist/chunks/canvas-preview-69fkk9yn.js +95 -0
- package/dist/chunks/canvas-preview-8xc0vn7v.js +458 -0
- package/dist/chunks/canvas-preview-9aqczacs.js +73 -0
- package/dist/chunks/canvas-preview-af86jz8t.js +141 -0
- package/dist/chunks/canvas-preview-aq5j4p25.js +240 -0
- package/dist/chunks/canvas-preview-b2v8wfnz.js +36 -0
- package/dist/chunks/canvas-preview-befkjea2.js +60 -0
- package/dist/chunks/canvas-preview-bxnqt8cd.js +103 -0
- package/dist/chunks/canvas-preview-cawy6vbz.js +352 -0
- package/dist/chunks/canvas-preview-crmzxxt8.js +518 -0
- package/dist/chunks/canvas-preview-drctegz4.js +591 -0
- package/dist/chunks/canvas-preview-e3vnkkc5.js +134 -0
- package/dist/chunks/canvas-preview-ec2mt7es.js +108 -0
- package/dist/chunks/canvas-preview-fxa0rfb0.js +61 -0
- package/dist/chunks/canvas-preview-g5fkjs34.js +598 -0
- package/dist/chunks/canvas-preview-jeddjex8.js +166 -0
- package/dist/chunks/canvas-preview-k1yg4fgp.js +571 -0
- package/dist/chunks/canvas-preview-kdqgrstc.js +194 -0
- package/dist/chunks/canvas-preview-kn7kvbfj.js +12 -0
- package/dist/chunks/canvas-preview-kp75gb63.js +141 -0
- package/dist/chunks/canvas-preview-mgcx2b2p.js +271 -0
- package/dist/chunks/canvas-preview-n2a35zv5.js +81 -0
- package/dist/chunks/canvas-preview-pkzqg96v.js +63 -0
- package/dist/chunks/canvas-preview-rp4x6y1h.js +144 -0
- package/dist/chunks/canvas-preview-s94ebwf5.js +112 -0
- package/dist/chunks/canvas-preview-skzwvw4d.js +23 -0
- package/dist/chunks/canvas-preview-srgfpdmf.js +60 -0
- package/dist/chunks/canvas-preview-tjhabynd.js +252 -0
- package/dist/chunks/canvas-preview-tnqe7k0b.js +374 -0
- package/dist/chunks/canvas-preview-vaatfznm.js +189 -0
- package/dist/chunks/canvas-preview-vm9zdp2t.js +398 -0
- package/dist/chunks/canvas-preview-w54ymtb3.js +272 -0
- package/dist/chunks/canvas-preview-wg80rbc2.js +46 -0
- package/dist/chunks/canvas-preview-y5y6ghaw.js +125 -0
- package/dist/chunks/canvas-preview-ycepb05s.js +176 -0
- package/dist/chunks/canvas-preview-yjz0f7n4.js +50 -0
- package/dist/chunks/canvas-preview-zaaj99wd.js +244 -0
- package/dist/src/canvas-preview.d.ts +29 -0
- package/dist/src/canvas-preview.js +271 -0
- package/dist/src/credentials.d.ts +44 -0
- package/dist/src/credentials.js +10 -0
- package/dist/src/deploy/cloudflare/worker.d.ts +50 -0
- package/dist/src/deploy/cloudflare/worker.js +196 -0
- package/dist/src/deploy/vercel/nitro.config.d.ts +2 -0
- package/dist/src/deploy/vercel/nitro.config.js +30 -0
- package/dist/src/deploy/vercel/server/routes/[...].d.ts +2 -0
- package/dist/src/deploy/vercel/server/routes/[...].js +18 -0
- package/dist/src/deploy/vercel/server/utils/host.d.ts +13 -0
- package/dist/src/deploy/vercel/server/utils/host.js +7 -0
- package/dist/src/deploy/vercel/server/utils/repair.d.ts +1 -0
- package/dist/src/deploy/vercel/server/utils/repair.js +6 -0
- package/dist/src/deploy/vercel/server/utils/step.d.ts +10 -0
- package/dist/src/deploy/vercel/server/utils/step.js +6 -0
- package/dist/src/deploy/vercel/server/workflows/drive.d.ts +14 -0
- package/dist/src/deploy/vercel/server/workflows/drive.js +13 -0
- package/dist/src/durable-object.d.ts +14 -0
- package/dist/src/durable-object.js +231 -0
- package/dist/src/hash.d.ts +1 -0
- package/dist/src/hash.js +6 -0
- package/dist/src/identity-key.d.ts +11 -0
- package/dist/src/identity-key.js +9 -0
- package/dist/src/identity.d.ts +12 -0
- package/dist/src/identity.js +6 -0
- package/dist/src/images/exif.d.ts +1 -0
- package/dist/src/images/exif.js +6 -0
- package/dist/src/images/node.d.ts +37 -0
- package/dist/src/images/node.js +12 -0
- package/dist/src/images/photon.d.ts +3 -0
- package/dist/src/images/photon.js +7 -0
- package/dist/src/images/worker.d.ts +1 -0
- package/dist/src/images/worker.js +23 -0
- package/dist/src/index.d.ts +20 -0
- package/dist/src/index.js +121 -0
- package/dist/src/key-file.d.ts +1 -0
- package/dist/src/key-file.js +7 -0
- package/dist/src/listen-fetch.d.ts +8 -0
- package/dist/src/listen-fetch.js +9 -0
- package/dist/src/listen.d.ts +4 -0
- package/dist/src/listen.js +10 -0
- package/dist/src/local/command.d.ts +20 -0
- package/dist/src/local/command.js +12 -0
- package/dist/src/local/feed.d.ts +7 -0
- package/dist/src/local/feed.js +6 -0
- package/dist/src/local/files.d.ts +18 -0
- package/dist/src/local/files.js +13 -0
- package/dist/src/local/git-watch.d.ts +15 -0
- package/dist/src/local/git-watch.js +7 -0
- package/dist/src/local/git.d.ts +5 -0
- package/dist/src/local/git.js +13 -0
- package/dist/src/local/github.d.ts +30 -0
- package/dist/src/local/github.js +9 -0
- package/dist/src/local/identity.d.ts +3 -0
- package/dist/src/local/identity.js +7 -0
- package/dist/src/local/jobs.d.ts +7 -0
- package/dist/src/local/jobs.js +7 -0
- package/dist/src/local/legacy-trust.d.ts +10 -0
- package/dist/src/local/legacy-trust.js +6 -0
- package/dist/src/local/patch.d.ts +16 -0
- package/dist/src/local/patch.js +17 -0
- package/dist/src/local/provider.d.ts +11 -0
- package/dist/src/local/provider.js +20 -0
- package/dist/src/local/snapshots.d.ts +7 -0
- package/dist/src/local/snapshots.js +11 -0
- package/dist/src/local/text.d.ts +1 -0
- package/dist/src/local/text.js +6 -0
- package/dist/src/local/tree.d.ts +50 -0
- package/dist/src/local/tree.js +16 -0
- package/dist/src/local/workspace-migration.d.ts +2 -0
- package/dist/src/local/workspace-migration.js +7 -0
- package/dist/src/local/worktrees.d.ts +46 -0
- package/dist/src/local/worktrees.js +14 -0
- package/dist/src/mcp/config.d.ts +75 -0
- package/dist/src/mcp/config.js +12 -0
- package/dist/src/mcp/connect.d.ts +15 -0
- package/dist/src/mcp/connect.js +9 -0
- package/dist/src/mcp/hub.d.ts +19 -0
- package/dist/src/mcp/hub.js +11 -0
- package/dist/src/mcp/oauth.d.ts +46 -0
- package/dist/src/mcp/oauth.js +12 -0
- package/dist/src/mcp/tools.d.ts +29 -0
- package/dist/src/mcp/tools.js +12 -0
- package/dist/src/open.d.ts +14 -0
- package/dist/src/open.js +46 -0
- package/dist/src/plugins/loader.d.ts +21 -0
- package/dist/src/plugins/loader.js +11 -0
- package/dist/src/postgres.d.ts +19 -0
- package/dist/src/postgres.js +8 -0
- package/dist/src/providers.d.ts +16 -0
- package/dist/src/providers.js +112 -0
- package/dist/src/secrets.d.ts +12 -0
- package/dist/src/secrets.js +8 -0
- package/dist/src/serve/binding.d.ts +39 -0
- package/dist/src/serve/binding.js +6 -0
- package/dist/src/serve/handler.d.ts +15 -0
- package/dist/src/serve/handler.js +8 -0
- package/dist/src/serve/stream-server.d.ts +6 -0
- package/dist/src/serve/stream-server.js +6 -0
- package/dist/src/session-index.d.ts +11 -0
- package/dist/src/session-index.js +11 -0
- package/dist/src/shell-env.d.ts +14 -0
- package/dist/src/shell-env.js +108 -0
- package/dist/src/sqlite-workers.d.ts +6 -0
- package/dist/src/sqlite-workers.js +6 -0
- package/dist/src/sqlite.d.ts +8 -0
- package/dist/src/sqlite.js +11 -0
- package/dist/src/store-rows.d.ts +13 -0
- package/dist/src/store-rows.js +26 -0
- package/dist/src/store-usage.d.ts +25 -0
- package/dist/src/store-usage.js +19 -0
- package/dist/src/usage-history.d.ts +54 -0
- package/dist/src/usage-history.js +14 -0
- package/dist/src/usage-scan.d.ts +24 -0
- package/dist/src/usage-scan.js +65 -0
- package/dist/src/usage-service.d.ts +23 -0
- package/dist/src/usage-service.js +16 -0
- package/dist/src/usage-summary.d.ts +9 -0
- package/dist/src/usage-summary.js +16 -0
- package/dist/src/usage-types.d.ts +86 -0
- package/dist/src/usage-types.js +0 -0
- package/dist/src/usage-worker.d.ts +1 -0
- package/dist/src/usage-worker.js +29 -0
- package/dist/src/usage.d.ts +195 -0
- package/dist/src/usage.js +23 -0
- package/package.json +140 -3
- package/src/canvas-preview.ts +403 -0
- package/src/credentials.ts +294 -0
- package/src/deploy/cloudflare/README.md +3 -0
- package/src/deploy/cloudflare/worker.ts +306 -0
- package/src/deploy/cloudflare/wrangler.jsonc +12 -0
- package/src/deploy/vercel/README.md +3 -0
- package/src/deploy/vercel/nitro.config.ts +29 -0
- package/src/deploy/vercel/server/routes/[...].ts +10 -0
- package/src/deploy/vercel/server/utils/host.ts +104 -0
- package/src/deploy/vercel/server/utils/repair.ts +35 -0
- package/src/deploy/vercel/server/utils/step.ts +25 -0
- package/src/deploy/vercel/server/workflows/drive.ts +46 -0
- package/src/deploy/vercel/tsconfig.json +9 -0
- package/src/deploy/vercel/types/bun.d.ts +2 -0
- package/src/durable-object.ts +386 -0
- package/src/hash.ts +5 -0
- package/src/identity-key.ts +20 -0
- package/src/identity.ts +66 -0
- package/src/images/exif.ts +69 -0
- package/src/images/node.ts +163 -0
- package/src/images/photon.ts +141 -0
- package/src/images/worker.ts +29 -0
- package/src/index.ts +39 -0
- package/src/key-file.ts +53 -0
- package/src/listen-fetch.ts +409 -0
- package/src/listen.ts +228 -0
- package/src/local/command.ts +161 -0
- package/src/local/feed.ts +70 -0
- package/src/local/files.ts +817 -0
- package/src/local/git-watch.ts +199 -0
- package/src/local/git.ts +919 -0
- package/src/local/github.ts +691 -0
- package/src/local/identity.ts +21 -0
- package/src/local/jobs.ts +573 -0
- package/src/local/legacy-trust.ts +105 -0
- package/src/local/patch.ts +334 -0
- package/src/local/provider.ts +184 -0
- package/src/local/snapshots.ts +778 -0
- package/src/local/text.ts +9 -0
- package/src/local/tree.ts +398 -0
- package/src/local/workspace-migration.ts +95 -0
- package/src/local/worktrees.ts +239 -0
- package/src/mcp/config.ts +330 -0
- package/src/mcp/connect.ts +92 -0
- package/src/mcp/hub.ts +836 -0
- package/src/mcp/oauth.ts +436 -0
- package/src/mcp/tools.ts +333 -0
- package/src/open.ts +449 -0
- package/src/plugins/loader.ts +186 -0
- package/src/postgres.ts +492 -0
- package/src/providers.ts +135 -0
- package/src/secrets.ts +31 -0
- package/src/serve/binding.ts +629 -0
- package/src/serve/handler.ts +362 -0
- package/src/serve/stream-server.ts +176 -0
- package/src/session-index.ts +99 -0
- package/src/shell-env.ts +153 -0
- package/src/sqlite-workers.ts +283 -0
- package/src/sqlite.ts +553 -0
- package/src/store-rows.ts +110 -0
- package/src/store-usage.ts +347 -0
- package/src/usage-history.ts +219 -0
- package/src/usage-scan.ts +81 -0
- package/src/usage-service.ts +82 -0
- package/src/usage-summary.ts +79 -0
- package/src/usage-types.ts +87 -0
- package/src/usage-worker.ts +24 -0
- package/src/usage.ts +776 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readErrorCode2
|
|
3
|
+
} from "./canvas-preview-mgcx2b2p.js";
|
|
4
|
+
|
|
5
|
+
// src/key-file.ts
|
|
6
|
+
import { link, mkdir, open, readFile, unlink } from "node:fs/promises";
|
|
7
|
+
import { dirname } from "node:path";
|
|
8
|
+
import { SdkError } from "@nyte-ai/schema";
|
|
9
|
+
async function readKeyFile2(path, name) {
|
|
10
|
+
const bytes = await readFile(path).then((stored) => new Uint8Array(stored), (error) => {
|
|
11
|
+
if (readErrorCode2(error) === "ENOENT")
|
|
12
|
+
return mintKeyFile(path);
|
|
13
|
+
throw error;
|
|
14
|
+
}).catch((error) => {
|
|
15
|
+
throw new SdkError("unavailable", `The ${name} at ${path} cannot be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
16
|
+
});
|
|
17
|
+
if (bytes.length !== 32)
|
|
18
|
+
throw new SdkError("invalid", `The ${name} at ${path} is ${bytes.length} bytes, not 32.`);
|
|
19
|
+
return bytes;
|
|
20
|
+
}
|
|
21
|
+
async function mintKeyFile(path) {
|
|
22
|
+
await mkdir(dirname(path), { recursive: true, mode: 448 });
|
|
23
|
+
const bytes = crypto.getRandomValues(new Uint8Array(32));
|
|
24
|
+
const temp = `${path}.${crypto.randomUUID()}`;
|
|
25
|
+
const file = await open(temp, "wx", 384);
|
|
26
|
+
try {
|
|
27
|
+
await file.writeFile(bytes);
|
|
28
|
+
await file.sync();
|
|
29
|
+
} finally {
|
|
30
|
+
await file.close();
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
await link(temp, path);
|
|
34
|
+
return bytes;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (readErrorCode2(error) !== "EEXIST")
|
|
37
|
+
throw error;
|
|
38
|
+
return new Uint8Array(await readFile(path));
|
|
39
|
+
} finally {
|
|
40
|
+
await unlink(temp).catch(() => {
|
|
41
|
+
return;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { readKeyFile2 };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {
|
|
2
|
+
photonImages2
|
|
3
|
+
} from "./canvas-preview-ec2mt7es.js";
|
|
4
|
+
|
|
5
|
+
// src/images/node.ts
|
|
6
|
+
import { readFile } from "node:fs/promises";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { Worker } from "node:worker_threads";
|
|
9
|
+
import { Type } from "typebox";
|
|
10
|
+
import { Value } from "typebox/value";
|
|
11
|
+
var size = Type.Object({ width: Type.Number(), height: Type.Number() });
|
|
12
|
+
var prepareReply2 = Type.Union([
|
|
13
|
+
Type.Object({ id: Type.Integer(), result: Type.Null() }),
|
|
14
|
+
Type.Object({
|
|
15
|
+
id: Type.Integer(),
|
|
16
|
+
result: Type.Object({
|
|
17
|
+
data: Type.String(),
|
|
18
|
+
mimeType: Type.String(),
|
|
19
|
+
resized: Type.Optional(Type.Object({ from: size, to: size }))
|
|
20
|
+
})
|
|
21
|
+
}),
|
|
22
|
+
Type.Object({ id: Type.Integer(), error: Type.String() })
|
|
23
|
+
]);
|
|
24
|
+
var IDLE_MS = 30000;
|
|
25
|
+
function nodeImages2(options = {}) {
|
|
26
|
+
const worker = options.worker ?? new URL(import.meta.url.endsWith(".ts") ? "./worker.ts" : "./worker.js", import.meta.url);
|
|
27
|
+
let wasm;
|
|
28
|
+
const loadWasm = () => wasm ??= readFile(options.wasm ?? resolvePhotonWasmPath2());
|
|
29
|
+
let thread;
|
|
30
|
+
let inProcess;
|
|
31
|
+
return {
|
|
32
|
+
async prepare(bytes, mimeType, limits) {
|
|
33
|
+
if (inProcess !== undefined)
|
|
34
|
+
return inProcess.prepare(bytes, mimeType, limits);
|
|
35
|
+
const module = await loadWasm();
|
|
36
|
+
try {
|
|
37
|
+
thread ??= new PhotonThread(worker, module, (ended) => {
|
|
38
|
+
if (thread === ended)
|
|
39
|
+
thread = undefined;
|
|
40
|
+
});
|
|
41
|
+
return await thread.prepare(bytes, mimeType, limits);
|
|
42
|
+
} catch (cause) {
|
|
43
|
+
if (!(cause instanceof WorkerUnavailable))
|
|
44
|
+
throw cause;
|
|
45
|
+
inProcess ??= photonImages2(module);
|
|
46
|
+
return inProcess.prepare(bytes, mimeType, limits);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function resolvePhotonWasmPath2() {
|
|
52
|
+
return fileURLToPath(import.meta.resolve("@silvia-odwyer/photon/photon_rs_bg.wasm"));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
class WorkerUnavailable extends Error {
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class PhotonThread {
|
|
59
|
+
#worker;
|
|
60
|
+
#pending = new Map;
|
|
61
|
+
#onExit;
|
|
62
|
+
#nextId = 0;
|
|
63
|
+
#idle;
|
|
64
|
+
#started = false;
|
|
65
|
+
constructor(entry, wasm, onExit) {
|
|
66
|
+
this.#onExit = onExit;
|
|
67
|
+
try {
|
|
68
|
+
this.#worker = new Worker(entry, { workerData: wasm });
|
|
69
|
+
} catch (cause) {
|
|
70
|
+
throw new WorkerUnavailable("The image worker cannot start", { cause });
|
|
71
|
+
}
|
|
72
|
+
this.#worker.on("message", (reply) => {
|
|
73
|
+
if (!Value.Check(prepareReply2, reply)) {
|
|
74
|
+
this.#fail(new Error("Malformed image worker reply"));
|
|
75
|
+
this.#worker.terminate();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this.#started = true;
|
|
79
|
+
const request = this.#pending.get(reply.id);
|
|
80
|
+
if (request === undefined)
|
|
81
|
+
return;
|
|
82
|
+
this.#pending.delete(reply.id);
|
|
83
|
+
if ("error" in reply)
|
|
84
|
+
request.reject(new Error(reply.error));
|
|
85
|
+
else
|
|
86
|
+
request.resolve(reply.result ?? undefined);
|
|
87
|
+
this.#settled();
|
|
88
|
+
});
|
|
89
|
+
this.#worker.on("error", (cause) => this.#fail(cause));
|
|
90
|
+
this.#worker.on("exit", (code) => this.#fail(new Error(`The image worker exited with code ${String(code)}`)));
|
|
91
|
+
}
|
|
92
|
+
prepare(bytes, mimeType, limits) {
|
|
93
|
+
const id = this.#nextId++;
|
|
94
|
+
const request = Promise.withResolvers();
|
|
95
|
+
this.#pending.set(id, request);
|
|
96
|
+
clearTimeout(this.#idle);
|
|
97
|
+
this.#idle = undefined;
|
|
98
|
+
this.#worker.ref();
|
|
99
|
+
const copy = bytes.slice();
|
|
100
|
+
this.#worker.postMessage({ id, bytes: copy, mimeType, limits }, [
|
|
101
|
+
copy.buffer
|
|
102
|
+
]);
|
|
103
|
+
return request.promise;
|
|
104
|
+
}
|
|
105
|
+
#settled() {
|
|
106
|
+
if (this.#pending.size > 0)
|
|
107
|
+
return;
|
|
108
|
+
this.#worker.unref();
|
|
109
|
+
this.#idle = setTimeout(() => {
|
|
110
|
+
this.#onExit(this);
|
|
111
|
+
this.#worker.terminate();
|
|
112
|
+
}, IDLE_MS);
|
|
113
|
+
this.#idle.unref();
|
|
114
|
+
}
|
|
115
|
+
#fail(cause) {
|
|
116
|
+
const failure = this.#started ? cause : new WorkerUnavailable("The image worker cannot start", { cause });
|
|
117
|
+
for (const request of this.#pending.values())
|
|
118
|
+
request.reject(failure);
|
|
119
|
+
this.#pending.clear();
|
|
120
|
+
clearTimeout(this.#idle);
|
|
121
|
+
this.#onExit(this);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export { prepareReply2, nodeImages2, resolvePhotonWasmPath2 };
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import {
|
|
2
|
+
listenFetch2
|
|
3
|
+
} from "./canvas-preview-w54ymtb3.js";
|
|
4
|
+
|
|
5
|
+
// src/listen.ts
|
|
6
|
+
import { createServer } from "node:http";
|
|
7
|
+
import { SdkError } from "@nyte-ai/schema";
|
|
8
|
+
import { encodeError, errorStatus } from "@nyte-ai/protocol";
|
|
9
|
+
async function listen2(host, options) {
|
|
10
|
+
const inner = await listenFetch2(host, { ...options, transport: { kind: "fetch" } });
|
|
11
|
+
if (options.transport.kind === "fetch")
|
|
12
|
+
return inner;
|
|
13
|
+
const responses = new Set;
|
|
14
|
+
const bound = await bind(options.transport, (request) => inner.fetch(request), responses).catch(async (error) => {
|
|
15
|
+
await inner.close();
|
|
16
|
+
throw new SdkError("unavailable", `Cannot listen: ${error instanceof Error ? error.message : String(error)}`);
|
|
17
|
+
});
|
|
18
|
+
const disconnectClients = () => {
|
|
19
|
+
inner.disconnectClients();
|
|
20
|
+
for (const response of responses)
|
|
21
|
+
response.destroy();
|
|
22
|
+
};
|
|
23
|
+
let closing = null;
|
|
24
|
+
const close = () => {
|
|
25
|
+
closing ??= (async () => {
|
|
26
|
+
disconnectClients();
|
|
27
|
+
await inner.close();
|
|
28
|
+
await new Promise((done) => {
|
|
29
|
+
bound.server.close(() => done());
|
|
30
|
+
bound.server.closeAllConnections();
|
|
31
|
+
});
|
|
32
|
+
})();
|
|
33
|
+
return closing;
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
address: bound.address,
|
|
37
|
+
fetch: (request) => inner.fetch(request),
|
|
38
|
+
disconnectClients,
|
|
39
|
+
close,
|
|
40
|
+
[Symbol.asyncDispose]: close
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async function bind(transport, fetch, responses) {
|
|
44
|
+
let origin = "http://127.0.0.1";
|
|
45
|
+
const server = createServer({ noDelay: true }, (incoming, outgoing) => {
|
|
46
|
+
respond(incoming, outgoing, origin, fetch, responses);
|
|
47
|
+
});
|
|
48
|
+
await new Promise((listening, failed) => {
|
|
49
|
+
server.once("error", failed);
|
|
50
|
+
server.listen(transport.port ?? 0, transport.hostname ?? "127.0.0.1", () => {
|
|
51
|
+
server.off("error", failed);
|
|
52
|
+
listening();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
const info = server.address();
|
|
56
|
+
if (info === null || typeof info === "string") {
|
|
57
|
+
server.close();
|
|
58
|
+
throw new Error("the socket has no address");
|
|
59
|
+
}
|
|
60
|
+
const hostname = info.address.includes(":") ? `[${info.address}]` : info.address;
|
|
61
|
+
origin = `http://${hostname}:${info.port}`;
|
|
62
|
+
return { server, address: origin };
|
|
63
|
+
}
|
|
64
|
+
async function respond(incoming, outgoing, origin, fetch, responses) {
|
|
65
|
+
const controller = new AbortController;
|
|
66
|
+
responses.add(outgoing);
|
|
67
|
+
outgoing.on("close", () => {
|
|
68
|
+
responses.delete(outgoing);
|
|
69
|
+
if (!outgoing.writableFinished)
|
|
70
|
+
controller.abort();
|
|
71
|
+
});
|
|
72
|
+
let request;
|
|
73
|
+
try {
|
|
74
|
+
request = readRequest(incoming, origin, controller.signal);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
await write(failure(new SdkError("invalid", error instanceof Error ? error.message : String(error))), outgoing);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
await write(await fetch(request), outgoing);
|
|
80
|
+
}
|
|
81
|
+
function readRequest(incoming, origin, signal) {
|
|
82
|
+
const headers = new Headers;
|
|
83
|
+
for (const [name, value] of Object.entries(incoming.headers)) {
|
|
84
|
+
if (name.startsWith(":") || value === undefined)
|
|
85
|
+
continue;
|
|
86
|
+
if (Array.isArray(value))
|
|
87
|
+
for (const each of value)
|
|
88
|
+
headers.append(name, each);
|
|
89
|
+
else
|
|
90
|
+
headers.set(name, value);
|
|
91
|
+
}
|
|
92
|
+
const host = incoming.headers.host;
|
|
93
|
+
const url = new URL(incoming.url ?? "/", host === undefined ? origin : `http://${host}`);
|
|
94
|
+
const method = incoming.method ?? "GET";
|
|
95
|
+
const body = method === "GET" || method === "HEAD" ? {} : { body: streamBody(incoming), duplex: "half" };
|
|
96
|
+
return new Request(url.href, { method, headers, signal, ...body });
|
|
97
|
+
}
|
|
98
|
+
function streamBody(incoming) {
|
|
99
|
+
return new ReadableStream({
|
|
100
|
+
start(controller) {
|
|
101
|
+
incoming.on("data", (chunk) => {
|
|
102
|
+
controller.enqueue(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));
|
|
103
|
+
if ((controller.desiredSize ?? 0) <= 0)
|
|
104
|
+
incoming.pause();
|
|
105
|
+
});
|
|
106
|
+
incoming.on("end", () => controller.close());
|
|
107
|
+
incoming.on("error", (error) => controller.error(error));
|
|
108
|
+
},
|
|
109
|
+
pull() {
|
|
110
|
+
incoming.resume();
|
|
111
|
+
},
|
|
112
|
+
cancel() {
|
|
113
|
+
incoming.destroy();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
async function write(response, outgoing) {
|
|
118
|
+
if (outgoing.destroyed) {
|
|
119
|
+
await response.body?.cancel().catch(() => {
|
|
120
|
+
return;
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const headers = {};
|
|
125
|
+
response.headers.forEach((value, name) => {
|
|
126
|
+
headers[name] = value;
|
|
127
|
+
});
|
|
128
|
+
const cookies = response.headers.getSetCookie();
|
|
129
|
+
if (cookies.length > 0)
|
|
130
|
+
headers["set-cookie"] = cookies;
|
|
131
|
+
outgoing.writeHead(response.status, headers);
|
|
132
|
+
outgoing.flushHeaders();
|
|
133
|
+
if (response.body === null) {
|
|
134
|
+
outgoing.end();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const reader = response.body.getReader();
|
|
138
|
+
const cancel = () => {
|
|
139
|
+
reader.cancel().catch(() => {
|
|
140
|
+
return;
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
outgoing.on("close", cancel);
|
|
144
|
+
try {
|
|
145
|
+
for (;; ) {
|
|
146
|
+
const read = await reader.read();
|
|
147
|
+
if (read.done)
|
|
148
|
+
break;
|
|
149
|
+
if (!outgoing.write(read.value))
|
|
150
|
+
await new Promise((drained) => {
|
|
151
|
+
const done = () => {
|
|
152
|
+
outgoing.off("drain", done);
|
|
153
|
+
outgoing.off("close", done);
|
|
154
|
+
drained();
|
|
155
|
+
};
|
|
156
|
+
outgoing.on("drain", done);
|
|
157
|
+
outgoing.on("close", done);
|
|
158
|
+
});
|
|
159
|
+
if (outgoing.destroyed)
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
outgoing.end();
|
|
163
|
+
} catch {
|
|
164
|
+
outgoing.destroy();
|
|
165
|
+
} finally {
|
|
166
|
+
outgoing.off("close", cancel);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function failure(error) {
|
|
170
|
+
return new Response(JSON.stringify(encodeError(error)), {
|
|
171
|
+
status: errorStatus[error.code],
|
|
172
|
+
headers: { "Content-Type": "application/json" }
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export { listen2 };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// src/local/feed.ts
|
|
2
|
+
import { closable } from "@nyte-ai/core";
|
|
3
|
+
function feed2(start, merge = (_waiting, next) => next) {
|
|
4
|
+
return closable(() => {
|
|
5
|
+
const state = { slot: null, outcome: null, wake: null };
|
|
6
|
+
const signal = () => {
|
|
7
|
+
const resolve = state.wake;
|
|
8
|
+
state.wake = null;
|
|
9
|
+
resolve?.();
|
|
10
|
+
};
|
|
11
|
+
const dispose = start({
|
|
12
|
+
push(value) {
|
|
13
|
+
if (state.outcome !== null)
|
|
14
|
+
return;
|
|
15
|
+
state.slot = { value: state.slot === null ? value : merge(state.slot.value, value) };
|
|
16
|
+
signal();
|
|
17
|
+
},
|
|
18
|
+
fail(error) {
|
|
19
|
+
state.outcome ??= { kind: "failed", error };
|
|
20
|
+
signal();
|
|
21
|
+
},
|
|
22
|
+
end() {
|
|
23
|
+
state.outcome ??= { kind: "ended" };
|
|
24
|
+
signal();
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return {
|
|
28
|
+
async pull() {
|
|
29
|
+
for (;; ) {
|
|
30
|
+
const held = state.slot;
|
|
31
|
+
if (held !== null) {
|
|
32
|
+
state.slot = null;
|
|
33
|
+
return { done: false, value: held.value };
|
|
34
|
+
}
|
|
35
|
+
const outcome = state.outcome;
|
|
36
|
+
if (outcome?.kind === "failed")
|
|
37
|
+
throw outcome.error;
|
|
38
|
+
if (outcome?.kind === "ended")
|
|
39
|
+
return { done: true, value: undefined };
|
|
40
|
+
await new Promise((resolve) => {
|
|
41
|
+
state.wake = resolve;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
close: () => dispose()
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { feed2 };
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// src/sqlite-workers.ts
|
|
2
|
+
import { Worker } from "node:worker_threads";
|
|
3
|
+
import { dev } from "@nyte-ai/core";
|
|
4
|
+
import { Type } from "typebox";
|
|
5
|
+
import { Value } from "typebox/value";
|
|
6
|
+
var rowSchema = Type.Record(Type.String(), Type.Union([Type.Null(), Type.String(), Type.Number(), Type.BigInt()]));
|
|
7
|
+
var replySchema = Type.Union([
|
|
8
|
+
Type.Object({ ready: Type.Literal(true) }),
|
|
9
|
+
Type.Object({ id: Type.Integer(), rows: Type.Array(rowSchema), executionMs: Type.Number() }),
|
|
10
|
+
Type.Object({
|
|
11
|
+
id: Type.Integer(),
|
|
12
|
+
error: Type.String(),
|
|
13
|
+
code: Type.Optional(Type.String()),
|
|
14
|
+
errcode: Type.Optional(Type.Number()),
|
|
15
|
+
errstr: Type.Optional(Type.String())
|
|
16
|
+
})
|
|
17
|
+
]);
|
|
18
|
+
var source = String.raw`
|
|
19
|
+
const { parentPort, workerData } = require("node:worker_threads");
|
|
20
|
+
const { DatabaseSync } = require("node:sqlite");
|
|
21
|
+
const db = new DatabaseSync(workerData.path, {
|
|
22
|
+
readOnly: workerData.role === "reader",
|
|
23
|
+
timeout: 5000,
|
|
24
|
+
});
|
|
25
|
+
if (String(db.prepare("PRAGMA journal_mode").get().journal_mode).toLowerCase() !== "wal")
|
|
26
|
+
throw new Error("SQLite workers require WAL mode");
|
|
27
|
+
if (workerData.role === "checkpoint") {
|
|
28
|
+
const version = db.prepare("SELECT sqlite_version() AS version").get().version;
|
|
29
|
+
const [major, minor] = version.split(".").map(Number);
|
|
30
|
+
if (major < 3 || (major === 3 && minor < 51))
|
|
31
|
+
throw new Error("SQLite " + version + " has no NOOP checkpoint");
|
|
32
|
+
db.exec("PRAGMA synchronous = NORMAL; PRAGMA checkpoint_fullfsync = ON");
|
|
33
|
+
}
|
|
34
|
+
const statements = new Map();
|
|
35
|
+
const frames = workerData.role === "checkpoint" ? db.prepare("PRAGMA wal_checkpoint(NOOP)") : null;
|
|
36
|
+
const checkpoint = workerData.role === "checkpoint" ? db.prepare("PRAGMA wal_checkpoint(PASSIVE)") : null;
|
|
37
|
+
let idle;
|
|
38
|
+
parentPort.on("message", ({ id, sql, params }) => {
|
|
39
|
+
const started = performance.now();
|
|
40
|
+
try {
|
|
41
|
+
let rows = [];
|
|
42
|
+
if (workerData.role === "checkpoint") {
|
|
43
|
+
const { log, checkpointed } = frames.get();
|
|
44
|
+
if (log - checkpointed >= 1000) checkpoint.get();
|
|
45
|
+
clearTimeout(idle);
|
|
46
|
+
idle = setTimeout(() => {
|
|
47
|
+
const { log, checkpointed } = frames.get();
|
|
48
|
+
if (log >= 1000 && checkpointed < log) checkpoint.get();
|
|
49
|
+
}, 100);
|
|
50
|
+
} else {
|
|
51
|
+
const size = params.reduce((total, value) => total + (typeof value === "string" ? value.length : ArrayBuffer.isView(value) ? value.byteLength : 0), 0);
|
|
52
|
+
let statement = size > 65536 ? undefined : statements.get(sql);
|
|
53
|
+
if (statement === undefined) {
|
|
54
|
+
statement = db.prepare(sql);
|
|
55
|
+
if (size <= 65536) {
|
|
56
|
+
if (statements.size >= 16) statements.delete(statements.keys().next().value);
|
|
57
|
+
statements.set(sql, statement);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
rows = statement.all(...params);
|
|
61
|
+
}
|
|
62
|
+
parentPort.postMessage({ id, rows, executionMs: performance.now() - started });
|
|
63
|
+
} catch (error) {
|
|
64
|
+
parentPort.postMessage({ id, error: String(error.message), code: error.code, errcode: error.errcode, errstr: error.errstr });
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
parentPort.postMessage({ ready: true });
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
class SqliteWorker {
|
|
71
|
+
ready;
|
|
72
|
+
worker;
|
|
73
|
+
nextId = 0;
|
|
74
|
+
pending = new Map;
|
|
75
|
+
stop;
|
|
76
|
+
constructor(path, role, unavailable) {
|
|
77
|
+
const worker = new Worker(source, { eval: true, workerData: { path, role } });
|
|
78
|
+
this.worker = worker;
|
|
79
|
+
let started = false;
|
|
80
|
+
let announce = () => {
|
|
81
|
+
return;
|
|
82
|
+
};
|
|
83
|
+
this.ready = new Promise((resolve) => {
|
|
84
|
+
announce = resolve;
|
|
85
|
+
});
|
|
86
|
+
const timeout = setTimeout(() => {
|
|
87
|
+
this.stop(new Error("SQLite worker did not start within 10 seconds"), true);
|
|
88
|
+
}, 1e4);
|
|
89
|
+
timeout.unref();
|
|
90
|
+
this.stop = (cause, notify) => {
|
|
91
|
+
if (this.worker === null)
|
|
92
|
+
return;
|
|
93
|
+
this.worker = null;
|
|
94
|
+
clearTimeout(timeout);
|
|
95
|
+
announce(false);
|
|
96
|
+
for (const request of this.pending.values())
|
|
97
|
+
request.reject(cause);
|
|
98
|
+
this.pending.clear();
|
|
99
|
+
if (notify)
|
|
100
|
+
unavailable(cause, started);
|
|
101
|
+
return worker.terminate();
|
|
102
|
+
};
|
|
103
|
+
worker.on("message", (reply) => {
|
|
104
|
+
if (!Value.Check(replySchema, reply)) {
|
|
105
|
+
this.stop(new Error("Malformed SQLite worker reply"), true);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if ("ready" in reply) {
|
|
109
|
+
clearTimeout(timeout);
|
|
110
|
+
started = true;
|
|
111
|
+
announce(true);
|
|
112
|
+
if (this.pending.size === 0)
|
|
113
|
+
worker.unref();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const request = this.pending.get(reply.id);
|
|
117
|
+
if (request === undefined)
|
|
118
|
+
return;
|
|
119
|
+
this.pending.delete(reply.id);
|
|
120
|
+
if (this.pending.size === 0)
|
|
121
|
+
worker.unref();
|
|
122
|
+
if ("error" in reply) {
|
|
123
|
+
request.reject(Object.assign(new Error(reply.error), reply));
|
|
124
|
+
} else {
|
|
125
|
+
dev("sqlite", "query", {
|
|
126
|
+
connection: role,
|
|
127
|
+
durationMs: performance.now() - request.at,
|
|
128
|
+
executionMs: reply.executionMs
|
|
129
|
+
});
|
|
130
|
+
request.resolve(reply.rows);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
worker.on("error", (cause) => {
|
|
134
|
+
this.stop(cause instanceof Error ? cause : new Error(String(cause)), true);
|
|
135
|
+
});
|
|
136
|
+
worker.on("exit", (code) => {
|
|
137
|
+
this.stop(new Error(`SQLite ${role} worker exited with code ${code}`), true);
|
|
138
|
+
});
|
|
139
|
+
if (role === "checkpoint")
|
|
140
|
+
worker.unref();
|
|
141
|
+
}
|
|
142
|
+
async query(sql, params) {
|
|
143
|
+
if (!await this.ready)
|
|
144
|
+
return;
|
|
145
|
+
const worker = this.worker;
|
|
146
|
+
if (worker === null)
|
|
147
|
+
throw new Error("SQLite worker stopped");
|
|
148
|
+
const id = this.nextId++;
|
|
149
|
+
return new Promise((resolve, reject) => {
|
|
150
|
+
this.pending.set(id, { resolve, reject, at: performance.now() });
|
|
151
|
+
worker.ref();
|
|
152
|
+
try {
|
|
153
|
+
worker.postMessage({ id, sql, params });
|
|
154
|
+
} catch (cause) {
|
|
155
|
+
this.pending.delete(id);
|
|
156
|
+
if (this.pending.size === 0)
|
|
157
|
+
worker.unref();
|
|
158
|
+
reject(cause);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
async close() {
|
|
163
|
+
await this.stop(new Error("SQLite worker closed"), false);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function sqliteWorkers2(database, path) {
|
|
167
|
+
let reader = null;
|
|
168
|
+
let readerUnavailable = false;
|
|
169
|
+
let closed = false;
|
|
170
|
+
let checking = false;
|
|
171
|
+
let checkAgain = false;
|
|
172
|
+
let checkpoint = null;
|
|
173
|
+
const warn = (role, cause) => {
|
|
174
|
+
process.emitWarning(`SQLite ${role} worker unavailable; using the main connection: ${cause.message}`, {
|
|
175
|
+
code: "NYTE_SQLITE_WORKER_UNAVAILABLE"
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
const handBack = (cause) => {
|
|
179
|
+
const stopped = checkpoint;
|
|
180
|
+
checkpoint = null;
|
|
181
|
+
stopped?.close();
|
|
182
|
+
if (closed)
|
|
183
|
+
return;
|
|
184
|
+
database.exec("PRAGMA wal_autocheckpoint = 1000");
|
|
185
|
+
warn("checkpoint", cause);
|
|
186
|
+
};
|
|
187
|
+
try {
|
|
188
|
+
checkpoint = new SqliteWorker(path, "checkpoint", handBack);
|
|
189
|
+
checkpoint.ready.then((ready) => {
|
|
190
|
+
if (ready && !closed)
|
|
191
|
+
database.exec("PRAGMA wal_autocheckpoint = 10000");
|
|
192
|
+
});
|
|
193
|
+
} catch (cause) {
|
|
194
|
+
warn("checkpoint", cause instanceof Error ? cause : new Error(String(cause)));
|
|
195
|
+
}
|
|
196
|
+
const check = () => {
|
|
197
|
+
if (checkpoint === null || closed)
|
|
198
|
+
return;
|
|
199
|
+
if (checking) {
|
|
200
|
+
checkAgain = true;
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
checking = true;
|
|
204
|
+
checkpoint.query("", []).catch((cause) => {
|
|
205
|
+
if (checkpoint !== null)
|
|
206
|
+
handBack(cause instanceof Error ? cause : new Error(String(cause)));
|
|
207
|
+
}).finally(() => {
|
|
208
|
+
checking = false;
|
|
209
|
+
if (checkAgain) {
|
|
210
|
+
checkAgain = false;
|
|
211
|
+
check();
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
return {
|
|
216
|
+
check,
|
|
217
|
+
async read(sql, params) {
|
|
218
|
+
if (closed)
|
|
219
|
+
throw new Error("SQLite store closed");
|
|
220
|
+
if (readerUnavailable)
|
|
221
|
+
return;
|
|
222
|
+
if (reader === null) {
|
|
223
|
+
try {
|
|
224
|
+
reader = new SqliteWorker(path, "reader", (cause, started) => {
|
|
225
|
+
reader = null;
|
|
226
|
+
readerUnavailable = !started;
|
|
227
|
+
warn("reader", cause);
|
|
228
|
+
});
|
|
229
|
+
} catch (cause) {
|
|
230
|
+
readerUnavailable = true;
|
|
231
|
+
warn("reader", cause instanceof Error ? cause : new Error(String(cause)));
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return reader.query(sql, params);
|
|
236
|
+
},
|
|
237
|
+
async close() {
|
|
238
|
+
closed = true;
|
|
239
|
+
await Promise.all([reader?.close(), checkpoint?.close()]);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export { sqliteWorkers2 };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { CanvasPreviewer } from "@nyte-ai/plugin/core/canvas-preview";
|
|
2
|
+
/**
|
|
3
|
+
* Returns the shell a previewer serves: every file of the canvas package's
|
|
4
|
+
* `dist/shell`, base64 by its path from it (`index.html`,
|
|
5
|
+
* `libraries/three.js`), as `@nyte-ai/canvas/shell-files` exports them.
|
|
6
|
+
*
|
|
7
|
+
* @category host
|
|
8
|
+
* @since 0.1.0
|
|
9
|
+
*/
|
|
10
|
+
export type CanvasShellFiles = Readonly<Record<string, string>>;
|
|
11
|
+
/**
|
|
12
|
+
* Finds a Chromium to preview with: `NYTE_CANVAS_CHROMIUM`, then
|
|
13
|
+
* Playwright's headless shell or Chromium, then an installed Chrome or
|
|
14
|
+
* Chromium. Undefined when there is none.
|
|
15
|
+
*
|
|
16
|
+
* @category host
|
|
17
|
+
* @since 0.1.0
|
|
18
|
+
*/
|
|
19
|
+
export declare function findChromium(): string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Returns a previewer over a headless Chromium on this machine
|
|
22
|
+
* (`findChromium`), serving `shell`; it answers `unavailable` when there is
|
|
23
|
+
* no Chromium or it fails to start, and stops the browser when the
|
|
24
|
+
* preview's signal aborts.
|
|
25
|
+
*
|
|
26
|
+
* @category host
|
|
27
|
+
* @since 0.1.0
|
|
28
|
+
*/
|
|
29
|
+
export declare function chromiumCanvasPreview(shell: CanvasShellFiles, executable?: () => string | undefined): CanvasPreviewer;
|