lecodes-web-host 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # lecodes-web-host
2
+
3
+ A compiled LeCodes bundle in the browser, in the container it is given — the runtime as
4
+ WebAssembly (`runtime/web`) with its host tables in TypeScript. A library: the page around the
5
+ container is the embedder's (`lecodes-web-player` is the one this repo ships).
6
+
7
+ ```ts
8
+ import { mount } from "lecodes-web-host"
9
+
10
+ const host = await mount(root, code, { onError })
11
+ host.destroy()
12
+ ```
13
+
14
+ ```ts
15
+ import { createHost, parseBundleHeader } from "lecodes-web-host"
16
+
17
+ const host = await createHost(root, { header: parseBundleHeader(code) })
18
+ host.registerPlugin("map", half) // a plugin's web half, before the bundle that names it
19
+ host.run(code, { onError }) // repeatably
20
+ host.setSafeArea({ top: 59, right: 0, bottom: 34, left: 0 })
21
+ ```
22
+
23
+ The host fills `root` and follows its size. It knows nothing around it — no device, no frame, no
24
+ zoom, no url, no message.
25
+
26
+ | file | |
27
+ |---|---|
28
+ | `host.ts` | puts the parts together: the module, the tables, the frame |
29
+ | `header.ts` | the bundle's header, and the runtime profile it means |
30
+ | `page.ts` | the container, its size in device px, the canvases |
31
+ | `engines/` | the 3D and the 2D surface (HostGL, HostScene2d) |
32
+ | `tables/` | fetch, storage, device, app, socket, service, input, media — a file per table |
33
+ | `plugins.ts` | the doors of the plugins' web halves |
34
+ | `safeArea.ts` | the browser's insets, or the embedder's |
35
+ | `errors.ts` | what a bundle threw, mapped to its source |
36
+ | `system.ts`, `materials.ts` | the engine's built-in blobs, the archives a bundle asks for |
37
+
38
+ The UI is `lecodes-web-canvas`: the TreeUI painted on a canvas over the scenes, a native view and
39
+ a video under the picture, the field being edited over it.
package/dist/api.d.ts ADDED
@@ -0,0 +1,190 @@
1
+ /** The runtime profile a bundle runs on: Filament + 2D + UI, creator-2d + UI, the UI alone. */
2
+ export type HostProfile = "full" | "2d" | "ui";
3
+ /** A face the app declared in its header, loaded before the first layout. */
4
+ export type BootFont = {
5
+ family: string;
6
+ weight: number;
7
+ italic: boolean;
8
+ url: string;
9
+ };
10
+ /** The header the compiler writes at the top of a bundle. */
11
+ export type BundleHeader = {
12
+ /** The SDK the bundle was compiled with; null = compiled before 2.0. Its major must be the host's. */
13
+ sdk: string | null;
14
+ name: string | null;
15
+ preloads: Set<string>;
16
+ /** `on` = the 3D engine up front, `defer` = the bundle creates it, `off` = the bundle names no 3D call. */
17
+ gl: "on" | "defer" | "off";
18
+ is2d: boolean;
19
+ /** The ids of the app's plugins. */
20
+ plugins: Set<string>;
21
+ bootFonts: BootFont[];
22
+ };
23
+ export type SafeAreaEdge = "top" | "right" | "bottom" | "left";
24
+ /** Insets in logical px; `bars` = the edges that are exact-height system bars. */
25
+ export type SafeArea = {
26
+ top: number;
27
+ right: number;
28
+ bottom: number;
29
+ left: number;
30
+ bars?: readonly SafeAreaEdge[];
31
+ };
32
+ /** A frame of a stack, in the app's source. */
33
+ export type MappedFrame = {
34
+ fn: string | null;
35
+ source: string;
36
+ line: number;
37
+ column: number;
38
+ };
39
+ /** What a bundle threw. `native` = the engine's (no place in the app's source); `nativeFrames` =
40
+ * the engine's functions, the innermost first, in a second report of the same error. */
41
+ export type PreviewError = {
42
+ message: string;
43
+ frames: MappedFrame[];
44
+ stack?: string;
45
+ native?: boolean;
46
+ detail?: string;
47
+ nativeFrames?: string[];
48
+ };
49
+ export type HostOptions = {
50
+ /** The header of the bundle the host is for (`parseBundleHeader`): its engines decide the profile,
51
+ * its materials the archives, its fonts the boot faces. */
52
+ header?: BundleHeader;
53
+ /** The runtime profile, in place of the header's (a host with no bundle yet: an editor). */
54
+ profile?: HostProfile;
55
+ /** false = an engine-only host: no UI renderer, no fonts. Default true. */
56
+ ui?: boolean;
57
+ /** The project, as the key its storage is kept under. Default: the header's name. */
58
+ project?: string;
59
+ /** Insets the embedder took of the container; absent = the page's own (`env(safe-area-inset-*)`). */
60
+ safeArea?: SafeArea;
61
+ /** device.language. Default: the browser's. */
62
+ language?: () => string;
63
+ /** The url the app was opened with (app.launchUrl). */
64
+ launchUrl?: string;
65
+ /** app.setOrientation sink; absent = a best-effort screen.orientation.lock on the page. */
66
+ onOrientation?: (mode: "landscape" | "portrait" | "auto") => void;
67
+ /** Listen for keyboard keys (Input.key). Default true. */
68
+ keyboard?: boolean;
69
+ /** Windows to listen on for keys besides the host's own. */
70
+ keyboardTargets?: Window[];
71
+ };
72
+ export type RunOptions = {
73
+ /** Called when this bundle throws, with the error mapped back to its source. */
74
+ onError?: (error: PreviewError) => void;
75
+ /** The bundle's header, in place of the one read from its first lines. */
76
+ header?: Pick<BundleHeader, "sdk" | "plugins">;
77
+ };
78
+ export type Run = {
79
+ /** Stop attributing errors to this run. */
80
+ dispose(): void;
81
+ };
82
+ export type WebViewChannel = {
83
+ emit(event: string, data?: any): void;
84
+ };
85
+ export type WebViewInstance = {
86
+ el: HTMLElement;
87
+ call?(method: string, args: any[]): any | Promise<any>;
88
+ destroy?(): void;
89
+ };
90
+ export type WebViewFactory = (params: any, channel: WebViewChannel, doc: Document) => WebViewInstance;
91
+ export type ServiceChannel = {
92
+ emit(event: string, data?: any): void;
93
+ };
94
+ export type ServiceInstance = {
95
+ call?(method: string, args: any[]): any | Promise<any>;
96
+ close?(): void;
97
+ };
98
+ export type ServiceFactory = (params: any, channel: ServiceChannel) => ServiceInstance;
99
+ export type HalfHost = {
100
+ /** `version` = the contract version the half was generated from. */
101
+ registerView(name: string, factory: WebViewFactory, version?: number): void;
102
+ registerService(name: string, factory: ServiceFactory, version?: number): void;
103
+ /** Store bytes in the host's buffer table: the handle a File crosses as. */
104
+ putBuffer(bytes: ArrayBuffer): number;
105
+ getBuffer(systemId: number): ArrayBuffer | Uint8Array | null | undefined;
106
+ };
107
+ /** What a plugin's web half exports. */
108
+ export type PluginHalf = (host: HalfHost) => void;
109
+ export type Rect = {
110
+ x: number;
111
+ y: number;
112
+ width: number;
113
+ height: number;
114
+ };
115
+ /** An element of the render tree: where it is, and the style it is painted in (resolved). */
116
+ export type RenderNode = {
117
+ id: number;
118
+ type: string;
119
+ name?: string;
120
+ rect: Rect;
121
+ style: Record<string, any>;
122
+ text?: string;
123
+ lines?: string[];
124
+ value?: string;
125
+ state?: {
126
+ pressed?: boolean;
127
+ focused?: boolean;
128
+ classes?: string[];
129
+ };
130
+ clip?: boolean;
131
+ scroll?: {
132
+ offset: number;
133
+ content: number;
134
+ axis: "x" | "y";
135
+ };
136
+ children: RenderNode[];
137
+ };
138
+ /** What is shown, bottom to top: the screen, the screen that left (in a transition), the widgets. */
139
+ export type Layer = {
140
+ tree: RenderNode;
141
+ leaving?: boolean;
142
+ widget?: boolean;
143
+ backdrop?: {
144
+ color: number;
145
+ };
146
+ };
147
+ export type HostUI = {
148
+ /** The layers the host paints now. */
149
+ frame(): Layer[];
150
+ /** The elements named `name`. */
151
+ findAll(name: string): RenderNode[];
152
+ /** The render tree as JSON / as text (what `lecodes render` writes). */
153
+ serialize(): unknown;
154
+ toText(): string;
155
+ /** A node's resolved padding: left, top, right, bottom. */
156
+ paddingOf(node: number): [number, number, number, number];
157
+ /** A pointer, in the app's logical px (what the host does with the page's own events). */
158
+ pointerDown(pointer: number, x: number, y: number): void;
159
+ pointerMove(pointer: number, x: number, y: number): void;
160
+ pointerUp(pointer: number, x: number, y: number): void;
161
+ };
162
+ export type Host = {
163
+ readonly profile: HostProfile;
164
+ /** The runtime module (an embedder that reads the engine directly: an editor). */
165
+ readonly M: Record<string, any>;
166
+ /** The runtime's world: the bundle's globals (`_creator`, `_creatorTree`, `_creatorApp`, …). */
167
+ readonly world: Record<string, unknown>;
168
+ /** The UI as the host paints it; null on an engine-only host. */
169
+ readonly ui: HostUI | null;
170
+ /** Evaluate a bundle. Throws when the bundle is of another SDK major than this host. */
171
+ run(code: string, options?: RunOptions): Run;
172
+ /** A plugin's web half, by the id an app lists it under; a bundle gets the ones its header names. */
173
+ registerPlugin(id: string, half: PluginHalf): void;
174
+ hasPlugin(id: string): boolean;
175
+ /** Where a stack of the running bundle leads in its source. */
176
+ locate(error: unknown): MappedFrame[];
177
+ /** The insets the app is told now. */
178
+ safeArea(): SafeArea;
179
+ /** The embedder's insets (it drew over the container), or null for the page's own. */
180
+ setSafeArea(area: SafeArea | null): void;
181
+ /** A warm link ("url") or a synthetic lifecycle event ("pause", "resume"). */
182
+ emitAppEvent(event: string, data?: string): void;
183
+ destroy(): void;
184
+ };
185
+ /** Bring a host up in `root`; it fills the element and follows its size. */
186
+ export type CreateHost = (root: HTMLElement, options?: HostOptions) => Promise<Host>;
187
+ /** A host for this bundle, and the bundle run on it. */
188
+ export type Mount = (root: HTMLElement, code: string, options?: Omit<HostOptions, "header" | "profile"> & RunOptions) => Promise<Host>;
189
+ export type ParseBundleHeader = (code: string) => BundleHeader;
190
+ export type ProfileOf = (header: BundleHeader) => HostProfile;