@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 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
- CAMUNDA_TOKEN_CACHE_DIR: '/tmp/cache',
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
- 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
- }
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
- 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,
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
- logger,
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;AAiD/B,sBAAK;AAhDN,6BAA+B;AAkD9B,yFAlDQ,aAAQ,OAkDR;AAjDT,kEAA8D;AAkD7D,kGAlDQ,qCAAiB,OAkDR;AAjDlB,+BAQc;AACd,qEAAiE;AACjE,mDAAoC;AAwCnC,0BAAO;AAvCR,8CAA+B;AAoC9B,oBAAI;AAnCL,mDAAoC;AAuCnC,0BAAO;AAtCR,qDAAsC;AAuCrC,4BAAQ;AAtCT,qDAAsC;AAuCrC,4BAAQ;AAtCT,+CAAgC;AAuC/B,sBAAK;AArCN,2DAAyD;AAAhD,oHAAA,gBAAgB,OAAA;AAIzB,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"}
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"}
@@ -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
  */
@@ -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
- Error.captureStackTrace(options.context);
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
- error.source = error.options.context.stack.split('\n');
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 */
@@ -1 +1 @@
1
- {"version":3,"file":"GotHooks.js","sourceRoot":"","sources":["../../src/lib/GotHooks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GotHooks.js","sourceRoot":"","sources":["../../src/lib/GotHooks.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,20 @@
1
- interface PollingOperationOptions<T> {
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: PollingOperationOptions<T>): Promise<T>;
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;AAYD,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;AACD;;;;;;;GAOG;AACH,SAAgB,gBAAgB,CAC/B,OAAmC;IAEnC,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,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,gBAAgB,CAAA;IACvD,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,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;oBACxB,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;AA9BD,4CA8BC"}
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.16",
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 --runInBand --testPathIgnorePatterns disconnection --testPathIgnorePatterns '\\.unit\\.' --testPathIgnorePatterns __tests__/config --testPathIgnorePatterns multitenancy --testPathIgnorePatterns __tests__/8.8 --testPathIgnorePatterns 8.7-sm-only --detectOpenHandles --verbose true -u",
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 --runInBand --testPathIgnorePatterns disconnection --testPathIgnorePatterns admin --testPathIgnorePatterns '\\.unit\\.' --testPathIgnorePatterns __tests__/config --testPathIgnorePatterns __tests__/8.8 - --detectOpenHandles --verbose true -u",
21
- "test:multitenancy8.8": "jest --runInBand --testPathIgnorePatterns disconnection --testPathIgnorePatterns admin --testPathIgnorePatterns '\\.unit\\.' --testPathIgnorePatterns __tests__/config - --detectOpenHandles --verbose true -u",
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",