@ffschrattenecker/tm1-mcp-server 6.0.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/CHANGELOG.md +1211 -0
- package/LICENSE +21 -0
- package/NOTICE +34 -0
- package/README.md +276 -0
- package/dist/config.d.ts +35 -0
- package/dist/config.js +238 -0
- package/dist/http-transport.d.ts +8 -0
- package/dist/http-transport.js +132 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +164 -0
- package/dist/lib/callgraph/callGraph.d.ts +124 -0
- package/dist/lib/callgraph/callGraph.js +334 -0
- package/dist/lib/callgraph/choreGraph.d.ts +23 -0
- package/dist/lib/callgraph/choreGraph.js +102 -0
- package/dist/lib/callgraph/dataFlow.d.ts +74 -0
- package/dist/lib/callgraph/dataFlow.js +271 -0
- package/dist/lib/callgraph/datasourceMembership.d.ts +31 -0
- package/dist/lib/callgraph/datasourceMembership.js +142 -0
- package/dist/lib/callgraph/lintTypes.d.ts +28 -0
- package/dist/lib/callgraph/lintTypes.js +2 -0
- package/dist/lib/callgraph/mdxMembers.d.ts +29 -0
- package/dist/lib/callgraph/mdxMembers.js +97 -0
- package/dist/lib/callgraph/referenceIndex.d.ts +179 -0
- package/dist/lib/callgraph/referenceIndex.js +584 -0
- package/dist/lib/callgraph/rulesLinter.d.ts +55 -0
- package/dist/lib/callgraph/rulesLinter.js +776 -0
- package/dist/lib/callgraph/rulesParser.d.ts +26 -0
- package/dist/lib/callgraph/rulesParser.js +71 -0
- package/dist/lib/callgraph/subsetUsage.d.ts +16 -0
- package/dist/lib/callgraph/subsetUsage.js +134 -0
- package/dist/lib/callgraph/tiParser.d.ts +21 -0
- package/dist/lib/callgraph/tiParser.js +672 -0
- package/dist/lib/callgraph/tiSignatures.d.ts +32 -0
- package/dist/lib/callgraph/tiSignatures.js +6185 -0
- package/dist/lib/callgraph/tm1-adapter.d.ts +32 -0
- package/dist/lib/callgraph/tm1-adapter.js +159 -0
- package/dist/lib/callgraph/types.d.ts +53 -0
- package/dist/lib/callgraph/types.js +2 -0
- package/dist/lib/callgraph/variableEnv.d.ts +86 -0
- package/dist/lib/callgraph/variableEnv.js +214 -0
- package/dist/lib/cell-address.d.ts +12 -0
- package/dist/lib/cell-address.js +71 -0
- package/dist/lib/complexity/antipatterns.d.ts +22 -0
- package/dist/lib/complexity/antipatterns.js +302 -0
- package/dist/lib/complexity/comment-classifier.d.ts +24 -0
- package/dist/lib/complexity/comment-classifier.js +43 -0
- package/dist/lib/complexity/cross-process.d.ts +78 -0
- package/dist/lib/complexity/cross-process.js +114 -0
- package/dist/lib/complexity/process-metrics.d.ts +78 -0
- package/dist/lib/complexity/process-metrics.js +278 -0
- package/dist/lib/complexity/rules-metrics.d.ts +31 -0
- package/dist/lib/complexity/rules-metrics.js +99 -0
- package/dist/lib/concurrency.d.ts +11 -0
- package/dist/lib/concurrency.js +27 -0
- package/dist/lib/control-name.d.ts +10 -0
- package/dist/lib/control-name.js +10 -0
- package/dist/lib/coordinate-error.d.ts +3 -0
- package/dist/lib/coordinate-error.js +29 -0
- package/dist/lib/credential-format.d.ts +23 -0
- package/dist/lib/credential-format.js +25 -0
- package/dist/lib/cube-stats/fetcher.d.ts +106 -0
- package/dist/lib/cube-stats/fetcher.js +194 -0
- package/dist/lib/feeders/brackets.d.ts +47 -0
- package/dist/lib/feeders/brackets.js +195 -0
- package/dist/lib/feeders/element-type-cache.d.ts +42 -0
- package/dist/lib/feeders/element-type-cache.js +86 -0
- package/dist/lib/feeders/static-heuristics.d.ts +101 -0
- package/dist/lib/feeders/static-heuristics.js +202 -0
- package/dist/lib/git-process.d.ts +67 -0
- package/dist/lib/git-process.js +243 -0
- package/dist/lib/mask-secrets.d.ts +10 -0
- package/dist/lib/mask-secrets.js +99 -0
- package/dist/lib/mdx.d.ts +3 -0
- package/dist/lib/mdx.js +13 -0
- package/dist/lib/naming/odata-filter.d.ts +15 -0
- package/dist/lib/naming/odata-filter.js +81 -0
- package/dist/lib/naming/rules.d.ts +34 -0
- package/dist/lib/naming/rules.js +215 -0
- package/dist/lib/pro-parser.d.ts +19 -0
- package/dist/lib/pro-parser.js +260 -0
- package/dist/lib/pro-serializer.d.ts +19 -0
- package/dist/lib/pro-serializer.js +170 -0
- package/dist/lib/process-parts-schema.d.ts +45 -0
- package/dist/lib/process-parts-schema.js +46 -0
- package/dist/lib/process-refs.d.ts +88 -0
- package/dist/lib/process-refs.js +441 -0
- package/dist/lib/rules-outline.d.ts +29 -0
- package/dist/lib/rules-outline.js +68 -0
- package/dist/lib/rules-patch.d.ts +6 -0
- package/dist/lib/rules-patch.js +59 -0
- package/dist/lib/safe-regex.d.ts +15 -0
- package/dist/lib/safe-regex.js +37 -0
- package/dist/lib/sample-cells.d.ts +48 -0
- package/dist/lib/sample-cells.js +146 -0
- package/dist/lib/slim-json-schema.d.ts +20 -0
- package/dist/lib/slim-json-schema.js +68 -0
- package/dist/lib/strip-comments.d.ts +23 -0
- package/dist/lib/strip-comments.js +61 -0
- package/dist/lib/tm1-events.d.ts +16 -0
- package/dist/lib/tm1-events.js +29 -0
- package/dist/lib/v12-compat/deprecated-ti.d.ts +17 -0
- package/dist/lib/v12-compat/deprecated-ti.js +257 -0
- package/dist/lib/v12-compat/scanner.d.ts +10 -0
- package/dist/lib/v12-compat/scanner.js +43 -0
- package/dist/lib/variables-ui-data.d.ts +26 -0
- package/dist/lib/variables-ui-data.js +77 -0
- package/dist/load-env.d.ts +2 -0
- package/dist/load-env.js +29 -0
- package/dist/logger.d.ts +4 -0
- package/dist/logger.js +60 -0
- package/dist/prompts/index.d.ts +3 -0
- package/dist/prompts/index.js +141 -0
- package/dist/resources/index.d.ts +5 -0
- package/dist/resources/index.js +237 -0
- package/dist/resources/list-handler.d.ts +40 -0
- package/dist/resources/list-handler.js +86 -0
- package/dist/resources/subscriptions.d.ts +19 -0
- package/dist/resources/subscriptions.js +80 -0
- package/dist/schemas/cells.d.ts +17 -0
- package/dist/schemas/cells.js +13 -0
- package/dist/schemas/common.d.ts +16 -0
- package/dist/schemas/common.js +14 -0
- package/dist/schemas/index.d.ts +10 -0
- package/dist/schemas/index.js +23 -0
- package/dist/schemas/metadata.d.ts +70 -0
- package/dist/schemas/metadata.js +47 -0
- package/dist/schemas/monitoring.d.ts +95 -0
- package/dist/schemas/monitoring.js +69 -0
- package/dist/schemas/processes.d.ts +74 -0
- package/dist/schemas/processes.js +66 -0
- package/dist/schemas/scheduling.d.ts +13 -0
- package/dist/schemas/scheduling.js +13 -0
- package/dist/schemas/security.d.ts +19 -0
- package/dist/schemas/security.js +16 -0
- package/dist/schemas/subsets.d.ts +12 -0
- package/dist/schemas/subsets.js +12 -0
- package/dist/schemas/views.d.ts +9 -0
- package/dist/schemas/views.js +9 -0
- package/dist/session-manager.d.ts +79 -0
- package/dist/session-manager.js +281 -0
- package/dist/tm1-client/connection/profile.d.ts +13 -0
- package/dist/tm1-client/connection/profile.js +99 -0
- package/dist/tm1-client/dispatcher.d.ts +5 -0
- package/dist/tm1-client/dispatcher.js +31 -0
- package/dist/tm1-client/http.d.ts +81 -0
- package/dist/tm1-client/http.js +547 -0
- package/dist/tm1-client/services/batch-service.d.ts +123 -0
- package/dist/tm1-client/services/batch-service.js +455 -0
- package/dist/tm1-client/services/cell-service.d.ts +71 -0
- package/dist/tm1-client/services/cell-service.js +333 -0
- package/dist/tm1-client/services/cellset-transform.d.ts +26 -0
- package/dist/tm1-client/services/cellset-transform.js +80 -0
- package/dist/tm1-client/services/chore-service.d.ts +68 -0
- package/dist/tm1-client/services/chore-service.js +179 -0
- package/dist/tm1-client/services/chore-status.d.ts +11 -0
- package/dist/tm1-client/services/chore-status.js +127 -0
- package/dist/tm1-client/services/cube-service.d.ts +134 -0
- package/dist/tm1-client/services/cube-service.js +294 -0
- package/dist/tm1-client/services/dimension-service.d.ts +118 -0
- package/dist/tm1-client/services/dimension-service.js +249 -0
- package/dist/tm1-client/services/element-service.d.ts +245 -0
- package/dist/tm1-client/services/element-service.js +809 -0
- package/dist/tm1-client/services/fallback.d.ts +44 -0
- package/dist/tm1-client/services/fallback.js +72 -0
- package/dist/tm1-client/services/file-service.d.ts +53 -0
- package/dist/tm1-client/services/file-service.js +218 -0
- package/dist/tm1-client/services/hierarchy-service.d.ts +129 -0
- package/dist/tm1-client/services/hierarchy-service.js +332 -0
- package/dist/tm1-client/services/monitoring-service.d.ts +34 -0
- package/dist/tm1-client/services/monitoring-service.js +123 -0
- package/dist/tm1-client/services/odata-page.d.ts +71 -0
- package/dist/tm1-client/services/odata-page.js +87 -0
- package/dist/tm1-client/services/process-service.d.ts +278 -0
- package/dist/tm1-client/services/process-service.js +874 -0
- package/dist/tm1-client/services/process-status.d.ts +21 -0
- package/dist/tm1-client/services/process-status.js +89 -0
- package/dist/tm1-client/services/security-service.d.ts +50 -0
- package/dist/tm1-client/services/security-service.js +93 -0
- package/dist/tm1-client/services/server-service.d.ts +116 -0
- package/dist/tm1-client/services/server-service.js +504 -0
- package/dist/tm1-client/services/subset-service.d.ts +37 -0
- package/dist/tm1-client/services/subset-service.js +122 -0
- package/dist/tm1-client/services/view-service.d.ts +46 -0
- package/dist/tm1-client/services/view-service.js +287 -0
- package/dist/tm1-client.d.ts +56 -0
- package/dist/tm1-client.js +126 -0
- package/dist/tools/analysis/analyze-callgraph.d.ts +28 -0
- package/dist/tools/analysis/analyze-callgraph.js +350 -0
- package/dist/tools/analysis/analyze-chore-graph.d.ts +2 -0
- package/dist/tools/analysis/analyze-chore-graph.js +148 -0
- package/dist/tools/analysis/analyze-object-usage.d.ts +2 -0
- package/dist/tools/analysis/analyze-object-usage.js +143 -0
- package/dist/tools/analysis/audit-complexity.d.ts +2 -0
- package/dist/tools/analysis/audit-complexity.js +255 -0
- package/dist/tools/analysis/audit-feeders.d.ts +2 -0
- package/dist/tools/analysis/audit-feeders.js +417 -0
- package/dist/tools/analysis/audit-naming.d.ts +2 -0
- package/dist/tools/analysis/audit-naming.js +379 -0
- package/dist/tools/analysis/check-v12-readiness.d.ts +2 -0
- package/dist/tools/analysis/check-v12-readiness.js +136 -0
- package/dist/tools/analysis/find-orphan-dimensions.d.ts +2 -0
- package/dist/tools/analysis/find-orphan-dimensions.js +55 -0
- package/dist/tools/analysis/invalidate-callgraph-cache.d.ts +2 -0
- package/dist/tools/analysis/invalidate-callgraph-cache.js +40 -0
- package/dist/tools/analysis/trace-data-flow.d.ts +2 -0
- package/dist/tools/analysis/trace-data-flow.js +138 -0
- package/dist/tools/annotations.d.ts +12 -0
- package/dist/tools/annotations.js +61 -0
- package/dist/tools/celldata/check-feeders.d.ts +2 -0
- package/dist/tools/celldata/check-feeders.js +48 -0
- package/dist/tools/celldata/check-writable-coords.d.ts +2 -0
- package/dist/tools/celldata/check-writable-coords.js +126 -0
- package/dist/tools/celldata/execute-mdx.d.ts +17 -0
- package/dist/tools/celldata/execute-mdx.js +126 -0
- package/dist/tools/celldata/get-cell-value.d.ts +2 -0
- package/dist/tools/celldata/get-cell-value.js +30 -0
- package/dist/tools/celldata/get-view-definition.d.ts +2 -0
- package/dist/tools/celldata/get-view-definition.js +29 -0
- package/dist/tools/celldata/get-view.d.ts +2 -0
- package/dist/tools/celldata/get-view.js +55 -0
- package/dist/tools/celldata/sample-cells.d.ts +2 -0
- package/dist/tools/celldata/sample-cells.js +101 -0
- package/dist/tools/celldata/trace-cell-calculation.d.ts +2 -0
- package/dist/tools/celldata/trace-cell-calculation.js +53 -0
- package/dist/tools/celldata/trace-feeders.d.ts +2 -0
- package/dist/tools/celldata/trace-feeders.js +44 -0
- package/dist/tools/celldata/write-cells.d.ts +2 -0
- package/dist/tools/celldata/write-cells.js +65 -0
- package/dist/tools/confirm.d.ts +10 -0
- package/dist/tools/confirm.js +64 -0
- package/dist/tools/define-tool.d.ts +51 -0
- package/dist/tools/define-tool.js +49 -0
- package/dist/tools/dimension-management/bulk-upsert-elements.d.ts +2 -0
- package/dist/tools/dimension-management/bulk-upsert-elements.js +103 -0
- package/dist/tools/dimension-management/create-dimension.d.ts +2 -0
- package/dist/tools/dimension-management/create-dimension.js +22 -0
- package/dist/tools/dimension-management/create-element-attribute.d.ts +2 -0
- package/dist/tools/dimension-management/create-element-attribute.js +26 -0
- package/dist/tools/dimension-management/create-element.d.ts +2 -0
- package/dist/tools/dimension-management/create-element.js +31 -0
- package/dist/tools/dimension-management/create-hierarchy.d.ts +2 -0
- package/dist/tools/dimension-management/create-hierarchy.js +22 -0
- package/dist/tools/dimension-management/delete-dimension.d.ts +2 -0
- package/dist/tools/dimension-management/delete-dimension.js +25 -0
- package/dist/tools/dimension-management/delete-element.d.ts +2 -0
- package/dist/tools/dimension-management/delete-element.js +26 -0
- package/dist/tools/dimension-management/delete-elements.d.ts +2 -0
- package/dist/tools/dimension-management/delete-elements.js +44 -0
- package/dist/tools/dimension-management/delete-hierarchy.d.ts +2 -0
- package/dist/tools/dimension-management/delete-hierarchy.js +35 -0
- package/dist/tools/dimension-management/get-element-attribute-values.d.ts +2 -0
- package/dist/tools/dimension-management/get-element-attribute-values.js +77 -0
- package/dist/tools/dimension-management/list-element-attributes.d.ts +2 -0
- package/dist/tools/dimension-management/list-element-attributes.js +31 -0
- package/dist/tools/dimension-management/move-element.d.ts +2 -0
- package/dist/tools/dimension-management/move-element.js +28 -0
- package/dist/tools/dimension-management/update-element-attribute-value.d.ts +2 -0
- package/dist/tools/dimension-management/update-element-attribute-value.js +80 -0
- package/dist/tools/dimension-management/update-element.d.ts +2 -0
- package/dist/tools/dimension-management/update-element.js +35 -0
- package/dist/tools/error-format.d.ts +22 -0
- package/dist/tools/error-format.js +130 -0
- package/dist/tools/fileops/delete-file.d.ts +2 -0
- package/dist/tools/fileops/delete-file.js +32 -0
- package/dist/tools/fileops/get-file-content.d.ts +2 -0
- package/dist/tools/fileops/get-file-content.js +76 -0
- package/dist/tools/fileops/list-files.d.ts +2 -0
- package/dist/tools/fileops/list-files.js +43 -0
- package/dist/tools/fileops/search-files.d.ts +2 -0
- package/dist/tools/fileops/search-files.js +76 -0
- package/dist/tools/fileops/upload-file.d.ts +2 -0
- package/dist/tools/fileops/upload-file.js +69 -0
- package/dist/tools/format.d.ts +57 -0
- package/dist/tools/format.js +157 -0
- package/dist/tools/hierarchy.d.ts +6 -0
- package/dist/tools/hierarchy.js +27 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.js +263 -0
- package/dist/tools/local-file.d.ts +16 -0
- package/dist/tools/local-file.js +88 -0
- package/dist/tools/metadata/get-ancestors.d.ts +2 -0
- package/dist/tools/metadata/get-ancestors.js +36 -0
- package/dist/tools/metadata/get-descendants.d.ts +2 -0
- package/dist/tools/metadata/get-descendants.js +64 -0
- package/dist/tools/metadata/get-hierarchy.d.ts +2 -0
- package/dist/tools/metadata/get-hierarchy.js +130 -0
- package/dist/tools/metadata/list-chores.d.ts +2 -0
- package/dist/tools/metadata/list-chores.js +62 -0
- package/dist/tools/metadata/list-cubes.d.ts +2 -0
- package/dist/tools/metadata/list-cubes.js +128 -0
- package/dist/tools/metadata/list-dimensions.d.ts +2 -0
- package/dist/tools/metadata/list-dimensions.js +131 -0
- package/dist/tools/metadata/list-processes-grouped.d.ts +2 -0
- package/dist/tools/metadata/list-processes-grouped.js +96 -0
- package/dist/tools/metadata/list-processes.d.ts +2 -0
- package/dist/tools/metadata/list-processes.js +108 -0
- package/dist/tools/metadata/resolve-default-members.d.ts +2 -0
- package/dist/tools/metadata/resolve-default-members.js +46 -0
- package/dist/tools/model-building/check-cube-rule.d.ts +2 -0
- package/dist/tools/model-building/check-cube-rule.js +51 -0
- package/dist/tools/model-building/clear-cube.d.ts +2 -0
- package/dist/tools/model-building/clear-cube.js +46 -0
- package/dist/tools/model-building/create-cube.d.ts +2 -0
- package/dist/tools/model-building/create-cube.js +34 -0
- package/dist/tools/model-building/delete-cube.d.ts +2 -0
- package/dist/tools/model-building/delete-cube.js +27 -0
- package/dist/tools/model-building/get-all-cube-rules.d.ts +2 -0
- package/dist/tools/model-building/get-all-cube-rules.js +149 -0
- package/dist/tools/model-building/get-cube-rules.d.ts +2 -0
- package/dist/tools/model-building/get-cube-rules.js +69 -0
- package/dist/tools/model-building/rules-source.d.ts +21 -0
- package/dist/tools/model-building/rules-source.js +49 -0
- package/dist/tools/model-building/search-rules.d.ts +2 -0
- package/dist/tools/model-building/search-rules.js +132 -0
- package/dist/tools/model-building/set-cube-rules.d.ts +2 -0
- package/dist/tools/model-building/set-cube-rules.js +81 -0
- package/dist/tools/model-building/unload-cube.d.ts +2 -0
- package/dist/tools/model-building/unload-cube.js +23 -0
- package/dist/tools/operations/diagnose-process-error.d.ts +2 -0
- package/dist/tools/operations/diagnose-process-error.js +178 -0
- package/dist/tools/operations/error-log-helpers.d.ts +25 -0
- package/dist/tools/operations/error-log-helpers.js +57 -0
- package/dist/tools/operations/get-audit-log.d.ts +2 -0
- package/dist/tools/operations/get-audit-log.js +70 -0
- package/dist/tools/operations/get-cube-stats.d.ts +2 -0
- package/dist/tools/operations/get-cube-stats.js +107 -0
- package/dist/tools/operations/get-error-log-content.d.ts +2 -0
- package/dist/tools/operations/get-error-log-content.js +164 -0
- package/dist/tools/operations/get-jobs.d.ts +3 -0
- package/dist/tools/operations/get-jobs.js +51 -0
- package/dist/tools/operations/get-message-log.d.ts +2 -0
- package/dist/tools/operations/get-message-log.js +63 -0
- package/dist/tools/operations/get-server-info.d.ts +2 -0
- package/dist/tools/operations/get-server-info.js +180 -0
- package/dist/tools/operations/get-server-state.d.ts +2 -0
- package/dist/tools/operations/get-server-state.js +129 -0
- package/dist/tools/operations/get-sessions.d.ts +2 -0
- package/dist/tools/operations/get-sessions.js +76 -0
- package/dist/tools/operations/get-threads.d.ts +3 -0
- package/dist/tools/operations/get-threads.js +50 -0
- package/dist/tools/operations/get-transaction-log.d.ts +2 -0
- package/dist/tools/operations/get-transaction-log.js +63 -0
- package/dist/tools/operations/list-error-logs.d.ts +8 -0
- package/dist/tools/operations/list-error-logs.js +157 -0
- package/dist/tools/operations/save-data.d.ts +2 -0
- package/dist/tools/operations/save-data.js +50 -0
- package/dist/tools/pagination.d.ts +32 -0
- package/dist/tools/pagination.js +116 -0
- package/dist/tools/scheduling/create-chore.d.ts +2 -0
- package/dist/tools/scheduling/create-chore.js +93 -0
- package/dist/tools/scheduling/delete-chore.d.ts +2 -0
- package/dist/tools/scheduling/delete-chore.js +22 -0
- package/dist/tools/scheduling/execute-chore.d.ts +2 -0
- package/dist/tools/scheduling/execute-chore.js +52 -0
- package/dist/tools/scheduling/toggle-chore.d.ts +2 -0
- package/dist/tools/scheduling/toggle-chore.js +22 -0
- package/dist/tools/scheduling/update-chore.d.ts +2 -0
- package/dist/tools/scheduling/update-chore.js +71 -0
- package/dist/tools/schemas/common.d.ts +10 -0
- package/dist/tools/schemas/common.js +23 -0
- package/dist/tools/schemas/items-analysis.d.ts +394 -0
- package/dist/tools/schemas/items-analysis.js +419 -0
- package/dist/tools/schemas/items-cells.d.ts +70 -0
- package/dist/tools/schemas/items-cells.js +65 -0
- package/dist/tools/schemas/items-common.d.ts +18 -0
- package/dist/tools/schemas/items-common.js +39 -0
- package/dist/tools/schemas/items-fileops.d.ts +11 -0
- package/dist/tools/schemas/items-fileops.js +13 -0
- package/dist/tools/schemas/items-metadata.d.ts +262 -0
- package/dist/tools/schemas/items-metadata.js +184 -0
- package/dist/tools/schemas/items-monitoring.d.ts +79 -0
- package/dist/tools/schemas/items-monitoring.js +78 -0
- package/dist/tools/schemas/items-processes.d.ts +279 -0
- package/dist/tools/schemas/items-processes.js +235 -0
- package/dist/tools/schemas/items-scheduling.d.ts +25 -0
- package/dist/tools/schemas/items-scheduling.js +26 -0
- package/dist/tools/schemas/items-security.d.ts +19 -0
- package/dist/tools/schemas/items-security.js +12 -0
- package/dist/tools/schemas/items-subsets.d.ts +2 -0
- package/dist/tools/schemas/items-subsets.js +4 -0
- package/dist/tools/schemas/items-views.d.ts +38 -0
- package/dist/tools/schemas/items-views.js +26 -0
- package/dist/tools/schemas/items.d.ts +12 -0
- package/dist/tools/schemas/items.js +23 -0
- package/dist/tools/schemas/markdown-capable.d.ts +21 -0
- package/dist/tools/schemas/markdown-capable.js +58 -0
- package/dist/tools/schemas/output-schema.d.ts +24 -0
- package/dist/tools/schemas/output-schema.js +25 -0
- package/dist/tools/security/assign-client-group.d.ts +2 -0
- package/dist/tools/security/assign-client-group.js +20 -0
- package/dist/tools/security/create-client.d.ts +2 -0
- package/dist/tools/security/create-client.js +28 -0
- package/dist/tools/security/delete-client.d.ts +2 -0
- package/dist/tools/security/delete-client.js +22 -0
- package/dist/tools/security/get-client.d.ts +2 -0
- package/dist/tools/security/get-client.js +20 -0
- package/dist/tools/security/list-clients.d.ts +2 -0
- package/dist/tools/security/list-clients.js +86 -0
- package/dist/tools/security/list-groups.d.ts +2 -0
- package/dist/tools/security/list-groups.js +43 -0
- package/dist/tools/security/remove-client-group.d.ts +2 -0
- package/dist/tools/security/remove-client-group.js +27 -0
- package/dist/tools/security/update-client.d.ts +2 -0
- package/dist/tools/security/update-client.js +22 -0
- package/dist/tools/subsets/create-subset.d.ts +2 -0
- package/dist/tools/subsets/create-subset.js +44 -0
- package/dist/tools/subsets/delete-subset.d.ts +2 -0
- package/dist/tools/subsets/delete-subset.js +26 -0
- package/dist/tools/subsets/get-subset.d.ts +2 -0
- package/dist/tools/subsets/get-subset.js +29 -0
- package/dist/tools/subsets/list-subsets.d.ts +2 -0
- package/dist/tools/subsets/list-subsets.js +36 -0
- package/dist/tools/subsets/update-subset.d.ts +2 -0
- package/dist/tools/subsets/update-subset.js +33 -0
- package/dist/tools/ti-development/check-process-code.d.ts +2 -0
- package/dist/tools/ti-development/check-process-code.js +96 -0
- package/dist/tools/ti-development/compile-process.d.ts +2 -0
- package/dist/tools/ti-development/compile-process.js +32 -0
- package/dist/tools/ti-development/copy-process.d.ts +2 -0
- package/dist/tools/ti-development/copy-process.js +32 -0
- package/dist/tools/ti-development/delete-process.d.ts +2 -0
- package/dist/tools/ti-development/delete-process.js +26 -0
- package/dist/tools/ti-development/diff-process-with-file.d.ts +2 -0
- package/dist/tools/ti-development/diff-process-with-file.js +208 -0
- package/dist/tools/ti-development/diff-processes.d.ts +2 -0
- package/dist/tools/ti-development/diff-processes.js +312 -0
- package/dist/tools/ti-development/execute-process.d.ts +8 -0
- package/dist/tools/ti-development/execute-process.js +109 -0
- package/dist/tools/ti-development/export-process-to-git.d.ts +2 -0
- package/dist/tools/ti-development/export-process-to-git.js +113 -0
- package/dist/tools/ti-development/export-process-to-pro.d.ts +2 -0
- package/dist/tools/ti-development/export-process-to-pro.js +117 -0
- package/dist/tools/ti-development/get-all-processes-code.d.ts +2 -0
- package/dist/tools/ti-development/get-all-processes-code.js +131 -0
- package/dist/tools/ti-development/get-process-code.d.ts +2 -0
- package/dist/tools/ti-development/get-process-code.js +100 -0
- package/dist/tools/ti-development/get-process-datasource.d.ts +2 -0
- package/dist/tools/ti-development/get-process-datasource.js +20 -0
- package/dist/tools/ti-development/get-process-parameters.d.ts +2 -0
- package/dist/tools/ti-development/get-process-parameters.js +25 -0
- package/dist/tools/ti-development/get-process-variables.d.ts +2 -0
- package/dist/tools/ti-development/get-process-variables.js +38 -0
- package/dist/tools/ti-development/get-process.d.ts +2 -0
- package/dist/tools/ti-development/get-process.js +163 -0
- package/dist/tools/ti-development/import-pro-file.d.ts +2 -0
- package/dist/tools/ti-development/import-pro-file.js +153 -0
- package/dist/tools/ti-development/import-process-from-git.d.ts +2 -0
- package/dist/tools/ti-development/import-process-from-git.js +198 -0
- package/dist/tools/ti-development/install-pro-bundle.d.ts +2 -0
- package/dist/tools/ti-development/install-pro-bundle.js +288 -0
- package/dist/tools/ti-development/preflight.d.ts +39 -0
- package/dist/tools/ti-development/preflight.js +56 -0
- package/dist/tools/ti-development/process-backup.d.ts +29 -0
- package/dist/tools/ti-development/process-backup.js +118 -0
- package/dist/tools/ti-development/search-code.d.ts +2 -0
- package/dist/tools/ti-development/search-code.js +208 -0
- package/dist/tools/ti-development/upsert-process.d.ts +2 -0
- package/dist/tools/ti-development/upsert-process.js +167 -0
- package/dist/tools/ti-development/validate-process-refs.d.ts +2 -0
- package/dist/tools/ti-development/validate-process-refs.js +73 -0
- package/dist/tools/views/create-mdx-view.d.ts +2 -0
- package/dist/tools/views/create-mdx-view.js +21 -0
- package/dist/tools/views/create-native-view.d.ts +2 -0
- package/dist/tools/views/create-native-view.js +81 -0
- package/dist/tools/views/delete-view.d.ts +2 -0
- package/dist/tools/views/delete-view.js +23 -0
- package/dist/tools/views/list-views.d.ts +2 -0
- package/dist/tools/views/list-views.js +32 -0
- package/dist/tools/with-annotations.d.ts +6 -0
- package/dist/tools/with-annotations.js +339 -0
- package/dist/types.d.ts +409 -0
- package/dist/types.js +91 -0
- package/dist/version.d.ts +4 -0
- package/dist/version.js +18 -0
- package/package.json +107 -0
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
import { createConnectionProfile, } from "./connection/profile.js";
|
|
2
|
+
import { TM1Error, TM1ErrorCode } from "../types.js";
|
|
3
|
+
import { PRODUCT, VERSION } from "../version.js";
|
|
4
|
+
import { getTm1Dispatcher, tm1Fetch } from "./dispatcher.js";
|
|
5
|
+
import { tm1Events } from "../lib/tm1-events.js";
|
|
6
|
+
import { maskSecretValues } from "../lib/mask-secrets.js";
|
|
7
|
+
const MAX_NETWORK_RETRIES = 3;
|
|
8
|
+
const BACKOFF_BASE_MS = 1000;
|
|
9
|
+
const USER_AGENT = `${PRODUCT}/${VERSION}`;
|
|
10
|
+
// R2-22: any successful mutating HTTP call invalidates the callgraph
|
|
11
|
+
// reference-index cache. Cheap (Map.clear()) and rebuild is lazy on next
|
|
12
|
+
// read — over-invalidation on non-graph-affecting calls (write_cells,
|
|
13
|
+
// upload_file, CheckRules) costs at most one rebuild. The alternative —
|
|
14
|
+
// per-service hooks across 17 mutation methods — risks drift as new
|
|
15
|
+
// mutating methods are added.
|
|
16
|
+
function isSafeHttpMethod(method) {
|
|
17
|
+
return method === "GET" || method === "HEAD";
|
|
18
|
+
}
|
|
19
|
+
// Link an external AbortSignal (e.g. from RequestHandlerExtra.signal) to a
|
|
20
|
+
// locally-owned timeout AbortController. Returns an unsubscribe function the
|
|
21
|
+
// caller must invoke in `finally` to avoid leaking the listener after the
|
|
22
|
+
// request resolves. If the external signal is already aborted, propagates the
|
|
23
|
+
// reason immediately.
|
|
24
|
+
function linkAbortSignals(local, external) {
|
|
25
|
+
if (!external)
|
|
26
|
+
return () => undefined;
|
|
27
|
+
if (external.aborted) {
|
|
28
|
+
local.abort(external.reason);
|
|
29
|
+
return () => undefined;
|
|
30
|
+
}
|
|
31
|
+
const onAbort = () => local.abort(external.reason);
|
|
32
|
+
external.addEventListener("abort", onAbort, { once: true });
|
|
33
|
+
return () => external.removeEventListener("abort", onAbort);
|
|
34
|
+
}
|
|
35
|
+
export class TM1HttpClient {
|
|
36
|
+
// config is private: it carries credentials (TM1Config.password), so it must
|
|
37
|
+
// not be reachable as `client.config` from the service layer or leak into the
|
|
38
|
+
// compiled .d.ts public surface. Services get only what they need —
|
|
39
|
+
// `version` (numeric getter) for version-conditional paths and `logger`
|
|
40
|
+
// for structured logs.
|
|
41
|
+
config;
|
|
42
|
+
logger;
|
|
43
|
+
sessionManager;
|
|
44
|
+
// v11↔v12 URL-rerooting seam. v11: identity (paths unchanged). v12: rewrites
|
|
45
|
+
// `/api/v1/...` to the database-rooted `/{instance}/api/v1/Databases('{db}')/...`.
|
|
46
|
+
profile;
|
|
47
|
+
constructor(config, sessionManager, logger) {
|
|
48
|
+
this.config = config;
|
|
49
|
+
this.sessionManager = sessionManager;
|
|
50
|
+
this.logger = logger;
|
|
51
|
+
this.profile = createConnectionProfile(config);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* TM1 major version (11 | 12) — the single numeric source of truth for
|
|
55
|
+
* version-conditional service code. Branch on this, never on the display
|
|
56
|
+
* string (`tm1Version`): a v12 connection can carry a "11.x"-looking
|
|
57
|
+
* TM1_VERSION string, but `version` is always authoritative.
|
|
58
|
+
*/
|
|
59
|
+
get version() {
|
|
60
|
+
return this.config.version;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* TM1 server version STRING (e.g. "11.8.…") — DISPLAY ONLY (server_info
|
|
64
|
+
* output, log fields, user-facing error messages). Do NOT branch on this;
|
|
65
|
+
* use the numeric `version` getter instead.
|
|
66
|
+
*/
|
|
67
|
+
get tm1Version() {
|
|
68
|
+
return this.config.tm1Version;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Make an authenticated HTTP request to the TM1 REST API.
|
|
72
|
+
*
|
|
73
|
+
* - Ensures an active session via SessionManager
|
|
74
|
+
* - On 401: re-authenticates once and retries
|
|
75
|
+
* - On network error for safe methods (GET/HEAD): retries up to 3 times with exponential backoff (1s, 2s, 4s)
|
|
76
|
+
* - On network error for non-safe methods (POST/PUT/PATCH/DELETE): does NOT retry — these are not idempotent
|
|
77
|
+
* and a retry could spawn duplicate side-effects (e.g. parallel TI runs on tm1.Execute)
|
|
78
|
+
* - On other HTTP errors: classifies and throws TM1Error
|
|
79
|
+
*/
|
|
80
|
+
/** @internal — for Service-layer use; not part of the public consumer API. */
|
|
81
|
+
async request(method, path, body, opts) {
|
|
82
|
+
const url = `${this.config.baseUrl}${this.profile.resolveApiPath(path)}`;
|
|
83
|
+
const isSafeMethod = isSafeHttpMethod(method);
|
|
84
|
+
const allowRetry = opts?.retry !== false;
|
|
85
|
+
const maxAttempts = isSafeMethod && allowRetry ? MAX_NETWORK_RETRIES : 0;
|
|
86
|
+
let lastError;
|
|
87
|
+
for (let attempt = 0; attempt <= maxAttempts; attempt++) {
|
|
88
|
+
if (attempt > 0) {
|
|
89
|
+
const delayMs = BACKOFF_BASE_MS * Math.pow(2, attempt - 1);
|
|
90
|
+
this.logger.warn({ attempt, delayMs, endpoint: path }, "Retrying after network error");
|
|
91
|
+
await sleep(delayMs);
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const cookie = await this.sessionManager.ensureSession();
|
|
95
|
+
const response = await this.executeRequest(url, method, cookie, body, opts?.timeoutMs, opts?.signal);
|
|
96
|
+
if (response.status === 401) {
|
|
97
|
+
this.logger.warn({ endpoint: path }, "Received 401, re-authenticating");
|
|
98
|
+
// Pass the cookie that got the 401 so a concurrent request that
|
|
99
|
+
// already rotated the session short-circuits to the fresh cookie
|
|
100
|
+
// instead of forcing another logout+login (staggered-401 churn).
|
|
101
|
+
const newCookie = await this.sessionManager.authenticate(cookie);
|
|
102
|
+
const retryResponse = await this.executeRequest(url, method, newCookie, body, opts?.timeoutMs, opts?.signal);
|
|
103
|
+
if (retryResponse.status === 401) {
|
|
104
|
+
throw new TM1Error({
|
|
105
|
+
code: TM1ErrorCode.AUTH_FAILED,
|
|
106
|
+
message: "Authentication failed after re-authentication attempt",
|
|
107
|
+
httpStatus: 401,
|
|
108
|
+
endpoint: path,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
const retryResult = await this.handleResponse(retryResponse, path);
|
|
112
|
+
if (!isSafeMethod) {
|
|
113
|
+
tm1Events.emit("mutation", { method, path });
|
|
114
|
+
}
|
|
115
|
+
return retryResult;
|
|
116
|
+
}
|
|
117
|
+
const result = await this.handleResponse(response, path);
|
|
118
|
+
if (!isSafeMethod) {
|
|
119
|
+
tm1Events.emit("mutation", { method, path });
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (error instanceof TM1Error) {
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
if (isTimeoutError(error)) {
|
|
128
|
+
const ms = opts?.timeoutMs ?? this.config.requestTimeoutMs;
|
|
129
|
+
throw new TM1Error({
|
|
130
|
+
code: TM1ErrorCode.LOCK_TIMEOUT,
|
|
131
|
+
message: `Request to ${path} timed out after ${ms}ms`,
|
|
132
|
+
endpoint: path,
|
|
133
|
+
hint: isSafeMethod
|
|
134
|
+
? "Query timed out — result set may be too large. Add filters or reduce scope. If a lock is suspected, use tm1_list_threads to diagnose."
|
|
135
|
+
: "Request timed out — TM1 server may be waiting on a lock held by another session. Use tm1_list_threads to diagnose and cancel the blocking thread.",
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (opts?.signal?.aborted) {
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
if (this.isNetworkError(error)) {
|
|
142
|
+
lastError = error;
|
|
143
|
+
this.logger.error({ err: error, attempt, endpoint: path }, "Network error during request");
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
throw new TM1Error({
|
|
147
|
+
code: TM1ErrorCode.CONNECTION_FAILED,
|
|
148
|
+
message: error instanceof Error ? error.message : String(error),
|
|
149
|
+
endpoint: path,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
throw new TM1Error({
|
|
154
|
+
code: TM1ErrorCode.CONNECTION_FAILED,
|
|
155
|
+
message: isSafeMethod
|
|
156
|
+
? `Request failed after ${MAX_NETWORK_RETRIES} retries: ${lastError instanceof Error ? lastError.message : String(lastError)}`
|
|
157
|
+
: `Request failed (no retry for ${method}): ${lastError instanceof Error ? lastError.message : String(lastError)}`,
|
|
158
|
+
endpoint: path,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Make an authenticated HTTP request that returns raw text (not JSON).
|
|
163
|
+
* Used for file content downloads where the response is CSV/TXT/etc.
|
|
164
|
+
* Re-auths once on 401 like request().
|
|
165
|
+
*/
|
|
166
|
+
/** @internal — for Service-layer use; not part of the public consumer API. */
|
|
167
|
+
async requestRaw(method, path, opts) {
|
|
168
|
+
const url = `${this.config.baseUrl}${this.profile.resolveApiPath(path)}`;
|
|
169
|
+
const effectiveTimeout = opts?.timeoutMs ?? this.config.requestTimeoutMs;
|
|
170
|
+
const cookie = await this.sessionManager.ensureSession();
|
|
171
|
+
const headers = {
|
|
172
|
+
Cookie: `TM1SessionId=${cookie}`,
|
|
173
|
+
Accept: "*/*",
|
|
174
|
+
"User-Agent": USER_AGENT,
|
|
175
|
+
"TM1-SessionContext": USER_AGENT,
|
|
176
|
+
"TM1-Session-Context": USER_AGENT,
|
|
177
|
+
};
|
|
178
|
+
const response = await this.withReauth((c) => {
|
|
179
|
+
const hdrs = { ...headers, Cookie: `TM1SessionId=${c}` };
|
|
180
|
+
return this.sendOnce(url, method, hdrs, undefined, effectiveTimeout, opts?.signal);
|
|
181
|
+
}, cookie, path, effectiveTimeout);
|
|
182
|
+
if (!response.ok) {
|
|
183
|
+
let body = "";
|
|
184
|
+
try {
|
|
185
|
+
body = await response.text();
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
/* ignore */
|
|
189
|
+
}
|
|
190
|
+
throw this.classifyHttpError(response.status, path, body || undefined);
|
|
191
|
+
}
|
|
192
|
+
const text = await response.text();
|
|
193
|
+
if (!isSafeHttpMethod(method)) {
|
|
194
|
+
tm1Events.emit("mutation", { method, path });
|
|
195
|
+
}
|
|
196
|
+
return text;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Make an authenticated HTTP request with a binary body (Buffer/Uint8Array).
|
|
200
|
+
* Used for blob/file uploads where the body is raw bytes (not JSON).
|
|
201
|
+
* Sends Content-Type: application/octet-stream by default.
|
|
202
|
+
* Re-auths once on 401 like request(). No network-error retries (non-safe methods).
|
|
203
|
+
*/
|
|
204
|
+
/** @internal — for Service-layer use; not part of the public consumer API. */
|
|
205
|
+
async requestBinary(method, path, body, contentType = "application/octet-stream", opts) {
|
|
206
|
+
const url = `${this.config.baseUrl}${this.profile.resolveApiPath(path)}`;
|
|
207
|
+
const effectiveTimeout = opts?.timeoutMs ?? this.config.requestTimeoutMs;
|
|
208
|
+
const cookie = await this.sessionManager.ensureSession();
|
|
209
|
+
const response = await this.withReauth((c) => this.sendOnce(url, method, {
|
|
210
|
+
Cookie: `TM1SessionId=${c}`,
|
|
211
|
+
Accept: "application/json,*/*",
|
|
212
|
+
"Content-Type": contentType,
|
|
213
|
+
"User-Agent": USER_AGENT,
|
|
214
|
+
"TM1-SessionContext": USER_AGENT,
|
|
215
|
+
"TM1-Session-Context": USER_AGENT,
|
|
216
|
+
}, body, effectiveTimeout, opts?.signal), cookie, path, effectiveTimeout);
|
|
217
|
+
if (!response.ok) {
|
|
218
|
+
let errBody = "";
|
|
219
|
+
try {
|
|
220
|
+
errBody = await response.text();
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
/* ignore */
|
|
224
|
+
}
|
|
225
|
+
throw this.classifyHttpError(response.status, path, errBody || undefined);
|
|
226
|
+
}
|
|
227
|
+
if (!isSafeHttpMethod(method)) {
|
|
228
|
+
tm1Events.emit("mutation", { method, path });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Low-level single fetch with timeout + AbortSignal wiring.
|
|
233
|
+
* Does NOT handle 401, retries, or error classification — callers own that.
|
|
234
|
+
*/
|
|
235
|
+
async sendOnce(url, method, headers, body, timeoutMs, externalSignal) {
|
|
236
|
+
const controller = new AbortController();
|
|
237
|
+
const timeout = setTimeout(() => controller.abort(new DOMException("Request timed out", "TimeoutError")), timeoutMs);
|
|
238
|
+
const unlink = linkAbortSignals(controller, externalSignal);
|
|
239
|
+
try {
|
|
240
|
+
return await tm1Fetch(url, {
|
|
241
|
+
method,
|
|
242
|
+
headers,
|
|
243
|
+
body,
|
|
244
|
+
signal: controller.signal,
|
|
245
|
+
dispatcher: getTm1Dispatcher(this.config),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
clearTimeout(timeout);
|
|
250
|
+
unlink();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Wraps a send function with cookie-refresh-on-401 and timeout→LOCK_TIMEOUT mapping.
|
|
255
|
+
* Used by requestRaw and requestBinary (which have no network-retry loop).
|
|
256
|
+
* On 401: calls sessionManager.authenticate() and retries once with the new cookie.
|
|
257
|
+
* The caller is responsible for obtaining the initial cookie via ensureSession().
|
|
258
|
+
*/
|
|
259
|
+
async withReauth(send, initialCookie, path, effectiveTimeout) {
|
|
260
|
+
let response;
|
|
261
|
+
try {
|
|
262
|
+
response = await send(initialCookie);
|
|
263
|
+
}
|
|
264
|
+
catch (err) {
|
|
265
|
+
if (isTimeoutError(err)) {
|
|
266
|
+
throw new TM1Error({
|
|
267
|
+
code: TM1ErrorCode.LOCK_TIMEOUT,
|
|
268
|
+
message: `Request to ${path} timed out after ${effectiveTimeout}ms`,
|
|
269
|
+
endpoint: path,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
throw err;
|
|
273
|
+
}
|
|
274
|
+
if (response.status === 401) {
|
|
275
|
+
const newCookie = await this.sessionManager.authenticate(initialCookie);
|
|
276
|
+
try {
|
|
277
|
+
response = await send(newCookie);
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
if (isTimeoutError(err)) {
|
|
281
|
+
throw new TM1Error({
|
|
282
|
+
code: TM1ErrorCode.LOCK_TIMEOUT,
|
|
283
|
+
message: `Request to ${path} timed out after ${effectiveTimeout}ms`,
|
|
284
|
+
endpoint: path,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
throw err;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return response;
|
|
291
|
+
}
|
|
292
|
+
async executeRequest(url, method, cookie, body, timeoutMs, externalSignal) {
|
|
293
|
+
const headers = {
|
|
294
|
+
Cookie: `TM1SessionId=${cookie}`,
|
|
295
|
+
Accept: "application/json",
|
|
296
|
+
"Accept-Encoding": "gzip, deflate",
|
|
297
|
+
"User-Agent": USER_AGENT,
|
|
298
|
+
"TM1-SessionContext": USER_AGENT,
|
|
299
|
+
"TM1-Session-Context": USER_AGENT,
|
|
300
|
+
};
|
|
301
|
+
const isWriteMethod = method === "POST" || method === "PUT" || method === "PATCH";
|
|
302
|
+
if (body !== undefined || isWriteMethod) {
|
|
303
|
+
headers["Content-Type"] = "application/json";
|
|
304
|
+
}
|
|
305
|
+
const serializedBody = body !== undefined
|
|
306
|
+
? JSON.stringify(body)
|
|
307
|
+
: isWriteMethod
|
|
308
|
+
? ""
|
|
309
|
+
: undefined;
|
|
310
|
+
return this.sendOnce(url, method, headers, serializedBody, timeoutMs ?? this.config.requestTimeoutMs, externalSignal);
|
|
311
|
+
}
|
|
312
|
+
async handleResponse(response, endpoint) {
|
|
313
|
+
if (response.ok) {
|
|
314
|
+
const text = await response.text();
|
|
315
|
+
if (response.status === 204 || !text) {
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
this.logger.debug({ endpoint, status: response.status }, "Request successful");
|
|
319
|
+
try {
|
|
320
|
+
return JSON.parse(text);
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
throw new TM1Error({
|
|
324
|
+
code: TM1ErrorCode.TM1_ERROR,
|
|
325
|
+
message: `TM1 returned a non-JSON response body (status ${response.status}) for ${endpoint}. ` +
|
|
326
|
+
`This usually means a proxy or gateway returned HTML/text instead of the TM1 REST API.`,
|
|
327
|
+
httpStatus: response.status,
|
|
328
|
+
endpoint,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
let details;
|
|
333
|
+
let errorBody;
|
|
334
|
+
try {
|
|
335
|
+
errorBody = await response.text();
|
|
336
|
+
if (errorBody) {
|
|
337
|
+
const parsed = JSON.parse(errorBody);
|
|
338
|
+
details = odataErrorText(parsed) ?? errorBody;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
/* ignore parse errors */
|
|
343
|
+
}
|
|
344
|
+
const error = this.classifyHttpError(response.status, endpoint, details);
|
|
345
|
+
this.logger.error({ endpoint, status: response.status, code: error.code }, error.message);
|
|
346
|
+
throw error;
|
|
347
|
+
}
|
|
348
|
+
classifyHttpError(status, endpoint, details) {
|
|
349
|
+
return classifyHttpError(status, endpoint, details);
|
|
350
|
+
}
|
|
351
|
+
isNetworkError(error) {
|
|
352
|
+
return isNetworkErrorImpl(error);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Map an HTTP status + TM1 error text onto a TM1Error. Module-level (not just a
|
|
357
|
+
* TM1HttpClient method) because `$batch` sub-responses arrive as plain
|
|
358
|
+
* {status, body} records inside a 200 envelope — BatchService has to classify
|
|
359
|
+
* them itself, and must do it identically to a standalone request rather than
|
|
360
|
+
* with a drifting copy of these rules.
|
|
361
|
+
*/
|
|
362
|
+
export function classifyHttpError(status, endpoint, rawDetails) {
|
|
363
|
+
// S4/S5: sanitize ONCE, here, where the server's own text enters our error
|
|
364
|
+
// model. Everything downstream reads the TM1Error this builds — the
|
|
365
|
+
// logger.error() in handleResponse and the MCP error envelope alike — so
|
|
366
|
+
// masking here covers both exits with no chance of one being forgotten.
|
|
367
|
+
//
|
|
368
|
+
// This is the gap key-based redaction cannot close: pino's `redact` masks a
|
|
369
|
+
// field NAMED password, but a TM1 failure such as
|
|
370
|
+
// "[ODBC Driver] login failed for PWD=hunter2" is a plain message string, and
|
|
371
|
+
// it went to the log and back to the client verbatim.
|
|
372
|
+
const details = rawDetails === undefined ? undefined : maskSecretValues(rawDetails);
|
|
373
|
+
{
|
|
374
|
+
// TM1 signals object-level security denial via the error MESSAGE, often
|
|
375
|
+
// with HTTP 400 (not 403) — e.g. reading a control cube as a non-admin
|
|
376
|
+
// returns 400 {"error":{"code":"65","message":"ObjectSecurityNoReadRights"}}.
|
|
377
|
+
// Classify by message so the caller gets PERMISSION_DENIED + its actionable
|
|
378
|
+
// hint instead of a generic TM1_ERROR. Verified live with a cube-only user.
|
|
379
|
+
if (details &&
|
|
380
|
+
/No(Read|Write|Admin)Rights|ObjectSecurity|SecurityAccess|not\s+authori[sz]ed/i.test(details)) {
|
|
381
|
+
return new TM1Error({
|
|
382
|
+
code: TM1ErrorCode.PERMISSION_DENIED,
|
|
383
|
+
message: details,
|
|
384
|
+
httpStatus: status,
|
|
385
|
+
endpoint,
|
|
386
|
+
details,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
switch (status) {
|
|
390
|
+
case 401:
|
|
391
|
+
return new TM1Error({
|
|
392
|
+
code: TM1ErrorCode.AUTH_FAILED,
|
|
393
|
+
message: details ?? "Authentication failed",
|
|
394
|
+
httpStatus: status,
|
|
395
|
+
endpoint,
|
|
396
|
+
details,
|
|
397
|
+
});
|
|
398
|
+
case 403:
|
|
399
|
+
return new TM1Error({
|
|
400
|
+
code: TM1ErrorCode.PERMISSION_DENIED,
|
|
401
|
+
message: details ?? "Permission denied",
|
|
402
|
+
httpStatus: status,
|
|
403
|
+
endpoint,
|
|
404
|
+
details,
|
|
405
|
+
});
|
|
406
|
+
case 404:
|
|
407
|
+
return new TM1Error({
|
|
408
|
+
code: TM1ErrorCode.NOT_FOUND,
|
|
409
|
+
message: details ?? "Resource not found",
|
|
410
|
+
httpStatus: status,
|
|
411
|
+
endpoint,
|
|
412
|
+
details,
|
|
413
|
+
});
|
|
414
|
+
case 409:
|
|
415
|
+
return new TM1Error({
|
|
416
|
+
code: TM1ErrorCode.CONFLICT,
|
|
417
|
+
message: details ?? "Resource conflict",
|
|
418
|
+
httpStatus: status,
|
|
419
|
+
endpoint,
|
|
420
|
+
details,
|
|
421
|
+
});
|
|
422
|
+
default:
|
|
423
|
+
return new TM1Error({
|
|
424
|
+
code: TM1ErrorCode.TM1_ERROR,
|
|
425
|
+
message: details ?? `TM1 API error (HTTP ${status})`,
|
|
426
|
+
httpStatus: status,
|
|
427
|
+
endpoint,
|
|
428
|
+
details,
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Is this a transient transport failure worth retrying (as opposed to our own
|
|
435
|
+
* timeout, a caller cancellation, or an application-level error)?
|
|
436
|
+
*/
|
|
437
|
+
function isNetworkErrorImpl(error) {
|
|
438
|
+
{
|
|
439
|
+
// TimeoutError is our own request timeout — handled separately as LOCK_TIMEOUT,
|
|
440
|
+
// never treated as a retryable network blip.
|
|
441
|
+
if (error instanceof DOMException && error.name === "TimeoutError") {
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
// AbortError is never a retryable network blip. Our own request timeout
|
|
445
|
+
// aborts with name "TimeoutError" (handled above → LOCK_TIMEOUT). A plain
|
|
446
|
+
// "AbortError" can only come from a caller-supplied signal (cancellation),
|
|
447
|
+
// which must propagate immediately — retrying a cancelled request 3× is a
|
|
448
|
+
// bug. The request loop also guards opts.signal.aborted before reaching
|
|
449
|
+
// here; this is the defensive backstop for any other abort source. Match on
|
|
450
|
+
// name (not just DOMException) since some fetch impls throw a plain Error.
|
|
451
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
// Primary, robust path: undici surfaces the OS-level failure on the wrapped
|
|
455
|
+
// cause's `.code` (e.g. ECONNREFUSED, ENOTFOUND, ETIMEDOUT, ECONNRESET). Some
|
|
456
|
+
// raw socket errors expose `.code` directly. Classifying by code is stable
|
|
457
|
+
// across Node/undici versions and locale, unlike message-substring matching.
|
|
458
|
+
const causeCode = errorCodeOf(error.cause);
|
|
459
|
+
if (causeCode !== undefined && NETWORK_ERROR_CODES.has(causeCode)) {
|
|
460
|
+
return true;
|
|
461
|
+
}
|
|
462
|
+
const directCode = errorCodeOf(error);
|
|
463
|
+
if (directCode !== undefined && NETWORK_ERROR_CODES.has(directCode)) {
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
// Fallback: undici wraps every fetch-level network failure in
|
|
467
|
+
// `TypeError: fetch failed`. This catches wrappers whose cause carries no
|
|
468
|
+
// recognised code (or no cause at all) — preserves the old TypeError branch.
|
|
469
|
+
if (error instanceof TypeError) {
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
// Last-resort backstop: an error that stringified a network failure into its
|
|
473
|
+
// message without exposing a code or cause (rare, but the old code matched
|
|
474
|
+
// it — kept so nothing is silently dropped). Not the primary classifier.
|
|
475
|
+
if (error instanceof Error) {
|
|
476
|
+
const msg = error.message.toLowerCase();
|
|
477
|
+
return (msg.includes("fetch failed") ||
|
|
478
|
+
msg.includes("econnrefused") ||
|
|
479
|
+
msg.includes("enotfound") ||
|
|
480
|
+
msg.includes("etimedout") ||
|
|
481
|
+
msg.includes("network"));
|
|
482
|
+
}
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* OS/undici-level error codes that indicate a transient network failure worth
|
|
488
|
+
* retrying (connection refused, DNS failure, timeout, socket reset, etc.).
|
|
489
|
+
* undici exposes these on `err.cause.code`; raw sockets on `err.code`.
|
|
490
|
+
*/
|
|
491
|
+
const NETWORK_ERROR_CODES = new Set([
|
|
492
|
+
"ECONNREFUSED", // connection actively refused (server down / wrong port)
|
|
493
|
+
"ENOTFOUND", // DNS: host not found
|
|
494
|
+
"EAI_AGAIN", // DNS: temporary resolution failure
|
|
495
|
+
"ETIMEDOUT", // OS-level connect/read timeout
|
|
496
|
+
"ECONNRESET", // socket reset by peer
|
|
497
|
+
"ECONNABORTED", // connection aborted mid-flight
|
|
498
|
+
"EPIPE", // broken pipe writing to a closed socket
|
|
499
|
+
"EHOSTUNREACH", // host unreachable
|
|
500
|
+
"ENETUNREACH", // network unreachable
|
|
501
|
+
"ENETDOWN", // network is down
|
|
502
|
+
"UND_ERR_CONNECT_TIMEOUT", // undici connect timeout
|
|
503
|
+
"UND_ERR_SOCKET", // undici socket error (e.g. other side closed)
|
|
504
|
+
]);
|
|
505
|
+
/**
|
|
506
|
+
* Read a string `.code` off an unknown error-ish value, narrowing safely under
|
|
507
|
+
* strict TS (`cause`/`error` are `unknown`). Returns undefined if absent or not
|
|
508
|
+
* a string.
|
|
509
|
+
*/
|
|
510
|
+
function errorCodeOf(value) {
|
|
511
|
+
if (typeof value !== "object" || value === null) {
|
|
512
|
+
return undefined;
|
|
513
|
+
}
|
|
514
|
+
const code = value.code;
|
|
515
|
+
return typeof code === "string" ? code : undefined;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Pull the message out of an OData error body. TM1 writes it two ways —
|
|
519
|
+
* `error.message` as a plain string, or `error.message.value` — so both are
|
|
520
|
+
* read, and anything else yields undefined so the caller can fall back to the
|
|
521
|
+
* raw body. Returning only strings matters: the previous chained-optional
|
|
522
|
+
* version could hand a whole object to a `string` field when `message` was an
|
|
523
|
+
* object without `value`.
|
|
524
|
+
*/
|
|
525
|
+
function odataErrorText(parsed) {
|
|
526
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
527
|
+
return undefined;
|
|
528
|
+
const err = parsed.error;
|
|
529
|
+
if (typeof err !== "object" || err === null)
|
|
530
|
+
return undefined;
|
|
531
|
+
const message = err.message;
|
|
532
|
+
if (typeof message === "string")
|
|
533
|
+
return message;
|
|
534
|
+
if (typeof message === "object" && message !== null) {
|
|
535
|
+
const value = message.value;
|
|
536
|
+
if (typeof value === "string")
|
|
537
|
+
return value;
|
|
538
|
+
}
|
|
539
|
+
return undefined;
|
|
540
|
+
}
|
|
541
|
+
function isTimeoutError(error) {
|
|
542
|
+
return error instanceof DOMException && error.name === "TimeoutError";
|
|
543
|
+
}
|
|
544
|
+
function sleep(ms) {
|
|
545
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
546
|
+
}
|
|
547
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { TM1Error } from "../../types.js";
|
|
2
|
+
import type { TM1HttpClient } from "../http.js";
|
|
3
|
+
/**
|
|
4
|
+
* Correlation id of the counter-probe's single sub-request. Namespaced so it
|
|
5
|
+
* can never collide with a caller id, and recognisable in a server-side log.
|
|
6
|
+
*/
|
|
7
|
+
export declare const BATCH_PROBE_ID = "__tm1_mcp_batch_probe__";
|
|
8
|
+
export declare const BATCH_MAX_REQUESTS = 200;
|
|
9
|
+
export declare const BATCH_MAX_PAYLOAD_BYTES = 1000000;
|
|
10
|
+
export interface BatchRequest {
|
|
11
|
+
/** Caller-chosen correlation id. Must be unique within one execute() call. */
|
|
12
|
+
id: string;
|
|
13
|
+
method: "GET" | "POST" | "PATCH" | "DELETE";
|
|
14
|
+
/** Same `/api/v1/...` form the other services use; rerooted for v12 by the profile. */
|
|
15
|
+
path: string;
|
|
16
|
+
body?: unknown;
|
|
17
|
+
}
|
|
18
|
+
export type BatchSubResult = {
|
|
19
|
+
id: string;
|
|
20
|
+
status: number;
|
|
21
|
+
ok: true;
|
|
22
|
+
body: unknown;
|
|
23
|
+
} | {
|
|
24
|
+
id: string;
|
|
25
|
+
status: number;
|
|
26
|
+
ok: false;
|
|
27
|
+
error: TM1Error;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Thrown when the server cannot serve `$batch` at all (endpoint absent, method
|
|
31
|
+
* refused, or an envelope we cannot read). Distinct from a sub-request failure,
|
|
32
|
+
* which is reported per item. Callers catch ONLY this to fall back to their
|
|
33
|
+
* per-request path — never a sub-request error, and never a systemic
|
|
34
|
+
* transport/auth failure (those propagate).
|
|
35
|
+
*
|
|
36
|
+
* GUARANTEE for those callers: this is raised only before the first successful
|
|
37
|
+
* batch on the connection — i.e. only ever out of the caller's FIRST pass.
|
|
38
|
+
*
|
|
39
|
+
* What that does NOT mean: "nothing was committed". TM1's `$batch` is
|
|
40
|
+
* non-atomic (verified live against 11.8: a failing sub-request leaves its
|
|
41
|
+
* siblings in the same envelope committed), and an ambiguous status such as 500
|
|
42
|
+
* can arrive after the server already applied part of the envelope. A caller
|
|
43
|
+
* that restarts per-request MAY therefore re-issue writes that already took
|
|
44
|
+
* effect.
|
|
45
|
+
*
|
|
46
|
+
* The requirement on callers is therefore: THE FIRST PASS MUST BE REPLAY-SAFE.
|
|
47
|
+
* `ElementService.bulkUpsert` satisfies this because its first pass is
|
|
48
|
+
* create-only, and re-creating an existing element degrades to the upsert path.
|
|
49
|
+
* A future caller whose first pass mutates existing state must NOT rely on this
|
|
50
|
+
* error to fall back. See `BatchService.markUnsupported`.
|
|
51
|
+
*/
|
|
52
|
+
export declare class BatchUnsupportedError extends Error {
|
|
53
|
+
constructor(reason: string);
|
|
54
|
+
}
|
|
55
|
+
export declare class BatchService {
|
|
56
|
+
private readonly http;
|
|
57
|
+
private supported;
|
|
58
|
+
private probeSpent;
|
|
59
|
+
constructor(http: TM1HttpClient);
|
|
60
|
+
/** True once a probe has established the server rejects `$batch` outright. */
|
|
61
|
+
get isKnownUnsupported(): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Record a "this server has no usable `$batch`" verdict — but ONLY while no
|
|
64
|
+
* batch has ever succeeded on this connection.
|
|
65
|
+
*
|
|
66
|
+
* That precondition is the whole safety argument for the caller's fallback.
|
|
67
|
+
* `BatchUnsupportedError` makes callers restart their operation on the
|
|
68
|
+
* per-request path. While `supported === null` no chunk has ever come back
|
|
69
|
+
* 200, so the failure can only be in the caller's FIRST pass. Note what that
|
|
70
|
+
* bounds: WHICH writes may be replayed, not WHETHER anything was committed —
|
|
71
|
+
* `$batch` is non-atomic, so sub-requests processed before the failure stay
|
|
72
|
+
* committed. Callers must keep that first pass replay-safe.
|
|
73
|
+
*
|
|
74
|
+
* Once `supported === true`, a later failure — a flaky gateway answering
|
|
75
|
+
* chunk 2 with HTML or a 400 — is a real error and is propagated verbatim, so
|
|
76
|
+
* a half-finished batch is never silently replayed. That is what keeps the
|
|
77
|
+
* destructive later passes (type PATCH, Components) out of the fallback
|
|
78
|
+
* window entirely: they only ever run after a batch has already succeeded.
|
|
79
|
+
*
|
|
80
|
+
* NOTE: for HTTP 400 the caller in `executeChunk` runs `counterProbe` BEFORE
|
|
81
|
+
* reaching here, so a payload-caused 400 never gets this far. This method
|
|
82
|
+
* still sees every other unsupported status directly.
|
|
83
|
+
*
|
|
84
|
+
* Returns the error to throw, or `undefined` when the verdict was refused.
|
|
85
|
+
*/
|
|
86
|
+
private markUnsupported;
|
|
87
|
+
/**
|
|
88
|
+
* Settle an ambiguous first-call 400 by asking the endpoint the simplest
|
|
89
|
+
* question there is: one minimal, side-effect-free GET in a well-formed
|
|
90
|
+
* envelope.
|
|
91
|
+
*
|
|
92
|
+
* Judges the ENVELOPE only, exactly as `executeChunk` does — a readable
|
|
93
|
+
* `responses` array means `$batch` is served here, whatever status the probe's
|
|
94
|
+
* own sub-request came back with. That distinction is load-bearing: a
|
|
95
|
+
* locked-down account may well be refused `Configuration/ProductVersion`
|
|
96
|
+
* (reported INSIDE the envelope), and reading that as "no $batch" would
|
|
97
|
+
* reintroduce the very misdiagnosis this probe exists to remove.
|
|
98
|
+
*
|
|
99
|
+
* Returns `true` when `$batch` demonstrably works, `false` when the endpoint
|
|
100
|
+
* refuses even this envelope. THROWS on a systemic transport/auth/timeout
|
|
101
|
+
* failure: that proves nothing either way, so the caller must draw no
|
|
102
|
+
* conclusion and the outage surfaces instead — same rule `executeChunk`
|
|
103
|
+
* already applies via `rethrowIfSystemic`.
|
|
104
|
+
*
|
|
105
|
+
* Does not go through `execute`/`executeChunk`, so it cannot recurse into
|
|
106
|
+
* itself or into the unsupported-detection path.
|
|
107
|
+
*/
|
|
108
|
+
private counterProbe;
|
|
109
|
+
/**
|
|
110
|
+
* Run `requests` in as few round-trips as possible and return one result per
|
|
111
|
+
* request, in the order given.
|
|
112
|
+
*
|
|
113
|
+
* A failing sub-request is DATA, not an exception: it comes back as
|
|
114
|
+
* `{ok:false, error}` so the caller can report partial success. Only a
|
|
115
|
+
* whole-envelope failure throws — `BatchUnsupportedError` if the server has no
|
|
116
|
+
* `$batch`, or the underlying TM1Error for a transport/auth/timeout failure.
|
|
117
|
+
*
|
|
118
|
+
* POST /api/v1/$batch
|
|
119
|
+
*/
|
|
120
|
+
execute(requests: BatchRequest[]): Promise<BatchSubResult[]>;
|
|
121
|
+
private executeChunk;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=batch-service.d.ts.map
|