@camunda8/sdk 8.7.16 → 8.7.17
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 +12 -0
- package/README.md +118 -23
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/AsyncTrace.d.ts +22 -0
- package/dist/lib/AsyncTrace.js +34 -0
- package/dist/lib/AsyncTrace.js.map +1 -0
- package/dist/lib/GotHooks.d.ts +2 -1
- package/dist/lib/GotHooks.js +13 -5
- package/dist/lib/GotHooks.js.map +1 -1
- package/dist/lib/PollingOperation.d.ts +37 -4
- package/dist/lib/PollingOperation.js +2 -8
- package/dist/lib/PollingOperation.js.map +1 -1
- package/dist/lib/QuerySubscription.d.ts +103 -0
- package/dist/lib/QuerySubscription.js +179 -0
- package/dist/lib/QuerySubscription.js.map +1 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [8.7.17](https://github.com/camunda/camunda-8-js-sdk/compare/v8.7.16...v8.7.17) (2025-06-18)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* enhance async stack trace. fixes [#506](https://github.com/camunda/camunda-8-js-sdk/issues/506) ([c443ab2](https://github.com/camunda/camunda-8-js-sdk/commit/c443ab20c7aed9a2f4e0b80e0df4ec3e2be8b15c))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* implement QuerySubscription. fixes [#502](https://github.com/camunda/camunda-8-js-sdk/issues/502) ([a4bbd61](https://github.com/camunda/camunda-8-js-sdk/commit/a4bbd612e8c34112e9dfcd19e0b00a81e0c24e81))
|
|
12
|
+
|
|
1
13
|
## [8.7.16](https://github.com/camunda/camunda-8-js-sdk/compare/v8.7.15...v8.7.16) (2025-06-11)
|
|
2
14
|
|
|
3
15
|
|
package/README.md
CHANGED
|
@@ -113,7 +113,7 @@ Here is an example of specifying a different cache directory via the constructor
|
|
|
113
113
|
import { Camunda8 } from '@camunda8/sdk'
|
|
114
114
|
|
|
115
115
|
const c8 = new Camunda8({
|
|
116
|
-
|
|
116
|
+
CAMUNDA_TOKEN_CACHE_DIR: '/tmp/cache',
|
|
117
117
|
})
|
|
118
118
|
```
|
|
119
119
|
|
|
@@ -174,14 +174,14 @@ You can add arbitrary headers to all requests by implementing `IOAuthProvider`:
|
|
|
174
174
|
import { Camunda8, Auth } from '@camunda8/sdk'
|
|
175
175
|
|
|
176
176
|
class MyCustomAuthProvider implements Auth.IOAuthProvider {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
177
|
+
async getToken(audience: string) {
|
|
178
|
+
// here we give a static example, but this class may read configuration,
|
|
179
|
+
// exchange credentials with an endpoint, manage token lifecycles, and so forth...
|
|
180
|
+
// Return an object which will be merged with the headers on the request
|
|
181
|
+
return {
|
|
182
|
+
'x-custom-auth-header': 'someCustomValue',
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
const customAuthProvider = new MyCustomAuthProvider()
|
|
@@ -242,19 +242,19 @@ Here is an example of doing this via the constructor, rather than via the enviro
|
|
|
242
242
|
import { Camunda8 } from '@camunda8/sdk'
|
|
243
243
|
|
|
244
244
|
const c8 = new Camunda8({
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
245
|
+
ZEEBE_ADDRESS: 'localhost:26500',
|
|
246
|
+
ZEEBE_REST_ADDRESS: 'http://localhost:8080',
|
|
247
|
+
ZEEBE_CLIENT_ID: 'zeebe',
|
|
248
|
+
ZEEBE_CLIENT_SECRET: 'zecret',
|
|
249
|
+
CAMUNDA_OAUTH_STRATEGY: 'OAUTH',
|
|
250
|
+
CAMUNDA_OAUTH_URL:
|
|
251
|
+
'http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token',
|
|
252
|
+
CAMUNDA_TASKLIST_BASE_URL: 'http://localhost:8082',
|
|
253
|
+
CAMUNDA_OPERATE_BASE_URL: 'http://localhost:8081',
|
|
254
|
+
CAMUNDA_OPTIMIZE_BASE_URL: 'http://localhost:8083',
|
|
255
|
+
CAMUNDA_MODELER_BASE_URL: 'http://localhost:8070/api',
|
|
256
|
+
CAMUNDA_TENANT_ID: '', // We can override values in the env by passing an empty string value
|
|
257
|
+
CAMUNDA_SECURE_CONNECTION: false,
|
|
258
258
|
})
|
|
259
259
|
```
|
|
260
260
|
|
|
@@ -304,11 +304,106 @@ const logger = pino({ level }) // Logging level controlled via the logging libra
|
|
|
304
304
|
|
|
305
305
|
logger.info('Pino logger created')
|
|
306
306
|
const c8 = new Camunda8({
|
|
307
|
-
|
|
307
|
+
logger,
|
|
308
308
|
})
|
|
309
309
|
c8.log.info('Using pino logger')
|
|
310
310
|
```
|
|
311
311
|
|
|
312
|
+
## Awaiting Asynchronous Query Data
|
|
313
|
+
|
|
314
|
+
Camunda 8 uses an eventually-consistent data architecture. When you start a process instance, data related to this process instance is not immediately available in the datastore. This leads to data synchronisation issues you need to manage in your application. To aid you with this, the SDK provides a utility: `PollingOperation`. You can pass a query API operation to this utility with a polling interval and a timeout.
|
|
315
|
+
|
|
316
|
+
The `PollingOperation` will execute the query repeatedly until the expected data is available in the data store, or the timeout is reached.
|
|
317
|
+
|
|
318
|
+
The following example will return the query response for a newly-created process instance as soon as the element instance data is available:
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
import { Camunda8, PollingOperation } from '@camunda8/sdk'
|
|
322
|
+
|
|
323
|
+
const c8 = new Camunda8()
|
|
324
|
+
|
|
325
|
+
const elementInstances = await PollingOperation({
|
|
326
|
+
operation: () =>
|
|
327
|
+
c8.searchElementInstances({
|
|
328
|
+
sort: [{ field: 'processInstanceKey' }],
|
|
329
|
+
filter: {
|
|
330
|
+
processInstanceKey: processInstance.processInstanceKey,
|
|
331
|
+
type: 'SERVICE_TASK',
|
|
332
|
+
},
|
|
333
|
+
}),
|
|
334
|
+
interval: 500,
|
|
335
|
+
timeout: 10000,
|
|
336
|
+
})
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
By default, the `PollingOperation` waits for a query response from the Orchestration Cluster API that has one or more results in the `items` array. If you have a more specific predicate, or are using one of the v1 component APIs, you can pass in a custom predicate function.
|
|
340
|
+
|
|
341
|
+
The following example waits for a process instance to be available to a query over the Operate API:
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import { Camunda8, PollingOperation } from '@camunda8/sdk'
|
|
345
|
+
|
|
346
|
+
const c8 = new Camunda8()
|
|
347
|
+
const c = c8.getOperateApiClient()
|
|
348
|
+
|
|
349
|
+
const process = await PollingOperation({
|
|
350
|
+
operation: () => c.getProcessInstance(p.processInstanceKey),
|
|
351
|
+
predicate: (res) => res.key === p.processInstanceKey,
|
|
352
|
+
interval: 500,
|
|
353
|
+
timeout: 15000,
|
|
354
|
+
})
|
|
355
|
+
```
|
|
356
|
+
## Subscribing to queries
|
|
357
|
+
|
|
358
|
+
You can subscribe to queries using a `QuerySubscription`. This is an event emitter that emits a `data` event when new data is available. You pass a predicate function that takes a `previous` and `current` query result set. In this predicate function you can examine the two states to see whether or not to emit a data event, and also perform data-processing — for example, removing items that were emitted in the last update.
|
|
359
|
+
|
|
360
|
+
Note that this is an experimental feature and may change in future releases. We are looking for feedback on this feature, please report issues in the GitHub repo or via a JIRA ticket if you have an account with Camunda.
|
|
361
|
+
|
|
362
|
+
Here is an example of using `QuerySubscription`:
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
import { Camunda8, QuerySubscription } from '@camunda8/sdk'
|
|
366
|
+
|
|
367
|
+
const c8 = new Camunda8()
|
|
368
|
+
|
|
369
|
+
const query = () =>
|
|
370
|
+
c8.searchProcessInstances({
|
|
371
|
+
filter: {
|
|
372
|
+
processDefinitionKey: key,
|
|
373
|
+
state: 'ACTIVE',
|
|
374
|
+
},
|
|
375
|
+
sort: [{ field: 'startDate', order: 'ASC' }],
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
const subscription = QuerySubscription({
|
|
379
|
+
query,
|
|
380
|
+
predicate: (previous, current) => {
|
|
381
|
+
// This is the default predicate, provided here as an example
|
|
382
|
+
const previousItems = (previous?.items ?? []) as Array<unknown>
|
|
383
|
+
const currentItems = current.items.filter(
|
|
384
|
+
(item) =>
|
|
385
|
+
!previousItems.some((prevItem) => isDeepStrictEqual(prevItem, item))
|
|
386
|
+
)
|
|
387
|
+
if (currentItems.length > 0) {
|
|
388
|
+
return {
|
|
389
|
+
...current,
|
|
390
|
+
items: currentItems,
|
|
391
|
+
page: { ...current.page, totalItems: currentItems.length },
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return false // No new items, do not emit
|
|
395
|
+
},
|
|
396
|
+
interval: 5000,
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
subscription.on('data', data => {
|
|
400
|
+
// new process instances
|
|
401
|
+
})
|
|
402
|
+
//...
|
|
403
|
+
subscription.cancel() // close subscription and free resources
|
|
404
|
+
// You can also use subscription.pause() and subscription.resume() to pause and resume the subscription
|
|
405
|
+
```
|
|
406
|
+
|
|
312
407
|
## Debugging
|
|
313
408
|
|
|
314
409
|
The SDK uses the [`debug`](https://github.com/debug-js/debug) library to help you debug specific issues. This produces verbose, low-level output from specific components to the console.
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import * as Optimize from './optimize';
|
|
|
9
9
|
import * as Tasklist from './tasklist';
|
|
10
10
|
import * as Zeebe from './zeebe';
|
|
11
11
|
export { PollingOperation } from './lib/PollingOperation';
|
|
12
|
+
export { QuerySubscription } from './lib/QuerySubscription';
|
|
12
13
|
export type { CamundaJobWorker } from './c8/lib/CamundaJobWorker';
|
|
13
14
|
export { CamundaSDKConfiguration } from './lib';
|
|
14
15
|
export { HTTPError } from './lib';
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.Zeebe = exports.Tasklist = exports.Optimize = exports.Operate = exports.Modeler = exports.CamundaRestClient = exports.Camunda8 = exports.Auth = exports.Admin = exports.Dto = exports.HTTPError = exports.CamundaSDKConfiguration = exports.PollingOperation = void 0;
|
|
26
|
+
exports.Zeebe = exports.Tasklist = exports.Optimize = exports.Operate = exports.Modeler = exports.CamundaRestClient = exports.Camunda8 = exports.Auth = exports.Admin = exports.Dto = exports.HTTPError = exports.CamundaSDKConfiguration = exports.QuerySubscription = exports.PollingOperation = void 0;
|
|
27
27
|
const Admin = __importStar(require("./admin"));
|
|
28
28
|
exports.Admin = Admin;
|
|
29
29
|
const c8_1 = require("./c8");
|
|
@@ -46,6 +46,8 @@ const Zeebe = __importStar(require("./zeebe"));
|
|
|
46
46
|
exports.Zeebe = Zeebe;
|
|
47
47
|
var PollingOperation_1 = require("./lib/PollingOperation");
|
|
48
48
|
Object.defineProperty(exports, "PollingOperation", { enumerable: true, get: function () { return PollingOperation_1.PollingOperation; } });
|
|
49
|
+
var QuerySubscription_1 = require("./lib/QuerySubscription");
|
|
50
|
+
Object.defineProperty(exports, "QuerySubscription", { enumerable: true, get: function () { return QuerySubscription_1.QuerySubscription; } });
|
|
49
51
|
CamundaSupportLogger_1.CamundaSupportLogger.getInstance();
|
|
50
52
|
var lib_2 = require("./lib");
|
|
51
53
|
Object.defineProperty(exports, "CamundaSDKConfiguration", { enumerable: true, get: function () { return lib_2.CamundaSDKConfiguration; } });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAgC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAgC;AAkD/B,sBAAK;AAjDN,6BAA+B;AAmD9B,yFAnDQ,aAAQ,OAmDR;AAlDT,kEAA8D;AAmD7D,kGAnDQ,qCAAiB,OAmDR;AAlDlB,+BAQc;AACd,qEAAiE;AACjE,mDAAoC;AAyCnC,0BAAO;AAxCR,8CAA+B;AAqC9B,oBAAI;AApCL,mDAAoC;AAwCnC,0BAAO;AAvCR,qDAAsC;AAwCrC,4BAAQ;AAvCT,qDAAsC;AAwCrC,4BAAQ;AAvCT,+CAAgC;AAwC/B,sBAAK;AAtCN,2DAAyD;AAAhD,oHAAA,gBAAgB,OAAA;AACzB,6DAA2D;AAAlD,sHAAA,iBAAiB,OAAA;AAI1B,2CAAoB,CAAC,WAAW,EAAE,CAAA;AAElC,6BAA+C;AAAtC,8GAAA,uBAAuB,OAAA;AAEhC,6BAAiC;AAAxB,gGAAA,SAAS,OAAA;AAElB;;;;;;;GAOG;AACU,QAAA,GAAG,GAAG;IAClB,QAAQ,EAAR,cAAQ;IACR,WAAW,EAAX,iBAAW;IACX,gBAAgB,EAAhB,sBAAgB;IAChB,WAAW,EAAX,iBAAW;IACX,gBAAgB,EAAhB,sBAAgB;IAChB,WAAW,EAAX,iBAAW;IACX,iBAAiB,EAAjB,uBAAiB;CACjB,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
3
|
+
/**
|
|
4
|
+
* Capturing useful async stack traces is challenging with got.
|
|
5
|
+
* See here: https://github.com/sindresorhus/got/blob/main/documentation/async-stack-traces.md
|
|
6
|
+
* This function stores the call point from the application of got requests.
|
|
7
|
+
* This enables users to see where the error originated from.
|
|
8
|
+
* It uses the AsyncLocalStorage to store the creation stack trace.
|
|
9
|
+
* The stack trace is captured from the point where this function is called, allowing you to see where the async operation was initiated.
|
|
10
|
+
* See QuerySubscription.spec.ts for an example of how to use this function.
|
|
11
|
+
* @param fn The function to run within the async context. It can return a value or a Promise.
|
|
12
|
+
* @param errorTag A meaningful tag to identify the error context, which will be included in the stack trace.
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
export declare function runWithAsyncErrorContext<T>(fn: () => T | Promise<T>, errorTag: string): T | Promise<T>;
|
|
16
|
+
/**
|
|
17
|
+
* This is the exported AsyncLocalStorage instance that holds the context for async operations.
|
|
18
|
+
* It is used in the GotHooks.ts to extract the stack trace of the async operation.
|
|
19
|
+
*/
|
|
20
|
+
export declare const asyncOperationContext: AsyncLocalStorage<{
|
|
21
|
+
creationStack: string;
|
|
22
|
+
}>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.asyncOperationContext = exports.runWithAsyncErrorContext = void 0;
|
|
4
|
+
const async_hooks_1 = require("async_hooks");
|
|
5
|
+
/**
|
|
6
|
+
* Capturing useful async stack traces is challenging with got.
|
|
7
|
+
* See here: https://github.com/sindresorhus/got/blob/main/documentation/async-stack-traces.md
|
|
8
|
+
* This function stores the call point from the application of got requests.
|
|
9
|
+
* This enables users to see where the error originated from.
|
|
10
|
+
* It uses the AsyncLocalStorage to store the creation stack trace.
|
|
11
|
+
* The stack trace is captured from the point where this function is called, allowing you to see where the async operation was initiated.
|
|
12
|
+
* See QuerySubscription.spec.ts for an example of how to use this function.
|
|
13
|
+
* @param fn The function to run within the async context. It can return a value or a Promise.
|
|
14
|
+
* @param errorTag A meaningful tag to identify the error context, which will be included in the stack trace.
|
|
15
|
+
* @returns
|
|
16
|
+
*/
|
|
17
|
+
function runWithAsyncErrorContext(fn, errorTag) {
|
|
18
|
+
return exports.asyncOperationContext.run(getAsyncStackTrace(errorTag), fn);
|
|
19
|
+
}
|
|
20
|
+
exports.runWithAsyncErrorContext = runWithAsyncErrorContext;
|
|
21
|
+
/**
|
|
22
|
+
* This is the exported AsyncLocalStorage instance that holds the context for async operations.
|
|
23
|
+
* It is used in the GotHooks.ts to extract the stack trace of the async operation.
|
|
24
|
+
*/
|
|
25
|
+
exports.asyncOperationContext = new async_hooks_1.AsyncLocalStorage();
|
|
26
|
+
function removeLeadingNewLines(str, errorTag) {
|
|
27
|
+
const parts = str.split('\n');
|
|
28
|
+
return [`${errorTag}:`, ...parts.slice(2)].join('\n');
|
|
29
|
+
}
|
|
30
|
+
function getAsyncStackTrace(errorTag) {
|
|
31
|
+
const creationStack = new Error(errorTag).stack?.replace(/^.*?\n/, '') || 'No stack available';
|
|
32
|
+
return { creationStack: removeLeadingNewLines(creationStack, errorTag) };
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=AsyncTrace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AsyncTrace.js","sourceRoot":"","sources":["../../src/lib/AsyncTrace.ts"],"names":[],"mappings":";;;AAAA,6CAA+C;AAE/C;;;;;;;;;;;GAWG;AACH,SAAgB,wBAAwB,CACvC,EAAwB,EACxB,QAAgB;IAEhB,OAAO,6BAAqB,CAAC,GAAG,CAAC,kBAAkB,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAA;AACnE,CAAC;AALD,4DAKC;AAED;;;GAGG;AACU,QAAA,qBAAqB,GAAG,IAAI,+BAAiB,EAEtD,CAAA;AAEJ,SAAS,qBAAqB,CAAC,GAAW,EAAE,QAAgB;IAC3D,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC7B,OAAO,CAAC,GAAG,QAAQ,GAAG,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACtD,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAgB;IAC3C,MAAM,aAAa,GAClB,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,oBAAoB,CAAA;IACzE,OAAO,EAAE,aAAa,EAAE,qBAAqB,CAAC,aAAa,EAAE,QAAQ,CAAC,EAAE,CAAA;AACzE,CAAC"}
|
package/dist/lib/GotHooks.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { BeforeErrorHook, HandlerFunction, Method } from 'got';
|
|
2
2
|
import { CamundaPlatform8Configuration } from './Configuration';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Capturing useful async stack traces is challenging with got.
|
|
5
|
+
* See here: https://github.com/sindresorhus/got/blob/main/documentation/async-stack-traces.md
|
|
5
6
|
* This function stores the call point from the application of got requests.
|
|
6
7
|
* This enables users to see where the error originated from.
|
|
7
8
|
*/
|
package/dist/lib/GotHooks.js
CHANGED
|
@@ -3,11 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.GotRetryConfig = exports.makeBeforeRetryHandlerFor401TokenRetry = exports.gotBeforeErrorHook = exports.beforeCallHook = void 0;
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
5
|
const got_1 = require("got");
|
|
6
|
+
const AsyncTrace_1 = require("./AsyncTrace");
|
|
6
7
|
const CamundaSupportLogger_1 = require("./CamundaSupportLogger");
|
|
7
8
|
const GotErrors_1 = require("./GotErrors");
|
|
8
9
|
const supportLogger = CamundaSupportLogger_1.CamundaSupportLogger.getInstance();
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* Capturing useful async stack traces is challenging with got.
|
|
12
|
+
* See here: https://github.com/sindresorhus/got/blob/main/documentation/async-stack-traces.md
|
|
11
13
|
* This function stores the call point from the application of got requests.
|
|
12
14
|
* This enables users to see where the error originated from.
|
|
13
15
|
*/
|
|
@@ -15,7 +17,13 @@ const beforeCallHook = (options, next) => {
|
|
|
15
17
|
if (Object.isFrozen(options.context)) {
|
|
16
18
|
options.context = { ...options.context, hasRetried: false };
|
|
17
19
|
}
|
|
18
|
-
|
|
20
|
+
// If we stored the creation stack in the async context, we can use it to enhance the stack trace of the request.
|
|
21
|
+
const creationStack = AsyncTrace_1.asyncOperationContext.getStore()?.creationStack;
|
|
22
|
+
const obj = {};
|
|
23
|
+
Error.captureStackTrace(obj, exports.beforeCallHook);
|
|
24
|
+
options.context.stack = creationStack
|
|
25
|
+
? `${creationStack}\n${obj.stack}`
|
|
26
|
+
: obj.stack;
|
|
19
27
|
supportLogger.log(`Rest call:`);
|
|
20
28
|
supportLogger.log(options);
|
|
21
29
|
return next(options);
|
|
@@ -39,12 +47,12 @@ const gotBeforeErrorHook = (config) => (error) => {
|
|
|
39
47
|
detail = details ?? '';
|
|
40
48
|
}
|
|
41
49
|
catch (e) {
|
|
42
|
-
;
|
|
43
50
|
error.statusCode = 0;
|
|
44
51
|
}
|
|
45
52
|
}
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
error.source = error.options.context.stack?.split('\n') ?? [
|
|
54
|
+
'No enhanced stack trace available',
|
|
55
|
+
];
|
|
48
56
|
error.message += ` (request to ${request?.options.url
|
|
49
57
|
.href}). ${JSON.stringify(detail)}`;
|
|
50
58
|
/** Hinting for error messages. See https://github.com/camunda/camunda-8-js-sdk/issues/456 */
|
package/dist/lib/GotHooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GotHooks.js","sourceRoot":"","sources":["../../src/lib/GotHooks.ts"],"names":[],"mappings":";;;AAAA,uDAAuD;AACvD,
|
|
1
|
+
{"version":3,"file":"GotHooks.js","sourceRoot":"","sources":["../../src/lib/GotHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
interface
|
|
1
|
+
interface PollingOperationOptionsBase<T> {
|
|
2
2
|
operation: () => Promise<T>;
|
|
3
|
-
/** predicate to check if the result is valid */
|
|
4
|
-
predicate?: (result: T) => boolean;
|
|
5
3
|
/** how often to poll in ms - defaults to 1000 */
|
|
6
4
|
interval?: number;
|
|
7
5
|
/** when to timeout - defaults to 30000 */
|
|
8
6
|
timeout?: number;
|
|
9
7
|
}
|
|
8
|
+
interface PollingOperationOptionsWithPredicate<T> extends PollingOperationOptionsBase<T> {
|
|
9
|
+
/** predicate to check if the result is valid */
|
|
10
|
+
predicate: (result: T) => boolean;
|
|
11
|
+
}
|
|
12
|
+
interface PollingOperationOptionsWithoutPredicate<T extends {
|
|
13
|
+
items: Array<unknown>;
|
|
14
|
+
}> extends PollingOperationOptionsBase<T> {
|
|
15
|
+
/** predicate to check if the result is valid - optional when T has items array */
|
|
16
|
+
predicate?: (result: T) => boolean;
|
|
17
|
+
}
|
|
10
18
|
/**
|
|
11
19
|
* Poll for a result of an operation until it returns an awaited result or times out.
|
|
12
20
|
* This is useful for operations that may take some time to complete, such as waiting for a process instance to finish or data to propagate to query indices.
|
|
@@ -14,8 +22,33 @@ interface PollingOperationOptions<T> {
|
|
|
14
22
|
* @param options options for the polling operation
|
|
15
23
|
* @returns either the result of the operation or an error if the operation times out. If results were returned, but the predicate was not met, a PredicateError is thrown.
|
|
16
24
|
* Otherwise, the failure is propagated as an error.
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* // Wait for a process instance to appear in the search results
|
|
28
|
+
* const elementInstances = await PollingOperation({
|
|
29
|
+
* operation: () =>
|
|
30
|
+
* c8.searchElementInstances({
|
|
31
|
+
* sort: [{ field: 'processInstanceKey' }],
|
|
32
|
+
* filter: {
|
|
33
|
+
* processInstanceKey: processInstance.processInstanceKey,
|
|
34
|
+
* type: 'SERVICE_TASK',
|
|
35
|
+
* },
|
|
36
|
+
* }),
|
|
37
|
+
* interval: 500,
|
|
38
|
+
* timeout: 10000,
|
|
39
|
+
* })
|
|
40
|
+
*
|
|
41
|
+
* // If the operation does not return an object with an `items` array (ie: a v1 API), you need to provide a predicate function to check if the result is the awaited one.
|
|
42
|
+
* const process = await PollingOperation({
|
|
43
|
+
* operation: () => c.getProcessInstance(p.processInstanceKey),
|
|
44
|
+
* predicate: (res) => res.key === p.processInstanceKey,
|
|
45
|
+
* interval: 500,
|
|
46
|
+
* timeout: 15000,
|
|
47
|
+
* })
|
|
48
|
+
*```
|
|
17
49
|
*/
|
|
18
50
|
export declare function PollingOperation<T extends {
|
|
19
51
|
items: Array<unknown>;
|
|
20
|
-
}>(options:
|
|
52
|
+
}>(options: PollingOperationOptionsWithoutPredicate<T>): Promise<T>;
|
|
53
|
+
export declare function PollingOperation<T>(options: PollingOperationOptionsWithPredicate<T>): Promise<T>;
|
|
21
54
|
export {};
|
|
@@ -16,24 +16,18 @@ class PredicateError extends Error {
|
|
|
16
16
|
this.result = null;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
/**
|
|
20
|
-
* Poll for a result of an operation until it returns an awaited result or times out.
|
|
21
|
-
* This is useful for operations that may take some time to complete, such as waiting for a process instance to finish or data to propagate to query indices.
|
|
22
|
-
* Takes an optional prediicate function to determine if the result is the awaited one. By default, it checks if the result is not null or undefined and has at least one item in the `items` array.
|
|
23
|
-
* @param options options for the polling operation
|
|
24
|
-
* @returns either the result of the operation or an error if the operation times out. If results were returned, but the predicate was not met, a PredicateError is thrown.
|
|
25
|
-
* Otherwise, the failure is propagated as an error.
|
|
26
|
-
*/
|
|
27
19
|
function PollingOperation(options) {
|
|
28
20
|
const interval = options.interval || 1000;
|
|
29
21
|
const timeout = options.timeout || 30000;
|
|
30
22
|
const operation = options.operation;
|
|
23
|
+
// Use default predicate if no predicate provided, otherwise use provided predicate
|
|
31
24
|
const predicate = options.predicate || defaultPredicate;
|
|
32
25
|
return new Promise((resolve, reject) => {
|
|
33
26
|
const startTime = Date.now();
|
|
34
27
|
const poll = async () => {
|
|
35
28
|
try {
|
|
36
29
|
const result = await operation();
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
37
31
|
if (!predicate(result)) {
|
|
38
32
|
const error = new PredicateError('Predicate did not match');
|
|
39
33
|
error.result = result;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PollingOperation.js","sourceRoot":"","sources":["../../src/lib/PollingOperation.ts"],"names":[],"mappings":";;;AAAA,SAAS,gBAAgB,CACxB,MAAS;IAET,OAAO,CACN,MAAM,KAAK,IAAI;QACf,MAAM,KAAK,SAAS;QACpB,MAAM,CAAC,KAAK;QACZ,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CACvB,CAAA;AACF,CAAC;
|
|
1
|
+
{"version":3,"file":"PollingOperation.js","sourceRoot":"","sources":["../../src/lib/PollingOperation.ts"],"names":[],"mappings":";;;AAAA,SAAS,gBAAgB,CACxB,MAAS;IAET,OAAO,CACN,MAAM,KAAK,IAAI;QACf,MAAM,KAAK,SAAS;QACpB,MAAM,CAAC,KAAK;QACZ,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CACvB,CAAA;AACF,CAAC;AAsBD,MAAM,cAAkB,SAAQ,KAAK;IAEpC,YAAY,OAAe;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAA;QAC5B,iDAAiD;QACjD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,SAAS,CAAC,CAAA;QACrD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;IACnB,CAAC;CACD;AAuCD,SAAgB,gBAAgB,CAC/B,OAEyE;IAEzE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAA;IACzC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,KAAK,CAAA;IACxC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAA;IACnC,mFAAmF;IACnF,MAAM,SAAS,GACd,OAAO,CAAC,SAAS,IAAK,gBAA2C,CAAA;IAClE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QAE5B,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;YACvB,IAAI,CAAC;gBACJ,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAA;gBAChC,8DAA8D;gBAC9D,IAAI,CAAC,SAAS,CAAC,MAAa,CAAC,EAAE,CAAC;oBAC/B,MAAM,KAAK,GAAG,IAAI,cAAc,CAAI,yBAAyB,CAAC,CAAA;oBAC9D,KAAK,CAAC,MAAM,GAAG,MAAM,CAAA;oBACrB,MAAM,KAAK,CAAA;gBACZ,CAAC;gBACD,OAAO,CAAC,MAAM,CAAC,CAAA;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,OAAO,EAAE,CAAC;oBACtC,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;gBAC3B,CAAC;qBAAM,CAAC;oBACP,MAAM,CAAC,KAAK,CAAC,CAAA;gBACd,CAAC;YACF,CAAC;QACF,CAAC,CAAA;QAED,IAAI,EAAE,CAAA;IACP,CAAC,CAAC,CAAA;AACH,CAAC;AAnCD,4CAmCC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import TypedEmitter from 'typed-emitter';
|
|
2
|
+
export declare function QuerySubscription<T>(options: QuerySubscriptionConstructorWithPredicate<T>): _QuerySubscription<T>;
|
|
3
|
+
export declare function QuerySubscription<T>(options: QuerySubscriptionConstructorsWithoutPredicate<T & {
|
|
4
|
+
items: Array<unknown>;
|
|
5
|
+
}>): _QuerySubscription<T>;
|
|
6
|
+
type QuerySubscriptionReturnValue<T> = void | null | undefined | T | true | false;
|
|
7
|
+
type QuerySubscriptionPredicate<T> = (previous: T | undefined, current: T) => QuerySubscriptionReturnValue<T> | Promise<QuerySubscriptionReturnValue<T>>;
|
|
8
|
+
type QuerySubscriptionEvents<T> = {
|
|
9
|
+
update: (update: T) => void;
|
|
10
|
+
};
|
|
11
|
+
interface QuerySubscriptionConstructorBase<T> {
|
|
12
|
+
query: () => Promise<T>;
|
|
13
|
+
interval?: number;
|
|
14
|
+
}
|
|
15
|
+
interface QuerySubscriptionConstructorWithPredicate<T> extends QuerySubscriptionConstructorBase<T> {
|
|
16
|
+
predicate: QuerySubscriptionPredicate<T>;
|
|
17
|
+
}
|
|
18
|
+
interface QuerySubscriptionConstructorsWithoutPredicate<T extends {
|
|
19
|
+
items: Array<unknown>;
|
|
20
|
+
}> extends QuerySubscriptionConstructorBase<T> {
|
|
21
|
+
/** predicate to check if the result is valid - optional when T has items array */
|
|
22
|
+
predicate?: QuerySubscriptionPredicate<T>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* @description QuerySubscription is a utility class that allows you to subscribe to a query and receive updates when the query result changes.
|
|
26
|
+
* It is useful for polling operations where you want to receive updates when the result of a query changes, such as when a process instance is created or updated.
|
|
27
|
+
* It uses a predicate function to determine whether to emit an update event. When using the Orchestration Cluster API, the default predicate checks if the result has new items compared to the previous state.
|
|
28
|
+
* The predicate function receives the previous state and the current state of the query result and should return a value that indicates whether to emit an update.
|
|
29
|
+
* If the predicate returns `true`, the current state is emitted. If it returns an object, that object is emitted as the update.
|
|
30
|
+
* If it returns `false`, no update is emitted.
|
|
31
|
+
* @experimental This is an experimental feature and may change in the future.
|
|
32
|
+
* It is not yet stable and may have breaking changes in future releases. We're still working on it, and we welcome feedback.
|
|
33
|
+
* Please use it with caution and be prepared for potential changes.
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const query = () =>
|
|
37
|
+
* c8.searchProcessInstances({
|
|
38
|
+
* filter: {
|
|
39
|
+
* processDefinitionKey: key,
|
|
40
|
+
* state: 'ACTIVE',
|
|
41
|
+
* },
|
|
42
|
+
* sort: [{ field: 'startDate', order: 'ASC' }],
|
|
43
|
+
* })
|
|
44
|
+
*
|
|
45
|
+
* const subscription = QuerySubscription({
|
|
46
|
+
* query,
|
|
47
|
+
* predicate: (previous, current) => { // This is the default predicate, shown here for clarity
|
|
48
|
+
* const previousItems = (previous?.items ?? []) as Array<unknown>
|
|
49
|
+
* const currentItems = current.items.filter(
|
|
50
|
+
* (item) =>
|
|
51
|
+
* !previousItems.some((prevItem) => isDeepStrictEqual(prevItem, item))
|
|
52
|
+
* )
|
|
53
|
+
* if (currentItems.length > 0) {
|
|
54
|
+
* return {
|
|
55
|
+
* ...current,
|
|
56
|
+
* items: currentItems,
|
|
57
|
+
* page: { ...current.page, totalItems: currentItems.length },
|
|
58
|
+
* }
|
|
59
|
+
* }
|
|
60
|
+
* return false // No new items, do not emit
|
|
61
|
+
* },
|
|
62
|
+
* interval: 500,
|
|
63
|
+
* })
|
|
64
|
+
* subscription.on('update', (data) => {
|
|
65
|
+
* console.log('Received new processes:', data.items)
|
|
66
|
+
* })
|
|
67
|
+
* // After some time
|
|
68
|
+
* subscription.stop() // Stop polling when no longer needed, you can also call `start()` to resume polling
|
|
69
|
+
* subscription.cancel() // Or cancel the subscription to free resources
|
|
70
|
+
* ```
|
|
71
|
+
* @see {@link PollingOperation} for a simpler polling operation that does a single query.
|
|
72
|
+
*/
|
|
73
|
+
declare class _QuerySubscription<T> {
|
|
74
|
+
private _query;
|
|
75
|
+
/** The current state of the query, used to compare with the next result */
|
|
76
|
+
private _state;
|
|
77
|
+
private _predicate;
|
|
78
|
+
private _pollHandle;
|
|
79
|
+
/** We prevent further polling while we are calculating the predicate */
|
|
80
|
+
private _predicateLock;
|
|
81
|
+
/** We prevent further polling while we are processing the current poll */
|
|
82
|
+
private _pollLock;
|
|
83
|
+
private _interval;
|
|
84
|
+
private emitter;
|
|
85
|
+
on: <E extends 'update'>(event: E, listener: QuerySubscriptionEvents<T>[E]) => TypedEmitter<QuerySubscriptionEvents<T>>;
|
|
86
|
+
off: <E extends 'update'>(event: E, listener: QuerySubscriptionEvents<T>[E]) => TypedEmitter<QuerySubscriptionEvents<T>>;
|
|
87
|
+
once: <E extends 'update'>(event: E, listener: QuerySubscriptionEvents<T>[E]) => TypedEmitter<QuerySubscriptionEvents<T>>;
|
|
88
|
+
private emit;
|
|
89
|
+
removeListener: <E extends 'update'>(event: E, listener: QuerySubscriptionEvents<T>[E]) => TypedEmitter<QuerySubscriptionEvents<T>>;
|
|
90
|
+
removeAllListeners: <E extends 'update'>(event?: E | undefined) => TypedEmitter<QuerySubscriptionEvents<T>>;
|
|
91
|
+
prependListener: <E extends 'update'>(event: E, listener: QuerySubscriptionEvents<T>[E]) => TypedEmitter<QuerySubscriptionEvents<T>>;
|
|
92
|
+
prependOnceListener: <E extends 'update'>(event: E, listener: QuerySubscriptionEvents<T>[E]) => TypedEmitter<QuerySubscriptionEvents<T>>;
|
|
93
|
+
listeners: <E extends 'update'>(event: E) => QuerySubscriptionEvents<T>[E][];
|
|
94
|
+
private _poll?;
|
|
95
|
+
constructor(options: QuerySubscriptionConstructorWithPredicate<T> | QuerySubscriptionConstructorsWithoutPredicate<T & {
|
|
96
|
+
items: Array<unknown>;
|
|
97
|
+
}>);
|
|
98
|
+
pause(): void;
|
|
99
|
+
cancel(): void;
|
|
100
|
+
resume(): void;
|
|
101
|
+
poll(): Promise<void>;
|
|
102
|
+
}
|
|
103
|
+
export {};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.QuerySubscription = void 0;
|
|
7
|
+
const node_events_1 = __importDefault(require("node:events"));
|
|
8
|
+
const node_util_1 = require("node:util");
|
|
9
|
+
const AsyncTrace_1 = require("./AsyncTrace");
|
|
10
|
+
function QuerySubscription(options) {
|
|
11
|
+
return new _QuerySubscription(options);
|
|
12
|
+
}
|
|
13
|
+
exports.QuerySubscription = QuerySubscription;
|
|
14
|
+
/**
|
|
15
|
+
* @description The default predicate function for QuerySubscription.
|
|
16
|
+
* It checks if the current query result has new items compared to the previous state.
|
|
17
|
+
* If there are new items, it returns the current state with only the new items and updates the totalItems count.
|
|
18
|
+
* If there are no new items, it returns false, indicating that no update should be emitted.
|
|
19
|
+
* @param previous the previous state of the query result
|
|
20
|
+
* @param current the current state of the query result
|
|
21
|
+
* @returns - If there are new items, returns the current state with only the new items and updated totalItems count.
|
|
22
|
+
* - If there are no new items, returns false, indicating that no update should be emitted.
|
|
23
|
+
*/
|
|
24
|
+
function defaultPredicate(previous, current) {
|
|
25
|
+
const previousItems = (previous?.items ?? []);
|
|
26
|
+
const currentItems = current.items.filter((item) => !previousItems.some((prevItem) => (0, node_util_1.isDeepStrictEqual)(prevItem, item)));
|
|
27
|
+
if (currentItems.length > 0) {
|
|
28
|
+
return {
|
|
29
|
+
...current,
|
|
30
|
+
items: currentItems,
|
|
31
|
+
page: { ...current.page, totalItems: currentItems.length },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return false; // No new items, do not emit
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* @description QuerySubscription is a utility class that allows you to subscribe to a query and receive updates when the query result changes.
|
|
38
|
+
* It is useful for polling operations where you want to receive updates when the result of a query changes, such as when a process instance is created or updated.
|
|
39
|
+
* It uses a predicate function to determine whether to emit an update event. When using the Orchestration Cluster API, the default predicate checks if the result has new items compared to the previous state.
|
|
40
|
+
* The predicate function receives the previous state and the current state of the query result and should return a value that indicates whether to emit an update.
|
|
41
|
+
* If the predicate returns `true`, the current state is emitted. If it returns an object, that object is emitted as the update.
|
|
42
|
+
* If it returns `false`, no update is emitted.
|
|
43
|
+
* @experimental This is an experimental feature and may change in the future.
|
|
44
|
+
* It is not yet stable and may have breaking changes in future releases. We're still working on it, and we welcome feedback.
|
|
45
|
+
* Please use it with caution and be prepared for potential changes.
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const query = () =>
|
|
49
|
+
* c8.searchProcessInstances({
|
|
50
|
+
* filter: {
|
|
51
|
+
* processDefinitionKey: key,
|
|
52
|
+
* state: 'ACTIVE',
|
|
53
|
+
* },
|
|
54
|
+
* sort: [{ field: 'startDate', order: 'ASC' }],
|
|
55
|
+
* })
|
|
56
|
+
*
|
|
57
|
+
* const subscription = QuerySubscription({
|
|
58
|
+
* query,
|
|
59
|
+
* predicate: (previous, current) => { // This is the default predicate, shown here for clarity
|
|
60
|
+
* const previousItems = (previous?.items ?? []) as Array<unknown>
|
|
61
|
+
* const currentItems = current.items.filter(
|
|
62
|
+
* (item) =>
|
|
63
|
+
* !previousItems.some((prevItem) => isDeepStrictEqual(prevItem, item))
|
|
64
|
+
* )
|
|
65
|
+
* if (currentItems.length > 0) {
|
|
66
|
+
* return {
|
|
67
|
+
* ...current,
|
|
68
|
+
* items: currentItems,
|
|
69
|
+
* page: { ...current.page, totalItems: currentItems.length },
|
|
70
|
+
* }
|
|
71
|
+
* }
|
|
72
|
+
* return false // No new items, do not emit
|
|
73
|
+
* },
|
|
74
|
+
* interval: 500,
|
|
75
|
+
* })
|
|
76
|
+
* subscription.on('update', (data) => {
|
|
77
|
+
* console.log('Received new processes:', data.items)
|
|
78
|
+
* })
|
|
79
|
+
* // After some time
|
|
80
|
+
* subscription.stop() // Stop polling when no longer needed, you can also call `start()` to resume polling
|
|
81
|
+
* subscription.cancel() // Or cancel the subscription to free resources
|
|
82
|
+
* ```
|
|
83
|
+
* @see {@link PollingOperation} for a simpler polling operation that does a single query.
|
|
84
|
+
*/
|
|
85
|
+
class _QuerySubscription {
|
|
86
|
+
constructor(options) {
|
|
87
|
+
/** The current state of the query, used to compare with the next result */
|
|
88
|
+
this._state = undefined;
|
|
89
|
+
this._pollHandle = null;
|
|
90
|
+
/** We prevent further polling while we are calculating the predicate */
|
|
91
|
+
this._predicateLock = false;
|
|
92
|
+
/** We prevent further polling while we are processing the current poll */
|
|
93
|
+
this._pollLock = false;
|
|
94
|
+
this._query = options.query;
|
|
95
|
+
if ('predicate' in options && options.predicate) {
|
|
96
|
+
this._predicate = options.predicate;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Use type assertion to handle the default predicate
|
|
100
|
+
this._predicate = defaultPredicate;
|
|
101
|
+
}
|
|
102
|
+
this._interval = options.interval || 1000;
|
|
103
|
+
this.resume();
|
|
104
|
+
this.emitter = new node_events_1.default();
|
|
105
|
+
// Delegate all EventEmitter methods to the internal emitter
|
|
106
|
+
this.on = this.emitter.on.bind(this.emitter);
|
|
107
|
+
this.off = this.emitter.off.bind(this.emitter);
|
|
108
|
+
this.once = this.emitter.once.bind(this.emitter);
|
|
109
|
+
this.emit = this.emitter.emit.bind(this.emitter);
|
|
110
|
+
this.removeListener = this.emitter.removeListener.bind(this.emitter);
|
|
111
|
+
this.removeAllListeners = this.emitter.removeAllListeners.bind(this.emitter);
|
|
112
|
+
this.prependListener = this.emitter.prependListener.bind(this.emitter);
|
|
113
|
+
this.prependOnceListener = this.emitter.prependOnceListener.bind(this.emitter);
|
|
114
|
+
this.listeners = this.emitter.listeners.bind(this.emitter);
|
|
115
|
+
}
|
|
116
|
+
pause() {
|
|
117
|
+
if (!this._pollHandle) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
clearInterval(this._pollHandle);
|
|
121
|
+
this._pollHandle = null;
|
|
122
|
+
this._pollLock = false;
|
|
123
|
+
}
|
|
124
|
+
cancel() {
|
|
125
|
+
this.pause();
|
|
126
|
+
this._state = undefined;
|
|
127
|
+
this._poll = undefined;
|
|
128
|
+
this._predicateLock = false;
|
|
129
|
+
this._pollLock = false;
|
|
130
|
+
this.removeAllListeners();
|
|
131
|
+
}
|
|
132
|
+
resume() {
|
|
133
|
+
if (this._pollHandle) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
this._pollHandle = setInterval(() => (0, AsyncTrace_1.runWithAsyncErrorContext)(this.poll.bind(this), 'QuerySubscription'), this._interval);
|
|
137
|
+
}
|
|
138
|
+
async poll() {
|
|
139
|
+
if (this._pollLock ||
|
|
140
|
+
this._predicateLock ||
|
|
141
|
+
this.listeners('update').length === 0) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
this._pollLock = true;
|
|
145
|
+
try {
|
|
146
|
+
this._poll = this._query();
|
|
147
|
+
const current = await this._poll;
|
|
148
|
+
if (this._state) {
|
|
149
|
+
// If we have a previous state, check if it is the same as the current one
|
|
150
|
+
if ((0, node_util_1.isDeepStrictEqual)(this._state, current)) {
|
|
151
|
+
// If the state is the same, we don't need to emit an update
|
|
152
|
+
this._pollLock = false;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
this._predicateLock = true;
|
|
157
|
+
const diff = await this._predicate(this._state, current);
|
|
158
|
+
this._state = current;
|
|
159
|
+
if (diff) {
|
|
160
|
+
if (diff === true) {
|
|
161
|
+
this.emit('update', current);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
this.emit('update', diff);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
;
|
|
170
|
+
error.message = `QuerySubscription: ${error.message}`;
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
this._predicateLock = false;
|
|
175
|
+
this._pollLock = false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=QuerySubscription.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuerySubscription.js","sourceRoot":"","sources":["../../src/lib/QuerySubscription.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camunda8/sdk",
|
|
3
|
-
"version": "8.7.
|
|
3
|
+
"version": "8.7.17",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
"sm:stop8.8": "docker compose --env-file docker/8.8/.env -f docker/8.8/docker-compose-multitenancy.yaml down",
|
|
16
16
|
"sm:start8.8": "docker compose --env-file docker/8.8/.env -f docker/8.8/docker-compose-multitenancy.yaml up -d && docker/suppress-es-deprecation.sh",
|
|
17
17
|
"test": "cross-env CAMUNDA_UNIT_TEST=true jest '\\.unit\\.' -u --detectOpenHandles --runInBand --testPathIgnorePatterns integration --testPathIgnorePatterns local-integration --testPathIgnorePatterns disconnection --testPathIgnorePatterns multitenancy --testPathIgnorePatterns __tests__/config",
|
|
18
|
-
"test:integration": "jest --
|
|
18
|
+
"test:integration": "jest --testPathIgnorePatterns disconnection --testPathIgnorePatterns '\\.unit\\.' --testPathIgnorePatterns __tests__/config --testPathIgnorePatterns multitenancy --testPathIgnorePatterns __tests__/8.8 --testPathIgnorePatterns 8.7-sm-only --detectOpenHandles --verbose true -u",
|
|
19
19
|
"test:integration8.8": "jest --runInBand --testPathIgnorePatterns admin --testPathIgnorePatterns disconnection --testPathIgnorePatterns '\\.unit\\.' --testPathIgnorePatterns __tests__/config --testPathIgnorePatterns multitenancy --testPathIgnorePatterns 8.7-sm-only --detectOpenHandles --verbose true -u",
|
|
20
|
-
"test:multitenancy": "jest --
|
|
21
|
-
"test:multitenancy8.8": "jest --
|
|
20
|
+
"test:multitenancy": "jest --testPathIgnorePatterns disconnection --testPathIgnorePatterns admin --testPathIgnorePatterns '\\.unit\\.' --testPathIgnorePatterns __tests__/config --testPathIgnorePatterns __tests__/8.8 - --detectOpenHandles --verbose true -u",
|
|
21
|
+
"test:multitenancy8.8": "jest --testPathIgnorePatterns disconnection --testPathIgnorePatterns admin --testPathIgnorePatterns '\\.unit\\.' --testPathIgnorePatterns __tests__/config - --detectOpenHandles --verbose true -u",
|
|
22
22
|
"test:local": "jest --runInBand --verbose true --detectOpenHandles local-integration -u",
|
|
23
23
|
"test:local-integration": "jest --runInBand --detectOpenHandles --verbose --testPathIgnorePatterns disconnection --testPathIgnorePatterns '\\.unit\\.' --testPathIgnorePatterns admin --testPathIgnorePatterns multitenancy --testPathIgnorePatterns __tests__/8.8 --testPathIgnorePatterns __tests__/config -u",
|
|
24
24
|
"test:docker": "jest --runInBand --testPathIgnorePatterns disconnection --testPathIgnorePatterns __tests__/config local-integration --detectOpenHandles --verbose true",
|