ziex 0.1.0-dev.785 → 0.1.0-dev.787

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ziex",
3
- "version": "0.1.0-dev.785",
3
+ "version": "0.1.0-dev.787",
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,10 @@
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
+ "./hono": "./hono/index.js",
14
+ "./aws-lambda": "./aws-lambda/index.js",
15
+ "./vercel": "./vercel/index.js"
13
16
  },
14
17
  "homepage": "https://ziex.dev",
15
18
  "repository": {
@@ -30,6 +33,17 @@
30
33
  ],
31
34
  "author": "Nurul Huda (Apon) <me@nurulhudaapon.com>",
32
35
  "license": "MIT",
36
+ "peerDependenciesMeta": {
37
+ "react": {
38
+ "optional": true
39
+ },
40
+ "react-dom": {
41
+ "optional": true
42
+ },
43
+ "hono": {
44
+ "optional": true
45
+ }
46
+ },
33
47
  "module": "index.js",
34
48
  "types": "index.d.ts"
35
49
  }
@@ -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 {};
@@ -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/wasm/index.d.ts CHANGED
@@ -27,6 +27,10 @@ export declare class ZxBridge {
27
27
  * Calls __zx_fetch_complete when done.
28
28
  */
29
29
  fetchAsync(urlPtr: number, urlLen: number, methodPtr: number, methodLen: number, headersPtr: number, headersLen: number, bodyPtr: number, bodyLen: number, timeoutMs: number, fetchId: bigint): void;
30
+ /** Submit a form action with bound-state round-trip.
31
+ * Sends the form as multipart/form-data + __zx_states field,
32
+ * then calls __zx_fetch_complete so WASM can apply state updates. */
33
+ submitFormActionAsync(form: HTMLFormElement, statesJson: string, fetchId: bigint): void;
30
34
  /** Set a timeout and callback when it fires */
31
35
  setTimeout(callbackId: bigint, delayMs: number): void;
32
36
  /** Set an interval and callback each time it fires */
@@ -61,11 +65,6 @@ export declare function init(options?: InitOptions): Promise<{
61
65
  source: WebAssembly.WebAssemblyInstantiatedSource;
62
66
  bridge: ZxBridge;
63
67
  }>;
64
- /** Initialize WASM with the ZX Bridge */
65
- export declare function edge(options?: InitOptions): Promise<{
66
- source: WebAssembly.WebAssemblyInstantiatedSource;
67
- bridge: ZxBridge;
68
- }>;
69
68
  declare global {
70
69
  interface HTMLElement {
71
70
  __zx_ref?: number;
package/wasm/index.js CHANGED
@@ -200,42 +200,54 @@ function storeValueGetRef(val) {
200
200
  jsz.memory = originalMemory;
201
201
  return tempRefView.getBigUint64(0, true);
202
202
  }
203
+ var textDecoder = new TextDecoder;
204
+ var textEncoder = new TextEncoder;
205
+ var memoryView = null;
206
+ var memoryBuffer = null;
207
+ function getMemoryView() {
208
+ const buf = jsz.memory.buffer;
209
+ if (buf !== memoryBuffer) {
210
+ memoryBuffer = buf;
211
+ memoryView = new Uint8Array(buf);
212
+ }
213
+ return memoryView;
214
+ }
215
+ var stringCache = new Map;
216
+ function stringCacheKey(ptr, len) {
217
+ return ptr * 65536 + len;
218
+ }
203
219
  function readString(ptr, len) {
204
- const memory = new Uint8Array(jsz.memory.buffer);
205
- return new TextDecoder().decode(memory.slice(ptr, ptr + len));
220
+ const key = stringCacheKey(ptr, len);
221
+ const cached = stringCache.get(key);
222
+ if (cached !== undefined)
223
+ return cached;
224
+ const str = textDecoder.decode(getMemoryView().subarray(ptr, ptr + len));
225
+ stringCache.set(key, str);
226
+ return str;
206
227
  }
207
228
  function writeBytes(ptr, data) {
208
- const memory = new Uint8Array(jsz.memory.buffer);
209
- memory.set(data, ptr);
229
+ getMemoryView().set(data, ptr);
210
230
  }
211
231
 
212
232
  class ZxBridge {
213
- #exports;
214
233
  #intervals = new Map;
215
234
  #websockets = new Map;
235
+ #alloc;
236
+ #handler;
237
+ #fetchCompleteHandler;
238
+ #wsOnOpenHandler;
239
+ #wsOnMessageHandler;
240
+ #wsOnErrorHandler;
241
+ #wsOnCloseHandler;
216
242
  constructor(exports) {
217
- this.#exports = exports;
218
- }
219
- get #alloc() {
220
- return this.#exports.__zx_alloc;
221
- }
222
- get #handler() {
223
- return this.#exports.__zx_cb;
224
- }
225
- get #fetchCompleteHandler() {
226
- return this.#exports.__zx_fetch_complete;
227
- }
228
- get #wsOnOpenHandler() {
229
- return this.#exports.__zx_ws_onopen;
230
- }
231
- get #wsOnMessageHandler() {
232
- return this.#exports.__zx_ws_onmessage;
233
- }
234
- get #wsOnErrorHandler() {
235
- return this.#exports.__zx_ws_onerror;
236
- }
237
- get #wsOnCloseHandler() {
238
- return this.#exports.__zx_ws_onclose;
243
+ this.#alloc = exports.__zx_alloc;
244
+ this.#handler = exports.__zx_cb;
245
+ this.#fetchCompleteHandler = exports.__zx_fetch_complete;
246
+ this.#wsOnOpenHandler = exports.__zx_ws_onopen;
247
+ this.#wsOnMessageHandler = exports.__zx_ws_onmessage;
248
+ this.#wsOnErrorHandler = exports.__zx_ws_onerror;
249
+ this.#wsOnCloseHandler = exports.__zx_ws_onclose;
250
+ this.#eventbridge = exports.__zx_eventbridge;
239
251
  }
