@hotmeshio/hotmesh 0.4.1 → 0.4.3

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 (38) hide show
  1. package/README.md +114 -51
  2. package/build/index.d.ts +2 -1
  3. package/build/index.js +3 -1
  4. package/build/package.json +3 -2
  5. package/build/services/activities/trigger.d.ts +1 -1
  6. package/build/services/activities/trigger.js +3 -3
  7. package/build/services/memflow/client.js +2 -1
  8. package/build/services/memflow/{context.d.ts → entity.d.ts} +34 -34
  9. package/build/services/memflow/{context.js → entity.js} +40 -40
  10. package/build/services/memflow/index.d.ts +2 -2
  11. package/build/services/memflow/index.js +2 -2
  12. package/build/services/memflow/workflow/common.d.ts +2 -2
  13. package/build/services/memflow/workflow/common.js +3 -3
  14. package/build/services/memflow/workflow/entityMethods.d.ts +14 -0
  15. package/build/services/memflow/workflow/{contextMethods.js → entityMethods.js} +11 -11
  16. package/build/services/memflow/workflow/index.d.ts +2 -2
  17. package/build/services/memflow/workflow/index.js +2 -2
  18. package/build/services/memflow/workflow/signal.d.ts +23 -1
  19. package/build/services/memflow/workflow/signal.js +23 -1
  20. package/build/services/memflow/workflow/sleepFor.d.ts +17 -1
  21. package/build/services/memflow/workflow/sleepFor.js +17 -1
  22. package/build/services/memflow/workflow/waitFor.d.ts +22 -1
  23. package/build/services/memflow/workflow/waitFor.js +22 -1
  24. package/build/services/store/index.d.ts +1 -1
  25. package/build/services/store/providers/postgres/kvsql.d.ts +1 -1
  26. package/build/services/store/providers/postgres/kvtypes/hash.d.ts +1 -1
  27. package/build/services/store/providers/postgres/kvtypes/hash.js +8 -8
  28. package/build/services/store/providers/postgres/postgres.d.ts +34 -1
  29. package/build/services/store/providers/postgres/postgres.js +211 -2
  30. package/build/services/store/providers/redis/_base.d.ts +1 -1
  31. package/build/services/store/providers/redis/_base.js +1 -1
  32. package/build/services/task/index.d.ts +12 -0
  33. package/build/services/task/index.js +47 -0
  34. package/build/types/job.d.ts +7 -0
  35. package/build/types/provider.d.ts +1 -0
  36. package/index.ts +2 -2
  37. package/package.json +4 -3
  38. package/build/services/memflow/workflow/contextMethods.d.ts +0 -14
@@ -18,6 +18,7 @@ import { KVTables } from './kvtables';
18
18
  declare class PostgresStoreService extends StoreService<ProviderClient, ProviderTransaction> {
19
19
  pgClient: PostgresClientType;
20
20
  kvTables: ReturnType<typeof KVTables>;
21
+ isScout: boolean;
21
22
  transact(): ProviderTransaction;
22
23
  constructor(storeClient: ProviderClient);
23
24
  init(namespace: string, appId: string, logger: ILogger): Promise<HotMeshApps>;
@@ -82,7 +83,7 @@ declare class PostgresStoreService extends StoreService<ProviderClient, Provider
82
83
  * `purposeful re-entry`.
83
84
  */
84
85
  collateSynthetic(jobId: string, guid: string, amount: number, transaction?: ProviderTransaction): Promise<number>;
85
- setStateNX(jobId: string, appId: string, status?: number): Promise<boolean>;
86
+ setStateNX(jobId: string, appId: string, status?: number, entity?: string): Promise<boolean>;
86
87
  getSchema(activityId: string, appVersion: AppVID): Promise<ActivityType>;
87
88
  getSchemas(appVersion: AppVID): Promise<Record<string, ActivityType>>;
88
89
  setSchemas(schemas: Record<string, ActivityType>, appVersion: AppVID): Promise<any>;
@@ -141,5 +142,37 @@ declare class PostgresStoreService extends StoreService<ProviderClient, Provider
141
142
  setThrottleRate(options: ThrottleOptions): Promise<void>;
142
143
  getThrottleRates(): Promise<StringStringType>;
143
144
  getThrottleRate(topic: string): Promise<number>;
145
+ /**
146
+ * Deploy time-aware notification triggers and functions
147
+ */
148
+ private deployTimeNotificationTriggers;
149
+ /**
150
+ * Enhanced time scout that uses LISTEN/NOTIFY to reduce polling
151
+ */
152
+ startTimeScoutWithNotifications(timeEventCallback: (jobId: string, gId: string, activityId: string, type: WorkListTaskType) => Promise<void>): Promise<void>;
153
+ /**
154
+ * Handle time notifications from PostgreSQL
155
+ */
156
+ private handleTimeNotification;
157
+ /**
158
+ * Process time hooks that are ready to be awakened
159
+ */
160
+ private processReadyTimeHooks;
161
+ /**
162
+ * Enhanced time scout process that uses notifications
163
+ */
164
+ private processTimeHooksWithNotifications;
165
+ /**
166
+ * Get the next awakening time from the database
167
+ */
168
+ private getNextAwakeningTime;
169
+ /**
170
+ * Update the time scout's sleep timing based on schedule changes
171
+ */
172
+ private updateTimeScoutSleep;
173
+ /**
174
+ * Enhanced shouldScout that can handle notifications
175
+ */
176
+ shouldScout(): Promise<boolean>;
144
177
  }
