UNPKG

@platform/cell.typesystem

Version:

The 'strongly typed sheets' system of the CellOS.

128 lines (127 loc) 4.43 kB
import { Uri, R, Schema } from './common'; import { TypedSheet } from './TypedSheet'; export class TypedSheetRefs { constructor(args) { this.ns = Uri.ns(TypedSheetRefs.PLACEHOLDER, false); this.typeDef = args.typeDef; this.typename = args.typename; this.parent = args.parent; this._ctx = args.ctx; } static create(args) { return new TypedSheetRefs(args); } static refLinkName(args) { const { typeDef } = args; const nameKey = 'type'; return nameKey; } static refLinkKey(args) { const { typeDef } = args; const nameKey = TypedSheetRefs.refLinkName({ typeDef }); return Schema.ref.links.toKey(nameKey); } static refLink(args) { const { typeDef, links = {} } = args; const linkName = TypedSheetRefs.refLinkName({ typeDef }); const linkKey = TypedSheetRefs.refLinkKey({ typeDef }); const link = Schema.ref.links.find(links).byName(linkName); return { linkKey, linkName, link }; } get isLoaded() { return Boolean(this._sheet); } get sheet() { if (!this.isLoaded) { const err = `Sheet '${this.ns.toString()}' property called before ready [isLoaded].`; throw new Error(err); } return this._sheet; } async load() { if (this.isLoaded) { return this; } if (this._load) { return this._load; } this.fire({ type: 'SHEET/refs/loading', payload: { sheet: this.parent.sheet, refs: this }, }); const promise = new Promise(async (resolve, reject) => { const { fetch, cache, event$, pool } = this._ctx; const linkInfo = await this.getLink(); const link = linkInfo.link; const pooledSheet = link && link.uri.type === 'NS' ? pool.sheet(link.uri) : undefined; if (pooledSheet) { this._sheet = pooledSheet; } else { await this.ensureLink(linkInfo); const def = this.typeDef; this._sheet = await TypedSheet.create({ implements: def.type.uri, ns: this.ns.toString(), fetch, cache, event$, pool, }); } delete this._load; this.fire({ type: 'SHEET/refs/loaded', payload: { sheet: this.parent.sheet, refs: this }, }); resolve(this); }); this._load = promise; return promise; } async data(options = {}) { if (!this.isLoaded) { await this.load(); } const typename = this.typename; return this.sheet.data(Object.assign(Object.assign({}, options), { typename })).load(); } fire(e) { this._ctx.event$.next(e); } async getCell() { const ns = this.parent.cell.ns; const key = this.parent.cell.key; const query = `${key}:${key}`; const res = await this._ctx.fetch.getCells({ ns, query }); return (res.cells || {})[key]; } async getLink() { const typeDef = this.typeDef; const data = (await this.getCell()) || {}; const links = data.links || {}; const { linkKey, link } = TypedSheetRefs.refLink({ typeDef, links }); return { data, links, linkKey, link }; } async ensureLink(input) { const { data, links, linkKey, link } = input || (await this.getLink()); if (this.ns.toString() === TypedSheetRefs.PLACEHOLDER) { this.ns = link ? Uri.ns(link.uri.toString()) : Uri.ns(Schema.cuid()); } if (!links[linkKey]) { const payload = { kind: 'CELL', ns: this.parent.sheet.uri.toString(), key: this.parent.cell.key, to: Object.assign(Object.assign({}, data), { links: Object.assign(Object.assign({}, links), { [linkKey]: this.ns.toString() }) }), }; const isChanged = !R.equals(data, payload.to); if (isChanged) { this.fire({ type: 'SHEET/change', payload }); } } } } TypedSheetRefs.PLACEHOLDER = `ns:${'0'.repeat(25)}`;