ethercalc
Version:
Multi-User Spreadsheet Server — TypeScript rewrite (Cloudflare fullstack)
84 lines (74 loc) • 3.01 kB
text/typescript
/**
* URL/env parsing that mirrors the legacy `multi/main.ls` preamble.
*
* BasePath = '.' normally.
* In Vite dev only, if location.href contains
* `(127.0.0.1|localhost|*.local):8080`, BasePath becomes
* `http://127.0.0.1:8000` (client-multi dev server → wrangler API).
* Production builds (including Sandstorm grains on localhost:8080) keep `.`.
* If any `?auth=` param exists and BasePath is `.`, it is bumped to `..`
* (the page moves one path segment deeper once we push history).
*
* Index (room name) = capture from `/=<index>` URL; fallback `foobar`.
*
* IsReadOnly = true when `auth=0` appears anywhere in the href.
* When `?auth=` is present in the query, IsReadOnly narrows to match only
* `?auth=0` (not the bare substring).
*
* Suffix = ''
* When `?auth=` is in the query, Suffix becomes `/view` (readonly) or
* `/edit` (writable). The legacy code also pushes this suffix into the
* history via `pushState` so reloads keep the suffix.
*/
export interface MultiEnv {
readonly basePath: string;
readonly index: string;
readonly isReadOnly: boolean;
readonly suffix: '' | '/view' | '/edit';
/** Path the UI should push into window.history.pushState. Null = no push. */
readonly pushStatePath: string | null;
}
/** Fallback room name when the URL doesn't match the `/=<room>` pattern. */
export const DEFAULT_INDEX = 'foobar';
const DEV_HOST_RE = /(?:127\.0\.0\.1|localhost|\.local):\d+/;
const INDEX_RE = /\/=([^_][^/?]*)(?:\?.*)?$/;
const AUTH_IN_HREF_RE = /auth=0/;
const AUTH_QUERY_RE = /\?auth=/;
const AUTH_IS_ZERO_RE = /\??auth=0/;
/** Resolve `VITE_ETHERCALC_BASE` with an optional test override. */
export function resolveViteApiBase(
override: string | undefined,
envValue: unknown = import.meta.env.VITE_ETHERCALC_BASE,
): string {
if (override !== undefined) return override;
return typeof envValue === 'string' ? envValue : '';
}
export function parseMultiEnv(
loc: { href: string; search: string },
/** Test seam — production callers omit this. */
viteBaseOverride?: string,
/** Test seam — defaults to `import.meta.env.DEV`. */
isDev: boolean = import.meta.env.DEV,
): MultiEnv {
const viteApi = resolveViteApiBase(viteBaseOverride);
let basePath: string =
viteApi.length > 0
? viteApi.replace(/\/$/, '')
: isDev && DEV_HOST_RE.test(loc.href)
? 'http://127.0.0.1:8000'
: '.';
const indexMatch = INDEX_RE.exec(loc.href);
const index = indexMatch ? (indexMatch[1] as string) : DEFAULT_INDEX;
let isReadOnly = AUTH_IN_HREF_RE.test(loc.href);
let suffix: MultiEnv['suffix'] = '';
let pushStatePath: string | null = null;
if (AUTH_QUERY_RE.test(loc.search)) {
isReadOnly = AUTH_IS_ZERO_RE.test(loc.search);
suffix = isReadOnly ? '/view' : '/edit';
if (basePath === '.') {
basePath = '..';
}
pushStatePath = `./=${index}${suffix}`;
}
return { basePath, index, isReadOnly, suffix, pushStatePath };
}