import { ConnectionCatalogDocument, type ConnectionCatalogDocument as ConnectionCatalogDocumentType, ConnectionPersistenceError, ConnectionRegistrationStore, ConnectionTargetStore, EMPTY_CONNECTION_CATALOG_DOCUMENT, EnvironmentCacheStore, registerConnectionInCatalog, removeCatalogValue, removeConnectionFromCatalog, replaceCatalogValue,} from "@t3tools/client-runtime/platform";import { TokenStore } from "@t3tools/client-runtime/authorization";import { ConnectionStorageOperationError, ConnectionTransientError, CredentialStore, DesktopSecureStorageUnavailableError, IndexedDbUnavailableError, ProfileStore,} from "@t3tools/client-runtime/connection";import { EnvironmentId, OrchestrationShellSnapshot, OrchestrationThread, ThreadId,} from "@t3tools/contracts";import * as Context from "effect/Context";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Ref from "effect/Ref";import * as Schema from "effect/Schema";import * as Semaphore from "effect/Semaphore"; const DATABASE_NAME = "t3code:connection-runtime";const DATABASE_VERSION = 2;const CATALOG_STORE_NAME = "catalog";const SHELL_STORE_NAME = "shell";const THREAD_STORE_NAME = "thread";const CATALOG_KEY = "document";const SHELL_SNAPSHOT_CACHE_SCHEMA_VERSION = 1; const StoredShellSnapshot = Schema.Struct({ schemaVersion: Schema.Literal(SHELL_SNAPSHOT_CACHE_SCHEMA_VERSION), environmentId: EnvironmentId, snapshot: OrchestrationShellSnapshot,});const StoredShellSnapshotJson = Schema.fromJsonString(StoredShellSnapshot);const StoredThreadSnapshot = Schema.Struct({ schemaVersion: Schema.Literal(1), environmentId: EnvironmentId, threadId: ThreadId, thread: OrchestrationThread,});const StoredThreadSnapshotJson = Schema.fromJsonString(StoredThreadSnapshot);const ConnectionCatalogDocumentJson = Schema.fromJsonString(ConnectionCatalogDocument);const decodeConnectionCatalogDocument = Schema.decodeUnknownEffect(ConnectionCatalogDocumentJson);const encodeConnectionCatalogDocument = Schema.encodeEffect(ConnectionCatalogDocumentJson);const decodeStoredShellSnapshot = Schema.decodeUnknownEffect(StoredShellSnapshotJson);const encodeStoredShellSnapshot = Schema.encodeEffect(StoredShellSnapshotJson);const decodeStoredThreadSnapshot = Schema.decodeUnknownEffect(StoredThreadSnapshotJson);const encodeStoredThreadSnapshot = Schema.encodeEffect(StoredThreadSnapshotJson); const openDatabase = Effect.fn("web.connectionStorage.openDatabase")(function* () { return yield* Effect.callback<IDBDatabase, ConnectionTransientError>((resume) => { if (typeof indexedDB === "undefined") { resume( Effect.fail(ConnectionTransientError.fromStorageFailure(new IndexedDbUnavailableError())), ); return; } const request = indexedDB.open(DATABASE_NAME, DATABASE_VERSION); request.addEventListener("upgradeneeded", () => { if (!request.result.objectStoreNames.contains(CATALOG_STORE_NAME)) { request.result.createObjectStore(CATALOG_STORE_NAME); } if (!request.result.objectStoreNames.contains(SHELL_STORE_NAME)) { request.result.createObjectStore(SHELL_STORE_NAME); } if (!request.result.objectStoreNames.contains(THREAD_STORE_NAME)) { request.result.createObjectStore(THREAD_STORE_NAME); } }); request.addEventListener("error", (event) => { resume( Effect.fail( ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "open", backend: "indexed-db", cause: request.error ?? event, }), ), ), ); }); request.addEventListener("success", () => { resume(Effect.succeed(request.result)); }); });}); function readDatabaseValue(database: IDBDatabase, storeName: string, key: IDBValidKey) { return Effect.callback<unknown, ConnectionTransientError>((resume) => { const request = database.transaction(storeName, "readonly").objectStore(storeName).get(key); request.addEventListener("error", (event) => { resume( Effect.fail( ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "read", backend: "indexed-db", storeName, key, cause: request.error ?? event, }), ), ), ); }); request.addEventListener("success", () => { resume(Effect.succeed(request.result)); }); }).pipe(Effect.withSpan("web.connectionStorage.readDatabaseValue"));} function writeDatabaseValue( database: IDBDatabase, storeName: string, key: IDBValidKey, value: unknown,) { return Effect.callback<void, ConnectionTransientError>((resume) => { const transaction = database.transaction(storeName, "readwrite"); transaction.addEventListener("error", (event) => { resume( Effect.fail( ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "write", backend: "indexed-db", storeName, key, cause: transaction.error ?? event, }), ), ), ); }); transaction.addEventListener("complete", () => { resume(Effect.void); }); transaction.objectStore(storeName).put(value, key); }).pipe(Effect.withSpan("web.connectionStorage.writeDatabaseValue"));} function removeDatabaseValue(database: IDBDatabase, storeName: string, key: IDBValidKey) { return Effect.callback<void, ConnectionTransientError>((resume) => { const transaction = database.transaction(storeName, "readwrite"); transaction.addEventListener("error", (event) => { resume( Effect.fail( ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "remove", backend: "indexed-db", storeName, key, cause: transaction.error ?? event, }), ), ), ); }); transaction.addEventListener("complete", () => { resume(Effect.void); }); transaction.objectStore(storeName).delete(key); }).pipe(Effect.withSpan("web.connectionStorage.removeDatabaseValue"));} function removeDatabaseValuesInRange(database: IDBDatabase, storeName: string, range: IDBKeyRange) { return Effect.callback<void, ConnectionTransientError>((resume) => { const transaction = database.transaction(storeName, "readwrite"); transaction.addEventListener("error", (event) => { resume( Effect.fail( ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "remove", backend: "indexed-db", storeName, cause: transaction.error ?? event, }), ), ), ); }); transaction.addEventListener("complete", () => { resume(Effect.void); }); const request = transaction.objectStore(storeName).openCursor(range); request.addEventListener("error", (event) => { resume( Effect.fail( ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "remove", backend: "indexed-db", storeName, cause: request.error ?? event, }), ), ), ); }); request.addEventListener("success", () => { const cursor = request.result; if (cursor === null) { return; } cursor.delete(); cursor.continue(); }); }).pipe(Effect.withSpan("web.connectionStorage.removeDatabaseValuesInRange"));} function threadCacheKey(environmentId: EnvironmentId, threadId: ThreadId) { return `${environmentId}:${threadId}`;} const decodeCatalog = Effect.fn("web.connectionStorage.decodeCatalog")(function* (raw: string) { return yield* decodeConnectionCatalogDocument(raw).pipe( Effect.mapError((cause) => ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "decode", backend: "schema", cause, }), ), ), );}); const encodeCatalog = Effect.fn("web.connectionStorage.encodeCatalog")(function* ( catalog: ConnectionCatalogDocumentType,) { return yield* encodeConnectionCatalogDocument(catalog).pipe( Effect.mapError((cause) => ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "encode", backend: "schema", cause, }), ), ), );}); export interface CatalogBackend { readonly read: Effect.Effect<string | null, ConnectionTransientError>; readonly write: (raw: string) => Effect.Effect<void, ConnectionTransientError>; readonly quarantine?: (raw: string) => Effect.Effect<void, ConnectionTransientError>;} export function makeCatalogBackend(database: IDBDatabase): CatalogBackend { const bridge = window.desktopBridge; if (bridge?.getConnectionCatalog !== undefined && bridge.setConnectionCatalog !== undefined) { return { read: Effect.tryPromise({ try: () => bridge.getConnectionCatalog!(), catch: (cause) => ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "load", backend: "desktop-secure-storage", cause, }), ), }), write: (raw) => Effect.tryPromise({ try: () => bridge.setConnectionCatalog!(raw), catch: (cause) => ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "save", backend: "desktop-secure-storage", cause, }), ), }).pipe( Effect.flatMap((stored) => stored ? Effect.void : Effect.fail( ConnectionTransientError.fromStorageFailure( new DesktopSecureStorageUnavailableError(), ), ), ), ), }; } return { read: readDatabaseValue(database, CATALOG_STORE_NAME, CATALOG_KEY).pipe( Effect.map((value) => (typeof value === "string" ? value : null)), ), write: (raw) => writeDatabaseValue(database, CATALOG_STORE_NAME, CATALOG_KEY, raw), quarantine: (raw) => writeDatabaseValue(database, CATALOG_STORE_NAME, `${CATALOG_KEY}:corrupt:${Date.now()}`, raw), };} interface CatalogStore { readonly read: Effect.Effect<ConnectionCatalogDocumentType, ConnectionTransientError>; readonly update: ( transform: (catalog: ConnectionCatalogDocumentType) => ConnectionCatalogDocumentType, ) => Effect.Effect<void, ConnectionTransientError>;} export const makeCatalogStore = Effect.fn("web.connectionStorage.makeCatalogStore")(function* ( backend: CatalogBackend,) { const state = yield* Ref.make<Option.Option<ConnectionCatalogDocumentType>>(Option.none()); const lock = yield* Semaphore.make(1); const loadUnlocked = Effect.fn("web.connectionStorage.loadCatalog")(function* () { const cached = yield* Ref.get(state); if (Option.isSome(cached)) { return cached.value; } const raw = yield* backend.read; let catalog = EMPTY_CONNECTION_CATALOG_DOCUMENT; if (raw !== null && raw.trim() !== "") { catalog = yield* decodeCatalog(raw).pipe( Effect.catchTags({ ConnectionTransientError: (error) => Effect.gen(function* () { yield* Effect.logWarning("Discarding a corrupt web connection catalog.", { error }); if (backend.quarantine !== undefined) { yield* backend.quarantine(raw).pipe( Effect.catchTags({ ConnectionTransientError: (error) => Effect.logWarning( "Could not quarantine the corrupt web connection catalog.", { error }, ), }), ); } const encoded = yield* encodeCatalog(EMPTY_CONNECTION_CATALOG_DOCUMENT); yield* backend.write(encoded).pipe( Effect.catchTags({ ConnectionTransientError: (error) => Effect.logWarning("Could not persist the recovered web connection catalog.", { error, }), }), ); return EMPTY_CONNECTION_CATALOG_DOCUMENT; }), }), ); } yield* Ref.set(state, Option.some(catalog)); return catalog; }); const read = lock.withPermits(1)(loadUnlocked()); const update: CatalogStore["update"] = Effect.fn("web.connectionStorage.updateCatalog")( function* (transform) { yield* lock.withPermits(1)( Effect.gen(function* () { const next = transform(yield* loadUnlocked()); yield* backend.write(yield* encodeCatalog(next)); yield* Ref.set(state, Option.some(next)); }), ); }, ); return { read, update } satisfies CatalogStore;}); export const connectionStorageLayer = Layer.effectContext( Effect.gen(function* () { const database = yield* Effect.acquireRelease(openDatabase(), (database) => Effect.sync(() => database.close()), ); const catalog = yield* makeCatalogStore(makeCatalogBackend(database)); const targetStore = ConnectionTargetStore.of({ list: catalog.read.pipe( Effect.map((document) => document.targets), Effect.mapError((cause) => ConnectionPersistenceError.fromStorageFailure({ operation: "list-targets", fallbackStage: "read", resource: "connection-catalog", cause, }), ), ), }); const registrationStore = ConnectionRegistrationStore.of({ register: (registration) => catalog .update((document) => registerConnectionInCatalog(document, registration)) .pipe( Effect.mapError((cause) => ConnectionPersistenceError.fromStorageFailure({ operation: "register-connection", fallbackStage: "write", resource: "connection-catalog", environmentId: registration.target.environmentId, cause, }), ), ), remove: (target) => catalog .update((document) => removeConnectionFromCatalog(document, target)) .pipe( Effect.mapError((cause) => ConnectionPersistenceError.fromStorageFailure({ operation: "remove-connection", fallbackStage: "write", resource: "connection-catalog", environmentId: target.environmentId, cause, }), ), ), }); const profileStore = ProfileStore.make({ get: (connectionId) => catalog.read.pipe( Effect.map((document) => Option.fromUndefinedOr( document.profiles.find((profile) => profile.connectionId === connectionId), ), ), ), put: (profile) => catalog.update((document) => ({ ...document, profiles: replaceCatalogValue(document.profiles, (value) => value.connectionId, profile), })), remove: (connectionId) => catalog.update((document) => ({ ...document, profiles: removeCatalogValue( document.profiles, (value) => value.connectionId, connectionId, ), })), }); const credentialStore = CredentialStore.make({ get: (connectionId) => catalog.read.pipe( Effect.map((document) => Option.fromUndefinedOr( document.credentials.find((entry) => entry.connectionId === connectionId)?.credential, ), ), ), put: (connectionId, credential) => catalog.update((document) => ({ ...document, credentials: replaceCatalogValue(document.credentials, (value) => value.connectionId, { connectionId, credential, }), })), remove: (connectionId) => catalog.update((document) => ({ ...document, credentials: removeCatalogValue( document.credentials, (value) => value.connectionId, connectionId, ), })), }); const remoteTokenStore = TokenStore.make({ get: (environmentId) => catalog.read.pipe( Effect.map((document) => Option.fromUndefinedOr( document.remoteDpopTokens.find((token) => token.environmentId === environmentId), ), ), ),Lines 1–500 of 729. Load more
import { ConnectionCatalogDocument, type ConnectionCatalogDocument as ConnectionCatalogDocumentType, ConnectionPersistenceError, ConnectionRegistrationStore, ConnectionTargetStore, EMPTY_CONNECTION_CATALOG_DOCUMENT, EnvironmentCacheStore, registerConnectionInCatalog, removeCatalogValue, removeConnectionFromCatalog, replaceCatalogValue, } from "@t3tools/client-runtime/platform"; import { TokenStore } from "@t3tools/client-runtime/authorization"; import { ConnectionStorageOperationError, ConnectionTransientError, CredentialStore, DesktopSecureStorageUnavailableError, IndexedDbUnavailableError, ProfileStore, } from "@t3tools/client-runtime/connection"; import { EnvironmentId, OrchestrationShellSnapshot, OrchestrationThread, ThreadId, } from "@t3tools/contracts"; import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Ref from "effect/Ref"; import * as Schema from "effect/Schema"; import * as Semaphore from "effect/Semaphore"; const DATABASE_NAME = "t3code:connection-runtime"; const DATABASE_VERSION = 2; const CATALOG_STORE_NAME = "catalog"; const SHELL_STORE_NAME = "shell"; const THREAD_STORE_NAME = "thread"; const CATALOG_KEY = "document"; const SHELL_SNAPSHOT_CACHE_SCHEMA_VERSION = 1; const StoredShellSnapshot = Schema.Struct({ schemaVersion: Schema.Literal(SHELL_SNAPSHOT_CACHE_SCHEMA_VERSION), environmentId: EnvironmentId, snapshot: OrchestrationShellSnapshot, }); const StoredShellSnapshotJson = Schema.fromJsonString(StoredShellSnapshot); const StoredThreadSnapshot = Schema.Struct({ schemaVersion: Schema.Literal(1), environmentId: EnvironmentId, threadId: ThreadId, thread: OrchestrationThread, }); const StoredThreadSnapshotJson = Schema.fromJsonString(StoredThreadSnapshot); const ConnectionCatalogDocumentJson = Schema.fromJsonString(ConnectionCatalogDocument); const decodeConnectionCatalogDocument = Schema.decodeUnknownEffect(ConnectionCatalogDocumentJson); const encodeConnectionCatalogDocument = Schema.encodeEffect(ConnectionCatalogDocumentJson); const decodeStoredShellSnapshot = Schema.decodeUnknownEffect(StoredShellSnapshotJson); const encodeStoredShellSnapshot = Schema.encodeEffect(StoredShellSnapshotJson); const decodeStoredThreadSnapshot = Schema.decodeUnknownEffect(StoredThreadSnapshotJson); const encodeStoredThreadSnapshot = Schema.encodeEffect(StoredThreadSnapshotJson); const openDatabase = Effect.fn("web.connectionStorage.openDatabase")(function* () { return yield* Effect.callback<IDBDatabase, ConnectionTransientError>((resume) => { if (typeof indexedDB === "undefined") { resume( Effect.fail(ConnectionTransientError.fromStorageFailure(new IndexedDbUnavailableError())), ); return; } const request = indexedDB.open(DATABASE_NAME, DATABASE_VERSION); request.addEventListener("upgradeneeded", () => { if (!request.result.objectStoreNames.contains(CATALOG_STORE_NAME)) { request.result.createObjectStore(CATALOG_STORE_NAME); } if (!request.result.objectStoreNames.contains(SHELL_STORE_NAME)) { request.result.createObjectStore(SHELL_STORE_NAME); } if (!request.result.objectStoreNames.contains(THREAD_STORE_NAME)) { request.result.createObjectStore(THREAD_STORE_NAME); } }); request.addEventListener("error", (event) => { resume( Effect.fail( ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "open", backend: "indexed-db", cause: request.error ?? event, }), ), ), ); }); request.addEventListener("success", () => { resume(Effect.succeed(request.result)); }); }); }); function readDatabaseValue(database: IDBDatabase, storeName: string, key: IDBValidKey) { return Effect.callback<unknown, ConnectionTransientError>((resume) => { const request = database.transaction(storeName, "readonly").objectStore(storeName).get(key); request.addEventListener("error", (event) => { resume( Effect.fail( ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "read", backend: "indexed-db", storeName, key, cause: request.error ?? event, }), ), ), ); }); request.addEventListener("success", () => { resume(Effect.succeed(request.result)); }); }).pipe(Effect.withSpan("web.connectionStorage.readDatabaseValue")); } function writeDatabaseValue( database: IDBDatabase, storeName: string, key: IDBValidKey, value: unknown, ) { return Effect.callback<void, ConnectionTransientError>((resume) => { const transaction = database.transaction(storeName, "readwrite"); transaction.addEventListener("error", (event) => { resume( Effect.fail( ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "write", backend: "indexed-db", storeName, key, cause: transaction.error ?? event, }), ), ), ); }); transaction.addEventListener("complete", () => { resume(Effect.void); }); transaction.objectStore(storeName).put(value, key); }).pipe(Effect.withSpan("web.connectionStorage.writeDatabaseValue")); } function removeDatabaseValue(database: IDBDatabase, storeName: string, key: IDBValidKey) { return Effect.callback<void, ConnectionTransientError>((resume) => { const transaction = database.transaction(storeName, "readwrite"); transaction.addEventListener("error", (event) => { resume( Effect.fail( ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "remove", backend: "indexed-db", storeName, key, cause: transaction.error ?? event, }), ), ), ); }); transaction.addEventListener("complete", () => { resume(Effect.void); }); transaction.objectStore(storeName).delete(key); }).pipe(Effect.withSpan("web.connectionStorage.removeDatabaseValue")); } function removeDatabaseValuesInRange(database: IDBDatabase, storeName: string, range: IDBKeyRange) { return Effect.callback<void, ConnectionTransientError>((resume) => { const transaction = database.transaction(storeName, "readwrite"); transaction.addEventListener("error", (event) => { resume( Effect.fail( ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "remove", backend: "indexed-db", storeName, cause: transaction.error ?? event, }), ), ), ); }); transaction.addEventListener("complete", () => { resume(Effect.void); }); const request = transaction.objectStore(storeName).openCursor(range); request.addEventListener("error", (event) => { resume( Effect.fail( ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "remove", backend: "indexed-db", storeName, cause: request.error ?? event, }), ), ), ); }); request.addEventListener("success", () => { const cursor = request.result; if (cursor === null) { return; } cursor.delete(); cursor.continue(); }); }).pipe(Effect.withSpan("web.connectionStorage.removeDatabaseValuesInRange")); } function threadCacheKey(environmentId: EnvironmentId, threadId: ThreadId) { return `${environmentId}:${threadId}`; } const decodeCatalog = Effect.fn("web.connectionStorage.decodeCatalog")(function* (raw: string) { return yield* decodeConnectionCatalogDocument(raw).pipe( Effect.mapError((cause) => ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "decode", backend: "schema", cause, }), ), ), ); }); const encodeCatalog = Effect.fn("web.connectionStorage.encodeCatalog")(function* ( catalog: ConnectionCatalogDocumentType, ) { return yield* encodeConnectionCatalogDocument(catalog).pipe( Effect.mapError((cause) => ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "encode", backend: "schema", cause, }), ), ), ); }); export interface CatalogBackend { readonly read: Effect.Effect<string | null, ConnectionTransientError>; readonly write: (raw: string) => Effect.Effect<void, ConnectionTransientError>; readonly quarantine?: (raw: string) => Effect.Effect<void, ConnectionTransientError>; } export function makeCatalogBackend(database: IDBDatabase): CatalogBackend { const bridge = window.desktopBridge; if (bridge?.getConnectionCatalog !== undefined && bridge.setConnectionCatalog !== undefined) { return { read: Effect.tryPromise({ try: () => bridge.getConnectionCatalog!(), catch: (cause) => ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "load", backend: "desktop-secure-storage", cause, }), ), }), write: (raw) => Effect.tryPromise({ try: () => bridge.setConnectionCatalog!(raw), catch: (cause) => ConnectionTransientError.fromStorageFailure( new ConnectionStorageOperationError({ operation: "save", backend: "desktop-secure-storage", cause, }), ), }).pipe( Effect.flatMap((stored) => stored ? Effect.void : Effect.fail( ConnectionTransientError.fromStorageFailure( new DesktopSecureStorageUnavailableError(), ), ), ), ), }; } return { read: readDatabaseValue(database, CATALOG_STORE_NAME, CATALOG_KEY).pipe( Effect.map((value) => (typeof value === "string" ? value : null)), ), write: (raw) => writeDatabaseValue(database, CATALOG_STORE_NAME, CATALOG_KEY, raw), quarantine: (raw) => writeDatabaseValue(database, CATALOG_STORE_NAME, `${CATALOG_KEY}:corrupt:${Date.now()}`, raw), }; } interface CatalogStore { readonly read: Effect.Effect<ConnectionCatalogDocumentType, ConnectionTransientError>; readonly update: ( transform: (catalog: ConnectionCatalogDocumentType) => ConnectionCatalogDocumentType, ) => Effect.Effect<void, ConnectionTransientError>; } export const makeCatalogStore = Effect.fn("web.connectionStorage.makeCatalogStore")(function* ( backend: CatalogBackend, ) { const state = yield* Ref.make<Option.Option<ConnectionCatalogDocumentType>>(Option.none()); const lock = yield* Semaphore.make(1); const loadUnlocked = Effect.fn("web.connectionStorage.loadCatalog")(function* () { const cached = yield* Ref.get(state); if (Option.isSome(cached)) { return cached.value; } const raw = yield* backend.read; let catalog = EMPTY_CONNECTION_CATALOG_DOCUMENT; if (raw !== null && raw.trim() !== "") { catalog = yield* decodeCatalog(raw).pipe( Effect.catchTags({ ConnectionTransientError: (error) => Effect.gen(function* () { yield* Effect.logWarning("Discarding a corrupt web connection catalog.", { error }); if (backend.quarantine !== undefined) { yield* backend.quarantine(raw).pipe( Effect.catchTags({ ConnectionTransientError: (error) => Effect.logWarning( "Could not quarantine the corrupt web connection catalog.", { error }, ), }), ); } const encoded = yield* encodeCatalog(EMPTY_CONNECTION_CATALOG_DOCUMENT); yield* backend.write(encoded).pipe( Effect.catchTags({ ConnectionTransientError: (error) => Effect.logWarning("Could not persist the recovered web connection catalog.", { error, }), }), ); return EMPTY_CONNECTION_CATALOG_DOCUMENT; }), }), ); } yield* Ref.set(state, Option.some(catalog)); return catalog; }); const read = lock.withPermits(1)(loadUnlocked()); const update: CatalogStore["update"] = Effect.fn("web.connectionStorage.updateCatalog")( function* (transform) { yield* lock.withPermits(1)( Effect.gen(function* () { const next = transform(yield* loadUnlocked()); yield* backend.write(yield* encodeCatalog(next)); yield* Ref.set(state, Option.some(next)); }), ); }, ); return { read, update } satisfies CatalogStore; }); export const connectionStorageLayer = Layer.effectContext( Effect.gen(function* () { const database = yield* Effect.acquireRelease(openDatabase(), (database) => Effect.sync(() => database.close()), ); const catalog = yield* makeCatalogStore(makeCatalogBackend(database)); const targetStore = ConnectionTargetStore.of({ list: catalog.read.pipe( Effect.map((document) => document.targets), Effect.mapError((cause) => ConnectionPersistenceError.fromStorageFailure({ operation: "list-targets", fallbackStage: "read", resource: "connection-catalog", cause, }), ), ), }); const registrationStore = ConnectionRegistrationStore.of({ register: (registration) => catalog .update((document) => registerConnectionInCatalog(document, registration)) .pipe( Effect.mapError((cause) => ConnectionPersistenceError.fromStorageFailure({ operation: "register-connection", fallbackStage: "write", resource: "connection-catalog", environmentId: registration.target.environmentId, cause, }), ), ), remove: (target) => catalog .update((document) => removeConnectionFromCatalog(document, target)) .pipe( Effect.mapError((cause) => ConnectionPersistenceError.fromStorageFailure({ operation: "remove-connection", fallbackStage: "write", resource: "connection-catalog", environmentId: target.environmentId, cause, }), ), ), }); const profileStore = ProfileStore.make({ get: (connectionId) => catalog.read.pipe( Effect.map((document) => Option.fromUndefinedOr( document.profiles.find((profile) => profile.connectionId === connectionId), ), ), ), put: (profile) => catalog.update((document) => ({ ...document, profiles: replaceCatalogValue(document.profiles, (value) => value.connectionId, profile), })), remove: (connectionId) => catalog.update((document) => ({ ...document, profiles: removeCatalogValue( document.profiles, (value) => value.connectionId, connectionId, ), })), }); const credentialStore = CredentialStore.make({ get: (connectionId) => catalog.read.pipe( Effect.map((document) => Option.fromUndefinedOr( document.credentials.find((entry) => entry.connectionId === connectionId)?.credential, ), ), ), put: (connectionId, credential) => catalog.update((document) => ({ ...document, credentials: replaceCatalogValue(document.credentials, (value) => value.connectionId, { connectionId, credential, }), })), remove: (connectionId) => catalog.update((document) => ({ ...document, credentials: removeCatalogValue( document.credentials, (value) => value.connectionId, connectionId, ), })), }); const remoteTokenStore = TokenStore.make({ get: (environmentId) => catalog.read.pipe( Effect.map((document) => Option.fromUndefinedOr( document.remoteDpopTokens.find((token) => token.environmentId === environmentId), ), ), ),