@lloyal-labs/desktop 0.1.0-alpha.6
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 +107 -0
- package/dist/content.d.ts +20 -0
- package/dist/content.d.ts.map +1 -0
- package/dist/content.js +84 -0
- package/dist/content.js.map +1 -0
- package/dist/engine.d.ts +62 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +233 -0
- package/dist/engine.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/preload-channels.d.ts +13 -0
- package/dist/preload-channels.d.ts.map +1 -0
- package/dist/preload-channels.js +16 -0
- package/dist/preload-channels.js.map +1 -0
- package/dist/preload.d.ts +4 -0
- package/dist/preload.d.ts.map +1 -0
- package/dist/preload.js +61 -0
- package/dist/preload.js.map +1 -0
- package/dist/read-bounded.d.ts +24 -0
- package/dist/read-bounded.d.ts.map +1 -0
- package/dist/read-bounded.js +70 -0
- package/dist/read-bounded.js.map +1 -0
- package/dist/window.d.ts +20 -0
- package/dist/window.d.ts.map +1 -0
- package/dist/window.js +49 -0
- package/dist/window.js.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Functional Source License, Version 1.1, Apache 2.0 Future License
|
|
2
|
+
|
|
3
|
+
## Abbreviation
|
|
4
|
+
|
|
5
|
+
FSL-1.1-Apache-2.0
|
|
6
|
+
|
|
7
|
+
## Notice
|
|
8
|
+
|
|
9
|
+
Copyright 2026 Lloyal Labs
|
|
10
|
+
|
|
11
|
+
## Terms and Conditions
|
|
12
|
+
|
|
13
|
+
### Licensor ("We")
|
|
14
|
+
|
|
15
|
+
The party offering the Software under these Terms and Conditions.
|
|
16
|
+
|
|
17
|
+
### The Software
|
|
18
|
+
|
|
19
|
+
The "Software" is each version of the software that we make available under
|
|
20
|
+
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
|
21
|
+
Conditions with the Software.
|
|
22
|
+
|
|
23
|
+
### License Grant
|
|
24
|
+
|
|
25
|
+
Subject to your compliance with this License Grant and the Patents,
|
|
26
|
+
Redistribution and Trademark clauses below, we hereby grant you the right to
|
|
27
|
+
use, copy, modify, create derivative works, publish, and distribute the
|
|
28
|
+
Software for any Permitted Purpose identified below.
|
|
29
|
+
|
|
30
|
+
### Permitted Purpose
|
|
31
|
+
|
|
32
|
+
A "Permitted Purpose" is any purpose other than a Competing Use. A
|
|
33
|
+
"Competing Use" means making the Software available to others in a
|
|
34
|
+
commercial product or service that:
|
|
35
|
+
|
|
36
|
+
1. substitutes for the Software;
|
|
37
|
+
|
|
38
|
+
2. substitutes for any other product or service we offer using the Software
|
|
39
|
+
that exists as of the date we make the Software available; or
|
|
40
|
+
|
|
41
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
42
|
+
|
|
43
|
+
Permitted Purposes specifically include using the Software:
|
|
44
|
+
|
|
45
|
+
1. for your internal use and access;
|
|
46
|
+
|
|
47
|
+
2. for non-commercial education;
|
|
48
|
+
|
|
49
|
+
3. for non-commercial research; and
|
|
50
|
+
|
|
51
|
+
4. in connection with professional services that you provide to a Licensee
|
|
52
|
+
using the Software in accordance with these Terms and Conditions.
|
|
53
|
+
|
|
54
|
+
### Patents
|
|
55
|
+
|
|
56
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our
|
|
57
|
+
patents, the license grant above includes a license under our patents. If you
|
|
58
|
+
make a claim against any party that the Software infringes or contributes to
|
|
59
|
+
the infringement of any patent, then your patent license to the Software ends
|
|
60
|
+
immediately.
|
|
61
|
+
|
|
62
|
+
### Redistribution
|
|
63
|
+
|
|
64
|
+
The Terms and Conditions apply to all copies, modifications and derivatives
|
|
65
|
+
of the Software.
|
|
66
|
+
|
|
67
|
+
If you redistribute any copies, modifications or derivatives of the Software,
|
|
68
|
+
you must include a copy of or a link to these Terms and Conditions and not
|
|
69
|
+
remove any copyright notices provided in or with the Software.
|
|
70
|
+
|
|
71
|
+
### Disclaimer
|
|
72
|
+
|
|
73
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND,
|
|
74
|
+
INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE,
|
|
75
|
+
MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
76
|
+
|
|
77
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO
|
|
78
|
+
THE SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
|
|
79
|
+
DAMAGES, EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
80
|
+
|
|
81
|
+
### Trademarks
|
|
82
|
+
|
|
83
|
+
Except for displaying the License Details and identifying us as the origin of
|
|
84
|
+
the Software, you have no right under these Terms and Conditions to use our
|
|
85
|
+
trademarks, trade names, service marks or product names.
|
|
86
|
+
|
|
87
|
+
## Grant of Future License
|
|
88
|
+
|
|
89
|
+
We hereby irrevocably grant you an additional license to use the Software
|
|
90
|
+
under the Apache License, Version 2.0 that is effective on the second
|
|
91
|
+
anniversary of the date we make the Software available. On or after that
|
|
92
|
+
date, you may use the Software under the Apache License, Version 2.0, in
|
|
93
|
+
which case the following will apply:
|
|
94
|
+
|
|
95
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
96
|
+
use this file except in compliance with the License.
|
|
97
|
+
|
|
98
|
+
You may obtain a copy of the License at
|
|
99
|
+
|
|
100
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
101
|
+
|
|
102
|
+
Unless required by applicable law or agreed to in writing, software
|
|
103
|
+
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
104
|
+
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
105
|
+
|
|
106
|
+
See the License for the specific language governing permissions and
|
|
107
|
+
limitations under the License.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Descriptor } from '@lloyal-labs/media';
|
|
2
|
+
/** The scheme's authority: `standard` schemes require one. Every route lives under the path, as on HTTP. */
|
|
3
|
+
export declare const CONTENT_ORIGIN = "attachment://store";
|
|
4
|
+
/**
|
|
5
|
+
* Must run BEFORE app ready — afterwards the registration is ignored SILENTLY.
|
|
6
|
+
* All four privileges are load-bearing: `standard` gives the scheme an origin
|
|
7
|
+
* so CSP can name it, `secure` keeps it out of the mixed-content rules,
|
|
8
|
+
* `supportFetchAPI` lets `fetch` address it, and without `corsEnabled` the
|
|
9
|
+
* scheme takes no part in CORS at all, so the handler's allow-origin header
|
|
10
|
+
* is never consulted and every fetch fails before reaching it.
|
|
11
|
+
*/
|
|
12
|
+
export declare function registerContentScheme(): void;
|
|
13
|
+
/**
|
|
14
|
+
* Serve reads from the project store; hand an upload to `ingest`. Reading needs
|
|
15
|
+
* no lock: OCI blobs are content-addressed and immutable. Writing is not ours —
|
|
16
|
+
* `ingest` belongs to whichever process owns the store, and takes a signal so a
|
|
17
|
+
* deadline here can actually stop it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function serveContentScheme(projectRoot: string, ingest: (bytes: Uint8Array, signal: AbortSignal) => Promise<Descriptor>): void;
|
|
20
|
+
//# sourceMappingURL=content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../src/content.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,4GAA4G;AAC5G,eAAO,MAAM,cAAc,uBAAuB,CAAC;AAEnD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAI5C;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,UAAU,CAAC,GACtE,IAAI,CAuCN"}
|
package/dist/content.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CONTENT_ORIGIN = void 0;
|
|
4
|
+
exports.registerContentScheme = registerContentScheme;
|
|
5
|
+
exports.serveContentScheme = serveContentScheme;
|
|
6
|
+
/**
|
|
7
|
+
* The content plane on a desktop: a custom scheme, not a port.
|
|
8
|
+
*
|
|
9
|
+
* A loopback server would be reachable by every process on the machine, and
|
|
10
|
+
* these routes authenticate nothing by design — a digest is identity, not
|
|
11
|
+
* authorization. `attachment://` is reachable only from this app's own
|
|
12
|
+
* renderers and opens no socket at all. The routes are rig's `resolveContent`,
|
|
13
|
+
* the same table the served host answers over HTTP, so a route added there
|
|
14
|
+
* arrives here for free and the two targets cannot answer one URL differently.
|
|
15
|
+
*
|
|
16
|
+
* @category Desktop
|
|
17
|
+
*/
|
|
18
|
+
const electron_1 = require("electron");
|
|
19
|
+
const node_1 = require("@lloyal-labs/rig/node");
|
|
20
|
+
const node_2 = require("@lloyal-labs/media/node");
|
|
21
|
+
const read_bounded_1 = require("./read-bounded");
|
|
22
|
+
/** The scheme's authority: `standard` schemes require one. Every route lives under the path, as on HTTP. */
|
|
23
|
+
exports.CONTENT_ORIGIN = 'attachment://store';
|
|
24
|
+
/**
|
|
25
|
+
* Must run BEFORE app ready — afterwards the registration is ignored SILENTLY.
|
|
26
|
+
* All four privileges are load-bearing: `standard` gives the scheme an origin
|
|
27
|
+
* so CSP can name it, `secure` keeps it out of the mixed-content rules,
|
|
28
|
+
* `supportFetchAPI` lets `fetch` address it, and without `corsEnabled` the
|
|
29
|
+
* scheme takes no part in CORS at all, so the handler's allow-origin header
|
|
30
|
+
* is never consulted and every fetch fails before reaching it.
|
|
31
|
+
*/
|
|
32
|
+
function registerContentScheme() {
|
|
33
|
+
electron_1.protocol.registerSchemesAsPrivileged([
|
|
34
|
+
{ scheme: 'attachment', privileges: { standard: true, secure: true, supportFetchAPI: true, corsEnabled: true, stream: true } },
|
|
35
|
+
]);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Serve reads from the project store; hand an upload to `ingest`. Reading needs
|
|
39
|
+
* no lock: OCI blobs are content-addressed and immutable. Writing is not ours —
|
|
40
|
+
* `ingest` belongs to whichever process owns the store, and takes a signal so a
|
|
41
|
+
* deadline here can actually stop it.
|
|
42
|
+
*/
|
|
43
|
+
function serveContentScheme(projectRoot, ingest) {
|
|
44
|
+
const media = (0, node_1.createProjectMediaStore)(projectRoot);
|
|
45
|
+
// The renderer is cross-origin to this scheme — a dev server in development,
|
|
46
|
+
// `file://` in a build — so it has no origin to echo. `*` grants nothing
|
|
47
|
+
// further: only this app's own renderers can address the scheme.
|
|
48
|
+
const cors = { 'Access-Control-Allow-Origin': '*' };
|
|
49
|
+
electron_1.protocol.handle('attachment', async (request) => {
|
|
50
|
+
const path = new URL(request.url).pathname;
|
|
51
|
+
// The plane's one WRITE, and the only route not answerable from the store.
|
|
52
|
+
if (request.method === 'POST' && path === '/v1/media/ingress') {
|
|
53
|
+
// ONE deadline over BOTH halves — the transfer and the ingest — and the same bounds the served host
|
|
54
|
+
// passes its route, so both targets admit the same thing.
|
|
55
|
+
const ctrl = new AbortController();
|
|
56
|
+
const timer = setTimeout(() => ctrl.abort(), node_2.DOCUMENT_UPLOAD_TIMEOUT_MS);
|
|
57
|
+
try {
|
|
58
|
+
const declared = Number(request.headers.get('content-length') ?? NaN);
|
|
59
|
+
if (Number.isFinite(declared) && declared > node_2.MAX_DOCUMENT_BYTES)
|
|
60
|
+
throw new read_bounded_1.TooLarge(`upload exceeds ${node_2.MAX_DOCUMENT_BYTES} bytes`);
|
|
61
|
+
const bytes = await (0, read_bounded_1.readBounded)(request.body, node_2.MAX_DOCUMENT_BYTES, ctrl.signal);
|
|
62
|
+
const root = await ingest(bytes, ctrl.signal);
|
|
63
|
+
return Response.json(root, { status: 201, headers: cors });
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
// The same statuses the HTTP route answers with: 413 too large, 408 too slow, else refused.
|
|
67
|
+
const late = err instanceof read_bounded_1.TooSlow || ctrl.signal.aborted;
|
|
68
|
+
const status = err instanceof read_bounded_1.TooLarge ? 413 : late ? 408 : 400;
|
|
69
|
+
const error = late
|
|
70
|
+
? `upload exceeded ${node_2.DOCUMENT_UPLOAD_TIMEOUT_MS}ms end to end`
|
|
71
|
+
: err instanceof Error ? err.message : 'ingest failed';
|
|
72
|
+
return Response.json({ error }, { status, headers: cors });
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
clearTimeout(timer);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const reply = (0, node_1.resolveContent)({ method: request.method, path, ifNoneMatch: request.headers.get('if-none-match') ?? undefined }, media);
|
|
79
|
+
if (!reply)
|
|
80
|
+
return new Response(null, { status: 404 });
|
|
81
|
+
return new Response(reply.body ?? null, { status: reply.status, headers: { ...reply.headers, ...cors } });
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.js","sourceRoot":"","sources":["../src/content.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Descriptor } from '@lloyal-labs/media';
|
|
2
|
+
import type { Frame, SessionState, Snapshot } from '@lloyal-labs/binding';
|
|
3
|
+
/** The slice of Electron's `UtilityProcess` the engine uses, structurally — a test hands in a fake. */
|
|
4
|
+
export interface EngineProcess {
|
|
5
|
+
postMessage(message: unknown): void;
|
|
6
|
+
on(event: 'message', listener: (message: unknown) => void): this;
|
|
7
|
+
on(event: 'exit', listener: (code: number) => void): this;
|
|
8
|
+
kill(): boolean;
|
|
9
|
+
stdout?: {
|
|
10
|
+
on(event: 'data', listener: (chunk: {
|
|
11
|
+
toString(): string;
|
|
12
|
+
}) => void): unknown;
|
|
13
|
+
} | null;
|
|
14
|
+
stderr?: {
|
|
15
|
+
on(event: 'data', listener: (chunk: {
|
|
16
|
+
toString(): string;
|
|
17
|
+
}) => void): unknown;
|
|
18
|
+
} | null;
|
|
19
|
+
}
|
|
20
|
+
export interface CreateEngineOpts<E, S> {
|
|
21
|
+
/** The engine binary — this project's own built cli boot, e.g. `join(process.cwd(), 'bin', 'run.js')`. */
|
|
22
|
+
bin: string;
|
|
23
|
+
/** Added to the engine's environment. `RR_BRIDGE` is set for you: it is what makes the cli boot mount the ipc binding. */
|
|
24
|
+
env?: Record<string, string>;
|
|
25
|
+
initialState: S;
|
|
26
|
+
reduce: (state: S, ev: E) => S;
|
|
27
|
+
/** Where a frame goes: the renderer, when its window is alive. */
|
|
28
|
+
forward: (frame: Frame<E>) => void;
|
|
29
|
+
/** The engine's own stdout/stderr lines. */
|
|
30
|
+
log?: (stream: 'stdout' | 'stderr' | 'exit', text: string) => void;
|
|
31
|
+
/** Fork it yourself. The default forks `bin` as an Electron utility process; a test hands in a fake. */
|
|
32
|
+
fork?: (bin: string, env: NodeJS.ProcessEnv) => EngineProcess;
|
|
33
|
+
}
|
|
34
|
+
export interface Engine<C, S> {
|
|
35
|
+
/** Post a command to the engine. False when the engine is not running. */
|
|
36
|
+
send(command: C): boolean;
|
|
37
|
+
/** The shell's fold as of the last frame forwarded: the renderer's seed on (re)load. */
|
|
38
|
+
snapshot(): Snapshot<S>;
|
|
39
|
+
/** Hand bytes to the engine and wait for the root descriptor it commits. `signal` carries the deadline across. */
|
|
40
|
+
ingest(bytes: Uint8Array, signal: AbortSignal): Promise<Descriptor>;
|
|
41
|
+
/** Stop the engine. */
|
|
42
|
+
kill(): void;
|
|
43
|
+
/** This session's life, as the renderer's view reads it. */
|
|
44
|
+
session(): SessionState;
|
|
45
|
+
/** Subscribe to it: the current state at once, then every change. Returns the unsubscribe. */
|
|
46
|
+
onSession(cb: (state: SessionState) => void): () => void;
|
|
47
|
+
/**
|
|
48
|
+
* Replace the engine with a fresh one — the reader's way back from a session
|
|
49
|
+
* that ended.
|
|
50
|
+
*
|
|
51
|
+
* The replacement is not forked until the old process has actually gone:
|
|
52
|
+
* `draining` says work cannot be taken, never that the model has been freed.
|
|
53
|
+
* Settles when the replacement is USABLE or has failed to start, and a request
|
|
54
|
+
* made in the meantime reuses the one in flight — what the reader asked for is
|
|
55
|
+
* a working harness, and between the fork and `ready` there is not one yet, so
|
|
56
|
+
* a second press would otherwise kill an engine still loading its model.
|
|
57
|
+
*/
|
|
58
|
+
restart(): Promise<void>;
|
|
59
|
+
readonly running: boolean;
|
|
60
|
+
}
|
|
61
|
+
export declare function createEngine<E, C, S>(opts: CreateEngineOpts<E, S>): Engine<C, S>;
|
|
62
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE1E,uGAAuG;AACvG,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACpC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;IACjE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1D,IAAI,IAAI,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE;QAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE;YAAE,QAAQ,IAAI,MAAM,CAAA;SAAE,KAAK,IAAI,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAClG,MAAM,CAAC,EAAE;QAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE;YAAE,QAAQ,IAAI,MAAM,CAAA;SAAE,KAAK,IAAI,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACnG;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC,EAAE,CAAC;IACpC,0GAA0G;IAC1G,GAAG,EAAE,MAAM,CAAC;IACZ,0HAA0H;IAC1H,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,YAAY,EAAE,CAAC,CAAC;IAChB,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IAC/B,kEAAkE;IAClE,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IACnC,4CAA4C;IAC5C,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnE,wGAAwG;IACxG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,KAAK,aAAa,CAAC;CAC/D;AASD,MAAM,WAAW,MAAM,CAAC,CAAC,EAAE,CAAC;IAC1B,0EAA0E;IAC1E,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC;IAC1B,wFAAwF;IACxF,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC;IACxB,kHAAkH;IAClH,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACpE,uBAAuB;IACvB,IAAI,IAAI,IAAI,CAAC;IACb,4DAA4D;IAC5D,OAAO,IAAI,YAAY,CAAC;IACxB,8FAA8F;IAC9F,SAAS,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACzD;;;;;;;;;;OAUG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CA4KhF"}
|
package/dist/engine.js
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createEngine = createEngine;
|
|
4
|
+
/**
|
|
5
|
+
* The engine: the project's own cli bin, forked as an Electron utility process
|
|
6
|
+
* with the `ipc` binding mounted, and the shell's fold over what it says.
|
|
7
|
+
*
|
|
8
|
+
* Streaming model, the same as every target: the engine emits raw events; the
|
|
9
|
+
* shell numbers each one (`seq` within this engine's `epoch`) and forwards the
|
|
10
|
+
* frame to the renderer, which folds it through the SAME pure reduce — only
|
|
11
|
+
* the small event crosses IPC, never the growing transcript. The shell also
|
|
12
|
+
* folds every event itself, purely to answer ONE snapshot per (re)load: the
|
|
13
|
+
* renderer seeds from it and applies only frames after the cut. An engine
|
|
14
|
+
* respawn is a new epoch, so a renderer that outlives one cannot mix two.
|
|
15
|
+
*
|
|
16
|
+
* The shell relays uploads to the engine and waits for the root descriptor it
|
|
17
|
+
* commits: the engine is the store's single writer.
|
|
18
|
+
*
|
|
19
|
+
* **It also owns the session's life, in the platform's own words.** A desktop
|
|
20
|
+
* shell has a host in every sense that matters to a reader — something that
|
|
21
|
+
* starts the harness, knows when it is usable and knows when it is gone — so it
|
|
22
|
+
* reports {@link SessionState} rather than inventing a second vocabulary for
|
|
23
|
+
* the same facts. `warming` at the fork, `live` at the child's own `ready`
|
|
24
|
+
* (which means the command channel is attached, and is the earliest honest
|
|
25
|
+
* boundary — a child exists while it is still loading a model), `draining`
|
|
26
|
+
* once a stop is asked for, then `reaped` or `died`. The renderer's IPC link
|
|
27
|
+
* stays up throughout, which is exactly why the session's state and the
|
|
28
|
+
* transport's are two facts and not one.
|
|
29
|
+
*
|
|
30
|
+
* @category Desktop
|
|
31
|
+
*/
|
|
32
|
+
const node_fs_1 = require("node:fs");
|
|
33
|
+
const electron_1 = require("electron");
|
|
34
|
+
/** The default: this project's cli boot as a utility process, with the bridge flag set and its output piped. */
|
|
35
|
+
function forkUtilityProcess(bin, env) {
|
|
36
|
+
// A missing binary is the one failure worth naming: the window would otherwise open onto an engine that never speaks.
|
|
37
|
+
if (!(0, node_fs_1.existsSync)(bin))
|
|
38
|
+
throw new Error(`engine not built: ${bin} not found — build the cli target first.`);
|
|
39
|
+
return electron_1.utilityProcess.fork(bin, [], { serviceName: 'harness-engine', stdio: 'pipe', env });
|
|
40
|
+
}
|
|
41
|
+
function createEngine(opts) {
|
|
42
|
+
const fork = opts.fork ?? forkUtilityProcess;
|
|
43
|
+
let epoch = Date.now();
|
|
44
|
+
let seq = 0;
|
|
45
|
+
let state = opts.initialState;
|
|
46
|
+
let child = null;
|
|
47
|
+
/** Which fork we own. A dead child's listeners stay attached to its own process object. */
|
|
48
|
+
let generation = 0;
|
|
49
|
+
let stopping = false; // we asked it to go
|
|
50
|
+
let replacing = false; // …and something is taking its place, so its end is not the session's
|
|
51
|
+
let awaitExit = null;
|
|
52
|
+
/** Armed across a replacement's startup: settled when it is usable, or when it failed to start. */
|
|
53
|
+
let awaitStartup = null;
|
|
54
|
+
let restarting = null;
|
|
55
|
+
let session = { phase: 'warming' };
|
|
56
|
+
const sessionListeners = new Set();
|
|
57
|
+
const pending = new Map();
|
|
58
|
+
let ingestId = 0;
|
|
59
|
+
const announce = (next) => {
|
|
60
|
+
session = next;
|
|
61
|
+
// A startup is over at its first outcome, either way. `warming` is not one.
|
|
62
|
+
if (next.phase === 'live' || next.phase === 'died') {
|
|
63
|
+
const up = awaitStartup;
|
|
64
|
+
awaitStartup = null;
|
|
65
|
+
up?.();
|
|
66
|
+
}
|
|
67
|
+
for (const cb of sessionListeners)
|
|
68
|
+
cb(next);
|
|
69
|
+
};
|
|
70
|
+
const post = (message) => {
|
|
71
|
+
if (!child)
|
|
72
|
+
return false;
|
|
73
|
+
try {
|
|
74
|
+
child.postMessage(message);
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const failPending = (why) => {
|
|
82
|
+
for (const [, waiting] of pending)
|
|
83
|
+
waiting.reject(new Error(why));
|
|
84
|
+
pending.clear();
|
|
85
|
+
};
|
|
86
|
+
function start() {
|
|
87
|
+
const mine = ++generation;
|
|
88
|
+
stopping = false;
|
|
89
|
+
replacing = false;
|
|
90
|
+
announce({ phase: 'warming' });
|
|
91
|
+
let proc;
|
|
92
|
+
try {
|
|
93
|
+
proc = fork(opts.bin, { ...process.env, ...opts.env, RR_BRIDGE: '1' });
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
// A shell whose engine will not start must still open and say so; throwing here would take
|
|
97
|
+
// the main process down before the window that could report it exists.
|
|
98
|
+
child = null;
|
|
99
|
+
opts.log?.('exit', err instanceof Error ? err.message : String(err));
|
|
100
|
+
announce({ phase: 'died' });
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
child = proc;
|
|
104
|
+
proc.stdout?.on('data', (d) => opts.log?.('stdout', d.toString().trimEnd()));
|
|
105
|
+
proc.stderr?.on('data', (d) => opts.log?.('stderr', d.toString().trimEnd()));
|
|
106
|
+
proc.on('message', (raw) => {
|
|
107
|
+
if (mine !== generation)
|
|
108
|
+
return; // a child we no longer own, still talking
|
|
109
|
+
const msg = raw;
|
|
110
|
+
if (msg?.t === 'ready') {
|
|
111
|
+
// The command channel is attached. Not a promise that the harness has finished booting —
|
|
112
|
+
// it is simply the first moment anything the reader does can reach it.
|
|
113
|
+
announce({ phase: 'live' });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (msg?.t === 'event' && msg.payload !== undefined) {
|
|
117
|
+
seq += 1;
|
|
118
|
+
state = opts.reduce(state, msg.payload);
|
|
119
|
+
opts.forward({ epoch, seq, ev: msg.payload });
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (typeof msg?.id !== 'number')
|
|
123
|
+
return;
|
|
124
|
+
const waiting = pending.get(msg.id);
|
|
125
|
+
if (!waiting)
|
|
126
|
+
return;
|
|
127
|
+
pending.delete(msg.id);
|
|
128
|
+
if (msg.t === 'ingested' && msg.root)
|
|
129
|
+
waiting.resolve(msg.root);
|
|
130
|
+
else if (msg.t === 'ingestFailed')
|
|
131
|
+
waiting.reject(new Error(msg.error ?? 'ingest failed'));
|
|
132
|
+
});
|
|
133
|
+
proc.on('exit', (code) => {
|
|
134
|
+
if (mine !== generation)
|
|
135
|
+
return;
|
|
136
|
+
opts.log?.('exit', String(code));
|
|
137
|
+
// Drop the handle FIRST: a stale one passes `if (!child)`, and a message to a dead process is discarded
|
|
138
|
+
// silently. An ingest in flight must FAIL, not hang.
|
|
139
|
+
child = null;
|
|
140
|
+
failPending('the engine stopped before the file was ingested');
|
|
141
|
+
const wake = awaitExit;
|
|
142
|
+
awaitExit = null;
|
|
143
|
+
// A replacement is coming: the reader is between engines, not without one, so nothing terminal
|
|
144
|
+
// is said and the banner does not flash "ended" on the way to a working session.
|
|
145
|
+
if (replacing)
|
|
146
|
+
return wake?.();
|
|
147
|
+
announce(stopping ? { phase: 'reaped' } : { phase: 'died', ...(code != null ? { code } : {}) });
|
|
148
|
+
wake?.();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
start();
|
|
152
|
+
return {
|
|
153
|
+
send: (command) => post({ t: 'command', payload: command }),
|
|
154
|
+
snapshot: () => ({ state, epoch, seq }),
|
|
155
|
+
session: () => session,
|
|
156
|
+
onSession(cb) {
|
|
157
|
+
sessionListeners.add(cb);
|
|
158
|
+
cb(session); // a renderer that loads mid-session is told where things stand
|
|
159
|
+
return () => { sessionListeners.delete(cb); };
|
|
160
|
+
},
|
|
161
|
+
ingest(bytes, signal) {
|
|
162
|
+
// An already-aborted signal fires no `abort` event, so the entry would sit in `pending` for the life of the process.
|
|
163
|
+
if (signal.aborted)
|
|
164
|
+
return Promise.reject(new Error('the ingest was cancelled'));
|
|
165
|
+
if (!child)
|
|
166
|
+
return Promise.reject(new Error('the engine is not running'));
|
|
167
|
+
const id = ++ingestId;
|
|
168
|
+
const answer = new Promise((resolve, reject) => pending.set(id, { resolve, reject }));
|
|
169
|
+
const settle = (fail) => {
|
|
170
|
+
const waiting = pending.get(id);
|
|
171
|
+
if (!waiting)
|
|
172
|
+
return;
|
|
173
|
+
pending.delete(id);
|
|
174
|
+
waiting.reject(fail);
|
|
175
|
+
};
|
|
176
|
+
signal.addEventListener('abort', () => {
|
|
177
|
+
post({ t: 'ingestCancel', id });
|
|
178
|
+
settle(new Error('the ingest was cancelled'));
|
|
179
|
+
}, { once: true });
|
|
180
|
+
if (!post({ t: 'ingest', id, bytes }))
|
|
181
|
+
settle(new Error('the engine is not running'));
|
|
182
|
+
return answer;
|
|
183
|
+
},
|
|
184
|
+
kill() {
|
|
185
|
+
if (!child)
|
|
186
|
+
return;
|
|
187
|
+
stopping = true;
|
|
188
|
+
announce({ phase: 'draining' });
|
|
189
|
+
try {
|
|
190
|
+
child.kill();
|
|
191
|
+
}
|
|
192
|
+
catch { /* already gone; its exit still arrives */ }
|
|
193
|
+
},
|
|
194
|
+
restart() {
|
|
195
|
+
if (restarting)
|
|
196
|
+
return restarting; // the reader pressed it twice; one engine, one promise
|
|
197
|
+
const done = (async () => {
|
|
198
|
+
if (child) {
|
|
199
|
+
stopping = true;
|
|
200
|
+
replacing = true;
|
|
201
|
+
announce({ phase: 'draining' });
|
|
202
|
+
const gone = new Promise((resolve) => { awaitExit = resolve; });
|
|
203
|
+
try {
|
|
204
|
+
child.kill();
|
|
205
|
+
}
|
|
206
|
+
catch { /* already gone; its exit still arrives */ }
|
|
207
|
+
await gone; // the model is resident until the process is gone: never two at once
|
|
208
|
+
}
|
|
209
|
+
// A fresh stream. The renderer compares frames only within an epoch, so the replacement
|
|
210
|
+
// must not reuse one — `Date.now()` alone can repeat inside a millisecond.
|
|
211
|
+
epoch = Math.max(Date.now(), epoch + 1);
|
|
212
|
+
seq = 0;
|
|
213
|
+
state = opts.initialState;
|
|
214
|
+
// Stay in flight until the replacement is usable or has failed, so a reader who presses
|
|
215
|
+
// again a moment later REUSES this one. Clearing at the fork would let the second press
|
|
216
|
+
// kill an engine that is still loading its model, throwing away the wait and starting it
|
|
217
|
+
// over — and the second press is exactly what an unresponsive-looking startup invites.
|
|
218
|
+
// It ends at `died` too, so a startup that failed leaves recovery available again.
|
|
219
|
+
const up = new Promise((resolve) => { awaitStartup = resolve; });
|
|
220
|
+
start();
|
|
221
|
+
await up;
|
|
222
|
+
})();
|
|
223
|
+
restarting = done;
|
|
224
|
+
void done.finally(() => { if (restarting === done)
|
|
225
|
+
restarting = null; });
|
|
226
|
+
return done;
|
|
227
|
+
},
|
|
228
|
+
get running() {
|
|
229
|
+
return child !== null;
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":";;AA6FA,oCA4KC;AAzQD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qCAAqC;AACrC,uCAA0C;AA6B1C,gHAAgH;AAChH,SAAS,kBAAkB,CAAC,GAAW,EAAE,GAAsB;IAC7D,sHAAsH;IACtH,IAAI,CAAC,IAAA,oBAAU,EAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAG,0CAA0C,CAAC,CAAC;IAC1G,OAAO,yBAAc,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAA6B,CAAC;AACzH,CAAC;AA8BD,SAAgB,YAAY,CAAU,IAA4B;IAChE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,kBAAkB,CAAC;IAC7C,IAAI,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC;IAC9B,IAAI,KAAK,GAAyB,IAAI,CAAC;IACvC,2FAA2F;IAC3F,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,QAAQ,GAAG,KAAK,CAAC,CAAG,oBAAoB;IAC5C,IAAI,SAAS,GAAG,KAAK,CAAC,CAAE,sEAAsE;IAC9F,IAAI,SAAS,GAAwB,IAAI,CAAC;IAC1C,mGAAmG;IACnG,IAAI,YAAY,GAAwB,IAAI,CAAC;IAC7C,IAAI,UAAU,GAAyB,IAAI,CAAC;IAC5C,IAAI,OAAO,GAAiB,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IACjD,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAiC,CAAC;IAClE,MAAM,OAAO,GAAG,IAAI,GAAG,EAA4E,CAAC;IACpG,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,MAAM,QAAQ,GAAG,CAAC,IAAkB,EAAQ,EAAE;QAC5C,OAAO,GAAG,IAAI,CAAC;QACf,4EAA4E;QAC5E,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YACnD,MAAM,EAAE,GAAG,YAAY,CAAC;YACxB,YAAY,GAAG,IAAI,CAAC;YACpB,EAAE,EAAE,EAAE,CAAC;QACT,CAAC;QACD,KAAK,MAAM,EAAE,IAAI,gBAAgB;YAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,CAAC,OAAgB,EAAW,EAAE;QACzC,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QACzB,IAAI,CAAC;YACH,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,CAAC,GAAW,EAAQ,EAAE;QACxC,KAAK,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,OAAO;YAAE,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAClE,OAAO,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC,CAAC;IAEF,SAAS,KAAK;QACZ,MAAM,IAAI,GAAG,EAAE,UAAU,CAAC;QAC1B,QAAQ,GAAG,KAAK,CAAC;QACjB,SAAS,GAAG,KAAK,CAAC;QAClB,QAAQ,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QAC/B,IAAI,IAAmB,CAAC;QACxB,IAAI,CAAC;YACH,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QACzE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,2FAA2F;YAC3F,uEAAuE;YACvE,KAAK,GAAG,IAAI,CAAC;YACb,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YACrE,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YAC5B,OAAO;QACT,CAAC;QACD,KAAK,GAAG,IAAI,CAAC;QACb,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC7E,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC7E,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;YACzB,IAAI,IAAI,KAAK,UAAU;gBAAE,OAAO,CAAG,0CAA0C;YAC7E,MAAM,GAAG,GAAG,GAAkF,CAAC;YAC/F,IAAI,GAAG,EAAE,CAAC,KAAK,OAAO,EAAE,CAAC;gBACvB,yFAAyF;gBACzF,uEAAuE;gBACvE,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;gBAC5B,OAAO;YACT,CAAC;YACD,IAAI,GAAG,EAAE,CAAC,KAAK,OAAO,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;gBACpD,GAAG,IAAI,CAAC,CAAC;gBACT,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;gBACxC,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC9C,OAAO;YACT,CAAC;YACD,IAAI,OAAO,GAAG,EAAE,EAAE,KAAK,QAAQ;gBAAE,OAAO;YACxC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACpC,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrB,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACvB,IAAI,GAAG,CAAC,CAAC,KAAK,UAAU,IAAI,GAAG,CAAC,IAAI;gBAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;iBAC3D,IAAI,GAAG,CAAC,CAAC,KAAK,cAAc;gBAAE,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,eAAe,CAAC,CAAC,CAAC;QAC7F,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACvB,IAAI,IAAI,KAAK,UAAU;gBAAE,OAAO;YAChC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YACjC,wGAAwG;YACxG,qDAAqD;YACrD,KAAK,GAAG,IAAI,CAAC;YACb,WAAW,CAAC,iDAAiD,CAAC,CAAC;YAC/D,MAAM,IAAI,GAAG,SAAS,CAAC;YACvB,SAAS,GAAG,IAAI,CAAC;YACjB,+FAA+F;YAC/F,iFAAiF;YACjF,IAAI,SAAS;gBAAE,OAAO,IAAI,EAAE,EAAE,CAAC;YAC/B,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YAChG,IAAI,EAAE,EAAE,CAAC;QACX,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,EAAE,CAAC;IAER,OAAO;QACL,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;QAC3D,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;QACvC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO;QACtB,SAAS,CAAC,EAAE;YACV,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACzB,EAAE,CAAC,OAAO,CAAC,CAAC,CAAG,+DAA+D;YAC9E,OAAO,GAAG,EAAE,GAAG,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,CAAC;QACD,MAAM,CAAC,KAAK,EAAE,MAAM;YAClB,qHAAqH;YACrH,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC,CAAC;YACjF,IAAI,CAAC,KAAK;gBAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAC;YAC1E,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC;YACtB,MAAM,MAAM,GAAG,IAAI,OAAO,CAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;YAClG,MAAM,MAAM,GAAG,CAAC,IAAW,EAAQ,EAAE;gBACnC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAChC,IAAI,CAAC,OAAO;oBAAE,OAAO;gBACrB,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACnB,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACvB,CAAC,CAAC;YACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;gBACpC,IAAI,CAAC,EAAE,CAAC,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC;gBAChC,MAAM,CAAC,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC,CAAC;YAChD,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;gBAAE,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAC;YACtF,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,IAAI;YACF,IAAI,CAAC,KAAK;gBAAE,OAAO;YACnB,QAAQ,GAAG,IAAI,CAAC;YAChB,QAAQ,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;YAChC,IAAI,CAAC;gBAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,0CAA0C,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO;YACL,IAAI,UAAU;gBAAE,OAAO,UAAU,CAAC,CAAG,uDAAuD;YAC5F,MAAM,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE;gBACvB,IAAI,KAAK,EAAE,CAAC;oBACV,QAAQ,GAAG,IAAI,CAAC;oBAChB,SAAS,GAAG,IAAI,CAAC;oBACjB,QAAQ,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;oBAChC,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,GAAG,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;oBACtE,IAAI,CAAC;wBAAC,KAAK,CAAC,IAAI,EAAE,CAAC;oBAAC,CAAC;oBAAC,MAAM,CAAC,CAAC,0CAA0C,CAAC,CAAC;oBAC1E,MAAM,IAAI,CAAC,CAAG,qEAAqE;gBACrF,CAAC;gBACD,wFAAwF;gBACxF,2EAA2E;gBAC3E,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;gBACxC,GAAG,GAAG,CAAC,CAAC;gBACR,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC;gBAC1B,wFAAwF;gBACxF,wFAAwF;gBACxF,yFAAyF;gBACzF,uFAAuF;gBACvF,mFAAmF;gBACnF,MAAM,EAAE,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,GAAG,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;gBACvE,KAAK,EAAE,CAAC;gBACR,MAAM,EAAE,CAAC;YACX,CAAC,CAAC,EAAE,CAAC;YACL,UAAU,GAAG,IAAI,CAAC;YAClB,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,UAAU,KAAK,IAAI;gBAAE,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACzE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,OAAO;YACT,OAAO,KAAK,KAAK,IAAI,CAAC;QACxB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@lloyal-labs/desktop` — a harness in a native window. The Electron main
|
|
3
|
+
* process is a thin host: it owns the window and forks the project's own cli
|
|
4
|
+
* bin as the engine; heavy work (native inference, the harness) lives there,
|
|
5
|
+
* so the UI thread never blocks.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
* @category Desktop
|
|
9
|
+
*/
|
|
10
|
+
export { createEngine } from './engine';
|
|
11
|
+
export type { Engine, EngineProcess, CreateEngineOpts } from './engine';
|
|
12
|
+
export { registerContentScheme, serveContentScheme, CONTENT_ORIGIN } from './content';
|
|
13
|
+
export { readBounded, TooLarge, TooSlow } from './read-bounded';
|
|
14
|
+
export { createWindow } from './window';
|
|
15
|
+
export type { CreateWindowOpts } from './window';
|
|
16
|
+
export { CHANNELS } from './preload-channels';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CHANNELS = exports.createWindow = exports.TooSlow = exports.TooLarge = exports.readBounded = exports.CONTENT_ORIGIN = exports.serveContentScheme = exports.registerContentScheme = exports.createEngine = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* `@lloyal-labs/desktop` — a harness in a native window. The Electron main
|
|
6
|
+
* process is a thin host: it owns the window and forks the project's own cli
|
|
7
|
+
* bin as the engine; heavy work (native inference, the harness) lives there,
|
|
8
|
+
* so the UI thread never blocks.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
* @category Desktop
|
|
12
|
+
*/
|
|
13
|
+
var engine_1 = require("./engine");
|
|
14
|
+
Object.defineProperty(exports, "createEngine", { enumerable: true, get: function () { return engine_1.createEngine; } });
|
|
15
|
+
var content_1 = require("./content");
|
|
16
|
+
Object.defineProperty(exports, "registerContentScheme", { enumerable: true, get: function () { return content_1.registerContentScheme; } });
|
|
17
|
+
Object.defineProperty(exports, "serveContentScheme", { enumerable: true, get: function () { return content_1.serveContentScheme; } });
|
|
18
|
+
Object.defineProperty(exports, "CONTENT_ORIGIN", { enumerable: true, get: function () { return content_1.CONTENT_ORIGIN; } });
|
|
19
|
+
var read_bounded_1 = require("./read-bounded");
|
|
20
|
+
Object.defineProperty(exports, "readBounded", { enumerable: true, get: function () { return read_bounded_1.readBounded; } });
|
|
21
|
+
Object.defineProperty(exports, "TooLarge", { enumerable: true, get: function () { return read_bounded_1.TooLarge; } });
|
|
22
|
+
Object.defineProperty(exports, "TooSlow", { enumerable: true, get: function () { return read_bounded_1.TooSlow; } });
|
|
23
|
+
var window_1 = require("./window");
|
|
24
|
+
Object.defineProperty(exports, "createWindow", { enumerable: true, get: function () { return window_1.createWindow; } });
|
|
25
|
+
var preload_channels_1 = require("./preload-channels");
|
|
26
|
+
Object.defineProperty(exports, "CHANNELS", { enumerable: true, get: function () { return preload_channels_1.CHANNELS; } });
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;GAQG;AACH,mCAAwC;AAA/B,sGAAA,YAAY,OAAA;AAErB,qCAAsF;AAA7E,gHAAA,qBAAqB,OAAA;AAAE,6GAAA,kBAAkB,OAAA;AAAE,yGAAA,cAAc,OAAA;AAClE,+CAAgE;AAAvD,2GAAA,WAAW,OAAA;AAAE,wGAAA,QAAQ,OAAA;AAAE,uGAAA,OAAO,OAAA;AACvC,mCAAwC;AAA/B,sGAAA,YAAY,OAAA;AAErB,uDAA8C;AAArC,4GAAA,QAAQ,OAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** The IPC channel names the shell and the preload agree on — importable without `electron`. */
|
|
2
|
+
export declare const CHANNELS: {
|
|
3
|
+
readonly event: "harness:event";
|
|
4
|
+
readonly command: "harness:command";
|
|
5
|
+
readonly snapshot: "harness:snapshot";
|
|
6
|
+
/** Main pushes each {@link SessionState} change here. */
|
|
7
|
+
readonly session: "harness:session";
|
|
8
|
+
/** …and answers with the current one here, so a renderer that loads mid-session is not left guessing. */
|
|
9
|
+
readonly sessionNow: "harness:session-now";
|
|
10
|
+
/** The reader asking for a working engine. Main owns what that means. */
|
|
11
|
+
readonly recover: "harness:recover";
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=preload-channels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preload-channels.d.ts","sourceRoot":"","sources":["../src/preload-channels.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,eAAO,MAAM,QAAQ;;;;IAInB,yDAAyD;;IAEzD,yGAAyG;;IAEzG,yEAAyE;;CAEjE,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CHANNELS = void 0;
|
|
4
|
+
/** The IPC channel names the shell and the preload agree on — importable without `electron`. */
|
|
5
|
+
exports.CHANNELS = {
|
|
6
|
+
event: 'harness:event',
|
|
7
|
+
command: 'harness:command',
|
|
8
|
+
snapshot: 'harness:snapshot',
|
|
9
|
+
/** Main pushes each {@link SessionState} change here. */
|
|
10
|
+
session: 'harness:session',
|
|
11
|
+
/** …and answers with the current one here, so a renderer that loads mid-session is not left guessing. */
|
|
12
|
+
sessionNow: 'harness:session-now',
|
|
13
|
+
/** The reader asking for a working engine. Main owns what that means. */
|
|
14
|
+
recover: 'harness:recover',
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=preload-channels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preload-channels.js","sourceRoot":"","sources":["../src/preload-channels.ts"],"names":[],"mappings":";;;AAAA,gGAAgG;AACnF,QAAA,QAAQ,GAAG;IACtB,KAAK,EAAE,eAAe;IACtB,OAAO,EAAE,iBAAiB;IAC1B,QAAQ,EAAE,kBAAkB;IAC5B,yDAAyD;IACzD,OAAO,EAAE,iBAAiB;IAC1B,yGAAyG;IACzG,UAAU,EAAE,qBAAqB;IACjC,yEAAyE;IACzE,OAAO,EAAE,iBAAiB;CAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preload.d.ts","sourceRoot":"","sources":["../src/preload.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,CAAC;AAEpB,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,aAAa,SAAuB,GAAG,IAAI,CAwCjF"}
|
package/dist/preload.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CHANNELS = void 0;
|
|
4
|
+
exports.preloadBridge = preloadBridge;
|
|
5
|
+
/**
|
|
6
|
+
* The preload bridge: `window.harness` for the renderer over Electron's
|
|
7
|
+
* contextBridge, the same {@link Bridge} shape the web target backs with
|
|
8
|
+
* `createBridge`, so the view is transport-agnostic. Frames come down on
|
|
9
|
+
* `harness:event`, commands go up on `harness:command`, the snapshot is one
|
|
10
|
+
* `harness:snapshot` round trip, and the content plane is the custom scheme.
|
|
11
|
+
*
|
|
12
|
+
* Import from `@lloyal-labs/desktop/preload` in the preload script only.
|
|
13
|
+
*
|
|
14
|
+
* @category Desktop
|
|
15
|
+
*/
|
|
16
|
+
const electron_1 = require("electron");
|
|
17
|
+
const preload_channels_1 = require("./preload-channels");
|
|
18
|
+
Object.defineProperty(exports, "CHANNELS", { enumerable: true, get: function () { return preload_channels_1.CHANNELS; } });
|
|
19
|
+
function preloadBridge(contentOrigin = 'attachment://store') {
|
|
20
|
+
const api = {
|
|
21
|
+
onEvent(cb) {
|
|
22
|
+
const h = (_e, frame) => cb(frame);
|
|
23
|
+
electron_1.ipcRenderer.on(preload_channels_1.CHANNELS.event, h);
|
|
24
|
+
return () => {
|
|
25
|
+
electron_1.ipcRenderer.removeListener(preload_channels_1.CHANNELS.event, h);
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
send(command) {
|
|
29
|
+
electron_1.ipcRenderer.send(preload_channels_1.CHANNELS.command, command);
|
|
30
|
+
},
|
|
31
|
+
requestSnapshot() {
|
|
32
|
+
return electron_1.ipcRenderer.invoke(preload_channels_1.CHANNELS.snapshot);
|
|
33
|
+
},
|
|
34
|
+
onSession(cb) {
|
|
35
|
+
// A renderer that has just loaded needs where things stand, not only what changes next: a
|
|
36
|
+
// session can sit at `live` for hours. The push wins a race with the answer, because the
|
|
37
|
+
// answer was true when it was asked and the push is true now.
|
|
38
|
+
let heard = false;
|
|
39
|
+
const h = (_e, state) => { heard = true; cb(state); };
|
|
40
|
+
electron_1.ipcRenderer.on(preload_channels_1.CHANNELS.session, h);
|
|
41
|
+
void electron_1.ipcRenderer.invoke(preload_channels_1.CHANNELS.sessionNow).then((now) => {
|
|
42
|
+
if (heard)
|
|
43
|
+
return;
|
|
44
|
+
heard = true;
|
|
45
|
+
cb(now);
|
|
46
|
+
});
|
|
47
|
+
return () => {
|
|
48
|
+
electron_1.ipcRenderer.removeListener(preload_channels_1.CHANNELS.session, h);
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
recover() {
|
|
52
|
+
// What a working session costs is the placement's business: here, a new engine process.
|
|
53
|
+
void electron_1.ipcRenderer.invoke(preload_channels_1.CHANNELS.recover);
|
|
54
|
+
},
|
|
55
|
+
contentOrigin() {
|
|
56
|
+
return contentOrigin;
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
electron_1.contextBridge.exposeInMainWorld('harness', api);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=preload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preload.js","sourceRoot":"","sources":["../src/preload.ts"],"names":[],"mappings":";;;AAiBA,sCAwCC;AAzDD;;;;;;;;;;GAUG;AACH,uCAAsD;AAEtD,yDAA8C;AAErC,yFAFA,2BAAQ,OAEA;AAEjB,SAAgB,aAAa,CAAU,aAAa,GAAG,oBAAoB;IACzE,MAAM,GAAG,GAAoB;QAC3B,OAAO,CAAC,EAA6B;YACnC,MAAM,CAAC,GAAG,CAAC,EAAW,EAAE,KAAe,EAAQ,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;YAC5D,sBAAW,CAAC,EAAE,CAAC,2BAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAClC,OAAO,GAAG,EAAE;gBACV,sBAAW,CAAC,cAAc,CAAC,2BAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAChD,CAAC,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,OAAU;YACb,sBAAW,CAAC,IAAI,CAAC,2BAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9C,CAAC;QACD,eAAe;YACb,OAAO,sBAAW,CAAC,MAAM,CAAC,2BAAQ,CAAC,QAAQ,CAAyB,CAAC;QACvE,CAAC;QACD,SAAS,CAAC,EAAiC;YACzC,0FAA0F;YAC1F,yFAAyF;YACzF,8DAA8D;YAC9D,IAAI,KAAK,GAAG,KAAK,CAAC;YAClB,MAAM,CAAC,GAAG,CAAC,EAAW,EAAE,KAAmB,EAAQ,EAAE,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACnF,sBAAW,CAAC,EAAE,CAAC,2BAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACpC,KAAM,sBAAW,CAAC,MAAM,CAAC,2BAAQ,CAAC,UAAU,CAA2B,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;gBACnF,IAAI,KAAK;oBAAE,OAAO;gBAClB,KAAK,GAAG,IAAI,CAAC;gBACb,EAAE,CAAC,GAAG,CAAC,CAAC;YACV,CAAC,CAAC,CAAC;YACH,OAAO,GAAG,EAAE;gBACV,sBAAW,CAAC,cAAc,CAAC,2BAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAClD,CAAC,CAAC;QACJ,CAAC;QACD,OAAO;YACL,wFAAwF;YACxF,KAAK,sBAAW,CAAC,MAAM,CAAC,2BAAQ,CAAC,OAAO,CAAC,CAAC;QAC5C,CAAC;QACD,aAAa;YACX,OAAO,aAAa,CAAC;QACvB,CAAC;KACF,CAAC;IACF,wBAAa,CAAC,iBAAiB,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading an upload under a ceiling and a deadline.
|
|
3
|
+
*
|
|
4
|
+
* Nothing here knows about Electron or the content plane — it is a stream, a
|
|
5
|
+
* cap and a signal — which is what lets it be tested against a stalled stream
|
|
6
|
+
* instead of a three-minute wait.
|
|
7
|
+
*
|
|
8
|
+
* @category Desktop
|
|
9
|
+
*/
|
|
10
|
+
/** Over the cap. Separate from a refusal so the client is told which limit. */
|
|
11
|
+
export declare class TooLarge extends Error {
|
|
12
|
+
}
|
|
13
|
+
/** Past the deadline — the transfer, the ingest, or both together. */
|
|
14
|
+
export declare class TooSlow extends Error {
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Read the body, refusing AT the cap rather than after it, and giving up the
|
|
18
|
+
* moment the signal says so. The abort is RACED against each read rather than
|
|
19
|
+
* checked between them: a stalled stream is precisely the case where no read
|
|
20
|
+
* resolves. Cancellation is started, never awaited: on a stream whose `read()`
|
|
21
|
+
* is stuck, `cancel()` can be stuck too.
|
|
22
|
+
*/
|
|
23
|
+
export declare function readBounded(body: ReadableStream<Uint8Array> | null, cap: number, signal: AbortSignal): Promise<Uint8Array>;
|
|
24
|
+
//# sourceMappingURL=read-bounded.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-bounded.d.ts","sourceRoot":"","sources":["../src/read-bounded.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,+EAA+E;AAC/E,qBAAa,QAAS,SAAQ,KAAK;CAAG;AACtC,sEAAsE;AACtE,qBAAa,OAAQ,SAAQ,KAAK;CAAG;AAErC;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,EACvC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,UAAU,CAAC,CAoCrB"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Reading an upload under a ceiling and a deadline.
|
|
4
|
+
*
|
|
5
|
+
* Nothing here knows about Electron or the content plane — it is a stream, a
|
|
6
|
+
* cap and a signal — which is what lets it be tested against a stalled stream
|
|
7
|
+
* instead of a three-minute wait.
|
|
8
|
+
*
|
|
9
|
+
* @category Desktop
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TooSlow = exports.TooLarge = void 0;
|
|
13
|
+
exports.readBounded = readBounded;
|
|
14
|
+
/** Over the cap. Separate from a refusal so the client is told which limit. */
|
|
15
|
+
class TooLarge extends Error {
|
|
16
|
+
}
|
|
17
|
+
exports.TooLarge = TooLarge;
|
|
18
|
+
/** Past the deadline — the transfer, the ingest, or both together. */
|
|
19
|
+
class TooSlow extends Error {
|
|
20
|
+
}
|
|
21
|
+
exports.TooSlow = TooSlow;
|
|
22
|
+
/**
|
|
23
|
+
* Read the body, refusing AT the cap rather than after it, and giving up the
|
|
24
|
+
* moment the signal says so. The abort is RACED against each read rather than
|
|
25
|
+
* checked between them: a stalled stream is precisely the case where no read
|
|
26
|
+
* resolves. Cancellation is started, never awaited: on a stream whose `read()`
|
|
27
|
+
* is stuck, `cancel()` can be stuck too.
|
|
28
|
+
*/
|
|
29
|
+
async function readBounded(body, cap, signal) {
|
|
30
|
+
if (signal.aborted)
|
|
31
|
+
throw new TooSlow('the upload was cancelled');
|
|
32
|
+
if (!body)
|
|
33
|
+
return new Uint8Array();
|
|
34
|
+
const reader = body.getReader();
|
|
35
|
+
let reject;
|
|
36
|
+
const cancelled = new Promise((_, rej) => { reject = rej; });
|
|
37
|
+
const abort = () => reject(new TooSlow('the upload was cancelled'));
|
|
38
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
39
|
+
const chunks = [];
|
|
40
|
+
let seen = 0;
|
|
41
|
+
try {
|
|
42
|
+
for (;;) {
|
|
43
|
+
const { done, value } = await Promise.race([reader.read(), cancelled]);
|
|
44
|
+
if (done)
|
|
45
|
+
break;
|
|
46
|
+
seen += value.byteLength;
|
|
47
|
+
if (seen > cap)
|
|
48
|
+
throw new TooLarge(`upload exceeds ${cap} bytes`);
|
|
49
|
+
chunks.push(value);
|
|
50
|
+
}
|
|
51
|
+
// Reaching EOF is not safety: the deadline can pass as the last chunk lands.
|
|
52
|
+
if (signal.aborted)
|
|
53
|
+
throw new TooSlow('the upload was cancelled');
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
void reader.cancel().catch(() => { });
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
signal.removeEventListener('abort', abort);
|
|
61
|
+
}
|
|
62
|
+
const out = new Uint8Array(seen);
|
|
63
|
+
let at = 0;
|
|
64
|
+
for (const c of chunks) {
|
|
65
|
+
out.set(c, at);
|
|
66
|
+
at += c.byteLength;
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=read-bounded.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-bounded.js","sourceRoot":"","sources":["../src/read-bounded.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAcH,kCAwCC;AApDD,+EAA+E;AAC/E,MAAa,QAAS,SAAQ,KAAK;CAAG;AAAtC,4BAAsC;AACtC,sEAAsE;AACtE,MAAa,OAAQ,SAAQ,KAAK;CAAG;AAArC,0BAAqC;AAErC;;;;;;GAMG;AACI,KAAK,UAAU,WAAW,CAC/B,IAAuC,EACvC,GAAW,EACX,MAAmB;IAEnB,IAAI,MAAM,CAAC,OAAO;QAAE,MAAM,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAClE,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,UAAU,EAAE,CAAC;IAEnC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IAChC,IAAI,MAA2B,CAAC;IAChC,MAAM,SAAS,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,KAAK,GAAG,GAAS,EAAE,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC;IAC1E,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAExD,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,CAAC;QACH,SAAS,CAAC;YACR,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;YACvE,IAAI,IAAI;gBAAE,MAAM;YAChB,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC;YACzB,IAAI,IAAI,GAAG,GAAG;gBAAE,MAAM,IAAI,QAAQ,CAAC,kBAAkB,GAAG,QAAQ,CAAC,CAAC;YAClE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;QACD,6EAA6E;QAC7E,IAAI,MAAM,CAAC,OAAO;YAAE,MAAM,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAAC;IACpE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACrC,MAAM,GAAG,CAAC;IACZ,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACf,EAAE,IAAI,CAAC,CAAC,UAAU,CAAC;IACrB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/window.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The window: one `BrowserWindow` over the preload bridge, links kept out of
|
|
3
|
+
* the renderer. The renderer loads the dev server's URL when electron-vite
|
|
4
|
+
* provides one, else the built page.
|
|
5
|
+
*
|
|
6
|
+
* @category Desktop
|
|
7
|
+
*/
|
|
8
|
+
import { BrowserWindow } from 'electron';
|
|
9
|
+
import type { BrowserWindowConstructorOptions } from 'electron';
|
|
10
|
+
export interface CreateWindowOpts {
|
|
11
|
+
/** The preload bundle. electron-vite names it after its entry and emits `.mjs`. */
|
|
12
|
+
preload: string;
|
|
13
|
+
/** The built page, loaded when no dev server URL is set (`ELECTRON_RENDERER_URL`). */
|
|
14
|
+
page: string;
|
|
15
|
+
title: string;
|
|
16
|
+
/** Window options over the defaults (size, colours). */
|
|
17
|
+
window?: Partial<BrowserWindowConstructorOptions>;
|
|
18
|
+
}
|
|
19
|
+
export declare function createWindow(opts: CreateWindowOpts): BrowserWindow;
|
|
20
|
+
//# sourceMappingURL=window.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../src/window.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,aAAa,EAAS,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,UAAU,CAAC;AAYhE,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,MAAM,CAAC,EAAE,OAAO,CAAC,+BAA+B,CAAC,CAAC;CACnD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,aAAa,CAsBlE"}
|
package/dist/window.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createWindow = createWindow;
|
|
4
|
+
/**
|
|
5
|
+
* The window: one `BrowserWindow` over the preload bridge, links kept out of
|
|
6
|
+
* the renderer. The renderer loads the dev server's URL when electron-vite
|
|
7
|
+
* provides one, else the built page.
|
|
8
|
+
*
|
|
9
|
+
* @category Desktop
|
|
10
|
+
*/
|
|
11
|
+
const electron_1 = require("electron");
|
|
12
|
+
/** Only http(s) links may leave the app. */
|
|
13
|
+
function isExternalUrl(url) {
|
|
14
|
+
try {
|
|
15
|
+
const p = new URL(url).protocol;
|
|
16
|
+
return p === 'http:' || p === 'https:';
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function createWindow(opts) {
|
|
23
|
+
const win = new electron_1.BrowserWindow({
|
|
24
|
+
width: 1100, height: 760, minWidth: 800, minHeight: 520, show: false, title: opts.title,
|
|
25
|
+
...opts.window,
|
|
26
|
+
webPreferences: { preload: opts.preload, contextIsolation: true, sandbox: false, ...(opts.window?.webPreferences ?? {}) },
|
|
27
|
+
});
|
|
28
|
+
win.once('ready-to-show', () => win.show());
|
|
29
|
+
// Links open in the system browser; the renderer never navigates away.
|
|
30
|
+
win.webContents.setWindowOpenHandler(({ url }) => {
|
|
31
|
+
if (isExternalUrl(url))
|
|
32
|
+
void electron_1.shell.openExternal(url);
|
|
33
|
+
return { action: 'deny' };
|
|
34
|
+
});
|
|
35
|
+
win.webContents.on('will-navigate', (e, url) => {
|
|
36
|
+
if (url !== win.webContents.getURL()) {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
if (isExternalUrl(url))
|
|
39
|
+
void electron_1.shell.openExternal(url);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
const dev = process.env.ELECTRON_RENDERER_URL;
|
|
43
|
+
if (dev)
|
|
44
|
+
void win.loadURL(dev);
|
|
45
|
+
else
|
|
46
|
+
void win.loadFile(opts.page);
|
|
47
|
+
return win;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=window.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"window.js","sourceRoot":"","sources":["../src/window.ts"],"names":[],"mappings":";;AA8BA,oCAsBC;AApDD;;;;;;GAMG;AACH,uCAAgD;AAGhD,4CAA4C;AAC5C,SAAS,aAAa,CAAC,GAAW;IAChC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QAChC,OAAO,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,QAAQ,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAYD,SAAgB,YAAY,CAAC,IAAsB;IACjD,MAAM,GAAG,GAAG,IAAI,wBAAa,CAAC;QAC5B,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK;QACvF,GAAG,IAAI,CAAC,MAAM;QACd,cAAc,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,IAAI,EAAE,CAAC,EAAE;KAC1H,CAAC,CAAC;IACH,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5C,uEAAuE;IACvE,GAAG,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;QAC/C,IAAI,aAAa,CAAC,GAAG,CAAC;YAAE,KAAK,gBAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACrD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE;QAC7C,IAAI,GAAG,KAAK,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;YACrC,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,IAAI,aAAa,CAAC,GAAG,CAAC;gBAAE,KAAK,gBAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACvD,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;IAC9C,IAAI,GAAG;QAAE,KAAK,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;;QAC1B,KAAK,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lloyal-labs/desktop",
|
|
3
|
+
"version": "0.1.0-alpha.6",
|
|
4
|
+
"description": "A harness in a native window: the Electron shell's main-process pieces — the engine over the project's own cli bin, the content scheme, the preload bridge and the window",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./preload": {
|
|
13
|
+
"types": "./dist/preload.d.ts",
|
|
14
|
+
"default": "./dist/preload.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/lloyal-ai/hdk.git",
|
|
23
|
+
"directory": "packages/desktop"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/lloyal-ai/hdk/tree/main/packages/desktop#readme",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/lloyal-ai/hdk/issues"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"desktop",
|
|
31
|
+
"electron",
|
|
32
|
+
"harness",
|
|
33
|
+
"shell"
|
|
34
|
+
],
|
|
35
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
36
|
+
"type": "commonjs",
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc -b"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@lloyal-labs/binding": "0.2.0-alpha.6",
|
|
42
|
+
"@lloyal-labs/media": "0.2.0-alpha.6",
|
|
43
|
+
"@lloyal-labs/rig": "5.6.0-alpha.6"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"electron": ">=38"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"electron": "^44.0.0"
|
|
50
|
+
},
|
|
51
|
+
"files": [
|
|
52
|
+
"dist/",
|
|
53
|
+
"LICENSE"
|
|
54
|
+
]
|
|
55
|
+
}
|