ziex 0.1.0-dev.526 → 0.1.0-dev.547
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 +1 -1
- package/package.json +1 -1
- package/react/dom.d.ts +2 -2
- package/react/index.js +55 -31
- package/wasm/index.d.ts +5 -2
- package/wasm/index.js +60 -9
- package/wasm/init.js +60 -9
package/index.js
CHANGED
package/package.json
CHANGED
package/react/dom.d.ts
CHANGED
|
@@ -127,8 +127,8 @@ export type DiscoveredComponent = {
|
|
|
127
127
|
* Finds all React component markers in the DOM and returns their metadata.
|
|
128
128
|
*
|
|
129
129
|
* This is a DOM-first approach that:
|
|
130
|
-
* 1. Walks the DOM
|
|
131
|
-
* 2.
|
|
130
|
+
* 1. Walks the DOM to find all `<!--$id name props-->` comment markers
|
|
131
|
+
* 2. Extracts name and props directly from the comment content (JSON-encoded)
|
|
132
132
|
* 3. Creates containers for React to render into
|
|
133
133
|
*
|
|
134
134
|
* @returns Array of discovered components with their containers and props
|
package/react/index.js
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
// src/react/dom.ts
|
|
2
2
|
function findCommentMarker(id) {
|
|
3
|
-
const
|
|
3
|
+
const startPrefix = `$${id} `;
|
|
4
4
|
const endMarker = `/$${id}`;
|
|
5
5
|
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT, null);
|
|
6
6
|
let startComment = null;
|
|
7
7
|
let endComment = null;
|
|
8
|
+
let name = "";
|
|
9
|
+
let props = {};
|
|
8
10
|
let node;
|
|
9
11
|
while (node = walker.nextNode()) {
|
|
10
12
|
const text = node.textContent?.trim() || "";
|
|
11
|
-
if (text
|
|
13
|
+
if (text.startsWith(startPrefix)) {
|
|
12
14
|
startComment = node;
|
|
15
|
+
const content = text.slice(startPrefix.length);
|
|
16
|
+
const jsonStart = content.indexOf("{");
|
|
17
|
+
if (jsonStart !== -1) {
|
|
18
|
+
name = content.slice(0, jsonStart).trim();
|
|
19
|
+
const jsonStr = content.slice(jsonStart);
|
|
20
|
+
try {
|
|
21
|
+
props = JSON.parse(jsonStr);
|
|
22
|
+
} catch {}
|
|
23
|
+
} else {
|
|
24
|
+
name = content.trim();
|
|
25
|
+
}
|
|
13
26
|
}
|
|
14
27
|
if (text === endMarker) {
|
|
15
28
|
endComment = node;
|
|
@@ -17,20 +30,10 @@ function findCommentMarker(id) {
|
|
|
17
30
|
}
|
|
18
31
|
}
|
|
19
32
|
if (startComment && endComment) {
|
|
20
|
-
return { startComment, endComment };
|
|
33
|
+
return { startComment, endComment, name, props };
|
|
21
34
|
}
|
|
22
35
|
return null;
|
|
23
36
|
}
|
|
24
|
-
function getComponentMetadata(id) {
|
|
25
|
-
const script = document.querySelector(`script[data-zx="${id}"]`);
|
|
26
|
-
if (script?.textContent) {
|
|
27
|
-
try {
|
|
28
|
-
const data = JSON.parse(script.textContent);
|
|
29
|
-
return { name: data.name || "", props: data.props || {} };
|
|
30
|
-
} catch {}
|
|
31
|
-
}
|
|
32
|
-
return { name: "", props: {} };
|
|
33
|
-
}
|
|
34
37
|
function createContainerBetweenMarkers(startComment, endComment) {
|
|
35
38
|
const container = document.createElement("div");
|
|
36
39
|
container.style.display = "contents";
|
|
@@ -48,8 +51,7 @@ async function prepareComponent(component) {
|
|
|
48
51
|
if (!marker) {
|
|
49
52
|
throw new Error(`Comment marker for ${component.id} not found`, { cause: component });
|
|
50
53
|
}
|
|
51
|
-
const
|
|
52
|
-
const props = metadata.props;
|
|
54
|
+
const props = marker.props;
|
|
53
55
|
const domNode = createContainerBetweenMarkers(marker.startComment, marker.endComment);
|
|
54
56
|
const Component = await component.import();
|
|
55
57
|
return { domNode, props, Component };
|
|
@@ -60,25 +62,47 @@ function filterComponents(components) {
|
|
|
60
62
|
}
|
|
61
63
|
function discoverComponents() {
|
|
62
64
|
const components = [];
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
65
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT, null);
|
|
66
|
+
const markers = [];
|
|
67
|
+
let node;
|
|
68
|
+
while (node = walker.nextNode()) {
|
|
69
|
+
const text = node.textContent?.trim() || "";
|
|
70
|
+
if (text.startsWith("$") && !text.startsWith("/$")) {
|
|
71
|
+
const spaceIdx = text.indexOf(" ");
|
|
72
|
+
if (spaceIdx !== -1) {
|
|
73
|
+
const id = text.slice(1, spaceIdx);
|
|
74
|
+
const content = text.slice(spaceIdx + 1);
|
|
75
|
+
const jsonStart = content.indexOf("{");
|
|
76
|
+
let name = "";
|
|
77
|
+
let props = {};
|
|
78
|
+
if (jsonStart !== -1) {
|
|
79
|
+
name = content.slice(0, jsonStart).trim();
|
|
80
|
+
try {
|
|
81
|
+
props = JSON.parse(content.slice(jsonStart));
|
|
82
|
+
} catch {}
|
|
83
|
+
} else {
|
|
84
|
+
name = content.trim();
|
|
85
|
+
}
|
|
86
|
+
markers.push({ id, name, props, startComment: node, endComment: null });
|
|
87
|
+
}
|
|
88
|
+
} else if (text.startsWith("/$")) {
|
|
89
|
+
const id = text.slice(2);
|
|
90
|
+
const marker = markers.find((m) => m.id === id && !m.endComment);
|
|
91
|
+
if (marker) {
|
|
92
|
+
marker.endComment = node;
|
|
93
|
+
}
|
|
76
94
|
}
|
|
77
|
-
|
|
78
|
-
|
|
95
|
+
}
|
|
96
|
+
for (const marker of markers) {
|
|
97
|
+
if (!marker.endComment)
|
|
79
98
|
continue;
|
|
80
99
|
const container = createContainerBetweenMarkers(marker.startComment, marker.endComment);
|
|
81
|
-
components.push({
|
|
100
|
+
components.push({
|
|
101
|
+
id: marker.id,
|
|
102
|
+
name: marker.name,
|
|
103
|
+
props: marker.props,
|
|
104
|
+
container
|
|
105
|
+
});
|
|
82
106
|
}
|
|
83
107
|
return components;
|
|
84
108
|
}
|
package/wasm/index.d.ts
CHANGED
|
@@ -18,8 +18,11 @@ export declare function storeValueGetRef(val: any): bigint;
|
|
|
18
18
|
export declare class ZxBridge {
|
|
19
19
|
#private;
|
|
20
20
|
constructor(exports: WebAssembly.Exports);
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Async fetch with full options support.
|
|
23
|
+
* Calls __zx_fetch_complete when done.
|
|
24
|
+
*/
|
|
25
|
+
fetchAsync(urlPtr: number, urlLen: number, methodPtr: number, methodLen: number, headersPtr: number, headersLen: number, bodyPtr: number, bodyLen: number, timeoutMs: number, fetchId: bigint): void;
|
|
23
26
|
/** Set a timeout and callback when it fires */
|
|
24
27
|
setTimeout(callbackId: bigint, delayMs: number): void;
|
|
25
28
|
/** Set an interval and callback each time it fires */
|
package/wasm/index.js
CHANGED
|
@@ -189,10 +189,13 @@ function readString(ptr, len) {
|
|
|
189
189
|
const memory = new Uint8Array(jsz.memory.buffer);
|
|
190
190
|
return new TextDecoder().decode(memory.slice(ptr, ptr + len));
|
|
191
191
|
}
|
|
192
|
+
function writeBytes(ptr, data) {
|
|
193
|
+
const memory = new Uint8Array(jsz.memory.buffer);
|
|
194
|
+
memory.set(data, ptr);
|
|
195
|
+
}
|
|
192
196
|
|
|
193
197
|
class ZxBridge {
|
|
194
198
|
#exports;
|
|
195
|
-
#nextCallbackId = BigInt(1);
|
|
196
199
|
#intervals = new Map;
|
|
197
200
|
constructor(exports) {
|
|
198
201
|
this.#exports = exports;
|
|
@@ -200,8 +203,8 @@ class ZxBridge {
|
|
|
200
203
|
get #handler() {
|
|
201
204
|
return this.#exports.__zx_cb;
|
|
202
205
|
}
|
|
203
|
-
#
|
|
204
|
-
return this.#
|
|
206
|
+
get #fetchCompleteHandler() {
|
|
207
|
+
return this.#exports.__zx_fetch_complete;
|
|
205
208
|
}
|
|
206
209
|
#invoke(type, id, data) {
|
|
207
210
|
const handler = this.#handler;
|
|
@@ -212,14 +215,62 @@ class ZxBridge {
|
|
|
212
215
|
const dataRef = storeValueGetRef(data);
|
|
213
216
|
handler(type, id, dataRef);
|
|
214
217
|
}
|
|
215
|
-
|
|
218
|
+
fetchAsync(urlPtr, urlLen, methodPtr, methodLen, headersPtr, headersLen, bodyPtr, bodyLen, timeoutMs, fetchId) {
|
|
216
219
|
const url = readString(urlPtr, urlLen);
|
|
217
|
-
|
|
218
|
-
|
|
220
|
+
const method = methodLen > 0 ? readString(methodPtr, methodLen) : "GET";
|
|
221
|
+
const headersJson = headersLen > 0 ? readString(headersPtr, headersLen) : "{}";
|
|
222
|
+
const body = bodyLen > 0 ? readString(bodyPtr, bodyLen) : undefined;
|
|
223
|
+
let headers = {};
|
|
224
|
+
try {
|
|
225
|
+
headers = JSON.parse(headersJson);
|
|
226
|
+
} catch {
|
|
227
|
+
for (const line of headersJson.split(`
|
|
228
|
+
`)) {
|
|
229
|
+
const colonIdx = line.indexOf(":");
|
|
230
|
+
if (colonIdx > 0) {
|
|
231
|
+
headers[line.slice(0, colonIdx)] = line.slice(colonIdx + 1);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const controller = new AbortController;
|
|
236
|
+
const timeout = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : null;
|
|
237
|
+
const fetchOptions = {
|
|
238
|
+
method,
|
|
239
|
+
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
240
|
+
body: method !== "GET" && method !== "HEAD" ? body : undefined,
|
|
241
|
+
signal: controller.signal
|
|
242
|
+
};
|
|
243
|
+
fetch(url, fetchOptions).then(async (response) => {
|
|
244
|
+
if (timeout)
|
|
245
|
+
clearTimeout(timeout);
|
|
246
|
+
const text = await response.text();
|
|
247
|
+
this.#notifyFetchComplete(fetchId, response.status, text, false);
|
|
219
248
|
}).catch((error) => {
|
|
220
|
-
|
|
249
|
+
if (timeout)
|
|
250
|
+
clearTimeout(timeout);
|
|
251
|
+
const isAbort = error.name === "AbortError";
|
|
252
|
+
const errorMsg = isAbort ? "Request timeout" : error.message ?? "Fetch failed";
|
|
253
|
+
this.#notifyFetchComplete(fetchId, 0, errorMsg, true);
|
|
221
254
|
});
|
|
222
255
|
}
|
|
256
|
+
#notifyFetchComplete(fetchId, statusCode, body, isError) {
|
|
257
|
+
const handler = this.#fetchCompleteHandler;
|
|
258
|
+
if (!handler) {
|
|
259
|
+
console.warn("__zx_fetch_complete not exported from WASM");
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const encoded = new TextEncoder().encode(body);
|
|
263
|
+
const allocFn = this.#exports.__zx_alloc;
|
|
264
|
+
let ptr = 0;
|
|
265
|
+
if (allocFn) {
|
|
266
|
+
ptr = allocFn(encoded.length);
|
|
267
|
+
} else {
|
|
268
|
+
const heapBase = this.#exports.__heap_base?.value ?? 65536;
|
|
269
|
+
ptr = heapBase + Number(fetchId % BigInt(256)) * 65536;
|
|
270
|
+
}
|
|
271
|
+
writeBytes(ptr, encoded);
|
|
272
|
+
handler(fetchId, statusCode, ptr, encoded.length, isError ? 1 : 0);
|
|
273
|
+
}
|
|
223
274
|
setTimeout(callbackId, delayMs) {
|
|
224
275
|
setTimeout(() => {
|
|
225
276
|
this.#invoke(CallbackType.Timeout, callbackId, null);
|
|
@@ -248,8 +299,8 @@ class ZxBridge {
|
|
|
248
299
|
return {
|
|
249
300
|
...jsz.importObject(),
|
|
250
301
|
__zx: {
|
|
251
|
-
|
|
252
|
-
bridgeRef.current?.
|
|
302
|
+
_fetchAsync: (urlPtr, urlLen, methodPtr, methodLen, headersPtr, headersLen, bodyPtr, bodyLen, timeoutMs, fetchId) => {
|
|
303
|
+
bridgeRef.current?.fetchAsync(urlPtr, urlLen, methodPtr, methodLen, headersPtr, headersLen, bodyPtr, bodyLen, timeoutMs, fetchId);
|
|
253
304
|
},
|
|
254
305
|
_setTimeout: (callbackId, delayMs) => {
|
|
255
306
|
bridgeRef.current?.setTimeout(callbackId, delayMs);
|
package/wasm/init.js
CHANGED
|
@@ -189,10 +189,13 @@ function readString(ptr, len) {
|
|
|
189
189
|
const memory = new Uint8Array(jsz.memory.buffer);
|
|
190
190
|
return new TextDecoder().decode(memory.slice(ptr, ptr + len));
|
|
191
191
|
}
|
|
192
|
+
function writeBytes(ptr, data) {
|
|
193
|
+
const memory = new Uint8Array(jsz.memory.buffer);
|
|
194
|
+
memory.set(data, ptr);
|
|
195
|
+
}
|
|
192
196
|
|
|
193
197
|
class ZxBridge {
|
|
194
198
|
#exports;
|
|
195
|
-
#nextCallbackId = BigInt(1);
|
|
196
199
|
#intervals = new Map;
|
|
197
200
|
constructor(exports) {
|
|
198
201
|
this.#exports = exports;
|
|
@@ -200,8 +203,8 @@ class ZxBridge {
|
|
|
200
203
|
get #handler() {
|
|
201
204
|
return this.#exports.__zx_cb;
|
|
202
205
|
}
|
|
203
|
-
#
|
|
204
|
-
return this.#
|
|
206
|
+
get #fetchCompleteHandler() {
|
|
207
|
+
return this.#exports.__zx_fetch_complete;
|
|
205
208
|
}
|
|
206
209
|
#invoke(type, id, data) {
|
|
207
210
|
const handler = this.#handler;
|
|
@@ -212,14 +215,62 @@ class ZxBridge {
|
|
|
212
215
|
const dataRef = storeValueGetRef(data);
|
|
213
216
|
handler(type, id, dataRef);
|
|
214
217
|
}
|
|
215
|
-
|
|
218
|
+
fetchAsync(urlPtr, urlLen, methodPtr, methodLen, headersPtr, headersLen, bodyPtr, bodyLen, timeoutMs, fetchId) {
|
|
216
219
|
const url = readString(urlPtr, urlLen);
|
|
217
|
-
|
|
218
|
-
|
|
220
|
+
const method = methodLen > 0 ? readString(methodPtr, methodLen) : "GET";
|
|
221
|
+
const headersJson = headersLen > 0 ? readString(headersPtr, headersLen) : "{}";
|
|
222
|
+
const body = bodyLen > 0 ? readString(bodyPtr, bodyLen) : undefined;
|
|
223
|
+
let headers = {};
|
|
224
|
+
try {
|
|
225
|
+
headers = JSON.parse(headersJson);
|
|
226
|
+
} catch {
|
|
227
|
+
for (const line of headersJson.split(`
|
|
228
|
+
`)) {
|
|
229
|
+
const colonIdx = line.indexOf(":");
|
|
230
|
+
if (colonIdx > 0) {
|
|
231
|
+
headers[line.slice(0, colonIdx)] = line.slice(colonIdx + 1);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const controller = new AbortController;
|
|
236
|
+
const timeout = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : null;
|
|
237
|
+
const fetchOptions = {
|
|
238
|
+
method,
|
|
239
|
+
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
240
|
+
body: method !== "GET" && method !== "HEAD" ? body : undefined,
|
|
241
|
+
signal: controller.signal
|
|
242
|
+
};
|
|
243
|
+
fetch(url, fetchOptions).then(async (response) => {
|
|
244
|
+
if (timeout)
|
|
245
|
+
clearTimeout(timeout);
|
|
246
|
+
const text = await response.text();
|
|
247
|
+
this.#notifyFetchComplete(fetchId, response.status, text, false);
|
|
219
248
|
}).catch((error) => {
|
|
220
|
-
|
|
249
|
+
if (timeout)
|
|
250
|
+
clearTimeout(timeout);
|
|
251
|
+
const isAbort = error.name === "AbortError";
|
|
252
|
+
const errorMsg = isAbort ? "Request timeout" : error.message ?? "Fetch failed";
|
|
253
|
+
this.#notifyFetchComplete(fetchId, 0, errorMsg, true);
|
|
221
254
|
});
|
|
222
255
|
}
|
|
256
|
+
#notifyFetchComplete(fetchId, statusCode, body, isError) {
|
|
257
|
+
const handler = this.#fetchCompleteHandler;
|
|
258
|
+
if (!handler) {
|
|
259
|
+
console.warn("__zx_fetch_complete not exported from WASM");
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const encoded = new TextEncoder().encode(body);
|
|
263
|
+
const allocFn = this.#exports.__zx_alloc;
|
|
264
|
+
let ptr = 0;
|
|
265
|
+
if (allocFn) {
|
|
266
|
+
ptr = allocFn(encoded.length);
|
|
267
|
+
} else {
|
|
268
|
+
const heapBase = this.#exports.__heap_base?.value ?? 65536;
|
|
269
|
+
ptr = heapBase + Number(fetchId % BigInt(256)) * 65536;
|
|
270
|
+
}
|
|
271
|
+
writeBytes(ptr, encoded);
|
|
272
|
+
handler(fetchId, statusCode, ptr, encoded.length, isError ? 1 : 0);
|
|
273
|
+
}
|
|
223
274
|
setTimeout(callbackId, delayMs) {
|
|
224
275
|
setTimeout(() => {
|
|
225
276
|
this.#invoke(CallbackType.Timeout, callbackId, null);
|
|
@@ -248,8 +299,8 @@ class ZxBridge {
|
|
|
248
299
|
return {
|
|
249
300
|
...jsz.importObject(),
|
|
250
301
|
__zx: {
|
|
251
|
-
|
|
252
|
-
bridgeRef.current?.
|
|
302
|
+
_fetchAsync: (urlPtr, urlLen, methodPtr, methodLen, headersPtr, headersLen, bodyPtr, bodyLen, timeoutMs, fetchId) => {
|
|
303
|
+
bridgeRef.current?.fetchAsync(urlPtr, urlLen, methodPtr, methodLen, headersPtr, headersLen, bodyPtr, bodyLen, timeoutMs, fetchId);
|
|
253
304
|
},
|
|
254
305
|
_setTimeout: (callbackId, delayMs) => {
|
|
255
306
|
bridgeRef.current?.setTimeout(callbackId, delayMs);
|