tm1npm 1.5.3 → 2.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.
Files changed (78) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/lib/index.d.ts +1 -1
  3. package/lib/index.d.ts.map +1 -1
  4. package/lib/services/ApplicationService.d.ts +19 -3
  5. package/lib/services/ApplicationService.d.ts.map +1 -1
  6. package/lib/services/ApplicationService.js +232 -6
  7. package/lib/services/AsyncOperationService.d.ts +8 -1
  8. package/lib/services/AsyncOperationService.d.ts.map +1 -1
  9. package/lib/services/AsyncOperationService.js +69 -26
  10. package/lib/services/ElementService.d.ts +67 -1
  11. package/lib/services/ElementService.d.ts.map +1 -1
  12. package/lib/services/ElementService.js +214 -0
  13. package/lib/services/FileService.d.ts.map +1 -1
  14. package/lib/services/HierarchyService.d.ts +26 -0
  15. package/lib/services/HierarchyService.d.ts.map +1 -1
  16. package/lib/services/HierarchyService.js +306 -0
  17. package/lib/services/ProcessService.d.ts +40 -22
  18. package/lib/services/ProcessService.d.ts.map +1 -1
  19. package/lib/services/ProcessService.js +118 -111
  20. package/lib/services/RestService.d.ts +213 -25
  21. package/lib/services/RestService.d.ts.map +1 -1
  22. package/lib/services/RestService.js +841 -263
  23. package/lib/services/SubsetService.d.ts +2 -0
  24. package/lib/services/SubsetService.d.ts.map +1 -1
  25. package/lib/services/SubsetService.js +33 -0
  26. package/lib/services/TM1Service.d.ts +44 -1
  27. package/lib/services/TM1Service.d.ts.map +1 -1
  28. package/lib/services/TM1Service.js +96 -4
  29. package/lib/services/index.d.ts +1 -1
  30. package/lib/services/index.d.ts.map +1 -1
  31. package/lib/tests/100PercentParityCheck.test.js +23 -6
  32. package/lib/tests/applicationService.issue38.test.d.ts +5 -0
  33. package/lib/tests/applicationService.issue38.test.d.ts.map +1 -0
  34. package/lib/tests/applicationService.issue38.test.js +237 -0
  35. package/lib/tests/asyncOperationService.test.js +51 -45
  36. package/lib/tests/bugfix28.test.js +12 -4
  37. package/lib/tests/elementService.issue37.test.d.ts +5 -0
  38. package/lib/tests/elementService.issue37.test.d.ts.map +1 -0
  39. package/lib/tests/elementService.issue37.test.js +413 -0
  40. package/lib/tests/elementService.issue38.test.d.ts +5 -0
  41. package/lib/tests/elementService.issue38.test.d.ts.map +1 -0
  42. package/lib/tests/elementService.issue38.test.js +79 -0
  43. package/lib/tests/hierarchyService.issue38.test.d.ts +5 -0
  44. package/lib/tests/hierarchyService.issue38.test.d.ts.map +1 -0
  45. package/lib/tests/hierarchyService.issue38.test.js +460 -0
  46. package/lib/tests/processService.comprehensive.test.js +9 -9
  47. package/lib/tests/processService.test.js +234 -0
  48. package/lib/tests/restService.test.d.ts +0 -4
  49. package/lib/tests/restService.test.d.ts.map +1 -1
  50. package/lib/tests/restService.test.js +1558 -143
  51. package/lib/tests/subsetService.issue38.test.d.ts +5 -0
  52. package/lib/tests/subsetService.issue38.test.d.ts.map +1 -0
  53. package/lib/tests/subsetService.issue38.test.js +113 -0
  54. package/lib/tests/tm1Service.test.js +80 -8
  55. package/package.json +1 -1
  56. package/src/index.ts +1 -1
  57. package/src/services/ApplicationService.ts +282 -10
  58. package/src/services/AsyncOperationService.ts +76 -29
  59. package/src/services/ElementService.ts +322 -1
  60. package/src/services/FileService.ts +3 -3
  61. package/src/services/HierarchyService.ts +419 -1
  62. package/src/services/ProcessService.ts +185 -142
  63. package/src/services/RestService.ts +1021 -267
  64. package/src/services/SubsetService.ts +48 -0
  65. package/src/services/TM1Service.ts +127 -6
  66. package/src/services/index.ts +1 -1
  67. package/src/tests/100PercentParityCheck.test.ts +29 -8
  68. package/src/tests/applicationService.issue38.test.ts +293 -0
  69. package/src/tests/asyncOperationService.test.ts +52 -48
  70. package/src/tests/bugfix28.test.ts +12 -4
  71. package/src/tests/elementService.issue37.test.ts +571 -0
  72. package/src/tests/elementService.issue38.test.ts +103 -0
  73. package/src/tests/hierarchyService.issue38.test.ts +599 -0
  74. package/src/tests/processService.comprehensive.test.ts +10 -10
  75. package/src/tests/processService.test.ts +295 -3
  76. package/src/tests/restService.test.ts +1844 -139
  77. package/src/tests/subsetService.issue38.test.ts +182 -0
  78. package/src/tests/tm1Service.test.ts +95 -11
