ziex 0.1.0-dev.786 → 0.1.0-dev.804
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/app.d.ts +112 -0
- package/aws-lambda/index.d.ts +96 -0
- package/aws-lambda/index.js +126 -0
- package/cloudflare/app.d.ts +2 -0
- package/cloudflare/do.d.ts +48 -0
- package/cloudflare/index.d.ts +4 -2
- package/cloudflare/index.js +1428 -132
- package/cloudflare/kv.d.ts +2 -19
- package/cloudflare/worker.d.ts +3 -39
- package/index.d.ts +2 -9
- package/index.js +726 -47
- package/kv.d.ts +27 -0
- package/package.json +12 -2
- package/runtime.d.ts +70 -0
- package/vercel/index.d.ts +26 -0
- package/vercel/index.js +18 -0
- package/wasi.d.ts +61 -0
- package/wasm/core.d.ts +78 -0
- package/wasm/index.d.ts +10 -32
- package/wasm/index.js +138 -44
- package/wasm/init.js +132 -44
- package/wasm/wasi.d.ts +28 -0
package/kv.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface KVNamespace {
|
|
2
|
+
get(key: string): Promise<string | null>;
|
|
3
|
+
put(key: string, value: string, options?: {
|
|
4
|
+
expiration?: number;
|
|
5
|
+
expirationTtl?: number;
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
delete(key: string): Promise<void>;
|
|
8
|
+
list(options?: {
|
|
9
|
+
prefix?: string;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
keys: {
|
|
12
|
+
name: string;
|
|
13
|
+
}[];
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* In-memory KV namespace. Used as the default shim on platforms that don't
|
|
18
|
+
* provide a real KV binding (e.g. Vercel). Data lives only for the lifetime
|
|
19
|
+
* of the isolate instance.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createMemoryKV(): KVNamespace;
|
|
22
|
+
/**
|
|
23
|
+
* Create a `__zx_kv` import object for use with `run({ kv: ... })`.
|
|
24
|
+
* Always returns a valid import object. When JSPI is unavailable all KV
|
|
25
|
+
* operations are stubbed (get → not-found, put/delete → success, list → []).
|
|
26
|
+
*/
|
|
27
|
+
export declare function createKVImports(bindings: Record<string, KVNamespace>, getMemory: () => WebAssembly.Memory): Record<string, unknown>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ziex",
|
|
3
|
-
"version": "0.1.0-dev.
|
|
3
|
+
"version": "0.1.0-dev.804",
|
|
4
4
|
"description": "ZX is a framework for building web applications with Zig.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"./react": "./react/index.js",
|
|
10
10
|
"./wasm": "./wasm/index.js",
|
|
11
11
|
"./wasm/init": "./wasm/init.js",
|
|
12
|
-
"./cloudflare": "./cloudflare/index.js"
|
|
12
|
+
"./cloudflare": "./cloudflare/index.js",
|
|
13
|
+
"./aws-lambda": "./aws-lambda/index.js",
|
|
14
|
+
"./vercel": "./vercel/index.js"
|
|
13
15
|
},
|
|
14
16
|
"homepage": "https://ziex.dev",
|
|
15
17
|
"repository": {
|
|
@@ -30,6 +32,14 @@
|
|
|
30
32
|
],
|
|
31
33
|
"author": "Nurul Huda (Apon) <me@nurulhudaapon.com>",
|
|
32
34
|
"license": "MIT",
|
|
35
|
+
"peerDependenciesMeta": {
|
|
36
|
+
"react": {
|
|
37
|
+
"optional": true
|
|
38
|
+
},
|
|
39
|
+
"react-dom": {
|
|
40
|
+
"optional": true
|
|
41
|
+
}
|
|
42
|
+
},
|
|
33
43
|
"module": "index.js",
|
|
34
44
|
"types": "index.d.ts"
|
|
35
45
|
}
|
package/runtime.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { WASI } from "./wasi";
|
|
2
|
+
import type { KVNamespace } from "./kv";
|
|
3
|
+
/** Minimal Durable Object namespace shape needed for WebSocket routing. */
|
|
4
|
+
export type DurableObjectNamespace = {
|
|
5
|
+
idFromName(name: string): unknown;
|
|
6
|
+
get(id: unknown): {
|
|
7
|
+
fetch(req: Request): Promise<Response>;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export type WsState = {
|
|
11
|
+
upgraded: boolean;
|
|
12
|
+
server: WebSocket | null;
|
|
13
|
+
pendingWrites: Uint8Array[];
|
|
14
|
+
messageQueue: Uint8Array[];
|
|
15
|
+
recvResolve: ((bytes: Uint8Array | null) => void) | null;
|
|
16
|
+
/** Resolved when ws_recv is called for the first time (WASM has entered the message loop). */
|
|
17
|
+
_resolveFirstSuspend?: () => void;
|
|
18
|
+
subscribe?: (topic: string) => void;
|
|
19
|
+
unsubscribe?: (topic: string) => void;
|
|
20
|
+
publish?: (topic: string, data: Uint8Array) => number;
|
|
21
|
+
isSubscribed?: (topic: string) => boolean;
|
|
22
|
+
};
|
|
23
|
+
/** Build the __zx_ws import object for a given connection state. */
|
|
24
|
+
export declare function buildWsImports(Suspending: any, mem: () => WebAssembly.Memory, decoder: TextDecoder, ws: WsState): {
|
|
25
|
+
ws_upgrade: () => void;
|
|
26
|
+
ws_write: (ptr: number, len: number) => void;
|
|
27
|
+
ws_close: (code: number, reason_ptr: number, reason_len: number) => void;
|
|
28
|
+
ws_recv: any;
|
|
29
|
+
ws_subscribe: (ptr: number, len: number) => void;
|
|
30
|
+
ws_unsubscribe: (ptr: number, len: number) => void;
|
|
31
|
+
ws_publish: (topic_ptr: number, topic_len: number, data_ptr: number, data_len: number) => number;
|
|
32
|
+
ws_is_subscribed: (ptr: number, len: number) => number;
|
|
33
|
+
};
|
|
34
|
+
/** Create WebSocketPair, wire message/close listeners, flush pending writes. */
|
|
35
|
+
export declare function attachWebSocket(ws: WsState): {
|
|
36
|
+
client: WebSocket;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Run a WASM module for a single request using JSPI.
|
|
40
|
+
*
|
|
41
|
+
* Pass `kv` as a map of binding names → KV namespaces. The Zig side selects
|
|
42
|
+
* a binding via `zx.kv.scope("name")`; the top-level `zx.kv.*` functions use
|
|
43
|
+
* `"default"`.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* return run({
|
|
48
|
+
* request, env, ctx, module,
|
|
49
|
+
* kv: { default: env.KV, users: env.USERS_KV },
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function run({ request, env, ctx, module, kv: kvBindings, imports, wasi, websocket: doNamespace, }: {
|
|
54
|
+
request: Request;
|
|
55
|
+
env?: unknown;
|
|
56
|
+
ctx?: {
|
|
57
|
+
waitUntil(promise: Promise<unknown>): void;
|
|
58
|
+
};
|
|
59
|
+
module: WebAssembly.Module;
|
|
60
|
+
/** KV namespace bindings — `{ default: env.KV, otherName: env.OTHER_KV }` */
|
|
61
|
+
kv?: Record<string, KVNamespace>;
|
|
62
|
+
imports?: (mem: () => WebAssembly.Memory) => Record<string, Record<string, unknown>>;
|
|
63
|
+
wasi?: WASI;
|
|
64
|
+
/**
|
|
65
|
+
* Durable Object namespace to use for WebSocket connections.
|
|
66
|
+
* When provided, WebSocket upgrade requests are automatically forwarded to
|
|
67
|
+
* the DO so that pub/sub works across multiple connected clients.
|
|
68
|
+
*/
|
|
69
|
+
websocket?: DurableObjectNamespace;
|
|
70
|
+
}): Promise<Response>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ziex adapter for Vercel Edge Functions.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { Ziex } from "ziex/cloudflare";
|
|
7
|
+
* import { handle } from "ziex/vercel";
|
|
8
|
+
* import module from "./app.wasm"; // or fetch at runtime
|
|
9
|
+
*
|
|
10
|
+
* const app = new Ziex({ module });
|
|
11
|
+
*
|
|
12
|
+
* export const config = { runtime: "edge" };
|
|
13
|
+
* export default handle(app);
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
type FetchApp = {
|
|
17
|
+
fetch(req: Request, env?: unknown, ctx?: unknown): Promise<Response>;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Wrap a Ziex app as a Vercel Edge Function handler.
|
|
21
|
+
*
|
|
22
|
+
* Returns a standard `(req: Request) => Promise<Response>` function.
|
|
23
|
+
* Vercel's edge runtime calls it directly — just export it as default.
|
|
24
|
+
*/
|
|
25
|
+
export declare function handle(app: FetchApp): (req: Request) => Promise<Response>;
|
|
26
|
+
export {};
|
package/vercel/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, {
|
|
5
|
+
get: all[name],
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
set: (newValue) => all[name] = () => newValue
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/vercel/index.ts
|
|
13
|
+
function handle(app) {
|
|
14
|
+
return (req) => app.fetch(req);
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
handle
|
|
18
|
+
};
|
package/wasi.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export declare class ProcExit extends Error {
|
|
2
|
+
readonly code: number;
|
|
3
|
+
constructor(code: number);
|
|
4
|
+
}
|
|
5
|
+
export type WASI = {
|
|
6
|
+
wasiImport: Record<string, (...args: any[]) => unknown>;
|
|
7
|
+
start(instance: {
|
|
8
|
+
exports: {
|
|
9
|
+
memory: WebAssembly.Memory;
|
|
10
|
+
_start: () => unknown;
|
|
11
|
+
};
|
|
12
|
+
}): number;
|
|
13
|
+
initialize?(instance: {
|
|
14
|
+
exports: {
|
|
15
|
+
memory: WebAssembly.Memory;
|
|
16
|
+
_initialize?: () => unknown;
|
|
17
|
+
};
|
|
18
|
+
}): void;
|
|
19
|
+
};
|
|
20
|
+
export declare function createWasiImports({ request, stdinData, onStdout, }: {
|
|
21
|
+
request: Request;
|
|
22
|
+
stdinData?: Uint8Array;
|
|
23
|
+
/** Called for each stdout chunk. When provided, chunks are NOT buffered internally. */
|
|
24
|
+
onStdout?: (chunk: Uint8Array) => void;
|
|
25
|
+
}): {
|
|
26
|
+
wasiImport: {
|
|
27
|
+
args_sizes_get(argc_ptr: number, argv_buf_size_ptr: number): number;
|
|
28
|
+
args_get(argv_ptr: number, argv_buf_ptr: number): number;
|
|
29
|
+
environ_sizes_get(count_ptr: number, buf_size_ptr: number): number;
|
|
30
|
+
environ_get(_environ_ptr: number, _environ_buf_ptr: number): number;
|
|
31
|
+
fd_write(fd: number, iovs_ptr: number, iovs_len: number, nwritten_ptr: number): number;
|
|
32
|
+
fd_read(fd: number, iovs_ptr: number, iovs_len: number, nread_ptr: number): number;
|
|
33
|
+
fd_fdstat_get(_fd: number, fdstat_ptr: number): number;
|
|
34
|
+
fd_prestat_get(_fd: number, _bufptr: number): number;
|
|
35
|
+
fd_prestat_dir_name(_fd: number, _path: number, _path_len: number): number;
|
|
36
|
+
fd_close(_fd: number): number;
|
|
37
|
+
fd_pread(_fd: number, _iovs: number, _iovs_len: number, _offset: bigint, nread_ptr: number): number;
|
|
38
|
+
fd_pwrite(_fd: number, _iovs: number, _iovs_len: number, _offset: bigint, nwritten_ptr: number): number;
|
|
39
|
+
fd_filestat_get(_fd: number, filestat_ptr: number): number;
|
|
40
|
+
fd_seek(_fd: number, _offset: bigint, _whence: number, newoffset_ptr: number): number;
|
|
41
|
+
proc_exit(code: number): never;
|
|
42
|
+
sched_yield(): number;
|
|
43
|
+
clock_time_get(_id: number, _precision: bigint, time_ptr: number): number;
|
|
44
|
+
random_get(buf_ptr: number, buf_len: number): number;
|
|
45
|
+
path_open(_fd: number, _dirflags: number, _path: number, _path_len: number, _oflags: number, _rights_base: bigint, _rights_inheriting: bigint, _fdflags: number, opened_fd_ptr: number): number;
|
|
46
|
+
path_create_directory(_fd: number, _path: number, _path_len: number): number;
|
|
47
|
+
path_unlink_file(_fd: number, _path: number, _path_len: number): number;
|
|
48
|
+
path_remove_directory(_fd: number, _path: number, _path_len: number): number;
|
|
49
|
+
path_rename(_fd: number, _old_path: number, _old_path_len: number, _new_fd: number, _new_path: number, _new_path_len: number): number;
|
|
50
|
+
path_filestat_get(_fd: number, _flags: number, _path: number, _path_len: number, filestat_ptr: number): number;
|
|
51
|
+
path_readlink(_fd: number, _path: number, _path_len: number, _buf: number, _buf_len: number, nread_ptr: number): number;
|
|
52
|
+
fd_readdir(_fd: number, _buf: number, _buf_len: number, _cookie: bigint, bufused_ptr: number): number;
|
|
53
|
+
poll_oneoff(_in: number, _out: number, _nsubscriptions: number, nevents_ptr: number): number;
|
|
54
|
+
};
|
|
55
|
+
setMemory: (m: WebAssembly.Memory) => void;
|
|
56
|
+
collectOutput: () => {
|
|
57
|
+
stdout: Uint8Array;
|
|
58
|
+
stderrText: string;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
export declare function mergeUint8Arrays(arrays: Uint8Array[]): Uint8Array;
|
package/wasm/core.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ZigJS } from "../../../../vendor/jsz/js/src";
|
|
2
|
+
/**
|
|
3
|
+
* Core WASM bridge — environment-agnostic (browser + edge).
|
|
4
|
+
* Contains no references to browser globals (document, window, WebSocket, HTMLFormElement).
|
|
5
|
+
*/
|
|
6
|
+
export declare const CallbackType: {
|
|
7
|
+
readonly Event: 0;
|
|
8
|
+
readonly FetchSuccess: 1;
|
|
9
|
+
readonly FetchError: 2;
|
|
10
|
+
readonly Timeout: 3;
|
|
11
|
+
readonly Interval: 4;
|
|
12
|
+
readonly WebSocketOpen: 5;
|
|
13
|
+
readonly WebSocketMessage: 6;
|
|
14
|
+
readonly WebSocketError: 7;
|
|
15
|
+
readonly WebSocketClose: 8;
|
|
16
|
+
};
|
|
17
|
+
export type CallbackTypeValue = typeof CallbackType[keyof typeof CallbackType];
|
|
18
|
+
export type CallbackHandler = (callbackType: number, id: bigint, dataRef: bigint) => void;
|
|
19
|
+
export type FetchCompleteHandler = (fetchId: bigint, statusCode: number, bodyPtr: number, bodyLen: number, isError: number) => void;
|
|
20
|
+
export type WsOnOpenHandler = (wsId: bigint, protocolPtr: number, protocolLen: number) => void;
|
|
21
|
+
export type WsOnMessageHandler = (wsId: bigint, dataPtr: number, dataLen: number, isBinary: number) => void;
|
|
22
|
+
export type WsOnErrorHandler = (wsId: bigint, msgPtr: number, msgLen: number) => void;
|
|
23
|
+
export type WsOnCloseHandler = (wsId: bigint, code: number, reasonPtr: number, reasonLen: number, wasClean: number) => void;
|
|
24
|
+
export declare const jsz: ZigJS;
|
|
25
|
+
/** Store a value using jsz.storeValue and get the 64-bit reference. */
|
|
26
|
+
export declare function storeValueGetRef(val: any): bigint;
|
|
27
|
+
/** Shared encoder/decoder — avoids allocating new instances on every call. */
|
|
28
|
+
export declare const textDecoder: TextDecoder;
|
|
29
|
+
export declare const textEncoder: TextEncoder;
|
|
30
|
+
export declare function getMemoryView(): Uint8Array;
|
|
31
|
+
/** Read a string from WASM memory */
|
|
32
|
+
export declare function readString(ptr: number, len: number): string;
|
|
33
|
+
/** Write bytes to WASM memory at a specific location */
|
|
34
|
+
export declare function writeBytes(ptr: number, data: Uint8Array): void;
|
|
35
|
+
/**
|
|
36
|
+
* Core ZX Bridge — works in both browser and edge runtimes.
|
|
37
|
+
* Contains fetch, timers, and logging. No DOM or browser-WebSocket references.
|
|
38
|
+
*
|
|
39
|
+
* Extend this class in browser environments to add DOM and WebSocket support.
|
|
40
|
+
*/
|
|
41
|
+
export declare class ZxBridgeCore {
|
|
42
|
+
#private;
|
|
43
|
+
protected readonly _alloc: (size: number) => number;
|
|
44
|
+
constructor(exports: WebAssembly.Exports);
|
|
45
|
+
/**
|
|
46
|
+
* Async fetch with full options support.
|
|
47
|
+
* Calls __zx_fetch_complete when done.
|
|
48
|
+
*/
|
|
49
|
+
fetchAsync(urlPtr: number, urlLen: number, methodPtr: number, methodLen: number, headersPtr: number, headersLen: number, bodyPtr: number, bodyLen: number, timeoutMs: number, fetchId: bigint): void;
|
|
50
|
+
/** Notify WASM that a fetch completed */
|
|
51
|
+
protected _notifyFetchComplete(fetchId: bigint, statusCode: number, body: string, isError: boolean): void;
|
|
52
|
+
/** Set a timeout and callback when it fires */
|
|
53
|
+
setTimeout(callbackId: bigint, delayMs: number): void;
|
|
54
|
+
/** Set an interval and callback each time it fires */
|
|
55
|
+
setInterval(callbackId: bigint, intervalMs: number): void;
|
|
56
|
+
/** Clear an interval */
|
|
57
|
+
clearInterval(callbackId: bigint): void;
|
|
58
|
+
/** Write a string to WASM memory, returning pointer and length */
|
|
59
|
+
protected _writeStringToWasm(str: string): {
|
|
60
|
+
ptr: number;
|
|
61
|
+
len: number;
|
|
62
|
+
};
|
|
63
|
+
protected _writeBytesToWasm(data: Uint8Array): {
|
|
64
|
+
ptr: number;
|
|
65
|
+
len: number;
|
|
66
|
+
};
|
|
67
|
+
/** Log a message from WASM at the given level (0=error, 1=warn, 2=info, 3=debug) */
|
|
68
|
+
static log(level: number, ptr: number, len: number): void;
|
|
69
|
+
/**
|
|
70
|
+
* Create the core import object for WASM instantiation.
|
|
71
|
+
* Includes only environment-agnostic bindings: log, fetch, and timers.
|
|
72
|
+
* Use ZxBridge.createImportObject (from wasm/index.ts) in browser contexts
|
|
73
|
+
* to additionally include DOM and WebSocket bindings.
|
|
74
|
+
*/
|
|
75
|
+
static createImportObject(bridgeRef: {
|
|
76
|
+
current: ZxBridgeCore | null;
|
|
77
|
+
}): WebAssembly.Imports;
|
|
78
|
+
}
|
package/wasm/index.d.ts
CHANGED
|
@@ -1,38 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
export { CallbackType, jsz, storeValueGetRef, textDecoder, textEncoder, getMemoryView, readString, writeBytes, ZxBridgeCore, } from "./core";
|
|
2
|
+
export type { CallbackTypeValue } from "./core";
|
|
3
|
+
import { ZxBridgeCore } from "./core";
|
|
2
4
|
/**
|
|
3
|
-
* ZX
|
|
4
|
-
*
|
|
5
|
+
* Browser ZX Bridge — extends ZxBridgeCore with DOM, WebSocket, and form-action support.
|
|
6
|
+
* Import this from environments that have access to browser globals.
|
|
7
|
+
* For edge runtimes, import ZxBridgeCore from ./core instead.
|
|
5
8
|
*/
|
|
6
|
-
export declare
|
|
7
|
-
readonly Event: 0;
|
|
8
|
-
readonly FetchSuccess: 1;
|
|
9
|
-
readonly FetchError: 2;
|
|
10
|
-
readonly Timeout: 3;
|
|
11
|
-
readonly Interval: 4;
|
|
12
|
-
readonly WebSocketOpen: 5;
|
|
13
|
-
readonly WebSocketMessage: 6;
|
|
14
|
-
readonly WebSocketError: 7;
|
|
15
|
-
readonly WebSocketClose: 8;
|
|
16
|
-
};
|
|
17
|
-
export type CallbackTypeValue = typeof CallbackType[keyof typeof CallbackType];
|
|
18
|
-
export declare const jsz: ZigJS;
|
|
19
|
-
/** Store a value using jsz.storeValue and get the 64-bit reference. */
|
|
20
|
-
export declare function storeValueGetRef(val: any): bigint;
|
|
21
|
-
/** ZX Bridge - provides JS APIs that callback into WASM */
|
|
22
|
-
export declare class ZxBridge {
|
|
9
|
+
export declare class ZxBridge extends ZxBridgeCore {
|
|
23
10
|
#private;
|
|
24
11
|
constructor(exports: WebAssembly.Exports);
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
* Calls __zx_fetch_complete when done.
|
|
28
|
-
*/
|
|
29
|
-
fetchAsync(urlPtr: number, urlLen: number, methodPtr: number, methodLen: number, headersPtr: number, headersLen: number, bodyPtr: number, bodyLen: number, timeoutMs: number, fetchId: bigint): void;
|
|
30
|
-
/** Set a timeout and callback when it fires */
|
|
31
|
-
setTimeout(callbackId: bigint, delayMs: number): void;
|
|
32
|
-
/** Set an interval and callback each time it fires */
|
|
33
|
-
setInterval(callbackId: bigint, intervalMs: number): void;
|
|
34
|
-
/** Clear an interval */
|
|
35
|
-
clearInterval(callbackId: bigint): void;
|
|
12
|
+
/** Submit a form action with bound-state round-trip. */
|
|
13
|
+
submitFormActionAsync(form: HTMLFormElement, statesJson: string, fetchId: bigint): void;
|
|
36
14
|
/**
|
|
37
15
|
* Create and connect a WebSocket.
|
|
38
16
|
* Calls __zx_ws_onopen, __zx_ws_onmessage, __zx_ws_onerror, __zx_ws_onclose.
|
|
@@ -44,7 +22,7 @@ export declare class ZxBridge {
|
|
|
44
22
|
wsClose(wsId: bigint, code: number, reasonPtr: number, reasonLen: number): void;
|
|
45
23
|
/** Handle a DOM event (called by event delegation) */
|
|
46
24
|
eventbridge(velementId: bigint, eventTypeId: number, event: Event): void;
|
|
47
|
-
/** Create the import object for WASM instantiation */
|
|
25
|
+
/** Create the full browser import object for WASM instantiation (includes DOM + WebSocket). */
|
|
48
26
|
static createImportObject(bridgeRef: {
|
|
49
27
|
current: ZxBridge | null;
|
|
50
28
|
}): WebAssembly.Imports;
|
package/wasm/index.js
CHANGED
|
@@ -178,7 +178,7 @@ class ZigJS {
|
|
|
178
178
|
return decoder.decode(data);
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
-
// src/wasm/
|
|
181
|
+
// src/wasm/core.ts
|
|
182
182
|
var CallbackType = {
|
|
183
183
|
Event: 0,
|
|
184
184
|
FetchSuccess: 1,
|
|
@@ -229,25 +229,17 @@ function writeBytes(ptr, data) {
|
|
|
229
229
|
getMemoryView().set(data, ptr);
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
class
|
|
232
|
+
class ZxBridgeCore {
|
|
233
233
|
#intervals = new Map;
|
|
234
|
-
|
|
235
|
-
#alloc;
|
|
234
|
+
_alloc;
|
|
236
235
|
#handler;
|
|
237
236
|
#fetchCompleteHandler;
|
|
238
|
-
#wsOnOpenHandler;
|
|
239
|
-
#wsOnMessageHandler;
|
|
240
|
-
#wsOnErrorHandler;
|
|
241
|
-
#wsOnCloseHandler;
|
|
242
237
|
constructor(exports) {
|
|
243
|
-
this
|
|
238
|
+
this._alloc = exports.__zx_alloc;
|
|
244
239
|
this.#handler = exports.__zx_cb;
|
|
245
240
|
this.#fetchCompleteHandler = exports.__zx_fetch_complete;
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
this.#wsOnErrorHandler = exports.__zx_ws_onerror;
|
|
249
|
-
this.#wsOnCloseHandler = exports.__zx_ws_onclose;
|
|
250
|
-
this.#eventbridge = exports.__zx_eventbridge;
|
|
241
|
+
if (exports.memory)
|
|
242
|
+
jsz.memory = exports.memory;
|
|
251
243
|
}
|
|
252
244
|
#invoke(type, id, data) {
|
|
253
245
|
const handler = this.#handler;
|
|
@@ -287,19 +279,19 @@ class ZxBridge {
|
|
|
287
279
|
if (timeout)
|
|
288
280
|
clearTimeout(timeout);
|
|
289
281
|
const text = await response.text();
|
|
290
|
-
this
|
|
282
|
+
this._notifyFetchComplete(fetchId, response.status, text, false);
|
|
291
283
|
}).catch((error) => {
|
|
292
284
|
if (timeout)
|
|
293
285
|
clearTimeout(timeout);
|
|
294
286
|
const isAbort = error.name === "AbortError";
|
|
295
287
|
const errorMsg = isAbort ? "Request timeout" : error.message ?? "Fetch failed";
|
|
296
|
-
this
|
|
288
|
+
this._notifyFetchComplete(fetchId, 0, errorMsg, true);
|
|
297
289
|
});
|
|
298
290
|
}
|
|
299
|
-
|
|
291
|
+
_notifyFetchComplete(fetchId, statusCode, body, isError) {
|
|
300
292
|
const handler = this.#fetchCompleteHandler;
|
|
301
293
|
const encoded = textEncoder.encode(body);
|
|
302
|
-
const ptr = this
|
|
294
|
+
const ptr = this._alloc(encoded.length);
|
|
303
295
|
writeBytes(ptr, encoded);
|
|
304
296
|
handler(fetchId, statusCode, ptr, encoded.length, isError ? 1 : 0);
|
|
305
297
|
}
|
|
@@ -321,6 +313,83 @@ class ZxBridge {
|
|
|
321
313
|
this.#intervals.delete(callbackId);
|
|
322
314
|
}
|
|
323
315
|
}
|
|
316
|
+
_writeStringToWasm(str) {
|
|
317
|
+
return this._writeBytesToWasm(textEncoder.encode(str));
|
|
318
|
+
}
|
|
319
|
+
_writeBytesToWasm(data) {
|
|
320
|
+
const ptr = this._alloc(data.length);
|
|
321
|
+
writeBytes(ptr, data);
|
|
322
|
+
return { ptr, len: data.length };
|
|
323
|
+
}
|
|
324
|
+
static log(level, ptr, len) {
|
|
325
|
+
const msg = textDecoder.decode(getMemoryView().subarray(ptr, ptr + len));
|
|
326
|
+
switch (level) {
|
|
327
|
+
case 0:
|
|
328
|
+
console.error(msg);
|
|
329
|
+
break;
|
|
330
|
+
case 1:
|
|
331
|
+
console.warn(msg);
|
|
332
|
+
break;
|
|
333
|
+
case 3:
|
|
334
|
+
console.debug(msg);
|
|
335
|
+
break;
|
|
336
|
+
default:
|
|
337
|
+
console.log(msg);
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
static createImportObject(bridgeRef) {
|
|
342
|
+
return {
|
|
343
|
+
...jsz.importObject(),
|
|
344
|
+
__zx: {
|
|
345
|
+
_log: (level, ptr, len) => ZxBridgeCore.log(level, ptr, len),
|
|
346
|
+
_fetchAsync: (urlPtr, urlLen, methodPtr, methodLen, headersPtr, headersLen, bodyPtr, bodyLen, timeoutMs, fetchId) => {
|
|
347
|
+
bridgeRef.current?.fetchAsync(urlPtr, urlLen, methodPtr, methodLen, headersPtr, headersLen, bodyPtr, bodyLen, timeoutMs, fetchId);
|
|
348
|
+
},
|
|
349
|
+
_setTimeout: (callbackId, delayMs) => {
|
|
350
|
+
bridgeRef.current?.setTimeout(callbackId, delayMs);
|
|
351
|
+
},
|
|
352
|
+
_setInterval: (callbackId, intervalMs) => {
|
|
353
|
+
bridgeRef.current?.setInterval(callbackId, intervalMs);
|
|
354
|
+
},
|
|
355
|
+
_clearInterval: (callbackId) => {
|
|
356
|
+
bridgeRef.current?.clearInterval(callbackId);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
// src/wasm/index.ts
|
|
363
|
+
class ZxBridge extends ZxBridgeCore {
|
|
364
|
+
#websockets = new Map;
|
|
365
|
+
#wsOnOpenHandler;
|
|
366
|
+
#wsOnMessageHandler;
|
|
367
|
+
#wsOnErrorHandler;
|
|
368
|
+
#wsOnCloseHandler;
|
|
369
|
+
#eventbridge;
|
|
370
|
+
constructor(exports) {
|
|
371
|
+
super(exports);
|
|
372
|
+
this.#wsOnOpenHandler = exports.__zx_ws_onopen;
|
|
373
|
+
this.#wsOnMessageHandler = exports.__zx_ws_onmessage;
|
|
374
|
+
this.#wsOnErrorHandler = exports.__zx_ws_onerror;
|
|
375
|
+
this.#wsOnCloseHandler = exports.__zx_ws_onclose;
|
|
376
|
+
this.#eventbridge = exports.__zx_eventbridge;
|
|
377
|
+
}
|
|
378
|
+
submitFormActionAsync(form, statesJson, fetchId) {
|
|
379
|
+
const formData = new FormData(form);
|
|
380
|
+
formData.append("__zx_states", statesJson);
|
|
381
|
+
fetch(window.location.href, {
|
|
382
|
+
method: "POST",
|
|
383
|
+
headers: { "X-ZX-Action": "1" },
|
|
384
|
+
body: formData
|
|
385
|
+
}).then(async (response) => {
|
|
386
|
+
const text = await response.text();
|
|
387
|
+
this._notifyFetchComplete(fetchId, response.status, text, false);
|
|
388
|
+
}).catch((error) => {
|
|
389
|
+
const msg = error instanceof Error ? error.message : "Fetch failed";
|
|
390
|
+
this._notifyFetchComplete(fetchId, 0, msg, true);
|
|
391
|
+
});
|
|
392
|
+
}
|
|
324
393
|
wsConnect(wsId, urlPtr, urlLen, protocolsPtr, protocolsLen) {
|
|
325
394
|
const url = readString(urlPtr, urlLen);
|
|
326
395
|
const protocolsStr = protocolsLen > 0 ? readString(protocolsPtr, protocolsLen) : "";
|
|
@@ -333,7 +402,7 @@ class ZxBridge {
|
|
|
333
402
|
if (!handler)
|
|
334
403
|
return;
|
|
335
404
|
const protocol = ws.protocol || "";
|
|
336
|
-
const { ptr, len } = this
|
|
405
|
+
const { ptr, len } = this._writeStringToWasm(protocol);
|
|
337
406
|
handler(wsId, ptr, len);
|
|
338
407
|
};
|
|
339
408
|
ws.onmessage = (event) => {
|
|
@@ -341,21 +410,15 @@ class ZxBridge {
|
|
|
341
410
|
if (!handler)
|
|
342
411
|
return;
|
|
343
412
|
const isBinary = event.data instanceof ArrayBuffer;
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
data = new Uint8Array(event.data);
|
|
347
|
-
} else {
|
|
348
|
-
data = textEncoder.encode(event.data);
|
|
349
|
-
}
|
|
350
|
-
const { ptr, len } = this.#writeBytesToWasm(data);
|
|
413
|
+
const data = isBinary ? new Uint8Array(event.data) : textEncoder.encode(event.data);
|
|
414
|
+
const { ptr, len } = this._writeBytesToWasm(data);
|
|
351
415
|
handler(wsId, ptr, len, isBinary ? 1 : 0);
|
|
352
416
|
};
|
|
353
|
-
ws.onerror = (
|
|
417
|
+
ws.onerror = (_event) => {
|
|
354
418
|
const handler = this.#wsOnErrorHandler;
|
|
355
419
|
if (!handler)
|
|
356
420
|
return;
|
|
357
|
-
const
|
|
358
|
-
const { ptr, len } = this.#writeStringToWasm(msg);
|
|
421
|
+
const { ptr, len } = this._writeStringToWasm("WebSocket error");
|
|
359
422
|
handler(wsId, ptr, len);
|
|
360
423
|
};
|
|
361
424
|
ws.onclose = (event) => {
|
|
@@ -363,7 +426,7 @@ class ZxBridge {
|
|
|
363
426
|
if (!handler)
|
|
364
427
|
return;
|
|
365
428
|
const reason = event.reason || "";
|
|
366
|
-
const { ptr, len } = this
|
|
429
|
+
const { ptr, len } = this._writeStringToWasm(reason);
|
|
367
430
|
handler(wsId, event.code, ptr, len, event.wasClean ? 1 : 0);
|
|
368
431
|
this.#websockets.delete(wsId);
|
|
369
432
|
};
|
|
@@ -372,7 +435,7 @@ class ZxBridge {
|
|
|
372
435
|
const handler = this.#wsOnErrorHandler;
|
|
373
436
|
if (handler) {
|
|
374
437
|
const msg = error instanceof Error ? error.message : "WebSocket connection failed";
|
|
375
|
-
const { ptr, len } = this
|
|
438
|
+
const { ptr, len } = this._writeStringToWasm(msg);
|
|
376
439
|
handler(wsId, ptr, len);
|
|
377
440
|
}
|
|
378
441
|
}
|
|
@@ -394,25 +457,14 @@ class ZxBridge {
|
|
|
394
457
|
return;
|
|
395
458
|
const reason = reasonLen > 0 ? readString(reasonPtr, reasonLen) : undefined;
|
|
396
459
|
try {
|
|
397
|
-
if (reason)
|
|
460
|
+
if (reason)
|
|
398
461
|
ws.close(code, reason);
|
|
399
|
-
|
|
462
|
+
else
|
|
400
463
|
ws.close(code);
|
|
401
|
-
}
|
|
402
464
|
} catch {
|
|
403
465
|
ws.close();
|
|
404
466
|
}
|
|
405
467
|
}
|
|
406
|
-
#writeStringToWasm(str) {
|
|
407
|
-
const encoded = textEncoder.encode(str);
|
|
408
|
-
return this.#writeBytesToWasm(encoded);
|
|
409
|
-
}
|
|
410
|
-
#writeBytesToWasm(data) {
|
|
411
|
-
const ptr = this.#alloc(data.length);
|
|
412
|
-
writeBytes(ptr, data);
|
|
413
|
-
return { ptr, len: data.length };
|
|
414
|
-
}
|
|
415
|
-
#eventbridge;
|
|
416
468
|
eventbridge(velementId, eventTypeId, event) {
|
|
417
469
|
if (!this.#eventbridge)
|
|
418
470
|
return;
|
|
@@ -423,6 +475,7 @@ class ZxBridge {
|
|
|
423
475
|
return {
|
|
424
476
|
...jsz.importObject(),
|
|
425
477
|
__zx: {
|
|
478
|
+
_log: (level, ptr, len) => ZxBridgeCore.log(level, ptr, len),
|
|
426
479
|
_fetchAsync: (urlPtr, urlLen, methodPtr, methodLen, headersPtr, headersLen, bodyPtr, bodyLen, timeoutMs, fetchId) => {
|
|
427
480
|
bridgeRef.current?.fetchAsync(urlPtr, urlLen, methodPtr, methodLen, headersPtr, headersLen, bodyPtr, bodyLen, timeoutMs, fetchId);
|
|
428
481
|
},
|
|
@@ -498,6 +551,41 @@ class ZxBridge {
|
|
|
498
551
|
parent.replaceChild(newChild, oldChild);
|
|
499
552
|
cleanupDomNodes(oldChild);
|
|
500
553
|
}
|
|
554
|
+
},
|
|
555
|
+
_getLocationHref: (bufPtr, bufLen) => {
|
|
556
|
+
const bytes = textEncoder.encode(window.location.href);
|
|
557
|
+
const len = Math.min(bytes.length, bufLen);
|
|
558
|
+
writeBytes(bufPtr, bytes.subarray(0, len));
|
|
559
|
+
return len;
|
|
560
|
+
},
|
|
561
|
+
_getFormData: (vnodeId, bufPtr, bufLen) => {
|
|
562
|
+
const form = domNodes.get(vnodeId);
|
|
563
|
+
if (!form || !(form instanceof HTMLFormElement))
|
|
564
|
+
return 0;
|
|
565
|
+
const formData = new FormData(form);
|
|
566
|
+
const urlEncoded = new URLSearchParams(formData).toString();
|
|
567
|
+
const bytes = textEncoder.encode(urlEncoded);
|
|
568
|
+
const len = Math.min(bytes.length, bufLen);
|
|
569
|
+
writeBytes(bufPtr, bytes.subarray(0, len));
|
|
570
|
+
return len;
|
|
571
|
+
},
|
|
572
|
+
_submitFormAction: (vnodeId) => {
|
|
573
|
+
const form = domNodes.get(vnodeId);
|
|
574
|
+
if (!form || !(form instanceof HTMLFormElement))
|
|
575
|
+
return;
|
|
576
|
+
const formData = new FormData(form);
|
|
577
|
+
fetch(window.location.href, {
|
|
578
|
+
method: "POST",
|
|
579
|
+
headers: { "X-ZX-Action": "1" },
|
|
580
|
+
body: formData
|
|
581
|
+
}).catch(() => {});
|
|
582
|
+
},
|
|
583
|
+
_submitFormActionAsync: (vnodeId, statesPtr, statesLen, fetchId) => {
|
|
584
|
+
const form = domNodes.get(vnodeId);
|
|
585
|
+
if (!form || !(form instanceof HTMLFormElement))
|
|
586
|
+
return;
|
|
587
|
+
const statesJson = statesLen > 0 ? readString(statesPtr, statesLen) : "[]";
|
|
588
|
+
bridgeRef.current?.submitFormActionAsync(form, statesJson, fetchId);
|
|
501
589
|
}
|
|
502
590
|
}
|
|
503
591
|
};
|
|
@@ -792,10 +880,16 @@ async function init(options = {}) {
|
|
|
792
880
|
return { source, bridge };
|
|
793
881
|
}
|
|
794
882
|
export {
|
|
883
|
+
writeBytes,
|
|
884
|
+
textEncoder,
|
|
885
|
+
textDecoder,
|
|
795
886
|
storeValueGetRef,
|
|
887
|
+
readString,
|
|
796
888
|
jsz,
|
|
797
889
|
initEventDelegation,
|
|
798
890
|
init,
|
|
891
|
+
getMemoryView,
|
|
892
|
+
ZxBridgeCore,
|
|
799
893
|
ZxBridge,
|
|
800
894
|
CallbackType
|
|
801
895
|
};
|