diff --git a/README.md b/README.md index 20d4ee6..211cabb 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,15 @@ -# @seneca/config +# @seneca/traverse -> _Seneca Config_ is a plugin for [Seneca](http://senecajs.org) +> _Seneca Traverse_ is a plugin for [Seneca](http://senecajs.org) -Live configuration plugin for the Seneca framework. +Data Traverse plugin for the Seneca framework. -Unlike static configuration, this plugin lets you store keyed -configuration in your deployed persistent storage so that you can -change it on the live system. This is useful for things like currency -exchange rates, feature flags, A/B testing etc. - - -[![npm version](https://img.shields.io/npm/v/@seneca/config.svg)](https://npmjs.com/package/@seneca/config) -[![build](https://github.com/senecajs/SenecaConfig/actions/workflows/build.yml/badge.svg)](https://github.com/senecajs/SenecaConfig/actions/workflows/build.yml) -[![Coverage Status](https://coveralls.io/repos/github/senecajs/SenecaConfig/badge.svg?branch=main)](https://coveralls.io/github/senecajs/SenecaConfig?branch=main) -[![Known Vulnerabilities](https://snyk.io/test/github/senecajs/SenecaConfig/badge.svg)](https://snyk.io/test/github/senecajs/SenecaConfig) +[![npm version](https://img.shields.io/npm/v/@seneca/traverse.svg)](https://npmjs.com/package/@seneca/traverse) +[![build](https://github.com/senecajs/SenecaTraverse/actions/workflows/build.yml/badge.svg)](https://github.com/senecajs/SenecaTraverse/actions/workflows/build.yml) +[![Coverage Status](https://coveralls.io/repos/github/senecajs/SenecaTraverse/badge.svg?branch=main)](https://coveralls.io/github/senecajs/SenecaTraverse?branch=main) +[![Known Vulnerabilities](https://snyk.io/test/github/senecajs/SenecaTraverse/badge.svg)](https://snyk.io/test/github/senecajs/SenecaTraverse) [![DeepScan grade](https://deepscan.io/api/teams/5016/projects/26547/branches/846930/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=5016&pid=26547&bid=846930) -[![Maintainability](https://api.codeclimate.com/v1/badges/3e5e5c11a17dbfbdd894/maintainability)](https://codeclimate.com/github/senecajs/SenecaConfig/maintainability) +[![Maintainability](https://api.codeclimate.com/v1/badges/3e5e5c11a17dbfbdd894/maintainability)](https://codeclimate.com/github/senecajs/SenecaTraverse/maintainability) | ![Voxgig](https://www.voxgig.com/res/img/vgt01r.png) | This open source module is sponsored and supported by [Voxgig](https://www.voxgig.com). | | ---------------------------------------------------- | --------------------------------------------------------------------------------------- | @@ -23,125 +17,54 @@ exchange rates, feature flags, A/B testing etc. ## Install ```sh -$ npm install @seneca/Config +$ npm install @seneca/traverse ``` ## Quick Example ```js -seneca.use('Config', {}) - -const initRes = await seneca.post('sys:config,init:val,key:a,val:1') -// === { ok: true, key: 'a', val: 1, entry: { key: 'a', val: 1 } } - -const getRes = await seneca.post('sys:config,get:val,key:a') -// === { ok: true, key: 'a', val: 1, entry: { key: 'a', val: 1 } } - -const setRes = await seneca.post('sys:config,set:val,key:a,val:2') -// === { ok: true, key: 'a', val: 1, entry: { key: 'a', val: 2 } } +seneca.use('Traverse', {}) +const depsRes = await seneca.post('sys:traverse,find:deps') +// === { ok: true, deps: [['foo/bar0,foo/bar1'],...] } ``` ## More Examples -Review the [unit tests](test/Config.test.ts) for more examples. - - +Review the [unit tests](test/Traverse.test.ts) for more examples. - ## Options -* `debug` : boolean -* `numparts` : number -* `canon` : object -* `init$` : boolean - +- `debug` : boolean +- `rootEntity` : string +- `relations` : { parental: array } - ## Action Patterns -* [sys:config,get:val](#-sysconfiggetval-) -* [sys:config,init:val](#-sysconfiginitval-) -* [sys:config,list:val](#-sysconfiglistval-) -* [sys:config,map:val](#-sysconfigmapval-) -* [sys:config,set:val](#-sysconfigsetval-) - +- [sys:traverse,find:deps](#-systraversefinddeps-) - ## Action Descriptions -### « `sys:config,get:val` » - -Get a config value by key. - - -#### Parameters - - -* __key__ : _string_ - - ----------- -### « `sys:config,init:val` » - -Initialise a config value by key (must not exist). - - -#### Parameters - - -* __key__ : _string_ -* __existing__ : _boolean_ (optional, default: `false`) - - ----------- -### « `sys:config,list:val` » - -List config values by query. - - -#### Parameters - - -* __q__ : _object_ (optional, default: `{}`) - - ----------- -### « `sys:config,map:val` » - -Get a map of config values by key prefix (dot separated). - - -#### Parameters - - -* __prefix__ : _string_ - - ----------- -### « `sys:config,set:val` » - -Set a config value by key (must exist). +### « `sys:traverse,find:deps` » +Returns a sorted list of entity pairs starting from a given entity. #### Parameters +- **rootEntity** : _string_ (optional, default: 'sys/user') +- **relations** : _object_ (optional, default : { parental: [] }) -* __key__ : _string_ - - ----------- - +--- diff --git a/dist-test/Traverse.test.js b/dist-test/Traverse.test.js index f7720c3..aa8733b 100644 --- a/dist-test/Traverse.test.js +++ b/dist-test/Traverse.test.js @@ -22,5 +22,1750 @@ const __2 = __importDefault(require("..")); await seneca.ready(); (0, code_1.expect)(seneca.find_plugin('Traverse')).exist(); }); + (0, node_test_1.test)('find-deps', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }); + // console.log('RES', res) + (0, code_1.expect)(res.deps).equal([ + // Level 0 + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar0', 'foo/zed0'], + // Level 1 + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar2', 'foo/bar3'], + ['foo/bar2', 'foo/bar9'], + ['foo/zed0', 'foo/zed1'], + // Level 2 + // Sort each level alphabetically. + // Thus, foo/bar3 should be listed first, + // although its parent is foo/bar2 + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/zed1', 'foo/zed2'], + // Level 3 + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ]); + }); + (0, node_test_1.test)('find-deps-empty-list', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }); + // console.log('RES', res) + (0, code_1.expect)(res.deps).equal([]); + }); + (0, node_test_1.test)('find-deps-no-children', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar1', 'foo/bar2'], + ['foo/bar2', 'foo/bar3'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }); + // console.log('RES', res) + (0, code_1.expect)(res.deps).equal([]); + }); + (0, node_test_1.test)('find-deps-cycle', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + // Cycle back to root + ['foo/bar2', 'foo/bar0'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }); + // console.log('RES', res) + // Should only traverse once, ignoring the cycle + (0, code_1.expect)(res.deps).equal([ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ]); + }); + (0, node_test_1.test)('find-deps-cycle-middle', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ['foo/bar2', 'foo/bar3'], + // Cycle bar1 -> bar2 -> bar3 -> bar1 + ['foo/bar3', 'foo/bar1'], + ['foo/bar2', 'foo/bar4'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }); + // Each node visited only once despite cycle + (0, code_1.expect)(res.deps).equal([ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ['foo/bar2', 'foo/bar3'], + ['foo/bar2', 'foo/bar4'], + ]); + }); + (0, node_test_1.test)('find-deps-linear', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ['foo/bar2', 'foo/bar3'], + ['foo/bar3', 'foo/bar4'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }); + // console.log('RES', res) + (0, code_1.expect)(res.deps).equal([ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ['foo/bar2', 'foo/bar3'], + ['foo/bar3', 'foo/bar4'], + ]); + }); + (0, node_test_1.test)('find-deps-duplicate', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + // Duplicate + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + // Duplicate + ['foo/bar1', 'foo/bar2'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }); + (0, code_1.expect)(res.deps).equal([ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ]); + }); + (0, node_test_1.test)('find-deps-convergent', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar3'], + // bar3 reachable from two paths + ['foo/bar2', 'foo/bar3'], + ['foo/bar3', 'foo/bar4'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }); + // console.log('RES', res) + // bar3 should only appear once (first path wins) + (0, code_1.expect)(res.deps).equal([ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar3'], + ['foo/bar3', 'foo/bar4'], + ]); + }); + (0, node_test_1.test)('find-deps-two-convergent', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar0', 'foo/bar3'], + ['foo/bar1', 'foo/bar4'], + // bar4 from two parents + ['foo/bar2', 'foo/bar4'], + ['foo/bar3', 'foo/bar5'], + ['foo/bar4', 'foo/bar6'], + // bar6 from two parents at different levels + ['foo/bar5', 'foo/bar6'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }); + (0, code_1.expect)(res.deps).equal([ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar0', 'foo/bar3'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar3', 'foo/bar5'], + ['foo/bar4', 'foo/bar6'], + ]); + }); + (0, node_test_1.test)('find-deps-self-ref', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + // Self loop + ['foo/bar1', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }); + // console.log('RES', res) + (0, code_1.expect)(res.deps).equal([ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ]); + }); + (0, node_test_1.test)('find-deps-all-default', async () => { + const seneca = makeSeneca().use(__2.default); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps'); + // console.log('RES', res) + (0, code_1.expect)(res.deps).equal([]); + }); + (0, node_test_1.test)('find-deps-empty-list', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }); + // console.log('RES', res) + (0, code_1.expect)(res.deps).equal([]); + }); + (0, node_test_1.test)('find-deps-l1', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar1', + }); + (0, code_1.expect)(res.deps).equal([ + // Level 0 + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + // Level 1 + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + // Level 2 + ['foo/bar7', 'foo/bar11'], + ]); + }); + (0, node_test_1.test)('find-deps-l1-convergent', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ['foo/bar4', 'foo/bar12'], + // bar12 converges from bar4 and bar5 + ['foo/bar5', 'foo/bar12'], + ['foo/bar12', 'foo/bar13'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar1', + }); + (0, code_1.expect)(res.deps).equal([ + // Level 0 + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + // Level 1 + ['foo/bar4', 'foo/bar7'], + ['foo/bar4', 'foo/bar12'], + ['foo/bar5', 'foo/bar8'], + // Level 2 + ['foo/bar7', 'foo/bar11'], + ['foo/bar12', 'foo/bar13'], + ]); + }); + (0, node_test_1.test)('find-deps-l1-cycle', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + // Cycle: bar1 -> bar4 -> bar7 -> bar1 + ['foo/bar7', 'foo/bar1'], + ['foo/bar8', 'foo/bar12'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar1', + }); + (0, code_1.expect)(res.deps).equal([ + // Level 0 + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + // Level 1 + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + // Level 2 + ['foo/bar7', 'foo/bar11'], + ['foo/bar8', 'foo/bar12'], + ]); + }); + (0, node_test_1.test)('find-deps-l2', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar3', + }); + // console.log('RES', res) + (0, code_1.expect)(res.deps).equal([ + // Level 0 + ['foo/bar3', 'foo/bar6'], + // Level 1 + ['foo/bar6', 'foo/bar10'], + ]); + }); + (0, node_test_1.test)('find-deps-l2-convergent', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ['foo/bar3', 'foo/bar12'], + ['foo/bar6', 'foo/bar13'], + ['foo/bar10', 'foo/bar14'], + // bar14 converges from bar10 and bar12 + ['foo/bar12', 'foo/bar14'], + ['foo/bar14', 'foo/bar15'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar3', + }); + (0, code_1.expect)(res.deps).equal([ + // Level 0 + ['foo/bar3', 'foo/bar6'], + ['foo/bar3', 'foo/bar12'], + // Level 1 + ['foo/bar6', 'foo/bar10'], + ['foo/bar6', 'foo/bar13'], + ['foo/bar12', 'foo/bar14'], + // Level 2 + ['foo/bar14', 'foo/bar15'], + ]); + }); + (0, node_test_1.test)('find-deps-l2-cycle', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ['foo/bar10', 'foo/bar12'], + // Cycle: bar3 -> bar6 -> bar10 -> bar12 -> bar3 + ['foo/bar12', 'foo/bar3'], + ['foo/bar10', 'foo/bar13'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar3', + }); + (0, code_1.expect)(res.deps).equal([ + // Level 0 + ['foo/bar3', 'foo/bar6'], + // Level 1 + ['foo/bar6', 'foo/bar10'], + // Level 2 + ['foo/bar10', 'foo/bar12'], + ['foo/bar10', 'foo/bar13'], + ]); + }); + (0, node_test_1.test)('find-deps-l2-multi-level-convergent', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ['foo/bar3', 'foo/bar12'], + ['foo/bar3', 'foo/bar13'], + ['foo/bar12', 'foo/bar14'], + // Second path to bar14 + ['foo/bar13', 'foo/bar14'], + // Third path to bar14 + ['foo/bar6', 'foo/bar14'], + ['foo/bar14', 'foo/bar15'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar3', + }); + (0, code_1.expect)(res.deps).equal([ + // Level 0 + ['foo/bar3', 'foo/bar6'], + ['foo/bar3', 'foo/bar12'], + ['foo/bar3', 'foo/bar13'], + // Level 1 + ['foo/bar6', 'foo/bar10'], + ['foo/bar12', 'foo/bar14'], + // Level 2 + ['foo/bar14', 'foo/bar15'], + ]); + }); + (0, node_test_1.test)('find-deps-single-cycle', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['A', 'B'], + ['A', 'C'], + ['C', 'E'], + ['C', 'D'], + ['E', 'G'], + ['E', 'F'], + ['F', 'H'], + ['C', 'A'], + ['N', 'M'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'C', + }); + (0, code_1.expect)(res.deps).equal([ + // Level 0 + ['C', 'A'], + ['C', 'D'], + ['C', 'E'], + // Level 1 + ['A', 'B'], + ['E', 'F'], + ['E', 'G'], + // Level 2 + ['F', 'H'], + ]); + }); + (0, node_test_1.test)('find-deps-single-missing-node', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['A', 'B'], + ['B', 'C'], + ['D', 'E'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'X', + }); + // X has no children + (0, code_1.expect)(res.deps).equal([]); + }); + (0, node_test_1.test)('find-deps-deep-linear-chain', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['node0', 'node1'], + ['node1', 'node2'], + ['node2', 'node3'], + ['node3', 'node4'], + ['node4', 'node5'], + ['node5', 'node6'], + ['node6', 'node7'], + ['node7', 'node8'], + ['node8', 'node9'], + ['node9', 'node10'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'node0', + }); + (0, code_1.expect)(res.deps).equal([ + ['node0', 'node1'], + ['node1', 'node2'], + ['node2', 'node3'], + ['node3', 'node4'], + ['node4', 'node5'], + ['node5', 'node6'], + ['node6', 'node7'], + ['node7', 'node8'], + ['node8', 'node9'], + ['node9', 'node10'], + ]); + }); + (0, node_test_1.test)('find-deps-binary-tree', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['A', 'B'], + ['A', 'C'], + ['B', 'D'], + ['B', 'E'], + ['C', 'F'], + ['C', 'G'], + ['D', 'H'], + ['D', 'I'], + ['E', 'J'], + ['E', 'K'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'A', + }); + (0, code_1.expect)(res.deps).equal([ + // Level 0 + ['A', 'B'], + ['A', 'C'], + // Level 1 + ['B', 'D'], + ['B', 'E'], + ['C', 'F'], + ['C', 'G'], + // Level 2 + ['D', 'H'], + ['D', 'I'], + ['E', 'J'], + ['E', 'K'], + ]); + }); + (0, node_test_1.test)('find-deps-diamond-pattern', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['A', 'B'], + ['A', 'C'], + ['B', 'D'], + ['C', 'D'], + ['D', 'E'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'A', + }); + // D is reached first from B (alphabetically first) + (0, code_1.expect)(res.deps).equal([ + // Level 0 + ['A', 'B'], + ['A', 'C'], + // Level 1 - D reached via B + ['B', 'D'], + // Level 2 + ['D', 'E'], + ]); + }); + (0, node_test_1.test)('find-deps-mixed-alph-sort', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['root', 'node10'], + ['root', 'node2'], + ['root', 'node1'], + ['root', 'node20'], + ['node1', 'child2'], + ['node2', 'child1'], + ['node10', 'child10'], + ['node20', 'child20'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'root', + }); + // Natural sorting: node1, node2, node10, node20 + (0, code_1.expect)(res.deps).equal([ + // Level 0 + ['root', 'node1'], + ['root', 'node2'], + ['root', 'node10'], + ['root', 'node20'], + // Level 1 + ['node1', 'child2'], + ['node2', 'child1'], + ['node10', 'child10'], + ['node20', 'child20'], + ]); + }); + (0, node_test_1.test)('find-deps-all-nodes-converge-to-one', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['root', 'A'], + ['root', 'B'], + ['root', 'C'], + ['A', 'target'], + ['B', 'target'], + ['C', 'target'], + ['target', 'end'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'root', + }); + // target reached first via A (alphabetically first) + (0, code_1.expect)(res.deps).equal([ + // Level 0 + ['root', 'A'], + ['root', 'B'], + ['root', 'C'], + // Level 1 - target via A + ['A', 'target'], + // Level 2 + ['target', 'end'], + ]); + }); + (0, node_test_1.test)('find-deps-deep-10-levels-complex', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + // ========== LEVEL 0 → LEVEL 1 ========== + ['foo', 'bar'], + ['foo', 'zed'], + ['foo', 'qux'], + // ========== LEVEL 1 → LEVEL 2 ========== + ['bar', 'bar1'], + ['bar', 'bar2'], + ['zed', 'zed1'], + ['zed', 'zed2'], + ['qux', 'qux1'], + // ========== LEVEL 2 → LEVEL 3 ========== + ['bar1', 'bar3'], + ['bar1', 'bar4'], + ['bar2', 'bar5'], + ['zed1', 'zed3'], + ['zed1', 'zed4'], + ['zed2', 'zed5'], + ['qux1', 'qux2'], + // ========== LEVEL 3 → LEVEL 4 ========== + ['bar3', 'bar6'], + ['bar3', 'bar7'], + ['bar4', 'bar8'], + ['bar5', 'bar9'], + ['zed3', 'zed6'], + ['zed4', 'zed7'], + ['zed5', 'zed8'], + ['qux2', 'qux3'], + // ========== LEVEL 4 → LEVEL 5 ========== + ['bar6', 'bar10'], + ['bar7', 'bar11'], + ['bar8', 'bar12'], + ['bar9', 'bar13'], + ['zed6', 'zed9'], + ['zed7', 'zed10'], + ['zed8', 'zed11'], + ['qux3', 'qux4'], + // ========== LEVEL 5 → LEVEL 6 (switch to composed names) ========== + ['bar10', 'foo/bar1'], + ['bar10', 'foo/bar2'], + ['bar11', 'foo/bar3'], + ['bar12', 'foo/bar4'], + ['bar13', 'foo/bar5'], + ['zed9', 'foo/zed1'], + ['zed10', 'foo/zed2'], + ['zed11', 'foo/zed3'], + // Cycle: qux4 → foo (already visited at level 0) + ['qux4', 'foo'], + ['qux4', 'foo/qux1'], + // ========== LEVEL 6 → LEVEL 7 ========== + ['foo/bar1', 'foo/bar6'], + ['foo/bar1', 'foo/bar7'], + ['foo/bar2', 'foo/bar8'], + ['foo/bar3', 'foo/bar9'], + ['foo/bar4', 'foo/bar10'], + ['foo/bar5', 'foo/bar11'], + ['foo/zed1', 'foo/zed4'], + ['foo/zed2', 'foo/zed5'], + ['foo/zed3', 'foo/zed6'], + ['foo/qux1', 'foo/qux2'], + // ========== LEVEL 7 → LEVEL 8 ========== + ['foo/bar6', 'bar/foo1'], + ['foo/bar7', 'bar/foo2'], + ['foo/bar8', 'bar/foo3'], + ['foo/bar9', 'bar/foo4'], + ['foo/bar10', 'bar/foo5'], + ['foo/bar11', 'bar/foo6'], + ['foo/zed4', 'zed/foo1'], + ['foo/zed5', 'zed/foo2'], + // Convergent: foo/zed6 and foo/qux2 both point to zed/foo3 + ['foo/zed6', 'zed/foo3'], + ['foo/qux2', 'zed/foo3'], + // ========== LEVEL 8 → LEVEL 9 ========== + ['bar/foo1', 'bar/foo7'], + ['bar/foo2', 'bar/foo8'], + ['bar/foo3', 'bar/foo9'], + ['bar/foo4', 'bar/foo10'], + ['bar/foo5', 'bar/foo11'], + ['bar/foo6', 'bar/foo12'], + ['zed/foo1', 'zed/foo4'], + ['zed/foo2', 'zed/foo5'], + ['zed/foo3', 'zed/foo6'], + // ========== LEVEL 9 → LEVEL 10 ========== + ['bar/foo7', 'qux/bar1'], + ['bar/foo8', 'qux/bar2'], + ['bar/foo9', 'qux/bar3'], + ['bar/foo10', 'qux/bar4'], + ['bar/foo11', 'qux/bar5'], + ['bar/foo12', 'qux/bar6'], + ['zed/foo4', 'qux/zed1'], + ['zed/foo5', 'qux/zed2'], + ['zed/foo6', 'qux/zed3'], + // ========== LEVEL 10 → LEVEL 11 ========== + ['qux/bar1', 'qux/bar7'], + ['qux/bar2', 'qux/bar8'], + ['qux/bar3', 'qux/bar9'], + ['qux/bar4', 'qux/bar10'], + ['qux/bar5', 'qux/bar11'], + ['qux/bar6', 'qux/bar12'], + ['qux/zed1', 'qux/zed4'], + ['qux/zed2', 'qux/zed5'], + ['qux/zed3', 'qux/zed6'], + ], + }, + }); + await seneca.ready(); + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo', + }); + (0, code_1.expect)(res.deps).equal([ + // ========== LEVEL 0 ========== + ['foo', 'bar'], + ['foo', 'qux'], + ['foo', 'zed'], + // ========== LEVEL 1 ========== + ['bar', 'bar1'], + ['bar', 'bar2'], + ['qux', 'qux1'], + ['zed', 'zed1'], + ['zed', 'zed2'], + // ========== LEVEL 2 ========== + ['bar1', 'bar3'], + ['bar1', 'bar4'], + ['bar2', 'bar5'], + ['qux1', 'qux2'], + ['zed1', 'zed3'], + ['zed1', 'zed4'], + ['zed2', 'zed5'], + // ========== LEVEL 3 ========== + ['bar3', 'bar6'], + ['bar3', 'bar7'], + ['bar4', 'bar8'], + ['bar5', 'bar9'], + ['qux2', 'qux3'], + ['zed3', 'zed6'], + ['zed4', 'zed7'], + ['zed5', 'zed8'], + // ========== LEVEL 4 ========== + ['bar6', 'bar10'], + ['bar7', 'bar11'], + ['bar8', 'bar12'], + ['bar9', 'bar13'], + ['qux3', 'qux4'], + ['zed6', 'zed9'], + ['zed7', 'zed10'], + ['zed8', 'zed11'], + // ========== LEVEL 5 ========== + ['bar10', 'foo/bar1'], + ['bar10', 'foo/bar2'], + ['bar11', 'foo/bar3'], + ['bar12', 'foo/bar4'], + ['bar13', 'foo/bar5'], + // Note: qux4 → foo creates cycle (foo visited at level 0) + ['qux4', 'foo/qux1'], + ['zed9', 'foo/zed1'], + ['zed10', 'foo/zed2'], + ['zed11', 'foo/zed3'], + // ========== LEVEL 6 ========== + ['foo/bar1', 'foo/bar6'], + ['foo/bar1', 'foo/bar7'], + ['foo/bar2', 'foo/bar8'], + ['foo/bar3', 'foo/bar9'], + ['foo/bar4', 'foo/bar10'], + ['foo/bar5', 'foo/bar11'], + ['foo/qux1', 'foo/qux2'], + ['foo/zed1', 'foo/zed4'], + ['foo/zed2', 'foo/zed5'], + ['foo/zed3', 'foo/zed6'], + // ========== LEVEL 7 ========== + ['foo/bar6', 'bar/foo1'], + ['foo/bar7', 'bar/foo2'], + ['foo/bar8', 'bar/foo3'], + ['foo/bar9', 'bar/foo4'], + ['foo/bar10', 'bar/foo5'], + ['foo/bar11', 'bar/foo6'], + ['foo/qux2', 'zed/foo3'], + ['foo/zed4', 'zed/foo1'], + ['foo/zed5', 'zed/foo2'], + // Note: foo/zed6 → zed/foo3 is skipped because zed/foo3 was already visited via foo/qux2 + // ========== LEVEL 8 ========== + ['bar/foo1', 'bar/foo7'], + ['bar/foo2', 'bar/foo8'], + ['bar/foo3', 'bar/foo9'], + ['bar/foo4', 'bar/foo10'], + ['bar/foo5', 'bar/foo11'], + ['bar/foo6', 'bar/foo12'], + ['zed/foo1', 'zed/foo4'], + ['zed/foo2', 'zed/foo5'], + ['zed/foo3', 'zed/foo6'], + // ========== LEVEL 9 ========== + ['bar/foo7', 'qux/bar1'], + ['bar/foo8', 'qux/bar2'], + ['bar/foo9', 'qux/bar3'], + ['bar/foo10', 'qux/bar4'], + ['bar/foo11', 'qux/bar5'], + ['bar/foo12', 'qux/bar6'], + ['zed/foo4', 'qux/zed1'], + ['zed/foo5', 'qux/zed2'], + ['zed/foo6', 'qux/zed3'], + // ========== LEVEL 10 ========== + ['qux/bar1', 'qux/bar7'], + ['qux/bar2', 'qux/bar8'], + ['qux/bar3', 'qux/bar9'], + ['qux/bar4', 'qux/bar10'], + ['qux/bar5', 'qux/bar11'], + ['qux/bar6', 'qux/bar12'], + ['qux/zed1', 'qux/zed4'], + ['qux/zed2', 'qux/zed5'], + ['qux/zed3', 'qux/zed6'], + ]); + }); + (0, node_test_1.test)('find-children', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar0', 'foo/zed0'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar2', 'foo/bar3'], + ['foo/bar2', 'foo/bar9'], + ['foo/zed0', 'foo/zed1'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/zed1', 'foo/zed2'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ], + }, + }); + await seneca.ready(); + const rootEntityId = '123'; + // Level 1: Direct children of bar0 + const bar1Ent = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }); + const bar2Ent = await seneca.entity('foo/bar2').save$({ + bar0_id: rootEntityId, + }); + const zed0Ent = await seneca.entity('foo/zed0').save$({ + bar0_id: rootEntityId, + }); + // Level 2: Children of bar1 + const bar4Ent = await seneca.entity('foo/bar4').save$({ + bar1_id: bar1Ent.id, + }); + const bar5Ent = await seneca.entity('foo/bar5').save$({ + bar1_id: bar1Ent.id, + }); + // Level 2: Children of bar2 + const bar3Ent = await seneca.entity('foo/bar3').save$({ + bar2_id: bar2Ent.id, + }); + const bar9Ent = await seneca.entity('foo/bar9').save$({ + bar2_id: bar2Ent.id, + }); + // Level 2: Children of zed0 + const zed1Ent = await seneca.entity('foo/zed1').save$({ + zed0_id: zed0Ent.id, + }); + // Level 3: Children of bar3 + const bar6Ent = await seneca.entity('foo/bar6').save$({ + bar3_id: bar3Ent.id, + }); + // Level 3: Children of bar4 + const bar7Ent = await seneca.entity('foo/bar7').save$({ + bar4_id: bar4Ent.id, + }); + // Level 3: Children of bar5 + const bar8Ent = await seneca.entity('foo/bar8').save$({ + bar5_id: bar5Ent.id, + }); + // Level 3: Children of zed1 + const zed2Ent = await seneca.entity('foo/zed2').save$({ + zed1_id: zed1Ent.id, + }); + // Level 4: Children of bar6 + const bar10Ent = await seneca.entity('foo/bar10').save$({ + bar6_id: bar6Ent.id, + }); + // Level 4: Children of bar7 + const bar11Ent = await seneca.entity('foo/bar11').save$({ + bar7_id: bar7Ent.id, + }); + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }); + (0, code_1.expect)(res.children).equal([ + // Level 1 + { + parent_id: rootEntityId, + child_id: bar1Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: rootEntityId, + child_id: bar2Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar2', + }, + { + parent_id: rootEntityId, + child_id: zed0Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/zed0', + }, + // Level 2 + { + parent_id: bar1Ent.id, + child_id: bar4Ent.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar4', + }, + { + parent_id: bar1Ent.id, + child_id: bar5Ent.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar5', + }, + { + parent_id: bar2Ent.id, + child_id: bar3Ent.id, + parent_canon: 'foo/bar2', + child_canon: 'foo/bar3', + }, + { + parent_id: bar2Ent.id, + child_id: bar9Ent.id, + parent_canon: 'foo/bar2', + child_canon: 'foo/bar9', + }, + { + parent_id: zed0Ent.id, + child_id: zed1Ent.id, + parent_canon: 'foo/zed0', + child_canon: 'foo/zed1', + }, + // Level 3 + { + parent_id: bar3Ent.id, + child_id: bar6Ent.id, + parent_canon: 'foo/bar3', + child_canon: 'foo/bar6', + }, + { + parent_id: bar4Ent.id, + child_id: bar7Ent.id, + parent_canon: 'foo/bar4', + child_canon: 'foo/bar7', + }, + { + parent_id: bar5Ent.id, + child_id: bar8Ent.id, + parent_canon: 'foo/bar5', + child_canon: 'foo/bar8', + }, + { + parent_id: zed1Ent.id, + child_id: zed2Ent.id, + parent_canon: 'foo/zed1', + child_canon: 'foo/zed2', + }, + // Level 4 + { + parent_id: bar6Ent.id, + child_id: bar10Ent.id, + parent_canon: 'foo/bar6', + child_canon: 'foo/bar10', + }, + { + parent_id: bar7Ent.id, + child_id: bar11Ent.id, + parent_canon: 'foo/bar7', + child_canon: 'foo/bar11', + }, + ]); + }); + (0, node_test_1.test)('find-children-empty-relations', async () => { + const seneca = makeSeneca().use(__2.default); + await seneca.ready(); + const rootEntityId = '123'; + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }); + (0, code_1.expect)(res.children).equal([]); + }); + (0, node_test_1.test)('find-children-no-matching-entities', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ], + }, + }); + await seneca.ready(); + const rootEntityId = '123'; + // Missing entities on data storage + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }); + (0, code_1.expect)(res.children).equal([]); + }); + (0, node_test_1.test)('find-children-partial-tree', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar3'], + ['foo/bar1', 'foo/bar4'], + ], + }, + }); + await seneca.ready(); + const rootEntityId = '123'; + // Only create bar1 and bar3, not bar2 or bar4 + const bar1Ent = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }); + const bar3Ent = await seneca.entity('foo/bar3').save$({ + bar1_id: bar1Ent.id, + }); + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }); + // Should only return entities that exist in the data storage + (0, code_1.expect)(res.children).equal([ + { + parent_id: rootEntityId, + child_id: bar1Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: bar1Ent.id, + child_id: bar3Ent.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar3', + }, + ]); + }); + (0, node_test_1.test)('find-children-default-root-entity', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['sys/user', 'user/settings'], + ['sys/user', 'user/project'], + ['user/project', 'project/release'], + ], + }, + }); + await seneca.ready(); + const rootEntityId = 'user-456'; + const settingsEnt = await seneca.entity('user/settings').save$({ + user_id: rootEntityId, + }); + const projectEnt = await seneca.entity('user/project').save$({ + user_id: rootEntityId, + }); + const releaseEnt = await seneca.entity('project/release').save$({ + project_id: projectEnt.id, + }); + const res = await seneca.post('sys:traverse,find:children', { + // rootEntity omitted - should default to 'sys/user' + rootEntityId: rootEntityId, + }); + (0, code_1.expect)(res.children).equal([ + { + parent_id: rootEntityId, + child_id: projectEnt.id, + parent_canon: 'sys/user', + child_canon: 'user/project', + }, + { + parent_id: rootEntityId, + child_id: settingsEnt.id, + parent_canon: 'sys/user', + child_canon: 'user/settings', + }, + { + parent_id: projectEnt.id, + child_id: releaseEnt.id, + parent_canon: 'user/project', + child_canon: 'project/release', + }, + ]); + }); + (0, node_test_1.test)('find-children-avoid-wrong-children', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar3'], + ], + }, + }); + await seneca.ready(); + const rootEntityId = '123'; + const bar1Ent = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }); + const bar2Ent = await seneca.entity('foo/bar2').save$({ + bar0_id: rootEntityId, + }); + // Create bar3 entities but with another parent_id + await seneca.entity('foo/bar3').save$({ + bar1_id: '456', + }); + await seneca.entity('foo/bar3').save$({ + bar1_id: '789', + }); + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }); + // Should not include other parent children + (0, code_1.expect)(res.children).equal([ + { + parent_id: rootEntityId, + child_id: bar1Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: rootEntityId, + child_id: bar2Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar2', + }, + ]); + }); + (0, node_test_1.test)('find-children-single-entity-tree', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [['foo/bar0', 'foo/bar1']], + }, + }); + await seneca.ready(); + const rootEntityId = '123'; + const bar1Ent = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }); + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }); + (0, code_1.expect)(res.children).equal([ + { + parent_id: rootEntityId, + child_id: bar1Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + ]); + }); + (0, node_test_1.test)('find-children-deep-linear-chain', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ['foo/bar2', 'foo/bar3'], + ['foo/bar3', 'foo/bar4'], + ['foo/bar4', 'foo/bar5'], + ], + }, + }); + await seneca.ready(); + const rootEntityId = '123'; + const bar1Ent = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }); + const bar2Ent = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent.id, + }); + const bar3Ent = await seneca.entity('foo/bar3').save$({ + bar2_id: bar2Ent.id, + }); + const bar4Ent = await seneca.entity('foo/bar4').save$({ + bar3_id: bar3Ent.id, + }); + const bar5Ent = await seneca.entity('foo/bar5').save$({ + bar4_id: bar4Ent.id, + }); + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }); + (0, code_1.expect)(res.children).equal([ + { + parent_id: rootEntityId, + child_id: bar1Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: bar1Ent.id, + child_id: bar2Ent.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + { + parent_id: bar2Ent.id, + child_id: bar3Ent.id, + parent_canon: 'foo/bar2', + child_canon: 'foo/bar3', + }, + { + parent_id: bar3Ent.id, + child_id: bar4Ent.id, + parent_canon: 'foo/bar3', + child_canon: 'foo/bar4', + }, + { + parent_id: bar4Ent.id, + child_id: bar5Ent.id, + parent_canon: 'foo/bar4', + child_canon: 'foo/bar5', + }, + ]); + }); + (0, node_test_1.test)('find-children-custom-key', async () => { + const seneca = makeSeneca().use(__2.default, { + customRef: { + 'foo/bar2': 'custom0_id', + 'foo/bar3': 'custom1_test', + }, + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar3'], + ], + }, + }); + await seneca.ready(); + const rootEntityId = '123'; + const bar1Ent = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }); + const bar2Ent = await seneca.entity('foo/bar2').save$({ + custom0_id: rootEntityId, + }); + const bar3Ent = await seneca.entity('foo/bar3').save$({ + custom1_test: bar1Ent.id, + }); + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }); + (0, code_1.expect)(res.children).equal([ + { + parent_id: rootEntityId, + child_id: bar1Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: rootEntityId, + child_id: bar2Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar2', + }, + { + parent_id: bar1Ent.id, + child_id: bar3Ent.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar3', + }, + ]); + }); + (0, node_test_1.test)('find-children-multi-inst', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ], + }, + }); + await seneca.ready(); + const rootEntityId = '123'; + const bar1Ent1 = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }); + const bar1Ent2 = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }); + const bar1Ent3 = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }); + const bar2Ent1 = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent1.id, + }); + const bar2Ent2 = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent2.id, + }); + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }); + (0, code_1.expect)(res.children).equal([ + { + parent_id: rootEntityId, + child_id: bar1Ent1.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: rootEntityId, + child_id: bar1Ent2.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: rootEntityId, + child_id: bar1Ent3.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: bar1Ent1.id, + child_id: bar2Ent1.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + { + parent_id: bar1Ent2.id, + child_id: bar2Ent2.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + ]); + }); + (0, node_test_1.test)('find-children-multiple-inst-multi-levels', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ['foo/bar2', 'foo/bar3'], + ], + }, + }); + await seneca.ready(); + const rootEntityId = '123'; + const bar1Ent1 = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }); + const bar1Ent2 = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }); + const bar1Ent3 = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }); + const bar2Ent1_1 = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent1.id, + }); + const bar2Ent1_2 = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent1.id, + }); + const bar2Ent2_1 = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent2.id, + }); + const bar2Ent2_2 = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent2.id, + }); + const bar2Ent3_1 = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent3.id, + }); + const bar3Ent1_1_1 = await seneca.entity('foo/bar3').save$({ + bar2_id: bar2Ent1_1.id, + }); + const bar3Ent1_1_2 = await seneca.entity('foo/bar3').save$({ + bar2_id: bar2Ent1_1.id, + }); + const bar3Ent2_2_1 = await seneca.entity('foo/bar3').save$({ + bar2_id: bar2Ent2_2.id, + }); + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }); + (0, code_1.expect)(res.children).equal([ + // Level 1: All bar1 children of bar0 + { + parent_id: rootEntityId, + child_id: bar1Ent1.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: rootEntityId, + child_id: bar1Ent2.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: rootEntityId, + child_id: bar1Ent3.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + // Level 2: All bar2 children of bar1Ent1 + { + parent_id: bar1Ent1.id, + child_id: bar2Ent1_1.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + { + parent_id: bar1Ent1.id, + child_id: bar2Ent1_2.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + // Level 2: All bar2 children of bar1Ent2 + { + parent_id: bar1Ent2.id, + child_id: bar2Ent2_1.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + { + parent_id: bar1Ent2.id, + child_id: bar2Ent2_2.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + // Level 2: All bar2 children of bar1Ent3 + { + parent_id: bar1Ent3.id, + child_id: bar2Ent3_1.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + // Level 3: All bar3 children of bar2Ent1_1 + { + parent_id: bar2Ent1_1.id, + child_id: bar3Ent1_1_1.id, + parent_canon: 'foo/bar2', + child_canon: 'foo/bar3', + }, + { + parent_id: bar2Ent1_1.id, + child_id: bar3Ent1_1_2.id, + parent_canon: 'foo/bar2', + child_canon: 'foo/bar3', + }, + // Level 3: All bar3 children of bar2Ent2_2 + { + parent_id: bar2Ent2_2.id, + child_id: bar3Ent2_2_1.id, + parent_canon: 'foo/bar2', + child_canon: 'foo/bar3', + }, + ]); + }); + (0, node_test_1.test)('find-children-single-cycle', async () => { + const seneca = makeSeneca().use(__2.default, { + relations: { + parental: [ + ['A', 'B'], + ['A', 'C'], + ['C', 'E'], + ['C', 'D'], + ['E', 'G'], + ['E', 'F'], + ['F', 'H'], + ['C', 'A'], + ['N', 'M'], + ], + }, + }); + await seneca.ready(); + let rootEntityId = '123'; + const aEnt = await seneca.entity('A').save$({ + C_id: rootEntityId, + }); + const cEnt = await seneca.entity('C').save$({ + id: rootEntityId, + A_id: aEnt.id, + }); + const bEnt = await seneca.entity('B').save$({ + A_id: aEnt.id, + }); + const dEnt = await seneca.entity('D').save$({ + C_id: cEnt.id, + }); + const eEnt = await seneca.entity('E').save$({ + C_id: cEnt.id, + }); + const fEnt = await seneca.entity('F').save$({ + E_id: eEnt.id, + }); + const gEnt = await seneca.entity('G').save$({ + E_id: eEnt.id, + }); + const hEnt = await seneca.entity('H').save$({ + F_id: fEnt.id, + }); + const nEnt = await seneca.entity('N').save$(); + await seneca.entity('M').save$({ + N_id: nEnt.id, + }); + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'C', + rootEntityId: cEnt.id, + }); + // Should traverse from C, + // including C->A but NOT A->C + (0, code_1.expect)(res.children).equal([ + { + parent_id: rootEntityId, + child_id: aEnt.id, + parent_canon: 'C', + child_canon: 'A', + }, + { + parent_id: rootEntityId, + child_id: dEnt.id, + parent_canon: 'C', + child_canon: 'D', + }, + { + parent_id: rootEntityId, + child_id: eEnt.id, + parent_canon: 'C', + child_canon: 'E', + }, + { + parent_id: aEnt.id, + child_id: bEnt.id, + parent_canon: 'A', + child_canon: 'B', + }, + { + parent_id: eEnt.id, + child_id: fEnt.id, + parent_canon: 'E', + child_canon: 'F', + }, + { + parent_id: eEnt.id, + child_id: gEnt.id, + parent_canon: 'E', + child_canon: 'G', + }, + { + parent_id: fEnt.id, + child_id: hEnt.id, + parent_canon: 'F', + child_canon: 'H', + }, + ]); + }); }); +function makeSeneca(opts = {}) { + const seneca = (0, seneca_1.default)({ legacy: false }).test().use('promisify').use('entity'); + return seneca; +} //# sourceMappingURL=Traverse.test.js.map \ No newline at end of file diff --git a/dist-test/Traverse.test.js.map b/dist-test/Traverse.test.js.map index d8ff3bc..216beba 100644 --- a/dist-test/Traverse.test.js.map +++ b/dist-test/Traverse.test.js.map @@ -1 +1 @@ -{"version":3,"file":"Traverse.test.js","sourceRoot":"","sources":["../test/Traverse.test.ts"],"names":[],"mappings":";AAAA,gEAAgE;;;;;AAEhE,yCAA0C;AAC1C,qCAAmC;AAEnC,oDAA2B;AAC3B,8CAA8C;AAC9C,8CAA8C;AAE9C,2CAA4B;AAC5B,2CAAyB;AAEzB,IAAA,oBAAQ,EAAC,UAAU,EAAE,GAAG,EAAE;IACxB,IAAA,gBAAI,EAAC,aAAa,EAAE,KAAK,IAAI,EAAE;QAC7B,IAAA,aAAM,EAAC,WAAW,CAAC,CAAC,KAAK,EAAE,CAAA;QAE3B,MAAM,MAAM,GAAG,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;aACrC,IAAI,EAAE;aACN,GAAG,CAAC,WAAW,CAAC;aAChB,GAAG,CAAC,QAAQ,CAAC;aACb,GAAG,CAAC,WAAQ,CAAC,CAAA;QAEhB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,IAAA,aAAM,EAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE,CAAA;IAChD,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"} \ No newline at end of file +{"version":3,"file":"Traverse.test.js","sourceRoot":"","sources":["../test/Traverse.test.ts"],"names":[],"mappings":";AAAA,gEAAgE;;;;;AAEhE,yCAA0C;AAC1C,qCAAmC;AAEnC,oDAA2B;AAC3B,8CAA8C;AAC9C,8CAA8C;AAE9C,2CAA4B;AAC5B,2CAAyB;AAEzB,IAAA,oBAAQ,EAAC,UAAU,EAAE,GAAG,EAAE;IACxB,IAAA,gBAAI,EAAC,aAAa,EAAE,KAAK,IAAI,EAAE;QAC7B,IAAA,aAAM,EAAC,WAAW,CAAC,CAAC,KAAK,EAAE,CAAA;QAE3B,MAAM,MAAM,GAAG,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;aACrC,IAAI,EAAE;aACN,GAAG,CAAC,WAAW,CAAC;aAChB,GAAG,CAAC,QAAQ,CAAC;aACb,GAAG,CAAC,WAAQ,CAAC,CAAA;QAChB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,IAAA,aAAM,EAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE,CAAA;IAChD,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,WAAW,EAAE,KAAK,IAAI,EAAE;QAC3B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;iBAC1B;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QACF,0BAA0B;QAE1B,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,UAAU;YACV,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YAExB,UAAU;YACV,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YAExB,UAAU;YACV,kCAAkC;YAClC,yCAAyC;YACzC,kCAAkC;YAClC,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YAExB,UAAU;YACV,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,UAAU,EAAE,WAAW,CAAC;SAC1B,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;QACtC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE,EAAE;aACb;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QACF,0BAA0B;QAE1B,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC5B,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,uBAAuB,EAAE,KAAK,IAAI,EAAE;QACvC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QACF,0BAA0B;QAE1B,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC5B,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,iBAAiB,EAAE,KAAK,IAAI,EAAE;QACjC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,qBAAqB;oBACrB,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QACF,0BAA0B;QAE1B,gDAAgD;QAChD,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;SACzB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,qCAAqC;oBACrC,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QAEF,4CAA4C;QAC5C,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;SACzB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,kBAAkB,EAAE,KAAK,IAAI,EAAE;QAClC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QACF,0BAA0B;QAE1B,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;SACzB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,qBAAqB,EAAE,KAAK,IAAI,EAAE;QACrC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,YAAY;oBACZ,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,YAAY;oBACZ,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;SACzB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;QACtC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,gCAAgC;oBAChC,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QACF,0BAA0B;QAE1B,iDAAiD;QACjD,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;SACzB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QAC1C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,wBAAwB;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,4CAA4C;oBAC5C,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;SACzB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,oBAAoB,EAAE,KAAK,IAAI,EAAE;QACpC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,YAAY;oBACZ,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QACF,0BAA0B;QAE1B,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;SACzB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,uBAAuB,EAAE,KAAK,IAAI,EAAE;QACvC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,CAAC,CAAA;QACzC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAA;QACvD,0BAA0B;QAE1B,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC5B,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;QACtC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE,EAAE;aACb;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QACF,0BAA0B;QAE1B,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC5B,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,cAAc,EAAE,KAAK,IAAI,EAAE;QAC9B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;iBAC1B;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,UAAU;YACV,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YAExB,UAAU;YACV,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YAExB,UAAU;YACV,CAAC,UAAU,EAAE,WAAW,CAAC;SAC1B,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;QACzC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,qCAAqC;oBACrC,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,WAAW,EAAE,WAAW,CAAC;iBAC3B;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,UAAU;YACV,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YAExB,UAAU;YACV,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,UAAU,EAAE,UAAU,CAAC;YAExB,UAAU;YACV,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,WAAW,EAAE,WAAW,CAAC;SAC3B,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,oBAAoB,EAAE,KAAK,IAAI,EAAE;QACpC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,sCAAsC;oBACtC,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,WAAW,CAAC;iBAC1B;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,UAAU;YACV,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YAExB,UAAU;YACV,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YAExB,UAAU;YACV,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,UAAU,EAAE,WAAW,CAAC;SAC1B,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,cAAc,EAAE,KAAK,IAAI,EAAE;QAC9B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;iBAC1B;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QACF,0BAA0B;QAE1B,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,UAAU;YACV,CAAC,UAAU,EAAE,UAAU,CAAC;YAExB,UAAU;YACV,CAAC,UAAU,EAAE,WAAW,CAAC;SAC1B,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;QACzC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,WAAW,EAAE,WAAW,CAAC;oBAC1B,uCAAuC;oBACvC,CAAC,WAAW,EAAE,WAAW,CAAC;oBAC1B,CAAC,WAAW,EAAE,WAAW,CAAC;iBAC3B;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,UAAU;YACV,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,WAAW,CAAC;YAEzB,UAAU;YACV,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,WAAW,EAAE,WAAW,CAAC;YAE1B,UAAU;YACV,CAAC,WAAW,EAAE,WAAW,CAAC;SAC3B,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,oBAAoB,EAAE,KAAK,IAAI,EAAE;QACpC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,WAAW,EAAE,WAAW,CAAC;oBAC1B,gDAAgD;oBAChD,CAAC,WAAW,EAAE,UAAU,CAAC;oBACzB,CAAC,WAAW,EAAE,WAAW,CAAC;iBAC3B;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,UAAU;YACV,CAAC,UAAU,EAAE,UAAU,CAAC;YAExB,UAAU;YACV,CAAC,UAAU,EAAE,WAAW,CAAC;YAEzB,UAAU;YACV,CAAC,WAAW,EAAE,WAAW,CAAC;YAC1B,CAAC,WAAW,EAAE,WAAW,CAAC;SAC3B,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,WAAW,EAAE,WAAW,CAAC;oBAC1B,uBAAuB;oBACvB,CAAC,WAAW,EAAE,WAAW,CAAC;oBAC1B,sBAAsB;oBACtB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,WAAW,EAAE,WAAW,CAAC;iBAC3B;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,UAAU;SACvB,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,UAAU;YACV,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,UAAU,EAAE,WAAW,CAAC;YAEzB,UAAU;YACV,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,WAAW,EAAE,WAAW,CAAC;YAE1B,UAAU;YACV,CAAC,WAAW,EAAE,WAAW,CAAC;SAC3B,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;iBACX;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,GAAG;SAChB,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,UAAU;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YAEV,UAAU;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YAEV,UAAU;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;SACX,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,+BAA+B,EAAE,KAAK,IAAI,EAAE;QAC/C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;iBACX;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,GAAG;SAChB,CAAC,CAAA;QAEF,oBAAoB;QACpB,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC5B,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;QAC7C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,OAAO,EAAE,OAAO,CAAC;oBAClB,CAAC,OAAO,EAAE,OAAO,CAAC;oBAClB,CAAC,OAAO,EAAE,OAAO,CAAC;oBAClB,CAAC,OAAO,EAAE,OAAO,CAAC;oBAClB,CAAC,OAAO,EAAE,OAAO,CAAC;oBAClB,CAAC,OAAO,EAAE,OAAO,CAAC;oBAClB,CAAC,OAAO,EAAE,OAAO,CAAC;oBAClB,CAAC,OAAO,EAAE,OAAO,CAAC;oBAClB,CAAC,OAAO,EAAE,OAAO,CAAC;oBAClB,CAAC,OAAO,EAAE,QAAQ,CAAC;iBACpB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,OAAO;SACpB,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,CAAC,OAAO,EAAE,OAAO,CAAC;YAClB,CAAC,OAAO,EAAE,OAAO,CAAC;YAClB,CAAC,OAAO,EAAE,OAAO,CAAC;YAClB,CAAC,OAAO,EAAE,OAAO,CAAC;YAClB,CAAC,OAAO,EAAE,OAAO,CAAC;YAClB,CAAC,OAAO,EAAE,OAAO,CAAC;YAClB,CAAC,OAAO,EAAE,OAAO,CAAC;YAClB,CAAC,OAAO,EAAE,OAAO,CAAC;YAClB,CAAC,OAAO,EAAE,OAAO,CAAC;YAClB,CAAC,OAAO,EAAE,QAAQ,CAAC;SACpB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,uBAAuB,EAAE,KAAK,IAAI,EAAE;QACvC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;iBACX;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,GAAG;SAChB,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,UAAU;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YAEV,UAAU;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YAEV,UAAU;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;SACX,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QAC3C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;iBACX;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,GAAG;SAChB,CAAC,CAAA;QAEF,mDAAmD;QACnD,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,UAAU;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YAEV,4BAA4B;YAC5B,CAAC,GAAG,EAAE,GAAG,CAAC;YAEV,UAAU;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;SACX,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QAC3C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,MAAM,EAAE,QAAQ,CAAC;oBAClB,CAAC,MAAM,EAAE,OAAO,CAAC;oBACjB,CAAC,MAAM,EAAE,OAAO,CAAC;oBACjB,CAAC,MAAM,EAAE,QAAQ,CAAC;oBAClB,CAAC,OAAO,EAAE,QAAQ,CAAC;oBACnB,CAAC,OAAO,EAAE,QAAQ,CAAC;oBACnB,CAAC,QAAQ,EAAE,SAAS,CAAC;oBACrB,CAAC,QAAQ,EAAE,SAAS,CAAC;iBACtB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,MAAM;SACnB,CAAC,CAAA;QAEF,gDAAgD;QAChD,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,UAAU;YACV,CAAC,MAAM,EAAE,OAAO,CAAC;YACjB,CAAC,MAAM,EAAE,OAAO,CAAC;YACjB,CAAC,MAAM,EAAE,QAAQ,CAAC;YAClB,CAAC,MAAM,EAAE,QAAQ,CAAC;YAElB,UAAU;YACV,CAAC,OAAO,EAAE,QAAQ,CAAC;YACnB,CAAC,OAAO,EAAE,QAAQ,CAAC;YACnB,CAAC,QAAQ,EAAE,SAAS,CAAC;YACrB,CAAC,QAAQ,EAAE,SAAS,CAAC;SACtB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,MAAM,EAAE,GAAG,CAAC;oBACb,CAAC,MAAM,EAAE,GAAG,CAAC;oBACb,CAAC,MAAM,EAAE,GAAG,CAAC;oBACb,CAAC,GAAG,EAAE,QAAQ,CAAC;oBACf,CAAC,GAAG,EAAE,QAAQ,CAAC;oBACf,CAAC,GAAG,EAAE,QAAQ,CAAC;oBACf,CAAC,QAAQ,EAAE,KAAK,CAAC;iBAClB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,MAAM;SACnB,CAAC,CAAA;QAEF,oDAAoD;QACpD,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,UAAU;YACV,CAAC,MAAM,EAAE,GAAG,CAAC;YACb,CAAC,MAAM,EAAE,GAAG,CAAC;YACb,CAAC,MAAM,EAAE,GAAG,CAAC;YAEb,yBAAyB;YACzB,CAAC,GAAG,EAAE,QAAQ,CAAC;YAEf,UAAU;YACV,CAAC,QAAQ,EAAE,KAAK,CAAC;SAClB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAClD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,0CAA0C;oBAC1C,CAAC,KAAK,EAAE,KAAK,CAAC;oBACd,CAAC,KAAK,EAAE,KAAK,CAAC;oBACd,CAAC,KAAK,EAAE,KAAK,CAAC;oBAEd,0CAA0C;oBAC1C,CAAC,KAAK,EAAE,MAAM,CAAC;oBACf,CAAC,KAAK,EAAE,MAAM,CAAC;oBACf,CAAC,KAAK,EAAE,MAAM,CAAC;oBACf,CAAC,KAAK,EAAE,MAAM,CAAC;oBACf,CAAC,KAAK,EAAE,MAAM,CAAC;oBAEf,0CAA0C;oBAC1C,CAAC,MAAM,EAAE,MAAM,CAAC;oBAChB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAChB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAChB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAChB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAChB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAChB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAEhB,0CAA0C;oBAC1C,CAAC,MAAM,EAAE,MAAM,CAAC;oBAChB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAChB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAChB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAChB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAChB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAChB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAChB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAEhB,0CAA0C;oBAC1C,CAAC,MAAM,EAAE,OAAO,CAAC;oBACjB,CAAC,MAAM,EAAE,OAAO,CAAC;oBACjB,CAAC,MAAM,EAAE,OAAO,CAAC;oBACjB,CAAC,MAAM,EAAE,OAAO,CAAC;oBACjB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAChB,CAAC,MAAM,EAAE,OAAO,CAAC;oBACjB,CAAC,MAAM,EAAE,OAAO,CAAC;oBACjB,CAAC,MAAM,EAAE,MAAM,CAAC;oBAEhB,qEAAqE;oBACrE,CAAC,OAAO,EAAE,UAAU,CAAC;oBACrB,CAAC,OAAO,EAAE,UAAU,CAAC;oBACrB,CAAC,OAAO,EAAE,UAAU,CAAC;oBACrB,CAAC,OAAO,EAAE,UAAU,CAAC;oBACrB,CAAC,OAAO,EAAE,UAAU,CAAC;oBACrB,CAAC,MAAM,EAAE,UAAU,CAAC;oBACpB,CAAC,OAAO,EAAE,UAAU,CAAC;oBACrB,CAAC,OAAO,EAAE,UAAU,CAAC;oBACrB,iDAAiD;oBACjD,CAAC,MAAM,EAAE,KAAK,CAAC;oBACf,CAAC,MAAM,EAAE,UAAU,CAAC;oBAEpB,0CAA0C;oBAC1C,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBAExB,0CAA0C;oBAC1C,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,WAAW,EAAE,UAAU,CAAC;oBACzB,CAAC,WAAW,EAAE,UAAU,CAAC;oBACzB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,2DAA2D;oBAC3D,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBAExB,0CAA0C;oBAC1C,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBAExB,2CAA2C;oBAC3C,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,WAAW,EAAE,UAAU,CAAC;oBACzB,CAAC,WAAW,EAAE,UAAU,CAAC;oBACzB,CAAC,WAAW,EAAE,UAAU,CAAC;oBACzB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBAExB,4CAA4C;oBAC5C,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACtD,UAAU,EAAE,KAAK;SAClB,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YACrB,gCAAgC;YAChC,CAAC,KAAK,EAAE,KAAK,CAAC;YACd,CAAC,KAAK,EAAE,KAAK,CAAC;YACd,CAAC,KAAK,EAAE,KAAK,CAAC;YAEd,gCAAgC;YAChC,CAAC,KAAK,EAAE,MAAM,CAAC;YACf,CAAC,KAAK,EAAE,MAAM,CAAC;YACf,CAAC,KAAK,EAAE,MAAM,CAAC;YACf,CAAC,KAAK,EAAE,MAAM,CAAC;YACf,CAAC,KAAK,EAAE,MAAM,CAAC;YAEf,gCAAgC;YAChC,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,MAAM,CAAC;YAEhB,gCAAgC;YAChC,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,MAAM,CAAC;YAEhB,gCAAgC;YAChC,CAAC,MAAM,EAAE,OAAO,CAAC;YACjB,CAAC,MAAM,EAAE,OAAO,CAAC;YACjB,CAAC,MAAM,EAAE,OAAO,CAAC;YACjB,CAAC,MAAM,EAAE,OAAO,CAAC;YACjB,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,MAAM,CAAC;YAChB,CAAC,MAAM,EAAE,OAAO,CAAC;YACjB,CAAC,MAAM,EAAE,OAAO,CAAC;YAEjB,gCAAgC;YAChC,CAAC,OAAO,EAAE,UAAU,CAAC;YACrB,CAAC,OAAO,EAAE,UAAU,CAAC;YACrB,CAAC,OAAO,EAAE,UAAU,CAAC;YACrB,CAAC,OAAO,EAAE,UAAU,CAAC;YACrB,CAAC,OAAO,EAAE,UAAU,CAAC;YACrB,0DAA0D;YAC1D,CAAC,MAAM,EAAE,UAAU,CAAC;YACpB,CAAC,MAAM,EAAE,UAAU,CAAC;YACpB,CAAC,OAAO,EAAE,UAAU,CAAC;YACrB,CAAC,OAAO,EAAE,UAAU,CAAC;YAErB,gCAAgC;YAChC,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YAExB,gCAAgC;YAChC,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,WAAW,EAAE,UAAU,CAAC;YACzB,CAAC,WAAW,EAAE,UAAU,CAAC;YACzB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,yFAAyF;YAEzF,gCAAgC;YAChC,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YAExB,gCAAgC;YAChC,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,WAAW,EAAE,UAAU,CAAC;YACzB,CAAC,WAAW,EAAE,UAAU,CAAC;YACzB,CAAC,WAAW,EAAE,UAAU,CAAC;YACzB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YAExB,iCAAiC;YACjC,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,UAAU,EAAE,WAAW,CAAC;YACzB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;YACxB,CAAC,UAAU,EAAE,UAAU,CAAC;SACzB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,eAAe,EAAE,KAAK,IAAI,EAAE;QAC/B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,WAAW,CAAC;oBACzB,CAAC,UAAU,EAAE,WAAW,CAAC;iBAC1B;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,YAAY,GAAG,KAAK,CAAA;QAE1B,mCAAmC;QACnC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,4BAA4B;QAC5B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,4BAA4B;QAC5B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,4BAA4B;QAC5B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,4BAA4B;QAC5B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,4BAA4B;QAC5B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,4BAA4B;QAC5B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,4BAA4B;QAC5B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,4BAA4B;QAC5B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC;YACtD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,4BAA4B;QAC5B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC;YACtD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE;YAC1D,UAAU,EAAE,UAAU;YACtB,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;YACzB,UAAU;YACV;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD,UAAU;YACV;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD,UAAU;YACV;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD,UAAU;YACV;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,QAAQ,CAAC,EAAE;gBACrB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,WAAW;aACzB;YACD;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,QAAQ,CAAC,EAAE;gBACrB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,WAAW;aACzB;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,+BAA+B,EAAE,KAAK,IAAI,EAAE;QAC/C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,CAAC,CAAA;QACzC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,YAAY,GAAG,KAAK,CAAA;QAE1B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE;YAC1D,UAAU,EAAE,UAAU;YACtB,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAChC,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,YAAY,GAAG,KAAK,CAAA;QAE1B,mCAAmC;QAEnC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE;YAC1D,UAAU,EAAE,UAAU;YACtB,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAChC,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,YAAY,GAAG,KAAK,CAAA;QAE1B,8CAA8C;QAC9C,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE;YAC1D,UAAU,EAAE,UAAU;YACtB,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAA;QAEF,6DAA6D;QAC7D,IAAA,aAAM,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;YACzB;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACnD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,eAAe,CAAC;oBAC7B,CAAC,UAAU,EAAE,cAAc,CAAC;oBAC5B,CAAC,cAAc,EAAE,iBAAiB,CAAC;iBACpC;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,YAAY,GAAG,UAAU,CAAA;QAE/B,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,KAAK,CAAC;YAC7D,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC;YAC3D,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC;YAC9D,UAAU,EAAE,UAAU,CAAC,EAAE;SAC1B,CAAC,CAAA;QAEF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE;YAC1D,oDAAoD;YACpD,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;YACzB;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,UAAU,CAAC,EAAE;gBACvB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,cAAc;aAC5B;YACD;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,WAAW,CAAC,EAAE;gBACxB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,eAAe;aAC7B;YACD;gBACE,SAAS,EAAE,UAAU,CAAC,EAAE;gBACxB,QAAQ,EAAE,UAAU,CAAC,EAAE;gBACvB,YAAY,EAAE,cAAc;gBAC5B,WAAW,EAAE,iBAAiB;aAC/B;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,YAAY,GAAG,KAAK,CAAA;QAE1B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,kDAAkD;QAClD,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpC,OAAO,EAAE,KAAK;SACf,CAAC,CAAA;QAEF,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpC,OAAO,EAAE,KAAK;SACf,CAAC,CAAA;QAEF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE;YAC1D,UAAU,EAAE,UAAU;YACtB,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAA;QAEF,2CAA2C;QAC3C,IAAA,aAAM,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;YACzB;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAClD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;aACrC;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,YAAY,GAAG,KAAK,CAAA;QAE1B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE;YAC1D,UAAU,EAAE,UAAU;YACtB,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;YACzB;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,YAAY,GAAG,KAAK,CAAA;QAE1B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,OAAO,CAAC,EAAE;SACpB,CAAC,CAAA;QAEF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE;YAC1D,UAAU,EAAE,UAAU;YACtB,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;YACzB;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QAC1C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,UAAU,EAAE,YAAY;gBACxB,UAAU,EAAE,cAAc;aAC3B;YAED,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,YAAY,GAAG,KAAK,CAAA;QAE1B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,UAAU,EAAE,YAAY;SACzB,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACpD,YAAY,EAAE,OAAO,CAAC,EAAE;SACzB,CAAC,CAAA;QAEF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE;YAC1D,UAAU,EAAE,UAAU;YACtB,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;YACzB;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,QAAQ,EAAE,OAAO,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QAC1C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,YAAY,GAAG,KAAK,CAAA;QAE1B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACrD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACrD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACrD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACrD,OAAO,EAAE,QAAQ,CAAC,EAAE;SACrB,CAAC,CAAA;QAEF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACrD,OAAO,EAAE,QAAQ,CAAC,EAAE;SACrB,CAAC,CAAA;QAEF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE;YAC1D,UAAU,EAAE,UAAU;YACtB,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;YACzB;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,QAAQ,CAAC,EAAE;gBACrB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,QAAQ,CAAC,EAAE;gBACrB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,QAAQ,CAAC,EAAE;gBACrB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,QAAQ,CAAC,EAAE;gBACtB,QAAQ,EAAE,QAAQ,CAAC,EAAE;gBACrB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,QAAQ,CAAC,EAAE;gBACtB,QAAQ,EAAE,QAAQ,CAAC,EAAE;gBACrB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;oBACxB,CAAC,UAAU,EAAE,UAAU,CAAC;iBACzB;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,MAAM,YAAY,GAAG,KAAK,CAAA;QAE1B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACrD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACrD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACrD,OAAO,EAAE,YAAY;SACtB,CAAC,CAAA;QAEF,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACvD,OAAO,EAAE,QAAQ,CAAC,EAAE;SACrB,CAAC,CAAA;QAEF,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACvD,OAAO,EAAE,QAAQ,CAAC,EAAE;SACrB,CAAC,CAAA;QAEF,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACvD,OAAO,EAAE,QAAQ,CAAC,EAAE;SACrB,CAAC,CAAA;QAEF,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACvD,OAAO,EAAE,QAAQ,CAAC,EAAE;SACrB,CAAC,CAAA;QAEF,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACvD,OAAO,EAAE,QAAQ,CAAC,EAAE;SACrB,CAAC,CAAA;QAEF,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACzD,OAAO,EAAE,UAAU,CAAC,EAAE;SACvB,CAAC,CAAA;QAEF,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACzD,OAAO,EAAE,UAAU,CAAC,EAAE;SACvB,CAAC,CAAA;QAEF,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;YACzD,OAAO,EAAE,UAAU,CAAC,EAAE;SACvB,CAAC,CAAA;QAEF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE;YAC1D,UAAU,EAAE,UAAU;YACtB,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAA;QAEF,IAAA,aAAM,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;YACzB,qCAAqC;YACrC;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,QAAQ,CAAC,EAAE;gBACrB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,QAAQ,CAAC,EAAE;gBACrB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,QAAQ,CAAC,EAAE;gBACrB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YAED,yCAAyC;YACzC;gBACE,SAAS,EAAE,QAAQ,CAAC,EAAE;gBACtB,QAAQ,EAAE,UAAU,CAAC,EAAE;gBACvB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,QAAQ,CAAC,EAAE;gBACtB,QAAQ,EAAE,UAAU,CAAC,EAAE;gBACvB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YAED,yCAAyC;YACzC;gBACE,SAAS,EAAE,QAAQ,CAAC,EAAE;gBACtB,QAAQ,EAAE,UAAU,CAAC,EAAE;gBACvB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,QAAQ,CAAC,EAAE;gBACtB,QAAQ,EAAE,UAAU,CAAC,EAAE;gBACvB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YAED,yCAAyC;YACzC;gBACE,SAAS,EAAE,QAAQ,CAAC,EAAE;gBACtB,QAAQ,EAAE,UAAU,CAAC,EAAE;gBACvB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YAED,2CAA2C;YAC3C;gBACE,SAAS,EAAE,UAAU,CAAC,EAAE;gBACxB,QAAQ,EAAE,YAAY,CAAC,EAAE;gBACzB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YACD;gBACE,SAAS,EAAE,UAAU,CAAC,EAAE;gBACxB,QAAQ,EAAE,YAAY,CAAC,EAAE;gBACzB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;YAED,2CAA2C;YAC3C;gBACE,SAAS,EAAE,UAAU,CAAC,EAAE;gBACxB,QAAQ,EAAE,YAAY,CAAC,EAAE;gBACzB,YAAY,EAAE,UAAU;gBACxB,WAAW,EAAE,UAAU;aACxB;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAA,gBAAI,EAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,WAAQ,EAAE;YACxC,SAAS,EAAE;gBACT,QAAQ,EAAE;oBACR,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;oBACV,CAAC,GAAG,EAAE,GAAG,CAAC;iBACX;aACF;SACF,CAAC,CAAA;QACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QAEpB,IAAI,YAAY,GAAG,KAAK,CAAA;QAExB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YAC1C,IAAI,EAAE,YAAY;SACnB,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YAC1C,EAAE,EAAE,YAAY;YAChB,IAAI,EAAE,IAAI,CAAC,EAAE;SACd,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YAC1C,IAAI,EAAE,IAAI,CAAC,EAAE;SACd,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YAC1C,IAAI,EAAE,IAAI,CAAC,EAAE;SACd,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YAC1C,IAAI,EAAE,IAAI,CAAC,EAAE;SACd,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YAC1C,IAAI,EAAE,IAAI,CAAC,EAAE;SACd,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YAC1C,IAAI,EAAE,IAAI,CAAC,EAAE;SACd,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YAC1C,IAAI,EAAE,IAAI,CAAC,EAAE;SACd,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAA;QAE7C,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YAC7B,IAAI,EAAE,IAAI,CAAC,EAAE;SACd,CAAC,CAAA;QAEF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE;YAC1D,UAAU,EAAE,GAAG;YACf,YAAY,EAAE,IAAI,CAAC,EAAE;SACtB,CAAC,CAAA;QAEF,0BAA0B;QAC1B,8BAA8B;QAC9B,IAAA,aAAM,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;YACzB;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,IAAI,CAAC,EAAE;gBACjB,YAAY,EAAE,GAAG;gBACjB,WAAW,EAAE,GAAG;aACjB;YACD;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,IAAI,CAAC,EAAE;gBACjB,YAAY,EAAE,GAAG;gBACjB,WAAW,EAAE,GAAG;aACjB;YACD;gBACE,SAAS,EAAE,YAAY;gBACvB,QAAQ,EAAE,IAAI,CAAC,EAAE;gBACjB,YAAY,EAAE,GAAG;gBACjB,WAAW,EAAE,GAAG;aACjB;YACD;gBACE,SAAS,EAAE,IAAI,CAAC,EAAE;gBAClB,QAAQ,EAAE,IAAI,CAAC,EAAE;gBACjB,YAAY,EAAE,GAAG;gBACjB,WAAW,EAAE,GAAG;aACjB;YACD;gBACE,SAAS,EAAE,IAAI,CAAC,EAAE;gBAClB,QAAQ,EAAE,IAAI,CAAC,EAAE;gBACjB,YAAY,EAAE,GAAG;gBACjB,WAAW,EAAE,GAAG;aACjB;YACD;gBACE,SAAS,EAAE,IAAI,CAAC,EAAE;gBAClB,QAAQ,EAAE,IAAI,CAAC,EAAE;gBACjB,YAAY,EAAE,GAAG;gBACjB,WAAW,EAAE,GAAG;aACjB;YACD;gBACE,SAAS,EAAE,IAAI,CAAC,EAAE;gBAClB,QAAQ,EAAE,IAAI,CAAC,EAAE;gBACjB,YAAY,EAAE,GAAG;gBACjB,WAAW,EAAE,GAAG;aACjB;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,SAAS,UAAU,CAAC,OAAY,EAAE;IAChC,MAAM,MAAM,GAAG,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC9E,OAAO,MAAM,CAAA;AACf,CAAC"} \ No newline at end of file diff --git a/dist/Traverse.d.ts b/dist/Traverse.d.ts index 0a5227a..f6610ec 100644 --- a/dist/Traverse.d.ts +++ b/dist/Traverse.d.ts @@ -1,5 +1,13 @@ +type EntityID = string; +type ParentChildRelation = [EntityID, EntityID]; +type Parental = ParentChildRelation[]; type TraverseOptionsFull = { debug: boolean; + rootEntity: EntityID; + relations: { + parental: Parental; + }; + customRef: Record; }; export type TraverseOptions = Partial; declare function Traverse(this: any, options: TraverseOptionsFull): void; diff --git a/dist/Traverse.d.ts.map b/dist/Traverse.d.ts.map index 348a22b..90a1593 100644 --- a/dist/Traverse.d.ts.map +++ b/dist/Traverse.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"Traverse.d.ts","sourceRoot":"","sources":["../src/Traverse.ts"],"names":[],"mappings":"AAEA,KAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;AAE1D,iBAAS,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,mBAAmB,QAOxD;AAUD,eAAe,QAAQ,CAAA"} \ No newline at end of file +{"version":3,"file":"Traverse.d.ts","sourceRoot":"","sources":["../src/Traverse.ts"],"names":[],"mappings":"AAIA,KAAK,QAAQ,GAAG,MAAM,CAAA;AAGtB,KAAK,mBAAmB,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;AAE/C,KAAK,QAAQ,GAAG,mBAAmB,EAAE,CAAA;AASrC,KAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE,OAAO,CAAA;IACd,UAAU,EAAE,QAAQ,CAAA;IACpB,SAAS,EAAE;QACT,QAAQ,EAAE,QAAQ,CAAA;KACnB,CAAA;IACD,SAAS,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;CACpC,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;AAE1D,iBAAS,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,mBAAmB,QAgLxD;AAeD,eAAe,QAAQ,CAAA"} \ No newline at end of file diff --git a/dist/Traverse.js b/dist/Traverse.js index 092ba7a..ce16420 100644 --- a/dist/Traverse.js +++ b/dist/Traverse.js @@ -1,16 +1,132 @@ "use strict"; /* Copyright © 2025 Seneca Project Contributors, MIT License. */ Object.defineProperty(exports, "__esModule", { value: true }); +const gubu_1 = require("gubu"); function Traverse(options) { const seneca = this; - const { Default } = seneca.valid; - seneca.fix('sys:traverse'); - // .message('find:deps', msgFindDeps) + // const { Default } = seneca.valid + seneca + .fix('sys:traverse') + .message('find:deps', { + rootEntity: (0, gubu_1.Optional)(String), + }, msgFindDeps) + .message('find:children', { + rootEntity: (0, gubu_1.Optional)(String), + rootEntityId: String, + }, msgFindChildren); + // Returns a sorted list of entity pairs + // starting from a given entity. + // In breadth-first order, sorting first by level, + // then alphabetically in each level. + async function msgFindDeps(msg) { + // const seneca = this + const allRelations = options.relations.parental; + const rootEntity = msg.rootEntity || options.rootEntity; + const deps = []; + const parentChildrenMap = new Map(); + for (const [parent, child] of allRelations) { + if (!parentChildrenMap.has(parent)) { + parentChildrenMap.set(parent, []); + } + parentChildrenMap.get(parent).push(child); + } + for (const children of parentChildrenMap.values()) { + children.sort(); + } + const visitedEntitiesSet = new Set([rootEntity]); + let currentLevel = [rootEntity]; + while (currentLevel.length > 0) { + const nextLevel = []; + let levelDeps = []; + for (const parent of currentLevel) { + const children = parentChildrenMap.get(parent) || []; + for (const child of children) { + if (visitedEntitiesSet.has(child)) { + continue; + } + levelDeps.push([parent, child]); + visitedEntitiesSet.add(child); + nextLevel.push(child); + } + } + levelDeps = compareRelations(levelDeps); + deps.push(...levelDeps); + currentLevel = nextLevel; + } + return { + ok: true, + deps, + }; + } + // Returns all discovered child + // instances with their parent relationship. + async function msgFindChildren(msg) { + const rootEntity = msg.rootEntity || options.rootEntity; + const rootEntityId = msg.rootEntityId; + const customRef = options.customRef; + const relationsQueueRes = await seneca.post('sys:traverse,find:deps', { + rootEntity, + }); + const relationsQueue = relationsQueueRes.deps; + const result = []; + const parentInstanceMap = new Map(); + parentInstanceMap.set(rootEntity, new Set([rootEntityId])); + for (const [parentCanon, childCanon] of relationsQueue) { + const parentInstances = parentInstanceMap.get(parentCanon); + if (!parentInstances || parentInstances.size === 0) { + continue; + } + const foreignRef = customRef[childCanon] || `${getEntityName(parentCanon)}_id`; + if (!parentInstanceMap.has(childCanon)) { + parentInstanceMap.set(childCanon, new Set()); + } + const childInstancesSet = parentInstanceMap.get(childCanon); + const childQueryPromises = Array.from(parentInstances).map(async (parentId) => { + const childInstances = await seneca.entity(childCanon).list$({ + [foreignRef]: parentId, + fields$: ['id'], + }); + return { parentId, childInstances }; + }); + const queryResults = await Promise.all(childQueryPromises); + for (const { parentId, childInstances } of queryResults) { + for (const childInst of childInstances) { + const childId = childInst.id; + childInstancesSet.add(childId); + result.push({ + parent_id: parentId, + child_id: childId, + parent_canon: parentCanon, + child_canon: childCanon, + }); + } + } + } + return { + ok: true, + children: result, + }; + } + function compareRelations(relations) { + return [...relations].sort((a, b) => a[0].localeCompare(b[0], undefined, { numeric: true }) || + a[1].localeCompare(b[1], undefined, { numeric: true })); + } + function getEntityName(entityId) { + const canonSeparatorIdx = entityId.lastIndexOf('/'); + return canonSeparatorIdx === -1 + ? entityId + : entityId.slice(canonSeparatorIdx + 1); + } } // Default options. const defaults = { // TODO: Enable debug logging debug: false, + rootEntity: 'sys/user', + relations: { + parental: [], + }, + customRef: {}, }; Object.assign(Traverse, { defaults }); exports.default = Traverse; diff --git a/dist/Traverse.js.map b/dist/Traverse.js.map index 3d3101d..0af5148 100644 --- a/dist/Traverse.js.map +++ b/dist/Traverse.js.map @@ -1 +1 @@ -{"version":3,"file":"Traverse.js","sourceRoot":"","sources":["../src/Traverse.ts"],"names":[],"mappings":";AAAA,gEAAgE;;AAQhE,SAAS,QAAQ,CAAY,OAA4B;IACvD,MAAM,MAAM,GAAQ,IAAI,CAAA;IAExB,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,KAAK,CAAA;IAEhC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;IAC1B,qCAAqC;AACvC,CAAC;AAED,mBAAmB;AACnB,MAAM,QAAQ,GAAwB;IACpC,6BAA6B;IAC7B,KAAK,EAAE,KAAK;CACb,CAAA;AAED,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAA;AAErC,kBAAe,QAAQ,CAAA;AAEvB,IAAI,WAAW,KAAK,OAAO,MAAM,EAAE,CAAC;IAClC,MAAM,CAAC,OAAO,GAAG,QAAQ,CAAA;AAC3B,CAAC"} \ No newline at end of file +{"version":3,"file":"Traverse.js","sourceRoot":"","sources":["../src/Traverse.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/TraverseDoc.d.ts b/dist/TraverseDoc.d.ts index 8cf5a2b..28edc0a 100644 --- a/dist/TraverseDoc.d.ts +++ b/dist/TraverseDoc.d.ts @@ -1,5 +1,12 @@ declare const docs: { - messages: {}; + messages: { + msgFindDeps: { + desc: string; + }; + msgFindChildren: { + desc: string; + }; + }; }; export default docs; //# sourceMappingURL=TraverseDoc.d.ts.map \ No newline at end of file diff --git a/dist/TraverseDoc.d.ts.map b/dist/TraverseDoc.d.ts.map index 8953d88..a8ce1e3 100644 --- a/dist/TraverseDoc.d.ts.map +++ b/dist/TraverseDoc.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"TraverseDoc.d.ts","sourceRoot":"","sources":["../src/TraverseDoc.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,IAAI;;CAMT,CAAA;AAED,eAAe,IAAI,CAAA"} \ No newline at end of file +{"version":3,"file":"TraverseDoc.d.ts","sourceRoot":"","sources":["../src/TraverseDoc.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,IAAI;;;;;;;;;CAST,CAAA;AAED,eAAe,IAAI,CAAA"} \ No newline at end of file diff --git a/dist/TraverseDoc.js b/dist/TraverseDoc.js index 9f90a6b..1e1d245 100644 --- a/dist/TraverseDoc.js +++ b/dist/TraverseDoc.js @@ -3,9 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true }); const docs = { messages: { - // msgFindDeps: { - // desc: 'Return a sorted list of dependencies.', - // } + msgFindDeps: { + desc: 'Returns a sorted list of entity pairs starting from a given entity.', + }, + msgFindChildren: { + desc: 'Returns all discovered child instances with their parent relationship.', + }, }, }; exports.default = docs; diff --git a/dist/TraverseDoc.js.map b/dist/TraverseDoc.js.map index 46917e6..66cc70d 100644 --- a/dist/TraverseDoc.js.map +++ b/dist/TraverseDoc.js.map @@ -1 +1 @@ -{"version":3,"file":"TraverseDoc.js","sourceRoot":"","sources":["../src/TraverseDoc.ts"],"names":[],"mappings":";AAAA,gEAAgE;;AAEhE,MAAM,IAAI,GAAG;IACX,QAAQ,EAAE;IACR,iBAAiB;IACjB,mDAAmD;IACnD,IAAI;KACL;CACF,CAAA;AAED,kBAAe,IAAI,CAAA;AAEnB,IAAI,WAAW,KAAK,OAAO,MAAM,EAAE,CAAC;IAClC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;AACvB,CAAC"} \ No newline at end of file +{"version":3,"file":"TraverseDoc.js","sourceRoot":"","sources":["../src/TraverseDoc.ts"],"names":[],"mappings":";AAAA,gEAAgE;;AAEhE,MAAM,IAAI,GAAG;IACX,QAAQ,EAAE;QACR,WAAW,EAAE;YACX,IAAI,EAAE,qEAAqE;SAC5E;QACD,eAAe,EAAE;YACf,IAAI,EAAE,wEAAwE;SAC/E;KACF;CACF,CAAA;AAED,kBAAe,IAAI,CAAA;AAEnB,IAAI,WAAW,KAAK,OAAO,MAAM,EAAE,CAAC;IAClC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;AACvB,CAAC"} \ No newline at end of file diff --git a/package.json b/package.json index f38e094..98507e0 100644 --- a/package.json +++ b/package.json @@ -42,12 +42,14 @@ "@seneca/doc": "^8.0.0", "@seneca/maintain": "^0.1.0", "@types/node": "^24.10.1", + "gubu": "^9.0.0", "prettier": "3.7.3", "seneca-msg-test": "^4.1.0", "typescript": "^5.9.3" }, "peerDependencies": { "@seneca/entity-util": ">=3", + "gubu": ">=9", "seneca": ">=3||>=4.0.0-rc2", "seneca-entity": ">=26", "seneca-promisify": ">=3" diff --git a/src/Traverse.ts b/src/Traverse.ts index 86adc15..9c3ff65 100644 --- a/src/Traverse.ts +++ b/src/Traverse.ts @@ -1,7 +1,28 @@ /* Copyright © 2025 Seneca Project Contributors, MIT License. */ +import { Optional } from 'gubu' + +type EntityID = string +type UUID = string + +type ParentChildRelation = [EntityID, EntityID] + +type Parental = ParentChildRelation[] + +type ChildInstance = { + parent_id: UUID + child_id: UUID + parent_canon: EntityID + child_canon: EntityID +} + type TraverseOptionsFull = { debug: boolean + rootEntity: EntityID + relations: { + parental: Parental + } + customRef: Record } export type TraverseOptions = Partial @@ -9,16 +30,190 @@ export type TraverseOptions = Partial function Traverse(this: any, options: TraverseOptionsFull) { const seneca: any = this - const { Default } = seneca.valid + // const { Default } = seneca.valid + + seneca + .fix('sys:traverse') + .message( + 'find:deps', + { + rootEntity: Optional(String), + }, + msgFindDeps, + ) + .message( + 'find:children', + { + rootEntity: Optional(String), + rootEntityId: String, + }, + msgFindChildren, + ) + + // Returns a sorted list of entity pairs + // starting from a given entity. + // In breadth-first order, sorting first by level, + // then alphabetically in each level. + async function msgFindDeps( + this: any, + msg: { + rootEntity?: EntityID + }, + ): Promise<{ ok: boolean; deps: ParentChildRelation[] }> { + // const seneca = this + const allRelations: Parental = options.relations.parental + const rootEntity = msg.rootEntity || options.rootEntity + const deps: ParentChildRelation[] = [] + + const parentChildrenMap: Map = new Map() + + for (const [parent, child] of allRelations) { + if (!parentChildrenMap.has(parent)) { + parentChildrenMap.set(parent, []) + } + + parentChildrenMap.get(parent)!.push(child) + } + + for (const children of parentChildrenMap.values()) { + children.sort() + } + + const visitedEntitiesSet: Set = new Set([rootEntity]) + let currentLevel: EntityID[] = [rootEntity] + + while (currentLevel.length > 0) { + const nextLevel: EntityID[] = [] + let levelDeps: ParentChildRelation[] = [] + + for (const parent of currentLevel) { + const children = parentChildrenMap.get(parent) || [] + + for (const child of children) { + if (visitedEntitiesSet.has(child)) { + continue + } + + levelDeps.push([parent, child]) + visitedEntitiesSet.add(child) + nextLevel.push(child) + } + } + + levelDeps = compareRelations(levelDeps) + deps.push(...levelDeps) + currentLevel = nextLevel + } + + return { + ok: true, + deps, + } + } + + // Returns all discovered child + // instances with their parent relationship. + async function msgFindChildren( + this: any, + msg: { + rootEntity?: EntityID + rootEntityId: UUID + }, + ): Promise<{ + ok: boolean + children: ChildInstance[] + }> { + const rootEntity: EntityID = msg.rootEntity || options.rootEntity + const rootEntityId = msg.rootEntityId + const customRef = options.customRef + const relationsQueueRes = await seneca.post('sys:traverse,find:deps', { + rootEntity, + }) + const relationsQueue = relationsQueueRes.deps + + const result: ChildInstance[] = [] + const parentInstanceMap = new Map>() + + parentInstanceMap.set(rootEntity, new Set([rootEntityId])) + + for (const [parentCanon, childCanon] of relationsQueue) { + const parentInstances = parentInstanceMap.get(parentCanon) + + if (!parentInstances || parentInstances.size === 0) { + continue + } + + const foreignRef = + customRef[childCanon] || `${getEntityName(parentCanon)}_id` + + if (!parentInstanceMap.has(childCanon)) { + parentInstanceMap.set(childCanon, new Set()) + } + + const childInstancesSet = parentInstanceMap.get(childCanon) + + const childQueryPromises = Array.from(parentInstances).map( + async (parentId) => { + const childInstances = await seneca.entity(childCanon).list$({ + [foreignRef]: parentId, + fields$: ['id'], + }) + + return { parentId, childInstances } + }, + ) + + const queryResults = await Promise.all(childQueryPromises) + + for (const { parentId, childInstances } of queryResults) { + for (const childInst of childInstances) { + const childId = childInst.id + + childInstancesSet!.add(childId) + + result.push({ + parent_id: parentId, + child_id: childId, + parent_canon: parentCanon, + child_canon: childCanon, + }) + } + } + } + + return { + ok: true, + children: result, + } + } + + function compareRelations( + relations: ParentChildRelation[], + ): ParentChildRelation[] { + return [...relations].sort( + (a, b) => + a[0].localeCompare(b[0], undefined, { numeric: true }) || + a[1].localeCompare(b[1], undefined, { numeric: true }), + ) + } - seneca.fix('sys:traverse') - // .message('find:deps', msgFindDeps) + function getEntityName(entityId: EntityID): string { + const canonSeparatorIdx = entityId.lastIndexOf('/') + return canonSeparatorIdx === -1 + ? entityId + : entityId.slice(canonSeparatorIdx + 1) + } } // Default options. const defaults: TraverseOptionsFull = { // TODO: Enable debug logging debug: false, + rootEntity: 'sys/user', + relations: { + parental: [], + }, + customRef: {}, } Object.assign(Traverse, { defaults }) diff --git a/src/TraverseDoc.ts b/src/TraverseDoc.ts index 500beda..0de700a 100644 --- a/src/TraverseDoc.ts +++ b/src/TraverseDoc.ts @@ -2,9 +2,12 @@ const docs = { messages: { - // msgFindDeps: { - // desc: 'Return a sorted list of dependencies.', - // } + msgFindDeps: { + desc: 'Returns a sorted list of entity pairs starting from a given entity.', + }, + msgFindChildren: { + desc: 'Returns all discovered child instances with their parent relationship.', + }, }, } diff --git a/test/Traverse.test.ts b/test/Traverse.test.ts index 1fc2f5d..88f5be6 100644 --- a/test/Traverse.test.ts +++ b/test/Traverse.test.ts @@ -19,9 +19,1993 @@ describe('Traverse', () => { .use('promisify') .use('entity') .use(Traverse) - await seneca.ready() expect(seneca.find_plugin('Traverse')).exist() }) + + test('find-deps', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }) + // console.log('RES', res) + + expect(res.deps).equal([ + // Level 0 + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar0', 'foo/zed0'], + + // Level 1 + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar2', 'foo/bar3'], + ['foo/bar2', 'foo/bar9'], + ['foo/zed0', 'foo/zed1'], + + // Level 2 + // Sort each level alphabetically. + // Thus, foo/bar3 should be listed first, + // although its parent is foo/bar2 + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/zed1', 'foo/zed2'], + + // Level 3 + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ]) + }) + + test('find-deps-empty-list', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }) + // console.log('RES', res) + + expect(res.deps).equal([]) + }) + + test('find-deps-no-children', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar1', 'foo/bar2'], + ['foo/bar2', 'foo/bar3'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }) + // console.log('RES', res) + + expect(res.deps).equal([]) + }) + + test('find-deps-cycle', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + // Cycle back to root + ['foo/bar2', 'foo/bar0'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }) + // console.log('RES', res) + + // Should only traverse once, ignoring the cycle + expect(res.deps).equal([ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ]) + }) + + test('find-deps-cycle-middle', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ['foo/bar2', 'foo/bar3'], + // Cycle bar1 -> bar2 -> bar3 -> bar1 + ['foo/bar3', 'foo/bar1'], + ['foo/bar2', 'foo/bar4'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }) + + // Each node visited only once despite cycle + expect(res.deps).equal([ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ['foo/bar2', 'foo/bar3'], + ['foo/bar2', 'foo/bar4'], + ]) + }) + + test('find-deps-linear', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ['foo/bar2', 'foo/bar3'], + ['foo/bar3', 'foo/bar4'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }) + // console.log('RES', res) + + expect(res.deps).equal([ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ['foo/bar2', 'foo/bar3'], + ['foo/bar3', 'foo/bar4'], + ]) + }) + + test('find-deps-duplicate', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + // Duplicate + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + // Duplicate + ['foo/bar1', 'foo/bar2'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }) + + expect(res.deps).equal([ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ]) + }) + + test('find-deps-convergent', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar3'], + // bar3 reachable from two paths + ['foo/bar2', 'foo/bar3'], + ['foo/bar3', 'foo/bar4'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }) + // console.log('RES', res) + + // bar3 should only appear once (first path wins) + expect(res.deps).equal([ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar3'], + ['foo/bar3', 'foo/bar4'], + ]) + }) + + test('find-deps-two-convergent', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar0', 'foo/bar3'], + ['foo/bar1', 'foo/bar4'], + // bar4 from two parents + ['foo/bar2', 'foo/bar4'], + ['foo/bar3', 'foo/bar5'], + ['foo/bar4', 'foo/bar6'], + // bar6 from two parents at different levels + ['foo/bar5', 'foo/bar6'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }) + + expect(res.deps).equal([ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar0', 'foo/bar3'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar3', 'foo/bar5'], + ['foo/bar4', 'foo/bar6'], + ]) + }) + + test('find-deps-self-ref', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + // Self loop + ['foo/bar1', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }) + // console.log('RES', res) + + expect(res.deps).equal([ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ]) + }) + + test('find-deps-all-default', async () => { + const seneca = makeSeneca().use(Traverse) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps') + // console.log('RES', res) + + expect(res.deps).equal([]) + }) + + test('find-deps-empty-list', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar0', + }) + // console.log('RES', res) + + expect(res.deps).equal([]) + }) + + test('find-deps-l1', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar1', + }) + + expect(res.deps).equal([ + // Level 0 + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + + // Level 1 + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + + // Level 2 + ['foo/bar7', 'foo/bar11'], + ]) + }) + + test('find-deps-l1-convergent', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ['foo/bar4', 'foo/bar12'], + // bar12 converges from bar4 and bar5 + ['foo/bar5', 'foo/bar12'], + ['foo/bar12', 'foo/bar13'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar1', + }) + + expect(res.deps).equal([ + // Level 0 + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + + // Level 1 + ['foo/bar4', 'foo/bar7'], + ['foo/bar4', 'foo/bar12'], + ['foo/bar5', 'foo/bar8'], + + // Level 2 + ['foo/bar7', 'foo/bar11'], + ['foo/bar12', 'foo/bar13'], + ]) + }) + + test('find-deps-l1-cycle', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + // Cycle: bar1 -> bar4 -> bar7 -> bar1 + ['foo/bar7', 'foo/bar1'], + ['foo/bar8', 'foo/bar12'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar1', + }) + + expect(res.deps).equal([ + // Level 0 + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + + // Level 1 + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + + // Level 2 + ['foo/bar7', 'foo/bar11'], + ['foo/bar8', 'foo/bar12'], + ]) + }) + + test('find-deps-l2', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar3', + }) + // console.log('RES', res) + + expect(res.deps).equal([ + // Level 0 + ['foo/bar3', 'foo/bar6'], + + // Level 1 + ['foo/bar6', 'foo/bar10'], + ]) + }) + + test('find-deps-l2-convergent', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ['foo/bar3', 'foo/bar12'], + ['foo/bar6', 'foo/bar13'], + ['foo/bar10', 'foo/bar14'], + // bar14 converges from bar10 and bar12 + ['foo/bar12', 'foo/bar14'], + ['foo/bar14', 'foo/bar15'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar3', + }) + + expect(res.deps).equal([ + // Level 0 + ['foo/bar3', 'foo/bar6'], + ['foo/bar3', 'foo/bar12'], + + // Level 1 + ['foo/bar6', 'foo/bar10'], + ['foo/bar6', 'foo/bar13'], + ['foo/bar12', 'foo/bar14'], + + // Level 2 + ['foo/bar14', 'foo/bar15'], + ]) + }) + + test('find-deps-l2-cycle', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ['foo/bar10', 'foo/bar12'], + // Cycle: bar3 -> bar6 -> bar10 -> bar12 -> bar3 + ['foo/bar12', 'foo/bar3'], + ['foo/bar10', 'foo/bar13'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar3', + }) + + expect(res.deps).equal([ + // Level 0 + ['foo/bar3', 'foo/bar6'], + + // Level 1 + ['foo/bar6', 'foo/bar10'], + + // Level 2 + ['foo/bar10', 'foo/bar12'], + ['foo/bar10', 'foo/bar13'], + ]) + }) + + test('find-deps-l2-multi-level-convergent', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar2', 'foo/bar3'], + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/bar0', 'foo/zed0'], + ['foo/zed0', 'foo/zed1'], + ['foo/zed1', 'foo/zed2'], + ['bar/baz0', 'bar/baz1'], + ['qux/test', 'qux/prod'], + ['foo/bar2', 'foo/bar9'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ['foo/bar3', 'foo/bar12'], + ['foo/bar3', 'foo/bar13'], + ['foo/bar12', 'foo/bar14'], + // Second path to bar14 + ['foo/bar13', 'foo/bar14'], + // Third path to bar14 + ['foo/bar6', 'foo/bar14'], + ['foo/bar14', 'foo/bar15'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo/bar3', + }) + + expect(res.deps).equal([ + // Level 0 + ['foo/bar3', 'foo/bar6'], + ['foo/bar3', 'foo/bar12'], + ['foo/bar3', 'foo/bar13'], + + // Level 1 + ['foo/bar6', 'foo/bar10'], + ['foo/bar12', 'foo/bar14'], + + // Level 2 + ['foo/bar14', 'foo/bar15'], + ]) + }) + + test('find-deps-single-cycle', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['A', 'B'], + ['A', 'C'], + ['C', 'E'], + ['C', 'D'], + ['E', 'G'], + ['E', 'F'], + ['F', 'H'], + ['C', 'A'], + ['N', 'M'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'C', + }) + + expect(res.deps).equal([ + // Level 0 + ['C', 'A'], + ['C', 'D'], + ['C', 'E'], + + // Level 1 + ['A', 'B'], + ['E', 'F'], + ['E', 'G'], + + // Level 2 + ['F', 'H'], + ]) + }) + + test('find-deps-single-missing-node', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['A', 'B'], + ['B', 'C'], + ['D', 'E'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'X', + }) + + // X has no children + expect(res.deps).equal([]) + }) + + test('find-deps-deep-linear-chain', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['node0', 'node1'], + ['node1', 'node2'], + ['node2', 'node3'], + ['node3', 'node4'], + ['node4', 'node5'], + ['node5', 'node6'], + ['node6', 'node7'], + ['node7', 'node8'], + ['node8', 'node9'], + ['node9', 'node10'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'node0', + }) + + expect(res.deps).equal([ + ['node0', 'node1'], + ['node1', 'node2'], + ['node2', 'node3'], + ['node3', 'node4'], + ['node4', 'node5'], + ['node5', 'node6'], + ['node6', 'node7'], + ['node7', 'node8'], + ['node8', 'node9'], + ['node9', 'node10'], + ]) + }) + + test('find-deps-binary-tree', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['A', 'B'], + ['A', 'C'], + ['B', 'D'], + ['B', 'E'], + ['C', 'F'], + ['C', 'G'], + ['D', 'H'], + ['D', 'I'], + ['E', 'J'], + ['E', 'K'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'A', + }) + + expect(res.deps).equal([ + // Level 0 + ['A', 'B'], + ['A', 'C'], + + // Level 1 + ['B', 'D'], + ['B', 'E'], + ['C', 'F'], + ['C', 'G'], + + // Level 2 + ['D', 'H'], + ['D', 'I'], + ['E', 'J'], + ['E', 'K'], + ]) + }) + + test('find-deps-diamond-pattern', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['A', 'B'], + ['A', 'C'], + ['B', 'D'], + ['C', 'D'], + ['D', 'E'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'A', + }) + + // D is reached first from B (alphabetically first) + expect(res.deps).equal([ + // Level 0 + ['A', 'B'], + ['A', 'C'], + + // Level 1 - D reached via B + ['B', 'D'], + + // Level 2 + ['D', 'E'], + ]) + }) + + test('find-deps-mixed-alph-sort', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['root', 'node10'], + ['root', 'node2'], + ['root', 'node1'], + ['root', 'node20'], + ['node1', 'child2'], + ['node2', 'child1'], + ['node10', 'child10'], + ['node20', 'child20'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'root', + }) + + // Natural sorting: node1, node2, node10, node20 + expect(res.deps).equal([ + // Level 0 + ['root', 'node1'], + ['root', 'node2'], + ['root', 'node10'], + ['root', 'node20'], + + // Level 1 + ['node1', 'child2'], + ['node2', 'child1'], + ['node10', 'child10'], + ['node20', 'child20'], + ]) + }) + + test('find-deps-all-nodes-converge-to-one', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['root', 'A'], + ['root', 'B'], + ['root', 'C'], + ['A', 'target'], + ['B', 'target'], + ['C', 'target'], + ['target', 'end'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'root', + }) + + // target reached first via A (alphabetically first) + expect(res.deps).equal([ + // Level 0 + ['root', 'A'], + ['root', 'B'], + ['root', 'C'], + + // Level 1 - target via A + ['A', 'target'], + + // Level 2 + ['target', 'end'], + ]) + }) + + test('find-deps-deep-10-levels-complex', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + // ========== LEVEL 0 → LEVEL 1 ========== + ['foo', 'bar'], + ['foo', 'zed'], + ['foo', 'qux'], + + // ========== LEVEL 1 → LEVEL 2 ========== + ['bar', 'bar1'], + ['bar', 'bar2'], + ['zed', 'zed1'], + ['zed', 'zed2'], + ['qux', 'qux1'], + + // ========== LEVEL 2 → LEVEL 3 ========== + ['bar1', 'bar3'], + ['bar1', 'bar4'], + ['bar2', 'bar5'], + ['zed1', 'zed3'], + ['zed1', 'zed4'], + ['zed2', 'zed5'], + ['qux1', 'qux2'], + + // ========== LEVEL 3 → LEVEL 4 ========== + ['bar3', 'bar6'], + ['bar3', 'bar7'], + ['bar4', 'bar8'], + ['bar5', 'bar9'], + ['zed3', 'zed6'], + ['zed4', 'zed7'], + ['zed5', 'zed8'], + ['qux2', 'qux3'], + + // ========== LEVEL 4 → LEVEL 5 ========== + ['bar6', 'bar10'], + ['bar7', 'bar11'], + ['bar8', 'bar12'], + ['bar9', 'bar13'], + ['zed6', 'zed9'], + ['zed7', 'zed10'], + ['zed8', 'zed11'], + ['qux3', 'qux4'], + + // ========== LEVEL 5 → LEVEL 6 (switch to composed names) ========== + ['bar10', 'foo/bar1'], + ['bar10', 'foo/bar2'], + ['bar11', 'foo/bar3'], + ['bar12', 'foo/bar4'], + ['bar13', 'foo/bar5'], + ['zed9', 'foo/zed1'], + ['zed10', 'foo/zed2'], + ['zed11', 'foo/zed3'], + // Cycle: qux4 → foo (already visited at level 0) + ['qux4', 'foo'], + ['qux4', 'foo/qux1'], + + // ========== LEVEL 6 → LEVEL 7 ========== + ['foo/bar1', 'foo/bar6'], + ['foo/bar1', 'foo/bar7'], + ['foo/bar2', 'foo/bar8'], + ['foo/bar3', 'foo/bar9'], + ['foo/bar4', 'foo/bar10'], + ['foo/bar5', 'foo/bar11'], + ['foo/zed1', 'foo/zed4'], + ['foo/zed2', 'foo/zed5'], + ['foo/zed3', 'foo/zed6'], + ['foo/qux1', 'foo/qux2'], + + // ========== LEVEL 7 → LEVEL 8 ========== + ['foo/bar6', 'bar/foo1'], + ['foo/bar7', 'bar/foo2'], + ['foo/bar8', 'bar/foo3'], + ['foo/bar9', 'bar/foo4'], + ['foo/bar10', 'bar/foo5'], + ['foo/bar11', 'bar/foo6'], + ['foo/zed4', 'zed/foo1'], + ['foo/zed5', 'zed/foo2'], + // Convergent: foo/zed6 and foo/qux2 both point to zed/foo3 + ['foo/zed6', 'zed/foo3'], + ['foo/qux2', 'zed/foo3'], + + // ========== LEVEL 8 → LEVEL 9 ========== + ['bar/foo1', 'bar/foo7'], + ['bar/foo2', 'bar/foo8'], + ['bar/foo3', 'bar/foo9'], + ['bar/foo4', 'bar/foo10'], + ['bar/foo5', 'bar/foo11'], + ['bar/foo6', 'bar/foo12'], + ['zed/foo1', 'zed/foo4'], + ['zed/foo2', 'zed/foo5'], + ['zed/foo3', 'zed/foo6'], + + // ========== LEVEL 9 → LEVEL 10 ========== + ['bar/foo7', 'qux/bar1'], + ['bar/foo8', 'qux/bar2'], + ['bar/foo9', 'qux/bar3'], + ['bar/foo10', 'qux/bar4'], + ['bar/foo11', 'qux/bar5'], + ['bar/foo12', 'qux/bar6'], + ['zed/foo4', 'qux/zed1'], + ['zed/foo5', 'qux/zed2'], + ['zed/foo6', 'qux/zed3'], + + // ========== LEVEL 10 → LEVEL 11 ========== + ['qux/bar1', 'qux/bar7'], + ['qux/bar2', 'qux/bar8'], + ['qux/bar3', 'qux/bar9'], + ['qux/bar4', 'qux/bar10'], + ['qux/bar5', 'qux/bar11'], + ['qux/bar6', 'qux/bar12'], + ['qux/zed1', 'qux/zed4'], + ['qux/zed2', 'qux/zed5'], + ['qux/zed3', 'qux/zed6'], + ], + }, + }) + await seneca.ready() + + const res = await seneca.post('sys:traverse,find:deps', { + rootEntity: 'foo', + }) + + expect(res.deps).equal([ + // ========== LEVEL 0 ========== + ['foo', 'bar'], + ['foo', 'qux'], + ['foo', 'zed'], + + // ========== LEVEL 1 ========== + ['bar', 'bar1'], + ['bar', 'bar2'], + ['qux', 'qux1'], + ['zed', 'zed1'], + ['zed', 'zed2'], + + // ========== LEVEL 2 ========== + ['bar1', 'bar3'], + ['bar1', 'bar4'], + ['bar2', 'bar5'], + ['qux1', 'qux2'], + ['zed1', 'zed3'], + ['zed1', 'zed4'], + ['zed2', 'zed5'], + + // ========== LEVEL 3 ========== + ['bar3', 'bar6'], + ['bar3', 'bar7'], + ['bar4', 'bar8'], + ['bar5', 'bar9'], + ['qux2', 'qux3'], + ['zed3', 'zed6'], + ['zed4', 'zed7'], + ['zed5', 'zed8'], + + // ========== LEVEL 4 ========== + ['bar6', 'bar10'], + ['bar7', 'bar11'], + ['bar8', 'bar12'], + ['bar9', 'bar13'], + ['qux3', 'qux4'], + ['zed6', 'zed9'], + ['zed7', 'zed10'], + ['zed8', 'zed11'], + + // ========== LEVEL 5 ========== + ['bar10', 'foo/bar1'], + ['bar10', 'foo/bar2'], + ['bar11', 'foo/bar3'], + ['bar12', 'foo/bar4'], + ['bar13', 'foo/bar5'], + // Note: qux4 → foo creates cycle (foo visited at level 0) + ['qux4', 'foo/qux1'], + ['zed9', 'foo/zed1'], + ['zed10', 'foo/zed2'], + ['zed11', 'foo/zed3'], + + // ========== LEVEL 6 ========== + ['foo/bar1', 'foo/bar6'], + ['foo/bar1', 'foo/bar7'], + ['foo/bar2', 'foo/bar8'], + ['foo/bar3', 'foo/bar9'], + ['foo/bar4', 'foo/bar10'], + ['foo/bar5', 'foo/bar11'], + ['foo/qux1', 'foo/qux2'], + ['foo/zed1', 'foo/zed4'], + ['foo/zed2', 'foo/zed5'], + ['foo/zed3', 'foo/zed6'], + + // ========== LEVEL 7 ========== + ['foo/bar6', 'bar/foo1'], + ['foo/bar7', 'bar/foo2'], + ['foo/bar8', 'bar/foo3'], + ['foo/bar9', 'bar/foo4'], + ['foo/bar10', 'bar/foo5'], + ['foo/bar11', 'bar/foo6'], + ['foo/qux2', 'zed/foo3'], + ['foo/zed4', 'zed/foo1'], + ['foo/zed5', 'zed/foo2'], + // Note: foo/zed6 → zed/foo3 is skipped because zed/foo3 was already visited via foo/qux2 + + // ========== LEVEL 8 ========== + ['bar/foo1', 'bar/foo7'], + ['bar/foo2', 'bar/foo8'], + ['bar/foo3', 'bar/foo9'], + ['bar/foo4', 'bar/foo10'], + ['bar/foo5', 'bar/foo11'], + ['bar/foo6', 'bar/foo12'], + ['zed/foo1', 'zed/foo4'], + ['zed/foo2', 'zed/foo5'], + ['zed/foo3', 'zed/foo6'], + + // ========== LEVEL 9 ========== + ['bar/foo7', 'qux/bar1'], + ['bar/foo8', 'qux/bar2'], + ['bar/foo9', 'qux/bar3'], + ['bar/foo10', 'qux/bar4'], + ['bar/foo11', 'qux/bar5'], + ['bar/foo12', 'qux/bar6'], + ['zed/foo4', 'qux/zed1'], + ['zed/foo5', 'qux/zed2'], + ['zed/foo6', 'qux/zed3'], + + // ========== LEVEL 10 ========== + ['qux/bar1', 'qux/bar7'], + ['qux/bar2', 'qux/bar8'], + ['qux/bar3', 'qux/bar9'], + ['qux/bar4', 'qux/bar10'], + ['qux/bar5', 'qux/bar11'], + ['qux/bar6', 'qux/bar12'], + ['qux/zed1', 'qux/zed4'], + ['qux/zed2', 'qux/zed5'], + ['qux/zed3', 'qux/zed6'], + ]) + }) + + test('find-children', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar0', 'foo/zed0'], + ['foo/bar1', 'foo/bar4'], + ['foo/bar1', 'foo/bar5'], + ['foo/bar2', 'foo/bar3'], + ['foo/bar2', 'foo/bar9'], + ['foo/zed0', 'foo/zed1'], + ['foo/bar3', 'foo/bar6'], + ['foo/bar4', 'foo/bar7'], + ['foo/bar5', 'foo/bar8'], + ['foo/zed1', 'foo/zed2'], + ['foo/bar6', 'foo/bar10'], + ['foo/bar7', 'foo/bar11'], + ], + }, + }) + await seneca.ready() + + const rootEntityId = '123' + + // Level 1: Direct children of bar0 + const bar1Ent = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }) + + const bar2Ent = await seneca.entity('foo/bar2').save$({ + bar0_id: rootEntityId, + }) + + const zed0Ent = await seneca.entity('foo/zed0').save$({ + bar0_id: rootEntityId, + }) + + // Level 2: Children of bar1 + const bar4Ent = await seneca.entity('foo/bar4').save$({ + bar1_id: bar1Ent.id, + }) + + const bar5Ent = await seneca.entity('foo/bar5').save$({ + bar1_id: bar1Ent.id, + }) + + // Level 2: Children of bar2 + const bar3Ent = await seneca.entity('foo/bar3').save$({ + bar2_id: bar2Ent.id, + }) + + const bar9Ent = await seneca.entity('foo/bar9').save$({ + bar2_id: bar2Ent.id, + }) + + // Level 2: Children of zed0 + const zed1Ent = await seneca.entity('foo/zed1').save$({ + zed0_id: zed0Ent.id, + }) + + // Level 3: Children of bar3 + const bar6Ent = await seneca.entity('foo/bar6').save$({ + bar3_id: bar3Ent.id, + }) + + // Level 3: Children of bar4 + const bar7Ent = await seneca.entity('foo/bar7').save$({ + bar4_id: bar4Ent.id, + }) + + // Level 3: Children of bar5 + const bar8Ent = await seneca.entity('foo/bar8').save$({ + bar5_id: bar5Ent.id, + }) + + // Level 3: Children of zed1 + const zed2Ent = await seneca.entity('foo/zed2').save$({ + zed1_id: zed1Ent.id, + }) + + // Level 4: Children of bar6 + const bar10Ent = await seneca.entity('foo/bar10').save$({ + bar6_id: bar6Ent.id, + }) + + // Level 4: Children of bar7 + const bar11Ent = await seneca.entity('foo/bar11').save$({ + bar7_id: bar7Ent.id, + }) + + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }) + + expect(res.children).equal([ + // Level 1 + { + parent_id: rootEntityId, + child_id: bar1Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: rootEntityId, + child_id: bar2Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar2', + }, + { + parent_id: rootEntityId, + child_id: zed0Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/zed0', + }, + // Level 2 + { + parent_id: bar1Ent.id, + child_id: bar4Ent.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar4', + }, + { + parent_id: bar1Ent.id, + child_id: bar5Ent.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar5', + }, + { + parent_id: bar2Ent.id, + child_id: bar3Ent.id, + parent_canon: 'foo/bar2', + child_canon: 'foo/bar3', + }, + { + parent_id: bar2Ent.id, + child_id: bar9Ent.id, + parent_canon: 'foo/bar2', + child_canon: 'foo/bar9', + }, + { + parent_id: zed0Ent.id, + child_id: zed1Ent.id, + parent_canon: 'foo/zed0', + child_canon: 'foo/zed1', + }, + // Level 3 + { + parent_id: bar3Ent.id, + child_id: bar6Ent.id, + parent_canon: 'foo/bar3', + child_canon: 'foo/bar6', + }, + { + parent_id: bar4Ent.id, + child_id: bar7Ent.id, + parent_canon: 'foo/bar4', + child_canon: 'foo/bar7', + }, + { + parent_id: bar5Ent.id, + child_id: bar8Ent.id, + parent_canon: 'foo/bar5', + child_canon: 'foo/bar8', + }, + { + parent_id: zed1Ent.id, + child_id: zed2Ent.id, + parent_canon: 'foo/zed1', + child_canon: 'foo/zed2', + }, + // Level 4 + { + parent_id: bar6Ent.id, + child_id: bar10Ent.id, + parent_canon: 'foo/bar6', + child_canon: 'foo/bar10', + }, + { + parent_id: bar7Ent.id, + child_id: bar11Ent.id, + parent_canon: 'foo/bar7', + child_canon: 'foo/bar11', + }, + ]) + }) + + test('find-children-empty-relations', async () => { + const seneca = makeSeneca().use(Traverse) + await seneca.ready() + + const rootEntityId = '123' + + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }) + + expect(res.children).equal([]) + }) + + test('find-children-no-matching-entities', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ], + }, + }) + await seneca.ready() + + const rootEntityId = '123' + + // Missing entities on data storage + + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }) + + expect(res.children).equal([]) + }) + + test('find-children-partial-tree', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar3'], + ['foo/bar1', 'foo/bar4'], + ], + }, + }) + await seneca.ready() + + const rootEntityId = '123' + + // Only create bar1 and bar3, not bar2 or bar4 + const bar1Ent = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }) + + const bar3Ent = await seneca.entity('foo/bar3').save$({ + bar1_id: bar1Ent.id, + }) + + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }) + + // Should only return entities that exist in the data storage + expect(res.children).equal([ + { + parent_id: rootEntityId, + child_id: bar1Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: bar1Ent.id, + child_id: bar3Ent.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar3', + }, + ]) + }) + + test('find-children-default-root-entity', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['sys/user', 'user/settings'], + ['sys/user', 'user/project'], + ['user/project', 'project/release'], + ], + }, + }) + await seneca.ready() + + const rootEntityId = 'user-456' + + const settingsEnt = await seneca.entity('user/settings').save$({ + user_id: rootEntityId, + }) + + const projectEnt = await seneca.entity('user/project').save$({ + user_id: rootEntityId, + }) + + const releaseEnt = await seneca.entity('project/release').save$({ + project_id: projectEnt.id, + }) + + const res = await seneca.post('sys:traverse,find:children', { + // rootEntity omitted - should default to 'sys/user' + rootEntityId: rootEntityId, + }) + + expect(res.children).equal([ + { + parent_id: rootEntityId, + child_id: projectEnt.id, + parent_canon: 'sys/user', + child_canon: 'user/project', + }, + { + parent_id: rootEntityId, + child_id: settingsEnt.id, + parent_canon: 'sys/user', + child_canon: 'user/settings', + }, + { + parent_id: projectEnt.id, + child_id: releaseEnt.id, + parent_canon: 'user/project', + child_canon: 'project/release', + }, + ]) + }) + + test('find-children-avoid-wrong-children', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar3'], + ], + }, + }) + await seneca.ready() + + const rootEntityId = '123' + + const bar1Ent = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }) + + const bar2Ent = await seneca.entity('foo/bar2').save$({ + bar0_id: rootEntityId, + }) + + // Create bar3 entities but with another parent_id + await seneca.entity('foo/bar3').save$({ + bar1_id: '456', + }) + + await seneca.entity('foo/bar3').save$({ + bar1_id: '789', + }) + + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }) + + // Should not include other parent children + expect(res.children).equal([ + { + parent_id: rootEntityId, + child_id: bar1Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: rootEntityId, + child_id: bar2Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar2', + }, + ]) + }) + + test('find-children-single-entity-tree', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [['foo/bar0', 'foo/bar1']], + }, + }) + await seneca.ready() + + const rootEntityId = '123' + + const bar1Ent = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }) + + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }) + + expect(res.children).equal([ + { + parent_id: rootEntityId, + child_id: bar1Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + ]) + }) + + test('find-children-deep-linear-chain', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ['foo/bar2', 'foo/bar3'], + ['foo/bar3', 'foo/bar4'], + ['foo/bar4', 'foo/bar5'], + ], + }, + }) + await seneca.ready() + + const rootEntityId = '123' + + const bar1Ent = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }) + + const bar2Ent = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent.id, + }) + + const bar3Ent = await seneca.entity('foo/bar3').save$({ + bar2_id: bar2Ent.id, + }) + + const bar4Ent = await seneca.entity('foo/bar4').save$({ + bar3_id: bar3Ent.id, + }) + + const bar5Ent = await seneca.entity('foo/bar5').save$({ + bar4_id: bar4Ent.id, + }) + + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }) + + expect(res.children).equal([ + { + parent_id: rootEntityId, + child_id: bar1Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: bar1Ent.id, + child_id: bar2Ent.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + { + parent_id: bar2Ent.id, + child_id: bar3Ent.id, + parent_canon: 'foo/bar2', + child_canon: 'foo/bar3', + }, + { + parent_id: bar3Ent.id, + child_id: bar4Ent.id, + parent_canon: 'foo/bar3', + child_canon: 'foo/bar4', + }, + { + parent_id: bar4Ent.id, + child_id: bar5Ent.id, + parent_canon: 'foo/bar4', + child_canon: 'foo/bar5', + }, + ]) + }) + + test('find-children-custom-key', async () => { + const seneca = makeSeneca().use(Traverse, { + customRef: { + 'foo/bar2': 'custom0_id', + 'foo/bar3': 'custom1_test', + }, + + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar0', 'foo/bar2'], + ['foo/bar1', 'foo/bar3'], + ], + }, + }) + await seneca.ready() + + const rootEntityId = '123' + + const bar1Ent = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }) + + const bar2Ent = await seneca.entity('foo/bar2').save$({ + custom0_id: rootEntityId, + }) + + const bar3Ent = await seneca.entity('foo/bar3').save$({ + custom1_test: bar1Ent.id, + }) + + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }) + + expect(res.children).equal([ + { + parent_id: rootEntityId, + child_id: bar1Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: rootEntityId, + child_id: bar2Ent.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar2', + }, + { + parent_id: bar1Ent.id, + child_id: bar3Ent.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar3', + }, + ]) + }) + + test('find-children-multi-inst', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ], + }, + }) + await seneca.ready() + + const rootEntityId = '123' + + const bar1Ent1 = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }) + + const bar1Ent2 = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }) + + const bar1Ent3 = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }) + + const bar2Ent1 = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent1.id, + }) + + const bar2Ent2 = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent2.id, + }) + + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }) + + expect(res.children).equal([ + { + parent_id: rootEntityId, + child_id: bar1Ent1.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: rootEntityId, + child_id: bar1Ent2.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: rootEntityId, + child_id: bar1Ent3.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: bar1Ent1.id, + child_id: bar2Ent1.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + { + parent_id: bar1Ent2.id, + child_id: bar2Ent2.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + ]) + }) + + test('find-children-multiple-inst-multi-levels', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['foo/bar0', 'foo/bar1'], + ['foo/bar1', 'foo/bar2'], + ['foo/bar2', 'foo/bar3'], + ], + }, + }) + await seneca.ready() + + const rootEntityId = '123' + + const bar1Ent1 = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }) + + const bar1Ent2 = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }) + + const bar1Ent3 = await seneca.entity('foo/bar1').save$({ + bar0_id: rootEntityId, + }) + + const bar2Ent1_1 = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent1.id, + }) + + const bar2Ent1_2 = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent1.id, + }) + + const bar2Ent2_1 = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent2.id, + }) + + const bar2Ent2_2 = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent2.id, + }) + + const bar2Ent3_1 = await seneca.entity('foo/bar2').save$({ + bar1_id: bar1Ent3.id, + }) + + const bar3Ent1_1_1 = await seneca.entity('foo/bar3').save$({ + bar2_id: bar2Ent1_1.id, + }) + + const bar3Ent1_1_2 = await seneca.entity('foo/bar3').save$({ + bar2_id: bar2Ent1_1.id, + }) + + const bar3Ent2_2_1 = await seneca.entity('foo/bar3').save$({ + bar2_id: bar2Ent2_2.id, + }) + + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'foo/bar0', + rootEntityId: rootEntityId, + }) + + expect(res.children).equal([ + // Level 1: All bar1 children of bar0 + { + parent_id: rootEntityId, + child_id: bar1Ent1.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: rootEntityId, + child_id: bar1Ent2.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + { + parent_id: rootEntityId, + child_id: bar1Ent3.id, + parent_canon: 'foo/bar0', + child_canon: 'foo/bar1', + }, + + // Level 2: All bar2 children of bar1Ent1 + { + parent_id: bar1Ent1.id, + child_id: bar2Ent1_1.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + { + parent_id: bar1Ent1.id, + child_id: bar2Ent1_2.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + + // Level 2: All bar2 children of bar1Ent2 + { + parent_id: bar1Ent2.id, + child_id: bar2Ent2_1.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + { + parent_id: bar1Ent2.id, + child_id: bar2Ent2_2.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + + // Level 2: All bar2 children of bar1Ent3 + { + parent_id: bar1Ent3.id, + child_id: bar2Ent3_1.id, + parent_canon: 'foo/bar1', + child_canon: 'foo/bar2', + }, + + // Level 3: All bar3 children of bar2Ent1_1 + { + parent_id: bar2Ent1_1.id, + child_id: bar3Ent1_1_1.id, + parent_canon: 'foo/bar2', + child_canon: 'foo/bar3', + }, + { + parent_id: bar2Ent1_1.id, + child_id: bar3Ent1_1_2.id, + parent_canon: 'foo/bar2', + child_canon: 'foo/bar3', + }, + + // Level 3: All bar3 children of bar2Ent2_2 + { + parent_id: bar2Ent2_2.id, + child_id: bar3Ent2_2_1.id, + parent_canon: 'foo/bar2', + child_canon: 'foo/bar3', + }, + ]) + }) + + test('find-children-single-cycle', async () => { + const seneca = makeSeneca().use(Traverse, { + relations: { + parental: [ + ['A', 'B'], + ['A', 'C'], + ['C', 'E'], + ['C', 'D'], + ['E', 'G'], + ['E', 'F'], + ['F', 'H'], + ['C', 'A'], + ['N', 'M'], + ], + }, + }) + await seneca.ready() + + let rootEntityId = '123' + + const aEnt = await seneca.entity('A').save$({ + C_id: rootEntityId, + }) + + const cEnt = await seneca.entity('C').save$({ + id: rootEntityId, + A_id: aEnt.id, + }) + + const bEnt = await seneca.entity('B').save$({ + A_id: aEnt.id, + }) + + const dEnt = await seneca.entity('D').save$({ + C_id: cEnt.id, + }) + + const eEnt = await seneca.entity('E').save$({ + C_id: cEnt.id, + }) + + const fEnt = await seneca.entity('F').save$({ + E_id: eEnt.id, + }) + + const gEnt = await seneca.entity('G').save$({ + E_id: eEnt.id, + }) + + const hEnt = await seneca.entity('H').save$({ + F_id: fEnt.id, + }) + + const nEnt = await seneca.entity('N').save$() + + await seneca.entity('M').save$({ + N_id: nEnt.id, + }) + + const res = await seneca.post('sys:traverse,find:children', { + rootEntity: 'C', + rootEntityId: cEnt.id, + }) + + // Should traverse from C, + // including C->A but NOT A->C + expect(res.children).equal([ + { + parent_id: rootEntityId, + child_id: aEnt.id, + parent_canon: 'C', + child_canon: 'A', + }, + { + parent_id: rootEntityId, + child_id: dEnt.id, + parent_canon: 'C', + child_canon: 'D', + }, + { + parent_id: rootEntityId, + child_id: eEnt.id, + parent_canon: 'C', + child_canon: 'E', + }, + { + parent_id: aEnt.id, + child_id: bEnt.id, + parent_canon: 'A', + child_canon: 'B', + }, + { + parent_id: eEnt.id, + child_id: fEnt.id, + parent_canon: 'E', + child_canon: 'F', + }, + { + parent_id: eEnt.id, + child_id: gEnt.id, + parent_canon: 'E', + child_canon: 'G', + }, + { + parent_id: fEnt.id, + child_id: hEnt.id, + parent_canon: 'F', + child_canon: 'H', + }, + ]) + }) }) + +function makeSeneca(opts: any = {}) { + const seneca = Seneca({ legacy: false }).test().use('promisify').use('entity') + return seneca +}