UNPKG

@useorbis/db-sdk

Version:

Orbis' Typescript SDK for building open-data experiences.

283 lines (282 loc) 10.5 kB
import { CeramicClient } from "@ceramicnetwork/http-client"; import { ModelInstanceDocument } from "@ceramicnetwork/stream-model-instance"; import { Model } from "@ceramicnetwork/stream-model"; import { SupportedChains } from "../index.js"; import { OrbisError } from "../util/results.js"; import { OrbisKeyDidAuth } from "../auth/keyDid.js"; import { StreamID } from "@ceramicnetwork/streamid"; import { parseSerializedSession } from "../util/session.js"; export class CeramicStorage { id = "ceramic"; userFriendlyName = "Ceramic Network"; supportedChains = [ SupportedChains.evm, SupportedChains.solana, SupportedChains.tezos, SupportedChains.stacks, ]; siwxResources = ["ceramic://*"]; #session; client; constructor(params) { if ("client" in params) { this.client = params.client; } else { this.client = new CeramicClient(params.gateway); } } get did() { return this.client.did; } get session() { if (!this.#session) { return false; } return this.#session; } async connect() { return; } async authorize({ authenticator, siwxOverwrites, }) { const userInformation = await authenticator.getUserInformation(); if (!this.supportedChains.includes(userInformation.chain)) { throw new OrbisError("[Ceramic] Unsupported authentication method. Chain not supported " + userInformation.chain, { supportedChains: this.supportedChains }); } const _siwxOverwrites = typeof siwxOverwrites === "object" ? siwxOverwrites.resources ? siwxOverwrites : { ...siwxOverwrites, resources: this.siwxResources } : { resources: this.siwxResources }; const { session, did } = await authenticator.authenticateDid({ siwxOverwrites: _siwxOverwrites, }); this.client.setDID(did); this.#session = session; return this.#session; } async setSession({ session, did, }) { const parsedSession = await parseSerializedSession(session); if (parsedSession.sessionType === "key-did") { const keyDid = await OrbisKeyDidAuth.fromSession(parsedSession.session); if (did) { const keyUser = await keyDid.getUserInformation(); if (did !== keyUser.did) { this.clearSession(); throw new OrbisError("[Ceramic:setSession] DID mismatch", { parsedDid: keyUser.did, expectedDid: did, }); } } const { session: resumedSession, did: parsedDid } = await keyDid.authenticateDid(); this.client.setDID(parsedDid); this.#session = resumedSession; return this.#session; } const didSession = parsedSession.session; if (!didSession.hasSession) { this.clearSession(); throw new OrbisError("[Ceramic] Invalid Ceramic session", { session: didSession, hasSession: didSession.hasSession, }); } if (didSession.isExpired) { this.clearSession(); throw new OrbisError("[Ceramic] Ceramic session expired", { session: didSession, isExpired: didSession.isExpired, }); } if (did) { if (didSession.id !== did) { this.clearSession(); throw new OrbisError("[Ceramic] Session did mismatch", { parsedDid: didSession.id, expectedDid: did, }); } } this.client.setDID(didSession.did); this.#session = didSession; return this.#session; } async clearSession() { this.#session = undefined; // @ts-ignore (force empty DID) this.client.setDID(undefined); } async assertCurrentUser(did) { if (!this.#session) { return false; } return this.client.did?.id === did; } async getDocument(id) { const doc = await ModelInstanceDocument.load(this.client, id); return { id: doc.id.toString(), model: doc.metadata.model.toString(), content: doc.content, controller: doc.metadata.controller, metadata: {}, }; } isStreamIdString(streamId) { try { const _ = StreamID.fromString(streamId); return true; } catch (_) { return false; } } isStreamId(streamId) { if (streamId instanceof StreamID) { return true; } return this.isStreamIdString(streamId); } async createDocument(params) { if (!this.#session) throw new OrbisError("[Ceramic] Unable to create document, no active Storage session."); const doc = await ModelInstanceDocument.create(this.client, params.content, { model: typeof params.model === "string" ? StreamID.fromString(params.model) : params.model, ...(params.context ? { context: typeof params.context === "string" ? StreamID.fromString(params.context) : params.context, } : {}), }); const { model, controller, ...metadata } = doc.metadata; return { id: doc.id.toString(), content: doc.content, model: model.toString(), context: doc.state.metadata.context?.toString() || (params.context && (typeof params.context === "string" ? params.context : params.context.toString())) || undefined, controller: controller, metadata: metadata || {}, }; } async createDocumentSingle(params) { if (!this.#session) throw new OrbisError("[Ceramic] Unable to create document, no active Storage session."); const doc = await ModelInstanceDocument.single(this.client, { model: typeof params.model === "string" ? StreamID.fromString(params.model) : params.model, ...(params.context ? { context: typeof params.context === "string" ? StreamID.fromString(params.context) : params.context, } : {}), }); await doc.replace(params.content); const { model, controller, ...metadata } = doc.metadata; return { id: doc.id.toString(), content: doc.content, model: model.toString(), context: doc.state.metadata.context?.toString() || (params.context && (typeof params.context === "string" ? params.context : params.context.toString())) || undefined, controller: controller, metadata: metadata || {}, }; } async createDocumentSet(params, unique) { if (!this.#session) throw new OrbisError("[Ceramic] Unable to create document, no active Storage session."); const doc = await ModelInstanceDocument.set(this.client, { model: typeof params.model === "string" ? StreamID.fromString(params.model) : params.model, ...(params.context ? { context: typeof params.context === "string" ? StreamID.fromString(params.context) : params.context, } : {}), }, unique); await doc.replace(params.content); const { model, controller, ...metadata } = doc.metadata; return { id: doc.id.toString(), content: doc.content, model: model.toString(), context: doc.state.metadata.context?.toString() || (params.context && (typeof params.context === "string" ? params.context : params.context.toString())) || undefined, controller: controller, metadata: metadata || {}, }; } async updateDocument(id, content) { if (!this.#session) throw new OrbisError("[Ceramic] Unable to update document, no active Storage session."); const doc = await ModelInstanceDocument.load(this.client, id); await doc.replace(content); const { model, controller, ...metadata } = doc.metadata; return { id: doc.id.toString(), content: doc.content, model: model.toString(), context: doc.state.metadata.context?.toString(), controller: controller, metadata: metadata || {}, }; } async updateDocumentBySetter(id, setter) { if (!this.#session) throw new OrbisError("[Ceramic] Unable to update document, no active Storage session."); const doc = (await ModelInstanceDocument.load(this.client, id)); await doc.replace(await setter(doc)); const { model, controller, ...metadata } = doc.metadata; return { id: doc.id.toString(), content: doc.content, model: model.toString(), context: doc.state.metadata.context?.toString(), controller: controller, metadata: metadata || {}, }; } async getModel(id) { const model = await Model.load(this.client, id); return { id: model.id.toString(), schema: model.content, metadata: model.metadata, }; } async createModel(modelDefinition) { if (!this.#session) throw new OrbisError("[Ceramic] Unable to create model, no active Storage session."); const model = await Model.create(this.client, modelDefinition, { controller: this.client.did?.id, }); return { id: model.id.toString(), }; } }