commit e5fbcb0ea8263bac1f635874e9a80a46bc93a6af
parent 91cddd459229ae80fdbdf83dbbe8a8e07d2df5d7
Author: triesap <tyson@radroots.org>
Date: Thu, 11 Jun 2026 14:39:38 -0700
web: rename app package and require managed env
- rename the workspace packages to radroots-web and radroots-web-app
- switch app env consumption to RADROOTS_WEB-prefixed variables
- require RADROOTS_WEB_APP_ENV_FILE for Vite builds and dev runs
- fix app type checks surfaced by the local web runtime command
Diffstat:
10 files changed, 107 insertions(+), 70 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
@@ -65,27 +65,23 @@ git submodule foreach 'git checkout $(git config -f $toplevel/.gitmodules submod
Install the application dependencies:
```bash
-yarn
+pnpm install
```
Configure local environment variables:
```bash
-echo 'VITE_PUBLIC_DEFAULT_RELAYS=ws://localhost:8080,ws://localhost:8081
-VITE_PUBLIC_RADROOTS_RELAY=ws://localhost:8082
-VITE_PUBLIC_RADROOTS_API=https://radroots.org
-VITE_PUBLIC_KEYVAL_NAME=rad-roots-pwa-dev-v1
-VITE_PUBLIC_NOSTR_CLIENT=ยป-`-,- rad roots' > app/.env.development
+(cd ../../../.. && uv run --project . --package radroots_scripts radroots-scripts local env generate)
```
Build the application:
```bash
-yarn build
+pnpm build:app
```
Run the application in development mode:
```bash
-yarn dev
+(cd ../../../.. && uv run --project . --package radroots_scripts radroots-scripts local web app run)
```
## Contributing
@@ -105,4 +101,4 @@ yarn dev
## License
-Refer to the LICENSE file in the repository for terms of use and distribution.
-\ No newline at end of file
+Refer to the LICENSE file in the repository for terms of use and distribution.
diff --git a/app/.env.example b/app/.env.example
@@ -1,12 +1,15 @@
-VITE_PUBLIC_DEFAULT_RELAYS=
-VITE_PUBLIC_RADROOTS_API=
-VITE_PUBLIC_RADROOTS_MEDIA=
-VITE_PUBLIC_KEYVAL_NAME=
-VITE_PUBLIC_SQL_WASM_URL=
-VITE_PUBLIC_GEOCODER_DB_URL=
-VITE_PUBLIC_NOSTR_CLIENT=
-PORT=
-VITE_PUBLIC_RADROOTS_RELAY=
-VITE_PLATFORM_NAME=
-VITE_PLATFORM_ACCENT=
-VITE_PLATFORM_DESCRIPTION=
+RADROOTS_WEB_APP_ENV_FILE=
+RADROOTS_WEB_APP_RUNTIME_MODE=
+RADROOTS_WEB_APP_DEV_HOST=
+RADROOTS_WEB_APP_DEV_PORT=
+RADROOTS_WEB_DEFAULT_RELAY_URLS=
+RADROOTS_WEB_RELAY_URL=
+RADROOTS_WEB_API_BASE_URL=
+RADROOTS_WEB_MEDIA_BASE_URL=
+RADROOTS_WEB_KEYVAL_NAME=
+RADROOTS_WEB_SQL_WASM_URL=
+RADROOTS_WEB_GEOCODER_DB_URL=
+RADROOTS_WEB_NOSTR_CLIENT=
+RADROOTS_WEB_APP_NAME=
+RADROOTS_WEB_APP_ACCENT=
+RADROOTS_WEB_APP_DESCRIPTION=
diff --git a/app/package.json b/app/package.json
@@ -1,5 +1,5 @@
{
- "name": "app",
+ "name": "radroots-web-app",
"private": true,
"version": "0.0.1",
"license": "GPL-3.0",
@@ -53,4 +53,4 @@
"svelte-maplibre": "^1.2.0",
"zod": "^3.23.8"
}
-}
-\ No newline at end of file
+}
diff --git a/app/src/lib/_env.ts b/app/src/lib/_env.ts
@@ -1,35 +1,35 @@
-const DEFAULT_RELAYS = import.meta.env.VITE_PUBLIC_DEFAULT_RELAYS;
-if (!DEFAULT_RELAYS || typeof DEFAULT_RELAYS !== 'string') throw new Error('Missing env var: VITE_PUBLIC_DEFAULT_RELAYS');
+const DEFAULT_RELAYS = import.meta.env.RADROOTS_WEB_DEFAULT_RELAY_URLS;
+if (!DEFAULT_RELAYS || typeof DEFAULT_RELAYS !== 'string') throw new Error('Missing env var: RADROOTS_WEB_DEFAULT_RELAY_URLS');
-const RADROOTS_API = import.meta.env.VITE_PUBLIC_RADROOTS_API;
-if (!RADROOTS_API || typeof RADROOTS_API !== 'string') throw new Error('Missing env var: VITE_PUBLIC_RADROOTS_API');
+const RADROOTS_API = import.meta.env.RADROOTS_WEB_API_BASE_URL;
+if (!RADROOTS_API || typeof RADROOTS_API !== 'string') throw new Error('Missing env var: RADROOTS_WEB_API_BASE_URL');
-const RADROOTS_MEDIA = import.meta.env.VITE_PUBLIC_RADROOTS_MEDIA;
-if (!RADROOTS_MEDIA || typeof RADROOTS_MEDIA !== 'string') throw new Error('Missing env var: VITE_PUBLIC_RADROOTS_MEDIA');
+const RADROOTS_MEDIA = import.meta.env.RADROOTS_WEB_MEDIA_BASE_URL;
+if (!RADROOTS_MEDIA || typeof RADROOTS_MEDIA !== 'string') throw new Error('Missing env var: RADROOTS_WEB_MEDIA_BASE_URL');
-const KEYVAL_NAME = import.meta.env.VITE_PUBLIC_KEYVAL_NAME;
-if (!KEYVAL_NAME || typeof KEYVAL_NAME !== 'string') throw new Error('Missing env var: VITE_PUBLIC_KEYVAL_NAME');
+const KEYVAL_NAME = import.meta.env.RADROOTS_WEB_KEYVAL_NAME;
+if (!KEYVAL_NAME || typeof KEYVAL_NAME !== 'string') throw new Error('Missing env var: RADROOTS_WEB_KEYVAL_NAME');
-const SQL_WASM_URL = import.meta.env.VITE_PUBLIC_SQL_WASM_URL;
-if (!SQL_WASM_URL || typeof SQL_WASM_URL !== 'string') throw new Error('Missing env var: VITE_PUBLIC_SQL_WASM_URL');
+const SQL_WASM_URL = import.meta.env.RADROOTS_WEB_SQL_WASM_URL;
+if (!SQL_WASM_URL || typeof SQL_WASM_URL !== 'string') throw new Error('Missing env var: RADROOTS_WEB_SQL_WASM_URL');
-const GEOCODER_DB_URL = import.meta.env.VITE_PUBLIC_GEOCODER_DB_URL;
-if (!GEOCODER_DB_URL || typeof GEOCODER_DB_URL !== 'string') throw new Error('Missing env var: VITE_PUBLIC_GEOCODER_DB_URL');
+const GEOCODER_DB_URL = import.meta.env.RADROOTS_WEB_GEOCODER_DB_URL;
+if (!GEOCODER_DB_URL || typeof GEOCODER_DB_URL !== 'string') throw new Error('Missing env var: RADROOTS_WEB_GEOCODER_DB_URL');
-const NOSTR_CLIENT = import.meta.env.VITE_PUBLIC_NOSTR_CLIENT;
-if (!NOSTR_CLIENT || typeof NOSTR_CLIENT !== 'string') throw new Error('Missing env var: VITE_PUBLIC_NOSTR_CLIENT');
+const NOSTR_CLIENT = import.meta.env.RADROOTS_WEB_NOSTR_CLIENT;
+if (!NOSTR_CLIENT || typeof NOSTR_CLIENT !== 'string') throw new Error('Missing env var: RADROOTS_WEB_NOSTR_CLIENT');
-const RADROOTS_RELAY = import.meta.env.VITE_PUBLIC_RADROOTS_RELAY;
-if (!RADROOTS_RELAY || typeof RADROOTS_RELAY !== 'string') throw new Error('Missing env var: VITE_PUBLIC_RADROOTS_RELAY');
+const RADROOTS_RELAY = import.meta.env.RADROOTS_WEB_RELAY_URL;
+if (!RADROOTS_RELAY || typeof RADROOTS_RELAY !== 'string') throw new Error('Missing env var: RADROOTS_WEB_RELAY_URL');
-const PLATFORM_NAME = import.meta.env.VITE_PLATFORM_NAME;
-if (!PLATFORM_NAME || typeof PLATFORM_NAME !== 'string') throw new Error('Missing env var: VITE_PLATFORM_NAME');
+const PLATFORM_NAME = import.meta.env.RADROOTS_WEB_APP_NAME;
+if (!PLATFORM_NAME || typeof PLATFORM_NAME !== 'string') throw new Error('Missing env var: RADROOTS_WEB_APP_NAME');
-const PLATFORM_ACCENT = import.meta.env.VITE_PLATFORM_ACCENT;
-if (!PLATFORM_ACCENT || typeof PLATFORM_ACCENT !== 'string') throw new Error('Missing env var: VITE_PLATFORM_ACCENT');
+const PLATFORM_ACCENT = import.meta.env.RADROOTS_WEB_APP_ACCENT;
+if (!PLATFORM_ACCENT || typeof PLATFORM_ACCENT !== 'string') throw new Error('Missing env var: RADROOTS_WEB_APP_ACCENT');
-const PLATFORM_DESCRIPTION = import.meta.env.VITE_PLATFORM_DESCRIPTION;
-if (!PLATFORM_DESCRIPTION || typeof PLATFORM_DESCRIPTION !== 'string') throw new Error('Missing env var: VITE_PLATFORM_DESCRIPTION');
+const PLATFORM_DESCRIPTION = import.meta.env.RADROOTS_WEB_APP_DESCRIPTION;
+if (!PLATFORM_DESCRIPTION || typeof PLATFORM_DESCRIPTION !== 'string') throw new Error('Missing env var: RADROOTS_WEB_APP_DESCRIPTION');
const PROD = import.meta.env.MODE === 'production';
diff --git a/app/src/lib/utils/backup/import.ts b/app/src/lib/utils/backup/import.ts
@@ -1,6 +1,7 @@
import { datastore, db, nostr_keys } from "$lib/utils/app";
import { ls } from "$lib/utils/i18n";
import { get_store, handle_err, parse_file_json } from "@radroots/apps-lib";
+import type { I18nPayloadValue } from "@radroots/apps-lib";
import type { ImportableAppState } from "@radroots/apps-lib-pwa/types/app";
import type { IError } from "@radroots/types-bindings";
import type { IdbClientConfig } from "@radroots/utils";
@@ -19,6 +20,9 @@ export type AppImportStateResult = {
const is_record = (value: unknown): value is Record<string, unknown> =>
typeof value === "object" && value !== null && !Array.isArray(value);
+const i18n_payload_value = (value: unknown): I18nPayloadValue =>
+ typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? value : "unknown";
+
const assert_config_match = (
current: IdbClientConfig,
incoming: IdbClientConfig,
@@ -48,7 +52,7 @@ export const validate_import_state = async (state: unknown): Promise<ImportableA
if (state.backup_version !== app_cfg.backup.version) {
throw_err(
ls_val(`error.configuration.import.unsupported_backup_version`, {
- backup_version: state.backup_version,
+ backup_version: i18n_payload_value(state.backup_version),
expected_version: app_cfg.backup.version,
})
);
diff --git a/app/src/routes/(cfg)/setup/+page.svelte b/app/src/routes/(cfg)/setup/+page.svelte
@@ -154,17 +154,19 @@
let farm_map_geop: GeolocationPoint | undefined = $state(undefined);
let farm_map_geoc: GeocoderReverseResult | undefined = $state(undefined);
- const farm_geop_lat = $derived(
- geop_is_valid(farm_map_geop)
- ? geol_lat_fmt(farm_map_geop.lat, `dms`, $locale, 3)
- : ``,
- );
+ const farm_geop_lat = $derived.by(() => {
+ const farm_map_geop_value = farm_map_geop;
+ return geop_is_valid(farm_map_geop_value)
+ ? geol_lat_fmt(farm_map_geop_value.lat, `dms`, $locale, 3)
+ : ``;
+ });
- const farm_geop_lng = $derived(
- geop_is_valid(farm_map_geop)
- ? geol_lng_fmt(farm_map_geop.lng, `dms`, $locale, 3)
- : ``,
- );
+ const farm_geop_lng = $derived.by(() => {
+ const farm_map_geop_value = farm_map_geop;
+ return geop_is_valid(farm_map_geop_value)
+ ? geol_lng_fmt(farm_map_geop_value.lng, `dms`, $locale, 3)
+ : ``;
+ });
let is_eula_scrolled = $state(false);
let is_loading_s = $state(false);
diff --git a/app/src/service-worker.js b/app/src/service-worker.js
@@ -30,12 +30,12 @@ const SQL_WASM_ENV = normalize_env_path(_env.SQL_WASM_URL);
const SQL_WASM_URL = SQL_WASM_ENV
? ensure_env_wasm_path(
SQL_WASM_ENV,
- "VITE_PUBLIC_SQL_WASM_URL",
+ "RADROOTS_WEB_SQL_WASM_URL",
)
: DEFAULT_SQL_WASM_PATH;
const GEOCODER_DB_ENV = normalize_env_path(_env.GEOCODER_DB_URL);
const GEOCODER_DB_URL = GEOCODER_DB_ENV
- ? ensure_env_asset_path(GEOCODER_DB_ENV, "VITE_PUBLIC_GEOCODER_DB_URL")
+ ? ensure_env_asset_path(GEOCODER_DB_ENV, "RADROOTS_WEB_GEOCODER_DB_URL")
: DEFAULT_GEOCODER_DATABASE_PATH;
const ASSET_URLS = [...new Set([SQL_WASM_URL, GEOCODER_DB_URL])];
const PRECACHE_URLS = [...new Set([...build, ...files, ...prerendered, APP_SHELL_URL])].filter(
diff --git a/app/vite.config.ts b/app/vite.config.ts
@@ -7,21 +7,50 @@ import path from "node:path";
import { defineConfig } from "vite";
export default defineConfig(({ mode }) => {
- dotenv_config({ path: mode === "development" ? ".env.development" : ".env.production" });
- const repo_root = path.resolve(__dirname, "..");
+ const web_repo_root = path.resolve(__dirname, "..");
+ const web_app_env_file = process.env.RADROOTS_WEB_APP_ENV_FILE;
+ if (!web_app_env_file) throw new Error("Missing env var: RADROOTS_WEB_APP_ENV_FILE");
+ dotenv_config({ path: path.resolve(web_app_env_file), override: true });
const app_package_path = path.resolve(__dirname, "package.json");
const app_package = JSON.parse(readFileSync(app_package_path, "utf8")) as { name?: string; version?: string };
const git_hash = (() => {
try {
- return execSync("git rev-parse HEAD", { cwd: repo_root }).toString().trim();
+ return execSync("git rev-parse HEAD", { cwd: web_repo_root }).toString().trim();
} catch {
return "unknown";
}
})();
+ const required_env = (key: string): string => {
+ const value = process.env[key];
+ if (!value) throw new Error(`Missing env var: ${key}`);
+ return value;
+ };
+ const required_package_string = (key: "name" | "version"): string => {
+ const value = app_package[key];
+ if (!value) throw new Error(`app package ${key} must be defined`);
+ return value;
+ };
+ const web_env_keys = [
+ "RADROOTS_WEB_APP_ACCENT",
+ "RADROOTS_WEB_APP_DESCRIPTION",
+ "RADROOTS_WEB_APP_NAME",
+ "RADROOTS_WEB_API_BASE_URL",
+ "RADROOTS_WEB_DEFAULT_RELAY_URLS",
+ "RADROOTS_WEB_GEOCODER_DB_URL",
+ "RADROOTS_WEB_KEYVAL_NAME",
+ "RADROOTS_WEB_MEDIA_BASE_URL",
+ "RADROOTS_WEB_NOSTR_CLIENT",
+ "RADROOTS_WEB_RELAY_URL",
+ "RADROOTS_WEB_SQL_WASM_URL",
+ ] as const;
+ const web_env_define = Object.fromEntries(
+ web_env_keys.map((key) => [`import.meta.env.${key}`, JSON.stringify(required_env(key))])
+ );
return {
build: {
sourcemap: true
},
+ envPrefix: "RADROOTS_WEB_",
plugins: [
tailwindcss(),
sveltekit(),
@@ -29,11 +58,13 @@ export default defineConfig(({ mode }) => {
define: {
'process.env.NODE_ENV': `"${mode}"`,
'__APP_GIT_HASH__': JSON.stringify(git_hash),
- '__APP_NAME__': JSON.stringify(`radroots/${app_package.name ?? "app"}`),
- '__APP_VERSION__': JSON.stringify(app_package.version ?? "0.0.0"),
+ '__APP_NAME__': JSON.stringify(`radroots/${required_package_string("name")}`),
+ '__APP_VERSION__': JSON.stringify(required_package_string("version")),
+ ...web_env_define,
},
server: {
- port: process.env.PORT ? Number(process.env.PORT) : 3000,
+ host: required_env("RADROOTS_WEB_APP_DEV_HOST"),
+ port: Number(required_env("RADROOTS_WEB_APP_DEV_PORT")),
fs: {
allow: [
path.resolve(__dirname, ".."),
diff --git a/package.json b/package.json
@@ -1,12 +1,12 @@
{
- "name": "pwa",
+ "name": "radroots-web",
"private": true,
"license": "GPL-3.0",
"scripts": {
"build": "turbo build",
- "build:app": "turbo build --filter=app",
+ "build:app": "turbo build --filter=radroots-web-app",
"build:lib": "turbo run build --filter=./lib/*",
- "dev:app": "turbo dev --filter=app --filter=./lib/* --concurrency 25",
+ "dev:app": "turbo dev --filter=radroots-web-app --filter=./lib/* --concurrency 25",
"dev:lib": "turbo dev --filter=./lib/* --concurrency 25"
},
"devDependencies": {
diff --git a/turbo.json b/turbo.json
@@ -1,6 +1,9 @@
{
"$schema": "https://turborepo.com/schema.json",
"ui": "tui",
+ "globalEnv": [
+ "RADROOTS_WEB_*"
+ ],
"tasks": {
"build": {
"dependsOn": [
@@ -29,7 +32,7 @@
"cache": false,
"persistent": true
},
- "app#build": {
+ "radroots-web-app#build": {
"dependsOn": [
"@radroots/apps-lib#build",
"@radroots/apps-lib-pwa#build",