web


git clone https://radroots.dev/git/web.git
Log | Files | Refs | Submodules | README | LICENSE

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:
MCONTRIBUTING.md | 15+++++----------
Mapp/.env.example | 27+++++++++++++++------------
Mapp/package.json | 5++---
Mapp/src/lib/_env.ts | 44++++++++++++++++++++++----------------------
Mapp/src/lib/utils/backup/import.ts | 6+++++-
Mapp/src/routes/(cfg)/setup/+page.svelte | 22++++++++++++----------
Mapp/src/service-worker.js | 4++--
Mapp/vite.config.ts | 43+++++++++++++++++++++++++++++++++++++------
Mpackage.json | 6+++---
Mturbo.json | 5++++-
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",