release: WRNexusJS 0.2.33

This commit is contained in:
2026-07-14 23:13:46 +05:30
parent 959ed24009
commit 936488ac06
56 changed files with 174 additions and 96 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ai",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/authz",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/cli",
"version": "0.2.32",
"version": "0.2.33",
"type": "module",
"main": "src/index.ts",
"exports": {
+9
View File
@@ -366,6 +366,15 @@ const MIGRATIONS: Migration[] = [
// No generated application files require automatic migration.
},
},
{
version: "0.2.33",
id: "fix-runtime-bug",
description:
"Fixes safe encoding and browser parsing of multiline component behavior metadata.",
apply() {
// No generated application files require automatic migration.
},
},
];
/** Release tooling uses this to require an explicit migration entry per version. */
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/compiler",
"version": "0.2.32",
"version": "0.2.33",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -925,7 +925,7 @@ function behaviorAttribute(behavior: ComponentBehavior | null): string {
return "";
}
const encoded = encodeURIComponent(JSON.stringify(behavior));
const encoded = Buffer.from(JSON.stringify(behavior), "utf8").toString("base64");
return ` data-wrn-behavior="${encoded}"`;
}
+66 -10
View File
@@ -16,6 +16,39 @@ async function compileAndImport(src: string): Promise<Record<string, unknown>> {
return import(pathToFileURL(file).href);
}
type CompiledBehavior = {
functions: string;
lifecycle: {
mount?: string;
update?: string;
unmount?: string;
};
watches: Array<{
state: string;
body: string;
}>;
};
function extractCompiledBehavior(output: string): CompiledBehavior {
const marker = 'data-wrn-behavior="';
const start = output.indexOf(marker);
expect(start).toBeGreaterThanOrEqual(0);
const valueStart = start + marker.length;
const valueEnd = output.indexOf('"', valueStart);
expect(valueEnd).toBeGreaterThan(valueStart);
const encoded = output.slice(valueStart, valueEnd);
expect(encoded).toMatch(/^[A-Za-z0-9+/]+=*$/);
return JSON.parse(Buffer.from(encoded, "base64").toString("utf8")) as CompiledBehavior;
}
test("parses a page with a layout member", () => {
const ast = parse(`page Home {\n layout = "public"\n view { <h1>Hi</h1> }\n}`);
expect(ast.kind).toBe("page");
@@ -382,11 +415,13 @@ component LifecycleTest {
`),
);
expect(output).toContain("%22mount%22");
const behavior = extractCompiledBehavior(output);
expect(output).toContain("%22update%22");
expect(behavior.lifecycle.mount).toContain("ready = true");
expect(output).toContain("%22unmount%22");
expect(behavior.lifecycle.update).toContain("console.log(ready)");
expect(behavior.lifecycle.unmount).toContain("ready = false");
});
test("component watchers compile into behavior metadata", () => {
const output = generate(
@@ -405,9 +440,13 @@ component WatchTest {
`),
);
expect(output).toContain("%22state%22%3A%22visible%22");
const behavior = extractCompiledBehavior(output);
expect(output).toContain("console.log");
expect(behavior.watches).toHaveLength(1);
expect(behavior.watches[0]?.state).toBe("visible");
expect(behavior.watches[0]?.body).toContain('console.log("changed", visible)');
});
test("behavior-only components still receive a reactive scope", () => {
const output = generate(
@@ -479,10 +518,9 @@ component BackToTop {
expect(output).toContain("data-wrn-behavior=");
expect(output).toContain("%7B");
expect(output).toContain("%22functions%22");
expect(output).not.toContain('data-wrn-behavior="{');
expect(output).not.toContain('data-wrn-behavior="%7B');
});
test("component watchers compile into behavior metadata", () => {
@@ -506,7 +544,7 @@ component WatchTest {
expect(match).not.toBeNull();
const behavior = JSON.parse(decodeURIComponent(match![1]));
const behavior = extractBehavior(output);
expect(behavior.watches).toEqual([
{
@@ -546,7 +584,7 @@ component LifecycleTest {
expect(match).not.toBeNull();
const behavior = JSON.parse(decodeURIComponent(match![1]));
const behavior = extractBehavior(output);
expect(behavior.lifecycle.mount).toContain("ready = true");
@@ -554,3 +592,21 @@ component LifecycleTest {
expect(behavior.lifecycle.unmount).toContain("ready = false");
});
function extractBehavior(output: string): {
functions: string;
lifecycle: {
mount?: string;
update?: string;
unmount?: string;
};
watches: Array<{
state: string;
body: string;
}>;
} {
const match = /data-wrn-behavior="([^"]+)"/.exec(output);
expect(match).not.toBeNull();
return JSON.parse(Buffer.from(match![1], "base64").toString("utf8"));
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/core",
"version": "0.2.32",
"version": "0.2.33",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/csr",
"version": "0.2.32",
"version": "0.2.33",
"type": "module",
"main": "src/index.ts",
"exports": {
+14 -1
View File
@@ -43,6 +43,17 @@ export const REACTIVE_RUNTIME = String.raw`
});
}
function decodeBehaviorPayload(value) {
var binary = window.atob(value);
var bytes = new Uint8Array(binary.length);
for (var index = 0; index < binary.length; index++) {
bytes[index] = binary.charCodeAt(index);
}
return new TextDecoder("utf-8").decode(bytes);
}
function parseBehavior(element) {
var raw = element.getAttribute(
"data-wrn-behavior",
@@ -53,7 +64,9 @@ export const REACTIVE_RUNTIME = String.raw`
}
try {
var decoded = decodeURIComponent(raw);
var decoded =
decodeBehaviorPayload(raw);
var parsed = JSON.parse(decoded);
if (
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/db",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/dev-server",
"version": "0.2.32",
"version": "0.2.33",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/encryption",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/helpers",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/i18n",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/jwt",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/mobile",
"version": "0.2.32",
"version": "0.2.33",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/native",
"version": "0.2.32",
"version": "0.2.33",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/oauth",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/pubsub",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/queue",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/reactive",
"version": "0.2.32",
"version": "0.2.33",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/router",
"version": "0.2.32",
"version": "0.2.33",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ssr",
"version": "0.2.32",
"version": "0.2.33",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/styles",
"version": "0.2.32",
"version": "0.2.33",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/test",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/tracking",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ui",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/uploader",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/validation",
"version": "0.2.32",
"version": "0.2.33",
"private": true,
"type": "module",
"main": "src/index.ts",