@voxgig/sdkgen 0.35.2 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/cmp/Main.js +0 -12
- package/dist/cmp/Main.js.map +1 -1
- package/dist/cmp/Readme.js +5 -5
- package/dist/cmp/Readme.js.map +1 -1
- package/dist/cmp/ReadmeEntity.js +74 -24
- package/dist/cmp/ReadmeEntity.js.map +1 -1
- package/dist/cmp/ReadmeExplanation.d.ts +2 -0
- package/dist/cmp/ReadmeExplanation.js +308 -0
- package/dist/cmp/ReadmeExplanation.js.map +1 -0
- package/dist/cmp/ReadmeHowto.d.ts +2 -0
- package/dist/cmp/ReadmeHowto.js +18 -0
- package/dist/cmp/ReadmeHowto.js.map +1 -0
- package/dist/cmp/ReadmeIntro.js +8 -23
- package/dist/cmp/ReadmeIntro.js.map +1 -1
- package/dist/cmp/ReadmeModel.js +55 -91
- package/dist/cmp/ReadmeModel.js.map +1 -1
- package/dist/cmp/ReadmeOptions.js +35 -11
- package/dist/cmp/ReadmeOptions.js.map +1 -1
- package/dist/cmp/ReadmeQuick.js +4 -1
- package/dist/cmp/ReadmeQuick.js.map +1 -1
- package/dist/cmp/ReadmeRef.js +1042 -40
- package/dist/cmp/ReadmeRef.js.map +1 -1
- package/dist/cmp/ReadmeTop.d.ts +2 -0
- package/dist/cmp/ReadmeTop.js +171 -0
- package/dist/cmp/ReadmeTop.js.map +1 -0
- package/dist/sdkgen.d.ts +7 -1
- package/dist/sdkgen.js +13 -1
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/project/.sdk/model/feature/feature-index.jsonic +5 -0
- package/project/.sdk/model/feature/log.jsonic +5 -1
- package/project/.sdk/model/feature/test.jsonic +1 -1
- package/project/.sdk/model/target/lua.jsonic +23 -0
- package/project/.sdk/model/target/php.jsonic +22 -0
- package/project/.sdk/model/target/py.jsonic +23 -0
- package/project/.sdk/model/target/rb.jsonic +23 -0
- package/project/.sdk/src/cmp/go/Config_go.ts +6 -2
- package/project/.sdk/src/cmp/go/Main_go.ts +2 -6
- package/project/.sdk/src/cmp/go/ReadmeExplanation_go.ts +42 -0
- package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +112 -0
- package/project/.sdk/src/cmp/go/ReadmeInstall_go.ts +29 -0
- package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +129 -0
- package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +163 -0
- package/project/.sdk/src/cmp/go/ReadmeTopHowto_go.ts +24 -0
- package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +67 -0
- package/project/.sdk/src/cmp/go/ReadmeTopTest_go.ts +40 -0
- package/project/.sdk/src/cmp/go/TestDirect_go.ts +46 -2
- package/project/.sdk/src/cmp/go/TestEntity_go.ts +360 -160
- package/project/.sdk/src/cmp/go/Test_go.ts +24 -1
- package/project/.sdk/src/cmp/js/EntityBase_js.ts +34 -0
- package/project/.sdk/src/cmp/js/Main_js.ts +10 -0
- package/project/.sdk/src/cmp/js/ReadmeExplanation_js.ts +33 -0
- package/project/.sdk/src/cmp/js/ReadmeHowto_js.ts +123 -0
- package/project/.sdk/src/cmp/js/ReadmeModel_js.ts +152 -0
- package/project/.sdk/src/cmp/js/ReadmeQuick_js.ts +1 -1
- package/project/.sdk/src/cmp/js/ReadmeTopHowto_js.ts +25 -0
- package/project/.sdk/src/cmp/js/ReadmeTopQuick_js.ts +65 -0
- package/project/.sdk/src/cmp/js/ReadmeTopTest_js.ts +36 -0
- package/project/.sdk/src/cmp/js/TestDirect_js.ts +53 -5
- package/project/.sdk/src/cmp/js/TestEntity_js.ts +114 -20
- package/project/.sdk/src/cmp/js/fragment/Entity.fragment.js +7 -139
- package/project/.sdk/src/cmp/js/fragment/EntityBase.fragment.js +149 -0
- package/project/.sdk/src/cmp/js/fragment/EntityCreateOp.fragment.js +6 -10
- package/project/.sdk/src/cmp/js/fragment/EntityListOp.fragment.js +6 -10
- package/project/.sdk/src/cmp/js/fragment/EntityLoadOp.fragment.js +7 -11
- package/project/.sdk/src/cmp/js/fragment/EntityRemoveOp.fragment.js +7 -11
- package/project/.sdk/src/cmp/js/fragment/EntityUpdateOp.fragment.js +7 -11
- package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +2 -0
- package/project/.sdk/src/cmp/js/fragment/SdkError.fragment.js +0 -2
- package/project/.sdk/src/cmp/lua/Config_lua.ts +116 -0
- package/project/.sdk/src/cmp/lua/EntityOperation_lua.ts +44 -0
- package/project/.sdk/src/cmp/lua/Entity_lua.ts +62 -0
- package/project/.sdk/src/cmp/lua/MainEntity_lua.ts +23 -0
- package/project/.sdk/src/cmp/lua/Main_lua.ts +133 -0
- package/project/.sdk/src/cmp/lua/Package_lua.ts +80 -0
- package/project/.sdk/src/cmp/lua/ReadmeExplanation_lua.ts +42 -0
- package/project/.sdk/src/cmp/lua/ReadmeHowto_lua.ts +100 -0
- package/project/.sdk/src/cmp/lua/ReadmeInstall_lua.ts +26 -0
- package/project/.sdk/src/cmp/lua/ReadmeModel_lua.ts +129 -0
- package/project/.sdk/src/cmp/lua/ReadmeQuick_lua.ts +99 -0
- package/project/.sdk/src/cmp/lua/ReadmeTopHowto_lua.ts +24 -0
- package/project/.sdk/src/cmp/lua/ReadmeTopQuick_lua.ts +55 -0
- package/project/.sdk/src/cmp/lua/ReadmeTopTest_lua.ts +38 -0
- package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +261 -0
- package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +485 -0
- package/project/.sdk/src/cmp/lua/Test_lua.ts +49 -0
- package/project/.sdk/src/cmp/lua/fragment/Entity.fragment.lua +147 -0
- package/project/.sdk/src/cmp/lua/fragment/EntityCreateOp.fragment.lua +27 -0
- package/project/.sdk/src/cmp/lua/fragment/EntityListOp.fragment.lua +24 -0
- package/project/.sdk/src/cmp/lua/fragment/EntityLoadOp.fragment.lua +30 -0
- package/project/.sdk/src/cmp/lua/fragment/EntityRemoveOp.fragment.lua +30 -0
- package/project/.sdk/src/cmp/lua/fragment/EntityUpdateOp.fragment.lua +30 -0
- package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +256 -0
- package/project/.sdk/src/cmp/lua/fragment/SdkError.fragment.lua +26 -0
- package/project/.sdk/src/cmp/lua/tsconfig.json +15 -0
- package/project/.sdk/src/cmp/lua/utility_lua.ts +84 -0
- package/project/.sdk/src/cmp/php/Config_php.ts +111 -0
- package/project/.sdk/src/cmp/php/EntityOperation_php.ts +44 -0
- package/project/.sdk/src/cmp/php/Entity_php.ts +62 -0
- package/project/.sdk/src/cmp/php/MainEntity_php.ts +24 -0
- package/project/.sdk/src/cmp/php/Main_php.ts +135 -0
- package/project/.sdk/src/cmp/php/Package_php.ts +87 -0
- package/project/.sdk/src/cmp/php/ReadmeExplanation_php.ts +42 -0
- package/project/.sdk/src/cmp/php/ReadmeHowto_php.ts +101 -0
- package/project/.sdk/src/cmp/php/ReadmeInstall_php.ts +19 -0
- package/project/.sdk/src/cmp/php/ReadmeModel_php.ts +129 -0
- package/project/.sdk/src/cmp/php/ReadmeQuick_php.ts +100 -0
- package/project/.sdk/src/cmp/php/ReadmeTopHowto_php.ts +24 -0
- package/project/.sdk/src/cmp/php/ReadmeTopQuick_php.ts +56 -0
- package/project/.sdk/src/cmp/php/ReadmeTopTest_php.ts +38 -0
- package/project/.sdk/src/cmp/php/TestDirect_php.ts +270 -0
- package/project/.sdk/src/cmp/php/TestEntity_php.ts +479 -0
- package/project/.sdk/src/cmp/php/Test_php.ts +56 -0
- package/project/.sdk/src/cmp/php/fragment/Entity.fragment.php +146 -0
- package/project/.sdk/src/cmp/php/fragment/EntityCreateOp.fragment.php +26 -0
- package/project/.sdk/src/cmp/php/fragment/EntityListOp.fragment.php +23 -0
- package/project/.sdk/src/cmp/php/fragment/EntityLoadOp.fragment.php +29 -0
- package/project/.sdk/src/cmp/php/fragment/EntityRemoveOp.fragment.php +29 -0
- package/project/.sdk/src/cmp/php/fragment/EntityUpdateOp.fragment.php +29 -0
- package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +233 -0
- package/project/.sdk/src/cmp/php/fragment/SdkError.fragment.php +24 -0
- package/project/.sdk/src/cmp/php/tsconfig.json +15 -0
- package/project/.sdk/src/cmp/php/utility_php.ts +85 -0
- package/project/.sdk/src/cmp/py/Config_py.ts +97 -0
- package/project/.sdk/src/cmp/py/EntityOperation_py.ts +44 -0
- package/project/.sdk/src/cmp/py/Entity_py.ts +62 -0
- package/project/.sdk/src/cmp/py/MainEntity_py.ts +22 -0
- package/project/.sdk/src/cmp/py/Main_py.ts +156 -0
- package/project/.sdk/src/cmp/py/Package_py.ts +75 -0
- package/project/.sdk/src/cmp/py/ReadmeExplanation_py.ts +41 -0
- package/project/.sdk/src/cmp/py/ReadmeHowto_py.ts +98 -0
- package/project/.sdk/src/cmp/py/ReadmeInstall_py.ts +25 -0
- package/project/.sdk/src/cmp/py/ReadmeModel_py.ts +130 -0
- package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +100 -0
- package/project/.sdk/src/cmp/py/ReadmeTopHowto_py.ts +24 -0
- package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +56 -0
- package/project/.sdk/src/cmp/py/ReadmeTopTest_py.ts +38 -0
- package/project/.sdk/src/cmp/py/TestDirect_py.ts +248 -0
- package/project/.sdk/src/cmp/py/TestEntity_py.ts +477 -0
- package/project/.sdk/src/cmp/py/Test_py.ts +55 -0
- package/project/.sdk/src/cmp/py/fragment/Entity.fragment.py +112 -0
- package/project/.sdk/src/cmp/py/fragment/EntityCreateOp.fragment.py +25 -0
- package/project/.sdk/src/cmp/py/fragment/EntityListOp.fragment.py +22 -0
- package/project/.sdk/src/cmp/py/fragment/EntityLoadOp.fragment.py +27 -0
- package/project/.sdk/src/cmp/py/fragment/EntityRemoveOp.fragment.py +27 -0
- package/project/.sdk/src/cmp/py/fragment/EntityUpdateOp.fragment.py +27 -0
- package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +222 -0
- package/project/.sdk/src/cmp/py/fragment/SdkError.fragment.py +16 -0
- package/project/.sdk/src/cmp/py/tsconfig.json +15 -0
- package/project/.sdk/src/cmp/py/utility_py.ts +84 -0
- package/project/.sdk/src/cmp/rb/Config_rb.ts +105 -0
- package/project/.sdk/src/cmp/rb/EntityOperation_rb.ts +44 -0
- package/project/.sdk/src/cmp/rb/Entity_rb.ts +62 -0
- package/project/.sdk/src/cmp/rb/MainEntity_rb.ts +23 -0
- package/project/.sdk/src/cmp/rb/Main_rb.ts +130 -0
- package/project/.sdk/src/cmp/rb/Package_rb.ts +111 -0
- package/project/.sdk/src/cmp/rb/ReadmeExplanation_rb.ts +42 -0
- package/project/.sdk/src/cmp/rb/ReadmeHowto_rb.ts +98 -0
- package/project/.sdk/src/cmp/rb/ReadmeInstall_rb.ts +31 -0
- package/project/.sdk/src/cmp/rb/ReadmeModel_rb.ts +129 -0
- package/project/.sdk/src/cmp/rb/ReadmeQuick_rb.ts +99 -0
- package/project/.sdk/src/cmp/rb/ReadmeTopHowto_rb.ts +24 -0
- package/project/.sdk/src/cmp/rb/ReadmeTopQuick_rb.ts +55 -0
- package/project/.sdk/src/cmp/rb/ReadmeTopTest_rb.ts +38 -0
- package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +260 -0
- package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +476 -0
- package/project/.sdk/src/cmp/rb/Test_rb.ts +50 -0
- package/project/.sdk/src/cmp/rb/fragment/Entity.fragment.rb +116 -0
- package/project/.sdk/src/cmp/rb/fragment/EntityCreateOp.fragment.rb +25 -0
- package/project/.sdk/src/cmp/rb/fragment/EntityListOp.fragment.rb +20 -0
- package/project/.sdk/src/cmp/rb/fragment/EntityLoadOp.fragment.rb +26 -0
- package/project/.sdk/src/cmp/rb/fragment/EntityRemoveOp.fragment.rb +26 -0
- package/project/.sdk/src/cmp/rb/fragment/EntityUpdateOp.fragment.rb +26 -0
- package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +203 -0
- package/project/.sdk/src/cmp/rb/fragment/SdkError.fragment.rb +16 -0
- package/project/.sdk/src/cmp/rb/tsconfig.json +15 -0
- package/project/.sdk/src/cmp/rb/utility_rb.ts +84 -0
- package/project/.sdk/src/cmp/ts/Main_ts.ts +7 -0
- package/project/.sdk/src/cmp/ts/ReadmeExplanation_ts.ts +34 -0
- package/project/.sdk/src/cmp/ts/ReadmeHowto_ts.ts +123 -0
- package/project/.sdk/src/cmp/ts/ReadmeInstall_ts.ts +1 -1
- package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +159 -0
- package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +69 -54
- package/project/.sdk/src/cmp/ts/ReadmeTopHowto_ts.ts +25 -0
- package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +65 -0
- package/project/.sdk/src/cmp/ts/ReadmeTopTest_ts.ts +36 -0
- package/project/.sdk/tm/go/feature/log_feature.go +133 -0
- package/project/.sdk/tm/go/src/feature/log/.gitkeep +0 -0
- package/project/.sdk/tm/go/test/runner_test.go +16 -2
- package/project/.sdk/tm/js/src/Context.js +142 -0
- package/project/.sdk/tm/js/src/Control.js +16 -0
- package/project/.sdk/tm/js/src/Operation.js +19 -0
- package/project/.sdk/tm/js/src/Point.js +24 -0
- package/project/.sdk/tm/js/src/README.md +1 -0
- package/project/.sdk/tm/js/src/Response.js +19 -0
- package/project/.sdk/tm/js/src/Result.js +21 -0
- package/project/.sdk/tm/js/src/Spec.js +26 -0
- package/project/.sdk/tm/js/src/feature/README.md +1 -0
- package/project/.sdk/tm/js/src/feature/base/BaseFeature.js +45 -0
- package/project/.sdk/tm/js/src/feature/log/LogFeature.js +46 -47
- package/project/.sdk/tm/js/src/feature/test/TestFeature.js +207 -0
- package/project/.sdk/tm/js/src/types.js +22 -0
- package/project/.sdk/tm/js/src/utility/CleanUtility.js +31 -0
- package/project/.sdk/tm/js/src/utility/DoneUtility.js +11 -4
- package/project/.sdk/tm/js/src/utility/FeatureAddUtility.js +42 -0
- package/project/.sdk/tm/js/src/utility/FeatureHookUtility.js +25 -0
- package/project/.sdk/tm/js/src/utility/FeatureInitUtility.js +11 -0
- package/project/.sdk/tm/js/src/utility/FetcherUtility.js +28 -0
- package/project/.sdk/tm/js/src/utility/MakeContextUtility.js +11 -0
- package/project/.sdk/tm/js/src/utility/MakeErrorUtility.js +55 -0
- package/project/.sdk/tm/js/src/utility/MakeFetchDefUtility.js +44 -0
- package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +93 -0
- package/project/.sdk/tm/js/src/utility/MakePointUtility.js +77 -0
- package/project/.sdk/tm/js/src/utility/MakeRequestUtility.js +63 -0
- package/project/.sdk/tm/js/src/utility/MakeResponseUtility.js +55 -0
- package/project/.sdk/tm/js/src/utility/MakeResultUtility.js +54 -0
- package/project/.sdk/tm/js/src/utility/MakeSpecUtility.js +58 -0
- package/project/.sdk/tm/js/src/utility/MakeUrlUtility.js +40 -0
- package/project/.sdk/tm/js/src/utility/ParamUtility.js +61 -0
- package/project/.sdk/tm/js/src/utility/PrepareAuthUtility.js +41 -0
- package/project/.sdk/tm/js/src/utility/PrepareBodyUtility.js +25 -0
- package/project/.sdk/tm/js/src/utility/PrepareHeadersUtility.js +18 -0
- package/project/.sdk/tm/js/src/utility/{MethodUtility.js → PrepareMethodUtility.js} +7 -7
- package/project/.sdk/tm/js/src/utility/PrepareParamsUtility.js +25 -0
- package/project/.sdk/tm/js/src/utility/PreparePathUtility.js +13 -0
- package/project/.sdk/tm/js/src/utility/PrepareQueryUtility.js +26 -0
- package/project/.sdk/tm/js/src/utility/README.md +1 -0
- package/project/.sdk/tm/js/src/utility/ResultBasicUtility.js +34 -0
- package/project/.sdk/tm/js/src/utility/ResultBodyUtility.js +18 -0
- package/project/.sdk/tm/js/src/utility/ResultHeadersUtility.js +22 -0
- package/project/.sdk/tm/js/src/utility/StructUtility.js +2219 -1078
- package/project/.sdk/tm/js/src/utility/TransformRequestUtility.js +28 -0
- package/project/.sdk/tm/js/src/utility/TransformResponseUtility.js +31 -0
- package/project/.sdk/tm/js/src/utility/Utility.js +61 -61
- package/project/.sdk/tm/js/test/README.md +1 -0
- package/project/.sdk/tm/js/test/exists.test.js +16 -0
- package/project/.sdk/tm/js/test/runner.js +323 -107
- package/project/.sdk/tm/js/test/utility/Custom.test.js +41 -63
- package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +390 -116
- package/project/.sdk/tm/js/test/utility/StructUtility.test.js +728 -175
- package/project/.sdk/tm/js/test/utility/index.js +9 -0
- package/project/.sdk/tm/js/test/utility.js +72 -0
- package/project/.sdk/tm/lua/LICENSE +22 -0
- package/project/.sdk/tm/lua/Makefile +10 -0
- package/project/.sdk/tm/lua/core/context.lua +208 -0
- package/project/.sdk/tm/lua/core/control.lua +17 -0
- package/project/.sdk/tm/lua/core/error.lua +30 -0
- package/project/.sdk/tm/lua/core/helpers.lua +30 -0
- package/project/.sdk/tm/lua/core/operation.lua +51 -0
- package/project/.sdk/tm/lua/core/response.lua +45 -0
- package/project/.sdk/tm/lua/core/result.lua +58 -0
- package/project/.sdk/tm/lua/core/spec.lua +29 -0
- package/project/.sdk/tm/lua/core/utility_type.lua +90 -0
- package/project/.sdk/tm/lua/feature/base_feature.lua +35 -0
- package/project/.sdk/tm/lua/feature/log_feature.lua +80 -0
- package/project/.sdk/tm/lua/feature/test_feature.lua +202 -0
- package/project/.sdk/tm/lua/src/feature/README.md +3 -0
- package/project/.sdk/tm/lua/src/feature/base/.gitkeep +0 -0
- package/project/.sdk/tm/lua/src/feature/log/.gitkeep +0 -0
- package/project/.sdk/tm/lua/src/feature/test/.gitkeep +0 -0
- package/project/.sdk/tm/lua/test/primary_utility_test.lua +1213 -0
- package/project/.sdk/tm/lua/test/runner.lua +86 -0
- package/project/.sdk/tm/lua/test/struct_runner.lua +602 -0
- package/project/.sdk/tm/lua/test/struct_utility_test.lua +959 -0
- package/project/.sdk/tm/lua/utility/clean.lua +7 -0
- package/project/.sdk/tm/lua/utility/done.lua +19 -0
- package/project/.sdk/tm/lua/utility/feature_add.lua +8 -0
- package/project/.sdk/tm/lua/utility/feature_hook.lua +21 -0
- package/project/.sdk/tm/lua/utility/feature_init.lua +24 -0
- package/project/.sdk/tm/lua/utility/fetcher.lua +96 -0
- package/project/.sdk/tm/lua/utility/make_context.lua +9 -0
- package/project/.sdk/tm/lua/utility/make_error.lua +73 -0
- package/project/.sdk/tm/lua/utility/make_fetch_def.lua +43 -0
- package/project/.sdk/tm/lua/utility/make_options.lua +116 -0
- package/project/.sdk/tm/lua/utility/make_point.lua +92 -0
- package/project/.sdk/tm/lua/utility/make_request.lua +58 -0
- package/project/.sdk/tm/lua/utility/make_response.lua +44 -0
- package/project/.sdk/tm/lua/utility/make_result.lua +51 -0
- package/project/.sdk/tm/lua/utility/make_spec.lua +72 -0
- package/project/.sdk/tm/lua/utility/make_url.lua +46 -0
- package/project/.sdk/tm/lua/utility/param.lua +68 -0
- package/project/.sdk/tm/lua/utility/prepare_auth.lua +39 -0
- package/project/.sdk/tm/lua/utility/prepare_body.lua +14 -0
- package/project/.sdk/tm/lua/utility/prepare_headers.lua +20 -0
- package/project/.sdk/tm/lua/utility/prepare_method.lua +17 -0
- package/project/.sdk/tm/lua/utility/prepare_params.lua +36 -0
- package/project/.sdk/tm/lua/utility/prepare_path.lua +19 -0
- package/project/.sdk/tm/lua/utility/prepare_query.lua +41 -0
- package/project/.sdk/tm/lua/utility/register.lua +71 -0
- package/project/.sdk/tm/lua/utility/result_basic.lua +32 -0
- package/project/.sdk/tm/lua/utility/result_body.lua +17 -0
- package/project/.sdk/tm/lua/utility/result_headers.lua +22 -0
- package/project/.sdk/tm/lua/utility/struct/struct.lua +3427 -0
- package/project/.sdk/tm/lua/utility/transform_request.lua +31 -0
- package/project/.sdk/tm/lua/utility/transform_response.lua +44 -0
- package/project/.sdk/tm/php/LICENSE +22 -0
- package/project/.sdk/tm/php/Makefile +10 -0
- package/project/.sdk/tm/php/core/Context.php +139 -0
- package/project/.sdk/tm/php/core/Control.php +18 -0
- package/project/.sdk/tm/php/core/Error.php +37 -0
- package/project/.sdk/tm/php/core/Helpers.php +25 -0
- package/project/.sdk/tm/php/core/Operation.php +36 -0
- package/project/.sdk/tm/php/core/Response.php +30 -0
- package/project/.sdk/tm/php/core/Result.php +35 -0
- package/project/.sdk/tm/php/core/Spec.php +38 -0
- package/project/.sdk/tm/php/core/UtilityType.php +89 -0
- package/project/.sdk/tm/php/feature/BaseFeature.php +37 -0
- package/project/.sdk/tm/php/feature/LogFeature.php +65 -0
- package/project/.sdk/tm/php/feature/TestFeature.php +156 -0
- package/project/.sdk/tm/php/src/feature/README.md +3 -0
- package/project/.sdk/tm/php/src/feature/base/.gitkeep +0 -0
- package/project/.sdk/tm/php/src/feature/log/.gitkeep +0 -0
- package/project/.sdk/tm/php/src/feature/test/.gitkeep +0 -0
- package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +1309 -0
- package/project/.sdk/tm/php/test/Runner.php +112 -0
- package/project/.sdk/tm/php/test/StructRunner.php +275 -0
- package/project/.sdk/tm/php/test/StructUtilityTest.php +1336 -0
- package/project/.sdk/tm/php/utility/Clean.php +12 -0
- package/project/.sdk/tm/php/utility/Done.php +26 -0
- package/project/.sdk/tm/php/utility/FeatureAdd.php +12 -0
- package/project/.sdk/tm/php/utility/FeatureHook.php +23 -0
- package/project/.sdk/tm/php/utility/FeatureInit.php +25 -0
- package/project/.sdk/tm/php/utility/Fetcher.php +105 -0
- package/project/.sdk/tm/php/utility/MakeContext.php +14 -0
- package/project/.sdk/tm/php/utility/MakeError.php +59 -0
- package/project/.sdk/tm/php/utility/MakeFetchDef.php +36 -0
- package/project/.sdk/tm/php/utility/MakeOptions.php +102 -0
- package/project/.sdk/tm/php/utility/MakePoint.php +87 -0
- package/project/.sdk/tm/php/utility/MakeRequest.php +57 -0
- package/project/.sdk/tm/php/utility/MakeResponse.php +43 -0
- package/project/.sdk/tm/php/utility/MakeResult.php +53 -0
- package/project/.sdk/tm/php/utility/MakeSpec.php +64 -0
- package/project/.sdk/tm/php/utility/MakeUrl.php +41 -0
- package/project/.sdk/tm/php/utility/Param.php +68 -0
- package/project/.sdk/tm/php/utility/PrepareAuth.php +33 -0
- package/project/.sdk/tm/php/utility/PrepareBody.php +15 -0
- package/project/.sdk/tm/php/utility/PrepareHeaders.php +18 -0
- package/project/.sdk/tm/php/utility/PrepareMethod.php +21 -0
- package/project/.sdk/tm/php/utility/PrepareParams.php +34 -0
- package/project/.sdk/tm/php/utility/PreparePath.php +20 -0
- package/project/.sdk/tm/php/utility/PrepareQuery.php +32 -0
- package/project/.sdk/tm/php/utility/Register.php +67 -0
- package/project/.sdk/tm/php/utility/ResultBasic.php +29 -0
- package/project/.sdk/tm/php/utility/ResultBody.php +17 -0
- package/project/.sdk/tm/php/utility/ResultHeaders.php +21 -0
- package/project/.sdk/tm/php/utility/TransformRequest.php +27 -0
- package/project/.sdk/tm/php/utility/TransformResponse.php +42 -0
- package/project/.sdk/tm/php/utility/struct/Struct.php +3535 -0
- package/project/.sdk/tm/py/Makefile +10 -0
- package/project/.sdk/tm/py/core/__init__.py +0 -0
- package/project/.sdk/tm/py/core/context.py +199 -0
- package/project/.sdk/tm/py/core/control.py +12 -0
- package/project/.sdk/tm/py/core/error.py +18 -0
- package/project/.sdk/tm/py/core/helpers.py +15 -0
- package/project/.sdk/tm/py/core/operation.py +37 -0
- package/project/.sdk/tm/py/core/response.py +34 -0
- package/project/.sdk/tm/py/core/result.py +44 -0
- package/project/.sdk/tm/py/core/spec.py +23 -0
- package/project/.sdk/tm/py/core/utility_type.py +82 -0
- package/project/.sdk/tm/py/entity/__init__.py +0 -0
- package/project/.sdk/tm/py/feature/__init__.py +0 -0
- package/project/.sdk/tm/py/feature/base_feature.py +61 -0
- package/project/.sdk/tm/py/feature/log_feature.py +84 -0
- package/project/.sdk/tm/py/feature/test_feature.py +164 -0
- package/project/.sdk/tm/py/src/feature/README.md +3 -0
- package/project/.sdk/tm/py/src/feature/base/.gitkeep +0 -0
- package/project/.sdk/tm/py/src/feature/log/.gitkeep +0 -0
- package/project/.sdk/tm/py/src/feature/test/.gitkeep +0 -0
- package/project/.sdk/tm/py/test/__init__.py +0 -0
- package/project/.sdk/tm/py/test/runner.py +90 -0
- package/project/.sdk/tm/py/test/struct_runner.py +411 -0
- package/project/.sdk/tm/py/test/test_primary_utility.py +1101 -0
- package/project/.sdk/tm/py/test/test_struct_utility.py +751 -0
- package/project/.sdk/tm/py/utility/__init__.py +0 -0
- package/project/.sdk/tm/py/utility/clean.py +5 -0
- package/project/.sdk/tm/py/utility/done.py +14 -0
- package/project/.sdk/tm/py/utility/feature_add.py +6 -0
- package/project/.sdk/tm/py/utility/feature_hook.py +15 -0
- package/project/.sdk/tm/py/utility/feature_init.py +18 -0
- package/project/.sdk/tm/py/utility/fetcher.py +95 -0
- package/project/.sdk/tm/py/utility/make_context.py +7 -0
- package/project/.sdk/tm/py/utility/make_error.py +64 -0
- package/project/.sdk/tm/py/utility/make_fetch_def.py +37 -0
- package/project/.sdk/tm/py/utility/make_options.py +103 -0
- package/project/.sdk/tm/py/utility/make_point.py +74 -0
- package/project/.sdk/tm/py/utility/make_request.py +52 -0
- package/project/.sdk/tm/py/utility/make_response.py +36 -0
- package/project/.sdk/tm/py/utility/make_result.py +41 -0
- package/project/.sdk/tm/py/utility/make_spec.py +68 -0
- package/project/.sdk/tm/py/utility/make_url.py +34 -0
- package/project/.sdk/tm/py/utility/param.py +55 -0
- package/project/.sdk/tm/py/utility/prepare_auth.py +34 -0
- package/project/.sdk/tm/py/utility/prepare_body.py +11 -0
- package/project/.sdk/tm/py/utility/prepare_headers.py +17 -0
- package/project/.sdk/tm/py/utility/prepare_method.py +15 -0
- package/project/.sdk/tm/py/utility/prepare_params.py +28 -0
- package/project/.sdk/tm/py/utility/prepare_path.py +16 -0
- package/project/.sdk/tm/py/utility/prepare_query.py +33 -0
- package/project/.sdk/tm/py/utility/register.py +68 -0
- package/project/.sdk/tm/py/utility/result_basic.py +26 -0
- package/project/.sdk/tm/py/utility/result_body.py +13 -0
- package/project/.sdk/tm/py/utility/result_headers.py +17 -0
- package/project/.sdk/tm/py/utility/transform_request.py +27 -0
- package/project/.sdk/tm/py/utility/transform_response.py +39 -0
- package/project/.sdk/tm/py/utility/voxgig_struct/__init__.py +72 -0
- package/project/.sdk/tm/py/utility/voxgig_struct/voxgig_struct.py +2770 -0
- package/project/.sdk/tm/rb/Gemfile +4 -0
- package/project/.sdk/tm/rb/LICENSE +22 -0
- package/project/.sdk/tm/rb/Makefile +10 -0
- package/project/.sdk/tm/rb/core/context.rb +105 -0
- package/project/.sdk/tm/rb/core/control.rb +11 -0
- package/project/.sdk/tm/rb/core/error.rb +24 -0
- package/project/.sdk/tm/rb/core/helpers.rb +16 -0
- package/project/.sdk/tm/rb/core/operation.rb +26 -0
- package/project/.sdk/tm/rb/core/response.rb +20 -0
- package/project/.sdk/tm/rb/core/result.rb +23 -0
- package/project/.sdk/tm/rb/core/spec.rb +23 -0
- package/project/.sdk/tm/rb/core/utility_type.rb +32 -0
- package/project/.sdk/tm/rb/feature/base_feature.rb +30 -0
- package/project/.sdk/tm/rb/feature/log_feature.rb +50 -0
- package/project/.sdk/tm/rb/feature/test_feature.rb +154 -0
- package/project/.sdk/tm/rb/src/feature/README.md +3 -0
- package/project/.sdk/tm/rb/src/feature/base/.gitkeep +0 -0
- package/project/.sdk/tm/rb/src/feature/log/.gitkeep +0 -0
- package/project/.sdk/tm/rb/src/feature/test/.gitkeep +0 -0
- package/project/.sdk/tm/rb/test/primary_utility_test.rb +1083 -0
- package/project/.sdk/tm/rb/test/runner.rb +70 -0
- package/project/.sdk/tm/rb/test/struct_runner.rb +309 -0
- package/project/.sdk/tm/rb/test/struct_utility_test.rb +670 -0
- package/project/.sdk/tm/rb/utility/clean.rb +4 -0
- package/project/.sdk/tm/rb/utility/done.rb +14 -0
- package/project/.sdk/tm/rb/utility/feature_add.rb +6 -0
- package/project/.sdk/tm/rb/utility/feature_hook.rb +11 -0
- package/project/.sdk/tm/rb/utility/feature_init.rb +16 -0
- package/project/.sdk/tm/rb/utility/fetcher.rb +67 -0
- package/project/.sdk/tm/rb/utility/make_context.rb +7 -0
- package/project/.sdk/tm/rb/utility/make_error.rb +44 -0
- package/project/.sdk/tm/rb/utility/make_fetch_def.rb +24 -0
- package/project/.sdk/tm/rb/utility/make_options.rb +57 -0
- package/project/.sdk/tm/rb/utility/make_point.rb +77 -0
- package/project/.sdk/tm/rb/utility/make_request.rb +44 -0
- package/project/.sdk/tm/rb/utility/make_response.rb +25 -0
- package/project/.sdk/tm/rb/utility/make_result.rb +33 -0
- package/project/.sdk/tm/rb/utility/make_spec.rb +50 -0
- package/project/.sdk/tm/rb/utility/make_url.rb +32 -0
- package/project/.sdk/tm/rb/utility/param.rb +48 -0
- package/project/.sdk/tm/rb/utility/prepare_auth.rb +26 -0
- package/project/.sdk/tm/rb/utility/prepare_body.rb +6 -0
- package/project/.sdk/tm/rb/utility/prepare_headers.rb +11 -0
- package/project/.sdk/tm/rb/utility/prepare_method.rb +5 -0
- package/project/.sdk/tm/rb/utility/prepare_params.rb +25 -0
- package/project/.sdk/tm/rb/utility/prepare_path.rb +13 -0
- package/project/.sdk/tm/rb/utility/prepare_query.rb +22 -0
- package/project/.sdk/tm/rb/utility/register.rb +63 -0
- package/project/.sdk/tm/rb/utility/result_basic.rb +23 -0
- package/project/.sdk/tm/rb/utility/result_body.rb +11 -0
- package/project/.sdk/tm/rb/utility/result_headers.rb +15 -0
- package/project/.sdk/tm/rb/utility/struct/voxgig_struct.rb +2256 -0
- package/project/.sdk/tm/rb/utility/transform_request.rb +15 -0
- package/project/.sdk/tm/rb/utility/transform_response.rb +23 -0
- package/src/cmp/Main.ts +1 -16
- package/src/cmp/Readme.ts +5 -5
- package/src/cmp/ReadmeEntity.ts +77 -25
- package/src/cmp/ReadmeExplanation.ts +333 -0
- package/src/cmp/ReadmeHowto.ts +28 -0
- package/src/cmp/ReadmeIntro.ts +10 -24
- package/src/cmp/ReadmeModel.ts +57 -88
- package/src/cmp/ReadmeOptions.ts +40 -11
- package/src/cmp/ReadmeQuick.ts +4 -1
- package/src/cmp/ReadmeRef.ts +1057 -40
- package/src/cmp/ReadmeTop.ts +213 -0
- package/src/sdkgen.ts +12 -0
- package/project/.sdk/src/cmp/js/Quick_js.ts +0 -78
- package/project/.sdk/src/cmp/js/TestAcceptEntity_js.ts +0 -13
- package/project/.sdk/src/cmp/js/TestAccept_js.ts +0 -18
- package/project/.sdk/tm/go/test/exists_test.go +0 -16
- package/project/.sdk/tm/js/src/utility/AuthUtility.js +0 -21
- package/project/.sdk/tm/js/src/utility/BodyUtility.js +0 -29
- package/project/.sdk/tm/js/src/utility/ErrorUtility.js +0 -33
- package/project/.sdk/tm/js/src/utility/FindparamUtility.js +0 -31
- package/project/.sdk/tm/js/src/utility/FullurlUtility.js +0 -39
- package/project/.sdk/tm/js/src/utility/HeadersUtility.js +0 -13
- package/project/.sdk/tm/js/src/utility/JoinurlUtility.js +0 -14
- package/project/.sdk/tm/js/src/utility/OperatorUtility.js +0 -44
- package/project/.sdk/tm/js/src/utility/OptionsUtility.js +0 -54
- package/project/.sdk/tm/js/src/utility/ParamsUtility.js +0 -21
- package/project/.sdk/tm/js/src/utility/QueryUtility.js +0 -21
- package/project/.sdk/tm/js/src/utility/ReqformUtility.js +0 -32
- package/project/.sdk/tm/js/src/utility/RequestUtility.js +0 -48
- package/project/.sdk/tm/js/src/utility/ResbasicUtility.js +0 -27
- package/project/.sdk/tm/js/src/utility/ResbodyUtility.js +0 -15
- package/project/.sdk/tm/js/src/utility/ResformUtility.js +0 -34
- package/project/.sdk/tm/js/src/utility/ResheadersUtility.js +0 -19
- package/project/.sdk/tm/js/src/utility/ResponseUtility.js +0 -37
- package/project/.sdk/tm/js/src/utility/ResultUtility.js +0 -28
- package/project/.sdk/tm/js/src/utility/SpecUtility.js +0 -35
|
@@ -1,1203 +1,2344 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/* Copyright (c) 2025-2026 Voxgig Ltd. MIT LICENSE. */
|
|
2
|
+
// VERSION: @voxgig/struct 0.0.10
|
|
4
3
|
/* Voxgig Struct
|
|
5
4
|
* =============
|
|
6
5
|
*
|
|
7
6
|
* Utility functions to manipulate in-memory JSON-like data
|
|
8
|
-
* structures.
|
|
9
|
-
* "
|
|
10
|
-
*
|
|
7
|
+
* structures. These structures assumed to be composed of nested
|
|
8
|
+
* "nodes", where a node is a list or map, and has named or indexed
|
|
9
|
+
* fields. The general design principle is "by-example". Transform
|
|
10
|
+
* specifications mirror the desired output. This implementation is
|
|
11
|
+
* designed for porting to multiple language, and to be tolerant of
|
|
12
|
+
* undefined values.
|
|
11
13
|
*
|
|
12
|
-
*
|
|
13
|
-
* -
|
|
14
|
-
* - items: list entries of a map or list as [key, value] pairs
|
|
15
|
-
* - getprop: safely get a property value by key
|
|
16
|
-
* - setprop: safely set a property value by key
|
|
17
|
-
* - getpath: get the value at a key path deep inside an object
|
|
14
|
+
* Main utilities
|
|
15
|
+
* - getpath: get the value at a key path deep inside an object.
|
|
18
16
|
* - merge: merge multiple nodes, overriding values in earlier nodes.
|
|
19
17
|
* - walk: walk a node tree, applying a function at each node and leaf.
|
|
20
18
|
* - inject: inject values from a data store into a new data structure.
|
|
21
19
|
* - transform: transform a data structure to an example structure.
|
|
20
|
+
* - validate: valiate a data structure against a shape specification.
|
|
21
|
+
*
|
|
22
|
+
* Minor utilities
|
|
23
|
+
* - isnode, islist, ismap, iskey, isfunc: identify value kinds.
|
|
24
|
+
* - isempty: undefined values, or empty nodes.
|
|
25
|
+
* - keysof: sorted list of node keys (ascending).
|
|
26
|
+
* - haskey: true if key value is defined.
|
|
27
|
+
* - clone: create a copy of a JSON-like data structure.
|
|
28
|
+
* - items: list entries of a map or list as [key, value] pairs.
|
|
29
|
+
* - getprop: safely get a property value by key.
|
|
30
|
+
* - setprop: safely set a property value by key.
|
|
31
|
+
* - stringify: human-friendly string version of a value.
|
|
32
|
+
* - escre: escape a regular expresion string.
|
|
33
|
+
* - escurl: escape a url.
|
|
34
|
+
* - join: join parts of a url, merging forward slashes.
|
|
35
|
+
*
|
|
36
|
+
* This set of functions and supporting utilities is designed to work
|
|
37
|
+
* uniformly across many languages, meaning that some code that may be
|
|
38
|
+
* functionally redundant in specific languages is still retained to
|
|
39
|
+
* keep the code human comparable.
|
|
40
|
+
*
|
|
41
|
+
* NOTE: Lists are assumed to be mutable and reference stable.
|
|
42
|
+
*
|
|
43
|
+
* NOTE: In this code JSON nulls are in general *not* considered the
|
|
44
|
+
* same as the undefined value in the given language. However most
|
|
45
|
+
* JSON parsers do use the undefined value to represent JSON
|
|
46
|
+
* null. This is ambiguous as JSON null is a separate value, not an
|
|
47
|
+
* undefined value. You should convert such values to a special value
|
|
48
|
+
* to represent JSON null, if this ambiguity creates issues
|
|
49
|
+
* (thankfully in most APIs, JSON nulls are not used). For example,
|
|
50
|
+
* the unit tests use the string "__NULL__" where necessary.
|
|
51
|
+
*
|
|
22
52
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
// String constants are explicitly defined.
|
|
54
|
+
// Mode value for inject step (bitfield).
|
|
55
|
+
const M_KEYPRE = 1
|
|
56
|
+
const M_KEYPOST = 2
|
|
57
|
+
const M_VAL = 4
|
|
58
|
+
// Special strings.
|
|
59
|
+
const S_BKEY = '`$KEY`'
|
|
60
|
+
const S_BANNO = '`$ANNO`'
|
|
61
|
+
const S_BEXACT = '`$EXACT`'
|
|
62
|
+
const S_BVAL = '`$VAL`'
|
|
63
|
+
const S_DKEY = '$KEY'
|
|
64
|
+
const S_DTOP = '$TOP'
|
|
65
|
+
const S_DERRS = '$ERRS'
|
|
66
|
+
const S_DSPEC = '$SPEC'
|
|
67
|
+
// General strings.
|
|
68
|
+
const S_list = 'list'
|
|
69
|
+
const S_base = 'base'
|
|
70
|
+
const S_boolean = 'boolean'
|
|
71
|
+
const S_function = 'function'
|
|
72
|
+
const S_symbol = 'symbol'
|
|
73
|
+
const S_instance = 'instance'
|
|
74
|
+
const S_key = 'key'
|
|
75
|
+
const S_any = 'any'
|
|
76
|
+
const S_nil = 'nil'
|
|
77
|
+
const S_null = 'null'
|
|
78
|
+
const S_number = 'number'
|
|
79
|
+
const S_object = 'object'
|
|
80
|
+
const S_string = 'string'
|
|
81
|
+
const S_decimal = 'decimal'
|
|
82
|
+
const S_integer = 'integer'
|
|
83
|
+
const S_map = 'map'
|
|
84
|
+
const S_scalar = 'scalar'
|
|
85
|
+
const S_node = 'node'
|
|
86
|
+
// Character strings.
|
|
87
|
+
const S_BT = '`'
|
|
88
|
+
const S_CN = ':'
|
|
89
|
+
const S_CS = ']'
|
|
90
|
+
const S_DS = '$'
|
|
91
|
+
const S_DT = '.'
|
|
92
|
+
const S_FS = '/'
|
|
93
|
+
const S_KEY = 'KEY'
|
|
94
|
+
const S_MT = ''
|
|
95
|
+
const S_OS = '['
|
|
96
|
+
const S_SP = ' '
|
|
97
|
+
const S_CM = ','
|
|
98
|
+
const S_VIZ = ': '
|
|
99
|
+
// Types
|
|
100
|
+
let t = 31
|
|
101
|
+
const T_any = (1 << t--) - 1
|
|
102
|
+
const T_noval = 1 << t--; // Means property absent, undefined. Also NOT a scalar!
|
|
103
|
+
const T_boolean = 1 << t--
|
|
104
|
+
const T_decimal = 1 << t--
|
|
105
|
+
const T_integer = 1 << t--
|
|
106
|
+
const T_number = 1 << t--
|
|
107
|
+
const T_string = 1 << t--
|
|
108
|
+
const T_function = 1 << t--
|
|
109
|
+
const T_symbol = 1 << t--
|
|
110
|
+
const T_null = 1 << t--; // The actual JSON null value.
|
|
111
|
+
t -= 7
|
|
112
|
+
const T_list = 1 << t--
|
|
113
|
+
const T_map = 1 << t--
|
|
114
|
+
const T_instance = 1 << t--
|
|
115
|
+
t -= 4
|
|
116
|
+
const T_scalar = 1 << t--
|
|
117
|
+
const T_node = 1 << t--
|
|
118
|
+
const TYPENAME = [
|
|
119
|
+
S_any,
|
|
120
|
+
S_nil,
|
|
121
|
+
S_boolean,
|
|
122
|
+
S_decimal,
|
|
123
|
+
S_integer,
|
|
124
|
+
S_number,
|
|
125
|
+
S_string,
|
|
126
|
+
S_function,
|
|
127
|
+
S_symbol,
|
|
128
|
+
S_null,
|
|
129
|
+
'', '', '',
|
|
130
|
+
'', '', '', '',
|
|
131
|
+
S_list,
|
|
132
|
+
S_map,
|
|
133
|
+
S_instance,
|
|
134
|
+
'', '', '', '',
|
|
135
|
+
S_scalar,
|
|
136
|
+
S_node,
|
|
137
|
+
]
|
|
138
|
+
// The standard undefined value for this language.
|
|
139
|
+
const NONE = undefined
|
|
140
|
+
// Private markers
|
|
141
|
+
const SKIP = { '`$SKIP`': true }
|
|
142
|
+
const DELETE = { '`$DELETE`': true }
|
|
143
|
+
// Regular expression constants
|
|
144
|
+
const R_INTEGER_KEY = /^[-0-9]+$/; // Match integer keys (including <0).
|
|
145
|
+
const R_ESCAPE_REGEXP = /[.*+?^${}()|[\]\\]/g; // Chars that need escaping in regexp.
|
|
146
|
+
const R_TRAILING_SLASH = /\/+$/; // Trailing slashes in URLs.
|
|
147
|
+
const R_LEADING_TRAILING_SLASH = /([^\/])\/+/; // Multiple slashes in URL middle.
|
|
148
|
+
const R_LEADING_SLASH = /^\/+/; // Leading slashes in URLs.
|
|
149
|
+
const R_QUOTES = /"/g; // Double quotes for removal.
|
|
150
|
+
const R_DOT = /\./g; // Dots in path strings.
|
|
151
|
+
const R_CLONE_REF = /^`\$REF:([0-9]+)`$/; // Copy reference in cloning.
|
|
152
|
+
const R_META_PATH = /^([^$]+)\$([=~])(.+)$/; // Meta path syntax.
|
|
153
|
+
const R_DOUBLE_DOLLAR = /\$\$/g; // Double dollar escape sequence.
|
|
154
|
+
const R_TRANSFORM_NAME = /`\$([A-Z]+)`/g; // Transform command names.
|
|
155
|
+
const R_INJECTION_FULL = /^`(\$[A-Z]+|[^`]*)[0-9]*`$/; // Full string injection pattern.
|
|
156
|
+
const R_BT_ESCAPE = /\$BT/g; // Backtick escape sequence.
|
|
157
|
+
const R_DS_ESCAPE = /\$DS/g; // Dollar sign escape sequence.
|
|
158
|
+
const R_INJECTION_PARTIAL = /`([^`]+)`/g; // Partial string injection pattern.
|
|
159
|
+
// Default max depth (for walk etc).
|
|
160
|
+
const MAXDEPTH = 32
|
|
161
|
+
// Return type string for narrowest type.
|
|
162
|
+
function typename(t) {
|
|
163
|
+
return getelem(TYPENAME, Math.clz32(t), TYPENAME[0])
|
|
164
|
+
}
|
|
165
|
+
// Get a defined value. Returns alt if val is undefined.
|
|
166
|
+
function getdef(val, alt) {
|
|
167
|
+
if (NONE === val) {
|
|
168
|
+
return alt
|
|
169
|
+
}
|
|
170
|
+
return val
|
|
51
171
|
}
|
|
52
|
-
|
|
53
|
-
const UNDEF = undefined
|
|
54
|
-
|
|
55
|
-
|
|
56
172
|
// Value is a node - defined, and a map (hash) or list (array).
|
|
173
|
+
// NOTE: typescript
|
|
174
|
+
// things
|
|
57
175
|
function isnode(val) {
|
|
58
|
-
|
|
176
|
+
return null != val && S_object == typeof val
|
|
59
177
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
178
|
// Value is a defined map (hash) with string keys.
|
|
63
179
|
function ismap(val) {
|
|
64
|
-
|
|
180
|
+
return null != val && S_object == typeof val && !Array.isArray(val)
|
|
65
181
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
182
|
// Value is a defined list (array) with integer keys (indexes).
|
|
69
183
|
function islist(val) {
|
|
70
|
-
|
|
184
|
+
return Array.isArray(val)
|
|
71
185
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
186
|
// Value is a defined string (non-empty) or integer key.
|
|
75
187
|
function iskey(key) {
|
|
76
|
-
|
|
77
|
-
|
|
188
|
+
const keytype = typeof key
|
|
189
|
+
return (S_string === keytype && S_MT !== key) || S_number === keytype
|
|
78
190
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
// Check for an "empty" value - undefined, null, empty string, array, object.
|
|
191
|
+
// Check for an "empty" value - undefined, empty string, array, object.
|
|
82
192
|
function isempty(val) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
193
|
+
return null == val || S_MT === val ||
|
|
194
|
+
(Array.isArray(val) && 0 === val.length) ||
|
|
195
|
+
(S_object === typeof val && 0 === Object.keys(val).length)
|
|
86
196
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// TOOD: TEST
|
|
197
|
+
// Value is a function.
|
|
90
198
|
function isfunc(val) {
|
|
91
|
-
|
|
199
|
+
return S_function === typeof val
|
|
200
|
+
}
|
|
201
|
+
// The integer size of the value. For arrays and strings, the length,
|
|
202
|
+
// for numbers, the integer part, for boolean, true is 1 and falso 0, for all other values, 0.
|
|
203
|
+
function size(val) {
|
|
204
|
+
if (islist(val)) {
|
|
205
|
+
return val.length
|
|
206
|
+
}
|
|
207
|
+
else if (ismap(val)) {
|
|
208
|
+
return Object.keys(val).length
|
|
209
|
+
}
|
|
210
|
+
const valtype = typeof val
|
|
211
|
+
if (S_string == valtype) {
|
|
212
|
+
return val.length
|
|
213
|
+
}
|
|
214
|
+
else if (S_number == typeof val) {
|
|
215
|
+
return Math.floor(val)
|
|
216
|
+
}
|
|
217
|
+
else if (S_boolean == typeof val) {
|
|
218
|
+
return true === val ? 1 : 0
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
return 0
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Extract part of an array or string into a new value, from the start
|
|
225
|
+
// point to the end point. If no end is specified, extract to the
|
|
226
|
+
// full length of the value. Negative arguments count from the end of
|
|
227
|
+
// the value. For numbers, perform min and max bounding, where start
|
|
228
|
+
// is inclusive, and end is *exclusive*.
|
|
229
|
+
// NOTE: input lists are not mutated by default. Use the mutate
|
|
230
|
+
// argument to mutate lists in place.
|
|
231
|
+
function slice(val, start, end, mutate) {
|
|
232
|
+
if (S_number === typeof val) {
|
|
233
|
+
start = null == start || S_number !== typeof start ? Number.MIN_SAFE_INTEGER : start
|
|
234
|
+
end = (null == end || S_number !== typeof end ? Number.MAX_SAFE_INTEGER : end) - 1
|
|
235
|
+
return Math.min(Math.max(val, start), end)
|
|
236
|
+
}
|
|
237
|
+
const vlen = size(val)
|
|
238
|
+
if (null != end && null == start) {
|
|
239
|
+
start = 0
|
|
240
|
+
}
|
|
241
|
+
if (null != start) {
|
|
242
|
+
if (start < 0) {
|
|
243
|
+
end = vlen + start
|
|
244
|
+
if (end < 0) {
|
|
245
|
+
end = 0
|
|
246
|
+
}
|
|
247
|
+
start = 0
|
|
248
|
+
}
|
|
249
|
+
else if (null != end) {
|
|
250
|
+
if (end < 0) {
|
|
251
|
+
end = vlen + end
|
|
252
|
+
if (end < 0) {
|
|
253
|
+
end = 0
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else if (vlen < end) {
|
|
257
|
+
end = vlen
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
end = vlen
|
|
262
|
+
}
|
|
263
|
+
if (vlen < start) {
|
|
264
|
+
start = vlen
|
|
265
|
+
}
|
|
266
|
+
if (-1 < start && start <= end && end <= vlen) {
|
|
267
|
+
if (islist(val)) {
|
|
268
|
+
if (mutate) {
|
|
269
|
+
for (let i = 0, j = start; j < end; i++, j++) {
|
|
270
|
+
val[i] = val[j]
|
|
271
|
+
}
|
|
272
|
+
val.length = (end - start)
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
val = val.slice(start, end)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else if (S_string === typeof val) {
|
|
279
|
+
val = val.substring(start, end)
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
if (islist(val)) {
|
|
284
|
+
val = []
|
|
285
|
+
}
|
|
286
|
+
else if (S_string === typeof val) {
|
|
287
|
+
val = S_MT
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return val
|
|
292
|
+
}
|
|
293
|
+
// String padding.
|
|
294
|
+
function pad(str, padding, padchar) {
|
|
295
|
+
str = S_string === typeof str ? str : stringify(str)
|
|
296
|
+
padding = null == padding ? 44 : padding
|
|
297
|
+
padchar = null == padchar ? S_SP : ((padchar + S_SP)[0])
|
|
298
|
+
return -1 < padding ? str.padEnd(padding, padchar) : str.padStart(0 - padding, padchar)
|
|
299
|
+
}
|
|
300
|
+
// Determine the type of a value as a bit code.
|
|
301
|
+
function typify(value) {
|
|
302
|
+
if (undefined === value) {
|
|
303
|
+
return T_noval
|
|
304
|
+
}
|
|
305
|
+
const typestr = typeof value
|
|
306
|
+
if (null === value) {
|
|
307
|
+
return T_scalar | T_null
|
|
308
|
+
}
|
|
309
|
+
else if (S_number === typestr) {
|
|
310
|
+
if (Number.isInteger(value)) {
|
|
311
|
+
return T_scalar | T_number | T_integer
|
|
312
|
+
}
|
|
313
|
+
else if (isNaN(value)) {
|
|
314
|
+
return T_noval
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
return T_scalar | T_number | T_decimal
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
else if (S_string === typestr) {
|
|
321
|
+
return T_scalar | T_string
|
|
322
|
+
}
|
|
323
|
+
else if (S_boolean === typestr) {
|
|
324
|
+
return T_scalar | T_boolean
|
|
325
|
+
}
|
|
326
|
+
else if (S_function === typestr) {
|
|
327
|
+
return T_scalar | T_function
|
|
328
|
+
}
|
|
329
|
+
// For languages that have symbolic atoms.
|
|
330
|
+
else if (S_symbol === typestr) {
|
|
331
|
+
return T_scalar | T_symbol
|
|
332
|
+
}
|
|
333
|
+
else if (Array.isArray(value)) {
|
|
334
|
+
return T_node | T_list
|
|
335
|
+
}
|
|
336
|
+
else if (S_object === typestr) {
|
|
337
|
+
if (value.constructor instanceof Function) {
|
|
338
|
+
let cname = value.constructor.name
|
|
339
|
+
if ('Object' !== cname && 'Array' !== cname) {
|
|
340
|
+
return T_node | T_instance
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return T_node | T_map
|
|
344
|
+
}
|
|
345
|
+
// Anything else (e.g. bigint) is considered T_any
|
|
346
|
+
return T_any
|
|
347
|
+
}
|
|
348
|
+
// Get a list element. The key should be an integer, or a string
|
|
349
|
+
// that can parse to an integer only. Negative integers count from the end of the list.
|
|
350
|
+
function getelem(val, key, alt) {
|
|
351
|
+
let out = NONE
|
|
352
|
+
if (NONE === val || NONE === key) {
|
|
353
|
+
return alt
|
|
354
|
+
}
|
|
355
|
+
if (islist(val)) {
|
|
356
|
+
let nkey = parseInt(key)
|
|
357
|
+
if (Number.isInteger(nkey) && ('' + key).match(R_INTEGER_KEY)) {
|
|
358
|
+
if (nkey < 0) {
|
|
359
|
+
key = val.length + nkey
|
|
360
|
+
}
|
|
361
|
+
out = val[key]
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (NONE === out) {
|
|
365
|
+
return 0 < (T_function & typify(alt)) ? alt() : alt
|
|
366
|
+
}
|
|
367
|
+
return out
|
|
368
|
+
}
|
|
369
|
+
// Safely get a property of a node. Undefined arguments return undefined.
|
|
370
|
+
// If the key is not found, return the alternative value, if any.
|
|
371
|
+
function getprop(val, key, alt) {
|
|
372
|
+
let out = alt
|
|
373
|
+
if (NONE === val || NONE === key) {
|
|
374
|
+
return alt
|
|
375
|
+
}
|
|
376
|
+
if (isnode(val)) {
|
|
377
|
+
out = val[key]
|
|
378
|
+
}
|
|
379
|
+
if (NONE === out) {
|
|
380
|
+
return alt
|
|
381
|
+
}
|
|
382
|
+
return out
|
|
383
|
+
}
|
|
384
|
+
// Convert different types of keys to string representation.
|
|
385
|
+
// String keys are returned as is.
|
|
386
|
+
// Number keys are converted to strings.
|
|
387
|
+
// Floats are truncated to integers.
|
|
388
|
+
// Booleans, objects, arrays, null, undefined all return empty string.
|
|
389
|
+
function strkey(key = NONE) {
|
|
390
|
+
if (NONE === key) {
|
|
391
|
+
return S_MT
|
|
392
|
+
}
|
|
393
|
+
const t = typify(key)
|
|
394
|
+
if (0 < (T_string & t)) {
|
|
395
|
+
return key
|
|
396
|
+
}
|
|
397
|
+
else if (0 < (T_boolean & t)) {
|
|
398
|
+
return S_MT
|
|
399
|
+
}
|
|
400
|
+
else if (0 < (T_number & t)) {
|
|
401
|
+
return key % 1 === 0 ? String(key) : String(Math.floor(key))
|
|
402
|
+
}
|
|
403
|
+
return S_MT
|
|
404
|
+
}
|
|
405
|
+
// Sorted keys of a map, or indexes (as strings) of a list.
|
|
406
|
+
// Root utility - only uses language facilities.
|
|
407
|
+
function keysof(val) {
|
|
408
|
+
return !isnode(val) ? [] :
|
|
409
|
+
ismap(val) ? Object.keys(val).sort() : val.map((_n, i) => S_MT + i)
|
|
410
|
+
}
|
|
411
|
+
// Value of property with name key in node val is defined.
|
|
412
|
+
// Root utility - only uses language facilities.
|
|
413
|
+
function haskey(val, key) {
|
|
414
|
+
return NONE !== getprop(val, key)
|
|
415
|
+
}
|
|
416
|
+
function items(val, apply) {
|
|
417
|
+
let out = keysof(val).map((k) => [k, val[k]])
|
|
418
|
+
if (null != apply) {
|
|
419
|
+
out = out.map(apply)
|
|
420
|
+
}
|
|
421
|
+
return out
|
|
422
|
+
}
|
|
423
|
+
// To replicate the array spread operator:
|
|
424
|
+
// a=1, b=[2,3], c=[4,5]
|
|
425
|
+
// [a,...b,c] -> [1,2,3,[4,5]]
|
|
426
|
+
// flatten([a,b,[c]]) -> [1,2,3,[4,5]]
|
|
427
|
+
// NOTE: [c] ensures c is not expanded
|
|
428
|
+
function flatten(list, depth) {
|
|
429
|
+
if (!islist(list)) {
|
|
430
|
+
return list
|
|
431
|
+
}
|
|
432
|
+
return list.flat(getdef(depth, 1))
|
|
433
|
+
}
|
|
434
|
+
// Filter item values using check function.
|
|
435
|
+
function filter(val, check) {
|
|
436
|
+
let all = items(val)
|
|
437
|
+
let numall = size(all)
|
|
438
|
+
let out = []
|
|
439
|
+
for (let i = 0; i < numall; i++) {
|
|
440
|
+
if (check(all[i])) {
|
|
441
|
+
out.push(all[i][1])
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return out
|
|
92
445
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
446
|
// Escape regular expression.
|
|
96
447
|
function escre(s) {
|
|
97
|
-
|
|
98
|
-
|
|
448
|
+
// s = null == s ? S_MT : s
|
|
449
|
+
return replace(s, R_ESCAPE_REGEXP, '\\$&')
|
|
99
450
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// Escape URL.
|
|
451
|
+
// Escape URLs.
|
|
103
452
|
function escurl(s) {
|
|
104
|
-
|
|
105
|
-
|
|
453
|
+
s = null == s ? S_MT : s
|
|
454
|
+
return encodeURIComponent(s)
|
|
106
455
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
456
|
+
// Replace a search string (all), or a regexp, in a source string.
|
|
457
|
+
function replace(s, from, to) {
|
|
458
|
+
let rs = s
|
|
459
|
+
let ts = typify(s)
|
|
460
|
+
if (0 === (T_string & ts)) {
|
|
461
|
+
rs = stringify(s)
|
|
462
|
+
}
|
|
463
|
+
else if (0 < ((T_noval | T_null) & ts)) {
|
|
464
|
+
rs = S_MT
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
rs = stringify(s)
|
|
468
|
+
}
|
|
469
|
+
return rs.replace(from, to)
|
|
116
470
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
471
|
+
// Concatenate url part strings, merging sep char as needed.
|
|
472
|
+
function join(arr, sep, url) {
|
|
473
|
+
const sarr = size(arr)
|
|
474
|
+
const sepdef = getdef(sep, S_CM)
|
|
475
|
+
const sepre = 1 === size(sepdef) ? escre(sepdef) : NONE
|
|
476
|
+
const out = filter(items(
|
|
477
|
+
// filter(arr, (n) => null != n[1] && S_MT !== n[1]),
|
|
478
|
+
filter(arr, (n) => (0 < (T_string & typify(n[1]))) && S_MT !== n[1]), (n) => {
|
|
479
|
+
let i = +n[0]
|
|
480
|
+
let s = n[1]
|
|
481
|
+
if (NONE !== sepre && S_MT !== sepre) {
|
|
482
|
+
if (url && 0 === i) {
|
|
483
|
+
s = replace(s, RegExp(sepre + '+$'), S_MT)
|
|
484
|
+
return s
|
|
485
|
+
}
|
|
486
|
+
if (0 < i) {
|
|
487
|
+
s = replace(s, RegExp('^' + sepre + '+'), S_MT)
|
|
488
|
+
}
|
|
489
|
+
if (i < sarr - 1 || !url) {
|
|
490
|
+
s = replace(s, RegExp(sepre + '+$'), S_MT)
|
|
491
|
+
}
|
|
492
|
+
s = replace(s, RegExp('([^' + sepre + '])' + sepre + '+([^' + sepre + '])'), '$1' + sepdef + '$2')
|
|
493
|
+
}
|
|
494
|
+
return s
|
|
495
|
+
}), (n) => S_MT !== n[1])
|
|
496
|
+
.join(sepdef)
|
|
497
|
+
return out
|
|
124
498
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
//
|
|
128
|
-
function
|
|
129
|
-
|
|
499
|
+
// Output JSON in a "standard" format, with 2 space indents, each property on a new line,
|
|
500
|
+
// and spaces after {[: and before ]}. Any "wierd" values (NaN, etc) are output as null.
|
|
501
|
+
// In general, the behaivor of of JavaScript's JSON.stringify(val,null,2) is followed.
|
|
502
|
+
function jsonify(val, flags) {
|
|
503
|
+
let str = S_null
|
|
504
|
+
if (null != val) {
|
|
505
|
+
try {
|
|
506
|
+
const indent = getprop(flags, 'indent', 2)
|
|
507
|
+
str = JSON.stringify(val, null, indent)
|
|
508
|
+
if (NONE === str) {
|
|
509
|
+
str = S_null
|
|
510
|
+
}
|
|
511
|
+
const offset = getprop(flags, 'offset', 0)
|
|
512
|
+
if (0 < offset) {
|
|
513
|
+
// Left offset entire indented JSON so that it aligns with surrounding code
|
|
514
|
+
// indented by offset. Assume first brace is on line with asignment, so not offset.
|
|
515
|
+
str = '{\n' +
|
|
516
|
+
join(items(slice(str.split('\n'), 1), (n) => pad(n[1], 0 - offset - size(n[1]))), '\n')
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
catch (e) {
|
|
520
|
+
str = '__JSONIFY_FAILED__'
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return str
|
|
130
524
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
525
|
+
// Safely stringify a value for humans (NOT JSON!).
|
|
526
|
+
function stringify(val, maxlen, pretty) {
|
|
527
|
+
let valstr = S_MT
|
|
528
|
+
pretty = !!pretty
|
|
529
|
+
if (NONE === val) {
|
|
530
|
+
return pretty ? '<>' : valstr
|
|
531
|
+
}
|
|
532
|
+
if (S_string === typeof val) {
|
|
533
|
+
valstr = val
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
try {
|
|
537
|
+
valstr = JSON.stringify(val, function (_key, val) {
|
|
538
|
+
if (val !== null &&
|
|
539
|
+
typeof val === "object" &&
|
|
540
|
+
!Array.isArray(val)) {
|
|
541
|
+
const sortedObj = {}
|
|
542
|
+
items(val, (n) => {
|
|
543
|
+
sortedObj[n[0]] = val[n[0]]
|
|
544
|
+
})
|
|
545
|
+
return sortedObj
|
|
546
|
+
}
|
|
547
|
+
return val
|
|
548
|
+
})
|
|
549
|
+
valstr = valstr.replace(R_QUOTES, S_MT)
|
|
550
|
+
}
|
|
551
|
+
catch (err) {
|
|
552
|
+
valstr = '__STRINGIFY_FAILED__'
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
if (null != maxlen && -1 < maxlen) {
|
|
556
|
+
let js = valstr.substring(0, maxlen)
|
|
557
|
+
valstr = maxlen < valstr.length ? (js.substring(0, maxlen - 3) + '...') : valstr
|
|
558
|
+
}
|
|
559
|
+
if (pretty) {
|
|
560
|
+
// Indicate deeper JSON levels with different terminal colors (simplistic wrt strings).
|
|
561
|
+
let c = items([81, 118, 213, 39, 208, 201, 45, 190, 129, 51, 160, 121, 226, 33, 207, 69], (n) => '\x1b[38;5;' + n[1] + 'm'), r = '\x1b[0m', d = 0, o = c[0], t = o
|
|
562
|
+
for (const ch of valstr) {
|
|
563
|
+
if (ch === '{' || ch === '[') {
|
|
564
|
+
d++
|
|
565
|
+
o = c[d % c.length]
|
|
566
|
+
t += o + ch
|
|
567
|
+
}
|
|
568
|
+
else if (ch === '}' || ch === ']') {
|
|
569
|
+
t += o + ch
|
|
570
|
+
d--
|
|
571
|
+
o = c[d % c.length]
|
|
572
|
+
}
|
|
573
|
+
else {
|
|
574
|
+
t += o + ch
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return t + r
|
|
578
|
+
}
|
|
579
|
+
return valstr
|
|
135
580
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
581
|
+
// Build a human friendly path string.
|
|
582
|
+
function pathify(val, startin, endin) {
|
|
583
|
+
let pathstr = NONE
|
|
584
|
+
let path = islist(val) ? val :
|
|
585
|
+
S_string == typeof val ? [val] :
|
|
586
|
+
S_number == typeof val ? [val] :
|
|
587
|
+
NONE
|
|
588
|
+
const start = null == startin ? 0 : -1 < startin ? startin : 0
|
|
589
|
+
const end = null == endin ? 0 : -1 < endin ? endin : 0
|
|
590
|
+
if (NONE != path && 0 <= start) {
|
|
591
|
+
path = slice(path, start, path.length - end)
|
|
592
|
+
if (0 === path.length) {
|
|
593
|
+
pathstr = '<root>'
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
pathstr = join(items(filter(path, (n) => iskey(n[1])), (n) => {
|
|
597
|
+
let p = n[1]
|
|
598
|
+
return S_number === typeof p ? S_MT + Math.floor(p) :
|
|
599
|
+
p.replace(R_DOT, S_MT)
|
|
600
|
+
}), S_DT)
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (NONE === pathstr) {
|
|
604
|
+
pathstr = '<unknown-path' + (NONE === val ? S_MT : S_CN + stringify(val, 47)) + '>'
|
|
605
|
+
}
|
|
606
|
+
return pathstr
|
|
158
607
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
608
|
// Clone a JSON-like data structure.
|
|
162
|
-
// NOTE: function values are *not* cloned.
|
|
609
|
+
// NOTE: function and instance values are copied, *not* cloned.
|
|
163
610
|
function clone(val) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
611
|
+
const refs = []
|
|
612
|
+
const reftype = T_function | T_instance
|
|
613
|
+
const replacer = (_k, v) => 0 < (reftype & typify(v)) ?
|
|
614
|
+
(refs.push(v), '`$REF:' + (refs.length - 1) + '`') : v
|
|
615
|
+
const reviver = (_k, v, m) => S_string === typeof v ?
|
|
616
|
+
(m = v.match(R_CLONE_REF), m ? refs[m[1]] : v) : v
|
|
617
|
+
const out = NONE === val ? NONE : JSON.parse(JSON.stringify(val, replacer), reviver)
|
|
618
|
+
return out
|
|
170
619
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
620
|
+
// Define a JSON Object using function arguments.
|
|
621
|
+
function jm(...kv) {
|
|
622
|
+
const kvsize = size(kv)
|
|
623
|
+
const o = {}
|
|
624
|
+
for (let i = 0; i < kvsize; i += 2) {
|
|
625
|
+
let k = getprop(kv, i, '$KEY' + i)
|
|
626
|
+
k = 'string' === typeof k ? k : stringify(k)
|
|
627
|
+
o[k] = getprop(kv, i + 1, null)
|
|
628
|
+
}
|
|
629
|
+
return o
|
|
179
630
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
// If the key is above the list size, append the value.
|
|
187
|
-
// If the value is UNDEF, remove the list element at index key, and shift the
|
|
188
|
-
// remaining elements down. These rules avoids "holes" in the list.
|
|
189
|
-
function setprop(parent, key, val) {
|
|
190
|
-
if (!iskey(key)) {
|
|
191
|
-
return parent
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (ismap(parent)) {
|
|
195
|
-
key = S.empty + key
|
|
196
|
-
if (UNDEF === val) {
|
|
197
|
-
delete parent[key]
|
|
631
|
+
// Define a JSON Array using function arguments.
|
|
632
|
+
function jt(...v) {
|
|
633
|
+
const vsize = size(v)
|
|
634
|
+
const a = new Array(vsize)
|
|
635
|
+
for (let i = 0; i < vsize; i++) {
|
|
636
|
+
a[i] = getprop(v, i, null)
|
|
198
637
|
}
|
|
199
|
-
|
|
200
|
-
|
|
638
|
+
return a
|
|
639
|
+
}
|
|
640
|
+
// Safely delete a property from an object or array element.
|
|
641
|
+
// Undefined arguments and invalid keys are ignored.
|
|
642
|
+
// Returns the (possibly modified) parent.
|
|
643
|
+
// For objects, the property is deleted using the delete operator.
|
|
644
|
+
// For arrays, the element at the index is removed and remaining elements are shifted down.
|
|
645
|
+
// NOTE: parent list may be new list, thus update references.
|
|
646
|
+
function delprop(parent, key) {
|
|
647
|
+
if (!iskey(key)) {
|
|
648
|
+
return parent
|
|
201
649
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
let keyI = +key
|
|
206
|
-
|
|
207
|
-
if (isNaN(keyI)) {
|
|
208
|
-
return parent
|
|
650
|
+
if (ismap(parent)) {
|
|
651
|
+
key = strkey(key)
|
|
652
|
+
delete parent[key]
|
|
209
653
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
654
|
+
else if (islist(parent)) {
|
|
655
|
+
// Ensure key is an integer.
|
|
656
|
+
let keyI = +key
|
|
657
|
+
if (isNaN(keyI)) {
|
|
658
|
+
return parent
|
|
659
|
+
}
|
|
660
|
+
keyI = Math.floor(keyI)
|
|
661
|
+
// Delete list element at position keyI, shifting later elements down.
|
|
662
|
+
const psize = size(parent)
|
|
663
|
+
if (0 <= keyI && keyI < psize) {
|
|
664
|
+
for (let pI = keyI; pI < psize - 1; pI++) {
|
|
665
|
+
parent[pI] = parent[pI + 1]
|
|
666
|
+
}
|
|
667
|
+
parent.length = parent.length - 1
|
|
218
668
|
}
|
|
219
|
-
parent.length = parent.length - 1
|
|
220
|
-
}
|
|
221
669
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
670
|
+
return parent
|
|
671
|
+
}
|
|
672
|
+
// Safely set a property. Undefined arguments and invalid keys are ignored.
|
|
673
|
+
// Returns the (possibly modified) parent.
|
|
674
|
+
// If the parent is a list, and the key is negative, prepend the value.
|
|
675
|
+
// NOTE: If the key is above the list size, append the value; below, prepend.
|
|
676
|
+
// NOTE: parent list may be new list, thus update references.
|
|
677
|
+
function setprop(parent, key, val) {
|
|
678
|
+
if (!iskey(key)) {
|
|
679
|
+
return parent
|
|
226
680
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
681
|
+
if (ismap(parent)) {
|
|
682
|
+
key = S_MT + key
|
|
683
|
+
const pany = parent
|
|
684
|
+
pany[key] = val
|
|
231
685
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
686
|
+
else if (islist(parent)) {
|
|
687
|
+
// Ensure key is an integer.
|
|
688
|
+
let keyI = +key
|
|
689
|
+
if (isNaN(keyI)) {
|
|
690
|
+
return parent
|
|
691
|
+
}
|
|
692
|
+
keyI = Math.floor(keyI)
|
|
693
|
+
// TODO: DELETE list element
|
|
694
|
+
// Set or append value at position keyI, or append if keyI out of bounds.
|
|
695
|
+
if (0 <= keyI) {
|
|
696
|
+
parent[slice(keyI, 0, size(parent) + 1)] = val
|
|
697
|
+
}
|
|
698
|
+
// Prepend value if keyI is negative
|
|
699
|
+
else {
|
|
700
|
+
parent.unshift(val)
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
return parent
|
|
235
704
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
// Walk a data structure depth first.
|
|
705
|
+
// Walk a data structure depth first, applying a function to each value.
|
|
239
706
|
function walk(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
707
|
+
// These arguments are the public interface.
|
|
708
|
+
val,
|
|
709
|
+
// Before descending into a node.
|
|
710
|
+
before,
|
|
711
|
+
// After descending into a node.
|
|
712
|
+
after,
|
|
713
|
+
// Maximum recursive depth, default: 32. Use null for infinite depth.
|
|
714
|
+
maxdepth,
|
|
715
|
+
// These areguments are used for recursive state.
|
|
716
|
+
key, parent, path) {
|
|
717
|
+
if (NONE === path) {
|
|
718
|
+
path = []
|
|
719
|
+
}
|
|
720
|
+
let out = null == before ? val : before(key, val, parent, path)
|
|
721
|
+
maxdepth = null != maxdepth && 0 <= maxdepth ? maxdepth : MAXDEPTH
|
|
722
|
+
if (0 === maxdepth || (null != path && 0 < maxdepth && maxdepth <= path.length)) {
|
|
723
|
+
return out
|
|
724
|
+
}
|
|
725
|
+
if (isnode(out)) {
|
|
726
|
+
for (let [ckey, child] of items(out)) {
|
|
727
|
+
setprop(out, ckey, walk(child, before, after, maxdepth, ckey, out, flatten([getdef(path, []), S_MT + ckey])))
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
out = null == after ? out : after(key, out, parent, path)
|
|
731
|
+
return out
|
|
258
732
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
for (let oI = 1; oI <
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
out = obj
|
|
287
|
-
}
|
|
288
|
-
else {
|
|
289
|
-
// Nodes win, also over nodes of a different kind.
|
|
290
|
-
if (!isnode(out) || (ismap(obj) && islist(out)) || (islist(obj) && ismap(out))) {
|
|
291
|
-
out = obj
|
|
733
|
+
// Merge a list of values into each other. Later values have
|
|
734
|
+
// precedence. Nodes override scalars. Node kinds (list or map)
|
|
735
|
+
// override each other, and do *not* merge. The first element is
|
|
736
|
+
// modified.
|
|
737
|
+
function merge(val, maxdepth) {
|
|
738
|
+
// const md: number = null == maxdepth ? MAXDEPTH : maxdepth < 0 ? 0 : maxdepth
|
|
739
|
+
const md = slice(maxdepth ?? MAXDEPTH, 0)
|
|
740
|
+
let out = NONE
|
|
741
|
+
// Handle edge cases.
|
|
742
|
+
if (!islist(val)) {
|
|
743
|
+
return val
|
|
744
|
+
}
|
|
745
|
+
const list = val
|
|
746
|
+
const lenlist = list.length
|
|
747
|
+
if (0 === lenlist) {
|
|
748
|
+
return NONE
|
|
749
|
+
}
|
|
750
|
+
else if (1 === lenlist) {
|
|
751
|
+
return list[0]
|
|
752
|
+
}
|
|
753
|
+
// Merge a list of values.
|
|
754
|
+
out = getprop(list, 0, {})
|
|
755
|
+
for (let oI = 1; oI < lenlist; oI++) {
|
|
756
|
+
let obj = list[oI]
|
|
757
|
+
if (!isnode(obj)) {
|
|
758
|
+
// Nodes win.
|
|
759
|
+
out = obj
|
|
292
760
|
}
|
|
293
761
|
else {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
762
|
+
// Current value at path end in overriding node.
|
|
763
|
+
let cur = [out]
|
|
764
|
+
// Current value at path end in destination node.
|
|
765
|
+
let dst = [out]
|
|
766
|
+
function before(key, val, _parent, path) {
|
|
767
|
+
const pI = size(path)
|
|
768
|
+
if (md <= pI) {
|
|
769
|
+
setprop(cur[pI - 1], key, val)
|
|
770
|
+
}
|
|
771
|
+
// Scalars just override directly.
|
|
772
|
+
else if (!isnode(val)) {
|
|
773
|
+
cur[pI] = val
|
|
774
|
+
}
|
|
775
|
+
// Descend into override node - Set up correct target in `after` function.
|
|
776
|
+
else {
|
|
777
|
+
// Descend into destination node using same key.
|
|
778
|
+
dst[pI] = 0 < pI ? getprop(dst[pI - 1], key) : dst[pI]
|
|
779
|
+
const tval = dst[pI]
|
|
780
|
+
// Destination empty, so create node (unless override is class instance).
|
|
781
|
+
if (NONE === tval && 0 === (T_instance & typify(val))) {
|
|
782
|
+
cur[pI] = islist(val) ? [] : {}
|
|
783
|
+
}
|
|
784
|
+
// Matching override and destination so continue with their values.
|
|
785
|
+
else if (typify(val) === typify(tval)) {
|
|
786
|
+
cur[pI] = tval
|
|
787
|
+
}
|
|
788
|
+
// Override wins.
|
|
789
|
+
else {
|
|
790
|
+
cur[pI] = val
|
|
791
|
+
// No need to descend when override wins (destination is discarded).
|
|
792
|
+
val = NONE
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
// console.log('BEFORE-END', pathify(path), '@', pI, key,
|
|
796
|
+
// stringify(val, -1, 1), stringify(parent, -1, 1),
|
|
797
|
+
// 'CUR=', stringify(cur, -1, 1), 'DST=', stringify(dst, -1, 1))
|
|
798
|
+
return val
|
|
319
799
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
800
|
+
function after(key, _val, _parent, path) {
|
|
801
|
+
const cI = size(path)
|
|
802
|
+
const target = cur[cI - 1]
|
|
803
|
+
const value = cur[cI]
|
|
804
|
+
// console.log('AFTER-PREP', pathify(path), '@', cI, cur, '|',
|
|
805
|
+
// stringify(key, -1, 1), stringify(value, -1, 1), 'T=', stringify(target, -1, 1))
|
|
806
|
+
setprop(target, key, value)
|
|
807
|
+
return value
|
|
324
808
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
809
|
+
// Walk overriding node, creating paths in output as needed.
|
|
810
|
+
out = walk(obj, before, after, maxdepth)
|
|
811
|
+
// console.log('WALK-DONE', out, obj)
|
|
328
812
|
}
|
|
329
|
-
}
|
|
330
813
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
814
|
+
if (0 === md) {
|
|
815
|
+
out = getelem(list, -1)
|
|
816
|
+
out = islist(out) ? [] : ismap(out) ? {} : out
|
|
817
|
+
}
|
|
818
|
+
return out
|
|
334
819
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
function getpath(path, store, current, state) {
|
|
345
|
-
|
|
346
|
-
const parts = islist(path) ? path : S.string === typeof path ? path.split(S.DT) : UNDEF
|
|
347
|
-
|
|
348
|
-
if (UNDEF === parts) {
|
|
349
|
-
return UNDEF
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
let root = store
|
|
353
|
-
let val = store
|
|
354
|
-
|
|
355
|
-
// An empty path (incl empty string) just finds the store.
|
|
356
|
-
if (null == path || null == store || (1 === parts.length && S.empty === parts[0])) {
|
|
357
|
-
// The actual store data may be in a store sub property, defined by state.base.
|
|
358
|
-
val = getprop(store, getprop(state, S.base), store)
|
|
359
|
-
}
|
|
360
|
-
else if (0 < parts.length) {
|
|
361
|
-
let pI = 0
|
|
362
|
-
|
|
363
|
-
// Relative path uses `current` argument.
|
|
364
|
-
if (S.empty === parts[0]) {
|
|
365
|
-
pI = 1
|
|
366
|
-
root = current
|
|
820
|
+
// Set a value using a path. Missing path parts are created.
|
|
821
|
+
// String paths create only maps. Use a string list to create list parts.
|
|
822
|
+
function setpath(store, path, val, injdef) {
|
|
823
|
+
const pathType = typify(path)
|
|
824
|
+
const parts = 0 < (T_list & pathType) ? path :
|
|
825
|
+
0 < (T_string & pathType) ? path.split(S_DT) :
|
|
826
|
+
0 < (T_number & pathType) ? [path] : NONE
|
|
827
|
+
if (NONE === parts) {
|
|
828
|
+
return NONE
|
|
367
829
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
let
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
val = getprop(val, parts[pI])
|
|
830
|
+
const base = getprop(injdef, S_base)
|
|
831
|
+
const numparts = size(parts)
|
|
832
|
+
let parent = getprop(store, base, store)
|
|
833
|
+
for (let pI = 0; pI < numparts - 1; pI++) {
|
|
834
|
+
const partKey = getelem(parts, pI)
|
|
835
|
+
let nextParent = getprop(parent, partKey)
|
|
836
|
+
if (!isnode(nextParent)) {
|
|
837
|
+
nextParent = 0 < (T_number & typify(getelem(parts, pI + 1))) ? [] : {}
|
|
838
|
+
setprop(parent, partKey, nextParent)
|
|
839
|
+
}
|
|
840
|
+
parent = nextParent
|
|
380
841
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
return val
|
|
842
|
+
if (DELETE === val) {
|
|
843
|
+
delprop(parent, getelem(parts, -1))
|
|
844
|
+
}
|
|
845
|
+
else {
|
|
846
|
+
setprop(parent, getelem(parts, -1), val)
|
|
847
|
+
}
|
|
848
|
+
return parent
|
|
390
849
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
// The path can also have the special syntax $NAME999 where NAME is upper case letters only,
|
|
399
|
-
// and 999 is any digits, which are discarded. This syntax specifies the name of a transform,
|
|
400
|
-
// and optionally allows transforms to be ordered by alphanumeric sorting.
|
|
401
|
-
function injectstr(val, store, current, state) {
|
|
402
|
-
if (S.string !== typeof val) {
|
|
403
|
-
return S.empty
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
let out = val
|
|
407
|
-
const m = val.match(/^`(\$[A-Z]+|[^`]+)[0-9]*`$/)
|
|
408
|
-
|
|
409
|
-
// Full string is an injection.
|
|
410
|
-
if (m) {
|
|
411
|
-
if (state) {
|
|
412
|
-
state.full = true
|
|
850
|
+
function getpath(store, path, injdef) {
|
|
851
|
+
// Operate on a string array.
|
|
852
|
+
const parts = islist(path) ? path :
|
|
853
|
+
'string' === typeof path ? path.split(S_DT) :
|
|
854
|
+
'number' === typeof path ? [strkey(path)] : NONE
|
|
855
|
+
if (NONE === parts) {
|
|
856
|
+
return NONE
|
|
413
857
|
}
|
|
414
|
-
let
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
else {
|
|
424
|
-
out = val.replace(/`([^`]+)`/g,
|
|
425
|
-
(_m, ref) => {
|
|
426
|
-
ref = 3 < ref.length ? ref.replace(/\$BT/g, S.BT).replace(/\$DS/g, S.DS) : ref
|
|
427
|
-
if (state) {
|
|
428
|
-
state.full = false
|
|
429
|
-
}
|
|
430
|
-
const found = getpath(ref, store, current, state)
|
|
431
|
-
|
|
432
|
-
return UNDEF === found ? S.empty :
|
|
433
|
-
S.object === typeof found ? JSON.stringify(found) :
|
|
434
|
-
found
|
|
435
|
-
})
|
|
436
|
-
|
|
437
|
-
// Also call the handler on the entire string.
|
|
438
|
-
if (state.handler) {
|
|
439
|
-
state.full = true
|
|
440
|
-
out = state.handler(state, out, current, val, store)
|
|
858
|
+
// let root = store
|
|
859
|
+
let val = store
|
|
860
|
+
const base = getprop(injdef, S_base)
|
|
861
|
+
const src = getprop(store, base, store)
|
|
862
|
+
const numparts = size(parts)
|
|
863
|
+
const dparent = getprop(injdef, 'dparent')
|
|
864
|
+
// An empty path (incl empty string) just finds the store.
|
|
865
|
+
if (null == path || null == store || (1 === numparts && S_MT === parts[0])) {
|
|
866
|
+
val = src
|
|
441
867
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
868
|
+
else if (0 < numparts) {
|
|
869
|
+
// Check for $ACTIONs
|
|
870
|
+
if (1 === numparts) {
|
|
871
|
+
val = getprop(store, parts[0])
|
|
872
|
+
}
|
|
873
|
+
if (!isfunc(val)) {
|
|
874
|
+
val = src
|
|
875
|
+
const m = parts[0].match(R_META_PATH)
|
|
876
|
+
if (m && injdef && injdef.meta) {
|
|
877
|
+
val = getprop(injdef.meta, m[1])
|
|
878
|
+
parts[0] = m[3]
|
|
879
|
+
}
|
|
880
|
+
const dpath = getprop(injdef, 'dpath')
|
|
881
|
+
for (let pI = 0; NONE !== val && pI < numparts; pI++) {
|
|
882
|
+
let part = parts[pI]
|
|
883
|
+
if (injdef && S_DKEY === part) {
|
|
884
|
+
part = getprop(injdef, S_key)
|
|
885
|
+
}
|
|
886
|
+
else if (injdef && part.startsWith('$GET:')) {
|
|
887
|
+
// $GET:path$ -> get store value, use as path part (string)
|
|
888
|
+
part = stringify(getpath(src, slice(part, 5, -1)))
|
|
889
|
+
}
|
|
890
|
+
else if (injdef && part.startsWith('$REF:')) {
|
|
891
|
+
// $REF:refpath$ -> get spec value, use as path part (string)
|
|
892
|
+
part = stringify(getpath(getprop(store, S_DSPEC), slice(part, 5, -1)))
|
|
893
|
+
}
|
|
894
|
+
else if (injdef && part.startsWith('$META:')) {
|
|
895
|
+
// $META:metapath$ -> get meta value, use as path part (string)
|
|
896
|
+
part = stringify(getpath(getprop(injdef, 'meta'), slice(part, 6, -1)))
|
|
897
|
+
}
|
|
898
|
+
// $$ escapes $
|
|
899
|
+
part = part.replace(R_DOUBLE_DOLLAR, '$')
|
|
900
|
+
if (S_MT === part) {
|
|
901
|
+
let ascends = 0
|
|
902
|
+
while (S_MT === parts[1 + pI]) {
|
|
903
|
+
ascends++
|
|
904
|
+
pI++
|
|
905
|
+
}
|
|
906
|
+
if (injdef && 0 < ascends) {
|
|
907
|
+
if (pI === parts.length - 1) {
|
|
908
|
+
ascends--
|
|
909
|
+
}
|
|
910
|
+
if (0 === ascends) {
|
|
911
|
+
val = dparent
|
|
912
|
+
}
|
|
913
|
+
else {
|
|
914
|
+
// const fullpath = slice(dpath, 0 - ascends).concat(parts.slice(pI + 1))
|
|
915
|
+
const fullpath = flatten([slice(dpath, 0 - ascends), parts.slice(pI + 1)])
|
|
916
|
+
if (ascends <= size(dpath)) {
|
|
917
|
+
val = getpath(store, fullpath)
|
|
918
|
+
}
|
|
919
|
+
else {
|
|
920
|
+
val = NONE
|
|
921
|
+
}
|
|
922
|
+
break
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
else {
|
|
926
|
+
val = dparent
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
else {
|
|
930
|
+
val = getprop(val, part)
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
// Inj may provide a custom handler to modify found value.
|
|
936
|
+
const handler = getprop(injdef, 'handler')
|
|
937
|
+
if (null != injdef && isfunc(handler)) {
|
|
938
|
+
const ref = pathify(path)
|
|
939
|
+
val = handler(injdef, val, ref, store)
|
|
940
|
+
}
|
|
941
|
+
// console.log('GETPATH', path, val)
|
|
942
|
+
return val
|
|
445
943
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
//
|
|
449
|
-
//
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
current,
|
|
541
|
-
childstate,
|
|
542
|
-
)
|
|
543
|
-
okI = childstate.keyI
|
|
544
|
-
|
|
545
|
-
childstate.mode = S.MKEYPOST
|
|
546
|
-
injectstr(origkey, store, current, childstate)
|
|
547
|
-
okI = childstate.keyI
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// Inject paths into string scalars.
|
|
553
|
-
else if (S.string === valtype) {
|
|
554
|
-
state.mode = S.MVAL
|
|
555
|
-
const newval = injectstr(val, store, current, state)
|
|
556
|
-
val = newval
|
|
557
|
-
|
|
558
|
-
setprop(state.parent, state.key, newval)
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// Other scalars are left in place unchanged.
|
|
562
|
-
|
|
563
|
-
// Custom modification.
|
|
564
|
-
if (modify) {
|
|
565
|
-
modify(
|
|
566
|
-
state.key,
|
|
567
|
-
val,
|
|
568
|
-
state.parent,
|
|
569
|
-
state,
|
|
570
|
-
current,
|
|
571
|
-
store
|
|
572
|
-
)
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
// Original val reference may no longer be correct.
|
|
576
|
-
const out = getprop(state.parent, S.DTOP)
|
|
577
|
-
|
|
578
|
-
// Output is only needed at the top level as the final result
|
|
579
|
-
return out
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
// Default inject handler for transforms. If the path resolves to a function,
|
|
584
|
-
// call the function passing the injection state. This is how transforms operate.
|
|
585
|
-
const injecthandler = (state, val, current, ref, store) => {
|
|
586
|
-
let out = val
|
|
587
|
-
|
|
588
|
-
if (S.function === typeof val && ref.startsWith('$')) {
|
|
589
|
-
out = val(state, val, current, store)
|
|
590
|
-
}
|
|
591
|
-
else if (S.MVAL === state.mode && state.full) {
|
|
592
|
-
setprop(state.parent, state.key, val)
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
return out
|
|
944
|
+
// Inject values from a data store into a node recursively, resolving
|
|
945
|
+
// paths against the store, or current if they are local. The modify
|
|
946
|
+
// argument allows custom modification of the result. The inj
|
|
947
|
+
// (Injection) argument is used to maintain recursive state.
|
|
948
|
+
function inject(val, store, injdef) {
|
|
949
|
+
const valtype = typeof val
|
|
950
|
+
let inj = injdef
|
|
951
|
+
// Create state if at root of injection. The input value is placed
|
|
952
|
+
// inside a virtual parent holder to simplify edge cases.
|
|
953
|
+
if (NONE === injdef || null == injdef.mode) {
|
|
954
|
+
// Set up state assuming we are starting in the virtual parent.
|
|
955
|
+
inj = new Injection(val, { [S_DTOP]: val })
|
|
956
|
+
inj.dparent = store
|
|
957
|
+
inj.errs = getprop(store, S_DERRS, [])
|
|
958
|
+
inj.meta.__d = 0
|
|
959
|
+
if (NONE !== injdef) {
|
|
960
|
+
inj.modify = null == injdef.modify ? inj.modify : injdef.modify
|
|
961
|
+
inj.extra = null == injdef.extra ? inj.extra : injdef.extra
|
|
962
|
+
inj.meta = null == injdef.meta ? inj.meta : injdef.meta
|
|
963
|
+
inj.handler = null == injdef.handler ? inj.handler : injdef.handler
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
inj.descend()
|
|
967
|
+
// console.log('INJ-START', val, inj.mode, inj.key, inj.val,
|
|
968
|
+
// 't=', inj.path, 'P=', inj.parent, 'dp=', inj.dparent, 'ST=', store.$TOP)
|
|
969
|
+
// Descend into node.
|
|
970
|
+
if (isnode(val)) {
|
|
971
|
+
// Keys are sorted alphanumerically to ensure determinism.
|
|
972
|
+
// Injection transforms ($FOO) are processed *after* other keys.
|
|
973
|
+
// NOTE: the optional digits suffix of the transform can thus be
|
|
974
|
+
// used to order the transforms.
|
|
975
|
+
let nodekeys
|
|
976
|
+
nodekeys = keysof(val)
|
|
977
|
+
if (ismap(val)) {
|
|
978
|
+
nodekeys = flatten([
|
|
979
|
+
filter(nodekeys, (n => !n[1].includes(S_DS))),
|
|
980
|
+
filter(nodekeys, (n => n[1].includes(S_DS))),
|
|
981
|
+
])
|
|
982
|
+
}
|
|
983
|
+
else {
|
|
984
|
+
nodekeys = keysof(val)
|
|
985
|
+
}
|
|
986
|
+
// Each child key-value pair is processed in three injection phases:
|
|
987
|
+
// 1. inj.mode=M_KEYPRE - Key string is injected, returning a possibly altered key.
|
|
988
|
+
// 2. inj.mode=M_VAL - The child value is injected.
|
|
989
|
+
// 3. inj.mode=M_KEYPOST - Key string is injected again, allowing child mutation.
|
|
990
|
+
for (let nkI = 0; nkI < nodekeys.length; nkI++) {
|
|
991
|
+
const childinj = inj.child(nkI, nodekeys)
|
|
992
|
+
const nodekey = childinj.key
|
|
993
|
+
childinj.mode = M_KEYPRE
|
|
994
|
+
// Peform the key:pre mode injection on the child key.
|
|
995
|
+
const prekey = _injectstr(nodekey, store, childinj)
|
|
996
|
+
// The injection may modify child processing.
|
|
997
|
+
nkI = childinj.keyI
|
|
998
|
+
nodekeys = childinj.keys
|
|
999
|
+
// Prevent further processing by returning an undefined prekey
|
|
1000
|
+
if (NONE !== prekey) {
|
|
1001
|
+
childinj.val = getprop(val, prekey)
|
|
1002
|
+
childinj.mode = M_VAL
|
|
1003
|
+
// Perform the val mode injection on the child value.
|
|
1004
|
+
// NOTE: return value is not used.
|
|
1005
|
+
inject(childinj.val, store, childinj)
|
|
1006
|
+
// The injection may modify child processing.
|
|
1007
|
+
nkI = childinj.keyI
|
|
1008
|
+
nodekeys = childinj.keys
|
|
1009
|
+
// Peform the key:post mode injection on the child key.
|
|
1010
|
+
childinj.mode = M_KEYPOST
|
|
1011
|
+
_injectstr(nodekey, store, childinj)
|
|
1012
|
+
// The injection may modify child processing.
|
|
1013
|
+
nkI = childinj.keyI
|
|
1014
|
+
nodekeys = childinj.keys
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
// Inject paths into string scalars.
|
|
1019
|
+
else if (S_string === valtype) {
|
|
1020
|
+
inj.mode = M_VAL
|
|
1021
|
+
val = _injectstr(val, store, inj)
|
|
1022
|
+
if (SKIP !== val) {
|
|
1023
|
+
inj.setval(val)
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
// Custom modification.
|
|
1027
|
+
if (inj.modify && SKIP !== val) {
|
|
1028
|
+
let mkey = inj.key
|
|
1029
|
+
let mparent = inj.parent
|
|
1030
|
+
let mval = getprop(mparent, mkey)
|
|
1031
|
+
inj.modify(mval, mkey, mparent, inj, store)
|
|
1032
|
+
}
|
|
1033
|
+
// console.log('INJ-VAL', val)
|
|
1034
|
+
inj.val = val
|
|
1035
|
+
// Original val reference may no longer be correct.
|
|
1036
|
+
// This return value is only used as the top level result.
|
|
1037
|
+
return getprop(inj.parent, S_DTOP)
|
|
596
1038
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
// The transform_* functions are define inject handlers (see InjectHandler).
|
|
600
|
-
|
|
601
|
-
|
|
1039
|
+
// The transform_* functions are special command inject handlers (see Injector).
|
|
602
1040
|
// Delete a key from a map or list.
|
|
603
|
-
const transform_DELETE = (
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
return UNDEF
|
|
1041
|
+
const transform_DELETE = (inj) => {
|
|
1042
|
+
inj.setval(NONE)
|
|
1043
|
+
return NONE
|
|
607
1044
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
1045
|
// Copy value from source data.
|
|
611
|
-
const transform_COPY = (
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
out = key
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
out = getprop(current, key)
|
|
620
|
-
setprop(parent, key, out)
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
return out
|
|
1046
|
+
const transform_COPY = (inj, _val) => {
|
|
1047
|
+
const ijname = 'COPY'
|
|
1048
|
+
if (!checkPlacement(M_VAL, ijname, T_any, inj)) {
|
|
1049
|
+
return NONE
|
|
1050
|
+
}
|
|
1051
|
+
let out = getprop(inj.dparent, inj.key)
|
|
1052
|
+
inj.setval(out)
|
|
1053
|
+
return out
|
|
624
1054
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
1055
|
// As a value, inject the key of the parent node.
|
|
628
1056
|
// As a key, defined the name of the key property in the source object.
|
|
629
|
-
const transform_KEY = (
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
1057
|
+
const transform_KEY = (inj) => {
|
|
1058
|
+
const { mode, path, parent } = inj
|
|
1059
|
+
// Do nothing in val mode - not an error.
|
|
1060
|
+
if (M_VAL !== mode) {
|
|
1061
|
+
return NONE
|
|
1062
|
+
}
|
|
1063
|
+
// Key is defined by $KEY meta property.
|
|
1064
|
+
const keyspec = getprop(parent, S_BKEY)
|
|
1065
|
+
if (NONE !== keyspec) {
|
|
1066
|
+
delprop(parent, S_BKEY)
|
|
1067
|
+
return getprop(inj.dparent, keyspec)
|
|
1068
|
+
}
|
|
1069
|
+
// Key is defined within general purpose $META object.
|
|
1070
|
+
// return getprop(getprop(parent, S_BANNO), S_KEY, getprop(path, path.length - 2))
|
|
1071
|
+
return getprop(getprop(parent, S_BANNO), S_KEY, getelem(path, -2))
|
|
643
1072
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
return UNDEF
|
|
1073
|
+
// Annotate node. Does nothing itself, just used by
|
|
1074
|
+
// other injectors, and is removed when called.
|
|
1075
|
+
const transform_ANNO = (inj) => {
|
|
1076
|
+
const { parent } = inj
|
|
1077
|
+
delprop(parent, S_BANNO)
|
|
1078
|
+
return NONE
|
|
651
1079
|
}
|
|
652
|
-
|
|
653
|
-
|
|
654
1080
|
// Merge a list of objects into the current object.
|
|
655
1081
|
// Must be a key in an object. The value is merged over the current object.
|
|
656
1082
|
// If the value is an array, the elements are first merged using `merge`.
|
|
657
1083
|
// If the value is the empty string, merge the top level store.
|
|
658
1084
|
// Format: { '`$MERGE`': '`source-path`' | ['`source-paths`', ...] }
|
|
659
|
-
const transform_MERGE = (
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
return key
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
return UNDEF
|
|
1085
|
+
const transform_MERGE = (inj) => {
|
|
1086
|
+
const { mode, key, parent } = inj
|
|
1087
|
+
// Ensures $MERGE is removed from parent list (val mode).
|
|
1088
|
+
let out = NONE
|
|
1089
|
+
if (M_KEYPRE === mode) {
|
|
1090
|
+
out = key
|
|
1091
|
+
}
|
|
1092
|
+
// Operate after child values have been transformed.
|
|
1093
|
+
else if (M_KEYPOST === mode) {
|
|
1094
|
+
out = key
|
|
1095
|
+
let args = getprop(parent, key)
|
|
1096
|
+
args = Array.isArray(args) ? args : [args]
|
|
1097
|
+
// Remove the $MERGE command from a parent map.
|
|
1098
|
+
inj.setval(NONE)
|
|
1099
|
+
// Literals in the parent have precedence, but we still merge onto
|
|
1100
|
+
// the parent object, so that node tree references are not changed.
|
|
1101
|
+
const mergelist = flatten([[parent], args, [clone(parent)]])
|
|
1102
|
+
merge(mergelist)
|
|
1103
|
+
}
|
|
1104
|
+
return out
|
|
683
1105
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
1106
|
// Convert a node to a list.
|
|
687
1107
|
// Format: ['`$EACH`', '`source-path-of-node`', child-template]
|
|
688
|
-
const transform_EACH = (
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
store
|
|
693
|
-
) => {
|
|
694
|
-
const { mode, keys, path, parent, nodes } = state
|
|
695
|
-
|
|
696
|
-
// Remove arguments to avoid spurious processing.
|
|
697
|
-
if (keys) {
|
|
698
|
-
keys.length = 1
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
// Defensive context checks.
|
|
702
|
-
if (S.MVAL !== mode || null == path || null == nodes) {
|
|
703
|
-
return UNDEF
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// Get arguments.
|
|
707
|
-
const srcpath = parent[1] // Path to source data.
|
|
708
|
-
const child = clone(parent[2]) // Child template.
|
|
709
|
-
|
|
710
|
-
// Source data
|
|
711
|
-
const src = getpath(srcpath, store, current, state)
|
|
712
|
-
|
|
713
|
-
// Create parallel data structures:
|
|
714
|
-
// source entries :: child templates
|
|
715
|
-
let tcurrent = []
|
|
716
|
-
let tval = []
|
|
717
|
-
|
|
718
|
-
const tkey = path[path.length - 2]
|
|
719
|
-
const target = nodes[path.length - 2] || nodes[path.length - 1]
|
|
720
|
-
|
|
721
|
-
if (isnode(src)) {
|
|
722
|
-
if (islist(src)) {
|
|
723
|
-
tval = src.map(() => clone(child))
|
|
1108
|
+
const transform_EACH = (inj, _val, _ref, store) => {
|
|
1109
|
+
const ijname = 'EACH'
|
|
1110
|
+
if (!checkPlacement(M_VAL, ijname, T_list, inj)) {
|
|
1111
|
+
return NONE
|
|
724
1112
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
1113
|
+
// Remove remaining keys to avoid spurious processing.
|
|
1114
|
+
slice(inj.keys, 0, 1, true)
|
|
1115
|
+
// const [err, srcpath, child] = injectorArgs([T_string, T_any], inj)
|
|
1116
|
+
const [err, srcpath, child] = injectorArgs([T_string, T_any], slice(inj.parent, 1))
|
|
1117
|
+
if (NONE !== err) {
|
|
1118
|
+
inj.errs.push('$' + ijname + ': ' + err)
|
|
1119
|
+
return NONE
|
|
732
1120
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
1121
|
+
// Source data.
|
|
1122
|
+
const srcstore = getprop(store, inj.base, store)
|
|
1123
|
+
const src = getpath(srcstore, srcpath, inj)
|
|
1124
|
+
const srctype = typify(src)
|
|
1125
|
+
// Create parallel data structures:
|
|
1126
|
+
// source entries :: child templates
|
|
1127
|
+
let tcur = []
|
|
1128
|
+
let tval = []
|
|
1129
|
+
const tkey = getelem(inj.path, -2)
|
|
1130
|
+
const target = getelem(inj.nodes, -2, () => getelem(inj.nodes, -1))
|
|
1131
|
+
// Create clones of the child template for each value of the current soruce.
|
|
1132
|
+
if (0 < (T_list & srctype)) {
|
|
1133
|
+
tval = items(src, () => clone(child))
|
|
1134
|
+
}
|
|
1135
|
+
else if (0 < (T_map & srctype)) {
|
|
1136
|
+
tval = items(src, (n => merge([
|
|
1137
|
+
clone(child),
|
|
1138
|
+
// Make a note of the key for $KEY transforms.
|
|
1139
|
+
{ [S_BANNO]: { KEY: n[0] } }
|
|
1140
|
+
], 1)))
|
|
1141
|
+
}
|
|
1142
|
+
let rval = []
|
|
1143
|
+
if (0 < size(tval)) {
|
|
1144
|
+
tcur = null == src ? NONE : Object.values(src)
|
|
1145
|
+
const ckey = getelem(inj.path, -2)
|
|
1146
|
+
const tpath = slice(inj.path, -1)
|
|
1147
|
+
const dpath = flatten([S_DTOP, srcpath.split(S_DT), '$:' + ckey])
|
|
1148
|
+
// Parent structure.
|
|
1149
|
+
tcur = { [ckey]: tcur }
|
|
1150
|
+
if (1 < size(tpath)) {
|
|
1151
|
+
const pkey = getelem(inj.path, -3, S_DTOP)
|
|
1152
|
+
tcur = { [pkey]: tcur }
|
|
1153
|
+
dpath.push('$:' + pkey)
|
|
1154
|
+
}
|
|
1155
|
+
const tinj = inj.child(0, [ckey])
|
|
1156
|
+
tinj.path = tpath
|
|
1157
|
+
tinj.nodes = slice(inj.nodes, -1)
|
|
1158
|
+
tinj.parent = getelem(tinj.nodes, -1)
|
|
1159
|
+
setprop(tinj.parent, ckey, tval)
|
|
1160
|
+
tinj.val = tval
|
|
1161
|
+
tinj.dpath = dpath
|
|
1162
|
+
tinj.dparent = tcur
|
|
1163
|
+
inject(tval, store, tinj)
|
|
1164
|
+
rval = tinj.val
|
|
1165
|
+
}
|
|
1166
|
+
// _updateAncestors(inj, target, tkey, rval)
|
|
1167
|
+
setprop(target, tkey, rval)
|
|
1168
|
+
// Prevent callee from damaging first list entry (since we are in `val` mode).
|
|
1169
|
+
return rval[0]
|
|
752
1170
|
}
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
1171
|
// Convert a node to a map.
|
|
757
|
-
// Format: { '`$PACK`':['
|
|
758
|
-
const transform_PACK = (
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
tval
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
1172
|
+
// Format: { '`$PACK`':['source-path', child-template]}
|
|
1173
|
+
const transform_PACK = (inj, _val, _ref, store) => {
|
|
1174
|
+
const { mode, key, path, parent, nodes } = inj
|
|
1175
|
+
const ijname = 'EACH'
|
|
1176
|
+
if (!checkPlacement(M_KEYPRE, ijname, T_map, inj)) {
|
|
1177
|
+
return NONE
|
|
1178
|
+
}
|
|
1179
|
+
// Get arguments.
|
|
1180
|
+
const args = getprop(parent, key)
|
|
1181
|
+
const [err, srcpath, origchildspec] = injectorArgs([T_string, T_any], args)
|
|
1182
|
+
if (NONE !== err) {
|
|
1183
|
+
inj.errs.push('$' + ijname + ': ' + err)
|
|
1184
|
+
return NONE
|
|
1185
|
+
}
|
|
1186
|
+
// Find key and target node.
|
|
1187
|
+
const tkey = getelem(path, -2)
|
|
1188
|
+
const pathsize = size(path)
|
|
1189
|
+
const target = getelem(nodes, pathsize - 2, () => getelem(nodes, pathsize - 1))
|
|
1190
|
+
// Source data
|
|
1191
|
+
const srcstore = getprop(store, inj.base, store)
|
|
1192
|
+
let src = getpath(srcstore, srcpath, inj)
|
|
1193
|
+
// Prepare source as a list.
|
|
1194
|
+
if (!islist(src)) {
|
|
1195
|
+
if (ismap(src)) {
|
|
1196
|
+
src = items(src, (item) => {
|
|
1197
|
+
setprop(item[1], S_BANNO, { KEY: item[0] })
|
|
1198
|
+
return item[1]
|
|
1199
|
+
})
|
|
1200
|
+
}
|
|
1201
|
+
else {
|
|
1202
|
+
src = NONE
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
if (null == src) {
|
|
1206
|
+
return NONE
|
|
1207
|
+
}
|
|
1208
|
+
// Get keypath.
|
|
1209
|
+
const keypath = getprop(origchildspec, S_BKEY)
|
|
1210
|
+
const childspec = delprop(origchildspec, S_BKEY)
|
|
1211
|
+
const child = getprop(childspec, S_BVAL, childspec)
|
|
1212
|
+
// Build parallel target object.
|
|
1213
|
+
let tval = {}
|
|
1214
|
+
items(src, (item) => {
|
|
1215
|
+
const srckey = item[0]
|
|
1216
|
+
const srcnode = item[1]
|
|
1217
|
+
let key = srckey
|
|
1218
|
+
if (NONE !== keypath) {
|
|
1219
|
+
if (keypath.startsWith('`')) {
|
|
1220
|
+
key = inject(keypath, merge([{}, store, { $TOP: srcnode }], 1))
|
|
1221
|
+
}
|
|
1222
|
+
else {
|
|
1223
|
+
key = getpath(srcnode, keypath, inj)
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
const tchild = clone(child)
|
|
1227
|
+
setprop(tval, key, tchild)
|
|
1228
|
+
const anno = getprop(srcnode, S_BANNO)
|
|
1229
|
+
if (NONE === anno) {
|
|
1230
|
+
delprop(tchild, S_BANNO)
|
|
1231
|
+
}
|
|
1232
|
+
else {
|
|
1233
|
+
setprop(tchild, S_BANNO, anno)
|
|
1234
|
+
}
|
|
1235
|
+
})
|
|
1236
|
+
let rval = {}
|
|
1237
|
+
if (!isempty(tval)) {
|
|
1238
|
+
// Build parallel source object.
|
|
1239
|
+
let tsrc = {}
|
|
1240
|
+
src.reduce((a, n, i) => {
|
|
1241
|
+
let kn = null == keypath ? i :
|
|
1242
|
+
keypath.startsWith('`') ?
|
|
1243
|
+
inject(keypath, merge([{}, store, { $TOP: n }], 1)) :
|
|
1244
|
+
getpath(n, keypath, inj)
|
|
1245
|
+
setprop(a, kn, n)
|
|
1246
|
+
return a
|
|
1247
|
+
}, tsrc)
|
|
1248
|
+
const tpath = slice(inj.path, -1)
|
|
1249
|
+
const ckey = getelem(inj.path, -2)
|
|
1250
|
+
const dpath = flatten([S_DTOP, srcpath.split(S_DT), '$:' + ckey])
|
|
1251
|
+
let tcur = { [ckey]: tsrc }
|
|
1252
|
+
if (1 < size(tpath)) {
|
|
1253
|
+
const pkey = getelem(inj.path, -3, S_DTOP)
|
|
1254
|
+
tcur = { [pkey]: tcur }
|
|
1255
|
+
dpath.push('$:' + pkey)
|
|
1256
|
+
}
|
|
1257
|
+
const tinj = inj.child(0, [ckey])
|
|
1258
|
+
tinj.path = tpath
|
|
1259
|
+
tinj.nodes = slice(inj.nodes, -1)
|
|
1260
|
+
tinj.parent = getelem(tinj.nodes, -1)
|
|
1261
|
+
tinj.val = tval
|
|
1262
|
+
tinj.dpath = dpath
|
|
1263
|
+
tinj.dparent = tcur
|
|
1264
|
+
inject(tval, store, tinj)
|
|
1265
|
+
rval = tinj.val
|
|
1266
|
+
}
|
|
1267
|
+
// _updateAncestors(inj, target, tkey, rval)
|
|
1268
|
+
setprop(target, tkey, rval)
|
|
1269
|
+
// Drop transform key.
|
|
1270
|
+
return NONE
|
|
1271
|
+
}
|
|
1272
|
+
// TODO: not found ref should removed key (setprop NONE)
|
|
1273
|
+
// Reference original spec (enables recursice transformations)
|
|
1274
|
+
// Format: ['`$REF`', '`spec-path`']
|
|
1275
|
+
const transform_REF = (inj, val, _ref, store) => {
|
|
1276
|
+
const { nodes } = inj
|
|
1277
|
+
if (M_VAL !== inj.mode) {
|
|
1278
|
+
return NONE
|
|
1279
|
+
}
|
|
1280
|
+
// Get arguments: ['`$REF`', 'ref-path'].
|
|
1281
|
+
const refpath = getprop(inj.parent, 1)
|
|
1282
|
+
inj.keyI = size(inj.keys)
|
|
1283
|
+
// Spec reference.
|
|
1284
|
+
const spec = getprop(store, S_DSPEC)()
|
|
1285
|
+
const dpath = slice(inj.path, 1)
|
|
1286
|
+
const ref = getpath(spec, refpath, {
|
|
1287
|
+
// TODO: test relative refs
|
|
1288
|
+
// dpath: inj.path.slice(1),
|
|
1289
|
+
dpath,
|
|
1290
|
+
// dparent: getpath(spec, inj.path.slice(1))
|
|
1291
|
+
dparent: getpath(spec, dpath),
|
|
1292
|
+
})
|
|
1293
|
+
let hasSubRef = false
|
|
1294
|
+
if (isnode(ref)) {
|
|
1295
|
+
walk(ref, (_k, v) => {
|
|
1296
|
+
if ('`$REF`' === v) {
|
|
1297
|
+
hasSubRef = true
|
|
1298
|
+
}
|
|
1299
|
+
return v
|
|
1300
|
+
})
|
|
1301
|
+
}
|
|
1302
|
+
let tref = clone(ref)
|
|
1303
|
+
const cpath = slice(inj.path, -3)
|
|
1304
|
+
const tpath = slice(inj.path, -1)
|
|
1305
|
+
let tcur = getpath(store, cpath)
|
|
1306
|
+
let tval = getpath(store, tpath)
|
|
1307
|
+
let rval = NONE
|
|
1308
|
+
if (!hasSubRef || NONE !== tval) {
|
|
1309
|
+
const tinj = inj.child(0, [getelem(tpath, -1)])
|
|
1310
|
+
tinj.path = tpath
|
|
1311
|
+
tinj.nodes = slice(inj.nodes, -1)
|
|
1312
|
+
tinj.parent = getelem(nodes, -2)
|
|
1313
|
+
tinj.val = tref
|
|
1314
|
+
tinj.dpath = flatten([cpath])
|
|
1315
|
+
tinj.dparent = tcur
|
|
1316
|
+
inject(tref, store, tinj)
|
|
1317
|
+
rval = tinj.val
|
|
1318
|
+
}
|
|
1319
|
+
else {
|
|
1320
|
+
rval = NONE
|
|
1321
|
+
}
|
|
1322
|
+
const grandparent = inj.setval(rval, 2)
|
|
1323
|
+
if (islist(grandparent) && inj.prior) {
|
|
1324
|
+
inj.prior.keyI--
|
|
1325
|
+
}
|
|
1326
|
+
return val
|
|
1327
|
+
}
|
|
1328
|
+
const transform_FORMAT = (inj, _val, _ref, store) => {
|
|
1329
|
+
// console.log('FORMAT-START', inj, _val)
|
|
1330
|
+
// Remove remaining keys to avoid spurious processing.
|
|
1331
|
+
slice(inj.keys, 0, 1, true)
|
|
1332
|
+
if (M_VAL !== inj.mode) {
|
|
1333
|
+
return NONE
|
|
1334
|
+
}
|
|
1335
|
+
// Get arguments: ['`$FORMAT`', 'name', child].
|
|
1336
|
+
// TODO: EACH and PACK should accept customm functions too
|
|
1337
|
+
const name = getprop(inj.parent, 1)
|
|
1338
|
+
const child = getprop(inj.parent, 2)
|
|
1339
|
+
// Source data.
|
|
1340
|
+
const tkey = getelem(inj.path, -2)
|
|
1341
|
+
const target = getelem(inj.nodes, -2, () => getelem(inj.nodes, -1))
|
|
1342
|
+
const cinj = injectChild(child, store, inj)
|
|
1343
|
+
const resolved = cinj.val
|
|
1344
|
+
let formatter = 0 < (T_function & typify(name)) ? name : getprop(FORMATTER, name)
|
|
1345
|
+
if (NONE === formatter) {
|
|
1346
|
+
inj.errs.push('$FORMAT: unknown format: ' + name + '.')
|
|
1347
|
+
return NONE
|
|
1348
|
+
}
|
|
1349
|
+
let out = walk(resolved, formatter)
|
|
1350
|
+
setprop(target, tkey, out)
|
|
1351
|
+
// _updateAncestors(inj, target, tkey, out)
|
|
1352
|
+
return out
|
|
1353
|
+
}
|
|
1354
|
+
const FORMATTER = {
|
|
1355
|
+
identity: (_k, v) => v,
|
|
1356
|
+
upper: (_k, v) => isnode(v) ? v : ('' + v).toUpperCase(),
|
|
1357
|
+
lower: (_k, v) => isnode(v) ? v : ('' + v).toLowerCase(),
|
|
1358
|
+
string: (_k, v) => isnode(v) ? v : ('' + v),
|
|
1359
|
+
number: (_k, v) => {
|
|
1360
|
+
if (isnode(v)) {
|
|
1361
|
+
return v
|
|
1362
|
+
}
|
|
1363
|
+
else {
|
|
1364
|
+
let n = Number(v)
|
|
1365
|
+
if (isNaN(n)) {
|
|
1366
|
+
n = 0
|
|
1367
|
+
}
|
|
1368
|
+
return n
|
|
1369
|
+
}
|
|
1370
|
+
},
|
|
1371
|
+
integer: (_k, v) => {
|
|
1372
|
+
if (isnode(v)) {
|
|
1373
|
+
return v
|
|
1374
|
+
}
|
|
1375
|
+
else {
|
|
1376
|
+
let n = Number(v)
|
|
1377
|
+
if (isNaN(n)) {
|
|
1378
|
+
n = 0
|
|
1379
|
+
}
|
|
1380
|
+
return n | 0
|
|
1381
|
+
}
|
|
1382
|
+
},
|
|
1383
|
+
concat: (k, v) => null == k && islist(v) ? join(items(v, (n => isnode(n[1]) ? S_MT : (S_MT + n[1]))), S_MT) : v
|
|
1384
|
+
}
|
|
1385
|
+
const transform_APPLY = (inj, _val, _ref, store) => {
|
|
1386
|
+
const ijname = 'APPLY'
|
|
1387
|
+
if (!checkPlacement(M_VAL, ijname, T_list, inj)) {
|
|
1388
|
+
return NONE
|
|
1389
|
+
}
|
|
1390
|
+
// const [err, apply, child] = injectorArgs([T_function, T_any], inj)
|
|
1391
|
+
const [err, apply, child] = injectorArgs([T_function, T_any], slice(inj.parent, 1))
|
|
1392
|
+
if (NONE !== err) {
|
|
1393
|
+
inj.errs.push('$' + ijname + ': ' + err)
|
|
1394
|
+
return NONE
|
|
1395
|
+
}
|
|
1396
|
+
const tkey = getelem(inj.path, -2)
|
|
1397
|
+
const target = getelem(inj.nodes, -2, () => getelem(inj.nodes, -1))
|
|
1398
|
+
const cinj = injectChild(child, store, inj)
|
|
1399
|
+
const resolved = cinj.val
|
|
1400
|
+
const out = apply(resolved, store, cinj)
|
|
1401
|
+
setprop(target, tkey, out)
|
|
1402
|
+
return out
|
|
832
1403
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
1404
|
// Transform data using spec.
|
|
836
|
-
// Only operates on static JSON-like data
|
|
1405
|
+
// Only operates on static JSON-like data.
|
|
837
1406
|
// Arrays are treated as if they are objects with indices as keys.
|
|
838
|
-
function transform(
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
)
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
1407
|
+
function transform(data, // Source data to transform into new data (original not mutated)
|
|
1408
|
+
spec, // Transform specification; output follows this shape
|
|
1409
|
+
injdef) {
|
|
1410
|
+
// Clone the spec so that the clone can be modified in place as the transform result.
|
|
1411
|
+
const origspec = spec
|
|
1412
|
+
spec = clone(origspec)
|
|
1413
|
+
const extra = injdef?.extra
|
|
1414
|
+
const collect = null != injdef?.errs
|
|
1415
|
+
const errs = injdef?.errs || []
|
|
1416
|
+
const extraTransforms = {}
|
|
1417
|
+
const extraData = null == extra ? NONE : items(extra)
|
|
1418
|
+
.reduce((a, n) => (n[0].startsWith(S_DS) ? extraTransforms[n[0]] = n[1] : (a[n[0]] = n[1]), a), {})
|
|
1419
|
+
const dataClone = merge([
|
|
1420
|
+
isempty(extraData) ? NONE : clone(extraData),
|
|
1421
|
+
clone(data),
|
|
1422
|
+
])
|
|
1423
|
+
// Define a top level store that provides transform operations.
|
|
1424
|
+
const store = merge([
|
|
1425
|
+
{
|
|
1426
|
+
// The inject function recognises this special location for the root of the source data.
|
|
1427
|
+
// NOTE: to escape data that contains "`$FOO`" keys at the top level,
|
|
1428
|
+
// place that data inside a holding map: { myholder: mydata }.
|
|
1429
|
+
$TOP: dataClone,
|
|
1430
|
+
$SPEC: () => origspec,
|
|
1431
|
+
// Escape backtick (this also works inside backticks).
|
|
1432
|
+
$BT: () => S_BT,
|
|
1433
|
+
// Escape dollar sign (this also works inside backticks).
|
|
1434
|
+
$DS: () => S_DS,
|
|
1435
|
+
// Insert current date and time as an ISO string.
|
|
1436
|
+
$WHEN: () => new Date().toISOString(),
|
|
1437
|
+
$DELETE: transform_DELETE,
|
|
1438
|
+
$COPY: transform_COPY,
|
|
1439
|
+
$KEY: transform_KEY,
|
|
1440
|
+
$ANNO: transform_ANNO,
|
|
1441
|
+
$MERGE: transform_MERGE,
|
|
1442
|
+
$EACH: transform_EACH,
|
|
1443
|
+
$PACK: transform_PACK,
|
|
1444
|
+
$REF: transform_REF,
|
|
1445
|
+
$FORMAT: transform_FORMAT,
|
|
1446
|
+
$APPLY: transform_APPLY,
|
|
1447
|
+
},
|
|
1448
|
+
// Custom extra transforms, if any.
|
|
1449
|
+
extraTransforms,
|
|
1450
|
+
{
|
|
1451
|
+
$ERRS: errs,
|
|
1452
|
+
}
|
|
1453
|
+
], 1)
|
|
1454
|
+
const out = inject(spec, store, injdef)
|
|
1455
|
+
const generr = (0 < size(errs) && !collect)
|
|
1456
|
+
if (generr) {
|
|
1457
|
+
throw new Error(join(errs, ' | '))
|
|
1458
|
+
}
|
|
1459
|
+
return out
|
|
889
1460
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1461
|
+
// A required string value. NOTE: Rejects empty strings.
|
|
1462
|
+
const validate_STRING = (inj) => {
|
|
1463
|
+
let out = getprop(inj.dparent, inj.key)
|
|
1464
|
+
const t = typify(out)
|
|
1465
|
+
if (0 === (T_string & t)) {
|
|
1466
|
+
let msg = _invalidTypeMsg(inj.path, S_string, t, out, 'V1010')
|
|
1467
|
+
inj.errs.push(msg)
|
|
1468
|
+
return NONE
|
|
1469
|
+
}
|
|
1470
|
+
if (S_MT === out) {
|
|
1471
|
+
let msg = 'Empty string at ' + pathify(inj.path, 1)
|
|
1472
|
+
inj.errs.push(msg)
|
|
1473
|
+
return NONE
|
|
1474
|
+
}
|
|
1475
|
+
return out
|
|
1476
|
+
}
|
|
1477
|
+
const validate_TYPE = (inj, _val, ref) => {
|
|
1478
|
+
const tname = slice(ref, 1).toLowerCase()
|
|
1479
|
+
const typev = 1 << (31 - TYPENAME.indexOf(tname))
|
|
1480
|
+
let out = getprop(inj.dparent, inj.key)
|
|
1481
|
+
const t = typify(out)
|
|
1482
|
+
// console.log('TYPE', tname, typev, tn(typev), 'O=', t, tn(t), out, 'C=', t & typev)
|
|
1483
|
+
if (0 === (t & typev)) {
|
|
1484
|
+
inj.errs.push(_invalidTypeMsg(inj.path, tname, t, out, 'V1001'))
|
|
1485
|
+
return NONE
|
|
1486
|
+
}
|
|
1487
|
+
return out
|
|
1488
|
+
}
|
|
1489
|
+
// Allow any value.
|
|
1490
|
+
const validate_ANY = (inj) => {
|
|
1491
|
+
let out = getprop(inj.dparent, inj.key)
|
|
1492
|
+
return out
|
|
1493
|
+
}
|
|
1494
|
+
// Specify child values for map or list.
|
|
1495
|
+
// Map syntax: {'`$CHILD`': child-template }
|
|
1496
|
+
// List syntax: ['`$CHILD`', child-template ]
|
|
1497
|
+
const validate_CHILD = (inj) => {
|
|
1498
|
+
const { mode, key, parent, keys, path } = inj
|
|
1499
|
+
// Setup data structures for validation by cloning child template.
|
|
1500
|
+
// Map syntax.
|
|
1501
|
+
if (M_KEYPRE === mode) {
|
|
1502
|
+
const childtm = getprop(parent, key)
|
|
1503
|
+
// Get corresponding current object.
|
|
1504
|
+
const pkey = getelem(path, -2)
|
|
1505
|
+
let tval = getprop(inj.dparent, pkey)
|
|
1506
|
+
if (NONE == tval) {
|
|
1507
|
+
tval = {}
|
|
930
1508
|
}
|
|
931
|
-
else {
|
|
932
|
-
|
|
933
|
-
|
|
1509
|
+
else if (!ismap(tval)) {
|
|
1510
|
+
inj.errs.push(_invalidTypeMsg(slice(inj.path, -1), S_object, typify(tval), tval), 'V0220')
|
|
1511
|
+
return NONE
|
|
934
1512
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
let t = typeof out
|
|
942
|
-
if(S.number !== t) {
|
|
943
|
-
state.errs.push(invalidTypeMsg(state.path,S.number,t,out))
|
|
944
|
-
return
|
|
1513
|
+
const ckeys = keysof(tval)
|
|
1514
|
+
for (let ckey of ckeys) {
|
|
1515
|
+
setprop(parent, ckey, clone(childtm))
|
|
1516
|
+
// NOTE: modifying inj! This extends the child value loop in inject.
|
|
1517
|
+
keys.push(ckey)
|
|
945
1518
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
state.errs.push(invalidTypeMsg(state.path,S.boolean,t,out))
|
|
957
|
-
return
|
|
1519
|
+
// Remove $CHILD to cleanup ouput.
|
|
1520
|
+
inj.setval(NONE)
|
|
1521
|
+
return NONE
|
|
1522
|
+
}
|
|
1523
|
+
// List syntax.
|
|
1524
|
+
if (M_VAL === mode) {
|
|
1525
|
+
if (!islist(parent)) {
|
|
1526
|
+
// $CHILD was not inside a list.
|
|
1527
|
+
inj.errs.push('Invalid $CHILD as value')
|
|
1528
|
+
return NONE
|
|
958
1529
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
let out = getprop(current, key)
|
|
966
|
-
|
|
967
|
-
let t = typeof out
|
|
968
|
-
|
|
969
|
-
if(null == out || S.object !== t) {
|
|
970
|
-
state.errs.push(invalidTypeMsg(state.path,S.object,t,out))
|
|
971
|
-
return
|
|
1530
|
+
const childtm = getprop(parent, 1)
|
|
1531
|
+
if (NONE === inj.dparent) {
|
|
1532
|
+
// Empty list as default.
|
|
1533
|
+
// parent.length = 0
|
|
1534
|
+
slice(parent, 0, 0, true)
|
|
1535
|
+
return NONE
|
|
972
1536
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
const { mode, key, parent } = state
|
|
979
|
-
let out = getprop(current, key)
|
|
980
|
-
|
|
981
|
-
let t = typeof out
|
|
982
|
-
if(!Array.isArray(out)) {
|
|
983
|
-
state.errs.push(invalidTypeMsg(state.path,S.array,t,out))
|
|
984
|
-
return
|
|
1537
|
+
if (!islist(inj.dparent)) {
|
|
1538
|
+
const msg = _invalidTypeMsg(slice(inj.path, -1), S_list, typify(inj.dparent), inj.dparent, 'V0230')
|
|
1539
|
+
inj.errs.push(msg)
|
|
1540
|
+
inj.keyI = size(parent)
|
|
1541
|
+
return inj.dparent
|
|
985
1542
|
}
|
|
986
|
-
|
|
1543
|
+
// Clone children abd reset inj key index.
|
|
1544
|
+
// The inject child loop will now iterate over the cloned children,
|
|
1545
|
+
// validating them againt the current list values.
|
|
1546
|
+
items(inj.dparent, (n) => setprop(parent, n[0], clone(childtm)))
|
|
1547
|
+
slice(parent, 0, inj.dparent.length, true)
|
|
1548
|
+
inj.keyI = 0
|
|
1549
|
+
const out = getprop(inj.dparent, 0)
|
|
987
1550
|
return out
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1551
|
+
}
|
|
1552
|
+
return NONE
|
|
1553
|
+
}
|
|
1554
|
+
// TODO: implement SOME, ALL
|
|
1555
|
+
// FIX: ONE should mean exactly one, not at least one (=SOME)
|
|
1556
|
+
// TODO: implement a generate validate_ALT to do all of these
|
|
1557
|
+
// Match at least one of the specified shapes.
|
|
1558
|
+
// Syntax: ['`$ONE`', alt0, alt1, ...]
|
|
1559
|
+
const validate_ONE = (inj, _val, _ref, store) => {
|
|
1560
|
+
const { mode, parent, keyI } = inj
|
|
1561
|
+
// Only operate in val mode, since parent is a list.
|
|
1562
|
+
if (M_VAL === mode) {
|
|
1563
|
+
if (!islist(parent) || 0 !== keyI) {
|
|
1564
|
+
inj.errs.push('The $ONE validator at field ' +
|
|
1565
|
+
pathify(inj.path, 1, 1) +
|
|
1566
|
+
' must be the first element of an array.')
|
|
1567
|
+
return
|
|
998
1568
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
const pkey = path[path.length-2]
|
|
1015
|
-
const tval = current[pkey]
|
|
1016
|
-
|
|
1017
|
-
const ckeys = keysof(tval)
|
|
1018
|
-
for(let ckey of ckeys) {
|
|
1019
|
-
parent[ckey] = clone(child)
|
|
1020
|
-
keys.push(ckey)
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
delete parent[key]
|
|
1024
|
-
|
|
1025
|
-
}
|
|
1026
|
-
else if(S.MVAL === mode) {
|
|
1027
|
-
if(!islist(parent)) {
|
|
1028
|
-
state.errs.push('Invalid $CHILD as value')
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
const child = parent[1]
|
|
1032
|
-
|
|
1033
|
-
if(UNDEF === current) {
|
|
1034
|
-
parent.length = 0
|
|
1035
|
-
return UNDEF
|
|
1036
|
-
}
|
|
1037
|
-
else if(!islist(current)) {
|
|
1038
|
-
state.errs.push(invalidTypeMsg(
|
|
1039
|
-
state.path.slice(0,state.path.length-1),S.array,typeof current,current))
|
|
1040
|
-
state.keyI = parent.length
|
|
1041
|
-
return current
|
|
1042
|
-
}
|
|
1043
|
-
else {
|
|
1044
|
-
current.map((n,i)=>parent[i]=clone(child))
|
|
1045
|
-
parent.length = current.length
|
|
1046
|
-
state.keyI = 0
|
|
1047
|
-
return current[0]
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
},
|
|
1051
|
-
|
|
1052
|
-
$ONE: (state, val, current)=>{
|
|
1053
|
-
const { mode, key, parent, keys, path, nodes } = state
|
|
1054
|
-
|
|
1055
|
-
if(S.MVAL === mode) {
|
|
1056
|
-
state.keyI = state.keys.length
|
|
1057
|
-
|
|
1058
|
-
let tvals = parent.slice(1)
|
|
1059
|
-
|
|
1060
|
-
for(let tval of tvals) {
|
|
1569
|
+
inj.keyI = size(inj.keys)
|
|
1570
|
+
// Clean up structure, replacing [$ONE, ...] with current
|
|
1571
|
+
inj.setval(inj.dparent, 2)
|
|
1572
|
+
inj.path = slice(inj.path, -1)
|
|
1573
|
+
inj.key = getelem(inj.path, -1)
|
|
1574
|
+
let tvals = slice(parent, 1)
|
|
1575
|
+
if (0 === size(tvals)) {
|
|
1576
|
+
inj.errs.push('The $ONE validator at field ' +
|
|
1577
|
+
pathify(inj.path, 1, 1) +
|
|
1578
|
+
' must have at least one argument.')
|
|
1579
|
+
return
|
|
1580
|
+
}
|
|
1581
|
+
// See if we can find a match.
|
|
1582
|
+
for (let tval of tvals) {
|
|
1583
|
+
// If match, then errs.length = 0
|
|
1061
1584
|
let terrs = []
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
const
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1585
|
+
const vstore = merge([{}, store], 1)
|
|
1586
|
+
vstore.$TOP = inj.dparent
|
|
1587
|
+
const vcurrent = validate(inj.dparent, tval, {
|
|
1588
|
+
extra: vstore,
|
|
1589
|
+
errs: terrs,
|
|
1590
|
+
meta: inj.meta,
|
|
1591
|
+
})
|
|
1592
|
+
inj.setval(vcurrent, -2)
|
|
1593
|
+
// Accept current value if there was a match
|
|
1594
|
+
if (0 === size(terrs)) {
|
|
1070
1595
|
return
|
|
1071
|
-
}
|
|
1072
|
-
else {
|
|
1073
|
-
setprop(grandparent, grandkey, UNDEF)
|
|
1074
|
-
}
|
|
1075
1596
|
}
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
.replace(/`\$([A-Z]+)`/g, (m,p1)=>p1.toLowerCase())
|
|
1082
|
-
|
|
1083
|
-
state.errs.push(invalidTypeMsg(
|
|
1084
|
-
state.path.slice(0,state.path.length-1),
|
|
1085
|
-
'one of '+valdesc,
|
|
1086
|
-
typeof current, current))
|
|
1087
|
-
}
|
|
1088
|
-
},
|
|
1089
|
-
|
|
1090
|
-
...(extra||{})
|
|
1091
|
-
},
|
|
1092
|
-
|
|
1093
|
-
(key,
|
|
1094
|
-
val,
|
|
1095
|
-
parent,
|
|
1096
|
-
state,
|
|
1097
|
-
current,
|
|
1098
|
-
store)=>{
|
|
1099
|
-
const cval = isnode(current) ? current[key] : UNDEF
|
|
1100
|
-
|
|
1101
|
-
if(UNDEF === cval) {
|
|
1102
|
-
return
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
const pval = parent[key]
|
|
1106
|
-
|
|
1107
|
-
const t = typeof pval
|
|
1108
|
-
|
|
1109
|
-
if(S.string === t && pval.includes('$')) {
|
|
1110
|
-
return
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
//const t = typeof val
|
|
1114
|
-
const ct = typeof cval
|
|
1115
|
-
|
|
1116
|
-
if(t !== ct && UNDEF !== pval) {
|
|
1117
|
-
state.errs.push(invalidTypeMsg(state.path,t,ct,cval))
|
|
1118
|
-
}
|
|
1119
|
-
else if(ismap(cval)) {
|
|
1120
|
-
const ckeys = keysof(cval)
|
|
1121
|
-
const pkeys = keysof(pval)
|
|
1122
|
-
|
|
1123
|
-
// Empty spec object {} means object can be open (any keys).
|
|
1124
|
-
if(0 < pkeys.length && true !== getprop(pval,'`$OPEN`')) {
|
|
1125
|
-
const badkeys = []
|
|
1126
|
-
for(ckey of ckeys) {
|
|
1127
|
-
if(!haskey(val, ckey)) {
|
|
1128
|
-
badkeys.push(ckey)
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
if(0 < badkeys.length) {
|
|
1132
|
-
state.errs.push('Unexpected keys at '+pathify(state.path)+
|
|
1133
|
-
': '+badkeys.join(', '))
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
else {
|
|
1137
|
-
merge([pval, cval])
|
|
1138
|
-
if(isnode(pval)) {
|
|
1139
|
-
delete pval['`$OPEN`']
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
else if(islist(cval)) {
|
|
1144
|
-
if(!islist(val)) {
|
|
1145
|
-
state.errs.push(invalidTypeMsg(state.path,t,ct,cval))
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
else {
|
|
1149
|
-
// Spec value was a default, copy over data
|
|
1150
|
-
parent[key] = cval
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
)
|
|
1154
|
-
|
|
1155
|
-
if(0 < errs.length && null == collecterrs) {
|
|
1156
|
-
throw new Error('Invalid data: '+errs.join('\n'))
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
return out
|
|
1597
|
+
}
|
|
1598
|
+
// There was no match.
|
|
1599
|
+
const valdesc = replace(join(items(tvals, (n) => stringify(n[1])), ', '), R_TRANSFORM_NAME, (_m, p1) => p1.toLowerCase())
|
|
1600
|
+
inj.errs.push(_invalidTypeMsg(inj.path, (1 < size(tvals) ? 'one of ' : '') + valdesc, typify(inj.dparent), inj.dparent, 'V0210'))
|
|
1601
|
+
}
|
|
1160
1602
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1603
|
+
const validate_EXACT = (inj) => {
|
|
1604
|
+
const { mode, parent, key, keyI } = inj
|
|
1605
|
+
// Only operate in val mode, since parent is a list.
|
|
1606
|
+
if (M_VAL === mode) {
|
|
1607
|
+
if (!islist(parent) || 0 !== keyI) {
|
|
1608
|
+
inj.errs.push('The $EXACT validator at field ' +
|
|
1609
|
+
pathify(inj.path, 1, 1) +
|
|
1610
|
+
' must be the first element of an array.')
|
|
1611
|
+
return
|
|
1612
|
+
}
|
|
1613
|
+
inj.keyI = size(inj.keys)
|
|
1614
|
+
// Clean up structure, replacing [$EXACT, ...] with current data parent
|
|
1615
|
+
inj.setval(inj.dparent, 2)
|
|
1616
|
+
// inj.path = slice(inj.path, 0, size(inj.path) - 1)
|
|
1617
|
+
inj.path = slice(inj.path, 0, -1)
|
|
1618
|
+
inj.key = getelem(inj.path, -1)
|
|
1619
|
+
let tvals = slice(parent, 1)
|
|
1620
|
+
if (0 === size(tvals)) {
|
|
1621
|
+
inj.errs.push('The $EXACT validator at field ' +
|
|
1622
|
+
pathify(inj.path, 1, 1) +
|
|
1623
|
+
' must have at least one argument.')
|
|
1624
|
+
return
|
|
1625
|
+
}
|
|
1626
|
+
// See if we can find an exact value match.
|
|
1627
|
+
let currentstr = undefined
|
|
1628
|
+
for (let tval of tvals) {
|
|
1629
|
+
let exactmatch = tval === inj.dparent
|
|
1630
|
+
if (!exactmatch && isnode(tval)) {
|
|
1631
|
+
currentstr = undefined === currentstr ? stringify(inj.dparent) : currentstr
|
|
1632
|
+
const tvalstr = stringify(tval)
|
|
1633
|
+
exactmatch = tvalstr === currentstr
|
|
1634
|
+
}
|
|
1635
|
+
if (exactmatch) {
|
|
1636
|
+
return
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
// There was no match.
|
|
1640
|
+
const valdesc = replace(join(items(tvals, (n) => stringify(n[1])), ', '), R_TRANSFORM_NAME, (_m, p1) => p1.toLowerCase())
|
|
1641
|
+
inj.errs.push(_invalidTypeMsg(inj.path, (1 < size(inj.path) ? '' : 'value ') +
|
|
1642
|
+
'exactly equal to ' + (1 === size(tvals) ? '' : 'one of ') + valdesc, typify(inj.dparent), inj.dparent, 'V0110'))
|
|
1643
|
+
}
|
|
1644
|
+
else {
|
|
1645
|
+
delprop(parent, key)
|
|
1646
|
+
}
|
|
1165
1647
|
}
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1648
|
+
// This is the "modify" argument to inject. Use this to perform
|
|
1649
|
+
// generic validation. Runs *after* any special commands.
|
|
1650
|
+
const _validation = (pval, key, parent, inj) => {
|
|
1651
|
+
if (NONE === inj) {
|
|
1652
|
+
return
|
|
1653
|
+
}
|
|
1654
|
+
if (SKIP === pval) {
|
|
1655
|
+
return
|
|
1656
|
+
}
|
|
1657
|
+
// select needs exact matches
|
|
1658
|
+
const exact = getprop(inj.meta, S_BEXACT, false)
|
|
1659
|
+
// Current val to verify.
|
|
1660
|
+
const cval = getprop(inj.dparent, key)
|
|
1661
|
+
if (NONE === inj || (!exact && NONE === cval)) {
|
|
1662
|
+
return
|
|
1663
|
+
}
|
|
1664
|
+
const ptype = typify(pval)
|
|
1665
|
+
// Delete any special commands remaining.
|
|
1666
|
+
if (0 < (T_string & ptype) && pval.includes(S_DS)) {
|
|
1667
|
+
return
|
|
1668
|
+
}
|
|
1669
|
+
const ctype = typify(cval)
|
|
1670
|
+
// Type mismatch.
|
|
1671
|
+
if (ptype !== ctype && NONE !== pval) {
|
|
1672
|
+
inj.errs.push(_invalidTypeMsg(inj.path, typename(ptype), ctype, cval, 'V0010'))
|
|
1673
|
+
return
|
|
1674
|
+
}
|
|
1675
|
+
if (ismap(cval)) {
|
|
1676
|
+
if (!ismap(pval)) {
|
|
1677
|
+
inj.errs.push(_invalidTypeMsg(inj.path, typename(ptype), ctype, cval, 'V0020'))
|
|
1678
|
+
return
|
|
1679
|
+
}
|
|
1680
|
+
const ckeys = keysof(cval)
|
|
1681
|
+
const pkeys = keysof(pval)
|
|
1682
|
+
// Empty spec object {} means object can be open (any keys).
|
|
1683
|
+
if (0 < size(pkeys) && true !== getprop(pval, '`$OPEN`')) {
|
|
1684
|
+
const badkeys = []
|
|
1685
|
+
for (let ckey of ckeys) {
|
|
1686
|
+
if (!haskey(pval, ckey)) {
|
|
1687
|
+
badkeys.push(ckey)
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
// Closed object, so reject extra keys not in shape.
|
|
1691
|
+
if (0 < size(badkeys)) {
|
|
1692
|
+
const msg = 'Unexpected keys at field ' + pathify(inj.path, 1) + S_VIZ + join(badkeys, ', ')
|
|
1693
|
+
inj.errs.push(msg)
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
else {
|
|
1697
|
+
// Object is open, so merge in extra keys.
|
|
1698
|
+
merge([pval, cval])
|
|
1699
|
+
if (isnode(pval)) {
|
|
1700
|
+
delprop(pval, '`$OPEN`')
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
else if (islist(cval)) {
|
|
1705
|
+
if (!islist(pval)) {
|
|
1706
|
+
inj.errs.push(_invalidTypeMsg(inj.path, typename(ptype), ctype, cval, 'V0030'))
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
else if (exact) {
|
|
1710
|
+
if (cval !== pval) {
|
|
1711
|
+
const pathmsg = 1 < size(inj.path) ? 'at field ' + pathify(inj.path, 1) + S_VIZ : S_MT
|
|
1712
|
+
inj.errs.push('Value ' + pathmsg + cval +
|
|
1713
|
+
' should equal ' + pval + S_DT)
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
else {
|
|
1717
|
+
// Spec value was a default, copy over data
|
|
1718
|
+
setprop(parent, key, cval)
|
|
1719
|
+
}
|
|
1720
|
+
return
|
|
1721
|
+
}
|
|
1722
|
+
// Validate a data structure against a shape specification. The shape
|
|
1723
|
+
// specification follows the "by example" principle. Plain data in
|
|
1724
|
+
// teh shape is treated as default values that also specify the
|
|
1725
|
+
// required type. Thus shape {a:1} validates {a:2}, since the types
|
|
1726
|
+
// (number) match, but not {a:'A'}. Shape {a;1} against data {}
|
|
1727
|
+
// returns {a:1} as a=1 is the default value of the a key. Special
|
|
1728
|
+
// validation commands (in the same syntax as transform ) are also
|
|
1729
|
+
// provided to specify required values. Thus shape {a:'`$STRING`'}
|
|
1730
|
+
// validates {a:'A'} but not {a:1}. Empty map or list means the node
|
|
1731
|
+
// is open, and if missing an empty default is inserted.
|
|
1732
|
+
function validate(data, // Source data to transform into new data (original not mutated)
|
|
1733
|
+
spec, // Transform specification; output follows this shape
|
|
1734
|
+
injdef) {
|
|
1735
|
+
const extra = injdef?.extra
|
|
1736
|
+
const collect = null != injdef?.errs
|
|
1737
|
+
const errs = injdef?.errs || []
|
|
1738
|
+
const store = merge([
|
|
1739
|
+
{
|
|
1740
|
+
// Remove the transform commands.
|
|
1741
|
+
$DELETE: null,
|
|
1742
|
+
$COPY: null,
|
|
1743
|
+
$KEY: null,
|
|
1744
|
+
$META: null,
|
|
1745
|
+
$MERGE: null,
|
|
1746
|
+
$EACH: null,
|
|
1747
|
+
$PACK: null,
|
|
1748
|
+
$STRING: validate_STRING,
|
|
1749
|
+
$NUMBER: validate_TYPE,
|
|
1750
|
+
$INTEGER: validate_TYPE,
|
|
1751
|
+
$DECIMAL: validate_TYPE,
|
|
1752
|
+
$BOOLEAN: validate_TYPE,
|
|
1753
|
+
$NULL: validate_TYPE,
|
|
1754
|
+
$NIL: validate_TYPE,
|
|
1755
|
+
$MAP: validate_TYPE,
|
|
1756
|
+
$LIST: validate_TYPE,
|
|
1757
|
+
$FUNCTION: validate_TYPE,
|
|
1758
|
+
$INSTANCE: validate_TYPE,
|
|
1759
|
+
$ANY: validate_ANY,
|
|
1760
|
+
$CHILD: validate_CHILD,
|
|
1761
|
+
$ONE: validate_ONE,
|
|
1762
|
+
$EXACT: validate_EXACT,
|
|
1763
|
+
},
|
|
1764
|
+
getdef(extra, {}),
|
|
1765
|
+
// A special top level value to collect errors.
|
|
1766
|
+
// NOTE: collecterrs parameter always wins.
|
|
1767
|
+
{
|
|
1768
|
+
$ERRS: errs,
|
|
1769
|
+
}
|
|
1770
|
+
], 1)
|
|
1771
|
+
let meta = getprop(injdef, 'meta', {})
|
|
1772
|
+
setprop(meta, S_BEXACT, getprop(meta, S_BEXACT, false))
|
|
1773
|
+
const out = transform(data, spec, {
|
|
1774
|
+
meta,
|
|
1775
|
+
extra: store,
|
|
1776
|
+
modify: _validation,
|
|
1777
|
+
handler: _validatehandler,
|
|
1778
|
+
errs,
|
|
1779
|
+
})
|
|
1780
|
+
const generr = (0 < size(errs) && !collect)
|
|
1781
|
+
if (generr) {
|
|
1782
|
+
throw new Error(join(errs, ' | '))
|
|
1783
|
+
}
|
|
1784
|
+
return out
|
|
1785
|
+
}
|
|
1786
|
+
const select_AND = (inj, _val, _ref, store) => {
|
|
1787
|
+
if (M_KEYPRE === inj.mode) {
|
|
1788
|
+
const terms = getprop(inj.parent, inj.key)
|
|
1789
|
+
const ppath = slice(inj.path, -1)
|
|
1790
|
+
const point = getpath(store, ppath)
|
|
1791
|
+
const vstore = merge([{}, store], 1)
|
|
1792
|
+
vstore.$TOP = point
|
|
1793
|
+
for (let term of terms) {
|
|
1794
|
+
let terrs = []
|
|
1795
|
+
validate(point, term, {
|
|
1796
|
+
extra: vstore,
|
|
1797
|
+
errs: terrs,
|
|
1798
|
+
meta: inj.meta,
|
|
1799
|
+
})
|
|
1800
|
+
if (0 != size(terrs)) {
|
|
1801
|
+
inj.errs.push('AND:' + pathify(ppath) + S_VIZ + stringify(point) + ' fail:' + stringify(terms))
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
const gkey = getelem(inj.path, -2)
|
|
1805
|
+
const gp = getelem(inj.nodes, -2)
|
|
1806
|
+
setprop(gp, gkey, point)
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
const select_OR = (inj, _val, _ref, store) => {
|
|
1810
|
+
if (M_KEYPRE === inj.mode) {
|
|
1811
|
+
const terms = getprop(inj.parent, inj.key)
|
|
1812
|
+
const ppath = slice(inj.path, -1)
|
|
1813
|
+
const point = getpath(store, ppath)
|
|
1814
|
+
const vstore = merge([{}, store], 1)
|
|
1815
|
+
vstore.$TOP = point
|
|
1816
|
+
for (let term of terms) {
|
|
1817
|
+
let terrs = []
|
|
1818
|
+
validate(point, term, {
|
|
1819
|
+
extra: vstore,
|
|
1820
|
+
errs: terrs,
|
|
1821
|
+
meta: inj.meta,
|
|
1822
|
+
})
|
|
1823
|
+
if (0 === size(terrs)) {
|
|
1824
|
+
const gkey = getelem(inj.path, -2)
|
|
1825
|
+
const gp = getelem(inj.nodes, -2)
|
|
1826
|
+
setprop(gp, gkey, point)
|
|
1827
|
+
return
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
inj.errs.push('OR:' + pathify(ppath) + S_VIZ + stringify(point) + ' fail:' + stringify(terms))
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
const select_NOT = (inj, _val, _ref, store) => {
|
|
1834
|
+
if (M_KEYPRE === inj.mode) {
|
|
1835
|
+
const term = getprop(inj.parent, inj.key)
|
|
1836
|
+
const ppath = slice(inj.path, -1)
|
|
1837
|
+
const point = getpath(store, ppath)
|
|
1838
|
+
const vstore = merge([{}, store], 1)
|
|
1839
|
+
vstore.$TOP = point
|
|
1840
|
+
let terrs = []
|
|
1841
|
+
validate(point, term, {
|
|
1842
|
+
extra: vstore,
|
|
1843
|
+
errs: terrs,
|
|
1844
|
+
meta: inj.meta,
|
|
1845
|
+
})
|
|
1846
|
+
if (0 == size(terrs)) {
|
|
1847
|
+
inj.errs.push('NOT:' + pathify(ppath) + S_VIZ + stringify(point) + ' fail:' + stringify(term))
|
|
1848
|
+
}
|
|
1849
|
+
const gkey = getelem(inj.path, -2)
|
|
1850
|
+
const gp = getelem(inj.nodes, -2)
|
|
1851
|
+
setprop(gp, gkey, point)
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
const select_CMP = (inj, _val, ref, store) => {
|
|
1855
|
+
if (M_KEYPRE === inj.mode) {
|
|
1856
|
+
const term = getprop(inj.parent, inj.key)
|
|
1857
|
+
// const src = getprop(store, inj.base, store)
|
|
1858
|
+
const gkey = getelem(inj.path, -2)
|
|
1859
|
+
// const tval = getprop(src, gkey)
|
|
1860
|
+
const ppath = slice(inj.path, -1)
|
|
1861
|
+
const point = getpath(store, ppath)
|
|
1862
|
+
let pass = false
|
|
1863
|
+
if ('$GT' === ref && point > term) {
|
|
1864
|
+
pass = true
|
|
1865
|
+
}
|
|
1866
|
+
else if ('$LT' === ref && point < term) {
|
|
1867
|
+
pass = true
|
|
1868
|
+
}
|
|
1869
|
+
else if ('$GTE' === ref && point >= term) {
|
|
1870
|
+
pass = true
|
|
1871
|
+
}
|
|
1872
|
+
else if ('$LTE' === ref && point <= term) {
|
|
1873
|
+
pass = true
|
|
1874
|
+
}
|
|
1875
|
+
else if ('$LIKE' === ref && stringify(point).match(RegExp(term))) {
|
|
1876
|
+
pass = true
|
|
1877
|
+
}
|
|
1878
|
+
if (pass) {
|
|
1879
|
+
// Update spec to match found value so that _validate does not complain.
|
|
1880
|
+
const gp = getelem(inj.nodes, -2)
|
|
1881
|
+
setprop(gp, gkey, point)
|
|
1882
|
+
}
|
|
1883
|
+
else {
|
|
1884
|
+
inj.errs.push('CMP: ' + pathify(ppath) + S_VIZ + stringify(point) +
|
|
1885
|
+
' fail:' + ref + ' ' + stringify(term))
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
return NONE
|
|
1889
|
+
}
|
|
1890
|
+
// Select children from a top-level object that match a MongoDB-style query.
|
|
1891
|
+
// Supports $and, $or, and equality comparisons.
|
|
1892
|
+
// For arrays, children are elements; for objects, children are values.
|
|
1893
|
+
// TODO: swap arg order for consistency
|
|
1894
|
+
function select(children, query) {
|
|
1895
|
+
if (!isnode(children)) {
|
|
1896
|
+
return []
|
|
1897
|
+
}
|
|
1898
|
+
if (ismap(children)) {
|
|
1899
|
+
children = items(children, n => {
|
|
1900
|
+
setprop(n[1], S_DKEY, n[0])
|
|
1901
|
+
return n[1]
|
|
1902
|
+
})
|
|
1903
|
+
}
|
|
1904
|
+
else {
|
|
1905
|
+
children = items(children, (n) => (setprop(n[1], S_DKEY, +n[0]), n[1]))
|
|
1906
|
+
}
|
|
1907
|
+
const results = []
|
|
1908
|
+
const injdef = {
|
|
1909
|
+
errs: [],
|
|
1910
|
+
meta: { [S_BEXACT]: true },
|
|
1911
|
+
extra: {
|
|
1912
|
+
$AND: select_AND,
|
|
1913
|
+
$OR: select_OR,
|
|
1914
|
+
$NOT: select_NOT,
|
|
1915
|
+
$GT: select_CMP,
|
|
1916
|
+
$LT: select_CMP,
|
|
1917
|
+
$GTE: select_CMP,
|
|
1918
|
+
$LTE: select_CMP,
|
|
1919
|
+
$LIKE: select_CMP,
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
const q = clone(query)
|
|
1923
|
+
walk(q, (_k, v) => {
|
|
1924
|
+
if (ismap(v)) {
|
|
1925
|
+
setprop(v, '`$OPEN`', getprop(v, '`$OPEN`', true))
|
|
1926
|
+
}
|
|
1927
|
+
return v
|
|
1928
|
+
})
|
|
1929
|
+
for (const child of children) {
|
|
1930
|
+
injdef.errs = []
|
|
1931
|
+
validate(child, clone(q), injdef)
|
|
1932
|
+
if (0 === size(injdef.errs)) {
|
|
1933
|
+
results.push(child)
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
return results
|
|
1937
|
+
}
|
|
1938
|
+
// Injection state used for recursive injection into JSON - like data structures.
|
|
1939
|
+
class Injection {
|
|
1940
|
+
constructor(val, parent) {
|
|
1941
|
+
this.val = val
|
|
1942
|
+
this.parent = parent
|
|
1943
|
+
this.errs = []
|
|
1944
|
+
this.dparent = NONE
|
|
1945
|
+
this.dpath = [S_DTOP]
|
|
1946
|
+
this.mode = M_VAL
|
|
1947
|
+
this.full = false
|
|
1948
|
+
this.keyI = 0
|
|
1949
|
+
this.keys = [S_DTOP]
|
|
1950
|
+
this.key = S_DTOP
|
|
1951
|
+
this.path = [S_DTOP]
|
|
1952
|
+
this.nodes = [parent]
|
|
1953
|
+
this.handler = _injecthandler
|
|
1954
|
+
this.base = S_DTOP
|
|
1955
|
+
this.meta = {}
|
|
1956
|
+
}
|
|
1957
|
+
toString(prefix) {
|
|
1958
|
+
return 'INJ' + (null == prefix ? '' : S_FS + prefix) + S_CN +
|
|
1959
|
+
pad(pathify(this.path, 1)) +
|
|
1960
|
+
MODENAME[this.mode] + (this.full ? '/full' : '') + S_CN +
|
|
1961
|
+
'key=' + this.keyI + S_FS + this.key + S_FS + S_OS + this.keys + S_CS +
|
|
1962
|
+
' p=' + stringify(this.parent, -1, 1) +
|
|
1963
|
+
' m=' + stringify(this.meta, -1, 1) +
|
|
1964
|
+
' d/' + pathify(this.dpath, 1) + '=' + stringify(this.dparent, -1, 1) +
|
|
1965
|
+
' r=' + stringify(this.nodes[0]?.[S_DTOP], -1, 1)
|
|
1966
|
+
}
|
|
1967
|
+
descend() {
|
|
1968
|
+
this.meta.__d++
|
|
1969
|
+
const parentkey = getelem(this.path, -2)
|
|
1970
|
+
// Resolve current node in store for local paths.
|
|
1971
|
+
if (NONE === this.dparent) {
|
|
1972
|
+
// Even if there's no data, dpath should continue to match path, so that
|
|
1973
|
+
// relative paths work properly.
|
|
1974
|
+
if (1 < size(this.dpath)) {
|
|
1975
|
+
this.dpath = flatten([this.dpath, parentkey])
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
else {
|
|
1979
|
+
// this.dparent is the containing node of the current store value.
|
|
1980
|
+
if (null != parentkey) {
|
|
1981
|
+
this.dparent = getprop(this.dparent, parentkey)
|
|
1982
|
+
let lastpart = getelem(this.dpath, -1)
|
|
1983
|
+
if (lastpart === '$:' + parentkey) {
|
|
1984
|
+
this.dpath = slice(this.dpath, -1)
|
|
1985
|
+
}
|
|
1986
|
+
else {
|
|
1987
|
+
this.dpath = flatten([this.dpath, parentkey])
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
// TODO: is this needed?
|
|
1992
|
+
return this.dparent
|
|
1993
|
+
}
|
|
1994
|
+
child(keyI, keys) {
|
|
1995
|
+
const key = strkey(keys[keyI])
|
|
1996
|
+
const val = this.val
|
|
1997
|
+
const cinj = new Injection(getprop(val, key), val)
|
|
1998
|
+
cinj.keyI = keyI
|
|
1999
|
+
cinj.keys = keys
|
|
2000
|
+
cinj.key = key
|
|
2001
|
+
cinj.path = flatten([getdef(this.path, []), key])
|
|
2002
|
+
cinj.nodes = flatten([getdef(this.nodes, []), [val]])
|
|
2003
|
+
cinj.mode = this.mode
|
|
2004
|
+
cinj.handler = this.handler
|
|
2005
|
+
cinj.modify = this.modify
|
|
2006
|
+
cinj.base = this.base
|
|
2007
|
+
cinj.meta = this.meta
|
|
2008
|
+
cinj.errs = this.errs
|
|
2009
|
+
cinj.prior = this
|
|
2010
|
+
cinj.dpath = flatten([this.dpath])
|
|
2011
|
+
cinj.dparent = this.dparent
|
|
2012
|
+
return cinj
|
|
2013
|
+
}
|
|
2014
|
+
setval(val, ancestor) {
|
|
2015
|
+
let parent = NONE
|
|
2016
|
+
if (null == ancestor || ancestor < 2) {
|
|
2017
|
+
parent = NONE === val ?
|
|
2018
|
+
this.parent = delprop(this.parent, this.key) :
|
|
2019
|
+
setprop(this.parent, this.key, val)
|
|
2020
|
+
}
|
|
2021
|
+
else {
|
|
2022
|
+
const aval = getelem(this.nodes, 0 - ancestor)
|
|
2023
|
+
const akey = getelem(this.path, 0 - ancestor)
|
|
2024
|
+
parent = NONE === val ?
|
|
2025
|
+
delprop(aval, akey) :
|
|
2026
|
+
setprop(aval, akey, val)
|
|
2027
|
+
}
|
|
2028
|
+
// console.log('SETVAL', val, this.key, this.parent)
|
|
2029
|
+
return parent
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
// Internal utilities
|
|
2033
|
+
// ==================
|
|
2034
|
+
// // Update all references to target in inj.nodes.
|
|
2035
|
+
// function _updateAncestors(_inj: Injection, target: any, tkey: any, tval: any) {
|
|
2036
|
+
// // SetProp is sufficient in TypeScript as target reference remains consistent even for lists.
|
|
2037
|
+
// setprop(target, tkey, tval)
|
|
2038
|
+
// }
|
|
2039
|
+
// Build a type validation error message.
|
|
2040
|
+
function _invalidTypeMsg(path, needtype, vt, v, _whence) {
|
|
2041
|
+
let vs = null == v ? 'no value' : stringify(v)
|
|
2042
|
+
return 'Expected ' +
|
|
2043
|
+
(1 < size(path) ? ('field ' + pathify(path, 1) + ' to be ') : '') +
|
|
2044
|
+
needtype + ', but found ' +
|
|
2045
|
+
(null != v ? typename(vt) + S_VIZ : '') + vs +
|
|
2046
|
+
// Uncomment to help debug validation errors.
|
|
2047
|
+
// ' [' + _whence + ']' +
|
|
2048
|
+
'.'
|
|
2049
|
+
}
|
|
2050
|
+
// Default inject handler for transforms. If the path resolves to a function,
|
|
2051
|
+
// call the function passing the injection inj. This is how transforms operate.
|
|
2052
|
+
const _injecthandler = (inj, val, ref, store) => {
|
|
2053
|
+
let out = val
|
|
2054
|
+
const iscmd = isfunc(val) && (NONE === ref || ref.startsWith(S_DS))
|
|
2055
|
+
// Only call val function if it is a special command ($NAME format).
|
|
2056
|
+
// TODO: OR if meta.'$CALL'
|
|
2057
|
+
if (iscmd) {
|
|
2058
|
+
out = val(inj, val, ref, store)
|
|
2059
|
+
}
|
|
2060
|
+
// Update parent with value. Ensures references remain in node tree.
|
|
2061
|
+
else if (M_VAL === inj.mode && inj.full) {
|
|
2062
|
+
inj.setval(val)
|
|
2063
|
+
}
|
|
2064
|
+
return out
|
|
2065
|
+
}
|
|
2066
|
+
const _validatehandler = (inj, val, ref, store) => {
|
|
2067
|
+
let out = val
|
|
2068
|
+
const m = ref.match(R_META_PATH)
|
|
2069
|
+
const ismetapath = null != m
|
|
2070
|
+
if (ismetapath) {
|
|
2071
|
+
if ('=' === m[2]) {
|
|
2072
|
+
inj.setval([S_BEXACT, val])
|
|
2073
|
+
}
|
|
2074
|
+
else {
|
|
2075
|
+
inj.setval(val)
|
|
2076
|
+
}
|
|
2077
|
+
inj.keyI = -1
|
|
2078
|
+
out = SKIP
|
|
2079
|
+
}
|
|
2080
|
+
else {
|
|
2081
|
+
out = _injecthandler(inj, val, ref, store)
|
|
2082
|
+
}
|
|
2083
|
+
return out
|
|
2084
|
+
}
|
|
2085
|
+
// Inject values from a data store into a string. Not a public utility - used by
|
|
2086
|
+
// `inject`. Inject are marked with `path` where path is resolved
|
|
2087
|
+
// with getpath against the store or current (if defined)
|
|
2088
|
+
// arguments. See `getpath`. Custom injection handling can be
|
|
2089
|
+
// provided by inj.handler (this is used for transform functions).
|
|
2090
|
+
// The path can also have the special syntax $NAME999 where NAME is
|
|
2091
|
+
// upper case letters only, and 999 is any digits, which are
|
|
2092
|
+
// discarded. This syntax specifies the name of a transform, and
|
|
2093
|
+
// optionally allows transforms to be ordered by alphanumeric sorting.
|
|
2094
|
+
function _injectstr(val, store, inj) {
|
|
2095
|
+
// Can't inject into non-strings
|
|
2096
|
+
if (S_string !== typeof val || S_MT === val) {
|
|
2097
|
+
return S_MT
|
|
2098
|
+
}
|
|
2099
|
+
let out = val
|
|
2100
|
+
// Pattern examples: "`a.b.c`", "`$NAME`", "`$NAME1`"
|
|
2101
|
+
const m = val.match(R_INJECTION_FULL)
|
|
2102
|
+
// Full string of the val is an injection.
|
|
2103
|
+
if (m) {
|
|
2104
|
+
if (null != inj) {
|
|
2105
|
+
inj.full = true
|
|
2106
|
+
}
|
|
2107
|
+
let pathref = m[1]
|
|
2108
|
+
// Special escapes inside injection.
|
|
2109
|
+
if (3 < size(pathref)) {
|
|
2110
|
+
pathref = pathref.replace(R_BT_ESCAPE, S_BT).replace(R_DS_ESCAPE, S_DS)
|
|
2111
|
+
}
|
|
2112
|
+
// Get the extracted path reference.
|
|
2113
|
+
out = getpath(store, pathref, inj)
|
|
2114
|
+
}
|
|
2115
|
+
else {
|
|
2116
|
+
// Check for injections within the string.
|
|
2117
|
+
const partial = (_m, ref) => {
|
|
2118
|
+
// Special escapes inside injection.
|
|
2119
|
+
if (3 < size(ref)) {
|
|
2120
|
+
ref = ref.replace(R_BT_ESCAPE, S_BT).replace(R_DS_ESCAPE, S_DS)
|
|
2121
|
+
}
|
|
2122
|
+
if (inj) {
|
|
2123
|
+
inj.full = false
|
|
2124
|
+
}
|
|
2125
|
+
const found = getpath(store, ref, inj)
|
|
2126
|
+
// Ensure inject value is a string.
|
|
2127
|
+
return NONE === found ? S_MT : S_string === typeof found ? found : JSON.stringify(found)
|
|
2128
|
+
}
|
|
2129
|
+
out = val.replace(R_INJECTION_PARTIAL, partial)
|
|
2130
|
+
// Also call the inj handler on the entire string, providing the
|
|
2131
|
+
// option for custom injection.
|
|
2132
|
+
if (null != inj && isfunc(inj.handler)) {
|
|
2133
|
+
inj.full = true
|
|
2134
|
+
out = inj.handler(inj, out, val, store)
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
return out
|
|
2138
|
+
}
|
|
2139
|
+
// Handler Utilities
|
|
2140
|
+
// =================
|
|
2141
|
+
const MODENAME = {
|
|
2142
|
+
[M_VAL]: 'val',
|
|
2143
|
+
[M_KEYPRE]: 'key:pre',
|
|
2144
|
+
[M_KEYPOST]: 'key:post',
|
|
2145
|
+
}
|
|
2146
|
+
const PLACEMENT = {
|
|
2147
|
+
[M_VAL]: 'value',
|
|
2148
|
+
[M_KEYPRE]: S_key,
|
|
2149
|
+
[M_KEYPOST]: S_key,
|
|
2150
|
+
}
|
|
2151
|
+
function checkPlacement(modes, ijname, parentTypes, inj) {
|
|
2152
|
+
if (0 === (modes & inj.mode)) {
|
|
2153
|
+
inj.errs.push('$' + ijname + ': invalid placement as ' + PLACEMENT[inj.mode] +
|
|
2154
|
+
', expected: ' + join(items([M_KEYPRE, M_KEYPOST, M_VAL].filter(m => modes & m), (n) => PLACEMENT[n[1]]), ',') + '.')
|
|
2155
|
+
return false
|
|
2156
|
+
}
|
|
2157
|
+
if (!isempty(parentTypes)) {
|
|
2158
|
+
const ptype = typify(inj.parent)
|
|
2159
|
+
if (0 === (parentTypes & ptype)) {
|
|
2160
|
+
inj.errs.push('$' + ijname + ': invalid placement in parent ' + typename(ptype) +
|
|
2161
|
+
', expected: ' + typename(parentTypes) + '.')
|
|
2162
|
+
return false
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
return true
|
|
2166
|
+
}
|
|
2167
|
+
// function injectorArgs(argTypes: number[], inj: Injection): any {
|
|
2168
|
+
function injectorArgs(argTypes, args) {
|
|
2169
|
+
const numargs = size(argTypes)
|
|
2170
|
+
const found = new Array(1 + numargs)
|
|
2171
|
+
found[0] = NONE
|
|
2172
|
+
for (let argI = 0; argI < numargs; argI++) {
|
|
2173
|
+
// const arg = inj.parent[1 + argI]
|
|
2174
|
+
const arg = args[argI]
|
|
2175
|
+
const argType = typify(arg)
|
|
2176
|
+
if (0 === (argTypes[argI] & argType)) {
|
|
2177
|
+
found[0] = 'invalid argument: ' + stringify(arg, 22) +
|
|
2178
|
+
' (' + typename(argType) + ' at position ' + (1 + argI) +
|
|
2179
|
+
') is not of type: ' + typename(argTypes[argI]) + '.'
|
|
2180
|
+
break
|
|
2181
|
+
}
|
|
2182
|
+
found[1 + argI] = arg
|
|
2183
|
+
}
|
|
2184
|
+
return found
|
|
2185
|
+
}
|
|
2186
|
+
function injectChild(child, store, inj) {
|
|
2187
|
+
let cinj = inj
|
|
2188
|
+
// Replace ['`$FORMAT`',...] with child
|
|
2189
|
+
if (null != inj.prior) {
|
|
2190
|
+
if (null != inj.prior.prior) {
|
|
2191
|
+
cinj = inj.prior.prior.child(inj.prior.keyI, inj.prior.keys)
|
|
2192
|
+
cinj.val = child
|
|
2193
|
+
setprop(cinj.parent, inj.prior.key, child)
|
|
2194
|
+
}
|
|
2195
|
+
else {
|
|
2196
|
+
cinj = inj.prior.child(inj.keyI, inj.keys)
|
|
2197
|
+
cinj.val = child
|
|
2198
|
+
setprop(cinj.parent, inj.key, child)
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
// console.log('FORMAT-INJECT-CHILD', child)
|
|
2202
|
+
inject(child, store, cinj)
|
|
2203
|
+
return cinj
|
|
2204
|
+
}
|
|
2205
|
+
class StructUtility {
|
|
2206
|
+
constructor() {
|
|
2207
|
+
this.clone = clone
|
|
2208
|
+
this.delprop = delprop
|
|
2209
|
+
this.escre = escre
|
|
2210
|
+
this.escurl = escurl
|
|
2211
|
+
this.filter = filter
|
|
2212
|
+
this.flatten = flatten
|
|
2213
|
+
this.getdef = getdef
|
|
2214
|
+
this.getelem = getelem
|
|
2215
|
+
this.getpath = getpath
|
|
2216
|
+
this.getprop = getprop
|
|
2217
|
+
this.haskey = haskey
|
|
2218
|
+
this.inject = inject
|
|
2219
|
+
this.isempty = isempty
|
|
2220
|
+
this.isfunc = isfunc
|
|
2221
|
+
this.iskey = iskey
|
|
2222
|
+
this.islist = islist
|
|
2223
|
+
this.ismap = ismap
|
|
2224
|
+
this.isnode = isnode
|
|
2225
|
+
this.items = items
|
|
2226
|
+
this.join = join
|
|
2227
|
+
this.jsonify = jsonify
|
|
2228
|
+
this.keysof = keysof
|
|
2229
|
+
this.merge = merge
|
|
2230
|
+
this.pad = pad
|
|
2231
|
+
this.pathify = pathify
|
|
2232
|
+
this.select = select
|
|
2233
|
+
this.setpath = setpath
|
|
2234
|
+
this.setprop = setprop
|
|
2235
|
+
this.size = size
|
|
2236
|
+
this.slice = slice
|
|
2237
|
+
this.strkey = strkey
|
|
2238
|
+
this.stringify = stringify
|
|
2239
|
+
this.transform = transform
|
|
2240
|
+
this.typify = typify
|
|
2241
|
+
this.typename = typename
|
|
2242
|
+
this.validate = validate
|
|
2243
|
+
this.walk = walk
|
|
2244
|
+
this.SKIP = SKIP
|
|
2245
|
+
this.DELETE = DELETE
|
|
2246
|
+
this.jm = jm
|
|
2247
|
+
this.jt = jt
|
|
2248
|
+
this.tn = typename
|
|
2249
|
+
this.T_any = T_any
|
|
2250
|
+
this.T_noval = T_noval
|
|
2251
|
+
this.T_boolean = T_boolean
|
|
2252
|
+
this.T_decimal = T_decimal
|
|
2253
|
+
this.T_integer = T_integer
|
|
2254
|
+
this.T_number = T_number
|
|
2255
|
+
this.T_string = T_string
|
|
2256
|
+
this.T_function = T_function
|
|
2257
|
+
this.T_symbol = T_symbol
|
|
2258
|
+
this.T_null = T_null
|
|
2259
|
+
this.T_list = T_list
|
|
2260
|
+
this.T_map = T_map
|
|
2261
|
+
this.T_instance = T_instance
|
|
2262
|
+
this.T_scalar = T_scalar
|
|
2263
|
+
this.T_node = T_node
|
|
2264
|
+
this.checkPlacement = checkPlacement
|
|
2265
|
+
this.injectorArgs = injectorArgs
|
|
2266
|
+
this.injectChild = injectChild
|
|
1173
2267
|
}
|
|
1174
|
-
return path.join('.')
|
|
1175
|
-
}
|
|
1176
|
-
return '<unknown-path>'
|
|
1177
2268
|
}
|
|
1178
2269
|
|
|
1179
2270
|
|
|
1180
2271
|
module.exports = {
|
|
2272
|
+
StructUtility,
|
|
2273
|
+
Injection,
|
|
2274
|
+
|
|
1181
2275
|
clone,
|
|
2276
|
+
delprop,
|
|
1182
2277
|
escre,
|
|
1183
2278
|
escurl,
|
|
2279
|
+
filter,
|
|
2280
|
+
flatten,
|
|
2281
|
+
getdef,
|
|
2282
|
+
getelem,
|
|
1184
2283
|
getpath,
|
|
1185
2284
|
getprop,
|
|
2285
|
+
haskey,
|
|
1186
2286
|
inject,
|
|
1187
2287
|
isempty,
|
|
2288
|
+
isfunc,
|
|
1188
2289
|
iskey,
|
|
1189
2290
|
islist,
|
|
1190
2291
|
ismap,
|
|
1191
2292
|
isnode,
|
|
1192
|
-
isfunc,
|
|
1193
|
-
haskey,
|
|
1194
|
-
keysof,
|
|
1195
2293
|
items,
|
|
2294
|
+
join,
|
|
2295
|
+
jsonify,
|
|
2296
|
+
keysof,
|
|
1196
2297
|
merge,
|
|
2298
|
+
pad,
|
|
2299
|
+
pathify,
|
|
2300
|
+
select,
|
|
2301
|
+
setpath,
|
|
1197
2302
|
setprop,
|
|
2303
|
+
size,
|
|
2304
|
+
slice,
|
|
2305
|
+
strkey,
|
|
1198
2306
|
stringify,
|
|
1199
2307
|
transform,
|
|
1200
|
-
|
|
1201
|
-
|
|
2308
|
+
typify,
|
|
2309
|
+
typename,
|
|
1202
2310
|
validate,
|
|
2311
|
+
walk,
|
|
2312
|
+
|
|
2313
|
+
SKIP,
|
|
2314
|
+
DELETE,
|
|
2315
|
+
|
|
2316
|
+
jm,
|
|
2317
|
+
jt,
|
|
2318
|
+
|
|
2319
|
+
T_any,
|
|
2320
|
+
T_noval,
|
|
2321
|
+
T_boolean,
|
|
2322
|
+
T_decimal,
|
|
2323
|
+
T_integer,
|
|
2324
|
+
T_number,
|
|
2325
|
+
T_string,
|
|
2326
|
+
T_function,
|
|
2327
|
+
T_symbol,
|
|
2328
|
+
T_null,
|
|
2329
|
+
T_list,
|
|
2330
|
+
T_map,
|
|
2331
|
+
T_instance,
|
|
2332
|
+
T_scalar,
|
|
2333
|
+
T_node,
|
|
2334
|
+
|
|
2335
|
+
M_KEYPRE,
|
|
2336
|
+
M_KEYPOST,
|
|
2337
|
+
M_VAL,
|
|
2338
|
+
|
|
2339
|
+
MODENAME,
|
|
2340
|
+
|
|
2341
|
+
checkPlacement,
|
|
2342
|
+
injectorArgs,
|
|
2343
|
+
injectChild,
|
|
1203
2344
|
}
|