UNPKG

@composedb/devtools

Version:

Development tools for ComposeDB projects.

432 lines (431 loc) 17.5 kB
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) }; }