UNPKG

manyfest

Version:

JSON Object Manifest for Data Description and Parsing

187 lines (143 loc) 6.81 kB
# Schema Manipulation Manyfest provides tools for remapping, merging and auto-generating schemas. These are useful when adapting a schema to different object shapes, combining schemas from multiple sources, or bootstrapping a schema from sample data. ## Address Remapping ### resolveAddressMappings Remap descriptors from one set of addresses to another. This permanently mutates the descriptor object. ```javascript const descriptors = { 'Address.Of.a': { Hash: 'a', DataType: 'Number' }, 'Address.Of.b': { Hash: 'b', DataType: 'String' } }; const mapping = { 'a': 'New.Address.Of.a', 'b': 'New.Address.Of.b' }; manifest.schemaManipulations.resolveAddressMappings(descriptors, mapping); // descriptors is now: // { // 'New.Address.Of.a': { Hash: 'a', DataType: 'Number' }, // 'New.Address.Of.b': { Hash: 'b', DataType: 'String' } // } ``` The mapping keys can be either addresses or hashes. If a key matches a descriptor's address directly, that descriptor is moved. If a key matches a descriptor's hash, the descriptor at that hash's address is moved. If a mapping key matches neither an existing address nor a hash, a new descriptor is created with the key as its hash. ### safeResolveAddressMappings The same operation, but returns a new object instead of mutating the original: ```javascript const original = { 'old.path': { Hash: 'value', DataType: 'String' } }; const remapped = manifest.schemaManipulations.safeResolveAddressMappings(original, { 'value': 'new.path' }); // original is unchanged // remapped is: { 'new.path': { Hash: 'value', DataType: 'String' } } ``` ## Merging Schemas ### mergeAddressMappings Combine two sets of descriptors. The destination (first argument) takes precedence when both contain the same address: ```javascript const base = { 'Name': { DataType: 'String' }, 'Email': { DataType: 'String' } }; const extra = { 'Email': { DataType: 'String', Required: true }, 'Phone': { DataType: 'String' } }; const merged = manifest.schemaManipulations.mergeAddressMappings(base, extra); // { // 'Name': { DataType: 'String' }, // 'Email': { DataType: 'String' }, <-- base wins (no Required) // 'Phone': { DataType: 'String' } <-- added from extra // } ``` Returns a new object. Neither input is mutated. ## Auto-Generating Schemas ### generateAddressses Generate a schema from a sample object. This recursively walks the object and produces a descriptor for every reachable address: ```javascript const sample = { name: 'Alice', age: 30, address: { city: 'Portland', zip: '97201' }, tags: ['admin', 'user'] }; const generated = manifest.objectAddressGeneration.generateAddressses(sample); // { // 'name': { Address: 'name', Hash: 'name', Name: 'name', DataType: 'String', Default: 'Alice', InSchema: false }, // 'age': { Address: 'age', Hash: 'age', Name: 'age', DataType: 'Number', Default: 30, InSchema: false }, // 'address': { Address: 'address', Hash: 'address', Name: 'address', DataType: 'Object', InSchema: false }, // 'address.city':{ Address: 'address.city', Hash: 'address.city', Name: 'address.city', DataType: 'String', Default: 'Portland', InSchema: false }, // 'address.zip': { Address: 'address.zip', Hash: 'address.zip', Name: 'address.zip', DataType: 'String', Default: '97201', InSchema: false }, // 'tags': { Address: 'tags', Hash: 'tags', Name: 'tags', DataType: 'Array', InSchema: false }, // 'tags[0]': { Address: 'tags[0]', Hash: 'tags[0]', Name: 'tags[0]', DataType: 'String', Default: 'admin', InSchema: false }, // 'tags[1]': { Address: 'tags[1]', Hash: 'tags[1]', Name: 'tags[1]', DataType: 'String', Default: 'user', InSchema: false } // } ``` Every generated descriptor has `InSchema: false`, which acts as a flag for tooling to prompt a developer to opt-in before including it in a final schema. Data types are inferred from JavaScript types: - `string` becomes `String` - `number` and `bigint` become `Number` - Arrays become `Array` (with each element also generated) - Objects become `Object` (with each property also generated) - `null` and `undefined` become `Any` - Functions and Symbols are skipped ## Use Cases ### Adapting a Schema to a Different API Shape ```javascript const baseSchema = { 'user.name': { Hash: 'UserName', DataType: 'String' }, 'user.email': { Hash: 'UserEmail', DataType: 'String' }, 'user.role': { Hash: 'UserRole', DataType: 'String' } }; // API v2 returns data in a flat shape const v2Mapping = { 'UserName': 'display_name', 'UserEmail': 'email_address', 'UserRole': 'access_level' }; const v2Schema = manifest.schemaManipulations.safeResolveAddressMappings(baseSchema, v2Mapping); // v2Schema addresses are now: display_name, email_address, access_level // Hashes remain: UserName, UserEmail, UserRole ``` ### Bootstrapping a Schema from Sample Data ```javascript // Take a sample API response const sampleResponse = await fetch('/api/users/1').then(r => r.json()); // Generate all possible addresses const generated = manifest.objectAddressGeneration.generateAddressses(sampleResponse); // Pick the ones you need and build a proper schema const schema = new libManyfest({ Scope: 'User', Descriptors: { 'data.id': { ...generated['data.id'], InSchema: true, Required: true }, 'data.name': { ...generated['data.name'], InSchema: true, Name: 'Display Name' }, 'data.email': { ...generated['data.email'], InSchema: true, Name: 'Email' } } }); ``` ### Combining Base and Extension Schemas ```javascript const coreFields = { 'ID': { DataType: 'Integer', Required: true }, 'Name': { DataType: 'String', Required: true }, 'Created': { DataType: 'DateTime' } }; const auditFields = { 'CreatedBy': { DataType: 'String' }, 'ModifiedBy': { DataType: 'String' }, 'Modified': { DataType: 'DateTime' } }; const fullSchema = manifest.schemaManipulations.mergeAddressMappings(coreFields, auditFields); // Contains all six fields ``` ## Notes - `resolveAddressMappings` mutates the input object; use `safeResolveAddressMappings` when you need to preserve the original - `mergeAddressMappings` gives precedence to the first (destination) argument on address conflicts - `generateAddressses` produces every reachable address including individual array elements -- the output can be large for complex objects - Generated schemas use `InSchema: false` as a signal that the descriptor was auto-generated and has not been reviewed - Symbols and functions in source objects are silently skipped during generation