release: WRNexusJS 0.2.29
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/ai",
|
"name": "@wrnexus/ai",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/authz",
|
"name": "@wrnexus/authz",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/authz — part of the WrNexus framework.",
|
"description": "@wrnexus/authz — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
+11
-11
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/cli",
|
"name": "@wrnexus/cli",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/cli — part of the WrNexus framework.",
|
"description": "@wrnexus/cli — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -28,16 +28,16 @@
|
|||||||
"wrnexus": "./dist/index.js"
|
"wrnexus": "./dist/index.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.28",
|
"@wrnexus/core": "^0.2.29",
|
||||||
"@wrnexus/router": "^0.2.28",
|
"@wrnexus/router": "^0.2.29",
|
||||||
"@wrnexus/csr": "^0.2.28",
|
"@wrnexus/csr": "^0.2.29",
|
||||||
"@wrnexus/compiler": "^0.2.28",
|
"@wrnexus/compiler": "^0.2.29",
|
||||||
"@wrnexus/styles": "^0.2.28",
|
"@wrnexus/styles": "^0.2.29",
|
||||||
"@wrnexus/dev-server": "^0.2.28",
|
"@wrnexus/dev-server": "^0.2.29",
|
||||||
"@wrnexus/ui": "^0.2.28",
|
"@wrnexus/ui": "^0.2.29",
|
||||||
"@wrnexus/validation": "^0.2.28",
|
"@wrnexus/validation": "^0.2.29",
|
||||||
"@wrnexus/i18n": "^0.2.28",
|
"@wrnexus/i18n": "^0.2.29",
|
||||||
"@wrnexus/db": "^0.2.28"
|
"@wrnexus/db": "^0.2.29"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/compiler",
|
"name": "@wrnexus/compiler",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/compiler — part of the WrNexus framework.",
|
"description": "@wrnexus/compiler — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/core",
|
"name": "@wrnexus/core",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/core — part of the WrNexus framework.",
|
"description": "@wrnexus/core — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/csr",
|
"name": "@wrnexus/csr",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/csr — part of the WrNexus framework.",
|
"description": "@wrnexus/csr — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.28"
|
"@wrnexus/core": "^0.2.29"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/db",
|
"name": "@wrnexus/db",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/db — part of the WrNexus framework.",
|
"description": "@wrnexus/db — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/dev-server",
|
"name": "@wrnexus/dev-server",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/dev-server — part of the WrNexus framework.",
|
"description": "@wrnexus/dev-server — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -25,18 +25,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.28",
|
"@wrnexus/core": "^0.2.29",
|
||||||
"@wrnexus/router": "^0.2.28",
|
"@wrnexus/router": "^0.2.29",
|
||||||
"@wrnexus/ssr": "^0.2.28",
|
"@wrnexus/ssr": "^0.2.29",
|
||||||
"@wrnexus/csr": "^0.2.28",
|
"@wrnexus/csr": "^0.2.29",
|
||||||
"@wrnexus/compiler": "^0.2.28",
|
"@wrnexus/compiler": "^0.2.29",
|
||||||
"@wrnexus/styles": "^0.2.28",
|
"@wrnexus/styles": "^0.2.29",
|
||||||
"@wrnexus/ui": "^0.2.28",
|
"@wrnexus/ui": "^0.2.29",
|
||||||
"@wrnexus/validation": "^0.2.28",
|
"@wrnexus/validation": "^0.2.29",
|
||||||
"@wrnexus/i18n": "^0.2.28",
|
"@wrnexus/i18n": "^0.2.29",
|
||||||
"@wrnexus/db": "^0.2.28",
|
"@wrnexus/db": "^0.2.29",
|
||||||
"@wrnexus/pubsub": "^0.2.28",
|
"@wrnexus/pubsub": "^0.2.29",
|
||||||
"@wrnexus/uploader": "^0.2.28"
|
"@wrnexus/uploader": "^0.2.29"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/encryption",
|
"name": "@wrnexus/encryption",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/encryption — part of the WrNexus framework.",
|
"description": "@wrnexus/encryption — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/helpers",
|
"name": "@wrnexus/helpers",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.28"
|
"@wrnexus/core": "^0.2.29"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/i18n",
|
"name": "@wrnexus/i18n",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/i18n — part of the WrNexus framework.",
|
"description": "@wrnexus/i18n — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.28"
|
"@wrnexus/core": "^0.2.29"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/jwt",
|
"name": "@wrnexus/jwt",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/jwt — part of the WrNexus framework.",
|
"description": "@wrnexus/jwt — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/mobile",
|
"name": "@wrnexus/mobile",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/mobile — part of the WrNexus framework.",
|
"description": "@wrnexus/mobile — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/native": "^0.2.28"
|
"@wrnexus/native": "^0.2.29"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/native",
|
"name": "@wrnexus/native",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/native — part of the WrNexus framework.",
|
"description": "@wrnexus/native — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/oauth",
|
"name": "@wrnexus/oauth",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/oauth — part of the WrNexus framework.",
|
"description": "@wrnexus/oauth — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/pubsub",
|
"name": "@wrnexus/pubsub",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/pubsub — part of the WrNexus framework.",
|
"description": "@wrnexus/pubsub — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/queue",
|
"name": "@wrnexus/queue",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/queue — part of the WrNexus framework.",
|
"description": "@wrnexus/queue — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/reactive",
|
"name": "@wrnexus/reactive",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/reactive — part of the WrNexus framework.",
|
"description": "@wrnexus/reactive — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/router",
|
"name": "@wrnexus/router",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/router — part of the WrNexus framework.",
|
"description": "@wrnexus/router — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,8 +21,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/compiler": "^0.2.28",
|
"@wrnexus/compiler": "^0.2.29",
|
||||||
"@wrnexus/core": "^0.2.28"
|
"@wrnexus/core": "^0.2.29"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/ssr",
|
"name": "@wrnexus/ssr",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/ssr — part of the WrNexus framework.",
|
"description": "@wrnexus/ssr — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.28"
|
"@wrnexus/core": "^0.2.29"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/styles",
|
"name": "@wrnexus/styles",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/styles — part of the WrNexus framework.",
|
"description": "@wrnexus/styles — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/uploader": "^0.2.28"
|
"@wrnexus/uploader": "^0.2.29"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/test",
|
"name": "@wrnexus/test",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/test — part of the WrNexus framework.",
|
"description": "@wrnexus/test — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/tracking",
|
"name": "@wrnexus/tracking",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/tracking — part of the WrNexus framework.",
|
"description": "@wrnexus/tracking — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/ui",
|
"name": "@wrnexus/ui",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/ui — part of the WrNexus framework.",
|
"description": "@wrnexus/ui — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"./ui.css": "./ui.css"
|
"./ui.css": "./ui.css"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.28"
|
"@wrnexus/core": "^0.2.29"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/uploader",
|
"name": "@wrnexus/uploader",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/uploader — part of the WrNexus framework.",
|
"description": "@wrnexus/uploader — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.28"
|
"@wrnexus/core": "^0.2.29"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/validation",
|
"name": "@wrnexus/validation",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/validation — part of the WrNexus framework.",
|
"description": "@wrnexus/validation — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "wrnexus",
|
"name": "wrnexus",
|
||||||
"displayName": "WRNexus Language Support",
|
"displayName": "WRNexus Language Support",
|
||||||
"description": "Syntax highlighting, formatting, diagnostics, snippets and completions for WrNexus .wrn files.",
|
"description": "Syntax highlighting, formatting, diagnostics, snippets and completions for WrNexus .wrn files.",
|
||||||
"version": "0.2.5",
|
"version": "0.2.6",
|
||||||
"publisher": "wrnexus",
|
"publisher": "wrnexus",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "SEE LICENSE IN LICENSE",
|
"license": "SEE LICENSE IN LICENSE",
|
||||||
|
|||||||
@@ -2,14 +2,10 @@
|
|||||||
|
|
||||||
const vscode = require("vscode");
|
const vscode = require("vscode");
|
||||||
|
|
||||||
const COMPONENT_DECLARATION =
|
const COMPONENT_DECLARATION = /^\s*(component|layout)\s+([A-Za-z_$][\w$]*)\s*\{/gm;
|
||||||
/^\s*(component|layout)\s+([A-Za-z_$][\w$]*)\s*\{/gm;
|
|
||||||
|
|
||||||
function getTagAtPosition(document, position) {
|
function getTagAtPosition(document, position) {
|
||||||
const range = document.getWordRangeAtPosition(
|
const range = document.getWordRangeAtPosition(position, /[A-Za-z_$][\w$]*/);
|
||||||
position,
|
|
||||||
/[A-Za-z_$][\w$]*/,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!range) {
|
if (!range) {
|
||||||
return null;
|
return null;
|
||||||
@@ -23,17 +19,13 @@ function getTagAtPosition(document, position) {
|
|||||||
|
|
||||||
const line = document.lineAt(position.line).text;
|
const line = document.lineAt(position.line).text;
|
||||||
const offset = document.offsetAt(position);
|
const offset = document.offsetAt(position);
|
||||||
const lineStart = document.offsetAt(
|
const lineStart = document.offsetAt(new vscode.Position(position.line, 0));
|
||||||
new vscode.Position(position.line, 0),
|
|
||||||
);
|
|
||||||
|
|
||||||
const characterOffset = offset - lineStart;
|
const characterOffset = offset - lineStart;
|
||||||
const before = line.slice(0, characterOffset);
|
const before = line.slice(0, characterOffset);
|
||||||
const after = line.slice(characterOffset);
|
const after = line.slice(characterOffset);
|
||||||
|
|
||||||
const insideTag =
|
const insideTag = before.lastIndexOf("<") > before.lastIndexOf(">") && after.includes(">");
|
||||||
before.lastIndexOf("<") > before.lastIndexOf(">") &&
|
|
||||||
after.includes(">");
|
|
||||||
|
|
||||||
if (!insideTag) {
|
if (!insideTag) {
|
||||||
return null;
|
return null;
|
||||||
@@ -75,21 +67,12 @@ async function findDeclaration(name) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nameOffset =
|
const nameOffset = match.index + match[0].indexOf(declarationName);
|
||||||
match.index +
|
|
||||||
match[0].indexOf(declarationName);
|
|
||||||
|
|
||||||
const start = document.positionAt(nameOffset);
|
const start = document.positionAt(nameOffset);
|
||||||
const end = document.positionAt(
|
const end = document.positionAt(nameOffset + declarationName.length);
|
||||||
nameOffset + declarationName.length,
|
|
||||||
);
|
|
||||||
|
|
||||||
matches.push(
|
matches.push(new vscode.Location(uri, new vscode.Range(start, end)));
|
||||||
new vscode.Location(
|
|
||||||
uri,
|
|
||||||
new vscode.Range(start, end),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,4 +114,4 @@ module.exports = {
|
|||||||
getTagAtPosition,
|
getTagAtPosition,
|
||||||
provideDefinition,
|
provideDefinition,
|
||||||
registerDefinitionProvider,
|
registerDefinitionProvider,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,14 +5,9 @@ const vscode = require("vscode");
|
|||||||
const COLLECTION_NAME = "wrnexus";
|
const COLLECTION_NAME = "wrnexus";
|
||||||
const WRN_LANGUAGE_ID = "wrn";
|
const WRN_LANGUAGE_ID = "wrn";
|
||||||
|
|
||||||
const TOP_LEVEL_PATTERN =
|
const TOP_LEVEL_PATTERN = /^\s*(page|component|layout)\s+([A-Za-z_$][\w$]*)\s*\{/;
|
||||||
/^\s*(page|component|layout)\s+([A-Za-z_$][\w$]*)\s*\{/;
|
|
||||||
|
|
||||||
const VALID_TOP_LEVEL_KINDS = new Set([
|
const VALID_TOP_LEVEL_KINDS = new Set(["page", "component", "layout"]);
|
||||||
"page",
|
|
||||||
"component",
|
|
||||||
"layout",
|
|
||||||
]);
|
|
||||||
|
|
||||||
const VALID_MEMBERS = {
|
const VALID_MEMBERS = {
|
||||||
page: new Set([
|
page: new Set([
|
||||||
@@ -28,21 +23,9 @@ const VALID_MEMBERS = {
|
|||||||
"realtime",
|
"realtime",
|
||||||
]),
|
]),
|
||||||
|
|
||||||
component: new Set([
|
component: new Set(["props", "state", "view", "style", "functions"]),
|
||||||
"props",
|
|
||||||
"state",
|
|
||||||
"view",
|
|
||||||
"style",
|
|
||||||
"functions",
|
|
||||||
]),
|
|
||||||
|
|
||||||
layout: new Set([
|
layout: new Set(["props", "state", "view", "style", "functions"]),
|
||||||
"props",
|
|
||||||
"state",
|
|
||||||
"view",
|
|
||||||
"style",
|
|
||||||
"functions",
|
|
||||||
]),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function createDiagnostic(
|
function createDiagnostic(
|
||||||
@@ -54,10 +37,7 @@ function createDiagnostic(
|
|||||||
code,
|
code,
|
||||||
) {
|
) {
|
||||||
const diagnostic = new vscode.Diagnostic(
|
const diagnostic = new vscode.Diagnostic(
|
||||||
new vscode.Range(
|
new vscode.Range(document.positionAt(startOffset), document.positionAt(endOffset)),
|
||||||
document.positionAt(startOffset),
|
|
||||||
document.positionAt(endOffset),
|
|
||||||
),
|
|
||||||
message,
|
message,
|
||||||
severity,
|
severity,
|
||||||
);
|
);
|
||||||
@@ -80,11 +60,7 @@ function lineDiagnostic(
|
|||||||
) {
|
) {
|
||||||
const line = document.lineAt(lineNumber);
|
const line = document.lineAt(lineNumber);
|
||||||
|
|
||||||
const diagnostic = new vscode.Diagnostic(
|
const diagnostic = new vscode.Diagnostic(line.range, message, severity);
|
||||||
line.range,
|
|
||||||
message,
|
|
||||||
severity,
|
|
||||||
);
|
|
||||||
|
|
||||||
diagnostic.source = "WRNexus";
|
diagnostic.source = "WRNexus";
|
||||||
|
|
||||||
@@ -96,18 +72,14 @@ function lineDiagnostic(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function stripComments(source) {
|
function stripComments(source) {
|
||||||
return source.replace(/<!--[\s\S]*?-->/g, (comment) =>
|
return source.replace(/<!--[\s\S]*?-->/g, (comment) => comment.replace(/[^\n]/g, " "));
|
||||||
comment.replace(/[^\n]/g, " "),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function findTopLevelDeclaration(document, source) {
|
function findTopLevelDeclaration(document, source) {
|
||||||
const match = TOP_LEVEL_PATTERN.exec(source);
|
const match = TOP_LEVEL_PATTERN.exec(source);
|
||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
const firstMeaningfulLine = source
|
const firstMeaningfulLine = source.split(/\r?\n/).findIndex((line) => line.trim().length > 0);
|
||||||
.split(/\r?\n/)
|
|
||||||
.findIndex((line) => line.trim().length > 0);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
diagnostic: lineDiagnostic(
|
diagnostic: lineDiagnostic(
|
||||||
@@ -166,9 +138,7 @@ function validateBalancedCharacters(document, source) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (source.startsWith("<!--", index)) {
|
||||||
source.startsWith("<!--", index)
|
|
||||||
) {
|
|
||||||
const commentEnd = source.indexOf("-->", index + 4);
|
const commentEnd = source.indexOf("-->", index + 4);
|
||||||
|
|
||||||
if (commentEnd === -1) {
|
if (commentEnd === -1) {
|
||||||
@@ -190,11 +160,7 @@ function validateBalancedCharacters(document, source) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (character === "{" || character === "[" || character === "(") {
|
||||||
character === "{" ||
|
|
||||||
character === "[" ||
|
|
||||||
character === "("
|
|
||||||
) {
|
|
||||||
stack.push({
|
stack.push({
|
||||||
character,
|
character,
|
||||||
offset: index,
|
offset: index,
|
||||||
@@ -203,11 +169,7 @@ function validateBalancedCharacters(document, source) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (character === "}" || character === "]" || character === ")") {
|
||||||
character === "}" ||
|
|
||||||
character === "]" ||
|
|
||||||
character === ")"
|
|
||||||
) {
|
|
||||||
const expectedOpening = pairs[character];
|
const expectedOpening = pairs[character];
|
||||||
const opening = stack.pop();
|
const opening = stack.pop();
|
||||||
|
|
||||||
@@ -227,12 +189,7 @@ function validateBalancedCharacters(document, source) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const opening of stack) {
|
for (const opening of stack) {
|
||||||
const expectedClosing =
|
const expectedClosing = opening.character === "{" ? "}" : opening.character === "[" ? "]" : ")";
|
||||||
opening.character === "{"
|
|
||||||
? "}"
|
|
||||||
: opening.character === "["
|
|
||||||
? "]"
|
|
||||||
: ")";
|
|
||||||
|
|
||||||
diagnostics.push(
|
diagnostics.push(
|
||||||
createDiagnostic(
|
createDiagnostic(
|
||||||
@@ -284,8 +241,7 @@ function validateHtmlTags(document, source) {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const cleaned = stripComments(source);
|
const cleaned = stripComments(source);
|
||||||
const tagPattern =
|
const tagPattern = /<\/?([A-Za-z][A-Za-z0-9_$:.-]*)(?:\s[\s\S]*?)?\/?>/g;
|
||||||
/<\/?([A-Za-z][A-Za-z0-9_$:.-]*)(?:\s[\s\S]*?)?\/?>/g;
|
|
||||||
|
|
||||||
let match;
|
let match;
|
||||||
|
|
||||||
@@ -358,18 +314,13 @@ function validateHtmlTags(document, source) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getRootBodyRange(source, rootMatch) {
|
function getRootBodyRange(source, rootMatch) {
|
||||||
const openingBrace =
|
const openingBrace = rootMatch.index + rootMatch[0].lastIndexOf("{");
|
||||||
rootMatch.index + rootMatch[0].lastIndexOf("{");
|
|
||||||
|
|
||||||
let depth = 0;
|
let depth = 0;
|
||||||
let quote = null;
|
let quote = null;
|
||||||
let escaped = false;
|
let escaped = false;
|
||||||
|
|
||||||
for (
|
for (let index = openingBrace; index < source.length; index += 1) {
|
||||||
let index = openingBrace;
|
|
||||||
index < source.length;
|
|
||||||
index += 1
|
|
||||||
) {
|
|
||||||
const character = source[index];
|
const character = source[index];
|
||||||
|
|
||||||
if (quote !== null) {
|
if (quote !== null) {
|
||||||
@@ -425,6 +376,12 @@ function findRootMembers(source, bodyStart, bodyEnd) {
|
|||||||
let quote = null;
|
let quote = null;
|
||||||
let escaped = false;
|
let escaped = false;
|
||||||
|
|
||||||
|
const skipLine = () => {
|
||||||
|
while (index < bodyEnd && source[index] !== "\n" && source[index] !== "\r") {
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
while (index < bodyEnd) {
|
while (index < bodyEnd) {
|
||||||
const character = source[index];
|
const character = source[index];
|
||||||
|
|
||||||
@@ -473,17 +430,11 @@ function findRootMembers(source, bodyStart, bodyEnd) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (depth === 0 && /[A-Za-z_]/.test(character)) {
|
||||||
depth === 0 &&
|
|
||||||
/[A-Za-z_]/.test(character)
|
|
||||||
) {
|
|
||||||
const start = index;
|
const start = index;
|
||||||
index += 1;
|
index += 1;
|
||||||
|
|
||||||
while (
|
while (index < bodyEnd && /[A-Za-z0-9_-]/.test(source[index])) {
|
||||||
index < bodyEnd &&
|
|
||||||
/[A-Za-z0-9_-]/.test(source[index])
|
|
||||||
) {
|
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,6 +446,11 @@ function findRootMembers(source, bodyStart, bodyEnd) {
|
|||||||
end: index,
|
end: index,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Single-line declarations must skip the rest of the line.
|
||||||
|
if (name === "state" || name === "layout") {
|
||||||
|
skipLine();
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,12 +460,7 @@ function findRootMembers(source, bodyStart, bodyEnd) {
|
|||||||
return members;
|
return members;
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateRootMembers(
|
function validateRootMembers(document, source, rootKind, rootMatch) {
|
||||||
document,
|
|
||||||
source,
|
|
||||||
rootKind,
|
|
||||||
rootMatch,
|
|
||||||
) {
|
|
||||||
const diagnostics = [];
|
const diagnostics = [];
|
||||||
const allowed = VALID_MEMBERS[rootKind];
|
const allowed = VALID_MEMBERS[rootKind];
|
||||||
|
|
||||||
@@ -518,11 +469,7 @@ function validateRootMembers(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bodyRange = getRootBodyRange(source, rootMatch);
|
const bodyRange = getRootBodyRange(source, rootMatch);
|
||||||
const members = findRootMembers(
|
const members = findRootMembers(source, bodyRange.start, bodyRange.end);
|
||||||
source,
|
|
||||||
bodyRange.start,
|
|
||||||
bodyRange.end,
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const member of members) {
|
for (const member of members) {
|
||||||
if (allowed.has(member.name)) {
|
if (allowed.has(member.name)) {
|
||||||
@@ -544,12 +491,7 @@ function validateRootMembers(
|
|||||||
return diagnostics;
|
return diagnostics;
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateRequiredView(
|
function validateRequiredView(document, source, rootKind, rootMatch) {
|
||||||
document,
|
|
||||||
source,
|
|
||||||
rootKind,
|
|
||||||
rootMatch,
|
|
||||||
) {
|
|
||||||
const bodyRange = getRootBodyRange(source, rootMatch);
|
const bodyRange = getRootBodyRange(source, rootMatch);
|
||||||
const body = source.slice(bodyRange.start, bodyRange.end);
|
const body = source.slice(bodyRange.start, bodyRange.end);
|
||||||
|
|
||||||
@@ -569,17 +511,10 @@ function validateRequiredView(
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateLayoutUsage(
|
function validateLayoutUsage(document, source, rootKind) {
|
||||||
document,
|
|
||||||
source,
|
|
||||||
rootKind,
|
|
||||||
) {
|
|
||||||
const diagnostics = [];
|
const diagnostics = [];
|
||||||
|
|
||||||
if (
|
if (rootKind !== "page" && /^\s*layout\s*=/m.test(source)) {
|
||||||
rootKind !== "page" &&
|
|
||||||
/^\s*layout\s*=/m.test(source)
|
|
||||||
) {
|
|
||||||
const match = /^\s*layout\s*=/m.exec(source);
|
const match = /^\s*layout\s*=/m.exec(source);
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
@@ -588,7 +523,7 @@ function validateLayoutUsage(
|
|||||||
document,
|
document,
|
||||||
match.index,
|
match.index,
|
||||||
match.index + match[0].length,
|
match.index + match[0].length,
|
||||||
"`layout = \"...\"` is only valid inside a page.",
|
'`layout = "..."` is only valid inside a page.',
|
||||||
vscode.DiagnosticSeverity.Error,
|
vscode.DiagnosticSeverity.Error,
|
||||||
"wrn-invalid-layout-member",
|
"wrn-invalid-layout-member",
|
||||||
),
|
),
|
||||||
@@ -611,16 +546,11 @@ function validateDocument(document) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const diagnostics = [];
|
const diagnostics = [];
|
||||||
const declaration = findTopLevelDeclaration(
|
const declaration = findTopLevelDeclaration(document, source);
|
||||||
document,
|
|
||||||
source,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (declaration.diagnostic) {
|
if (declaration.diagnostic) {
|
||||||
diagnostics.push(declaration.diagnostic);
|
diagnostics.push(declaration.diagnostic);
|
||||||
diagnostics.push(
|
diagnostics.push(...validateBalancedCharacters(document, source));
|
||||||
...validateBalancedCharacters(document, source),
|
|
||||||
);
|
|
||||||
|
|
||||||
return diagnostics;
|
return diagnostics;
|
||||||
}
|
}
|
||||||
@@ -630,8 +560,7 @@ function validateDocument(document) {
|
|||||||
createDiagnostic(
|
createDiagnostic(
|
||||||
document,
|
document,
|
||||||
declaration.match.index,
|
declaration.match.index,
|
||||||
declaration.match.index +
|
declaration.match.index + declaration.match[0].length,
|
||||||
declaration.match[0].length,
|
|
||||||
`Unsupported WRN declaration \`${declaration.kind}\`.`,
|
`Unsupported WRN declaration \`${declaration.kind}\`.`,
|
||||||
vscode.DiagnosticSeverity.Error,
|
vscode.DiagnosticSeverity.Error,
|
||||||
"wrn-invalid-kind",
|
"wrn-invalid-kind",
|
||||||
@@ -641,48 +570,21 @@ function validateDocument(document) {
|
|||||||
return diagnostics;
|
return diagnostics;
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostics.push(
|
diagnostics.push(...validateBalancedCharacters(document, source));
|
||||||
...validateBalancedCharacters(document, source),
|
|
||||||
);
|
|
||||||
|
|
||||||
diagnostics.push(
|
diagnostics.push(...validateHtmlTags(document, source));
|
||||||
...validateHtmlTags(document, source),
|
|
||||||
);
|
|
||||||
|
|
||||||
diagnostics.push(
|
diagnostics.push(...validateRootMembers(document, source, declaration.kind, declaration.match));
|
||||||
...validateRootMembers(
|
|
||||||
document,
|
|
||||||
source,
|
|
||||||
declaration.kind,
|
|
||||||
declaration.match,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
diagnostics.push(
|
diagnostics.push(...validateRequiredView(document, source, declaration.kind, declaration.match));
|
||||||
...validateRequiredView(
|
|
||||||
document,
|
|
||||||
source,
|
|
||||||
declaration.kind,
|
|
||||||
declaration.match,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
diagnostics.push(
|
diagnostics.push(...validateLayoutUsage(document, source, declaration.kind));
|
||||||
...validateLayoutUsage(
|
|
||||||
document,
|
|
||||||
source,
|
|
||||||
declaration.kind,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return diagnostics;
|
return diagnostics;
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerDiagnostics(context) {
|
function registerDiagnostics(context) {
|
||||||
const collection =
|
const collection = vscode.languages.createDiagnosticCollection(COLLECTION_NAME);
|
||||||
vscode.languages.createDiagnosticCollection(
|
|
||||||
COLLECTION_NAME,
|
|
||||||
);
|
|
||||||
|
|
||||||
const timers = new Map();
|
const timers = new Map();
|
||||||
|
|
||||||
@@ -691,9 +593,7 @@ function registerDiagnostics(context) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousTimer = timers.get(
|
const previousTimer = timers.get(document.uri.toString());
|
||||||
document.uri.toString(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (previousTimer) {
|
if (previousTimer) {
|
||||||
clearTimeout(previousTimer);
|
clearTimeout(previousTimer);
|
||||||
@@ -702,10 +602,7 @@ function registerDiagnostics(context) {
|
|||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
timers.delete(document.uri.toString());
|
timers.delete(document.uri.toString());
|
||||||
|
|
||||||
collection.set(
|
collection.set(document.uri, validateDocument(document));
|
||||||
document.uri,
|
|
||||||
validateDocument(document),
|
|
||||||
);
|
|
||||||
}, 150);
|
}, 150);
|
||||||
|
|
||||||
timers.set(document.uri.toString(), timer);
|
timers.set(document.uri.toString(), timer);
|
||||||
@@ -726,19 +623,17 @@ function registerDiagnostics(context) {
|
|||||||
|
|
||||||
vscode.workspace.onDidSaveTextDocument(update),
|
vscode.workspace.onDidSaveTextDocument(update),
|
||||||
|
|
||||||
vscode.workspace.onDidCloseTextDocument(
|
vscode.workspace.onDidCloseTextDocument((document) => {
|
||||||
(document) => {
|
const key = document.uri.toString();
|
||||||
const key = document.uri.toString();
|
const timer = timers.get(key);
|
||||||
const timer = timers.get(key);
|
|
||||||
|
|
||||||
if (timer) {
|
if (timer) {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
timers.delete(key);
|
timers.delete(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
collection.delete(document.uri);
|
collection.delete(document.uri);
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
|
|
||||||
{
|
{
|
||||||
dispose() {
|
dispose() {
|
||||||
@@ -758,4 +653,4 @@ module.exports = {
|
|||||||
validateDocument,
|
validateDocument,
|
||||||
validateHtmlTags,
|
validateHtmlTags,
|
||||||
validateRootMembers,
|
validateRootMembers,
|
||||||
};
|
};
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/ai",
|
"name": "@wrnexus/ai",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/authz",
|
"name": "@wrnexus/authz",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/cli",
|
"name": "@wrnexus/cli",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -329,6 +329,16 @@ const MIGRATIONS: Migration[] = [
|
|||||||
// No project file migration is required for this framework release.
|
// 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. */
|
/** Release tooling uses this to require an explicit migration entry per version. */
|
||||||
|
|||||||
+214
-16
@@ -19,17 +19,44 @@ import type { GatewayAuth, GatewaySecurity } from "@wrnexus/dev-server";
|
|||||||
|
|
||||||
const frameworkVersion = currentCliVersion();
|
const frameworkVersion = currentCliVersion();
|
||||||
|
|
||||||
|
export type WorkspaceEnvironment = "development" | "staging" | "production" | (string & {});
|
||||||
|
|
||||||
|
export interface WorkspaceEnvironmentConfig {
|
||||||
|
protocol?: "http" | "https";
|
||||||
|
rootDomain?: string;
|
||||||
|
port?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface WorkspaceApp {
|
export interface WorkspaceApp {
|
||||||
name: string;
|
name: string;
|
||||||
dir: string;
|
dir: string;
|
||||||
domains: string[];
|
|
||||||
|
/**
|
||||||
|
* Generates <subdomain>.<environment.rootDomain>.
|
||||||
|
* Example: subdomain "sso" + rootDomain "local.wrnexus.test".
|
||||||
|
*/
|
||||||
|
subdomain?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Explicit hostname overrides by environment.
|
||||||
|
*/
|
||||||
|
domains?: string[] | Partial<Record<WorkspaceEnvironment, string | string[]>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete public URL override by environment.
|
||||||
|
*/
|
||||||
|
urls?: Partial<Record<WorkspaceEnvironment, string>>;
|
||||||
|
|
||||||
port?: number;
|
port?: number;
|
||||||
/** Per-app access control enforced at the gateway (basic auth, IP allowlist, forward-auth). */
|
|
||||||
auth?: GatewayAuth;
|
auth?: GatewayAuth;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkspaceConfig {
|
export interface WorkspaceConfig {
|
||||||
|
defaultEnvironment?: WorkspaceEnvironment;
|
||||||
|
|
||||||
|
environments?: Record<WorkspaceEnvironment, WorkspaceEnvironmentConfig>;
|
||||||
|
|
||||||
apps: WorkspaceApp[];
|
apps: WorkspaceApp[];
|
||||||
/** Gateway-wide security (trusted hosts, rate limit, headers, access log). */
|
|
||||||
security?: GatewaySecurity;
|
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)) {
|
if (config.apps.some((app) => app.name === name || app.dir.replace(/\\/g, "/") === dir)) {
|
||||||
throw new Error(`Workspace app '${name}' already exists.`);
|
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.`);
|
throw new Error(`Workspace domain '${domain}' is already assigned.`);
|
||||||
}
|
}
|
||||||
const appRoot = join(resolvedRoot, "apps", name);
|
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`.");
|
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. */
|
/** Scaffold a monorepo workspace with two starter apps + a shared package. */
|
||||||
export function createWorkspace(name: string): void {
|
export function createWorkspace(name: string): void {
|
||||||
if (!name) {
|
if (!name) {
|
||||||
@@ -329,24 +380,171 @@ export async function loadWorkspaceConfig(root: string): Promise<WorkspaceConfig
|
|||||||
/** Run the multi-app gateway from `wrnexus.workspace.ts`. */
|
/** Run the multi-app gateway from `wrnexus.workspace.ts`. */
|
||||||
export async function runGateway(root: string, args: string[]): Promise<void> {
|
export async function runGateway(root: string, args: string[]): Promise<void> {
|
||||||
const config = await loadWorkspaceConfig(resolve(root));
|
const config = await loadWorkspaceConfig(resolve(root));
|
||||||
const portArg = args.find((a) => a.startsWith("--port="));
|
|
||||||
const hostArg = args.find((a) => a.startsWith("--host="));
|
const portArg = args.find((arg) => arg.startsWith("--port="));
|
||||||
const port = portArg ? Number(portArg.split("=")[1]) : 3000;
|
|
||||||
|
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 hostname = hostArg?.split("=")[1];
|
||||||
const mode = args.includes("--prod") ? "production" : "development";
|
|
||||||
|
|
||||||
const { startGateway } = await import("@wrnexus/dev-server");
|
const { startGateway } = await import("@wrnexus/dev-server");
|
||||||
|
|
||||||
await startGateway({
|
await startGateway({
|
||||||
port,
|
port,
|
||||||
hostname,
|
hostname,
|
||||||
mode,
|
mode: environment === "production" ? "production" : "development",
|
||||||
security: config.security,
|
environment,
|
||||||
apps: config.apps.map((a) => ({
|
security: resolved.security,
|
||||||
name: a.name,
|
apps: resolved.apps.map((app) => ({
|
||||||
dir: resolve(root, a.dir),
|
name: app.name,
|
||||||
domains: a.domains,
|
dir: resolve(root, app.dir),
|
||||||
port: a.port,
|
domains: app.domains,
|
||||||
auth: a.auth,
|
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<string, string>();
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/compiler",
|
"name": "@wrnexus/compiler",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/core",
|
"name": "@wrnexus/core",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/csr",
|
"name": "@wrnexus/csr",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/db",
|
"name": "@wrnexus/db",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/dev-server",
|
"name": "@wrnexus/dev-server",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export interface GatewayApp {
|
|||||||
dir: string;
|
dir: string;
|
||||||
/** Host names routed to this app (e.g. ["localhost", "web.localhost"]). */
|
/** Host names routed to this app (e.g. ["localhost", "web.localhost"]). */
|
||||||
domains: string[];
|
domains: string[];
|
||||||
|
publicOrigin?: string;
|
||||||
/** Optional fixed internal port; otherwise assigned from the gateway port. */
|
/** Optional fixed internal port; otherwise assigned from the gateway port. */
|
||||||
port?: number;
|
port?: number;
|
||||||
/** Access control enforced at the edge for this app. */
|
/** Access control enforced at the edge for this app. */
|
||||||
@@ -59,6 +60,7 @@ export interface GatewayOptions {
|
|||||||
port?: number;
|
port?: number;
|
||||||
hostname?: string;
|
hostname?: string;
|
||||||
mode?: "development" | "production";
|
mode?: "development" | "production";
|
||||||
|
environment?: string;
|
||||||
apps: GatewayApp[];
|
apps: GatewayApp[];
|
||||||
security?: GatewaySecurity;
|
security?: GatewaySecurity;
|
||||||
}
|
}
|
||||||
@@ -79,7 +81,7 @@ interface WsBridge {
|
|||||||
origin: string;
|
origin: string;
|
||||||
path: string;
|
path: string;
|
||||||
backend?: WebSocket;
|
backend?: WebSocket;
|
||||||
queue: (string | ArrayBufferLike | ArrayBufferView)[];
|
queue: Array<string | ArrayBuffer>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Decide whether a gateway child should be relaunched after it exits. */
|
/** Decide whether a gateway child should be relaunched after it exits. */
|
||||||
@@ -262,6 +264,11 @@ export async function startGateway(opts: GatewayOptions): Promise<RunningGateway
|
|||||||
const port = opts.port ?? 3000;
|
const port = opts.port ?? 3000;
|
||||||
const mode = opts.mode ?? "development";
|
const mode = opts.mode ?? "development";
|
||||||
const hostname = opts.hostname ?? defaultGatewayHostname(mode);
|
const hostname = opts.hostname ?? defaultGatewayHostname(mode);
|
||||||
|
const environment = opts.environment ?? (mode === "production" ? "production" : "development");
|
||||||
|
|
||||||
|
const workspaceOrigins = Object.fromEntries(
|
||||||
|
opts.apps.map((app) => [app.name, app.publicOrigin ?? `http://${app.domains[0]}:${port}`]),
|
||||||
|
);
|
||||||
const displayHost = hostname === "0.0.0.0" || hostname === "::" ? "localhost" : hostname;
|
const displayHost = hostname === "0.0.0.0" || hostname === "::" ? "localhost" : hostname;
|
||||||
const serveEntry = fileURLToPath(import.meta.resolve("@wrnexus/dev-server/serve-entry"));
|
const serveEntry = fileURLToPath(import.meta.resolve("@wrnexus/dev-server/serve-entry"));
|
||||||
|
|
||||||
@@ -281,12 +288,28 @@ export async function startGateway(opts: GatewayOptions): Promise<RunningGateway
|
|||||||
mode === "production"
|
mode === "production"
|
||||||
? spawn(process.execPath, [join(dir, "dist", "server.js")], {
|
? spawn(process.execPath, [join(dir, "dist", "server.js")], {
|
||||||
stdio: "inherit",
|
stdio: "inherit",
|
||||||
env: { ...process.env, PORT: String(appPort) },
|
env: {
|
||||||
|
...process.env,
|
||||||
|
PORT: String(appPort),
|
||||||
|
WRNEXUS_ENV: environment,
|
||||||
|
WRNEXUS_APP_NAME: app.name,
|
||||||
|
WRNEXUS_APP_ORIGIN: app.publicOrigin ?? `http://${app.domains[0]}:${port}`,
|
||||||
|
WRNEXUS_WORKSPACE_ORIGINS: JSON.stringify(workspaceOrigins),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
: spawn(
|
: spawn(
|
||||||
process.execPath,
|
process.execPath,
|
||||||
[serveEntry, join(dir, "app"), String(appPort), mode, "127.0.0.1"],
|
[serveEntry, join(dir, "app"), String(appPort), mode, "127.0.0.1"],
|
||||||
{ stdio: "inherit" },
|
{
|
||||||
|
stdio: "inherit",
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
WRNEXUS_ENV: environment,
|
||||||
|
WRNEXUS_APP_NAME: app.name,
|
||||||
|
WRNEXUS_APP_ORIGIN: app.publicOrigin ?? `http://${app.domains[0]}:${port}`,
|
||||||
|
WRNEXUS_WORKSPACE_ORIGINS: JSON.stringify(workspaceOrigins),
|
||||||
|
},
|
||||||
|
},
|
||||||
);
|
);
|
||||||
target.child = child;
|
target.child = child;
|
||||||
|
|
||||||
@@ -415,14 +438,60 @@ export async function startGateway(opts: GatewayOptions): Promise<RunningGateway
|
|||||||
for (const m of ws.data.queue) backend.send(m);
|
for (const m of ws.data.queue) backend.send(m);
|
||||||
ws.data.queue = [];
|
ws.data.queue = [];
|
||||||
});
|
});
|
||||||
backend.addEventListener("message", (e) => 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("close", () => ws.close());
|
||||||
backend.addEventListener("error", () => ws.close());
|
backend.addEventListener("error", () => ws.close());
|
||||||
},
|
},
|
||||||
message(ws, message) {
|
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;
|
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) {
|
close(ws) {
|
||||||
ws.data.backend?.close();
|
ws.data.backend?.close();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/encryption",
|
"name": "@wrnexus/encryption",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/helpers",
|
"name": "@wrnexus/helpers",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
||||||
|
|||||||
@@ -136,3 +136,11 @@ export function redirectToLogin(
|
|||||||
target.searchParams.set(options.returnToParam ?? "returnTo", original.href);
|
target.searchParams.set(options.returnToParam ?? "returnTo", original.href);
|
||||||
return Response.redirect(target, options.status ?? 302);
|
return Response.redirect(target, options.status ?? 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
appOrigin,
|
||||||
|
appUrl,
|
||||||
|
currentAppName,
|
||||||
|
currentAppOrigin,
|
||||||
|
workspaceAppOrigins,
|
||||||
|
} from "./workspace.ts";
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
function workspaceOrigins(): Record<string, string> {
|
||||||
|
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<string, string>;
|
||||||
|
} 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<Record<string, string>> {
|
||||||
|
return Object.freeze({ ...workspaceOrigins() });
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/i18n",
|
"name": "@wrnexus/i18n",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/jwt",
|
"name": "@wrnexus/jwt",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/mobile",
|
"name": "@wrnexus/mobile",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/native",
|
"name": "@wrnexus/native",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/oauth",
|
"name": "@wrnexus/oauth",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/pubsub",
|
"name": "@wrnexus/pubsub",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/queue",
|
"name": "@wrnexus/queue",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/reactive",
|
"name": "@wrnexus/reactive",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/router",
|
"name": "@wrnexus/router",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/ssr",
|
"name": "@wrnexus/ssr",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/styles",
|
"name": "@wrnexus/styles",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/test",
|
"name": "@wrnexus/test",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/tracking",
|
"name": "@wrnexus/tracking",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/ui",
|
"name": "@wrnexus/ui",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/uploader",
|
"name": "@wrnexus/uploader",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/validation",
|
"name": "@wrnexus/validation",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
Reference in New Issue
Block a user