@hotmeshio/hotmesh 0.14.2 → 0.14.4

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 (32) hide show
  1. package/build/package.json +5 -3
  2. package/build/services/durable/worker.js +4 -0
  3. package/build/services/engine/init.js +1 -1
  4. package/build/services/engine/schema.js +5 -1
  5. package/build/services/mapper/index.d.ts +57 -2
  6. package/build/services/mapper/index.js +57 -2
  7. package/build/services/pipe/index.d.ts +444 -10
  8. package/build/services/pipe/index.js +444 -10
  9. package/build/services/quorum/index.js +1 -1
  10. package/build/services/router/consumption/index.js +20 -2
  11. package/build/services/router/error-handling/index.js +1 -1
  12. package/build/services/store/factory.d.ts +1 -1
  13. package/build/services/store/factory.js +2 -2
  14. package/build/services/store/index.d.ts +1 -1
  15. package/build/services/store/providers/postgres/kvsql.d.ts +11 -1
  16. package/build/services/store/providers/postgres/kvsql.js +22 -12
  17. package/build/services/store/providers/postgres/kvtables.js +39 -6
  18. package/build/services/store/providers/postgres/kvtypes/hash/basic.js +6 -6
  19. package/build/services/store/providers/postgres/kvtypes/hash/scan.js +2 -1
  20. package/build/services/store/providers/postgres/kvtypes/list.js +7 -6
  21. package/build/services/store/providers/postgres/kvtypes/string.js +3 -3
  22. package/build/services/store/providers/postgres/kvtypes/zset.js +7 -7
  23. package/build/services/store/providers/postgres/postgres.d.ts +3 -2
  24. package/build/services/store/providers/postgres/postgres.js +55 -55
  25. package/build/services/store/providers/postgres/time-notify.js +18 -25
  26. package/build/services/store/providers/store-initializable.d.ts +1 -1
  27. package/build/services/stream/registry.d.ts +1 -0
  28. package/build/services/stream/registry.js +12 -8
  29. package/build/services/worker/index.js +3 -1
  30. package/build/types/hotmesh.d.ts +8 -0
  31. package/package.json +5 -3
  32. package/vitest.config.mts +1 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.14.2",
3
+ "version": "0.14.4",
4
4
  "description": "Durable Workflow",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -14,8 +14,8 @@
14
14
  "obfuscate": "ts-node scripts/obfuscate.ts",
15
15
  "clean-build": "npm run clean && npm run build",
16
16
  "clean-build-obfuscate": "npm run clean-build && npm run obfuscate",
17
- "docs": "typedoc",
18
- "docs:clean": "rimraf ./docs/hotmesh && typedoc",
17
+ "docs": "typedoc && cp -R docs/hotmesh/* docs/ && rm -rf docs/hotmesh",
18
+ "docs:clean": "rimraf ./docs/hotmesh && typedoc && cp -R docs/hotmesh/* docs/ && rm -rf docs/hotmesh",
19
19
  "lint": "eslint . --ext .ts",
20
20
  "lint:fix": "eslint . --fix --ext .ts",
21
21
  "start": "ts-node src/index.ts",
@@ -47,6 +47,7 @@
47
47
  "test:durable:retrypolicy": "vitest run tests/durable/retry-policy",
48
48
  "test:durable:sleep": "vitest run tests/durable/sleep/postgres.test.ts",
49
49
  "test:durable:signal": "vitest run tests/durable/signal/postgres.test.ts",
50
+ "test:durable:readonly": "docker compose --profile readonly up -d --build && docker compose exec hotmesh-readonly npx vitest run --config tests/durable/readonly/vitest.config.mts",
50
51
  "test:durable:unknown": "vitest run tests/durable/unknown/postgres.test.ts",
51
52
  "test:durable:exporter": "HMSH_LOGLEVEL=info vitest run tests/durable/exporter",
52
53
  "test:durable:exporter:debug": "EXPORT_DEBUG=1 HMSH_LOGLEVEL=error vitest run tests/durable/basic/postgres.test.ts",
@@ -85,6 +86,7 @@
85
86
  "test:unit": "vitest run tests/unit"
86
87
  },
