@platform/cell.typesystem
Version:
The 'strongly typed sheets' system of the CellOS.
194 lines (193 loc) • 6.72 kB
JavaScript
import { Subject } from 'rxjs';
import { share, takeUntil } from 'rxjs/operators';
import { TypeClient } from '../../TypeSystem.core';
import { ERROR, ErrorList, MemoryCache, Uri, util } from './common';
import { TypedSheetData } from './TypedSheetData';
import { TypedSheetState } from './TypedSheetState';
import { SheetPool } from '../TypedSheet.SheetPool';
const fromClient = (client) => {
const fetch = util.fetcher.fromClient(client);
return {
load: (ns) => TypedSheet.load({ fetch, ns }),
};
};
export class TypedSheet {
constructor(args) {
this._dispose$ = new Subject();
this._data = {};
this.dispose$ = this._dispose$.pipe(share());
this.uri = Uri.ns(args.uri);
this.implements = Uri.ns(args.implements);
this.pool = args.pool;
const pool = this.pool;
const cache = args.cache || MemoryCache.create();
const event$ = args.event$ || new Subject();
const dispose$ = this.dispose$;
this.event$ = event$.asObservable().pipe(takeUntil(dispose$), share());
this.state = TypedSheetState.create({ sheet: this, event$, fetch: args.fetch, cache });
this._ctx = TypedSheet.ctx({ fetch: this.state.fetch, event$, dispose$, cache, pool });
this._typeDefs = args.types;
this._errorList = ErrorList.create({ defaultType: ERROR.TYPE.SHEET, errors: args.errors });
pool.add(this);
}
static ctx(args) {
const fetch = args.fetch;
const cache = args.cache || MemoryCache.create();
const event$ = args.event$ || new Subject();
const dispose$ = args.dispose$ || new Subject();
const pool = args.pool || SheetPool.create();
return {
event$,
dispose$,
fetch,
cache,
pool,
sheet: {
load(args) {
return TypedSheet.load(Object.assign(Object.assign({}, args), { fetch, cache, event$ }));
},
create(args) {
return TypedSheet.create(Object.assign(Object.assign({}, args), { fetch, cache, event$ }));
},
},
};
}
static async load(args) {
var _a, _b, _c, _d;
const { fetch, cache, event$ } = args;
const sheetNs = Uri.ns(args.ns);
const pool = args.pool || SheetPool.create();
if (pool.exists(args.ns)) {
return pool.sheet(args.ns);
}
const res = await args.fetch.getNs({ ns: sheetNs.toString() });
if (res.error) {
throw new Error(res.error.message);
}
if (!((_b = (_a = res.ns) === null || _a === void 0 ? void 0 : _a.type) === null || _b === void 0 ? void 0 : _b.implements)) {
const err = `The namespace (${sheetNs}) does not contain an "implements" type reference.`;
throw new Error(err);
}
const implementsNs = Uri.ns((_d = (_c = res.ns) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.implements);
const typeDefs = await TypeClient.load({
ns: implementsNs.toString(),
fetch,
cache,
});
const types = typeDefs.defs;
const errors = typeDefs.errors;
return new TypedSheet({
uri: sheetNs,
implements: implementsNs,
types,
fetch,
cache,
event$,
errors,
pool,
});
}
static async create(args) {
const { fetch, event$, cache } = args;
const implementsNs = Uri.ns(args.implements);
const sheetNs = args.ns ? Uri.ns(args.ns) : Uri.create.ns(Uri.cuid());
const pool = args.pool || SheetPool.create();
if (args.ns && pool.exists(args.ns)) {
return pool.sheet(args.ns);
}
const typeDefs = await TypeClient.load({
ns: implementsNs.toString(),
fetch,
cache,
});
const types = typeDefs.defs;
const errors = typeDefs.errors;
return new TypedSheet({
uri: sheetNs,
implements: implementsNs,
types,
fetch,
cache,
event$,
errors,
pool,
});
}
dispose() {
this._data = {};
this._dispose$.next();
this._dispose$.complete();
this.state.dispose();
}
get isDisposed() {
return this._dispose$.isStopped;
}
get ok() {
return this.errors.length === 0;
}
get errors() {
return this._errorList.list;
}
get types() {
if (!this._types) {
const types = [];
this._typeDefs.forEach(def => {
const { typename, columns } = def;
const item = types.find(item => item.typename === typename);
if (item) {
item.columns = [...item.columns, ...columns];
}
else {
types.push({ typename, columns });
}
});
this._types = types;
}
return this._types;
}
toString() {
return this.uri.toString();
}
async info() {
const res = await this._ctx.fetch.getNs({ ns: this.uri.toString() });
const exists = Boolean(res.ns);
const ns = (res.ns || {});
return { exists, ns };
}
data(input) {
this.throwIfDisposed('data');
const args = typeof input === 'string' ? { typename: input } : input;
const { typename, range } = args;
const ctx = this._ctx;
if (this._data[typename]) {
const res = this._data[typename];
if (args.range && res.range !== args.range) {
res.expandRange(args.range);
}
return res;
}
const defs = this._typeDefs;
const def = defs.find(def => def.typename === typename);
if (!def) {
const names = defs.map(def => `'${def.typename}'`).join(', ');
const err = `Definitions for typename '${typename}' not found. Available typenames: ${names}.`;
throw new Error(err);
}
const types = def.columns;
const res = TypedSheetData.create({
sheet: this,
typename,
types,
ctx,
range,
});
this._data[typename] = res;
return res;
}
throwIfDisposed(action) {
if (this.isDisposed) {
throw new Error(`Cannot ${action} because [TypedSheet] is disposed.`);
}
}
}
TypedSheet.client = fromClient;