@voxgig/sdkgen-langpack 0.0.1
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/.sdk/model/target/dart.aon +63 -0
- package/.sdk/model/target/haskell.aon +72 -0
- package/.sdk/model/target/lean.aon +91 -0
- package/.sdk/src/cmp/dart/Config_dart.ts +248 -0
- package/.sdk/src/cmp/dart/EntityBase_dart.ts +34 -0
- package/.sdk/src/cmp/dart/EntityOperation_dart.ts +43 -0
- package/.sdk/src/cmp/dart/EntityTypes_dart.ts +207 -0
- package/.sdk/src/cmp/dart/Entity_dart.ts +87 -0
- package/.sdk/src/cmp/dart/Gitignore_dart.ts +34 -0
- package/.sdk/src/cmp/dart/MainEntity_dart.ts +33 -0
- package/.sdk/src/cmp/dart/Main_dart.ts +178 -0
- package/.sdk/src/cmp/dart/Package_dart.ts +82 -0
- package/.sdk/src/cmp/dart/ReadmeEntity_dart.ts +177 -0
- package/.sdk/src/cmp/dart/ReadmeExamplesTest_dart.ts +238 -0
- package/.sdk/src/cmp/dart/ReadmeExplanation_dart.ts +53 -0
- package/.sdk/src/cmp/dart/ReadmeHowto_dart.ts +152 -0
- package/.sdk/src/cmp/dart/ReadmeInstall_dart.ts +59 -0
- package/.sdk/src/cmp/dart/ReadmeIntro_dart.ts +65 -0
- package/.sdk/src/cmp/dart/ReadmeModel_dart.ts +151 -0
- package/.sdk/src/cmp/dart/ReadmeOptions_dart.ts +63 -0
- package/.sdk/src/cmp/dart/ReadmeQuick_dart.ts +225 -0
- package/.sdk/src/cmp/dart/ReadmeRef_dart.ts +397 -0
- package/.sdk/src/cmp/dart/ReadmeTopHowto_dart.ts +24 -0
- package/.sdk/src/cmp/dart/ReadmeTopQuick_dart.ts +106 -0
- package/.sdk/src/cmp/dart/ReadmeTopTest_dart.ts +78 -0
- package/.sdk/src/cmp/dart/SdkError_dart.ts +42 -0
- package/.sdk/src/cmp/dart/TestDirect_dart.ts +516 -0
- package/.sdk/src/cmp/dart/TestEntity_dart.ts +628 -0
- package/.sdk/src/cmp/dart/Test_dart.ts +138 -0
- package/.sdk/src/cmp/dart/fragment/Config.data.fragment.dart +98 -0
- package/.sdk/src/cmp/dart/fragment/Config.fragment.dart +70 -0
- package/.sdk/src/cmp/dart/fragment/Direct.test.fragment.dart +25 -0
- package/.sdk/src/cmp/dart/fragment/Entity.fragment.dart +29 -0
- package/.sdk/src/cmp/dart/fragment/Entity.test.fragment.dart +28 -0
- package/.sdk/src/cmp/dart/fragment/EntityBase.fragment.dart +302 -0
- package/.sdk/src/cmp/dart/fragment/EntityCreateOp.fragment.dart +98 -0
- package/.sdk/src/cmp/dart/fragment/EntityListOp.fragment.dart +90 -0
- package/.sdk/src/cmp/dart/fragment/EntityLoadOp.fragment.dart +102 -0
- package/.sdk/src/cmp/dart/fragment/EntityRemoveOp.fragment.dart +108 -0
- package/.sdk/src/cmp/dart/fragment/EntityUpdateOp.fragment.dart +102 -0
- package/.sdk/src/cmp/dart/fragment/Main.fragment.dart +339 -0
- package/.sdk/src/cmp/dart/fragment/SdkError.fragment.dart +26 -0
- package/.sdk/src/cmp/dart/tsconfig.json +15 -0
- package/.sdk/src/cmp/dart/utility_dart.ts +155 -0
- package/.sdk/src/cmp/haskell/Config_haskell.ts +98 -0
- package/.sdk/src/cmp/haskell/Entity_haskell.ts +13 -0
- package/.sdk/src/cmp/haskell/Gitignore_haskell.ts +27 -0
- package/.sdk/src/cmp/haskell/Main_haskell.ts +123 -0
- package/.sdk/src/cmp/haskell/Package_haskell.ts +60 -0
- package/.sdk/src/cmp/haskell/ReadmeEntity_haskell.ts +186 -0
- package/.sdk/src/cmp/haskell/ReadmeExamplesTest_haskell.ts +135 -0
- package/.sdk/src/cmp/haskell/ReadmeExplanation_haskell.ts +48 -0
- package/.sdk/src/cmp/haskell/ReadmeHowto_haskell.ts +168 -0
- package/.sdk/src/cmp/haskell/ReadmeInstall_haskell.ts +51 -0
- package/.sdk/src/cmp/haskell/ReadmeIntro_haskell.ts +67 -0
- package/.sdk/src/cmp/haskell/ReadmeModel_haskell.ts +162 -0
- package/.sdk/src/cmp/haskell/ReadmeOptions_haskell.ts +74 -0
- package/.sdk/src/cmp/haskell/ReadmeQuick_haskell.ts +241 -0
- package/.sdk/src/cmp/haskell/ReadmeRef_haskell.ts +408 -0
- package/.sdk/src/cmp/haskell/ReadmeTopHowto_haskell.ts +31 -0
- package/.sdk/src/cmp/haskell/ReadmeTopQuick_haskell.ts +104 -0
- package/.sdk/src/cmp/haskell/ReadmeTopTest_haskell.ts +80 -0
- package/.sdk/src/cmp/haskell/Test_haskell.ts +297 -0
- package/.sdk/src/cmp/haskell/tsconfig.json +15 -0
- package/.sdk/src/cmp/haskell/utility_haskell.ts +166 -0
- package/.sdk/src/cmp/lean/Config_lean.ts +93 -0
- package/.sdk/src/cmp/lean/Entity_lean.ts +9 -0
- package/.sdk/src/cmp/lean/Gitignore_lean.ts +18 -0
- package/.sdk/src/cmp/lean/Main_lean.ts +120 -0
- package/.sdk/src/cmp/lean/Package_lean.ts +93 -0
- package/.sdk/src/cmp/lean/ReadmeEntity_lean.ts +29 -0
- package/.sdk/src/cmp/lean/ReadmeExplanation_lean.ts +19 -0
- package/.sdk/src/cmp/lean/ReadmeHowto_lean.ts +25 -0
- package/.sdk/src/cmp/lean/ReadmeInstall_lean.ts +39 -0
- package/.sdk/src/cmp/lean/ReadmeIntro_lean.ts +25 -0
- package/.sdk/src/cmp/lean/ReadmeModel_lean.ts +24 -0
- package/.sdk/src/cmp/lean/ReadmeOptions_lean.ts +22 -0
- package/.sdk/src/cmp/lean/ReadmeQuick_lean.ts +50 -0
- package/.sdk/src/cmp/lean/ReadmeRef_lean.ts +40 -0
- package/.sdk/src/cmp/lean/ReadmeTopHowto_lean.ts +16 -0
- package/.sdk/src/cmp/lean/ReadmeTopQuick_lean.ts +23 -0
- package/.sdk/src/cmp/lean/ReadmeTopTest_lean.ts +19 -0
- package/.sdk/src/cmp/lean/Test_lean.ts +261 -0
- package/.sdk/src/cmp/lean/utility_lean.ts +129 -0
- package/.sdk/tm/dart/LICENSE +22 -0
- package/.sdk/tm/dart/Makefile +50 -0
- package/.sdk/tm/dart/lib/Context.dart +150 -0
- package/.sdk/tm/dart/lib/Control.dart +15 -0
- package/.sdk/tm/dart/lib/Operation.dart +31 -0
- package/.sdk/tm/dart/lib/Point.dart +51 -0
- package/.sdk/tm/dart/lib/Response.dart +32 -0
- package/.sdk/tm/dart/lib/Result.dart +39 -0
- package/.sdk/tm/dart/lib/Spec.dart +49 -0
- package/.sdk/tm/dart/lib/feature/audit/AuditFeature.dart +103 -0
- package/.sdk/tm/dart/lib/feature/base/BaseFeature.dart +76 -0
- package/.sdk/tm/dart/lib/feature/cache/CacheFeature.dart +152 -0
- package/.sdk/tm/dart/lib/feature/clienttrack/ClienttrackFeature.dart +104 -0
- package/.sdk/tm/dart/lib/feature/cost/CostFeature.dart +446 -0
- package/.sdk/tm/dart/lib/feature/debug/DebugFeature.dart +153 -0
- package/.sdk/tm/dart/lib/feature/idempotency/IdempotencyFeature.dart +92 -0
- package/.sdk/tm/dart/lib/feature/log/LogFeature.dart +85 -0
- package/.sdk/tm/dart/lib/feature/metrics/MetricsFeature.dart +103 -0
- package/.sdk/tm/dart/lib/feature/netsim/NetsimFeature.dart +186 -0
- package/.sdk/tm/dart/lib/feature/paging/PagingFeature.dart +240 -0
- package/.sdk/tm/dart/lib/feature/proxy/ProxyFeature.dart +118 -0
- package/.sdk/tm/dart/lib/feature/ratelimit/RatelimitFeature.dart +103 -0
- package/.sdk/tm/dart/lib/feature/rbac/RbacFeature.dart +121 -0
- package/.sdk/tm/dart/lib/feature/retry/RetryFeature.dart +155 -0
- package/.sdk/tm/dart/lib/feature/secrets/SecretsFeature.dart +649 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/capability.dart +105 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/catalog.dart +46 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/config.dart +297 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/depend.dart +198 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/env.dart +144 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/export.dart +58 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/graph.dart +160 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/host.dart +1067 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/order.dart +172 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/plugin.dart +23 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/point.dart +117 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/ref.dart +105 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/resolve.dart +58 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/types.dart +201 -0
- package/.sdk/tm/dart/lib/feature/secrets/plugin/version.dart +116 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/aws.dart +285 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/azuresecrets.dart +178 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/boru.dart +145 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/crypto.dart +194 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/doppler.dart +101 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/gcpsecrets.dart +143 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/hashicorp.dart +190 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/httpjson.dart +363 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/infisical.dart +149 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/onepassword.dart +146 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/secretspec.dart +116 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/sigv4.dart +237 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/addr.dart +138 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/json.dart +276 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/provider.dart +31 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/providers.dart +243 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/sekreto.dart +730 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/spec.dart +200 -0
- package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/support.dart +217 -0
- package/.sdk/tm/dart/lib/feature/streaming/StreamingFeature.dart +97 -0
- package/.sdk/tm/dart/lib/feature/telemetry/TelemetryFeature.dart +136 -0
- package/.sdk/tm/dart/lib/feature/test/TestFeature.dart +328 -0
- package/.sdk/tm/dart/lib/feature/timeout/TimeoutFeature.dart +104 -0
- package/.sdk/tm/dart/lib/utility/CleanUtility.dart +12 -0
- package/.sdk/tm/dart/lib/utility/DoneUtility.dart +18 -0
- package/.sdk/tm/dart/lib/utility/ErrUtility.dart +46 -0
- package/.sdk/tm/dart/lib/utility/FeatureAddUtility.dart +35 -0
- package/.sdk/tm/dart/lib/utility/FeatureHookUtility.dart +20 -0
- package/.sdk/tm/dart/lib/utility/FeatureInitUtility.dart +9 -0
- package/.sdk/tm/dart/lib/utility/FetcherUtility.dart +104 -0
- package/.sdk/tm/dart/lib/utility/GraphqlUtility.dart +162 -0
- package/.sdk/tm/dart/lib/utility/MakeContextUtility.dart +6 -0
- package/.sdk/tm/dart/lib/utility/MakeErrorUtility.dart +71 -0
- package/.sdk/tm/dart/lib/utility/MakeFetchDefUtility.dart +42 -0
- package/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +233 -0
- package/.sdk/tm/dart/lib/utility/MakePointUtility.dart +130 -0
- package/.sdk/tm/dart/lib/utility/MakeRequestUtility.dart +66 -0
- package/.sdk/tm/dart/lib/utility/MakeResponseUtility.dart +59 -0
- package/.sdk/tm/dart/lib/utility/MakeResultUtility.dart +49 -0
- package/.sdk/tm/dart/lib/utility/MakeSpecUtility.dart +77 -0
- package/.sdk/tm/dart/lib/utility/MakeUrlUtility.dart +51 -0
- package/.sdk/tm/dart/lib/utility/ParamUtility.dart +56 -0
- package/.sdk/tm/dart/lib/utility/PrepareAuthUtility.dart +46 -0
- package/.sdk/tm/dart/lib/utility/PrepareBodyUtility.dart +19 -0
- package/.sdk/tm/dart/lib/utility/PrepareHeadersUtility.dart +11 -0
- package/.sdk/tm/dart/lib/utility/PrepareMethodUtility.dart +30 -0
- package/.sdk/tm/dart/lib/utility/PrepareParamsUtility.dart +23 -0
- package/.sdk/tm/dart/lib/utility/PreparePathUtility.dart +9 -0
- package/.sdk/tm/dart/lib/utility/PrepareQueryUtility.dart +18 -0
- package/.sdk/tm/dart/lib/utility/ResultBasicUtility.dart +29 -0
- package/.sdk/tm/dart/lib/utility/ResultBodyUtility.dart +15 -0
- package/.sdk/tm/dart/lib/utility/ResultHeadersUtility.dart +18 -0
- package/.sdk/tm/dart/lib/utility/StructUtility.dart +127 -0
- package/.sdk/tm/dart/lib/utility/TransformRequestUtility.dart +28 -0
- package/.sdk/tm/dart/lib/utility/TransformResponseUtility.dart +44 -0
- package/.sdk/tm/dart/lib/utility/Utility.dart +239 -0
- package/.sdk/tm/dart/lib/utility/voxgig_struct.dart +2462 -0
- package/.sdk/tm/dart/src/feature/README.md +3 -0
- package/.sdk/tm/dart/src/feature/audit/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/base/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/cache/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/clienttrack/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/cost/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/debug/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/idempotency/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/log/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/metrics/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/netsim/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/paging/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/proxy/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/ratelimit/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/rbac/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/retry/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/secrets/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/streaming/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/telemetry/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/test/.gitkeep +0 -0
- package/.sdk/tm/dart/src/feature/timeout/.gitkeep +0 -0
- package/.sdk/tm/dart/test/custom_test.dart +65 -0
- package/.sdk/tm/dart/test/exists_test.dart +12 -0
- package/.sdk/tm/dart/test/feature/harness.dart +347 -0
- package/.sdk/tm/dart/test/feature/secrets/secrets_test.dart +1107 -0
- package/.sdk/tm/dart/test/feature_test.dart +1193 -0
- package/.sdk/tm/dart/test/harness.dart +153 -0
- package/.sdk/tm/dart/test/netsim_test.dart +48 -0
- package/.sdk/tm/dart/test/omni.dart +520 -0
- package/.sdk/tm/dart/test/omni_smoke_test.dart +143 -0
- package/.sdk/tm/dart/test/pipeline_test.dart +608 -0
- package/.sdk/tm/dart/test/primary_test.dart +476 -0
- package/.sdk/tm/dart/test/sdk-test-control.json +19 -0
- package/.sdk/tm/dart/test/struct_test.dart +468 -0
- package/.sdk/tm/dart/test/utility.dart +198 -0
- package/.sdk/tm/dart/test/vendor/omni/omni.dart +7 -0
- package/.sdk/tm/dart/test/vendor/omni/runner.dart +653 -0
- package/.sdk/tm/dart/test/vendor/omni/util.dart +232 -0
- package/.sdk/tm/haskell/LICENSE +21 -0
- package/.sdk/tm/haskell/Makefile +22 -0
- package/.sdk/tm/haskell/VERSION +1 -0
- package/.sdk/tm/haskell/src/SdkFeatures.hs +1440 -0
- package/.sdk/tm/haskell/src/SdkHelpers.hs +337 -0
- package/.sdk/tm/haskell/src/SdkJson.hs +111 -0
- package/.sdk/tm/haskell/src/SdkRuntime.hs +1279 -0
- package/.sdk/tm/haskell/src/SdkTypes.hs +195 -0
- package/.sdk/tm/haskell/src/VoxgigStruct.hs +2299 -0
- package/.sdk/tm/haskell/src/Vregex.hs +237 -0
- package/.sdk/tm/haskell/src/feature/README.md +5 -0
- package/.sdk/tm/haskell/src/feature/audit/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/base/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/cache/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/clienttrack/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/debug/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/idempotency/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/log/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/metrics/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/netsim/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/paging/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/proxy/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/ratelimit/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/rbac/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/retry/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/streaming/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/telemetry/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/test/.gitkeep +0 -0
- package/.sdk/tm/haskell/src/feature/timeout/.gitkeep +0 -0
- package/.sdk/tm/haskell/test/Harness.hs +281 -0
- package/.sdk/tm/haskell/test/OmniResolver.hs +252 -0
- package/.sdk/tm/haskell/test/Runner.hs +49 -0
- package/.sdk/tm/haskell/test/StructCorpus.hs +449 -0
- package/.sdk/tm/haskell/test/TCustomUtility.hs +38 -0
- package/.sdk/tm/haskell/test/TFeature.hs +794 -0
- package/.sdk/tm/haskell/test/TNetsim.hs +50 -0
- package/.sdk/tm/haskell/test/TPipeline.hs +334 -0
- package/.sdk/tm/haskell/test/TPrimaryCorpus.hs +263 -0
- package/.sdk/tm/haskell/test/TPrimaryUtility.hs +386 -0
- package/.sdk/tm/haskell/test/Testutil.hs +59 -0
- package/.sdk/tm/haskell/test/vendor/omni/Omni.hs +1147 -0
- package/.sdk/tm/lean/LICENSE +21 -0
- package/.sdk/tm/lean/Makefile +32 -0
- package/.sdk/tm/lean/VERSION +1 -0
- package/.sdk/tm/lean/src/SdkFeature.lean +171 -0
- package/.sdk/tm/lean/src/SdkFeatures.lean +787 -0
- package/.sdk/tm/lean/src/SdkJson.lean +134 -0
- package/.sdk/tm/lean/src/SdkRuntime.lean +374 -0
- package/.sdk/tm/lean/src/SdkUtility.lean +809 -0
- package/.sdk/tm/lean/src/VoxgigStruct.lean +2880 -0
- package/.sdk/tm/lean/src/Vregex.lean +412 -0
- package/.sdk/tm/lean/src/feature/README.md +5 -0
- package/.sdk/tm/lean/src/feature/audit/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/base/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/cache/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/clienttrack/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/cost/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/debug/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/idempotency/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/log/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/metrics/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/netsim/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/paging/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/proxy/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/ratelimit/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/rbac/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/retry/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/streaming/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/telemetry/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/test/.gitkeep +0 -0
- package/.sdk/tm/lean/src/feature/timeout/.gitkeep +0 -0
- package/.sdk/tm/lean/test/OmniResolver.lean +358 -0
- package/.sdk/tm/lean/test/OmniSmoke.lean +174 -0
- package/.sdk/tm/lean/test/StructCorpus.lean +368 -0
- package/.sdk/tm/lean/test/TFeature.lean +281 -0
- package/.sdk/tm/lean/test/TPrimaryUtility.lean +227 -0
- package/.sdk/tm/lean/test/vendor/omni/Omni.lean +997 -0
- package/LICENSE +21 -0
- package/README.md +104 -0
- package/package.json +78 -0
- package/sdkgen-package.json +22 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
// ignore_for_file: non_constant_identifier_names
|
|
2
|
+
|
|
3
|
+
import '../../utility/voxgig_struct.dart' as vs;
|
|
4
|
+
|
|
5
|
+
import '../base/BaseFeature.dart';
|
|
6
|
+
|
|
7
|
+
// Cost tracking and spend budget. Uses BOTH seams, which is the point of
|
|
8
|
+
// the feature: money is spent per HTTP ATTEMPT (a retried call is charged
|
|
9
|
+
// again, because the upstream API charges it again), but it is owed by an
|
|
10
|
+
// OPERATION. So the transport wrap prices each attempt, and PreDone
|
|
11
|
+
// attributes the running total to `<entity>.<op>` and to the caller
|
|
12
|
+
// (`ctrl.actor`, the same actor the audit feature records).
|
|
13
|
+
//
|
|
14
|
+
// The price of an attempt comes from the first source that answers: a
|
|
15
|
+
// response header (`header` x `perUnit`), the rate table (`rates`, keyed
|
|
16
|
+
// '<entity>.<op>' / '<op>' / '*'), then the flat `unit`. A body figure
|
|
17
|
+
// (`path` x `perUnit`, e.g. 'usage.total_tokens') is read at PreDone
|
|
18
|
+
// instead, from the already-parsed result. A body figure describes the
|
|
19
|
+
// whole call, so it REPLACES the per-attempt estimate rather than adding
|
|
20
|
+
// to it.
|
|
21
|
+
//
|
|
22
|
+
// `budget` caps total spend. With `onBudget` = 'deny' a further operation
|
|
23
|
+
// is refused at PrePoint, before an endpoint is resolved and before
|
|
24
|
+
// anything reaches the network.
|
|
25
|
+
//
|
|
26
|
+
// ORDER MATTERS. Cost must sit INSIDE the cache, or a response served from
|
|
27
|
+
// cache is charged for money that was never spent. Aggregates live on the
|
|
28
|
+
// client track (`cost`).
|
|
29
|
+
class CostFeature extends BaseFeature {
|
|
30
|
+
dynamic _client;
|
|
31
|
+
final Map<dynamic, dynamic> _pending = {};
|
|
32
|
+
int _seq = 0;
|
|
33
|
+
|
|
34
|
+
CostFeature() {
|
|
35
|
+
version = '0.0.1';
|
|
36
|
+
name = 'cost';
|
|
37
|
+
active = true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@override
|
|
41
|
+
dynamic init(dynamic ctx, dynamic opts) {
|
|
42
|
+
_client = ctx.client;
|
|
43
|
+
options = opts is Map ? Map<String, dynamic>.from(opts) : {};
|
|
44
|
+
active = true == options['active'];
|
|
45
|
+
_pending.clear();
|
|
46
|
+
_seq = 0;
|
|
47
|
+
|
|
48
|
+
final limit = _limit();
|
|
49
|
+
final track = _client.track;
|
|
50
|
+
if (null == track['cost']) {
|
|
51
|
+
track['cost'] = <String, dynamic>{
|
|
52
|
+
'currency': options['currency'] ?? 'USD',
|
|
53
|
+
'total': <String, dynamic>{
|
|
54
|
+
'calls': 0,
|
|
55
|
+
'attempts': 0,
|
|
56
|
+
'amount': 0,
|
|
57
|
+
'reported': 0,
|
|
58
|
+
'estimated': 0,
|
|
59
|
+
},
|
|
60
|
+
'ops': <String, dynamic>{},
|
|
61
|
+
'actors': <String, dynamic>{},
|
|
62
|
+
'budget': <String, dynamic>{
|
|
63
|
+
'limit': limit,
|
|
64
|
+
'spent': 0,
|
|
65
|
+
'remaining': limit,
|
|
66
|
+
'exceeded': false,
|
|
67
|
+
},
|
|
68
|
+
'last': null,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!active) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
final self = this;
|
|
77
|
+
final utility = ctx.utility;
|
|
78
|
+
final inner = utility.fetcher;
|
|
79
|
+
|
|
80
|
+
utility.fetcher = (dynamic ctx2, dynamic url, dynamic fetchdef) async {
|
|
81
|
+
return self._charge(ctx2, url, fetchdef, inner);
|
|
82
|
+
};
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Budget gate. Runs before endpoint resolution, so a refused call costs
|
|
87
|
+
// nothing at all.
|
|
88
|
+
@override
|
|
89
|
+
dynamic PrePoint(dynamic ctx) {
|
|
90
|
+
if (!active) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Mark the context as running through the pipeline, so _charge knows a
|
|
95
|
+
// PreDone is coming and does not commit the spend itself.
|
|
96
|
+
var pending = _pending[ctx];
|
|
97
|
+
if (null == pending) {
|
|
98
|
+
pending = _pending[ctx] = _newPending();
|
|
99
|
+
}
|
|
100
|
+
pending['piped'] = true;
|
|
101
|
+
|
|
102
|
+
final limit = _limit();
|
|
103
|
+
if (0 >= limit) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
final cost = _client.track['cost'];
|
|
108
|
+
if (cost['total']['amount'] < limit) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
cost['budget']['exceeded'] = true;
|
|
113
|
+
|
|
114
|
+
if ('deny' != options['onBudget']) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
final err = ctx.error(
|
|
119
|
+
'cost_budget',
|
|
120
|
+
'Cost budget of ' +
|
|
121
|
+
limit.toString() +
|
|
122
|
+
' ' +
|
|
123
|
+
cost['currency'].toString() +
|
|
124
|
+
' is spent (' +
|
|
125
|
+
cost['total']['amount'].toString() +
|
|
126
|
+
' ' +
|
|
127
|
+
cost['currency'].toString() +
|
|
128
|
+
' used)');
|
|
129
|
+
// Short-circuit endpoint resolution; the pipeline surfaces this error.
|
|
130
|
+
ctx.out['point'] = err;
|
|
131
|
+
return err;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
Future<dynamic> _charge(
|
|
135
|
+
dynamic ctx, dynamic url, dynamic fetchdef, dynamic inner) async {
|
|
136
|
+
dynamic res;
|
|
137
|
+
var threw = false;
|
|
138
|
+
|
|
139
|
+
// A throwing transport still costs an attempt. Without this, a run of
|
|
140
|
+
// connection-level failures under `retry` (which catches the throw and
|
|
141
|
+
// tries again) would be charged nothing at all, and an onBudget =
|
|
142
|
+
// 'deny' ceiling could never stop it.
|
|
143
|
+
try {
|
|
144
|
+
res = await Future.value(inner(ctx, url, fetchdef));
|
|
145
|
+
} catch (err) {
|
|
146
|
+
threw = true;
|
|
147
|
+
res = err;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
final priced = _price(ctx, threw ? null : res);
|
|
151
|
+
final amount = priced['amount'];
|
|
152
|
+
final source = priced['source'];
|
|
153
|
+
|
|
154
|
+
final cost = _client.track['cost'];
|
|
155
|
+
|
|
156
|
+
var pending = _pending[ctx];
|
|
157
|
+
if (null == pending) {
|
|
158
|
+
pending = _pending[ctx] = _newPending();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
pending['attempts'] = pending['attempts'] + 1;
|
|
162
|
+
|
|
163
|
+
// Accumulated here, committed once at PreDone. Adding each attempt to
|
|
164
|
+
// the running total and then subtracting it again when a body figure
|
|
165
|
+
// supersedes it loses precision to catastrophic cancellation
|
|
166
|
+
// (5 + (0.01 - 5) is not 0.01 in binary floating point).
|
|
167
|
+
//
|
|
168
|
+
// Reported and estimated are kept apart per ATTEMPT, not per operation:
|
|
169
|
+
// a 503 priced from the rate table followed by a 200 carrying the cost
|
|
170
|
+
// header is part estimate, part reported, and collapsing both into the
|
|
171
|
+
// final attempt's category would corrupt the split.
|
|
172
|
+
pending['amount'] = pending['amount'] + amount;
|
|
173
|
+
final bucket = ('header' == source || 'body' == source) ? 'reported' : 'estimated';
|
|
174
|
+
pending[bucket] = pending[bucket] + amount;
|
|
175
|
+
pending['source'] = source;
|
|
176
|
+
|
|
177
|
+
cost['total']['attempts'] = cost['total']['attempts'] + 1;
|
|
178
|
+
|
|
179
|
+
// direct() and graphql() reach the transport without dispatching any
|
|
180
|
+
// pipeline hooks - no PrePoint to gate on, and no PreDone to commit.
|
|
181
|
+
// Their spend is committed here instead, or it would never be counted
|
|
182
|
+
// and could run past an onBudget = 'deny' ceiling indefinitely.
|
|
183
|
+
// `piped` is set by PrePoint, so its absence is the signal.
|
|
184
|
+
if (true != pending['piped']) {
|
|
185
|
+
_commit(ctx, pending, '_', 'direct');
|
|
186
|
+
_pending.remove(ctx);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (threw) {
|
|
190
|
+
throw res;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return res;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
Map<String, dynamic> _newPending() {
|
|
197
|
+
return <String, dynamic>{
|
|
198
|
+
'attempts': 0,
|
|
199
|
+
'amount': 0,
|
|
200
|
+
'reported': 0,
|
|
201
|
+
'estimated': 0,
|
|
202
|
+
'source': 'none',
|
|
203
|
+
'piped': false,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Attribute the operation's spend once the call is finished.
|
|
208
|
+
@override
|
|
209
|
+
dynamic PreDone(dynamic ctx) {
|
|
210
|
+
_finish(ctx, true);
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// A failed operation still spent the money. When the pipeline throws,
|
|
215
|
+
// PreDone never runs, so without this the attempts are counted and the
|
|
216
|
+
// spend is not, and a budget could never see the cost of a failed call.
|
|
217
|
+
// Whichever hook fires first consumes the pending entry, so it commits
|
|
218
|
+
// exactly once.
|
|
219
|
+
@override
|
|
220
|
+
dynamic PreUnexpected(dynamic ctx) {
|
|
221
|
+
_finish(ctx, false);
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
void _finish(dynamic ctx, bool done) {
|
|
226
|
+
if (!active) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (!_pending.containsKey(ctx)) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
final pending = _pending[ctx];
|
|
233
|
+
_pending.remove(ctx);
|
|
234
|
+
|
|
235
|
+
// A FAILED operation that made no attempt never reached the network:
|
|
236
|
+
// PrePoint creates the pending entry to mark the context as piped, and
|
|
237
|
+
// then the budget gate refuses the call (rbac, or an unresolvable
|
|
238
|
+
// endpoint, short-circuits just as early). Committing it would count a
|
|
239
|
+
// call that never happened and file a zero-amount record as `last`.
|
|
240
|
+
//
|
|
241
|
+
// A SUCCEEDED operation that made no attempt is the opposite case: it
|
|
242
|
+
// was served from the cache. That is a real call, and the fact that it
|
|
243
|
+
// cost nothing is the whole point of ordering cost inside the cache.
|
|
244
|
+
if (!done && 0 == pending['attempts']) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
final entity = (null == ctx.op ? '_' : (ctx.op.entity ?? '_')).toString();
|
|
249
|
+
final opname = (null == ctx.op ? '_' : (ctx.op.name ?? '_')).toString();
|
|
250
|
+
|
|
251
|
+
_commit(ctx, pending, entity, opname);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Commit one operation's spend: totals, budget, per-op and per-actor
|
|
255
|
+
// attribution, and the record. Shared by _finish and the raw-request path
|
|
256
|
+
// in _charge, which has no PreDone to reach.
|
|
257
|
+
void _commit(dynamic ctx, dynamic pending, String entity, String opname) {
|
|
258
|
+
final cost = _client.track['cost'];
|
|
259
|
+
|
|
260
|
+
var amount = pending['amount'];
|
|
261
|
+
var reported = pending['reported'];
|
|
262
|
+
var estimated = pending['estimated'];
|
|
263
|
+
var source = pending['source'];
|
|
264
|
+
|
|
265
|
+
// A body figure prices the whole call, so it replaces the per-attempt
|
|
266
|
+
// estimate rather than adding to it - and, being server-stated, the
|
|
267
|
+
// whole amount counts as reported.
|
|
268
|
+
final body = _body(ctx);
|
|
269
|
+
if (null != body) {
|
|
270
|
+
amount = body;
|
|
271
|
+
reported = body;
|
|
272
|
+
estimated = 0;
|
|
273
|
+
source = 'body';
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
_spend(cost, amount, reported, estimated);
|
|
277
|
+
|
|
278
|
+
final actor = (ctx.ctrl is Map ? ctx.ctrl['actor'] : null) ??
|
|
279
|
+
options['actor'] ??
|
|
280
|
+
'anonymous';
|
|
281
|
+
|
|
282
|
+
cost['total']['calls'] = cost['total']['calls'] + 1;
|
|
283
|
+
_bump(cost['ops'], entity + '.' + opname, amount);
|
|
284
|
+
_bump(cost['actors'], actor.toString(), amount);
|
|
285
|
+
|
|
286
|
+
_seq = _seq + 1;
|
|
287
|
+
final record = <String, dynamic>{
|
|
288
|
+
'seq': _seq,
|
|
289
|
+
'entity': entity,
|
|
290
|
+
'op': opname,
|
|
291
|
+
'actor': actor,
|
|
292
|
+
'amount': amount,
|
|
293
|
+
'currency': cost['currency'],
|
|
294
|
+
'source': source,
|
|
295
|
+
'attempts': pending['attempts'],
|
|
296
|
+
};
|
|
297
|
+
cost['last'] = record;
|
|
298
|
+
|
|
299
|
+
final sink = options['sink'];
|
|
300
|
+
if (sink is Function) {
|
|
301
|
+
// A sink must never break the call it is reporting on.
|
|
302
|
+
try {
|
|
303
|
+
sink(record);
|
|
304
|
+
} catch (_e) {}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Price one attempt: a reported header figure, else the rate table, else
|
|
309
|
+
// the flat unit.
|
|
310
|
+
Map<String, dynamic> _price(dynamic ctx, dynamic res) {
|
|
311
|
+
final header = options['header'];
|
|
312
|
+
if (header is String && '' != header) {
|
|
313
|
+
final v = _header(res, header);
|
|
314
|
+
if (null != v) {
|
|
315
|
+
return <String, dynamic>{'amount': v * _perUnit(), 'source': 'header'};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
final rate = _rate(ctx);
|
|
320
|
+
if (null != rate) {
|
|
321
|
+
return <String, dynamic>{'amount': rate, 'source': 'table'};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
final unit = options['unit'];
|
|
325
|
+
if (unit is num && 0 != unit) {
|
|
326
|
+
return <String, dynamic>{'amount': unit, 'source': 'unit'};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return <String, dynamic>{'amount': 0, 'source': 'none'};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// The rate table uses the same lookup grammar as rbac's rules:
|
|
333
|
+
// '<entity>.<op>', then '<op>', then '*'.
|
|
334
|
+
num? _rate(dynamic ctx) {
|
|
335
|
+
final rates = options['rates'] ?? {};
|
|
336
|
+
|
|
337
|
+
var entity = _entname(ctx.entity);
|
|
338
|
+
if ('' == entity) {
|
|
339
|
+
entity = (null == ctx.op ? '' : (ctx.op.entity ?? '')).toString();
|
|
340
|
+
}
|
|
341
|
+
final opname = (null == ctx.op ? '' : (ctx.op.name ?? '')).toString();
|
|
342
|
+
|
|
343
|
+
for (final key in [entity + '.' + opname, opname, '*']) {
|
|
344
|
+
final r = vs.getprop(rates, key);
|
|
345
|
+
if (r is num) {
|
|
346
|
+
return r;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// A usage figure from the parsed result body, priced by perUnit. Read
|
|
353
|
+
// here, not at the transport seam, because the body is consumed once.
|
|
354
|
+
num? _body(dynamic ctx) {
|
|
355
|
+
final path = options['path'];
|
|
356
|
+
if (path is! String || '' == path) {
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
if (null == ctx.result) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
final body = ctx.result.body;
|
|
363
|
+
if (body is! Map) {
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
final v = vs.getpath(body, path);
|
|
367
|
+
final n = v is num ? v : num.tryParse((v ?? '').toString());
|
|
368
|
+
if (null == n) {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
return n * _perUnit();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
void _spend(dynamic cost, num amount, num reported, num estimated) {
|
|
375
|
+
final total = cost['total'];
|
|
376
|
+
total['amount'] = total['amount'] + amount;
|
|
377
|
+
total['reported'] = total['reported'] + reported;
|
|
378
|
+
total['estimated'] = total['estimated'] + estimated;
|
|
379
|
+
|
|
380
|
+
final budget = cost['budget'];
|
|
381
|
+
final limit = budget['limit'];
|
|
382
|
+
budget['spent'] = total['amount'];
|
|
383
|
+
if (0 < limit) {
|
|
384
|
+
final left = limit - total['amount'];
|
|
385
|
+
budget['remaining'] = left < 0 ? 0 : left;
|
|
386
|
+
if (total['amount'] >= limit) {
|
|
387
|
+
budget['exceeded'] = true;
|
|
388
|
+
}
|
|
389
|
+
} else {
|
|
390
|
+
budget['remaining'] = 0;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
void _bump(dynamic bucket, String key, num amount) {
|
|
395
|
+
var b = bucket[key];
|
|
396
|
+
if (null == b) {
|
|
397
|
+
b = bucket[key] = <String, dynamic>{'calls': 0, 'amount': 0};
|
|
398
|
+
}
|
|
399
|
+
b['calls'] = b['calls'] + 1;
|
|
400
|
+
b['amount'] = b['amount'] + amount;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// HTTP header names are case-insensitive and a custom transport keeps
|
|
404
|
+
// conventional casing ('X-Request-Cost'), so scan rather than index.
|
|
405
|
+
num? _header(dynamic res, String name) {
|
|
406
|
+
if (null == res) {
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
final headers = vs.getprop(res, 'headers');
|
|
410
|
+
if (headers is! Map) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
final lower = name.toLowerCase();
|
|
414
|
+
for (final k in headers.keys) {
|
|
415
|
+
if (k.toString().toLowerCase() == lower) {
|
|
416
|
+
final v = headers[k];
|
|
417
|
+
return v is num ? v : num.tryParse(v.toString());
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
String _entname(dynamic ent) {
|
|
424
|
+
if (null == ent) {
|
|
425
|
+
return '';
|
|
426
|
+
}
|
|
427
|
+
if (ent is Map) {
|
|
428
|
+
return (vs.getprop(ent, 'name', '') ?? '').toString();
|
|
429
|
+
}
|
|
430
|
+
try {
|
|
431
|
+
return (ent.name ?? '').toString();
|
|
432
|
+
} catch (_e) {
|
|
433
|
+
return '';
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
num _perUnit() {
|
|
438
|
+
final p = options['perUnit'];
|
|
439
|
+
return p is num ? p : 0;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
num _limit() {
|
|
443
|
+
final b = options['budget'];
|
|
444
|
+
return b is num ? b : 0;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// ignore_for_file: non_constant_identifier_names
|
|
2
|
+
|
|
3
|
+
import '../base/BaseFeature.dart';
|
|
4
|
+
|
|
5
|
+
// Request/response capture for debugging. Records a bounded ring buffer of
|
|
6
|
+
// per-operation traces — method, URL, redacted headers, response status and
|
|
7
|
+
// timing — on the client track (`debug.entries`). Sensitive header values
|
|
8
|
+
// (matching `redact`, default authorization/cookie/api-key style names) are
|
|
9
|
+
// masked. An optional `onEntry` callback receives each finished entry (e.g.
|
|
10
|
+
// to stream to a console). `max` caps the buffer (default 100).
|
|
11
|
+
class DebugFeature extends BaseFeature {
|
|
12
|
+
dynamic _client;
|
|
13
|
+
final Map<dynamic, dynamic> _entries = {};
|
|
14
|
+
|
|
15
|
+
DebugFeature() {
|
|
16
|
+
version = '0.0.1';
|
|
17
|
+
name = 'debug';
|
|
18
|
+
active = true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@override
|
|
22
|
+
dynamic init(dynamic ctx, dynamic opts) {
|
|
23
|
+
_client = ctx.client;
|
|
24
|
+
options = opts is Map ? Map<String, dynamic>.from(opts) : {};
|
|
25
|
+
active = true == options['active'];
|
|
26
|
+
_entries.clear();
|
|
27
|
+
|
|
28
|
+
final track = _client.track;
|
|
29
|
+
if (null == track['debug']) {
|
|
30
|
+
track['debug'] = <String, dynamic>{'entries': <dynamic>[]};
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@override
|
|
36
|
+
dynamic PreRequest(dynamic ctx) {
|
|
37
|
+
final spec = ctx.spec;
|
|
38
|
+
final entry = <String, dynamic>{
|
|
39
|
+
'op': (null == ctx.op ? '_' : (ctx.op.entity ?? '_')).toString() +
|
|
40
|
+
'.' +
|
|
41
|
+
(null == ctx.op ? '_' : (ctx.op.name ?? '_')).toString(),
|
|
42
|
+
'method': null == spec ? null : spec.method,
|
|
43
|
+
'url': null == spec ? null : (spec.url ?? spec.path),
|
|
44
|
+
'headers': _redact(null == spec ? null : spec.headers),
|
|
45
|
+
'start': _now(),
|
|
46
|
+
'status': null,
|
|
47
|
+
'ok': null,
|
|
48
|
+
'durationMs': null,
|
|
49
|
+
'error': null,
|
|
50
|
+
};
|
|
51
|
+
_entries[ctx] = entry;
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@override
|
|
56
|
+
dynamic PreResponse(dynamic ctx) {
|
|
57
|
+
final entry = _entries[ctx];
|
|
58
|
+
if (null == entry) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
final response = ctx.response;
|
|
62
|
+
if (null != response) {
|
|
63
|
+
entry['status'] = response.status;
|
|
64
|
+
entry['url'] = entry['url'] ?? (null == ctx.spec ? null : ctx.spec.url);
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@override
|
|
70
|
+
dynamic PreDone(dynamic ctx) {
|
|
71
|
+
_finish(ctx, true);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@override
|
|
76
|
+
dynamic PreUnexpected(dynamic ctx) {
|
|
77
|
+
final entry = _entries[ctx];
|
|
78
|
+
if (null != entry && ctx.ctrl is Map) {
|
|
79
|
+
final err = ctx.ctrl['err'];
|
|
80
|
+
if (null != err) {
|
|
81
|
+
try {
|
|
82
|
+
entry['error'] = (err as dynamic).message;
|
|
83
|
+
} catch (_e) {
|
|
84
|
+
entry['error'] = err.toString();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
_finish(ctx, false);
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
void _finish(dynamic ctx, bool ok) {
|
|
93
|
+
final entry = _entries[ctx];
|
|
94
|
+
if (null == entry) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
_entries.remove(ctx);
|
|
98
|
+
entry['ok'] = ok && (null == ctx.result || false != ctx.result.ok);
|
|
99
|
+
var dur = _now() - entry['start'];
|
|
100
|
+
entry['durationMs'] = dur < 0 ? 0 : dur;
|
|
101
|
+
if (null == entry['status'] && null != ctx.result) {
|
|
102
|
+
entry['status'] = ctx.result.status;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
final List buf = _client.track['debug']['entries'];
|
|
106
|
+
buf.add(entry);
|
|
107
|
+
final max = null == options['max'] ? 100 : (options['max'] as num).toInt();
|
|
108
|
+
while (buf.length > max) {
|
|
109
|
+
buf.removeAt(0);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
final onEntry = options['onEntry'];
|
|
113
|
+
if (onEntry is Function) {
|
|
114
|
+
try {
|
|
115
|
+
onEntry(entry);
|
|
116
|
+
} catch (_e) {
|
|
117
|
+
// Callback failures are swallowed.
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
dynamic _redact(dynamic headers) {
|
|
123
|
+
if (headers is! Map) {
|
|
124
|
+
return {};
|
|
125
|
+
}
|
|
126
|
+
final patterns = options['redact'] ??
|
|
127
|
+
[
|
|
128
|
+
'authorization',
|
|
129
|
+
'cookie',
|
|
130
|
+
'set-cookie',
|
|
131
|
+
'api-key',
|
|
132
|
+
'apikey',
|
|
133
|
+
'x-api-key',
|
|
134
|
+
'idempotency-key'
|
|
135
|
+
];
|
|
136
|
+
final out = <String, dynamic>{};
|
|
137
|
+
for (final k in headers.keys) {
|
|
138
|
+
out[k.toString()] =
|
|
139
|
+
(patterns as List).contains(k.toString().toLowerCase())
|
|
140
|
+
? '<redacted>'
|
|
141
|
+
: headers[k];
|
|
142
|
+
}
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
num _now() {
|
|
147
|
+
final now = options['now'];
|
|
148
|
+
if (now is Function) {
|
|
149
|
+
return now();
|
|
150
|
+
}
|
|
151
|
+
return DateTime.now().millisecondsSinceEpoch;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// ignore_for_file: non_constant_identifier_names
|
|
2
|
+
|
|
3
|
+
import 'dart:math';
|
|
4
|
+
|
|
5
|
+
import '../base/BaseFeature.dart';
|
|
6
|
+
|
|
7
|
+
// Idempotency keys for mutating operations. Adds an `Idempotency-Key`
|
|
8
|
+
// header (name configurable) to unsafe requests so a server can
|
|
9
|
+
// de-duplicate retried writes. The key is set once, at PreRequest, before
|
|
10
|
+
// the request is built — so it is stable across transport-level retries of
|
|
11
|
+
// the same call. A caller-supplied header is never overwritten.
|
|
12
|
+
class IdempotencyFeature extends BaseFeature {
|
|
13
|
+
dynamic _client;
|
|
14
|
+
|
|
15
|
+
IdempotencyFeature() {
|
|
16
|
+
version = '0.0.1';
|
|
17
|
+
name = 'idempotency';
|
|
18
|
+
active = true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@override
|
|
22
|
+
dynamic init(dynamic ctx, dynamic opts) {
|
|
23
|
+
_client = ctx.client;
|
|
24
|
+
options = opts is Map ? Map<String, dynamic>.from(opts) : {};
|
|
25
|
+
active = true == options['active'];
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@override
|
|
30
|
+
dynamic PreRequest(dynamic ctx) {
|
|
31
|
+
final spec = ctx.spec;
|
|
32
|
+
if (null == spec) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!_mutating(ctx)) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
final header = options['header'] ?? 'Idempotency-Key';
|
|
41
|
+
spec.headers ??= {};
|
|
42
|
+
|
|
43
|
+
// Respect a key the caller already provided.
|
|
44
|
+
if (null != _existing(spec.headers, header)) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
final key = _genkey();
|
|
49
|
+
spec.headers[header] = key;
|
|
50
|
+
|
|
51
|
+
final track = _client.track;
|
|
52
|
+
if (null == track['idempotency']) {
|
|
53
|
+
track['idempotency'] = <String, dynamic>{'issued': 0, 'last': null};
|
|
54
|
+
}
|
|
55
|
+
track['idempotency']['issued']++;
|
|
56
|
+
track['idempotency']['last'] = key;
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
bool _mutating(dynamic ctx) {
|
|
61
|
+
final methods = options['methods'] ?? ['POST', 'PUT', 'PATCH', 'DELETE'];
|
|
62
|
+
final method = (null == ctx.spec || null == ctx.spec.method)
|
|
63
|
+
? ''
|
|
64
|
+
: ctx.spec.method.toString().toUpperCase();
|
|
65
|
+
if ('' != method && (methods as List).contains(method)) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
final opname = null == ctx.op ? null : ctx.op.name;
|
|
69
|
+
final ops = options['ops'] ?? ['create', 'update', 'remove'];
|
|
70
|
+
return (ops as List).contains(opname);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
dynamic _existing(dynamic headers, String header) {
|
|
74
|
+
final lower = header.toLowerCase();
|
|
75
|
+
for (final k in (headers as Map).keys) {
|
|
76
|
+
if (k.toString().toLowerCase() == lower) {
|
|
77
|
+
return headers[k];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
String _genkey() {
|
|
84
|
+
final keygen = options['keygen'];
|
|
85
|
+
if (keygen is Function) {
|
|
86
|
+
return keygen().toString();
|
|
87
|
+
}
|
|
88
|
+
final rng = Random();
|
|
89
|
+
final h = () => rng.nextInt(0x8000000).toRadixString(16);
|
|
90
|
+
return (h() + h() + h() + h()).padRight(24, '0').substring(0, 24);
|
|
91
|
+
}
|
|
92
|
+
}
|