UNPKG

nimma

Version:

Scalable JSONPath engine.

1,706 lines (1,562 loc) 39.7 kB
import * as assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import Nimma from '../index.mjs'; import { RuntimeError } from '../runtime/errors/index.mjs'; function collect(input, expressions, opts) { const collected = {}; const _ = (expr, scope) => { collected[expr] ??= []; collected[expr].push([scope.value, scope.path]); }; Nimma.query( input, expressions.reduce((mapped, expression) => { mapped[expression] = _.bind(null, expression); return mapped; }, {}), opts, ); return collected; } describe('Nimma', () => { it('root', () => { const document = { info: { contact: { test: 'c', }, x: 'foo', }, }; const collected = collect(document, ['$']); assert.deepEqual(collected, { $: [[document, []]], }); }); it('works', () => { const document = { info: { contact: { test: 'c', }, x: 'foo', }, }; const collected = collect(document, ['$.info.contact']); assert.deepEqual(collected, { '$.info.contact': [[{ test: 'c' }, ['info', 'contact']]], }); }); it('works#2', () => { const document = { info: { contact: { foo: 'bar', test: 'c', }, x: 'foo', }, }; const collected = collect(document, ['$.info.contact.*']); assert.deepEqual(collected, { '$.info.contact.*': [ ['bar', ['info', 'contact', 'foo']], ['c', ['info', 'contact', 'test']], ], }); }); it('works#3', () => { const document = { info: { contact: { bar: false, foo: 'a', 'foo-2': 'b', 'foo-3': 'c', }, x: 'foo', }, }; const collected = collect(document, [ '$.info..[?(@property.startsWith("foo"))]', ]); assert.deepEqual(collected, { '$.info..[?(@property.startsWith("foo"))]': [ ['a', ['info', 'contact', 'foo']], ['b', ['info', 'contact', 'foo-2']], ['c', ['info', 'contact', 'foo-3']], ], }); }); it('works#4', () => { const document = { info: { contact: { bar: false, foo: 'a', 'foo-2': 'b', 'foo-3': 'c', }, x: 'foo', }, }; const collected = collect(document, [ '$.info..*[?(@property.startsWith("foo"))]', ]); assert.deepEqual(collected, { '$.info..*[?(@property.startsWith("foo"))]': [ ['a', ['info', 'contact', 'foo']], ['b', ['info', 'contact', 'foo-2']], ['c', ['info', 'contact', 'foo-3']], ], }); }); it('works#5', () => { const document = { paths: { bar: { get: { put: { baz: 2, }, }, put: 2, }, foo: { get: { post: {}, }, }, x: '', }, }; const collected = collect(document, [ "$.paths.*[?( @property === 'get' || @property === 'put' || @property === 'post' )]", ]); assert.deepEqual(collected, { "$.paths.*[?( @property === 'get' || @property === 'put' || @property === 'post' )]": [ [{ put: { baz: 2 } }, ['paths', 'bar', 'get']], [2, ['paths', 'bar', 'put']], [{ post: {} }, ['paths', 'foo', 'get']], ], }); }); it('works#6', () => { const document = { paths: { bar: { get: { put: { baz: 2, }, }, put: 2, }, foo: { get: { post: {}, }, }, x: '', }, }; const collected = collect(document, [ "$..[?( @property === 'get' || @property === 'put' || @property === 'post' )]", ]); assert.deepEqual(collected, { "$..[?( @property === 'get' || @property === 'put' || @property === 'post' )]": [ [{ put: { baz: 2 } }, ['paths', 'bar', 'get']], [{ baz: 2 }, ['paths', 'bar', 'get', 'put']], [2, ['paths', 'bar', 'put']], [{ post: {} }, ['paths', 'foo', 'get']], [{}, ['paths', 'foo', 'get', 'post']], ], }); }); it('works#7', () => { const document = { paths: { bar: { get: { put: { baz: 2, }, }, put: 2, }, foo: { get: { post: {}, }, }, x: '', }, }; const collected = collect(document, [ "$..paths..[?( @property === 'get' || @property === 'put' || @property === 'post' )]", ]); assert.deepEqual(collected, { "$..paths..[?( @property === 'get' || @property === 'put' || @property === 'post' )]": [ [{ put: { baz: 2 } }, ['paths', 'bar', 'get']], [{ baz: 2 }, ['paths', 'bar', 'get', 'put']], [2, ['paths', 'bar', 'put']], [{ post: {} }, ['paths', 'foo', 'get']], [{}, ['paths', 'foo', 'get', 'post']], ], }); }); it('works#8', () => { const document = { paths: { bar: { get: { put: { baz: 2, }, }, put: 2, }, foo: { get: { post: {}, }, }, x: '', }, }; const collected = collect(document, [ "$..paths..*[?( @property === 'get' || @property === 'put' || @property === 'post' )]", ]); assert.deepEqual(collected, { "$..paths..*[?( @property === 'get' || @property === 'put' || @property === 'post' )]": [ [{ put: { baz: 2 } }, ['paths', 'bar', 'get']], [{ baz: 2 }, ['paths', 'bar', 'get', 'put']], [2, ['paths', 'bar', 'put']], [{ post: {} }, ['paths', 'foo', 'get']], [{}, ['paths', 'foo', 'get', 'post']], ], }); }); it('works#9', () => { const document = { paths: { bar: { get: { put: { baz: 2, }, }, put: 2, }, foo: { get: { post: {}, }, }, get: {}, x: '', }, }; const collected = collect(document, ['$..paths..get']); assert.deepEqual(collected, { '$..paths..get': [ [{ put: { baz: 2 } }, ['paths', 'bar', 'get']], [{ post: {} }, ['paths', 'foo', 'get']], [{}, ['paths', 'get']], ], }); }); it('works#10', () => { const document = { bar: { foo: { bar: { c: true, bar: { x: { c: false, }, }, }, }, }, foo: { x: { bar: { d: { c: 'yup!', }, }, }, }, }; const collected = collect(document, ['$..bar..c']); assert.deepEqual(collected, { '$..bar..c': [ [true, ['bar', 'foo', 'bar', 'c']], [false, ['bar', 'foo', 'bar', 'bar', 'x', 'c']], ['yup!', ['foo', 'x', 'bar', 'd', 'c']], ], }); }); it('works#11', () => { const document = { bar: { 200: { foo: 'a', z: { foo: 'b', }, }, 401: { foo: 'c', z: { foo: 'd', 900: { foo: 'e', }, }, }, }, }; const collected = collect(document, ['$.bar[?( @property >= 400 )]..foo']); assert.deepEqual(collected, { '$.bar[?( @property >= 400 )]..foo': [ ['c', ['bar', '401', 'foo']], ['e', ['bar', '401', 'z', '900', 'foo']], ['d', ['bar', '401', 'z', 'foo']], ], }); }); it('works#12', () => { const document = { bar: { 200: { foo: 'a', z: { foo: 'b', }, }, 401: { foo: 'c', z: { foo: 'd', 900: { foo: 'e', }, }, }, }, }; const collected = collect(document, ['$..[?( @property >= 400 )]..foo']); assert.deepEqual(collected, { '$..[?( @property >= 400 )]..foo': [ ['c', ['bar', '401', 'foo']], ['e', ['bar', '401', 'z', '900', 'foo']], ['d', ['bar', '401', 'z', 'foo']], ], }); }); it('works#13', () => { const document = { bar: { 200: { foo: 'a', z: { foo: 'b', }, }, 401: { foo: 'c', z: { foo: { 900: { foo: 'e', }, }, }, }, }, }; const collected = collect(document, [ '$..foo..[?( @property >= 900 )]..foo', ]); assert.deepEqual(collected, { '$..foo..[?( @property >= 900 )]..foo': [ ['e', ['bar', '401', 'z', 'foo', '900', 'foo']], ], }); }); it('works#14', () => { const document = { bar: { examples: { foo: 'a', z: { foo: 'b', }, }, }, }; const collected = collect(document, ['$..examples.*', '$..examples..*']); assert.deepEqual(collected, { '$..examples.*': [ ['a', ['bar', 'examples', 'foo']], [{ foo: 'b' }, ['bar', 'examples', 'z']], ], '$..examples..*': [ ['a', ['bar', 'examples', 'foo']], [{ foo: 'b' }, ['bar', 'examples', 'z']], ['b', ['bar', 'examples', 'z', 'foo']], ], }); }); it('works#15', () => { const document = { info: { contact: { test: 'c', }, }, }; const collected = collect(document, ['$.info']); assert.deepEqual(collected, { '$.info': [[{ contact: { test: 'c' } }, ['info']]], }); }); it('works#16', () => { const document = { parameters: [ { in: 'header', name: 'fooA', }, ], foo: { parameters: [ { in: 'header', name: 'd 1', }, ], }, }; const collected = collect(document, [ "$..parameters[?(@.in === 'header')]", ]); assert.deepEqual(collected, { "$..parameters[?(@.in === 'header')]": [ [{ in: 'header', name: 'fooA' }, ['parameters', 0]], [{ in: 'header', name: 'd 1' }, ['foo', 'parameters', 0]], ], }); }); it('works#17', () => { const document = { bar: { user: { name: 'Eva', }, }, foo: { user: { name: 'John', }, }, }; const collected = collect(document, [ "$..[?((@parentProperty === 'foo' || @parentProperty === 'bar') && @.name)]", "$..[?(@parent && @parent.user && @parent.user.name === 'Eva')]", ]); assert.deepEqual(collected, { "$..[?((@parentProperty === 'foo' || @parentProperty === 'bar') && @.name)]": [ [{ name: 'Eva' }, ['bar', 'user']], [{ name: 'John' }, ['foo', 'user']], ], "$..[?(@parent && @parent.user && @parent.user.name === 'Eva')]": [ ['Eva', ['bar', 'user', 'name']], ], }); }); it('works#18', () => { const document = { example: 'test', examples: { user: { name: 'Eva', }, foo: { user: { name: 'John', }, }, }, author: { name: 'Jakub', }, }; const collected = collect(document, ['$.examples..*']); assert.deepEqual(collected, { '$.examples..*': [ [{ name: 'Eva' }, ['examples', 'user']], ['Eva', ['examples', 'user', 'name']], [{ user: { name: 'John' } }, ['examples', 'foo']], [{ name: 'John' }, ['examples', 'foo', 'user']], ['John', ['examples', 'foo', 'user', 'name']], ], }); }); it('works#19', () => { const document = { channels: { '/a': { publish: { a: { payload: 2, }, }, }, '/b': { publish: { b: { payload: 4, }, }, }, }, }; const collected = collect(document, [ '$.channels[*][publish,subscribe][?(@.schemaFormat === void 0)].payload', ]); assert.deepEqual(collected, { '$.channels[*][publish,subscribe][?(@.schemaFormat === void 0)].payload': [ [2, ['channels', '/a', 'publish', 'a', 'payload']], [4, ['channels', '/b', 'publish', 'b', 'payload']], ], }); }); it('works#20', () => { const document = { openapi: '3.0.2', components: { links: { address: { operationId: 'getUserAddressByUUID', parameters: { param: { value: 'value', in: 'header', }, }, }, }, }, }; const collected = collect(document, ['$..parameters[?(@.in)]']); assert.deepEqual(collected, { '$..parameters[?(@.in)]': [ [ { in: 'header', value: 'value' }, ['components', 'links', 'address', 'parameters', 'param'], ], ], }); }); it('works#21', () => { const document = { firstName: 'John', lastName: 'doe', age: 26, address: { streetAddress: 'naist street', city: 'Nara', postalCode: '630-0192', }, phoneNumbers: [ { type: 'iPhone', number: '0123-4567-8888', }, { type: 'home', number: '0123-4567-8910', }, ], }; const collected = collect(document, ['$.address~', '$[*]~']); assert.deepEqual(collected, { '$.address~': [['address', ['address']]], '$[*]~': [ ['firstName', ['firstName']], ['lastName', ['lastName']], ['age', ['age']], ['address', ['address']], ['phoneNumbers', ['phoneNumbers']], ], }); }); it('works#22', () => { const document = { test1: { example: true, }, }; const collected = collect(document, ['$[?(@ && @.example)]']); assert.deepEqual(collected, { '$[?(@ && @.example)]': [[{ example: true }, ['test1']]], }); }); it('works#24', () => { const document = { channels: [ { publish: { foo: { payload: 2 }, bar: { schemaFormat: 2, payload: 4 }, }, }, ], }; const collected = collect(document, [ '$.channels[*][publish,subscribe][?(@.schemaFormat === void 0)].payload', ]); assert.deepEqual(collected, { '$.channels[*][publish,subscribe][?(@.schemaFormat === void 0)].payload': [[2, ['channels', 0, 'publish', 'foo', 'payload']]], }); }); it('works#25', () => { const document = { continents: [ { id: '1', name: 'Europe', countries: [ { id: '4', name: 'Austria', }, { id: '5', name: 'Belgium', }, { id: '6', name: 'Croatia', }, ], }, { id: '2', name: 'Asia', cities: [ { id: '7', name: 'Japan', }, { id: '8', name: 'Indonesia', }, ], }, ], }; const collected = collect(document, [ '$.continents[:-1].countries[0:2].name', '$.continents[:1].countries[::2].name', '$.continents[:1].countries[0,1,2].name', ]); assert.deepEqual(collected, { '$.continents[:1].countries[0,1,2].name': [ ['Austria', ['continents', 0, 'countries', 0, 'name']], ['Belgium', ['continents', 0, 'countries', 1, 'name']], ['Croatia', ['continents', 0, 'countries', 2, 'name']], ], '$.continents[:-1].countries[0:2].name': [ ['Austria', ['continents', 0, 'countries', 0, 'name']], ['Belgium', ['continents', 0, 'countries', 1, 'name']], ], '$.continents[:1].countries[::2].name': [ ['Austria', ['continents', 0, 'countries', 0, 'name']], ['Croatia', ['continents', 0, 'countries', 2, 'name']], ], }); }); it('works#26', () => { const document = [ 'Moscow', 'Saint Petersburg', 'Kazan', 'Novosibirsk', 'Krasnoyarsk', 'Omsk', 'Tula', 'Nizhny Novgorod', 'Norilsk', 'Kaliningrad', 'Ryazan', ]; const collected = collect(document, [ '$[1::2]', '$[1:-5:2]', '$[10::2]', '$[1:5:3]', ]); assert.deepEqual(collected, { '$[1:-5:2]': [ ['Saint Petersburg', [1]], ['Novosibirsk', [3]], ['Omsk', [5]], ], '$[1::2]': [ ['Saint Petersburg', [1]], ['Novosibirsk', [3]], ['Omsk', [5]], ['Nizhny Novgorod', [7]], ['Kaliningrad', [9]], ], '$[10::2]': [['Ryazan', [10]]], '$[1:5:3]': [ ['Saint Petersburg', [1]], ['Krasnoyarsk', [4]], ], }); }); it('works#27', () => { const document = { size: 'xl', }; const collected = collect(document, ['$.size', "$['size']"]); assert.deepEqual(collected, { '$.size': [['xl', ['size']]], "$['size']": [['xl', ['size']]], }); }); it('works#28', () => { const document = { Europe: { East: { Poland: { cities: ['Poznań'], }, }, West: {}, }, }; const collected = collect(document, ['$.Europe[*]..cities[?(@ ~= "^P")]']); assert.deepEqual(collected, { '$.Europe[*]..cities[?(@ ~= "^P")]': [ ['Poznań', ['Europe', 'East', 'Poland', 'cities', 0]], ], }); }); it('works#29', () => { const document = { paths: { '/some-url/{someId}': { parameters: [ { name: 'someId', schema: { type: 'integer', }, }, ], }, }, }; const collected = collect(document, [ '$.paths..parameters[?(@.name ~= "^id$|_?(id|Id)$")].schema', ]); assert.deepEqual(collected, { '$.paths..parameters[?(@.name ~= "^id$|_?(id|Id)$")].schema': [ [ { type: 'integer' }, ['paths', '/some-url/{someId}', 'parameters', 0, 'schema'], ], ], }); }); it('works#30', () => { const document = { Europe: { East: { Poland: { cities: ['Poznań', 'P.Zdrój'], }, }, West: {}, }, }; const collected = collect(document, [ '$.Europe[*]..cities[?(@ ~= "^P\\\\.")]', ]); assert.deepEqual(collected, { '$.Europe[*]..cities[?(@ ~= "^P\\\\.")]': [ ['P.Zdrój', ['Europe', 'East', 'Poland', 'cities', 1]], ], }); }); it('works#31', () => { const document = { Asia: ['Malaysia', 'Indonesia', 'Thailand', 'Laos', 'Myanmar', 'Vietnam'], Europe: ['Austria', 'Belgium', 'Czechia', 'France', 'Germany'], }; const collected = collect(document, [ '$.Europe[2:-2:2]', '$.Asia[0:5]', '$.Europe[2:-1:-5]', '$.Asia[0:-3:-3]', ]); assert.deepEqual(collected, { '$.Europe[2:-2:2]': [['Czechia', ['Europe', 2]]], '$.Asia[0:5]': [ ['Malaysia', ['Asia', 0]], ['Indonesia', ['Asia', 1]], ['Thailand', ['Asia', 2]], ['Laos', ['Asia', 3]], ['Myanmar', ['Asia', 4]], ], }); }); it('works#32', () => { const document = [ { country: 'Poland', languages: [], }, ]; const collected = collect(document, ['$[2][country,languages]']); assert.deepEqual(collected, {}); }); it('works#33', () => { const document = [ { country: 'Poland', languages: [], }, { country: 'Czech Republic', languages: [], }, { country: 'Slovakia', languages: [], }, ]; const collected = collect(document, ['$[1][country,languages]']); assert.deepEqual(collected, { '$[1][country,languages]': [ ['Czech Republic', [1, 'country']], [[], [1, 'languages']], ], }); }); it('works#34', () => { const document = { data: { geo: { countries: { city: { id: 123, code: { id: 456, }, }, street: { name: { id: 789, }, }, }, }, }, }; const collected = collect(document, ['$.data[*][*][city,street]..id']); assert.deepEqual(collected, { '$.data[*][*][city,street]..id': [ [123, ['data', 'geo', 'countries', 'city', 'id']], [456, ['data', 'geo', 'countries', 'city', 'code', 'id']], [789, ['data', 'geo', 'countries', 'street', 'name', 'id']], ], }); }); describe.only('custom shorthands', () => { it('should support deep shorthands', () => { const document = { paths: { '/users': { get: { responses: { 200: { description: 'A list of users.', content: { 'application/json': { schema: { type: 'array', items: { $ref: '#/components/schemas/User', }, }, }, }, }, }, }, }, }, components: { schemas: { User: { type: 'object', properties: { id: { type: 'string', }, address: { type: 'object', properties: { street: { type: 'string', }, }, }, }, }, Extensions: { type: 'object', patternProperties: { '^x-': true, }, additionalProperties: false, 'x-ignore': { type: 'object', properties: { foo: { type: 'string', }, }, }, }, Name: { oneOf: [ { type: 'string', }, { type: 'null', }, ], }, }, }, }; const shorthands = { schema: function (scope, state, initialValue) { if (state.value < initialValue) return; const nextValue = (initialValue << 1) + 1; if (state.initialValue === initialValue) { if (isSchema(scope.sandbox.value)) { state.value = nextValue; return true; } state.value = -1; return false; } if (state.initialValue === nextValue) { const property = scope.path.at(-1); switch (true) { case ARRAY_ONLY_SCHEMA.includes(property): if (Array.isArray(scope.sandbox.value)) { state.value = initialValue; } else { state.value = -1; } return false; case OBJECT_ONLY_SCHEMA.includes(property): if (isPlainObject(scope.sandbox.value)) { state.value = initialValue; } else { state.value = -1; } return false; case property === 'items': if (Array.isArray(scope.sandbox.value)) { state.value = initialValue; } else if (isSchema(scope.sandbox.value)) { state.value = nextValue; return true; } else { state.value = -1; } return false; case TOP_LEVEL.includes(property): if (isSchema(scope.sandbox.value)) { state.value = nextValue; return true; } state.value = -1; return false; default: state.value = -1; return false; } } return state.initialValue === initialValue; }, }; const ARRAY_ONLY_SCHEMA = ['allOf', 'oneOf', 'anyOf', 'prefixItems']; const OBJECT_ONLY_SCHEMA = [ 'properties', 'patternProperties', '$defs', 'definitions', ]; const TOP_LEVEL = [ 'if', 'then', 'else', 'not', 'additionalProperties', 'unevaluatedProperties', 'items', 'contains', 'additionalItems', 'unevaluatedItems', ]; function isSchema(value) { return ( (isPlainObject(value) && !Object.hasOwn(value, '$ref')) || typeof value === 'boolean' ); } function isPlainObject(value) { return ( typeof value === 'object' && value !== null && !Array.isArray(value) ); } const collected = collect( document, [ '$.paths[*][get,put].responses[*].content[*].schema..@@schema(2)', '$.components.schemas[*]..@@schema(2)', ], { customShorthands: shorthands, }, ); assert.deepEqual(collected, { '$.paths[*][get,put].responses[*].content[*].schema..@@schema(2)': [ [ document.paths['/users'].get.responses[200].content[ 'application/json' ].schema, [ 'paths', '/users', 'get', 'responses', '200', 'content', 'application/json', 'schema', ], ], ], '$.components.schemas[*]..@@schema(2)': [ [document.components.schemas.User, ['components', 'schemas', 'User']], [ document.components.schemas.User.properties.id, ['components', 'schemas', 'User', 'properties', 'id'], ], [ document.components.schemas.User.properties.address, ['components', 'schemas', 'User', 'properties', 'address'], ], [ document.components.schemas.User.properties.address.properties .street, [ 'components', 'schemas', 'User', 'properties', 'address', 'properties', 'street', ], ], [ document.components.schemas.Extensions, ['components', 'schemas', 'Extensions'], ], [ document.components.schemas.Extensions.patternProperties['^x-'], ['components', 'schemas', 'Extensions', 'patternProperties', '^x-'], ], [ document.components.schemas.Extensions.additionalProperties, ['components', 'schemas', 'Extensions', 'additionalProperties'], ], [document.components.schemas.Name, ['components', 'schemas', 'Name']], [ document.components.schemas.Name.oneOf[0], ['components', 'schemas', 'Name', 'oneOf', 0], ], [ document.components.schemas.Name.oneOf[1], ['components', 'schemas', 'Name', 'oneOf', 1], ], ], }); }); }); it('works#35', () => { const document = { definitions: { propA: { properties: { a: {}, }, }, propB: { allOf: [ { properties: { b: {}, }, }, ], }, }, }; const collected = collect(document, [ '$.definitions.*.properties', '$.definitions.*.allOf.*.properties', ]); assert.deepEqual(collected, { '$.definitions.*.properties': [ [{ a: {} }, ['definitions', 'propA', 'properties']], ], '$.definitions.*.allOf.*.properties': [ [{ b: {} }, ['definitions', 'propB', 'allOf', 0, 'properties']], ], }); }); it('works#36', () => { const document = { foo: { bar: 'foo-bar', baz: 'foo-baz', }, baz: { foo: { bar: 'baz-foo-bar', }, }, bar: { foo: 'bar-foo', }, }; const collected = collect(document, [ '$.[?(@.bar)]', '$.foo.[?(@.bar)]', '$.foo.[bar]', '$.foo.[bar,baz]', ]); assert.deepEqual(collected, { '$.[?(@.bar)]': [ [{ bar: 'foo-bar', baz: 'foo-baz' }, ['foo']], [{ bar: 'baz-foo-bar' }, ['baz', 'foo']], ], '$.foo.[bar]': [['foo-bar', ['foo', 'bar']]], '$.foo.[bar,baz]': [ ['foo-bar', ['foo', 'bar']], ['foo-baz', ['foo', 'baz']], ], }); }); it('works#37', () => { const document = { paths: { '/pet': { get: { responses: { 200: { description: 'successful operation', }, 401: { description: 'Unauthorized', }, 400: { description: 'Invalid status value', }, 404: { description: 'Not Found', }, }, }, }, }, }; const collected = collect(document, ['$.paths.*.*.responses[401,404]']); assert.deepEqual(collected, { '$.paths.*.*.responses[401,404]': [ [ { description: 'Unauthorized' }, ['paths', '/pet', 'get', 'responses', '401'], ], [ { description: 'Not Found' }, ['paths', '/pet', 'get', 'responses', '404'], ], ], }); }); it('works#38', () => { const document = { foo: { example: { abc: { foo: true, }, example: true, foo: false, schema: true, oops: '2', baz: { foo: true, }, }, foo: 'abc', schema: true, }, }; const collected = collect(document, [ '$..[?(@.example && @.schema)]..[?(@.example && @.schema)]..foo', '$..[?(@.example && @.schema)]..[?(@.example && @.schema)][*].foo', ]); assert.deepEqual(collected, { '$..[?(@.example && @.schema)]..[?(@.example && @.schema)]..foo': [ [true, ['foo', 'example', 'abc', 'foo']], [false, ['foo', 'example', 'foo']], [true, ['foo', 'example', 'baz', 'foo']], ], '$..[?(@.example && @.schema)]..[?(@.example && @.schema)][*].foo': [ [true, ['foo', 'example', 'abc', 'foo']], [true, ['foo', 'example', 'baz', 'foo']], ], }); }); it('works#39', () => { const document = { baz: { a: { foo: { baz: { foo: true, }, }, }, x: { baz: { foo: true, }, }, }, }; const collected = collect(document, [ '$.baz[*].baz..foo', '$.baz[*]..baz..foo', '$..[?(@.foo)]..baz..foo', ]); assert.deepEqual(collected, { '$.baz[*].baz..foo': [[true, ['baz', 'x', 'baz', 'foo']]], '$.baz[*]..baz..foo': [ [true, ['baz', 'a', 'foo', 'baz', 'foo']], [true, ['baz', 'x', 'baz', 'foo']], ], '$..[?(@.foo)]..baz..foo': [[true, ['baz', 'a', 'foo', 'baz', 'foo']]], }); }); it('works#40', () => { const document = { baz: { baz: { foo: {}, a: { baz: { foo: true, }, }, baz: { foo: true, baz: { foo: true, }, }, }, }, }; const collected = collect(document, ['$.baz..baz.baz..foo']); assert.deepEqual(collected, { '$.baz..baz.baz..foo': [ [true, ['baz', 'baz', 'baz', 'foo']], [true, ['baz', 'baz', 'baz', 'baz', 'foo']], ], }); }); it('works#41', () => { const document = { baz: { baz: { foo: {}, a: { baz: { foo: true, test: { baz: { foo: { foo: { foo: 'x', }, }, }, }, }, }, baz: { foo: true, baz: { foo: true, a: { foo: { baz: { foo: { foo: { abc: { foo: 'matched', }, foo: 'another match', }, x: { foo: { foo: 'missed', }, }, }, }, }, }, }, }, }, }, }; const collected = collect(document, [ '$.baz..baz..foo.foo.foo', '$.baz..baz.baz..foo.foo..foo', ]); assert.deepEqual(collected, { '$.baz..baz.baz..foo.foo..foo': [ [ 'matched', [ 'baz', 'baz', 'baz', 'baz', 'a', 'foo', 'baz', 'foo', 'foo', 'abc', 'foo', ], ], [ 'another match', ['baz', 'baz', 'baz', 'baz', 'a', 'foo', 'baz', 'foo', 'foo', 'foo'], ], ], '$.baz..baz..foo.foo.foo': [ ['x', ['baz', 'baz', 'a', 'baz', 'test', 'baz', 'foo', 'foo', 'foo']], [ 'matched', [ 'baz', 'baz', 'baz', 'baz', 'a', 'foo', 'baz', 'foo', 'foo', 'abc', 'foo', ], ], [ 'another match', ['baz', 'baz', 'baz', 'baz', 'a', 'foo', 'baz', 'foo', 'foo', 'foo'], ], ], }); }); it('works#42', () => { const document = { effect_changes: [], effect_entries: [ { effect: 'Wenn ein Pokémon mit dieser Fähigkeit nur noch 1/3 seiner maximalen hp oder weniger hat, werden all seine fire Attacken verstärkt, so dass sie 1,5× so viel regular damage anrichten wie sonst.', language: { name: 'de', url: 'https://pokeapi.co/api/v2/language/6/', }, short_effect: 'Erhöht den Schaden von fire Attacken um 50% wenn nur noch 1/3 der maximalen hp oder weniger übrig sind.', }, { effect: 'When this Pokémon has 1/3 or less of its HP remaining, its fire-type moves inflict 1.5× as much regular damage.', language: { name: 'en', url: 'https://pokeapi.co/api/v2/language/9/', }, short_effect: 'Strengthens fire moves to inflict 1.5× damage at 1/3 max HP or less.', }, ], generation: { name: 'generation-iii', url: 'https://pokeapi.co/api/v2/generation/3/', }, id: 66, is_main_series: true, name: 'blaze', }; const collected = collect(document, [ `$.effect_entries[?(@.language.name=='en')].short_effect`, ]); assert.deepEqual(collected, { "$.effect_entries[?(@.language.name=='en')].short_effect": [ [ 'Strengthens fire moves to inflict 1.5× damage at 1/3 max HP or less.', ['effect_entries', 1, 'short_effect'], ], ], }); }); it('$..[bar,baz]..[bar,foo]', () => { const document = { bar: { baz: { a: { baz: [ { bar: true, }, { foo: { baz: { foo: true, bar: false, }, }, }, ], }, }, }, foo: { baz: { a: {}, baz: {}, }, }, }; const collected = collect(document, ['$..[bar,baz]..[bar,foo]']); assert.deepEqual(collected, { '$..[bar,baz]..[bar,foo]': [ [true, ['bar', 'baz', 'a', 'baz', 0, 'bar']], [ { baz: { foo: true, bar: false, }, }, ['bar', 'baz', 'a', 'baz', 1, 'foo'], ], [true, ['bar', 'baz', 'a', 'baz', 1, 'foo', 'baz', 'foo']], [false, ['bar', 'baz', 'a', 'baz', 1, 'foo', 'baz', 'bar']], ], }); }); it('$..info^^', () => { const document = { info: {}, }; const collected = collect(document, ['$..info^^']); assert.deepEqual(collected, {}); }); it('$..info^~', () => { const document = { info: {}, }; const collected = collect(document, ['$..info^~']); assert.deepEqual(collected, { '$..info^~': [[null, []]], }); }); it('given runtime errors, throws AggregateError', () => { const n = new Nimma(['$.a', '$.b', '$.c', '$.d', '$.e', '$.f']); const fn = n.query.bind( n, { a: {}, b: {}, c: {}, d: {}, e: {}, f: {} }, { '$.a'() { throw 'Oops'; }, '$.b'() { throw {}; }, '$.c'() { throw new Error('Ah!'); }, '$.d'() { throw new TypeError('{}.c is not a function'); }, '$.e': (() => () => { throw new Error('I have no name!'); })(), '$.f': function coolName() { throw new Error('That is a really cool name'); }, }, ); assert.throws( fn, new AggregateError( [ new RuntimeError('$.a threw: "Oops"'), new RuntimeError('$.b threw: unknown'), new RuntimeError('$.c threw: Error("Ah!")'), new RuntimeError('$.d threw: TypeError("{}.c is not a function")'), new RuntimeError('$.e threw: Error("I have no name!")'), ], 'Error running Nimma', ), ); }); });