UNPKG

@ceramicnetwork/stream-model-instance

Version:

Ceramic Model Instance Document stream type

217 lines • 8.89 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var ModelInstanceDocument_1; import jsonpatch from 'fast-json-patch'; import * as dagCbor from '@ipld/dag-cbor'; import { randomBytes } from '@stablelib/random'; import sizeof from 'object-sizeof'; import { Stream, StreamStatic, SyncOptions, CeramicSigner, } from '@ceramicnetwork/common'; import { StreamRef } from '@ceramicnetwork/streamid'; import { fromString } from 'uint8arrays'; const DEFAULT_CREATE_OPTS = { anchor: true, publish: true, sync: SyncOptions.NEVER_SYNC, syncTimeoutSeconds: 0, }; const DEFAULT_DETERMINISTIC_OPTS = { anchor: false, publish: false, sync: SyncOptions.PREFER_CACHE, }; const DEFAULT_LOAD_OPTS = { sync: SyncOptions.PREFER_CACHE }; const DEFAULT_UPDATE_OPTS = { anchor: true, publish: true }; async function throwReadOnlyError() { throw new Error('Historical stream commits cannot be modified. Load the stream without specifying a commit to make updates.'); } let ModelInstanceDocument = ModelInstanceDocument_1 = class ModelInstanceDocument extends Stream { constructor() { super(...arguments); this._isReadOnly = false; } get content() { return super.content; } get metadata() { const metadata = this.state$.value.metadata; return { controller: metadata.controllers[0], model: metadata.model, unique: metadata.unique, context: metadata.context, shouldIndex: metadata.shouldIndex, }; } static async create(ceramic, content, metadata, opts = {}) { opts = { ...DEFAULT_CREATE_OPTS, ...opts }; const signer = opts.asDID ? CeramicSigner.fromDID(opts.asDID) : opts.signer || ceramic.signer; const commit = await ModelInstanceDocument_1.makeGenesis(signer, content, metadata); return ceramic.createStreamFromGenesis(ModelInstanceDocument_1.STREAM_TYPE_ID, commit, opts); } static async single(ceramic, metadata, opts = {}) { opts = { ...DEFAULT_DETERMINISTIC_OPTS, ...opts }; const signer = opts.asDID ? CeramicSigner.fromDID(opts.asDID) : opts.signer || ceramic.signer; metadata = { ...metadata, deterministic: true }; const commit = await ModelInstanceDocument_1.makeGenesis(signer, null, metadata); return ceramic.createStreamFromGenesis(ModelInstanceDocument_1.STREAM_TYPE_ID, commit, opts); } static async set(ceramic, metadata, unique, opts = {}) { opts = { ...DEFAULT_DETERMINISTIC_OPTS, ...opts }; const signer = opts.asDID ? CeramicSigner.fromDID(opts.asDID) : opts.signer || ceramic.signer; metadata = { ...metadata, deterministic: true }; const commit = await ModelInstanceDocument_1.makeGenesis(signer, null, metadata, unique); return ceramic.createStreamFromGenesis(ModelInstanceDocument_1.STREAM_TYPE_ID, commit, opts); } static async load(reader, streamId, opts = {}) { opts = { ...DEFAULT_LOAD_OPTS, ...opts }; const streamRef = StreamRef.from(streamId); if (streamRef.type != ModelInstanceDocument_1.STREAM_TYPE_ID) { throw new Error(`StreamID ${streamRef.toString()} does not refer to a '${ModelInstanceDocument_1.STREAM_TYPE_NAME}' stream, but to a ${streamRef.typeName}`); } return reader.loadStream(streamRef, opts); } async replace(content, metadata = undefined, opts = {}) { opts = { ...DEFAULT_UPDATE_OPTS, ...opts }; validateContentLength(content); const signer = opts.asDID ? CeramicSigner.fromDID(opts.asDID) : opts.signer || this.api.signer; let header = undefined; if (metadata && metadata.shouldIndex != null) { header = { shouldIndex: metadata.shouldIndex, }; } const updateCommit = await ModelInstanceDocument_1.makeUpdateCommit(signer, this.commitId, this.content, content, header); const updated = await this.api.applyCommit(this.id, updateCommit, opts); this.state$.next(updated.state); } async patch(jsonPatch, metadata = undefined, opts = {}) { opts = { ...DEFAULT_UPDATE_OPTS, ...opts }; const signer = opts.asDID ? CeramicSigner.fromDID(opts.asDID) : opts.signer || this.api.signer; jsonPatch.forEach((patch) => { switch (patch.op) { case 'add': { validateContentLength(patch.value); break; } case 'replace': { validateContentLength(patch.value); break; } default: { break; } } }); const rawCommit = { data: jsonPatch, prev: this.tip, id: this.id.cid, }; if (metadata?.shouldIndex != null) { rawCommit.header = { shouldIndex: metadata.shouldIndex, }; } const commit = await signer.createDagJWS(rawCommit); const updated = await this.api.applyCommit(this.id, commit, opts); this.state$.next(updated.state); } shouldIndex(shouldIndex, opts = {}) { return this.patch([], { shouldIndex: shouldIndex }, opts); } makeReadOnly() { this.replace = throwReadOnlyError; this.patch = throwReadOnlyError; this.sync = throwReadOnlyError; this._isReadOnly = true; } get isReadOnly() { return this._isReadOnly; } static makeUpdateCommit(signer, prev, oldContent, newContent, header) { const commit = ModelInstanceDocument_1._makeRawCommit(prev, oldContent, newContent, header); return signer.createDagJWS(commit); } static _makeRawCommit(prev, oldContent, newContent, header) { const patch = jsonpatch.compare(oldContent ?? {}, newContent ?? {}); const rawCommit = { data: patch, prev: prev.commit, id: prev.baseID.cid, }; if (header != null) { rawCommit.header = header; } return rawCommit; } static async makeGenesis(context, content, metadata, unique) { const commit = await this._makeRawGenesis(context.signer, content, metadata, unique); if (metadata.deterministic) { dagCbor.encode(commit); return commit; } else { return context.signer.createDagJWS(commit); } } static async _makeRawGenesis(signer, content, metadata, unique) { if (!metadata.model) { throw new Error(`Must specify a 'model' when creating a ModelInstanceDocument`); } validateContentLength(content); let controller = metadata.controller; if (!controller) { controller = await signer.asController(); } const header = { controllers: [controller], model: metadata.model.bytes, sep: 'model', }; if (metadata.deterministic) { if (Array.isArray(unique)) { header.unique = fromString(unique.join('|'), 'utf8'); } } else { header.unique = randomBytes(12); } if (metadata.context) { if (!metadata.context?.bytes) { throw new Error('Context must be a StreamID'); } header.context = metadata.context.bytes; } return { data: content, header }; } }; ModelInstanceDocument.STREAM_TYPE_NAME = 'MID'; ModelInstanceDocument.STREAM_TYPE_ID = 3; ModelInstanceDocument.MAX_DOCUMENT_SIZE = 16000000; ModelInstanceDocument = ModelInstanceDocument_1 = __decorate([ StreamStatic() ], ModelInstanceDocument); export { ModelInstanceDocument }; export function validateContentLength(content) { if (content) { const contentLength = sizeof(content); if (contentLength > ModelInstanceDocument.MAX_DOCUMENT_SIZE) { throw new Error(`Content has length of ${contentLength}B which exceeds maximum size of ${ModelInstanceDocument.MAX_DOCUMENT_SIZE}B`); } } } //# sourceMappingURL=model-instance-document.js.map