UNPKG

@ceramicnetwork/stream-model

Version:
147 lines • 6.35 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 Model_1; import { Stream, StreamStatic, SyncOptions, } from '@ceramicnetwork/common'; import { StreamID, StreamRef } from '@ceramicnetwork/streamid'; import { CID } from 'multiformats/cid'; import { create } from 'multiformats/hashes/digest'; import { code, encode } from '@ipld/dag-cbor'; import { identity } from 'multiformats/hashes/identity'; import { asDIDString } from '@ceramicnetwork/codecs'; import { decode } from 'codeco'; import { ModelDefinition, ModelRelationsDefinitionV2 } from './codecs.js'; export async function loadInterfaceImplements(reader, modelID) { const model = await Model.load(reader, modelID); if (model.content.version === '1.0' || !model.content.interface) { throw new Error(`Model ${modelID} is not an interface`); } return model.content.implements ?? []; } export async function loadAllModelInterfaces(reader, interfaces, loading = {}) { const toLoad = interfaces.map((modelID) => { if (loading[modelID] == null) { loading[modelID] = loadInterfaceImplements(reader, modelID).then((ownImplements) => { return loadAllModelInterfaces(reader, ownImplements, loading).then((subImplements) => { return [...ownImplements, ...subImplements]; }); }); } return loading[modelID]; }); const loaded = await Promise.all(toLoad); return Array.from(new Set(interfaces.concat(loaded.flat()))); } export const MODEL_VERSION_REGEXP = /^[0-9]+\.[0-9]+$/; export function parseModelVersion(version) { if (!MODEL_VERSION_REGEXP.test(version)) { throw new Error(`Unsupported version format: ${version}`); } const [major, minor] = version.split('.').map((part) => parseInt(part, 10)); return [major, minor]; } const DEFAULT_LOAD_OPTS = { sync: SyncOptions.PREFER_CACHE }; async function throwReadOnlyError() { throw new Error('Historical stream commits cannot be modified. Load the stream without specifying a commit to make updates.'); } let Model = Model_1 = class Model extends Stream { constructor() { super(...arguments); this._isReadOnly = false; } get content() { return super.content; } get metadata() { return { controller: asDIDString(this.state$.value.metadata.controllers[0]), model: Model_1.MODEL, }; } static async create(ceramic, content, metadata) { Model_1.assertVersionValid(content, 'minor'); Model_1.assertComplete(content); const opts = { publish: true, anchor: true, sync: SyncOptions.NEVER_SYNC, }; const commit = await Model_1._makeGenesis(ceramic.signer, content, metadata); const model = await ceramic.createStreamFromGenesis(Model_1.STREAM_TYPE_ID, commit, opts); return model; } static assertComplete(content, _streamId) { decode(ModelDefinition, content); } static assertVersionValid(content, satisfies = 'minor') { if (content.version == null) { throw new Error(`Missing version for model ${content.name}`); } const [expectedMajor, expectedMinor] = parseModelVersion(Model_1.VERSION); const [major, minor] = parseModelVersion(content.version); if (major > expectedMajor || (satisfies === 'minor' && major === expectedMajor && minor > expectedMinor)) { throw new Error(`Unsupported version ${content.version} for model ${content.name}, the maximum version supported by the Ceramic node is ${Model_1.VERSION}. Please update your Ceramic node to a newer version supporting at least version ${content.version} of the Model definition.`); } } static assertRelationsValid(content) { if (content.relations != null) { decode(ModelRelationsDefinitionV2, content.relations); } } static async load(ceramic, streamId, opts = {}) { opts = { ...DEFAULT_LOAD_OPTS, ...opts }; const streamRef = StreamRef.from(streamId); if (streamRef.type != Model_1.STREAM_TYPE_ID) { throw new Error(`StreamID ${streamRef.toString()} does not refer to a '${Model_1.STREAM_TYPE_NAME}' stream, but to a ${streamRef.typeName}`); } const model = await ceramic.loadStream(streamRef, opts); return model; } static async _makeGenesis(context, content, metadata) { const commit = await this._makeRawGenesis(context.signer, content, metadata); return context.signer.createDagJWS(commit); } static async _makeRawGenesis(signer, content, metadata) { if (content == null) { throw new Error(`Genesis content cannot be null`); } if (!metadata) { metadata = { controller: await signer.asController() }; } else if (!metadata.controller) { metadata.controller = await signer.asController(); } const header = { controllers: [metadata.controller], model: Model_1.MODEL.bytes, sep: 'model', }; return { data: content, header }; } makeReadOnly() { this.sync = throwReadOnlyError; this._isReadOnly = true; } get isReadOnly() { return this._isReadOnly; } }; Model.STREAM_TYPE_NAME = 'model'; Model.STREAM_TYPE_ID = 2; Model.MODEL = (function () { const data = encode('model-v1'); const multihash = identity.digest(data); const digest = create(code, multihash.bytes); const cid = CID.createV1(code, digest); return new StreamID('UNLOADABLE', cid); })(); Model.VERSION = '2.0'; Model = Model_1 = __decorate([ StreamStatic() ], Model); export { Model }; //# sourceMappingURL=model.js.map