diff --git a/index.js b/index.js index cef3b18f..db843221 100644 --- a/index.js +++ b/index.js @@ -3,17 +3,16 @@ * @module mapeo-schema */ import * as cenc from 'compact-encoding' -import * as JSONSchemas from './dist/schemas.js' +import * as Schemas from './dist/schemas.js' import * as ProtobufSchemas from './types/proto/index.js' import schemasPrefix from './schemasPrefix.js' import { inheritsFromCommon, formatSchemaKey } from './utils.js' const dataTypeIdSize = 6 const schemaVersionSize = 2 - /** - * @param {import('./types/schema/index').MapeoRecord} obj - Object to be encoded - * @returns {import('./types/proto/index').ProtobufSchemas} + * @param {import('./types').JSONSchema} obj - Object to be encoded + * @returns {import('./types').ProtobufSchema} */ const jsonSchemaToProto = (obj) => { const commonKeys = [ @@ -49,12 +48,12 @@ const jsonSchemaToProto = (obj) => { } /** - * @param {import('./types/proto/index').ProtobufSchemas} protobufObj + * @param {import('./types').ProtobufSchema} protobufObj * @param {Object} obj * @param {Number} obj.schemaVersion * @param {String} obj.schemaType * @param {String} obj.version - * @returns {import('./types/schema/index').MapeoRecord} + * @returns {import('./types').JSONSchema} */ const protoToJsonSchema = ( protobufObj, @@ -117,7 +116,7 @@ export const decodeBlockPrefix = (buf) => { /** * Validate an object against the schema type - * @param {import('./types/schema/index').MapeoRecord} obj - Object to be encoded + * @param {import('./types').JSONSchema} obj - Object to be encoded * @returns {Boolean} indicating if the object is valid */ export const validate = (obj) => { @@ -138,7 +137,7 @@ export const validate = (obj) => { delete obj.schemaType } - const validatefn = JSONSchemas[key] + const validatefn = Schemas[key] const isValid = validatefn(obj) if (!isValid) throw new Error(JSON.stringify(validatefn.errors, null, 4)) return isValid @@ -146,7 +145,7 @@ export const validate = (obj) => { /** * Encode a an object validated against a schema as a binary protobuf to send to an hypercore. - * @param {import('./types/schema/index').MapeoRecord} obj - Object to be encoded + * @param {import('./types').JSONSchema} obj - Object to be encoded * @returns {Buffer} protobuf encoded buffer with dataTypeIdSize + schemaVersionSize bytes prepended, one for the type of record and the other for the version of the schema */ export const encode = (obj) => { const key = formatSchemaKey(obj.schemaType, obj.schemaVersion) @@ -171,7 +170,7 @@ export const encode = (obj) => { /** * Decode a Buffer as an object validated against the corresponding schema * @param {Buffer} buf - Buffer to be decoded - * @returns {import('./types/schema/index').MapeoRecord} + * @returns {import('./types').JSONSchema} * */ export const decode = (buf, { coreId, seq }) => { const { dataTypeId, schemaVersion } = decodeBlockPrefix(buf) diff --git a/package.json b/package.json index 88290dbb..9e74f720 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,11 @@ "description": "JSON schema and flow types for Mapeo", "main": "src/index.js", "type": "module", + "exports": { + "types/": "./types/index.d.ts", + "types/schema": "./types/schema/index.d.ts", + "types/proto": "./types/proto/index.d.ts" + }, "scripts": { "example": "node examples/schema_test.js", "protobuf": "buf generate ./proto", diff --git a/scripts/generate.js b/scripts/generate.js index 0a2ebc8b..5928912f 100644 --- a/scripts/generate.js +++ b/scripts/generate.js @@ -2,8 +2,9 @@ // This script is use to generate various files to be used at runtime: // * dist/schemas.js - all the validating functions for jsonSchema // * types/schema/index.d.ts - Union type for all the JsonSchemas -// * types/schema/index.d.ts - Union type for all the ProtobufSchemas +// * types/proto/index.d.ts - Union type for all the ProtobufSchemas // * types/proto/index.js - Exports all protobufs from one file +// * types/index.d.ts - re-exports JSONSchema and ProtobufSchema types for better importing import fs from 'node:fs' import path from 'path' @@ -51,20 +52,35 @@ const ajv = new Ajv({ }) ajv.addKeyword('meta:enum') -// generate code +// generate validation code let schemaValidations = standaloneCode(ajv, schemaExports) const dist = path.join(__dirname, '../dist') if (!fs.existsSync(dist)) { fs.mkdirSync(dist) } -// dump all to file + +// dist/schemas.js fs.writeFileSync( path.join(__dirname, '../dist', 'schemas.js'), schemaValidations ) -// generate types/schema/index.d.ts +const latestSchemaVersions = schemas.reduce( + (acc, { schemaVersion, schemaType }) => { + if (!acc[schemaType]) { + acc[schemaType] = schemaVersion + } else { + if (acc[schemaType] < schemaVersion) { + acc[schemaType] = schemaVersion + } + } + return acc + }, + {} +) + +// types/schema/index.d.ts const jsonSchemaType = ` ${schemas .map( @@ -83,21 +99,30 @@ schemaType?: string; type?: string; schemaVersion?: number; } -export type MapeoRecord = (${schemas +export type JSONSchema = (${schemas .map( /** @param {Object} schema */ - ({ schemaVersion, schemaType }) => { - return `${formatSchemaType(schemaType)}_${schemaVersion}` - } + ({ schemaVersion, schemaType }) => + `${formatSchemaType(schemaType)}_${schemaVersion}` ) .join(' | ')}) & base -` +${schemas + .map(({ schemaType, schemaVersion }) => { + const as = + latestSchemaVersions[schemaType] !== schemaVersion + ? `as ${formatSchemaType(schemaType)}_${schemaVersion}` + : '' + return `export { ${formatSchemaType( + schemaType + )} ${as} } from './${schemaType}/v${latestSchemaVersions[schemaType]}'` + }) + .join('\n')}` fs.writeFileSync( path.join(__dirname, '../types/schema/index.d.ts'), jsonSchemaType ) -// generate index.js for protobuf schemas and index.d.ts +// types/proto/index.d.ts and types/proto/index.js const protobufFiles = glob.sync('../types/proto/*/*.ts', { cwd: 'scripts' }) const obj = protobufFiles .filter((f) => !f.match(/.d.ts/)) @@ -116,7 +141,18 @@ const union = obj ({ schemaType, schemaVersion }) => `${formatSchemaType(schemaType)}_${schemaVersion.replace('v', '')}` ) - .join(' & ') + .join(' | ') + +const individualExports = schemas + .map( + ({ schemaType, schemaVersion }) => + `export { ${formatSchemaType(schemaType)}_${schemaVersion} ${ + latestSchemaVersions[schemaType] === schemaVersion + ? `as ${formatSchemaType(schemaType)}` + : '' + }} from './${schemaType}/v${schemaVersion}'` + ) + .join('\n') obj.forEach(({ schemaType, schemaVersion }) => { const linejs = `export { ${formatSchemaType( @@ -143,5 +179,13 @@ fs.writeFileSync( fs.writeFileSync( path.join(__dirname, '../types/proto/index.d.ts'), `${linesdts.join('\n')} -export type ProtobufSchemas = ${union}` +export type ProtobufSchema = ${union} +${individualExports}` +) + +// types/index.d.ts +fs.writeFileSync( + path.join(__dirname, '../types/index.d.ts'), + `export { ProtobufSchema } from './proto' +export { JSONSchema } from './schema'` )