@hotmeshio/hotmesh 0.5.0 → 0.5.2

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 (49) hide show
  1. package/README.md +234 -237
  2. package/build/modules/errors.d.ts +9 -0
  3. package/build/modules/errors.js +9 -0
  4. package/build/package.json +16 -14
  5. package/build/services/hotmesh/index.d.ts +9 -11
  6. package/build/services/hotmesh/index.js +9 -11
  7. package/build/services/memflow/entity.d.ts +168 -4
  8. package/build/services/memflow/entity.js +177 -15
  9. package/build/services/memflow/index.d.ts +8 -0
  10. package/build/services/memflow/index.js +8 -0
  11. package/build/services/memflow/worker.js +25 -0
  12. package/build/services/memflow/workflow/execChild.js +1 -0
  13. package/build/services/memflow/workflow/execHook.d.ts +2 -2
  14. package/build/services/memflow/workflow/execHook.js +19 -9
  15. package/build/services/memflow/workflow/index.d.ts +2 -4
  16. package/build/services/memflow/workflow/index.js +2 -4
  17. package/build/services/memflow/workflow/interruption.d.ts +28 -0
  18. package/build/services/memflow/workflow/interruption.js +43 -0
  19. package/build/services/memflow/workflow/proxyActivities.js +1 -0
  20. package/build/services/memflow/workflow/sleepFor.js +1 -0
  21. package/build/services/memflow/workflow/waitFor.js +4 -4
  22. package/build/services/search/index.d.ts +10 -0
  23. package/build/services/search/providers/postgres/postgres.d.ts +12 -0
  24. package/build/services/search/providers/postgres/postgres.js +209 -0
  25. package/build/services/search/providers/redis/ioredis.d.ts +4 -0
  26. package/build/services/search/providers/redis/ioredis.js +13 -0
  27. package/build/services/search/providers/redis/redis.d.ts +4 -0
  28. package/build/services/search/providers/redis/redis.js +13 -0
  29. package/build/services/store/providers/postgres/kvsql.d.ts +13 -37
  30. package/build/services/store/providers/postgres/kvsql.js +2 -2
  31. package/build/services/store/providers/postgres/kvtypes/hash/basic.d.ts +16 -0
  32. package/build/services/store/providers/postgres/kvtypes/hash/basic.js +480 -0
  33. package/build/services/store/providers/postgres/kvtypes/hash/expire.d.ts +5 -0
  34. package/build/services/store/providers/postgres/kvtypes/hash/expire.js +33 -0
  35. package/build/services/store/providers/postgres/kvtypes/hash/index.d.ts +29 -0
  36. package/build/services/store/providers/postgres/kvtypes/hash/index.js +190 -0
  37. package/build/services/store/providers/postgres/kvtypes/hash/jsonb.d.ts +14 -0
  38. package/build/services/store/providers/postgres/kvtypes/hash/jsonb.js +699 -0
  39. package/build/services/store/providers/postgres/kvtypes/hash/scan.d.ts +10 -0
  40. package/build/services/store/providers/postgres/kvtypes/hash/scan.js +91 -0
  41. package/build/services/store/providers/postgres/kvtypes/hash/types.d.ts +19 -0
  42. package/build/services/store/providers/postgres/kvtypes/hash/types.js +2 -0
  43. package/build/services/store/providers/postgres/kvtypes/hash/utils.d.ts +18 -0
  44. package/build/services/store/providers/postgres/kvtypes/hash/utils.js +90 -0
  45. package/build/types/memflow.d.ts +1 -1
  46. package/build/types/meshdata.d.ts +1 -1
  47. package/package.json +16 -14
  48. package/build/services/store/providers/postgres/kvtypes/hash.d.ts +0 -60
  49. package/build/services/store/providers/postgres/kvtypes/hash.js +0 -1287
@@ -2,7 +2,7 @@ import { KeyStoreParams } from '../../../../types/hotmesh';
2
2
  import { PostgresClientType } from '../../../../types/postgres';
3
3
  import { ProviderTransaction } from '../../../../types/provider';
4
4
  import { stringModule } from './kvtypes/string';
5
- import { hashModule } from './kvtypes/hash';
5
+ import { hashModule } from './kvtypes/hash/index';
6
6
  import { listModule } from './kvtypes/list';
