refactor: move private fields to _internal struct

This commit is contained in:
Nurul Huda (Apon) 2026-04-18 17:53:38 +06:00
parent 6cd0901d4c
commit 08c462ba35
No known key found for this signature in database
GPG Key ID: 5D3F1DE2855A2F79
52 changed files with 186 additions and 186 deletions

View File

@ -369,7 +369,7 @@ jobs:
gh release create "$TAG" \
--repo "$OWNER/$REPO_NAME" \
--title "$TAG" \
--notes "Release ${TAG} synced from [ziex-dev/ziex@${TAG}](https://github.com/ziex-dev/ziex/releases/tag/${TAG})" \
--notes "Release ${TAG} - synced from [ziex-dev/ziex@${TAG}](https://github.com/ziex-dev/ziex/releases/tag/${TAG})" \
|| echo "Release $TAG may already exist for $REPO_NAME, skipping..."
cd -

View File

@ -341,7 +341,7 @@ echo -e "\033[2mResults written to: $RESULTS_FILE\033[0m"
# Generate bench.zon from results
ZON_FILE="../site/app/pages/bench.zon"
{
echo "// Auto-generated by bench/bench.sh do not edit"
echo "// Auto-generated by bench/bench.sh - do not edit"
echo ".{"
tail -n +2 "$RESULTS_FILE" | while IFS=',' read -r fw idle peak build_time img_mb bin_mb cold_ms cpu_avg cpu_peak rps p50 p99; do
label=$(get_label "$fw")

View File

@ -18,7 +18,7 @@ pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exclude_lsp = b.option(bool, "exclude-lsp", "Exclude the LSP server to speed up builds") orelse false;
const exclude_core_lang = b.option(bool, "exclude-core-lang", "Exclude core language tools (Ast/Parse/sourcemap) only needed by CLI") orelse false;
const exclude_core_lang = b.option(bool, "exclude-core-lang", "Exclude core language tools (Ast/Parse/sourcemap) - only needed by CLI") orelse false;
const exclude_db = b.option(bool, "exclude-db", "Exclude database adapter to speed up builds") orelse false;
const is_client = b.option(bool, "is-client", "Building for the browser (client)") orelse false;
const is_edge = b.option(bool, "is-edge", "Building for a WASI-based edge runtime") orelse false;
@ -48,12 +48,8 @@ pub fn build(b: *std.Build) !void {
const cachez_dep = b.dependency("cachez", .{ .target = target, .optimize = optimize });
const adapters_dep = b.dependency("adapters", .{ .target = target, .optimize = optimize });
// --- Sub-modules (cached independently) --- //
// Style module (35K lines, zero internal deps cached after first compile)
// --- Features Module --- //
const zx_style_mod = b.addModule("zx_style", .{ .root_source_file = b.path("src/style/root.zig"), .target = target, .optimize = optimize });
// Core language module (Ast, Parse, sourcemap only compiled when referenced)
const zx_core_lang_mod = b.addModule("zx_core_lang", .{ .root_source_file = b.path("src/core/root.zig"), .target = target, .optimize = optimize });
zx_core_lang_mod.addImport("tree_sitter", tree_sitter_dep.module("tree_sitter"));
zx_core_lang_mod.addImport("tree_sitter_zx", tree_sitter_zx_dep.module("tree_sitter_zx"));

View File

@ -73,7 +73,7 @@ export function activateMultilineStringDecorator(
),
);
// -- Copy command strip \\ prefixes when copying from multiline string -- //
// -- Copy command - strip \\ prefixes when copying from multiline string -- //
context.subscriptions.push(
vscode.commands.registerCommand(
"zx.copyFromMultilineString",
@ -81,7 +81,7 @@ export function activateMultilineStringDecorator(
),
);
// -- Paste command auto-prefix pasted text with \\ -- //
// -- Paste command - auto-prefix pasted text with \\ -- //
context.subscriptions.push(
vscode.commands.registerCommand(
"zx.pasteInMultilineString",
@ -89,7 +89,7 @@ export function activateMultilineStringDecorator(
),
);
// -- Folding provider collapse multiline string blocks -- //
// -- Folding provider - collapse multiline string blocks -- //
context.subscriptions.push(
vscode.languages.registerFoldingRangeProvider(
[{ language: "zx" }, { language: "zig" }],

View File

@ -267,7 +267,7 @@
]
},
"zig-multiline-string": {
"comment": "Zig multiline string literal lines (\\\\...) must match before HTML tags",
"comment": "Zig multiline string literal lines (\\\\...) - must match before HTML tags",
"name": "string.quoted.other.multiline.zig",
"match": "^\\s*\\\\\\\\.*$"
},

View File

@ -57,7 +57,7 @@ echo "==> Publishing @ziex/cli* to local registry..."
cd "$VERDACCIO_DIR"
npm publish --workspaces --access public --tag dev --registry "$REGISTRY" 2>&1
# Build and publish ziex to local registry (skip on Windows bun build crashes)
# Build and publish ziex to local registry (skip on Windows - bun build crashes)
if [ "$IS_WINDOWS" = false ]; then
echo "==> Building and publishing ziex to local registry..."
cd "$SCRIPT_DIR/ziex"
@ -77,7 +77,7 @@ mkdir -p "$SCRIPT_DIR/_check_tmp"
cd "$SCRIPT_DIR/_check_tmp"
export npm_config_cache="$SCRIPT_DIR/_check_tmp/.npm-cache"
# Test runner writes PASS/FAIL to $RESULTS_DIR so tests can run in parallel
# Test runner - writes PASS/FAIL to $RESULTS_DIR so tests can run in parallel
check() {
local name="$1" cmd="$2" expected="$3"
local safe_name="${name//[\/[:space:]]/_}"
@ -88,7 +88,7 @@ check() {
echo " PASS: $name"
else
echo "FAIL" > "$RESULTS_DIR/$safe_name"
echo " FAIL: $name expected '$expected', got: $output"
echo " FAIL: $name - expected '$expected', got: $output"
fi
}
@ -106,7 +106,7 @@ check "npx @ziex/cli version" \
"$VERSION" &
pids+=($!)
# ziex via npx (skip on Windows not built)
# ziex via npx (skip on Windows - not built)
if [ "$IS_WINDOWS" = false ]; then
check "npx ziex version" \
"npx --yes --registry '$REGISTRY' ziex@dev version" \

View File

@ -64,7 +64,7 @@ async function main() {
process.exit(0);
}
// By now the user has been typing give the fetch a short grace period
// By now the user has been typing - give the fetch a short grace period
// in case it hasn't resolved yet, then fall back to the static list.
const dynamicTemplates = await Promise.race([
templatesFetch,
@ -113,7 +113,7 @@ async function main() {
s.stop('Replacement failed', 1);
}
// 4. Fix Zig fingerprint the fingerprint is a u64 packed struct:
// 4. Fix Zig fingerprint - the fingerprint is a u64 packed struct:
// lower 32 bits = random id, upper 32 bits = CRC-32 of the package name.
s.start('Updating package fingerprint...');
try {

View File

@ -25,7 +25,7 @@ describe("fingerprint", () => {
const zigFp = zigGenerate(name);
const nodeFp = generateZigFingerprint(name);
// Extract checksum (upper 32 bits) from both the random id will differ
// Extract checksum (upper 32 bits) from both - the random id will differ
const zigChecksum = BigInt(zigFp) >> 32n;
const nodeChecksum = BigInt(nodeFp) >> 32n;
expect(nodeChecksum).toBe(zigChecksum);

View File

@ -63,7 +63,7 @@ async function main() {
process.exit(0);
}
// By now the user has been typing give the fetch a short grace period
// By now the user has been typing - give the fetch a short grace period
// in case it hasn't resolved yet, then fall back to the static list.
const dynamicTemplates = await Promise.race([
templatesFetch,
@ -112,7 +112,7 @@ async function main() {
s.stop('Replacement failed', 1);
}
// 4. Fix Zig fingerprint Zig reports the correct value when the package
// 4. Fix Zig fingerprint - Zig reports the correct value when the package
// name changes; we capture it and patch build.zig.zon automatically.
s.start('Updating package fingerprint...');
try {

View File

@ -36,7 +36,7 @@ fn innerInitSingle(b: *std.Build, build_item: Build) !Output {
var arena = std.heap.ArenaAllocator.init(b.allocator);
const alloc = arena.allocator();
// Create config for single build (outdir is NOT in config injected by exe from CLI arg)
// Create config for single build (outdir is NOT in config - injected by exe from CLI arg)
var obj = std.json.ObjectMap.init(alloc);
try obj.put("name", .{ .string = build_item.name orelse "bun build" });
const config_val = try build_item.config.toJsonValue(b, alloc);

View File

@ -23,7 +23,7 @@ sourcemap: ?Sourcemap = null,
/// Minify the output (all sub-options at once)
minify: ?bool = null,
/// External packages not bundled
/// External packages - not bundled
external: []const []const u8 = &.{},
/// Public path prefix for asset URLs
@ -40,7 +40,7 @@ splitting: ?bool = null,
pub fn toJsonValue(self: BunBuildConfig, b: *std.Build, arena: std.mem.Allocator) !std.json.Value {
var obj = std.json.ObjectMap.init(arena);
// entrypoints required array
// entrypoints - required array
var eps = std.json.Array.init(arena);
for (self.entrypoints) |lp| {
try eps.append(.{ .string = lp.getPath(b) });

View File

@ -41,7 +41,7 @@ fn innerInitSingle(b: *std.Build, build_item: Build) !Output {
var arena = std.heap.ArenaAllocator.init(b.allocator);
const alloc = arena.allocator();
// Create config for single build (output is NOT in config injected by exe from CLI arg)
// Create config for single build (output is NOT in config - injected by exe from CLI arg)
var obj = std.json.ObjectMap.init(alloc);
try obj.put("name", .{ .string = build_item.name orelse "tailwindcss" });
const config_val = try build_item.config.toJsonValue(b, alloc);

View File

@ -6,11 +6,11 @@ import type { WASI } from "./wasi";
/**
* Anything that can be resolved to a `WebAssembly.Module`:
* - `WebAssembly.Module` already compiled (Cloudflare Workers, wrangler)
* - `ArrayBuffer` / `ArrayBufferView` raw WASM bytes
* - `Response` a fetch() response whose body is the WASM binary
* - `string` an HTTP(S) URL or an absolute file path (Bun)
* - `URL` a URL object
* - `WebAssembly.Module` - already compiled (Cloudflare Workers, wrangler)
* - `ArrayBuffer` / `ArrayBufferView` - raw WASM bytes
* - `Response` - a fetch() response whose body is the WASM binary
* - `string` - an HTTP(S) URL or an absolute file path (Bun)
* - `URL` - a URL object
*/
export type WasmInput =
| WebAssembly.Module
@ -22,7 +22,7 @@ export type WasmInput =
/**
* Resolve any supported WASM input to a compiled `WebAssembly.Module`.
* The result is NOT cached here cache it at the call site if needed.
* The result is NOT cached here - cache it at the call site if needed.
*/
export async function resolveModule(input: WasmInput): Promise<WebAssembly.Module> {
if (typeof input === 'string') {
@ -49,7 +49,7 @@ export async function resolveModule(input: WasmInput): Promise<WebAssembly.Modul
}
// Anything else is assumed to be an already-compiled WebAssembly.Module.
// Intentionally avoid `instanceof WebAssembly.Module` it fails across
// Intentionally avoid `instanceof WebAssembly.Module` - it fails across
// different VM contexts (e.g. Vercel's edge runtime simulator).
return input as unknown as WebAssembly.Module;
}
@ -62,7 +62,7 @@ type DOKey<Env> = { [K in keyof Env]: Env[K] extends DurableObjectNamespace ? K
type DBKey<Env> = { [K in keyof Env]: Env[K] extends D1Database ? K : never }[keyof Env];
type ZiexOptions<Env> = {
/** WASM module accepts any {@link WasmInput}. Resolved and cached on first request. */
/** WASM module - accepts any {@link WasmInput}. Resolved and cached on first request. */
module: WasmInput;
/** Optional pre-configured WASI instance. */
wasi?: WASI;
@ -71,8 +71,8 @@ type ZiexOptions<Env> = {
/**
* KV namespace bindings. Two forms are supported:
*
* - **Env key**: a single key from `Env` whose value is a `KVNamespace` used as the `"default"` binding.
* - **Name map**: `{ [bindingName]: envKey }` map namespace names to env keys.
* - **Env key**: a single key from `Env` whose value is a `KVNamespace` - used as the `"default"` binding.
* - **Name map**: `{ [bindingName]: envKey }` - map namespace names to env keys.
*
* @example Single env key (becomes the "default" binding)
* ```ts
@ -104,7 +104,7 @@ type ZiexOptions<Env> = {
};
/**
* Main Ziex application class. Mirrors the Hono API style construct once,
* Main Ziex application class. Mirrors the Hono API style - construct once,
* export as default, and the runtime calls `fetch` for you.
*
* Works on Cloudflare Workers, Bun, and Vercel Edge out of the box.
@ -156,14 +156,14 @@ export class Ziex<Env = Record<string, unknown>> {
const { kv } = this.options;
if (kv === undefined) return undefined;
if (typeof kv === 'object' && kv !== null) {
// { [name]: envKey } map of namespace names to env keys
// { [name]: envKey } - map of namespace names to env keys
const result: Record<string, KVNamespace> = {};
for (const [name, key] of Object.entries(kv)) {
result[name] = env[key as keyof Env] as unknown as KVNamespace;
}
return result;
}
// Single env key becomes the "default" binding
// Single env key - becomes the "default" binding
return { default: env[kv as keyof Env] as unknown as KVNamespace };
}

View File

@ -68,7 +68,7 @@ export function buildWsImports(
ws.recvResolve = (bytes) => resolve(deliver(bytes));
});
}) : (_buf_ptr: number, _buf_max: number): number => -1,
// Pub/sub delegates to optional callbacks (real in DO, no-ops otherwise)
// Pub/sub - delegates to optional callbacks (real in DO, no-ops otherwise)
ws_subscribe: (ptr: number, len: number): void => { ws.subscribe?.(readStr(ptr, len)); },
ws_unsubscribe: (ptr: number, len: number): void => { ws.unsubscribe?.(readStr(ptr, len)); },
ws_publish: (topic_ptr: number, topic_len: number, data_ptr: number, data_len: number): number => {
@ -147,7 +147,7 @@ function executeWasm(
return Promise.resolve();
}
// NOTE: no await start() runs synchronously until the first Suspending
// NOTE: no await - start() runs synchronously until the first Suspending
// call, writing __EDGE_META__ to stderr and the HTML shell to stdout.
// The runtime streams stdout to the client while WASM is suspended.
const start = (WebAssembly as any).promising(instance.exports._start as Function);
@ -218,9 +218,9 @@ export async function run({
env?: unknown;
ctx?: { waitUntil(promise: Promise<unknown>): void };
module: WebAssembly.Module;
/** KV namespace bindings `{ default: env.KV, otherName: env.OTHER_KV }` */
/** KV namespace bindings - `{ default: env.KV, otherName: env.OTHER_KV }` */
kv?: Record<string, KVNamespace>;
/** D1 bindings `{ default: env.DB, analytics: env.ANALYTICS_DB }` */
/** D1 bindings - `{ default: env.DB, analytics: env.ANALYTICS_DB }` */
db?: Record<string, D1Database>;
imports?: (mem: () => WebAssembly.Memory) => Record<string, Record<string, unknown>>;
wasi?: WASI;
@ -304,7 +304,7 @@ export async function run({
const earlyMeta = parseEdgeMeta(earlyStderrText);
if (earlyMeta.streaming) {
// Page opted into streaming (zx.PageOptions{ .streaming = true })
// Page opted into streaming (zx.PageOptions{ .streaming = true }) -
// pipe stdout incrementally to the client as WASM yields via sleep_ms.
const { readable, writable } = new TransformStream<Uint8Array, Uint8Array>();
streamWriter = writable.getWriter();

View File

@ -20,7 +20,7 @@ type FetchApp = { fetch(req: Request, env?: unknown, ctx?: unknown): Promise<Res
* Wrap a Ziex app as a Vercel Edge Function handler.
*
* Returns a standard `(req: Request) => Promise<Response>` function.
* Vercel's edge runtime calls it directly just export it as default.
* Vercel's edge runtime calls it directly - just export it as default.
*/
export function handle(app: FetchApp): (req: Request) => Promise<Response> {
return (req) => app.fetch(req);

View File

@ -43,7 +43,7 @@ export function createWasiImports({
const encodedArgs = argStrings.map((a) => encoder.encode(a + "\0"));
const argBufSize = encodedArgs.reduce((s, a) => s + a.length, 0);
// WASM memory set after instantiation
// WASM memory - set after instantiation
let wasmMemory: WebAssembly.Memory = null!;
const setMemory = (m: WebAssembly.Memory) => { wasmMemory = m; };
@ -142,7 +142,7 @@ export function createWasiImports({
return 0;
},
fd_prestat_get(_fd: number, _bufptr: number): number {
return 8; // WASI_EBADF no preopened directories
return 8; // WASI_EBADF - no preopened directories
},
fd_prestat_dir_name(_fd: number, _path: number, _path_len: number): number {
return 8; // WASI_EBADF
@ -205,7 +205,7 @@ export function createWasiImports({
v().setUint32(bufused_ptr, 0, true);
return 76;
},
// Stub for poll_oneoff no blocking I/O in edge runtimes.
// Stub for poll_oneoff - no blocking I/O in edge runtimes.
poll_oneoff(_in: number, _out: number, _nsubscriptions: number, nevents_ptr: number): number {
v().setUint32(nevents_ptr, 0, true);
return 0;

View File

@ -1,7 +1,7 @@
import { ZigJS } from "../../../../vendor/jsz/js/src";
/**
* Core WASM bridge environment-agnostic (browser + edge).
* Core WASM bridge - environment-agnostic (browser + edge).
* Contains no references to browser globals (document, window, WebSocket, HTMLFormElement).
*/
export const CallbackType = {
@ -41,7 +41,7 @@ export function storeValueGetRef(val: any): bigint {
return tempRefView.getBigUint64(0, true);
}
/** Shared encoder/decoder avoids allocating new instances on every call. */
/** Shared encoder/decoder - avoids allocating new instances on every call. */
export const textDecoder = new TextDecoder();
export const textEncoder = new TextEncoder();
@ -103,7 +103,7 @@ export function invokeWasmExport<F extends (...args: any[]) => any>(
}
/**
* Core ZX Bridge works in both browser and edge runtimes.
* Core ZX Bridge - works in both browser and edge runtimes.
* Contains fetch, timers, and logging. No DOM or browser-WebSocket references.
*
* Extend this class in browser environments to add DOM and WebSocket support.

View File

@ -33,7 +33,7 @@ import type {
} from "./core";
/**
* Browser ZX Bridge extends ZxBridgeCore with DOM, WebSocket, and form-action support.
* Browser ZX Bridge - extends ZxBridgeCore with DOM, WebSocket, and form-action support.
* Import this from environments that have access to browser globals.
* For edge runtimes, import ZxBridgeCore from ./core instead.
*/

View File

@ -1,9 +1,9 @@
/**
* WASI/edge WASM bridge zero dependencies on ZigJS (jsz) or browser globals.
* WASI/edge WASM bridge - zero dependencies on ZigJS (jsz) or browser globals.
*
* Import this (and only this) from edge/server runtimes. It provides the
* minimal __zx import namespace needed by server-side WASM: log, fetch, and
* timers. The jsz importObject is intentionally omitted the server binary
* timers. The jsz importObject is intentionally omitted - the server binary
* does not use jsz value-passing.
*/
@ -19,7 +19,7 @@ export class ZxWasiBridge {
readonly #cb: ((type: number, id: bigint, data: bigint) => void) | undefined;
readonly #intervals: Map<bigint, ReturnType<typeof setInterval>> = new Map();
// Cached memory view invalidated when the WASM buffer grows.
// Cached memory view - invalidated when the WASM buffer grows.
#memView: Uint8Array | null = null;
#memBuf: ArrayBufferLike | null = null;
@ -128,7 +128,7 @@ export class ZxWasiBridge {
* Create the WASI import object for WASM instantiation.
*
* Returns only the `__zx` namespace (log, fetch, timers).
* Does NOT include jsz.importObject() the server binary does not use jsz.
* Does NOT include jsz.importObject() - the server binary does not use jsz.
*/
static createImportObject(bridgeRef: { current: ZxWasiBridge | null }): { __zx: Record<string, unknown> } {
return {

View File

@ -62,7 +62,7 @@
cursor: pointer;
}
/* Playground IDE Styles re-uses home.css design tokens */
/* Playground IDE Styles - re-uses home.css design tokens */
:root {
--zx-primary: #4ade80;
@ -914,7 +914,7 @@ body {
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolygon points='5 3 19 12 5 21 5 3'/%3E%3C/svg%3E");
}
/* Done / error / cached / prefetched override icon */
/* Done / error / cached / prefetched - override icon */
.pg-status-step--done .pg-status-step-icon {
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") !important;
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") !important;

View File

@ -1,4 +1,4 @@
// Auto-generated by bench/bench.sh do not edit
// Auto-generated by bench/bench.sh - do not edit
.{
.{
.id = "ziex",

View File

@ -101,7 +101,7 @@ fn SiteStats(allocator: zx.Allocator, props: struct { loading: bool }) zx.Compon
/// Pause execution for `duration_ms` milliseconds.
/// On native (dev server): uses std.Thread.sleep.
/// On WASM (edge/Cloudflare): calls a Suspending JS import so the Worker can
/// flush partial response chunks to the client before resuming enabling
/// flush partial response chunks to the client before resuming - enabling
/// true streaming with JSPI.
fn wait(duration_ms: u64) void {
if (comptime builtin.cpu.arch == .wasm32) {
@ -111,7 +111,7 @@ fn wait(duration_ms: u64) void {
}
}
/// Suspending WASM import implemented as `setTimeout` on the JS side.
/// Suspending WASM import - implemented as `setTimeout` on the JS side.
/// Only linked in WASM builds; dead-code-eliminated in native builds.
extern "__zx_sys" fn sleep_ms(ms: u32) void;

View File

@ -391,11 +391,11 @@ fn DisplayingData(allocator: zx.Allocator, props: DisplayingDataProps) zx.Compon
<h2>Displaying data</h2>
<p>ZX lets you embed dynamic content using curly braces <code>{"{expression}"}</code>. Expressions are automatically formatted based on their type:</p>
<ul>
<li><strong>Strings</strong> displayed as text (HTML-escaped for safety)</li>
<li><strong>Numbers</strong> formatted as decimal</li>
<li><strong>Booleans</strong> displayed as "true" or "false"</li>
<li><strong>Enums</strong> displayed as the tag name</li>
<li><strong>Optionals</strong> unwrapped if present, otherwise renders nothing</li>
<li><strong>Strings</strong> - displayed as text (HTML-escaped for safety)</li>
<li><strong>Numbers</strong> - formatted as decimal</li>
<li><strong>Booleans</strong> - displayed as "true" or "false"</li>
<li><strong>Enums</strong> - displayed as the tag name</li>
<li><strong>Optionals</strong> - unwrapped if present, otherwise renders nothing</li>
</ul>
<ExampleBlock id="text-expr" zx_code={props.zx_text} zig_code={props.zig_text} html_code={props.html_text} filename="user_greeting.zx" />
<p>Different types are automatically handled:</p>
@ -558,7 +558,7 @@ fn ChildrenProps(allocator: zx.Allocator, props: ChildrenPropsProps) zx.Componen
return (
<section id="children" class="section" @allocator={allocator}>
<h2>Passing children to components</h2>
<p>Components can accept <em>children</em> content passed between opening and closing tags. Add a <code>children: zx.Component</code> field to your props:</p>
<p>Components can accept <em>children</em> - content passed between opening and closing tags. Add a <code>children: zx.Component</code> field to your props:</p>
<ExampleBlock id="children" zx_code={props.zx_code} zig_code={props.zig_code} html_code={props.html_code} filename="card_demo.zx" />
<p>This pattern is useful for creating wrapper components like cards, modals, or layout containers. See the <a href="/reference#children-props">Children Props documentation</a> for more details.</p>
</section>
@ -603,10 +603,10 @@ fn PagesAndLayouts(allocator: zx.Allocator, props: PagesAndLayoutsProps) zx.Comp
<h3>PageContext and LayoutContext</h3>
<p>Both contexts provide:</p>
<ul>
<li><code>request</code> HTTP request with headers, query params, body</li>
<li><code>response</code> HTTP response for setting headers</li>
<li><code>arena</code> Request-scoped allocator (recommended)</li>
<li><code>allocator</code> Global allocator for persistent allocations</li>
<li><code>request</code> - HTTP request with headers, query params, body</li>
<li><code>response</code> - HTTP response for setting headers</li>
<li><code>arena</code> - Request-scoped allocator (recommended)</li>
<li><code>allocator</code> - Global allocator for persistent allocations</li>
</ul>
<div class="callout tip">
<strong>Best practice:</strong>
@ -651,8 +651,8 @@ fn Plugins(allocator: zx.Allocator) zx.Component {
<h3>Builtin Plugins</h3>
<p>ZX ships with the following builtin plugins available via <code>zx.plugins</code>:</p>
<ul>
<li><strong><code>zx.plugins.tailwind</code></strong> Compile Tailwind CSS styles</li>
<li><strong><code>zx.plugins.esbuild</code></strong> Bundle TypeScript/JavaScript for client-side code</li>
<li><strong><code>zx.plugins.tailwind</code></strong> - Compile Tailwind CSS styles</li>
<li><strong><code>zx.plugins.esbuild</code></strong> - Bundle TypeScript/JavaScript for client-side code</li>
</ul>
<h3>Tailwind CSS Plugin</h3>

View File

@ -835,7 +835,7 @@ outputsRun.addEventListener('click', async () => {
ph.appendChild(phLabel);
viewport.appendChild(ph);
// If a background prefetch is in flight, wait result will be in cache
// If a background prefetch is in flight, wait - result will be in cache
if (prefetchPromise) await prefetchPromise;
const compiled = await runTranspileAndBuild(true);

View File

@ -451,8 +451,8 @@ fn PluginsSection(allocator: zx.Allocator) zx.Component {
<h3 id="builtin-plugins">Builtin Plugins</h3>
<p>ZX ships with the following builtin plugins available via <code>zx.plugins</code>:</p>
<ul>
<li><strong><code>zx.plugins.tailwind</code></strong> Compile Tailwind CSS styles</li>
<li><strong><code>zx.plugins.esbuild</code></strong> Bundle TypeScript/JavaScript for client-side code</li>
<li><strong><code>zx.plugins.tailwind</code></strong> - Compile Tailwind CSS styles</li>
<li><strong><code>zx.plugins.esbuild</code></strong> - Bundle TypeScript/JavaScript for client-side code</li>
</ul>
<h3 id="tailwind-plugin">Tailwind CSS Plugin</h3>
@ -619,8 +619,8 @@ fn PluginsSection(allocator: zx.Allocator) zx.Component {
<h4>Plugin Lifecycle</h4>
<ul>
<li><strong><code>before_transpile</code></strong> Runs before ZX transpiles <code>.zx</code> files to Zig. Useful for preprocessing source files.</li>
<li><strong><code>after_transpile</code></strong> Runs after ZX transpilation. Useful for post-processing like CSS compilation, JS bundling, or asset optimization.</li>
<li><strong><code>before_transpile</code></strong> - Runs before ZX transpiles <code>.zx</code> files to Zig. Useful for preprocessing source files.</li>
<li><strong><code>after_transpile</code></strong> - Runs after ZX transpilation. Useful for post-processing like CSS compilation, JS bundling, or asset optimization.</li>
</ul>
<h4>Using <code>{"{outdir}"}</code></h4>
@ -650,9 +650,9 @@ fn CachingSection(allocator: zx.Allocator) zx.Component {
<h3 id="caching-overview">Overview</h3>
<p>Caching can be applied at three levels:</p>
<ul>
<li><strong>Component caching</strong> Cache individual components using the <code>@caching</code> attribute</li>
<li><strong>Page caching</strong> Cache entire pages using <code>PageOptions.caching</code></li>
<li><strong>Layout caching</strong> Cache layouts using <code>LayoutOptions.caching</code></li>
<li><strong>Component caching</strong> - Cache individual components using the <code>@caching</code> attribute</li>
<li><strong>Page caching</strong> - Cache entire pages using <code>PageOptions.caching</code></li>
<li><strong>Layout caching</strong> - Cache layouts using <code>LayoutOptions.caching</code></li>
</ul>
<h3 id="caching-components">Component Caching</h3>
@ -716,18 +716,18 @@ fn CachingSection(allocator: zx.Allocator) zx.Component {
<CodeBlock code={cache_invalidation_example} />
<p><strong>Available functions:</strong></p>
<ul>
<li><code>zx.cache.del(key)</code> Delete a specific cache entry by exact key. Returns <code>true</code> if deleted.</li>
<li><code>zx.cache.delPrefix(prefix)</code> Delete all entries matching a prefix. Returns the count of deleted entries.</li>
<li><code>zx.cache.get(key)</code> Get cached HTML by key. Returns <code>?[]const u8</code>.</li>
<li><code>zx.cache.del(key)</code> - Delete a specific cache entry by exact key. Returns <code>true</code> if deleted.</li>
<li><code>zx.cache.delPrefix(prefix)</code> - Delete all entries matching a prefix. Returns the count of deleted entries.</li>
<li><code>zx.cache.get(key)</code> - Get cached HTML by key. Returns <code>?[]const u8</code>.</li>
</ul>
<h3 id="caching-best-practices">Best Practices</h3>
<ul>
<li><strong>Cache static content aggressively</strong> Headers, footers, navigation, and other rarely-changing content benefit most from caching.</li>
<li><strong>Use custom keys for related content</strong> Group related cache entries with a common prefix for easy bulk invalidation.</li>
<li><strong>Be careful with user-specific content</strong> Don't cache components that display user-specific data without including user identity in the cache key.</li>
<li><strong>Start with short TTLs</strong> Begin with shorter cache durations and increase as you verify correctness.</li>
<li><strong>Invalidate on data changes</strong> Call <code>zx.cache.del()</code> or <code>zx.cache.delPrefix()</code> when underlying data changes.</li>
<li><strong>Cache static content aggressively</strong> - Headers, footers, navigation, and other rarely-changing content benefit most from caching.</li>
<li><strong>Use custom keys for related content</strong> - Group related cache entries with a common prefix for easy bulk invalidation.</li>
<li><strong>Be careful with user-specific content</strong> - Don't cache components that display user-specific data without including user identity in the cache key.</li>
<li><strong>Start with short TTLs</strong> - Begin with shorter cache durations and increase as you verify correctness.</li>
<li><strong>Invalidate on data changes</strong> - Call <code>zx.cache.del()</code> or <code>zx.cache.delPrefix()</code> when underlying data changes.</li>
</ul>
<div class="callout tip">
@ -926,7 +926,7 @@ fn CLICommand(allocator: zx.Allocator, props: struct { command: CLICommandInfo }
<h4>Arguments</h4>
<ul>
{for (args) |arg| (
<li><code>{arg.name}</code> {arg.description}</li>
<li><code>{arg.name}</code> - {arg.description}</li>
)}
</ul>
</div>
@ -937,7 +937,7 @@ fn CLICommand(allocator: zx.Allocator, props: struct { command: CLICommandInfo }
<h4>Flags</h4>
<ul>
{for (flags) |flag| (
<li><code>{flag.name}</code> {flag.description}</li>
<li><code>{flag.name}</code> - {flag.description}</li>
)}
</ul>
</div>

View File

@ -85,9 +85,7 @@ pub const Component = union(enum) {
// allocator.create() does NOT run field default initializers, so explicitly
// set all fields that ComponentCtx defines with defaults.
ctx.allocator = allocator;
if (@hasField(CtxType, "_component_id")) ctx._component_id = "";
if (@hasField(CtxType, "_id")) ctx._id = 0;
if (@hasField(CtxType, "_state_index")) ctx._state_index = 0;
if (@hasField(CtxType, "_internal")) ctx._internal = .{};
ctx.children = if (@hasField(@TypeOf(props), "children")) props.children else null;
if (@hasField(CtxType, "props")) {
const PropsFieldType = @FieldType(CtxType, "props");
@ -132,8 +130,10 @@ pub const Component = union(enum) {
const CtxType = @typeInfo(FirstPropType).pointer.child;
const ctx_ptr: *CtxType = @ptrCast(@alignCast(@constCast(propsPtr orelse @panic("ctx is null"))));
// Reset slot counters on every call so hooks run in stable order.
if (@hasField(CtxType, "_state_index")) ctx_ptr._state_index = 0;
if (@hasField(CtxType, "_handler_index")) ctx_ptr._handler_index = 0;
if (@hasField(CtxType, "_internal")) {
ctx_ptr._internal.state_idx = 0;
ctx_ptr._internal.handler_idx = 0;
}
return normalize(func(ctx_ptr));
}
if (first_is_allocator and param_count == 1) {
@ -152,8 +152,10 @@ pub const Component = union(enum) {
if (!first_is_ctx_ptr) return;
const CtxType = @typeInfo(FirstPropType).pointer.child;
const ctx_ptr: *CtxType = @ptrCast(@alignCast(@constCast(propsPtr orelse return)));
if (@hasField(CtxType, "_component_id")) ctx_ptr._component_id = component_id;
if (@hasField(CtxType, "_id")) ctx_ptr._id = instance_id;
if (@hasField(CtxType, "_internal")) {
ctx_ptr._internal.component_id = component_id;
ctx_ptr._internal.instance_id = instance_id;
}
}
fn deinit(propsPtr: ?*const anyopaque, alloc: Allocator) void {

View File

@ -1,18 +1,18 @@
//! Structured zig dbuidld output parser for watch mode.
//!
//! zig --watch stderr output order per build cycle:
//! 1. [optional] `zig build-exe ...` compilation started (change detected)
//! 2. `Build Summary: N/M steps succeeded; X failed` outcome
//! 1. [optional] `zig build-exe ...` - compilation started (change detected)
//! 2. `Build Summary: N/M steps succeeded; X failed` - outcome
//! 3. Build tree (+/- lines per step)
//! 4. Blank line
//! 5. Error diagnostics (if failed): `file:line:col: error: msg` blocks
//! 6. `error: the following command failed with N compilation errors:` end of errors
//! 6. `error: the following command failed with N compilation errors:` - end of errors
//!
//! Events returned from processLine:
//! change_detected a `zig build-exe/lib/obj` line seen recompilation started
//! should_restart binary mtime changed after a successful Build Summary
//! errors structured diagnostics, emitted when the error block ends
//! resolved previous build had errors, current succeeded
//! change_detected - a `zig build-exe/lib/obj` line seen recompilation started
//! should_restart - binary mtime changed after a successful Build Summary
//! errors - structured diagnostics, emitted when the error block ends
//! resolved - previous build had errors, current succeeded
const std = @import("std");
const builtin = @import("builtin");
const log = std.log.scoped(.builder);
@ -62,7 +62,7 @@ pub const Event = union(enum) {
should_restart: u64, // build_duration_ms
errors: BuildResult,
resolved,
build_complete_no_change: u64, // build_duration_ms successful build but binary unchanged
build_complete_no_change: u64, // build_duration_ms - successful build but binary unchanged
assets_installed: AssetChange, // asset-only change (public/assets files copied, no recompilation)
};
@ -418,7 +418,7 @@ pub fn parseDiagnostic(allocator: std.mem.Allocator, line: []const u8) ?Diagnost
const message = line[msg_start..];
if (location.len == 0 or message.len == 0) return null;
// Parse "file:LINE:COL" find the last two colons.
// Parse "file:LINE:COL" - find the last two colons.
var last_colon: usize = 0;
var prev_colon: usize = 0;
var j: usize = 0;
@ -853,7 +853,7 @@ pub fn main() !void {
try child.spawn();
defer _ = child.kill() catch {};
// Use a dummy binary path we just want to see events
// Use a dummy binary path - we just want to see events
var state = BuildState.init(allocator, "zig-out/bin/app", 0);
state.first_build_done = true;
defer state.deinit();

View File

@ -314,7 +314,7 @@ fn serveWebSocket(ds: *DevServer, sock: *http.Server.WebSocket) !noreturn {
};
log.debug("ws: connected message sent", .{});
// Drain incoming frames on a dedicated thread mirrors WebServer.zig's
// Drain incoming frames on a dedicated thread - mirrors WebServer.zig's
// recvWebSocketMessages pattern. Without this, the connection stalls because
// unread frames (pings, close frames, etc.) block the underlying TCP stream.
const recv_thread = std.Thread.spawn(.{}, recvWebSocketFrames, .{sock}) catch |err| {
@ -341,7 +341,7 @@ fn serveWebSocket(ds: *DevServer, sock: *http.Server.WebSocket) !noreturn {
while (true) {
const cur = ds.update_id.load(.acquire);
if (cur == last_id) {
// No pending event wait up to 30 s then ping to keep the connection alive.
// No pending event - wait up to 30 s then ping to keep the connection alive.
std.Thread.Futex.timedWait(&ds.update_id, last_id, 30 * std.time.ns_per_s) catch {
try sock.writeMessage("", .ping);
continue;
@ -396,8 +396,8 @@ fn serializeNotification(gpa: Allocator, notification: Notification) ![]u8 {
}
/// Proxy a request to the inner app binary.
/// `head_buffer` raw HTTP request head (including terminating \r\n\r\n).
/// `buffered_extra` body bytes already consumed by the http.Server reader.
/// `head_buffer` - raw HTTP request head (including terminating \r\n\r\n).
/// `buffered_extra` - body bytes already consumed by the http.Server reader.
fn proxyToInner(
ds: *DevServer,
client: std.net.Stream,
@ -406,7 +406,7 @@ fn proxyToInner(
) !void {
const inner_addr = try std.net.Address.parseIp("127.0.0.1", ds.inner_port);
// Retry while the inner server is (re)starting up to 2 s.
// Retry while the inner server is (re)starting - up to 2 s.
const inner: std.net.Stream = for (0..200) |_| {
if (std.net.tcpConnectToAddress(inner_addr)) |s| break s else |_| std.Thread.sleep(10 * std.time.ns_per_ms);
} else return error.ConnectionRefused;

View File

@ -83,7 +83,7 @@ fn remapSingle(allocator: std.mem.Allocator, d: *Builder.Diagnostic) !void {
defer allocator.free(decoded);
base64.Decoder.decode(decoded, b64_data) catch return;
// Parse JSON sourcemap extract "sources" and "mappings" fields
// Parse JSON sourcemap - extract "sources" and "mappings" fields
const source_file = extractJsonStringField(decoded, "sources") orelse return;
const mappings_str = extractJsonStringField(decoded, "mappings") orelse return;

View File

@ -62,7 +62,7 @@
if (document.hidden) {
// Defer actions while tab is hidden; keep only the latest pending message
// so we apply the final state when the user switches back.
// "connected" and "building" are status-only always store them but
// "connected" and "building" are status-only - always store them but
// don't discard a more important pending action (reload/error/asset_update).
if (msg.type === "connected" || msg.type === "building") {
if (!state.pendingMessage) state.pendingMessage = msg;
@ -188,7 +188,7 @@
}
if (!cssReloaded && files.length > 0) {
// Non-CSS assets changed (images, fonts, etc.) bust cache on matching elements
// Non-CSS assets changed (images, fonts, etc.) - bust cache on matching elements
const selectors = files
.filter((f) => !f.endsWith(".css"))
.map((f) => `img[src*="${f}"],source[srcset*="${f}"]`)

View File

@ -75,15 +75,19 @@ pub fn ComponentCtx(comptime PropsType: type) type {
allocator: Allocator,
children: ?zx.Component = null,
_id: u16 = 0,
_component_id: []const u8 = "",
_state_index: u32 = 0,
_handler_index: u32 = 0,
_internal: Internal = .{},
pub const Internal = struct {
instance_id: u16 = 0,
component_id: []const u8 = "",
state_idx: u32 = 0,
handler_idx: u32 = 0,
};
pub fn state(self: *Self, comptime T: type, initial: T) reactivity.StateInstance(T) {
const slot = (1 << 20) + self._state_index;
self._state_index += 1;
return reactivity.State(T).getOrCreate(self.allocator, self._component_id, slot, initial) catch @panic("State(T).getOrCreate");
const slot = (1 << 20) + self._internal.state_idx;
self._internal.state_idx += 1;
return reactivity.State(T).getOrCreate(self.allocator, self._internal.component_id, slot, initial) catch @panic("State(T).getOrCreate");
}
pub fn sbind(self: *Self, comptime handler: anytype, states: anytype) zx.EventHandler {
@ -99,7 +103,7 @@ pub fn ComponentCtx(comptime PropsType: type) type {
const alloc = if (platform.role == .client) client_allocator else self.allocator;
const bound_states = zx.EventHandler.buildStates(alloc, states);
return zx.EventHandler.serverSS(handler, alloc, &self._handler_index, bound_states);
return zx.EventHandler.serverSS(handler, alloc, &self._internal.handler_idx, bound_states);
}
pub fn bind(self: *Self, comptime handler: anytype) zx.EventHandler {
@ -116,20 +120,20 @@ pub fn ComponentCtx(comptime PropsType: type) type {
return switch (FnType) {
// Client
fn (*ClientEvent) void => zx.EventHandler.client(handler),
fn (*ClientEvent.Stateful) void => zx.EventHandler.clientS(handler, alloc, self._component_id),
fn (*ClientEvent.Stateful) void => zx.EventHandler.clientS(handler, alloc, self._internal.component_id),
// Server
fn (*ServerEvent.Stateful) void => zx.EventHandler.serverS(handler, alloc, self._component_id, self._state_index, &self._handler_index),
fn (*ServerEvent) void => zx.EventHandler.server(handler, alloc, &self._handler_index),
fn (*ServerEvent.Stateful) void => zx.EventHandler.serverS(handler, alloc, self._internal.component_id, self._internal.state_idx, &self._internal.handler_idx),
fn (*ServerEvent) void => zx.EventHandler.server(handler, alloc, &self._internal.handler_idx),
// Server Actions
fn (*ActionContext.Stateful) void => zx.EventHandler.actionStateful(handler, alloc, self._component_id, self._state_index, &self._handler_index),
fn (*ActionContext.Stateful) void => zx.EventHandler.actionStateful(handler, alloc, self._internal.component_id, self._internal.state_idx, &self._internal.handler_idx),
fn (ActionContext, *StateContext) void => actionBind(handler, alloc, self),
fn (*ActionContext) void => actionBind(handler, alloc, self),
else => blk: {
if (comptime params.len == 1 and params[0].type.? == *ServerEvent) {
break :blk zx.EventHandler.server(handler, alloc, &self._handler_index);
break :blk zx.EventHandler.server(handler, alloc, &self._internal.handler_idx);
}
if (comptime params.len == 2 and
@typeInfo(params[0].type.?) == .@"struct" and
@ -164,9 +168,9 @@ fn actionBind(comptime handler: anytype, alloc: Allocator, ctx: anytype) zx.Even
}
}
};
const bound = reactivity.collectStateBoundEntries(alloc, ctx._component_id, ctx._state_index);
ctx._handler_index += 1;
const h_id = ctx._handler_index;
const bound = reactivity.collectStateBoundEntries(alloc, ctx._internal.component_id, ctx._internal.state_idx);
ctx._internal.handler_idx += 1;
const h_id = ctx._internal.handler_idx;
const ec = alloc.create(zx.EventHandler.Context) catch @panic("OOM");
ec.* = .{ .handler_id = h_id, .bound_states = bound };
return zx.EventHandler{

View File

@ -500,7 +500,7 @@ pub fn transpileReturn(self: *Ast, node: ts.Node, ctx: *TranspileContext) !void
// Check if we need to initialize _zx with allocator
const allocator_value = try getAllocatorAttribute(self, child);
// Synthesized _zx init no source mapping (it's boilerplate)
// Synthesized _zx init - no source mapping (it's boilerplate)
try ctx.write("var _zx = @import(\"zx\").");
if (allocator_value) |alloc| {
try ctx.write("allocInit(");
@ -1361,7 +1361,7 @@ fn writeChildrenValue(self: *Ast, children: []const ts.Node, ctx: *TranspileCont
/// When preserve_whitespace is true (e.g. for <pre>), text nodes won't be trimmed
fn writeHtmlElement(self: *Ast, node: ts.Node, tag: []const u8, tag_name_byte: u32, end_tag_start_byte: u32, end_tag_end_byte: u32, attributes: []const ZxAttribute, children: []const ts.Node, ctx: *TranspileContext, preserve_whitespace: bool) !void {
_ = node;
// _zx.ele( is synthesized no source mapping
// _zx.ele( is synthesized - no source mapping
try ctx.write("_zx.ele");
if (ctx.paren_byte) |p| {
try ctx.writeM("(", p, self);

View File

@ -240,7 +240,7 @@ pub const Handler = struct {
}
}
/// Open a .zx file in ZLS if not already tracked reads from disk, transpiles to .zig, sends didOpen.
/// Open a .zx file in ZLS if not already tracked - reads from disk, transpiles to .zig, sends didOpen.
/// For imported files we send transpiled .zig (not raw .zx) so ZLS can resolve exported types.
fn ensureZxFileOpenInZls(handler: *Handler, arena: std.mem.Allocator, doc_dir: []const u8, rel_path: []const u8) void {
const joined = std.fs.path.join(handler.allocator, &.{ doc_dir, rel_path }) catch return;

View File

@ -16,7 +16,7 @@ const opts = @import("options.zig");
const ctxs = @import("contexts.zig");
const reactivity = @import("runtime/client/reactivity.zig");
// -- Core Language (separate cached module excluded by default for user builds) --//
// -- Core Language --//
pub const Ast = if (!module_options.exclude_core_lang) @import("zx_core_lang").Ast else @compileError("core_lang is excluded. Set exclude-core-lang=false to enable.");
pub const Parse = if (!module_options.exclude_core_lang) @import("zx_core_lang").Parse else @compileError("core_lang is excluded. Set exclude-core-lang=false to enable.");
pub const Validate = if (!module_options.exclude_core_lang) @import("zx_core_lang").Validate else @compileError("core_lang is excluded. Set exclude-core-lang=false to enable.");
@ -63,7 +63,6 @@ pub const EventHandler = @import("runtime/core/EventHandler.zig");
pub const State = reactivity.State;
// --- Options --- //
pub const AppOptions = app_module.ServerConfig;
pub const PageOptions = opts.PageOptions;
pub const LayoutOptions = opts.LayoutOptions;
pub const NotFoundOptions = opts.NotFoundOptions;

View File

@ -64,6 +64,7 @@ pub const ComponentMeta = struct {
}
fn wrapper(allocator: std.mem.Allocator, cmp_name: []const u8, props_json: ?[]const u8) zx.Component {
_ = cmp_name;
// Case 1: Component takes only allocator - fn Component(allocator) Component
if (first_is_allocator and param_count == 1) {
return normalizeResult(func(allocator));
@ -83,15 +84,17 @@ pub const ComponentMeta = struct {
ctx.allocator = allocator;
ctx.children = null;
ctx._id = instance_counter;
// Reset slot counters so hooks run in stable order every render.
if (@hasField(CtxType, "_internal")) {
ctx._internal = .{
.instance_id = instance_counter,
.component_id = current_render_id,
.state_idx = 0,
.handler_idx = 0,
};
}
instance_counter +%= 1; // Wrap around on overflow
// Reset both slot counters so hooks run in stable order every render.
ctx._component_id = current_render_id;
ctx._state_index = 0;
if (@hasField(CtxType, "_handler_index")) ctx._handler_index = 0;
if (@hasField(CtxType, "_component_name")) ctx._component_name = cmp_name;
// Parse props if the context has a props field
if (@hasField(CtxType, "props")) {
const PropsFieldType = @FieldType(CtxType, "props");

View File

@ -1,4 +1,4 @@
//! Client-side Event wraps a browser JS event object.
//! Client-side Event - wraps a browser JS event object.
//!
//! Provides DOM event methods: `value()`, `key()`, `preventDefault()`.
//! For state access, use `Event.Stateful` via `ctx.bind()`.
@ -52,10 +52,9 @@ pub fn key(self: Event) ?[]const u8 {
return event.ref.getAlloc(real_js.String, gpa, "key") catch null;
}
// --- Stateful --- //
/// Stateful client event provides `state()` access to bound component state.
/// Stateful client event - provides `state()` access to bound component state.
/// Use `fn(*zx.client.Event.Stateful) void` with `ctx.bind()` to get this type.
pub const Stateful = struct {
_inner: *Event,
@ -89,5 +88,4 @@ pub const Stateful = struct {
pub fn key(self: Stateful) ?[]const u8 {
return self._inner.key();
}
};

View File

@ -82,7 +82,7 @@ pub fn State(comptime T: type) type {
pub const ValueType = T;
value: T,
/// The owning component ID used to call scheduleRender on mutation.
/// The owning component ID - used to call scheduleRender on mutation.
component_id: []const u8,
pub fn init(value: T, component_id: []const u8) Self {
@ -165,7 +165,7 @@ pub fn State(comptime T: type) type {
if (state_store.get(key)) |entry| {
return @ptrCast(@alignCast(entry.ptr));
}
@panic("State not found ensure sc.state() is called in the same order as ctx.state()");
@panic("State not found - ensure sc.state() is called in the same order as ctx.state()");
}
};
}
@ -220,11 +220,10 @@ pub fn scheduleRender(component_id: []const u8) void {
return;
}
}
// component_id not registered nested component inside a CSR parent.
// component_id not registered - nested component inside a CSR parent.
// Re-render all so the parent picks up the state change.
client.renderAll();
}
}
pub const EventHandler = zx.EventHandler;

View File

@ -25,17 +25,17 @@ pub extern "__zx" fn _snv(vnode_id: u64, ptr: [*]const u8, len: usize) void;
// DOM tree mutation
/// parent.appendChild(child) both nodes looked up by vnode_id.
/// parent.appendChild(child) - both nodes looked up by vnode_id.
pub extern "__zx" fn _ac(parent_id: u64, child_id: u64) void;
/// parent.insertBefore(child, ref) all nodes looked up by vnode_id.
/// parent.insertBefore(child, ref) - all nodes looked up by vnode_id.
pub extern "__zx" fn _ib(parent_id: u64, child_id: u64, ref_id: u64) void;
/// parent.removeChild(child) looked up by vnode_id.
/// parent.removeChild(child) - looked up by vnode_id.
/// Also recursively removes all descendants from the JS domNodes registry.
pub extern "__zx" fn _rc(parent_id: u64, child_id: u64) void;
/// parent.replaceChild(new_child, old_child) looked up by vnode_id.
/// parent.replaceChild(new_child, old_child) - looked up by vnode_id.
/// Also removes old_child subtree from the JS domNodes registry.
pub extern "__zx" fn _rpc(parent_id: u64, new_id: u64, old_id: u64) void;

View File

@ -1,15 +1,15 @@
//! Shared Event types for both client-side and server-side event handlers.
//!
//! Both `zx.client.Event` and `zx.server.Event` share this state interface:
//! - `Event.Stateful` wrapper with `state()` for handlers bound via `ctx.bind()`
//! - `StateHandle(T)` returned by `state()`, provides `.get()` / `.set()`
//! - `StateContext` positional state accessor used internally by the bind wrappers
//! - `Event.Stateful` - wrapper with `state()` for handlers bound via `ctx.bind()`
//! - `StateHandle(T)` - returned by `state()`, provides `.get()` / `.set()`
//! - `StateContext` - positional state accessor used internally by the bind wrappers
const std = @import("std");
const zx = @import("../../root.zig");
/// A handle to a single state value.
/// Returned by `e.state(T)` or `sc.state(T)` call `.get()` to read, `.set(val)` to write back.
/// Returned by `e.state(T)` or `sc.state(T)` - call `.get()` to read, `.set(val)` to write back.
pub fn StateHandle(comptime T: type) type {
return struct {
_ctx: *StateContext,
@ -32,7 +32,7 @@ pub fn StateHandle(comptime T: type) type {
/// Server-side accessor for component states round-tripped through a server event.
///
/// Call `sc.state(T)` in the same order as `ctx.state(T)` in the render function
/// to access each bound state no index needed.
/// to access each bound state - no index needed.
pub const StateContext = struct {
arena: std.mem.Allocator,
_allocator: std.mem.Allocator,
@ -61,7 +61,7 @@ pub const StateContext = struct {
}
/// Access the next bound state in call order, deserializing it to type `T`.
/// Returns a StateHandle with `.get()` / `.set()` same ergonomics as Event.Stateful.state().
/// Returns a StateHandle with `.get()` / `.set()` - same ergonomics as Event.Stateful.state().
pub fn state(self: *StateContext, comptime T: type) StateHandle(T) {
const i = self._index;
self._index += 1;

View File

@ -281,7 +281,7 @@ pub fn resolveRouteHandler(handlers: ServerMeta.RouteHandlers, method: zx.server
}
/// Execute cascading Proxy() handlers from root "/" down to the target path.
/// Does NOT execute local page_proxy/route_proxy those are handled by the caller.
/// Does NOT execute local page_proxy/route_proxy - those are handled by the caller.
pub fn executeCascadingProxies(
path: []const u8,
req: zx.server.Request,

View File

@ -144,7 +144,7 @@ fn storedTypeHash(raw: []const u8) !u64 {
return std.fmt.parseUnsigned(u64, raw[start..i], 10);
}
// -- Impl: Noop (WASM default replaced by edge adapter at startup) -- //
// -- Impl: Noop (WASM default - replaced by edge adapter at startup) -- //
fn noopGet(_: *anyopaque, _: []const u8, _: std.mem.Allocator, _: []const u8) anyerror!?[]u8 {
return null;
@ -162,7 +162,7 @@ const noop_vtable = VTable{
.list = &noopList,
};
// -- Impl: Filesystem (native default persists to datadir/kv/<ns>/) -- //
// -- Impl: Filesystem (native default - persists to datadir/kv/<ns>/) -- //
const kv_store_base = zx_options.datadir ++ std.fs.path.sep_str ++ "kv";
fn keyPath(ns: []const u8, key: []const u8, buf: *[1024]u8) ?[]u8 {

View File

@ -75,7 +75,7 @@ pub const VNode = struct {
if (element.children) |children| {
// Flatten fragments: lift fragment children into this node's
// children list (React-style fragments produce no DOM node).
// children list (React-style - fragments produce no DOM node).
const flat = try flattenComponents(allocator, children);
try self.children.ensureTotalCapacity(allocator, flat.len);
for (flat, 0..) |child, child_index| {
@ -425,7 +425,7 @@ fn flattenInto(children: []const zx.Component, result: []zx.Component, idx: *usi
/// reused node whose old_index >= lastPlacedIndex stays, update tracker.
/// no match PLACEMENT.
/// Delete every old child not reused.
/// Backward pass (our addition for reference_id) emit PLACEMENT/MOVE patches
/// Backward pass (our addition for reference_id) - emit PLACEMENT/MOVE patches
/// back-to-front so each patch's reference_id points to an already-resolved node.
fn reconcileChildren(
allocator: zx.Allocator,

View File

@ -1,4 +1,4 @@
//! Server-side Action context for server form action handlers.
//! Server-side Action - context for server form action handlers.
//!
//! Provides form data parsing via `data()`.
//! For state access, use `Action.Stateful` via `ctx.bind()` or `fn(*zx.server.Action.Stateful) void`.
@ -60,7 +60,7 @@ pub fn data(self: Action, comptime T: type) T {
return result;
}
/// Stateful server action provides `state()` access to bound component state.
/// Stateful server action - provides `state()` access to bound component state.
/// Use `fn(*zx.server.Action.Stateful) void` with `ctx.bind()` to get this type.
pub const Stateful = struct {
_inner: *Action,

View File

@ -1,4 +1,4 @@
//! Server-side Event context for server event handlers.
//! Server-side Event - context for server event handlers.
//!
//! Provides the event payload value via `value()`.
//! For state access, use `Event.Stateful` via `ctx.bind()`.
@ -26,7 +26,7 @@ pub fn value(self: Event) ?[]const u8 {
return self.payload.value;
}
/// Stateful server event provides `state()` access to bound component state.
/// Stateful server event - provides `state()` access to bound component state.
/// Use `fn(*zx.server.Event.Stateful) void` with `ctx.bind()` to get this type.
pub const Stateful = struct {
_inner: *Event,
@ -42,4 +42,3 @@ pub const Stateful = struct {
return self._inner.value();
}
};

View File

@ -700,7 +700,7 @@ pub const ServerMeta = struct {
}
/// Wrapper to allow routes to return void or !void.
/// Route function shape: `(ctx: zx.RouteContext, app?: App, state?: State)` positional.
/// Route function shape: `(ctx: zx.RouteContext, app?: App, state?: State)` - positional.
fn wrapRoute(comptime routeFn: anytype) RouteHandler {
const FnInfo = @typeInfo(@TypeOf(routeFn)).@"fn";
const R = FnInfo.return_type.?;

View File

@ -11,7 +11,7 @@ const render = @import("render.zig");
pub const PageFn = *const fn (zx.PageContext, ?*const anyopaque, ?*const anyopaque) anyerror!zx.Component;
pub const DispatchResult = union(enum) {
/// Request did not match this handler type continue normal handling.
/// Request did not match this handler type - continue normal handling.
not_triggered,
/// Handler was invoked successfully. `body` is serialized JSON state, or null if no state.
ok: struct { body: ?[]u8 = null },

View File

@ -195,10 +195,10 @@ pub fn run() !void {
// Action was invoked natively, continue to render the page below
},
.not_found => {
// No page handler fall through to API route dispatch below
// No page handler - fall through to API route dispatch below
},
.component => |cmp| {
// Page rendered successfully write it out
// Page rendered successfully - write it out
wasi_res.setContentTypeStr("text/html");
const streaming_enabled = core_handler.isStreamingEnabled(route);
@ -290,7 +290,7 @@ pub fn run() !void {
}
if (wasi_socket.upgraded) {
// WebSocket message loop do not send HTTP response
// WebSocket message loop - do not send HTTP response
const upgrade_data = wasi_socket.upgradeData();
if (handlers.socket_open) |open_fn| {
open_fn(socket, upgrade_data, allocator, allocator) catch {};

View File

@ -87,7 +87,7 @@ pub extern "__zx_db" fn db_values(
) i32;
// ---------------------------------------------------------------------------
// WebSocket WASI bridge Cloudflare Worker binding
// WebSocket WASI bridge - Cloudflare Worker binding
// ---------------------------------------------------------------------------
/// Extern imports provided by worker.ts via the __zx_ws import namespace.
@ -106,7 +106,7 @@ pub extern "__zx_ws" fn ws_publish(topic_ptr: [*]const u8, topic_len: usize, dat
pub extern "__zx_ws" fn ws_is_subscribed(topic_ptr: [*]const u8, topic_len: usize) i32;
// ---------------------------------------------------------------------------
// Logging bridge forwards std.log calls to the JS console with level info
// Logging bridge - forwards std.log calls to the JS console with level info
// ---------------------------------------------------------------------------
/// Provided by the JS runtime via the __zx namespace (ZxBridge).

View File

@ -1,14 +1,14 @@
//! ZXON ZX Object Notation (Ziex Exchange Object Notation)
//! ZXON - ZX Object Notation (Ziex Exchange Object Notation)
//!
//! A compact positional serialization format for Zig types. Structs are encoded
//! as ordered value arrays `[field1, field2, ...]` field names are known at
//! as ordered value arrays `[field1, field2, ...]` - field names are known at
//! comptime on both ends, so only values are transmitted.
//!
//! Compared to `std.json`, this pulls in ~78 KB less code in WASM builds.
const std = @import("std");
/// Options for serialize/parse reserved for future use.
/// Options for serialize/parse - reserved for future use.
pub const Options = struct {};
//

View File

@ -546,7 +546,7 @@ pub const ZxContext = struct {
// If client option is set, return a client component (for @rendering={.client})
// Render the component on the server for SSR, then hydrate on client.
// On Browser (already on the client), skip the CSR wrapper just render
// On Browser (already on the client), skip the CSR wrapper - just render
// the component directly as a component_fn.
if (options.client != null and zx.platform.role == .client) {
return .{ .component_fn = comp_fn };

View File

@ -61,7 +61,7 @@ test "sm > sourceToGenerated exact match" {
try testing.expectEqual(@as(i32, 5), result.generated_line);
try testing.expectEqual(@as(i32, 8), result.generated_column);
// Position between two mappings on same source line should use closest before
// Position between two mappings on same source line - should use closest before
const between = decoded.sourceToGenerated(2, 7).?;
try testing.expectEqual(@as(i32, 5), between.generated_line);
// col 7 is 3 past the mapping at col 4, so generated col = 8 + 3 = 11
@ -108,7 +108,7 @@ test "sm > lookup returns null for unmapped position" {
var decoded = try sm.decode(allocator);
defer decoded.deinit();
// Line before any mapping should return null
// Line before any mapping - should return null
const result = decoded.sourceToGenerated(0, 0);
try testing.expectEqual(@as(?sourcemap.Mapping, null), result);
}
@ -233,7 +233,7 @@ test "sm > e2e expression in element" {
}
// The expression {name} at source line 3 should map somewhere in generated that contains "name"
// Find "name" in the source it's at line 3, the expression is after "Hello "
// Find "name" in the source - it's at line 3, the expression is after "Hello "
// In .zx, line 3 is: " <p>Hello {name}</p>"
// "name" starts at col 16 (after 8 spaces + "<p>Hello {")
const expr_mapping = decoded.sourceToGenerated(3, 17).?;

File diff suppressed because one or more lines are too long