@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.
- package/README.md +114 -51
- package/build/index.d.ts +2 -1
- package/build/index.js +3 -1
- package/build/package.json +3 -2
- package/build/services/activities/trigger.d.ts +1 -1
- package/build/services/activities/trigger.js +3 -3
- package/build/services/memflow/client.js +2 -1
- package/build/services/memflow/{context.d.ts → entity.d.ts} +34 -34
- package/build/services/memflow/{context.js → entity.js} +40 -40
- package/build/services/memflow/index.d.ts +2 -2
- package/build/services/memflow/index.js +2 -2
- package/build/services/memflow/workflow/common.d.ts +2 -2
- package/build/services/memflow/workflow/common.js +3 -3
- package/build/services/memflow/workflow/entityMethods.d.ts +14 -0
- package/build/services/memflow/workflow/{contextMethods.js → entityMethods.js} +11 -11
- package/build/services/memflow/workflow/index.d.ts +2 -2
- package/build/services/memflow/workflow/index.js +2 -2
- package/build/services/memflow/workflow/signal.d.ts +23 -1
- package/build/services/memflow/workflow/signal.js +23 -1
- package/build/services/memflow/workflow/sleepFor.d.ts +17 -1
- package/build/services/memflow/workflow/sleepFor.js +17 -1
- package/build/services/memflow/workflow/waitFor.d.ts +22 -1
- package/build/services/memflow/workflow/waitFor.js +22 -1
- package/build/services/store/index.d.ts +1 -1
- package/build/services/store/providers/postgres/kvsql.d.ts +1 -1
- package/build/services/store/providers/postgres/kvtypes/hash.d.ts +1 -1
- package/build/services/store/providers/postgres/kvtypes/hash.js +8 -8
- package/build/services/store/providers/postgres/postgres.d.ts +34 -1
- package/build/services/store/providers/postgres/postgres.js +211 -2
- package/build/services/store/providers/redis/_base.d.ts +1 -1
- package/build/services/store/providers/redis/_base.js +1 -1
- package/build/services/task/index.d.ts +12 -0
- package/build/services/task/index.js +47 -0
- package/build/types/job.d.ts +7 -0
- package/build/types/provider.d.ts +1 -0
- package/index.ts +2 -2
- package/package.json +4 -3
- 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;
|
package/build/types/job.d.ts
CHANGED
|
@@ -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
|
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.
|
|
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://
|
|
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:
|
|
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>;
|