ziex 0.1.0-dev.517 → 0.1.0-dev.526
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 +1 -1
- package/index.js +1 -1
- package/package.json +4 -3
- package/wasm/index.d.ts +38 -26
- package/wasm/index.js +125 -57
- package/wasm/init.d.ts +1 -0
- package/wasm/init.js +345 -0
package/README.md
CHANGED
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ziex",
|
|
3
|
-
"version": "0.1.0-dev.
|
|
3
|
+
"version": "0.1.0-dev.526",
|
|
4
4
|
"description": "ZX is a framework for building web applications with Zig.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./index.js",
|
|
9
9
|
"./react": "./react/index.js",
|
|
10
|
-
"./wasm": "./wasm/index.js"
|
|
10
|
+
"./wasm": "./wasm/index.js",
|
|
11
|
+
"./wasm/init": "./wasm/init.js"
|
|
11
12
|
},
|
|
12
13
|
"homepage": "https://github.com/nurulhudaapon/zx",
|
|
13
14
|
"repository": {
|
|
14
15
|
"type": "git",
|
|
15
|
-
"url": "https://github.com/nurulhudaapon/zx.git"
|
|
16
|
+
"url": "git+https://github.com/nurulhudaapon/zx.git"
|
|
16
17
|
},
|
|
17
18
|
"keywords": [
|
|
18
19
|
"zx",
|
package/wasm/index.d.ts
CHANGED
|
@@ -1,40 +1,52 @@
|
|
|
1
1
|
import { ZigJS } from "../../../../vendor/jsz/js/src";
|
|
2
|
+
/**
|
|
3
|
+
* ZX Client Bridge - Unified JS↔WASM communication layer
|
|
4
|
+
* Handles events, fetch, timers, and other async callbacks using jsz
|
|
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
|
+
};
|
|
13
|
+
export type CallbackTypeValue = typeof CallbackType[keyof typeof CallbackType];
|
|
2
14
|
export declare const jsz: ZigJS;
|
|
3
|
-
|
|
15
|
+
/** Store a value using jsz.storeValue and get the 64-bit reference. */
|
|
16
|
+
export declare function storeValueGetRef(val: any): bigint;
|
|
17
|
+
/** ZX Bridge - provides JS APIs that callback into WASM */
|
|
18
|
+
export declare class ZxBridge {
|
|
4
19
|
#private;
|
|
5
|
-
exports: WebAssembly.Exports;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
constructor(exports: WebAssembly.Exports);
|
|
21
|
+
/** Fetch a URL and callback with the response */
|
|
22
|
+
fetch(urlPtr: number, urlLen: number, callbackId: bigint): void;
|
|
23
|
+
/** Set a timeout and callback when it fires */
|
|
24
|
+
setTimeout(callbackId: bigint, delayMs: number): void;
|
|
25
|
+
/** Set an interval and callback each time it fires */
|
|
26
|
+
setInterval(callbackId: bigint, intervalMs: number): void;
|
|
27
|
+
/** Clear an interval */
|
|
28
|
+
clearInterval(callbackId: bigint): void;
|
|
29
|
+
/** Handle a DOM event (called by event delegation) */
|
|
30
|
+
eventbridge(velementId: bigint, eventTypeId: number, event: Event): void;
|
|
31
|
+
/** Create the import object for WASM instantiation */
|
|
32
|
+
static createImportObject(bridgeRef: {
|
|
33
|
+
current: ZxBridge | null;
|
|
34
|
+
}): WebAssembly.Imports;
|
|
16
35
|
}
|
|
17
|
-
|
|
36
|
+
/** Initialize event delegation */
|
|
37
|
+
export declare function initEventDelegation(bridge: ZxBridge, rootSelector?: string): void;
|
|
18
38
|
export type InitOptions = {
|
|
19
|
-
/** URL to the WASM file (default: /assets/main.wasm) */
|
|
20
39
|
url?: string;
|
|
21
|
-
/** CSS selector for the event delegation root element (default: 'body') */
|
|
22
40
|
eventDelegationRoot?: string;
|
|
23
41
|
importObject?: WebAssembly.Imports;
|
|
24
42
|
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
43
|
+
/** Initialize WASM with the ZX Bridge */
|
|
44
|
+
export declare function init(options?: InitOptions): Promise<{
|
|
45
|
+
source: WebAssembly.WebAssemblyInstantiatedSource;
|
|
46
|
+
bridge: ZxBridge;
|
|
47
|
+
}>;
|
|
29
48
|
declare global {
|
|
30
|
-
interface Window {
|
|
31
|
-
_zx: ZXInstance;
|
|
32
|
-
}
|
|
33
49
|
interface HTMLElement {
|
|
34
|
-
/**
|
|
35
|
-
* The VElement ID of the element
|
|
36
|
-
*/
|
|
37
50
|
__zx_ref?: number;
|
|
38
51
|
}
|
|
39
52
|
}
|
|
40
|
-
export {};
|
package/wasm/index.js
CHANGED
|
@@ -168,8 +168,102 @@ class ZigJS {
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
// src/wasm/index.ts
|
|
171
|
-
var
|
|
172
|
-
|
|
171
|
+
var CallbackType = {
|
|
172
|
+
Event: 0,
|
|
173
|
+
FetchSuccess: 1,
|
|
174
|
+
FetchError: 2,
|
|
175
|
+
Timeout: 3,
|
|
176
|
+
Interval: 4
|
|
177
|
+
};
|
|
178
|
+
var jsz = new ZigJS;
|
|
179
|
+
var tempRefBuffer = new ArrayBuffer(8);
|
|
180
|
+
var tempRefView = new DataView(tempRefBuffer);
|
|
181
|
+
function storeValueGetRef(val) {
|
|
182
|
+
const originalMemory = jsz.memory;
|
|
183
|
+
jsz.memory = { buffer: tempRefBuffer };
|
|
184
|
+
jsz.storeValue(0, val);
|
|
185
|
+
jsz.memory = originalMemory;
|
|
186
|
+
return tempRefView.getBigUint64(0, true);
|
|
187
|
+
}
|
|
188
|
+
function readString(ptr, len) {
|
|
189
|
+
const memory = new Uint8Array(jsz.memory.buffer);
|
|
190
|
+
return new TextDecoder().decode(memory.slice(ptr, ptr + len));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
class ZxBridge {
|
|
194
|
+
#exports;
|
|
195
|
+
#nextCallbackId = BigInt(1);
|
|
196
|
+
#intervals = new Map;
|
|
197
|
+
constructor(exports) {
|
|
198
|
+
this.#exports = exports;
|
|
199
|
+
}
|
|
200
|
+
get #handler() {
|
|
201
|
+
return this.#exports.__zx_cb;
|
|
202
|
+
}
|
|
203
|
+
#getNextId() {
|
|
204
|
+
return this.#nextCallbackId++;
|
|
205
|
+
}
|
|
206
|
+
#invoke(type, id, data) {
|
|
207
|
+
const handler = this.#handler;
|
|
208
|
+
if (!handler) {
|
|
209
|
+
console.warn("__zx_cb not exported from WASM");
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const dataRef = storeValueGetRef(data);
|
|
213
|
+
handler(type, id, dataRef);
|
|
214
|
+
}
|
|
215
|
+
fetch(urlPtr, urlLen, callbackId) {
|
|
216
|
+
const url = readString(urlPtr, urlLen);
|
|
217
|
+
fetch(url).then((response) => response.text()).then((text) => {
|
|
218
|
+
this.#invoke(CallbackType.FetchSuccess, callbackId, text);
|
|
219
|
+
}).catch((error) => {
|
|
220
|
+
this.#invoke(CallbackType.FetchError, callbackId, error.message ?? "Fetch failed");
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
setTimeout(callbackId, delayMs) {
|
|
224
|
+
setTimeout(() => {
|
|
225
|
+
this.#invoke(CallbackType.Timeout, callbackId, null);
|
|
226
|
+
}, delayMs);
|
|
227
|
+
}
|
|
228
|
+
setInterval(callbackId, intervalMs) {
|
|
229
|
+
const handle = setInterval(() => {
|
|
230
|
+
this.#invoke(CallbackType.Interval, callbackId, null);
|
|
231
|
+
}, intervalMs);
|
|
232
|
+
this.#intervals.set(callbackId, handle);
|
|
233
|
+
}
|
|
234
|
+
clearInterval(callbackId) {
|
|
235
|
+
const handle = this.#intervals.get(callbackId);
|
|
236
|
+
if (handle !== undefined) {
|
|
237
|
+
clearInterval(handle);
|
|
238
|
+
this.#intervals.delete(callbackId);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
eventbridge(velementId, eventTypeId, event) {
|
|
242
|
+
const eventRef = storeValueGetRef(event);
|
|
243
|
+
const eventbridge = this.#exports.__zx_eventbridge;
|
|
244
|
+
if (eventbridge)
|
|
245
|
+
eventbridge(velementId, eventTypeId, eventRef);
|
|
246
|
+
}
|
|
247
|
+
static createImportObject(bridgeRef) {
|
|
248
|
+
return {
|
|
249
|
+
...jsz.importObject(),
|
|
250
|
+
__zx: {
|
|
251
|
+
_fetch: (urlPtr, urlLen, callbackId) => {
|
|
252
|
+
bridgeRef.current?.fetch(urlPtr, urlLen, callbackId);
|
|
253
|
+
},
|
|
254
|
+
_setTimeout: (callbackId, delayMs) => {
|
|
255
|
+
bridgeRef.current?.setTimeout(callbackId, delayMs);
|
|
256
|
+
},
|
|
257
|
+
_setInterval: (callbackId, intervalMs) => {
|
|
258
|
+
bridgeRef.current?.setInterval(callbackId, intervalMs);
|
|
259
|
+
},
|
|
260
|
+
_clearInterval: (callbackId) => {
|
|
261
|
+
bridgeRef.current?.clearInterval(callbackId);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
173
267
|
var DELEGATED_EVENTS = [
|
|
174
268
|
"click",
|
|
175
269
|
"dblclick",
|
|
@@ -212,71 +306,45 @@ var EVENT_TYPE_MAP = {
|
|
|
212
306
|
touchmove: 17,
|
|
213
307
|
scroll: 18
|
|
214
308
|
};
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
addEvent(event) {
|
|
229
|
-
if (this.events.length >= MAX_EVENTS)
|
|
230
|
-
this.events.length = 0;
|
|
231
|
-
const idx = this.events.push(event);
|
|
232
|
-
return idx - 1;
|
|
233
|
-
}
|
|
234
|
-
initEventDelegation(rootSelector = "body") {
|
|
235
|
-
if (this.#eventDelegationInitialized)
|
|
236
|
-
return;
|
|
237
|
-
const root = document.querySelector(rootSelector);
|
|
238
|
-
if (!root)
|
|
239
|
-
return;
|
|
240
|
-
for (const eventType of DELEGATED_EVENTS) {
|
|
241
|
-
root.addEventListener(eventType, (event) => {
|
|
242
|
-
this.#handleDelegatedEvent(eventType, event);
|
|
243
|
-
}, { passive: eventType.startsWith("touch") || eventType === "scroll" });
|
|
244
|
-
}
|
|
245
|
-
this.#eventDelegationInitialized = true;
|
|
246
|
-
}
|
|
247
|
-
#handleDelegatedEvent(eventType, event) {
|
|
248
|
-
let target = event.target;
|
|
249
|
-
while (target && target !== document.body) {
|
|
250
|
-
const zxRef = target.__zx_ref;
|
|
251
|
-
if (zxRef !== undefined) {
|
|
252
|
-
const eventId = this.addEvent(event);
|
|
253
|
-
const handleEvent = this.exports.handleEvent;
|
|
254
|
-
if (typeof handleEvent === "function") {
|
|
255
|
-
const eventTypeId = EVENT_TYPE_MAP[eventType] ?? 0;
|
|
256
|
-
handleEvent(BigInt(zxRef), eventTypeId, BigInt(eventId));
|
|
309
|
+
function initEventDelegation(bridge, rootSelector = "body") {
|
|
310
|
+
const root = document.querySelector(rootSelector);
|
|
311
|
+
if (!root)
|
|
312
|
+
return;
|
|
313
|
+
for (const eventType of DELEGATED_EVENTS) {
|
|
314
|
+
root.addEventListener(eventType, (event) => {
|
|
315
|
+
let target = event.target;
|
|
316
|
+
while (target && target !== document.body) {
|
|
317
|
+
const zxRef = target.__zx_ref;
|
|
318
|
+
if (zxRef !== undefined) {
|
|
319
|
+
bridge.eventbridge(BigInt(zxRef), EVENT_TYPE_MAP[eventType] ?? 0, event);
|
|
320
|
+
break;
|
|
257
321
|
}
|
|
258
|
-
|
|
322
|
+
target = target.parentElement;
|
|
259
323
|
}
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
getZxRef(element) {
|
|
264
|
-
return element.__zx_ref;
|
|
324
|
+
}, { passive: eventType.startsWith("touch") || eventType === "scroll" });
|
|
265
325
|
}
|
|
266
326
|
}
|
|
327
|
+
var DEFAULT_URL = "/assets/main.wasm";
|
|
267
328
|
async function init(options = {}) {
|
|
268
|
-
const url = options
|
|
269
|
-
const
|
|
270
|
-
const
|
|
329
|
+
const url = options.url ?? DEFAULT_URL;
|
|
330
|
+
const bridgeRef = { current: null };
|
|
331
|
+
const importObject = Object.assign({}, ZxBridge.createImportObject(bridgeRef), options.importObject);
|
|
332
|
+
const source = await WebAssembly.instantiateStreaming(fetch(url), importObject);
|
|
333
|
+
const { instance } = source;
|
|
271
334
|
jsz.memory = instance.exports.memory;
|
|
272
|
-
|
|
273
|
-
|
|
335
|
+
const bridge = new ZxBridge(instance.exports);
|
|
336
|
+
bridgeRef.current = bridge;
|
|
337
|
+
initEventDelegation(bridge, options.eventDelegationRoot ?? "body");
|
|
274
338
|
const main = instance.exports.mainClient;
|
|
275
339
|
if (typeof main === "function")
|
|
276
340
|
main();
|
|
277
|
-
return
|
|
341
|
+
return { source, bridge };
|
|
278
342
|
}
|
|
279
343
|
export {
|
|
344
|
+
storeValueGetRef,
|
|
280
345
|
jsz,
|
|
281
|
-
|
|
346
|
+
initEventDelegation,
|
|
347
|
+
init,
|
|
348
|
+
ZxBridge,
|
|
349
|
+
CallbackType
|
|
282
350
|
};
|
package/wasm/init.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/wasm/init.js
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
// ../../vendor/jsz/js/src/zigjs.ts
|
|
2
|
+
var NAN_PREFIX = 2146959360;
|
|
3
|
+
var predefined = {
|
|
4
|
+
nan: 0,
|
|
5
|
+
null: 1,
|
|
6
|
+
true: 2,
|
|
7
|
+
false: 3,
|
|
8
|
+
undefined: 4,
|
|
9
|
+
globalThis: 5,
|
|
10
|
+
runtime: 6
|
|
11
|
+
};
|
|
12
|
+
var PREDEFINED_ID_MAX = 6;
|
|
13
|
+
var encoder = new TextEncoder;
|
|
14
|
+
var decoder = new TextDecoder("utf-8");
|
|
15
|
+
|
|
16
|
+
class ZigJS {
|
|
17
|
+
memory;
|
|
18
|
+
values = [NaN, null, true, false, undefined, globalThis, this];
|
|
19
|
+
idPool = [];
|
|
20
|
+
importObject() {
|
|
21
|
+
return {
|
|
22
|
+
"zig-js": {
|
|
23
|
+
valueGet: this.valueGet.bind(this),
|
|
24
|
+
valueSet: this.valueSet.bind(this),
|
|
25
|
+
valueDeinit: this.valueDeinit.bind(this),
|
|
26
|
+
valueObjectCreate: this.valueObjectCreate.bind(this),
|
|
27
|
+
valueStringCreate: this.valueStringCreate.bind(this),
|
|
28
|
+
valueStringLen: this.valueStringLen.bind(this),
|
|
29
|
+
valueStringCopy: this.valueStringCopy.bind(this),
|
|
30
|
+
valueNew: this.valueNew.bind(this),
|
|
31
|
+
funcApply: this.funcApply.bind(this)
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
valueGet(out, id, ptr, len) {
|
|
36
|
+
const val = this.loadValue(id);
|
|
37
|
+
const str = this.loadString(ptr, len);
|
|
38
|
+
const result = Reflect.get(val, str);
|
|
39
|
+
this.storeValue(out, result);
|
|
40
|
+
}
|
|
41
|
+
valueSet(id, ptr, len, refAddr) {
|
|
42
|
+
const obj = this.loadValue(id);
|
|
43
|
+
const str = this.loadString(ptr, len);
|
|
44
|
+
const val = this.loadRef(refAddr);
|
|
45
|
+
Reflect.set(obj, str, val);
|
|
46
|
+
}
|
|
47
|
+
valueDeinit(id) {
|
|
48
|
+
if (id > PREDEFINED_ID_MAX) {
|
|
49
|
+
this.values[id] = null;
|
|
50
|
+
this.idPool.push(id);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
valueObjectCreate(out) {
|
|
54
|
+
this.storeValue(out, new Object);
|
|
55
|
+
}
|
|
56
|
+
valueStringCreate(out, ptr, len) {
|
|
57
|
+
const str = this.loadString(ptr, len);
|
|
58
|
+
this.storeValue(out, str);
|
|
59
|
+
}
|
|
60
|
+
valueStringLen(id) {
|
|
61
|
+
const val = this.loadValue(id);
|
|
62
|
+
const buf = encoder.encode(val);
|
|
63
|
+
return buf.byteLength;
|
|
64
|
+
}
|
|
65
|
+
valueStringCopy(id, ptr, max) {
|
|
66
|
+
if (this.memory == null)
|
|
67
|
+
return;
|
|
68
|
+
const val = this.loadValue(id);
|
|
69
|
+
const bytes = encoder.encode(val);
|
|
70
|
+
if (bytes.byteLength > max)
|
|
71
|
+
return;
|
|
72
|
+
new Uint8Array(this.memory.buffer, ptr, bytes.length).set(bytes);
|
|
73
|
+
}
|
|
74
|
+
valueNew(out, id, argsAddr, argsLen) {
|
|
75
|
+
const fn = this.loadValue(id);
|
|
76
|
+
const args = [];
|
|
77
|
+
for (let i = 0;i < argsLen; i++) {
|
|
78
|
+
args.push(this.loadRef(argsAddr + i * 8));
|
|
79
|
+
}
|
|
80
|
+
const result = Reflect.construct(fn, args);
|
|
81
|
+
this.storeValue(out, result);
|
|
82
|
+
}
|
|
83
|
+
funcApply(out, id, thisRefAddr, argsAddr, argsLen) {
|
|
84
|
+
const fn = this.loadValue(id);
|
|
85
|
+
const thisVal = this.loadRef(thisRefAddr);
|
|
86
|
+
const args = [];
|
|
87
|
+
for (let i = 0;i < argsLen; i++) {
|
|
88
|
+
args.push(this.loadRef(argsAddr + i * 8));
|
|
89
|
+
}
|
|
90
|
+
const result = Reflect.apply(fn, thisVal, args);
|
|
91
|
+
this.storeValue(out, result);
|
|
92
|
+
}
|
|
93
|
+
loadValue(id) {
|
|
94
|
+
return this.values[id];
|
|
95
|
+
}
|
|
96
|
+
deleteValue(id) {
|
|
97
|
+
const val = this.values[id];
|
|
98
|
+
this.valueDeinit(id);
|
|
99
|
+
return val;
|
|
100
|
+
}
|
|
101
|
+
loadRef(refAddr) {
|
|
102
|
+
if (this.memory == null)
|
|
103
|
+
return;
|
|
104
|
+
const view = new DataView(this.memory.buffer);
|
|
105
|
+
const floatVal = view.getFloat64(refAddr, true);
|
|
106
|
+
if (!isNaN(floatVal))
|
|
107
|
+
return floatVal;
|
|
108
|
+
const id = this.loadRefId(refAddr);
|
|
109
|
+
return this.values[id];
|
|
110
|
+
}
|
|
111
|
+
loadRefId(refAddr) {
|
|
112
|
+
if (this.memory == null)
|
|
113
|
+
return 0;
|
|
114
|
+
return new DataView(this.memory.buffer).getUint32(refAddr, true);
|
|
115
|
+
}
|
|
116
|
+
storeValue(out, val) {
|
|
117
|
+
if (this.memory == null)
|
|
118
|
+
return;
|
|
119
|
+
const view = new DataView(this.memory.buffer);
|
|
120
|
+
if (typeof val === "number") {
|
|
121
|
+
if (isNaN(val)) {
|
|
122
|
+
view.setUint32(out, predefined.nan, true);
|
|
123
|
+
view.setUint32(out + 4, NAN_PREFIX, true);
|
|
124
|
+
} else {
|
|
125
|
+
view.setFloat64(out, val, true);
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (val === null) {
|
|
130
|
+
view.setUint32(out, predefined.null, true);
|
|
131
|
+
view.setUint32(out + 4, NAN_PREFIX, true);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (val === undefined) {
|
|
135
|
+
view.setUint32(out, predefined.undefined, true);
|
|
136
|
+
view.setUint32(out + 4, NAN_PREFIX, true);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
let id = this.idPool.pop();
|
|
140
|
+
if (id === undefined) {
|
|
141
|
+
id = this.values.length;
|
|
142
|
+
}
|
|
143
|
+
this.values[id] = val;
|
|
144
|
+
let typeId = 0;
|
|
145
|
+
switch (typeof val) {
|
|
146
|
+
case "object":
|
|
147
|
+
typeId = 1;
|
|
148
|
+
break;
|
|
149
|
+
case "string":
|
|
150
|
+
typeId = 2;
|
|
151
|
+
break;
|
|
152
|
+
case "symbol":
|
|
153
|
+
typeId = 3;
|
|
154
|
+
break;
|
|
155
|
+
case "function":
|
|
156
|
+
typeId = 4;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
view.setUint32(out, Number(id), true);
|
|
160
|
+
view.setUint32(out + 4, NAN_PREFIX | typeId, true);
|
|
161
|
+
}
|
|
162
|
+
loadString(ptr, len) {
|
|
163
|
+
if (this.memory == null)
|
|
164
|
+
return "";
|
|
165
|
+
const arr = new Uint8ClampedArray(this.memory.buffer, ptr, Number(len));
|
|
166
|
+
const data = arr.slice();
|
|
167
|
+
return decoder.decode(data);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// src/wasm/index.ts
|
|
171
|
+
var CallbackType = {
|
|
172
|
+
Event: 0,
|
|
173
|
+
FetchSuccess: 1,
|
|
174
|
+
FetchError: 2,
|
|
175
|
+
Timeout: 3,
|
|
176
|
+
Interval: 4
|
|
177
|
+
};
|
|
178
|
+
var jsz = new ZigJS;
|
|
179
|
+
var tempRefBuffer = new ArrayBuffer(8);
|
|
180
|
+
var tempRefView = new DataView(tempRefBuffer);
|
|
181
|
+
function storeValueGetRef(val) {
|
|
182
|
+
const originalMemory = jsz.memory;
|
|
183
|
+
jsz.memory = { buffer: tempRefBuffer };
|
|
184
|
+
jsz.storeValue(0, val);
|
|
185
|
+
jsz.memory = originalMemory;
|
|
186
|
+
return tempRefView.getBigUint64(0, true);
|
|
187
|
+
}
|
|
188
|
+
function readString(ptr, len) {
|
|
189
|
+
const memory = new Uint8Array(jsz.memory.buffer);
|
|
190
|
+
return new TextDecoder().decode(memory.slice(ptr, ptr + len));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
class ZxBridge {
|
|
194
|
+
#exports;
|
|
195
|
+
#nextCallbackId = BigInt(1);
|
|
196
|
+
#intervals = new Map;
|
|
197
|
+
constructor(exports) {
|
|
198
|
+
this.#exports = exports;
|
|
199
|
+
}
|
|
200
|
+
get #handler() {
|
|
201
|
+
return this.#exports.__zx_cb;
|
|
202
|
+
}
|
|
203
|
+
#getNextId() {
|
|
204
|
+
return this.#nextCallbackId++;
|
|
205
|
+
}
|
|
206
|
+
#invoke(type, id, data) {
|
|
207
|
+
const handler = this.#handler;
|
|
208
|
+
if (!handler) {
|
|
209
|
+
console.warn("__zx_cb not exported from WASM");
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const dataRef = storeValueGetRef(data);
|
|
213
|
+
handler(type, id, dataRef);
|
|
214
|
+
}
|
|
215
|
+
fetch(urlPtr, urlLen, callbackId) {
|
|
216
|
+
const url = readString(urlPtr, urlLen);
|
|
217
|
+
fetch(url).then((response) => response.text()).then((text) => {
|
|
218
|
+
this.#invoke(CallbackType.FetchSuccess, callbackId, text);
|
|
219
|
+
}).catch((error) => {
|
|
220
|
+
this.#invoke(CallbackType.FetchError, callbackId, error.message ?? "Fetch failed");
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
setTimeout(callbackId, delayMs) {
|
|
224
|
+
setTimeout(() => {
|
|
225
|
+
this.#invoke(CallbackType.Timeout, callbackId, null);
|
|
226
|
+
}, delayMs);
|
|
227
|
+
}
|
|
228
|
+
setInterval(callbackId, intervalMs) {
|
|
229
|
+
const handle = setInterval(() => {
|
|
230
|
+
this.#invoke(CallbackType.Interval, callbackId, null);
|
|
231
|
+
}, intervalMs);
|
|
232
|
+
this.#intervals.set(callbackId, handle);
|
|
233
|
+
}
|
|
234
|
+
clearInterval(callbackId) {
|
|
235
|
+
const handle = this.#intervals.get(callbackId);
|
|
236
|
+
if (handle !== undefined) {
|
|
237
|
+
clearInterval(handle);
|
|
238
|
+
this.#intervals.delete(callbackId);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
eventbridge(velementId, eventTypeId, event) {
|
|
242
|
+
const eventRef = storeValueGetRef(event);
|
|
243
|
+
const eventbridge = this.#exports.__zx_eventbridge;
|
|
244
|
+
if (eventbridge)
|
|
245
|
+
eventbridge(velementId, eventTypeId, eventRef);
|
|
246
|
+
}
|
|
247
|
+
static createImportObject(bridgeRef) {
|
|
248
|
+
return {
|
|
249
|
+
...jsz.importObject(),
|
|
250
|
+
__zx: {
|
|
251
|
+
_fetch: (urlPtr, urlLen, callbackId) => {
|
|
252
|
+
bridgeRef.current?.fetch(urlPtr, urlLen, callbackId);
|
|
253
|
+
},
|
|
254
|
+
_setTimeout: (callbackId, delayMs) => {
|
|
255
|
+
bridgeRef.current?.setTimeout(callbackId, delayMs);
|
|
256
|
+
},
|
|
257
|
+
_setInterval: (callbackId, intervalMs) => {
|
|
258
|
+
bridgeRef.current?.setInterval(callbackId, intervalMs);
|
|
259
|
+
},
|
|
260
|
+
_clearInterval: (callbackId) => {
|
|
261
|
+
bridgeRef.current?.clearInterval(callbackId);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
var DELEGATED_EVENTS = [
|
|
268
|
+
"click",
|
|
269
|
+
"dblclick",
|
|
270
|
+
"input",
|
|
271
|
+
"change",
|
|
272
|
+
"submit",
|
|
273
|
+
"focus",
|
|
274
|
+
"blur",
|
|
275
|
+
"keydown",
|
|
276
|
+
"keyup",
|
|
277
|
+
"keypress",
|
|
278
|
+
"mouseenter",
|
|
279
|
+
"mouseleave",
|
|
280
|
+
"mousedown",
|
|
281
|
+
"mouseup",
|
|
282
|
+
"mousemove",
|
|
283
|
+
"touchstart",
|
|
284
|
+
"touchend",
|
|
285
|
+
"touchmove",
|
|
286
|
+
"scroll"
|
|
287
|
+
];
|
|
288
|
+
var EVENT_TYPE_MAP = {
|
|
289
|
+
click: 0,
|
|
290
|
+
dblclick: 1,
|
|
291
|
+
input: 2,
|
|
292
|
+
change: 3,
|
|
293
|
+
submit: 4,
|
|
294
|
+
focus: 5,
|
|
295
|
+
blur: 6,
|
|
296
|
+
keydown: 7,
|
|
297
|
+
keyup: 8,
|
|
298
|
+
keypress: 9,
|
|
299
|
+
mouseenter: 10,
|
|
300
|
+
mouseleave: 11,
|
|
301
|
+
mousedown: 12,
|
|
302
|
+
mouseup: 13,
|
|
303
|
+
mousemove: 14,
|
|
304
|
+
touchstart: 15,
|
|
305
|
+
touchend: 16,
|
|
306
|
+
touchmove: 17,
|
|
307
|
+
scroll: 18
|
|
308
|
+
};
|
|
309
|
+
function initEventDelegation(bridge, rootSelector = "body") {
|
|
310
|
+
const root = document.querySelector(rootSelector);
|
|
311
|
+
if (!root)
|
|
312
|
+
return;
|
|
313
|
+
for (const eventType of DELEGATED_EVENTS) {
|
|
314
|
+
root.addEventListener(eventType, (event) => {
|
|
315
|
+
let target = event.target;
|
|
316
|
+
while (target && target !== document.body) {
|
|
317
|
+
const zxRef = target.__zx_ref;
|
|
318
|
+
if (zxRef !== undefined) {
|
|
319
|
+
bridge.eventbridge(BigInt(zxRef), EVENT_TYPE_MAP[eventType] ?? 0, event);
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
target = target.parentElement;
|
|
323
|
+
}
|
|
324
|
+
}, { passive: eventType.startsWith("touch") || eventType === "scroll" });
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
var DEFAULT_URL = "/assets/main.wasm";
|
|
328
|
+
async function init(options = {}) {
|
|
329
|
+
const url = options.url ?? DEFAULT_URL;
|
|
330
|
+
const bridgeRef = { current: null };
|
|
331
|
+
const importObject = Object.assign({}, ZxBridge.createImportObject(bridgeRef), options.importObject);
|
|
332
|
+
const source = await WebAssembly.instantiateStreaming(fetch(url), importObject);
|
|
333
|
+
const { instance } = source;
|
|
334
|
+
jsz.memory = instance.exports.memory;
|
|
335
|
+
const bridge = new ZxBridge(instance.exports);
|
|
336
|
+
bridgeRef.current = bridge;
|
|
337
|
+
initEventDelegation(bridge, options.eventDelegationRoot ?? "body");
|
|
338
|
+
const main = instance.exports.mainClient;
|
|
339
|
+
if (typeof main === "function")
|
|
340
|
+
main();
|
|
341
|
+
return { source, bridge };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/wasm/init.ts
|
|
345
|
+
(() => init())();
|