145
178
  export { PostgresStoreService };
@@ -39,6 +39,7 @@ class PostgresStoreService extends __1.StoreService {
39
39
  }
40
40
  constructor(storeClient) {
41
41
  super(storeClient);
42
+ this.isScout = false;
42
43
  //Instead of directly referencing the 'pg' package and methods like 'query',
43
44
  // the PostgresStore wraps the 'pg' client in a class that implements
44
45
  // the Redis client interface. This allows the same methods to be called
@@ -58,6 +59,8 @@ class PostgresStoreService extends __1.StoreService {
58
59
  this.logger = logger;
59
60
  //confirm db tables exist
60
61
  await this.kvTables.deploy(appId);
62
+ // Deploy time notification triggers
63
+ await this.deployTimeNotificationTriggers(appId);
61
64
  //note: getSettings will contact db to confirm r/w access
62
65
  const settings = await this.getSettings(true);
63
66
  this.cache = new cache_1.Cache(appId, settings);
@@ -564,9 +567,10 @@ class PostgresStoreService extends __1.StoreService {
564
567
  });
565
568
  return await this.kvsql(transaction).hincrbyfloat(jobKey, guid, amount);
566
569
  }
567
- async setStateNX(jobId, appId, status) {
570
+ async setStateNX(jobId, appId, status, entity) {
568
571
  const hashKey = this.mintKey(key_1.KeyType.JOB_STATE, { appId, jobId });
569
- const result = await this.kvsql().hsetnx(hashKey, ':', status?.toString() ?? '1');
572
+ const result = await this.kvsql().hsetnx(hashKey, ':', status?.toString() ?? '1', undefined, //multi
573
+ entity);
570
574
  return this.isSuccessful(result);
571
575
  }
572
576
  async getSchema(activityId, appVersion) {
@@ -1032,5 +1036,210 @@ class PostgresStoreService extends __1.StoreService {
1032
1036
  }
1033
1037
  return resolveRate(response, topic);
1034
1038
  }
1039
+ /**
1040
+ * Deploy time-aware notification triggers and functions
1041
+ */
1042
+ async deployTimeNotificationTriggers(appId) {
1043
+ const schemaName = this.kvsql().safeName(appId);
1044
+ const client = this.pgClient;
1045
+ try {
1046
+ // Read the SQL template and replace schema placeholder
1047
+ const fs = await Promise.resolve().then(() => __importStar(require('fs')));
1048
+ const path = await Promise.resolve().then(() => __importStar(require('path')));
1049
+ const sqlTemplate = fs.readFileSync(path.join(__dirname, 'time-notify.sql'), 'utf8');
1050
+ const sql = sqlTemplate.replace(/{schema}/g, schemaName);
1051
+ // Execute the entire SQL as one statement (functions contain $$ blocks with semicolons)
1052
+ await client.query(sql);
1053
+ this.logger.info('postgres-time-notifications-deployed', {
1054
+ appId,
1055
+ schemaName,
1056
+ message: 'Time-aware notifications ENABLED - using LISTEN/NOTIFY instead of polling'
1057
+ });
1058
+ }
1059
+ catch (error) {
1060
+ this.logger.error('postgres-time-notifications-deploy-error', {
1061
+ appId,
1062
+ schemaName,
1063
+ error: error.message
1064
+ });
1065
+ // Don't throw - fall back to polling mode
1066
+ }
1067
+ }
1068
+ /**
1069
+ * Enhanced time scout that uses LISTEN/NOTIFY to reduce polling
1070
+ */
1071
+ async startTimeScoutWithNotifications(timeEventCallback) {
1072
+ const channelName = `time_hooks_${this.appId}`;
1073
+ try {
1074
+ // Set up LISTEN for time notifications
1075
+ await this.pgClient.query(`LISTEN "${channelName}"`);
1076
+ // Set up notification handler
1077
+ this.pgClient.on('notification', (notification) => {
1078
+ this.handleTimeNotification(notification, timeEventCallback);
1079
+ });
1080
+ this.logger.debug('postgres-time-scout-notifications-started', {
1081
+ appId: this.appId,
1082
+ channelName
1083
+ });
1084
+ // Start the enhanced time scout loop
1085
+ await this.processTimeHooksWithNotifications(timeEventCallback);
1086
+ }
1087
+ catch (error) {
1088
+ this.logger.error('postgres-time-scout-notifications-error', {
1089
+ appId: this.appId,
1090
+ error
1091
+ });
1092
+ // Fall back to regular polling mode
1093
+ throw error;
1094
+ }
1095
+ }
1096
+ /**
1097
+ * Handle time notifications from PostgreSQL
1098
+ */
1099
+ async handleTimeNotification(notification, timeEventCallback) {
1100
+ try {
1101
+ const payload = JSON.parse(notification.payload);
1102
+ const { type, app_id, next_awakening, ready_at } = payload;
1103
+ if (app_id !== this.appId) {
1104
+ return; // Not for this app
1105
+ }
1106
+ this.logger.debug('postgres-time-notification-received', {
1107
+ type,
1108
+ appId: app_id,
1109
+ nextAwakening: next_awakening,
1110
+ readyAt: ready_at
1111
+ });
1112
+ if (type === 'time_hooks_ready') {
1113
+ // Process any ready time hooks immediately
1114
+ await this.processReadyTimeHooks(timeEventCallback);
1115
+ }
1116
+ else if (type === 'time_schedule_updated') {
1117
+ // Update our sleep timing if we're the time scout
1118
+ await this.updateTimeScoutSleep(next_awakening);
1119
+ }
1120
+ }
1121
+ catch (error) {
1122
+ this.logger.error('postgres-time-notification-handle-error', {
1123
+ notification,
1124
+ error
1125
+ });
1126
+ }
1127
+ }
1128
+ /**
1129
+ * Process time hooks that are ready to be awakened
1130
+ */
1131
+ async processReadyTimeHooks(timeEventCallback) {
1132
+ let hasMoreTasks = true;
1133
+ while (hasMoreTasks) {
1134
+ const workListTask = await this.getNextTask();
1135
+ if (Array.isArray(workListTask)) {
1136
+ const [listKey, target, gId, activityId, type] = workListTask;
1137
+ if (type === 'child') {
1138
+ // Skip child tasks - they're handled by ancestors
1139
+ }
1140
+ else if (type === 'delist') {
1141
+ // Delist the signal key
1142
+ const key = this.mintKey(key_1.KeyType.SIGNALS, { appId: this.appId });
1143
+ await this.delistSignalKey(key, target);
1144
+ }
1145
+ else {
1146
+ // Process the task
1147
+ await timeEventCallback(target, gId, activityId, type);
1148
+ }
1149
+ }
1150
+ else if (workListTask === true) {
1151
+ // A worklist was emptied, continue processing
1152
+ continue;
1153
+ }
1154
+ else {
1155
+ // No more tasks ready
1156
+ hasMoreTasks = false;
1157
+ }
1158
+ }
1159
+ }
1160
+ /**
1161
+ * Enhanced time scout process that uses notifications
1162
+ */
1163
+ async processTimeHooksWithNotifications(timeEventCallback) {
1164
+ let currentSleepTimeout = null;
1165
+ // Function to calculate next sleep duration
1166
+ const calculateNextSleep = async () => {
1167
+ const nextAwakeningTime = await this.getNextAwakeningTime();
1168
+ if (nextAwakeningTime) {
1169
+ const sleepMs = nextAwakeningTime - Date.now();
1170
+ return Math.max(sleepMs, 0);
1171
+ }
1172
+ // Default sleep if no tasks scheduled
1173
+ return enums_1.HMSH_FIDELITY_SECONDS * 1000;
1174
+ };
1175
+ // Main loop
1176
+ while (true) {
1177
+ try {
1178
+ if (await this.shouldScout()) {
1179
+ // Process any ready tasks
1180
+ await this.processReadyTimeHooks(timeEventCallback);
1181
+ // Calculate next sleep
1182
+ const sleepMs = await calculateNextSleep();
1183
+ // Sleep with ability to be interrupted by notifications
1184
+ await new Promise((resolve) => {
1185
+ currentSleepTimeout = setTimeout(resolve, sleepMs);
1186
+ });
1187
+ }
1188
+ else {
1189
+ // Not the scout, sleep longer
1190
+ await (0, utils_1.sleepFor)(enums_1.HMSH_SCOUT_INTERVAL_SECONDS * 1000);
1191
+ }
1192
+ }
1193
+ catch (error) {
1194
+ this.logger.error('postgres-time-scout-loop-error', { error });
1195
+ await (0, utils_1.sleepFor)(1000);
1196
+ }
1197
+ }
1198
+ }
1199
+ /**
1200
+ * Get the next awakening time from the database
1201
+ */
1202
+ async getNextAwakeningTime() {
1203
+ const schemaName = this.kvsql().safeName(this.appId);
1204
+ const appKey = `${this.appId}:time_range`;
1205
+ try {
1206
+ const result = await this.pgClient.query(`SELECT ${schemaName}.get_next_awakening_time($1) as next_time`, [appKey]);
1207
+ if (result.rows[0]?.next_time) {
1208
+ return new Date(result.rows[0].next_time).getTime();
1209
+ }
1210
+ return null;
1211
+ }
1212
+ catch (error) {
1213
+ this.logger.error('postgres-get-next-awakening-error', { error });
1214
+ return null;
1215
+ }
1216
+ }
1217
+ /**
1218
+ * Update the time scout's sleep timing based on schedule changes
1219
+ */
1220
+ async updateTimeScoutSleep(nextAwakening) {
1221
+ // This could be used to interrupt current sleep and recalculate
1222
+ // For now, just log the schedule update
1223
+ this.logger.debug('postgres-time-schedule-updated', {
1224
+ nextAwakening,
1225
+ currentTime: Date.now()
1226
+ });
1227
+ }
1228
+ /**
1229
+ * Enhanced shouldScout that can handle notifications
1230
+ */
1231
+ async shouldScout() {
1232
+ const wasScout = this.isScout;
1233
+ const isScout = wasScout || (this.isScout = await this.reserveScoutRole('time'));
1234
+ if (isScout) {
1235
+ if (!wasScout) {
1236
+ setTimeout(() => {
1237
+ this.isScout = false;
1238
+ }, enums_1.HMSH_SCOUT_INTERVAL_SECONDS * 1000);
1239
+ }
1240
+ return true;
1241
+ }
1242
+ return false;
1243
+ }
1035
1244
  }
