diff --git a/.publish/ai/package.json b/.publish/ai/package.json index c535f49d..c81bfbc2 100644 --- a/.publish/ai/package.json +++ b/.publish/ai/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ai", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.", "license": "MIT", diff --git a/.publish/authz/package.json b/.publish/authz/package.json index 41f51e5d..a067d076 100644 --- a/.publish/authz/package.json +++ b/.publish/authz/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/authz", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/authz — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/cli/package.json b/.publish/cli/package.json index 29cf36cb..4f34180a 100644 --- a/.publish/cli/package.json +++ b/.publish/cli/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/cli", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/cli — part of the WrNexus framework.", "license": "MIT", @@ -28,16 +28,16 @@ "wrnexus": "./dist/index.js" }, "dependencies": { - "@wrnexus/core": "^0.2.28", - "@wrnexus/router": "^0.2.28", - "@wrnexus/csr": "^0.2.28", - "@wrnexus/compiler": "^0.2.28", - "@wrnexus/styles": "^0.2.28", - "@wrnexus/dev-server": "^0.2.28", - "@wrnexus/ui": "^0.2.28", - "@wrnexus/validation": "^0.2.28", - "@wrnexus/i18n": "^0.2.28", - "@wrnexus/db": "^0.2.28" + "@wrnexus/core": "^0.2.29", + "@wrnexus/router": "^0.2.29", + "@wrnexus/csr": "^0.2.29", + "@wrnexus/compiler": "^0.2.29", + "@wrnexus/styles": "^0.2.29", + "@wrnexus/dev-server": "^0.2.29", + "@wrnexus/ui": "^0.2.29", + "@wrnexus/validation": "^0.2.29", + "@wrnexus/i18n": "^0.2.29", + "@wrnexus/db": "^0.2.29" }, "files": [ "dist" diff --git a/.publish/compiler/package.json b/.publish/compiler/package.json index 00ce5ecb..0baba426 100644 --- a/.publish/compiler/package.json +++ b/.publish/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/compiler", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/compiler — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/core/package.json b/.publish/core/package.json index 4fb07c70..a4a79e4f 100644 --- a/.publish/core/package.json +++ b/.publish/core/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/core", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/core — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/csr/package.json b/.publish/csr/package.json index f8b4222d..218e7994 100644 --- a/.publish/csr/package.json +++ b/.publish/csr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/csr", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/csr — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.28" + "@wrnexus/core": "^0.2.29" }, "files": [ "dist" diff --git a/.publish/db/package.json b/.publish/db/package.json index 978799ae..75003b05 100644 --- a/.publish/db/package.json +++ b/.publish/db/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/db", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/db — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/dev-server/package.json b/.publish/dev-server/package.json index 6b496205..324ed4f5 100644 --- a/.publish/dev-server/package.json +++ b/.publish/dev-server/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/dev-server", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/dev-server — part of the WrNexus framework.", "license": "MIT", @@ -25,18 +25,18 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.28", - "@wrnexus/router": "^0.2.28", - "@wrnexus/ssr": "^0.2.28", - "@wrnexus/csr": "^0.2.28", - "@wrnexus/compiler": "^0.2.28", - "@wrnexus/styles": "^0.2.28", - "@wrnexus/ui": "^0.2.28", - "@wrnexus/validation": "^0.2.28", - "@wrnexus/i18n": "^0.2.28", - "@wrnexus/db": "^0.2.28", - "@wrnexus/pubsub": "^0.2.28", - "@wrnexus/uploader": "^0.2.28" + "@wrnexus/core": "^0.2.29", + "@wrnexus/router": "^0.2.29", + "@wrnexus/ssr": "^0.2.29", + "@wrnexus/csr": "^0.2.29", + "@wrnexus/compiler": "^0.2.29", + "@wrnexus/styles": "^0.2.29", + "@wrnexus/ui": "^0.2.29", + "@wrnexus/validation": "^0.2.29", + "@wrnexus/i18n": "^0.2.29", + "@wrnexus/db": "^0.2.29", + "@wrnexus/pubsub": "^0.2.29", + "@wrnexus/uploader": "^0.2.29" }, "files": [ "dist" diff --git a/.publish/encryption/package.json b/.publish/encryption/package.json index d14ccb37..845cd9eb 100644 --- a/.publish/encryption/package.json +++ b/.publish/encryption/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/encryption", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/encryption — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/helpers/package.json b/.publish/helpers/package.json index a3d1ca50..1f538247 100644 --- a/.publish/helpers/package.json +++ b/.publish/helpers/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/helpers", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "Safe convenience helpers for WrNexus request contexts and common application flows.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.28" + "@wrnexus/core": "^0.2.29" }, "files": [ "dist" diff --git a/.publish/i18n/package.json b/.publish/i18n/package.json index 4b044d6e..aa2a8a46 100644 --- a/.publish/i18n/package.json +++ b/.publish/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/i18n", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/i18n — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.28" + "@wrnexus/core": "^0.2.29" }, "files": [ "dist" diff --git a/.publish/jwt/package.json b/.publish/jwt/package.json index 64f50ba4..0087f5d0 100644 --- a/.publish/jwt/package.json +++ b/.publish/jwt/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/jwt", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/jwt — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/mobile/package.json b/.publish/mobile/package.json index bf318a6c..e3efc4e3 100644 --- a/.publish/mobile/package.json +++ b/.publish/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/mobile", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/mobile — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/native": "^0.2.28" + "@wrnexus/native": "^0.2.29" }, "files": [ "dist" diff --git a/.publish/native/package.json b/.publish/native/package.json index fb04282e..74e22c8e 100644 --- a/.publish/native/package.json +++ b/.publish/native/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/native", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/native — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/oauth/package.json b/.publish/oauth/package.json index ab71d43c..30577247 100644 --- a/.publish/oauth/package.json +++ b/.publish/oauth/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/oauth", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/oauth — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/pubsub/package.json b/.publish/pubsub/package.json index aca2d2fe..44987175 100644 --- a/.publish/pubsub/package.json +++ b/.publish/pubsub/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/pubsub", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/pubsub — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/queue/package.json b/.publish/queue/package.json index 652f0aed..f5081005 100644 --- a/.publish/queue/package.json +++ b/.publish/queue/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/queue", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/queue — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/reactive/package.json b/.publish/reactive/package.json index 42cfb1d0..a259566f 100644 --- a/.publish/reactive/package.json +++ b/.publish/reactive/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/reactive", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/reactive — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/router/package.json b/.publish/router/package.json index 394e4410..f15718d0 100644 --- a/.publish/router/package.json +++ b/.publish/router/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/router", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/router — part of the WrNexus framework.", "license": "MIT", @@ -21,8 +21,8 @@ } }, "dependencies": { - "@wrnexus/compiler": "^0.2.28", - "@wrnexus/core": "^0.2.28" + "@wrnexus/compiler": "^0.2.29", + "@wrnexus/core": "^0.2.29" }, "files": [ "dist" diff --git a/.publish/ssr/package.json b/.publish/ssr/package.json index e710af69..0efd0938 100644 --- a/.publish/ssr/package.json +++ b/.publish/ssr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ssr", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/ssr — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.28" + "@wrnexus/core": "^0.2.29" }, "files": [ "dist" diff --git a/.publish/styles/package.json b/.publish/styles/package.json index 6567fa60..2e34b7b7 100644 --- a/.publish/styles/package.json +++ b/.publish/styles/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/styles", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/styles — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/uploader": "^0.2.28" + "@wrnexus/uploader": "^0.2.29" }, "files": [ "dist" diff --git a/.publish/test/package.json b/.publish/test/package.json index d99671ac..6f92d47a 100644 --- a/.publish/test/package.json +++ b/.publish/test/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/test", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/test — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/tracking/package.json b/.publish/tracking/package.json index a701a7d8..484110dc 100644 --- a/.publish/tracking/package.json +++ b/.publish/tracking/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/tracking", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/tracking — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/ui/package.json b/.publish/ui/package.json index aca4e3d1..549b0f11 100644 --- a/.publish/ui/package.json +++ b/.publish/ui/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ui", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/ui — part of the WrNexus framework.", "license": "MIT", @@ -22,7 +22,7 @@ "./ui.css": "./ui.css" }, "dependencies": { - "@wrnexus/core": "^0.2.28" + "@wrnexus/core": "^0.2.29" }, "files": [ "dist", diff --git a/.publish/uploader/package.json b/.publish/uploader/package.json index 03a613eb..d8216971 100644 --- a/.publish/uploader/package.json +++ b/.publish/uploader/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/uploader", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/uploader — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.28" + "@wrnexus/core": "^0.2.29" }, "files": [ "dist" diff --git a/.publish/validation/package.json b/.publish/validation/package.json index af9595a8..e387ef84 100644 --- a/.publish/validation/package.json +++ b/.publish/validation/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/validation", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "description": "@wrnexus/validation — part of the WrNexus framework.", "license": "MIT", diff --git a/editors/vscode/package.json b/editors/vscode/package.json index bfda88c2..7c39087f 100644 --- a/editors/vscode/package.json +++ b/editors/vscode/package.json @@ -2,7 +2,7 @@ "name": "wrnexus", "displayName": "WRNexus Language Support", "description": "Syntax highlighting, formatting, diagnostics, snippets and completions for WrNexus .wrn files.", - "version": "0.2.5", + "version": "0.2.6", "publisher": "wrnexus", "private": true, "license": "SEE LICENSE IN LICENSE", diff --git a/editors/vscode/src/definition.js b/editors/vscode/src/definition.js index d4be17e7..9842f541 100644 --- a/editors/vscode/src/definition.js +++ b/editors/vscode/src/definition.js @@ -2,14 +2,10 @@ const vscode = require("vscode"); -const COMPONENT_DECLARATION = - /^\s*(component|layout)\s+([A-Za-z_$][\w$]*)\s*\{/gm; +const COMPONENT_DECLARATION = /^\s*(component|layout)\s+([A-Za-z_$][\w$]*)\s*\{/gm; function getTagAtPosition(document, position) { - const range = document.getWordRangeAtPosition( - position, - /[A-Za-z_$][\w$]*/, - ); + const range = document.getWordRangeAtPosition(position, /[A-Za-z_$][\w$]*/); if (!range) { return null; @@ -23,17 +19,13 @@ function getTagAtPosition(document, position) { const line = document.lineAt(position.line).text; const offset = document.offsetAt(position); - const lineStart = document.offsetAt( - new vscode.Position(position.line, 0), - ); + const lineStart = document.offsetAt(new vscode.Position(position.line, 0)); const characterOffset = offset - lineStart; const before = line.slice(0, characterOffset); const after = line.slice(characterOffset); - const insideTag = - before.lastIndexOf("<") > before.lastIndexOf(">") && - after.includes(">"); + const insideTag = before.lastIndexOf("<") > before.lastIndexOf(">") && after.includes(">"); if (!insideTag) { return null; @@ -75,21 +67,12 @@ async function findDeclaration(name) { continue; } - const nameOffset = - match.index + - match[0].indexOf(declarationName); + const nameOffset = match.index + match[0].indexOf(declarationName); const start = document.positionAt(nameOffset); - const end = document.positionAt( - nameOffset + declarationName.length, - ); + const end = document.positionAt(nameOffset + declarationName.length); - matches.push( - new vscode.Location( - uri, - new vscode.Range(start, end), - ), - ); + matches.push(new vscode.Location(uri, new vscode.Range(start, end))); } } @@ -131,4 +114,4 @@ module.exports = { getTagAtPosition, provideDefinition, registerDefinitionProvider, -}; \ No newline at end of file +}; diff --git a/editors/vscode/src/diagnostics.js b/editors/vscode/src/diagnostics.js index 01b1d245..ee27b798 100644 --- a/editors/vscode/src/diagnostics.js +++ b/editors/vscode/src/diagnostics.js @@ -5,14 +5,9 @@ const vscode = require("vscode"); const COLLECTION_NAME = "wrnexus"; const WRN_LANGUAGE_ID = "wrn"; -const TOP_LEVEL_PATTERN = - /^\s*(page|component|layout)\s+([A-Za-z_$][\w$]*)\s*\{/; +const TOP_LEVEL_PATTERN = /^\s*(page|component|layout)\s+([A-Za-z_$][\w$]*)\s*\{/; -const VALID_TOP_LEVEL_KINDS = new Set([ - "page", - "component", - "layout", -]); +const VALID_TOP_LEVEL_KINDS = new Set(["page", "component", "layout"]); const VALID_MEMBERS = { page: new Set([ @@ -28,21 +23,9 @@ const VALID_MEMBERS = { "realtime", ]), - component: new Set([ - "props", - "state", - "view", - "style", - "functions", - ]), + component: new Set(["props", "state", "view", "style", "functions"]), - layout: new Set([ - "props", - "state", - "view", - "style", - "functions", - ]), + layout: new Set(["props", "state", "view", "style", "functions"]), }; function createDiagnostic( @@ -54,10 +37,7 @@ function createDiagnostic( code, ) { const diagnostic = new vscode.Diagnostic( - new vscode.Range( - document.positionAt(startOffset), - document.positionAt(endOffset), - ), + new vscode.Range(document.positionAt(startOffset), document.positionAt(endOffset)), message, severity, ); @@ -80,11 +60,7 @@ function lineDiagnostic( ) { const line = document.lineAt(lineNumber); - const diagnostic = new vscode.Diagnostic( - line.range, - message, - severity, - ); + const diagnostic = new vscode.Diagnostic(line.range, message, severity); diagnostic.source = "WRNexus"; @@ -96,18 +72,14 @@ function lineDiagnostic( } function stripComments(source) { - return source.replace(//g, (comment) => - comment.replace(/[^\n]/g, " "), - ); + return source.replace(//g, (comment) => comment.replace(/[^\n]/g, " ")); } function findTopLevelDeclaration(document, source) { const match = TOP_LEVEL_PATTERN.exec(source); if (!match) { - const firstMeaningfulLine = source - .split(/\r?\n/) - .findIndex((line) => line.trim().length > 0); + const firstMeaningfulLine = source.split(/\r?\n/).findIndex((line) => line.trim().length > 0); return { diagnostic: lineDiagnostic( @@ -166,9 +138,7 @@ function validateBalancedCharacters(document, source) { continue; } - if ( - source.startsWith("", index + 4); if (commentEnd === -1) { @@ -190,11 +160,7 @@ function validateBalancedCharacters(document, source) { continue; } - if ( - character === "{" || - character === "[" || - character === "(" - ) { + if (character === "{" || character === "[" || character === "(") { stack.push({ character, offset: index, @@ -203,11 +169,7 @@ function validateBalancedCharacters(document, source) { continue; } - if ( - character === "}" || - character === "]" || - character === ")" - ) { + if (character === "}" || character === "]" || character === ")") { const expectedOpening = pairs[character]; const opening = stack.pop(); @@ -227,12 +189,7 @@ function validateBalancedCharacters(document, source) { } for (const opening of stack) { - const expectedClosing = - opening.character === "{" - ? "}" - : opening.character === "[" - ? "]" - : ")"; + const expectedClosing = opening.character === "{" ? "}" : opening.character === "[" ? "]" : ")"; diagnostics.push( createDiagnostic( @@ -284,8 +241,7 @@ function validateHtmlTags(document, source) { ]); const cleaned = stripComments(source); - const tagPattern = - /<\/?([A-Za-z][A-Za-z0-9_$:.-]*)(?:\s[\s\S]*?)?\/?>/g; + const tagPattern = /<\/?([A-Za-z][A-Za-z0-9_$:.-]*)(?:\s[\s\S]*?)?\/?>/g; let match; @@ -358,18 +314,13 @@ function validateHtmlTags(document, source) { } function getRootBodyRange(source, rootMatch) { - const openingBrace = - rootMatch.index + rootMatch[0].lastIndexOf("{"); + const openingBrace = rootMatch.index + rootMatch[0].lastIndexOf("{"); let depth = 0; let quote = null; let escaped = false; - for ( - let index = openingBrace; - index < source.length; - index += 1 - ) { + for (let index = openingBrace; index < source.length; index += 1) { const character = source[index]; if (quote !== null) { @@ -425,6 +376,12 @@ function findRootMembers(source, bodyStart, bodyEnd) { let quote = null; let escaped = false; + const skipLine = () => { + while (index < bodyEnd && source[index] !== "\n" && source[index] !== "\r") { + index += 1; + } + }; + while (index < bodyEnd) { const character = source[index]; @@ -473,17 +430,11 @@ function findRootMembers(source, bodyStart, bodyEnd) { continue; } - if ( - depth === 0 && - /[A-Za-z_]/.test(character) - ) { + if (depth === 0 && /[A-Za-z_]/.test(character)) { const start = index; index += 1; - while ( - index < bodyEnd && - /[A-Za-z0-9_-]/.test(source[index]) - ) { + while (index < bodyEnd && /[A-Za-z0-9_-]/.test(source[index])) { index += 1; } @@ -495,6 +446,11 @@ function findRootMembers(source, bodyStart, bodyEnd) { end: index, }); + // Single-line declarations must skip the rest of the line. + if (name === "state" || name === "layout") { + skipLine(); + } + continue; } @@ -504,12 +460,7 @@ function findRootMembers(source, bodyStart, bodyEnd) { return members; } -function validateRootMembers( - document, - source, - rootKind, - rootMatch, -) { +function validateRootMembers(document, source, rootKind, rootMatch) { const diagnostics = []; const allowed = VALID_MEMBERS[rootKind]; @@ -518,11 +469,7 @@ function validateRootMembers( } const bodyRange = getRootBodyRange(source, rootMatch); - const members = findRootMembers( - source, - bodyRange.start, - bodyRange.end, - ); + const members = findRootMembers(source, bodyRange.start, bodyRange.end); for (const member of members) { if (allowed.has(member.name)) { @@ -544,12 +491,7 @@ function validateRootMembers( return diagnostics; } -function validateRequiredView( - document, - source, - rootKind, - rootMatch, -) { +function validateRequiredView(document, source, rootKind, rootMatch) { const bodyRange = getRootBodyRange(source, rootMatch); const body = source.slice(bodyRange.start, bodyRange.end); @@ -569,17 +511,10 @@ function validateRequiredView( ]; } -function validateLayoutUsage( - document, - source, - rootKind, -) { +function validateLayoutUsage(document, source, rootKind) { const diagnostics = []; - if ( - rootKind !== "page" && - /^\s*layout\s*=/m.test(source) - ) { + if (rootKind !== "page" && /^\s*layout\s*=/m.test(source)) { const match = /^\s*layout\s*=/m.exec(source); if (match) { @@ -588,7 +523,7 @@ function validateLayoutUsage( document, match.index, match.index + match[0].length, - "`layout = \"...\"` is only valid inside a page.", + '`layout = "..."` is only valid inside a page.', vscode.DiagnosticSeverity.Error, "wrn-invalid-layout-member", ), @@ -611,16 +546,11 @@ function validateDocument(document) { } const diagnostics = []; - const declaration = findTopLevelDeclaration( - document, - source, - ); + const declaration = findTopLevelDeclaration(document, source); if (declaration.diagnostic) { diagnostics.push(declaration.diagnostic); - diagnostics.push( - ...validateBalancedCharacters(document, source), - ); + diagnostics.push(...validateBalancedCharacters(document, source)); return diagnostics; } @@ -630,8 +560,7 @@ function validateDocument(document) { createDiagnostic( document, declaration.match.index, - declaration.match.index + - declaration.match[0].length, + declaration.match.index + declaration.match[0].length, `Unsupported WRN declaration \`${declaration.kind}\`.`, vscode.DiagnosticSeverity.Error, "wrn-invalid-kind", @@ -641,48 +570,21 @@ function validateDocument(document) { return diagnostics; } - diagnostics.push( - ...validateBalancedCharacters(document, source), - ); + diagnostics.push(...validateBalancedCharacters(document, source)); - diagnostics.push( - ...validateHtmlTags(document, source), - ); + diagnostics.push(...validateHtmlTags(document, source)); - diagnostics.push( - ...validateRootMembers( - document, - source, - declaration.kind, - declaration.match, - ), - ); + diagnostics.push(...validateRootMembers(document, source, declaration.kind, declaration.match)); - diagnostics.push( - ...validateRequiredView( - document, - source, - declaration.kind, - declaration.match, - ), - ); + diagnostics.push(...validateRequiredView(document, source, declaration.kind, declaration.match)); - diagnostics.push( - ...validateLayoutUsage( - document, - source, - declaration.kind, - ), - ); + diagnostics.push(...validateLayoutUsage(document, source, declaration.kind)); return diagnostics; } function registerDiagnostics(context) { - const collection = - vscode.languages.createDiagnosticCollection( - COLLECTION_NAME, - ); + const collection = vscode.languages.createDiagnosticCollection(COLLECTION_NAME); const timers = new Map(); @@ -691,9 +593,7 @@ function registerDiagnostics(context) { return; } - const previousTimer = timers.get( - document.uri.toString(), - ); + const previousTimer = timers.get(document.uri.toString()); if (previousTimer) { clearTimeout(previousTimer); @@ -702,10 +602,7 @@ function registerDiagnostics(context) { const timer = setTimeout(() => { timers.delete(document.uri.toString()); - collection.set( - document.uri, - validateDocument(document), - ); + collection.set(document.uri, validateDocument(document)); }, 150); timers.set(document.uri.toString(), timer); @@ -726,19 +623,17 @@ function registerDiagnostics(context) { vscode.workspace.onDidSaveTextDocument(update), - vscode.workspace.onDidCloseTextDocument( - (document) => { - const key = document.uri.toString(); - const timer = timers.get(key); + vscode.workspace.onDidCloseTextDocument((document) => { + const key = document.uri.toString(); + const timer = timers.get(key); - if (timer) { - clearTimeout(timer); - timers.delete(key); - } + if (timer) { + clearTimeout(timer); + timers.delete(key); + } - collection.delete(document.uri); - }, - ), + collection.delete(document.uri); + }), { dispose() { @@ -758,4 +653,4 @@ module.exports = { validateDocument, validateHtmlTags, validateRootMembers, -}; \ No newline at end of file +}; diff --git a/editors/vscode/wrnexus-0.2.5.vsix b/editors/vscode/wrnexus-0.2.5.vsix index 17333bc2..68c3c4d2 100644 Binary files a/editors/vscode/wrnexus-0.2.5.vsix and b/editors/vscode/wrnexus-0.2.5.vsix differ diff --git a/editors/vscode/wrnexus-0.2.6.vsix b/editors/vscode/wrnexus-0.2.6.vsix new file mode 100644 index 00000000..692c2864 Binary files /dev/null and b/editors/vscode/wrnexus-0.2.6.vsix differ diff --git a/packages/ai/package.json b/packages/ai/package.json index c1f608f7..a6b4439e 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ai", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.", diff --git a/packages/authz/package.json b/packages/authz/package.json index 02e0943b..0d55404e 100644 --- a/packages/authz/package.json +++ b/packages/authz/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/authz", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/cli/package.json b/packages/cli/package.json index 2f52d8ef..954bfba6 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/cli", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/cli/src/update.ts b/packages/cli/src/update.ts index f8208222..6f9b7a82 100644 --- a/packages/cli/src/update.ts +++ b/packages/cli/src/update.ts @@ -329,6 +329,16 @@ const MIGRATIONS: Migration[] = [ // No project file migration is required for this framework release. }, }, + { + version: "0.2.29", + id: "workspace-config-improvements", + description: + "Adds environment-aware workspace domains, app URL helpers, component tag syntax, layouts, and editor improvements.", + apply() { + // No generated project files require automatic changes. + // Existing applications remain compatible with this release. + }, + }, ]; /** Release tooling uses this to require an explicit migration entry per version. */ diff --git a/packages/cli/src/workspace.ts b/packages/cli/src/workspace.ts index 7f7dd3d9..e07d3e3f 100644 --- a/packages/cli/src/workspace.ts +++ b/packages/cli/src/workspace.ts @@ -19,17 +19,44 @@ import type { GatewayAuth, GatewaySecurity } from "@wrnexus/dev-server"; const frameworkVersion = currentCliVersion(); +export type WorkspaceEnvironment = "development" | "staging" | "production" | (string & {}); + +export interface WorkspaceEnvironmentConfig { + protocol?: "http" | "https"; + rootDomain?: string; + port?: number; +} + export interface WorkspaceApp { name: string; dir: string; - domains: string[]; + + /** + * Generates .. + * Example: subdomain "sso" + rootDomain "local.wrnexus.test". + */ + subdomain?: string; + + /** + * Explicit hostname overrides by environment. + */ + domains?: string[] | Partial>; + + /** + * Complete public URL override by environment. + */ + urls?: Partial>; + port?: number; - /** Per-app access control enforced at the gateway (basic auth, IP allowlist, forward-auth). */ auth?: GatewayAuth; } + export interface WorkspaceConfig { + defaultEnvironment?: WorkspaceEnvironment; + + environments?: Record; + apps: WorkspaceApp[]; - /** Gateway-wide security (trusted hosts, rate limit, headers, access log). */ security?: GatewaySecurity; } @@ -263,7 +290,13 @@ export async function addWorkspaceApp(root: string, name: string, args: string[] if (config.apps.some((app) => app.name === name || app.dir.replace(/\\/g, "/") === dir)) { throw new Error(`Workspace app '${name}' already exists.`); } - if (config.apps.some((app) => app.domains.includes(domain))) { + const normalizedDomain = domain.toLowerCase(); + + if ( + config.apps.some((app) => + configuredDomains(app).some((configured) => configured.toLowerCase() === normalizedDomain), + ) + ) { throw new Error(`Workspace domain '${domain}' is already assigned.`); } const appRoot = join(resolvedRoot, "apps", name); @@ -285,6 +318,24 @@ export async function addWorkspaceApp(root: string, name: string, args: string[] console.log("\nRun `bun install`, then `bun run dev`."); } +function configuredDomains(app: WorkspaceApp): string[] { + if (!app.domains) { + return []; + } + + if (Array.isArray(app.domains)) { + return app.domains; + } + + return Object.values(app.domains).flatMap((value) => { + if (!value) { + return []; + } + + return Array.isArray(value) ? value : [value]; + }); +} + /** Scaffold a monorepo workspace with two starter apps + a shared package. */ export function createWorkspace(name: string): void { if (!name) { @@ -329,24 +380,171 @@ export async function loadWorkspaceConfig(root: string): Promise { const config = await loadWorkspaceConfig(resolve(root)); - const portArg = args.find((a) => a.startsWith("--port=")); - const hostArg = args.find((a) => a.startsWith("--host=")); - const port = portArg ? Number(portArg.split("=")[1]) : 3000; + + const portArg = args.find((arg) => arg.startsWith("--port=")); + + const hostArg = args.find((arg) => arg.startsWith("--host=")); + + const environmentArg = args.find((arg) => arg.startsWith("--environment=")); + + const profileArg = args.find((arg) => arg.startsWith("--profile=")); + + const environment = + environmentArg?.split("=")[1] || + profileArg?.split("=")[1] || + process.env.WRNEXUS_ENV || + process.env.WRNEXUS_PROFILE || + config.defaultEnvironment || + (args.includes("--prod") ? "production" : "development"); + + const resolved = resolveWorkspaceConfig(config, environment); + + const port = portArg + ? Number(portArg.split("=")[1]) + : resolved.environment === "development" + ? 3000 + : 80; + const hostname = hostArg?.split("=")[1]; - const mode = args.includes("--prod") ? "production" : "development"; const { startGateway } = await import("@wrnexus/dev-server"); + await startGateway({ port, hostname, - mode, - security: config.security, - apps: config.apps.map((a) => ({ - name: a.name, - dir: resolve(root, a.dir), - domains: a.domains, - port: a.port, - auth: a.auth, + mode: environment === "production" ? "production" : "development", + environment, + security: resolved.security, + apps: resolved.apps.map((app) => ({ + name: app.name, + dir: resolve(root, app.dir), + domains: app.domains, + publicOrigin: app.publicOrigin, + port: app.port, + auth: app.auth, })), }); } + +function environmentVariableName(appName: string): string { + return `WRNEXUS_APP_${appName.replace(/[^A-Za-z0-9]/g, "_").toUpperCase()}_URL`; +} + +function normalizeOrigin(value: string): string { + const url = new URL(value); + + if (url.protocol !== "http:" && url.protocol !== "https:") { + throw new Error(`Application URL must use http or https: ${value}`); + } + + return url.origin; +} + +function valuesForEnvironment( + value: WorkspaceApp["domains"], + environment: WorkspaceEnvironment, +): string[] { + if (!value) return []; + + if (Array.isArray(value)) { + return value; + } + + const selected = value[environment]; + + if (!selected) return []; + + return Array.isArray(selected) ? selected : [selected]; +} + +export interface ResolvedWorkspaceApp extends WorkspaceApp { + domains: string[]; + publicOrigin: string; +} + +export interface ResolvedWorkspaceConfig { + environment: WorkspaceEnvironment; + apps: ResolvedWorkspaceApp[]; + security?: GatewaySecurity; +} + +export function resolveWorkspaceConfig( + config: WorkspaceConfig, + environment: WorkspaceEnvironment, +): ResolvedWorkspaceConfig { + const environmentConfig = config.environments?.[environment] ?? {}; + + const protocol = environmentConfig.protocol ?? (environment === "production" ? "https" : "http"); + + const configuredPort = environmentConfig.port; + + const apps = config.apps.map((app) => { + const envOverride = process.env[environmentVariableName(app.name)]; + + const configuredUrl = app.urls?.[environment]; + + const explicitDomains = valuesForEnvironment(app.domains, environment); + + const generatedDomain = + app.subdomain && environmentConfig.rootDomain + ? `${app.subdomain}.${environmentConfig.rootDomain}` + : undefined; + + const domains = + explicitDomains.length > 0 ? explicitDomains : generatedDomain ? [generatedDomain] : []; + + let publicOrigin: string; + + if (envOverride) { + publicOrigin = normalizeOrigin(envOverride); + } else if (configuredUrl) { + publicOrigin = normalizeOrigin(configuredUrl); + } else if (domains[0]) { + const port = + configuredPort && + !( + (protocol === "http" && configuredPort === 80) || + (protocol === "https" && configuredPort === 443) + ) + ? `:${configuredPort}` + : ""; + + publicOrigin = `${protocol}://${domains[0]}${port}`; + } else { + throw new Error( + `No domain or URL configured for workspace app "${app.name}" in environment "${environment}".`, + ); + } + + const publicUrl = new URL(publicOrigin); + + return { + ...app, + domains: domains.length > 0 ? domains : [publicUrl.hostname], + publicOrigin, + }; + }); + + const usedDomains = new Map(); + + for (const app of apps) { + for (const domain of app.domains) { + const normalized = domain.toLowerCase(); + const owner = usedDomains.get(normalized); + + if (owner) { + throw new Error( + `Workspace domain "${domain}" is assigned to both "${owner}" and "${app.name}".`, + ); + } + + usedDomains.set(normalized, app.name); + } + } + + return { + environment, + apps, + security: config.security, + }; +} diff --git a/packages/compiler/package.json b/packages/compiler/package.json index 9e408bb8..93fbe60b 100644 --- a/packages/compiler/package.json +++ b/packages/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/compiler", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/core/package.json b/packages/core/package.json index 5063a060..81ead3d2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/core", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/csr/package.json b/packages/csr/package.json index 451084c9..6d929aaf 100644 --- a/packages/csr/package.json +++ b/packages/csr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/csr", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/db/package.json b/packages/db/package.json index 0dcbe1b2..fe5cdf60 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/db", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/dev-server/package.json b/packages/dev-server/package.json index 6ecd932e..4ed22b4e 100644 --- a/packages/dev-server/package.json +++ b/packages/dev-server/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/dev-server", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/dev-server/src/gateway.ts b/packages/dev-server/src/gateway.ts index 15425736..f0592c3b 100644 --- a/packages/dev-server/src/gateway.ts +++ b/packages/dev-server/src/gateway.ts @@ -35,6 +35,7 @@ export interface GatewayApp { dir: string; /** Host names routed to this app (e.g. ["localhost", "web.localhost"]). */ domains: string[]; + publicOrigin?: string; /** Optional fixed internal port; otherwise assigned from the gateway port. */ port?: number; /** Access control enforced at the edge for this app. */ @@ -59,6 +60,7 @@ export interface GatewayOptions { port?: number; hostname?: string; mode?: "development" | "production"; + environment?: string; apps: GatewayApp[]; security?: GatewaySecurity; } @@ -79,7 +81,7 @@ interface WsBridge { origin: string; path: string; backend?: WebSocket; - queue: (string | ArrayBufferLike | ArrayBufferView)[]; + queue: Array; } /** Decide whether a gateway child should be relaunched after it exits. */ @@ -262,6 +264,11 @@ export async function startGateway(opts: GatewayOptions): Promise [app.name, app.publicOrigin ?? `http://${app.domains[0]}:${port}`]), + ); const displayHost = hostname === "0.0.0.0" || hostname === "::" ? "localhost" : hostname; const serveEntry = fileURLToPath(import.meta.resolve("@wrnexus/dev-server/serve-entry")); @@ -281,12 +288,28 @@ export async function startGateway(opts: GatewayOptions): Promise ws.send(e.data as string | ArrayBufferLike)); + backend.addEventListener("message", async (event) => { + const data = event.data; + + if (typeof data === "string") { + ws.send(data); + return; + } + + if (data instanceof Blob) { + const buffer = await data.arrayBuffer(); + ws.send(buffer); + return; + } + + if (data instanceof ArrayBuffer) { + ws.send(data); + return; + } + + if (ArrayBuffer.isView(data)) { + const buffer = data.buffer.slice( + data.byteOffset, + data.byteOffset + data.byteLength, + ) as ArrayBuffer; + + ws.send(buffer); + } + }); backend.addEventListener("close", () => ws.close()); backend.addEventListener("error", () => ws.close()); }, message(ws, message) { + let data: string | ArrayBuffer; + + if (typeof message === "string") { + data = message; + } else if (message instanceof ArrayBuffer) { + data = message; + } else { + const view = message as ArrayBufferView; + + data = view.buffer.slice( + view.byteOffset, + view.byteOffset + view.byteLength, + ) as ArrayBuffer; + } + const backend = ws.data.backend; - if (backend && backend.readyState === WebSocket.OPEN) backend.send(message); - else ws.data.queue.push(message); + + if (backend && backend.readyState === WebSocket.OPEN) { + backend.send(data); + } else { + ws.data.queue.push(data); + } }, close(ws) { ws.data.backend?.close(); diff --git a/packages/encryption/package.json b/packages/encryption/package.json index 43127462..8874108a 100644 --- a/packages/encryption/package.json +++ b/packages/encryption/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/encryption", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/helpers/package.json b/packages/helpers/package.json index 7e5fbcce..a5b4a43f 100644 --- a/packages/helpers/package.json +++ b/packages/helpers/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/helpers", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "description": "Safe convenience helpers for WrNexus request contexts and common application flows.", diff --git a/packages/helpers/src/index.ts b/packages/helpers/src/index.ts index 33a52a31..91b80f33 100644 --- a/packages/helpers/src/index.ts +++ b/packages/helpers/src/index.ts @@ -136,3 +136,11 @@ export function redirectToLogin( target.searchParams.set(options.returnToParam ?? "returnTo", original.href); return Response.redirect(target, options.status ?? 302); } + +export { + appOrigin, + appUrl, + currentAppName, + currentAppOrigin, + workspaceAppOrigins, +} from "./workspace.ts"; diff --git a/packages/helpers/src/workspace.ts b/packages/helpers/src/workspace.ts new file mode 100644 index 00000000..ca55bb49 --- /dev/null +++ b/packages/helpers/src/workspace.ts @@ -0,0 +1,58 @@ +function workspaceOrigins(): Record { + const value = process.env.WRNEXUS_WORKSPACE_ORIGINS; + + if (!value) { + return {}; + } + + try { + const parsed = JSON.parse(value); + + if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { + return {}; + } + + return parsed as Record; + } catch { + return {}; + } +} + +function normalizePath(path: string): string { + if (!path) return "/"; + + return path.startsWith("/") ? path : `/${path}`; +} + +export function appOrigin(appName: string): string { + const origins = workspaceOrigins(); + const origin = origins[appName]; + + if (!origin) { + throw new Error( + `Unknown workspace app "${appName}". Available apps: ${ + Object.keys(origins).join(", ") || "none" + }.`, + ); + } + + return new URL(origin).origin; +} + +export function appUrl(appName: string, path = "/"): string { + return new URL(normalizePath(path), `${appOrigin(appName)}/`).href; +} + +export function currentAppName(): string | undefined { + return process.env.WRNEXUS_APP_NAME; +} + +export function currentAppOrigin(): string | undefined { + const value = process.env.WRNEXUS_APP_ORIGIN; + + return value ? new URL(value).origin : undefined; +} + +export function workspaceAppOrigins(): Readonly> { + return Object.freeze({ ...workspaceOrigins() }); +} diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 7d8d7f8b..41da8a44 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/i18n", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/jwt/package.json b/packages/jwt/package.json index d5f46105..82073a7d 100644 --- a/packages/jwt/package.json +++ b/packages/jwt/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/jwt", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/mobile/package.json b/packages/mobile/package.json index bbe78391..17047d85 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/mobile", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/native/package.json b/packages/native/package.json index 7b909e02..9ddc61ab 100644 --- a/packages/native/package.json +++ b/packages/native/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/native", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/oauth/package.json b/packages/oauth/package.json index 987437eb..3371fe0a 100644 --- a/packages/oauth/package.json +++ b/packages/oauth/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/oauth", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/pubsub/package.json b/packages/pubsub/package.json index d71fda71..7fb273d0 100644 --- a/packages/pubsub/package.json +++ b/packages/pubsub/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/pubsub", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/queue/package.json b/packages/queue/package.json index 95fe539f..679f783d 100644 --- a/packages/queue/package.json +++ b/packages/queue/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/queue", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/reactive/package.json b/packages/reactive/package.json index bb611b35..33d6f626 100644 --- a/packages/reactive/package.json +++ b/packages/reactive/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/reactive", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/router/package.json b/packages/router/package.json index 8b94485c..496e6080 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/router", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/ssr/package.json b/packages/ssr/package.json index bc7c755a..ac49eceb 100644 --- a/packages/ssr/package.json +++ b/packages/ssr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ssr", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/styles/package.json b/packages/styles/package.json index 9be35def..bd78691b 100644 --- a/packages/styles/package.json +++ b/packages/styles/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/styles", - "version": "0.2.28", + "version": "0.2.29", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/test/package.json b/packages/test/package.json index f901b71b..17f3a3b3 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/test", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/tracking/package.json b/packages/tracking/package.json index ccb0a3de..86ff9254 100644 --- a/packages/tracking/package.json +++ b/packages/tracking/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/tracking", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index a8c9ca60..8e6ccb88 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ui", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/uploader/package.json b/packages/uploader/package.json index 07b29cab..ec0de20e 100644 --- a/packages/uploader/package.json +++ b/packages/uploader/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/uploader", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/validation/package.json b/packages/validation/package.json index 0d53a7b4..315b86f6 100644 --- a/packages/validation/package.json +++ b/packages/validation/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/validation", - "version": "0.2.28", + "version": "0.2.29", "private": true, "type": "module", "main": "src/index.ts",