tm1npm 1.6.0 → 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 (34) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/lib/services/ApplicationService.d.ts.map +1 -1
  3. package/lib/services/AsyncOperationService.d.ts +8 -1
  4. package/lib/services/AsyncOperationService.d.ts.map +1 -1
  5. package/lib/services/AsyncOperationService.js +69 -26
  6. package/lib/services/FileService.d.ts.map +1 -1
  7. package/lib/services/ProcessService.d.ts +18 -13
  8. package/lib/services/ProcessService.d.ts.map +1 -1
  9. package/lib/services/ProcessService.js +28 -17
  10. package/lib/services/RestService.d.ts +213 -25
  11. package/lib/services/RestService.d.ts.map +1 -1
  12. package/lib/services/RestService.js +840 -271
  13. package/lib/services/TM1Service.d.ts +42 -1
  14. package/lib/services/TM1Service.d.ts.map +1 -1
  15. package/lib/services/TM1Service.js +94 -4
  16. package/lib/tests/asyncOperationService.test.js +51 -45
  17. package/lib/tests/processService.comprehensive.test.js +2 -2
  18. package/lib/tests/processService.test.js +20 -6
  19. package/lib/tests/restService.test.d.ts +0 -4
  20. package/lib/tests/restService.test.d.ts.map +1 -1
  21. package/lib/tests/restService.test.js +1558 -143
  22. package/lib/tests/tm1Service.test.js +80 -8
  23. package/package.json +1 -1
  24. package/src/services/ApplicationService.ts +4 -4
  25. package/src/services/AsyncOperationService.ts +76 -29
  26. package/src/services/FileService.ts +3 -3
  27. package/src/services/ProcessService.ts +67 -37
  28. package/src/services/RestService.ts +1020 -278
  29. package/src/services/TM1Service.ts +124 -6
  30. package/src/tests/asyncOperationService.test.ts +52 -48
  31. package/src/tests/processService.comprehensive.test.ts +3 -3
  32. package/src/tests/processService.test.ts +21 -9
  33. package/src/tests/restService.test.ts +1844 -139
  34. package/src/tests/tm1Service.test.ts +95 -11
@@ -7,6 +7,7 @@
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  const TM1Service_1 = require("../services/TM1Service");
9
9
  const RestService_1 = require("../services/RestService");
10
+ const User_1 = require("../objects/User");
10
11
  // Mock all service dependencies
11
12
  jest.mock('../services/RestService');
12
13
  jest.mock('../services/DimensionService');
@@ -22,6 +23,19 @@ jest.mock('../services/FileService');
22
23
  jest.mock('../services/SessionService');
23
24
  jest.mock('../services/ServerService');
24
25
  jest.mock('../services/MonitoringService');