240
252
  #invoke(type, id, data) {
241
253
  const handler = this.#handler;
@@ -286,11 +298,26 @@ class ZxBridge {
286
298
  }
287
299
  #notifyFetchComplete(fetchId, statusCode, body, isError) {
288
300
  const handler = this.#fetchCompleteHandler;
289
- const encoded = new TextEncoder().encode(body);
301
+ const encoded = textEncoder.encode(body);
290
302
  const ptr = this.#alloc(encoded.length);
291
303
  writeBytes(ptr, encoded);
292
304
  handler(fetchId, statusCode, ptr, encoded.length, isError ? 1 : 0);
293
305
  }
306
+ submitFormActionAsync(form, statesJson, fetchId) {
307
+ const formData = new FormData(form);
308
+ formData.append("__zx_states", statesJson);
309
+ fetch(window.location.href, {
310
+ method: "POST",
311
+ headers: { "X-ZX-Action": "1" },
312
+ body: formData
313
+ }).then(async (response) => {
314
+ const text = await response.text();
315
+ this.#notifyFetchComplete(fetchId, response.status, text, false);
316
+ }).catch((error) => {
317
+ const msg = error instanceof Error ? error.message : "Fetch failed";
318
+ this.#notifyFetchComplete(fetchId, 0, msg, true);
319
+ });
320
+ }
294
321
  setTimeout(callbackId, delayMs) {
295
322
  setTimeout(() => {
296
323
  this.#invoke(CallbackType.Timeout, callbackId, null);
@@ -333,7 +360,7 @@ class ZxBridge {
333
360
  if (isBinary) {
334
361
  data = new Uint8Array(event.data);
335
362
  } else {
336
- data = new TextEncoder().encode(event.data);
363
+ data = textEncoder.encode(event.data);
337
364
  }
338
365
  const { ptr, len } = this.#writeBytesToWasm(data);
339
366
  handler(wsId, ptr, len, isBinary ? 1 : 0);
@@ -369,12 +396,11 @@ class ZxBridge {
369
396
  const ws = this.#websockets.get(wsId);
370
397
  if (!ws || ws.readyState !== WebSocket.OPEN)
371
398
  return;
372
- const memory = new Uint8Array(jsz.memory.buffer);
373
- const data = memory.slice(dataPtr, dataPtr + dataLen);
399
+ const memory = getMemoryView();
374
400
  if (isBinary) {
375
- ws.send(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
401
+ ws.send(memory.slice(dataPtr, dataPtr + dataLen));
376
402
  } else {
377
- ws.send(new TextDecoder().decode(data));
403
+ ws.send(textDecoder.decode(memory.subarray(dataPtr, dataPtr + dataLen)));
378
404
  }
379
405
  }
380
406
  wsClose(wsId, code, reasonPtr, reasonLen) {
@@ -393,7 +419,7 @@ class ZxBridge {
393
419
  }
394
420
  }
395
421
  #writeStringToWasm(str) {
396
- const encoded = new TextEncoder().encode(str);
422
+ const encoded = textEncoder.encode(str);
397
423
  return this.#writeBytesToWasm(encoded);
398
424
  }
399
425
  #writeBytesToWasm(data) {
@@ -401,11 +427,12 @@ class ZxBridge {
401
427
  writeBytes(ptr, data);
402
428
  return { ptr, len: data.length };
403
429
  }
430
+ #eventbridge;
404
431
  eventbridge(velementId, eventTypeId, event) {
432
+ if (!this.#eventbridge)
433
+ return;
405
434
  const eventRef = storeValueGetRef(event);
406
- const eventbridge = this.#exports.__zx_eventbridge;
407
- if (eventbridge)
408
- eventbridge(velementId, eventTypeId, eventRef);
435
+ this.#eventbridge(velementId, eventTypeId, eventRef);
409
436
  }
410
437
  static createImportObject(bridgeRef) {
411
438
  return {
@@ -432,15 +459,109 @@ class ZxBridge {
432
459
  _wsClose: (wsId, code, reasonPtr, reasonLen) => {
433
460
  bridgeRef.current?.wsClose(wsId, code, reasonPtr, reasonLen);
434
461
  },
435
- _ce: (id) => {
462
+ _ce: (id, vnodeId) => {
436
463
  const tagName = TAG_NAMES[id];
437
464
  const el = id >= SVG_TAG_START_INDEX ? document.createElementNS("http://www.w3.org/2000/svg", tagName) : document.createElement(tagName);
465
+ el.__zx_ref = Number(vnodeId);
466
+ domNodes.set(vnodeId, el);
438
467
  return storeValueGetRef(el);
468
+ },
469
+ _ct: (ptr, len, vnodeId) => {
470
+ const text = readString(ptr, len);
471
+ const node = document.createTextNode(text);
472
+ node.__zx_ref = Number(vnodeId);
473
+ domNodes.set(vnodeId, node);
474
+ return storeValueGetRef(node);
475
+ },
476
+ _sa: (vnodeId, namePtr, nameLen, valPtr, valLen) => {
477
+ domNodes.get(vnodeId)?.setAttribute(readString(namePtr, nameLen), readString(valPtr, valLen));
478
+ },
479
+ _ra: (vnodeId, namePtr, nameLen) => {
480
+ domNodes.get(vnodeId)?.removeAttribute(readString(namePtr, nameLen));
481
+ },
482
+ _snv: (vnodeId, ptr, len) => {
483
+ const node = domNodes.get(vnodeId);
484
+ if (node)
485
+ node.nodeValue = readString(ptr, len);
486
+ },
487
+ _ac: (parentId, childId) => {
488
+ const parent = domNodes.get(parentId);
489
+ const child = domNodes.get(childId);
490
+ if (parent && child)
491
+ parent.appendChild(child);
492
+ },
493
+ _ib: (parentId, childId, refId) => {
494
+ const parent = domNodes.get(parentId);
495
+ const child = domNodes.get(childId);
496
+ const ref = domNodes.get(refId) ?? null;
497
+ if (parent && child)
498
+ parent.insertBefore(child, ref);
499
+ },
500
+ _rc: (parentId, childId) => {
501
+ const parent = domNodes.get(parentId);
502
+ const child = domNodes.get(childId);
503
+ if (parent && child) {
504
+ parent.removeChild(child);
505
+ cleanupDomNodes(child);
506
+ }
507
+ },
508
+ _rpc: (parentId, newId, oldId) => {
509
+ const parent = domNodes.get(parentId);
510
+ const newChild = domNodes.get(newId);
511
+ const oldChild = domNodes.get(oldId);
512
+ if (parent && newChild && oldChild) {
513
+ parent.replaceChild(newChild, oldChild);
514
+ cleanupDomNodes(oldChild);
515
+ }
516
+ },
517
+ _getLocationHref: (bufPtr, bufLen) => {
518
+ const bytes = textEncoder.encode(window.location.href);
519
+ const len = Math.min(bytes.length, bufLen);
520
+ writeBytes(bufPtr, bytes.subarray(0, len));
521
+ return len;
522
+ },
523
+ _getFormData: (vnodeId, bufPtr, bufLen) => {
524
+ const form = domNodes.get(vnodeId);
525
+ if (!form || !(form instanceof HTMLFormElement))
526
+ return 0;
527
+ const formData = new FormData(form);
528
+ const urlEncoded = new URLSearchParams(formData).toString();
529
+ const bytes = textEncoder.encode(urlEncoded);
530
+ const len = Math.min(bytes.length, bufLen);
531
+ writeBytes(bufPtr, bytes.subarray(0, len));
532
+ return len;
533
+ },
534
+ _submitFormAction: (vnodeId) => {
535
+ const form = domNodes.get(vnodeId);
536
+ if (!form || !(form instanceof HTMLFormElement))
537
+ return;
538
+ const formData = new FormData(form);
539
+ fetch(window.location.href, {
540
+ method: "POST",
541
+ headers: { "X-ZX-Action": "1" },
542
+ body: formData
543
+ }).catch(() => {});
544
+ },
545
+ _submitFormActionAsync: (vnodeId, statesPtr, statesLen, fetchId) => {
546
+ const form = domNodes.get(vnodeId);
547
+ if (!form || !(form instanceof HTMLFormElement))
548
+ return;
549
+ const statesJson = statesLen > 0 ? readString(statesPtr, statesLen) : "[]";
550
+ bridgeRef.current?.submitFormActionAsync(form, statesJson, fetchId);
439
551
  }
440
552
  }
441
553
  };
442
554
  }
443
555
  }
556
+ var domNodes = new Map;
557
+ function cleanupDomNodes(node) {
558
+ const ref = node.__zx_ref;
559
+ if (ref !== undefined)
560
+ domNodes.delete(BigInt(ref));
561
+ const children = node.childNodes;
562
+ for (let i = 0;i < children.length; i++)
563
+ cleanupDomNodes(children[i]);
564
+ }
444
565
  var SVG_TAG_START_INDEX = 140;
445
566
  var TAG_NAMES = [
446
567
  "aside",
@@ -696,7 +817,8 @@ function initEventDelegation(bridge, rootSelector = "body") {
696
817
  const zxRef = target.__zx_ref;
697
818
  if (zxRef !== undefined) {
698
819
  bridge.eventbridge(BigInt(zxRef), EVENT_TYPE_MAP[eventType] ?? 0, event);
699
- break;
820
+ if (event.cancelBubble)
821
+ break;
700
822
  }
701
823
  target = target.parentElement;
702
824
  }
@@ -719,27 +841,11 @@ async function init(options = {}) {
719
841
  main();
720
842
  return { source, bridge };
721
843
  }
722
- async function edge(options = {}) {
723
- const url = options.url ?? DEFAULT_URL;
724
- const bridgeRef = { current: null };
725
- const importObject = Object.assign({}, ZxBridge.createImportObject(bridgeRef), options.importObject);
726
- const source = await WebAssembly.instantiateStreaming(fetch(url), importObject);
727
- const { instance } = source;
728
- jsz.memory = instance.exports.memory;
729
- const bridge = new ZxBridge(instance.exports);
730
- bridgeRef.current = bridge;
731
- initEventDelegation(bridge, options.eventDelegationRoot ?? "body");
732
- const main = instance.exports.main;
733
- if (typeof main === "function")
734
- main();
735
- return { source, bridge };
736
- }
737
844
  export {
738
845
  storeValueGetRef,
739
846
  jsz,
740
847
  initEventDelegation,
741
848
  init,
742
- edge,
743
849
  ZxBridge,
744
850
  CallbackType
745
851
  };
package/wasm/init.js CHANGED
@@ -189,42 +189,54 @@ function storeValueGetRef(val) {
189
189
  jsz.memory = originalMemory;
190
190
  return tempRefView.getBigUint64(0, true);
191
191
  }
192
+ var textDecoder = new TextDecoder;
193
+ var textEncoder = new TextEncoder;
194
+ var memoryView = null;
195
+ var memoryBuffer = null;
196
+ function getMemoryView() {
197
+ const buf = jsz.memory.buffer;
198
+ if (buf !== memoryBuffer) {
199
+ memoryBuffer = buf;
200
+ memoryView = new Uint8Array(buf);
201
+ }
202
+ return memoryView;
203
+ }
204
+ var stringCache = new Map;
205
+ function stringCacheKey(ptr, len) {
206
+ return ptr * 65536 + len;
207
+ }
192
208
  function readString(ptr, len) {
193
- const memory = new Uint8Array(jsz.memory.buffer);
194
- return new TextDecoder().decode(memory.slice(ptr, ptr + len));
209
+ const key = stringCacheKey(ptr, len);
210
+ const cached = stringCache.get(key);
211
+ if (cached !== undefined)
212
+ return cached;
213
+ const str = textDecoder.decode(getMemoryView().subarray(ptr, ptr + len));
214
+ stringCache.set(key, str);
215
+ return str;
195
216
  }
196
217
  function writeBytes(ptr, data) {
197
- const memory = new Uint8Array(jsz.memory.buffer);
198
- memory.set(data, ptr);
218
+ getMemoryView().set(data, ptr);
199
219
  }
200
220
 
201
221
  class ZxBridge {
202
- #exports;
203
222
  #intervals = new Map;
204
223
  #websockets = new Map;
224
+ #alloc;
225
+ #handler;
226
+ #fetchCompleteHandler;
227
+ #wsOnOpenHandler;
228
+ #wsOnMessageHandler;
229
+ #wsOnErrorHandler;
230
+ #wsOnCloseHandler;
205
231
  constructor(exports) {
206
- this.#exports = exports;
207
- }
208
- get #alloc() {
209
- return this.#exports.__zx_alloc;
210
- }
211
- get #handler() {
212
- return this.#exports.__zx_cb;
213
- }
214
- get #fetchCompleteHandler() {
215
- return this.#exports.__zx_fetch_complete;
216
- }
217
- get #wsOnOpenHandler() {
218
- return this.#exports.__zx_ws_onopen;
219
- }
220
- get #wsOnMessageHandler() {
221
- return this.#exports.__zx_ws_onmessage;
222
- }
223
- get #wsOnErrorHandler() {
224
- return this.#exports.__zx_ws_onerror;
225
- }
226
- get #wsOnCloseHandler() {
227
- return this.#exports.__zx_ws_onclose;
232
+ this.#alloc = exports.__zx_alloc;
233
+ this.#handler = exports.__zx_cb;
234
+ this.#fetchCompleteHandler = exports.__zx_fetch_complete;
235
+ this.#wsOnOpenHandler = exports.__zx_ws_onopen;
236
+ this.#wsOnMessageHandler = exports.__zx_ws_onmessage;
237
+ this.#wsOnErrorHandler = exports.__zx_ws_onerror;
238
+ this.#wsOnCloseHandler = exports.__zx_ws_onclose;
239
+ this.#eventbridge = exports.__zx_eventbridge;
228
240
  }
