@composedb/devtools
Version:
Development tools for ComposeDB projects.
432 lines (431 loc) • 17.5 kB
JavaScript
function _check_private_redeclaration(obj, privateCollection) {
if (privateCollection.has(obj)) {
throw new TypeError("Cannot initialize the same private elements twice on an object");
}
}
function _class_apply_descriptor_get(receiver, descriptor) {
if (descriptor.get) {
return descriptor.get.call(receiver);
}
return descriptor.value;
}
function _class_apply_descriptor_set(receiver, descriptor, value) {
if (descriptor.set) {
descriptor.set.call(receiver, value);
} else {
if (!descriptor.writable) {
throw new TypeError("attempted to set read only private field");
}
descriptor.value = value;
}
}
function _class_extract_field_descriptor(receiver, privateMap, action) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to " + action + " private field on non-instance");
}
return privateMap.get(receiver);
}
function _class_private_field_get(receiver, privateMap) {
var descriptor = _class_extract_field_descriptor(receiver, privateMap, "get");
return _class_apply_descriptor_get(receiver, descriptor);
}
function _class_private_field_init(obj, privateMap, value) {
_check_private_redeclaration(obj, privateMap);
privateMap.set(obj, value);
}
function _class_private_field_set(receiver, privateMap, value) {
var descriptor = _class_extract_field_descriptor(receiver, privateMap, "set");
_class_apply_descriptor_set(receiver, descriptor, value);
return value;
}
import { camelCase, pascalCase } from 'change-case';
import { JsonReference } from 'json-ptr';
import { SCALAR_RUNTIME_TYPES } from '../schema/scalars.js';
import { sortKeys, viewDefinitionToRuntime } from '../utils.js';
/** @internal */ export function getName(base, prefix = '') {
const withCase = pascalCase(base);
return withCase.startsWith(prefix) ? withCase : prefix + withCase;
}
/** @internal */ export function getStringScalarType(schema) {
return SCALAR_RUNTIME_TYPES[schema.title] ?? 'string';
}
const NON_INDEXABLE_FIELD_TYPES = [
'list',
'reference',
'view'
];
const INDEXABLE_REFERENCE_TYPES = [
'enum',
'object'
];
var _accountData = /*#__PURE__*/ new WeakMap(), _commonEmbeds = /*#__PURE__*/ new WeakMap(), _modelAccountRelation = /*#__PURE__*/ new WeakMap(), _modelName = /*#__PURE__*/ new WeakMap(), _modelRelations = /*#__PURE__*/ new WeakMap(), _modelSchema = /*#__PURE__*/ new WeakMap(), _modelViews = /*#__PURE__*/ new WeakMap(), _modelIndices = /*#__PURE__*/ new WeakMap(), _objects = /*#__PURE__*/ new WeakMap(), _enums = /*#__PURE__*/ new WeakMap(), _unions = /*#__PURE__*/ new WeakMap(), _immutableFields = /*#__PURE__*/ new WeakMap();
/** @internal */ export class RuntimeModelBuilder {
build() {
const modelObject = this._buildObject(_class_private_field_get(this, _modelSchema));
_class_private_field_get(this, _objects)[_class_private_field_get(this, _modelName)] = modelObject;
this._buildRelations(_class_private_field_get(this, _modelRelations));
this._buildViews(modelObject, _class_private_field_get(this, _modelViews));
this._buildIndices(modelObject, _class_private_field_get(this, _modelIndices));
return {
accountData: _class_private_field_get(this, _accountData),
objects: _class_private_field_get(this, _objects),
enums: _class_private_field_get(this, _enums),
unions: _class_private_field_get(this, _unions)
};
}
_getName(schema, params, isReference = false) {
return isReference && typeof schema.title === 'string' ? _class_private_field_get(this, _commonEmbeds).includes(schema.title) ? schema.title : getName(schema.title, _class_private_field_get(this, _modelName)) : params.ownName ?? _class_private_field_get(this, _modelName);
}
_getReferenceSchema(reference) {
const ref = new JsonReference(reference);
const schema = ref.resolve(_class_private_field_get(this, _modelSchema));
if (schema == null) {
throw new Error(`Missing reference: ${reference}`);
}
return schema;
}
_buildObject(schema, params = {}) {
const ownName = this._getName(schema, params);
const requiredProps = schema.required ?? [];
const fields = {};
for (const [propKey, propSchema] of Object.entries(schema.properties ?? {})){
fields[propKey] = this._buildObjectField(propSchema, {
ownName: propKey,
parentName: ownName,
required: requiredProps.includes(propKey),
immutable: _class_private_field_get(this, _immutableFields).includes(propKey)
});
}
return fields;
}
_buildObjectField(schema, params = {}) {
if (schema.$ref != null) {
return this._buildReferenceSchema(schema.$ref, params);
}
switch(schema.type){
case 'array':
return this._buildList(schema, params);
case 'object':
return this._buildObjectReferenceField(schema, params);
default:
return this._buildScalar(schema, params);
}
}
_buildList(schema, params = {}) {
if (typeof schema.items !== 'object' || Array.isArray(schema.items)) {
throw new Error('Unsupported items schema in array');
}
const required = params.required ?? false;
const immutable = params.immutable ?? false;
const items = schema.items;
if (items.$ref != null) {
return {
type: 'list',
required,
immutable,
item: this._buildListReference(items.$ref, params)
};
}
if (items.type == null) {
throw new Error('Missing schema $ref or type for array items');
}
let item;
switch(items.type){
case 'array':
throw new Error('Unsupported array in array');
case 'object':
item = this._buildObjectReferenceField(items, params);
break;
default:
item = this._buildScalar(items, params);
break;
}
return {
type: 'list',
required,
immutable,
item
};
}
_buildListReference(reference, params = {}) {
const schema = this._getReferenceSchema(reference);
switch(schema.type){
case 'array':
throw new Error('Unsupported array in array reference');
case 'object':
return this._buildObjectReferenceField(schema, params);
case 'string':
return schema.enum != null && schema.title != null ? this._buildEnumReferenceField(schema) : this._buildScalar(schema, params);
default:
return this._buildScalar(schema, params);
}
}
_buildObjectReferenceField(schema, params = {}) {
const ownName = this._getName(schema, params, true);
if (_class_private_field_get(this, _objects)[ownName] == null) {
_class_private_field_get(this, _objects)[ownName] = this._buildObject(schema, {
...params,
ownName
});
}
return {
type: 'reference',
refType: 'object',
refName: ownName,
required: params.required ?? false,
immutable: params.immutable ?? false
};
}
_buildEnumReferenceField(schema, params = {}) {
const ownName = this._getName(schema, params, true);
if (_class_private_field_get(this, _enums)[ownName] == null) {
_class_private_field_get(this, _enums)[ownName] = schema.enum;
}
return {
type: 'reference',
refType: 'enum',
refName: ownName,
required: params.required ?? false,
immutable: params.immutable ?? false
};
}
_buildReferenceSchema(reference, params = {}) {
const schema = this._getReferenceSchema(reference);
switch(schema.type){
case 'array':
return this._buildList(schema, params);
case 'object':
return this._buildObjectReferenceField(schema, params);
case 'string':
return schema.enum != null && schema.title != null ? this._buildEnumReferenceField(schema) : this._buildScalar(schema, params);
default:
return this._buildScalar(schema, params);
}
}
_buildScalar(schema, params = {}) {
if (schema.type == null) {
throw new Error('Missing scalar type');
}
const required = params.required ?? false;
const immutable = params.immutable ?? false;
switch(schema.type){
case 'boolean':
case 'integer':
return {
type: schema.type,
required,
immutable
};
case 'number':
return {
type: 'float',
required,
immutable
};
case 'string':
return {
type: getStringScalarType(schema),
required,
immutable
};
}
}
_buildRelations(relations = {}) {
for (const [key, relation] of Object.entries(relations)){
if (relation.type === 'account') {
const relationKey = camelCase(`${key}Of${_class_private_field_get(this, _modelName)}`);
_class_private_field_get(this, _accountData)[`${relationKey}List`] = {
type: 'account',
name: _class_private_field_get(this, _modelName),
property: key
};
if (_class_private_field_get(this, _modelAccountRelation).type === 'set') {
_class_private_field_get(this, _accountData)[relationKey] = {
type: 'account-set',
name: _class_private_field_get(this, _modelName),
property: key
};
}
}
}
}
_buildViews(object, views = {}) {
for (const [key, view] of Object.entries(views)){
object[key] = viewDefinitionToRuntime(view);
}
}
_buildIndices(object, indices) {
for (const index of indices){
for (const field of index.fields){
const path = field.path[0];
const objectField = object[path];
if (objectField == null) {
throw new Error(`Could not resolve ${field.path.join('.')} as a valid path`);
}
if (objectField.type === 'reference') {
if (INDEXABLE_REFERENCE_TYPES.includes(objectField.refType)) {
objectField.indexed = true;
} else {
throw new Error(`${field.path.join('.')} is not indexable`);
}
} else if (NON_INDEXABLE_FIELD_TYPES.includes(objectField.type)) {
throw new Error(`${field.path.join('.')} is not indexable`);
}
objectField.indexed = true;
}
}
}
constructor(params){
_class_private_field_init(this, _accountData, {
writable: true,
value: {}
});
_class_private_field_init(this, _commonEmbeds, {
writable: true,
value: void 0
});
_class_private_field_init(this, _modelAccountRelation, {
writable: true,
value: void 0
});
_class_private_field_init(this, _modelName, {
writable: true,
value: void 0
});
_class_private_field_init(this, _modelRelations, {
writable: true,
value: void 0
});
_class_private_field_init(this, _modelSchema, {
writable: true,
value: void 0
});
_class_private_field_init(this, _modelViews, {
writable: true,
value: void 0
});
_class_private_field_init(this, _modelIndices, {
writable: true,
value: void 0
});
_class_private_field_init(this, _objects, {
writable: true,
value: {}
});
_class_private_field_init(this, _enums, {
writable: true,
value: {}
});
_class_private_field_init(this, _unions, {
writable: true,
value: {}
});
_class_private_field_init(this, _immutableFields, {
writable: true,
value: []
});
_class_private_field_set(this, _commonEmbeds, params.commonEmbeds ?? []);
_class_private_field_set(this, _modelAccountRelation, params.definition.accountRelation);
_class_private_field_set(this, _modelName, params.name);
_class_private_field_set(this, _modelRelations, params.definition.relations ?? {});
_class_private_field_set(this, _modelSchema, params.definition.schema);
_class_private_field_set(this, _modelViews, params.views);
_class_private_field_set(this, _modelIndices, params.indices);
const immutableFields = new Set(params.definition.version === '1.0' ? [] : params.definition.immutableFields ?? []);
if (_class_private_field_get(this, _modelAccountRelation).type === 'set') {
// Add fields defined in the SET account relation as immutable
for (const field of _class_private_field_get(this, _modelAccountRelation).fields){
immutableFields.add(field);
}
}
_class_private_field_set(this, _immutableFields, Array.from(immutableFields));
}
}
/** @internal */ export function createRuntimeDefinition(definition) {
const runtime = {
models: {},
objects: {},
enums: {},
accountData: {}
};
for (const [modelID, modelDefinition] of Object.entries(definition.models)){
const modelName = definition.aliases?.[modelID] ?? modelDefinition.name;
const isV1 = modelDefinition.version === '1.0';
const interfaceDefinition = isV1 ? {
interface: false,
implements: []
} : {
interface: modelDefinition.interface,
implements: modelDefinition.implements
};
// Add name to model metadata mapping
runtime.models[modelName] = {
...interfaceDefinition,
id: modelID,
accountRelation: modelDefinition.accountRelation
};
// Extract objects, enums, relations and views from model schema
const modelViews = modelDefinition.views ?? {};
const compositeModelViews = definition.views?.models?.[modelID] ?? {};
const modelBuilder = new RuntimeModelBuilder({
commonEmbeds: definition.commonEmbeds,
name: modelName,
definition: modelDefinition,
views: {
...modelViews,
...compositeModelViews
},
indices: definition.indices?.[modelID] ?? []
});
const builtModel = modelBuilder.build();
// Inject extracted types to runtime definition
Object.assign(runtime.accountData, builtModel.accountData);
Object.assign(runtime.objects, builtModel.objects);
Object.assign(runtime.enums, builtModel.enums);
// Attach entry-point to account store based on relation type
const key = camelCase(modelName);
if (modelDefinition.version !== '1.0' && modelDefinition.interface) {
runtime.accountData[key + 'List'] = {
type: 'connection',
name: modelName
};
} else {
const relationType = modelDefinition.accountRelation.type;
switch(relationType){
case 'list':
runtime.accountData[key + 'List'] = {
type: 'connection',
name: modelName
};
break;
case 'set':
// relation to single document in the set based on input
runtime.accountData[key] = {
type: 'set',
name: modelName
};
// relation to all documents in the set
runtime.accountData[key + 'List'] = {
type: 'connection',
name: modelName
};
break;
case 'single':
runtime.accountData[key] = {
type: 'node',
name: modelName
};
break;
default:
throw new Error(`Unsupported account relation type: ${relationType}`);
}
}
}
return {
...runtime,
models: sortKeys(runtime.models),
objects: sortKeys(runtime.objects),
enums: sortKeys(runtime.enums),
accountData: sortKeys(runtime.accountData)
};
}