1036
1245
  exports.PostgresStoreService = PostgresStoreService;
@@ -75,7 +75,7 @@ declare abstract class RedisStoreBase<ClientProvider extends ProviderClient, Tra
75
75
  * `purposeful re-entry`.
76
76
  */
77
77
  collateSynthetic(jobId: string, guid: string, amount: number, transaction?: TransactionProvider): Promise<number>;
78
- setStateNX(jobId: string, appId: string, status?: number): Promise<boolean>;
78
+ setStateNX(jobId: string, appId: string, status?: number, entity?: string): Promise<boolean>;
79
79
  getSchema(activityId: string, appVersion: AppVID): Promise<ActivityType>;
80
80
  getSchemas(appVersion: AppVID): Promise<Record<string, ActivityType>>;
81
81
  setSchemas(schemas: Record<string, ActivityType>, appVersion: AppVID): Promise<any>;
@@ -539,7 +539,7 @@ class RedisStoreBase extends __1.StoreService {
539
539
  });
540
540
  return await (transaction || this.storeClient)[this.commands.hincrbyfloat](jobKey, guid, amount.toString());
541
541
  }
542
- async setStateNX(jobId, appId, status) {
542
+ async setStateNX(jobId, appId, status, entity) {
543
543
  const hashKey = this.mintKey(key_1.KeyType.JOB_STATE, { appId, jobId });
544
544
  const result = await this.storeClient[this.commands.hsetnx](hashKey, ':', status?.toString() ?? '1');
545
545
  return this.isSuccessful(result);
@@ -32,5 +32,17 @@ declare class TaskService {
32
32
  registerWebHook(topic: string, context: JobState, dad: string, expire: number, transaction?: ProviderTransaction): Promise<string>;
33
33
  processWebHookSignal(topic: string, data: Record<string, unknown>): Promise<[string, string, string, string] | undefined>;
34
34
  deleteWebHookSignal(topic: string, data: Record<string, unknown>): Promise<number>;
35
+ /**
36
+ * Enhanced processTimeHooks that uses notifications for PostgreSQL stores
37
+ */
38
+ processTimeHooksWithNotifications(timeEventCallback: (jobId: string, gId: string, activityId: string, type: WorkListTaskType) => Promise<void>): Promise<void>;
39
+ /**
40
+ * Check if this is a PostgreSQL store
41
+ */
42
+ private isPostgresStore;
43
+ /**
44
+ * Check if the store supports notifications
45
+ */
46
+ private supportsNotifications;
35
47
  }
36
48
  export { TaskService };
@@ -202,5 +202,52 @@ class TaskService {
202
202
  throw new Error('signaler.process:error: hook rule not found');
203
203
  }
204
204
  }
205
+ /**
206
+ * Enhanced processTimeHooks that uses notifications for PostgreSQL stores
207
+ */
208
+ async processTimeHooksWithNotifications(timeEventCallback) {
209
+ // Check if the store supports notifications
210
+ if (this.isPostgresStore() && this.supportsNotifications()) {
211
+ try {
212
+ this.logger.info('task-using-notification-mode', {
213
+ appId: this.store.appId,
214
+ message: 'Time scout using PostgreSQL LISTEN/NOTIFY mode for efficient task processing'
215
+ });
216
+ // Use the PostgreSQL store's notification-based approach
217
+ await this.store.startTimeScoutWithNotifications(timeEventCallback);
218
+ }
219
+ catch (error) {
220
+ this.logger.warn('task-notifications-fallback', {
221
+ appId: this.store.appId,
222
+ error: error.message,
223
+ fallbackTo: 'polling',
224
+ message: 'Notification mode failed - falling back to traditional polling'
225
+ });
226
+ // Fall back to regular polling
227
+ await this.processTimeHooks(timeEventCallback);
228
+ }
229
+ }
230
+ else {
231
+ this.logger.info('task-using-polling-mode', {
232
+ appId: this.store.appId,
233
+ storeType: this.store.constructor.name,
234
+ message: 'Time scout using traditional polling mode (notifications not available)'
235
+ });
236
+ // Use regular polling for non-PostgreSQL stores
237
+ await this.processTimeHooks(timeEventCallback);
238
+ }
239
+ }
240
+ /**
241
+ * Check if this is a PostgreSQL store
242
+ */
243
+ isPostgresStore() {
244
+ return this.store.constructor.name === 'PostgresStoreService';
245
+ }
246
+ /**
247
+ * Check if the store supports notifications
248
+ */
249
+ supportsNotifications() {
250
+ return typeof this.store.startTimeScoutWithNotifications === 'function';
251
+ }
205
252
  }