229
241
  #invoke(type, id, data) {
230
242
  const handler = this.#handler;
@@ -275,11 +287,26 @@ class ZxBridge {
275
287
  }
276
288
  #notifyFetchComplete(fetchId, statusCode, body, isError) {
277
289
  const handler = this.#fetchCompleteHandler;
278
- const encoded = new TextEncoder().encode(body);
290
+ const encoded = textEncoder.encode(body);
279
291
  const ptr = this.#alloc(encoded.length);
280
292
  writeBytes(ptr, encoded);
281
293
  handler(fetchId, statusCode, ptr, encoded.length, isError ? 1 : 0);
282
294
  }
295
+ submitFormActionAsync(form, statesJson, fetchId) {
296
+ const formData = new FormData(form);
297
+ formData.append("__zx_states", statesJson);
298
+ fetch(window.location.href, {
299
+ method: "POST",
300
+ headers: { "X-ZX-Action": "1" },
301
+ body: formData
302
+ }).then(async (response) => {
303
+ const text = await response.text();
304
+ this.#notifyFetchComplete(fetchId, response.status, text, false);
305
+ }).catch((error) => {
306
+ const msg = error instanceof Error ? error.message : "Fetch failed";
307
+ this.#notifyFetchComplete(fetchId, 0, msg, true);
308
+ });
309
+ }
283
310
  setTimeout(callbackId, delayMs) {
284
311
  setTimeout(() => {
285
312
  this.#invoke(CallbackType.Timeout, callbackId, null);
@@ -322,7 +349,7 @@ class ZxBridge {
322
349
  if (isBinary) {
323
350
  data = new Uint8Array(event.data);
324
351
  } else {
325
- data = new TextEncoder().encode(event.data);
352
+ data = textEncoder.encode(event.data);
326
353
  }
327
354
  const { ptr, len } = this.#writeBytesToWasm(data);
328
355
  handler(wsId, ptr, len, isBinary ? 1 : 0);
@@ -358,12 +385,11 @@ class ZxBridge {
358
385
  const ws = this.#websockets.get(wsId);
359
386
  if (!ws || ws.readyState !== WebSocket.OPEN)
360
387
  return;
361
- const memory = new Uint8Array(jsz.memory.buffer);
362
- const data = memory.slice(dataPtr, dataPtr + dataLen);
388
+ const memory = getMemoryView();
363
389
  if (isBinary) {
364
- ws.send(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
390
+ ws.send(memory.slice(dataPtr, dataPtr + dataLen));
365
391
  } else {
366
- ws.send(new TextDecoder().decode(data));
392
+ ws.send(textDecoder.decode(memory.subarray(dataPtr, dataPtr + dataLen)));
367
393
  }
368
394
  }
369
395
  wsClose(wsId, code, reasonPtr, reasonLen) {
@@ -382,7 +408,7 @@ class ZxBridge {
382
408
  }
383
409
  }
384
410
  #writeStringToWasm(str) {
385
- const encoded = new TextEncoder().encode(str);
411
+ const encoded = textEncoder.encode(str);
386
412
  return this.#writeBytesToWasm(encoded);
387
413
  }
388
414
  #writeBytesToWasm(data) {
@@ -390,11 +416,12 @@ class ZxBridge {
390
416
  writeBytes(ptr, data);
391
417
  return { ptr, len: data.length };
392
418
  }
419
+ #eventbridge;
393
420
  eventbridge(velementId, eventTypeId, event) {
421
+ if (!this.#eventbridge)
422
+ return;
394
423
  const eventRef = storeValueGetRef(event);
395
- const eventbridge = this.#exports.__zx_eventbridge;
396
- if (eventbridge)
397
- eventbridge(velementId, eventTypeId, eventRef);
424
+ this.#eventbridge(velementId, eventTypeId, eventRef);
398
425
  }
399
426
  static createImportObject(bridgeRef) {
400
427
  return {
@@ -421,15 +448,109 @@ class ZxBridge {
421
448
  _wsClose: (wsId, code, reasonPtr, reasonLen) => {
422
449
  bridgeRef.current?.wsClose(wsId, code, reasonPtr, reasonLen);
423
450
  },
424
- _ce: (id) => {
451
+ _ce: (id, vnodeId) => {
425
452
  const tagName = TAG_NAMES[id];
426
453
  const el = id >= SVG_TAG_START_INDEX ? document.createElementNS("http://www.w3.org/2000/svg", tagName) : document.createElement(tagName);
454
+ el.__zx_ref = Number(vnodeId);
455
+ domNodes.set(vnodeId, el);
427
456
  return storeValueGetRef(el);
457
+ },
458
+ _ct: (ptr, len, vnodeId) => {
459
+ const text = readString(ptr, len);
460
+ const node = document.createTextNode(text);
461
+ node.__zx_ref = Number(vnodeId);
462
+ domNodes.set(vnodeId, node);
463
+ return storeValueGetRef(node);
464
+ },
465
+ _sa: (vnodeId, namePtr, nameLen, valPtr, valLen) => {
466
+ domNodes.get(vnodeId)?.setAttribute(readString(namePtr, nameLen), readString(valPtr, valLen));
467
+ },
468
+ _ra: (vnodeId, namePtr, nameLen) => {
469
+ domNodes.get(vnodeId)?.removeAttribute(readString(namePtr, nameLen));
470
+ },
471
+ _snv: (vnodeId, ptr, len) => {
472
+ const node = domNodes.get(vnodeId);
473
+ if (node)
474
+ node.nodeValue = readString(ptr, len);
475
+ },
476
+ _ac: (parentId, childId) => {
477
+ const parent = domNodes.get(parentId);
478
+ const child = domNodes.get(childId);
479
+ if (parent && child)
480
+ parent.appendChild(child);
481
+ },
482
+ _ib: (parentId, childId, refId) => {
483
+ const parent = domNodes.get(parentId);
484
+ const child = domNodes.get(childId);
485
+ const ref = domNodes.get(refId) ?? null;
486
+ if (parent && child)
487
+ parent.insertBefore(child, ref);
488
+ },
489
+ _rc: (parentId, childId) => {
490
+ const parent = domNodes.get(parentId);
491
+ const child = domNodes.get(childId);
492
+ if (parent && child) {
493
+ parent.removeChild(child);
494
+ cleanupDomNodes(child);
495
+ }
496
+ },
497
+ _rpc: (parentId, newId, oldId) => {
498
+ const parent = domNodes.get(parentId);
499
+ const newChild = domNodes.get(newId);
500
+ const oldChild = domNodes.get(oldId);
501
+ if (parent && newChild && oldChild) {
502
+ parent.replaceChild(newChild, oldChild);
503
+ cleanupDomNodes(oldChild);
504
+ }
505
+ },
506
+ _getLocationHref: (bufPtr, bufLen) => {
507
+ const bytes = textEncoder.encode(window.location.href);
508
+ const len = Math.min(bytes.length, bufLen);
509
+ writeBytes(bufPtr, bytes.subarray(0, len));
510
+ return len;
511
+ },
512
+ _getFormData: (vnodeId, bufPtr, bufLen) => {
513
+ const form = domNodes.get(vnodeId);
514
+ if (!form || !(form instanceof HTMLFormElement))
515
+ return 0;
516
+ const formData = new FormData(form);
517
+ const urlEncoded = new URLSearchParams(formData).toString();
518
+ const bytes = textEncoder.encode(urlEncoded);
519
+ const len = Math.min(bytes.length, bufLen);
520
+ writeBytes(bufPtr, bytes.subarray(0, len));
521
+ return len;
522
+ },
523
+ _submitFormAction: (vnodeId) => {
524
+ const form = domNodes.get(vnodeId);
525
+ if (!form || !(form instanceof HTMLFormElement))
526
+ return;
527
+ const formData = new FormData(form);
528
+ fetch(window.location.href, {
529
+ method: "POST",
530
+ headers: { "X-ZX-Action": "1" },
531
+ body: formData
532
+ }).catch(() => {});
533
+ },
534
+ _submitFormActionAsync: (vnodeId, statesPtr, statesLen, fetchId) => {
535
+ const form = domNodes.get(vnodeId);
536
+ if (!form || !(form instanceof HTMLFormElement))
537
+ return;
538
+ const statesJson = statesLen > 0 ? readString(statesPtr, statesLen) : "[]";
539
+ bridgeRef.current?.submitFormActionAsync(form, statesJson, fetchId);
428
540
  }
429
541
  }
430
542
  };
431
543
  }
432
544
  }
545
+ var domNodes = new Map;
546
+ function cleanupDomNodes(node) {
547
+ const ref = node.__zx_ref;
548
+ if (ref !== undefined)
549
+ domNodes.delete(BigInt(ref));
550
+ const children = node.childNodes;
551
+ for (let i = 0;i < children.length; i++)
552
+ cleanupDomNodes(children[i]);
553
+ }
433
554
  var SVG_TAG_START_INDEX = 140;
434
555
  var TAG_NAMES = [
435
556
  "aside",
@@ -685,7 +806,8 @@ function initEventDelegation(bridge, rootSelector = "body") {
685
806
  const zxRef = target.__zx_ref;
686
807
  if (zxRef !== undefined) {
687
808
  bridge.eventbridge(BigInt(zxRef), EVENT_TYPE_MAP[eventType] ?? 0, event);
688
- break;
809
+ if (event.cancelBubble)
810
+ break;
689
811
  }
690
812
  target = target.parentElement;
691
813
  }