Release 1.0.8 with platform, security, and UI hardening.
Adds API filter registry, style theme registry, SW bitmask cache clear, KV namespacing, session expiry checks, accessibility improvements, and expanded test coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+76
-3
@@ -3,7 +3,7 @@
|
||||
* Central config dictionary and environment discovery
|
||||
*/
|
||||
|
||||
import { getProvider } from './storage.js';
|
||||
import { configureKeyValueStoreBackend, getProvider } from './storage.js';
|
||||
import { api as apiClient } from './api.js';
|
||||
|
||||
// Private storage instance for config
|
||||
@@ -86,6 +86,7 @@ export const CONFIG_KEYS = {
|
||||
THEME_COLOR: 'THEME_COLOR',
|
||||
BACKGROUND_COLOR: 'BACKGROUND_COLOR',
|
||||
UI_SHELL: 'UI_SHELL',
|
||||
STYLE_THEME: 'STYLE_THEME',
|
||||
INITIAL_ROUTE: 'INITIAL_ROUTE',
|
||||
STORAGE_BACKEND: 'STORAGE_BACKEND',
|
||||
API_BASE_URL: 'API_BASE_URL',
|
||||
@@ -93,7 +94,9 @@ export const CONFIG_KEYS = {
|
||||
SECURITY_CONFIG: 'SECURITY_CONFIG',
|
||||
LOCALE: 'LOCALE',
|
||||
/** Development host: extra dev UI, SW dev behavior, etc. Layered via getConfig (storage → profile → bundler). */
|
||||
DEV_HOST: 'DEV_HOST'
|
||||
DEV_HOST: 'DEV_HOST',
|
||||
/** Whether the app should register and use a service worker. */
|
||||
SERVICE_WORKER_ENABLED: 'SERVICE_WORKER_ENABLED'
|
||||
};
|
||||
|
||||
// do not allow edits on these keys via setConfig - they are meant to be set from profile or env vars and not overridden at runtime
|
||||
@@ -136,6 +139,25 @@ function resolveDesktopApiBaseURL() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve service-worker enablement from profile.
|
||||
* Defaults to true for backward compatibility with PWA templates.
|
||||
* @param {Object|null|undefined} appConfig
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function resolveServiceWorkerEnabled(appConfig = {}) {
|
||||
if (typeof appConfig?.service_worker?.enabled === 'boolean') {
|
||||
return appConfig.service_worker.enabled;
|
||||
}
|
||||
if (typeof appConfig?.serviceWorker?.enabled === 'boolean') {
|
||||
return appConfig.serviceWorker.enabled;
|
||||
}
|
||||
if (typeof appConfig?.pwa?.service_worker?.enabled === 'boolean') {
|
||||
return appConfig.pwa.service_worker.enabled;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize environment config
|
||||
* @param {Object} appConfig - Configuration from profile
|
||||
@@ -157,6 +179,12 @@ export function initEnv(appConfig) {
|
||||
THEME_COLOR: appConfig.theme_color || appConfig.themeColor || '#000000',
|
||||
BACKGROUND_COLOR: appConfig.background_color || appConfig.backgroundColor || '#ffffff',
|
||||
UI_SHELL: appConfig.ui_shell || appConfig.uiShell || 'EmptyShell',
|
||||
STYLE_THEME:
|
||||
appConfig.style_theme
|
||||
|| appConfig.styleTheme
|
||||
|| appConfig.ui?.style_theme
|
||||
|| appConfig.ui?.styleTheme
|
||||
|| null,
|
||||
INITIAL_ROUTE: appConfig.initial_route || appConfig.initialRoute || appConfig.ui?.initial_route || appConfig.ui?.initialRoute || '/home',
|
||||
STORAGE_BACKEND: appConfig.storage?.backend || 'localStorage',
|
||||
API_BASE_URL: resolvedApiBaseURL,
|
||||
@@ -164,10 +192,12 @@ export function initEnv(appConfig) {
|
||||
SECURITY_CONFIG: appConfig.security || {},
|
||||
LOCALE: appConfig.locale || null,
|
||||
[CONFIG_KEYS.DEV_HOST]: resolveDevHostFlag(appConfig),
|
||||
[CONFIG_KEYS.SERVICE_WORKER_ENABLED]: resolveServiceWorkerEnabled(appConfig),
|
||||
// Store full profile for advanced access
|
||||
_profile: appConfig
|
||||
};
|
||||
apiClient.setBaseURL(resolvedApiBaseURL);
|
||||
configureKeyValueStoreBackend(config.STORAGE_BACKEND);
|
||||
}
|
||||
|
||||
function resolveSystemLocale() {
|
||||
@@ -397,7 +427,28 @@ export async function syncDocumentHeadFromConfig(options = {}) {
|
||||
* @param {any} altValue - Alternative value if not found
|
||||
* @returns {Promise<any>} Config value or altValue
|
||||
*/
|
||||
export function getConfigSync(key, altValue = null) {
|
||||
if (config.hasOwnProperty(key)) {
|
||||
return config[key];
|
||||
}
|
||||
|
||||
if (typeof import.meta !== 'undefined' && import.meta.env) {
|
||||
const envKey = `VITE_${key}`;
|
||||
const envValue = import.meta.env[envKey];
|
||||
if (envValue !== undefined) {
|
||||
return envValue;
|
||||
}
|
||||
}
|
||||
|
||||
return altValue;
|
||||
}
|
||||
|
||||
export async function getConfig(key, altValue = null) {
|
||||
// Locked profile keys cannot be overridden by persisted storage values.
|
||||
if (isConfigKeyLocked(key) && config.hasOwnProperty(key)) {
|
||||
return config[key];
|
||||
}
|
||||
|
||||
// 1. Check if key exists in storage first (quick check to avoid unnecessary get)
|
||||
try {
|
||||
const exists = await storage.hasKey(key);
|
||||
@@ -440,7 +491,7 @@ export async function getConfig(key, altValue = null) {
|
||||
* Otherwise, save to storage (for persistence).
|
||||
* @param {string} key - Config key
|
||||
* @param {any} value - Value to set
|
||||
* @returns {Promise<void>}
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
export async function setConfig(key, value) {
|
||||
if (isConfigKeyLocked(key)) {
|
||||
@@ -520,6 +571,28 @@ export function isDevelopment() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether service worker registration is enabled for this app.
|
||||
* Synchronous: uses in-memory config after {@link initEnv} / {@link bootstrapEnv}.
|
||||
*/
|
||||
export function isServiceWorkerEnabledSync(altValue = true) {
|
||||
if (config && Object.prototype.hasOwnProperty.call(config, CONFIG_KEYS.SERVICE_WORKER_ENABLED)) {
|
||||
return Boolean(config[CONFIG_KEYS.SERVICE_WORKER_ENABLED]);
|
||||
}
|
||||
return altValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Async profile-aware service worker enablement check.
|
||||
*/
|
||||
export async function isServiceWorkerEnabled(altValue = true) {
|
||||
const configured = await getConfig(CONFIG_KEYS.SERVICE_WORKER_ENABLED, null);
|
||||
if (typeof configured === 'boolean') {
|
||||
return configured;
|
||||
}
|
||||
return isServiceWorkerEnabledSync(altValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverse of {@link isDevelopment} when no explicit production flag exists.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user