ziex 0.1.0-dev.1259 → 0.1.0-dev.1360
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 +20 -24
- package/cloudflare/index.js +101 -15
- package/index.js +78 -4
- package/package.json +2 -2
- package/pkg/ziex/src/fetch.d.ts +5 -0
- package/{wasm → pkg/ziex/src/wasm}/core.d.ts +2 -2
- package/{wasm → pkg/ziex/src/wasm}/index.d.ts +2 -1
- package/vendor/jsz/js/src/index.d.ts +1 -0
- package/vendor/jsz/js/src/zigjs.d.ts +107 -0
- package/wasm/index.js +92 -11
- package/wasm/init.dev.js +1310 -0
- package/wasm/init.js +3 -1229
- /package/{app.d.ts → pkg/ziex/src/app.d.ts} +0 -0
- /package/{aws-lambda → pkg/ziex/src/aws-lambda}/index.d.ts +0 -0
- /package/{browser → pkg/ziex/src/browser}/kv.d.ts +0 -0
- /package/{cloudflare → pkg/ziex/src/cloudflare}/app.d.ts +0 -0
- /package/{cloudflare → pkg/ziex/src/cloudflare}/db.d.ts +0 -0
- /package/{cloudflare → pkg/ziex/src/cloudflare}/do.d.ts +0 -0
- /package/{cloudflare → pkg/ziex/src/cloudflare}/index.d.ts +0 -0
- /package/{cloudflare → pkg/ziex/src/cloudflare}/kv.d.ts +0 -0
- /package/{cloudflare → pkg/ziex/src/cloudflare}/worker.d.ts +0 -0
- /package/{db.d.ts → pkg/ziex/src/db.d.ts} +0 -0
- /package/{index.d.ts → pkg/ziex/src/index.d.ts} +0 -0
- /package/{kv.d.ts → pkg/ziex/src/kv.d.ts} +0 -0
- /package/{react → pkg/ziex/src/react}/dom.d.ts +0 -0
- /package/{react → pkg/ziex/src/react}/index.d.ts +0 -0
- /package/{react → pkg/ziex/src/react}/types.d.ts +0 -0
- /package/{runtime.d.ts → pkg/ziex/src/runtime.d.ts} +0 -0
- /package/{vercel → pkg/ziex/src/vercel}/index.d.ts +0 -0
- /package/{wasi.d.ts → pkg/ziex/src/wasi.d.ts} +0 -0
- /package/{wasm → pkg/ziex/src/wasm}/dom.d.ts +0 -0
- /package/{wasm → pkg/ziex/src/wasm}/init.d.ts +0 -0
- /package/{wasm → pkg/ziex/src/wasm}/types.d.ts +0 -0
- /package/{wasm → pkg/ziex/src/wasm}/wasi.d.ts +0 -0
- /package/{zx.d.ts → pkg/ziex/src/zx.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -8,37 +8,29 @@ Ziex combines the power and performance of Zig with the expressiveness and simpl
|
|
|
8
8
|
|
|
9
9
|
**[Documentation →](https://ziex.dev/learn)**
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
> **Note:** Most of the API and syntax are finalized and stable, and server-side rendering (SSR) features are production-ready, Ziex continues to evolve with ongoing improvements to client-side rendering and state management, see the [roadmap](#roadmap). You can start using the documented features today, as they are stable and unlikely to change. Areas still under development are not yet documented and will be added as they mature. See [versions](#versions) for Zig and Ziex versions compatibility.
|
|
13
12
|
|
|
14
|
-
|
|
15
13
|
## Getting Started
|
|
16
14
|
|
|
17
15
|
### 1. Installing CLI (optional)
|
|
18
16
|
|
|
19
|
-
macOS/Linux
|
|
20
17
|
```bash
|
|
18
|
+
# macOS/Linux
|
|
21
19
|
curl -fsSL https://ziex.dev/install | bash
|
|
22
|
-
|
|
23
|
-
Windows
|
|
24
|
-
```
|
|
20
|
+
|
|
21
|
+
# Windows
|
|
25
22
|
powershell -c "irm ziex.dev/install.ps1 | iex"
|
|
26
23
|
```
|
|
27
24
|
|
|
25
|
+
|
|
28
26
|
### 2. Initializing Project
|
|
27
|
+
|
|
29
28
|
```powershell
|
|
30
29
|
zx init
|
|
31
30
|
# or
|
|
32
31
|
npm init ziex
|
|
33
32
|
```
|
|
34
33
|
|
|
35
|
-
### 3. Running Project
|
|
36
|
-
```powershell
|
|
37
|
-
zx dev
|
|
38
|
-
# or
|
|
39
|
-
npm run dev
|
|
40
|
-
```
|
|
41
|
-
|
|
42
34
|
You will need [compatible](#versions) Zig version when using zx CLI or you can use the `Node` template from `npm init ziex`
|
|
43
35
|
|
|
44
36
|
Read [Getting Started →](https://ziex.dev/learn) for more details.
|
|
@@ -159,6 +151,7 @@ const zx = @import("zx");
|
|
|
159
151
|
</details>
|
|
160
152
|
|
|
161
153
|
## Features
|
|
154
|
+
|
|
162
155
|
- **Declarative UI**: Declarative UI components using with full access to Zig's control flow.
|
|
163
156
|
- **Full-Stack Capabilities**: Build both frontend and backend of web application.
|
|
164
157
|
- **Fast**: Significantly faster at SSR than many other frameworks.
|
|
@@ -170,24 +163,27 @@ const zx = @import("zx");
|
|
|
170
163
|
- **Developer Tooling**: CLI, hot reload, and editor extensions for the best DX.
|
|
171
164
|
|
|
172
165
|
## Versions
|
|
173
|
-
| Zig | Ziex | Branch | Status |
|
|
174
|
-
|-------------|---------------------|-------------------|----------------|
|
|
175
|
-
| `0.17.x` | | `zig-0.17` | Development |
|
|
176
|
-
| `0.16.x` | `0.1.0-dev.1068`-* | `main` | **Latest** |
|
|
177
|
-
| `0.15.x` | `0.1.0-dev-1050` | `zig-0.15` | Latest (`Zig 0.15.x`) |
|
|
178
166
|
|
|
167
|
+
| Zig | Ziex | Branch | Status |
|
|
168
|
+
| -------- | --------------------------------------------------------------------------------- | ---------- | ----------- |
|
|
169
|
+
| `0.17.x` | | `main` | Development |
|
|
170
|
+
| `0.16.x` | [`0.1.0-dev.1259`](https://github.com/ziex-dev/ziex/releases/tag/v0.1.0-dev.1259) | `zig-0.16` | **Latest** |
|
|
171
|
+
| `0.15.x` | [`0.1.0-dev-1050`](https://github.com/ziex-dev/ziex/releases/tag/v0.1.0-dev.1050) | `zig-0.15` | Outdated |
|
|
179
172
|
|
|
180
173
|
## Roadmap
|
|
181
|
-
[Ziex 0.1.0](https://github.com/ziex-dev/ziex/milestone/2) is targeted after `Zig 0.17.0` release. You can see the [roadmap](https://github.com/ziex-dev/ziex/milestone) to learn more.
|
|
182
174
|
|
|
183
|
-
|
|
175
|
+
[Ziex 0.1.0](https://github.com/ziex-dev/ziex/milestone/2) is planned for release after `Zig 0.17.0` is available. You can view the [roadmap](https://github.com/ziex-dev/ziex/milestones) to learn more.
|
|
176
|
+
|
|
177
|
+
The [`0.1.0`](https://github.com/ziex-dev/ziex/milestone/2) release will indicate that Ziex is production-ready, with all major features fully implemented, and will receive patch releases for bug fixes.
|
|
178
|
+
|
|
179
|
+
The [`1.0.0`](https://github.com/ziex-dev/ziex/milestone/6) release will signify long-term support for that major version, receiving bug fixes and minor updates.
|
|
184
180
|
|
|
185
181
|
## Editor Support
|
|
186
182
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
183
|
+
- [VSCode](https://marketplace.visualstudio.com/items?itemName=ziex.ziex)/[VSCode Forks](https://open-vsx.org/extension/ziex/ziex)
|
|
184
|
+
- [Neovim](/ide/neovim/)
|
|
185
|
+
- [Helix](/ide/helix/)
|
|
186
|
+
- [Zed](/ide/zed/)
|
|
191
187
|
|
|
192
188
|
## Community
|
|
193
189
|
|
package/cloudflare/index.js
CHANGED
|
@@ -193,6 +193,76 @@ class ZigJS {
|
|
|
193
193
|
return decoder.decode(data);
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
|
+
// src/fetch.ts
|
|
197
|
+
function createFetchImports(getMemory) {
|
|
198
|
+
const encoder2 = new TextEncoder;
|
|
199
|
+
const decoder2 = new TextDecoder;
|
|
200
|
+
function readStr(ptr, len) {
|
|
201
|
+
return decoder2.decode(new Uint8Array(getMemory().buffer, ptr, len));
|
|
202
|
+
}
|
|
203
|
+
function writeBytes(buf_ptr, buf_max, data) {
|
|
204
|
+
if (data.length > buf_max)
|
|
205
|
+
return -2;
|
|
206
|
+
new Uint8Array(getMemory().buffer, buf_ptr, data.length).set(data);
|
|
207
|
+
return data.length;
|
|
208
|
+
}
|
|
209
|
+
function readBytes(ptr, len) {
|
|
210
|
+
return new Uint8Array(getMemory().buffer, ptr, len);
|
|
211
|
+
}
|
|
212
|
+
function parseHeaders(headersJson) {
|
|
213
|
+
let headers = {};
|
|
214
|
+
try {
|
|
215
|
+
headers = JSON.parse(headersJson);
|
|
216
|
+
} catch {
|
|
217
|
+
for (const line of headersJson.split(`
|
|
218
|
+
`)) {
|
|
219
|
+
const colonIdx = line.indexOf(":");
|
|
220
|
+
if (colonIdx > 0) {
|
|
221
|
+
headers[line.slice(0, colonIdx)] = line.slice(colonIdx + 1);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return headers;
|
|
226
|
+
}
|
|
227
|
+
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) {
|
|
228
|
+
const url = readStr(url_ptr, url_len);
|
|
229
|
+
const method = method_len > 0 ? readStr(method_ptr, method_len) : "GET";
|
|
230
|
+
const headersJson = headers_len > 0 ? readStr(headers_ptr, headers_len) : "{}";
|
|
231
|
+
const body = body_len > 0 ? readBytes(body_ptr, body_len) : undefined;
|
|
232
|
+
const headers = parseHeaders(headersJson);
|
|
233
|
+
const controller = new AbortController;
|
|
234
|
+
const timeout = timeout_ms > 0 ? setTimeout(() => controller.abort(), timeout_ms) : null;
|
|
235
|
+
try {
|
|
236
|
+
const response = await fetch(url, {
|
|
237
|
+
method,
|
|
238
|
+
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
239
|
+
body: method !== "GET" && method !== "HEAD" ? body : undefined,
|
|
240
|
+
signal: controller.signal
|
|
241
|
+
});
|
|
242
|
+
if (timeout)
|
|
243
|
+
clearTimeout(timeout);
|
|
244
|
+
new DataView(getMemory().buffer).setUint16(status_out, response.status, true);
|
|
245
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
246
|
+
return writeBytes(buf_ptr, buf_max, bytes);
|
|
247
|
+
} catch (err) {
|
|
248
|
+
if (timeout)
|
|
249
|
+
clearTimeout(timeout);
|
|
250
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
251
|
+
console.error(`wasm fetch failed: ${url} (${msg})`);
|
|
252
|
+
return -1;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const Suspending = WebAssembly.Suspending;
|
|
256
|
+
if (typeof Suspending !== "function") {
|
|
257
|
+
return {
|
|
258
|
+
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
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
fetch: new Suspending(doFetch)
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
196
266
|
// src/wasm/core.ts
|
|
197
267
|
var CallbackType = {
|
|
198
268
|
Event: 0,
|
|
@@ -291,7 +361,7 @@ class ZxBridgeCore {
|
|
|
291
361
|
const url = readString(urlPtr, urlLen);
|
|
292
362
|
const method = methodLen > 0 ? readString(methodPtr, methodLen) : "GET";
|
|
293
363
|
const headersJson = headersLen > 0 ? readString(headersPtr, headersLen) : "{}";
|
|
294
|
-
const body = bodyLen > 0 ?
|
|
364
|
+
const body = bodyLen > 0 ? getMemoryView().subarray(bodyPtr, bodyPtr + bodyLen) : undefined;
|
|
295
365
|
let headers = {};
|
|
296
366
|
try {
|
|
297
367
|
headers = JSON.parse(headersJson);
|
|
@@ -315,8 +385,8 @@ class ZxBridgeCore {
|
|
|
315
385
|
fetch(url, fetchOptions).then(async (response) => {
|
|
316
386
|
if (timeout)
|
|
317
387
|
clearTimeout(timeout);
|
|
318
|
-
const
|
|
319
|
-
this._notifyFetchComplete(fetchId, response.status,
|
|
388
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
389
|
+
this._notifyFetchComplete(fetchId, response.status, bytes, false);
|
|
320
390
|
}).catch((error) => {
|
|
321
391
|
if (timeout)
|
|
322
392
|
clearTimeout(timeout);
|
|
@@ -327,7 +397,7 @@ class ZxBridgeCore {
|
|
|
327
397
|
}
|
|
328
398
|
_notifyFetchComplete(fetchId, statusCode, body, isError) {
|
|
329
399
|
const handler = this.#fetchCompleteHandler;
|
|
330
|
-
const encoded = textEncoder.encode(body);
|
|
400
|
+
const encoded = typeof body === "string" ? textEncoder.encode(body) : body;
|
|
331
401
|
const ptr = this._alloc(encoded.length);
|
|
332
402
|
writeBytes(ptr, encoded);
|
|
333
403
|
invokeWasmExport(handler, fetchId, statusCode, ptr, encoded.length, isError ? 1 : 0);
|
|
@@ -384,6 +454,11 @@ class ZxBridgeCore {
|
|
|
384
454
|
static createImportObject(bridgeRef) {
|
|
385
455
|
return {
|
|
386
456
|
...jsz.importObject(),
|
|
457
|
+
__zx_net: createFetchImports(() => {
|
|
458
|
+
if (!jsz.memory)
|
|
459
|
+
throw new Error("WASM memory is not ready");
|
|
460
|
+
return jsz.memory;
|
|
461
|
+
}),
|
|
387
462
|
__zx: {
|
|
388
463
|
_log: (level, ptr, len) => ZxBridgeCore.log(level, ptr, len),
|
|
389
464
|
_fetchAsync: (urlPtr, urlLen, methodPtr, methodLen, headersPtr, headersLen, bodyPtr, bodyLen, timeoutMs, fetchId) => {
|
|
@@ -682,12 +757,12 @@ class ZxBridge extends ZxBridgeCore {
|
|
|
682
757
|
clearEventHandlerModes(velementId) {
|
|
683
758
|
eventHandlerModes.delete(velementId);
|
|
684
759
|
}
|
|
685
|
-
submitFormActionAsync(form, statesJson, fetchId) {
|
|
760
|
+
submitFormActionAsync(form, actionId, statesJson, fetchId) {
|
|
686
761
|
const formData = new FormData(form);
|
|
687
762
|
formData.append("__$states", statesJson);
|
|
688
763
|
fetch(window.location.href, {
|
|
689
764
|
method: "POST",
|
|
690
|
-
headers: { "
|
|
765
|
+
headers: { "x-action": String(actionId >>> 0) },
|
|
691
766
|
body: formData
|
|
692
767
|
}).then(async (response) => {
|
|
693
768
|
const text = await response.text();
|
|
@@ -793,6 +868,11 @@ class ZxBridge extends ZxBridgeCore {
|
|
|
793
868
|
static createImportObject(bridgeRef) {
|
|
794
869
|
return {
|
|
795
870
|
...jsz.importObject(),
|
|
871
|
+
__zx_net: createFetchImports(() => {
|
|
872
|
+
if (!jsz.memory)
|
|
873
|
+
throw new Error("WASM memory is not ready");
|
|
874
|
+
return jsz.memory;
|
|
875
|
+
}),
|
|
796
876
|
__zx: {
|
|
797
877
|
_log: (level, ptr, len) => ZxBridgeCore.log(level, ptr, len),
|
|
798
878
|
_setEventHandlerMode: (vnodeId, eventTypeId, maySuspend) => {
|
|
@@ -911,23 +991,23 @@ class ZxBridge extends ZxBridgeCore {
|
|
|
911
991
|
writeBytes(bufPtr, bytes.subarray(0, len));
|
|
912
992
|
return len;
|
|
913
993
|
},
|
|
914
|
-
_submitFormAction: (vnodeId) => {
|
|
994
|
+
_submitFormAction: (vnodeId, actionId) => {
|
|
915
995
|
const form = domNodes.get(vnodeId);
|
|
916
996
|
if (!form || !(form instanceof HTMLFormElement))
|
|
917
997
|
return;
|
|
918
998
|
const formData = new FormData(form);
|
|
919
999
|
fetch(window.location.href, {
|
|
920
1000
|
method: "POST",
|
|
921
|
-
headers: { "
|
|
1001
|
+
headers: { "x-action": String(actionId >>> 0) },
|
|
922
1002
|
body: formData
|
|
923
1003
|
}).catch(() => {});
|
|
924
1004
|
},
|
|
925
|
-
_submitFormActionAsync: (vnodeId, statesPtr, statesLen, fetchId) => {
|
|
1005
|
+
_submitFormActionAsync: (vnodeId, actionId, statesPtr, statesLen, fetchId) => {
|
|
926
1006
|
const form = domNodes.get(vnodeId);
|
|
927
1007
|
if (!form || !(form instanceof HTMLFormElement))
|
|
928
1008
|
return;
|
|
929
1009
|
const statesJson = statesLen > 0 ? readString(statesPtr, statesLen) : "[]";
|
|
930
|
-
bridgeRef.current?.submitFormActionAsync(form, statesJson, fetchId);
|
|
1010
|
+
bridgeRef.current?.submitFormActionAsync(form, actionId, statesJson, fetchId);
|
|
931
1011
|
}
|
|
932
1012
|
}
|
|
933
1013
|
};
|
|
@@ -1259,7 +1339,8 @@ async function init(options = {}) {
|
|
|
1259
1339
|
domNodes.clear();
|
|
1260
1340
|
}
|
|
1261
1341
|
};
|
|
1262
|
-
|
|
1342
|
+
if (true)
|
|
1343
|
+
registerDevReinit(normalizedOptions);
|
|
1263
1344
|
return { source, bridge };
|
|
1264
1345
|
}
|
|
1265
1346
|
|
|
@@ -1324,7 +1405,7 @@ class ZxWasiBridge {
|
|
|
1324
1405
|
const url = this.#readString(urlPtr, urlLen);
|
|
1325
1406
|
const method = methodLen > 0 ? this.#readString(methodPtr, methodLen) : "GET";
|
|
1326
1407
|
const headersJson = headersLen > 0 ? this.#readString(headersPtr, headersLen) : "{}";
|
|
1327
|
-
const body = bodyLen > 0 ? this.#
|
|
1408
|
+
const body = bodyLen > 0 ? this.#view().subarray(bodyPtr, bodyPtr + bodyLen) : undefined;
|
|
1328
1409
|
let headers = {};
|
|
1329
1410
|
try {
|
|
1330
1411
|
headers = JSON.parse(headersJson);
|
|
@@ -1346,7 +1427,8 @@ class ZxWasiBridge {
|
|
|
1346
1427
|
}).then(async (res) => {
|
|
1347
1428
|
if (timeout)
|
|
1348
1429
|
clearTimeout(timeout);
|
|
1349
|
-
|
|
1430
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
1431
|
+
this.#notifyFetchComplete(fetchId, res.status, bytes, false);
|
|
1350
1432
|
}).catch((err) => {
|
|
1351
1433
|
if (timeout)
|
|
1352
1434
|
clearTimeout(timeout);
|
|
@@ -1355,7 +1437,7 @@ class ZxWasiBridge {
|
|
|
1355
1437
|
});
|
|
1356
1438
|
}
|
|
1357
1439
|
#notifyFetchComplete(fetchId, status, body, isError) {
|
|
1358
|
-
const encoded = encoder2.encode(body);
|
|
1440
|
+
const encoded = typeof body === "string" ? encoder2.encode(body) : body;
|
|
1359
1441
|
const ptr = this.#alloc(encoded.length);
|
|
1360
1442
|
this.#writeBytes(ptr, encoded);
|
|
1361
1443
|
this.#fetchCompleteHandler(fetchId, status, ptr, encoded.length, isError ? 1 : 0);
|
|
@@ -1562,7 +1644,9 @@ function createWasiImports({
|
|
|
1562
1644
|
"--method",
|
|
1563
1645
|
request.method,
|
|
1564
1646
|
"--search",
|
|
1565
|
-
url.search
|
|
1647
|
+
url.search,
|
|
1648
|
+
"--url",
|
|
1649
|
+
url.href
|
|
1566
1650
|
];
|
|
1567
1651
|
request.headers.forEach((value, name) => {
|
|
1568
1652
|
if (value)
|
|
@@ -2015,6 +2099,7 @@ async function run({
|
|
|
2015
2099
|
__zx_ws: buildWsImports(jspi ? Suspending : null, mem, new TextDecoder, wsState),
|
|
2016
2100
|
__zx_kv: createKVImports(kvBindings ?? { default: createMemoryKV() }, mem),
|
|
2017
2101
|
__zx_db: createD1Imports(dbBindings ?? {}, mem),
|
|
2102
|
+
__zx_net: createFetchImports(mem),
|
|
2018
2103
|
...imports ? imports(mem) : {},
|
|
2019
2104
|
...ZxWasiBridge.createImportObject(bridgeRef)
|
|
2020
2105
|
});
|
|
@@ -2189,6 +2274,7 @@ function createWebSocketDO(module, options) {
|
|
|
2189
2274
|
__zx_ws: wsImports,
|
|
2190
2275
|
__zx_kv: createKVImports(kvBindings ?? { default: createMemoryKV() }, mem),
|
|
2191
2276
|
__zx_db: createD1Imports(dbBindings ?? {}, mem),
|
|
2277
|
+
__zx_net: createFetchImports(mem),
|
|
2192
2278
|
...options?.imports ? options.imports(mem) : {},
|
|
2193
2279
|
...bridgeImports
|
|
2194
2280
|
});
|
package/index.js
CHANGED
|
@@ -74,7 +74,7 @@ class ZxWasiBridge {
|
|
|
74
74
|
const url = this.#readString(urlPtr, urlLen);
|
|
75
75
|
const method = methodLen > 0 ? this.#readString(methodPtr, methodLen) : "GET";
|
|
76
76
|
const headersJson = headersLen > 0 ? this.#readString(headersPtr, headersLen) : "{}";
|
|
77
|
-
const body = bodyLen > 0 ? this.#
|
|
77
|
+
const body = bodyLen > 0 ? this.#view().subarray(bodyPtr, bodyPtr + bodyLen) : undefined;
|
|
78
78
|
let headers = {};
|
|
79
79
|
try {
|
|
80
80
|
headers = JSON.parse(headersJson);
|
|
@@ -96,7 +96,8 @@ class ZxWasiBridge {
|
|
|
96
96
|
}).then(async (res) => {
|
|
97
97
|
if (timeout)
|
|
98
98
|
clearTimeout(timeout);
|
|
99
|
-
|
|
99
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
100
|
+
this.#notifyFetchComplete(fetchId, res.status, bytes, false);
|
|
100
101
|
}).catch((err) => {
|
|
101
102
|
if (timeout)
|
|
102
103
|
clearTimeout(timeout);
|
|
@@ -105,7 +106,7 @@ class ZxWasiBridge {
|
|
|
105
106
|
});
|
|
106
107
|
}
|
|
107
108
|
#notifyFetchComplete(fetchId, status, body, isError) {
|
|
108
|
-
const encoded = encoder.encode(body);
|
|
109
|
+
const encoded = typeof body === "string" ? encoder.encode(body) : body;
|
|
109
110
|
const ptr = this.#alloc(encoded.length);
|
|
110
111
|
this.#writeBytes(ptr, encoded);
|
|
111
112
|
this.#fetchCompleteHandler(fetchId, status, ptr, encoded.length, isError ? 1 : 0);
|
|
@@ -265,6 +266,76 @@ function createKVImports(bindings, getMemory) {
|
|
|
265
266
|
};
|
|
266
267
|
}
|
|
267
268
|
|
|
269
|
+
// src/fetch.ts
|
|
270
|
+
function createFetchImports(getMemory) {
|
|
271
|
+
const encoder2 = new TextEncoder;
|
|
272
|
+
const decoder2 = new TextDecoder;
|
|
273
|
+
function readStr(ptr, len) {
|
|
274
|
+
return decoder2.decode(new Uint8Array(getMemory().buffer, ptr, len));
|
|
275
|
+
}
|
|
276
|
+
function writeBytes(buf_ptr, buf_max, data) {
|
|
277
|
+
if (data.length > buf_max)
|
|
278
|
+
return -2;
|
|
279
|
+
new Uint8Array(getMemory().buffer, buf_ptr, data.length).set(data);
|
|
280
|
+
return data.length;
|
|
281
|
+
}
|
|
282
|
+
function readBytes(ptr, len) {
|
|
283
|
+
return new Uint8Array(getMemory().buffer, ptr, len);
|
|
284
|
+
}
|
|
285
|
+
function parseHeaders(headersJson) {
|
|
286
|
+
let headers = {};
|
|
287
|
+
try {
|
|
288
|
+
headers = JSON.parse(headersJson);
|
|
289
|
+
} catch {
|
|
290
|
+
for (const line of headersJson.split(`
|
|
291
|
+
`)) {
|
|
292
|
+
const colonIdx = line.indexOf(":");
|
|
293
|
+
if (colonIdx > 0) {
|
|
294
|
+
headers[line.slice(0, colonIdx)] = line.slice(colonIdx + 1);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return headers;
|
|
299
|
+
}
|
|
300
|
+
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) {
|
|
301
|
+
const url = readStr(url_ptr, url_len);
|
|
302
|
+
const method = method_len > 0 ? readStr(method_ptr, method_len) : "GET";
|
|
303
|
+
const headersJson = headers_len > 0 ? readStr(headers_ptr, headers_len) : "{}";
|
|
304
|
+
const body = body_len > 0 ? readBytes(body_ptr, body_len) : undefined;
|
|
305
|
+
const headers = parseHeaders(headersJson);
|
|
306
|
+
const controller = new AbortController;
|
|
307
|
+
const timeout = timeout_ms > 0 ? setTimeout(() => controller.abort(), timeout_ms) : null;
|
|
308
|
+
try {
|
|
309
|
+
const response = await fetch(url, {
|
|
310
|
+
method,
|
|
311
|
+
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
312
|
+
body: method !== "GET" && method !== "HEAD" ? body : undefined,
|
|
313
|
+
signal: controller.signal
|
|
314
|
+
});
|
|
315
|
+
if (timeout)
|
|
316
|
+
clearTimeout(timeout);
|
|
317
|
+
new DataView(getMemory().buffer).setUint16(status_out, response.status, true);
|
|
318
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
319
|
+
return writeBytes(buf_ptr, buf_max, bytes);
|
|
320
|
+
} catch (err) {
|
|
321
|
+
if (timeout)
|
|
322
|
+
clearTimeout(timeout);
|
|
323
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
324
|
+
console.error(`wasm fetch failed: ${url} (${msg})`);
|
|
325
|
+
return -1;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const Suspending = WebAssembly.Suspending;
|
|
329
|
+
if (typeof Suspending !== "function") {
|
|
330
|
+
return {
|
|
331
|
+
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
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
fetch: new Suspending(doFetch)
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
268
339
|
// src/db.ts
|
|
269
340
|
function decodeBlob(base64) {
|
|
270
341
|
const binary = atob(base64);
|
|
@@ -430,7 +501,9 @@ function createWasiImports({
|
|
|
430
501
|
"--method",
|
|
431
502
|
request.method,
|
|
432
503
|
"--search",
|
|
433
|
-
url.search
|
|
504
|
+
url.search,
|
|
505
|
+
"--url",
|
|
506
|
+
url.href
|
|
434
507
|
];
|
|
435
508
|
request.headers.forEach((value, name) => {
|
|
436
509
|
if (value)
|
|
@@ -883,6 +956,7 @@ async function run({
|
|
|
883
956
|
__zx_ws: buildWsImports(jspi ? Suspending : null, mem, new TextDecoder, wsState),
|
|
884
957
|
__zx_kv: createKVImports(kvBindings ?? { default: createMemoryKV() }, mem),
|
|
885
958
|
__zx_db: createD1Imports(dbBindings ?? {}, mem),
|
|
959
|
+
__zx_net: createFetchImports(mem),
|
|
886
960
|
...imports ? imports(mem) : {},
|
|
887
961
|
...ZxWasiBridge.createImportObject(bridgeRef)
|
|
888
962
|
});
|
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.1360",
|
|
4
4
|
"description": "Ziex is a framework for building web applications with Zig.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"license": "MIT",
|
|
38
38
|
"scripts": {},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@ziex/cli": "0.1.0-dev.
|
|
40
|
+
"@ziex/cli": "0.1.0-dev.1360"
|
|
41
41
|
},
|
|
42
42
|
"peerDependenciesMeta": {
|
|
43
43
|
"react": {
|
|
@@ -49,8 +49,8 @@ export declare class ZxBridgeCore {
|
|
|
49
49
|
* Calls __zx_fetch_complete when done.
|
|
50
50
|
*/
|
|
51
51
|
fetchAsync(urlPtr: number, urlLen: number, methodPtr: number, methodLen: number, headersPtr: number, headersLen: number, bodyPtr: number, bodyLen: number, timeoutMs: number, fetchId: bigint): void;
|
|
52
|
-
/** Notify WASM that a fetch completed */
|
|
53
|
-
protected _notifyFetchComplete(fetchId: bigint, statusCode: number, body: string, isError: boolean): void;
|
|
52
|
+
/** Notify WASM that a fetch completed. `body` is raw bytes on success, text on error. */
|
|
53
|
+
protected _notifyFetchComplete(fetchId: bigint, statusCode: number, body: string | Uint8Array, isError: boolean): void;
|
|
54
54
|
/** Set a timeout and callback when it fires */
|
|
55
55
|
setTimeout(callbackId: bigint, delayMs: number): void;
|
|
56
56
|
/** Set an interval and callback each time it fires */
|
|
@@ -14,7 +14,7 @@ export declare class ZxBridge extends ZxBridgeCore {
|
|
|
14
14
|
setEventHandlerMode(velementId: bigint, eventTypeId: number, maySuspend: boolean): void;
|
|
15
15
|
clearEventHandlerModes(velementId: bigint): void;
|
|
16
16
|
/** Submit a form action with bound-state round-trip. */
|
|
17
|
-
submitFormActionAsync(form: HTMLFormElement, statesJson: string, fetchId: bigint): void;
|
|
17
|
+
submitFormActionAsync(form: HTMLFormElement, actionId: number, statesJson: string, fetchId: bigint): void;
|
|
18
18
|
/**
|
|
19
19
|
* Create and connect a WebSocket.
|
|
20
20
|
* Calls __zx_ws_onopen, __zx_ws_onmessage, __zx_ws_onerror, __zx_ws_onclose.
|
|
@@ -58,6 +58,7 @@ export declare function init(options?: InitOptions): Promise<{
|
|
|
58
58
|
bridge: ZxBridge;
|
|
59
59
|
}>;
|
|
60
60
|
declare global {
|
|
61
|
+
const __DEV__: boolean;
|
|
61
62
|
interface HTMLElement {
|
|
62
63
|
__zx_ref?: number;
|
|
63
64
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './zigjs';
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Predefined references.
|
|
3
|
+
* */
|
|
4
|
+
export declare const predefined: {
|
|
5
|
+
nan: number;
|
|
6
|
+
null: number;
|
|
7
|
+
true: number;
|
|
8
|
+
false: number;
|
|
9
|
+
undefined: number;
|
|
10
|
+
globalThis: number;
|
|
11
|
+
runtime: number;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The main state.
|
|
15
|
+
* */
|
|
16
|
+
export declare class ZigJS {
|
|
17
|
+
/**
|
|
18
|
+
* Set this to the memory of your WebAssembly instance prior to making
|
|
19
|
+
* any Wasm calls that might interface with JS.
|
|
20
|
+
*
|
|
21
|
+
* We have to use a Memory object directly (rather than an ArrayBuffer)
|
|
22
|
+
* so that we can always access memory even if it is resized. Otherwise,
|
|
23
|
+
* a resize could detach the array buffer.
|
|
24
|
+
* */
|
|
25
|
+
memory?: WebAssembly.Memory;
|
|
26
|
+
/**
|
|
27
|
+
* The values, indexed by ID (number). Duplicate values can be in this
|
|
28
|
+
* if they are loaded multiple times. That just acts as duplicate references.
|
|
29
|
+
* */
|
|
30
|
+
private values;
|
|
31
|
+
/**
|
|
32
|
+
* When a value is removed from the values array, its ID is put
|
|
33
|
+
* in the pool so that we can use it next. This avoids fragmentation in
|
|
34
|
+
* our array keys.
|
|
35
|
+
* */
|
|
36
|
+
private idPool;
|
|
37
|
+
/**
|
|
38
|
+
* Returns the import object that should be merged with your import
|
|
39
|
+
* object when instantiating your wasm instance. This injects the required
|
|
40
|
+
* functions into the wasm environment.
|
|
41
|
+
* */
|
|
42
|
+
importObject(): ImportObject;
|
|
43
|
+
/**
|
|
44
|
+
* Get a value from the JS environment.
|
|
45
|
+
* */
|
|
46
|
+
protected valueGet(out: number, id: number, ptr: number, len: number): void;
|
|
47
|
+
/**
|
|
48
|
+
* Set a value on an object.
|
|
49
|
+
* */
|
|
50
|
+
protected valueSet(id: number, ptr: number, len: number, refAddr: number): void;
|
|
51
|
+
/**
|
|
52
|
+
* Dereference a value, allowing the JS environment to potentially GC it.
|
|
53
|
+
* */
|
|
54
|
+
protected valueDeinit(id: number): void;
|
|
55
|
+
/**
|
|
56
|
+
* Create an empty object.
|
|
57
|
+
* */
|
|
58
|
+
protected valueObjectCreate(out: number): void;
|
|
59
|
+
/**
|
|
60
|
+
* Creates a string on the JS side from a UTF-8 encoded string in wasm memory.
|
|
61
|
+
* */
|
|
62
|
+
protected valueStringCreate(out: number, ptr: number, len: number): void;
|
|
63
|
+
/**
|
|
64
|
+
* Returns the length of the string given by id.
|
|
65
|
+
* */
|
|
66
|
+
protected valueStringLen(id: number): number;
|
|
67
|
+
/**
|
|
68
|
+
* Copy the string at id "id" into the shared memory at ptr.
|
|
69
|
+
* */
|
|
70
|
+
protected valueStringCopy(id: number, ptr: number, max: number): void;
|
|
71
|
+
/**
|
|
72
|
+
* Call a constructor given by id.
|
|
73
|
+
* */
|
|
74
|
+
protected valueNew(out: number, id: number, argsAddr: number, argsLen: number): void;
|
|
75
|
+
/**
|
|
76
|
+
* Call a function given by id.
|
|
77
|
+
* */
|
|
78
|
+
protected funcApply(out: number, id: number, thisRefAddr: number, argsAddr: number, argsLen: number): void;
|
|
79
|
+
/**
|
|
80
|
+
* This can be used to load a value given by an ID. A WASM function
|
|
81
|
+
* might return a value as an ID. This can be used to retrieve it.
|
|
82
|
+
* */
|
|
83
|
+
loadValue(id: number): any;
|
|
84
|
+
/**
|
|
85
|
+
* This removes the value from the values list and returns it. This
|
|
86
|
+
* will not remove predefined values but can still be used to retrieve
|
|
87
|
+
* them.
|
|
88
|
+
* */
|
|
89
|
+
deleteValue(id: number): any;
|
|
90
|
+
loadRef(refAddr: number): any;
|
|
91
|
+
loadRefId(refAddr: number): number;
|
|
92
|
+
storeValue(out: number, val: any): void;
|
|
93
|
+
loadString(ptr: number, len: number): string;
|
|
94
|
+
}
|
|
95
|
+
export interface ImportObject {
|
|
96
|
+
"zig-js": {
|
|
97
|
+
valueGet: (out: number, id: number, ptr: number, len: number) => void;
|
|
98
|
+
valueSet: (id: number, ptr: number, len: number, valueRef: number) => void;
|
|
99
|
+
valueObjectCreate: (out: number) => void;
|
|
100
|
+
valueStringCreate: (out: number, ptr: number, len: number) => void;
|
|
101
|
+
valueStringLen: (id: number) => number;
|
|
102
|
+
valueStringCopy: (id: number, ptr: number, max: number) => void;
|
|
103
|
+
valueDeinit: (id: number) => void;
|
|
104
|
+
valueNew: (out: number, funcId: number, argsPtr: number, argsLen: number) => void;
|
|
105
|
+
funcApply: (out: number, funcId: number, thisRef: number, argsPtr: number, argsLen: number) => void;
|
|
106
|
+
};
|
|
107
|
+
}
|