@@ -180,7 +180,7 @@ class ProcessService extends ObjectService_1.ObjectService {
180
180
  }
181
181
  const config = {};
182
182
  if (timeout) {
183
- config.timeout = timeout * 1000;
183
+ config.timeout = timeout;
184
184
  }
185
185
  return await this.rest.post(url, JSON.stringify(body), config);
186
186
  }
@@ -193,93 +193,54 @@ class ProcessService extends ObjectService_1.ObjectService {
193
193
  const url = (0, Utils_1.formatUrl)("/Processes('{}')/tm1.Compile", processName);
194
194
  return await this.rest.post(url, '{}');
195
195
  }
196
- async compileProcess(processName) {
197
- var _a, _b, _c;
198
- /** Compile a process and return detailed compilation results
196
+ async compileProcess(process) {
197
+ /** Compile an unbound process and return syntax errors
199
198
  *
200
- * :param process_name: name of the process
201
- * :return: compilation result with success status and error details
199
+ * :param process: Instance of .Process class
200
+ * :return: list of syntax errors (empty if successful)
202
201
  */
203
- try {
204
- const response = await this.compile(processName);
205
- // Check if compilation was successful
206
- if (response.status === 200) {
207
- return {
208
- success: true,
209
- errors: []
210
- };
211
- }
212
- else {
213
- return {
214
- success: false,
215
- errors: [response.statusText || 'Compilation failed']
216
- };
217
- }
218
- }
219
- catch (error) {
220
- const errorMessage = ((_c = (_b = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.message) || error.message || 'Unknown compilation error';
221
- return {
222
- success: false,
223
- errors: [errorMessage]
224
- };
225
- }
202
+ const url = '/CompileProcess';
203
+ const payload = { Process: process.bodyAsDict };
204
+ const response = await this.rest.post(url, JSON.stringify(payload));
205
+ return response.data.value;
226
206
  }
227
207
  /**
228
- * Poll execution status for async process execution
208
+ * Poll for async execution result
209
+ *
210
+ * :param asyncId: async operation ID returned from executeWithReturn with returnAsyncId=true
211
+ * :return: tuple of [success, status, errorLogFile] or null if not ready
229
212
  */
230
- async pollExecuteWithReturn(processName, parameters, timeout = 300, pollInterval = 5) {
231
- /** Execute process asynchronously and poll for completion
232
- *
233
- * :param process_name: name of the process
234
- * :param parameters: dictionary of process parameters
235
- * :param timeout: maximum time to wait for completion (seconds)
236
- * :param poll_interval: time between status checks (seconds)
237
- * :return: execution result when completed
238
- */
239
- // Start async execution
240
- const executeUrl = (0, Utils_1.formatUrl)("/Processes('{}')/tm1.ExecuteAsync", processName);
241
- const body = {};
242
- if (parameters && Object.keys(parameters).length > 0) {
243
- body.Parameters = Object.entries(parameters).map(([name, value]) => ({
244
- Name: name,
245
- Value: value
246
- }));
247
- }
248
- const executeResponse = await this.rest.post(executeUrl, JSON.stringify(body));
249
- const executionId = executeResponse.data.ID || executeResponse.data.ExecutionId;
250
- if (!executionId) {
251
- throw new TM1Exception_1.TM1Exception('Failed to start async process execution');
252
- }
253
- // Poll for completion
254
- const startTime = Date.now();
255
- const maxTime = timeout * 1000;
256
- while (Date.now() - startTime < maxTime) {
257
- try {
258
- // Check execution status
259
- const statusUrl = `/ExecutionStatus('${executionId}')`;
260
- const statusResponse = await this.rest.get(statusUrl);
261
- const status = statusResponse.data;
262
- if (status.Status === 'Completed') {
263
- // Get execution results
264
- const resultUrl = `/ExecutionResults('${executionId}')`;
265
- const resultResponse = await this.rest.get(resultUrl);
266
- return resultResponse.data;
267
- }
268
- else if (status.Status === 'Failed') {
269
- throw new TM1Exception_1.TM1Exception(`Process execution failed: ${status.ErrorMessage || 'Unknown error'}`);
270
- }
271
- // Wait before next poll
272
- await new Promise(resolve => setTimeout(resolve, pollInterval * 1000));
213
+ async pollExecuteWithReturn(asyncId) {
214
+ var _a, _b;
215
+ try {
216
+ const response = await this.rest.retrieve_async_response(asyncId);
217
+ // tm1py returns None while the async op is still in-flight (status 202).
218
+ if (response.status !== 200 && response.status !== 201) {
219
+ return null;
273
220
  }
274
- catch (error) {
275
- if (error instanceof TM1Exception_1.TM1Exception) {
276
- throw error;
277
- }
278
- // If status check fails, wait and retry
279
- await new Promise(resolve => setTimeout(resolve, pollInterval * 1000));
221
+ // TODO: tm1py handles TM1 < v11 binary-wrapped responses via
222
+ // build_response_from_binary_response. Add support if needed.
223
+ return this._executeWithReturnParseResponse(response.data);
224
+ }
225
+ catch (error) {
226
+ // 404 means the async resource hasn't materialized yet — return null
227
+ // so the caller can retry. 202 means still running. Both are
228
+ // retryable, unlike AsyncOperationService which treats 404 as
229
+ // terminal FAILED for locally-tracked operations.
230
+ const err = error;
231
+ const status = (_a = err === null || err === void 0 ? void 0 : err.status) !== null && _a !== void 0 ? _a : (_b = err === null || err === void 0 ? void 0 : err.response) === null || _b === void 0 ? void 0 : _b.status;
232
+ if (status === 202 || status === 404) {
233
+ return null;
280
234
  }
235
+ throw error;
281
236
  }
282
- throw new TM1Exception_1.TM1Exception(`Process execution timed out after ${timeout} seconds`);
237
+ }
238
+ _executeWithReturnParseResponse(executionSummary) {
239
+ var _a, _b;
240
+ const success = executionSummary.ProcessExecuteStatusCode === 'CompletedSuccessfully';
241
+ const status = executionSummary.ProcessExecuteStatusCode;
242
+ const errorLogFile = (_b = (_a = executionSummary.ErrorLogFile) === null || _a === void 0 ? void 0 : _a.Filename) !== null && _b !== void 0 ? _b : null;
243
+ return [success, status, errorLogFile];
283
244
  }
284
245
  async executeProcessWithReturn(processName, parameters) {
285
246
  /** Execute process with return values
@@ -301,7 +262,7 @@ class ProcessService extends ObjectService_1.ObjectService {
301
262
  // For now, return empty string
302
263
  return '';
303
264
  }
304
- async getProcessDebugBreakpoints(debugId) {
265
+ async debugGetBreakpoints(debugId) {
305
266
  /** Get debug breakpoints for a debug context
306
267
  *
307
268
  * :param debugId: debug session ID
@@ -311,18 +272,17 @@ class ProcessService extends ObjectService_1.ObjectService {
311
272
  const response = await this.rest.get(url);
312
273
  return response.data.value.map((bp) => ProcessDebugBreakpoint_1.ProcessDebugBreakpoint.fromDict(bp));
313
274
  }
314
- async createProcessDebugBreakpoint(debugId, breakpoint) {
315
- /** Create a debug breakpoint in a debug context
275
+ async debugAddBreakpoint(debugId, breakpoint) {
276
+ /** Add a single breakpoint to a debug context (delegates to debugAddBreakpoints)
316
277
  *
317
278
  * :param debugId: debug session ID
318
279
  * :param breakpoint: ProcessDebugBreakpoint object
319
280
  * :return: response
320
281
  */
321
- const url = (0, Utils_1.formatUrl)("/ProcessDebugContexts('{}')/Breakpoints", debugId);
322
- return await this.rest.post(url, breakpoint.body);
282
+ return this.debugAddBreakpoints(debugId, [breakpoint]);
323
283
  }
324
- async deleteProcessDebugBreakpoint(debugId, breakpointId) {
325
- /** Delete a debug breakpoint from a debug context
284
+ async debugRemoveBreakpoint(debugId, breakpointId) {
285
+ /** Remove a breakpoint from a debug context
326
286
  *
327
287
  * :param debugId: debug session ID
328
288
  * :param breakpointId: ID of the breakpoint
@@ -584,9 +544,11 @@ class ProcessService extends ObjectService_1.ObjectService {
584
544
  }
585
545
  const config = {};
586
546
  if (timeout) {
587
- config.timeout = timeout * 1000;
547
+ config.timeout = timeout;
588
548
  }
589
- const response = await this.rest.post(url, JSON.stringify(body), config);
549
+ // rest.post returns AxiosResponse | string (string only when caller
550
+ // opts into returnAsyncId). debugProcess never does, so narrow.
551
+ const response = (await this.rest.post(url, JSON.stringify(body), config));
590
552
  return response.data;
591
553
  }
592
554
  /**
@@ -676,6 +638,53 @@ class ProcessService extends ObjectService_1.ObjectService {
676
638
  throw new TM1Exception_1.TM1Exception(`Unexpected TI return status: '${status}' for expression: '${expression}'`);
677
639
  }
678
640
  }
641
+ /**
642
+ * Evaluate a TI expression and return the string result.
643
+ *
644
+ * Creates a temporary process with sFunc = {formula}, compiles it,
645
+ * starts a debug session, adds a data breakpoint on sFunc, continues
646
+ * execution to evaluate, reads the result, and cleans up.
647
+ */
648
+ async evaluateTiExpression(formula) {
649
+ // tm1py uses formula[formula.find("=") + 1:] which greedily strips at the
650
+ // first "=" anywhere in the string. We use a regex to only strip a leading
651
+ // "=" prefix (e.g. "=NOW;" → "NOW;"), avoiding mangling formulas with
652
+ // embedded "=" (e.g. comparisons like "IF(1=1,...)").
653
+ formula = formula.replace(/^\s*=\s*/, '');
654
+ // Ensure semicolon at end
655
+ if (!formula.trim().endsWith(';')) {
656
+ formula += ';';
657
+ }
658
+ const prologList = [`sFunc = ${formula}`, "sDebug='Stop';"];
659
+ const processName = `}TM1py${(0, uuid_1.v4)()}`;
660
+ const p = new Process_1.Process(processName, false, undefined, undefined, undefined, undefined, Process_1.Process.AUTO_GENERATED_STATEMENTS + prologList.join('\r\n'), '', '', '');
661
+ const syntaxErrors = await this.compileProcess(p);
662
+ if (syntaxErrors && syntaxErrors.length > 0) {
663
+ throw new Error(syntaxErrors.map(e => `Line ${e.LineNumber}: ${e.Message}`).join('; '));
664
+ }
665
+ try {
666
+ await this.create(p);
667
+ const debugContext = await this.debugProcess(processName);
668
+ const debugId = debugContext.ID;
669
+ const breakpoint = new ProcessDebugBreakpoint_1.ProcessDebugBreakpoint(1, ProcessDebugBreakpoint_1.BreakPointType.PROCESS_DEBUG_CONTEXT_DATA_BREAK_POINT, true, ProcessDebugBreakpoint_1.HitMode.BREAK_ALWAYS, 0, '', 'sFunc');
670
+ await this.debugAddBreakpoint(debugId, breakpoint);
671
+ await this.debugContinue(debugId);
672
+ const result = await this.debugGetVariableValues(debugId);
673
+ await this.debugContinue(debugId);
674
+ if (!result || !('sFunc' in result)) {
675
+ throw new Error('unknown error: no formula result found');
676
+ }
677
+ return result['sFunc'];
678
+ }
679
+ finally {
680
+ try {
681
+ await this.delete(processName);
682
+ }
683
+ catch (_) {
684
+ // Cleanup failure should not mask the original error
685
+ }
686
+ }
687
+ }
679
688
  /**
680
689
  * Analyze process dependencies (what cubes/dimensions/processes it uses)
681
690
  *
@@ -685,8 +694,8 @@ class ProcessService extends ObjectService_1.ObjectService {
685
694
  * @example
686
695
  * ```typescript
687
696
  * const deps = await processService.analyzeProcessDependencies('ImportData');
688
- * console.log('Cubes used:', deps.cubes);
689
- * console.log('Dimensions used:', deps.dimensions);
697
+ * // deps.cubes => array of cube names referenced in the process
698
+ * // deps.dimensions => array of dimension names referenced in the process
690
699
  * ```
691
700
  */
692
701
  async analyzeProcessDependencies(processName) {
@@ -747,36 +756,34 @@ class ProcessService extends ObjectService_1.ObjectService {
747
756
  * Validate process syntax without executing it
748
757
  *
749
758
  * @param processName - Name of the process
750
- * @returns Promise<{isValid: boolean, errors: any[]}> - Validation result
759
+ * @returns Promise<ValidationResult> - Validation result
751
760
  *
752
761
  * @example
753
762
  * ```typescript
754
763
  * const result = await processService.validateProcessSyntax('MyProcess');
755
764
  * if (!result.isValid) {
756
- * console.error('Errors:', result.errors);
765
+ * // result.errors contains ValidationError[] entries
757
766
  * }
758
767
  * ```
759
768
  */
760
769
  async validateProcessSyntax(processName) {
770
+ var _a, _b, _c;
761
771
  try {
762
- const result = await this.compileProcess(processName);
772
+ const response = await this.compile(processName);
773
+ if (response.status === 200) {
774
+ return { isValid: true, errors: [] };
775
+ }
763
776
  return {
764
- isValid: result.success,
765
- errors: result.errors.map((error, index) => ({
766
- line: index + 1,
767
- message: error,
768
- severity: 'Error'
769
- }))
777
+ isValid: false,
778
+ errors: [{ line: 0, message: response.statusText || 'Compilation failed', severity: 'Error' }]
770
779
  };
771
780
  }
772
781
  catch (error) {
782
+ const err = error;
783
+ const errorMessage = ((_c = (_b = (_a = err.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.message) || err.message || 'Validation failed';
773
784
  return {
774
785
  isValid: false,
775
- errors: [{
776
- line: 0,
777
- message: error.message || 'Validation failed',
778
- severity: 'Error'
779
- }]
786
+ errors: [{ line: 0, message: errorMessage, severity: 'Error' }]
780
787
  };
781
788
  }
782
789
  }
@@ -789,7 +796,7 @@ class ProcessService extends ObjectService_1.ObjectService {
789
796
  * @example
790
797
  * ```typescript
791
798
  * const plan = await processService.getProcessExecutionPlan('ImportData');
792
- * console.log('Estimated execution time:', plan.estimatedTime);
799
+ * // plan.estimatedTime => estimated execution time (ms)
793
800
  * ```
794
801
  */
795
802
  async getProcessExecutionPlan(processName) {
@@ -864,7 +871,9 @@ class ProcessService extends ObjectService_1.ObjectService {
864
871
  asyncOps.updateOperationStatus(operationId, AsyncOperationService_1.OperationStatus.COMPLETED, result);
865
872
  })
866
873
  .catch((error) => {
867
- asyncOps.updateOperationStatus(operationId, AsyncOperationService_1.OperationStatus.FAILED, undefined, error.message || String(error));
874
+ var _a;
875
+ const message = (_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : String(error);
876
+ asyncOps.updateOperationStatus(operationId, AsyncOperationService_1.OperationStatus.FAILED, undefined, message);
868
877
  });
869
878
  return operationId;
870
879
  }
@@ -877,9 +886,7 @@ class ProcessService extends ObjectService_1.ObjectService {
877
886
  * @example
878
887
  * ```typescript
879
888
  * const status = await processService.pollProcessExecution(operationId);
880
- * if (status === OperationStatus.COMPLETED) {
881
- * console.log('Process completed!');
882
- * }
889
+ * // if (status === OperationStatus.COMPLETED) handle completion
883
890
  * ```
884
891
  */
885
892
  async pollProcessExecution(operationId) {
@@ -898,7 +905,7 @@ class ProcessService extends ObjectService_1.ObjectService {
898
905
  * @example
899
906
  * ```typescript
900
907
  * await processService.cancelProcessExecution(operationId);
901
- * console.log('Process execution cancelled');
908
+ * // cancellation is acknowledged once the promise resolves
902
909
  * ```
903
910
  */
904
911
  async cancelProcessExecution(operationId) {
@@ -1,4 +1,5 @@
1
1
  import { AxiosResponse, AxiosRequestConfig } from 'axios';
2
+ import * as https from 'https';
2
3
  export declare enum AuthenticationMode {
3
4
  BASIC = 1,
4
5
  WIA = 2,
@@ -25,6 +26,9 @@ export interface RestServiceConfig {
25
26
  timeout?: number;
26
27
  cancelAtTimeout?: boolean;
27
28
  asyncRequestsMode?: boolean;
29
+ asyncPollingInitialDelay?: number;
30
+ asyncPollingMaxDelay?: number;
31
+ asyncPollingBackoffFactor?: number;
28
32
  connectionPoolSize?: number;
29
33
  poolConnections?: number;
30
34
  instance?: string;
@@ -36,21 +40,95 @@ export interface RestServiceConfig {
36
40
  apiKey?: string;
37
41
  accessToken?: string;
38
42
  tenant?: string;
43
+ iamUrl?: string;
44
+ paUrl?: string;
45
+ cpdUrl?: string;
46
+ gateway?: string;
47
+ integratedLogin?: boolean;
48
+ integratedLoginDomain?: string;
49
+ integratedLoginService?: string;
50
+ integratedLoginHost?: string;
51
+ integratedLoginDelegate?: boolean;
52
+ proxies?: {
53
+ http?: string;
54
+ https?: string;
55
+ };
56
+ sslContext?: https.Agent;
57
+ cert?: string | [string, string];
58
+ reConnectOnSessionTimeout?: boolean;
59
+ reConnectOnRemoteDisconnect?: boolean;
60
+ remoteDisconnectMaxRetries?: number;
61
+ remoteDisconnectRetryDelay?: number;
62
+ remoteDisconnectMaxDelay?: number;
63
+ }
64
+ export interface RequestOptions extends Omit<AxiosRequestConfig, 'timeout'> {
65
+ asyncRequestsMode?: boolean;
66
+ returnAsyncId?: boolean;
67
+ timeout?: number;
68
+ cancelAtTimeout?: boolean;
69
+ idempotent?: boolean;
70
+ verifyResponse?: boolean;
39
71
  }
40
72
  export declare class RestService {
41
73
  private static readonly HEADERS;
42
74
  private static readonly DEFAULT_CONNECTION_POOL_SIZE;
43
75
  private static readonly DEFAULT_POOL_CONNECTIONS;
76
+ private static readonly SESSION_COOKIE_NAMES;
44
77
  private axiosInstance;
45
78
  private config;
46
- private sessionId?;
79
+ private sessionCookies;
47
80
  private sandboxName?;
48
81
  private isConnected;
49
82
  private _serverVersion?;
83
+ private _asyncRequestsMode;
84
+ private _cancelAtTimeout;
85
+ private _timeout;
86
+ private _asyncPollingInitialDelay;
87
+ private _asyncPollingMaxDelay;
88
+ private _asyncPollingBackoffFactor;
89
+ private _reConnectOnSessionTimeout;
90
+ private _reConnectOnRemoteDisconnect;
91
+ private _remoteDisconnectMaxRetries;
92
+ private _remoteDisconnectRetryDelay;
93
+ private _remoteDisconnectMaxDelay;
94
+ private _isAdmin?;
95
+ private _isDataAdmin?;
96
+ private _isSecurityAdmin?;
97
+ private _isOpsAdmin?;
98
+ private _activeUserGroupsPromise?;
50
99
  get version(): string | undefined;
100
+ get isAdmin(): boolean;
101
+ get isDataAdmin(): boolean;
102
+ get isSecurityAdmin(): boolean;
103
+ get isOpsAdmin(): boolean;
51
104
  constructor(config: RestServiceConfig);
105
+ private getSessionCookieValue;
106
+ private buildCookieHeader;
107
+ private parseSetCookieHeaders;
108
+ private removeAuthorizationHeader;
109
+ private deleteHeaderCaseInsensitive;
52
110
  private setupAxiosInstance;
53
111
  private buildBaseUrl;
112
+ /**
113
+ * Pick the deployment topology based on the provided config, mirroring
114
+ * tm1py's _determine_auth_mode + _construct_service_and_auth_root dispatch.
115
+ *
116
+ * Note: authUrl is intentionally excluded from the v12 signal set because
117
+ * tm1npm historically uses authUrl for CAM SSO (unlike tm1py, where auth_url
118
+ * is a v12-only field). apiKey is also excluded to avoid collision with the
119
+ * existing BASIC_API_KEY auth flow.
120
+ */
121
+ private determineTopology;
122
+ /**
123
+ * Resolve the TM1 service root and auth root URLs for the configured topology.
124
+ * Mirrors tm1py's _construct_service_and_auth_root return tuple.
125
+ */
126
+ private resolveRoots;
127
+ private rootsV11;
128
+ private rootsIbmCloud;
129
+ private rootsPaProxy;
130
+ private rootsS2s;
131
+ private rootsFromBaseUrl;
54
132
  private setupInterceptors;
55
133
  /**
56
134
  * Determine if a request should be retried
@@ -61,42 +139,118 @@ export declare class RestService {
61
139
  */
62
140
  private canRetryRequest;
63
141
  /**
64
- * Retry a failed request with exponential backoff
142
+ * Retry a failed request with exponential backoff, reconnecting the
143
+ * session before replay. Mirrors tm1py's _handle_remote_disconnect,
144
+ * which calls _manage_http_adapter() + connect() prior to retrying
145
+ * so a dropped session is re-established rather than replayed dead.
65
146
  */
66
147
  private retryRequest;
67
148
  private extractErrorMessage;
149
+ private waitTimeGenerator;
150
+ private _executeSyncRequest;
151
+ private _executeAsyncRequest;
152
+ private _pollAsyncResponse;
153
+ private _request;
68
154
  connect(): Promise<void>;
69
155
  disconnect(): Promise<void>;
70
- get(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse>;
71
- post(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse>;
72
- patch(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse>;
73
- put(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse>;
74
- delete(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse>;
156
+ /**
157
+ * When `returnAsyncId: true`, the caller receives the async id string
158
+ * iff the server returns `202 Accepted`. If TM1 short-circuits with
159
+ * `200/201`, the full `AxiosResponse` is returned instead — the
160
+ * declared `Promise<string>` return type is a best-effort narrowing.
161
+ */
162
+ get(url: string, options: RequestOptions & {
163
+ returnAsyncId: true;
164
+ }): Promise<string | AxiosResponse>;
165
+ get(url: string, options?: RequestOptions): Promise<AxiosResponse>;
166
+ post(url: string, data: any, options: RequestOptions & {
167
+ returnAsyncId: true;
168
+ }): Promise<string | AxiosResponse>;
169
+ post(url: string, data?: any, options?: RequestOptions): Promise<AxiosResponse>;
170
+ patch(url: string, data: any, options: RequestOptions & {
171
+ returnAsyncId: true;
172
+ }): Promise<string | AxiosResponse>;
173
+ patch(url: string, data?: any, options?: RequestOptions): Promise<AxiosResponse>;
174
+ put(url: string, data: any, options: RequestOptions & {
175
+ returnAsyncId: true;
176
+ }): Promise<string | AxiosResponse>;
177
+ put(url: string, data?: any, options?: RequestOptions): Promise<AxiosResponse>;
178
+ delete(url: string, options: RequestOptions & {
179
+ returnAsyncId: true;
180
+ }): Promise<string | AxiosResponse>;
181
+ delete(url: string, options?: RequestOptions): Promise<AxiosResponse>;
75
182
  getSessionId(): string | undefined;
76
183
  setSandbox(sandboxName?: string): void;
77
184
  getSandbox(): string | undefined;
78
185
  isLoggedIn(): boolean;
79
186
  getApiMetadata(): Promise<any>;
80
187
  /**
81
- * Set up authentication based on configuration
188
+ * Build an httpsAgent option that skips TLS verification when verify is false.
82
189
  */
83
- private setupAuthentication;
190
+ private static insecureAgentOption;
191
+ /**
192
+ * Normalise a Set-Cookie header value (string | string[] | undefined) into a string[].
193
+ */
194
+ private static normaliseSetCookie;
195
+ /**
196
+ * Extract a named cookie value from raw Set-Cookie headers.
197
+ */
198
+ private static extractCookieValue;
199
+ /**
200
+ * Build Basic Authorization header.
201
+ * Mirrors tm1py's _build_authorization_token_basic.
202
+ */
203
+ private static _buildAuthorizationTokenBasic;
204
+ /**
205
+ * Build CAMNamespace Authorization header.
206
+ * Mirrors tm1py's _build_authorization_token_cam (non-gateway path).
207
+ */
208
+ private static _buildAuthorizationTokenCam;
84
209
  /**
85
- * Set up CAM (Cognos Access Manager) authentication
210
+ * Build CAMPassport Authorization token via gateway SSO.
211
+ * Mirrors tm1py's _build_authorization_token_cam (gateway path).
212
+ * Makes a GET request to the gateway URL with CAMNamespace as a query
213
+ * parameter and extracts the cam_passport cookie from the response.
214
+ *
215
+ * Note: tm1py uses HttpNegotiateAuth (NTLM/Kerberos) for gateway requests,
216
+ * which is Windows-only. This implementation sends a plain GET and relies on
217
+ * the gateway being accessible without NTLM. For environments requiring NTLM,
218
+ * pass a pre-obtained cam_passport via config.camPassport instead.
86
219
  */
87
- private setupCamAuthentication;
220
+ private static _buildAuthorizationTokenCamSso;
88
221
  /**
89
- * Set up CAM SSO authentication
222
+ * Generate IBM IAM Cloud access token.
223
+ * Mirrors tm1py's _generate_ibm_iam_cloud_access_token.
90
224
  */
91
- private setupCamSsoAuthentication;
225
+ private _generateIbmIamCloudAccessToken;
92
226
  /**
93
- * Set up Service-to-Service authentication
227
+ * Generate CPD (Cloud Pak for Data) access token.
228
+ * Mirrors tm1py's _generate_cpd_access_token.
94
229
  */
95
- private setupServiceToServiceAuthentication;
230
+ private _generateCpdAccessToken;
96
231
  /**
97
- * Get the authentication mode being used
232
+ * Authenticate with PA Proxy using a CPD JWT token.
233
+ * Mirrors tm1py's PA_PROXY flow in _start_session.
234
+ */
235
+ private _authenticateWithPaProxy;
236
+ /**
237
+ * Authenticate Service-to-Service (v12).
238
+ * Mirrors tm1py's SERVICE_TO_SERVICE flow in _start_session:
239
+ * Uses Basic auth with applicationClientId:applicationClientSecret,
240
+ * then POSTs {"User": user} to the auth endpoint.
241
+ */
242
+ private _authenticateServiceToService;
243
+ /**
244
+ * Determine the authentication mode from config.
245
+ * Mirrors tm1py's _determine_auth_mode, using the URL topology as the
246
+ * primary discriminator for v12 modes.
98
247
  */
99
248
  getAuthenticationMode(): AuthenticationMode;
249
+ /**
250
+ * Set up authentication based on configuration.
251
+ * Mirrors tm1py's _start_session routing.
252
+ */
253
+ private setupAuthentication;
100
254
  /**
101
255
  * Re-authenticate using stored configuration
102
256
  */
@@ -162,19 +316,27 @@ export declare class RestService {
162
316
  */
163
317
  set_version(version: string): void;
164
318
  /**
165
- * Check if current user is admin
319
+ * Fetch the active user's group names as a CaseAndSpaceInsensitiveSet so
320
+ * membership tests are case- and whitespace-insensitive (mirrors tm1py).
321
+ * Concurrent callers (e.g. Promise.all([is_admin(), is_data_admin(), ...]))
322
+ * coalesce onto a single in-flight request.
323
+ */
324
+ private fetchActiveUserGroupNames;
325
+ /**
326
+ * Check if current user is admin. Result is cached after the first
327
+ * computation, and pre-populated when the configured user is ADMIN.
166
328
  */
167
329
  is_admin(): Promise<boolean>;
168
330
  /**
169
- * Check if current user is data admin
331
+ * Check if current user is data admin (member of Admin or DataAdmin).
170
332
  */
171
333
  is_data_admin(): Promise<boolean>;
172
334
  /**
173
- * Check if current user is ops admin
335
+ * Check if current user is ops admin (member of Admin or OperationsAdmin).
174
336
  */
175
337
  is_ops_admin(): Promise<boolean>;
176
338
  /**
177
- * Check if current user is security admin
339
+ * Check if current user is security admin (member of Admin or SecurityAdmin).
178
340
  */
179
341
  is_security_admin(): Promise<boolean>;
180
342
  /**
@@ -191,6 +353,23 @@ export declare class RestService {
191
353
  get_http_headers(): {
192
354
  [key: string]: string;
193
355
  };
356
+ /**
357
+ * Insert `tm1.compact=v0` into the Accept header (after the
358
+ * `application/json` segment) and return the previous header value.
359
+ * Mirrors tm1py's add_compact_json_header.
360
+ */
361
+ add_compact_json_header(): string;
362
+ /**
363
+ * Decode a Base64-encoded password to its UTF-8 plaintext form
364
+ * (mirrors tm1py's b64_decode_password).
365
+ */
366
+ static b64_decode_password(encryptedPassword: string): string;
367
+ /**
368
+ * Coerce a boolean/number/string config value to a boolean. Strings
369
+ * are stripped of whitespace and lowercased before comparison with
370
+ * `'true'` (mirrors tm1py's translate_to_boolean).
371
+ */
372
+ static translate_to_boolean(value: unknown): boolean;
194
373
  /**
195
374
  * Cancel an async operation by ID
196
375
  */
@@ -198,15 +377,24 @@ export declare class RestService {
198
377
  /**
199
378
  * Retrieve async operation response
200
379
  */
201
- retrieve_async_response(async_id: string): Promise<any>;
380
+ retrieve_async_response(async_id: string): Promise<AxiosResponse>;
202
381
  /**
203
- * Get async operation status
382
+ * TM1 v12 returns completed async results with HTTP 200 and encodes
383
+ * the true operation status in the `asyncresult` header (e.g.
384
+ * "500 Internal Server Error"). Mirror tm1py's
385
+ * `_transform_async_response` by throwing on any embedded non-2xx
386
+ * status so callers are not handed a 500 as "success".
204
387
  */
205
- get_async_operation_status(async_id: string): Promise<string>;
388
+ private verifyAsyncResultHeader;
206
389
  /**
207
- * Wait for async operation to complete
390
+ * Wait for async operation to complete using a fixed polling cadence.
391
+ *
392
+ * Unlike the internal dispatcher's {@link waitTimeGenerator} (capped
393
+ * exponential backoff), this public helper polls every
394
+ * {@link poll_interval_seconds} seconds so existing callers who tuned
395
+ * the cadence keep their original behavior.
208
396
  */
209
- wait_for_async_operation(async_id: string, timeout_seconds?: number, poll_interval_seconds?: number): Promise<any>;
397
+ wait_for_async_operation(async_id: string, timeout_seconds?: number, poll_interval_seconds?: number, cancel_at_timeout?: boolean): Promise<any>;
210
398
  /**
211
399
  * Get active user name
212
400
  */