ziex 0.1.0-dev.1458 → 0.1.0-dev.1567

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/index.js CHANGED
@@ -1,9 +1,14 @@
1
+ // src/wasm/constants.ts
2
+ var CallbackType_FetchSuccess = 1;
3
+ var CallbackType_FetchError = 2;
4
+ var CallbackType_Timeout = 3;
5
+ var CallbackType_Interval = 4;
6
+
1
7
  // src/wasm/wasi.ts
2
8
  var decoder = new TextDecoder();
3
9
  var encoder = new TextEncoder();
4
10
  var ZxWasiBridge = class {
5
11
  #alloc;
6
- #fetchCompleteHandler;
7
12
  #memory;
8
13
  #cb;
9
14
  #intervals = /* @__PURE__ */ new Map();
@@ -13,7 +18,6 @@ var ZxWasiBridge = class {
13
18
  constructor(exports) {
14
19
  this.#memory = exports.memory;
15
20
  this.#alloc = exports.__zx_alloc;
16
- this.#fetchCompleteHandler = exports.__zx_fetch_complete;
17
21
  this.#cb = exports.__zx_cb;
18
22
  }
19
23
  #view() {
@@ -38,6 +42,9 @@ var ZxWasiBridge = class {
38
42
  if (start + data.length > view.byteLength) return;
39
43
  view.set(data, start);
40
44
  }
45
+ #invoke(type, id, a = 0n, b = 0n, c = 0n) {
46
+ this.#cb?.(type, id, a, b, c);
47
+ }
41
48
  log(level, ptr, len) {
42
49
  const msg = decoder.decode(this.#view().subarray(ptr, ptr + len));
43
50
  switch (level) {
@@ -90,13 +97,14 @@ var ZxWasiBridge = class {
90
97
  const encoded = typeof body === "string" ? encoder.encode(body) : body;
91
98
  const ptr = this.#alloc(encoded.length);
92
99
  this.#writeBytes(ptr, encoded);
93
- this.#fetchCompleteHandler(fetchId, status, ptr, encoded.length, isError ? 1 : 0);
100
+ const type = isError ? CallbackType_FetchError : CallbackType_FetchSuccess;
101
+ this.#invoke(type, fetchId, BigInt(status), BigInt(ptr), BigInt(encoded.length));
94
102
  }
95
103
  setTimeout(callbackId, delayMs) {
96
- setTimeout(() => this.#cb?.(3, callbackId, 0n), delayMs);
104
+ setTimeout(() => this.#invoke(CallbackType_Timeout, callbackId), delayMs);
97
105
  }
98
106
  setInterval(callbackId, intervalMs) {
99
- const handle = setInterval(() => this.#cb?.(4, callbackId, 0n), intervalMs);
107
+ const handle = setInterval(() => this.#invoke(CallbackType_Interval, callbackId), intervalMs);
100
108
  this.#intervals.set(callbackId, handle);
101
109
  }
102
110
  clearInterval(callbackId) {
@@ -150,7 +158,7 @@ var ZxWasiBridge = class {
150
158
  function createFetchImports(getMemory) {
151
159
  const encoder3 = new TextEncoder();
152
160
  const decoder3 = new TextDecoder();
153
- function readStr(ptr, len) {
161
+ function readStr2(ptr, len) {
154
162
  return decoder3.decode(new Uint8Array(getMemory().buffer, ptr, len));
155
163
  }
156
164
  function writeBytes(buf_ptr, buf_max, data) {
@@ -176,9 +184,9 @@ function createFetchImports(getMemory) {
176
184
  return headers;
177
185
  }
178
186
  async function doFetch(url_ptr, url_len, method_ptr, method_len, headers_ptr, headers_len, body_ptr, body_len, timeout_ms, status_out, buf_ptr, buf_max) {
179
- const url = readStr(url_ptr, url_len);
180
- const method = method_len > 0 ? readStr(method_ptr, method_len) : "GET";
181
- const headersJson = headers_len > 0 ? readStr(headers_ptr, headers_len) : "{}";
187
+ const url = readStr2(url_ptr, url_len);
188
+ const method = method_len > 0 ? readStr2(method_ptr, method_len) : "GET";
189
+ const headersJson = headers_len > 0 ? readStr2(headers_ptr, headers_len) : "{}";
182
190
  const body = body_len > 0 ? readBytes(body_ptr, body_len) : void 0;
183
191
  const headers = parseHeaders(headersJson);
184
192
  const controller = new AbortController();
@@ -201,14 +209,14 @@ function createFetchImports(getMemory) {
201
209
  return -1;
202
210
  }
203
211
  }
204
- const Suspending = WebAssembly.Suspending;
205
- if (typeof Suspending !== "function") {
212
+ const Suspending2 = WebAssembly.Suspending;
213
+ if (typeof Suspending2 !== "function") {
206
214
  return {
207
215
  fetch: (_url_ptr, _url_len, _method_ptr, _method_len, _headers_ptr, _headers_len, _body_ptr, _body_len, _timeout_ms, _status_out, _buf_ptr, _buf_max) => -1
208
216
  };
209
217
  }
210
218
  return {
211
- fetch: new Suspending(doFetch)
219
+ fetch: new Suspending2(doFetch)
212
220
  };
213
221
  }
214
222
 
@@ -490,90 +498,96 @@ function bindWasmAlloc(allocRef, exports) {
490
498
  }
491
499
 
492
500
  // src/runtime/kv/extern.ts
501
+ var Suspending = WebAssembly.Suspending;
502
+ var jspi = typeof Suspending === "function";
503
+ var miss = jspi ? -1 : 0;
493
504
  function putOptions(ttlSeconds) {
494
505
  if (ttlSeconds < 60) return void 0;
495
506
  return { expirationTtl: ttlSeconds };
496
507
  }
508
+ function kvDel(binding, key) {
509
+ const b = binding;
510
+ return (b.del ?? b.delete).call(b, key);
511
+ }
497
512
  function isSyncKVNamespace(binding) {
498
- const candidate = binding;
499
- return typeof candidate.getSync === "function" && typeof candidate.putSync === "function" && typeof candidate.deleteSync === "function" && typeof candidate.listSync === "function";
513
+ return typeof binding.get === "function" && binding.get.constructor.name !== "AsyncFunction";
500
514
  }
501
- function createKVImports(bindings, getMemory, allocRef) {
502
- const encoder3 = new TextEncoder();
503
- const decoder3 = new TextDecoder();
504
- function readStr(ptr, len) {
505
- return decoder3.decode(new Uint8Array(getMemory().buffer, ptr, len));
506
- }
507
- function binding(ns) {
508
- return bindings[ns] ?? bindings["default"] ?? null;
515
+ function listOpts(prefix) {
516
+ return prefix.length > 0 ? { prefix } : void 0;
517
+ }
518
+ function maybeThen(r, f) {
519
+ return r instanceof Promise ? r.then(f) : f(r);
520
+ }
521
+ function readStr(getMemory, ptr, len) {
522
+ return textDecoder.decode(new Uint8Array(getMemory().buffer, ptr, len));
523
+ }
524
+ function resolve(bindings, ns) {
525
+ const b = bindings[ns] ?? bindings["default"] ?? null;
526
+ if (!b || jspi) return b;
527
+ return isSyncKVNamespace(b) ? b : null;
528
+ }
529
+ function encodeGet(getMemory, allocRef, value, out_ptr) {
530
+ if (value === null) return -1;
531
+ return writeBytesOut(getMemory, allocRef, out_ptr, textEncoder.encode(value));
532
+ }
533
+ function encodeList(getMemory, allocRef, names, out_ptr) {
534
+ if (names.length === 0) {
535
+ new DataView(getMemory().buffer).setUint32(out_ptr, 0, true);
536
+ return 0;
509
537
  }
510
- const Suspending = WebAssembly.Suspending;
511
- if (typeof Suspending !== "function") {
512
- let syncBinding2 = function(ns) {
513
- const candidate = binding(ns);
514
- return candidate && isSyncKVNamespace(candidate) ? candidate : null;
515
- };
516
- var syncBinding = syncBinding2;
517
- return {
518
- kv_get: (ns_ptr, ns_len, key_ptr, key_len, out_ptr) => {
519
- const b = syncBinding2(readStr(ns_ptr, ns_len));
520
- if (!b) return -1;
521
- const value = b.getSync(readStr(key_ptr, key_len));
522
- if (value === null) return -1;
523
- return writeBytesOut(getMemory, allocRef, out_ptr, encoder3.encode(value));
524
- },
525
- kv_put: (ns_ptr, ns_len, key_ptr, key_len, val_ptr, val_len, ttl_seconds) => {
526
- const b = syncBinding2(readStr(ns_ptr, ns_len));
527
- if (!b) return 0;
528
- b.putSync(readStr(key_ptr, key_len), readStr(val_ptr, val_len), putOptions(ttl_seconds));
529
- return 0;
530
- },
531
- kv_delete: (ns_ptr, ns_len, key_ptr, key_len) => {
532
- const b = syncBinding2(readStr(ns_ptr, ns_len));
533
- if (!b) return 0;
534
- b.deleteSync(readStr(key_ptr, key_len));
535
- return 0;
536
- },
537
- kv_list: (ns_ptr, ns_len, pfx_ptr, pfx_len, out_ptr) => {
538
- const b = syncBinding2(readStr(ns_ptr, ns_len));
539
- if (!b) return writeJsonOut(getMemory, allocRef, out_ptr, []);
540
- const prefix = readStr(pfx_ptr, pfx_len);
541
- const result = b.listSync(prefix.length > 0 ? { prefix } : void 0);
542
- return writeJsonOut(getMemory, allocRef, out_ptr, result.keys.map((k) => k.name));
543
- }
544
- };
538
+ return writeJsonOut(getMemory, allocRef, out_ptr, names);
539
+ }
540
+ function get(b, key, out_ptr, getMemory, allocRef) {
541
+ return maybeThen(b.get(key), (v) => encodeGet(getMemory, allocRef, v, out_ptr));
542
+ }
543
+ function put(b, key, val, ttl) {
544
+ return maybeThen(b.put(key, val, putOptions(ttl)), () => 0);
545
+ }
546
+ function del(b, key) {
547
+ return maybeThen(kvDel(b, key), () => 0);
548
+ }
549
+ function list(b, prefix, out_ptr, getMemory, allocRef) {
550
+ return maybeThen(
551
+ b.list(listOpts(prefix)),
552
+ (result) => encodeList(getMemory, allocRef, result.keys.map((k) => k.name), out_ptr)
553
+ );
554
+ }
555
+ function wrapImport(impl) {
556
+ if (!jspi) {
557
+ return ((...args) => {
558
+ const r = impl(...args);
559
+ if (r instanceof Promise) throw new Error("async kv binding requires JSPI");
560
+ return r;
561
+ });
545
562
  }
563
+ return new Suspending(impl);
564
+ }
565
+ function createKVImports(bindings, getMemory, allocRef) {
546
566
  return {
547
- kv_get: new Suspending(async (ns_ptr, ns_len, key_ptr, key_len, out_ptr) => {
548
- const b = binding(readStr(ns_ptr, ns_len));
567
+ kv_get: wrapImport((ns_ptr, ns_len, key_ptr, key_len, out_ptr) => {
568
+ const b = resolve(bindings, readStr(getMemory, ns_ptr, ns_len));
549
569
  if (!b) return -1;
550
- const value = await b.get(readStr(key_ptr, key_len));
551
- if (value === null) return -1;
552
- return writeBytesOut(getMemory, allocRef, out_ptr, encoder3.encode(value));
570
+ return get(b, readStr(getMemory, key_ptr, key_len), out_ptr, getMemory, allocRef);
553
571
  }),
554
- kv_put: new Suspending(async (ns_ptr, ns_len, key_ptr, key_len, val_ptr, val_len, ttl_seconds) => {
555
- const b = binding(readStr(ns_ptr, ns_len));
556
- if (!b) return -1;
557
- await b.put(readStr(key_ptr, key_len), readStr(val_ptr, val_len), putOptions(ttl_seconds));
558
- return 0;
572
+ kv_put: wrapImport((ns_ptr, ns_len, key_ptr, key_len, val_ptr, val_len, ttl_seconds) => {
573
+ const b = resolve(bindings, readStr(getMemory, ns_ptr, ns_len));
574
+ if (!b) return miss;
575
+ return put(
576
+ b,
577
+ readStr(getMemory, key_ptr, key_len),
578
+ readStr(getMemory, val_ptr, val_len),
579
+ ttl_seconds
580
+ );
559
581
  }),
560
- kv_delete: new Suspending(async (ns_ptr, ns_len, key_ptr, key_len) => {
561
- const b = binding(readStr(ns_ptr, ns_len));
562
- if (!b) return -1;
563
- await b.delete(readStr(key_ptr, key_len));
564
- return 0;
582
+ kv_delete: wrapImport((ns_ptr, ns_len, key_ptr, key_len) => {
583
+ const b = resolve(bindings, readStr(getMemory, ns_ptr, ns_len));
584
+ if (!b) return miss;
585
+ return del(b, readStr(getMemory, key_ptr, key_len));
565
586
  }),
566
- kv_list: new Suspending(async (ns_ptr, ns_len, prefix_ptr, prefix_len, out_ptr) => {
567
- const b = binding(readStr(ns_ptr, ns_len));
587
+ kv_list: wrapImport((ns_ptr, ns_len, pfx_ptr, pfx_len, out_ptr) => {
588
+ const b = resolve(bindings, readStr(getMemory, ns_ptr, ns_len));
568
589
  if (!b) return writeJsonOut(getMemory, allocRef, out_ptr, []);
569
- const prefix = readStr(prefix_ptr, prefix_len);
570
- const result = await b.list(prefix.length > 0 ? { prefix } : void 0);
571
- const names = result.keys.map((k) => k.name);
572
- if (names.length === 0) {
573
- new DataView(getMemory().buffer).setUint32(out_ptr, 0, true);
574
- return 0;
575
- }
576
- return writeJsonOut(getMemory, allocRef, out_ptr, names);
590
+ return list(b, readStr(getMemory, pfx_ptr, pfx_len), out_ptr, getMemory, allocRef);
577
591
  })
578
592
  };
579
593
  }
@@ -645,21 +659,21 @@ function valuesToWireRows(rows) {
645
659
  }
646
660
  function createDbImports(bindings, getMemory, allocRef) {
647
661
  const decoder3 = new TextDecoder();
648
- function readStr(ptr, len) {
662
+ function readStr2(ptr, len) {
649
663
  return decoder3.decode(new Uint8Array(getMemory().buffer, ptr, len));
650
664
  }
651
665
  function binding(ns) {
652
666
  return bindings[ns] ?? bindings["default"] ?? null;
653
667
  }
654
668
  async function statement(ns_ptr, ns_len, sql_ptr, sql_len, bindings_ptr, bindings_len) {
655
- const database = binding(readStr(ns_ptr, ns_len));
669
+ const database = binding(readStr2(ns_ptr, ns_len));
656
670
  if (!database) return null;
657
- const sql = readStr(sql_ptr, sql_len);
658
- const bindingsJson = JSON.parse(readStr(bindings_ptr, bindings_len));
671
+ const sql = readStr2(sql_ptr, sql_len);
672
+ const bindingsJson = JSON.parse(readStr2(bindings_ptr, bindings_len));
659
673
  return database.prepare(sql).bind(...toPositionalBindings(bindingsJson));
660
674
  }
661
- const Suspending = WebAssembly.Suspending;
662
- if (typeof Suspending !== "function") {
675
+ const Suspending2 = WebAssembly.Suspending;
676
+ if (typeof Suspending2 !== "function") {
663
677
  return {
664
678
  db_open: (_ns, _ns_len) => -1,
665
679
  db_run: (_a, _b, _c, _d, _e, _f, _g) => -1,
@@ -669,8 +683,8 @@ function createDbImports(bindings, getMemory, allocRef) {
669
683
  };
670
684
  }
671
685
  return {
672
- db_open: (ns_ptr, ns_len) => binding(readStr(ns_ptr, ns_len)) ? 0 : -1,
673
- db_run: new Suspending(async (ns_ptr, ns_len, sql_ptr, sql_len, bindings_ptr, bindings_len, out_ptr) => {
686
+ db_open: (ns_ptr, ns_len) => binding(readStr2(ns_ptr, ns_len)) ? 0 : -1,
687
+ db_run: new Suspending2(async (ns_ptr, ns_len, sql_ptr, sql_len, bindings_ptr, bindings_len, out_ptr) => {
674
688
  const stmt = await statement(ns_ptr, ns_len, sql_ptr, sql_len, bindings_ptr, bindings_len);
675
689
  if (!stmt) return -1;
676
690
  const result = await stmt.run();
@@ -679,7 +693,7 @@ function createDbImports(bindings, getMemory, allocRef) {
679
693
  changes: result.meta?.changes ?? 0
680
694
  });
681
695
  }),
682
- db_get: new Suspending(async (ns_ptr, ns_len, sql_ptr, sql_len, bindings_ptr, bindings_len, out_ptr) => {
696
+ db_get: new Suspending2(async (ns_ptr, ns_len, sql_ptr, sql_len, bindings_ptr, bindings_len, out_ptr) => {
683
697
  const stmt = await statement(ns_ptr, ns_len, sql_ptr, sql_len, bindings_ptr, bindings_len);
684
698
  if (!stmt) return -1;
685
699
  const row = await stmt.first();
@@ -689,7 +703,7 @@ function createDbImports(bindings, getMemory, allocRef) {
689
703
  }
690
704
  return writeJsonOut(getMemory, allocRef, out_ptr, [objectToWireRow(row)]);
691
705
  }),
692
- db_all: new Suspending(async (ns_ptr, ns_len, sql_ptr, sql_len, bindings_ptr, bindings_len, out_ptr) => {
706
+ db_all: new Suspending2(async (ns_ptr, ns_len, sql_ptr, sql_len, bindings_ptr, bindings_len, out_ptr) => {
693
707
  const stmt = await statement(ns_ptr, ns_len, sql_ptr, sql_len, bindings_ptr, bindings_len);
694
708
  if (!stmt) return -1;
695
709
  const result = await stmt.all();
@@ -700,7 +714,7 @@ function createDbImports(bindings, getMemory, allocRef) {
700
714
  }
701
715
  return writeJsonOut(getMemory, allocRef, out_ptr, rows);
702
716
  }),
703
- db_values: new Suspending(async (ns_ptr, ns_len, sql_ptr, sql_len, bindings_ptr, bindings_len, out_ptr) => {
717
+ db_values: new Suspending2(async (ns_ptr, ns_len, sql_ptr, sql_len, bindings_ptr, bindings_len, out_ptr) => {
704
718
  const stmt = await statement(ns_ptr, ns_len, sql_ptr, sql_len, bindings_ptr, bindings_len);
705
719
  if (!stmt) return -1;
706
720
  const rows = await stmt.raw();
@@ -722,8 +736,7 @@ var ProcExit = class extends Error {
722
736
  };
723
737
  function createWasiImports({
724
738
  request,
725
- stdinData,
726
- onStdout
739
+ stdinData
727
740
  }) {
728
741
  const encoder3 = new TextEncoder();
729
742
  const url = new URL(request.url);
@@ -747,8 +760,6 @@ function createWasiImports({
747
760
  const setMemory = (m2) => {
748
761
  wasmMemory = m2;
749
762
  };
750
- const stdoutChunks = [];
751
- let stderrMeta = "";
752
763
  let stderrPartial = "";
753
764
  const stderrDecoder = new TextDecoder("utf-8", { fatal: false, ignoreBOM: true });
754
765
  function processStderrChunk(chunk) {
@@ -756,9 +767,7 @@ function createWasiImports({
756
767
  const lines = (stderrPartial + text).split("\n");
757
768
  stderrPartial = lines.pop() ?? "";
758
769
  for (const line of lines) {
759
- if (line.startsWith("__ZIEX_META__:")) {
760
- stderrMeta += line + "\n";
761
- } else if (line.length > 0) {
770
+ if (line.length > 0) {
762
771
  console.error("[ziex]", line);
763
772
  }
764
773
  }
@@ -804,10 +813,7 @@ function createWasiImports({
804
813
  const buf_ptr = dv.getUint32(iovs_ptr + i * 8, true);
805
814
  const buf_len = dv.getUint32(iovs_ptr + i * 8 + 4, true);
806
815
  const chunk = mem.slice(buf_ptr, buf_ptr + buf_len);
807
- if (fd === 1) {
808
- if (onStdout) onStdout(chunk);
809
- else stdoutChunks.push(chunk);
810
- } else if (fd === 2) processStderrChunk(chunk);
816
+ if (fd === 2) processStderrChunk(chunk);
811
817
  written += buf_len;
812
818
  }
813
819
  dv.setUint32(nwritten_ptr, written, true);
@@ -975,26 +981,12 @@ function createWasiImports({
975
981
  v().setUint32(bufused_ptr, 0, true);
976
982
  return 76;
977
983
  },
978
- // Stub for poll_oneoff - no blocking I/O in edge runtimes.
979
984
  poll_oneoff(_in, _out, _nsubscriptions, nevents_ptr) {
980
985
  v().setUint32(nevents_ptr, 0, true);
981
986
  return 0;
982
987
  }
983
988
  };
984
- function collectOutput() {
985
- const remaining = stderrDecoder.decode(void 0, { stream: false });
986
- const tail = stderrPartial + remaining;
987
- if (tail.length > 0) {
988
- if (tail.startsWith("__ZIEX_META__:")) stderrMeta += tail;
989
- else console.error("[ziex]", tail);
990
- stderrPartial = "";
991
- }
992
- return {
993
- stdout: mergeUint8Arrays(stdoutChunks),
994
- stderrText: stderrMeta
995
- };
996
- }
997
- return { wasiImport, setMemory, collectOutput };
989
+ return { wasiImport, setMemory };
998
990
  }
999
991
  function mergeUint8Arrays(arrays) {
1000
992
  const totalLen = arrays.reduce((sum, arr) => sum + arr.length, 0);
@@ -1008,50 +1000,78 @@ function mergeUint8Arrays(arrays) {
1008
1000
  }
1009
1001
 
1010
1002
  // src/runtime/kv/memory.ts
1011
- function createMemoryKV() {
1012
- const store = /* @__PURE__ */ new Map();
1013
- function read(key) {
1014
- const entry = store.get(key);
1015
- if (!entry) return null;
1016
- const expiresAt = entry[1];
1017
- if (expiresAt !== void 0 && Date.now() >= expiresAt) {
1018
- store.delete(key);
1019
- return null;
1020
- }
1021
- return entry[0];
1022
- }
1023
- function write(key, value, options) {
1024
- let expiresAt;
1025
- if (options?.expiration !== void 0) {
1026
- expiresAt = options.expiration * 1e3;
1027
- } else if (options?.expirationTtl !== void 0) {
1028
- expiresAt = Date.now() + options.expirationTtl * 1e3;
1029
- }
1030
- store.set(key, [value, expiresAt]);
1003
+ var store = /* @__PURE__ */ new Map();
1004
+ async function get2(key) {
1005
+ const entry = store.get(key);
1006
+ if (!entry) return null;
1007
+ const expiresAt = entry[1];
1008
+ if (expiresAt !== void 0 && Date.now() >= expiresAt) {
1009
+ store.delete(key);
1010
+ return null;
1011
+ }
1012
+ return entry[0];
1013
+ }
1014
+ async function put2(key, value, options) {
1015
+ let expiresAt;
1016
+ if (options?.expiration !== void 0) {
1017
+ expiresAt = options.expiration * 1e3;
1018
+ } else if (options?.expirationTtl !== void 0) {
1019
+ expiresAt = Date.now() + options.expirationTtl * 1e3;
1020
+ }
1021
+ store.set(key, [value, expiresAt]);
1022
+ }
1023
+ async function del2(key) {
1024
+ store.delete(key);
1025
+ }
1026
+ async function list2(options) {
1027
+ const keys = [];
1028
+ for (const name of [...store.keys()]) {
1029
+ if (options?.prefix && !name.startsWith(options.prefix)) continue;
1030
+ if (await get2(name) !== null) keys.push({ name });
1031
1031
  }
1032
+ return { keys };
1033
+ }
1034
+
1035
+ // src/runtime/index.ts
1036
+ function buildHttpImports(mem, http) {
1037
+ const decoder3 = new TextDecoder();
1032
1038
  return {
1033
- async get(key) {
1034
- return read(key);
1035
- },
1036
- async put(key, value, options) {
1037
- write(key, value, options);
1039
+ commit: (status, meta_ptr, meta_len) => {
1040
+ http.status = status >>> 0;
1041
+ http.committed = true;
1042
+ http.headers = new Headers();
1043
+ http.streaming = false;
1044
+ if (meta_len > 0) {
1045
+ try {
1046
+ const raw = decoder3.decode(new Uint8Array(mem().buffer, meta_ptr >>> 0, meta_len >>> 0));
1047
+ const parsed = JSON.parse(raw);
1048
+ if (parsed.streaming === true) http.streaming = true;
1049
+ if (Array.isArray(parsed.headers)) {
1050
+ for (const [name, value] of parsed.headers) {
1051
+ http.headers.append(name, value);
1052
+ }
1053
+ }
1054
+ } catch {
1055
+ }
1056
+ }
1038
1057
  },
1039
- async delete(key) {
1040
- store.delete(key);
1058
+ write: (ptr, len) => {
1059
+ if (len <= 0) return;
1060
+ const chunk = new Uint8Array(mem().buffer, ptr >>> 0, len >>> 0).slice();
1061
+ if (http.streamWriter) void http.streamWriter.write(chunk);
1062
+ else http.bodyChunks.push(chunk);
1041
1063
  },
1042
- async list(options) {
1043
- const keys = [...store.keys()].filter((k) => {
1044
- if (options?.prefix && !k.startsWith(options.prefix)) return false;
1045
- return read(k) !== null;
1046
- }).map((name) => ({ name }));
1047
- return { keys };
1064
+ end: () => {
1065
+ http.ended = true;
1066
+ if (http.streamWriter) {
1067
+ void http.streamWriter.close();
1068
+ http.streamWriter = null;
1069
+ }
1048
1070
  }
1049
1071
  };
1050
1072
  }
1051
-
1052
- // src/runtime/index.ts
1053
- function buildWsImports(Suspending, mem, decoder3, ws) {
1054
- const readStr = (ptr, len) => decoder3.decode(new Uint8Array(mem().buffer, ptr, len));
1073
+ function buildWsImports(Suspending2, mem, decoder3, ws) {
1074
+ const readStr2 = (ptr, len) => decoder3.decode(new Uint8Array(mem().buffer, ptr, len));
1055
1075
  return {
1056
1076
  ws_upgrade: () => {
1057
1077
  ws.upgraded = true;
@@ -1067,7 +1087,7 @@ function buildWsImports(Suspending, mem, decoder3, ws) {
1067
1087
  ws_close: (code, reason_ptr, reason_len) => {
1068
1088
  ws.server?.close(code, decoder3.decode(new Uint8Array(mem().buffer, reason_ptr, reason_len)));
1069
1089
  },
1070
- ws_recv: Suspending ? new Suspending(async (buf_ptr, buf_max) => {
1090
+ ws_recv: Suspending2 ? new Suspending2(async (buf_ptr, buf_max) => {
1071
1091
  if (ws._resolveFirstSuspend) {
1072
1092
  const fn = ws._resolveFirstSuspend;
1073
1093
  ws._resolveFirstSuspend = void 0;
@@ -1080,23 +1100,23 @@ function buildWsImports(Suspending, mem, decoder3, ws) {
1080
1100
  return n;
1081
1101
  };
1082
1102
  if (ws.messageQueue.length > 0) return deliver(ws.messageQueue.shift());
1083
- return new Promise((resolve) => {
1084
- ws.recvResolve = (bytes) => resolve(deliver(bytes));
1103
+ return new Promise((resolve2) => {
1104
+ ws.recvResolve = (bytes) => resolve2(deliver(bytes));
1085
1105
  });
1086
1106
  }) : (_buf_ptr, _buf_max) => -1,
1087
1107
  // Pub/sub - delegates to optional callbacks (real in DO, no-ops otherwise)
1088
1108
  ws_subscribe: (ptr, len) => {
1089
- ws.subscribe?.(readStr(ptr, len));
1109
+ ws.subscribe?.(readStr2(ptr, len));
1090
1110
  },
1091
1111
  ws_unsubscribe: (ptr, len) => {
1092
- ws.unsubscribe?.(readStr(ptr, len));
1112
+ ws.unsubscribe?.(readStr2(ptr, len));
1093
1113
  },
1094
1114
  ws_publish: (topic_ptr, topic_len, data_ptr, data_len) => {
1095
- const topic = readStr(topic_ptr, topic_len);
1115
+ const topic = readStr2(topic_ptr, topic_len);
1096
1116
  const data = new Uint8Array(mem().buffer, data_ptr, data_len).slice();
1097
1117
  return ws.publish?.(topic, data) ?? 0;
1098
1118
  },
1099
- ws_is_subscribed: (ptr, len) => ws.isSubscribed?.(readStr(ptr, len)) ? 1 : 0
1119
+ ws_is_subscribed: (ptr, len) => ws.isSubscribed?.(readStr2(ptr, len)) ? 1 : 0
1100
1120
  };
1101
1121
  }
1102
1122
  function attachWebSocket(ws) {
@@ -1127,14 +1147,14 @@ function attachWebSocket(ws) {
1127
1147
  });
1128
1148
  return { client };
1129
1149
  }
1130
- function buildSysImports(jspi, Suspending) {
1150
+ function buildSysImports(jspi2, Suspending2) {
1131
1151
  return {
1132
- sleep_ms: jspi ? new Suspending(async (ms) => new Promise((r) => setTimeout(r, ms))) : (_ms) => {
1152
+ sleep_ms: jspi2 ? new Suspending2(async (ms) => new Promise((r) => setTimeout(r, ms))) : (_ms) => {
1133
1153
  }
1134
1154
  };
1135
1155
  }
1136
- function executeWasm(instance, jspi, Suspending, wsState) {
1137
- if (!jspi) {
1156
+ function executeWasm(instance, jspi2, Suspending2, wsState) {
1157
+ if (!jspi2) {
1138
1158
  try {
1139
1159
  instance.exports._start();
1140
1160
  } catch (e) {
@@ -1154,25 +1174,6 @@ function executeWasm(instance, jspi, Suspending, wsState) {
1154
1174
  }
1155
1175
  });
1156
1176
  }
1157
- function parseZiexMeta(stderrText) {
1158
- const meta = { status: 200, headers: new Headers(), streaming: false };
1159
- const metaPrefix = "__ZIEX_META__:";
1160
- const metaLine = stderrText.split("\n").find((line) => line.startsWith(metaPrefix));
1161
- if (metaLine) {
1162
- try {
1163
- const parsed = JSON.parse(metaLine.slice(metaPrefix.length));
1164
- if (parsed.status) meta.status = parsed.status;
1165
- if (parsed.streaming === true) meta.streaming = true;
1166
- if (Array.isArray(parsed.headers)) {
1167
- for (const [name, value] of parsed.headers) {
1168
- meta.headers.append(name, value);
1169
- }
1170
- }
1171
- } catch {
1172
- }
1173
- }
1174
- return meta;
1175
- }
1176
1177
  async function run({
1177
1178
  request,
1178
1179
  env,
@@ -1182,29 +1183,32 @@ async function run({
1182
1183
  db: dbBindings,
1183
1184
  imports,
1184
1185
  wasi,
1185
- websocket: doNamespace
1186
+ sticky
1186
1187
  }) {
1187
- if (doNamespace && request.headers.get("upgrade")?.toLowerCase() === "websocket") {
1188
- const id = doNamespace.idFromName(new URL(request.url).pathname);
1189
- return doNamespace.get(id).fetch(request);
1188
+ if (sticky && request.headers.get("upgrade")?.toLowerCase() === "websocket") {
1189
+ const id = sticky.idFromName(new URL(request.url).pathname);
1190
+ return sticky.get(id).fetch(request);
1190
1191
  }
1191
1192
  const stdinData = request.body ? new Uint8Array(await request.arrayBuffer()) : void 0;
1192
- const stdoutChunks = [];
1193
- let streamWriter = null;
1194
- const { wasiImport, setMemory, collectOutput } = createWasiImports({
1193
+ const httpState = {
1194
+ committed: false,
1195
+ ended: false,
1196
+ status: 200,
1197
+ streaming: false,
1198
+ headers: new Headers(),
1199
+ bodyChunks: [],
1200
+ streamWriter: null
1201
+ };
1202
+ const { wasiImport, setMemory } = createWasiImports({
1195
1203
  request,
1196
- stdinData,
1197
- onStdout: (chunk) => {
1198
- if (streamWriter) void streamWriter.write(chunk);
1199
- else stdoutChunks.push(chunk);
1200
- }
1204
+ stdinData
1201
1205
  });
1202
1206
  let wasmMemory = null;
1203
1207
  const mem = () => wasmMemory;
1204
1208
  const bridgeRef = [null];
1205
1209
  const allocRef = [null];
1206
- const Suspending = WebAssembly.Suspending;
1207
- const jspi = typeof Suspending === "function";
1210
+ const Suspending2 = WebAssembly.Suspending;
1211
+ const jspi2 = typeof Suspending2 === "function";
1208
1212
  const wsState = {
1209
1213
  upgraded: false,
1210
1214
  server: null,
@@ -1214,15 +1218,16 @@ async function run({
1214
1218
  };
1215
1219
  const importObject = {
1216
1220
  wasi_snapshot_preview1: { ...wasi?.wasiImport, ...wasiImport },
1217
- __zx_sys: buildSysImports(jspi, Suspending),
1218
- __zx_ws: buildWsImports(jspi ? Suspending : null, mem, new TextDecoder(), wsState),
1221
+ __zx_sys: buildSysImports(jspi2, Suspending2),
1222
+ __zx_ws: buildWsImports(jspi2 ? Suspending2 : null, mem, new TextDecoder(), wsState),
1223
+ __zx_http: buildHttpImports(mem, httpState),
1219
1224
  __zx_net: createFetchImports(mem),
1220
1225
  ...imports ? imports(mem) : {},
1221
1226
  ...ZxWasiBridge.createImportObject(bridgeRef)
1222
1227
  };
1223
1228
  if (true) {
1224
1229
  importObject.__zx_kv = createKVImports(
1225
- kvBindings ?? { default: createMemoryKV() },
1230
+ kvBindings ?? { default: { get: get2, put: put2, del: del2, list: list2 } },
1226
1231
  mem,
1227
1232
  allocRef
1228
1233
  );
@@ -1239,34 +1244,37 @@ async function run({
1239
1244
  setMemory(wasmMemory);
1240
1245
  bridgeRef[0] = new ZxWasiBridge(instance.exports);
1241
1246
  bindWasmAlloc(allocRef, instance.exports);
1242
- const wasmPromise = executeWasm(instance, jspi, Suspending, wsState);
1247
+ const wasmPromise = executeWasm(instance, jspi2, Suspending2, wsState);
1243
1248
  if (wsState.upgraded) {
1244
1249
  const server = attachWebSocket(wsState);
1245
1250
  ctx?.waitUntil(wasmPromise);
1246
1251
  return new Response(null, { status: 101, webSocket: server.client });
1247
1252
  }
1248
- const { stderrText: earlyStderrText } = collectOutput();
1249
- const earlyMeta = parseZiexMeta(earlyStderrText);
1250
- if (earlyMeta.streaming) {
1253
+ if (httpState.committed && httpState.streaming) {
1251
1254
  const { readable, writable } = new TransformStream();
1252
- streamWriter = writable.getWriter();
1253
- for (const chunk of stdoutChunks) void streamWriter.write(chunk);
1254
- stdoutChunks.length = 0;
1255
- void wasmPromise.finally(() => streamWriter?.close());
1256
- return new Response(readable, { status: earlyMeta.status, headers: earlyMeta.headers });
1255
+ httpState.streamWriter = writable.getWriter();
1256
+ for (const chunk of httpState.bodyChunks) void httpState.streamWriter.write(chunk);
1257
+ httpState.bodyChunks.length = 0;
1258
+ void wasmPromise.finally(() => {
1259
+ if (httpState.streamWriter) {
1260
+ void httpState.streamWriter.close();
1261
+ httpState.streamWriter = null;
1262
+ }
1263
+ });
1264
+ return new Response(readable, { status: httpState.status, headers: httpState.headers });
1257
1265
  }
1258
1266
  await wasmPromise;
1259
- const { stderrText } = collectOutput();
1260
- const meta = parseZiexMeta(stderrText);
1261
- const body = mergeUint8Arrays(stdoutChunks);
1262
- meta.headers.delete("transfer-encoding");
1263
- const nullBody = meta.status === 101 || meta.status === 204 || meta.status === 205 || meta.status === 304;
1267
+ const status = httpState.status;
1268
+ const headers = httpState.headers;
1269
+ headers.delete("transfer-encoding");
1270
+ const nullBody = status === 101 || status === 204 || status === 205 || status === 304;
1264
1271
  if (nullBody) {
1265
- meta.headers.delete("content-length");
1266
- return new Response(null, { status: meta.status, headers: meta.headers });
1272
+ headers.delete("content-length");
1273
+ return new Response(null, { status, headers });
1267
1274
  }
1268
- if (!meta.headers.has("content-length")) meta.headers.set("content-length", String(body.byteLength));
1269
- return new Response(body.buffer, { status: meta.status, headers: meta.headers });
1275
+ const body = mergeUint8Arrays(httpState.bodyChunks);
1276
+ if (!headers.has("content-length")) headers.set("content-length", String(body.byteLength));
1277
+ return new Response(body.buffer, { status, headers });
1270
1278
  }
1271
1279
 
1272
1280
  // src/app.ts
@@ -1303,7 +1311,7 @@ var Ziex = class {
1303
1311
  */
1304
1312
  this.fetch = async (request, env, ctx) => {
1305
1313
  const module = await this.getModule();
1306
- const { wasi, imports, websocket } = this.options;
1314
+ const { wasi, imports, sticky } = this.options;
1307
1315
  return run({
1308
1316
  request,
1309
1317
  env,
@@ -1313,7 +1321,7 @@ var Ziex = class {
1313
1321
  imports,
1314
1322
  kv: this.resolveKV(env),
1315
1323
  db: this.resolveDB(env),
1316
- websocket: websocket !== void 0 ? env[websocket] : void 0
1324
+ sticky: sticky !== void 0 ? env[sticky] : void 0
1317
1325
  });
1318
1326
  };
1319
1327
  this.options = options;
@@ -1322,6 +1330,27 @@ var Ziex = class {
1322
1330
  if (!this.resolved) this.resolved = await resolveModule(this.options.module);
1323
1331
  return this.resolved;
1324
1332
  }
1333
+ /**
1334
+ * Enable sticky upgrade forwarding and return host config for platform adapters
1335
+ * (e.g. Cloudflare {@linkcode stateful}).
1336
+ *
1337
+ * Requires `options.module` to already be a compiled `WebAssembly.Module`.
1338
+ */
1339
+ stickyHost(binding) {
1340
+ this.options.sticky = binding;
1341
+ const mod = this.options.module;
1342
+ if (mod instanceof ArrayBuffer || ArrayBuffer.isView(mod) || mod instanceof Response || mod instanceof URL || typeof mod === "string") {
1343
+ throw new Error(
1344
+ 'sticky handlers require a compiled WebAssembly.Module (e.g. `import module from "./app.wasm"` under wrangler)'
1345
+ );
1346
+ }
1347
+ return {
1348
+ module: mod,
1349
+ imports: this.options.imports,
1350
+ kv: (env) => this.resolveKV(env),
1351
+ db: (env) => this.resolveDB(env)
1352
+ };
1353
+ }
1325
1354
  resolveKV(env) {
1326
1355
  const { kv } = this.options;
1327
1356
  if (kv === void 0) return void 0;