@powerhousedao/reactor-browser 6.2.2-dev.43 → 6.2.2-dev.45

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["DocumentChangeType","DriveCollectionId","isDriveAuthError","PropagationMode","createSetDriveNameAction","createSetDriveIconAction","copyNode","moveNode","DriveCollectionId","DriveCollectionId","REACTOR_SCHEMA","#namespace","#storage","#readMap","#writeMap"],"sources":["../src/actions/drive.ts","../src/constants.ts","../src/graphql/adapters.ts","../src/graphql/constants.ts","../src/graphql/fetchers.ts","../src/graphql/batch.ts","../src/graphql/document-fetcher.ts","../src/graphql/graphql-client-document-cache.ts","../src/graphql/mutators.ts","../src/hooks/allowed-document-model-modules.ts","../src/hooks/selected-folder.ts","../src/hooks/child-nodes.ts","../src/hooks/config/set-config-by-key.ts","../src/hooks/config/utils.ts","../src/hooks/config/set-config-by-object.ts","../src/hooks/config/use-value-by-key.ts","../src/hooks/connection-state.ts","../src/hooks/document-of-type.ts","../src/hooks/supported-document-types.ts","../src/hooks/document-types.ts","../src/hooks/document-version-status.ts","../src/utils/validate-document.ts","../src/utils/download-document.ts","../src/hooks/download-document.ts","../src/hooks/drive-by-id.ts","../src/hooks/editor-modules.ts","../src/utils/preload-editor.ts","../src/hooks/use-editor-preloader.ts","../src/hooks/file-drag-and-drop.ts","../src/hooks/folder-by-id.ts","../src/graphql/events.ts","../src/graphql/document-cache-client-middleware.ts","../src/hooks/init-graphql-reactor-client.ts","../src/hooks/items-in-selected-folder.ts","../src/hooks/node-actions.ts","../src/hooks/node-by-id.ts","../src/hooks/node-path.ts","../src/hooks/parent-folder.ts","../src/hooks/selected-document.ts","../src/hooks/subgraph-modules.ts","../src/hooks/theme.ts","../src/hooks/use-drive-system-info.ts","../src/hooks/use-editor-file-drop.ts","../src/utils/drives.ts","../src/utils/get-revision-from-date.ts","../src/utils/switchboard.ts","../src/utils/upgrade-preview.ts","../src/hooks/use-get-switchboard-link.ts","../src/hooks/use-on-drop-file.ts","../src/hooks/user-permissions.ts","../src/hooks/use-attachments.ts","../src/pglite/drop.ts","../src/reactor.ts","../src/remote-controller/action-tracker.ts","../src/remote-controller/remote-client.ts","../src/remote-controller/remote-controller.ts","../src/storage/base-storage.ts","../src/storage/local-storage.ts"],"sourcesContent":["import {\n DocumentChangeType,\n DriveCollectionId,\n isDriveAuthError,\n PropagationMode,\n type IReactorClient,\n type PollBehavior,\n} from \"@powerhousedao/reactor\";\nimport {\n driveCreateDocument,\n setAvailableOffline,\n setDriveIcon as createSetDriveIconAction,\n setDriveName as createSetDriveNameAction,\n setSharingType,\n type DocumentDriveDocument,\n type DriveInput,\n type SharingType,\n} from \"@powerhousedao/shared/document-drive\";\nimport type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { getUserPermissions } from \"../utils/user.js\";\nimport { showPHModal } from \"../hooks/modals.js\";\n\nconst DEFAULT_INITIAL_SYNC_TIMEOUT_MS = 30_000;\n\n// In-flight remote registrations keyed by collectionId. sync.list()/sync.add()\n// is not atomic, so concurrent addRemoteDrive calls for the same drive would\n// both miss the existing remote and register duplicates. Concurrent callers\n// share the first registration instead.\nconst inFlightRemoteRegistrations = new Map<string, Promise<unknown>>();\n\nexport type AddRemoteDriveOptions = {\n pollBehavior?: PollBehavior;\n /**\n * When true, wait for the drive document to be materialized locally\n * (i.e. queryable via the reactor) before resolving. Without this,\n * `addRemoteDrive` returns as soon as the remote is registered with\n * the sync manager, before initial backfill delivers the drive.\n */\n awaitInitialSync?: boolean;\n /** Timeout for the initial-sync wait. Defaults to 30s. */\n initialSyncTimeoutMs?: number;\n signal?: AbortSignal;\n};\n\n/**\n * Resolves once a document with the given id is queryable through the\n * reactor client. Subscribes to Created events filtered by id and\n * short-circuits if the document already exists.\n */\nexport async function waitForDocumentReady(\n reactorClient: IReactorClient,\n documentId: string,\n options?: { timeoutMs?: number; signal?: AbortSignal },\n): Promise<void> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_INITIAL_SYNC_TIMEOUT_MS;\n const signal = options?.signal;\n\n return new Promise<void>((resolve, reject) => {\n let settled = false;\n // eslint-disable-next-line prefer-const\n let unsubscribe: (() => void) | undefined;\n // eslint-disable-next-line prefer-const\n let timer: ReturnType<typeof setTimeout> | undefined;\n let abortHandler: (() => void) | undefined;\n\n const settle = (action: () => void) => {\n if (settled) return;\n settled = true;\n unsubscribe?.();\n if (timer) clearTimeout(timer);\n if (abortHandler && signal) {\n signal.removeEventListener(\"abort\", abortHandler);\n }\n action();\n };\n\n unsubscribe = reactorClient.subscribe({ ids: [documentId] }, (event) => {\n if (event.type === DocumentChangeType.Created) {\n settle(() => resolve());\n }\n });\n\n reactorClient\n .find({ ids: [documentId] })\n .then((existing) => {\n if (existing.results.length > 0) {\n settle(() => resolve());\n }\n })\n .catch(() => {\n // Ignore: the subscription will still resolve if the document arrives.\n });\n\n if (signal) {\n if (signal.aborted) {\n settle(() => reject(new DOMException(\"Aborted\", \"AbortError\")));\n return;\n }\n abortHandler = () => {\n settle(() => reject(new DOMException(\"Aborted\", \"AbortError\")));\n };\n signal.addEventListener(\"abort\", abortHandler);\n }\n\n timer = setTimeout(() => {\n settle(() =>\n reject(\n new Error(\n `Timed out after ${timeoutMs}ms waiting for document ${documentId}`,\n ),\n ),\n );\n }, timeoutMs);\n });\n}\n\nexport async function addDrive(input: DriveInput, preferredEditor?: string) {\n const { isAllowedToCreateDocuments } = getUserPermissions();\n if (!isAllowedToCreateDocuments) {\n throw new Error(\"User is not allowed to create drives\");\n }\n\n const reactorClient = window.ph?.reactorClient;\n if (!reactorClient) {\n throw new Error(\"ReactorClient not initialized\");\n }\n\n const driveDoc = driveCreateDocument({\n global: {\n name: input.global.name || \"\",\n icon: input.global.icon ?? null,\n nodes: [],\n },\n });\n\n if (preferredEditor) {\n driveDoc.header.meta = { preferredEditor };\n }\n\n return await reactorClient.create<DocumentDriveDocument>(driveDoc);\n}\n\nexport async function addRemoteDrive(\n url: string,\n driveId?: string,\n options?: AddRemoteDriveOptions,\n) {\n // remote drives are a full reactor client feature (sync manager + find)\n const reactorClient = window.ph?.reactorClientModule?.client;\n if (!reactorClient) {\n throw new Error(\"ReactorClient not initialized\");\n }\n\n const sync =\n window.ph?.reactorClientModule?.reactorModule?.syncModule?.syncManager;\n if (!sync) {\n throw new Error(\"Sync not initialized\");\n }\n\n // Fetch drive info from the REST endpoint to get both id and graphqlEndpoint\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to resolve drive info from ${url}`);\n }\n const driveInfo = (await response.json()) as {\n id: string;\n graphqlEndpoint: string;\n };\n\n const resolvedDriveId = driveId ?? driveInfo.id;\n const collectionId = DriveCollectionId.forDrive(resolvedDriveId);\n\n const inFlight = inFlightRemoteRegistrations.get(collectionId.key);\n try {\n if (inFlight) {\n await inFlight;\n } else {\n const existingRemote = sync\n .list()\n .find((remote) => remote.meta.collectionId.equals(collectionId));\n\n if (!existingRemote) {\n const remoteName = crypto.randomUUID();\n const registration = sync\n .add(\n remoteName,\n collectionId,\n {\n type: \"gql\",\n parameters: {\n url: driveInfo.graphqlEndpoint,\n },\n },\n undefined,\n options?.pollBehavior\n ? { pollBehavior: options.pollBehavior }\n : undefined,\n )\n .finally(() => inFlightRemoteRegistrations.delete(collectionId.key));\n inFlightRemoteRegistrations.set(collectionId.key, registration);\n await registration;\n }\n }\n } catch (error) {\n // Any drive add that fails because the caller isn't authorized (the\n // switchboard rejected it — Forbidden/Unauthorized) prompts a login,\n // regardless of which flow triggered the add. Re-throw so callers still\n // see the failure.\n if (isDriveAuthError(error)) {\n showPHModal({ type: \"driveAuthRequired\" });\n }\n throw error;\n }\n\n if (options?.awaitInitialSync) {\n await waitForDocumentReady(reactorClient, resolvedDriveId, {\n timeoutMs: options.initialSyncTimeoutMs,\n signal: options.signal,\n });\n }\n\n return resolvedDriveId;\n}\n\nexport async function deleteDrive(driveId: string) {\n const { isAllowedToCreateDocuments } = getUserPermissions();\n if (!isAllowedToCreateDocuments) {\n throw new Error(\"User is not allowed to delete drives\");\n }\n\n const reactorClient = window.ph?.reactorClient;\n if (!reactorClient) {\n throw new Error(\"ReactorClient not initialized\");\n }\n\n const sync =\n window.ph?.reactorClientModule?.reactorModule?.syncModule?.syncManager;\n if (sync) {\n const collectionId = DriveCollectionId.forDrive(driveId);\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 await reactorClient.deleteDocument(driveId, PropagationMode.Cascade);\n}\n\nexport async function renameDrive(\n driveId: string,\n name: string,\n): Promise<PHDocument | undefined> {\n const { isAllowedToCreateDocuments } = getUserPermissions();\n if (!isAllowedToCreateDocuments) {\n throw new Error(\"User is not allowed to rename drives\");\n }\n\n // drive renaming is only available on the full reactor client\n const reactorClient = window.ph?.reactorClientModule?.client;\n if (!reactorClient) {\n throw new Error(\"ReactorClient not initialized\");\n }\n return await reactorClient.rename(driveId, name);\n}\n\nexport async function setDriveAvailableOffline(\n driveId: string,\n availableOffline: boolean,\n): Promise<PHDocument | undefined> {\n const { isAllowedToCreateDocuments } = getUserPermissions();\n if (!isAllowedToCreateDocuments) {\n throw new Error(\"User is not allowed to change drive availability\");\n }\n\n const reactorClient = window.ph?.reactorClient;\n if (!reactorClient) {\n throw new Error(\"ReactorClient not initialized\");\n }\n return await reactorClient.execute(driveId, \"main\", [\n setAvailableOffline({ availableOffline }),\n ]);\n}\n\nexport async function setDriveSharingType(\n driveId: string,\n sharingType: SharingType,\n): Promise<PHDocument | undefined> {\n const { isAllowedToCreateDocuments } = getUserPermissions();\n if (!isAllowedToCreateDocuments) {\n throw new Error(\"User is not allowed to change drive sharing type\");\n }\n\n const reactorClient = window.ph?.reactorClient;\n if (!reactorClient) {\n throw new Error(\"ReactorClient not initialized\");\n }\n return await reactorClient.execute(driveId, \"main\", [\n setSharingType({ type: sharingType }),\n ]);\n}\n\nexport async function setDriveMetadata(\n driveId: string,\n metadata: { name?: string | null; icon?: string | null },\n): Promise<PHDocument | undefined> {\n const { isAllowedToCreateDocuments } = getUserPermissions();\n if (!isAllowedToCreateDocuments) {\n throw new Error(\"User is not allowed to update drive metadata\");\n }\n\n const reactorClient = window.ph?.reactorClient;\n if (!reactorClient) {\n throw new Error(\"ReactorClient not initialized\");\n }\n\n const actions: Array<\n | ReturnType<typeof createSetDriveNameAction>\n | ReturnType<typeof createSetDriveIconAction>\n > = [];\n if (metadata.name) {\n actions.push(createSetDriveNameAction({ name: metadata.name }));\n }\n if (metadata.icon !== undefined && metadata.icon !== null) {\n actions.push(createSetDriveIconAction({ icon: metadata.icon }));\n }\n if (actions.length === 0) {\n return undefined;\n }\n\n return await reactorClient.execute(driveId, \"main\", actions);\n}\n","export const DEFAULT_DRIVE_EDITOR_ID = \"powerhouse/generic-drive-explorer\";\nexport const COMMON_PACKAGE_ID = \"powerhouse/common\";\n\n/** Document types that represent a \"drive\": a container of nodes. */\nexport const DRIVE_DOCUMENT_TYPES = [\n \"powerhouse/document-drive\",\n \"powerhouse/reactor-drive\",\n] as const;\n","import type {\n DocumentOperations,\n PHBaseState,\n PHDocument,\n PHDocumentHeader,\n} from \"document-model\";\nimport { map, pipe } from \"remeda\";\nimport { z } from \"zod\";\nimport type {\n FindDocumentsQuery,\n GetDocumentWithOperationsQuery,\n} from \"./gen/schema.js\";\nimport type { TStateSchemaZodObject } from \"./types.js\";\n\ntype QueryDocumentResult = NonNullable<\n GetDocumentWithOperationsQuery[\"document\"]\n>[\"document\"];\n\ntype FindDocumentsItems = NonNullable<\n FindDocumentsQuery[\"findDocuments\"]\n>[\"items\"];\n\nexport function phDocumentFromQuery<\n TDocumentSchema extends TStateSchemaZodObject,\n>(document: QueryDocumentResult, documentSchema?: TDocumentSchema) {\n const phDocument = {\n header: phDocumentHeaderFromQuery(document),\n state: phDocumentStateFromQuery(document),\n initialState: phDocumentStateFromQuery(document),\n operations:\n phDocumentOperationsFromGetDocumentWithOperationsQuery(document),\n clipboard: [],\n };\n if (documentSchema !== undefined) documentSchema.parse(phDocument);\n return phDocument as PHDocument;\n}\n\nexport function phDocumentsFromQuery<\n TDocumentSchema extends TStateSchemaZodObject,\n>(items: FindDocumentsItems, documentSchema?: TDocumentSchema) {\n const documents = pipe(\n items,\n map((document) => phDocumentFromQuery(document, documentSchema)),\n );\n return documents;\n}\n\nfunction phDocumentHeaderFromQuery(queryDocument: QueryDocumentResult) {\n const phDocumentHeader = {\n branch: \"main\",\n id: queryDocument.id,\n name: queryDocument.name,\n documentType: queryDocument.documentType,\n createdAtUtcIso:\n queryDocument.createdAtUtcIso instanceof Date\n ? queryDocument.createdAtUtcIso.toUTCString()\n : queryDocument.createdAtUtcIso,\n lastModifiedAtUtcIso:\n queryDocument.lastModifiedAtUtcIso instanceof Date\n ? queryDocument.lastModifiedAtUtcIso.toUTCString()\n : queryDocument.lastModifiedAtUtcIso,\n slug: queryDocument.slug ?? \"\",\n };\n return phDocumentHeader as PHDocumentHeader;\n}\n\nfunction phDocumentStateFromQuery<\n TDocumentSchema extends TStateSchemaZodObject,\n>(queryDocument: QueryDocumentResult, documentSchema?: TDocumentSchema) {\n if (documentSchema !== undefined)\n return documentSchema.shape.state.parse(queryDocument.state);\n return queryDocument.state as PHBaseState;\n}\n\nfunction phDocumentOperationsFromGetDocumentWithOperationsQuery(\n queryDocument: QueryDocumentResult,\n) {\n if (\n queryDocument.operations === null ||\n queryDocument.operations === undefined\n )\n return {\n global: [],\n };\n\n const documentOperations = {\n global: [...queryDocument.operations.items],\n };\n return documentOperations as DocumentOperations;\n}\nexport function identifierFromMutateDocumentOperationVariables(\n variables: unknown,\n) {\n return z\n .object({\n documentIdentifier: z.string(),\n })\n .parse(variables).documentIdentifier;\n}\n","export const DEFAULT_DRIVE_ID = \"powerhouse\" as const;\nexport const DEFAULT_SWITCHBOARD_URL = \"http://localhost:4001/graphql\" as const;\nexport const graphqlEventsToSyncDrive = [\n \"CreateEmptyDocument\",\n \"CreateDocument\",\n \"AddChildren\",\n \"RemoveChildren\",\n \"MoveChildren\",\n \"DeleteDocument\",\n \"DeleteDocuments\",\n] as const;\n\nexport const graphqlDocumentEvents = [\n \"MutateDocument\",\n \"MutateDocumentAsync\",\n \"DeleteDocument\",\n] as const;\n\nexport const graphqlDocumentsEvents = [\"DeleteDocuments\"] as const;\n","import { map } from \"remeda\";\nimport { phDocumentFromQuery } from \"./adapters.js\";\nimport type { TStateSchemaZodObject } from \"./types.js\";\n\nexport async function reactorGraphqlFetchDocument<\n TDocumentSchema extends TStateSchemaZodObject,\n>(identifier: string, documentSchema?: TDocumentSchema) {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n try {\n const result = await client.GetDocument({\n identifier,\n });\n const document = result.document?.document;\n if (!document) return undefined;\n return phDocumentFromQuery(document, documentSchema);\n } catch {\n return undefined;\n }\n}\n\nexport async function reactorGraphqlBatchFetchDocuments(\n identifiers: readonly string[],\n) {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n const promises = map(identifiers, (identifier) =>\n reactorGraphqlFetchDocument(identifier),\n );\n return await Promise.all(promises);\n}\n","import { funnel } from \"remeda\";\n\ntype PromiseCallbacks<Result> = Readonly<\n Parameters<ConstructorParameters<typeof Promise<Result>>[0]>\n>;\n\ntype BatchRequest<Params extends unknown[], Result> = {\n readonly params: Params;\n readonly promiseCallbacks: PromiseCallbacks<Result>;\n};\n\nexport type Batch<Params extends unknown[], Result> = {\n call: (...params: Params) => Promise<Result>;\n cancel: () => void;\n flush: () => void;\n readonly isIdle: boolean;\n};\n\n/**\n * A reference implementation for an async batching utility function built on\n * top of the `funnel` general purpose execution utility function. It will\n * accumulate all params passed to an async `call` method within the defined\n * burst duration and then use an async executor to process them in one\n * invocation. It then extracts an individual result for each call which is\n * used to resolve the original call.\n *\n * This allows synchronizing multiple async calls while keeping each call site\n * isolated from the rest (for example, as react components).\n *\n * This reference implementation can be copied into your project as-is, or you\n * can use it as the basis for a more complex implementation with additional\n * features.\n *\n * @param callback - The main function that takes a batch and returns an\n * aggregated response. The typing for the it's parameters will derive the\n * typing for the extractor and the `call` method.\n * @param extractor - A function that takes the aggregated response and extracts\n * from it the result for each individual call. The function is called with both\n * the index in the batch, and the params passed to the `call` method. This\n * allows handling APIs that return batch results as both objects and arrays.\n * @param maxBurstDurationMs - The period of time where the batcher would\n * collect params before triggering the executor. When set to 0 the batcher\n * does not incur any additional delays to the execution and would trigger at\n * the next tick, just like a regular async function would. This is also the\n * default value.\n * @returns A Funnel object with the `call` method augmented to support async\n * response.\n */\nexport function batch<Params extends unknown[], BatchResponse, Result>(\n callback: (requests: readonly Params[]) => Promise<BatchResponse>,\n extractor: (\n response: BatchResponse,\n index: number,\n ...params: Params\n ) => Result,\n maxBurstDurationMs = 0,\n): Batch<Params, Result> {\n const batchFunnel = funnel(\n (requests: readonly BatchRequest<Params, Result>[]) => {\n callback(requests.map(({ params }) => params))\n .then((response) => {\n for (const [\n index,\n {\n params,\n promiseCallbacks: [resolve, reject],\n },\n ] of requests.entries()) {\n try {\n const result = extractor(response, index, ...params);\n resolve(result);\n } catch (error) {\n reject(error);\n }\n }\n })\n .catch((error) => {\n for (const {\n promiseCallbacks: [, reject],\n } of requests) {\n reject(error);\n }\n });\n },\n {\n reducer: (\n requests: readonly BatchRequest<Params, Result>[] | undefined,\n request: BatchRequest<Params, Result>,\n ) => [...(requests ?? []), request],\n maxBurstDurationMs,\n triggerAt: \"end\",\n },\n );\n\n return {\n ...batchFunnel,\n\n call: (...params: Params) =>\n new Promise<Result>((...promiseCallbacks) => {\n batchFunnel.call({ promiseCallbacks, params });\n }),\n };\n}\n","import type { PHDocument } from \"document-model\";\nimport { filter, isTruthy, map, mapToObj, pipe, prop, unique } from \"remeda\";\nimport { type Batch, batch } from \"./batch.js\";\nimport { reactorGraphqlBatchFetchDocuments } from \"./fetchers.js\";\n\nfunction makeDocumentsById(documents: (PHDocument | undefined)[] = []) {\n return pipe(\n documents,\n filter(isTruthy),\n mapToObj((document) => [document.header.id, document]),\n );\n}\n\nexport class DocumentFetcher {\n private batchGetDocuments: Batch<[id: string], PHDocument>;\n\n constructor() {\n this.batchGetDocuments = batch(\n async (requests: readonly [id: string][]) => {\n const ids = unique(map(requests, ([id]) => id));\n const documents = await reactorGraphqlBatchFetchDocuments(ids);\n\n return makeDocumentsById(documents);\n },\n (documentsById, _, id) => {\n const document = prop(documentsById, id);\n return document;\n },\n );\n }\n\n get(id: string): Promise<PHDocument> {\n return this.batchGetDocuments.call(id);\n }\n\n getBatch(ids: string[]): Promise<PHDocument[]> {\n return Promise.all(map(ids, (id) => this.get(id)));\n }\n}\n","import type { PHDocument } from \"document-model\";\nimport { forEach } from \"remeda\";\nimport { addPromiseState, readPromiseState } from \"../document-cache.js\";\nimport type {\n FulfilledPromise,\n IDocumentCache,\n PromiseWithState,\n} from \"../types/documents.js\";\nimport { DocumentFetcher } from \"./document-fetcher.js\";\n\nexport class GraphQLClientDocumentCache implements IDocumentCache {\n private fetcher: DocumentFetcher;\n\n private documents = new Map<string, PromiseWithState<PHDocument>>();\n\n private batchPromises = new Map<\n string,\n {\n promises: readonly Promise<PHDocument>[];\n promise: PromiseWithState<PHDocument[]>;\n }\n >();\n\n private listeners = new Map<string, (() => void)[]>();\n\n constructor() {\n this.fetcher = new DocumentFetcher();\n\n window.addEventListener(\"MutateDocument\", (event) => {\n this.handleDocumentMutated(event.detail.identifier).catch(console.error);\n });\n\n window.addEventListener(\"MutateDocumentAsync\", (event) => {\n this.handleDocumentMutated(event.detail.identifier).catch(console.error);\n });\n }\n\n get(id: string, refetch?: boolean): Promise<PHDocument> {\n const current = this.documents.get(id);\n\n if (current) {\n if (current.status === \"pending\") {\n return current;\n }\n\n if (!refetch) {\n return current;\n }\n }\n\n const promise = addPromiseState(\n this.fetcher.get(id).then((document) => {\n this.invalidateBatchesContaining(id);\n return document;\n }),\n );\n\n this.documents.set(id, promise);\n\n return promise;\n }\n\n getBatch(ids: string[]): Promise<PHDocument[]> {\n const key = ids.join(\",\");\n const cached = this.batchPromises.get(key);\n\n const currentPromises = ids.map((id) => this.get(id));\n\n if (cached) {\n const samePromises = currentPromises.every(\n (promise, index) => promise === cached.promises[index],\n );\n\n if (samePromises) {\n return cached.promise;\n }\n }\n\n const states = currentPromises.map((promise) =>\n readPromiseState(promise as PromiseWithState<PHDocument>),\n );\n\n const allSettled = states.every((state) => state.status !== \"pending\");\n\n if (allSettled) {\n const values = states\n .filter(\n (state): state is { status: \"fulfilled\"; value: PHDocument } =>\n state.status === \"fulfilled\",\n )\n .map((state) => state.value);\n\n const batchPromise = Promise.resolve(values) as PromiseWithState<\n PHDocument[]\n >;\n\n batchPromise.status = \"fulfilled\";\n (batchPromise as FulfilledPromise<PHDocument[]>).value = values;\n\n this.batchPromises.set(key, {\n promises: currentPromises,\n promise: batchPromise,\n });\n\n return batchPromise;\n }\n\n const batchPromise = addPromiseState(\n Promise.allSettled(currentPromises).then((results) => {\n const documents: PHDocument[] = [];\n for (const result of results) {\n if (result.status === \"fulfilled\") {\n documents.push(result.value);\n } else {\n console.warn(\n \"[GraphQLClientDocumentCache] Skipped unavailable document:\",\n result.reason,\n );\n }\n }\n return documents;\n }),\n );\n\n this.batchPromises.set(key, {\n promises: currentPromises,\n promise: batchPromise,\n });\n\n return batchPromise;\n }\n\n private invalidateBatchesContaining(documentId: string): void {\n for (const key of this.batchPromises.keys()) {\n if (key.split(\",\").includes(documentId)) {\n this.batchPromises.delete(key);\n }\n }\n }\n\n subscribe(id: string | string[], callback: () => void): () => void {\n const ids = Array.isArray(id) ? id : [id];\n\n for (const documentId of ids) {\n const listeners = this.listeners.get(documentId) ?? [];\n this.listeners.set(documentId, [...listeners, callback]);\n }\n\n return () => {\n for (const documentId of ids) {\n const listeners = this.listeners.get(documentId) ?? [];\n this.listeners.set(\n documentId,\n listeners.filter((listener) => listener !== callback),\n );\n }\n };\n }\n\n private notify(id: string): void {\n const listeners = this.listeners.get(id) ?? [];\n\n for (const listener of listeners) {\n listener();\n }\n }\n\n private async handleDocumentMutated(id: string) {\n this.invalidateBatchesContaining(id);\n await this.get(id);\n this.notify(id);\n }\n\n private handleDocumentDeleted(id: string) {\n this.documents.delete(id);\n this.invalidateBatchesContaining(id);\n this.notify(id);\n }\n\n private handleDocumentsDeleted(ids: string[]) {\n forEach(ids, (id) => this.handleDocumentDeleted(id));\n }\n}\n","import type { PHDocument } from \"document-model\";\nimport { DEFAULT_DRIVE_ID } from \"./constants.js\";\nimport type { Scalars } from \"./gen/schema.js\";\n\nexport async function reactorGraphqlCreateDocument<\n TDocument extends PHDocument,\n>(document: TDocument, parentIdentifier = DEFAULT_DRIVE_ID) {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n\n const result = await client.CreateDocument({\n document,\n parentIdentifier,\n });\n\n return result;\n}\n\nexport async function reactorGraphqlDeleteDocument(identifier: string) {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n\n const result = await client.DeleteDocument({\n identifier,\n });\n\n return result;\n}\n\nexport async function reactorGraphqlDeleteDocuments(identifiers: string[]) {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n\n const result = await client.DeleteDocuments({\n identifiers,\n });\n\n return result;\n}\n\nexport async function reactorGraphqlMutateDocument(\n documentIdentifier: string,\n ...actions: ReadonlyArray<Scalars[\"JSONObject\"][\"input\"]>\n) {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n\n const result = await client.MutateDocument({\n documentIdentifier,\n actions,\n });\n\n return result;\n}\n","import { useAllowedDocumentTypes } from \"./config/editor.js\";\nimport { useDocumentModelModules } from \"./document-model-modules.js\";\n\nexport function useAllowedDocumentModelModules() {\n const documentModelModules = useDocumentModelModules();\n const allowedDocumentTypes = useAllowedDocumentTypes();\n if (!allowedDocumentTypes?.length) return documentModelModules;\n return documentModelModules?.filter((module) =>\n allowedDocumentTypes.includes(module.documentModel.global.id),\n );\n}\n","import type { FolderNode } from \"@powerhousedao/shared/document-drive\";\nimport { isFolderNodeKind } from \"../utils/nodes.js\";\nimport { useSelectedNode } from \"./selected-node.js\";\n\n/** Returns the selected folder. */\nexport function useSelectedFolder(): FolderNode | undefined {\n const selectedNode = useSelectedNode();\n if (isFolderNodeKind(selectedNode)) return selectedNode;\n return undefined;\n}\n","import type { Node } from \"@powerhousedao/shared/document-drive\";\nimport { sortNodesByName } from \"../utils/nodes.js\";\nimport { useNodesInSelectedDrive } from \"./items-in-selected-drive.js\";\nimport { useSelectedFolder } from \"./selected-folder.js\";\n\n/** Returns the child nodes for the selected drive or folder. */\nexport function useNodesInSelectedDriveOrFolder(): Node[] {\n const nodes = useNodesInSelectedDrive();\n const selectedFolder = useSelectedFolder();\n const selectedFolderId = selectedFolder?.id;\n if (!nodes) return [];\n if (!selectedFolderId)\n return sortNodesByName(nodes.filter((n) => !n.parentFolder));\n return sortNodesByName(\n nodes.filter((n) => n.parentFolder === selectedFolderId),\n );\n}\n","import type {\n PHAppConfig,\n PHAppConfigKey,\n PHDocumentEditorConfig,\n PHDocumentEditorConfigKey,\n PHGlobalConfig,\n PHGlobalConfigKey,\n} from \"@powerhousedao/reactor-browser\";\nimport { phGlobalConfigSetters } from \"./connect.js\";\nimport { phAppConfigSetters, phDocumentEditorConfigSetters } from \"./editor.js\";\n\nexport function setPHGlobalConfigByKey<TKey extends PHGlobalConfigKey>(\n key: TKey,\n value: PHGlobalConfig[TKey] | undefined,\n) {\n const setter = phGlobalConfigSetters[key];\n setter(value);\n}\n\nexport function setPHAppConfigByKey<TKey extends PHAppConfigKey>(\n key: TKey,\n value: PHAppConfig[TKey] | undefined,\n) {\n const setter = phAppConfigSetters[key];\n setter(value);\n}\n\nexport function setPHDocumentEditorConfigByKey<\n TKey extends PHDocumentEditorConfigKey,\n>(key: TKey, value: PHDocumentEditorConfig[TKey] | undefined) {\n const setter = phDocumentEditorConfigSetters[key];\n setter(value);\n}\n","import type {\n PHGlobalConfig,\n PHGlobalConfigKey,\n PHGlobalConfigSetters,\n} from \"@powerhousedao/reactor-browser\";\nimport { phGlobalConfigSetters } from \"./connect.js\";\n\nexport function callGlobalSetterForKey<TKey extends PHGlobalConfigKey>(\n key: TKey,\n value: PHGlobalConfig[TKey] | undefined,\n) {\n const setter = phGlobalConfigSetters[key] as PHGlobalConfigSetters[TKey];\n setter(value);\n}\n","import type {\n PHAppConfig,\n PHAppConfigKey,\n PHDocumentEditorConfig,\n PHDocumentEditorConfigKey,\n PHGlobalConfig,\n PHGlobalConfigKey,\n} from \"@powerhousedao/reactor-browser\";\nimport { useEffect, useState } from \"react\";\nimport { callGlobalSetterForKey } from \"./utils.js\";\n\nexport function setDefaultPHGlobalConfig(config: PHGlobalConfig) {\n for (const key of Object.keys(config) as PHGlobalConfigKey[]) {\n callGlobalSetterForKey(key, config[key]);\n }\n}\n\nexport function useSetDefaultPHGlobalConfig(config: PHGlobalConfig) {\n const [isInitialized, setIsInitialized] = useState(false);\n\n useEffect(() => {\n if (isInitialized) return;\n setDefaultPHGlobalConfig(config);\n setIsInitialized(true);\n }, [config, isInitialized]);\n}\n\nexport function useResetPHGlobalConfig(defaultConfigForReset: PHGlobalConfig) {\n return function reset() {\n setPHGlobalConfig(defaultConfigForReset);\n };\n}\n\nexport function setPHGlobalConfig(config: Partial<PHGlobalConfig>) {\n for (const key of Object.keys(config) as PHGlobalConfigKey[]) {\n callGlobalSetterForKey(key, config[key]);\n }\n}\n\nexport function useSetPHGlobalConfig(config: Partial<PHGlobalConfig>) {\n const [isInitialized, setIsInitialized] = useState(false);\n\n useEffect(() => {\n if (isInitialized) return;\n setPHGlobalConfig(config);\n setIsInitialized(true);\n }, [config, isInitialized]);\n}\n\n/** Sets the global drive config.\n *\n * Pass in a partial object of the global drive config to set.\n */\nexport function setPHAppConfig(config: Partial<PHAppConfig>) {\n for (const key of Object.keys(config) as PHAppConfigKey[]) {\n callGlobalSetterForKey(key, config[key]);\n }\n}\n\n/** Sets the global document config.\n *\n * Pass in a partial object of the global document config to set.\n */\nexport function setPHDocumentEditorConfig(\n config: Partial<PHDocumentEditorConfig>,\n) {\n for (const key of Object.keys(config) as PHDocumentEditorConfigKey[]) {\n callGlobalSetterForKey(key, config[key]);\n }\n}\n\n/** Wrapper hook for setting the global app config.\n *\n * Automatically sets the global app config when the component mounts.\n *\n * Pass in a partial object of the global app config to set.\n */\nexport function useSetPHAppConfig(config: Partial<PHAppConfig>) {\n const [isInitialized, setIsInitialized] = useState(false);\n\n useEffect(() => {\n if (isInitialized) return;\n setPHAppConfig(config);\n setIsInitialized(true);\n }, [config, isInitialized]);\n}\n\n/** Wrapper hook for setting the global document editor config.\n *\n * Automatically sets the global document editor config when the component mounts.\n *\n * Pass in a partial object of the global document editor config to set.\n */\nexport function useSetPHDocumentEditorConfig(\n config: Partial<PHDocumentEditorConfig>,\n) {\n const [isInitialized, setIsInitialized] = useState(false);\n\n useEffect(() => {\n if (isInitialized) return;\n setPHDocumentEditorConfig(config);\n setIsInitialized(true);\n }, [config, isInitialized]);\n}\n","import type {\n PHAppConfigKey,\n PHDocumentEditorConfigKey,\n PHGlobalConfigKey,\n} from \"@powerhousedao/reactor-browser\";\nimport { phGlobalConfigHooks } from \"./connect.js\";\nimport { phAppConfigHooks, phDocumentEditorConfigHooks } from \"./editor.js\";\n\nexport function usePHGlobalConfigByKey<TKey extends PHGlobalConfigKey>(\n key: TKey,\n) {\n const useValueHook = phGlobalConfigHooks[key];\n return useValueHook();\n}\n\n/** Gets the value of an item in the global drive config for a given key.\n *\n * Strongly typed, inferred from type definition for the key.\n */\nexport function usePHAppConfigByKey<TKey extends PHAppConfigKey>(key: TKey) {\n const useValueHook = phAppConfigHooks[key];\n return useValueHook();\n}\n\n/** Gets the value of an item in the global document config for a given key.\n *\n * Strongly typed, inferred from type definition for the key.\n */\nexport function usePHDocumentEditorConfigByKey<\n TKey extends PHDocumentEditorConfigKey,\n>(key: TKey) {\n const useValueHook = phDocumentEditorConfigHooks[key];\n return useValueHook();\n}\n","import type { ConnectionStateSnapshot } from \"@powerhousedao/reactor\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { useSync } from \"./reactor.js\";\n\n/**\n * Returns a map of remote name to connection state snapshot for all remotes.\n * Re-renders when any remote's connection state changes.\n */\nexport function useConnectionStates(): ReadonlyMap<\n string,\n ConnectionStateSnapshot\n> {\n const syncManager = useSync();\n const [states, setStates] = useState<\n ReadonlyMap<string, ConnectionStateSnapshot>\n >(() => buildSnapshot(syncManager));\n const unsubscribesRef = useRef<Array<() => void>>([]);\n\n useEffect(() => {\n if (!syncManager) return;\n\n function subscribe() {\n // Clean up previous subscriptions\n for (const unsub of unsubscribesRef.current) {\n unsub();\n }\n unsubscribesRef.current = [];\n\n const remotes = syncManager!.list();\n for (const remote of remotes) {\n const unsub = remote.channel.onConnectionStateChange(() => {\n setStates(buildSnapshot(syncManager));\n });\n unsubscribesRef.current.push(unsub);\n }\n\n // Set initial state\n setStates(buildSnapshot(syncManager));\n }\n\n subscribe();\n\n // Re-subscribe periodically to pick up added/removed remotes\n const interval = setInterval(subscribe, 5000);\n\n return () => {\n clearInterval(interval);\n for (const unsub of unsubscribesRef.current) {\n unsub();\n }\n unsubscribesRef.current = [];\n };\n }, [syncManager]);\n\n return states;\n}\n\n/**\n * Returns the connection state snapshot for a specific remote by name.\n */\nexport function useConnectionState(\n remoteName: string,\n): ConnectionStateSnapshot | undefined {\n const states = useConnectionStates();\n return states.get(remoteName);\n}\n\nfunction buildSnapshot(\n syncManager: ReturnType<typeof useSync>,\n): ReadonlyMap<string, ConnectionStateSnapshot> {\n const map = new Map<string, ConnectionStateSnapshot>();\n if (!syncManager) return map;\n for (const remote of syncManager.list()) {\n map.set(remote.meta.name, remote.channel.getConnectionState());\n }\n return map;\n}\n","import { ModuleNotFoundError } from \"@powerhousedao/reactor\";\nimport type { DocumentDispatch } from \"@powerhousedao/reactor-browser\";\nimport type { Action, PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { DocumentTypeMismatchError } from \"../errors.js\";\nimport { useDocumentById } from \"./document-by-id.js\";\nimport { useDocumentModelModuleById } from \"./document-model-modules.js\";\n\n/** Returns a document of a specific type, throws an error if the found document has a different type */\nexport function useDocumentOfType<\n TDocument extends PHDocument,\n TAction extends Action,\n>(\n documentId: string | null | undefined,\n documentType: string | null | undefined,\n) {\n const [document, dispatch] = useDocumentById(documentId);\n const documentModelModule = useDocumentModelModuleById(documentType);\n\n if (!documentId || !documentType) return [];\n\n if (!document) {\n throw new Error(`Document not found: ${documentId}`);\n }\n if (!documentModelModule) {\n throw new ModuleNotFoundError(documentType);\n }\n\n if (document.header.documentType !== documentType) {\n throw new DocumentTypeMismatchError(\n documentId,\n documentType,\n document.header.documentType,\n );\n }\n\n return [document, dispatch] as [TDocument, DocumentDispatch<TAction>];\n}\n","import { useDocumentModelModules } from \"./document-model-modules.js\";\n\n/** Returns the supported document types for the reactor (derived from the document model modules) */\nexport function useSupportedDocumentTypesInReactor() {\n const documentModelModules = useDocumentModelModules();\n return documentModelModules?.map((module) => module.documentModel.global.id);\n}\n","import { useAllowedDocumentTypes } from \"./config/editor.js\";\nimport { useSupportedDocumentTypesInReactor } from \"./supported-document-types.js\";\n\n/** Returns the document types a app supports.\n *\n * If present, uses the `allowedDocumentTypes` config value.\n * Otherwise, uses the supported document types from the reactor.\n */\nexport function useDocumentTypes() {\n const allowedDocumentTypes = useAllowedDocumentTypes();\n const supportedDocumentTypes = useSupportedDocumentTypesInReactor();\n return allowedDocumentTypes ?? supportedDocumentTypes;\n}\n","import type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { useDocumentModelModules } from \"./document-model-modules.js\";\nimport { useModelRegistry } from \"./reactor.js\";\n\nexport type DocumentVersionStatus =\n | { kind: \"current\"; documentVersion: number }\n | {\n kind: \"upgrade-available\";\n documentVersion: number;\n latestVersion: number;\n canUpgrade: boolean;\n }\n | {\n kind: \"unsupported\";\n documentVersion: number;\n availableVersions: number[];\n };\n\n/**\n * Classifies a document's model version against the installed module\n * versions. Pure logic, extracted for testing.\n */\nexport function getDocumentVersionStatus(\n documentVersion: number,\n availableVersions: number[],\n hasUpgradePath: (fromVersion: number, toVersion: number) => boolean,\n): DocumentVersionStatus | undefined {\n if (availableVersions.length === 0) {\n return undefined;\n }\n const sorted = [...availableVersions].sort((a, b) => a - b);\n const latestVersion = sorted[sorted.length - 1];\n if (documentVersion > latestVersion) {\n return { kind: \"unsupported\", documentVersion, availableVersions: sorted };\n }\n if (documentVersion === latestVersion) {\n return { kind: \"current\", documentVersion };\n }\n return {\n kind: \"upgrade-available\",\n documentVersion,\n latestVersion,\n canUpgrade: hasUpgradePath(documentVersion, latestVersion),\n };\n}\n\n/**\n * Compares the given document's model version against the versions available\n * from installed Vetra packages. Returns undefined while packages load or\n * when the document type has no installed modules.\n */\nexport function useDocumentVersionStatus(\n document: PHDocument | undefined,\n): DocumentVersionStatus | undefined {\n const modules = useDocumentModelModules();\n const registry = useModelRegistry();\n if (!document || !modules) {\n return undefined;\n }\n const documentType = document.header.documentType;\n const documentVersion = document.state.document.version || 1;\n const availableVersions = modules\n .filter((m) => m.documentModel.global.id === documentType)\n .map((m) => m.version ?? 1);\n\n return getDocumentVersionStatus(\n documentVersion,\n availableVersions,\n (fromVersion, toVersion) => {\n if (!registry) {\n return false;\n }\n try {\n registry.computeUpgradePath(documentType, fromVersion, toVersion);\n return true;\n } catch {\n return false;\n }\n },\n );\n}\n","import type {\n DocumentModelDocument,\n PHDocument,\n ValidationError,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n validateInitialState,\n validateModules,\n validateStateSchemaName,\n} from \"@powerhousedao/shared/document-model\";\n\nexport const validateDocument = (document: PHDocument) => {\n const errors: ValidationError[] = [];\n\n if (document.header.documentType !== \"powerhouse/document-model\") {\n return errors;\n }\n\n const doc = document as DocumentModelDocument;\n const specs = doc.state.global.specifications[0];\n\n // validate initial state errors\n const initialStateErrors = Object.keys(specs.state).reduce<ValidationError[]>(\n (acc, scopeKey) => {\n const scope = scopeKey as keyof typeof specs.state;\n\n return [\n ...acc,\n ...validateInitialState(\n specs.state[scope].initialValue,\n scope !== \"global\",\n ).map((err) => ({\n ...err,\n message: `${err.message}. Scope: ${scope}`,\n details: { ...err.details, scope },\n })),\n ];\n },\n [],\n );\n\n // validate schema state errors\n const schemaStateErrors = Object.keys(specs.state).reduce<ValidationError[]>(\n (acc, scopeKey) => {\n const scope = scopeKey as keyof typeof specs.state;\n const isGlobalScope = scope === \"global\";\n\n return [\n ...acc,\n ...validateStateSchemaName(\n specs.state[scope].schema,\n doc.state.global?.name || doc.header.name || \"\",\n !isGlobalScope ? scope : \"\",\n !isGlobalScope,\n ).map((err) => ({\n ...err,\n message: `${err.message}. Scope: ${scope}`,\n details: { ...err.details, scope },\n })),\n ];\n },\n [],\n );\n\n // modules validation\n const modulesErrors = validateModules(specs.modules);\n\n return [...initialStateErrors, ...schemaStateErrors, ...modulesErrors];\n};\n","import type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport normalizeException from \"normalize-exception\";\nimport { hasAtLeast } from \"remeda\";\nimport { exportFile } from \"../actions/document.js\";\nimport { showPHModal } from \"../hooks/modals.js\";\nimport { validateDocument } from \"./validate-document.js\";\n\nfunction defaultHandleError(error: Error) {\n console.error(`Failed to export document: ${error.message}`);\n}\n\nfunction handleDocumentValidation(document: PHDocument) {\n if (hasAtLeast(validateDocument(document), 1)) return false;\n return true;\n}\n\nexport function downloadDocument(\n document: PHDocument | undefined,\n handleError = defaultHandleError,\n) {\n if (!document) return;\n const isValid = handleDocumentValidation(document);\n\n if (!isValid) {\n showPHModal({\n type: \"downloadDocumentWithErrors\",\n documentId: document.header.id,\n });\n return;\n }\n exportFile(document).catch((error) => handleError(normalizeException(error)));\n}\n","import { downloadDocument } from \"../utils/download-document.js\";\nimport { useGetDocument } from \"./document-cache.js\";\nimport { usePHToast } from \"./toast.js\";\n\nexport function useDownloadDocument(id: string | undefined) {\n const getDocument = useGetDocument();\n const toast = usePHToast();\n\n return async () => {\n if (!id) return;\n const handleError = (error: Error) =>\n toast?.(`Failed to export document: ${error.message}`);\n try {\n const document = await getDocument(id);\n downloadDocument(document, handleError);\n } catch (error) {\n handleError(error as Error);\n }\n };\n}\n","import type {\n DocumentDriveAction,\n DocumentDriveDocument,\n} from \"@powerhousedao/shared/document-drive\";\nimport type { DocumentDispatch } from \"../types/documents.js\";\nimport { useDispatch } from \"./dispatch.js\";\nimport { useDrives } from \"./drives.js\";\n\nexport function useDriveById(\n driveId: string | undefined | null,\n): [DocumentDriveDocument, DocumentDispatch<DocumentDriveAction>] {\n const drives = useDrives();\n const foundDrive = drives?.find((drive) => drive.header.id === driveId);\n const [drive, dispatch] = useDispatch(foundDrive);\n if (!foundDrive) {\n throw new Error(`Drive with id ${driveId} not found`);\n }\n return [drive, dispatch] as [\n DocumentDriveDocument,\n DocumentDispatch<DocumentDriveAction>,\n ];\n}\n","import type { EditorModule } from \"document-model\";\nimport { DEFAULT_DRIVE_EDITOR_ID, DRIVE_DOCUMENT_TYPES } from \"../constants.js\";\nimport { useVetraPackages } from \"./vetra-packages.js\";\n\n/** An editor is a drive \"app\" if it targets any supported drive document type. */\nfunction isDriveEditor(module: EditorModule): boolean {\n const driveTypes = DRIVE_DOCUMENT_TYPES as readonly string[];\n return module.documentTypes.some((t) => driveTypes.includes(t));\n}\n\nexport function useEditorModules(): EditorModule[] | undefined {\n const vetraPackages = useVetraPackages();\n return vetraPackages\n .flatMap((pkg) => pkg.editors)\n .filter((module) => !isDriveEditor(module));\n}\n\nexport function useAppModules(): EditorModule[] | undefined {\n const vetraPackages = useVetraPackages();\n return vetraPackages.flatMap((pkg) => pkg.editors).filter(isDriveEditor);\n}\n\nexport function useFallbackEditorModule(\n documentType: string | null | undefined,\n): EditorModule | undefined {\n const editorModules = useEditorModules();\n if (!documentType) return undefined;\n if (editorModules?.length === 0) return undefined;\n\n const modulesForType = editorModules?.filter((module) =>\n module.documentTypes.includes(documentType),\n );\n return modulesForType?.[0];\n}\n\nexport function useAppModuleById(\n id: string | null | undefined,\n): EditorModule | undefined {\n const appModules = useAppModules();\n return appModules?.find((module) => module.config.id === id);\n}\n\nexport function useDefaultAppModule(): EditorModule | undefined {\n const defaultAppModule = useAppModuleById(DEFAULT_DRIVE_EDITOR_ID);\n return defaultAppModule;\n}\n\nexport function useEditorModuleById(\n id: string | null | undefined,\n): EditorModule | undefined {\n const editorModules = useEditorModules();\n return editorModules?.find((module) => module.config.id === id);\n}\n\nexport function useEditorModulesForDocumentType(\n documentType: string | null | undefined,\n) {\n const editorModules = useEditorModules();\n if (!documentType) return undefined;\n\n const modulesForType = editorModules?.filter((module) =>\n module.documentTypes.includes(documentType),\n );\n return modulesForType;\n}\n","import type { EditorModule } from \"document-model\";\n\n// React.lazy internals + an optional explicit preload hook.\ntype PreloadableComponent = EditorModule[\"Component\"] & {\n preload?: () => Promise<unknown>;\n _payload?: { _status: number };\n _init?: (payload: unknown) => unknown;\n};\n\n// Starts an editor's lazy chunk download without rendering it. Returns the\n// in-flight promise while uninitialized/pending, undefined once loaded.\nexport function preloadEditorModule(\n module: EditorModule,\n): Promise<unknown> | undefined {\n const Component = module.Component as PreloadableComponent;\n\n if (typeof Component.preload === \"function\") {\n return Component.preload();\n }\n\n const payload = Component._payload;\n const init = Component._init;\n if (!payload || typeof init !== \"function\") return undefined;\n\n // _init triggers the import: returns the module once resolved, throws the\n // pending promise while in flight (or the error if the load already failed).\n try {\n init(payload);\n } catch (thrown) {\n if (thrown && typeof (thrown as PromiseLike<unknown>).then === \"function\") {\n return thrown as Promise<unknown>;\n }\n }\n return undefined;\n}\n\ntype NetworkInformation = {\n saveData?: boolean;\n effectiveType?: string;\n};\n\n// Whether the connection is good enough for speculative preloading.\n// Unknown connection info is treated as \"ok\".\nexport function hasPreloadBandwidth(): boolean {\n if (typeof navigator === \"undefined\") return false;\n const connection = (\n navigator as Navigator & { connection?: NetworkInformation }\n ).connection;\n if (!connection) return true;\n if (connection.saveData) return false;\n return ![\"slow-2g\", \"2g\"].includes(connection.effectiveType ?? \"\");\n}\n","import { useEffect } from \"react\";\nimport {\n hasPreloadBandwidth,\n preloadEditorModule,\n} from \"../utils/preload-editor.js\";\nimport { useAppModules, useEditorModules } from \"./editor-modules.js\";\n\ntype IdleDeadline = { didTimeout: boolean; timeRemaining: () => number };\n\nfunction requestIdle(cb: (deadline: IdleDeadline) => void): number {\n if (typeof window.requestIdleCallback === \"function\") {\n return window.requestIdleCallback(cb);\n }\n // Fallback: hand out a short, draining time budget so pump processes a slice\n // and reschedules, rather than emptying the whole queue in one task.\n return window.setTimeout(() => {\n const start = Date.now();\n cb({\n didTimeout: false,\n timeRemaining: () => Math.max(0, 8 - (Date.now() - start)),\n });\n }, 200);\n}\n\nfunction cancelIdle(handle: number): void {\n if (typeof window.cancelIdleCallback === \"function\") {\n window.cancelIdleCallback(handle);\n } else {\n window.clearTimeout(handle);\n }\n}\n\n// Warms every registered editor's lazy chunk during browser idle time when\n// bandwidth allows, so opening a document doesn't wait on a network fetch.\nexport function useEditorPreloader(): void {\n const editorModules = useEditorModules();\n const appModules = useAppModules();\n\n useEffect(() => {\n const queue = [...(editorModules ?? []), ...(appModules ?? [])];\n if (queue.length === 0 || !hasPreloadBandwidth()) return;\n\n let cancelled = false;\n let handle = 0;\n\n const pump = (deadline: IdleDeadline) => {\n while (\n !cancelled &&\n queue.length > 0 &&\n (deadline.didTimeout || deadline.timeRemaining() > 0)\n ) {\n const editorModule = queue.shift()!;\n void preloadEditorModule(editorModule);\n }\n if (!cancelled && queue.length > 0) handle = requestIdle(pump);\n };\n\n handle = requestIdle(pump);\n\n return () => {\n cancelled = true;\n if (handle) cancelIdle(handle);\n };\n }, [editorModules, appModules]);\n}\n","import type { Node } from \"@powerhousedao/shared\";\nimport type { DragEventHandler } from \"react\";\nimport {\n allPass,\n filter,\n find,\n hasAtLeast,\n isArray,\n isDefined,\n isIncludedIn,\n isStrictEqual,\n isTruthy,\n last,\n map,\n once,\n pipe,\n split,\n} from \"remeda\";\nimport { useIsDragAndDropEnabled } from \"./config/editor.js\";\nimport { useSelectedFolder } from \"./selected-folder.js\";\nimport { useDropTarget } from \"./use-drop-target.js\";\n\n/* Supported file extensions, more can be added here */\nconst allowedExtensions = [\"zip\", \"phd\", \"phdm\"] as const;\n\nconst hasFilesType = (types: readonly string[]) =>\n isDefined(find(types, (type) => isStrictEqual(type, \"Files\")));\n\n/* A drop is a file drop when the data transfer types array has \"Files\" */\nconst isFileDrop = (event: React.DragEvent<Element>) =>\n allPass(event.dataTransfer.types, [isArray, hasAtLeast(1), hasFilesType]);\n\n/* Marker attribute editors set on their root element to opt out of the\n * outer DropZone, so they can handle arbitrary file drops themselves. */\nexport const EDITOR_FILE_DROP_OPT_OUT_ATTR = \"data-accepts-files\";\n\nconst isInsideEditorFileDropOptOut = (event: React.DragEvent<Element>) => {\n const target = event.target;\n if (!(target instanceof Element)) return false;\n return target.closest(`[${EDITOR_FILE_DROP_OPT_OUT_ATTR}]`) !== null;\n};\n\nconst hasAllowedExtension = (file: File) =>\n pipe(\n file,\n (file) => file.name,\n split(\".\"),\n last(),\n isIncludedIn(allowedExtensions),\n );\n\n/* Gets uploaded files from the drop event data transfer */\nconst getFileItems = (event: React.DragEvent<Element>) =>\n pipe(\n [...event.dataTransfer.items],\n filter((item) => isStrictEqual(item.kind, \"file\")),\n map((item) => item.getAsFile()),\n filter(isTruthy),\n );\n\n/* Allows uploading of files by drag and drop.\n * Intended for use in the drop-zone component in connect.\n */\nexport function useDropFile(\n handleAddFile: (file: File, parent: Node | undefined) => Promise<void>,\n) {\n const { isDropTarget, setTarget, unsetTarget } = useDropTarget();\n const isDragAndDropEnabled = useIsDragAndDropEnabled();\n const selectedFolder = useSelectedFolder();\n\n function handleDragEvent(event: React.DragEvent<Element>, cb?: () => void) {\n if (!isDragAndDropEnabled) return;\n if (!isFileDrop(event)) return;\n if (isInsideEditorFileDropOptOut(event)) {\n // Hide the DropZone overlay while the cursor is over an editor that\n // opts in to its own file drops, so the overlay doesn't strand the\n // user covering the opt-out region.\n unsetTarget();\n return;\n }\n event.preventDefault();\n event.stopPropagation();\n cb?.();\n }\n\n const handleAddFiles = (event: React.DragEvent<Element>) =>\n Promise.all(\n pipe(\n event,\n getFileItems,\n filter(hasAllowedExtension),\n map((file) => handleAddFile(file, selectedFolder)),\n ),\n );\n\n const onDragEnter: DragEventHandler = (event) => handleDragEvent(event);\n\n const onDragOver: DragEventHandler = (event) =>\n handleDragEvent(event, setTarget);\n\n const onDragLeave: DragEventHandler = (event) =>\n handleDragEvent(event, unsetTarget);\n\n const onDrop: DragEventHandler = (event) =>\n handleDragEvent(\n event,\n once(() => {\n unsetTarget();\n handleAddFiles(event).catch(console.error);\n }),\n );\n\n return {\n onDragEnter,\n onDragOver,\n onDragLeave,\n onDrop,\n isDropTarget,\n };\n}\n","import type { FolderNode } from \"@powerhousedao/shared/document-drive\";\nimport { useFolderNodesInSelectedDrive } from \"./items-in-selected-drive.js\";\n\nexport function useFolderById(\n id: string | null | undefined,\n): FolderNode | undefined {\n const folders = useFolderNodesInSelectedDrive();\n return folders?.find((n) => n.id === id);\n}\n","import type {\n GraphQLClientDocumentEvent,\n GraphQLClientDocumentsEvent,\n GraphQLDocumentEventOperationName,\n GraphQLDocumentEventsOperationName,\n} from \"./types.js\";\n\nexport function dispatchGraphQLClientDocumentEvent(\n operationName: GraphQLDocumentEventOperationName,\n identifier: string,\n) {\n const event: GraphQLClientDocumentEvent = new CustomEvent(operationName, {\n detail: { identifier },\n });\n window.dispatchEvent(event);\n}\n\nexport function dispatchGraphQLClientDocumentsEvent(\n operationName: GraphQLDocumentEventsOperationName,\n identifiers: string[],\n) {\n const event: GraphQLClientDocumentsEvent = new CustomEvent(operationName, {\n detail: { identifiers },\n });\n window.dispatchEvent(event);\n}\n","import { isIncludedIn, isStrictEqual } from \"remeda\";\nimport { identifierFromMutateDocumentOperationVariables } from \"./adapters.js\";\nimport { graphqlEventsToSyncDrive } from \"./constants.js\";\nimport { dispatchGraphQLClientDocumentEvent } from \"./events.js\";\nimport type { SdkFunctionWrapper } from \"./gen/schema.js\";\n\nexport const documentCacheClientMiddleware: SdkFunctionWrapper = async (\n action,\n operationName,\n operationType,\n variables: unknown,\n) => {\n console.log({ operationName, operationType, variables });\n const result = await action();\n\n if (isIncludedIn(operationName, graphqlEventsToSyncDrive)) {\n window.dispatchEvent(new CustomEvent(operationName));\n }\n\n if (isStrictEqual(operationName, \"MutateDocument\")) {\n dispatchGraphQLClientDocumentEvent(\n operationName,\n identifierFromMutateDocumentOperationVariables(variables),\n );\n }\n\n return result;\n};\n","import type { DocumentDriveDocument } from \"@powerhousedao/shared\";\nimport { DriveDocumentSchema } from \"@powerhousedao/shared/document-drive\";\nimport { useEffect, useState } from \"react\";\nimport { forEach } from \"remeda\";\nimport { phDocumentFromQuery } from \"../graphql/adapters.js\";\nimport { createClient } from \"../graphql/client.js\";\nimport {\n DEFAULT_DRIVE_ID,\n DEFAULT_SWITCHBOARD_URL,\n graphqlEventsToSyncDrive,\n} from \"../graphql/constants.js\";\nimport { documentCacheClientMiddleware } from \"../graphql/document-cache-client-middleware.js\";\nimport { GraphQLClientDocumentCache } from \"../graphql/graphql-client-document-cache.js\";\nimport {\n callEventHandlerRegisterFunctions,\n commonGlobalEventHandlerFunctions,\n} from \"./add-ph-event-handlers.js\";\nimport { setDocumentCache } from \"./document-cache.js\";\nimport { setDrives } from \"./drives.js\";\nimport { setGraphQLReactorClient } from \"./graphql-reactor-client.js\";\nimport { setSelectedDrive } from \"./selected-drive.js\";\nimport { setSelectedNode } from \"./selected-node.js\";\n\nexport function useInitReactorGraphqlClient(\n switchboardUrl = DEFAULT_SWITCHBOARD_URL,\n driveId = DEFAULT_DRIVE_ID,\n) {\n const [hasInit, setHasInit] = useState(false);\n\n useEffect(() => {\n if (hasInit) return;\n\n initGraphQLReactorClientWithDocumentCache(switchboardUrl, driveId)\n .then(() => setHasInit(true))\n .catch(console.error);\n }, [hasInit]);\n\n return hasInit;\n}\n\nasync function reactorGraphqlFetchDrive(\n identifier: string,\n): Promise<DocumentDriveDocument> {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n\n const result = await client.GetDocument({ identifier });\n\n if (!result.document?.document) {\n throw new Error(\"Could not fetch drive with id: \" + identifier);\n }\n\n const drive = phDocumentFromQuery(\n result.document.document,\n DriveDocumentSchema,\n ) as DocumentDriveDocument;\n return drive;\n}\n\nasync function reactorGraphqlSyncDrive(driveId: string) {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n const drive = await reactorGraphqlFetchDrive(driveId);\n setDrives([drive]);\n setSelectedDrive(drive);\n}\n\nasync function initGraphQLReactorClientWithDocumentCache(\n switchboardUrl: string,\n driveId: string,\n) {\n if (!window.ph) {\n window.ph = {};\n }\n\n callEventHandlerRegisterFunctions(commonGlobalEventHandlerFunctions);\n\n const client = createClient(switchboardUrl, documentCacheClientMiddleware);\n setGraphQLReactorClient(client);\n await reactorGraphqlSyncDrive(driveId);\n setSelectedNode(undefined);\n setDocumentCache(new GraphQLClientDocumentCache());\n\n forEach(graphqlEventsToSyncDrive, (name) => {\n window.addEventListener(name, () => {\n reactorGraphqlSyncDrive(driveId).catch(console.error);\n });\n });\n}\n","import type {\n FileNode,\n FolderNode,\n Node,\n} from \"@powerhousedao/shared/document-drive\";\nimport type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { isFileNodeKind, isFolderNodeKind } from \"../utils/nodes.js\";\nimport {\n useDocumentsInSelectedDrive,\n useNodesInSelectedDrive,\n} from \"./items-in-selected-drive.js\";\nimport { useSelectedFolder } from \"./selected-folder.js\";\n\n/** Returns the nodes in the selected folder. */\nexport function useNodesInSelectedFolder(): Node[] | undefined {\n const selectedFolder = useSelectedFolder();\n const nodes = useNodesInSelectedDrive();\n if (!selectedFolder || !nodes) return undefined;\n\n return nodes.filter((n) => n.parentFolder === selectedFolder.id);\n}\n\n/** Returns the file nodes in the selected folder. */\nexport function useFileNodesInSelectedFolder(): FileNode[] | undefined {\n const nodes = useNodesInSelectedFolder();\n if (!nodes) return undefined;\n return nodes.filter((n) => isFileNodeKind(n));\n}\n\n/** Returns the folder nodes in the selected folder. */\nexport function useFolderNodesInSelectedFolder(): FolderNode[] | undefined {\n const nodes = useNodesInSelectedFolder();\n if (!nodes) return undefined;\n return nodes.filter((n) => isFolderNodeKind(n));\n}\n\n/** Returns the documents in the selected folder. */\nexport function useDocumentsInSelectedFolder(): PHDocument[] | undefined {\n const documents = useDocumentsInSelectedDrive();\n const fileNodes = useFileNodesInSelectedFolder();\n const fileNodeIds = fileNodes?.map((node) => node.id);\n return documents?.filter((d) => fileNodeIds?.includes(d.header.id));\n}\n","import type { Node } from \"@powerhousedao/shared/document-drive\";\nimport {\n addFile,\n addFolder,\n copyNode,\n moveNode,\n renameDriveNode,\n renameNode,\n} from \"../actions/document.js\";\nimport { useDrives } from \"./drives.js\";\nimport { useFolderById } from \"./folder-by-id.js\";\nimport { useSelectedDriveSafe } from \"./selected-drive.js\";\nimport { useSelectedFolder } from \"./selected-folder.js\";\nimport { setSelectedNode, useSelectedNode } from \"./selected-node.js\";\n\nfunction resolveNode(driveId: string, node: Node | undefined) {\n return node?.id !== driveId ? node : undefined;\n}\n\nexport function useNodeActions() {\n const [selectedDrive] = useSelectedDriveSafe();\n const selectedFolder = useSelectedFolder();\n const selectedNode = useSelectedNode();\n const selectedParentFolder = useFolderById(selectedNode?.parentFolder);\n const selectedDriveId = selectedDrive?.header.id;\n const drives = useDrives();\n\n async function onAddFile(file: File, parent: Node | undefined) {\n if (!selectedDriveId) return;\n\n const fileName = file.name.replace(/\\..+/gim, \"\");\n\n return addFile(\n file,\n selectedDriveId,\n fileName,\n resolveNode(selectedDriveId, parent)?.id,\n );\n }\n\n async function onAddFolder(name: string, parent: Node | undefined) {\n if (!selectedDriveId) return;\n\n return addFolder(\n selectedDriveId,\n name,\n resolveNode(selectedDriveId, parent)?.id,\n );\n }\n\n async function onRenameNode(\n newName: string,\n node: Node,\n ): Promise<Node | undefined> {\n if (!selectedDriveId) return;\n\n const resolvedNode = resolveNode(selectedDriveId, node);\n if (!resolvedNode) {\n console.error(`Node ${node.id} not found`);\n return;\n }\n\n return await renameNode(selectedDriveId, node.id, newName);\n }\n\n async function onCopyNode(src: Node, target: Node | undefined) {\n if (!selectedDriveId) return;\n const resolvedSrc = resolveNode(selectedDriveId, src);\n if (!resolvedSrc) {\n console.error(`Node ${src.id} not found`);\n return;\n }\n const resolvedTarget = resolveNode(selectedDriveId, target);\n\n await copyNode(selectedDriveId, resolvedSrc, resolvedTarget);\n }\n\n async function onMoveNode(src: Node, target: Node | undefined) {\n if (!selectedDriveId) return;\n\n const resolvedSrc = resolveNode(selectedDriveId, src);\n if (!resolvedSrc) {\n console.error(`Node ${src.id} not found`);\n return;\n }\n const resolvedTarget = resolveNode(selectedDriveId, target);\n\n // if node is already on target then ignore move\n if (\n (!resolvedTarget?.id && !src.parentFolder) ||\n resolvedTarget?.id === src.parentFolder\n ) {\n return;\n }\n await moveNode(selectedDriveId, resolvedSrc, resolvedTarget);\n }\n\n async function onDuplicateNode(src: Node) {\n if (!selectedDriveId) return;\n\n const resolvedSrc = resolveNode(selectedDriveId, src);\n if (!resolvedSrc) {\n console.error(`Node ${src.id} not found`);\n return;\n }\n\n const target = resolveNode(\n selectedDriveId,\n selectedFolder ?? selectedParentFolder,\n );\n await copyNode(selectedDriveId, resolvedSrc, target);\n }\n async function onAddAndSelectNewFolder(name: string) {\n if (!name) return;\n if (!selectedDriveId) return;\n\n const resolvedTarget = resolveNode(\n selectedDriveId,\n selectedFolder ?? selectedParentFolder,\n );\n if (!resolvedTarget) return;\n\n const newFolder = await onAddFolder(name, resolvedTarget);\n\n if (newFolder) {\n setSelectedNode(newFolder);\n }\n }\n\n async function onRenameDriveNodes(\n newName: string,\n nodeId: string,\n ): Promise<void> {\n if (!drives) return;\n\n // Find all drives that contain this node\n const drivesWithNode = drives.filter((drive) =>\n drive.state.global.nodes.some((n) => n.id === nodeId),\n );\n\n // Update node name in all parent drives\n await Promise.all(\n drivesWithNode.map((drive) =>\n renameDriveNode(drive.header.id, nodeId, newName),\n ),\n );\n }\n\n return {\n onAddFile,\n onAddFolder,\n onRenameNode,\n onCopyNode,\n onMoveNode,\n onDuplicateNode,\n onAddAndSelectNewFolder,\n onRenameDriveNodes,\n };\n}\n","import type { Node } from \"@powerhousedao/shared/document-drive\";\nimport { useNodesInSelectedDrive } from \"./items-in-selected-drive.js\";\n\n/** Returns a node in the selected drive by id. */\nexport function useNodeById(id: string | null | undefined): Node | undefined {\n const nodes = useNodesInSelectedDrive();\n return nodes?.find((n) => n.id === id);\n}\n","import type { Node } from \"@powerhousedao/shared/document-drive\";\nimport { useNodesInSelectedDrive } from \"./items-in-selected-drive.js\";\nimport { useSelectedNode } from \"./selected-node.js\";\n\n/** Returns the path to a node in the selected drive */\nexport function useNodePathById(id: string | null | undefined) {\n const nodes = useNodesInSelectedDrive();\n if (!nodes) return [];\n\n const path: Node[] = [];\n let current = nodes.find((n) => n.id === id);\n\n while (current) {\n path.push(current);\n if (!current.parentFolder) break;\n current = nodes.find((n) => n.id === current?.parentFolder);\n }\n\n return path.reverse();\n}\n\n/** Returns the path to the currently selected node in the selected drive. */\nexport function useSelectedNodePath() {\n const selectedNode = useSelectedNode();\n return useNodePathById(selectedNode?.id);\n}\n","import type { FolderNode } from \"@powerhousedao/shared/document-drive\";\nimport { useFolderById } from \"./folder-by-id.js\";\nimport { useNodeById } from \"./node-by-id.js\";\nimport { useSelectedNode } from \"./selected-node.js\";\n\nexport function useNodeParentFolderById(\n id: string | null | undefined,\n): FolderNode | undefined {\n const node = useNodeById(id);\n const parentFolder = useFolderById(node?.parentFolder);\n return parentFolder;\n}\n\nexport function useParentFolderForSelectedNode() {\n const node = useSelectedNode();\n return useNodeParentFolderById(node?.id);\n}\n","import type { DocumentDispatch } from \"@powerhousedao/reactor-browser\";\nimport { isFileNode } from \"@powerhousedao/shared/document-drive\";\nimport type { Action, PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { NoSelectedDocumentError } from \"../errors.js\";\nimport type { DispatchFn, UseDispatchResult } from \"./dispatch.js\";\nimport { useDocumentById } from \"./document-by-id.js\";\nimport { useDocumentOfType } from \"./document-of-type.js\";\nimport { useSelectedNode } from \"./selected-node.js\";\n\n/** Returns the selected document id */\nexport function useSelectedDocumentId(): string | undefined {\n const selectedNode = useSelectedNode();\n return selectedNode && isFileNode(selectedNode) ? selectedNode.id : undefined;\n}\n\n/** Returns the selected document. */\nexport function useSelectedDocument(): readonly [\n PHDocument,\n DispatchFn<Action>,\n] {\n const selectedDocumentId = useSelectedDocumentId();\n const [document, dispatch] = useDocumentById(selectedDocumentId);\n if (!document) {\n throw new NoSelectedDocumentError();\n }\n return [document, dispatch] as const;\n}\n\n/** Returns the selected document. */\nexport function useSelectedDocumentSafe(): UseDispatchResult<\n PHDocument,\n Action\n> {\n const selectedDocumentId = useSelectedDocumentId();\n return useDocumentById(selectedDocumentId);\n}\n\n/** Returns the selected document of a specific type, throws an error if the found document has a different type */\nexport function useSelectedDocumentOfType(\n documentType: null | undefined,\n): never[];\nexport function useSelectedDocumentOfType<\n TDocument extends PHDocument,\n TAction extends Action,\n>(documentType: string): [TDocument, DocumentDispatch<TAction>];\nexport function useSelectedDocumentOfType<\n TDocument extends PHDocument,\n TAction extends Action,\n>(\n documentType: string | null | undefined,\n): never[] | [TDocument, DocumentDispatch<TAction>] {\n const documentId = useSelectedDocumentId();\n\n if (!documentType) {\n return [];\n }\n if (!documentId) {\n throw new NoSelectedDocumentError();\n }\n return useDocumentOfType<TDocument, TAction>(documentId, documentType);\n}\n","import type { SubgraphModule } from \"@powerhousedao/shared/document-model\";\nimport { useVetraPackages } from \"./vetra-packages.js\";\n\nexport function useSubgraphModules(): SubgraphModule[] | undefined {\n const vetraPackages = useVetraPackages();\n return vetraPackages.flatMap((pkg) => pkg.subgraphs || []);\n}\n","import { useEffect, useSyncExternalStore } from \"react\";\n\ntype Theme = \"light\" | \"dark\";\ntype SystemTheme = Theme;\ntype StoredTheme = \"light\" | \"dark\" | \"system\";\n\nconst STORED_THEME_KEY = \"ph:theme\" as const;\nconst UPDATE_STORED_THEME = \"ph:updateStoredTheme\" as const;\nconst STORED_THEME_UPDATED = \"ph:storedThemeUpdated\" as const;\nconst SYSTEM_THEME_UPDATED = \"ph:systemThemeUpdated\" as const;\nconst isServer = typeof window === \"undefined\";\n\ntype UpdateStoredThemeEvent = CustomEvent<{ storedTheme: StoredTheme }>;\ntype StoredThemeUpdatedEvent = CustomEvent<{ storedTheme: StoredTheme }>;\ntype SystemThemeUpdatedEvent = CustomEvent<{ systemTheme: SystemTheme }>;\n\ntype ThemeWindowEvents = {\n [UPDATE_STORED_THEME]: UpdateStoredThemeEvent;\n [STORED_THEME_UPDATED]: StoredThemeUpdatedEvent;\n [SYSTEM_THEME_UPDATED]: SystemThemeUpdatedEvent;\n};\n\ndeclare global {\n interface WindowEventMap extends ThemeWindowEvents {}\n}\n\nfunction setStoredTheme(storedTheme: StoredTheme) {\n if (isServer) return;\n localStorage.setItem(STORED_THEME_KEY, storedTheme);\n}\n\nfunction setTheme(storedTheme: StoredTheme) {\n if (isServer) return;\n const updateStoredThemeEvent = new CustomEvent(UPDATE_STORED_THEME, {\n detail: {\n storedTheme,\n },\n });\n window.dispatchEvent(updateStoredThemeEvent);\n}\n\nfunction handleUpdateStoredTheme(event: UpdateStoredThemeEvent) {\n if (isServer) return;\n const storedTheme = event.detail.storedTheme;\n setStoredTheme(storedTheme);\n const storedThemeUpdatedEvent = new CustomEvent(STORED_THEME_UPDATED, {\n detail: { storedTheme },\n });\n window.dispatchEvent(storedThemeUpdatedEvent);\n}\n\nfunction getStoredTheme() {\n if (isServer) return undefined;\n const storedTheme = localStorage.getItem(STORED_THEME_KEY) ?? undefined;\n return storedTheme as StoredTheme;\n}\n\nfunction getPrefersDarkMediaQuery() {\n if (isServer) return;\n const prefersDarkMediaQuery = window.matchMedia(\n \"(prefers-color-scheme: dark)\",\n );\n return prefersDarkMediaQuery;\n}\n\nfunction getPrefersDark() {\n if (isServer) return false;\n const prefersDark = getPrefersDarkMediaQuery();\n if (prefersDark?.matches) return true;\n return false;\n}\n\nfunction getSystemTheme(): SystemTheme {\n if (isServer) return \"light\";\n const prefersDark = getPrefersDark();\n if (prefersDark) return \"dark\";\n return \"light\";\n}\n\nfunction handleSystemThemeChange(event: MediaQueryListEvent) {\n const isDark = event.matches;\n const systemTheme = isDark ? \"dark\" : \"light\";\n const systemThemeUpdatedEvent = new CustomEvent(SYSTEM_THEME_UPDATED, {\n detail: { systemTheme },\n });\n window.dispatchEvent(systemThemeUpdatedEvent);\n}\n\nfunction toggleDark(isDark: boolean) {\n if (isServer) return;\n document.documentElement.classList.toggle(\"dark\", isDark);\n}\n\nexport function initTheme() {\n if (isServer) return;\n\n useEffect(() => {\n window.addEventListener(UPDATE_STORED_THEME, handleUpdateStoredTheme);\n const prefersDarkMediaQuery = getPrefersDarkMediaQuery();\n prefersDarkMediaQuery?.addEventListener(\"change\", handleSystemThemeChange);\n return () => {\n window.removeEventListener(UPDATE_STORED_THEME, handleUpdateStoredTheme);\n prefersDarkMediaQuery?.removeEventListener(\n \"change\",\n handleSystemThemeChange,\n );\n };\n }, []);\n}\n\nfunction subscribeToStoredTheme(onStoreChange: () => void) {\n if (isServer) return () => {};\n // `storage` fires in every OTHER same-origin browsing context (e.g. an\n // embedding parent window), keeping embedded instances in sync live.\n const handleStorage = (event: StorageEvent) => {\n if (event.key === STORED_THEME_KEY || event.key === null) onStoreChange();\n };\n window.addEventListener(STORED_THEME_UPDATED, onStoreChange);\n window.addEventListener(\"storage\", handleStorage);\n return () => {\n window.removeEventListener(STORED_THEME_UPDATED, onStoreChange);\n window.removeEventListener(\"storage\", handleStorage);\n };\n}\n\nfunction subscribeToSystemTheme(onStoreChange: () => void) {\n if (isServer) return () => {};\n window.addEventListener(SYSTEM_THEME_UPDATED, onStoreChange);\n return () => {\n window.removeEventListener(SYSTEM_THEME_UPDATED, onStoreChange);\n };\n}\n\nexport function useTheme() {\n const storedTheme = useSyncExternalStore(\n subscribeToStoredTheme,\n () => getStoredTheme(),\n () => \"system\" as const,\n );\n const systemTheme = useSyncExternalStore(\n subscribeToSystemTheme,\n () => getSystemTheme(),\n () => \"light\" as const,\n );\n\n const isSystem = storedTheme === undefined || storedTheme === \"system\";\n\n const theme = isSystem ? systemTheme : storedTheme;\n const isDark = theme === \"dark\";\n\n useEffect(() => {\n toggleDark(isDark);\n }, [isDark]);\n\n return {\n theme,\n isSystem,\n setTheme,\n } as const;\n}\n","import {\n DriveCollectionId,\n type GqlRequestChannel,\n} from \"@powerhousedao/reactor\";\nimport type { DocumentDriveDocument } from \"@powerhousedao/shared/document-drive\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useSyncList } from \"./reactor.js\";\n\nexport type DriveSystemInfoState =\n | { status: \"local\" }\n | { status: \"loading\" }\n | { status: \"error\"; message: string }\n | {\n status: \"ready\";\n version: string;\n gitHash: string;\n gitUrl: string | null;\n host: string;\n };\n\nexport function deriveSystemUrl(channelUrl: string): string | null {\n try {\n const url = new URL(channelUrl);\n url.search = \"\";\n url.hash = \"\";\n const suffix = \"/graphql/r\";\n if (url.pathname.endsWith(suffix)) {\n url.pathname = url.pathname.slice(0, -suffix.length) + \"/graphql/system\";\n } else {\n url.pathname = \"/graphql/system\";\n }\n return url.toString();\n } catch {\n return null;\n }\n}\n\nconst cache = new Map<string, DriveSystemInfoState>();\n\nexport function useDriveSystemInfo(\n drive: DocumentDriveDocument | undefined,\n): DriveSystemInfoState {\n const remotes = useSyncList();\n const driveId = drive?.header.id;\n\n const systemUrl = useMemo(() => {\n if (!driveId) return null;\n const remote = remotes.find((r) =>\n r.meta.collectionId.equals(DriveCollectionId.forDrive(driveId)),\n );\n const channelUrl = (remote?.channel as GqlRequestChannel | undefined)\n ?.config.url;\n if (typeof channelUrl !== \"string\") return null;\n return deriveSystemUrl(channelUrl);\n }, [remotes, driveId]);\n\n const [state, setState] = useState<DriveSystemInfoState>(() =>\n systemUrl\n ? (cache.get(systemUrl) ?? { status: \"loading\" })\n : { status: \"local\" },\n );\n\n useEffect(() => {\n if (!systemUrl) {\n setState({ status: \"local\" });\n return;\n }\n\n const cached = cache.get(systemUrl);\n if (cached && cached.status !== \"loading\") {\n setState(cached);\n return;\n }\n\n setState({ status: \"loading\" });\n cache.set(systemUrl, { status: \"loading\" });\n\n const controller = new AbortController();\n fetch(systemUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n query: \"{ system { version gitHash gitUrl } }\",\n }),\n signal: controller.signal,\n })\n .then(async (res) => {\n const json = (await res.json()) as {\n data?: {\n system?: {\n version: string;\n gitHash: string;\n gitUrl: string | null;\n };\n };\n errors?: Array<{ message: string }>;\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n const sys = json.data?.system;\n if (!sys) throw new Error(\"Missing system in response\");\n const next: DriveSystemInfoState = {\n status: \"ready\",\n version: sys.version,\n gitHash: sys.gitHash,\n gitUrl: sys.gitUrl ?? null,\n host: new URL(systemUrl).host,\n };\n cache.set(systemUrl, next);\n setState(next);\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted) return;\n const message = err instanceof Error ? err.message : String(err);\n console.error(message);\n const next: DriveSystemInfoState = { status: \"error\", message };\n cache.set(systemUrl, next);\n setState(next);\n });\n\n return () => controller.abort();\n }, [systemUrl]);\n\n return state;\n}\n","import {\n useCallback,\n useRef,\n useState,\n type DragEvent,\n type DragEventHandler,\n} from \"react\";\nimport { EDITOR_FILE_DROP_OPT_OUT_ATTR } from \"./file-drag-and-drop.js\";\n\nexport type UseEditorFileDropOptions = {\n /** Lowercase file extensions including the dot (e.g. [\".png\", \".pdf\"]).\n * When omitted, all files are accepted. */\n accept?: readonly string[];\n /** Called with the files that passed the extension filter. */\n onFiles: (files: File[]) => void;\n};\n\nexport type UseEditorFileDropResult = {\n /** Spread onto the editor's root element. Includes the opt-out attribute\n * so the outer DropZone leaves file drops alone within this subtree. */\n dragProps: {\n onDragEnter: DragEventHandler<Element>;\n onDragOver: DragEventHandler<Element>;\n onDragLeave: DragEventHandler<Element>;\n onDrop: DragEventHandler<Element>;\n } & Record<typeof EDITOR_FILE_DROP_OPT_OUT_ATTR, \"\">;\n /** True while a file drag is hovering anywhere inside the editor root. */\n isDragOver: boolean;\n};\n\nconst hasFiles = (event: DragEvent<Element>) =>\n event.dataTransfer.types.includes(\"Files\");\n\nconst filterByExtension = (files: FileList, accept?: readonly string[]) => {\n const all = Array.from(files);\n if (!accept || accept.length === 0) return all;\n const lowerAccept = accept.map((ext) => ext.toLowerCase());\n return all.filter((file) => {\n const lower = file.name.toLowerCase();\n return lowerAccept.some((ext) => lower.endsWith(ext));\n });\n};\n\nexport function useEditorFileDrop(\n options: UseEditorFileDropOptions,\n): UseEditorFileDropResult {\n const { accept, onFiles } = options;\n const [isDragOver, setIsDragOver] = useState(false);\n const depthRef = useRef(0);\n\n const onDragOver = useCallback<DragEventHandler<Element>>((event) => {\n if (!hasFiles(event)) return;\n event.preventDefault();\n }, []);\n\n const onDragEnter = useCallback<DragEventHandler<Element>>((event) => {\n if (!hasFiles(event)) return;\n depthRef.current += 1;\n if (depthRef.current === 1) setIsDragOver(true);\n }, []);\n\n const onDragLeave = useCallback<DragEventHandler<Element>>((event) => {\n if (!hasFiles(event)) return;\n depthRef.current = Math.max(0, depthRef.current - 1);\n if (depthRef.current === 0) setIsDragOver(false);\n }, []);\n\n const onDrop = useCallback<DragEventHandler<Element>>(\n (event) => {\n if (!hasFiles(event)) return;\n event.preventDefault();\n depthRef.current = 0;\n setIsDragOver(false);\n const accepted = filterByExtension(event.dataTransfer.files, accept);\n if (accepted.length === 0) return;\n onFiles(accepted);\n },\n [accept, onFiles],\n );\n\n return {\n dragProps: {\n onDragEnter,\n onDragOver,\n onDragLeave,\n onDrop,\n [EDITOR_FILE_DROP_OPT_OUT_ATTR]: \"\",\n },\n isDragOver,\n };\n}\n","import type { IReactorClient } from \"@powerhousedao/reactor\";\nimport { SyncStatus } from \"@powerhousedao/reactor\";\nimport type {\n DocumentDriveDocument,\n SharingType,\n} from \"@powerhousedao/shared/document-drive\";\nimport { DRIVE_DOCUMENT_TYPES } from \"../constants.js\";\n\nexport type UISyncStatus =\n | \"INITIAL_SYNC\"\n | \"SUCCESS\"\n | \"CONFLICT\"\n | \"MISSING\"\n | \"ERROR\"\n | \"SYNCING\";\n\nconst syncStatusToUI: Record<SyncStatus, UISyncStatus> = {\n [SyncStatus.Synced]: \"SUCCESS\",\n [SyncStatus.Outgoing]: \"SYNCING\",\n [SyncStatus.Incoming]: \"SYNCING\",\n [SyncStatus.OutgoingAndIncoming]: \"SYNCING\",\n [SyncStatus.Error]: \"ERROR\",\n};\n\nexport async function getDrives(\n reactor: IReactorClient,\n): Promise<DocumentDriveDocument[]> {\n // SearchFilter.type takes one string, so query each drive type and merge.\n const perType = await Promise.all(\n DRIVE_DOCUMENT_TYPES.map((type) => reactor.find({ type })),\n );\n return perType.flatMap((r) => r.results) as DocumentDriveDocument[];\n}\n\nexport function getSyncStatus(\n documentId: string,\n sharingType: SharingType,\n): Promise<UISyncStatus | undefined> {\n return Promise.resolve(getSyncStatusSync(documentId, sharingType));\n}\n\nexport function getSyncStatusSync(\n documentId: string,\n sharingType: SharingType,\n): UISyncStatus | undefined {\n if (sharingType === \"LOCAL\") return;\n\n const syncManager =\n window.ph?.reactorClientModule?.reactorModule?.syncModule?.syncManager;\n if (!syncManager) return;\n\n const status = syncManager.getSyncStatus(documentId);\n if (status === undefined) return;\n\n return syncStatusToUI[status];\n}\n","import type { Operation } from \"@powerhousedao/shared/document-model\";\n\nexport const getRevisionFromDate = (\n startDate?: Date,\n endDate?: Date,\n operations: Operation[] = [],\n) => {\n if (!startDate || !endDate) return 0;\n\n const operation = operations.find((operation) => {\n const operationDate = new Date(operation.timestampUtcMs);\n return operationDate >= startDate && operationDate <= endDate;\n });\n\n return operation ? operation.index : 0;\n};\n","import * as lzString from \"lz-string\";\nimport { GetDocumentWithOperationsDocument } from \"../graphql/gen/schema.js\";\n\nexport async function getDriveIdBySlug(driveUrl: string, slug: string) {\n if (!driveUrl) {\n return;\n }\n\n const urlParts = driveUrl.split(\"/\");\n urlParts.pop(); // remove id\n urlParts.pop(); // remove /d\n urlParts.push(\"drives\"); // add /drives\n const drivesUrl = urlParts.join(\"/\");\n const result = await fetch(drivesUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n query: `\n query getDriveIdBySlug($slug: String!) {\n driveIdBySlug(slug: $slug)\n }\n `,\n variables: {\n slug,\n },\n }),\n });\n\n const data = (await result.json()) as {\n data: { driveIdBySlug: string };\n };\n\n return data.data.driveIdBySlug;\n}\n\nexport function getSlugFromDriveUrl(driveUrl: string) {\n const urlParts = driveUrl.split(\"/\");\n return urlParts.pop();\n}\n\nexport function getSwitchboardGatewayUrlFromDriveUrl(driveUrl: string) {\n const urlParts = driveUrl.split(\"/\");\n urlParts.pop(); // remove id\n urlParts.pop(); // remove /d\n urlParts.push(\"graphql\"); // add /graphql\n return urlParts.join(\"/\");\n}\n\nexport function getDocumentGraphqlQuery() {\n const loc = GetDocumentWithOperationsDocument.loc;\n if (!loc) {\n throw new Error(\n \"GetDocumentWithOperationsDocument is misconfigured, loc is missing.\",\n );\n }\n return loc.source.body;\n}\n\nexport function buildDocumentSubgraphQuery(\n identifier: string,\n authToken?: string,\n) {\n const query = getDocumentGraphqlQuery();\n const variables = { identifier };\n const headers = authToken\n ? {\n Authorization: `Bearer ${authToken}`,\n }\n : undefined;\n\n const payload: Record<string, string> = {\n document: query.trim(),\n variables: JSON.stringify(variables, null, 2),\n };\n if (headers) {\n payload.headers = JSON.stringify(headers);\n }\n return lzString.compressToEncodedURIComponent(JSON.stringify(payload));\n}\n\nexport function buildDocumentSubgraphUrl(\n driveUrl: string,\n identifier: string,\n authToken?: string,\n) {\n const encodedQuery = buildDocumentSubgraphQuery(identifier, authToken);\n return `${driveUrl}?explorerURLState=${encodedQuery}`;\n}\n","import type { IDocumentModelRegistry } from \"@powerhousedao/reactor\";\nimport type {\n Action,\n PHDocument,\n UpgradeTransition,\n} from \"@powerhousedao/shared/document-model\";\n\nconst NON_DOMAIN_SCOPES = new Set([\"auth\", \"document\"]);\n\nexport type UpgradeStepInfo = {\n toVersion: number;\n description: string;\n};\n\nexport type DocumentUpgradePreview = {\n fromVersion: number;\n toVersion: number;\n steps: UpgradeStepInfo[];\n addedFields: string[];\n removedFields: string[];\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction walkShapes(\n before: unknown,\n after: unknown,\n path: string,\n added: string[],\n removed: string[],\n): void {\n if (isPlainObject(before) && isPlainObject(after)) {\n const beforeKeys = Object.keys(before);\n const afterKeys = Object.keys(after);\n for (const key of afterKeys) {\n const childPath = path ? `${path}.${key}` : key;\n if (!beforeKeys.includes(key)) {\n added.push(childPath);\n continue;\n }\n walkShapes(before[key], after[key], childPath, added, removed);\n }\n for (const key of beforeKeys) {\n if (!afterKeys.includes(key)) {\n removed.push(path ? `${path}.${key}` : key);\n }\n }\n return;\n }\n\n if (Array.isArray(before) && Array.isArray(after)) {\n if (before.length > 0 && after.length > 0) {\n walkShapes(before[0], after[0], `${path}[]`, added, removed);\n }\n }\n}\n\n/**\n * Recursively diffs the structural shape of two values, returning dot-paths\n * of keys present in `after` but not `before` (added) and vice versa\n * (removed). Array fields are compared by the shape of a representative\n * element (the first element on each side, when both sides have one) using\n * the `path[]` notation, e.g. `todos[].status`. Only key presence is\n * compared — array length and primitive values are ignored.\n */\nexport function diffStateShapes(\n before: unknown,\n after: unknown,\n): { added: string[]; removed: string[] } {\n const added: string[] = [];\n const removed: string[] = [];\n walkShapes(before, after, \"\", added, removed);\n return { added, removed };\n}\n\n/**\n * Computes a dry-run preview of upgrading `document` to the latest\n * registered version of its document model: the version jump, the upgrade\n * steps that will run, and which state fields will be added or removed.\n * Applies the upgrade reducers against a deep clone of the document, so the\n * original is left untouched.\n *\n * Returns undefined when the registry is unavailable, the document is\n * already at (or above) the latest registered version, or the upgrade path\n * cannot be computed.\n */\nexport function getDocumentUpgradePreview(\n document: PHDocument,\n registry: IDocumentModelRegistry | undefined,\n): DocumentUpgradePreview | undefined {\n if (!registry) {\n return undefined;\n }\n\n const documentType = document.header.documentType;\n const fromVersion = document.state.document.version || 1;\n let latestVersion: number;\n try {\n latestVersion = registry.getLatestVersion(documentType);\n } catch {\n return undefined;\n }\n if (fromVersion >= latestVersion) {\n return undefined;\n }\n\n let transitions: UpgradeTransition[];\n try {\n transitions = registry.computeUpgradePath(\n documentType,\n fromVersion,\n latestVersion,\n );\n } catch {\n return undefined;\n }\n\n const stubAction: Action = {\n id: \"\",\n type: \"UPGRADE_DOCUMENT\",\n scope: \"document\",\n timestampUtcMs: \"\",\n input: {\n documentId: document.header.id,\n model: documentType,\n fromVersion,\n toVersion: latestVersion,\n },\n };\n\n let upgraded = structuredClone(document);\n for (const transition of transitions) {\n upgraded = transition.upgradeReducer(upgraded, stubAction);\n }\n\n const addedFields: string[] = [];\n const removedFields: string[] = [];\n const scopes = new Set([\n ...Object.keys(document.state),\n ...Object.keys(upgraded.state),\n ]);\n for (const scope of scopes) {\n if (NON_DOMAIN_SCOPES.has(scope)) {\n continue;\n }\n const beforeScope = (document.state as Record<string, unknown>)[scope];\n const afterScope = (upgraded.state as Record<string, unknown>)[scope];\n const { added, removed } = diffStateShapes(beforeScope, afterScope);\n for (const path of added) {\n addedFields.push(`${scope}.${path}`);\n }\n for (const path of removed) {\n removedFields.push(`${scope}.${path}`);\n }\n }\n\n return {\n fromVersion,\n toVersion: latestVersion,\n steps: transitions.map((transition) => ({\n toVersion: transition.toVersion,\n description: transition.description ?? \"\",\n })),\n addedFields,\n removedFields,\n };\n}\n","import {\n DriveCollectionId,\n type GqlRequestChannel,\n} from \"@powerhousedao/reactor\";\nimport type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { useMemo } from \"react\";\nimport { isDefined } from \"remeda\";\nimport { buildDocumentSubgraphUrl } from \"../utils/index.js\";\nimport { useRenown, useSyncList, useUser } from \"./connect.js\";\nimport { useSelectedDriveSafe } from \"./selected-drive.js\";\n\n/**\n * Hook that returns a function to generate a document's switchboard URL.\n * Only returns a function for documents in remote drives.\n * Returns null for local drives or when the document/drive cannot be determined.\n *\n * The returned function generates a fresh bearer token and builds the switchboard URL\n * with authentication when called.\n *\n * @param document - The document to create a switchboard URL generator for\n * @returns An async function that returns the switchboard URL, or null if not applicable\n */\nexport function useGetSwitchboardLink(\n document: PHDocument | undefined,\n): (() => Promise<string>) | null {\n const [drive] = useSelectedDriveSafe();\n const remotes = useSyncList();\n\n const isRemoteDrive = useMemo(() => {\n if (!isDefined(drive)) return false;\n\n return remotes.some((remote) =>\n remote.meta.collectionId.equals(\n DriveCollectionId.forDrive(drive.header.id),\n ),\n );\n }, [remotes, drive]);\n const remoteUrl = useMemo(() => {\n if (!isDefined(drive)) return null;\n\n try {\n const remote = remotes.find((remote) =>\n remote.meta.collectionId.equals(\n DriveCollectionId.forDrive(drive.header.id),\n ),\n );\n\n const channelUrl = (remote?.channel as GqlRequestChannel | undefined)\n ?.config.url;\n if (typeof channelUrl === \"string\") {\n return channelUrl;\n }\n\n return null;\n } catch (error) {\n console.error(\"Error determining remote URL:\", error);\n return null;\n }\n }, [remotes, drive]);\n const renown = useRenown();\n const user = useUser();\n\n return useMemo(() => {\n if (!isRemoteDrive || !document?.header.id || !remoteUrl) {\n return null;\n }\n\n return async () => {\n // Get bearer token if user is authenticated\n const token = user?.address\n ? await renown?.getBearerToken({\n expiresIn: 600,\n aud: remoteUrl,\n })\n : undefined;\n\n // Build and return the switchboard URL with the document subgraph query\n return buildDocumentSubgraphUrl(remoteUrl, document.header.id, token);\n };\n }, [isRemoteDrive, remoteUrl, document, user, renown]);\n}\n","import { addFileWithProgress } from \"../actions/document.js\";\nimport type {\n ConflictResolution,\n FileUploadProgressCallback,\n UseOnDropFile,\n} from \"../types/upload.js\";\nimport { useDocumentTypes } from \"./document-types.js\";\nimport { useSelectedDriveId } from \"./selected-drive.js\";\nimport { useSelectedFolder } from \"./selected-folder.js\";\n\nexport const useOnDropFile: UseOnDropFile = (\n documentTypesOverride?: string[],\n) => {\n const selectedDriveId = useSelectedDriveId();\n const selectedFolder = useSelectedFolder();\n const documentTypes = useDocumentTypes();\n\n const onDropFile = async (\n file: File,\n onProgress?: FileUploadProgressCallback,\n resolveConflict?: ConflictResolution,\n ) => {\n if (!selectedDriveId) {\n console.warn(\"No selected drive - upload skipped\");\n return;\n }\n\n const fileName = file.name.replace(/\\..+/gim, \"\");\n const targetNodeId = selectedFolder?.id;\n\n // Return the FileNode directly from addFileWithProgress\n return await addFileWithProgress(\n file,\n selectedDriveId,\n fileName,\n targetNodeId,\n onProgress,\n documentTypesOverride ?? documentTypes,\n resolveConflict,\n );\n };\n\n return onDropFile;\n};\n","import { useAllowList } from \"./connect.js\";\nimport { useUser } from \"./renown.js\";\nexport function useUserPermissions() {\n const user = useUser();\n const allowList = useAllowList();\n if (!allowList) {\n return {\n isAllowedToCreateDocuments: true,\n isAllowedToEditDocuments: true,\n };\n }\n\n return {\n isAllowedToCreateDocuments: allowList.includes(user?.address ?? \"\"),\n isAllowedToEditDocuments: allowList.includes(user?.address ?? \"\"),\n };\n}\n","import {\n createAttachmentClient,\n type AttachmentDownloadInput,\n type AttachmentHeader,\n type IAttachmentClient,\n type PreprocessResult,\n} from \"@powerhousedao/reactor-attachments/client\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { useAttachmentService } from \"./attachment-service.js\";\n\n/** Returns an IAttachmentClient wrapping the current IAttachmentService, or undefined if none is set. */\nexport function useAttachments(): IAttachmentClient | undefined {\n const service = useAttachmentService();\n return useMemo(\n () => (service ? createAttachmentClient(service) : undefined),\n [service],\n );\n}\n\nexport type UseAttachmentPreviewInput = {\n documentId: string;\n /** Pass null/undefined to render nothing (the hook stays idle). */\n ref: AttachmentDownloadInput[\"ref\"] | null | undefined;\n /**\n * How many times a failed attempt is retried before the hook settles on\n * error. Retrying matters because a freshly attached ref is not\n * immediately downloadable: the server's reference index authorizes\n * downloads and only learns the (document, ref) pair once the operation\n * has synced and been projected. Defaults to 3.\n */\n retries?: number;\n /** Fixed delay between attempts, in milliseconds. Defaults to 3000. */\n retryDelayMs?: number;\n};\n\nconst DEFAULT_PREVIEW_RETRIES = 3;\nconst DEFAULT_PREVIEW_RETRY_DELAY_MS = 3_000;\n\nexport type UseAttachmentPreviewReturn = {\n /** Object URL ready for img/iframe/video src; undefined while loading or on error. */\n url: string | undefined;\n header: AttachmentHeader | undefined;\n loading: boolean;\n error: Error | undefined;\n};\n\n/**\n * Document-authorized inline preview of an attachment. Downloads the bytes\n * through the normal authorized flow, exposes them as an object URL, and\n * revokes it automatically on unmount and whenever documentId/ref change —\n * editors never touch blobs or URL lifecycles. Failed attempts are retried\n * (`retries` × `retryDelayMs`) so a preview requested right after attaching\n * appears as soon as the server's reference index catches up.\n */\nexport function useAttachmentPreview({\n documentId,\n ref,\n retries = DEFAULT_PREVIEW_RETRIES,\n retryDelayMs = DEFAULT_PREVIEW_RETRY_DELAY_MS,\n}: UseAttachmentPreviewInput): UseAttachmentPreviewReturn {\n const client = useAttachments();\n const [state, setState] = useState<UseAttachmentPreviewReturn>({\n url: undefined,\n header: undefined,\n loading: false,\n error: undefined,\n });\n\n useEffect(() => {\n if (!client || !ref) {\n setState({\n url: undefined,\n header: undefined,\n loading: false,\n error: undefined,\n });\n return;\n }\n let cancelled = false;\n let revoke: (() => void) | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let attempt = 0;\n setState({\n url: undefined,\n header: undefined,\n loading: true,\n error: undefined,\n });\n const load = () => {\n client\n .downloadObjectUrl({ documentId, ref })\n .then((result) => {\n if (cancelled) {\n result.revoke();\n return;\n }\n revoke = result.revoke;\n setState({\n url: result.url,\n header: result.header,\n loading: false,\n error: undefined,\n });\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n if (attempt < retries) {\n attempt += 1;\n timer = setTimeout(load, retryDelayMs);\n return; // stay in loading state while the index catches up\n }\n setState({\n url: undefined,\n header: undefined,\n loading: false,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n });\n };\n load();\n return () => {\n cancelled = true;\n if (timer !== undefined) clearTimeout(timer);\n revoke?.();\n };\n }, [client, documentId, ref, retries, retryDelayMs]);\n\n return state;\n}\n\n/** Upload lifecycle status. progress is coarse (0 before/during, 1 on Done) because RemoteAttachmentUpload buffers the full body before issuing a single PUT. */\nexport enum UploadStatus {\n None = \"None\",\n Hashing = \"Hashing\",\n Uploading = \"Uploading\",\n Done = \"Done\",\n Error = \"Error\",\n}\n\nexport type UseAttachmentUploadReturn = {\n preprocess: (file: Blob) => Promise<PreprocessResult>;\n upload: (results: PreprocessResult) => Promise<void>;\n status: UploadStatus;\n progress: number;\n error: Error | undefined;\n};\n\n/** Hook for managing the full attachment preprocess + upload lifecycle. preprocess and upload callbacks are stable (useCallback) and depend only on the current IAttachmentClient reference. */\nexport function useAttachmentUpload(): UseAttachmentUploadReturn {\n const [status, setStatus] = useState<UploadStatus>(UploadStatus.None);\n const [progress, setProgress] = useState(0);\n const [error, setError] = useState<Error | undefined>(undefined);\n const client = useAttachments();\n\n const preprocess = useCallback(\n async (file: Blob): Promise<PreprocessResult> => {\n if (!client) throw new Error(\"AttachmentClient not available\");\n setError(undefined);\n setStatus(UploadStatus.Hashing);\n try {\n return await client.preprocess(file);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setStatus(UploadStatus.Error);\n throw err;\n }\n },\n [client],\n );\n\n const upload = useCallback(\n async (results: PreprocessResult): Promise<void> => {\n if (!client) throw new Error(\"AttachmentClient not available\");\n setError(undefined);\n setStatus(UploadStatus.Uploading);\n setProgress(0);\n try {\n await client.reserve(results.options, (handle) =>\n handle.send(results.stream()),\n );\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setStatus(UploadStatus.Error);\n throw err;\n }\n setProgress(1);\n setStatus(UploadStatus.Done);\n },\n [client],\n );\n\n return { preprocess, upload, status, progress, error };\n}\n","import type { PGlite } from \"@electric-sql/pglite\";\nimport { REACTOR_SCHEMA } from \"@powerhousedao/reactor\";\n\nasync function dropTablesInSchema(pg: PGlite, schema: string): Promise<void> {\n await pg.exec(`\nDO $$\nDECLARE\n _schemaname text := '${schema}';\n _tablename text;\nBEGIN\n FOR _tablename IN SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = _schemaname LOOP\n RAISE INFO 'Dropping table %.%', _schemaname, _tablename;\n EXECUTE format('DROP TABLE %I.%I CASCADE;', _schemaname, _tablename);\n END LOOP;\n IF NOT FOUND THEN\n RAISE WARNING 'Schema % does not exist', _schemaname;\n END IF;\nEND $$;\n`);\n}\n\nexport async function truncateAllTables(\n pg: PGlite,\n schema: string = REACTOR_SCHEMA,\n): Promise<void> {\n await dropTablesInSchema(pg, schema);\n}\n\nexport async function dropAllReactorStorage(pg: PGlite): Promise<void> {\n await dropTablesInSchema(pg, REACTOR_SCHEMA);\n\n // legacy\n await dropTablesInSchema(pg, \"public\");\n}\n","import type { IReactorClient } from \"@powerhousedao/reactor\";\nimport { setDrives } from \"./hooks/drives.js\";\nimport { getDrives } from \"./utils/drives.js\";\n\nexport type ReactorDefaultDrivesConfig = {\n defaultDrivesUrl?: string[];\n};\n\nexport type RefreshReactorDataConfig = {\n debounceDelayMs?: number;\n immediateThresholdMs?: number;\n};\n\nconst DEFAULT_DEBOUNCE_DELAY_MS = 200;\nconst DEFAULT_IMMEDIATE_THRESHOLD_MS = 1000;\n\nasync function _refreshReactorData(reactor: IReactorClient) {\n const drives = await getDrives(reactor);\n\n setDrives(drives);\n}\n\nasync function _refreshReactorDataClient(reactor: IReactorClient | undefined) {\n if (!reactor) return;\n\n setDrives(await getDrives(reactor));\n}\n\nfunction createDebouncedRefreshReactorData(\n debounceDelayMs = DEFAULT_DEBOUNCE_DELAY_MS,\n immediateThresholdMs = DEFAULT_IMMEDIATE_THRESHOLD_MS,\n) {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n let lastRefreshTime = 0;\n\n return (reactor: IReactorClient, immediate = false) => {\n const now = Date.now();\n const timeSinceLastRefresh = now - lastRefreshTime;\n\n if (timeout !== null) {\n clearTimeout(timeout);\n }\n\n if (immediate || timeSinceLastRefresh >= immediateThresholdMs) {\n lastRefreshTime = now;\n return _refreshReactorData(reactor);\n }\n\n return new Promise<void>((resolve) => {\n timeout = setTimeout(() => {\n lastRefreshTime = Date.now();\n void _refreshReactorData(reactor).then(resolve);\n }, debounceDelayMs);\n });\n };\n}\n\nfunction createDebouncedRefreshReactorDataClient(\n debounceDelayMs = DEFAULT_DEBOUNCE_DELAY_MS,\n immediateThresholdMs = DEFAULT_IMMEDIATE_THRESHOLD_MS,\n) {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n let lastRefreshTime = 0;\n\n return (reactor: IReactorClient | undefined, immediate = false) => {\n const now = Date.now();\n const timeSinceLastRefresh = now - lastRefreshTime;\n\n if (timeout !== null) {\n clearTimeout(timeout);\n }\n\n if (immediate || timeSinceLastRefresh >= immediateThresholdMs) {\n lastRefreshTime = now;\n return _refreshReactorDataClient(reactor);\n }\n\n return new Promise<void>((resolve) => {\n timeout = setTimeout(() => {\n lastRefreshTime = Date.now();\n void _refreshReactorDataClient(reactor).then(resolve);\n }, debounceDelayMs);\n });\n };\n}\n\nexport const refreshReactorData = createDebouncedRefreshReactorData();\nexport const refreshReactorDataClient =\n createDebouncedRefreshReactorDataClient();\n","import type { Action } from \"@powerhousedao/shared/document-model\";\nimport type { TrackedAction } from \"./types.js\";\n\n/**\n * Tracks pending actions with their operation context (prevOpHash, prevOpIndex).\n * Actions are accumulated until flushed (on push).\n */\nexport class ActionTracker {\n private pending: TrackedAction[] = [];\n\n /** Track a new action with its operation context. */\n track(action: Action, prevOpHash: string, prevOpIndex: number): void {\n this.pending.push({ action, prevOpHash, prevOpIndex });\n }\n\n /** Flush all pending actions and return them. Clears the internal queue. */\n flush(): TrackedAction[] {\n const actions = this.pending;\n this.pending = [];\n return actions;\n }\n\n /** Number of pending actions. */\n get count(): number {\n return this.pending.length;\n }\n\n /** Prepend previously flushed actions back to the queue (for retry on failure). */\n restore(actions: TrackedAction[]): void {\n this.pending = [...actions, ...this.pending];\n }\n\n /** Clear all pending actions without returning them. */\n clear(): void {\n this.pending = [];\n }\n}\n","import type {\n GetDocumentResult,\n GetDocumentWithOperationsResult,\n GetOperationsResult,\n IRemoteClient,\n PropagationMode,\n RemoteControllerGraphQLClient,\n RemoteDocumentData,\n RemoteOperation,\n RemoteOperationResultPage,\n} from \"./types.js\";\n\n/**\n * Thin facade over the GraphQL SDK for remote document operations.\n */\nconst DEFAULT_PAGE_SIZE = 100;\n\nexport class RemoteClient implements IRemoteClient {\n private readonly pageSize: number;\n\n constructor(\n private readonly client: RemoteControllerGraphQLClient,\n pageSize?: number,\n ) {\n this.pageSize = pageSize ?? DEFAULT_PAGE_SIZE;\n }\n\n /** Fetch a document by identifier. Returns null if not found. */\n async getDocument(\n identifier: string,\n branch?: string,\n ): Promise<GetDocumentResult | null> {\n const result = await this.client.GetDocument({\n identifier,\n view: branch ? { branch } : undefined,\n });\n return result.document ?? null;\n }\n\n /**\n * Fetch a document and its operations.\n *\n * When scopes are provided and BatchGetDocumentWithOperations is available,\n * fetches the document and per-scope operations in a single HTTP request.\n * Otherwise falls back to GetDocumentWithOperations for the first page,\n * then paginates remaining operations per scope.\n */\n async getDocumentWithOperations(\n identifier: string,\n branch?: string,\n sinceRevision?: Record<string, number>,\n scopes?: string[],\n ): Promise<GetDocumentWithOperationsResult | null> {\n // Fast path: batch document + per-scope operations in one request\n if (\n this.client.BatchGetDocumentWithOperations &&\n scopes &&\n scopes.length > 0\n ) {\n return this.batchGetDocumentWithOperations(\n identifier,\n branch,\n sinceRevision,\n scopes,\n );\n }\n\n // Standard path: GetDocumentWithOperations + paginate if needed\n const result = await this.client.GetDocumentWithOperations({\n identifier,\n view: branch ? { branch } : undefined,\n operationsPaging: {\n limit: this.pageSize,\n cursor: null,\n },\n });\n\n if (!result.document) return null;\n\n const doc = result.document.document;\n const opsPage = doc.operations;\n const operationsByScope: Record<string, RemoteOperation[]> = {};\n\n if (opsPage) {\n for (const op of opsPage.items) {\n (operationsByScope[op.action.scope] ??= []).push(op);\n }\n }\n\n // Check if we have all expected operations by comparing against revisionsList\n const expectedTotal = doc.revisionsList.reduce(\n (sum, r) => sum + r.revision,\n 0,\n );\n const fetchedTotal = opsPage?.items.length ?? 0;\n\n if (fetchedTotal >= expectedTotal) {\n return {\n document: doc,\n childIds: result.document.childIds,\n operations: { operationsByScope },\n };\n }\n\n // Missing operations — fetch all per scope\n const allScopes = doc.revisionsList.map((r) => r.scope);\n const allOps = await this.getAllOperations(\n doc.id,\n branch,\n sinceRevision,\n allScopes,\n );\n\n return {\n document: doc,\n childIds: result.document.childIds,\n operations: allOps,\n };\n }\n\n /**\n * Fetch document + per-scope operations in a single HTTP request\n * via BatchGetDocumentWithOperations, then paginate any remaining pages.\n */\n private async batchGetDocumentWithOperations(\n identifier: string,\n branch: string | undefined,\n sinceRevision: Record<string, number> | undefined,\n scopes: string[],\n ): Promise<GetDocumentWithOperationsResult | null> {\n const view = branch ? { branch } : undefined;\n const filters = scopes.map((scope) => ({\n documentId: identifier,\n branch: branch ?? null,\n sinceRevision: sinceRevision?.[scope] ?? 0,\n scopes: [scope],\n }));\n const pagings = scopes.map(() => ({\n limit: this.pageSize,\n cursor: null as string | null,\n }));\n\n const result = await this.client.BatchGetDocumentWithOperations!(\n identifier,\n view,\n filters,\n pagings,\n );\n\n if (!result.document) return null;\n\n const operationsByScope: Record<string, RemoteOperation[]> = {};\n let pending: {\n scope: string;\n filter: (typeof filters)[0];\n cursor: string;\n }[] = [];\n\n for (let i = 0; i < scopes.length; i++) {\n const page = result.operations[i];\n for (const op of page.items) {\n (operationsByScope[op.action.scope] ??= []).push(op);\n }\n if (page.hasNextPage && page.cursor) {\n pending.push({\n scope: scopes[i],\n filter: filters[i],\n cursor: page.cursor,\n });\n }\n }\n\n // Continue pagination for scopes with more pages\n while (pending.length > 0) {\n const pages = await this.fetchOperationPages(\n pending.map((p) => p.filter),\n pending.map((p) => ({ limit: this.pageSize, cursor: p.cursor })),\n );\n\n const nextPending: typeof pending = [];\n for (let i = 0; i < pending.length; i++) {\n const page = pages[i];\n for (const op of page.items) {\n (operationsByScope[op.action.scope] ??= []).push(op);\n }\n if (page.hasNextPage && page.cursor) {\n nextPending.push({ ...pending[i], cursor: page.cursor });\n }\n }\n pending = nextPending;\n }\n\n return {\n document: result.document.document,\n childIds: result.document.childIds,\n operations: { operationsByScope },\n };\n }\n\n /**\n * Fetch all operations for a document, paginating through all pages.\n * Each scope is queried individually because the API only returns\n * pagination cursors for single-scope queries.\n */\n async getAllOperations(\n documentId: string,\n branch?: string,\n sinceRevision?: Record<string, number>,\n scopes?: string[],\n ): Promise<GetOperationsResult> {\n // When scopes are specified, query each scope in parallel.\n // Uses a single composed request per pagination round when available.\n if (scopes && scopes.length > 0) {\n const operationsByScope: Record<string, RemoteOperation[]> = {};\n\n // Tracks scopes still being paginated, each with its own filter and cursor\n let pending = scopes.map((scope) => ({\n scope,\n filter: {\n documentId,\n branch: branch ?? null,\n sinceRevision: sinceRevision?.[scope] ?? 0,\n scopes: [scope],\n },\n cursor: null as string | null,\n }));\n\n while (pending.length > 0) {\n const pages = await this.fetchOperationPages(\n pending.map((p) => p.filter),\n pending.map((p) => ({ limit: this.pageSize, cursor: p.cursor })),\n );\n\n const nextPending: typeof pending = [];\n\n for (let i = 0; i < pending.length; i++) {\n const page = pages[i];\n for (const op of page.items) {\n (operationsByScope[op.action.scope] ??= []).push(op);\n }\n if (page.hasNextPage && page.cursor) {\n nextPending.push({ ...pending[i], cursor: page.cursor });\n }\n }\n\n pending = nextPending;\n }\n\n return { operationsByScope };\n }\n\n // No scopes specified — single query for all scopes (no per-scope sinceRevision)\n return this.fetchOperationsForScope(documentId, branch);\n }\n\n /**\n * Fetch one page of operations per filter.\n * Uses the composed query (single HTTP request) when available,\n * otherwise falls back to parallel individual requests.\n */\n private async fetchOperationPages(\n filters: Parameters<\n RemoteControllerGraphQLClient[\"GetDocumentOperations\"]\n >[0][\"filter\"][],\n pagings: Parameters<\n RemoteControllerGraphQLClient[\"GetDocumentOperations\"]\n >[0][\"paging\"][],\n ): Promise<RemoteOperationResultPage[]> {\n if (this.client.BatchGetDocumentOperations) {\n return this.client.BatchGetDocumentOperations(filters, pagings);\n }\n\n return Promise.all(\n filters.map((filter, i) =>\n this.client\n .GetDocumentOperations({ filter, paging: pagings[i] })\n .then((r) => r.documentOperations),\n ),\n );\n }\n\n /** Fetch all pages of operations for a single scope (or all scopes if none specified). */\n private async fetchOperationsForScope(\n documentId: string,\n branch?: string,\n sinceRevision?: number,\n scope?: string,\n ): Promise<GetOperationsResult> {\n const operationsByScope: Record<string, RemoteOperation[]> = {};\n let cursor: string | null | undefined;\n let hasNextPage = true;\n\n while (hasNextPage) {\n const result = await this.client.GetDocumentOperations({\n filter: {\n documentId,\n branch: branch ?? null,\n sinceRevision: sinceRevision ?? 0,\n scopes: scope ? [scope] : null,\n },\n paging: {\n limit: this.pageSize,\n cursor: cursor ?? null,\n },\n });\n\n const page = result.documentOperations;\n\n for (const op of page.items) {\n const s = op.action.scope;\n (operationsByScope[s] ??= []).push(op);\n }\n\n hasNextPage = page.hasNextPage;\n cursor = page.cursor;\n }\n\n return { operationsByScope };\n }\n\n /** Push actions to an existing document via MutateDocument. */\n async pushActions(\n documentIdentifier: string,\n actions: ReadonlyArray<NonNullable<unknown>>,\n branch?: string,\n ): Promise<RemoteDocumentData> {\n const result = await this.client.MutateDocument({\n documentIdentifier,\n actions,\n view: branch ? { branch } : undefined,\n });\n return result.mutateDocument;\n }\n\n /** Create a new document on the remote. */\n async createDocument(\n document: NonNullable<unknown>,\n parentIdentifier?: string,\n ): Promise<RemoteDocumentData> {\n const result = await this.client.CreateDocument({\n document,\n parentIdentifier: parentIdentifier ?? null,\n });\n return result.createDocument;\n }\n\n /** Create an empty document of a given type on the remote. */\n async createEmptyDocument(\n documentType: string,\n parentIdentifier?: string,\n ): Promise<RemoteDocumentData> {\n const result = await this.client.CreateEmptyDocument({\n documentType,\n parentIdentifier: parentIdentifier ?? null,\n });\n return result.createEmptyDocument;\n }\n\n /** Delete a document on the remote. Returns true if successful. */\n async deleteDocument(\n identifier: string,\n propagate?: PropagationMode,\n ): Promise<boolean> {\n const result = await this.client.DeleteDocument({\n identifier,\n propagate,\n });\n return result.deleteDocument;\n }\n}\n","import type {\n Action,\n DocumentOperations,\n Operation,\n PHBaseState,\n PHDocument,\n PHDocumentHeader,\n} from \"@powerhousedao/shared/document-model\";\nimport type { PHDocumentController } from \"document-model\";\nimport { ActionTracker } from \"./action-tracker.js\";\nimport { RemoteClient } from \"./remote-client.js\";\nimport type {\n ConflictStrategy,\n DocumentChangeListener,\n IRemoteClient,\n IRemoteController,\n PropagationMode,\n PushResult,\n RemoteControllerOptions,\n RemoteDocumentChangeEvent,\n RemoteDocumentData,\n RemoteOperation,\n SyncStatus,\n TrackedAction,\n} from \"./types.js\";\nimport {\n ConflictError,\n buildPulledDocument,\n convertRemoteOperations,\n extractRevisionMap,\n hasRevisionConflict,\n screamingSnakeToCamel,\n} from \"./utils.js\";\n\n/** Extract TState from a PHDocumentController subclass. */\ntype InferState<C> = C extends PHDocumentController<infer S> ? S : never;\n\n/**\n * Extract action methods from a controller type.\n * These are the dynamically-added methods (not on the base PHDocumentController prototype).\n */\ntype ActionMethodsOf<C, TRemote> = {\n [K in Exclude<keyof C, keyof PHDocumentController<any>>]: C[K] extends (\n input: infer I,\n ) => unknown\n ? (input: I) => TRemote & ActionMethodsOf<C, TRemote>\n : C[K];\n};\n\n/** The full return type: RemoteDocumentController + action methods. */\nexport type RemoteDocumentControllerWith<C extends PHDocumentController<any>> =\n RemoteDocumentController<C> & ActionMethodsOf<C, RemoteDocumentController<C>>;\n\n/**\n * A controller that wraps a PHDocumentController with remote push/pull capabilities.\n * Composes a local controller and adds GraphQL-based sync with a reactor server.\n */\nexport class RemoteDocumentController<\n TController extends PHDocumentController<any>,\n> implements IRemoteController<InferState<TController>> {\n private inner: TController;\n private readonly remoteClient: IRemoteClient;\n private readonly tracker = new ActionTracker();\n private readonly options: RemoteControllerOptions;\n private documentId: string;\n private remoteRevision: Record<string, number> = {};\n private hasPulled = false;\n private pushScheduled = false;\n private pushQueue: Promise<void> = Promise.resolve();\n private listeners: DocumentChangeListener[] = [];\n\n private constructor(inner: TController, options: RemoteControllerOptions) {\n this.inner = inner;\n this.options = options;\n this.documentId = options.documentId ?? \"\";\n this.remoteClient = new RemoteClient(\n options.client,\n options.operationsPageSize,\n );\n\n this.setupActionInterceptors();\n }\n\n // --- State access (delegated to inner controller) ---\n\n get header(): PHDocumentHeader {\n return this.inner.header;\n }\n\n get state(): InferState<TController> {\n return this.inner.state as InferState<TController>;\n }\n\n get operations(): DocumentOperations {\n return this.inner.operations;\n }\n\n get document(): PHDocument<InferState<TController>> {\n return this.inner.document as PHDocument<InferState<TController>>;\n }\n\n get status(): SyncStatus {\n return {\n pendingActionCount: this.tracker.count,\n connected: this.documentId !== \"\",\n documentId: this.documentId,\n remoteRevision: { ...this.remoteRevision },\n };\n }\n\n /** Register a listener for document changes. Returns an unsubscribe function. */\n onChange(listener: DocumentChangeListener): () => void {\n this.listeners.push(listener);\n return () => {\n this.listeners = this.listeners.filter((l) => l !== listener);\n };\n }\n\n private notifyListeners(source: RemoteDocumentChangeEvent[\"source\"]): void {\n if (this.listeners.length === 0) return;\n const event: RemoteDocumentChangeEvent = {\n source,\n document: this.document,\n };\n for (const listener of this.listeners) {\n listener(event);\n }\n }\n\n // --- Remote operations ---\n\n /** Push all pending actions to remote, then pull latest state. */\n async push(): Promise<PushResult> {\n let tracked = this.tracker.flush();\n\n if (tracked.length === 0 && this.documentId !== \"\") {\n // Nothing to push, just pull (reuses the fetched document)\n const remoteDocument = await this.pull();\n return {\n remoteDocument,\n actionCount: 0,\n operations: [],\n };\n }\n\n try {\n await this.ensureRemoteDocument();\n\n // Conflict detection: check if remote has changed since last pull\n if (this.options.onConflict && tracked.length > 0) {\n tracked = await this.handleConflicts(tracked, this.options.onConflict);\n }\n } catch (error) {\n // Pre-push failure: restore actions so they can be retried\n this.tracker.restore(tracked);\n throw error;\n }\n\n let pushedActions: Action[] = [];\n\n try {\n if (tracked.length > 0) {\n const actions = await this.prepareActionsForPush(tracked);\n pushedActions = actions;\n\n await this.remoteClient.pushActions(\n this.documentId,\n actions,\n this.options.branch,\n );\n }\n } catch (error) {\n // Push failed: restore actions so they can be retried\n this.tracker.restore(tracked);\n throw error;\n }\n\n // Pull remote state to reconcile (remote is source of truth).\n // If this fails, actions were already pushed — do NOT restore them.\n const remoteDocument = await this.pull();\n\n return {\n remoteDocument,\n actionCount: tracked.length,\n operations: pushedActions,\n };\n }\n\n /** Delete the document on the remote. */\n async delete(propagate?: PropagationMode): Promise<boolean> {\n if (this.documentId === \"\") {\n throw new Error(\"Cannot delete: no document ID set\");\n }\n const result = await this.remoteClient.deleteDocument(\n this.documentId,\n propagate,\n );\n return result;\n }\n\n /** Pull latest state from remote, replacing local document. Returns the remote document data. */\n async pull(): Promise<RemoteDocumentData> {\n if (this.documentId === \"\") {\n throw new Error(\"Cannot pull: no document ID set\");\n }\n\n const { remoteDoc, operations } = await this.fetchDocumentAndOperations();\n\n // Get module from inner controller\n const initialDoc = this.inner.module.utils.createDocument();\n const pulledDocument = buildPulledDocument(\n remoteDoc,\n operations,\n initialDoc,\n this.options.branch ?? \"main\",\n );\n\n // Recreate inner controller with pulled document\n const ControllerClass = this.inner.constructor as new (\n doc?: PHDocument<PHBaseState>,\n ) => TController;\n this.inner = new ControllerClass(pulledDocument);\n\n // Re-setup interceptors on the new inner instance\n this.setupActionInterceptors();\n\n // Clear tracker (remote is source of truth)\n this.tracker.clear();\n\n // Update remote revision\n this.remoteRevision = extractRevisionMap(remoteDoc.revisionsList);\n\n this.notifyListeners(\"pull\");\n\n return remoteDoc;\n }\n\n // --- Static factories ---\n\n /**\n * Pull an existing document from remote and create a controller for it.\n */\n static async pull<C extends PHDocumentController<any>>(\n ControllerClass: new (doc?: PHDocument<any>) => C,\n options: RemoteControllerOptions,\n ): Promise<RemoteDocumentControllerWith<C>> {\n // Create a temporary instance to access the module\n const temp = new ControllerClass();\n const remote = new RemoteDocumentController(temp, options);\n\n if (options.documentId) {\n await remote.pull();\n }\n\n return remote as RemoteDocumentControllerWith<C>;\n }\n\n /**\n * Wrap an existing controller instance with remote capabilities.\n * Pending local actions on the inner controller are NOT tracked\n * (only new actions through the remote controller are tracked).\n */\n static from<C extends PHDocumentController<any>>(\n controller: C,\n options: RemoteControllerOptions,\n ): RemoteDocumentControllerWith<C> {\n return new RemoteDocumentController(\n controller,\n options,\n ) as RemoteDocumentControllerWith<C>;\n }\n\n // --- Private methods ---\n\n /** Create the document on the remote if it doesn't exist yet. */\n private async ensureRemoteDocument(): Promise<void> {\n if (this.documentId !== \"\") return;\n const remoteDoc = await this.remoteClient.createEmptyDocument(\n this.inner.header.documentType,\n this.options.parentIdentifier,\n );\n this.documentId = remoteDoc.id;\n }\n\n /** Set up interceptors for all action methods on the inner controller. */\n private setupActionInterceptors(): void {\n // Get the module's action keys from the inner controller\n const module = (this.inner as Record<string, unknown>)[\"module\"] as {\n actions: Record<string, unknown>;\n };\n\n for (const actionType in module.actions) {\n // Skip if it's a property on our own class\n if (actionType in RemoteDocumentController.prototype) {\n continue;\n }\n\n Object.defineProperty(this, actionType, {\n value: (input: unknown) => {\n // Snapshot operation counts per scope BEFORE applying\n const opCountsBefore: Record<string, number> = {};\n for (const scope in this.inner.operations) {\n opCountsBefore[scope] = this.inner.operations[scope].length;\n }\n\n // Apply locally via inner controller\n (\n this.inner as unknown as Record<string, (input: unknown) => unknown>\n )[actionType](input);\n\n // Find which scope got the new operation\n const newOp = this.findNewOperation(opCountsBefore);\n\n // Get prevOp in the SAME scope as the new operation\n const prevOp = newOp\n ? this.getLastOperationInScope(newOp.action.scope, newOp)\n : undefined;\n const prevOpHash = prevOp?.hash ?? \"\";\n const prevOpIndex = prevOp?.index ?? -1;\n\n if (!newOp) {\n // Action produced no operation (NOOP) — nothing to track\n return this;\n }\n\n // Track the action for push\n this.tracker.track(newOp.action, prevOpHash, prevOpIndex);\n this.notifyListeners(\"action\");\n\n if (this.options.mode === \"streaming\") {\n this.schedulePush();\n }\n\n return this;\n },\n enumerable: true,\n configurable: true,\n });\n }\n }\n\n /**\n * Find the new operation added after applying an action,\n * by comparing current operation counts against a previous snapshot.\n */\n private findNewOperation(\n opCountsBefore: Record<string, number>,\n ): Operation | undefined {\n const ops = this.inner.operations;\n for (const scope in ops) {\n const scopeOps = ops[scope];\n const prevCount = opCountsBefore[scope] ?? 0;\n if (scopeOps.length > prevCount) {\n return scopeOps[scopeOps.length - 1];\n }\n }\n return undefined;\n }\n\n /**\n * Get the last operation in a specific scope, optionally excluding\n * a given operation (e.g. the one just added).\n */\n private getLastOperationInScope(\n scope: string,\n excludeOp?: Operation,\n ): Operation | undefined {\n const scopeOps = this.inner.operations[scope];\n if (scopeOps.length === 0) return undefined;\n for (let i = scopeOps.length - 1; i >= 0; i--) {\n if (scopeOps[i] !== excludeOp) return scopeOps[i];\n }\n return undefined;\n }\n\n /**\n * Detect and handle conflicts between local pending actions and remote state.\n * Returns the (possibly rebased) tracked actions to push.\n */\n private async handleConflicts(\n localTracked: TrackedAction[],\n strategy: ConflictStrategy,\n ): Promise<TrackedAction[]> {\n // Fetch current remote document to get latest revisions\n const remoteResult = await this.remoteClient.getDocument(\n this.documentId,\n this.options.branch,\n );\n if (!remoteResult) {\n throw new Error(`Document \"${this.documentId}\" not found on remote`);\n }\n\n const currentRevision = extractRevisionMap(\n remoteResult.document.revisionsList,\n );\n\n // Only check scopes that local actions touch\n const localScopes = new Set(localTracked.map((t) => t.action.scope));\n\n if (\n !hasRevisionConflict(currentRevision, this.remoteRevision, localScopes)\n ) {\n return localTracked;\n }\n\n // Fetch new remote operations for conflicting scopes in parallel,\n // using the correct sinceRevision for each scope.\n const conflictingScopes = [...localScopes].filter(\n (scope) =>\n (currentRevision[scope] ?? 0) > (this.remoteRevision[scope] ?? 0),\n );\n const { operationsByScope } = await this.remoteClient.getAllOperations(\n this.documentId,\n this.options.branch,\n this.remoteRevision,\n conflictingScopes,\n );\n const remoteOperations: Record<string, RemoteOperation[]> = {};\n for (const [scope, ops] of Object.entries(operationsByScope)) {\n remoteOperations[scope] = ops;\n }\n\n const conflictInfo = {\n remoteOperations,\n localActions: localTracked,\n knownRevision: { ...this.remoteRevision },\n currentRevision: { ...currentRevision },\n };\n\n if (strategy === \"reject\") {\n throw new ConflictError(conflictInfo);\n }\n\n if (strategy === \"rebase\") {\n return this.pullAndReplay(localTracked.map((t) => t.action));\n }\n\n // Custom merge handler (only possibility left after narrowing)\n const mergedActions = await strategy(conflictInfo);\n return this.pullAndReplay(mergedActions);\n }\n\n /**\n * Pull latest remote state and replay actions through interceptors.\n * Returns newly tracked actions with correct prevOpHash values.\n */\n private async pullAndReplay(actions: Action[]): Promise<TrackedAction[]> {\n await this.pull();\n\n for (const action of actions) {\n // Action types are SCREAMING_SNAKE_CASE but interceptors use camelCase\n const methodName = screamingSnakeToCamel(action.type);\n const method = (\n this as unknown as Record<string, (input: unknown) => unknown>\n )[methodName];\n if (typeof method === \"function\") {\n method.call(this, action.input);\n }\n }\n\n return this.tracker.flush();\n }\n\n /** Prepare actions for push, optionally signing them. */\n private async prepareActionsForPush(\n tracked: { action: Action; prevOpHash: string; prevOpIndex: number }[],\n ) {\n const actions: Action[] = [];\n\n for (const { action, prevOpHash, prevOpIndex } of tracked) {\n let prepared: Action = {\n ...action,\n context: {\n ...action.context,\n prevOpHash,\n prevOpIndex,\n },\n };\n\n if (this.options.signer) {\n prepared = await this.signAction(prepared);\n }\n\n actions.push(prepared);\n }\n\n return actions;\n }\n\n /** Sign an action using the configured signer, preserving existing signatures. */\n private async signAction(action: Action): Promise<Action> {\n const signer = this.options.signer!;\n const signature = await signer.signAction(action);\n const existingSignatures = action.context?.signer?.signatures ?? [];\n return {\n ...action,\n context: {\n ...action.context,\n signer: {\n user: signer.user!,\n app: signer.app!,\n signatures: [...existingSignatures, signature],\n },\n },\n };\n }\n\n /**\n * Fetch document and operations from the remote.\n *\n * On the first pull, uses the combined document+operations query.\n * On subsequent pulls, fetches only new operations per scope using\n * sinceRevision, then merges with existing local operations.\n * Falls back to a full fetch if the merge produces a count mismatch.\n */\n private async fetchDocumentAndOperations(): Promise<{\n remoteDoc: RemoteDocumentData;\n operations: DocumentOperations;\n }> {\n // Incremental fetch: use sinceRevision per scope\n if (this.hasPulled) {\n return this.incrementalFetch();\n }\n\n // Initial fetch: combined document + operations query\n const result = await this.remoteClient.getDocumentWithOperations(\n this.documentId,\n this.options.branch,\n );\n\n if (!result) {\n throw new Error(`Document \"${this.documentId}\" not found on remote`);\n }\n\n this.hasPulled = true;\n return {\n remoteDoc: result.document,\n operations: convertRemoteOperations(result.operations.operationsByScope),\n };\n }\n\n /**\n * Incremental fetch: fetches the document and only new operations per scope\n * using sinceRevision in a single request when possible.\n * Falls back to a full fetch on count mismatch.\n */\n private async incrementalFetch(): Promise<{\n remoteDoc: RemoteDocumentData;\n operations: DocumentOperations;\n }> {\n const scopes = Object.keys(this.remoteRevision);\n\n const result = await this.remoteClient.getDocumentWithOperations(\n this.documentId,\n this.options.branch,\n this.remoteRevision,\n scopes.length > 0 ? scopes : undefined,\n );\n\n if (!result) {\n throw new Error(`Document \"${this.documentId}\" not found on remote`);\n }\n\n const remoteDoc = result.document;\n const expectedRevision = extractRevisionMap(remoteDoc.revisionsList);\n\n const newOps = convertRemoteOperations(result.operations.operationsByScope);\n const merged = this.mergeOperations(this.inner.operations, newOps);\n\n // Validate: merged operation counts must match remote revisions\n if (this.hasExpectedOperationCounts(merged, expectedRevision)) {\n return { remoteDoc, operations: merged };\n }\n\n // Mismatch — do a full fetch\n return this.fullFetch(remoteDoc);\n }\n\n /**\n * Full fetch fallback: fetches all operations from the beginning.\n * Used when an incremental fetch produces a count mismatch.\n */\n private async fullFetch(remoteDoc: RemoteDocumentData): Promise<{\n remoteDoc: RemoteDocumentData;\n operations: DocumentOperations;\n }> {\n const { operationsByScope } = await this.remoteClient.getAllOperations(\n this.documentId,\n this.options.branch,\n );\n\n return {\n remoteDoc,\n operations: convertRemoteOperations(operationsByScope),\n };\n }\n\n /**\n * Validate that the merged operations match the expected revision per scope.\n * Each scope's operation count should equal its revision number.\n */\n private hasExpectedOperationCounts(\n operations: DocumentOperations,\n expectedRevision: Record<string, number>,\n ): boolean {\n for (const [scope, revision] of Object.entries(expectedRevision)) {\n const opCount = scope in operations ? operations[scope].length : 0;\n if (opCount !== revision) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Merge existing local operations with newly fetched operations.\n * Appends new operations to existing ones per scope.\n */\n private mergeOperations(\n existingOps: DocumentOperations,\n newOps: DocumentOperations,\n ): DocumentOperations {\n const merged: DocumentOperations = {};\n\n // Copy existing operations\n for (const [scope, ops] of Object.entries(existingOps)) {\n if (ops.length > 0) {\n merged[scope] = [...ops];\n }\n }\n\n // Append new operations per scope\n for (const [scope, ops] of Object.entries(newOps)) {\n if (ops.length > 0) {\n (merged[scope] ??= []).push(...ops);\n }\n }\n\n return merged;\n }\n\n /** Schedule a push via microtask (for streaming mode coalescing). */\n private schedulePush(): void {\n if (this.pushScheduled) return;\n this.pushScheduled = true;\n queueMicrotask(() => {\n this.pushScheduled = false;\n // Chain onto the push queue to prevent concurrent pushes\n this.pushQueue = this.pushQueue.then(async () => {\n try {\n await this.push();\n } catch (error: unknown) {\n // Actions remain in tracker for retry\n this.options.onPushError?.(error);\n }\n });\n });\n }\n}\n","export abstract class BaseStorage<V> implements Iterable<[string, V]> {\n abstract get(key: string): V | undefined;\n abstract set(key: string, value: V): void;\n abstract delete(key: string): boolean;\n abstract has(key: string): boolean;\n abstract clear(): void;\n abstract entries(): IterableIterator<[string, V]>;\n abstract keys(): IterableIterator<string>;\n abstract values(): IterableIterator<V>;\n\n [Symbol.iterator](): IterableIterator<[string, V]> {\n return this.entries();\n }\n\n forEach(\n callback: (value: V, key: string, storage: BaseStorage<V>) => void,\n ): void {\n for (const [key, value] of this) {\n callback(value, key, this);\n }\n }\n}\n","import { BaseStorage } from \"./base-storage.js\";\n\nexport class BrowserLocalStorage<V> extends BaseStorage<V> {\n #namespace: string;\n #storage = window.localStorage;\n constructor(namespace: string) {\n super();\n this.#namespace = namespace;\n }\n\n #readMap(): Map<string, V> {\n const raw = this.#storage.getItem(this.#namespace);\n\n if (!raw) {\n return new Map();\n }\n\n return new Map(JSON.parse(raw) as [string, V][]);\n }\n\n #writeMap(map: Map<string, V>): void {\n this.#storage.setItem(\n this.#namespace,\n JSON.stringify(Array.from(map.entries())),\n );\n }\n\n get(key: string): V | undefined {\n return this.#readMap().get(key);\n }\n\n set(key: string, value: V): void {\n const map = this.#readMap();\n map.set(key, value);\n this.#writeMap(map);\n }\n\n delete(key: string): boolean {\n const map = this.#readMap();\n const deleted = map.delete(key);\n if (deleted) {\n this.#writeMap(map);\n }\n return deleted;\n }\n\n has(key: string): boolean {\n return this.#readMap().has(key);\n }\n\n clear(): void {\n this.#storage.removeItem(this.#namespace);\n }\n\n entries(): IterableIterator<[string, V]> {\n return this.#readMap().entries();\n }\n\n keys(): IterableIterator<string> {\n return this.#readMap().keys();\n }\n\n values(): IterableIterator<V> {\n return this.#readMap().values();\n }\n\n [Symbol.iterator](): IterableIterator<[string, V]> {\n return this.#readMap().entries();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["DocumentChangeType","DriveCollectionId","isDriveAuthError","PropagationMode","createSetDriveNameAction","createSetDriveIconAction","copyNode","moveNode","DriveCollectionId","DriveCollectionId","REACTOR_SCHEMA","#namespace","#storage","#readMap","#writeMap"],"sources":["../src/actions/drive.ts","../src/constants.ts","../src/graphql/adapters.ts","../src/graphql/constants.ts","../src/graphql/fetchers.ts","../src/graphql/batch.ts","../src/graphql/document-fetcher.ts","../src/graphql/graphql-client-document-cache.ts","../src/graphql/mutators.ts","../src/hooks/allowed-document-model-modules.ts","../src/hooks/selected-folder.ts","../src/hooks/child-nodes.ts","../src/hooks/config/set-config-by-key.ts","../src/hooks/config/utils.ts","../src/hooks/config/set-config-by-object.ts","../src/hooks/config/use-value-by-key.ts","../src/hooks/connection-state.ts","../src/hooks/document-of-type.ts","../src/hooks/supported-document-types.ts","../src/hooks/document-types.ts","../src/hooks/document-version-status.ts","../src/utils/validate-document.ts","../src/utils/download-document.ts","../src/hooks/download-document.ts","../src/hooks/drive-by-id.ts","../src/hooks/editor-modules.ts","../src/utils/preload-editor.ts","../src/hooks/use-editor-preloader.ts","../src/hooks/file-drag-and-drop.ts","../src/hooks/folder-by-id.ts","../src/graphql/events.ts","../src/graphql/document-cache-client-middleware.ts","../src/hooks/init-graphql-reactor-client.ts","../src/hooks/items-in-selected-folder.ts","../src/hooks/node-actions.ts","../src/hooks/node-by-id.ts","../src/hooks/node-path.ts","../src/hooks/parent-folder.ts","../src/hooks/selected-document.ts","../src/hooks/subgraph-modules.ts","../src/hooks/theme.ts","../src/hooks/use-drive-system-info.ts","../src/hooks/use-editor-file-drop.ts","../src/utils/drives.ts","../src/utils/get-revision-from-date.ts","../src/utils/switchboard.ts","../src/utils/upgrade-preview.ts","../src/hooks/use-get-switchboard-link.ts","../src/hooks/use-on-drop-file.ts","../src/hooks/user-permissions.ts","../src/hooks/use-attachments.ts","../src/pglite/drop.ts","../src/reactor.ts","../src/remote-controller/action-tracker.ts","../src/remote-controller/remote-client.ts","../src/remote-controller/remote-controller.ts","../src/storage/base-storage.ts","../src/storage/local-storage.ts"],"sourcesContent":["import {\n DocumentChangeType,\n DriveCollectionId,\n isDriveAuthError,\n PropagationMode,\n type IReactorClient,\n type PollBehavior,\n} from \"@powerhousedao/reactor\";\nimport {\n driveCreateDocument,\n setAvailableOffline,\n setDriveIcon as createSetDriveIconAction,\n setDriveName as createSetDriveNameAction,\n setSharingType,\n type DocumentDriveDocument,\n type DriveInput,\n type SharingType,\n} from \"@powerhousedao/shared/document-drive\";\nimport type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { getUserPermissions } from \"../utils/user.js\";\nimport { showPHModal } from \"../hooks/modals.js\";\n\nconst DEFAULT_INITIAL_SYNC_TIMEOUT_MS = 30_000;\n\n// In-flight remote registrations keyed by collectionId. sync.list()/sync.add()\n// is not atomic, so concurrent addRemoteDrive calls for the same drive would\n// both miss the existing remote and register duplicates. Concurrent callers\n// share the first registration instead.\nconst inFlightRemoteRegistrations = new Map<string, Promise<unknown>>();\n\nexport type AddRemoteDriveOptions = {\n pollBehavior?: PollBehavior;\n /**\n * When true, wait for the drive document to be materialized locally\n * (i.e. queryable via the reactor) before resolving. Without this,\n * `addRemoteDrive` returns as soon as the remote is registered with\n * the sync manager, before initial backfill delivers the drive.\n */\n awaitInitialSync?: boolean;\n /** Timeout for the initial-sync wait. Defaults to 30s. */\n initialSyncTimeoutMs?: number;\n signal?: AbortSignal;\n};\n\n/**\n * Resolves once a document with the given id is queryable through the\n * reactor client. Subscribes to Created events filtered by id and\n * short-circuits if the document already exists.\n */\nexport async function waitForDocumentReady(\n reactorClient: IReactorClient,\n documentId: string,\n options?: { timeoutMs?: number; signal?: AbortSignal },\n): Promise<void> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_INITIAL_SYNC_TIMEOUT_MS;\n const signal = options?.signal;\n\n return new Promise<void>((resolve, reject) => {\n let settled = false;\n // eslint-disable-next-line prefer-const\n let unsubscribe: (() => void) | undefined;\n // eslint-disable-next-line prefer-const\n let timer: ReturnType<typeof setTimeout> | undefined;\n let abortHandler: (() => void) | undefined;\n\n const settle = (action: () => void) => {\n if (settled) return;\n settled = true;\n unsubscribe?.();\n if (timer) clearTimeout(timer);\n if (abortHandler && signal) {\n signal.removeEventListener(\"abort\", abortHandler);\n }\n action();\n };\n\n unsubscribe = reactorClient.subscribe({ ids: [documentId] }, (event) => {\n if (event.type === DocumentChangeType.Created) {\n settle(() => resolve());\n }\n });\n\n reactorClient\n .find({ ids: [documentId] })\n .then((existing) => {\n if (existing.results.length > 0) {\n settle(() => resolve());\n }\n })\n .catch(() => {\n // Ignore: the subscription will still resolve if the document arrives.\n });\n\n if (signal) {\n if (signal.aborted) {\n settle(() => reject(new DOMException(\"Aborted\", \"AbortError\")));\n return;\n }\n abortHandler = () => {\n settle(() => reject(new DOMException(\"Aborted\", \"AbortError\")));\n };\n signal.addEventListener(\"abort\", abortHandler);\n }\n\n timer = setTimeout(() => {\n settle(() =>\n reject(\n new Error(\n `Timed out after ${timeoutMs}ms waiting for document ${documentId}`,\n ),\n ),\n );\n }, timeoutMs);\n });\n}\n\nexport async function addDrive(input: DriveInput, preferredEditor?: string) {\n const { isAllowedToCreateDocuments } = getUserPermissions();\n if (!isAllowedToCreateDocuments) {\n throw new Error(\"User is not allowed to create drives\");\n }\n\n const reactorClient = window.ph?.reactorClient;\n if (!reactorClient) {\n throw new Error(\"ReactorClient not initialized\");\n }\n\n const driveDoc = driveCreateDocument({\n global: {\n name: input.global.name || \"\",\n icon: input.global.icon ?? null,\n nodes: [],\n },\n });\n\n if (preferredEditor) {\n driveDoc.header.meta = { preferredEditor };\n }\n\n return await reactorClient.create<DocumentDriveDocument>(driveDoc);\n}\n\nexport async function addRemoteDrive(\n url: string,\n driveId?: string,\n options?: AddRemoteDriveOptions,\n) {\n // remote drives are a full reactor client feature (sync manager + find)\n const reactorClient = window.ph?.reactorClientModule?.client;\n if (!reactorClient) {\n throw new Error(\"ReactorClient not initialized\");\n }\n\n const sync =\n window.ph?.reactorClientModule?.reactorModule?.syncModule?.syncManager;\n if (!sync) {\n throw new Error(\"Sync not initialized\");\n }\n\n // Fetch drive info from the REST endpoint to get both id and graphqlEndpoint\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to resolve drive info from ${url}`);\n }\n const driveInfo = (await response.json()) as {\n id: string;\n graphqlEndpoint: string;\n };\n\n const resolvedDriveId = driveId ?? driveInfo.id;\n const collectionId = DriveCollectionId.forDrive(resolvedDriveId);\n\n const inFlight = inFlightRemoteRegistrations.get(collectionId.key);\n try {\n if (inFlight) {\n await inFlight;\n } else {\n const existingRemote = sync\n .list()\n .find((remote) => remote.meta.collectionId.equals(collectionId));\n\n if (!existingRemote) {\n const remoteName = crypto.randomUUID();\n const registration = sync\n .add(\n remoteName,\n collectionId,\n {\n type: \"gql\",\n parameters: {\n url: driveInfo.graphqlEndpoint,\n },\n },\n undefined,\n options?.pollBehavior\n ? { pollBehavior: options.pollBehavior }\n : undefined,\n )\n .finally(() => inFlightRemoteRegistrations.delete(collectionId.key));\n inFlightRemoteRegistrations.set(collectionId.key, registration);\n await registration;\n }\n }\n } catch (error) {\n // Any drive add that fails because the caller isn't authorized (the\n // switchboard rejected it — Forbidden/Unauthorized) prompts a login,\n // regardless of which flow triggered the add. Re-throw so callers still\n // see the failure.\n if (isDriveAuthError(error)) {\n showPHModal({ type: \"driveAuthRequired\" });\n }\n throw error;\n }\n\n if (options?.awaitInitialSync) {\n await waitForDocumentReady(reactorClient, resolvedDriveId, {\n timeoutMs: options.initialSyncTimeoutMs,\n signal: options.signal,\n });\n }\n\n return resolvedDriveId;\n}\n\nexport async function deleteDrive(driveId: string) {\n const { isAllowedToCreateDocuments } = getUserPermissions();\n if (!isAllowedToCreateDocuments) {\n throw new Error(\"User is not allowed to delete drives\");\n }\n\n const reactorClient = window.ph?.reactorClient;\n if (!reactorClient) {\n throw new Error(\"ReactorClient not initialized\");\n }\n\n const sync =\n window.ph?.reactorClientModule?.reactorModule?.syncModule?.syncManager;\n if (sync) {\n const collectionId = DriveCollectionId.forDrive(driveId);\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 await reactorClient.deleteDocument(driveId, PropagationMode.Cascade);\n}\n\nexport async function renameDrive(\n driveId: string,\n name: string,\n): Promise<PHDocument | undefined> {\n const { isAllowedToCreateDocuments } = getUserPermissions();\n if (!isAllowedToCreateDocuments) {\n throw new Error(\"User is not allowed to rename drives\");\n }\n\n // drive renaming is only available on the full reactor client\n const reactorClient = window.ph?.reactorClientModule?.client;\n if (!reactorClient) {\n throw new Error(\"ReactorClient not initialized\");\n }\n return await reactorClient.rename(driveId, name);\n}\n\nexport async function setDriveAvailableOffline(\n driveId: string,\n availableOffline: boolean,\n): Promise<PHDocument | undefined> {\n const { isAllowedToCreateDocuments } = getUserPermissions();\n if (!isAllowedToCreateDocuments) {\n throw new Error(\"User is not allowed to change drive availability\");\n }\n\n const reactorClient = window.ph?.reactorClient;\n if (!reactorClient) {\n throw new Error(\"ReactorClient not initialized\");\n }\n return await reactorClient.execute(driveId, \"main\", [\n setAvailableOffline({ availableOffline }),\n ]);\n}\n\nexport async function setDriveSharingType(\n driveId: string,\n sharingType: SharingType,\n): Promise<PHDocument | undefined> {\n const { isAllowedToCreateDocuments } = getUserPermissions();\n if (!isAllowedToCreateDocuments) {\n throw new Error(\"User is not allowed to change drive sharing type\");\n }\n\n const reactorClient = window.ph?.reactorClient;\n if (!reactorClient) {\n throw new Error(\"ReactorClient not initialized\");\n }\n return await reactorClient.execute(driveId, \"main\", [\n setSharingType({ type: sharingType }),\n ]);\n}\n\nexport async function setDriveMetadata(\n driveId: string,\n metadata: { name?: string | null; icon?: string | null },\n): Promise<PHDocument | undefined> {\n const { isAllowedToCreateDocuments } = getUserPermissions();\n if (!isAllowedToCreateDocuments) {\n throw new Error(\"User is not allowed to update drive metadata\");\n }\n\n const reactorClient = window.ph?.reactorClient;\n if (!reactorClient) {\n throw new Error(\"ReactorClient not initialized\");\n }\n\n const actions: Array<\n | ReturnType<typeof createSetDriveNameAction>\n | ReturnType<typeof createSetDriveIconAction>\n > = [];\n if (metadata.name) {\n actions.push(createSetDriveNameAction({ name: metadata.name }));\n }\n if (metadata.icon !== undefined && metadata.icon !== null) {\n actions.push(createSetDriveIconAction({ icon: metadata.icon }));\n }\n if (actions.length === 0) {\n return undefined;\n }\n\n return await reactorClient.execute(driveId, \"main\", actions);\n}\n","export const DEFAULT_DRIVE_EDITOR_ID = \"powerhouse/generic-drive-explorer\";\nexport const COMMON_PACKAGE_ID = \"powerhouse/common\";\n\n/** Document types that represent a \"drive\": a container of nodes. */\nexport const DRIVE_DOCUMENT_TYPES = [\n \"powerhouse/document-drive\",\n \"powerhouse/reactor-drive\",\n] as const;\n","import type {\n DocumentOperations,\n PHBaseState,\n PHDocument,\n PHDocumentHeader,\n} from \"document-model\";\nimport { map, pipe } from \"remeda\";\nimport { z } from \"zod\";\nimport type {\n FindDocumentsQuery,\n GetDocumentWithOperationsQuery,\n} from \"./gen/schema.js\";\nimport type { TStateSchemaZodObject } from \"./types.js\";\n\ntype QueryDocumentResult = NonNullable<\n GetDocumentWithOperationsQuery[\"document\"]\n>[\"document\"];\n\ntype FindDocumentsItems = NonNullable<\n FindDocumentsQuery[\"findDocuments\"]\n>[\"items\"];\n\nexport function phDocumentFromQuery<\n TDocumentSchema extends TStateSchemaZodObject,\n>(document: QueryDocumentResult, documentSchema?: TDocumentSchema) {\n const phDocument = {\n header: phDocumentHeaderFromQuery(document),\n state: phDocumentStateFromQuery(document),\n initialState: phDocumentStateFromQuery(document),\n operations:\n phDocumentOperationsFromGetDocumentWithOperationsQuery(document),\n clipboard: [],\n };\n if (documentSchema !== undefined) documentSchema.parse(phDocument);\n return phDocument as PHDocument;\n}\n\nexport function phDocumentsFromQuery<\n TDocumentSchema extends TStateSchemaZodObject,\n>(items: FindDocumentsItems, documentSchema?: TDocumentSchema) {\n const documents = pipe(\n items,\n map((document) => phDocumentFromQuery(document, documentSchema)),\n );\n return documents;\n}\n\nfunction phDocumentHeaderFromQuery(queryDocument: QueryDocumentResult) {\n const phDocumentHeader = {\n branch: \"main\",\n id: queryDocument.id,\n name: queryDocument.name,\n documentType: queryDocument.documentType,\n createdAtUtcIso:\n queryDocument.createdAtUtcIso instanceof Date\n ? queryDocument.createdAtUtcIso.toUTCString()\n : queryDocument.createdAtUtcIso,\n lastModifiedAtUtcIso:\n queryDocument.lastModifiedAtUtcIso instanceof Date\n ? queryDocument.lastModifiedAtUtcIso.toUTCString()\n : queryDocument.lastModifiedAtUtcIso,\n slug: queryDocument.slug ?? \"\",\n };\n return phDocumentHeader as PHDocumentHeader;\n}\n\nfunction phDocumentStateFromQuery<\n TDocumentSchema extends TStateSchemaZodObject,\n>(queryDocument: QueryDocumentResult, documentSchema?: TDocumentSchema) {\n if (documentSchema !== undefined)\n return documentSchema.shape.state.parse(queryDocument.state);\n return queryDocument.state as PHBaseState;\n}\n\nfunction phDocumentOperationsFromGetDocumentWithOperationsQuery(\n queryDocument: QueryDocumentResult,\n) {\n if (\n queryDocument.operations === null ||\n queryDocument.operations === undefined\n )\n return {\n global: [],\n };\n\n const documentOperations = {\n global: [...queryDocument.operations.items],\n };\n return documentOperations as DocumentOperations;\n}\nexport function identifierFromMutateDocumentOperationVariables(\n variables: unknown,\n) {\n return z\n .object({\n documentIdentifier: z.string(),\n })\n .parse(variables).documentIdentifier;\n}\n","export const DEFAULT_DRIVE_ID = \"powerhouse\" as const;\nexport const DEFAULT_SWITCHBOARD_URL = \"http://localhost:4001/graphql\" as const;\nexport const graphqlEventsToSyncDrive = [\n \"CreateEmptyDocument\",\n \"CreateDocument\",\n \"AddChildren\",\n \"RemoveChildren\",\n \"MoveChildren\",\n \"DeleteDocument\",\n \"DeleteDocuments\",\n] as const;\n\nexport const graphqlDocumentEvents = [\n \"MutateDocument\",\n \"MutateDocumentAsync\",\n \"DeleteDocument\",\n] as const;\n\nexport const graphqlDocumentsEvents = [\"DeleteDocuments\"] as const;\n","import { map } from \"remeda\";\nimport { phDocumentFromQuery } from \"./adapters.js\";\nimport type { TStateSchemaZodObject } from \"./types.js\";\n\nexport async function reactorGraphqlFetchDocument<\n TDocumentSchema extends TStateSchemaZodObject,\n>(identifier: string, documentSchema?: TDocumentSchema) {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n try {\n const result = await client.GetDocument({\n identifier,\n });\n const document = result.document?.document;\n if (!document) return undefined;\n return phDocumentFromQuery(document, documentSchema);\n } catch {\n return undefined;\n }\n}\n\nexport async function reactorGraphqlBatchFetchDocuments(\n identifiers: readonly string[],\n) {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n const promises = map(identifiers, (identifier) =>\n reactorGraphqlFetchDocument(identifier),\n );\n return await Promise.all(promises);\n}\n","import { funnel } from \"remeda\";\n\ntype PromiseCallbacks<Result> = Readonly<\n Parameters<ConstructorParameters<typeof Promise<Result>>[0]>\n>;\n\ntype BatchRequest<Params extends unknown[], Result> = {\n readonly params: Params;\n readonly promiseCallbacks: PromiseCallbacks<Result>;\n};\n\nexport type Batch<Params extends unknown[], Result> = {\n call: (...params: Params) => Promise<Result>;\n cancel: () => void;\n flush: () => void;\n readonly isIdle: boolean;\n};\n\n/**\n * A reference implementation for an async batching utility function built on\n * top of the `funnel` general purpose execution utility function. It will\n * accumulate all params passed to an async `call` method within the defined\n * burst duration and then use an async executor to process them in one\n * invocation. It then extracts an individual result for each call which is\n * used to resolve the original call.\n *\n * This allows synchronizing multiple async calls while keeping each call site\n * isolated from the rest (for example, as react components).\n *\n * This reference implementation can be copied into your project as-is, or you\n * can use it as the basis for a more complex implementation with additional\n * features.\n *\n * @param callback - The main function that takes a batch and returns an\n * aggregated response. The typing for the it's parameters will derive the\n * typing for the extractor and the `call` method.\n * @param extractor - A function that takes the aggregated response and extracts\n * from it the result for each individual call. The function is called with both\n * the index in the batch, and the params passed to the `call` method. This\n * allows handling APIs that return batch results as both objects and arrays.\n * @param maxBurstDurationMs - The period of time where the batcher would\n * collect params before triggering the executor. When set to 0 the batcher\n * does not incur any additional delays to the execution and would trigger at\n * the next tick, just like a regular async function would. This is also the\n * default value.\n * @returns A Funnel object with the `call` method augmented to support async\n * response.\n */\nexport function batch<Params extends unknown[], BatchResponse, Result>(\n callback: (requests: readonly Params[]) => Promise<BatchResponse>,\n extractor: (\n response: BatchResponse,\n index: number,\n ...params: Params\n ) => Result,\n maxBurstDurationMs = 0,\n): Batch<Params, Result> {\n const batchFunnel = funnel(\n (requests: readonly BatchRequest<Params, Result>[]) => {\n callback(requests.map(({ params }) => params))\n .then((response) => {\n for (const [\n index,\n {\n params,\n promiseCallbacks: [resolve, reject],\n },\n ] of requests.entries()) {\n try {\n const result = extractor(response, index, ...params);\n resolve(result);\n } catch (error) {\n reject(error);\n }\n }\n })\n .catch((error) => {\n for (const {\n promiseCallbacks: [, reject],\n } of requests) {\n reject(error);\n }\n });\n },\n {\n reducer: (\n requests: readonly BatchRequest<Params, Result>[] | undefined,\n request: BatchRequest<Params, Result>,\n ) => [...(requests ?? []), request],\n maxBurstDurationMs,\n triggerAt: \"end\",\n },\n );\n\n return {\n ...batchFunnel,\n\n call: (...params: Params) =>\n new Promise<Result>((...promiseCallbacks) => {\n batchFunnel.call({ promiseCallbacks, params });\n }),\n };\n}\n","import type { PHDocument } from \"document-model\";\nimport { filter, isTruthy, map, mapToObj, pipe, prop, unique } from \"remeda\";\nimport { type Batch, batch } from \"./batch.js\";\nimport { reactorGraphqlBatchFetchDocuments } from \"./fetchers.js\";\n\nfunction makeDocumentsById(documents: (PHDocument | undefined)[] = []) {\n return pipe(\n documents,\n filter(isTruthy),\n mapToObj((document) => [document.header.id, document]),\n );\n}\n\nexport class DocumentFetcher {\n private batchGetDocuments: Batch<[id: string], PHDocument>;\n\n constructor() {\n this.batchGetDocuments = batch(\n async (requests: readonly [id: string][]) => {\n const ids = unique(map(requests, ([id]) => id));\n const documents = await reactorGraphqlBatchFetchDocuments(ids);\n\n return makeDocumentsById(documents);\n },\n (documentsById, _, id) => {\n const document = prop(documentsById, id);\n return document;\n },\n );\n }\n\n get(id: string): Promise<PHDocument> {\n return this.batchGetDocuments.call(id);\n }\n\n getBatch(ids: string[]): Promise<PHDocument[]> {\n return Promise.all(map(ids, (id) => this.get(id)));\n }\n}\n","import type { PHDocument } from \"document-model\";\nimport { forEach } from \"remeda\";\nimport { addPromiseState, readPromiseState } from \"../document-cache.js\";\nimport type {\n FulfilledPromise,\n IDocumentCache,\n PromiseWithState,\n} from \"../types/documents.js\";\nimport { DocumentFetcher } from \"./document-fetcher.js\";\n\nexport class GraphQLClientDocumentCache implements IDocumentCache {\n private fetcher: DocumentFetcher;\n\n private documents = new Map<string, PromiseWithState<PHDocument>>();\n\n private batchPromises = new Map<\n string,\n {\n promises: readonly Promise<PHDocument>[];\n promise: PromiseWithState<PHDocument[]>;\n }\n >();\n\n private listeners = new Map<string, (() => void)[]>();\n\n constructor() {\n this.fetcher = new DocumentFetcher();\n\n window.addEventListener(\"MutateDocument\", (event) => {\n this.handleDocumentMutated(event.detail.identifier).catch(console.error);\n });\n\n window.addEventListener(\"MutateDocumentAsync\", (event) => {\n this.handleDocumentMutated(event.detail.identifier).catch(console.error);\n });\n }\n\n get(id: string, refetch?: boolean): Promise<PHDocument> {\n const current = this.documents.get(id);\n\n if (current) {\n if (current.status === \"pending\") {\n return current;\n }\n\n if (!refetch) {\n return current;\n }\n }\n\n const promise = addPromiseState(\n this.fetcher.get(id).then((document) => {\n this.invalidateBatchesContaining(id);\n return document;\n }),\n );\n\n this.documents.set(id, promise);\n\n return promise;\n }\n\n getBatch(ids: string[]): Promise<PHDocument[]> {\n const key = ids.join(\",\");\n const cached = this.batchPromises.get(key);\n\n const currentPromises = ids.map((id) => this.get(id));\n\n if (cached) {\n const samePromises = currentPromises.every(\n (promise, index) => promise === cached.promises[index],\n );\n\n if (samePromises) {\n return cached.promise;\n }\n }\n\n const states = currentPromises.map((promise) =>\n readPromiseState(promise as PromiseWithState<PHDocument>),\n );\n\n const allSettled = states.every((state) => state.status !== \"pending\");\n\n if (allSettled) {\n const values = states\n .filter(\n (state): state is { status: \"fulfilled\"; value: PHDocument } =>\n state.status === \"fulfilled\",\n )\n .map((state) => state.value);\n\n const batchPromise = Promise.resolve(values) as PromiseWithState<\n PHDocument[]\n >;\n\n batchPromise.status = \"fulfilled\";\n (batchPromise as FulfilledPromise<PHDocument[]>).value = values;\n\n this.batchPromises.set(key, {\n promises: currentPromises,\n promise: batchPromise,\n });\n\n return batchPromise;\n }\n\n const batchPromise = addPromiseState(\n Promise.allSettled(currentPromises).then((results) => {\n const documents: PHDocument[] = [];\n for (const result of results) {\n if (result.status === \"fulfilled\") {\n documents.push(result.value);\n } else {\n console.warn(\n \"[GraphQLClientDocumentCache] Skipped unavailable document:\",\n result.reason,\n );\n }\n }\n return documents;\n }),\n );\n\n this.batchPromises.set(key, {\n promises: currentPromises,\n promise: batchPromise,\n });\n\n return batchPromise;\n }\n\n private invalidateBatchesContaining(documentId: string): void {\n for (const key of this.batchPromises.keys()) {\n if (key.split(\",\").includes(documentId)) {\n this.batchPromises.delete(key);\n }\n }\n }\n\n subscribe(id: string | string[], callback: () => void): () => void {\n const ids = Array.isArray(id) ? id : [id];\n\n for (const documentId of ids) {\n const listeners = this.listeners.get(documentId) ?? [];\n this.listeners.set(documentId, [...listeners, callback]);\n }\n\n return () => {\n for (const documentId of ids) {\n const listeners = this.listeners.get(documentId) ?? [];\n this.listeners.set(\n documentId,\n listeners.filter((listener) => listener !== callback),\n );\n }\n };\n }\n\n private notify(id: string): void {\n const listeners = this.listeners.get(id) ?? [];\n\n for (const listener of listeners) {\n listener();\n }\n }\n\n private async handleDocumentMutated(id: string) {\n this.invalidateBatchesContaining(id);\n await this.get(id);\n this.notify(id);\n }\n\n private handleDocumentDeleted(id: string) {\n this.documents.delete(id);\n this.invalidateBatchesContaining(id);\n this.notify(id);\n }\n\n private handleDocumentsDeleted(ids: string[]) {\n forEach(ids, (id) => this.handleDocumentDeleted(id));\n }\n}\n","import type { PHDocument } from \"document-model\";\nimport { DEFAULT_DRIVE_ID } from \"./constants.js\";\nimport type { Scalars } from \"./gen/schema.js\";\n\nexport async function reactorGraphqlCreateDocument<\n TDocument extends PHDocument,\n>(document: TDocument, parentIdentifier = DEFAULT_DRIVE_ID) {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n\n const result = await client.CreateDocument({\n document,\n parentIdentifier,\n });\n\n return result;\n}\n\nexport async function reactorGraphqlDeleteDocument(identifier: string) {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n\n const result = await client.DeleteDocument({\n identifier,\n });\n\n return result;\n}\n\nexport async function reactorGraphqlDeleteDocuments(identifiers: string[]) {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n\n const result = await client.DeleteDocuments({\n identifiers,\n });\n\n return result;\n}\n\nexport async function reactorGraphqlMutateDocument(\n documentIdentifier: string,\n ...actions: ReadonlyArray<Scalars[\"JSONObject\"][\"input\"]>\n) {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n\n const result = await client.MutateDocument({\n documentIdentifier,\n actions,\n });\n\n return result;\n}\n","import { useAllowedDocumentTypes } from \"./config/editor.js\";\nimport { useDocumentModelModules } from \"./document-model-modules.js\";\n\nexport function useAllowedDocumentModelModules() {\n const documentModelModules = useDocumentModelModules();\n const allowedDocumentTypes = useAllowedDocumentTypes();\n if (!allowedDocumentTypes?.length) return documentModelModules;\n return documentModelModules?.filter((module) =>\n allowedDocumentTypes.includes(module.documentModel.global.id),\n );\n}\n","import type { FolderNode } from \"@powerhousedao/shared/document-drive\";\nimport { isFolderNodeKind } from \"../utils/nodes.js\";\nimport { useSelectedNode } from \"./selected-node.js\";\n\n/** Returns the selected folder. */\nexport function useSelectedFolder(): FolderNode | undefined {\n const selectedNode = useSelectedNode();\n if (isFolderNodeKind(selectedNode)) return selectedNode;\n return undefined;\n}\n","import type { Node } from \"@powerhousedao/shared/document-drive\";\nimport { sortNodesByName } from \"../utils/nodes.js\";\nimport { useNodesInSelectedDrive } from \"./items-in-selected-drive.js\";\nimport { useSelectedFolder } from \"./selected-folder.js\";\n\n/** Returns the child nodes for the selected drive or folder. */\nexport function useNodesInSelectedDriveOrFolder(): Node[] {\n const nodes = useNodesInSelectedDrive();\n const selectedFolder = useSelectedFolder();\n const selectedFolderId = selectedFolder?.id;\n if (!nodes) return [];\n if (!selectedFolderId)\n return sortNodesByName(nodes.filter((n) => !n.parentFolder));\n return sortNodesByName(\n nodes.filter((n) => n.parentFolder === selectedFolderId),\n );\n}\n","import type {\n PHAppConfig,\n PHAppConfigKey,\n PHDocumentEditorConfig,\n PHDocumentEditorConfigKey,\n PHGlobalConfig,\n PHGlobalConfigKey,\n} from \"@powerhousedao/reactor-browser\";\nimport { phGlobalConfigSetters } from \"./connect.js\";\nimport { phAppConfigSetters, phDocumentEditorConfigSetters } from \"./editor.js\";\n\nexport function setPHGlobalConfigByKey<TKey extends PHGlobalConfigKey>(\n key: TKey,\n value: PHGlobalConfig[TKey] | undefined,\n) {\n const setter = phGlobalConfigSetters[key];\n setter(value);\n}\n\nexport function setPHAppConfigByKey<TKey extends PHAppConfigKey>(\n key: TKey,\n value: PHAppConfig[TKey] | undefined,\n) {\n const setter = phAppConfigSetters[key];\n setter(value);\n}\n\nexport function setPHDocumentEditorConfigByKey<\n TKey extends PHDocumentEditorConfigKey,\n>(key: TKey, value: PHDocumentEditorConfig[TKey] | undefined) {\n const setter = phDocumentEditorConfigSetters[key];\n setter(value);\n}\n","import type {\n PHGlobalConfig,\n PHGlobalConfigKey,\n PHGlobalConfigSetters,\n} from \"@powerhousedao/reactor-browser\";\nimport { phGlobalConfigSetters } from \"./connect.js\";\n\nexport function callGlobalSetterForKey<TKey extends PHGlobalConfigKey>(\n key: TKey,\n value: PHGlobalConfig[TKey] | undefined,\n) {\n const setter = phGlobalConfigSetters[key] as PHGlobalConfigSetters[TKey];\n setter(value);\n}\n","import type {\n PHAppConfig,\n PHAppConfigKey,\n PHDocumentEditorConfig,\n PHDocumentEditorConfigKey,\n PHGlobalConfig,\n PHGlobalConfigKey,\n} from \"@powerhousedao/reactor-browser\";\nimport { useEffect, useState } from \"react\";\nimport { callGlobalSetterForKey } from \"./utils.js\";\n\nexport function setDefaultPHGlobalConfig(config: PHGlobalConfig) {\n for (const key of Object.keys(config) as PHGlobalConfigKey[]) {\n callGlobalSetterForKey(key, config[key]);\n }\n}\n\nexport function useSetDefaultPHGlobalConfig(config: PHGlobalConfig) {\n const [isInitialized, setIsInitialized] = useState(false);\n\n useEffect(() => {\n if (isInitialized) return;\n setDefaultPHGlobalConfig(config);\n setIsInitialized(true);\n }, [config, isInitialized]);\n}\n\nexport function useResetPHGlobalConfig(defaultConfigForReset: PHGlobalConfig) {\n return function reset() {\n setPHGlobalConfig(defaultConfigForReset);\n };\n}\n\nexport function setPHGlobalConfig(config: Partial<PHGlobalConfig>) {\n for (const key of Object.keys(config) as PHGlobalConfigKey[]) {\n callGlobalSetterForKey(key, config[key]);\n }\n}\n\nexport function useSetPHGlobalConfig(config: Partial<PHGlobalConfig>) {\n const [isInitialized, setIsInitialized] = useState(false);\n\n useEffect(() => {\n if (isInitialized) return;\n setPHGlobalConfig(config);\n setIsInitialized(true);\n }, [config, isInitialized]);\n}\n\n/** Sets the global drive config.\n *\n * Pass in a partial object of the global drive config to set.\n */\nexport function setPHAppConfig(config: Partial<PHAppConfig>) {\n for (const key of Object.keys(config) as PHAppConfigKey[]) {\n callGlobalSetterForKey(key, config[key]);\n }\n}\n\n/** Sets the global document config.\n *\n * Pass in a partial object of the global document config to set.\n */\nexport function setPHDocumentEditorConfig(\n config: Partial<PHDocumentEditorConfig>,\n) {\n for (const key of Object.keys(config) as PHDocumentEditorConfigKey[]) {\n callGlobalSetterForKey(key, config[key]);\n }\n}\n\n/** Wrapper hook for setting the global app config.\n *\n * Automatically sets the global app config when the component mounts.\n *\n * Pass in a partial object of the global app config to set.\n */\nexport function useSetPHAppConfig(config: Partial<PHAppConfig>) {\n const [isInitialized, setIsInitialized] = useState(false);\n\n useEffect(() => {\n if (isInitialized) return;\n setPHAppConfig(config);\n setIsInitialized(true);\n }, [config, isInitialized]);\n}\n\n/** Wrapper hook for setting the global document editor config.\n *\n * Automatically sets the global document editor config when the component mounts.\n *\n * Pass in a partial object of the global document editor config to set.\n */\nexport function useSetPHDocumentEditorConfig(\n config: Partial<PHDocumentEditorConfig>,\n) {\n const [isInitialized, setIsInitialized] = useState(false);\n\n useEffect(() => {\n if (isInitialized) return;\n setPHDocumentEditorConfig(config);\n setIsInitialized(true);\n }, [config, isInitialized]);\n}\n","import type {\n PHAppConfigKey,\n PHDocumentEditorConfigKey,\n PHGlobalConfigKey,\n} from \"@powerhousedao/reactor-browser\";\nimport { phGlobalConfigHooks } from \"./connect.js\";\nimport { phAppConfigHooks, phDocumentEditorConfigHooks } from \"./editor.js\";\n\nexport function usePHGlobalConfigByKey<TKey extends PHGlobalConfigKey>(\n key: TKey,\n) {\n const useValueHook = phGlobalConfigHooks[key];\n return useValueHook();\n}\n\n/** Gets the value of an item in the global drive config for a given key.\n *\n * Strongly typed, inferred from type definition for the key.\n */\nexport function usePHAppConfigByKey<TKey extends PHAppConfigKey>(key: TKey) {\n const useValueHook = phAppConfigHooks[key];\n return useValueHook();\n}\n\n/** Gets the value of an item in the global document config for a given key.\n *\n * Strongly typed, inferred from type definition for the key.\n */\nexport function usePHDocumentEditorConfigByKey<\n TKey extends PHDocumentEditorConfigKey,\n>(key: TKey) {\n const useValueHook = phDocumentEditorConfigHooks[key];\n return useValueHook();\n}\n","import type { ConnectionStateSnapshot } from \"@powerhousedao/reactor\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { useSync } from \"./reactor.js\";\n\n/**\n * Returns a map of remote name to connection state snapshot for all remotes.\n * Re-renders when any remote's connection state changes.\n */\nexport function useConnectionStates(): ReadonlyMap<\n string,\n ConnectionStateSnapshot\n> {\n const syncManager = useSync();\n const [states, setStates] = useState<\n ReadonlyMap<string, ConnectionStateSnapshot>\n >(() => buildSnapshot(syncManager));\n const unsubscribesRef = useRef<Array<() => void>>([]);\n\n useEffect(() => {\n if (!syncManager) return;\n\n function subscribe() {\n // Clean up previous subscriptions\n for (const unsub of unsubscribesRef.current) {\n unsub();\n }\n unsubscribesRef.current = [];\n\n const remotes = syncManager!.list();\n for (const remote of remotes) {\n const unsub = remote.channel.onConnectionStateChange(() => {\n setStates(buildSnapshot(syncManager));\n });\n unsubscribesRef.current.push(unsub);\n }\n\n // Set initial state\n setStates(buildSnapshot(syncManager));\n }\n\n subscribe();\n\n // Re-subscribe periodically to pick up added/removed remotes\n const interval = setInterval(subscribe, 5000);\n\n return () => {\n clearInterval(interval);\n for (const unsub of unsubscribesRef.current) {\n unsub();\n }\n unsubscribesRef.current = [];\n };\n }, [syncManager]);\n\n return states;\n}\n\n/**\n * Returns the connection state snapshot for a specific remote by name.\n */\nexport function useConnectionState(\n remoteName: string,\n): ConnectionStateSnapshot | undefined {\n const states = useConnectionStates();\n return states.get(remoteName);\n}\n\nfunction buildSnapshot(\n syncManager: ReturnType<typeof useSync>,\n): ReadonlyMap<string, ConnectionStateSnapshot> {\n const map = new Map<string, ConnectionStateSnapshot>();\n if (!syncManager) return map;\n for (const remote of syncManager.list()) {\n map.set(remote.meta.name, remote.channel.getConnectionState());\n }\n return map;\n}\n","import { ModuleNotFoundError } from \"@powerhousedao/reactor\";\nimport type { DocumentDispatch } from \"@powerhousedao/reactor-browser\";\nimport type { Action, PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { DocumentTypeMismatchError } from \"../errors.js\";\nimport { useDocumentById } from \"./document-by-id.js\";\nimport { useDocumentModelModuleById } from \"./document-model-modules.js\";\n\n/** Returns a document of a specific type, throws an error if the found document has a different type */\nexport function useDocumentOfType<\n TDocument extends PHDocument,\n TAction extends Action,\n>(\n documentId: string | null | undefined,\n documentType: string | null | undefined,\n) {\n const [document, dispatch] = useDocumentById(documentId);\n const documentModelModule = useDocumentModelModuleById(documentType);\n\n if (!documentId || !documentType) return [];\n\n if (!document) {\n throw new Error(`Document not found: ${documentId}`);\n }\n if (!documentModelModule) {\n throw new ModuleNotFoundError(documentType);\n }\n\n if (document.header.documentType !== documentType) {\n throw new DocumentTypeMismatchError(\n documentId,\n documentType,\n document.header.documentType,\n );\n }\n\n return [document, dispatch] as [TDocument, DocumentDispatch<TAction>];\n}\n","import { useDocumentModelModules } from \"./document-model-modules.js\";\n\n/** Returns the supported document types for the reactor (derived from the document model modules) */\nexport function useSupportedDocumentTypesInReactor() {\n const documentModelModules = useDocumentModelModules();\n return documentModelModules?.map((module) => module.documentModel.global.id);\n}\n","import { useAllowedDocumentTypes } from \"./config/editor.js\";\nimport { useSupportedDocumentTypesInReactor } from \"./supported-document-types.js\";\n\n/** Returns the document types a app supports.\n *\n * If present, uses the `allowedDocumentTypes` config value.\n * Otherwise, uses the supported document types from the reactor.\n */\nexport function useDocumentTypes() {\n const allowedDocumentTypes = useAllowedDocumentTypes();\n const supportedDocumentTypes = useSupportedDocumentTypesInReactor();\n return allowedDocumentTypes ?? supportedDocumentTypes;\n}\n","import type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { useDocumentModelModules } from \"./document-model-modules.js\";\nimport { useModelRegistry } from \"./reactor.js\";\n\nexport type DocumentVersionStatus =\n | { kind: \"current\"; documentVersion: number }\n | {\n kind: \"upgrade-available\";\n documentVersion: number;\n latestVersion: number;\n canUpgrade: boolean;\n }\n | {\n kind: \"unsupported\";\n documentVersion: number;\n availableVersions: number[];\n };\n\n/**\n * Classifies a document's model version against the installed module\n * versions. Pure logic, extracted for testing.\n */\nexport function getDocumentVersionStatus(\n documentVersion: number,\n availableVersions: number[],\n hasUpgradePath: (fromVersion: number, toVersion: number) => boolean,\n): DocumentVersionStatus | undefined {\n if (availableVersions.length === 0) {\n return undefined;\n }\n const sorted = [...availableVersions].sort((a, b) => a - b);\n const latestVersion = sorted[sorted.length - 1];\n if (documentVersion > latestVersion) {\n return { kind: \"unsupported\", documentVersion, availableVersions: sorted };\n }\n if (documentVersion === latestVersion) {\n return { kind: \"current\", documentVersion };\n }\n return {\n kind: \"upgrade-available\",\n documentVersion,\n latestVersion,\n canUpgrade: hasUpgradePath(documentVersion, latestVersion),\n };\n}\n\n/**\n * Compares the given document's model version against the versions available\n * from installed Vetra packages. Returns undefined while packages load or\n * when the document type has no installed modules.\n */\nexport function useDocumentVersionStatus(\n document: PHDocument | undefined,\n): DocumentVersionStatus | undefined {\n const modules = useDocumentModelModules();\n const registry = useModelRegistry();\n if (!document || !modules) {\n return undefined;\n }\n const documentType = document.header.documentType;\n const documentVersion = document.state.document.version || 1;\n const availableVersions = modules\n .filter((m) => m.documentModel.global.id === documentType)\n .map((m) => m.version ?? 1);\n\n return getDocumentVersionStatus(\n documentVersion,\n availableVersions,\n (fromVersion, toVersion) => {\n if (!registry) {\n return false;\n }\n try {\n registry.computeUpgradePath(documentType, fromVersion, toVersion);\n return true;\n } catch {\n return false;\n }\n },\n );\n}\n","import type {\n DocumentModelDocument,\n PHDocument,\n ValidationError,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n validateInitialState,\n validateModules,\n validateStateSchemaName,\n} from \"@powerhousedao/shared/document-model\";\n\nexport const validateDocument = (document: PHDocument) => {\n const errors: ValidationError[] = [];\n\n if (document.header.documentType !== \"powerhouse/document-model\") {\n return errors;\n }\n\n const doc = document as DocumentModelDocument;\n const specs = doc.state.global.specifications[0];\n\n // validate initial state errors\n const initialStateErrors = Object.keys(specs.state).reduce<ValidationError[]>(\n (acc, scopeKey) => {\n const scope = scopeKey as keyof typeof specs.state;\n\n return [\n ...acc,\n ...validateInitialState(\n specs.state[scope].initialValue,\n scope !== \"global\",\n ).map((err) => ({\n ...err,\n message: `${err.message}. Scope: ${scope}`,\n details: { ...err.details, scope },\n })),\n ];\n },\n [],\n );\n\n // validate schema state errors\n const schemaStateErrors = Object.keys(specs.state).reduce<ValidationError[]>(\n (acc, scopeKey) => {\n const scope = scopeKey as keyof typeof specs.state;\n const isGlobalScope = scope === \"global\";\n\n return [\n ...acc,\n ...validateStateSchemaName(\n specs.state[scope].schema,\n doc.state.global?.name || doc.header.name || \"\",\n !isGlobalScope ? scope : \"\",\n !isGlobalScope,\n ).map((err) => ({\n ...err,\n message: `${err.message}. Scope: ${scope}`,\n details: { ...err.details, scope },\n })),\n ];\n },\n [],\n );\n\n // modules validation\n const modulesErrors = validateModules(specs.modules);\n\n return [...initialStateErrors, ...schemaStateErrors, ...modulesErrors];\n};\n","import type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport normalizeException from \"normalize-exception\";\nimport { hasAtLeast } from \"remeda\";\nimport { exportFile } from \"../actions/document.js\";\nimport { showPHModal } from \"../hooks/modals.js\";\nimport { validateDocument } from \"./validate-document.js\";\n\nfunction defaultHandleError(error: Error) {\n console.error(`Failed to export document: ${error.message}`);\n}\n\nfunction handleDocumentValidation(document: PHDocument) {\n if (hasAtLeast(validateDocument(document), 1)) return false;\n return true;\n}\n\nexport function downloadDocument(\n document: PHDocument | undefined,\n handleError = defaultHandleError,\n) {\n if (!document) return;\n const isValid = handleDocumentValidation(document);\n\n if (!isValid) {\n showPHModal({\n type: \"downloadDocumentWithErrors\",\n documentId: document.header.id,\n });\n return;\n }\n exportFile(document).catch((error) => handleError(normalizeException(error)));\n}\n","import { downloadDocument } from \"../utils/download-document.js\";\nimport { useGetDocument } from \"./document-cache.js\";\nimport { usePHToast } from \"./toast.js\";\n\nexport function useDownloadDocument(id: string | undefined) {\n const getDocument = useGetDocument();\n const toast = usePHToast();\n\n return async () => {\n if (!id) return;\n const handleError = (error: Error) =>\n toast?.(`Failed to export document: ${error.message}`);\n try {\n const document = await getDocument(id);\n downloadDocument(document, handleError);\n } catch (error) {\n handleError(error as Error);\n }\n };\n}\n","import type {\n DocumentDriveAction,\n DocumentDriveDocument,\n} from \"@powerhousedao/shared/document-drive\";\nimport type { DocumentDispatch } from \"../types/documents.js\";\nimport { useDispatch } from \"./dispatch.js\";\nimport { useDrives } from \"./drives.js\";\n\nexport function useDriveById(\n driveId: string | undefined | null,\n): [DocumentDriveDocument, DocumentDispatch<DocumentDriveAction>] {\n const drives = useDrives();\n const foundDrive = drives?.find((drive) => drive.header.id === driveId);\n const [drive, dispatch] = useDispatch(foundDrive);\n if (!foundDrive) {\n throw new Error(`Drive with id ${driveId} not found`);\n }\n return [drive, dispatch] as [\n DocumentDriveDocument,\n DocumentDispatch<DocumentDriveAction>,\n ];\n}\n","import type { EditorModule } from \"document-model\";\nimport { DEFAULT_DRIVE_EDITOR_ID, DRIVE_DOCUMENT_TYPES } from \"../constants.js\";\nimport { useVetraPackages } from \"./vetra-packages.js\";\n\n/** An editor is a drive \"app\" if it targets any supported drive document type. */\nfunction isDriveEditor(module: EditorModule): boolean {\n const driveTypes = DRIVE_DOCUMENT_TYPES as readonly string[];\n return module.documentTypes.some((t) => driveTypes.includes(t));\n}\n\nexport function useEditorModules(): EditorModule[] | undefined {\n const vetraPackages = useVetraPackages();\n return vetraPackages\n .flatMap((pkg) => pkg.editors)\n .filter((module) => !isDriveEditor(module));\n}\n\nexport function useAppModules(): EditorModule[] | undefined {\n const vetraPackages = useVetraPackages();\n return vetraPackages.flatMap((pkg) => pkg.editors).filter(isDriveEditor);\n}\n\nexport function useFallbackEditorModule(\n documentType: string | null | undefined,\n): EditorModule | undefined {\n const editorModules = useEditorModules();\n if (!documentType) return undefined;\n if (editorModules?.length === 0) return undefined;\n\n const modulesForType = editorModules?.filter((module) =>\n module.documentTypes.includes(documentType),\n );\n return modulesForType?.[0];\n}\n\nexport function useAppModuleById(\n id: string | null | undefined,\n): EditorModule | undefined {\n const appModules = useAppModules();\n return appModules?.find((module) => module.config.id === id);\n}\n\nexport function useDefaultAppModule(): EditorModule | undefined {\n const defaultAppModule = useAppModuleById(DEFAULT_DRIVE_EDITOR_ID);\n return defaultAppModule;\n}\n\nexport function useEditorModuleById(\n id: string | null | undefined,\n): EditorModule | undefined {\n const editorModules = useEditorModules();\n return editorModules?.find((module) => module.config.id === id);\n}\n\nexport function useEditorModulesForDocumentType(\n documentType: string | null | undefined,\n) {\n const editorModules = useEditorModules();\n if (!documentType) return undefined;\n\n const modulesForType = editorModules?.filter((module) =>\n module.documentTypes.includes(documentType),\n );\n return modulesForType;\n}\n","import type { EditorModule } from \"document-model\";\n\n// React.lazy internals + an optional explicit preload hook.\ntype PreloadableComponent = EditorModule[\"Component\"] & {\n preload?: () => Promise<unknown>;\n _payload?: { _status: number };\n _init?: (payload: unknown) => unknown;\n};\n\n// Starts an editor's lazy chunk download without rendering it. Returns the\n// in-flight promise while uninitialized/pending, undefined once loaded.\nexport function preloadEditorModule(\n module: EditorModule,\n): Promise<unknown> | undefined {\n const Component = module.Component as PreloadableComponent;\n\n if (typeof Component.preload === \"function\") {\n return Component.preload();\n }\n\n const payload = Component._payload;\n const init = Component._init;\n if (!payload || typeof init !== \"function\") return undefined;\n\n // _init triggers the import: returns the module once resolved, throws the\n // pending promise while in flight (or the error if the load already failed).\n try {\n init(payload);\n } catch (thrown) {\n if (thrown && typeof (thrown as PromiseLike<unknown>).then === \"function\") {\n return thrown as Promise<unknown>;\n }\n }\n return undefined;\n}\n\ntype NetworkInformation = {\n saveData?: boolean;\n effectiveType?: string;\n};\n\n// Whether the connection is good enough for speculative preloading.\n// Unknown connection info is treated as \"ok\".\nexport function hasPreloadBandwidth(): boolean {\n if (typeof navigator === \"undefined\") return false;\n const connection = (\n navigator as Navigator & { connection?: NetworkInformation }\n ).connection;\n if (!connection) return true;\n if (connection.saveData) return false;\n return ![\"slow-2g\", \"2g\"].includes(connection.effectiveType ?? \"\");\n}\n","import { useEffect } from \"react\";\nimport {\n hasPreloadBandwidth,\n preloadEditorModule,\n} from \"../utils/preload-editor.js\";\nimport { useAppModules, useEditorModules } from \"./editor-modules.js\";\n\ntype IdleDeadline = { didTimeout: boolean; timeRemaining: () => number };\n\nfunction requestIdle(cb: (deadline: IdleDeadline) => void): number {\n if (typeof window.requestIdleCallback === \"function\") {\n return window.requestIdleCallback(cb);\n }\n // Fallback: hand out a short, draining time budget so pump processes a slice\n // and reschedules, rather than emptying the whole queue in one task.\n return window.setTimeout(() => {\n const start = Date.now();\n cb({\n didTimeout: false,\n timeRemaining: () => Math.max(0, 8 - (Date.now() - start)),\n });\n }, 200);\n}\n\nfunction cancelIdle(handle: number): void {\n if (typeof window.cancelIdleCallback === \"function\") {\n window.cancelIdleCallback(handle);\n } else {\n window.clearTimeout(handle);\n }\n}\n\n// Warms every registered editor's lazy chunk during browser idle time when\n// bandwidth allows, so opening a document doesn't wait on a network fetch.\nexport function useEditorPreloader(): void {\n const editorModules = useEditorModules();\n const appModules = useAppModules();\n\n useEffect(() => {\n const queue = [...(editorModules ?? []), ...(appModules ?? [])];\n if (queue.length === 0 || !hasPreloadBandwidth()) return;\n\n let cancelled = false;\n let handle = 0;\n\n const pump = (deadline: IdleDeadline) => {\n while (\n !cancelled &&\n queue.length > 0 &&\n (deadline.didTimeout || deadline.timeRemaining() > 0)\n ) {\n const editorModule = queue.shift()!;\n void preloadEditorModule(editorModule);\n }\n if (!cancelled && queue.length > 0) handle = requestIdle(pump);\n };\n\n handle = requestIdle(pump);\n\n return () => {\n cancelled = true;\n if (handle) cancelIdle(handle);\n };\n }, [editorModules, appModules]);\n}\n","import type { Node } from \"@powerhousedao/shared\";\nimport type { DragEventHandler } from \"react\";\nimport {\n allPass,\n filter,\n find,\n hasAtLeast,\n isArray,\n isDefined,\n isIncludedIn,\n isStrictEqual,\n isTruthy,\n last,\n map,\n once,\n pipe,\n split,\n} from \"remeda\";\nimport { useIsDragAndDropEnabled } from \"./config/editor.js\";\nimport { useSelectedFolder } from \"./selected-folder.js\";\nimport { useDropTarget } from \"./use-drop-target.js\";\n\n/* Supported file extensions, more can be added here */\nconst allowedExtensions = [\"zip\", \"phd\", \"phdm\"] as const;\n\nconst hasFilesType = (types: readonly string[]) =>\n isDefined(find(types, (type) => isStrictEqual(type, \"Files\")));\n\n/* A drop is a file drop when the data transfer types array has \"Files\" */\nconst isFileDrop = (event: React.DragEvent<Element>) =>\n allPass(event.dataTransfer.types, [isArray, hasAtLeast(1), hasFilesType]);\n\n/* Marker attribute editors set on their root element to opt out of the\n * outer DropZone, so they can handle arbitrary file drops themselves. */\nexport const EDITOR_FILE_DROP_OPT_OUT_ATTR = \"data-accepts-files\";\n\nconst isInsideEditorFileDropOptOut = (event: React.DragEvent<Element>) => {\n const target = event.target;\n if (!(target instanceof Element)) return false;\n return target.closest(`[${EDITOR_FILE_DROP_OPT_OUT_ATTR}]`) !== null;\n};\n\nconst hasAllowedExtension = (file: File) =>\n pipe(\n file,\n (file) => file.name,\n split(\".\"),\n last(),\n isIncludedIn(allowedExtensions),\n );\n\n/* Gets uploaded files from the drop event data transfer */\nconst getFileItems = (event: React.DragEvent<Element>) =>\n pipe(\n [...event.dataTransfer.items],\n filter((item) => isStrictEqual(item.kind, \"file\")),\n map((item) => item.getAsFile()),\n filter(isTruthy),\n );\n\n/* Allows uploading of files by drag and drop.\n * Intended for use in the drop-zone component in connect.\n */\nexport function useDropFile(\n handleAddFile: (file: File, parent: Node | undefined) => Promise<void>,\n) {\n const { isDropTarget, setTarget, unsetTarget } = useDropTarget();\n const isDragAndDropEnabled = useIsDragAndDropEnabled();\n const selectedFolder = useSelectedFolder();\n\n function handleDragEvent(event: React.DragEvent<Element>, cb?: () => void) {\n if (!isDragAndDropEnabled) return;\n if (!isFileDrop(event)) return;\n if (isInsideEditorFileDropOptOut(event)) {\n // Hide the DropZone overlay while the cursor is over an editor that\n // opts in to its own file drops, so the overlay doesn't strand the\n // user covering the opt-out region.\n unsetTarget();\n return;\n }\n event.preventDefault();\n event.stopPropagation();\n cb?.();\n }\n\n const handleAddFiles = (event: React.DragEvent<Element>) =>\n Promise.all(\n pipe(\n event,\n getFileItems,\n filter(hasAllowedExtension),\n map((file) => handleAddFile(file, selectedFolder)),\n ),\n );\n\n const onDragEnter: DragEventHandler = (event) => handleDragEvent(event);\n\n const onDragOver: DragEventHandler = (event) =>\n handleDragEvent(event, setTarget);\n\n const onDragLeave: DragEventHandler = (event) =>\n handleDragEvent(event, unsetTarget);\n\n const onDrop: DragEventHandler = (event) =>\n handleDragEvent(\n event,\n once(() => {\n unsetTarget();\n handleAddFiles(event).catch(console.error);\n }),\n );\n\n return {\n onDragEnter,\n onDragOver,\n onDragLeave,\n onDrop,\n isDropTarget,\n };\n}\n","import type { FolderNode } from \"@powerhousedao/shared/document-drive\";\nimport { useFolderNodesInSelectedDrive } from \"./items-in-selected-drive.js\";\n\nexport function useFolderById(\n id: string | null | undefined,\n): FolderNode | undefined {\n const folders = useFolderNodesInSelectedDrive();\n return folders?.find((n) => n.id === id);\n}\n","import type {\n GraphQLClientDocumentEvent,\n GraphQLClientDocumentsEvent,\n GraphQLDocumentEventOperationName,\n GraphQLDocumentEventsOperationName,\n} from \"./types.js\";\n\nexport function dispatchGraphQLClientDocumentEvent(\n operationName: GraphQLDocumentEventOperationName,\n identifier: string,\n) {\n const event: GraphQLClientDocumentEvent = new CustomEvent(operationName, {\n detail: { identifier },\n });\n window.dispatchEvent(event);\n}\n\nexport function dispatchGraphQLClientDocumentsEvent(\n operationName: GraphQLDocumentEventsOperationName,\n identifiers: string[],\n) {\n const event: GraphQLClientDocumentsEvent = new CustomEvent(operationName, {\n detail: { identifiers },\n });\n window.dispatchEvent(event);\n}\n","import { isIncludedIn, isStrictEqual } from \"remeda\";\nimport { identifierFromMutateDocumentOperationVariables } from \"./adapters.js\";\nimport { graphqlEventsToSyncDrive } from \"./constants.js\";\nimport { dispatchGraphQLClientDocumentEvent } from \"./events.js\";\nimport type { SdkFunctionWrapper } from \"./gen/schema.js\";\n\nexport const documentCacheClientMiddleware: SdkFunctionWrapper = async (\n action,\n operationName,\n operationType,\n variables: unknown,\n) => {\n console.log({ operationName, operationType, variables });\n const result = await action();\n\n if (isIncludedIn(operationName, graphqlEventsToSyncDrive)) {\n window.dispatchEvent(new CustomEvent(operationName));\n }\n\n if (isStrictEqual(operationName, \"MutateDocument\")) {\n dispatchGraphQLClientDocumentEvent(\n operationName,\n identifierFromMutateDocumentOperationVariables(variables),\n );\n }\n\n return result;\n};\n","import type { DocumentDriveDocument } from \"@powerhousedao/shared\";\nimport { DriveDocumentSchema } from \"@powerhousedao/shared/document-drive\";\nimport { useEffect, useState } from \"react\";\nimport { forEach } from \"remeda\";\nimport { phDocumentFromQuery } from \"../graphql/adapters.js\";\nimport { createClient } from \"../graphql/client.js\";\nimport {\n DEFAULT_DRIVE_ID,\n DEFAULT_SWITCHBOARD_URL,\n graphqlEventsToSyncDrive,\n} from \"../graphql/constants.js\";\nimport { documentCacheClientMiddleware } from \"../graphql/document-cache-client-middleware.js\";\nimport { GraphQLClientDocumentCache } from \"../graphql/graphql-client-document-cache.js\";\nimport {\n callEventHandlerRegisterFunctions,\n commonGlobalEventHandlerFunctions,\n} from \"./add-ph-event-handlers.js\";\nimport { setDocumentCache } from \"./document-cache.js\";\nimport { setDrives } from \"./drives.js\";\nimport { setGraphQLReactorClient } from \"./graphql-reactor-client.js\";\nimport { setSelectedDrive } from \"./selected-drive.js\";\nimport { setSelectedNode } from \"./selected-node.js\";\n\nexport function useInitReactorGraphqlClient(\n switchboardUrl = DEFAULT_SWITCHBOARD_URL,\n driveId = DEFAULT_DRIVE_ID,\n) {\n const [hasInit, setHasInit] = useState(false);\n\n useEffect(() => {\n if (hasInit) return;\n\n initGraphQLReactorClientWithDocumentCache(switchboardUrl, driveId)\n .then(() => setHasInit(true))\n .catch(console.error);\n }, [hasInit]);\n\n return hasInit;\n}\n\nasync function reactorGraphqlFetchDrive(\n identifier: string,\n): Promise<DocumentDriveDocument> {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n\n const result = await client.GetDocument({ identifier });\n\n if (!result.document?.document) {\n throw new Error(\"Could not fetch drive with id: \" + identifier);\n }\n\n const drive = phDocumentFromQuery(\n result.document.document,\n DriveDocumentSchema,\n ) as DocumentDriveDocument;\n return drive;\n}\n\nasync function reactorGraphqlSyncDrive(driveId: string) {\n const client = window.ph?.reactorGraphQLClient;\n\n if (!client) {\n throw new Error(\n \"Please call `useInitReactorGraphqlClient` to use its functions\",\n );\n }\n const drive = await reactorGraphqlFetchDrive(driveId);\n setDrives([drive]);\n setSelectedDrive(drive);\n}\n\nasync function initGraphQLReactorClientWithDocumentCache(\n switchboardUrl: string,\n driveId: string,\n) {\n if (!window.ph) {\n window.ph = {};\n }\n\n callEventHandlerRegisterFunctions(commonGlobalEventHandlerFunctions);\n\n const client = createClient(switchboardUrl, documentCacheClientMiddleware);\n setGraphQLReactorClient(client);\n await reactorGraphqlSyncDrive(driveId);\n setSelectedNode(undefined);\n setDocumentCache(new GraphQLClientDocumentCache());\n\n forEach(graphqlEventsToSyncDrive, (name) => {\n window.addEventListener(name, () => {\n reactorGraphqlSyncDrive(driveId).catch(console.error);\n });\n });\n}\n","import type {\n FileNode,\n FolderNode,\n Node,\n} from \"@powerhousedao/shared/document-drive\";\nimport type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { isFileNodeKind, isFolderNodeKind } from \"../utils/nodes.js\";\nimport {\n useDocumentsInSelectedDrive,\n useNodesInSelectedDrive,\n} from \"./items-in-selected-drive.js\";\nimport { useSelectedFolder } from \"./selected-folder.js\";\n\n/** Returns the nodes in the selected folder. */\nexport function useNodesInSelectedFolder(): Node[] | undefined {\n const selectedFolder = useSelectedFolder();\n const nodes = useNodesInSelectedDrive();\n if (!selectedFolder || !nodes) return undefined;\n\n return nodes.filter((n) => n.parentFolder === selectedFolder.id);\n}\n\n/** Returns the file nodes in the selected folder. */\nexport function useFileNodesInSelectedFolder(): FileNode[] | undefined {\n const nodes = useNodesInSelectedFolder();\n if (!nodes) return undefined;\n return nodes.filter((n) => isFileNodeKind(n));\n}\n\n/** Returns the folder nodes in the selected folder. */\nexport function useFolderNodesInSelectedFolder(): FolderNode[] | undefined {\n const nodes = useNodesInSelectedFolder();\n if (!nodes) return undefined;\n return nodes.filter((n) => isFolderNodeKind(n));\n}\n\n/** Returns the documents in the selected folder. */\nexport function useDocumentsInSelectedFolder(): PHDocument[] | undefined {\n const documents = useDocumentsInSelectedDrive();\n const fileNodes = useFileNodesInSelectedFolder();\n const fileNodeIds = fileNodes?.map((node) => node.id);\n return documents?.filter((d) => fileNodeIds?.includes(d.header.id));\n}\n","import type { Node } from \"@powerhousedao/shared/document-drive\";\nimport {\n addFile,\n addFolder,\n copyNode,\n moveNode,\n renameDriveNode,\n renameNode,\n} from \"../actions/document.js\";\nimport { useDrives } from \"./drives.js\";\nimport { useFolderById } from \"./folder-by-id.js\";\nimport { useSelectedDriveSafe } from \"./selected-drive.js\";\nimport { useSelectedFolder } from \"./selected-folder.js\";\nimport { setSelectedNode, useSelectedNode } from \"./selected-node.js\";\n\nfunction resolveNode(driveId: string, node: Node | undefined) {\n return node?.id !== driveId ? node : undefined;\n}\n\nexport function useNodeActions() {\n const [selectedDrive] = useSelectedDriveSafe();\n const selectedFolder = useSelectedFolder();\n const selectedNode = useSelectedNode();\n const selectedParentFolder = useFolderById(selectedNode?.parentFolder);\n const selectedDriveId = selectedDrive?.header.id;\n const drives = useDrives();\n\n async function onAddFile(file: File, parent: Node | undefined) {\n if (!selectedDriveId) return;\n\n const fileName = file.name.replace(/\\..+/gim, \"\");\n\n return addFile(\n file,\n selectedDriveId,\n fileName,\n resolveNode(selectedDriveId, parent)?.id,\n );\n }\n\n async function onAddFolder(name: string, parent: Node | undefined) {\n if (!selectedDriveId) return;\n\n return addFolder(\n selectedDriveId,\n name,\n resolveNode(selectedDriveId, parent)?.id,\n );\n }\n\n async function onRenameNode(\n newName: string,\n node: Node,\n ): Promise<Node | undefined> {\n if (!selectedDriveId) return;\n\n const resolvedNode = resolveNode(selectedDriveId, node);\n if (!resolvedNode) {\n console.error(`Node ${node.id} not found`);\n return;\n }\n\n return await renameNode(selectedDriveId, node.id, newName);\n }\n\n async function onCopyNode(src: Node, target: Node | undefined) {\n if (!selectedDriveId) return;\n const resolvedSrc = resolveNode(selectedDriveId, src);\n if (!resolvedSrc) {\n console.error(`Node ${src.id} not found`);\n return;\n }\n const resolvedTarget = resolveNode(selectedDriveId, target);\n\n await copyNode(selectedDriveId, resolvedSrc, resolvedTarget);\n }\n\n async function onMoveNode(src: Node, target: Node | undefined) {\n if (!selectedDriveId) return;\n\n const resolvedSrc = resolveNode(selectedDriveId, src);\n if (!resolvedSrc) {\n console.error(`Node ${src.id} not found`);\n return;\n }\n const resolvedTarget = resolveNode(selectedDriveId, target);\n\n // if node is already on target then ignore move\n if (\n (!resolvedTarget?.id && !src.parentFolder) ||\n resolvedTarget?.id === src.parentFolder\n ) {\n return;\n }\n await moveNode(selectedDriveId, resolvedSrc, resolvedTarget);\n }\n\n async function onDuplicateNode(src: Node) {\n if (!selectedDriveId) return;\n\n const resolvedSrc = resolveNode(selectedDriveId, src);\n if (!resolvedSrc) {\n console.error(`Node ${src.id} not found`);\n return;\n }\n\n const target = resolveNode(\n selectedDriveId,\n selectedFolder ?? selectedParentFolder,\n );\n await copyNode(selectedDriveId, resolvedSrc, target);\n }\n async function onAddAndSelectNewFolder(name: string) {\n if (!name) return;\n if (!selectedDriveId) return;\n\n const resolvedTarget = resolveNode(\n selectedDriveId,\n selectedFolder ?? selectedParentFolder,\n );\n if (!resolvedTarget) return;\n\n const newFolder = await onAddFolder(name, resolvedTarget);\n\n if (newFolder) {\n setSelectedNode(newFolder);\n }\n }\n\n async function onRenameDriveNodes(\n newName: string,\n nodeId: string,\n ): Promise<void> {\n if (!drives) return;\n\n // Find all drives that contain this node\n const drivesWithNode = drives.filter((drive) =>\n drive.state.global.nodes.some((n) => n.id === nodeId),\n );\n\n // Update node name in all parent drives\n await Promise.all(\n drivesWithNode.map((drive) =>\n renameDriveNode(drive.header.id, nodeId, newName),\n ),\n );\n }\n\n return {\n onAddFile,\n onAddFolder,\n onRenameNode,\n onCopyNode,\n onMoveNode,\n onDuplicateNode,\n onAddAndSelectNewFolder,\n onRenameDriveNodes,\n };\n}\n","import type { Node } from \"@powerhousedao/shared/document-drive\";\nimport { useNodesInSelectedDrive } from \"./items-in-selected-drive.js\";\n\n/** Returns a node in the selected drive by id. */\nexport function useNodeById(id: string | null | undefined): Node | undefined {\n const nodes = useNodesInSelectedDrive();\n return nodes?.find((n) => n.id === id);\n}\n","import type { Node } from \"@powerhousedao/shared/document-drive\";\nimport { useNodesInSelectedDrive } from \"./items-in-selected-drive.js\";\nimport { useSelectedNode } from \"./selected-node.js\";\n\n/** Returns the path to a node in the selected drive */\nexport function useNodePathById(id: string | null | undefined) {\n const nodes = useNodesInSelectedDrive();\n if (!nodes) return [];\n\n const path: Node[] = [];\n let current = nodes.find((n) => n.id === id);\n\n while (current) {\n path.push(current);\n if (!current.parentFolder) break;\n current = nodes.find((n) => n.id === current?.parentFolder);\n }\n\n return path.reverse();\n}\n\n/** Returns the path to the currently selected node in the selected drive. */\nexport function useSelectedNodePath() {\n const selectedNode = useSelectedNode();\n return useNodePathById(selectedNode?.id);\n}\n","import type { FolderNode } from \"@powerhousedao/shared/document-drive\";\nimport { useFolderById } from \"./folder-by-id.js\";\nimport { useNodeById } from \"./node-by-id.js\";\nimport { useSelectedNode } from \"./selected-node.js\";\n\nexport function useNodeParentFolderById(\n id: string | null | undefined,\n): FolderNode | undefined {\n const node = useNodeById(id);\n const parentFolder = useFolderById(node?.parentFolder);\n return parentFolder;\n}\n\nexport function useParentFolderForSelectedNode() {\n const node = useSelectedNode();\n return useNodeParentFolderById(node?.id);\n}\n","import type { DocumentDispatch } from \"@powerhousedao/reactor-browser\";\nimport { isFileNode } from \"@powerhousedao/shared/document-drive\";\nimport type { Action, PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { NoSelectedDocumentError } from \"../errors.js\";\nimport type { DispatchFn, UseDispatchResult } from \"./dispatch.js\";\nimport { useDocumentById } from \"./document-by-id.js\";\nimport { useDocumentOfType } from \"./document-of-type.js\";\nimport { useSelectedNode } from \"./selected-node.js\";\n\n/** Returns the selected document id */\nexport function useSelectedDocumentId(): string | undefined {\n const selectedNode = useSelectedNode();\n return selectedNode && isFileNode(selectedNode) ? selectedNode.id : undefined;\n}\n\n/** Returns the selected document. */\nexport function useSelectedDocument(): readonly [\n PHDocument,\n DispatchFn<Action>,\n] {\n const selectedDocumentId = useSelectedDocumentId();\n const [document, dispatch] = useDocumentById(selectedDocumentId);\n if (!document) {\n throw new NoSelectedDocumentError();\n }\n return [document, dispatch] as const;\n}\n\n/** Returns the selected document. */\nexport function useSelectedDocumentSafe(): UseDispatchResult<\n PHDocument,\n Action\n> {\n const selectedDocumentId = useSelectedDocumentId();\n return useDocumentById(selectedDocumentId);\n}\n\n/** Returns the selected document of a specific type, throws an error if the found document has a different type */\nexport function useSelectedDocumentOfType(\n documentType: null | undefined,\n): never[];\nexport function useSelectedDocumentOfType<\n TDocument extends PHDocument,\n TAction extends Action,\n>(documentType: string): [TDocument, DocumentDispatch<TAction>];\nexport function useSelectedDocumentOfType<\n TDocument extends PHDocument,\n TAction extends Action,\n>(\n documentType: string | null | undefined,\n): never[] | [TDocument, DocumentDispatch<TAction>] {\n const documentId = useSelectedDocumentId();\n\n if (!documentType) {\n return [];\n }\n if (!documentId) {\n throw new NoSelectedDocumentError();\n }\n return useDocumentOfType<TDocument, TAction>(documentId, documentType);\n}\n","import type { SubgraphModule } from \"@powerhousedao/shared/document-model\";\nimport { useVetraPackages } from \"./vetra-packages.js\";\n\nexport function useSubgraphModules(): SubgraphModule[] | undefined {\n const vetraPackages = useVetraPackages();\n return vetraPackages.flatMap((pkg) => pkg.subgraphs || []);\n}\n","import { useEffect, useSyncExternalStore } from \"react\";\n\ntype Theme = \"light\" | \"dark\";\ntype SystemTheme = Theme;\ntype StoredTheme = \"light\" | \"dark\" | \"system\";\n\nconst STORED_THEME_KEY = \"ph:theme\" as const;\nconst UPDATE_STORED_THEME = \"ph:updateStoredTheme\" as const;\nconst STORED_THEME_UPDATED = \"ph:storedThemeUpdated\" as const;\nconst SYSTEM_THEME_UPDATED = \"ph:systemThemeUpdated\" as const;\nconst isServer = typeof window === \"undefined\";\n\ntype UpdateStoredThemeEvent = CustomEvent<{ storedTheme: StoredTheme }>;\ntype StoredThemeUpdatedEvent = CustomEvent<{ storedTheme: StoredTheme }>;\ntype SystemThemeUpdatedEvent = CustomEvent<{ systemTheme: SystemTheme }>;\n\ntype ThemeWindowEvents = {\n [UPDATE_STORED_THEME]: UpdateStoredThemeEvent;\n [STORED_THEME_UPDATED]: StoredThemeUpdatedEvent;\n [SYSTEM_THEME_UPDATED]: SystemThemeUpdatedEvent;\n};\n\ndeclare global {\n interface WindowEventMap extends ThemeWindowEvents {}\n}\n\nfunction setStoredTheme(storedTheme: StoredTheme) {\n if (isServer) return;\n localStorage.setItem(STORED_THEME_KEY, storedTheme);\n}\n\nfunction setTheme(storedTheme: StoredTheme) {\n if (isServer) return;\n const updateStoredThemeEvent = new CustomEvent(UPDATE_STORED_THEME, {\n detail: {\n storedTheme,\n },\n });\n window.dispatchEvent(updateStoredThemeEvent);\n}\n\nfunction handleUpdateStoredTheme(event: UpdateStoredThemeEvent) {\n if (isServer) return;\n const storedTheme = event.detail.storedTheme;\n setStoredTheme(storedTheme);\n const storedThemeUpdatedEvent = new CustomEvent(STORED_THEME_UPDATED, {\n detail: { storedTheme },\n });\n window.dispatchEvent(storedThemeUpdatedEvent);\n}\n\nfunction getStoredTheme() {\n if (isServer) return undefined;\n const storedTheme = localStorage.getItem(STORED_THEME_KEY) ?? undefined;\n return storedTheme as StoredTheme;\n}\n\nfunction getPrefersDarkMediaQuery() {\n if (isServer) return;\n const prefersDarkMediaQuery = window.matchMedia(\n \"(prefers-color-scheme: dark)\",\n );\n return prefersDarkMediaQuery;\n}\n\nfunction getPrefersDark() {\n if (isServer) return false;\n const prefersDark = getPrefersDarkMediaQuery();\n if (prefersDark?.matches) return true;\n return false;\n}\n\nfunction getSystemTheme(): SystemTheme {\n if (isServer) return \"light\";\n const prefersDark = getPrefersDark();\n if (prefersDark) return \"dark\";\n return \"light\";\n}\n\nfunction handleSystemThemeChange(event: MediaQueryListEvent) {\n const isDark = event.matches;\n const systemTheme = isDark ? \"dark\" : \"light\";\n const systemThemeUpdatedEvent = new CustomEvent(SYSTEM_THEME_UPDATED, {\n detail: { systemTheme },\n });\n window.dispatchEvent(systemThemeUpdatedEvent);\n}\n\nfunction toggleDark(isDark: boolean) {\n if (isServer) return;\n document.documentElement.classList.toggle(\"dark\", isDark);\n}\n\nexport function initTheme() {\n if (isServer) return;\n\n useEffect(() => {\n window.addEventListener(UPDATE_STORED_THEME, handleUpdateStoredTheme);\n const prefersDarkMediaQuery = getPrefersDarkMediaQuery();\n prefersDarkMediaQuery?.addEventListener(\"change\", handleSystemThemeChange);\n return () => {\n window.removeEventListener(UPDATE_STORED_THEME, handleUpdateStoredTheme);\n prefersDarkMediaQuery?.removeEventListener(\n \"change\",\n handleSystemThemeChange,\n );\n };\n }, []);\n}\n\nfunction subscribeToStoredTheme(onStoreChange: () => void) {\n if (isServer) return () => {};\n // `storage` fires in every OTHER same-origin browsing context (e.g. an\n // embedding parent window), keeping embedded instances in sync live.\n const handleStorage = (event: StorageEvent) => {\n if (event.key === STORED_THEME_KEY || event.key === null) onStoreChange();\n };\n window.addEventListener(STORED_THEME_UPDATED, onStoreChange);\n window.addEventListener(\"storage\", handleStorage);\n return () => {\n window.removeEventListener(STORED_THEME_UPDATED, onStoreChange);\n window.removeEventListener(\"storage\", handleStorage);\n };\n}\n\nfunction subscribeToSystemTheme(onStoreChange: () => void) {\n if (isServer) return () => {};\n window.addEventListener(SYSTEM_THEME_UPDATED, onStoreChange);\n return () => {\n window.removeEventListener(SYSTEM_THEME_UPDATED, onStoreChange);\n };\n}\n\nexport function useTheme() {\n const storedTheme = useSyncExternalStore(\n subscribeToStoredTheme,\n () => getStoredTheme(),\n () => \"system\" as const,\n );\n const systemTheme = useSyncExternalStore(\n subscribeToSystemTheme,\n () => getSystemTheme(),\n () => \"light\" as const,\n );\n\n const isSystem = storedTheme === undefined || storedTheme === \"system\";\n\n const theme = isSystem ? systemTheme : storedTheme;\n const isDark = theme === \"dark\";\n\n useEffect(() => {\n toggleDark(isDark);\n }, [isDark]);\n\n return {\n theme,\n isSystem,\n setTheme,\n } as const;\n}\n","import {\n DriveCollectionId,\n type GqlRequestChannel,\n} from \"@powerhousedao/reactor\";\nimport type { DocumentDriveDocument } from \"@powerhousedao/shared/document-drive\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useSyncList } from \"./reactor.js\";\n\nexport type DriveSystemInfoState =\n | { status: \"local\" }\n | { status: \"loading\" }\n | { status: \"error\"; message: string }\n | {\n status: \"ready\";\n version: string;\n gitHash: string;\n gitUrl: string | null;\n host: string;\n };\n\nexport function deriveSystemUrl(channelUrl: string): string | null {\n try {\n const url = new URL(channelUrl);\n url.search = \"\";\n url.hash = \"\";\n const suffix = \"/graphql/r\";\n if (url.pathname.endsWith(suffix)) {\n url.pathname = url.pathname.slice(0, -suffix.length) + \"/graphql/system\";\n } else {\n url.pathname = \"/graphql/system\";\n }\n return url.toString();\n } catch {\n return null;\n }\n}\n\nconst cache = new Map<string, DriveSystemInfoState>();\n\nexport function useDriveSystemInfo(\n drive: DocumentDriveDocument | undefined,\n): DriveSystemInfoState {\n const remotes = useSyncList();\n const driveId = drive?.header.id;\n\n const systemUrl = useMemo(() => {\n if (!driveId) return null;\n const remote = remotes.find((r) =>\n r.meta.collectionId.equals(DriveCollectionId.forDrive(driveId)),\n );\n const channelUrl = (remote?.channel as GqlRequestChannel | undefined)\n ?.config.url;\n if (typeof channelUrl !== \"string\") return null;\n return deriveSystemUrl(channelUrl);\n }, [remotes, driveId]);\n\n const [state, setState] = useState<DriveSystemInfoState>(() =>\n systemUrl\n ? (cache.get(systemUrl) ?? { status: \"loading\" })\n : { status: \"local\" },\n );\n\n useEffect(() => {\n if (!systemUrl) {\n setState({ status: \"local\" });\n return;\n }\n\n const cached = cache.get(systemUrl);\n if (cached && cached.status !== \"loading\") {\n setState(cached);\n return;\n }\n\n setState({ status: \"loading\" });\n cache.set(systemUrl, { status: \"loading\" });\n\n const controller = new AbortController();\n fetch(systemUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n query: \"{ system { version gitHash gitUrl } }\",\n }),\n signal: controller.signal,\n })\n .then(async (res) => {\n const json = (await res.json()) as {\n data?: {\n system?: {\n version: string;\n gitHash: string;\n gitUrl: string | null;\n };\n };\n errors?: Array<{ message: string }>;\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n const sys = json.data?.system;\n if (!sys) throw new Error(\"Missing system in response\");\n const next: DriveSystemInfoState = {\n status: \"ready\",\n version: sys.version,\n gitHash: sys.gitHash,\n gitUrl: sys.gitUrl ?? null,\n host: new URL(systemUrl).host,\n };\n cache.set(systemUrl, next);\n setState(next);\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted) return;\n const message = err instanceof Error ? err.message : String(err);\n console.error(message);\n const next: DriveSystemInfoState = { status: \"error\", message };\n cache.set(systemUrl, next);\n setState(next);\n });\n\n return () => controller.abort();\n }, [systemUrl]);\n\n return state;\n}\n","import {\n useCallback,\n useRef,\n useState,\n type DragEvent,\n type DragEventHandler,\n} from \"react\";\nimport { EDITOR_FILE_DROP_OPT_OUT_ATTR } from \"./file-drag-and-drop.js\";\n\nexport type UseEditorFileDropOptions = {\n /** Lowercase file extensions including the dot (e.g. [\".png\", \".pdf\"]).\n * When omitted, all files are accepted. */\n accept?: readonly string[];\n /** Called with the files that passed the extension filter. */\n onFiles: (files: File[]) => void;\n};\n\nexport type UseEditorFileDropResult = {\n /** Spread onto the editor's root element. Includes the opt-out attribute\n * so the outer DropZone leaves file drops alone within this subtree. */\n dragProps: {\n onDragEnter: DragEventHandler<Element>;\n onDragOver: DragEventHandler<Element>;\n onDragLeave: DragEventHandler<Element>;\n onDrop: DragEventHandler<Element>;\n } & Record<typeof EDITOR_FILE_DROP_OPT_OUT_ATTR, \"\">;\n /** True while a file drag is hovering anywhere inside the editor root. */\n isDragOver: boolean;\n};\n\nconst hasFiles = (event: DragEvent<Element>) =>\n event.dataTransfer.types.includes(\"Files\");\n\nconst filterByExtension = (files: FileList, accept?: readonly string[]) => {\n const all = Array.from(files);\n if (!accept || accept.length === 0) return all;\n const lowerAccept = accept.map((ext) => ext.toLowerCase());\n return all.filter((file) => {\n const lower = file.name.toLowerCase();\n return lowerAccept.some((ext) => lower.endsWith(ext));\n });\n};\n\nexport function useEditorFileDrop(\n options: UseEditorFileDropOptions,\n): UseEditorFileDropResult {\n const { accept, onFiles } = options;\n const [isDragOver, setIsDragOver] = useState(false);\n const depthRef = useRef(0);\n\n const onDragOver = useCallback<DragEventHandler<Element>>((event) => {\n if (!hasFiles(event)) return;\n event.preventDefault();\n }, []);\n\n const onDragEnter = useCallback<DragEventHandler<Element>>((event) => {\n if (!hasFiles(event)) return;\n depthRef.current += 1;\n if (depthRef.current === 1) setIsDragOver(true);\n }, []);\n\n const onDragLeave = useCallback<DragEventHandler<Element>>((event) => {\n if (!hasFiles(event)) return;\n depthRef.current = Math.max(0, depthRef.current - 1);\n if (depthRef.current === 0) setIsDragOver(false);\n }, []);\n\n const onDrop = useCallback<DragEventHandler<Element>>(\n (event) => {\n if (!hasFiles(event)) return;\n event.preventDefault();\n depthRef.current = 0;\n setIsDragOver(false);\n const accepted = filterByExtension(event.dataTransfer.files, accept);\n if (accepted.length === 0) return;\n onFiles(accepted);\n },\n [accept, onFiles],\n );\n\n return {\n dragProps: {\n onDragEnter,\n onDragOver,\n onDragLeave,\n onDrop,\n [EDITOR_FILE_DROP_OPT_OUT_ATTR]: \"\",\n },\n isDragOver,\n };\n}\n","import type { IReactorClient } from \"@powerhousedao/reactor\";\nimport { SyncStatus } from \"@powerhousedao/reactor\";\nimport type {\n DocumentDriveDocument,\n SharingType,\n} from \"@powerhousedao/shared/document-drive\";\nimport { DRIVE_DOCUMENT_TYPES } from \"../constants.js\";\n\nexport type UISyncStatus =\n | \"INITIAL_SYNC\"\n | \"SUCCESS\"\n | \"CONFLICT\"\n | \"MISSING\"\n | \"ERROR\"\n | \"SYNCING\";\n\nconst syncStatusToUI: Record<SyncStatus, UISyncStatus> = {\n [SyncStatus.Synced]: \"SUCCESS\",\n [SyncStatus.Outgoing]: \"SYNCING\",\n [SyncStatus.Incoming]: \"SYNCING\",\n [SyncStatus.OutgoingAndIncoming]: \"SYNCING\",\n [SyncStatus.Error]: \"ERROR\",\n};\n\nexport async function getDrives(\n reactor: IReactorClient,\n): Promise<DocumentDriveDocument[]> {\n // SearchFilter.type takes one string, so query each drive type and merge.\n const perType = await Promise.all(\n DRIVE_DOCUMENT_TYPES.map((type) => reactor.find({ type })),\n );\n return perType.flatMap((r) => r.results) as DocumentDriveDocument[];\n}\n\nexport function getSyncStatus(\n documentId: string,\n sharingType: SharingType,\n): Promise<UISyncStatus | undefined> {\n return Promise.resolve(getSyncStatusSync(documentId, sharingType));\n}\n\nexport function getSyncStatusSync(\n documentId: string,\n sharingType: SharingType,\n): UISyncStatus | undefined {\n if (sharingType === \"LOCAL\") return;\n\n const syncManager =\n window.ph?.reactorClientModule?.reactorModule?.syncModule?.syncManager;\n if (!syncManager) return;\n\n const status = syncManager.getSyncStatus(documentId);\n if (status === undefined) return;\n\n return syncStatusToUI[status];\n}\n","import type { Operation } from \"@powerhousedao/shared/document-model\";\n\nexport const getRevisionFromDate = (\n startDate?: Date,\n endDate?: Date,\n operations: Operation[] = [],\n) => {\n if (!startDate || !endDate) return 0;\n\n const operation = operations.find((operation) => {\n const operationDate = new Date(operation.timestampUtcMs);\n return operationDate >= startDate && operationDate <= endDate;\n });\n\n return operation ? operation.index : 0;\n};\n","import * as lzString from \"lz-string\";\nimport { GetDocumentWithOperationsDocument } from \"../graphql/gen/schema.js\";\n\nexport async function getDriveIdBySlug(driveUrl: string, slug: string) {\n if (!driveUrl) {\n return;\n }\n\n const urlParts = driveUrl.split(\"/\");\n urlParts.pop(); // remove id\n urlParts.pop(); // remove /d\n urlParts.push(\"drives\"); // add /drives\n const drivesUrl = urlParts.join(\"/\");\n const result = await fetch(drivesUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n query: `\n query getDriveIdBySlug($slug: String!) {\n driveIdBySlug(slug: $slug)\n }\n `,\n variables: {\n slug,\n },\n }),\n });\n\n const data = (await result.json()) as {\n data: { driveIdBySlug: string };\n };\n\n return data.data.driveIdBySlug;\n}\n\nexport function getSlugFromDriveUrl(driveUrl: string) {\n const urlParts = driveUrl.split(\"/\");\n return urlParts.pop();\n}\n\nexport function getSwitchboardGatewayUrlFromDriveUrl(driveUrl: string) {\n const urlParts = driveUrl.split(\"/\");\n urlParts.pop(); // remove id\n urlParts.pop(); // remove /d\n urlParts.push(\"graphql\"); // add /graphql\n return urlParts.join(\"/\");\n}\n\nexport function getDocumentGraphqlQuery() {\n const loc = GetDocumentWithOperationsDocument.loc;\n if (!loc) {\n throw new Error(\n \"GetDocumentWithOperationsDocument is misconfigured, loc is missing.\",\n );\n }\n return loc.source.body;\n}\n\nexport function buildDocumentSubgraphQuery(\n identifier: string,\n authToken?: string,\n) {\n const query = getDocumentGraphqlQuery();\n const variables = { identifier };\n const headers = authToken\n ? {\n Authorization: `Bearer ${authToken}`,\n }\n : undefined;\n\n const payload: Record<string, string> = {\n document: query.trim(),\n variables: JSON.stringify(variables, null, 2),\n };\n if (headers) {\n payload.headers = JSON.stringify(headers);\n }\n return lzString.compressToEncodedURIComponent(JSON.stringify(payload));\n}\n\nexport function buildDocumentSubgraphUrl(\n driveUrl: string,\n identifier: string,\n authToken?: string,\n) {\n const encodedQuery = buildDocumentSubgraphQuery(identifier, authToken);\n return `${driveUrl}?explorerURLState=${encodedQuery}`;\n}\n","import type { IDocumentModelRegistry } from \"@powerhousedao/reactor\";\nimport type {\n Action,\n PHDocument,\n UpgradeTransition,\n} from \"@powerhousedao/shared/document-model\";\n\nconst NON_DOMAIN_SCOPES = new Set([\"auth\", \"document\"]);\n\nexport type UpgradeStepInfo = {\n toVersion: number;\n description: string;\n};\n\nexport type DocumentUpgradePreview = {\n fromVersion: number;\n toVersion: number;\n steps: UpgradeStepInfo[];\n addedFields: string[];\n removedFields: string[];\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction walkShapes(\n before: unknown,\n after: unknown,\n path: string,\n added: string[],\n removed: string[],\n): void {\n if (isPlainObject(before) && isPlainObject(after)) {\n const beforeKeys = Object.keys(before);\n const afterKeys = Object.keys(after);\n for (const key of afterKeys) {\n const childPath = path ? `${path}.${key}` : key;\n if (!beforeKeys.includes(key)) {\n added.push(childPath);\n continue;\n }\n walkShapes(before[key], after[key], childPath, added, removed);\n }\n for (const key of beforeKeys) {\n if (!afterKeys.includes(key)) {\n removed.push(path ? `${path}.${key}` : key);\n }\n }\n return;\n }\n\n if (Array.isArray(before) && Array.isArray(after)) {\n if (before.length > 0 && after.length > 0) {\n walkShapes(before[0], after[0], `${path}[]`, added, removed);\n }\n }\n}\n\n/**\n * Recursively diffs the structural shape of two values, returning dot-paths\n * of keys present in `after` but not `before` (added) and vice versa\n * (removed). Array fields are compared by the shape of a representative\n * element (the first element on each side, when both sides have one) using\n * the `path[]` notation, e.g. `todos[].status`. Only key presence is\n * compared — array length and primitive values are ignored.\n */\nexport function diffStateShapes(\n before: unknown,\n after: unknown,\n): { added: string[]; removed: string[] } {\n const added: string[] = [];\n const removed: string[] = [];\n walkShapes(before, after, \"\", added, removed);\n return { added, removed };\n}\n\n/**\n * Computes a dry-run preview of upgrading `document` to the latest\n * registered version of its document model: the version jump, the upgrade\n * steps that will run, and which state fields will be added or removed.\n * Applies the upgrade reducers against a deep clone of the document, so the\n * original is left untouched.\n *\n * Returns undefined when the registry is unavailable, the document is\n * already at (or above) the latest registered version, or the upgrade path\n * cannot be computed.\n */\nexport function getDocumentUpgradePreview(\n document: PHDocument,\n registry: IDocumentModelRegistry | undefined,\n): DocumentUpgradePreview | undefined {\n if (!registry) {\n return undefined;\n }\n\n const documentType = document.header.documentType;\n const fromVersion = document.state.document.version || 1;\n let latestVersion: number;\n try {\n latestVersion = registry.getLatestVersion(documentType);\n } catch {\n return undefined;\n }\n if (fromVersion >= latestVersion) {\n return undefined;\n }\n\n let transitions: UpgradeTransition[];\n try {\n transitions = registry.computeUpgradePath(\n documentType,\n fromVersion,\n latestVersion,\n );\n } catch {\n return undefined;\n }\n\n const stubAction: Action = {\n id: \"\",\n type: \"UPGRADE_DOCUMENT\",\n scope: \"document\",\n timestampUtcMs: \"\",\n input: {\n documentId: document.header.id,\n model: documentType,\n fromVersion,\n toVersion: latestVersion,\n },\n };\n\n // The dry-run executes real upgrade reducers during render; a migration\n // that is not implemented yet (codegen's manual stub throws) must not\n // crash the caller.\n let upgraded = structuredClone(document);\n try {\n for (const transition of transitions) {\n upgraded = transition.upgradeReducer(upgraded, stubAction);\n }\n } catch {\n return undefined;\n }\n\n const addedFields: string[] = [];\n const removedFields: string[] = [];\n const scopes = new Set([\n ...Object.keys(document.state),\n ...Object.keys(upgraded.state),\n ]);\n for (const scope of scopes) {\n if (NON_DOMAIN_SCOPES.has(scope)) {\n continue;\n }\n const beforeScope = (document.state as Record<string, unknown>)[scope];\n const afterScope = (upgraded.state as Record<string, unknown>)[scope];\n const { added, removed } = diffStateShapes(beforeScope, afterScope);\n for (const path of added) {\n addedFields.push(`${scope}.${path}`);\n }\n for (const path of removed) {\n removedFields.push(`${scope}.${path}`);\n }\n }\n\n return {\n fromVersion,\n toVersion: latestVersion,\n steps: transitions.map((transition) => ({\n toVersion: transition.toVersion,\n description: transition.description ?? \"\",\n })),\n addedFields,\n removedFields,\n };\n}\n","import {\n DriveCollectionId,\n type GqlRequestChannel,\n} from \"@powerhousedao/reactor\";\nimport type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { useMemo } from \"react\";\nimport { isDefined } from \"remeda\";\nimport { buildDocumentSubgraphUrl } from \"../utils/index.js\";\nimport { useRenown, useSyncList, useUser } from \"./connect.js\";\nimport { useSelectedDriveSafe } from \"./selected-drive.js\";\n\n/**\n * Hook that returns a function to generate a document's switchboard URL.\n * Only returns a function for documents in remote drives.\n * Returns null for local drives or when the document/drive cannot be determined.\n *\n * The returned function generates a fresh bearer token and builds the switchboard URL\n * with authentication when called.\n *\n * @param document - The document to create a switchboard URL generator for\n * @returns An async function that returns the switchboard URL, or null if not applicable\n */\nexport function useGetSwitchboardLink(\n document: PHDocument | undefined,\n): (() => Promise<string>) | null {\n const [drive] = useSelectedDriveSafe();\n const remotes = useSyncList();\n\n const isRemoteDrive = useMemo(() => {\n if (!isDefined(drive)) return false;\n\n return remotes.some((remote) =>\n remote.meta.collectionId.equals(\n DriveCollectionId.forDrive(drive.header.id),\n ),\n );\n }, [remotes, drive]);\n const remoteUrl = useMemo(() => {\n if (!isDefined(drive)) return null;\n\n try {\n const remote = remotes.find((remote) =>\n remote.meta.collectionId.equals(\n DriveCollectionId.forDrive(drive.header.id),\n ),\n );\n\n const channelUrl = (remote?.channel as GqlRequestChannel | undefined)\n ?.config.url;\n if (typeof channelUrl === \"string\") {\n return channelUrl;\n }\n\n return null;\n } catch (error) {\n console.error(\"Error determining remote URL:\", error);\n return null;\n }\n }, [remotes, drive]);\n const renown = useRenown();\n const user = useUser();\n\n return useMemo(() => {\n if (!isRemoteDrive || !document?.header.id || !remoteUrl) {\n return null;\n }\n\n return async () => {\n // Get bearer token if user is authenticated\n const token = user?.address\n ? await renown?.getBearerToken({\n expiresIn: 600,\n aud: remoteUrl,\n })\n : undefined;\n\n // Build and return the switchboard URL with the document subgraph query\n return buildDocumentSubgraphUrl(remoteUrl, document.header.id, token);\n };\n }, [isRemoteDrive, remoteUrl, document, user, renown]);\n}\n","import { addFileWithProgress } from \"../actions/document.js\";\nimport type {\n ConflictResolution,\n FileUploadProgressCallback,\n UseOnDropFile,\n} from \"../types/upload.js\";\nimport { useDocumentTypes } from \"./document-types.js\";\nimport { useSelectedDriveId } from \"./selected-drive.js\";\nimport { useSelectedFolder } from \"./selected-folder.js\";\n\nexport const useOnDropFile: UseOnDropFile = (\n documentTypesOverride?: string[],\n) => {\n const selectedDriveId = useSelectedDriveId();\n const selectedFolder = useSelectedFolder();\n const documentTypes = useDocumentTypes();\n\n const onDropFile = async (\n file: File,\n onProgress?: FileUploadProgressCallback,\n resolveConflict?: ConflictResolution,\n ) => {\n if (!selectedDriveId) {\n console.warn(\"No selected drive - upload skipped\");\n return;\n }\n\n const fileName = file.name.replace(/\\..+/gim, \"\");\n const targetNodeId = selectedFolder?.id;\n\n // Return the FileNode directly from addFileWithProgress\n return await addFileWithProgress(\n file,\n selectedDriveId,\n fileName,\n targetNodeId,\n onProgress,\n documentTypesOverride ?? documentTypes,\n resolveConflict,\n );\n };\n\n return onDropFile;\n};\n","import { useAllowList } from \"./connect.js\";\nimport { useUser } from \"./renown.js\";\nexport function useUserPermissions() {\n const user = useUser();\n const allowList = useAllowList();\n if (!allowList) {\n return {\n isAllowedToCreateDocuments: true,\n isAllowedToEditDocuments: true,\n };\n }\n\n return {\n isAllowedToCreateDocuments: allowList.includes(user?.address ?? \"\"),\n isAllowedToEditDocuments: allowList.includes(user?.address ?? \"\"),\n };\n}\n","import {\n createAttachmentClient,\n type AttachmentDownloadInput,\n type AttachmentHeader,\n type IAttachmentClient,\n type PreprocessResult,\n} from \"@powerhousedao/reactor-attachments/client\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { useAttachmentService } from \"./attachment-service.js\";\n\n/** Returns an IAttachmentClient wrapping the current IAttachmentService, or undefined if none is set. */\nexport function useAttachments(): IAttachmentClient | undefined {\n const service = useAttachmentService();\n return useMemo(\n () => (service ? createAttachmentClient(service) : undefined),\n [service],\n );\n}\n\nexport type UseAttachmentPreviewInput = {\n documentId: string;\n /** Pass null/undefined to render nothing (the hook stays idle). */\n ref: AttachmentDownloadInput[\"ref\"] | null | undefined;\n /**\n * How many times a failed attempt is retried before the hook settles on\n * error. Retrying matters because a freshly attached ref is not\n * immediately downloadable: the server's reference index authorizes\n * downloads and only learns the (document, ref) pair once the operation\n * has synced and been projected. Defaults to 3.\n */\n retries?: number;\n /** Fixed delay between attempts, in milliseconds. Defaults to 3000. */\n retryDelayMs?: number;\n};\n\nconst DEFAULT_PREVIEW_RETRIES = 3;\nconst DEFAULT_PREVIEW_RETRY_DELAY_MS = 3_000;\n\nexport type UseAttachmentPreviewReturn = {\n /** Object URL ready for img/iframe/video src; undefined while loading or on error. */\n url: string | undefined;\n header: AttachmentHeader | undefined;\n loading: boolean;\n error: Error | undefined;\n};\n\n/**\n * Document-authorized inline preview of an attachment. Downloads the bytes\n * through the normal authorized flow, exposes them as an object URL, and\n * revokes it automatically on unmount and whenever documentId/ref change —\n * editors never touch blobs or URL lifecycles. Failed attempts are retried\n * (`retries` × `retryDelayMs`) so a preview requested right after attaching\n * appears as soon as the server's reference index catches up.\n */\nexport function useAttachmentPreview({\n documentId,\n ref,\n retries = DEFAULT_PREVIEW_RETRIES,\n retryDelayMs = DEFAULT_PREVIEW_RETRY_DELAY_MS,\n}: UseAttachmentPreviewInput): UseAttachmentPreviewReturn {\n const client = useAttachments();\n const [state, setState] = useState<UseAttachmentPreviewReturn>({\n url: undefined,\n header: undefined,\n loading: false,\n error: undefined,\n });\n\n useEffect(() => {\n if (!client || !ref) {\n setState({\n url: undefined,\n header: undefined,\n loading: false,\n error: undefined,\n });\n return;\n }\n let cancelled = false;\n let revoke: (() => void) | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let attempt = 0;\n setState({\n url: undefined,\n header: undefined,\n loading: true,\n error: undefined,\n });\n const load = () => {\n client\n .downloadObjectUrl({ documentId, ref })\n .then((result) => {\n if (cancelled) {\n result.revoke();\n return;\n }\n revoke = result.revoke;\n setState({\n url: result.url,\n header: result.header,\n loading: false,\n error: undefined,\n });\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n if (attempt < retries) {\n attempt += 1;\n timer = setTimeout(load, retryDelayMs);\n return; // stay in loading state while the index catches up\n }\n setState({\n url: undefined,\n header: undefined,\n loading: false,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n });\n };\n load();\n return () => {\n cancelled = true;\n if (timer !== undefined) clearTimeout(timer);\n revoke?.();\n };\n }, [client, documentId, ref, retries, retryDelayMs]);\n\n return state;\n}\n\n/** Upload lifecycle status. progress is coarse (0 before/during, 1 on Done) because RemoteAttachmentUpload buffers the full body before issuing a single PUT. */\nexport enum UploadStatus {\n None = \"None\",\n Hashing = \"Hashing\",\n Uploading = \"Uploading\",\n Done = \"Done\",\n Error = \"Error\",\n}\n\nexport type UseAttachmentUploadReturn = {\n preprocess: (file: Blob) => Promise<PreprocessResult>;\n upload: (results: PreprocessResult) => Promise<void>;\n status: UploadStatus;\n progress: number;\n error: Error | undefined;\n};\n\n/** Hook for managing the full attachment preprocess + upload lifecycle. preprocess and upload callbacks are stable (useCallback) and depend only on the current IAttachmentClient reference. */\nexport function useAttachmentUpload(): UseAttachmentUploadReturn {\n const [status, setStatus] = useState<UploadStatus>(UploadStatus.None);\n const [progress, setProgress] = useState(0);\n const [error, setError] = useState<Error | undefined>(undefined);\n const client = useAttachments();\n\n const preprocess = useCallback(\n async (file: Blob): Promise<PreprocessResult> => {\n if (!client) throw new Error(\"AttachmentClient not available\");\n setError(undefined);\n setStatus(UploadStatus.Hashing);\n try {\n return await client.preprocess(file);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setStatus(UploadStatus.Error);\n throw err;\n }\n },\n [client],\n );\n\n const upload = useCallback(\n async (results: PreprocessResult): Promise<void> => {\n if (!client) throw new Error(\"AttachmentClient not available\");\n setError(undefined);\n setStatus(UploadStatus.Uploading);\n setProgress(0);\n try {\n await client.reserve(results.options, (handle) =>\n handle.send(results.stream()),\n );\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setStatus(UploadStatus.Error);\n throw err;\n }\n setProgress(1);\n setStatus(UploadStatus.Done);\n },\n [client],\n );\n\n return { preprocess, upload, status, progress, error };\n}\n","import type { PGlite } from \"@electric-sql/pglite\";\nimport { REACTOR_SCHEMA } from \"@powerhousedao/reactor\";\n\nasync function dropTablesInSchema(pg: PGlite, schema: string): Promise<void> {\n await pg.exec(`\nDO $$\nDECLARE\n _schemaname text := '${schema}';\n _tablename text;\nBEGIN\n FOR _tablename IN SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = _schemaname LOOP\n RAISE INFO 'Dropping table %.%', _schemaname, _tablename;\n EXECUTE format('DROP TABLE %I.%I CASCADE;', _schemaname, _tablename);\n END LOOP;\n IF NOT FOUND THEN\n RAISE WARNING 'Schema % does not exist', _schemaname;\n END IF;\nEND $$;\n`);\n}\n\nexport async function truncateAllTables(\n pg: PGlite,\n schema: string = REACTOR_SCHEMA,\n): Promise<void> {\n await dropTablesInSchema(pg, schema);\n}\n\nexport async function dropAllReactorStorage(pg: PGlite): Promise<void> {\n await dropTablesInSchema(pg, REACTOR_SCHEMA);\n\n // legacy\n await dropTablesInSchema(pg, \"public\");\n}\n","import type { IReactorClient } from \"@powerhousedao/reactor\";\nimport { setDrives } from \"./hooks/drives.js\";\nimport { getDrives } from \"./utils/drives.js\";\n\nexport type ReactorDefaultDrivesConfig = {\n defaultDrivesUrl?: string[];\n};\n\nexport type RefreshReactorDataConfig = {\n debounceDelayMs?: number;\n immediateThresholdMs?: number;\n};\n\nconst DEFAULT_DEBOUNCE_DELAY_MS = 200;\nconst DEFAULT_IMMEDIATE_THRESHOLD_MS = 1000;\n\nasync function _refreshReactorData(reactor: IReactorClient) {\n const drives = await getDrives(reactor);\n\n setDrives(drives);\n}\n\nasync function _refreshReactorDataClient(reactor: IReactorClient | undefined) {\n if (!reactor) return;\n\n setDrives(await getDrives(reactor));\n}\n\nfunction createDebouncedRefreshReactorData(\n debounceDelayMs = DEFAULT_DEBOUNCE_DELAY_MS,\n immediateThresholdMs = DEFAULT_IMMEDIATE_THRESHOLD_MS,\n) {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n let lastRefreshTime = 0;\n\n return (reactor: IReactorClient, immediate = false) => {\n const now = Date.now();\n const timeSinceLastRefresh = now - lastRefreshTime;\n\n if (timeout !== null) {\n clearTimeout(timeout);\n }\n\n if (immediate || timeSinceLastRefresh >= immediateThresholdMs) {\n lastRefreshTime = now;\n return _refreshReactorData(reactor);\n }\n\n return new Promise<void>((resolve) => {\n timeout = setTimeout(() => {\n lastRefreshTime = Date.now();\n void _refreshReactorData(reactor).then(resolve);\n }, debounceDelayMs);\n });\n };\n}\n\nfunction createDebouncedRefreshReactorDataClient(\n debounceDelayMs = DEFAULT_DEBOUNCE_DELAY_MS,\n immediateThresholdMs = DEFAULT_IMMEDIATE_THRESHOLD_MS,\n) {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n let lastRefreshTime = 0;\n\n return (reactor: IReactorClient | undefined, immediate = false) => {\n const now = Date.now();\n const timeSinceLastRefresh = now - lastRefreshTime;\n\n if (timeout !== null) {\n clearTimeout(timeout);\n }\n\n if (immediate || timeSinceLastRefresh >= immediateThresholdMs) {\n lastRefreshTime = now;\n return _refreshReactorDataClient(reactor);\n }\n\n return new Promise<void>((resolve) => {\n timeout = setTimeout(() => {\n lastRefreshTime = Date.now();\n void _refreshReactorDataClient(reactor).then(resolve);\n }, debounceDelayMs);\n });\n };\n}\n\nexport const refreshReactorData = createDebouncedRefreshReactorData();\nexport const refreshReactorDataClient =\n createDebouncedRefreshReactorDataClient();\n","import type { Action } from \"@powerhousedao/shared/document-model\";\nimport type { TrackedAction } from \"./types.js\";\n\n/**\n * Tracks pending actions with their operation context (prevOpHash, prevOpIndex).\n * Actions are accumulated until flushed (on push).\n */\nexport class ActionTracker {\n private pending: TrackedAction[] = [];\n\n /** Track a new action with its operation context. */\n track(action: Action, prevOpHash: string, prevOpIndex: number): void {\n this.pending.push({ action, prevOpHash, prevOpIndex });\n }\n\n /** Flush all pending actions and return them. Clears the internal queue. */\n flush(): TrackedAction[] {\n const actions = this.pending;\n this.pending = [];\n return actions;\n }\n\n /** Number of pending actions. */\n get count(): number {\n return this.pending.length;\n }\n\n /** Prepend previously flushed actions back to the queue (for retry on failure). */\n restore(actions: TrackedAction[]): void {\n this.pending = [...actions, ...this.pending];\n }\n\n /** Clear all pending actions without returning them. */\n clear(): void {\n this.pending = [];\n }\n}\n","import type {\n GetDocumentResult,\n GetDocumentWithOperationsResult,\n GetOperationsResult,\n IRemoteClient,\n PropagationMode,\n RemoteControllerGraphQLClient,\n RemoteDocumentData,\n RemoteOperation,\n RemoteOperationResultPage,\n} from \"./types.js\";\n\n/**\n * Thin facade over the GraphQL SDK for remote document operations.\n */\nconst DEFAULT_PAGE_SIZE = 100;\n\nexport class RemoteClient implements IRemoteClient {\n private readonly pageSize: number;\n\n constructor(\n private readonly client: RemoteControllerGraphQLClient,\n pageSize?: number,\n ) {\n this.pageSize = pageSize ?? DEFAULT_PAGE_SIZE;\n }\n\n /** Fetch a document by identifier. Returns null if not found. */\n async getDocument(\n identifier: string,\n branch?: string,\n ): Promise<GetDocumentResult | null> {\n const result = await this.client.GetDocument({\n identifier,\n view: branch ? { branch } : undefined,\n });\n return result.document ?? null;\n }\n\n /**\n * Fetch a document and its operations.\n *\n * When scopes are provided and BatchGetDocumentWithOperations is available,\n * fetches the document and per-scope operations in a single HTTP request.\n * Otherwise falls back to GetDocumentWithOperations for the first page,\n * then paginates remaining operations per scope.\n */\n async getDocumentWithOperations(\n identifier: string,\n branch?: string,\n sinceRevision?: Record<string, number>,\n scopes?: string[],\n ): Promise<GetDocumentWithOperationsResult | null> {\n // Fast path: batch document + per-scope operations in one request\n if (\n this.client.BatchGetDocumentWithOperations &&\n scopes &&\n scopes.length > 0\n ) {\n return this.batchGetDocumentWithOperations(\n identifier,\n branch,\n sinceRevision,\n scopes,\n );\n }\n\n // Standard path: GetDocumentWithOperations + paginate if needed\n const result = await this.client.GetDocumentWithOperations({\n identifier,\n view: branch ? { branch } : undefined,\n operationsPaging: {\n limit: this.pageSize,\n cursor: null,\n },\n });\n\n if (!result.document) return null;\n\n const doc = result.document.document;\n const opsPage = doc.operations;\n const operationsByScope: Record<string, RemoteOperation[]> = {};\n\n if (opsPage) {\n for (const op of opsPage.items) {\n (operationsByScope[op.action.scope] ??= []).push(op);\n }\n }\n\n // Check if we have all expected operations by comparing against revisionsList\n const expectedTotal = doc.revisionsList.reduce(\n (sum, r) => sum + r.revision,\n 0,\n );\n const fetchedTotal = opsPage?.items.length ?? 0;\n\n if (fetchedTotal >= expectedTotal) {\n return {\n document: doc,\n childIds: result.document.childIds,\n operations: { operationsByScope },\n };\n }\n\n // Missing operations — fetch all per scope\n const allScopes = doc.revisionsList.map((r) => r.scope);\n const allOps = await this.getAllOperations(\n doc.id,\n branch,\n sinceRevision,\n allScopes,\n );\n\n return {\n document: doc,\n childIds: result.document.childIds,\n operations: allOps,\n };\n }\n\n /**\n * Fetch document + per-scope operations in a single HTTP request\n * via BatchGetDocumentWithOperations, then paginate any remaining pages.\n */\n private async batchGetDocumentWithOperations(\n identifier: string,\n branch: string | undefined,\n sinceRevision: Record<string, number> | undefined,\n scopes: string[],\n ): Promise<GetDocumentWithOperationsResult | null> {\n const view = branch ? { branch } : undefined;\n const filters = scopes.map((scope) => ({\n documentId: identifier,\n branch: branch ?? null,\n sinceRevision: sinceRevision?.[scope] ?? 0,\n scopes: [scope],\n }));\n const pagings = scopes.map(() => ({\n limit: this.pageSize,\n cursor: null as string | null,\n }));\n\n const result = await this.client.BatchGetDocumentWithOperations!(\n identifier,\n view,\n filters,\n pagings,\n );\n\n if (!result.document) return null;\n\n const operationsByScope: Record<string, RemoteOperation[]> = {};\n let pending: {\n scope: string;\n filter: (typeof filters)[0];\n cursor: string;\n }[] = [];\n\n for (let i = 0; i < scopes.length; i++) {\n const page = result.operations[i];\n for (const op of page.items) {\n (operationsByScope[op.action.scope] ??= []).push(op);\n }\n if (page.hasNextPage && page.cursor) {\n pending.push({\n scope: scopes[i],\n filter: filters[i],\n cursor: page.cursor,\n });\n }\n }\n\n // Continue pagination for scopes with more pages\n while (pending.length > 0) {\n const pages = await this.fetchOperationPages(\n pending.map((p) => p.filter),\n pending.map((p) => ({ limit: this.pageSize, cursor: p.cursor })),\n );\n\n const nextPending: typeof pending = [];\n for (let i = 0; i < pending.length; i++) {\n const page = pages[i];\n for (const op of page.items) {\n (operationsByScope[op.action.scope] ??= []).push(op);\n }\n if (page.hasNextPage && page.cursor) {\n nextPending.push({ ...pending[i], cursor: page.cursor });\n }\n }\n pending = nextPending;\n }\n\n return {\n document: result.document.document,\n childIds: result.document.childIds,\n operations: { operationsByScope },\n };\n }\n\n /**\n * Fetch all operations for a document, paginating through all pages.\n * Each scope is queried individually because the API only returns\n * pagination cursors for single-scope queries.\n */\n async getAllOperations(\n documentId: string,\n branch?: string,\n sinceRevision?: Record<string, number>,\n scopes?: string[],\n ): Promise<GetOperationsResult> {\n // When scopes are specified, query each scope in parallel.\n // Uses a single composed request per pagination round when available.\n if (scopes && scopes.length > 0) {\n const operationsByScope: Record<string, RemoteOperation[]> = {};\n\n // Tracks scopes still being paginated, each with its own filter and cursor\n let pending = scopes.map((scope) => ({\n scope,\n filter: {\n documentId,\n branch: branch ?? null,\n sinceRevision: sinceRevision?.[scope] ?? 0,\n scopes: [scope],\n },\n cursor: null as string | null,\n }));\n\n while (pending.length > 0) {\n const pages = await this.fetchOperationPages(\n pending.map((p) => p.filter),\n pending.map((p) => ({ limit: this.pageSize, cursor: p.cursor })),\n );\n\n const nextPending: typeof pending = [];\n\n for (let i = 0; i < pending.length; i++) {\n const page = pages[i];\n for (const op of page.items) {\n (operationsByScope[op.action.scope] ??= []).push(op);\n }\n if (page.hasNextPage && page.cursor) {\n nextPending.push({ ...pending[i], cursor: page.cursor });\n }\n }\n\n pending = nextPending;\n }\n\n return { operationsByScope };\n }\n\n // No scopes specified — single query for all scopes (no per-scope sinceRevision)\n return this.fetchOperationsForScope(documentId, branch);\n }\n\n /**\n * Fetch one page of operations per filter.\n * Uses the composed query (single HTTP request) when available,\n * otherwise falls back to parallel individual requests.\n */\n private async fetchOperationPages(\n filters: Parameters<\n RemoteControllerGraphQLClient[\"GetDocumentOperations\"]\n >[0][\"filter\"][],\n pagings: Parameters<\n RemoteControllerGraphQLClient[\"GetDocumentOperations\"]\n >[0][\"paging\"][],\n ): Promise<RemoteOperationResultPage[]> {\n if (this.client.BatchGetDocumentOperations) {\n return this.client.BatchGetDocumentOperations(filters, pagings);\n }\n\n return Promise.all(\n filters.map((filter, i) =>\n this.client\n .GetDocumentOperations({ filter, paging: pagings[i] })\n .then((r) => r.documentOperations),\n ),\n );\n }\n\n /** Fetch all pages of operations for a single scope (or all scopes if none specified). */\n private async fetchOperationsForScope(\n documentId: string,\n branch?: string,\n sinceRevision?: number,\n scope?: string,\n ): Promise<GetOperationsResult> {\n const operationsByScope: Record<string, RemoteOperation[]> = {};\n let cursor: string | null | undefined;\n let hasNextPage = true;\n\n while (hasNextPage) {\n const result = await this.client.GetDocumentOperations({\n filter: {\n documentId,\n branch: branch ?? null,\n sinceRevision: sinceRevision ?? 0,\n scopes: scope ? [scope] : null,\n },\n paging: {\n limit: this.pageSize,\n cursor: cursor ?? null,\n },\n });\n\n const page = result.documentOperations;\n\n for (const op of page.items) {\n const s = op.action.scope;\n (operationsByScope[s] ??= []).push(op);\n }\n\n hasNextPage = page.hasNextPage;\n cursor = page.cursor;\n }\n\n return { operationsByScope };\n }\n\n /** Push actions to an existing document via MutateDocument. */\n async pushActions(\n documentIdentifier: string,\n actions: ReadonlyArray<NonNullable<unknown>>,\n branch?: string,\n ): Promise<RemoteDocumentData> {\n const result = await this.client.MutateDocument({\n documentIdentifier,\n actions,\n view: branch ? { branch } : undefined,\n });\n return result.mutateDocument;\n }\n\n /** Create a new document on the remote. */\n async createDocument(\n document: NonNullable<unknown>,\n parentIdentifier?: string,\n ): Promise<RemoteDocumentData> {\n const result = await this.client.CreateDocument({\n document,\n parentIdentifier: parentIdentifier ?? null,\n });\n return result.createDocument;\n }\n\n /** Create an empty document of a given type on the remote. */\n async createEmptyDocument(\n documentType: string,\n parentIdentifier?: string,\n ): Promise<RemoteDocumentData> {\n const result = await this.client.CreateEmptyDocument({\n documentType,\n parentIdentifier: parentIdentifier ?? null,\n });\n return result.createEmptyDocument;\n }\n\n /** Delete a document on the remote. Returns true if successful. */\n async deleteDocument(\n identifier: string,\n propagate?: PropagationMode,\n ): Promise<boolean> {\n const result = await this.client.DeleteDocument({\n identifier,\n propagate,\n });\n return result.deleteDocument;\n }\n}\n","import type {\n Action,\n DocumentOperations,\n Operation,\n PHBaseState,\n PHDocument,\n PHDocumentHeader,\n} from \"@powerhousedao/shared/document-model\";\nimport type { PHDocumentController } from \"document-model\";\nimport { ActionTracker } from \"./action-tracker.js\";\nimport { RemoteClient } from \"./remote-client.js\";\nimport type {\n ConflictStrategy,\n DocumentChangeListener,\n IRemoteClient,\n IRemoteController,\n PropagationMode,\n PushResult,\n RemoteControllerOptions,\n RemoteDocumentChangeEvent,\n RemoteDocumentData,\n RemoteOperation,\n SyncStatus,\n TrackedAction,\n} from \"./types.js\";\nimport {\n ConflictError,\n buildPulledDocument,\n convertRemoteOperations,\n extractRevisionMap,\n hasRevisionConflict,\n screamingSnakeToCamel,\n} from \"./utils.js\";\n\n/** Extract TState from a PHDocumentController subclass. */\ntype InferState<C> = C extends PHDocumentController<infer S> ? S : never;\n\n/**\n * Extract action methods from a controller type.\n * These are the dynamically-added methods (not on the base PHDocumentController prototype).\n */\ntype ActionMethodsOf<C, TRemote> = {\n [K in Exclude<keyof C, keyof PHDocumentController<any>>]: C[K] extends (\n input: infer I,\n ) => unknown\n ? (input: I) => TRemote & ActionMethodsOf<C, TRemote>\n : C[K];\n};\n\n/** The full return type: RemoteDocumentController + action methods. */\nexport type RemoteDocumentControllerWith<C extends PHDocumentController<any>> =\n RemoteDocumentController<C> & ActionMethodsOf<C, RemoteDocumentController<C>>;\n\n/**\n * A controller that wraps a PHDocumentController with remote push/pull capabilities.\n * Composes a local controller and adds GraphQL-based sync with a reactor server.\n */\nexport class RemoteDocumentController<\n TController extends PHDocumentController<any>,\n> implements IRemoteController<InferState<TController>> {\n private inner: TController;\n private readonly remoteClient: IRemoteClient;\n private readonly tracker = new ActionTracker();\n private readonly options: RemoteControllerOptions;\n private documentId: string;\n private remoteRevision: Record<string, number> = {};\n private hasPulled = false;\n private pushScheduled = false;\n private pushQueue: Promise<void> = Promise.resolve();\n private listeners: DocumentChangeListener[] = [];\n\n private constructor(inner: TController, options: RemoteControllerOptions) {\n this.inner = inner;\n this.options = options;\n this.documentId = options.documentId ?? \"\";\n this.remoteClient = new RemoteClient(\n options.client,\n options.operationsPageSize,\n );\n\n this.setupActionInterceptors();\n }\n\n // --- State access (delegated to inner controller) ---\n\n get header(): PHDocumentHeader {\n return this.inner.header;\n }\n\n get state(): InferState<TController> {\n return this.inner.state as InferState<TController>;\n }\n\n get operations(): DocumentOperations {\n return this.inner.operations;\n }\n\n get document(): PHDocument<InferState<TController>> {\n return this.inner.document as PHDocument<InferState<TController>>;\n }\n\n get status(): SyncStatus {\n return {\n pendingActionCount: this.tracker.count,\n connected: this.documentId !== \"\",\n documentId: this.documentId,\n remoteRevision: { ...this.remoteRevision },\n };\n }\n\n /** Register a listener for document changes. Returns an unsubscribe function. */\n onChange(listener: DocumentChangeListener): () => void {\n this.listeners.push(listener);\n return () => {\n this.listeners = this.listeners.filter((l) => l !== listener);\n };\n }\n\n private notifyListeners(source: RemoteDocumentChangeEvent[\"source\"]): void {\n if (this.listeners.length === 0) return;\n const event: RemoteDocumentChangeEvent = {\n source,\n document: this.document,\n };\n for (const listener of this.listeners) {\n listener(event);\n }\n }\n\n // --- Remote operations ---\n\n /** Push all pending actions to remote, then pull latest state. */\n async push(): Promise<PushResult> {\n let tracked = this.tracker.flush();\n\n if (tracked.length === 0 && this.documentId !== \"\") {\n // Nothing to push, just pull (reuses the fetched document)\n const remoteDocument = await this.pull();\n return {\n remoteDocument,\n actionCount: 0,\n operations: [],\n };\n }\n\n try {\n await this.ensureRemoteDocument();\n\n // Conflict detection: check if remote has changed since last pull\n if (this.options.onConflict && tracked.length > 0) {\n tracked = await this.handleConflicts(tracked, this.options.onConflict);\n }\n } catch (error) {\n // Pre-push failure: restore actions so they can be retried\n this.tracker.restore(tracked);\n throw error;\n }\n\n let pushedActions: Action[] = [];\n\n try {\n if (tracked.length > 0) {\n const actions = await this.prepareActionsForPush(tracked);\n pushedActions = actions;\n\n await this.remoteClient.pushActions(\n this.documentId,\n actions,\n this.options.branch,\n );\n }\n } catch (error) {\n // Push failed: restore actions so they can be retried\n this.tracker.restore(tracked);\n throw error;\n }\n\n // Pull remote state to reconcile (remote is source of truth).\n // If this fails, actions were already pushed — do NOT restore them.\n const remoteDocument = await this.pull();\n\n return {\n remoteDocument,\n actionCount: tracked.length,\n operations: pushedActions,\n };\n }\n\n /** Delete the document on the remote. */\n async delete(propagate?: PropagationMode): Promise<boolean> {\n if (this.documentId === \"\") {\n throw new Error(\"Cannot delete: no document ID set\");\n }\n const result = await this.remoteClient.deleteDocument(\n this.documentId,\n propagate,\n );\n return result;\n }\n\n /** Pull latest state from remote, replacing local document. Returns the remote document data. */\n async pull(): Promise<RemoteDocumentData> {\n if (this.documentId === \"\") {\n throw new Error(\"Cannot pull: no document ID set\");\n }\n\n const { remoteDoc, operations } = await this.fetchDocumentAndOperations();\n\n // Get module from inner controller\n const initialDoc = this.inner.module.utils.createDocument();\n const pulledDocument = buildPulledDocument(\n remoteDoc,\n operations,\n initialDoc,\n this.options.branch ?? \"main\",\n );\n\n // Recreate inner controller with pulled document\n const ControllerClass = this.inner.constructor as new (\n doc?: PHDocument<PHBaseState>,\n ) => TController;\n this.inner = new ControllerClass(pulledDocument);\n\n // Re-setup interceptors on the new inner instance\n this.setupActionInterceptors();\n\n // Clear tracker (remote is source of truth)\n this.tracker.clear();\n\n // Update remote revision\n this.remoteRevision = extractRevisionMap(remoteDoc.revisionsList);\n\n this.notifyListeners(\"pull\");\n\n return remoteDoc;\n }\n\n // --- Static factories ---\n\n /**\n * Pull an existing document from remote and create a controller for it.\n */\n static async pull<C extends PHDocumentController<any>>(\n ControllerClass: new (doc?: PHDocument<any>) => C,\n options: RemoteControllerOptions,\n ): Promise<RemoteDocumentControllerWith<C>> {\n // Create a temporary instance to access the module\n const temp = new ControllerClass();\n const remote = new RemoteDocumentController(temp, options);\n\n if (options.documentId) {\n await remote.pull();\n }\n\n return remote as RemoteDocumentControllerWith<C>;\n }\n\n /**\n * Wrap an existing controller instance with remote capabilities.\n * Pending local actions on the inner controller are NOT tracked\n * (only new actions through the remote controller are tracked).\n */\n static from<C extends PHDocumentController<any>>(\n controller: C,\n options: RemoteControllerOptions,\n ): RemoteDocumentControllerWith<C> {\n return new RemoteDocumentController(\n controller,\n options,\n ) as RemoteDocumentControllerWith<C>;\n }\n\n // --- Private methods ---\n\n /** Create the document on the remote if it doesn't exist yet. */\n private async ensureRemoteDocument(): Promise<void> {\n if (this.documentId !== \"\") return;\n const remoteDoc = await this.remoteClient.createEmptyDocument(\n this.inner.header.documentType,\n this.options.parentIdentifier,\n );\n this.documentId = remoteDoc.id;\n }\n\n /** Set up interceptors for all action methods on the inner controller. */\n private setupActionInterceptors(): void {\n // Get the module's action keys from the inner controller\n const module = (this.inner as Record<string, unknown>)[\"module\"] as {\n actions: Record<string, unknown>;\n };\n\n for (const actionType in module.actions) {\n // Skip if it's a property on our own class\n if (actionType in RemoteDocumentController.prototype) {\n continue;\n }\n\n Object.defineProperty(this, actionType, {\n value: (input: unknown) => {\n // Snapshot operation counts per scope BEFORE applying\n const opCountsBefore: Record<string, number> = {};\n for (const scope in this.inner.operations) {\n opCountsBefore[scope] = this.inner.operations[scope].length;\n }\n\n // Apply locally via inner controller\n (\n this.inner as unknown as Record<string, (input: unknown) => unknown>\n )[actionType](input);\n\n // Find which scope got the new operation\n const newOp = this.findNewOperation(opCountsBefore);\n\n // Get prevOp in the SAME scope as the new operation\n const prevOp = newOp\n ? this.getLastOperationInScope(newOp.action.scope, newOp)\n : undefined;\n const prevOpHash = prevOp?.hash ?? \"\";\n const prevOpIndex = prevOp?.index ?? -1;\n\n if (!newOp) {\n // Action produced no operation (NOOP) — nothing to track\n return this;\n }\n\n // Track the action for push\n this.tracker.track(newOp.action, prevOpHash, prevOpIndex);\n this.notifyListeners(\"action\");\n\n if (this.options.mode === \"streaming\") {\n this.schedulePush();\n }\n\n return this;\n },\n enumerable: true,\n configurable: true,\n });\n }\n }\n\n /**\n * Find the new operation added after applying an action,\n * by comparing current operation counts against a previous snapshot.\n */\n private findNewOperation(\n opCountsBefore: Record<string, number>,\n ): Operation | undefined {\n const ops = this.inner.operations;\n for (const scope in ops) {\n const scopeOps = ops[scope];\n const prevCount = opCountsBefore[scope] ?? 0;\n if (scopeOps.length > prevCount) {\n return scopeOps[scopeOps.length - 1];\n }\n }\n return undefined;\n }\n\n /**\n * Get the last operation in a specific scope, optionally excluding\n * a given operation (e.g. the one just added).\n */\n private getLastOperationInScope(\n scope: string,\n excludeOp?: Operation,\n ): Operation | undefined {\n const scopeOps = this.inner.operations[scope];\n if (scopeOps.length === 0) return undefined;\n for (let i = scopeOps.length - 1; i >= 0; i--) {\n if (scopeOps[i] !== excludeOp) return scopeOps[i];\n }\n return undefined;\n }\n\n /**\n * Detect and handle conflicts between local pending actions and remote state.\n * Returns the (possibly rebased) tracked actions to push.\n */\n private async handleConflicts(\n localTracked: TrackedAction[],\n strategy: ConflictStrategy,\n ): Promise<TrackedAction[]> {\n // Fetch current remote document to get latest revisions\n const remoteResult = await this.remoteClient.getDocument(\n this.documentId,\n this.options.branch,\n );\n if (!remoteResult) {\n throw new Error(`Document \"${this.documentId}\" not found on remote`);\n }\n\n const currentRevision = extractRevisionMap(\n remoteResult.document.revisionsList,\n );\n\n // Only check scopes that local actions touch\n const localScopes = new Set(localTracked.map((t) => t.action.scope));\n\n if (\n !hasRevisionConflict(currentRevision, this.remoteRevision, localScopes)\n ) {\n return localTracked;\n }\n\n // Fetch new remote operations for conflicting scopes in parallel,\n // using the correct sinceRevision for each scope.\n const conflictingScopes = [...localScopes].filter(\n (scope) =>\n (currentRevision[scope] ?? 0) > (this.remoteRevision[scope] ?? 0),\n );\n const { operationsByScope } = await this.remoteClient.getAllOperations(\n this.documentId,\n this.options.branch,\n this.remoteRevision,\n conflictingScopes,\n );\n const remoteOperations: Record<string, RemoteOperation[]> = {};\n for (const [scope, ops] of Object.entries(operationsByScope)) {\n remoteOperations[scope] = ops;\n }\n\n const conflictInfo = {\n remoteOperations,\n localActions: localTracked,\n knownRevision: { ...this.remoteRevision },\n currentRevision: { ...currentRevision },\n };\n\n if (strategy === \"reject\") {\n throw new ConflictError(conflictInfo);\n }\n\n if (strategy === \"rebase\") {\n return this.pullAndReplay(localTracked.map((t) => t.action));\n }\n\n // Custom merge handler (only possibility left after narrowing)\n const mergedActions = await strategy(conflictInfo);\n return this.pullAndReplay(mergedActions);\n }\n\n /**\n * Pull latest remote state and replay actions through interceptors.\n * Returns newly tracked actions with correct prevOpHash values.\n */\n private async pullAndReplay(actions: Action[]): Promise<TrackedAction[]> {\n await this.pull();\n\n for (const action of actions) {\n // Action types are SCREAMING_SNAKE_CASE but interceptors use camelCase\n const methodName = screamingSnakeToCamel(action.type);\n const method = (\n this as unknown as Record<string, (input: unknown) => unknown>\n )[methodName];\n if (typeof method === \"function\") {\n method.call(this, action.input);\n }\n }\n\n return this.tracker.flush();\n }\n\n /** Prepare actions for push, optionally signing them. */\n private async prepareActionsForPush(\n tracked: { action: Action; prevOpHash: string; prevOpIndex: number }[],\n ) {\n const actions: Action[] = [];\n\n for (const { action, prevOpHash, prevOpIndex } of tracked) {\n let prepared: Action = {\n ...action,\n context: {\n ...action.context,\n prevOpHash,\n prevOpIndex,\n },\n };\n\n if (this.options.signer) {\n prepared = await this.signAction(prepared);\n }\n\n actions.push(prepared);\n }\n\n return actions;\n }\n\n /** Sign an action using the configured signer, preserving existing signatures. */\n private async signAction(action: Action): Promise<Action> {\n const signer = this.options.signer!;\n const signature = await signer.signAction(action);\n const existingSignatures = action.context?.signer?.signatures ?? [];\n return {\n ...action,\n context: {\n ...action.context,\n signer: {\n user: signer.user!,\n app: signer.app!,\n signatures: [...existingSignatures, signature],\n },\n },\n };\n }\n\n /**\n * Fetch document and operations from the remote.\n *\n * On the first pull, uses the combined document+operations query.\n * On subsequent pulls, fetches only new operations per scope using\n * sinceRevision, then merges with existing local operations.\n * Falls back to a full fetch if the merge produces a count mismatch.\n */\n private async fetchDocumentAndOperations(): Promise<{\n remoteDoc: RemoteDocumentData;\n operations: DocumentOperations;\n }> {\n // Incremental fetch: use sinceRevision per scope\n if (this.hasPulled) {\n return this.incrementalFetch();\n }\n\n // Initial fetch: combined document + operations query\n const result = await this.remoteClient.getDocumentWithOperations(\n this.documentId,\n this.options.branch,\n );\n\n if (!result) {\n throw new Error(`Document \"${this.documentId}\" not found on remote`);\n }\n\n this.hasPulled = true;\n return {\n remoteDoc: result.document,\n operations: convertRemoteOperations(result.operations.operationsByScope),\n };\n }\n\n /**\n * Incremental fetch: fetches the document and only new operations per scope\n * using sinceRevision in a single request when possible.\n * Falls back to a full fetch on count mismatch.\n */\n private async incrementalFetch(): Promise<{\n remoteDoc: RemoteDocumentData;\n operations: DocumentOperations;\n }> {\n const scopes = Object.keys(this.remoteRevision);\n\n const result = await this.remoteClient.getDocumentWithOperations(\n this.documentId,\n this.options.branch,\n this.remoteRevision,\n scopes.length > 0 ? scopes : undefined,\n );\n\n if (!result) {\n throw new Error(`Document \"${this.documentId}\" not found on remote`);\n }\n\n const remoteDoc = result.document;\n const expectedRevision = extractRevisionMap(remoteDoc.revisionsList);\n\n const newOps = convertRemoteOperations(result.operations.operationsByScope);\n const merged = this.mergeOperations(this.inner.operations, newOps);\n\n // Validate: merged operation counts must match remote revisions\n if (this.hasExpectedOperationCounts(merged, expectedRevision)) {\n return { remoteDoc, operations: merged };\n }\n\n // Mismatch — do a full fetch\n return this.fullFetch(remoteDoc);\n }\n\n /**\n * Full fetch fallback: fetches all operations from the beginning.\n * Used when an incremental fetch produces a count mismatch.\n */\n private async fullFetch(remoteDoc: RemoteDocumentData): Promise<{\n remoteDoc: RemoteDocumentData;\n operations: DocumentOperations;\n }> {\n const { operationsByScope } = await this.remoteClient.getAllOperations(\n this.documentId,\n this.options.branch,\n );\n\n return {\n remoteDoc,\n operations: convertRemoteOperations(operationsByScope),\n };\n }\n\n /**\n * Validate that the merged operations match the expected revision per scope.\n * Each scope's operation count should equal its revision number.\n */\n private hasExpectedOperationCounts(\n operations: DocumentOperations,\n expectedRevision: Record<string, number>,\n ): boolean {\n for (const [scope, revision] of Object.entries(expectedRevision)) {\n const opCount = scope in operations ? operations[scope].length : 0;\n if (opCount !== revision) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Merge existing local operations with newly fetched operations.\n * Appends new operations to existing ones per scope.\n */\n private mergeOperations(\n existingOps: DocumentOperations,\n newOps: DocumentOperations,\n ): DocumentOperations {\n const merged: DocumentOperations = {};\n\n // Copy existing operations\n for (const [scope, ops] of Object.entries(existingOps)) {\n if (ops.length > 0) {\n merged[scope] = [...ops];\n }\n }\n\n // Append new operations per scope\n for (const [scope, ops] of Object.entries(newOps)) {\n if (ops.length > 0) {\n (merged[scope] ??= []).push(...ops);\n }\n }\n\n return merged;\n }\n\n /** Schedule a push via microtask (for streaming mode coalescing). */\n private schedulePush(): void {\n if (this.pushScheduled) return;\n this.pushScheduled = true;\n queueMicrotask(() => {\n this.pushScheduled = false;\n // Chain onto the push queue to prevent concurrent pushes\n this.pushQueue = this.pushQueue.then(async () => {\n try {\n await this.push();\n } catch (error: unknown) {\n // Actions remain in tracker for retry\n this.options.onPushError?.(error);\n }\n });\n });\n }\n}\n","export abstract class BaseStorage<V> implements Iterable<[string, V]> {\n abstract get(key: string): V | undefined;\n abstract set(key: string, value: V): void;\n abstract delete(key: string): boolean;\n abstract has(key: string): boolean;\n abstract clear(): void;\n abstract entries(): IterableIterator<[string, V]>;\n abstract keys(): IterableIterator<string>;\n abstract values(): IterableIterator<V>;\n\n [Symbol.iterator](): IterableIterator<[string, V]> {\n return this.entries();\n }\n\n forEach(\n callback: (value: V, key: string, storage: BaseStorage<V>) => void,\n ): void {\n for (const [key, value] of this) {\n callback(value, key, this);\n }\n }\n}\n","import { BaseStorage } from \"./base-storage.js\";\n\nexport class BrowserLocalStorage<V> extends BaseStorage<V> {\n #namespace: string;\n #storage = window.localStorage;\n constructor(namespace: string) {\n super();\n this.#namespace = namespace;\n }\n\n #readMap(): Map<string, V> {\n const raw = this.#storage.getItem(this.#namespace);\n\n if (!raw) {\n return new Map();\n }\n\n return new Map(JSON.parse(raw) as [string, V][]);\n }\n\n #writeMap(map: Map<string, V>): void {\n this.#storage.setItem(\n this.#namespace,\n JSON.stringify(Array.from(map.entries())),\n );\n }\n\n get(key: string): V | undefined {\n return this.#readMap().get(key);\n }\n\n set(key: string, value: V): void {\n const map = this.#readMap();\n map.set(key, value);\n this.#writeMap(map);\n }\n\n delete(key: string): boolean {\n const map = this.#readMap();\n const deleted = map.delete(key);\n if (deleted) {\n this.#writeMap(map);\n }\n return deleted;\n }\n\n has(key: string): boolean {\n return this.#readMap().has(key);\n }\n\n clear(): void {\n this.#storage.removeItem(this.#namespace);\n }\n\n entries(): IterableIterator<[string, V]> {\n return this.#readMap().entries();\n }\n\n keys(): IterableIterator<string> {\n return this.#readMap().keys();\n }\n\n values(): IterableIterator<V> {\n return this.#readMap().values();\n }\n\n [Symbol.iterator](): IterableIterator<[string, V]> {\n return this.#readMap().entries();\n }\n}\n"],"mappings":"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"}