@powerhousedao/connect-core 6.2.3-dev.89 → 6.2.3-dev.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index-Beqvfchq.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/{multi-reactor-91g8OeBB.js → multi-reactor-COKWkIh5.js} +2 -2
- package/dist/{multi-reactor-91g8OeBB.js.map → multi-reactor-COKWkIh5.js.map} +1 -1
- package/dist/{plugin-host-CMr7lN40.js → plugin-host-CaTO1tq-.js} +2 -2
- package/dist/{plugin-host-CMr7lN40.js.map → plugin-host-CaTO1tq-.js.map} +1 -1
- package/dist/{plugin-ui-DHlnXJmN.js → plugin-ui-BS5j5g1d.js} +3 -3
- package/dist/{plugin-ui-DHlnXJmN.js.map → plugin-ui-BS5j5g1d.js.map} +1 -1
- package/dist/plugins.js +2 -2
- package/dist/{router-C4hDJ4_J.js → router-C5uD96ZP.js} +2 -2
- package/dist/{router-C4hDJ4_J.js.map → router-C5uD96ZP.js.map} +1 -1
- package/dist/router.js +2 -2
- package/dist/{runtime-CtZfHUnR.js → runtime-ClrasOcu.js} +3 -3
- package/dist/{runtime-CtZfHUnR.js.map → runtime-ClrasOcu.js.map} +1 -1
- package/dist/runtime-config.js +4 -4
- package/dist/{runtime-flag-DvxW_SD4.js → runtime-flag-DwQ3Jpdw.js} +2 -2
- package/dist/{runtime-flag-DvxW_SD4.js.map → runtime-flag-DwQ3Jpdw.js.map} +1 -1
- package/dist/runtime.js +2 -2
- package/dist/{views-BtUVboYl.js → views-LyRJcSKu.js} +20 -6
- package/dist/views-LyRJcSKu.js.map +1 -0
- package/dist/views.js +1 -1
- package/package.json +8 -8
- package/src/views/modals.tsx +15 -3
- package/dist/views-BtUVboYl.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-CtZfHUnR.js","names":[],"sources":["../src/runtime/multi-reactor-flag.ts","../src/runtime/reactor-worker-flag.ts","../src/runtime/reactor-client-mode.ts","../src/runtime/app-reactor-client.ts","../src/runtime/new-drive.ts","../src/runtime/drive-placement.ts","../src/runtime/drive-placements.ts","../src/runtime/ph-store.ts","../src/runtime/local-package.ts"],"sourcesContent":["import { getRuntimeConfig } from \"./runtime-config.js\";\n\n// Config only: the flag is in the worker fingerprint, so a per-tab value would split tabs.\nexport function isMultiReactorEnabled(): boolean {\n return getRuntimeConfig().connect.instance?.multiReactor ?? false;\n}\n","import { getRuntimeConfig } from \"./runtime-config.js\";\nimport {\n isRuntimeFlagEnabled,\n resolveRuntimeFlag,\n type RuntimeFlagInput,\n} from \"./runtime-flag.js\";\n\nexport const REACTOR_WORKER_QUERY_KEY = \"reactorWorker\";\nexport const REACTOR_WORKER_STORAGE_KEY = \"ph:reactorWorker\";\n\nexport type ReactorWorkerFlagInput = RuntimeFlagInput;\n\nexport function resolveReactorWorkerEnabled(\n input: ReactorWorkerFlagInput,\n): boolean {\n return resolveRuntimeFlag(input);\n}\n\nexport function isReactorWorkerEnabled(): boolean {\n return isRuntimeFlagEnabled({\n queryKey: REACTOR_WORKER_QUERY_KEY,\n storageKey: REACTOR_WORKER_STORAGE_KEY,\n readConfigFlag: () =>\n getRuntimeConfig().connect.instance?.reactorWorker ?? false,\n });\n}\n","import type { PHReactorClientMode } from \"@powerhousedao/shared/clis\";\nimport { getRuntimeConfig } from \"./runtime-config.js\";\nimport { isMultiReactorEnabled } from \"./multi-reactor-flag.js\";\nimport { isReactorWorkerEnabled } from \"./reactor-worker-flag.js\";\nimport { logger } from \"document-model\";\n\n/**\n * Which of the four reactor clients Connect boots. Standardized by the\n * multi-reactor work; this resolves the single selector Connect acts on:\n *\n * in-process | worker | remote | router\n *\n * Precedence: an explicit `?reactorClient=` query override (kept in\n * localStorage so it survives a reload), then a stored override, then the\n * served `connect.instance.reactorClient` (projected from the host's reactor\n * backends), then the LEGACY booleans — so a config that only sets\n * reactorWorker/multiReactor behaves exactly as before.\n *\n * `remote` is the only mode that builds NO local reactor (no PGlite): it is a\n * pure client to a remote Switchboard. `router` keeps the local reactor and\n * adds remote backends; `worker`/`in-process` are the local reactor alone.\n */\nexport type ReactorClientMode = PHReactorClientMode;\n\nconst MODES: ReadonlySet<string> = new Set([\n \"in-process\",\n \"worker\",\n \"remote\",\n \"router\",\n]);\n\nexport const REACTOR_CLIENT_QUERY_KEY = \"reactorClient\";\nexport const REACTOR_CLIENT_STORAGE_KEY = \"ph:reactorClient\";\n\nconst asMode = (value: string | null | undefined) =>\n value && MODES.has(value) ? (value as ReactorClientMode) : undefined;\n\nconst LOCAL_ONLY: ReadonlySet<ReactorClientMode> = new Set([\n \"in-process\",\n \"worker\",\n]);\n\n/** Set while this page load keeps its reactor to itself against the host's `newDrives`. */\nlet blockedNewDrives: Error | undefined;\n\n/** While new drives go to a switchboard, a local-only override applies only from the URL, never from storage. */\nfunction fromTab(newDrives: string | undefined): ReactorClientMode | undefined {\n if (typeof window === \"undefined\") return undefined;\n let query: ReactorClientMode | undefined;\n try {\n query = asMode(\n new URLSearchParams(window.location.search).get(REACTOR_CLIENT_QUERY_KEY),\n );\n } catch {\n return undefined;\n }\n const localOnly = (mode: ReactorClientMode | undefined) =>\n newDrives !== undefined && mode !== undefined && LOCAL_ONLY.has(mode);\n try {\n if (query) {\n if (localOnly(query)) {\n window.localStorage.removeItem(REACTOR_CLIENT_STORAGE_KEY);\n } else {\n window.localStorage.setItem(REACTOR_CLIENT_STORAGE_KEY, query);\n }\n return query;\n }\n const stored = asMode(\n window.localStorage.getItem(REACTOR_CLIENT_STORAGE_KEY),\n );\n if (!localOnly(stored)) return stored;\n window.localStorage.removeItem(REACTOR_CLIENT_STORAGE_KEY);\n logger.warn(\n \"Ignoring the stored ?reactorClient=@mode: new drives go to @switchboard\",\n stored,\n newDrives,\n );\n return undefined;\n } catch {\n // Blocked storage: the query still applies for this page load.\n return query;\n }\n}\n\nexport function resolveReactorClientMode(): ReactorClientMode {\n const instance = getRuntimeConfig().connect.instance;\n const newDrives = instance?.router?.newDrives;\n const override = fromTab(newDrives);\n blockedNewDrives =\n override && newDrives !== undefined && LOCAL_ONLY.has(override)\n ? new Error(\n `New drives go to ${newDrives}, but this browser is set to keep its reactor to itself (?reactorClient=${override}).`,\n )\n : undefined;\n if (override) return override;\n const configured = instance?.reactorClient;\n if (configured && MODES.has(configured)) return configured;\n // Legacy fallback — unchanged behaviour for configs without reactorClient.\n if (isMultiReactorEnabled()) return \"router\";\n if (isReactorWorkerEnabled()) return \"worker\";\n return \"in-process\";\n}\n\n/** Why this page load can't create a drive where the host says, or undefined when it can. */\nexport function newDrivesBlocked(): Error | undefined {\n return blockedNewDrives;\n}\n","import type { IReactorClient } from \"@powerhousedao/reactor\";\nimport {\n getDrives,\n getFullReactorClient,\n getPHStore,\n recordBackendAuthError,\n setBackendAuth,\n setDrives,\n setFullReactorClient,\n} from \"@powerhousedao/reactor-browser\";\nimport type {\n PHConnectRemoteBackend,\n PHConnectRouterSettings,\n} from \"@powerhousedao/shared/clis\";\nimport type { DocumentDriveDocument } from \"@powerhousedao/shared/document-drive\";\nimport type { ISigner } from \"@powerhousedao/shared/document-model\";\nimport { logger } from \"document-model\";\nimport { switchboardOfDrive } from \"@powerhousedao/shared/host-tokens\";\nimport type * as MultiReactor from \"./multi-reactor.js\";\nimport type {\n LoadedDocumentModels,\n LocalReactorModule,\n} from \"./multi-reactor.js\";\nimport type { ReactorClientMode } from \"./reactor-client-mode.js\";\nimport { resolveReactorClientMode } from \"./reactor-client-mode.js\";\nimport { isReactorWorkerEnabled } from \"./reactor-worker-flag.js\";\nimport { setDriveRelister } from \"./drive-relist.js\";\n\n// Kept here: multi-reactor.ts is loaded lazily.\nconst REMOTE_BACKEND_NAME = \"switchboard-remote\";\n\ntype MultiReactorModule = typeof MultiReactor;\n\nexport type AppReactorClientParams = {\n mode: ReactorClientMode;\n /** Built, and ready (the worker has started up). Undefined in `remote`\n * mode, where no local reactor is built. */\n module: LocalReactorModule | undefined;\n /** The fallback switchboard when `remoteBackends` is empty. */\n remoteDriveUrl: string | undefined;\n /** One per switchboard, by host key; `url` is a drive address on it. */\n remoteBackends?: readonly PHConnectRemoteBackend[];\n /** Where new drives go and the router's timeouts, from the host config. */\n router?: PHConnectRouterSettings;\n /** Config placements known at build time; later ones go through `setConfiguredPlacement`. */\n collections?: Readonly<Record<string, string>>;\n /** Signs routed writes; unsigned (anonymous) without one. */\n signer?: ISigner;\n documentModelModules: LoadedDocumentModels;\n loadRouter?: () => Promise<MultiReactorModule>;\n};\n\nconst loadMultiReactor = (): Promise<MultiReactorModule> =>\n import(\"./multi-reactor.js\");\n\n/** What a mode builds in the tab; `remote` builds no local reactor (no PGlite). */\nexport type ReactorClientPlan = {\n mode: ReactorClientMode;\n /** `router`: the local reactor plus remote backends. */\n multiReactor: boolean;\n needsLocalReactor: boolean;\n /** The local reactor runs in the shared worker. */\n localAsWorker: boolean;\n};\n\nexport function planReactorClient(\n mode: ReactorClientMode = resolveReactorClientMode(),\n): ReactorClientPlan {\n const needsLocalReactor = mode !== \"remote\";\n return {\n mode,\n multiReactor: mode === \"router\",\n needsLocalReactor,\n localAsWorker:\n needsLocalReactor &&\n (mode === \"worker\" || (mode === \"router\" && isReactorWorkerEnabled())),\n };\n}\n\n/** Builds the tab's reactor, e.g. Connect's PGlite or worker reactor. */\nexport type LocalReactorFactory = (\n plan: ReactorClientPlan,\n) => Promise<LocalReactorModule>;\n\nexport type CreateAppReactorClientOptions = Omit<\n AppReactorClientParams,\n \"mode\" | \"module\"\n> & {\n plan?: ReactorClientPlan;\n /** Called only when the plan needs a local reactor. */\n localReactor?: LocalReactorFactory;\n};\n\n/** Builds the local reactor the plan needs, then selects the app's client. */\nexport async function createAppReactorClient(\n options: CreateAppReactorClientOptions,\n) {\n const { localReactor, ...params } = options;\n const plan = options.plan ?? planReactorClient();\n let module: LocalReactorModule | undefined;\n if (plan.needsLocalReactor) {\n if (!localReactor) {\n throw new Error(\n `reactorClient mode \"${plan.mode}\" needs a local reactor; pass localReactor`,\n );\n }\n module = await localReactor(plan);\n }\n const { client, router } = await selectClient({\n ...params,\n mode: plan.mode,\n module,\n });\n return { plan, module, client, router };\n}\n\n/**\n * Pick the client for the resolved mode:\n * - `in-process` / `worker`: the local client, router module never loaded.\n * - `router`: the local reactor plus remote backends.\n * - `remote`: a remote-only router — a full IReactorClient that proxies to\n * the Switchboard over GraphQL with NO local reactor (no PGlite).\n */\nexport async function selectAppReactorClient(\n params: AppReactorClientParams,\n): Promise<IReactorClient> {\n return (await selectClient(params)).client;\n}\n\n/** The app's client, and the router when it is one. */\nasync function selectClient(params: AppReactorClientParams): Promise<{\n client: IReactorClient;\n router?: MultiReactor.ConnectRoutingClient;\n}> {\n if (params.mode === \"remote\") {\n setFullReactorClient(undefined);\n const remote = await buildRemoteOnly(params);\n setFullReactorClient(remote);\n return { client: remote, router: remote };\n }\n if (params.mode !== \"router\") {\n if (getPHStore()?.fullReactorClient) {\n setFullReactorClient(undefined);\n }\n return { client: requireModule(params).client };\n }\n setFullReactorClient(undefined);\n const routed = await buildRouter(params);\n setFullReactorClient(routed);\n return routed\n ? { client: routed, router: routed }\n : { client: requireModule(params).client };\n}\n\nfunction requireModule(params: AppReactorClientParams): LocalReactorModule {\n if (!params.module) {\n throw new Error(\n `reactorClient mode \"${params.mode}\" needs a local reactor, but none was built`,\n );\n }\n return params.module;\n}\n\n/**\n * A Switchboard that cannot answer must not fail boot: list the tab's drives.\n *\n * A Switchboard that REFUSES the caller (401/403) must not fail boot either:\n * the refusal is recorded (`backendAuth`) so the root view shows the sign-in\n * gate, and the drive list is empty until the user is someone the backend\n * accepts. A remote-only client has no local drives to fall back on.\n */\nexport async function getAppDrives(\n client: IReactorClient,\n local: IReactorClient,\n): Promise<DocumentDriveDocument[]> {\n if (client === local) {\n try {\n const drives = await getDrives(local);\n setBackendAuth(undefined);\n return drives;\n } catch (error) {\n if (recordBackendAuthError(error, \"switchboard\")) {\n logger.warn(\n \"The Switchboard refused this caller; showing the sign-in gate: @error\",\n error,\n );\n return [];\n }\n throw error;\n }\n }\n try {\n const drives = await getDrives(client);\n setBackendAuth(undefined);\n return drives;\n } catch (error) {\n if (recordBackendAuthError(error, \"switchboard\")) {\n logger.warn(\n \"A remote backend refused this caller; listing the local reactor's drives: @error\",\n error,\n );\n } else {\n logger.warn(\n \"Could not list drives through the router; listing the local reactor's: @error\",\n error,\n );\n }\n return getDrives(local);\n }\n}\n\n/**\n * The boot refresh of the drives store. It lists through `getAppDrives`\n * whichever client is in use, so a Switchboard that refuses the caller is\n * recorded for the sign-in gate here too: the remote-only client is its own\n * `local`, and a bare `refreshReactorDataClient` would let the 401 escape\n * and fail the boot before the gate can render.\n */\nexport async function refreshAppDrives(\n client: IReactorClient,\n local: IReactorClient,\n): Promise<void> {\n setDrives(await getAppDrives(client, local));\n}\n\n/**\n * Lists the drives again as the caller the tab now is, through the clients\n * boot left in the store: after a sign-in landing or a sign-out that followed\n * a backend's refusal. Nothing to do before boot has set a client.\n */\nexport async function relistAppDrives(): Promise<void> {\n const client = getFullReactorClient();\n if (!client) return;\n const local = getPHStore()?.reactorClientModule?.client ?? client;\n await refreshAppDrives(client, local);\n}\n\n// The backend auth gate relists through drive-relist (see there)\nsetDriveRelister(relistAppDrives);\n\n/** The switchboards to route to: the host's remote backends, else the default drive's. */\nfunction switchboardRemotes(params: AppReactorClientParams): {\n remotes: MultiReactor.SwitchboardRemote[];\n unparsed: string[];\n} {\n const listed: readonly PHConnectRemoteBackend[] = params.remoteBackends\n ?.length\n ? params.remoteBackends\n : params.remoteDriveUrl\n ? [{ name: REMOTE_BACKEND_NAME, url: params.remoteDriveUrl }]\n : [];\n const remotes: MultiReactor.SwitchboardRemote[] = [];\n const unparsed: string[] = [];\n for (const { url, ...remote } of listed) {\n const graphqlUrl = switchboardOfDrive(url)?.graphql;\n if (graphqlUrl) remotes.push({ ...remote, graphqlUrl });\n else unparsed.push(url);\n }\n return { remotes, unparsed };\n}\n\nasync function buildRouter(\n params: AppReactorClientParams,\n): Promise<MultiReactor.ConnectRoutingClient | undefined> {\n const { remotes, unparsed } = switchboardRemotes(params);\n for (const url of unparsed) {\n logger.warn(\n \"multiReactor is on but the remote drive URL @url does not parse; skipping it\",\n url,\n );\n }\n // New drives belong on a switchboard: without the router they would be\n // created in this browser, so it is required.\n const newDrives = params.router?.newDrives;\n if (remotes.length === 0) {\n if (newDrives !== undefined) {\n throw new Error(\n `New drives go to ${newDrives}, but this Connect has no address for it, so it can't start.`,\n );\n }\n logger.warn(\n \"multiReactor is on but no remote drive URL is configured; using the local reactor only\",\n );\n return undefined;\n }\n let multiReactor: MultiReactorModule;\n try {\n multiReactor = await (params.loadRouter ?? loadMultiReactor)();\n } catch (error) {\n if (newDrives !== undefined) {\n throw new Error(\n `New drives go to ${newDrives}, but the router that reaches it didn't load, so Connect can't start.`,\n { cause: error },\n );\n }\n logger.error(\"Could not load the multi-reactor router: @error\", error);\n return undefined;\n }\n logger.info(\n \"Multi-reactor routing to @urls\",\n remotes.map((remote) => remote.graphqlUrl).join(\", \"),\n );\n return multiReactor.buildMultiReactorClient({\n module: requireModule(params),\n remotes,\n collections: params.collections,\n ...params.router,\n signer: params.signer,\n documentModelModules: params.documentModelModules,\n });\n}\n\n/**\n * The `remote` mode: a remote-only router — a full IReactorClient that proxies\n * to the Switchboard over GraphQL with NO local backend, so no local reactor\n * and no PGlite are ever built. A missing/unparseable remote URL is a hard\n * config error here (unlike `router`, there is no local reactor to fall back\n * to).\n */\nasync function buildRemoteOnly(\n params: AppReactorClientParams,\n): Promise<MultiReactor.ConnectRoutingClient> {\n const { remotes, unparsed } = switchboardRemotes(params);\n if (unparsed.length > 0) {\n throw new Error(\n `reactorClient \"remote\": the remote drive URL \"${unparsed[0]}\" does not resolve to a Switchboard GraphQL endpoint`,\n );\n }\n if (remotes.length === 0) {\n throw new Error(\n 'reactorClient \"remote\" needs a remote drive URL — declare a remote backend / default drive the client can proxy to',\n );\n }\n const multiReactor = await (params.loadRouter ?? loadMultiReactor)();\n logger.info(\n \"Remote reactor client → @urls\",\n remotes.map((remote) => remote.graphqlUrl).join(\", \"),\n );\n return multiReactor.buildMultiReactorClient({\n module: undefined,\n remotes,\n collections: params.collections,\n ...params.router,\n signer: params.signer,\n documentModelModules: params.documentModelModules,\n });\n}\n","import { isDriveAuthError } from \"@powerhousedao/reactor\";\n\n/** A drive create the switchboard new drives go to did not answer. */\nexport class NewDriveUnansweredError extends Error {\n override readonly name = \"NewDriveUnansweredError\";\n\n constructor(\n readonly backend: string,\n options?: ErrorOptions,\n ) {\n super(`${backend} isn't answering, so the drive wasn't created.`, options);\n }\n}\n\n/** `auth`: the switchboard refused the caller, so signing in may help. */\nexport class NewDriveRefusedError extends Error {\n override readonly name = \"NewDriveRefusedError\";\n\n constructor(\n readonly backend: string,\n readonly auth: boolean,\n reason: string | undefined,\n options?: ErrorOptions,\n ) {\n super(\n reason\n ? `${backend} refused the new drive: ${reason}`\n : `${backend} refused the new drive.`,\n options,\n );\n }\n}\n\n// fetch's network failure: Node, Chrome, Firefox, Safari.\nconst NETWORK_FAILURE =\n /fetch failed|failed to fetch|networkerror|load failed/i;\n\ntype Failure = {\n name?: unknown;\n message?: unknown;\n cause?: unknown;\n statusCode?: unknown;\n category?: unknown;\n response?: { status?: unknown; errors?: { message?: unknown }[] };\n};\n\n/** Each error in the cause chain, outermost first. */\nfunction* chain(error: unknown): Generator<Failure> {\n for (let at = error; at; at = (at as Failure).cause) yield at as Failure;\n}\n\nfunction statusOf(at: Failure): number | undefined {\n const status = at.response?.status ?? at.statusCode;\n return typeof status === \"number\" ? status : undefined;\n}\n\n/** A failure to reach a switchboard, not an answer from it. */\nfunction isUnanswered(error: unknown): boolean {\n for (const at of chain(error)) {\n if (\n at.name === \"SwitchboardUnreachableError\" ||\n at.name === \"TimeoutError\" ||\n at.name === \"ProbeTimeoutError\" ||\n at.category === \"network\" ||\n (at instanceof TypeError && NETWORK_FAILURE.test(at.message))\n ) {\n return true;\n }\n const status = statusOf(at);\n if (status !== undefined && status >= 500) return true;\n }\n return false;\n}\n\nfunction isAuthRefusal(error: unknown): boolean {\n for (const at of chain(error)) {\n if (isDriveAuthError(at)) return true;\n const status = statusOf(at);\n if (status === 401 || status === 403) return true;\n }\n return false;\n}\n\n/** An answer from a server, not a failure in this browser. */\nfunction isServerAnswer(error: unknown): boolean {\n for (const at of chain(error)) {\n if (\n at.response !== undefined ||\n statusOf(at) !== undefined ||\n at.name === \"GraphQLRequestError\" ||\n at.name === \"MisrouteUnresolvedError\"\n ) {\n return true;\n }\n }\n return false;\n}\n\n/** The first reason the switchboard gave, if it gave one. */\nfunction reasonOf(error: unknown): string | undefined {\n for (const at of chain(error)) {\n const message = at.response?.errors?.[0]?.message;\n if (typeof message === \"string\" && message !== \"\") return message;\n if (at.name === \"GraphQLRequestError\" && typeof at.message === \"string\") {\n return at.message;\n }\n }\n return undefined;\n}\n\n/** A failed drive create, named for the switchboard `client` creates new drives on; else `error` itself. */\nexport function newDriveError(client: unknown, error: unknown): unknown {\n const backend = (client as { newDrives?: unknown } | null | undefined)\n ?.newDrives;\n if (typeof backend !== \"string\") return error;\n if (isUnanswered(error)) {\n return new NewDriveUnansweredError(backend, { cause: error });\n }\n if (!isServerAnswer(error)) return error;\n return new NewDriveRefusedError(\n backend,\n isAuthRefusal(error),\n reasonOf(error),\n { cause: error },\n );\n}\n","import { DriveCollectionId } from \"@powerhousedao/reactor\";\nimport type {\n PHConnectCollection,\n PHConnectRemoteBackend,\n PHConnectRuntimeDefaultDrive,\n} from \"@powerhousedao/shared/clis\";\nimport { switchboardOfDrive } from \"@powerhousedao/shared/host-tokens\";\nimport { logger } from \"document-model\";\n\n/** The router backend of the reactor in this browser. */\nexport const LOCAL_BACKEND_NAME = \"connect-local\";\n\n/** A drive the host keeps on a switchboard, and where its config opens it. */\nexport type SwitchboardDrive = {\n /** The host's name for it: its address's slug, or a collection's key. */\n slug: string;\n url: string;\n /** The switchboard's backend. */\n remote: string;\n /** `remote` (no copy here) or `LOCAL_BACKEND_NAME` (a synced copy). */\n configured: string;\n /** Not a default drive: its address may not be the one it is served at. */\n collection: boolean;\n};\n\n/** A switchboard drive with the names callers reach it by. */\nexport type ResolvedSwitchboardDrive = SwitchboardDrive & {\n id?: string;\n servedSlug?: string;\n};\n\n/** A drive this browser moved away from its host config. */\nexport type PlacementEntry = {\n slug: string;\n backend: string;\n configWas: string;\n};\n\ntype PlacementConfig = {\n drives?: { defaultDrives?: readonly PHConnectRuntimeDefaultDrive[] };\n instance?: {\n remoteBackends?: readonly PHConnectRemoteBackend[];\n collections?: Record<string, PHConnectCollection>;\n };\n};\n\n/** The config's local default drives, placed on this browser so a\n * switchboard `newDrives` doesn't take them. */\nexport function localDrivePlacements(\n defaultDrives: readonly PHConnectRuntimeDefaultDrive[],\n): Record<string, string> {\n return Object.fromEntries(\n defaultDrives.flatMap((drive) =>\n !(\"url\" in drive) && drive.id ? [[drive.id, LOCAL_BACKEND_NAME]] : [],\n ),\n );\n}\n\nexport function keepsLocalCopy(drive: PHConnectRuntimeDefaultDrive): boolean {\n return !(\"url\" in drive) || drive.sync !== false;\n}\n\n/** Remote default drives with a backend, then collections. */\nexport function switchboardDrives(config: PlacementConfig): SwitchboardDrive[] {\n const drives: SwitchboardDrive[] = [];\n for (const drive of config.drives?.defaultDrives ?? []) {\n if (!(\"url\" in drive) || !drive.backend) continue;\n const slug = switchboardOfDrive(drive.url)?.slug;\n if (slug === undefined) continue;\n drives.push({\n slug,\n url: drive.url,\n remote: drive.backend,\n configured: drive.sync === false ? drive.backend : LOCAL_BACKEND_NAME,\n collection: false,\n });\n }\n const remotes = config.instance?.remoteBackends ?? [];\n for (const [slug, entry] of Object.entries(\n config.instance?.collections ?? {},\n )) {\n const remote = remotes.find((r) => r.name === entry.backend);\n const base = remote && switchboardOfDrive(remote.url)?.base;\n if (!base || drives.some((drive) => drive.slug === slug)) continue;\n drives.push({\n slug,\n url: `${base}/d/${encodeURIComponent(slug)}`,\n remote: entry.backend,\n configured: entry.sync === false ? entry.backend : LOCAL_BACKEND_NAME,\n collection: true,\n });\n }\n return drives;\n}\n\n/** Stale: no such drive, a backend it can't open on, back to config, or a changed config value. */\nexport function applyPlacementEntries(\n drives: readonly SwitchboardDrive[],\n entries: readonly PlacementEntry[],\n): { applied: PlacementEntry[]; stale: PlacementEntry[] } {\n const applied: PlacementEntry[] = [];\n const stale: PlacementEntry[] = [];\n for (const entry of entries) {\n const drive = drives.find((d) => d.slug === entry.slug);\n const holds =\n drive !== undefined &&\n entry.configWas === drive.configured &&\n entry.backend !== drive.configured &&\n (entry.backend === drive.remote || entry.backend === LOCAL_BACKEND_NAME);\n (holds ? applied : stale).push(entry);\n }\n return { applied, stale };\n}\n\n/** A served slug names a declared one as is, or hashed (`<slug>-<8 hex>`). */\nexport function servesSlug(declared: string, served: string): boolean {\n return (\n served === declared ||\n (served.startsWith(`${declared}-`) &&\n /^[0-9a-f]{8}$/.test(served.slice(declared.length + 1)))\n );\n}\n\ntype DriveNames = { id: string; slug: string };\n\nfunction withinDeadline<T>(work: Promise<T>, timeoutMs: number): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const deadline = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(`no answer in ${timeoutMs} ms`)),\n timeoutMs,\n );\n });\n return Promise.race([work, deadline]).finally(() => clearTimeout(timer));\n}\n\nexport type ResolveSwitchboardDrivesOptions = {\n timeoutMs?: number;\n /** A collection whose address does not answer: find it among its switchboard's drives. */\n findServed?: (drive: SwitchboardDrive) => Promise<DriveNames | undefined>;\n};\n\n/** An unreachable drive keeps only the slug in its address. */\nexport async function resolveSwitchboardDrives(\n drives: readonly SwitchboardDrive[],\n fetchInfo: (url: string) => Promise<DriveNames>,\n { timeoutMs = 5_000, findServed }: ResolveSwitchboardDrivesOptions = {},\n): Promise<ResolvedSwitchboardDrive[]> {\n return Promise.all(\n drives.map(async (drive): Promise<ResolvedSwitchboardDrive> => {\n try {\n const { id, slug } = await withinDeadline(\n fetchInfo(drive.url),\n timeoutMs,\n );\n return { ...drive, id, servedSlug: slug };\n } catch (error) {\n if (drive.collection && findServed) {\n try {\n const served = await withinDeadline(findServed(drive), timeoutMs);\n if (served) {\n return { ...drive, id: served.id, servedSlug: served.slug };\n }\n } catch (findError) {\n logger.warn(\n \"Could not find drive @slug on @remote: @error\",\n drive.slug,\n drive.remote,\n findError,\n );\n }\n }\n logger.warn(\n \"Could not read drive @url; routing it by its address only: @error\",\n drive.url,\n error,\n );\n const slug = switchboardOfDrive(drive.url)?.slug;\n return slug === undefined ? drive : { ...drive, servedSlug: slug };\n }\n }),\n );\n}\n\n/** The address the drive is served at. */\nexport function servedUrl(drive: ResolvedSwitchboardDrive): string {\n const at = switchboardOfDrive(drive.url);\n return at && drive.servedSlug && drive.servedSlug !== at.slug\n ? `${at.base}/d/${encodeURIComponent(drive.servedSlug)}`\n : drive.url;\n}\n\n/** The keys a placement is installed under: the id and the served slug. */\nexport function placementKeys(drive: ResolvedSwitchboardDrive): string[] {\n return [drive.id, drive.servedSlug].filter(\n (key): key is string => key !== undefined && key !== \"\",\n );\n}\n\nexport type SyncRemotes = {\n list(): readonly {\n meta: { name: string; collectionId: DriveCollectionId };\n }[];\n remove(name: string): Promise<void>;\n};\n\n/** Drops the sync remote of every given drive; its local data is left unread. */\nexport async function removeLocalCopies(\n sync: SyncRemotes,\n resolved: readonly { id?: string }[],\n): Promise<void> {\n for (const drive of resolved) {\n if (!drive.id) continue;\n const collectionId = DriveCollectionId.forDrive(drive.id);\n const remotes = sync\n .list()\n .filter((remote) => remote.meta.collectionId.equals(collectionId));\n for (const remote of remotes) {\n await sync.remove(remote.meta.name);\n }\n }\n}\n","import type { RoutingReactorClient } from \"@powerhousedao/reactor-router\";\nimport { driveDocumentType } from \"@powerhousedao/shared/document-drive\";\nimport { logger } from \"document-model\";\nimport {\n applyPlacementEntries,\n servesSlug,\n LOCAL_BACKEND_NAME,\n placementKeys,\n removeLocalCopies,\n servedUrl,\n type PlacementEntry,\n type ResolvedSwitchboardDrive,\n type SwitchboardDrive,\n type SyncRemotes,\n} from \"./drive-placement.js\";\n\n/** A collection by its declared slug among its switchboard's drives, e.g. one served hashed. */\nexport async function findServedDrive(\n router: Pick<RoutingReactorClient, \"backends\">,\n drive: SwitchboardDrive,\n): Promise<{ id: string; slug: string } | undefined> {\n const backend = router.backends.find((b) => b.name === drive.remote);\n if (!backend) return undefined;\n const { results } = await backend.api.find({ type: driveDocumentType });\n const served = results.find(\n (doc) => doc.header.slug && servesSlug(drive.slug, doc.header.slug),\n );\n return served && { id: served.header.id, slug: served.header.slug };\n}\n\n/** Where a drive opens given its stored move, if any: the move, else the host config. */\nexport function effectiveBackend(\n drive: SwitchboardDrive,\n entry: PlacementEntry | undefined,\n): string {\n return entry?.backend ?? drive.configured;\n}\n\n/** A drive opening on `backend` reads its copy here, which must arrive first. */\nexport function needsCopy(backend: string): boolean {\n return backend === LOCAL_BACKEND_NAME;\n}\n\n/** A copy has caught up once every scope is at least at the source's revision. */\nexport function caughtUp(\n local: Readonly<Record<string, number>>,\n source: Readonly<Record<string, number>>,\n): boolean {\n return Object.entries(source).every(\n ([scope, revision]) => (local[scope] ?? -1) >= revision,\n );\n}\n\n/** Where this browser keeps its moves; per origin, so every tab shares it. */\nexport interface PlacementStore {\n list(): Promise<PlacementEntry[]>;\n put(entry: PlacementEntry): Promise<void>;\n delete(slug: string): Promise<void>;\n}\n\n/** The router's two placement layers. */\nexport type PlacementRouter = {\n setPlacement(key: string, backend: string | undefined): void;\n setConfiguredPlacement(key: string, backend: string | undefined): void;\n};\n\nexport type PlacementSync = SyncRemotes & {\n /** Adds a sync remote for the drive. */\n add(url: string, driveId: string): Promise<void>;\n /** Bounded wait until the local copy holds what `remote` holds; rejects when it does not. */\n untilCopied(driveId: string, remote: string): Promise<void>;\n};\n\nexport type DrivePlacement = {\n drive: ResolvedSwitchboardDrive;\n /** Where the drive opens in this browser now. */\n backend: string;\n /** This browser moved it away from the host config. */\n moved: boolean;\n /** A move is running. */\n busy: boolean;\n /** Where a running move takes the drive. */\n movingTo?: string;\n};\n\n/** Config placement plus this browser's moves: store, then sync remote, then router. */\nexport class DrivePlacements {\n private applied = new Map<string, PlacementEntry>();\n private resolved: ResolvedSwitchboardDrive[] = [];\n private router: PlacementRouter | undefined;\n private readonly busy = new Map<string, string>();\n private attached = false;\n private readonly listeners = new Set<() => void>();\n private snapshot = new Map<string, DrivePlacement>();\n\n constructor(\n readonly drives: readonly SwitchboardDrive[],\n applied: readonly PlacementEntry[],\n private readonly store: PlacementStore,\n private readonly sync: PlacementSync,\n ) {\n for (const entry of applied) this.applied.set(entry.slug, entry);\n }\n\n /** Reads the store and drops the entries the config has overtaken (O2). */\n static async load(\n drives: readonly SwitchboardDrive[],\n store: PlacementStore,\n sync: PlacementSync,\n ): Promise<DrivePlacements> {\n let entries: PlacementEntry[] = [];\n try {\n entries = await store.list();\n } catch (error) {\n logger.warn(\"Could not read this browser's drive moves: @error\", error);\n }\n const { applied, stale } = applyPlacementEntries(drives, entries);\n for (const entry of stale) {\n store.delete(entry.slug).catch((error: unknown) => {\n logger.warn(\n \"Could not drop drive move @slug: @error\",\n entry.slug,\n error,\n );\n });\n }\n return new DrivePlacements(drives, applied, store, sync);\n }\n\n /** Where `drive` opens in this browser: a move, else the config. */\n backendOf(drive: SwitchboardDrive): string {\n return effectiveBackend(drive, this.applied.get(drive.slug));\n }\n\n /** Whether a remote default drive at `url` keeps a copy here. */\n keepsCopy(url: string): boolean {\n const drive = this.drives.find((d) => d.url === url);\n return !drive || this.backendOf(drive) === LOCAL_BACKEND_NAME;\n }\n\n /** Drives that open on their switchboard with no copy here. */\n placed(): SwitchboardDrive[] {\n return this.drives.filter(\n (drive) => this.backendOf(drive) !== LOCAL_BACKEND_NAME,\n );\n }\n\n /** Collections this browser keeps a copy of; default drives are added with the others. */\n keptCollections(): ResolvedSwitchboardDrive[] {\n return this.resolved.filter(\n (drive) =>\n drive.collection &&\n drive.id !== undefined &&\n this.backendOf(drive) === LOCAL_BACKEND_NAME,\n );\n }\n\n /** Installs both layers for every drive the router can be told about. */\n attach(\n router: PlacementRouter | undefined,\n resolved: readonly ResolvedSwitchboardDrive[],\n ): void {\n this.router = router;\n this.resolved = [...resolved];\n this.attached = true;\n for (const drive of this.resolved) this.install(drive);\n this.notify();\n }\n\n /**\n * Attaches, follows the moves other tabs made since `load`, then brings\n * the sync remotes in line; returns the collections to keep a copy of.\n */\n async start(\n router: PlacementRouter | undefined,\n resolved: readonly ResolvedSwitchboardDrive[],\n syncsDrives: boolean,\n ): Promise<ResolvedSwitchboardDrive[]> {\n this.attach(router, resolved);\n await this.reload();\n if (!syncsDrives) return [];\n const placed = new Set(this.placed().map((drive) => drive.slug));\n await removeLocalCopies(\n this.sync,\n this.resolved.filter(\n (drive) => placed.has(drive.slug) && !this.busy.has(drive.slug),\n ),\n );\n return this.keptCollections();\n }\n\n /** The drive's placement in this browser; undefined for a drive the host does not place on a switchboard. */\n placementOf(driveId: string): DrivePlacement | undefined {\n return this.snapshot.get(driveId);\n }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /** Opens the drive on `backend` in this browser: its switchboard, or a copy here. */\n async move(driveId: string, backend: string): Promise<void> {\n const drive = this.resolved.find((d) => d.id === driveId);\n if (!drive?.id) {\n throw new Error(\n `Drive ${driveId} is not on a switchboard this app links`,\n );\n }\n if (backend !== drive.remote && backend !== LOCAL_BACKEND_NAME) {\n throw new Error(`Drive ${drive.slug} can't open on ${backend}`);\n }\n if (this.busy.has(drive.slug) || this.backendOf(drive) === backend) return;\n const previous = this.applied.get(drive.slug);\n const entry: PlacementEntry | undefined =\n backend === drive.configured\n ? undefined\n : { slug: drive.slug, backend, configWas: drive.configured };\n const id = drive.id;\n this.busy.set(drive.slug, backend);\n this.notify();\n try {\n await this.record(drive.slug, entry);\n if (needsCopy(backend)) {\n try {\n await this.sync.add(servedUrl(drive), id);\n await this.sync.untilCopied(id, drive.remote);\n } catch (error) {\n // Store first: the removal's broadcast makes other tabs re-read it.\n await this.record(drive.slug, previous);\n await removeLocalCopies(this.sync, [drive]);\n throw error;\n }\n } else {\n try {\n await removeLocalCopies(this.sync, [drive]);\n } catch (error) {\n await this.record(drive.slug, previous);\n throw error;\n }\n }\n this.install(drive);\n } finally {\n this.busy.delete(drive.slug);\n this.notify();\n }\n }\n\n /** Back to where the host config opens the drive. */\n reset(driveId: string): Promise<void> {\n const drive = this.resolved.find((d) => d.id === driveId);\n return drive ? this.move(driveId, drive.configured) : Promise.resolve();\n }\n\n /** Re-reads the store after another tab moved a drive; true when something changed. */\n async reload(): Promise<boolean> {\n if (!this.attached) return false;\n const next = await this.readMoves();\n if (!next) return false;\n const changed = this.drives.filter(\n (drive) =>\n !this.busy.has(drive.slug) &&\n effectiveBackend(drive, next.get(drive.slug)) !== this.backendOf(drive),\n );\n if (changed.length === 0) return false;\n await Promise.all(\n changed.map((drive) => this.follow(drive, next.get(drive.slug))),\n );\n this.notify();\n return true;\n }\n\n /** A copy kept by another tab is routed here only once it has arrived. */\n private async follow(\n drive: SwitchboardDrive,\n entry: PlacementEntry | undefined,\n ): Promise<void> {\n const resolved = this.resolved.find((d) => d.slug === drive.slug);\n if (needsCopy(effectiveBackend(drive, entry)) && resolved?.id) {\n this.busy.set(drive.slug, LOCAL_BACKEND_NAME);\n this.notify();\n try {\n await this.sync.untilCopied(resolved.id, resolved.remote);\n } catch (error) {\n logger.warn(\n \"Drive @slug was not copied into this browser; leaving it where it opens: @error\",\n drive.slug,\n error,\n );\n return;\n } finally {\n this.busy.delete(drive.slug);\n }\n // The mover may have rolled back while this tab waited.\n const latest = await this.readMoves();\n if (!latest) return;\n entry = latest.get(drive.slug);\n }\n if (entry) this.applied.set(drive.slug, entry);\n else this.applied.delete(drive.slug);\n if (resolved) this.install(resolved);\n }\n\n private async readMoves(): Promise<Map<string, PlacementEntry> | undefined> {\n let entries: PlacementEntry[];\n try {\n entries = await this.store.list();\n } catch (error) {\n logger.warn(\"Could not read this browser's drive moves: @error\", error);\n return undefined;\n }\n const { applied } = applyPlacementEntries(this.drives, entries);\n return new Map(applied.map((entry) => [entry.slug, entry]));\n }\n\n private async record(\n slug: string,\n entry: PlacementEntry | undefined,\n ): Promise<void> {\n if (entry) {\n await this.store.put(entry);\n this.applied.set(slug, entry);\n } else {\n await this.store.delete(slug);\n this.applied.delete(slug);\n }\n }\n\n private install(drive: ResolvedSwitchboardDrive): void {\n const router = this.router;\n if (!router) return;\n const configured =\n drive.configured === drive.remote ? drive.remote : undefined;\n const runtime = this.applied.get(drive.slug)?.backend;\n for (const key of placementKeys(drive)) {\n router.setConfiguredPlacement(key, configured);\n router.setPlacement(key, runtime);\n }\n }\n\n private notify(): void {\n const snapshot = new Map<string, DrivePlacement>();\n for (const drive of this.resolved) {\n if (!drive.id) continue;\n const backend = this.backendOf(drive);\n snapshot.set(drive.id, {\n drive,\n backend,\n moved: backend !== drive.configured,\n busy: this.busy.has(drive.slug),\n ...(this.busy.has(drive.slug)\n ? { movingTo: this.busy.get(drive.slug) }\n : {}),\n });\n }\n this.snapshot = snapshot;\n for (const listener of [...this.listeners]) listener();\n }\n}\n","import type { PHStore } from \"@powerhousedao/reactor-browser\";\nimport {\n bindPHStoreToWindow,\n createPHStore,\n getActivePHStore,\n} from \"@powerhousedao/reactor-browser\";\n\n/** The app's PH store: the active one, else a new one bound to `window.ph`.\n * Values set on `window.ph` before carry over. */\nexport function getConnectPHStore(): PHStore {\n const active = getActivePHStore();\n if (active) return active;\n const store = createPHStore();\n bindPHStoreToWindow(store);\n return store;\n}\n","import type { DocumentModelLib } from \"document-model\";\n\n/** A module namespace, as `import * as pkg` or Vite HMR hands it over. */\nexport type ModuleNamespace = Record<string, any> & {\n [Symbol.toStringTag]: \"Module\";\n};\n\nexport type LocalPackage = DocumentModelLib<any> | ModuleNamespace;\n\n/** The package itself, or a loader (`() => import(\"./index.js\")`) that keeps it off the boot path. */\nexport type LocalPackageSource = LocalPackage | (() => Promise<LocalPackage>);\n\n/** Starts loading the project's package; a loader runs once, here. */\nexport function loadLocalPackage(\n source: LocalPackageSource | undefined,\n): DocumentModelLib | Promise<DocumentModelLib> | undefined {\n const pkg = (typeof source === \"function\" ? source() : source) as\n | DocumentModelLib\n | Promise<DocumentModelLib>\n | undefined;\n // Consumers await it; this only stops an early rejection being reported as unhandled.\n if (pkg instanceof Promise) pkg.catch(() => undefined);\n return pkg;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runtime-ClrasOcu.js","names":[],"sources":["../src/runtime/multi-reactor-flag.ts","../src/runtime/reactor-worker-flag.ts","../src/runtime/reactor-client-mode.ts","../src/runtime/app-reactor-client.ts","../src/runtime/new-drive.ts","../src/runtime/drive-placement.ts","../src/runtime/drive-placements.ts","../src/runtime/ph-store.ts","../src/runtime/local-package.ts"],"sourcesContent":["import { getRuntimeConfig } from \"./runtime-config.js\";\n\n// Config only: the flag is in the worker fingerprint, so a per-tab value would split tabs.\nexport function isMultiReactorEnabled(): boolean {\n return getRuntimeConfig().connect.instance?.multiReactor ?? false;\n}\n","import { getRuntimeConfig } from \"./runtime-config.js\";\nimport {\n isRuntimeFlagEnabled,\n resolveRuntimeFlag,\n type RuntimeFlagInput,\n} from \"./runtime-flag.js\";\n\nexport const REACTOR_WORKER_QUERY_KEY = \"reactorWorker\";\nexport const REACTOR_WORKER_STORAGE_KEY = \"ph:reactorWorker\";\n\nexport type ReactorWorkerFlagInput = RuntimeFlagInput;\n\nexport function resolveReactorWorkerEnabled(\n input: ReactorWorkerFlagInput,\n): boolean {\n return resolveRuntimeFlag(input);\n}\n\nexport function isReactorWorkerEnabled(): boolean {\n return isRuntimeFlagEnabled({\n queryKey: REACTOR_WORKER_QUERY_KEY,\n storageKey: REACTOR_WORKER_STORAGE_KEY,\n readConfigFlag: () =>\n getRuntimeConfig().connect.instance?.reactorWorker ?? false,\n });\n}\n","import type { PHReactorClientMode } from \"@powerhousedao/shared/clis\";\nimport { getRuntimeConfig } from \"./runtime-config.js\";\nimport { isMultiReactorEnabled } from \"./multi-reactor-flag.js\";\nimport { isReactorWorkerEnabled } from \"./reactor-worker-flag.js\";\nimport { logger } from \"document-model\";\n\n/**\n * Which of the four reactor clients Connect boots. Standardized by the\n * multi-reactor work; this resolves the single selector Connect acts on:\n *\n * in-process | worker | remote | router\n *\n * Precedence: an explicit `?reactorClient=` query override (kept in\n * localStorage so it survives a reload), then a stored override, then the\n * served `connect.instance.reactorClient` (projected from the host's reactor\n * backends), then the LEGACY booleans — so a config that only sets\n * reactorWorker/multiReactor behaves exactly as before.\n *\n * `remote` is the only mode that builds NO local reactor (no PGlite): it is a\n * pure client to a remote Switchboard. `router` keeps the local reactor and\n * adds remote backends; `worker`/`in-process` are the local reactor alone.\n */\nexport type ReactorClientMode = PHReactorClientMode;\n\nconst MODES: ReadonlySet<string> = new Set([\n \"in-process\",\n \"worker\",\n \"remote\",\n \"router\",\n]);\n\nexport const REACTOR_CLIENT_QUERY_KEY = \"reactorClient\";\nexport const REACTOR_CLIENT_STORAGE_KEY = \"ph:reactorClient\";\n\nconst asMode = (value: string | null | undefined) =>\n value && MODES.has(value) ? (value as ReactorClientMode) : undefined;\n\nconst LOCAL_ONLY: ReadonlySet<ReactorClientMode> = new Set([\n \"in-process\",\n \"worker\",\n]);\n\n/** Set while this page load keeps its reactor to itself against the host's `newDrives`. */\nlet blockedNewDrives: Error | undefined;\n\n/** While new drives go to a switchboard, a local-only override applies only from the URL, never from storage. */\nfunction fromTab(newDrives: string | undefined): ReactorClientMode | undefined {\n if (typeof window === \"undefined\") return undefined;\n let query: ReactorClientMode | undefined;\n try {\n query = asMode(\n new URLSearchParams(window.location.search).get(REACTOR_CLIENT_QUERY_KEY),\n );\n } catch {\n return undefined;\n }\n const localOnly = (mode: ReactorClientMode | undefined) =>\n newDrives !== undefined && mode !== undefined && LOCAL_ONLY.has(mode);\n try {\n if (query) {\n if (localOnly(query)) {\n window.localStorage.removeItem(REACTOR_CLIENT_STORAGE_KEY);\n } else {\n window.localStorage.setItem(REACTOR_CLIENT_STORAGE_KEY, query);\n }\n return query;\n }\n const stored = asMode(\n window.localStorage.getItem(REACTOR_CLIENT_STORAGE_KEY),\n );\n if (!localOnly(stored)) return stored;\n window.localStorage.removeItem(REACTOR_CLIENT_STORAGE_KEY);\n logger.warn(\n \"Ignoring the stored ?reactorClient=@mode: new drives go to @switchboard\",\n stored,\n newDrives,\n );\n return undefined;\n } catch {\n // Blocked storage: the query still applies for this page load.\n return query;\n }\n}\n\nexport function resolveReactorClientMode(): ReactorClientMode {\n const instance = getRuntimeConfig().connect.instance;\n const newDrives = instance?.router?.newDrives;\n const override = fromTab(newDrives);\n blockedNewDrives =\n override && newDrives !== undefined && LOCAL_ONLY.has(override)\n ? new Error(\n `New drives go to ${newDrives}, but this browser is set to keep its reactor to itself (?reactorClient=${override}).`,\n )\n : undefined;\n if (override) return override;\n const configured = instance?.reactorClient;\n if (configured && MODES.has(configured)) return configured;\n // Legacy fallback — unchanged behaviour for configs without reactorClient.\n if (isMultiReactorEnabled()) return \"router\";\n if (isReactorWorkerEnabled()) return \"worker\";\n return \"in-process\";\n}\n\n/** Why this page load can't create a drive where the host says, or undefined when it can. */\nexport function newDrivesBlocked(): Error | undefined {\n return blockedNewDrives;\n}\n","import type { IReactorClient } from \"@powerhousedao/reactor\";\nimport {\n getDrives,\n getFullReactorClient,\n getPHStore,\n recordBackendAuthError,\n setBackendAuth,\n setDrives,\n setFullReactorClient,\n} from \"@powerhousedao/reactor-browser\";\nimport type {\n PHConnectRemoteBackend,\n PHConnectRouterSettings,\n} from \"@powerhousedao/shared/clis\";\nimport type { DocumentDriveDocument } from \"@powerhousedao/shared/document-drive\";\nimport type { ISigner } from \"@powerhousedao/shared/document-model\";\nimport { logger } from \"document-model\";\nimport { switchboardOfDrive } from \"@powerhousedao/shared/host-tokens\";\nimport type * as MultiReactor from \"./multi-reactor.js\";\nimport type {\n LoadedDocumentModels,\n LocalReactorModule,\n} from \"./multi-reactor.js\";\nimport type { ReactorClientMode } from \"./reactor-client-mode.js\";\nimport { resolveReactorClientMode } from \"./reactor-client-mode.js\";\nimport { isReactorWorkerEnabled } from \"./reactor-worker-flag.js\";\nimport { setDriveRelister } from \"./drive-relist.js\";\n\n// Kept here: multi-reactor.ts is loaded lazily.\nconst REMOTE_BACKEND_NAME = \"switchboard-remote\";\n\ntype MultiReactorModule = typeof MultiReactor;\n\nexport type AppReactorClientParams = {\n mode: ReactorClientMode;\n /** Built, and ready (the worker has started up). Undefined in `remote`\n * mode, where no local reactor is built. */\n module: LocalReactorModule | undefined;\n /** The fallback switchboard when `remoteBackends` is empty. */\n remoteDriveUrl: string | undefined;\n /** One per switchboard, by host key; `url` is a drive address on it. */\n remoteBackends?: readonly PHConnectRemoteBackend[];\n /** Where new drives go and the router's timeouts, from the host config. */\n router?: PHConnectRouterSettings;\n /** Config placements known at build time; later ones go through `setConfiguredPlacement`. */\n collections?: Readonly<Record<string, string>>;\n /** Signs routed writes; unsigned (anonymous) without one. */\n signer?: ISigner;\n documentModelModules: LoadedDocumentModels;\n loadRouter?: () => Promise<MultiReactorModule>;\n};\n\nconst loadMultiReactor = (): Promise<MultiReactorModule> =>\n import(\"./multi-reactor.js\");\n\n/** What a mode builds in the tab; `remote` builds no local reactor (no PGlite). */\nexport type ReactorClientPlan = {\n mode: ReactorClientMode;\n /** `router`: the local reactor plus remote backends. */\n multiReactor: boolean;\n needsLocalReactor: boolean;\n /** The local reactor runs in the shared worker. */\n localAsWorker: boolean;\n};\n\nexport function planReactorClient(\n mode: ReactorClientMode = resolveReactorClientMode(),\n): ReactorClientPlan {\n const needsLocalReactor = mode !== \"remote\";\n return {\n mode,\n multiReactor: mode === \"router\",\n needsLocalReactor,\n localAsWorker:\n needsLocalReactor &&\n (mode === \"worker\" || (mode === \"router\" && isReactorWorkerEnabled())),\n };\n}\n\n/** Builds the tab's reactor, e.g. Connect's PGlite or worker reactor. */\nexport type LocalReactorFactory = (\n plan: ReactorClientPlan,\n) => Promise<LocalReactorModule>;\n\nexport type CreateAppReactorClientOptions = Omit<\n AppReactorClientParams,\n \"mode\" | \"module\"\n> & {\n plan?: ReactorClientPlan;\n /** Called only when the plan needs a local reactor. */\n localReactor?: LocalReactorFactory;\n};\n\n/** Builds the local reactor the plan needs, then selects the app's client. */\nexport async function createAppReactorClient(\n options: CreateAppReactorClientOptions,\n) {\n const { localReactor, ...params } = options;\n const plan = options.plan ?? planReactorClient();\n let module: LocalReactorModule | undefined;\n if (plan.needsLocalReactor) {\n if (!localReactor) {\n throw new Error(\n `reactorClient mode \"${plan.mode}\" needs a local reactor; pass localReactor`,\n );\n }\n module = await localReactor(plan);\n }\n const { client, router } = await selectClient({\n ...params,\n mode: plan.mode,\n module,\n });\n return { plan, module, client, router };\n}\n\n/**\n * Pick the client for the resolved mode:\n * - `in-process` / `worker`: the local client, router module never loaded.\n * - `router`: the local reactor plus remote backends.\n * - `remote`: a remote-only router — a full IReactorClient that proxies to\n * the Switchboard over GraphQL with NO local reactor (no PGlite).\n */\nexport async function selectAppReactorClient(\n params: AppReactorClientParams,\n): Promise<IReactorClient> {\n return (await selectClient(params)).client;\n}\n\n/** The app's client, and the router when it is one. */\nasync function selectClient(params: AppReactorClientParams): Promise<{\n client: IReactorClient;\n router?: MultiReactor.ConnectRoutingClient;\n}> {\n if (params.mode === \"remote\") {\n setFullReactorClient(undefined);\n const remote = await buildRemoteOnly(params);\n setFullReactorClient(remote);\n return { client: remote, router: remote };\n }\n if (params.mode !== \"router\") {\n if (getPHStore()?.fullReactorClient) {\n setFullReactorClient(undefined);\n }\n return { client: requireModule(params).client };\n }\n setFullReactorClient(undefined);\n const routed = await buildRouter(params);\n setFullReactorClient(routed);\n return routed\n ? { client: routed, router: routed }\n : { client: requireModule(params).client };\n}\n\nfunction requireModule(params: AppReactorClientParams): LocalReactorModule {\n if (!params.module) {\n throw new Error(\n `reactorClient mode \"${params.mode}\" needs a local reactor, but none was built`,\n );\n }\n return params.module;\n}\n\n/**\n * A Switchboard that cannot answer must not fail boot: list the tab's drives.\n *\n * A Switchboard that REFUSES the caller (401/403) must not fail boot either:\n * the refusal is recorded (`backendAuth`) so the root view shows the sign-in\n * gate, and the drive list is empty until the user is someone the backend\n * accepts. A remote-only client has no local drives to fall back on.\n */\nexport async function getAppDrives(\n client: IReactorClient,\n local: IReactorClient,\n): Promise<DocumentDriveDocument[]> {\n if (client === local) {\n try {\n const drives = await getDrives(local);\n setBackendAuth(undefined);\n return drives;\n } catch (error) {\n if (recordBackendAuthError(error, \"switchboard\")) {\n logger.warn(\n \"The Switchboard refused this caller; showing the sign-in gate: @error\",\n error,\n );\n return [];\n }\n throw error;\n }\n }\n try {\n const drives = await getDrives(client);\n setBackendAuth(undefined);\n return drives;\n } catch (error) {\n if (recordBackendAuthError(error, \"switchboard\")) {\n logger.warn(\n \"A remote backend refused this caller; listing the local reactor's drives: @error\",\n error,\n );\n } else {\n logger.warn(\n \"Could not list drives through the router; listing the local reactor's: @error\",\n error,\n );\n }\n return getDrives(local);\n }\n}\n\n/**\n * The boot refresh of the drives store. It lists through `getAppDrives`\n * whichever client is in use, so a Switchboard that refuses the caller is\n * recorded for the sign-in gate here too: the remote-only client is its own\n * `local`, and a bare `refreshReactorDataClient` would let the 401 escape\n * and fail the boot before the gate can render.\n */\nexport async function refreshAppDrives(\n client: IReactorClient,\n local: IReactorClient,\n): Promise<void> {\n setDrives(await getAppDrives(client, local));\n}\n\n/**\n * Lists the drives again as the caller the tab now is, through the clients\n * boot left in the store: after a sign-in landing or a sign-out that followed\n * a backend's refusal. Nothing to do before boot has set a client.\n */\nexport async function relistAppDrives(): Promise<void> {\n const client = getFullReactorClient();\n if (!client) return;\n const local = getPHStore()?.reactorClientModule?.client ?? client;\n await refreshAppDrives(client, local);\n}\n\n// The backend auth gate relists through drive-relist (see there)\nsetDriveRelister(relistAppDrives);\n\n/** The switchboards to route to: the host's remote backends, else the default drive's. */\nfunction switchboardRemotes(params: AppReactorClientParams): {\n remotes: MultiReactor.SwitchboardRemote[];\n unparsed: string[];\n} {\n const listed: readonly PHConnectRemoteBackend[] = params.remoteBackends\n ?.length\n ? params.remoteBackends\n : params.remoteDriveUrl\n ? [{ name: REMOTE_BACKEND_NAME, url: params.remoteDriveUrl }]\n : [];\n const remotes: MultiReactor.SwitchboardRemote[] = [];\n const unparsed: string[] = [];\n for (const { url, ...remote } of listed) {\n const graphqlUrl = switchboardOfDrive(url)?.graphql;\n if (graphqlUrl) remotes.push({ ...remote, graphqlUrl });\n else unparsed.push(url);\n }\n return { remotes, unparsed };\n}\n\nasync function buildRouter(\n params: AppReactorClientParams,\n): Promise<MultiReactor.ConnectRoutingClient | undefined> {\n const { remotes, unparsed } = switchboardRemotes(params);\n for (const url of unparsed) {\n logger.warn(\n \"multiReactor is on but the remote drive URL @url does not parse; skipping it\",\n url,\n );\n }\n // New drives belong on a switchboard: without the router they would be\n // created in this browser, so it is required.\n const newDrives = params.router?.newDrives;\n if (remotes.length === 0) {\n if (newDrives !== undefined) {\n throw new Error(\n `New drives go to ${newDrives}, but this Connect has no address for it, so it can't start.`,\n );\n }\n logger.warn(\n \"multiReactor is on but no remote drive URL is configured; using the local reactor only\",\n );\n return undefined;\n }\n let multiReactor: MultiReactorModule;\n try {\n multiReactor = await (params.loadRouter ?? loadMultiReactor)();\n } catch (error) {\n if (newDrives !== undefined) {\n throw new Error(\n `New drives go to ${newDrives}, but the router that reaches it didn't load, so Connect can't start.`,\n { cause: error },\n );\n }\n logger.error(\"Could not load the multi-reactor router: @error\", error);\n return undefined;\n }\n logger.info(\n \"Multi-reactor routing to @urls\",\n remotes.map((remote) => remote.graphqlUrl).join(\", \"),\n );\n return multiReactor.buildMultiReactorClient({\n module: requireModule(params),\n remotes,\n collections: params.collections,\n ...params.router,\n signer: params.signer,\n documentModelModules: params.documentModelModules,\n });\n}\n\n/**\n * The `remote` mode: a remote-only router — a full IReactorClient that proxies\n * to the Switchboard over GraphQL with NO local backend, so no local reactor\n * and no PGlite are ever built. A missing/unparseable remote URL is a hard\n * config error here (unlike `router`, there is no local reactor to fall back\n * to).\n */\nasync function buildRemoteOnly(\n params: AppReactorClientParams,\n): Promise<MultiReactor.ConnectRoutingClient> {\n const { remotes, unparsed } = switchboardRemotes(params);\n if (unparsed.length > 0) {\n throw new Error(\n `reactorClient \"remote\": the remote drive URL \"${unparsed[0]}\" does not resolve to a Switchboard GraphQL endpoint`,\n );\n }\n if (remotes.length === 0) {\n throw new Error(\n 'reactorClient \"remote\" needs a remote drive URL — declare a remote backend / default drive the client can proxy to',\n );\n }\n const multiReactor = await (params.loadRouter ?? loadMultiReactor)();\n logger.info(\n \"Remote reactor client → @urls\",\n remotes.map((remote) => remote.graphqlUrl).join(\", \"),\n );\n return multiReactor.buildMultiReactorClient({\n module: undefined,\n remotes,\n collections: params.collections,\n ...params.router,\n signer: params.signer,\n documentModelModules: params.documentModelModules,\n });\n}\n","import { isDriveAuthError } from \"@powerhousedao/reactor\";\n\n/** A drive create the switchboard new drives go to did not answer. */\nexport class NewDriveUnansweredError extends Error {\n override readonly name = \"NewDriveUnansweredError\";\n\n constructor(\n readonly backend: string,\n options?: ErrorOptions,\n ) {\n super(`${backend} isn't answering, so the drive wasn't created.`, options);\n }\n}\n\n/** `auth`: the switchboard refused the caller, so signing in may help. */\nexport class NewDriveRefusedError extends Error {\n override readonly name = \"NewDriveRefusedError\";\n\n constructor(\n readonly backend: string,\n readonly auth: boolean,\n reason: string | undefined,\n options?: ErrorOptions,\n ) {\n super(\n reason\n ? `${backend} refused the new drive: ${reason}`\n : `${backend} refused the new drive.`,\n options,\n );\n }\n}\n\n// fetch's network failure: Node, Chrome, Firefox, Safari.\nconst NETWORK_FAILURE =\n /fetch failed|failed to fetch|networkerror|load failed/i;\n\ntype Failure = {\n name?: unknown;\n message?: unknown;\n cause?: unknown;\n statusCode?: unknown;\n category?: unknown;\n response?: { status?: unknown; errors?: { message?: unknown }[] };\n};\n\n/** Each error in the cause chain, outermost first. */\nfunction* chain(error: unknown): Generator<Failure> {\n for (let at = error; at; at = (at as Failure).cause) yield at as Failure;\n}\n\nfunction statusOf(at: Failure): number | undefined {\n const status = at.response?.status ?? at.statusCode;\n return typeof status === \"number\" ? status : undefined;\n}\n\n/** A failure to reach a switchboard, not an answer from it. */\nfunction isUnanswered(error: unknown): boolean {\n for (const at of chain(error)) {\n if (\n at.name === \"SwitchboardUnreachableError\" ||\n at.name === \"TimeoutError\" ||\n at.name === \"ProbeTimeoutError\" ||\n at.category === \"network\" ||\n (at instanceof TypeError && NETWORK_FAILURE.test(at.message))\n ) {\n return true;\n }\n const status = statusOf(at);\n if (status !== undefined && status >= 500) return true;\n }\n return false;\n}\n\nfunction isAuthRefusal(error: unknown): boolean {\n for (const at of chain(error)) {\n if (isDriveAuthError(at)) return true;\n const status = statusOf(at);\n if (status === 401 || status === 403) return true;\n }\n return false;\n}\n\n/** An answer from a server, not a failure in this browser. */\nfunction isServerAnswer(error: unknown): boolean {\n for (const at of chain(error)) {\n if (\n at.response !== undefined ||\n statusOf(at) !== undefined ||\n at.name === \"GraphQLRequestError\" ||\n at.name === \"MisrouteUnresolvedError\"\n ) {\n return true;\n }\n }\n return false;\n}\n\n/** The first reason the switchboard gave, if it gave one. */\nfunction reasonOf(error: unknown): string | undefined {\n for (const at of chain(error)) {\n const message = at.response?.errors?.[0]?.message;\n if (typeof message === \"string\" && message !== \"\") return message;\n if (at.name === \"GraphQLRequestError\" && typeof at.message === \"string\") {\n return at.message;\n }\n }\n return undefined;\n}\n\n/** A failed drive create, named for the switchboard `client` creates new drives on; else `error` itself. */\nexport function newDriveError(client: unknown, error: unknown): unknown {\n const backend = (client as { newDrives?: unknown } | null | undefined)\n ?.newDrives;\n if (typeof backend !== \"string\") return error;\n if (isUnanswered(error)) {\n return new NewDriveUnansweredError(backend, { cause: error });\n }\n if (!isServerAnswer(error)) return error;\n return new NewDriveRefusedError(\n backend,\n isAuthRefusal(error),\n reasonOf(error),\n { cause: error },\n );\n}\n","import { DriveCollectionId } from \"@powerhousedao/reactor\";\nimport type {\n PHConnectCollection,\n PHConnectRemoteBackend,\n PHConnectRuntimeDefaultDrive,\n} from \"@powerhousedao/shared/clis\";\nimport { switchboardOfDrive } from \"@powerhousedao/shared/host-tokens\";\nimport { logger } from \"document-model\";\n\n/** The router backend of the reactor in this browser. */\nexport const LOCAL_BACKEND_NAME = \"connect-local\";\n\n/** A drive the host keeps on a switchboard, and where its config opens it. */\nexport type SwitchboardDrive = {\n /** The host's name for it: its address's slug, or a collection's key. */\n slug: string;\n url: string;\n /** The switchboard's backend. */\n remote: string;\n /** `remote` (no copy here) or `LOCAL_BACKEND_NAME` (a synced copy). */\n configured: string;\n /** Not a default drive: its address may not be the one it is served at. */\n collection: boolean;\n};\n\n/** A switchboard drive with the names callers reach it by. */\nexport type ResolvedSwitchboardDrive = SwitchboardDrive & {\n id?: string;\n servedSlug?: string;\n};\n\n/** A drive this browser moved away from its host config. */\nexport type PlacementEntry = {\n slug: string;\n backend: string;\n configWas: string;\n};\n\ntype PlacementConfig = {\n drives?: { defaultDrives?: readonly PHConnectRuntimeDefaultDrive[] };\n instance?: {\n remoteBackends?: readonly PHConnectRemoteBackend[];\n collections?: Record<string, PHConnectCollection>;\n };\n};\n\n/** The config's local default drives, placed on this browser so a\n * switchboard `newDrives` doesn't take them. */\nexport function localDrivePlacements(\n defaultDrives: readonly PHConnectRuntimeDefaultDrive[],\n): Record<string, string> {\n return Object.fromEntries(\n defaultDrives.flatMap((drive) =>\n !(\"url\" in drive) && drive.id ? [[drive.id, LOCAL_BACKEND_NAME]] : [],\n ),\n );\n}\n\nexport function keepsLocalCopy(drive: PHConnectRuntimeDefaultDrive): boolean {\n return !(\"url\" in drive) || drive.sync !== false;\n}\n\n/** Remote default drives with a backend, then collections. */\nexport function switchboardDrives(config: PlacementConfig): SwitchboardDrive[] {\n const drives: SwitchboardDrive[] = [];\n for (const drive of config.drives?.defaultDrives ?? []) {\n if (!(\"url\" in drive) || !drive.backend) continue;\n const slug = switchboardOfDrive(drive.url)?.slug;\n if (slug === undefined) continue;\n drives.push({\n slug,\n url: drive.url,\n remote: drive.backend,\n configured: drive.sync === false ? drive.backend : LOCAL_BACKEND_NAME,\n collection: false,\n });\n }\n const remotes = config.instance?.remoteBackends ?? [];\n for (const [slug, entry] of Object.entries(\n config.instance?.collections ?? {},\n )) {\n const remote = remotes.find((r) => r.name === entry.backend);\n const base = remote && switchboardOfDrive(remote.url)?.base;\n if (!base || drives.some((drive) => drive.slug === slug)) continue;\n drives.push({\n slug,\n url: `${base}/d/${encodeURIComponent(slug)}`,\n remote: entry.backend,\n configured: entry.sync === false ? entry.backend : LOCAL_BACKEND_NAME,\n collection: true,\n });\n }\n return drives;\n}\n\n/** Stale: no such drive, a backend it can't open on, back to config, or a changed config value. */\nexport function applyPlacementEntries(\n drives: readonly SwitchboardDrive[],\n entries: readonly PlacementEntry[],\n): { applied: PlacementEntry[]; stale: PlacementEntry[] } {\n const applied: PlacementEntry[] = [];\n const stale: PlacementEntry[] = [];\n for (const entry of entries) {\n const drive = drives.find((d) => d.slug === entry.slug);\n const holds =\n drive !== undefined &&\n entry.configWas === drive.configured &&\n entry.backend !== drive.configured &&\n (entry.backend === drive.remote || entry.backend === LOCAL_BACKEND_NAME);\n (holds ? applied : stale).push(entry);\n }\n return { applied, stale };\n}\n\n/** A served slug names a declared one as is, or hashed (`<slug>-<8 hex>`). */\nexport function servesSlug(declared: string, served: string): boolean {\n return (\n served === declared ||\n (served.startsWith(`${declared}-`) &&\n /^[0-9a-f]{8}$/.test(served.slice(declared.length + 1)))\n );\n}\n\ntype DriveNames = { id: string; slug: string };\n\nfunction withinDeadline<T>(work: Promise<T>, timeoutMs: number): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const deadline = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(`no answer in ${timeoutMs} ms`)),\n timeoutMs,\n );\n });\n return Promise.race([work, deadline]).finally(() => clearTimeout(timer));\n}\n\nexport type ResolveSwitchboardDrivesOptions = {\n timeoutMs?: number;\n /** A collection whose address does not answer: find it among its switchboard's drives. */\n findServed?: (drive: SwitchboardDrive) => Promise<DriveNames | undefined>;\n};\n\n/** An unreachable drive keeps only the slug in its address. */\nexport async function resolveSwitchboardDrives(\n drives: readonly SwitchboardDrive[],\n fetchInfo: (url: string) => Promise<DriveNames>,\n { timeoutMs = 5_000, findServed }: ResolveSwitchboardDrivesOptions = {},\n): Promise<ResolvedSwitchboardDrive[]> {\n return Promise.all(\n drives.map(async (drive): Promise<ResolvedSwitchboardDrive> => {\n try {\n const { id, slug } = await withinDeadline(\n fetchInfo(drive.url),\n timeoutMs,\n );\n return { ...drive, id, servedSlug: slug };\n } catch (error) {\n if (drive.collection && findServed) {\n try {\n const served = await withinDeadline(findServed(drive), timeoutMs);\n if (served) {\n return { ...drive, id: served.id, servedSlug: served.slug };\n }\n } catch (findError) {\n logger.warn(\n \"Could not find drive @slug on @remote: @error\",\n drive.slug,\n drive.remote,\n findError,\n );\n }\n }\n logger.warn(\n \"Could not read drive @url; routing it by its address only: @error\",\n drive.url,\n error,\n );\n const slug = switchboardOfDrive(drive.url)?.slug;\n return slug === undefined ? drive : { ...drive, servedSlug: slug };\n }\n }),\n );\n}\n\n/** The address the drive is served at. */\nexport function servedUrl(drive: ResolvedSwitchboardDrive): string {\n const at = switchboardOfDrive(drive.url);\n return at && drive.servedSlug && drive.servedSlug !== at.slug\n ? `${at.base}/d/${encodeURIComponent(drive.servedSlug)}`\n : drive.url;\n}\n\n/** The keys a placement is installed under: the id and the served slug. */\nexport function placementKeys(drive: ResolvedSwitchboardDrive): string[] {\n return [drive.id, drive.servedSlug].filter(\n (key): key is string => key !== undefined && key !== \"\",\n );\n}\n\nexport type SyncRemotes = {\n list(): readonly {\n meta: { name: string; collectionId: DriveCollectionId };\n }[];\n remove(name: string): Promise<void>;\n};\n\n/** Drops the sync remote of every given drive; its local data is left unread. */\nexport async function removeLocalCopies(\n sync: SyncRemotes,\n resolved: readonly { id?: string }[],\n): Promise<void> {\n for (const drive of resolved) {\n if (!drive.id) continue;\n const collectionId = DriveCollectionId.forDrive(drive.id);\n const remotes = sync\n .list()\n .filter((remote) => remote.meta.collectionId.equals(collectionId));\n for (const remote of remotes) {\n await sync.remove(remote.meta.name);\n }\n }\n}\n","import type { RoutingReactorClient } from \"@powerhousedao/reactor-router\";\nimport { driveDocumentType } from \"@powerhousedao/shared/document-drive\";\nimport { logger } from \"document-model\";\nimport {\n applyPlacementEntries,\n servesSlug,\n LOCAL_BACKEND_NAME,\n placementKeys,\n removeLocalCopies,\n servedUrl,\n type PlacementEntry,\n type ResolvedSwitchboardDrive,\n type SwitchboardDrive,\n type SyncRemotes,\n} from \"./drive-placement.js\";\n\n/** A collection by its declared slug among its switchboard's drives, e.g. one served hashed. */\nexport async function findServedDrive(\n router: Pick<RoutingReactorClient, \"backends\">,\n drive: SwitchboardDrive,\n): Promise<{ id: string; slug: string } | undefined> {\n const backend = router.backends.find((b) => b.name === drive.remote);\n if (!backend) return undefined;\n const { results } = await backend.api.find({ type: driveDocumentType });\n const served = results.find(\n (doc) => doc.header.slug && servesSlug(drive.slug, doc.header.slug),\n );\n return served && { id: served.header.id, slug: served.header.slug };\n}\n\n/** Where a drive opens given its stored move, if any: the move, else the host config. */\nexport function effectiveBackend(\n drive: SwitchboardDrive,\n entry: PlacementEntry | undefined,\n): string {\n return entry?.backend ?? drive.configured;\n}\n\n/** A drive opening on `backend` reads its copy here, which must arrive first. */\nexport function needsCopy(backend: string): boolean {\n return backend === LOCAL_BACKEND_NAME;\n}\n\n/** A copy has caught up once every scope is at least at the source's revision. */\nexport function caughtUp(\n local: Readonly<Record<string, number>>,\n source: Readonly<Record<string, number>>,\n): boolean {\n return Object.entries(source).every(\n ([scope, revision]) => (local[scope] ?? -1) >= revision,\n );\n}\n\n/** Where this browser keeps its moves; per origin, so every tab shares it. */\nexport interface PlacementStore {\n list(): Promise<PlacementEntry[]>;\n put(entry: PlacementEntry): Promise<void>;\n delete(slug: string): Promise<void>;\n}\n\n/** The router's two placement layers. */\nexport type PlacementRouter = {\n setPlacement(key: string, backend: string | undefined): void;\n setConfiguredPlacement(key: string, backend: string | undefined): void;\n};\n\nexport type PlacementSync = SyncRemotes & {\n /** Adds a sync remote for the drive. */\n add(url: string, driveId: string): Promise<void>;\n /** Bounded wait until the local copy holds what `remote` holds; rejects when it does not. */\n untilCopied(driveId: string, remote: string): Promise<void>;\n};\n\nexport type DrivePlacement = {\n drive: ResolvedSwitchboardDrive;\n /** Where the drive opens in this browser now. */\n backend: string;\n /** This browser moved it away from the host config. */\n moved: boolean;\n /** A move is running. */\n busy: boolean;\n /** Where a running move takes the drive. */\n movingTo?: string;\n};\n\n/** Config placement plus this browser's moves: store, then sync remote, then router. */\nexport class DrivePlacements {\n private applied = new Map<string, PlacementEntry>();\n private resolved: ResolvedSwitchboardDrive[] = [];\n private router: PlacementRouter | undefined;\n private readonly busy = new Map<string, string>();\n private attached = false;\n private readonly listeners = new Set<() => void>();\n private snapshot = new Map<string, DrivePlacement>();\n\n constructor(\n readonly drives: readonly SwitchboardDrive[],\n applied: readonly PlacementEntry[],\n private readonly store: PlacementStore,\n private readonly sync: PlacementSync,\n ) {\n for (const entry of applied) this.applied.set(entry.slug, entry);\n }\n\n /** Reads the store and drops the entries the config has overtaken (O2). */\n static async load(\n drives: readonly SwitchboardDrive[],\n store: PlacementStore,\n sync: PlacementSync,\n ): Promise<DrivePlacements> {\n let entries: PlacementEntry[] = [];\n try {\n entries = await store.list();\n } catch (error) {\n logger.warn(\"Could not read this browser's drive moves: @error\", error);\n }\n const { applied, stale } = applyPlacementEntries(drives, entries);\n for (const entry of stale) {\n store.delete(entry.slug).catch((error: unknown) => {\n logger.warn(\n \"Could not drop drive move @slug: @error\",\n entry.slug,\n error,\n );\n });\n }\n return new DrivePlacements(drives, applied, store, sync);\n }\n\n /** Where `drive` opens in this browser: a move, else the config. */\n backendOf(drive: SwitchboardDrive): string {\n return effectiveBackend(drive, this.applied.get(drive.slug));\n }\n\n /** Whether a remote default drive at `url` keeps a copy here. */\n keepsCopy(url: string): boolean {\n const drive = this.drives.find((d) => d.url === url);\n return !drive || this.backendOf(drive) === LOCAL_BACKEND_NAME;\n }\n\n /** Drives that open on their switchboard with no copy here. */\n placed(): SwitchboardDrive[] {\n return this.drives.filter(\n (drive) => this.backendOf(drive) !== LOCAL_BACKEND_NAME,\n );\n }\n\n /** Collections this browser keeps a copy of; default drives are added with the others. */\n keptCollections(): ResolvedSwitchboardDrive[] {\n return this.resolved.filter(\n (drive) =>\n drive.collection &&\n drive.id !== undefined &&\n this.backendOf(drive) === LOCAL_BACKEND_NAME,\n );\n }\n\n /** Installs both layers for every drive the router can be told about. */\n attach(\n router: PlacementRouter | undefined,\n resolved: readonly ResolvedSwitchboardDrive[],\n ): void {\n this.router = router;\n this.resolved = [...resolved];\n this.attached = true;\n for (const drive of this.resolved) this.install(drive);\n this.notify();\n }\n\n /**\n * Attaches, follows the moves other tabs made since `load`, then brings\n * the sync remotes in line; returns the collections to keep a copy of.\n */\n async start(\n router: PlacementRouter | undefined,\n resolved: readonly ResolvedSwitchboardDrive[],\n syncsDrives: boolean,\n ): Promise<ResolvedSwitchboardDrive[]> {\n this.attach(router, resolved);\n await this.reload();\n if (!syncsDrives) return [];\n const placed = new Set(this.placed().map((drive) => drive.slug));\n await removeLocalCopies(\n this.sync,\n this.resolved.filter(\n (drive) => placed.has(drive.slug) && !this.busy.has(drive.slug),\n ),\n );\n return this.keptCollections();\n }\n\n /** The drive's placement in this browser; undefined for a drive the host does not place on a switchboard. */\n placementOf(driveId: string): DrivePlacement | undefined {\n return this.snapshot.get(driveId);\n }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /** Opens the drive on `backend` in this browser: its switchboard, or a copy here. */\n async move(driveId: string, backend: string): Promise<void> {\n const drive = this.resolved.find((d) => d.id === driveId);\n if (!drive?.id) {\n throw new Error(\n `Drive ${driveId} is not on a switchboard this app links`,\n );\n }\n if (backend !== drive.remote && backend !== LOCAL_BACKEND_NAME) {\n throw new Error(`Drive ${drive.slug} can't open on ${backend}`);\n }\n if (this.busy.has(drive.slug) || this.backendOf(drive) === backend) return;\n const previous = this.applied.get(drive.slug);\n const entry: PlacementEntry | undefined =\n backend === drive.configured\n ? undefined\n : { slug: drive.slug, backend, configWas: drive.configured };\n const id = drive.id;\n this.busy.set(drive.slug, backend);\n this.notify();\n try {\n await this.record(drive.slug, entry);\n if (needsCopy(backend)) {\n try {\n await this.sync.add(servedUrl(drive), id);\n await this.sync.untilCopied(id, drive.remote);\n } catch (error) {\n // Store first: the removal's broadcast makes other tabs re-read it.\n await this.record(drive.slug, previous);\n await removeLocalCopies(this.sync, [drive]);\n throw error;\n }\n } else {\n try {\n await removeLocalCopies(this.sync, [drive]);\n } catch (error) {\n await this.record(drive.slug, previous);\n throw error;\n }\n }\n this.install(drive);\n } finally {\n this.busy.delete(drive.slug);\n this.notify();\n }\n }\n\n /** Back to where the host config opens the drive. */\n reset(driveId: string): Promise<void> {\n const drive = this.resolved.find((d) => d.id === driveId);\n return drive ? this.move(driveId, drive.configured) : Promise.resolve();\n }\n\n /** Re-reads the store after another tab moved a drive; true when something changed. */\n async reload(): Promise<boolean> {\n if (!this.attached) return false;\n const next = await this.readMoves();\n if (!next) return false;\n const changed = this.drives.filter(\n (drive) =>\n !this.busy.has(drive.slug) &&\n effectiveBackend(drive, next.get(drive.slug)) !== this.backendOf(drive),\n );\n if (changed.length === 0) return false;\n await Promise.all(\n changed.map((drive) => this.follow(drive, next.get(drive.slug))),\n );\n this.notify();\n return true;\n }\n\n /** A copy kept by another tab is routed here only once it has arrived. */\n private async follow(\n drive: SwitchboardDrive,\n entry: PlacementEntry | undefined,\n ): Promise<void> {\n const resolved = this.resolved.find((d) => d.slug === drive.slug);\n if (needsCopy(effectiveBackend(drive, entry)) && resolved?.id) {\n this.busy.set(drive.slug, LOCAL_BACKEND_NAME);\n this.notify();\n try {\n await this.sync.untilCopied(resolved.id, resolved.remote);\n } catch (error) {\n logger.warn(\n \"Drive @slug was not copied into this browser; leaving it where it opens: @error\",\n drive.slug,\n error,\n );\n return;\n } finally {\n this.busy.delete(drive.slug);\n }\n // The mover may have rolled back while this tab waited.\n const latest = await this.readMoves();\n if (!latest) return;\n entry = latest.get(drive.slug);\n }\n if (entry) this.applied.set(drive.slug, entry);\n else this.applied.delete(drive.slug);\n if (resolved) this.install(resolved);\n }\n\n private async readMoves(): Promise<Map<string, PlacementEntry> | undefined> {\n let entries: PlacementEntry[];\n try {\n entries = await this.store.list();\n } catch (error) {\n logger.warn(\"Could not read this browser's drive moves: @error\", error);\n return undefined;\n }\n const { applied } = applyPlacementEntries(this.drives, entries);\n return new Map(applied.map((entry) => [entry.slug, entry]));\n }\n\n private async record(\n slug: string,\n entry: PlacementEntry | undefined,\n ): Promise<void> {\n if (entry) {\n await this.store.put(entry);\n this.applied.set(slug, entry);\n } else {\n await this.store.delete(slug);\n this.applied.delete(slug);\n }\n }\n\n private install(drive: ResolvedSwitchboardDrive): void {\n const router = this.router;\n if (!router) return;\n const configured =\n drive.configured === drive.remote ? drive.remote : undefined;\n const runtime = this.applied.get(drive.slug)?.backend;\n for (const key of placementKeys(drive)) {\n router.setConfiguredPlacement(key, configured);\n router.setPlacement(key, runtime);\n }\n }\n\n private notify(): void {\n const snapshot = new Map<string, DrivePlacement>();\n for (const drive of this.resolved) {\n if (!drive.id) continue;\n const backend = this.backendOf(drive);\n snapshot.set(drive.id, {\n drive,\n backend,\n moved: backend !== drive.configured,\n busy: this.busy.has(drive.slug),\n ...(this.busy.has(drive.slug)\n ? { movingTo: this.busy.get(drive.slug) }\n : {}),\n });\n }\n this.snapshot = snapshot;\n for (const listener of [...this.listeners]) listener();\n }\n}\n","import type { PHStore } from \"@powerhousedao/reactor-browser\";\nimport {\n bindPHStoreToWindow,\n createPHStore,\n getActivePHStore,\n} from \"@powerhousedao/reactor-browser\";\n\n/** The app's PH store: the active one, else a new one bound to `window.ph`.\n * Values set on `window.ph` before carry over. */\nexport function getConnectPHStore(): PHStore {\n const active = getActivePHStore();\n if (active) return active;\n const store = createPHStore();\n bindPHStoreToWindow(store);\n return store;\n}\n","import type { DocumentModelLib } from \"document-model\";\n\n/** A module namespace, as `import * as pkg` or Vite HMR hands it over. */\nexport type ModuleNamespace = Record<string, any> & {\n [Symbol.toStringTag]: \"Module\";\n};\n\nexport type LocalPackage = DocumentModelLib<any> | ModuleNamespace;\n\n/** The package itself, or a loader (`() => import(\"./index.js\")`) that keeps it off the boot path. */\nexport type LocalPackageSource = LocalPackage | (() => Promise<LocalPackage>);\n\n/** Starts loading the project's package; a loader runs once, here. */\nexport function loadLocalPackage(\n source: LocalPackageSource | undefined,\n): DocumentModelLib | Promise<DocumentModelLib> | undefined {\n const pkg = (typeof source === \"function\" ? source() : source) as\n | DocumentModelLib\n | Promise<DocumentModelLib>\n | undefined;\n // Consumers await it; this only stops an early rejection being reported as unhandled.\n if (pkg instanceof Promise) pkg.catch(() => undefined);\n return pkg;\n}\n"],"mappings":"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"}
|
package/dist/runtime-config.js
CHANGED
|
@@ -5,12 +5,12 @@ import { resolveOriginPlaceholders } from "@powerhousedao/shared/connect/origin-
|
|
|
5
5
|
//#region src/runtime/runtime-config.ts
|
|
6
6
|
const loader = () => getConnectRegistry().runtimeConfig ??= new ConfigLoader(new JsonConfigAdapter({ path: `${import.meta.env?.BASE_URL ?? "/"}powerhouse.config.json` }));
|
|
7
7
|
async function loadRuntimeConfig(options = {}) {
|
|
8
|
-
const pluginHost = options.plugins ? import("./plugin-host-
|
|
8
|
+
const pluginHost = options.plugins ? import("./plugin-host-CaTO1tq-.js").then((n) => n.r).then(async (host) => {
|
|
9
9
|
host.registerConnectPlugins(options.plugins);
|
|
10
10
|
await host.knownPlugins();
|
|
11
11
|
return host;
|
|
12
12
|
}) : void 0;
|
|
13
|
-
const pluginUi = options.plugins ? import("./plugin-ui-
|
|
13
|
+
const pluginUi = options.plugins ? import("./plugin-ui-BS5j5g1d.js").then((n) => n.a) : void 0;
|
|
14
14
|
pluginHost?.catch(() => void 0);
|
|
15
15
|
pluginUi?.catch(() => void 0);
|
|
16
16
|
let config = await loader().read();
|
|
@@ -20,13 +20,13 @@ async function loadRuntimeConfig(options = {}) {
|
|
|
20
20
|
loader().prime(config);
|
|
21
21
|
}
|
|
22
22
|
if (config.connect?.composition) {
|
|
23
|
-
const { isRuntimeFlagEnabled } = await import("./runtime-flag-
|
|
23
|
+
const { isRuntimeFlagEnabled } = await import("./runtime-flag-DwQ3Jpdw.js").then((n) => n.r);
|
|
24
24
|
if (isRuntimeFlagEnabled({
|
|
25
25
|
queryKey: "pluginRunner",
|
|
26
26
|
storageKey: "ph:flag:pluginRunner",
|
|
27
27
|
readConfigFlag: () => true
|
|
28
28
|
})) {
|
|
29
|
-
const { applyComposition } = await pluginHost ?? await import("./plugin-host-
|
|
29
|
+
const { applyComposition } = await pluginHost ?? await import("./plugin-host-CaTO1tq-.js").then((n) => n.r);
|
|
30
30
|
config.connect = await applyComposition(config.connect);
|
|
31
31
|
loader().prime(config);
|
|
32
32
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { k as __exportAll } from "./views-
|
|
1
|
+
import { k as __exportAll } from "./views-LyRJcSKu.js";
|
|
2
2
|
//#region src/runtime/runtime-flag.ts
|
|
3
3
|
var runtime_flag_exports = /* @__PURE__ */ __exportAll({
|
|
4
4
|
isRuntimeFlagEnabled: () => isRuntimeFlagEnabled,
|
|
@@ -44,4 +44,4 @@ function parseFlag(value) {
|
|
|
44
44
|
//#endregion
|
|
45
45
|
export { resolveRuntimeFlag as n, runtime_flag_exports as r, isRuntimeFlagEnabled as t };
|
|
46
46
|
|
|
47
|
-
//# sourceMappingURL=runtime-flag-
|
|
47
|
+
//# sourceMappingURL=runtime-flag-DwQ3Jpdw.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-flag-
|
|
1
|
+
{"version":3,"file":"runtime-flag-DwQ3Jpdw.js","names":[],"sources":["../src/runtime/runtime-flag.ts"],"sourcesContent":["export type RuntimeFlagInput = {\n configFlag: boolean;\n queryParam?: string | null;\n storedValue?: string | null;\n};\n\n// A query-param override wins, then a stored dev override, then the config flag.\nexport function resolveRuntimeFlag(input: RuntimeFlagInput): boolean {\n const fromQuery = parseFlag(input.queryParam);\n if (fromQuery !== undefined) {\n return fromQuery;\n }\n const fromStore = parseFlag(input.storedValue);\n if (fromStore !== undefined) {\n return fromStore;\n }\n return input.configFlag;\n}\n\nexport type RuntimeFlagKeys = {\n queryKey: string;\n storageKey: string;\n readConfigFlag: () => boolean;\n};\n\n// Effective flag from config + dev override; a query-param override is persisted\n// to localStorage so it survives a refresh. Blocked storage reads as no override.\nexport function isRuntimeFlagEnabled(keys: RuntimeFlagKeys): boolean {\n const configFlag = keys.readConfigFlag();\n if (typeof window === \"undefined\") {\n return configFlag;\n }\n const queryParam = new URLSearchParams(window.location.search).get(\n keys.queryKey,\n );\n const storedValue = readStoredValue(keys.storageKey);\n const enabled = resolveRuntimeFlag({ configFlag, queryParam, storedValue });\n // Persist only an explicit override, not an unrecognized value.\n if (parseFlag(queryParam) !== undefined) {\n persistStoredValue(keys.storageKey, enabled);\n }\n return enabled;\n}\n\nfunction readStoredValue(storageKey: string): string | null {\n try {\n return window.localStorage.getItem(storageKey);\n } catch {\n return null;\n }\n}\n\nfunction persistStoredValue(storageKey: string, enabled: boolean): void {\n try {\n window.localStorage.setItem(storageKey, enabled ? \"true\" : \"false\");\n } catch {\n // The override still applies for this page load.\n }\n}\n\nfunction parseFlag(value: string | null | undefined): boolean | undefined {\n if (value === null || value === undefined) {\n return undefined;\n }\n const normalized = value.trim().toLowerCase();\n if (normalized === \"true\" || normalized === \"1\") {\n return true;\n }\n if (normalized === \"false\" || normalized === \"0\") {\n return false;\n }\n // Unrecognized value (e.g. a typo): no override, fall through to store/config.\n return undefined;\n}\n"],"mappings":";;;;;;AAOA,SAAgB,mBAAmB,OAAkC;CACnE,MAAM,YAAY,UAAU,MAAM,UAAU;CAC5C,IAAI,cAAc,KAAA,GAChB,OAAO;CAET,MAAM,YAAY,UAAU,MAAM,WAAW;CAC7C,IAAI,cAAc,KAAA,GAChB,OAAO;CAET,OAAO,MAAM;AACf;AAUA,SAAgB,qBAAqB,MAAgC;CACnE,MAAM,aAAa,KAAK,eAAe;CACvC,IAAI,OAAO,WAAW,aACpB,OAAO;CAET,MAAM,aAAa,IAAI,gBAAgB,OAAO,SAAS,MAAM,CAAC,CAAC,IAC7D,KAAK,QACP;CAEA,MAAM,UAAU,mBAAmB;EAAE;EAAY;EAAY,aADzC,gBAAgB,KAAK,UAC8B;CAAE,CAAC;CAE1E,IAAI,UAAU,UAAU,MAAM,KAAA,GAC5B,mBAAmB,KAAK,YAAY,OAAO;CAE7C,OAAO;AACT;AAEA,SAAS,gBAAgB,YAAmC;CAC1D,IAAI;EACF,OAAO,OAAO,aAAa,QAAQ,UAAU;CAC/C,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,mBAAmB,YAAoB,SAAwB;CACtE,IAAI;EACF,OAAO,aAAa,QAAQ,YAAY,UAAU,SAAS,OAAO;CACpE,QAAQ,CAER;AACF;AAEA,SAAS,UAAU,OAAuD;CACxE,IAAI,UAAU,QAAQ,UAAU,KAAA,GAC9B;CAEF,MAAM,aAAa,MAAM,KAAK,CAAC,CAAC,YAAY;CAC5C,IAAI,eAAe,UAAU,eAAe,KAC1C,OAAO;CAET,IAAI,eAAe,WAAW,eAAe,KAC3C,OAAO;AAIX"}
|
package/dist/runtime.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getIsEmbedded, useIsEmbedded } from "./embedded.js";
|
|
2
2
|
import { getRuntimeConfig, loadRuntimeConfig, primeRuntimeConfig } from "./runtime-config.js";
|
|
3
|
-
import { n as resolveRuntimeFlag, t as isRuntimeFlagEnabled } from "./runtime-flag-
|
|
4
|
-
import { A as resolveReactorClientMode, C as planReactorClient, D as REACTOR_CLIENT_QUERY_KEY, E as selectAppReactorClient, F as isMultiReactorEnabled, M as REACTOR_WORKER_STORAGE_KEY, N as isReactorWorkerEnabled, O as REACTOR_CLIENT_STORAGE_KEY, P as resolveReactorWorkerEnabled, S as getAppDrives, T as relistAppDrives, _ as switchboardDrives, a as effectiveBackend, b as newDriveError, c as LOCAL_BACKEND_NAME, d as localDrivePlacements, f as placementKeys, g as servesSlug, h as servedUrl, i as caughtUp, j as REACTOR_WORKER_QUERY_KEY, k as newDrivesBlocked, l as applyPlacementEntries, m as resolveSwitchboardDrives, n as getConnectPHStore, o as findServedDrive, p as removeLocalCopies, r as DrivePlacements, s as needsCopy, t as loadLocalPackage, u as keepsLocalCopy, v as NewDriveRefusedError, w as refreshAppDrives, x as createAppReactorClient, y as NewDriveUnansweredError } from "./runtime-
|
|
3
|
+
import { n as resolveRuntimeFlag, t as isRuntimeFlagEnabled } from "./runtime-flag-DwQ3Jpdw.js";
|
|
4
|
+
import { A as resolveReactorClientMode, C as planReactorClient, D as REACTOR_CLIENT_QUERY_KEY, E as selectAppReactorClient, F as isMultiReactorEnabled, M as REACTOR_WORKER_STORAGE_KEY, N as isReactorWorkerEnabled, O as REACTOR_CLIENT_STORAGE_KEY, P as resolveReactorWorkerEnabled, S as getAppDrives, T as relistAppDrives, _ as switchboardDrives, a as effectiveBackend, b as newDriveError, c as LOCAL_BACKEND_NAME, d as localDrivePlacements, f as placementKeys, g as servesSlug, h as servedUrl, i as caughtUp, j as REACTOR_WORKER_QUERY_KEY, k as newDrivesBlocked, l as applyPlacementEntries, m as resolveSwitchboardDrives, n as getConnectPHStore, o as findServedDrive, p as removeLocalCopies, r as DrivePlacements, s as needsCopy, t as loadLocalPackage, u as keepsLocalCopy, v as NewDriveRefusedError, w as refreshAppDrives, x as createAppReactorClient, y as NewDriveUnansweredError } from "./runtime-ClrasOcu.js";
|
|
5
5
|
import { t as useBackendAuthGate } from "./backend-auth-gate-DNKg_v4Y.js";
|
|
6
6
|
export { DrivePlacements, LOCAL_BACKEND_NAME, NewDriveRefusedError, NewDriveUnansweredError, REACTOR_CLIENT_QUERY_KEY, REACTOR_CLIENT_STORAGE_KEY, REACTOR_WORKER_QUERY_KEY, REACTOR_WORKER_STORAGE_KEY, applyPlacementEntries, caughtUp, createAppReactorClient, effectiveBackend, findServedDrive, getAppDrives, getConnectPHStore, getIsEmbedded, getRuntimeConfig, isMultiReactorEnabled, isReactorWorkerEnabled, isRuntimeFlagEnabled, keepsLocalCopy, loadLocalPackage, loadRuntimeConfig, localDrivePlacements, needsCopy, newDriveError, newDrivesBlocked, placementKeys, planReactorClient, primeRuntimeConfig, refreshAppDrives, relistAppDrives, removeLocalCopies, resolveReactorClientMode, resolveReactorWorkerEnabled, resolveRuntimeFlag, resolveSwitchboardDrives, selectAppReactorClient, servedUrl, servesSlug, switchboardDrives, useBackendAuthGate, useIsEmbedded };
|
|
@@ -871,21 +871,35 @@ function DeleteItemModal() {
|
|
|
871
871
|
const node = useNodeById(modal?.id);
|
|
872
872
|
const parent = useNodeParentFolderById(modal?.id);
|
|
873
873
|
const { drive } = useDriveRouteData();
|
|
874
|
+
const [pending, setPending] = useState(false);
|
|
875
|
+
const [error, setError] = useState();
|
|
874
876
|
const remove = async () => {
|
|
875
|
-
closePHModal();
|
|
876
877
|
if (!drive || !modal) return;
|
|
877
|
-
|
|
878
|
+
setPending(true);
|
|
879
|
+
setError(void 0);
|
|
880
|
+
try {
|
|
881
|
+
await deleteNode(drive.header.id, modal.id);
|
|
882
|
+
} catch (cause) {
|
|
883
|
+
setError(cause instanceof Error ? cause.message : String(cause));
|
|
884
|
+
setPending(false);
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
closePHModal();
|
|
878
888
|
setSelectedNode(parent);
|
|
879
889
|
};
|
|
880
|
-
return /* @__PURE__ */
|
|
890
|
+
return /* @__PURE__ */ jsxs(Dialog, {
|
|
881
891
|
title: `Delete ${node?.name ?? "item"}?`,
|
|
882
892
|
actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Cancel, {}), /* @__PURE__ */ jsx("button", {
|
|
883
893
|
type: "button",
|
|
884
894
|
className: primaryClass,
|
|
895
|
+
disabled: pending,
|
|
885
896
|
onClick: () => void remove(),
|
|
886
|
-
children: "Delete"
|
|
897
|
+
children: pending ? "Deleting…" : "Delete"
|
|
887
898
|
})] }),
|
|
888
|
-
children: /* @__PURE__ */ jsx("p", { children: node?.kind === "folder" ? "The folder and everything in it will be deleted." : "The document will be deleted." })
|
|
899
|
+
children: [/* @__PURE__ */ jsx("p", { children: node?.kind === "folder" ? "The folder and everything in it will be deleted." : "The document will be deleted." }), error && /* @__PURE__ */ jsx("p", {
|
|
900
|
+
className: "text-core-muted",
|
|
901
|
+
children: error
|
|
902
|
+
})]
|
|
889
903
|
});
|
|
890
904
|
}
|
|
891
905
|
/** Upgrades a document to its model's latest version. */
|
|
@@ -1369,4 +1383,4 @@ function PackagesTab() {
|
|
|
1369
1383
|
//#endregion
|
|
1370
1384
|
export { EditorHost as C, DirectoryListing as D, HISTORY_PAGE_SIZE as E, formatModified as O, NodeView as S, DocumentHistory as T, ThemeToggle as _, DriveNotFoundPage as a, DocumentView as b, CoreLayout as c, CreateDocumentModal as d, DeleteItemModal as f, LoginModal as g, DriveAuthRequiredModal as h, CoreRouteError as i, __exportAll as k, CoreRoot as l, DownloadDocumentWithErrorsModal as m, AppearanceTab as n, NotFoundPage as o, DocumentVersionUnsupportedModal as p, CoreSettingsPage as r, CoreHeader as s, PackagesTab as t, ConfirmDocumentUpgradeModal as u, BackendAuthGate as v, DocumentJson as w, DriveLayout as x, DriveIndex as y };
|
|
1371
1385
|
|
|
1372
|
-
//# sourceMappingURL=views-
|
|
1386
|
+
//# sourceMappingURL=views-LyRJcSKu.js.map
|