26
+ jest.mock('../services/AnnotationService');
27
+ jest.mock('../services/ChoreService');
28
+ jest.mock('../services/GitService');
29
+ jest.mock('../services/ApplicationService');
30
+ jest.mock('../services/SandboxService');
31
+ jest.mock('../services/JobService');
32
+ jest.mock('../services/UserService');
33
+ jest.mock('../services/ThreadService');
34
+ jest.mock('../services/TransactionLogService');
35
+ jest.mock('../services/MessageLogService');
36
+ jest.mock('../services/ConfigurationService');
37
+ jest.mock('../services/AuditLogService');
38
+ jest.mock('../services/LoggerService');
25
39
  describe('TM1Service', () => {
26
40
  let tm1Service;
27
41
  let mockRestService;
@@ -56,6 +70,8 @@ describe('TM1Service', () => {
56
70
  setSandbox: jest.fn(),
57
71
  getSandbox: jest.fn().mockReturnValue('test-sandbox'),
58
72
  isLoggedIn: jest.fn().mockReturnValue(true),
73
+ getVersion: jest.fn().mockResolvedValue('12.0.0'),
74
+ version: undefined,
59
75
  };
60
76
  // Mock RestService constructor
61
77
  RestService_1.RestService.mockImplementation(() => mockRestService);
@@ -75,6 +91,7 @@ describe('TM1Service', () => {
75
91
  expect(tm1Service.security).toBeDefined();
76
92
  expect(tm1Service.files).toBeDefined();
77
93
  expect(tm1Service.sessions).toBeDefined();
94
+ expect(tm1Service.applications).toBeDefined();
78
95
  });
79
96
  test('should create RestService with provided config', () => {
80
97
  expect(RestService_1.RestService).toHaveBeenCalledWith(mockConfig);
@@ -123,14 +140,16 @@ describe('TM1Service', () => {
123
140
  });
124
141
  });
125
142
  describe('User and Authentication', () => {
126
- test('should get current user with whoami', async () => {
127
- // Mock security service getCurrentUser method
143
+ test('should get current user as User object with whoami', async () => {
144
+ const expectedUser = new User_1.User('test-user', ['ADMIN'], 'Test User', undefined, User_1.UserType.Admin, true);
128
145
  const mockSecurityService = {
129
- getCurrentUser: jest.fn().mockResolvedValue({ name: 'test-user' })
146
+ getCurrentUser: jest.fn().mockResolvedValue(expectedUser)
130
147
  };
131
148
  tm1Service.security = mockSecurityService;
132
149
  const result = await tm1Service.whoami();
133
- expect(result).toBe('test-user');
150
+ expect(result).toBeInstanceOf(User_1.User);
151
+ expect(result.name).toBe('test-user');
152
+ expect(result).toBe(expectedUser);
134
153
  expect(mockSecurityService.getCurrentUser).toHaveBeenCalledTimes(1);
135
154
  });
136
155
  test('should check if user is logged in', () => {
@@ -157,11 +176,11 @@ describe('TM1Service', () => {
157
176
  expect(result).toEqual({ metadata: 'test-metadata' });
158
177
  expect(mockRestService.get).toHaveBeenCalledWith('/$metadata');
159
178
  });
160
- test('should get TM1 version', async () => {
161
- mockRestService.get.mockResolvedValueOnce(mockResponse({ value: '12.0.0' }));
179
+ test('should get TM1 version via cached RestService.getVersion', async () => {
180
+ mockRestService.getVersion.mockResolvedValueOnce('12.0.0');
162
181
  const result = await tm1Service.getVersion();
163
182
  expect(result).toBe('12.0.0');
164
- expect(mockRestService.get).toHaveBeenCalledWith('/Configuration/ProductVersion');
183
+ expect(mockRestService.getVersion).toHaveBeenCalledTimes(1);
165
184
  });
166
185
  test('should handle metadata retrieval errors', async () => {
167
186
  const metadataError = new Error('Metadata not available');
@@ -170,7 +189,7 @@ describe('TM1Service', () => {
170
189
  });
171
190
  test('should handle version retrieval errors', async () => {
172
191
  const versionError = new Error('Version not available');
173
- mockRestService.get.mockRejectedValueOnce(versionError);
192
+ mockRestService.getVersion.mockRejectedValueOnce(versionError);
174
193
  await expect(tm1Service.getVersion()).rejects.toThrow('Version not available');
175
194
  });
176
195
  });
@@ -286,5 +305,58 @@ describe('TM1Service', () => {
286
305
  expect(monitoring1).toBe(monitoring2);
287
306
  });
288
307
  });
308
+ describe('Lazy Services (Issue #82)', () => {
309
+ const lazyServiceNames = [
310
+ 'annotations',
311
+ 'chores',
312
+ 'git',
313
+ 'sandboxes',
314
+ 'jobs',
315
+ 'users',
316
+ 'threads',
317
+ 'transactionLogs',
318
+ 'messageLogs',
319
+ 'configuration',
320
+ 'auditLogs',
321
+ 'loggers',
322
+ ];
323
+ test.each(lazyServiceNames)('should lazy-initialize %s service', (serviceName) => {
324
+ const instance = tm1Service[serviceName];
325
+ expect(instance).toBeDefined();
326
+ });
327
+ test.each(lazyServiceNames)('should cache %s service instance across accesses', (serviceName) => {
328
+ const first = tm1Service[serviceName];
329
+ const second = tm1Service[serviceName];
330
+ expect(second).toBe(first);
331
+ });
332
+ });
333
+ describe('Version Getter (Issue #82)', () => {
334
+ test('should expose cached version via sync getter', () => {
335
+ Object.defineProperty(mockRestService, 'version', {
336
+ get: () => '11.8.0',
337
+ configurable: true,
338
+ });
339
+ expect(tm1Service.version).toBe('11.8.0');
340
+ });
341
+ test('should return undefined when version has not been fetched yet', () => {
342
+ Object.defineProperty(mockRestService, 'version', {
343
+ get: () => undefined,
344
+ configurable: true,
345
+ });
346
+ expect(tm1Service.version).toBeUndefined();
347
+ });
348
+ });
349
+ describe('reConnect (Issue #82)', () => {
350
+ test('should call connect without disconnecting (tm1py parity)', async () => {
351
+ await tm1Service.reConnect();
352
+ expect(mockRestService.connect).toHaveBeenCalledTimes(1);
353
+ expect(mockRestService.disconnect).not.toHaveBeenCalled();
354
+ });
355
+ test('should propagate errors from connect', async () => {
356
+ mockRestService.connect.mockRejectedValueOnce(new Error('Connect failed'));
357
+ await expect(tm1Service.reConnect()).rejects.toThrow('Connect failed');
358
+ expect(mockRestService.connect).toHaveBeenCalledTimes(1);
359
+ });
360
+ });
289
361
  });
290
362
  //# sourceMappingURL=tm1Service.test.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tm1npm",
3
- "version": "1.6.0",
3
+ "version": "2.0.0",
4
4
  "description": "A Node.js module for TM1",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -1,4 +1,4 @@
1
- import { AxiosRequestConfig, AxiosResponse } from 'axios';
1
+ import { AxiosResponse } from 'axios';
2
2
  import { promises as fs } from 'fs';
3
3
  import { RestService } from './RestService';
4
4
  import { ObjectService } from './ObjectService';
@@ -152,7 +152,7 @@ export class ApplicationService extends ObjectService {
152
152
  requestName
153
153
  );
154
154
 
155
- const arrayBufferResponse = await this.rest.get(contentUrl, { responseType: 'arraybuffer' } as AxiosRequestConfig);
155
+ const arrayBufferResponse = await this.rest.get(contentUrl, { responseType: 'arraybuffer' });
156
156
  const metadataResponse = await this.rest.get(metadataUrl);
157
157
 
158
158
  const buffer = Buffer.from(arrayBufferResponse.data);
@@ -183,7 +183,7 @@ export class ApplicationService extends ObjectService {
183
183
  );
184
184
  await this.rest.put(contentUrl, application.content, {
185
185
  headers: this.binaryHttpHeader
186
- } as AxiosRequestConfig);
186
+ });
187
187
  }
188
188
 
189
189
  return response;
@@ -204,7 +204,7 @@ export class ApplicationService extends ObjectService {
204
204
  );
205
205
  return await this.rest.post(url, application.content, {
206
206
  headers: this.binaryHttpHeader
207
- } as AxiosRequestConfig);
207
+ });
208
208
  }
209
209
 
210
210
  const url = formatUrl(
@@ -52,6 +52,12 @@ export interface AsyncOperation {
52
52
  result?: any;
53
53
  parameters?: Record<string, any>;
54
54
  metadata?: Record<string, any>;
55
+ /**
56
+ * When true, the operation is tracked with a client-side UUID and the
57
+ * TM1 server has no record of it. `getAsyncOperationStatus` returns the
58
+ * cached status instead of polling `/_async('{id}')`.
59
+ */
60
+ trackedLocally?: boolean;
55
61
  }
56
62
 
57
63
  /**
@@ -113,30 +119,81 @@ export class AsyncOperationService {
113
119
  return operation.status;
114
120
  }
115
121
 
116
- // Poll TM1 server for updated status
122
+ // Locally tracked operations hold a client-side UUID; the TM1 server
123
+ // would return 404 for them. Rely on the in-memory cache, which is
124
+ // populated by background .then()/.catch() callbacks in helpers like
125
+ // ProcessService.executeWithReturnAsync.
126
+ if (operation.trackedLocally) {
127
+ return operation.status;
128
+ }
129
+
130
+ // Poll TM1 server for updated status. /_async('{id}') returns 202 while
131
+ // the op is pending, and 200/201 with the final operation payload once done.
132
+ // TM1 v12 may encode embedded failures in the `asyncresult` response header.
117
133
  try {
118
- const url = formatUrl("/AsyncOperations('{}')", operationId);
119
- const response = await this.rest.get(url);
120
- const serverStatus = this.mapServerStatus(response.data.Status);
134
+ const url = formatUrl("/_async('{}')", operationId);
135
+ const response = await this.rest.get(url, { asyncRequestsMode: false });
136
+ const serverStatus = this.deriveStatusFromResponse(response);
121
137
 
122
- // Update operation status
123
138
  operation.status = serverStatus;
124
139
  if (this.isTerminalStatus(serverStatus)) {
125
140
  operation.endTime = new Date();
126
- if (serverStatus === OperationStatus.COMPLETED && response.data.Result) {
127
- operation.result = response.data.Result;
128
- } else if (serverStatus === OperationStatus.FAILED && response.data.Error) {
129
- operation.error = response.data.Error;
141
+ if (serverStatus === OperationStatus.COMPLETED) {
142
+ operation.result = response.data;
143
+ } else if (serverStatus === OperationStatus.FAILED) {
144
+ operation.error = this.extractErrorFromResponse(response);
130
145
  }
131
146
  }
132
147
 
133
148
  return serverStatus;
134
- } catch (error) {
135
- // If server doesn't support AsyncOperations endpoint, return cached status
149
+ } catch (error: any) {
150
+ // HTTP 4xx/5xx surfaces as a thrown TM1RestException treat as a terminal FAILED
151
+ // so callers stop polling. Network errors (no status) leave cached status intact.
152
+ // Note: 404 is treated as FAILED here (operation never materialized). This differs
153
+ // from ProcessService.pollExecuteWithReturn which treats 404 as "not ready yet"
154
+ // because process async IDs can take a moment to register on the server.
155
+ const status = error?.status ?? error?.response?.status;
156
+ if (typeof status === 'number' && status >= 400) {
157
+ operation.status = OperationStatus.FAILED;
158
+ operation.endTime = new Date();
159
+ operation.error = error?.message ?? String(error);
160
+ return OperationStatus.FAILED;
161
+ }
136
162
  return operation.status;
137
163
  }
138
164
  }
139
165
 
166
+ private deriveStatusFromResponse(response: any): OperationStatus {
167
+ if (response.status === 202) {
168
+ return OperationStatus.RUNNING;
169
+ }
170
+ const asyncResult = response.headers?.['asyncresult'];
171
+ if (typeof asyncResult === 'string') {
172
+ const embedded = parseInt(asyncResult.trim().split(/\s+/)[0], 10);
173
+ if (!Number.isNaN(embedded) && (embedded < 200 || embedded >= 300)) {
174
+ return OperationStatus.FAILED;
175
+ }
176
+ }
177
+ if (response.status === 200 || response.status === 201) {
178
+ return OperationStatus.COMPLETED;
179
+ }
180
+ return OperationStatus.PENDING;
181
+ }
182
+
183
+ private extractErrorFromResponse(response: any): string {
184
+ const asyncResult = response.headers?.['asyncresult'];
185
+ if (typeof asyncResult === 'string') {
186
+ return asyncResult;
187
+ }
188
+ if (typeof response.data === 'string') {
189
+ return response.data;
190
+ }
191
+ if (response.data?.error?.message) {
192
+ return response.data.error.message;
193
+ }
194
+ return JSON.stringify(response.data);
195
+ }
196
+
140
197
  /**
141
198
  * List all active async operations
142
199
  *
@@ -178,11 +235,10 @@ export class AsyncOperationService {
178
235
  this.stopPolling(operationId);
179
236
 
180
237
  try {
181
- // Try to cancel on server if supported
182
- const url = formatUrl("/AsyncOperations('{}')/Cancel", operationId);
183
- await this.rest.post(url, {});
238
+ const url = formatUrl("/_async('{}')", operationId);
239
+ await this.rest.delete(url, { asyncRequestsMode: false });
184
240
  } catch (error) {
185
- // If server doesn't support cancellation, just mark as cancelled locally
241
+ console.warn(`Failed to cancel async operation ${operationId} on server:`, error);
186
242
  }
187
243
 
188
244
  // Update operation status
@@ -243,6 +299,9 @@ export class AsyncOperationService {
243
299
  public async createAsyncOperation(definition: AsyncOperationDefinition): Promise<string> {
244
300
  const operationId = this.generateOperationId();
245
301
 
302
+ // generateOperationId produces a client-side UUID; the server has no
303
+ // record of it, so polling /_async('{id}') would 404. Mark as locally
304
+ // tracked so getAsyncOperationStatus returns the cached status instead.
246
305
  const operation: AsyncOperation = {
247
306
  id: operationId,
248
307
  type: definition.type,
@@ -250,7 +309,8 @@ export class AsyncOperationService {
250
309
  status: OperationStatus.PENDING,
251
310
  startTime: new Date(),
252
311
  parameters: definition.parameters,
253
- metadata: definition.metadata
312
+ metadata: definition.metadata,
313
+ trackedLocally: true
254
314
  };
255
315
 
256
316
  this.operations.set(operationId, operation);
@@ -442,19 +502,6 @@ export class AsyncOperationService {
442
502
  status === OperationStatus.TIMEOUT;
443
503
  }
444
504
 
445
- private mapServerStatus(serverStatus: string): OperationStatus {
446
- const statusMap: Record<string, OperationStatus> = {
447
- 'Pending': OperationStatus.PENDING,
448
- 'Running': OperationStatus.RUNNING,
449
- 'CompletedSuccessfully': OperationStatus.COMPLETED,
450
- 'CompletedWithErrors': OperationStatus.FAILED,
451
- 'Cancelled': OperationStatus.CANCELLED,
452
- 'Timeout': OperationStatus.TIMEOUT
453
- };
454
-
455
- return statusMap[serverStatus] || OperationStatus.PENDING;
456
- }
457
-
458
505
  private stopPolling(operationId: string): void {
459
506
  const intervalId = this.pollingIntervals.get(operationId);
460
507
  if (intervalId) {
@@ -1,6 +1,6 @@
1
1
  import path from 'path';
2
- import { AxiosRequestConfig, AxiosResponse } from 'axios';
3
- import { RestService } from './RestService';
2
+ import { AxiosResponse } from 'axios';
3
+ import { RequestOptions, RestService } from './RestService';
4
4
  import { ObjectService } from './ObjectService';
5
5
  import { verifyVersion } from '../utils/Utils';
6
6
 
@@ -227,7 +227,7 @@ export class FileService extends ObjectService {
227
227
  }
228
228
 
229
229
  private async uploadFileContentWithoutMpu(url: string, content: Buffer): Promise<AxiosResponse> {
230
- const config: AxiosRequestConfig = {
230
+ const config: RequestOptions = {
231
231
  headers: this.binaryHttpHeader
232
232
  };
233
233
  return await this.rest.put(url, content, config);
@@ -1,6 +1,6 @@
1
1
  import { AxiosResponse } from 'axios';
2
2
  import { v4 as uuidv4 } from 'uuid';
3
- import { RestService } from './RestService';
3
+ import { RequestOptions, RestService } from './RestService';
4
4
  import { ObjectService } from './ObjectService';
5
5
  import { Process } from '../objects/Process';
6
6
  import { ProcessDebugBreakpoint, BreakPointType, HitMode } from '../objects/ProcessDebugBreakpoint';
@@ -13,6 +13,26 @@ export interface CompileSyntaxError {
13
13
  Message: string;
14
14
  }
15
15
 
16
+ interface ProcessExecuteBody {
17
+ Parameters?: Array<{ Name: string; Value: unknown }>;
18
+ }
19
+
20
+ interface ProcessExecuteSummary {
21
+ ProcessExecuteStatusCode: string;
22
+ ErrorLogFile?: { Filename?: string } | null;
23
+ }
24
+
25
+ interface ValidationError {
26
+ line: number;
27
+ message: string;
28
+ severity: string;
29
+ }
30
+
31
+ interface ValidationResult {
32
+ isValid: boolean;
33
+ errors: ValidationError[];
34
+ }
35
+
16
36
  export class ProcessService extends ObjectService {
17
37
  /** Service to handle Object Updates for TI Processes
18
38
  *
@@ -79,7 +99,7 @@ export class ProcessService extends ObjectService {
79
99
 
80
100
  const response = await this.rest.get(url);
81
101
  const responseAsDict = response.data;
82
- return responseAsDict.value.map((p: any) => Process.fromDict(p));
102
+ return responseAsDict.value.map((p: Record<string, unknown>) => Process.fromDict(p));
83
103
  }
84
104
 
85
105
  public async getAllNames(skipControlProcesses: boolean = false): Promise<string[]> {
@@ -93,7 +113,7 @@ export class ProcessService extends ObjectService {
93
113
  const url = "/Processes?$select=Name" + (skipControlProcesses ? modelProcessFilter : "");
94
114
 
95
115
  const response = await this.rest.get(url);
96
- const processes = response.data.value.map((process: any) => process.Name);
116
+ const processes = response.data.value.map((process: { Name: string }) => process.Name);
97
117
  return processes;
98
118
  }
99
119
 
@@ -174,8 +194,8 @@ export class ProcessService extends ObjectService {
174
194
  * :return: response
175
195
  */
176
196
  const url = formatUrl("/Processes('{}')/tm1.Execute", processName);
177
-
178
- const body: any = {};
197
+
198
+ const body: ProcessExecuteBody = {};
179
199
  if (parameters && Object.keys(parameters).length > 0) {
180
200
  body.Parameters = Object.entries(parameters).map(([name, value]) => ({
181
201
  Name: name,
@@ -199,8 +219,8 @@ export class ProcessService extends ObjectService {
199
219
  * :return: response including execution details
200
220
  */
201
221
  const url = formatUrl("/Processes('{}')/tm1.ExecuteWithReturn?$expand=*", processName);
202
-
203
- const body: any = {};
222
+
223
+ const body: ProcessExecuteBody = {};
204
224
  if (parameters && Object.keys(parameters).length > 0) {
205
225
  body.Parameters = Object.entries(parameters).map(([name, value]) => ({
206
226
  Name: name,
@@ -208,9 +228,9 @@ export class ProcessService extends ObjectService {
208
228
  }));
209
229
  }
210
230
 
211
- const config: any = {};
231
+ const config: RequestOptions = {};
212
232
  if (timeout) {
213
- config.timeout = timeout * 1000;
233
+ config.timeout = timeout;
214
234
  }
215
235
 
216
236
  return await this.rest.post(url, JSON.stringify(body), config);
@@ -247,12 +267,20 @@ export class ProcessService extends ObjectService {
247
267
  public async pollExecuteWithReturn(asyncId: string): Promise<[boolean, string, string | null] | null> {
248
268
  try {
249
269
  const response = await this.rest.retrieve_async_response(asyncId);
270
+ // tm1py returns None while the async op is still in-flight (status 202).
271
+ if (response.status !== 200 && response.status !== 201) {
272
+ return null;
273
+ }
250
274
  // TODO: tm1py handles TM1 < v11 binary-wrapped responses via
251
275
  // build_response_from_binary_response. Add support if needed.
252
- return this._executeWithReturnParseResponse(response);
253
- } catch (error: any) {
254
- // Return null for HTTP 202 (accepted/pending) or 404 (not found yet)
255
- const status = error?.status ?? error?.response?.status;
276
+ return this._executeWithReturnParseResponse(response.data);
277
+ } catch (error) {
278
+ // 404 means the async resource hasn't materialized yet return null
279
+ // so the caller can retry. 202 means still running. Both are
280
+ // retryable, unlike AsyncOperationService which treats 404 as
281
+ // terminal FAILED for locally-tracked operations.
282
+ const err = error as { status?: number; response?: { status?: number } };
283
+ const status = err?.status ?? err?.response?.status;
256
284
  if (status === 202 || status === 404) {
257
285
  return null;
258
286
  }
@@ -260,7 +288,7 @@ export class ProcessService extends ObjectService {
260
288
  }
261
289
  }
262
290
 
263
- private _executeWithReturnParseResponse(executionSummary: any): [boolean, string, string | null] {
291
+ private _executeWithReturnParseResponse(executionSummary: ProcessExecuteSummary): [boolean, string, string | null] {
264
292
  const success = executionSummary.ProcessExecuteStatusCode === 'CompletedSuccessfully';
265
293
  const status = executionSummary.ProcessExecuteStatusCode;
266
294
  const errorLogFile = executionSummary.ErrorLogFile?.Filename ?? null;
@@ -300,7 +328,7 @@ export class ProcessService extends ObjectService {
300
328
  */
301
329
  const url = formatUrl("/ProcessDebugContexts('{}')/Breakpoints", debugId);
302
330
  const response = await this.rest.get(url);
303
- return response.data.value.map((bp: any) => ProcessDebugBreakpoint.fromDict(bp));
331
+ return response.data.value.map((bp: Record<string, unknown>) => ProcessDebugBreakpoint.fromDict(bp));
304
332
  }
305
333
 
306
334
  public async debugAddBreakpoint(
@@ -532,7 +560,7 @@ export class ProcessService extends ObjectService {
532
560
  }
533
561
 
534
562
  const response = await this.rest.get(url);
535
- return response.data.value.map((log: any) => log.Filename);
563
+ return response.data.value.map((log: { Filename: string }) => log.Filename);
536
564
  }
537
565
 
538
566
  public async getErrorLogFilenames(
@@ -619,7 +647,7 @@ export class ProcessService extends ObjectService {
619
647
  }
620
648
 
621
649
  const response = await this.rest.get(url);
622
- return response.data.value.map((process: any) => process.Name);
650
+ return response.data.value.map((process: { Name: string }) => process.Name);
623
651
  }
624
652
 
625
653
  public async updateOrCreate(process: Process): Promise<AxiosResponse> {
@@ -673,7 +701,7 @@ export class ProcessService extends ObjectService {
673
701
  processName
674
702
  );
675
703
 
676
- const body: any = {};
704
+ const body: ProcessExecuteBody = {};
677
705
  if (parameters && Object.keys(parameters).length > 0) {
678
706
  body.Parameters = Object.entries(parameters).map(([name, value]) => ({
679
707
  Name: name,
@@ -681,12 +709,14 @@ export class ProcessService extends ObjectService {
681
709
  }));
682
710
  }
683
711
 
684
- const config: any = {};
712
+ const config: RequestOptions = {};
685
713
  if (timeout) {
686
- config.timeout = timeout * 1000;
714
+ config.timeout = timeout;
687
715
  }
688
716
 
689
- const response = await this.rest.post(url, JSON.stringify(body), config);
717
+ // rest.post returns AxiosResponse | string (string only when caller
718
+ // opts into returnAsyncId). debugProcess never does, so narrow.
719
+ const response = (await this.rest.post(url, JSON.stringify(body), config)) as AxiosResponse;
690
720
  return response.data;
691
721
  }
692
722
 
@@ -885,8 +915,8 @@ export class ProcessService extends ObjectService {
885
915
  * @example
886
916
  * ```typescript
887
917
  * const deps = await processService.analyzeProcessDependencies('ImportData');
888
- * console.log('Cubes used:', deps.cubes);
889
- * console.log('Dimensions used:', deps.dimensions);
918
+ * // deps.cubes => array of cube names referenced in the process
919
+ * // deps.dimensions => array of dimension names referenced in the process
890
920
  * ```
891
921
  */
892
922
  public async analyzeProcessDependencies(processName: string): Promise<any> {
@@ -954,17 +984,17 @@ export class ProcessService extends ObjectService {
954
984
  * Validate process syntax without executing it
955
985
  *
956
986
  * @param processName - Name of the process
957
- * @returns Promise<{isValid: boolean, errors: any[]}> - Validation result
987
+ * @returns Promise<ValidationResult> - Validation result
958
988
  *
959
989
  * @example
960
990
  * ```typescript
961
991
  * const result = await processService.validateProcessSyntax('MyProcess');
962
992
  * if (!result.isValid) {
963
- * console.error('Errors:', result.errors);
993
+ * // result.errors contains ValidationError[] entries
964
994
  * }
965
995
  * ```
966
996
  */
967
- public async validateProcessSyntax(processName: string): Promise<{isValid: boolean, errors: any[]}> {
997
+ public async validateProcessSyntax(processName: string): Promise<ValidationResult> {
968
998
  try {
969
999
  const response = await this.compile(processName);
970
1000
  if (response.status === 200) {
@@ -974,8 +1004,9 @@ export class ProcessService extends ObjectService {
974
1004
  isValid: false,
975
1005
  errors: [{ line: 0, message: response.statusText || 'Compilation failed', severity: 'Error' }]
976
1006
  };
977
- } catch (error: any) {
978
- const errorMessage = error.response?.data?.error?.message || error.message || 'Validation failed';
1007
+ } catch (error) {
1008
+ const err = error as { response?: { data?: { error?: { message?: string } } }; message?: string };
1009
+ const errorMessage = err.response?.data?.error?.message || err.message || 'Validation failed';
979
1010
  return {
980
1011
  isValid: false,
981
1012
  errors: [{ line: 0, message: errorMessage, severity: 'Error' }]
@@ -992,7 +1023,7 @@ export class ProcessService extends ObjectService {
992
1023
  * @example
993
1024
  * ```typescript
994
1025
  * const plan = await processService.getProcessExecutionPlan('ImportData');
995
- * console.log('Estimated execution time:', plan.estimatedTime);
1026
+ * // plan.estimatedTime => estimated execution time (ms)
996
1027
  * ```
997
1028
  */
998
1029
  public async getProcessExecutionPlan(processName: string): Promise<any> {
@@ -1072,19 +1103,20 @@ export class ProcessService extends ObjectService {
1072
1103
 
1073
1104
  // Execute process asynchronously
1074
1105
  this.executeWithReturn(processName, parameters)
1075
- .then((result: any) => {
1106
+ .then((result: unknown) => {
1076
1107
  asyncOps.updateOperationStatus(
1077
1108
  operationId,
1078
1109
  OperationStatus.COMPLETED,
1079
1110
  result
1080
1111
  );
1081
1112
  })
1082
- .catch((error: any) => {
1113
+ .catch((error: unknown) => {
1114
+ const message = (error as { message?: string })?.message ?? String(error);
1083
1115
  asyncOps.updateOperationStatus(
1084
1116
  operationId,
1085
1117
  OperationStatus.FAILED,
1086
1118
  undefined,
1087
- error.message || String(error)
1119
+ message
1088
1120
  );
1089
1121
  });
1090
1122
 
@@ -1100,9 +1132,7 @@ export class ProcessService extends ObjectService {
1100
1132
  * @example
1101
1133
  * ```typescript
1102
1134
  * const status = await processService.pollProcessExecution(operationId);
1103
- * if (status === OperationStatus.COMPLETED) {
1104
- * console.log('Process completed!');
1105
- * }
1135
+ * // if (status === OperationStatus.COMPLETED) handle completion
1106
1136
  * ```
1107
1137
  */
1108
1138
  public async pollProcessExecution(operationId: string): Promise<OperationStatus> {
@@ -1123,7 +1153,7 @@ export class ProcessService extends ObjectService {
1123
1153
  * @example
1124
1154
  * ```typescript
1125
1155
  * await processService.cancelProcessExecution(operationId);
1126
- * console.log('Process execution cancelled');
1156
+ * // cancellation is acknowledged once the promise resolves
1127
1157
  * ```
1128
1158
  */
1129
1159
  public async cancelProcessExecution(operationId: string): Promise<void> {
@@ -1134,4 +1164,4 @@ export class ProcessService extends ObjectService {
1134
1164
 
1135
1165
  await asyncOps.cancelAsyncOperation(operationId);
1136
1166
  }
1137
- }
1167
+ }