87
88
  "keywords": [
89
+ "Invisible Infrastructure",
88
90
  "Headless Orchestration",
89
91
  "Durable Workflows",
90
92
  "Data in Motion",
@@ -516,10 +516,12 @@ class WorkerService {
516
516
  if (WorkerService.instances.has(targetTopic)) {
517
517
  return await WorkerService.instances.get(targetTopic);
518
518
  }
519
+ const readonly = providerConfig.readonly ?? false;
519
520
  const workerEntry = {
520
521
  topic: activityTopic,
521
522
  connection: providerConfig,
522
523
  callback: this.wrapActivityFunctions().bind(this),
524
+ readonly,
523
525
  };
524
526
  if (config.workerCredentials) {
525
527
  workerEntry.workerCredentials = config.workerCredentials;
@@ -644,10 +646,12 @@ class WorkerService {
644
646
  const targetNamespace = config?.namespace ?? factory_1.APP_ID;
645
647
  const optionsHash = WorkerService.hashOptions(config?.connection);
646
648
  const targetTopic = `${optionsHash}.${targetNamespace}.${workflowTopic}`;
649
+ const readonly = providerConfig.readonly ?? false;
647
650
  const workerEntry = {
648
651
  topic: taskQueue,
649
652
  workflowName: workflowFunctionName,
650
653
  connection: providerConfig,
654
+ readonly,
651
655
  callback: this.wrapWorkflowFunction(workflowFunction, workflowTopic, workflowFunctionName, config).bind(this),
652
656
  };
653
657
  if (config.workerCredentials) {
@@ -30,7 +30,7 @@ async function initSearchChannel(instance, search, store) {
30
30
  }
31
31
  exports.initSearchChannel = initSearchChannel;
32
32
  async function initStoreChannel(instance, store) {
33
- instance.store = await factory_2.StoreServiceFactory.init(store, instance.namespace, instance.appId, instance.logger);
33
+ instance.store = await factory_2.StoreServiceFactory.init(store, instance.namespace, instance.appId, instance.logger, instance.guid, 'engine');
34
34
  }
35
35
  exports.initStoreChannel = initStoreChannel;
36
36
  async function initSubChannel(instance, sub, store) {
@@ -16,7 +16,11 @@ const activities_1 = __importDefault(require("../activities"));
16
16
  async function initActivity(instance, topic, data = {}, context) {
17
17
  const [activityId, schema] = await getSchema(instance, topic);
18
18
  if (!schema) {
19
- throw new Error(`Activity schema not found for "${activityId}" (topic: ${topic}) in app ${instance.appId}`);
19
+ const err = new Error(`Activity schema not found for "${activityId}" (topic: ${topic}) in app ${instance.appId}. ` +
20
+ `This is typically caused by a worker activity whose topic collides with the graph subscribes topic. ` +
21
+ `Redeploy with a distinct worker topic.`);
22
+ err.code = 598;
23
+ throw err;
20
24
  }
21
25
  const ActivityHandler = activities_1.default[schema.type];
22
26
  if (ActivityHandler) {
@@ -1,10 +1,42 @@
1
1
  import { JobState } from '../../types/job';
2
2
  import { TransitionRule } from '../../types/transition';
3
3
  import { StreamCode } from '../../types';
4
+ /**
5
+ * Evaluates and transforms data-mapping rules against live job state.
6
+ *
7
+ * @remarks
8
+ * MapperService is the bridge between a workflow's declarative mapping
9
+ * rules (including `@pipe` expressions) and the runtime job data. It
10
+ * recursively walks a rule tree, delegating leaf-level resolution to
11
+ * the {@link Pipe} engine. Static helpers such as {@link evaluate}
12
+ * also power transition-condition checks during workflow execution.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const mapper = new MapperService(
17
+ * { greeting: '{data.name}', score: { '@pipe': [['{data.x}', '{data.y}'], ['{@math.add}']] } },
18
+ * jobState,
19
+ * );
20
+ * const result = mapper.mapRules();
21
+ * // => { greeting: 'Alice', score: 7 }
22
+ * ```
23
+ */
4
24
  declare class MapperService {
5
25
  private rules;
6
26
  private data;
27
+ /**
28
+ * @param rules - The mapping rule tree to evaluate. May contain
29
+ * literal values, `{data.*}` references, or nested `@pipe` objects.
30
+ * @param data - The current {@link JobState} used to resolve references.
31
+ */
7
32
  constructor(rules: Record<string, unknown>, data: JobState);
33
+ /**
34
+ * Recursively resolves every rule in the tree against the current job
35
+ * state and returns a fully-evaluated copy.
36
+ *
37
+ * @returns A plain object mirroring the rule structure with all
38
+ * expressions replaced by their resolved values.
39
+ */
8
40
  mapRules(): Record<string, unknown>;
9
41
  private traverseRules;
10
42
  /**
@@ -20,8 +52,31 @@ declare class MapperService {
20
52
  */
21
53
  private resolve;
22
54
  /**
23
- * Evaluates a transition rule against the current job state and incoming Stream message
24
- * to determine which (if any) transition should be taken.
55
+ * Evaluates a transition rule against the current job state and an
56
+ * incoming Stream message code to decide whether the transition fires.
57
+ *
58
+ * @remarks
59
+ * Supports both simple boolean rules (`true` / `false`) and compound
60
+ * match rules with optional AND / OR gating. When the rule includes
61
+ * a `match` array, each entry's `actual` expression is resolved via
62
+ * {@link Pipe.resolve} and compared to the `expected` value.
63
+ *
64
+ * @param transitionRule - A boolean shorthand or a {@link TransitionRule}
65
+ * containing `code`, optional `gate`, and optional `match` conditions.
66
+ * @param context - The current {@link JobState} used to resolve
67
+ * `actual` expressions inside match entries.
68
+ * @param code - The {@link StreamCode} returned by the preceding
69
+ * activity (e.g. `200`).
70
+ * @returns `true` if the transition should be taken, `false` otherwise.
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * const shouldTransition = MapperService.evaluate(
75
+ * { code: 200, match: [{ expected: true, actual: '{data.isReady}' }] },
76
+ * jobState,
77
+ * 200,
78
+ * );
79
+ * ```
25
80
  */
26
81
  static evaluate(transitionRule: TransitionRule | boolean, context: JobState, code: StreamCode): boolean;
27
82
  }
@@ -2,11 +2,43 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MapperService = void 0;
4
4
  const pipe_1 = require("../pipe");
5
+ /**
6
+ * Evaluates and transforms data-mapping rules against live job state.
7
+ *
8
+ * @remarks
9
+ * MapperService is the bridge between a workflow's declarative mapping
10
+ * rules (including `@pipe` expressions) and the runtime job data. It
11
+ * recursively walks a rule tree, delegating leaf-level resolution to
12
+ * the {@link Pipe} engine. Static helpers such as {@link evaluate}
13
+ * also power transition-condition checks during workflow execution.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const mapper = new MapperService(
18
+ * { greeting: '{data.name}', score: { '@pipe': [['{data.x}', '{data.y}'], ['{@math.add}']] } },
19
+ * jobState,
20
+ * );
21
+ * const result = mapper.mapRules();
22
+ * // => { greeting: 'Alice', score: 7 }
23
+ * ```
24
+ */
5
25
  class MapperService {
26
+ /**
27
+ * @param rules - The mapping rule tree to evaluate. May contain
28
+ * literal values, `{data.*}` references, or nested `@pipe` objects.
29
+ * @param data - The current {@link JobState} used to resolve references.
30
+ */
6
31
  constructor(rules, data) {
7
32
  this.rules = rules;
8
33
  this.data = data;
9
34
  }
35
+ /**
36
+ * Recursively resolves every rule in the tree against the current job
37
+ * state and returns a fully-evaluated copy.
38
+ *
39
+ * @returns A plain object mirroring the rule structure with all
40
+ * expressions replaced by their resolved values.
41
+ */
10
42
  mapRules() {
11
43
  return this.traverseRules(this.rules);
12
44
  }
@@ -46,8 +78,31 @@ class MapperService {
46
78
  return pipe.process();
47
79
  }
48
80
  /**
49
- * Evaluates a transition rule against the current job state and incoming Stream message
50
- * to determine which (if any) transition should be taken.
81
+ * Evaluates a transition rule against the current job state and an
82
+ * incoming Stream message code to decide whether the transition fires.
83
+ *
84
+ * @remarks
85
+ * Supports both simple boolean rules (`true` / `false`) and compound
86
+ * match rules with optional AND / OR gating. When the rule includes
87
+ * a `match` array, each entry's `actual` expression is resolved via
88
+ * {@link Pipe.resolve} and compared to the `expected` value.
89
+ *
90
+ * @param transitionRule - A boolean shorthand or a {@link TransitionRule}
91
+ * containing `code`, optional `gate`, and optional `match` conditions.
92
+ * @param context - The current {@link JobState} used to resolve
93
+ * `actual` expressions inside match entries.
94
+ * @param code - The {@link StreamCode} returned by the preceding
95
+ * activity (e.g. `200`).
96
+ * @returns `true` if the transition should be taken, `false` otherwise.
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * const shouldTransition = MapperService.evaluate(
101
+ * { code: 200, match: [{ expected: true, actual: '{data.isReady}' }] },
102
+ * jobState,
103
+ * 200,
104
+ * );
105
+ * ```
51
106
  */
52
107
  static evaluate(transitionRule, context, code) {
53
108
  if (typeof transitionRule === 'boolean') {