206
253
  exports.TaskService = TaskService;
@@ -95,6 +95,13 @@ type ExtensionType = {
95
95
  * will be added after the job is resumed if relevant.
96
96
  */
97
97
  pending?: number;
98
+ /**
99
+ * Entity to organize the job in the database by type.
100
+ * This is useful for querying and filtering jobs by type
101
+ * as the <your-app-id>.jobs table is organized by entity.
102
+ * Add partial indexes to the entity column to speed up queries.
103
+ */
104
+ entity?: string;
98
105
  };
99
106
  /**
100
107
  * job_status semaphore
@@ -84,6 +84,7 @@ export interface SetOptions {
84
84
  export interface HSetOptions {
85
85
  nx?: boolean;
86
86
  ex?: number;
87
+ entity?: string;
87
88
  }
88
89
  export interface ZAddOptions {
89
90
  nx?: boolean;
package/index.ts CHANGED
@@ -5,6 +5,7 @@ import { MemFlow } from './services/memflow';
5
5
  import { ClientService as Client } from './services/memflow/client';
6
6
  import { ConnectionService as Connection } from './services/memflow/connection';
7
7
  import { Search } from './services/memflow/search';
8
+ import { Entity } from './services/memflow/entity';
8
9
  import { WorkerService as Worker } from './services/memflow/worker';
9
10
  import { WorkflowService as workflow } from './services/memflow/workflow';
10
11
  import { WorkflowHandleService as WorkflowHandle } from './services/memflow/handle';
@@ -21,8 +22,6 @@ import { RedisConnection as ConnectorIORedis } from './services/connector/provid
21
22
  import { RedisConnection as ConnectorRedis } from './services/connector/providers/redis';
22
23
  import { NatsConnection as ConnectorNATS } from './services/connector/providers/nats';
23
24
 
24
- //const { Client, Connection, Search, Worker, workflow } = MemFlow;
25
-
26
25
  export {
27
26
  //Provider Connectors
28
27
  Connector, //factory
@@ -44,6 +43,7 @@ export {
44
43
  Connection,
45
44
  proxyActivities,
46
45
  Search,
46
+ Entity,
47
47
  Worker,
48
48
  workflow,
49
49
  WorkflowHandle,
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Permanent-Memory Workflows & AI Agents",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
7
- "homepage": "https://hotmesh.io/",
7
+ "homepage": "https://github.com/hotmeshio/sdk-typescript/",
8
8
  "publishConfig": {
9
9
  "access": "public"
10
10
  },
@@ -33,7 +33,8 @@
33
33
  "test:memflow:collision": "NODE_ENV=test jest ./tests/memflow/collision/*.test.ts --detectOpenHandles --forceExit --verbose",
34
34
  "test:memflow:fatal": "NODE_ENV=test jest ./tests/memflow/fatal/*.test.ts --detectOpenHandles --forceExit --verbose",
35
35
  "test:memflow:goodbye": "NODE_ENV=test jest ./tests/memflow/goodbye/*.test.ts --detectOpenHandles --forceExit --verbose",
36
- "test:memflow:context": "NODE_ENV=test jest ./tests/memflow/context/postgres.test.ts --detectOpenHandles --forceExit --verbose",
36
+ "test:memflow:entity": "NODE_ENV=test HMSH_LOGLEVEL=debug jest ./tests/memflow/entity/postgres.test.ts --detectOpenHandles --forceExit --verbose",
37
+ "test:memflow:agent": "NODE_ENV=test HMSH_LOGLEVEL=debug jest ./tests/memflow/agent/postgres.test.ts --detectOpenHandles --forceExit --verbose",
37
38
  "test:memflow:hello": "HMSH_TELEMETRY=debug HMSH_LOGLEVEL=debug HMSH_IS_CLUSTER=true NODE_ENV=test jest ./tests/memflow/helloworld/*.test.ts --detectOpenHandles --forceExit --verbose",
38
39
  "test:memflow:hook": "NODE_ENV=test jest ./tests/memflow/hook/*.test.ts --detectOpenHandles --forceExit --verbose",
39
40
  "test:memflow:interrupt": "NODE_ENV=test jest ./tests/memflow/interrupt/*.test.ts --detectOpenHandles --forceExit --verbose",
@@ -1,14 +0,0 @@
1
- import { Context } from './common';
2
- /**
3
- * Returns a context session handle for interacting with the workflow's JSONB context storage.
4
- * @returns {Promise<Context>} A context session for workflow data.
5
- *
6
- * @example
7
- * ```typescript
8
- * const context = await workflow.context();
9
- * await context.set({ user: { id: 123 } });
10
- * await context.merge({ user: { name: "John" } });
11
- * const user = await context.get("user");
12
- * ```
13
- */
14
- export declare function context(): Promise<Context>;