7
7
  import { zsetModule } from './kvtypes/zset';
8
8
  /**
@@ -30,8 +30,8 @@ export declare class KVSQL {
30
30
  */
31
31
  tableForKey(key: string, stats_type?: 'hash' | 'sorted_set' | 'list'): string;
32
32
  safeName(input: string, prefix?: string): string;
33
- set: (key: string, value: string, options?: import("../../../../types/provider").SetOptions, multi?: ProviderTransaction) => Promise<boolean>;
34
- _set: (key: string, value: string, options?: import("../../../../types/provider").SetOptions) => {
33
+ set: (key: string, value: string, options?: import("./kvtypes/hash/index").SetOptions, multi?: ProviderTransaction) => Promise<boolean>;
34
+ _set: (key: string, value: string, options?: import("./kvtypes/hash/index").SetOptions) => {
35
35
  sql: string;
36
36
  params: any[];
37
37
  };
@@ -47,51 +47,27 @@ export declare class KVSQL {
47
47
  };
48
48
  setnx: (key: string, value: string, multi?: ProviderTransaction) => Promise<boolean>;
49
49
  setnxex: (key: string, value: string, delay: number, multi?: ProviderTransaction) => Promise<boolean>;
50
- hset: (key: string, fields: Record<string, string>, options?: import("../../../../types/provider").HSetOptions, multi?: ProviderTransaction) => Promise<any>;
51
- _hset: (key: string, fields: Record<string, string>, options?: import("../../../../types/provider").HSetOptions) => {
52
- sql: string;
53
- params: any[];
54
- };
50
+ hset: (key: string, fields: Record<string, string>, options?: import("./kvtypes/hash/index").HSetOptions, multi?: any) => Promise<any>;
51
+ _hset: (key: string, fields: Record<string, string>, options?: import("./kvtypes/hash/index").HSetOptions) => import("./kvtypes/hash/types").SqlResult;
55
52
  hsetnx: (key: string, field: string, value: string, multi?: ProviderTransaction, entity?: string) => Promise<number>;
56
53
  hget: (key: string, field: string, multi?: ProviderTransaction) => Promise<string>;
57
- _hget: (key: string, field: string) => {
58
- sql: string;
59
- params: any[];
60
- };
54
+ _hget: (key: string, field: string) => import("./kvtypes/hash/types").SqlResult;
61
55
  hdel: (key: string, fields: string[], multi?: unknown) => Promise<number>;
62
- _hdel: (key: string, fields: string[]) => {
63
- sql: string;
64
- params: any[];
65
- };
56
+ _hdel: (key: string, fields: string[]) => import("./kvtypes/hash/types").SqlResult;
66
57
  hmget: (key: string, fields: string[], multi?: ProviderTransaction) => Promise<string[]>;
67
- _hmget: (key: string, fields: string[]) => {
68
- sql: string;
69
- params: any[];
70
- };
58
+ _hmget: (key: string, fields: string[]) => import("./kvtypes/hash/types").SqlResult;
71
59
  hgetall: (key: string, multi?: ProviderTransaction) => Promise<Record<string, string>>;
72
60
  hincrbyfloat: (key: string, field: string, increment: number, multi?: ProviderTransaction) => Promise<number>;
73
- _hincrbyfloat: (key: string, field: string, increment: number) => {
74
- sql: string;
75
- params: any[];
76
- };
77
- hscan: (key: string, cursor: string, count?: number, pattern?: string, multi?: ProviderTransaction) => Promise<import("../../../../types/provider").HScanResult>;
78
- _hscan: (key: string, cursor: string, count: number, pattern?: string) => {
79
- sql: string;
80
- params: any[];
81
- };
61
+ _hincrbyfloat: (key: string, field: string, increment: number) => import("./kvtypes/hash/types").SqlResult;
62
+ hscan: (key: string, cursor: string, count?: number, pattern?: string, multi?: ProviderTransaction) => Promise<import("./kvtypes/hash/index").HScanResult>;
63
+ _hscan: (key: string, cursor: string, count: number, pattern?: string) => import("./kvtypes/hash/types").SqlResult;
82
64
  expire: (key: string, seconds: number, multi?: ProviderTransaction) => Promise<boolean>;
83
- _expire: (key: string, seconds: number) => {
84
- sql: string;
85
- params: any[];
86
- };
65
+ _expire: (key: string, seconds: number) => import("./kvtypes/hash/types").SqlResult;
87
66
  scan: (cursor: number, count?: number, pattern?: string, multi?: ProviderTransaction) => Promise<{
88
67
  cursor: number;
89
68
  keys: string[];
90
69
  }>;
91
- _scan: (cursor: number, count: number, pattern?: string) => {
92
- sql: string;
93
- params: any[];
94
- };
70
+ _scan: (cursor: number, count: number, pattern?: string) => import("./kvtypes/hash/types").SqlResult;
95
71
  lrange: (key: string, start: number, end: number, multi?: ProviderTransaction) => Promise<string[]>;
96
72
  _lrange: (key: string, start: number, end: number) => {
97
73
  sql: string;
@@ -4,7 +4,7 @@ exports.KVSQL = void 0;
4
4
  const key_1 = require("../../../../modules/key");
5
5
  const kvtransaction_1 = require("./kvtransaction");
6
6
  const string_1 = require("./kvtypes/string");
7
- const hash_1 = require("./kvtypes/hash");
7
+ const index_1 = require("./kvtypes/hash/index");
8
8
  const list_1 = require("./kvtypes/list");
9
9
  const zset_1 = require("./kvtypes/zset");
10
10
  /**
@@ -72,7 +72,7 @@ class KVSQL {
72
72
  this.pgClient = pgClient;
73
73
  this.namespace = namespace;
74
74
  this.appId = appId;
75
- this.hash = (0, hash_1.hashModule)(this);
75
+ this.hash = (0, index_1.hashModule)(this);
76
76
  this.list = (0, list_1.listModule)(this);
77
77
  this.zset = (0, zset_1.zsetModule)(this);
78
78
  this.string = (0, string_1.stringModule)(this);
@@ -0,0 +1,16 @@
1
+ import { HashContext, SqlResult, HSetOptions, ProviderTransaction } from './types';
2
+ export declare function createBasicOperations(context: HashContext['context']): {
3
+ hsetnx(key: string, field: string, value: string, multi?: ProviderTransaction, entity?: string): Promise<number>;
4
+ hset(key: string, fields: Record<string, string>, options?: HSetOptions, multi?: ProviderTransaction): Promise<number | any>;
5
+ hget(key: string, field: string, multi?: ProviderTransaction): Promise<string | null>;
6
+ hdel(key: string, fields: string[], multi?: unknown): Promise<number>;
7
+ hmget(key: string, fields: string[], multi?: ProviderTransaction): Promise<(string | null)[]>;
8
+ hgetall(key: string, multi?: ProviderTransaction): Promise<Record<string, string>>;
9
+ hincrbyfloat(key: string, field: string, increment: number, multi?: ProviderTransaction): Promise<number>;
10
+ };
11
+ export declare function _hset(context: HashContext['context'], key: string, fields: Record<string, string>, options?: HSetOptions): SqlResult;
12
+ export declare function _hget(context: HashContext['context'], key: string, field: string): SqlResult;
13
+ export declare function _hdel(context: HashContext['context'], key: string, fields: string[]): SqlResult;
14
+ export declare function _hmget(context: HashContext['context'], key: string, fields: string[]): SqlResult;
15
+ export declare function _hgetall(context: HashContext['context'], key: string): SqlResult;
16
+ export declare function _hincrbyfloat(context: HashContext['context'], key: string, field: string, increment: number): SqlResult;
@@ -0,0 +1,480 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports._hincrbyfloat = exports._hgetall = exports._hmget = exports._hdel = exports._hget = exports._hset = exports.createBasicOperations = void 0;
4
+ const utils_1 = require("./utils");
5
+ function createBasicOperations(context) {
6
+ return {
7
+ async hsetnx(key, field, value, multi, entity) {
8
+ const { sql, params } = _hset(context, key, { [field]: value }, { nx: true, entity });
9
+ if (multi) {
10
+ multi.addCommand(sql, params, 'number');
11
+ return Promise.resolve(0);
12
+ }
13
+ else {
14
+ try {
15
+ const res = await context.pgClient.query(sql, params);
16
+ return res.rowCount;
17
+ }
18
+ catch (err) {
19
+ console.error('hsetnx error', err, sql, params);
20
+ return 0;
21
+ }
22
+ }
23
+ },
24
+ async hset(key, fields, options, multi) {
25
+ const { sql, params } = _hset(context, key, fields, options);
26
+ if (multi) {
27
+ multi.addCommand(sql, params, 'number');
28
+ return Promise.resolve(0);
29
+ }
30
+ else {
31
+ try {
32
+ const res = await context.pgClient.query(sql, params);
33
+ // Check if this is a JSONB operation that returns a value
34
+ const isJsonbOperation = Object.keys(fields).some(k => k.startsWith('@context:') && k !== '@context');
35
+ if (isJsonbOperation && res.rows[0]?.new_value !== undefined) {
36
+ let returnValue;
37
+ try {
38
+ // Try to parse as JSON, fallback to string if it fails
39
+ returnValue = JSON.parse(res.rows[0].new_value);
40
+ }
41
+ catch {
42
+ returnValue = res.rows[0].new_value;
43
+ }
44
+ return returnValue;
45
+ }
46
+ return res.rowCount;
47
+ }
48
+ catch (err) {
49
+ console.error('hset error', err, sql, params);
50
+ return 0;
51
+ }
52
+ }
53
+ },
54
+ async hget(key, field, multi) {
55
+ const { sql, params } = _hget(context, key, field);
56
+ if (multi) {
57
+ multi.addCommand(sql, params, 'string', (rows) => {
58
+ return rows[0]?.value || null;
59
+ });
60
+ return Promise.resolve(null);
61
+ }
62
+ else {
63
+ const res = await context.pgClient.query(sql, params);
64
+ return res.rows[0]?.value || null;
65
+ }
66
+ },
67
+ async hdel(key, fields, multi) {
68
+ // Ensure fields is an array
69
+ if (!Array.isArray(fields)) {
70
+ fields = [fields];
71
+ }
72
+ const { sql, params } = _hdel(context, key, fields);
73
+ if (multi) {
74
+ multi.addCommand(sql, params, 'number');
75
+ return Promise.resolve(0);
76
+ }
77
+ else {
78
+ const res = await context.pgClient.query(sql, params);
79
+ return Number(res.rows[0]?.count || 0);
80
+ }
81
+ },
82
+ async hmget(key, fields, multi) {
83
+ const { sql, params } = _hmget(context, key, fields);
84
+ const processRows = (rows) => {
85
+ const tableName = context.tableForKey(key, 'hash');
86
+ const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
87
+ if (isJobsTableResult) {
88
+ return (0, utils_1.processJobsRows)(rows, fields);
89
+ }
90
+ else {
91
+ const fieldValueMap = new Map();
92
+ for (const row of rows) {
93
+ fieldValueMap.set(row.field, row.value);
94
+ }
95
+ return fields.map((field) => fieldValueMap.get(field) || null);
96
+ }
97
+ };
98
+ if (multi) {
99
+ multi.addCommand(sql, params, 'array', (rows) => {
100
+ return processRows(rows);
101
+ });
102
+ return Promise.resolve([]);
103
+ }
104
+ else {
105
+ try {
106
+ const res = await context.pgClient.query(sql, params);
107
+ return processRows(res.rows);
108
+ }
109
+ catch (err) {
110
+ console.error('hmget error', err, sql, params);
111
+ throw err;
112
+ }
113
+ }
114
+ },
115
+ async hgetall(key, multi) {
116
+ const tableName = context.tableForKey(key, 'hash');
117
+ const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
118
+ const { sql, params } = _hgetall(context, key);
119
+ const processRows = (rows) => {
120
+ if (isJobsTableResult) {
121
+ return (0, utils_1.processJobsRows)(rows);
122
+ }
123
+ else {
124
+ return (0, utils_1.processRegularRows)(rows);
125
+ }
126
+ };
127
+ if (multi) {
128
+ multi.addCommand(sql, params, 'object', (rows) => {
129
+ return processRows(rows);
130
+ });
131
+ return Promise.resolve({});
132
+ }
133
+ else {
134
+ try {
135
+ const res = await context.pgClient.query(sql, params);
136
+ return processRows(res.rows);
137
+ }
138
+ catch (err) {
139
+ console.error('hgetall error', err, sql, params);
140
+ throw err;
141
+ }
142
+ }
143
+ },
144
+ async hincrbyfloat(key, field, increment, multi) {
145
+ const { sql, params } = _hincrbyfloat(context, key, field, increment);
146
+ if (multi) {
147
+ multi.addCommand(sql, params, 'number', (rows) => {
148
+ return parseFloat(rows[0].value);
149
+ });
150
+ return Promise.resolve(0);
151
+ }
152
+ else {
153
+ const res = await context.pgClient.query(sql, params);
154
+ return parseFloat(res.rows[0].value);
155
+ }
156
+ },
157
+ };
158
+ }
159
+ exports.createBasicOperations = createBasicOperations;
160
+ // Internal helper functions
161
+ function _hset(context, key, fields, options) {
162
+ const tableName = context.tableForKey(key, 'hash');
163
+ const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
164
+ const fieldEntries = Object.entries(fields);
165
+ const isStatusOnly = fieldEntries.length === 1 && fieldEntries[0][0] === ':';
166
+ let targetTable = tableName; // Default table name
167
+ if (isJobsTableResult) {
168
+ if (isStatusOnly) {
169
+ // Target the jobs table directly when setting only the status field
170
+ targetTable = tableName;
171
+ }
172
+ else {
173
+ // For other fields, target the attributes table
174
+ targetTable = `${tableName}_attributes`;
175
+ }
176
+ }
177
+ const params = [];
178
+ let sql = '';
179
+ if (isJobsTableResult && isStatusOnly) {
180
+ if (options?.nx) {
181
+ // Use WHERE NOT EXISTS to enforce nx
182
+ sql = `
183
+ INSERT INTO ${targetTable} (id, key, status, entity)
184
+ SELECT gen_random_uuid(), $1, $2, $3
185
+ WHERE NOT EXISTS (
186
+ SELECT 1 FROM ${targetTable}
187
+ WHERE key = $1 AND is_live
188
+ )
189
+ RETURNING 1 as count
190
+ `;
191
+ params.push(key, fields[':'], options?.entity ?? null);
192
+ }
193
+ else {
194
+ // Update existing job or insert new one
195
+ sql = `
196
+ INSERT INTO ${targetTable} (id, key, status, entity)
197
+ VALUES (gen_random_uuid(), $1, $2, $3)
198
+ ON CONFLICT (key) WHERE is_live DO UPDATE SET status = EXCLUDED.status
199
+ RETURNING 1 as count
200
+ `;
201
+ params.push(key, fields[':'], options?.entity ?? null);
202
+ }
203
+ }
204
+ else if (isJobsTableResult) {
205
+ const schemaName = context.safeName(context.appId);
206
+ const conflictAction = options?.nx
207
+ ? 'ON CONFLICT DO NOTHING'
208
+ : `ON CONFLICT (job_id, field) DO UPDATE SET value = EXCLUDED.value`;
209
+ const placeholders = fieldEntries
210
+ .map(([field, value], index) => {
211
+ const baseIndex = index * 3 + 2; // Adjusted baseIndex
212
+ params.push(field, value, (0, utils_1.deriveType)(field));
213
+ return `($${baseIndex}, $${baseIndex + 1}, $${baseIndex + 2}::${schemaName}.type_enum)`;
214
+ })
215
+ .join(', ');
216
+ sql = `
217
+ INSERT INTO ${targetTable} (job_id, field, value, type)
218
+ SELECT
219
+ job.id,
220
+ vals.field,
221
+ vals.value,
222
+ vals.type
223
+ FROM (
224
+ SELECT id FROM ${tableName} WHERE key = $1 AND is_live
225
+ ) AS job
226
+ CROSS JOIN (
227
+ VALUES ${placeholders}
228
+ ) AS vals(field, value, type)
229
+ ${conflictAction}
230
+ RETURNING 1 as count
231
+ `;
232
+ params.unshift(key); // Add key as first parameter
233
+ }
234
+ else {
235
+ // For non-jobs tables
236
+ const conflictAction = options?.nx
237
+ ? 'ON CONFLICT DO NOTHING'
238
+ : `ON CONFLICT (key, field) DO UPDATE SET value = EXCLUDED.value`;
239
+ const placeholders = fieldEntries
240
+ .map(([field, value], index) => {
241
+ params.push(field, value);
242
+ return `($1, $${index * 2 + 2}, $${index * 2 + 3})`;
243
+ })
244
+ .join(', ');
245
+ sql = `
246
+ INSERT INTO ${targetTable} (key, field, value)
247
+ VALUES ${placeholders}
248
+ ${conflictAction}
249
+ RETURNING 1 as count
250
+ `;
251
+ params.unshift(key); // Add key as the first parameter
252
+ }
253
+ return { sql, params };
254
+ }
255
+ exports._hset = _hset;
256
+ function _hget(context, key, field) {
257
+ const tableName = context.tableForKey(key, 'hash');
258
+ const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
259
+ const isStatusField = field === ':';
260
+ const isContextField = field === '@';
261
+ if (isJobsTableResult && isStatusField) {
262
+ // Fetch status from jobs table
263
+ const sql = `
264
+ SELECT status::text AS value
265
+ FROM ${tableName}
266
+ WHERE key = $1 AND is_live
267
+ `;
268
+ return { sql, params: [key] };
269
+ }
270
+ else if (isJobsTableResult && isContextField) {
271
+ // Fetch context from jobs table
272
+ const sql = `
273
+ SELECT context::text AS value
274
+ FROM ${tableName}
275
+ WHERE key = $1 AND is_live
276
+ `;
277
+ return { sql, params: [key] };
278
+ }
279
+ else if (isJobsTableResult) {
280
+ // Fetch a specific field from the attributes table for a job
281
+ const sql = `
282
+ SELECT value
283
+ FROM ${tableName}_attributes
284
+ WHERE job_id = (
285
+ SELECT id FROM ${tableName}
286
+ WHERE key = $1 AND is_live
287
+ )
288
+ AND field = $2
289
+ `;
290
+ return { sql, params: [key, field] };
291
+ }
292
+ else {
293
+ // Non-jobs tables
294
+ const baseQuery = `
295
+ SELECT value
296
+ FROM ${tableName}
297
+ WHERE key = $1 AND field = $2
298
+ `;
299
+ const sql = context.appendExpiryClause(baseQuery, tableName);
300
+ return { sql, params: [key, field] };
301
+ }
302
+ }
303
+ exports._hget = _hget;
304
+ function _hdel(context, key, fields) {
305
+ const tableName = context.tableForKey(key, 'hash');
306
+ const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
307
+ const targetTable = isJobsTableResult ? `${tableName}_attributes` : tableName;
308
+ const fieldPlaceholders = fields.map((_, i) => `$${i + 2}`).join(', ');
309
+ const params = [key, ...fields];
310
+ if (isJobsTableResult) {
311
+ const sql = `
312
+ WITH valid_job AS (
313
+ SELECT id
314
+ FROM ${tableName}
315
+ WHERE key = $1 AND is_live
316
+ ),
317
+ deleted AS (
318
+ DELETE FROM ${targetTable}
319
+ WHERE job_id IN (SELECT id FROM valid_job) AND field IN (${fieldPlaceholders})
320
+ RETURNING 1
321
+ )
322
+ SELECT COUNT(*) as count FROM deleted
323
+ `;
324
+ return { sql, params };
325
+ }
326
+ else {
327
+ const sql = `
328
+ WITH deleted AS (
329
+ DELETE FROM ${targetTable}
330
+ WHERE key = $1 AND field IN (${fieldPlaceholders})
331
+ RETURNING 1
332
+ )
333
+ SELECT COUNT(*) as count FROM deleted
334
+ `;
335
+ return { sql, params };
336
+ }
337
+ }
338
+ exports._hdel = _hdel;
339
+ function _hmget(context, key, fields) {
340
+ const tableName = context.tableForKey(key, 'hash');
341
+ const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
342
+ if (isJobsTableResult) {
343
+ const sql = `
344
+ WITH valid_job AS (
345
+ SELECT id, status, context
346
+ FROM ${tableName}
347
+ WHERE key = $1
348
+ AND (expired_at IS NULL OR expired_at > NOW())
349
+ LIMIT 1
350
+ ),
351
+ job_fields AS (
352
+ -- Include both status and context fields from jobs table
353
+ SELECT
354
+ 'status' AS field,
355
+ status::text AS value
356
+ FROM valid_job
357
+
358
+ UNION ALL
359
+
360
+ SELECT
361
+ 'context' AS field,
362
+ context::text AS value
363
+ FROM valid_job
364
+
365
+ UNION ALL
366
+
367
+ -- Get attribute fields with proper type handling
368
+ SELECT
369
+ a.field,
370
+ a.value
371
+ FROM ${tableName}_attributes a
372
+ JOIN valid_job j ON j.id = a.job_id
373
+ WHERE a.field = ANY($2::text[])
374
+ )
375
+ SELECT field, value
376
+ FROM job_fields
377
+ ORDER BY field
378
+ `;
379
+ return { sql, params: [key, fields] };
380
+ }
381
+ else {
382
+ // Non-job tables logic remains the same
383
+ const baseQuery = `
384
+ SELECT field, value
385
+ FROM ${tableName}
386
+ WHERE key = $1
387
+ AND field = ANY($2::text[])
388
+ `;
389
+ const sql = context.appendExpiryClause(baseQuery, tableName);
390
+ return { sql, params: [key, fields] };
391
+ }
392
+ }
393
+ exports._hmget = _hmget;
394
+ function _hgetall(context, key) {
395
+ const tableName = context.tableForKey(key, 'hash');
396
+ const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
397
+ if (isJobsTableResult) {
398
+ const sql = `
399
+ WITH valid_job AS (
400
+ SELECT id, status, context
401
+ FROM ${tableName}
402
+ WHERE key = $1 AND is_live
403
+ ),
404
+ job_data AS (
405
+ SELECT 'status' AS field, status::text AS value
406
+ FROM ${tableName}
407
+ WHERE key = $1 AND is_live
408
+
409
+ UNION ALL
410
+
411
+ SELECT 'context' AS field, context::text AS value
412
+ FROM ${tableName}
413
+ WHERE key = $1 AND is_live
414
+ ),
415
+ attribute_data AS (
416
+ SELECT field, value
417
+ FROM ${tableName}_attributes
418
+ WHERE job_id IN (SELECT id FROM valid_job)
419
+ )
420
+ SELECT * FROM job_data
421
+ UNION ALL
422
+ SELECT * FROM attribute_data;
423
+ `;
424
+ return { sql, params: [key] };
425
+ }
426
+ else {
427
+ // Non-job tables
428
+ const sql = context.appendExpiryClause(`
429
+ SELECT field, value
430
+ FROM ${tableName}
431
+ WHERE key = $1
432
+ `, tableName);
433
+ return { sql, params: [key] };
434
+ }
435
+ }
436
+ exports._hgetall = _hgetall;
437
+ function _hincrbyfloat(context, key, field, increment) {
438
+ const tableName = context.tableForKey(key, 'hash');
439
+ const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
440
+ const isStatusField = field === ':';
441
+ if (isJobsTableResult && isStatusField) {
442
+ const sql = `
443
+ UPDATE ${tableName}
444
+ SET status = status + $2
445
+ WHERE key = $1 AND is_live
446
+ RETURNING status::text AS value
447
+ `;
448
+ return { sql, params: [key, increment] };
449
+ }
450
+ else if (isJobsTableResult) {
451
+ // Update the condition here
452
+ const sql = `
453
+ WITH valid_job AS (
454
+ SELECT id
455
+ FROM ${tableName}
456
+ WHERE key = $1 AND is_live
457
+ )
458
+ INSERT INTO ${tableName}_attributes (job_id, field, value, type)
459
+ SELECT id, $2, ($3::double precision)::text, $4
460
+ FROM valid_job
461
+ ON CONFLICT (job_id, field) DO UPDATE
462
+ SET
463
+ value = ((COALESCE(${tableName}_attributes.value, '0')::double precision) + $3::double precision)::text,
464
+ type = EXCLUDED.type
465
+ RETURNING value;
466
+ `;
467
+ return { sql, params: [key, field, increment, (0, utils_1.deriveType)(field)] };
468
+ }
469
+ else {
470
+ const sql = `
471
+ INSERT INTO ${tableName} (key, field, value)
472
+ VALUES ($1, $2, ($3)::text)
473
+ ON CONFLICT (key, field) DO UPDATE
474
+ SET value = ((COALESCE(${tableName}.value, '0')::double precision + $3::double precision)::text)
475
+ RETURNING value
476
+ `;
477
+ return { sql, params: [key, field, increment] };
478
+ }
479
+ }
480
+ exports._hincrbyfloat = _hincrbyfloat;
@@ -0,0 +1,5 @@
1
+ import { HashContext, SqlResult, ProviderTransaction } from './types';
2
+ export declare function createExpireOperations(context: HashContext['context']): {
3
+ expire(key: string, seconds: number, multi?: ProviderTransaction): Promise<boolean>;
4
+ };
5
+ export declare function _expire(context: HashContext['context'], key: string, seconds: number): SqlResult;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports._expire = exports.createExpireOperations = void 0;
4
+ function createExpireOperations(context) {
5
+ return {
6
+ async expire(key, seconds, multi) {
7
+ const { sql, params } = _expire(context, key, seconds);
8
+ if (multi) {
9
+ multi.addCommand(sql, params, 'boolean');
10
+ return Promise.resolve(true);
11
+ }
12
+ else {
13
+ const res = await context.pgClient.query(sql, params);
14
+ return res.rowCount > 0;
15
+ }
16
+ },
17
+ };
18
+ }
19
+ exports.createExpireOperations = createExpireOperations;
20
+ function _expire(context, key, seconds) {
21
+ // Only job tables are ever expired
22
+ const tableName = context.tableForKey(key);
23
+ const expiryTime = new Date(Date.now() + seconds * 1000);
24
+ const sql = `
25
+ UPDATE ${tableName}
26
+ SET expired_at = $2
27
+ WHERE key = $1 AND is_live
28
+ RETURNING true as success
29
+ `;
30
+ const params = [key, expiryTime];
31
+ return { sql, params };
32
+ }
33
+ exports._expire = _expire;
@@ -0,0 +1,29 @@
1
+ import { KVSQL } from '../../kvsql';
2
+ import { HSetOptions } from './types';
3
+ import { isJobsTable } from './utils';
4
+ export declare const hashModule: (context: KVSQL) => {
5
+ hset(key: string, fields: Record<string, string>, options?: HSetOptions, multi?: any): Promise<number | any>;
6
+ _hset: (key: string, fields: Record<string, string>, options?: HSetOptions) => import("./types").SqlResult;
7
+ _hget: (key: string, field: string) => import("./types").SqlResult;
8
+ _hdel: (key: string, fields: string[]) => import("./types").SqlResult;
9
+ _hmget: (key: string, fields: string[]) => import("./types").SqlResult;
10
+ _hincrbyfloat: (key: string, field: string, increment: number) => import("./types").SqlResult;
11
+ _hscan: (key: string, cursor: string, count: number, pattern?: string) => import("./types").SqlResult;
12
+ _expire: (key: string, seconds: number) => import("./types").SqlResult;
13
+ _scan: (cursor: number, count: number, pattern?: string) => import("./types").SqlResult;
14
+ isJobsTable: typeof isJobsTable;
15
+ expire(key: string, seconds: number, multi?: import("./types").ProviderTransaction): Promise<boolean>;
16
+ hscan(key: string, cursor: string, count?: number, pattern?: string, multi?: import("./types").ProviderTransaction): Promise<import("./types").HScanResult>;
17
+ scan(cursor: number, count?: number, pattern?: string, multi?: import("./types").ProviderTransaction): Promise<{
18
+ cursor: number;
19
+ keys: string[];
20
+ }>;
21
+ hsetnx(key: string, field: string, value: string, multi?: import("./types").ProviderTransaction, entity?: string): Promise<number>;
22
+ hget(key: string, field: string, multi?: import("./types").ProviderTransaction): Promise<string>;
23
+ hdel(key: string, fields: string[], multi?: unknown): Promise<number>;
24
+ hmget(key: string, fields: string[], multi?: import("./types").ProviderTransaction): Promise<string[]>;
25
+ hgetall(key: string, multi?: import("./types").ProviderTransaction): Promise<Record<string, string>>;
26
+ hincrbyfloat(key: string, field: string, increment: number, multi?: import("./types").ProviderTransaction): Promise<number>;
27
+ };
28
+ export { isJobsTable, deriveType } from './utils';
29
+ export * from './types';