@platform/cell.typesystem
Version:
The 'strongly typed sheets' system of the CellOS.
128 lines (127 loc) • 4.43 kB
JavaScript
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)}`;