@hotmeshio/hotmesh 0.14.1 → 0.14.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 (52) hide show
  1. package/build/package.json +2 -1
  2. package/build/services/engine/init.js +1 -1
  3. package/build/services/engine/schema.js +5 -1
  4. package/build/services/pipe/functions/array.d.ts +219 -0
  5. package/build/services/pipe/functions/array.js +219 -0
  6. package/build/services/pipe/functions/bitwise.d.ts +94 -0
  7. package/build/services/pipe/functions/bitwise.js +94 -0
  8. package/build/services/pipe/functions/conditional.d.ts +161 -0
  9. package/build/services/pipe/functions/conditional.js +161 -0
  10. package/build/services/pipe/functions/cron.d.ts +23 -4
  11. package/build/services/pipe/functions/cron.js +23 -4
  12. package/build/services/pipe/functions/date.d.ts +737 -6
  13. package/build/services/pipe/functions/date.js +742 -5
  14. package/build/services/pipe/functions/json.d.ts +42 -0
  15. package/build/services/pipe/functions/json.js +42 -0
  16. package/build/services/pipe/functions/logical.d.ts +38 -0
  17. package/build/services/pipe/functions/logical.js +38 -0
  18. package/build/services/pipe/functions/math.d.ts +533 -0
  19. package/build/services/pipe/functions/math.js +533 -0
  20. package/build/services/pipe/functions/number.d.ts +258 -0
  21. package/build/services/pipe/functions/number.js +258 -0
  22. package/build/services/pipe/functions/object.d.ts +321 -0
  23. package/build/services/pipe/functions/object.js +321 -0
  24. package/build/services/pipe/functions/string.d.ts +306 -0
  25. package/build/services/pipe/functions/string.js +306 -0
  26. package/build/services/pipe/functions/symbol.d.ts +112 -0
  27. package/build/services/pipe/functions/symbol.js +112 -0
  28. package/build/services/pipe/functions/unary.d.ts +65 -0
  29. package/build/services/pipe/functions/unary.js +65 -0
  30. package/build/services/quorum/index.js +1 -1
  31. package/build/services/router/consumption/index.js +20 -2
  32. package/build/services/router/error-handling/index.js +1 -1
  33. package/build/services/store/factory.d.ts +1 -1
  34. package/build/services/store/factory.js +2 -2
  35. package/build/services/store/index.d.ts +1 -1
  36. package/build/services/store/providers/postgres/kvsql.d.ts +11 -1
  37. package/build/services/store/providers/postgres/kvsql.js +22 -12
  38. package/build/services/store/providers/postgres/kvtables.js +39 -6
  39. package/build/services/store/providers/postgres/kvtypes/hash/basic.js +6 -6
  40. package/build/services/store/providers/postgres/kvtypes/hash/scan.js +2 -1
  41. package/build/services/store/providers/postgres/kvtypes/list.js +7 -6
  42. package/build/services/store/providers/postgres/kvtypes/string.js +3 -3
  43. package/build/services/store/providers/postgres/kvtypes/zset.js +7 -7
  44. package/build/services/store/providers/postgres/postgres.d.ts +3 -2
  45. package/build/services/store/providers/postgres/postgres.js +55 -55
  46. package/build/services/store/providers/postgres/time-notify.js +18 -25
  47. package/build/services/store/providers/store-initializable.d.ts +1 -1
  48. package/build/services/virtual/index.js +6 -0
  49. package/build/services/virtual/schemas/factory.js +1 -1
  50. package/build/services/worker/index.js +1 -1
  51. package/build/types/virtual.d.ts +21 -0
  52. package/package.json +2 -1
@@ -1,12 +1,124 @@
1
+ /**
2
+ * Provides methods for returning common symbolic values and objects,
3
+ * such as `null`, `undefined`, `whitespace`, `object`, `array`, and `date`.
4
+ * These methods can be used in a variety of contexts where it is necessary
5
+ * to represent these values or objects programmatically.
6
+ *
7
+ * @remarks
8
+ * Invoked in mapping rules using `{@symbol.<method>}` syntax.
9
+ * Symbol methods take no parameters; they are called directly
10
+ * in the `@pipe` structure.
11
+ */
1
12
  declare class SymbolHandler {
13
+ /**
14
+ * Returns the value `null`, representing a deliberate non-value.
15
+ *
16
+ * @returns {null} The null value
17
+ * @example
18
+ * ```yaml
19
+ * set_null:
20
+ * "@pipe":
21
+ * - ["{@symbol.null}"]
22
+ * ```
23
+ */
2
24
  null(): null;
25
+ /**
26
+ * Returns the value `undefined`, representing a variable
27
+ * that has not been assigned a value.
28
+ *
29
+ * @returns {undefined} The undefined value
30
+ * @example
31
+ * ```yaml
32
+ * set_undefined:
33
+ * "@pipe":
34
+ * - ["{@symbol.undefined}"]
35
+ * ```
36
+ */
3
37
  undefined(): undefined;
38
+ /**
39
+ * Returns a single whitespace character as a string.
40
+ *
41
+ * @returns {string} A single space character
42
+ * @example
43
+ * ```yaml
44
+ * set_whitespace:
45
+ * "@pipe":
46
+ * - ["{@symbol.whitespace}"]
47
+ * ```
48
+ */
4
49
  whitespace(): string;
50
+ /**
51
+ * Returns an empty object (`{}`).
52
+ *
53
+ * @returns {object} An empty object
54
+ * @example
55
+ * ```yaml
56
+ * set_object:
57
+ * "@pipe":
58
+ * - ["{@symbol.object}"]
59
+ * ```
60
+ */
5
61
  object(): object;
62
+ /**
63
+ * Returns an empty array (`[]`).
64
+ *
65
+ * @returns {any[]} An empty array
66
+ * @example
67
+ * ```yaml
68
+ * set_array:
69
+ * "@pipe":
70
+ * - ["{@symbol.array}"]
71
+ * ```
72
+ */
6
73
  array(): any[];
74
+ /**
75
+ * Returns the positive infinity value (`Infinity`).
76
+ *
77
+ * @returns {number} Positive infinity
78
+ * @example
79
+ * ```yaml
80
+ * set_pos_infinity:
81
+ * "@pipe":
82
+ * - ["{@symbol.posInfinity}"]
83
+ * ```
84
+ */
7
85
  posInfinity(): number;
86
+ /**
87
+ * Returns the negative infinity value (`-Infinity`).
88
+ *
89
+ * @returns {number} Negative infinity
90
+ * @example
91
+ * ```yaml
92
+ * set_neg_infinity:
93
+ * "@pipe":
94
+ * - ["{@symbol.negInfinity}"]
95
+ * ```
96
+ */
8
97
  negInfinity(): number;
98
+ /**
99
+ * Returns the not-a-number value (`NaN`).
100
+ *
101
+ * @returns {number} NaN
102
+ * @example
103
+ * ```yaml
104
+ * set_nan:
105
+ * "@pipe":
106
+ * - ["{@symbol.NaN}"]
107
+ * ```
108
+ */
9
109
  NaN(): number;
110
+ /**
111
+ * Returns the current date and time as a Date object.
112
+ *
113
+ * @returns {Date} The current date and time
114
+ * @example
115
+ * ```yaml
116
+ * set_date:
117
+ * "@pipe":
118
+ * - ["{@symbol.date}"]
119
+ * - ["{@json.stringify}"]
120
+ * ```
121
+ */
10
122
  date(): Date;
11
123
  }
12
124
  export { SymbolHandler };
@@ -1,31 +1,143 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SymbolHandler = void 0;
4
+ /**
5
+ * Provides methods for returning common symbolic values and objects,
6
+ * such as `null`, `undefined`, `whitespace`, `object`, `array`, and `date`.
7
+ * These methods can be used in a variety of contexts where it is necessary
8
+ * to represent these values or objects programmatically.
9
+ *
10
+ * @remarks
11
+ * Invoked in mapping rules using `{@symbol.<method>}` syntax.
12
+ * Symbol methods take no parameters; they are called directly
13
+ * in the `@pipe` structure.
14
+ */
4
15
  class SymbolHandler {
16
+ /**
17
+ * Returns the value `null`, representing a deliberate non-value.
18
+ *
19
+ * @returns {null} The null value
20
+ * @example
21
+ * ```yaml
22
+ * set_null:
23
+ * "@pipe":
24
+ * - ["{@symbol.null}"]
25
+ * ```
26
+ */
5
27
  null() {
6
28
  return null;
7
29
  }
30
+ /**
31
+ * Returns the value `undefined`, representing a variable
32
+ * that has not been assigned a value.
33
+ *
34
+ * @returns {undefined} The undefined value
35
+ * @example
36
+ * ```yaml
37
+ * set_undefined:
38
+ * "@pipe":
39
+ * - ["{@symbol.undefined}"]
40
+ * ```
41
+ */
8
42
  undefined() {
9
43
  return undefined;
10
44
  }
45
+ /**
46
+ * Returns a single whitespace character as a string.
47
+ *
48
+ * @returns {string} A single space character
49
+ * @example
50
+ * ```yaml
51
+ * set_whitespace:
52
+ * "@pipe":
53
+ * - ["{@symbol.whitespace}"]
54
+ * ```
55
+ */
11
56
  whitespace() {
12
57
  return ' ';
13
58
  }
59
+ /**
60
+ * Returns an empty object (`{}`).
61
+ *
62
+ * @returns {object} An empty object
63
+ * @example
64
+ * ```yaml
65
+ * set_object:
66
+ * "@pipe":
67
+ * - ["{@symbol.object}"]
68
+ * ```
69
+ */
14
70
  object() {
15
71
  return {};
16
72
  }
73
+ /**
74
+ * Returns an empty array (`[]`).
75
+ *
76
+ * @returns {any[]} An empty array
77
+ * @example
78
+ * ```yaml
79
+ * set_array:
80
+ * "@pipe":
81
+ * - ["{@symbol.array}"]
82
+ * ```
83
+ */
17
84
  array() {
18
85
  return [];
19
86
  }
87
+ /**
88
+ * Returns the positive infinity value (`Infinity`).
89
+ *
90
+ * @returns {number} Positive infinity
91
+ * @example
92
+ * ```yaml
93
+ * set_pos_infinity:
94
+ * "@pipe":
95
+ * - ["{@symbol.posInfinity}"]
96
+ * ```
97
+ */
20
98
  posInfinity() {
21
99
  return Infinity;
22
100
  }
101
+ /**
102
+ * Returns the negative infinity value (`-Infinity`).
103
+ *
104
+ * @returns {number} Negative infinity
105
+ * @example
106
+ * ```yaml
107
+ * set_neg_infinity:
108
+ * "@pipe":
109
+ * - ["{@symbol.negInfinity}"]
110
+ * ```
111
+ */
23
112
  negInfinity() {
24
113
  return -Infinity;
25
114
  }
115
+ /**
116
+ * Returns the not-a-number value (`NaN`).
117
+ *
118
+ * @returns {number} NaN
119
+ * @example
120
+ * ```yaml
121
+ * set_nan:
122
+ * "@pipe":
123
+ * - ["{@symbol.NaN}"]
124
+ * ```
125
+ */
26
126
  NaN() {
27
127
  return NaN;
28
128
  }
129
+ /**
130
+ * Returns the current date and time as a Date object.
131
+ *
132
+ * @returns {Date} The current date and time
133
+ * @example
134
+ * ```yaml
135
+ * set_date:
136
+ * "@pipe":
137
+ * - ["{@symbol.date}"]
138
+ * - ["{@json.stringify}"]
139
+ * ```
140
+ */
29
141
  date() {
30
142
  return new Date();
31
143
  }
@@ -1,7 +1,72 @@
1
+ /**
2
+ * Provides unary operation functions for use in HotMesh mapping rules.
3
+ * The functions facilitate unary operations such as logical negation,
4
+ * making a number positive or negative, and bitwise negation during
5
+ * the mapping process. Each transformation is a function that expects
6
+ * one input parameter from the prior row in the `@pipe` structure.
7
+ *
8
+ * @remarks
9
+ * Invoked in mapping rules using `{@unary.<method>}` syntax.
10
+ */
1
11
  declare class UnaryHandler {
12
+ /**
13
+ * Returns the logical negation of a boolean value.
14
+ *
15
+ * @param {boolean} value - The boolean value to negate
16
+ * @returns {boolean} The negated boolean value
17
+ * @example
18
+ * ```yaml
19
+ * not_value:
20
+ * "@pipe":
21
+ * - ["{a.output.data.value}"]
22
+ * - ["{@unary.not}"]
23
+ * ```
24
+ */
2
25
  not(value: boolean): boolean;
26
+ /**
27
+ * Returns the positive representation of a number using
28
+ * the unary plus operator.
29
+ *
30
+ * @param {number} value - The number to convert
31
+ * @returns {number} The positive representation of the value
32
+ * @example
33
+ * ```yaml
34
+ * positive_value:
35
+ * "@pipe":
36
+ * - ["{a.output.data.value}"]
37
+ * - ["{@unary.positive}"]
38
+ * ```
39
+ */
3
40
  positive(value: number): number;
41
+ /**
42
+ * Returns the negative representation of a number using
43
+ * the unary negation operator.
44
+ *
45
+ * @param {number} value - The number to negate
46
+ * @returns {number} The negated number
47
+ * @example
48
+ * ```yaml
49
+ * negative_value:
50
+ * "@pipe":
51
+ * - ["{a.output.data.value}"]
52
+ * - ["{@unary.negative}"]
53
+ * ```
54
+ */
4
55
  negative(value: number): number;
56
+ /**
57
+ * Returns the bitwise negation of a number using the
58
+ * bitwise NOT operator (`~`).
59
+ *
60
+ * @param {number} value - The number to bitwise negate
61
+ * @returns {number} The bitwise negation of the value
62
+ * @example
63
+ * ```yaml
64
+ * bitwise_not_value:
65
+ * "@pipe":
66
+ * - ["{a.output.data.value}"]
67
+ * - ["{@unary.bitwise_not}"]
68
+ * ```
69
+ */
5
70
  bitwise_not(value: number): number;
6
71
  }
7
72
  export { UnaryHandler };
@@ -1,16 +1,81 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.UnaryHandler = void 0;
4
+ /**
5
+ * Provides unary operation functions for use in HotMesh mapping rules.
6
+ * The functions facilitate unary operations such as logical negation,
7
+ * making a number positive or negative, and bitwise negation during
8
+ * the mapping process. Each transformation is a function that expects
9
+ * one input parameter from the prior row in the `@pipe` structure.
10
+ *
11
+ * @remarks
12
+ * Invoked in mapping rules using `{@unary.<method>}` syntax.
13
+ */
4
14
  class UnaryHandler {
15
+ /**
16
+ * Returns the logical negation of a boolean value.
17
+ *
18
+ * @param {boolean} value - The boolean value to negate
19
+ * @returns {boolean} The negated boolean value
20
+ * @example
21
+ * ```yaml
22
+ * not_value:
23
+ * "@pipe":
24
+ * - ["{a.output.data.value}"]
25
+ * - ["{@unary.not}"]
26
+ * ```
27
+ */
5
28
  not(value) {
6
29
  return !value;
7
30
  }
31
+ /**
32
+ * Returns the positive representation of a number using
33
+ * the unary plus operator.
34
+ *
35
+ * @param {number} value - The number to convert
36
+ * @returns {number} The positive representation of the value
37
+ * @example
38
+ * ```yaml
39
+ * positive_value:
40
+ * "@pipe":
41
+ * - ["{a.output.data.value}"]
42
+ * - ["{@unary.positive}"]
43
+ * ```
44
+ */
8
45
  positive(value) {
9
46
  return +value;
10
47
  }
48
+ /**
49
+ * Returns the negative representation of a number using
50
+ * the unary negation operator.
51
+ *
52
+ * @param {number} value - The number to negate
53
+ * @returns {number} The negated number
54
+ * @example
55
+ * ```yaml
56
+ * negative_value:
57
+ * "@pipe":
58
+ * - ["{a.output.data.value}"]
59
+ * - ["{@unary.negative}"]
60
+ * ```
61
+ */
11
62
  negative(value) {
12
63
  return -value;
13
64
  }
65
+ /**
66
+ * Returns the bitwise negation of a number using the
67
+ * bitwise NOT operator (`~`).
68
+ *
69
+ * @param {number} value - The number to bitwise negate
70
+ * @returns {number} The bitwise negation of the value
71
+ * @example
72
+ * ```yaml
73
+ * bitwise_not_value:
74
+ * "@pipe":
75
+ * - ["{a.output.data.value}"]
76
+ * - ["{@unary.bitwise_not}"]
77
+ * ```
78
+ */
14
79
  bitwise_not(value) {
15
80
  return ~value;
16
81
  }
@@ -54,7 +54,7 @@ class QuorumService {
54
54
  * @private
55
55
  */
56
56
  async initStoreChannel(store) {
57
- this.store = await factory_2.StoreServiceFactory.init(store, this.namespace, this.appId, this.logger);
57
+ this.store = await factory_2.StoreServiceFactory.init(store, this.namespace, this.appId, this.logger, this.guid, 'quorum');
58
58
  }
59
59
  /**
60
60
  * @private
@@ -439,8 +439,26 @@ class ConsumptionManager {
439
439
  output = this.errorHandler.structureUnhandledError(input, err instanceof Error ? err : new Error(String(err)));
440
440
  }
441
441
  try {
442
- const messageId = await this.publishResponse(input, output);
443
- telemetry.setStreamAttributes({ 'app.worker.mid': messageId });
442
+ // When the ENGINE itself fails to process a message (e.g., schema not
443
+ // found, missing subscription), do NOT republish the error back to the
444
+ // engine stream — that creates an infinite poison loop. The engine
445
+ // When the ENGINE encounters an infrastructure error (schema not found,
446
+ // subscription missing — code 598), the message is permanently unprocessable.
447
+ // Do NOT republish it — that creates an infinite poison loop. Only suppress
448
+ // these specific infrastructure errors; application-level errors (retries,
449
+ // duplicates, workflow failures) must still flow through normally.
450
+ if (group === 'ENGINE' && output?.code === 598) {
451
+ this.logger.error(`stream-engine-dispatch-fatal`, {
452
+ stream, id, group,
453
+ aid: input.metadata?.aid,
454
+ jid: input.metadata?.jid,
455
+ message: output.data?.message,
456
+ });
457
+ }
458
+ else {
459
+ const messageId = await this.publishResponse(input, output);
460
+ telemetry.setStreamAttributes({ 'app.worker.mid': messageId });
461
+ }
444
462
  }
445
463
  catch (publishErr) {
446
464
  // If publishResponse fails, still ack the message to prevent
@@ -55,7 +55,7 @@ class ErrorHandler {
55
55
  }
56
56
  const result = {
57
57
  status: 'error',
58
- code: config_1.HMSH_CODE_UNKNOWN,
58
+ code: err.code || config_1.HMSH_CODE_UNKNOWN,
59
59
  metadata: { ...input.metadata, guid: (0, utils_1.guid)() },
60
60
  data: error,
61
61
  };
@@ -3,6 +3,6 @@ import { ProviderClient, ProviderTransaction } from '../../types/provider';
3
3
  import { StoreInitializable } from './providers/store-initializable';
4
4
  import { StoreService } from './index';
5
5
  declare class StoreServiceFactory {
6
- static init(providerClient: ProviderClient, namespace: string, appId: string, logger: ILogger): Promise<StoreService<ProviderClient, ProviderTransaction> & StoreInitializable>;
6
+ static init(providerClient: ProviderClient, namespace: string, appId: string, logger: ILogger, guid?: string, role?: string): Promise<StoreService<ProviderClient, ProviderTransaction> & StoreInitializable>;
7
7
  }
8
8
  export { StoreServiceFactory };
@@ -4,12 +4,12 @@ exports.StoreServiceFactory = void 0;
4
4
  const utils_1 = require("../../modules/utils");
5
5
  const postgres_1 = require("./providers/postgres/postgres");
6
6
  class StoreServiceFactory {
7
- static async init(providerClient, namespace, appId, logger) {
7
+ static async init(providerClient, namespace, appId, logger, guid, role) {
8
8
  let service;
9
9
  if ((0, utils_1.identifyProvider)(providerClient) === 'postgres') {
10
10
  service = new postgres_1.PostgresStoreService(providerClient);
11
11
  } //etc
12
- await service.init(namespace, appId, logger);
12
+ await service.init(namespace, appId, logger, guid, role);
13
13
  return service;
14
14
  }
15
15
  }
@@ -21,7 +21,7 @@ declare abstract class StoreService<Provider extends ProviderClient, Transaction
21
21
  serializer: Serializer;
22
22
  constructor(client: Provider);
23
23
  abstract transact(): TransactionProvider;
24
- abstract init(namespace: string, appId: string, logger: ILogger): Promise<HotMeshApps>;
24
+ abstract init(namespace: string, appId: string, logger: ILogger, guid?: string, role?: string): Promise<HotMeshApps>;
25
25
  abstract mintKey(type: KeyType, params: KeyStoreParams): string;
26
26
  abstract getSettings(bCreate?: boolean): Promise<HotMeshSettings>;
27
27
  abstract setSettings(manifest: HotMeshSettings): Promise<any>;
@@ -26,9 +26,19 @@ export declare class KVSQL {
26
26
  exec(...args: any[]): Promise<Array<any>>;
27
27
  mintKey(type: KeyType, params: KeyStoreParams): string;
28
28
  /**
29
- * Resolves the table name when provided a key
29
+ * Resolves the table name when provided a key.
30
+ * Public tables (applications, connections) are no longer routed through
31
+ * the KV layer — they use direct SQL in postgres.ts.
30
32
  */
31
33
  tableForKey(key: string, stats_type?: 'hash' | 'sorted_set' | 'list'): string;
34
+ /**
35
+ * Strips the `hmsh:<appId>:<entity>:` prefix from a full Redis-style key,
36
+ * keeping only the meaningful suffix for SQL storage. Applied only to the
37
+ * `key` column in SQL params — never to member values, field names, or values.
38
+ *
39
+ * Excluded tables (jobs, streams, job attributes) retain the full key.
40
+ */
41
+ storageKey(fullKey: string): string;
32
42
  safeName(input: string, prefix?: string): string;
33
43
  set: (key: string, value: string, options?: import("./kvtypes/hash/index").SetOptions, multi?: ProviderTransaction) => Promise<boolean>;
34
44
  _set: (key: string, value: string, options?: import("./kvtypes/hash/index").SetOptions) => {
@@ -110,16 +110,12 @@ class KVSQL {
110
110
  return '';
111
111
  }
112
112
  /**
113
- * Resolves the table name when provided a key
113
+ * Resolves the table name when provided a key.
114
+ * Public tables (applications, connections) are no longer routed through
115
+ * the KV layer — they use direct SQL in postgres.ts.
114
116
  */
115
117
  tableForKey(key, stats_type) {
116
- if (key === key_1.HMNS) {
117
- return 'public.hotmesh_connections';
118
- }
119
118
  const [_, appName, abbrev, ...rest] = key.split(':');
120
- if (appName === 'a') {
121
- return 'public.hotmesh_applications';
122
- }
123
119
  const id = rest?.length ? rest.join(':') : '';
124
120
  const entity = key_1.KeyService.resolveEntityType(abbrev, id);
125
121
  if (this.safeName(this.appId) !== this.safeName(appName)) {
@@ -145,13 +141,27 @@ class KVSQL {
145
141
  if (entity === 'unknown_entity') {
146
142
  throw new Error(`Unknown entity type abbreviation: ${abbrev}`);
147
143
  }
148
- else if (entity === 'applications') {
149
- return 'public.hotmesh_applications';
150
- }
151
144
  else {
152
145
  return `${schemaName}.${entity}`;
153
146
  }
154
147
  }
148
+ /**
149
+ * Strips the `hmsh:<appId>:<entity>:` prefix from a full Redis-style key,
150
+ * keeping only the meaningful suffix for SQL storage. Applied only to the
151
+ * `key` column in SQL params — never to member values, field names, or values.
152
+ *
153
+ * Excluded tables (jobs, streams, job attributes) retain the full key.
154
+ */
155
+ storageKey(fullKey) {
156
+ const parts = fullKey.split(':');
157
+ if (parts.length < 3)
158
+ return fullKey;
159
+ const entity = parts[2];
160
+ // Excluded tables: jobs (j), streams (x), job attributes (d)
161
+ if (entity === 'j' || entity === 'x' || entity === 'd')
162
+ return fullKey;
163
+ return parts.slice(3).join(':');
164
+ }
155
165
  safeName(input, prefix = '') {
156
166
  if (!input) {
157
167
  return 'connections';
@@ -186,6 +196,7 @@ class KVSQL {
186
196
  AND (expired_at IS NULL OR expired_at > NOW())
187
197
  LIMIT 1;
188
198
  `;
199
+ return { sql, params: [key] };
189
200
  }
190
201
  else {
191
202
  sql = `
@@ -194,9 +205,8 @@ class KVSQL {
194
205
  AND (expiry IS NULL OR expiry > NOW())
195
206
  LIMIT 1;
196
207
  `;
208
+ return { sql, params: [this.storageKey(key)] };
197
209
  }
198
- const params = [key];
199
- return { sql, params };
200
210
  }
201
211
  }
202
212
  exports.KVSQL = KVSQL;
@@ -137,6 +137,39 @@ const KVTables = (context) => ({
137
137
  for (const tableDef of tableDefinitions) {
138
138
  const fullTableName = `${tableDef.schema}.${tableDef.name}`;
139
139
  switch (tableDef.type) {
140
+ case 'relational_app':
141
+ await client.query(`
142
+ CREATE TABLE IF NOT EXISTS ${fullTableName} (
143
+ app_id TEXT PRIMARY KEY,
144
+ version TEXT NOT NULL DEFAULT '1',
145
+ active BOOLEAN DEFAULT TRUE,
146
+ settings JSONB DEFAULT '{}',
147
+ created_at TIMESTAMPTZ DEFAULT NOW(),
148
+ updated_at TIMESTAMPTZ DEFAULT NOW()
149
+ );
150
+ `);
151
+ await client.query(`
152
+ CREATE TABLE IF NOT EXISTS public.hmsh_application_versions (
153
+ app_id TEXT NOT NULL REFERENCES public.hmsh_applications(app_id) ON DELETE CASCADE,
154
+ version TEXT NOT NULL,
155
+ status TEXT NOT NULL DEFAULT 'deployed',
156
+ deployed_at TIMESTAMPTZ DEFAULT NOW(),
157
+ PRIMARY KEY (app_id, version)
158
+ );
159
+ `);
160
+ break;
161
+ case 'relational_connection':
162
+ await client.query(`
163
+ CREATE TABLE IF NOT EXISTS ${fullTableName} (
164
+ guid TEXT NOT NULL,
165
+ app_id TEXT NOT NULL,
166
+ role TEXT NOT NULL,
167
+ version TEXT NOT NULL,
168
+ connected_at TIMESTAMPTZ DEFAULT NOW(),
169
+ PRIMARY KEY (guid, app_id)
170
+ );
171
+ `);
172
+ break;
140
173
  case 'string':
141
174
  await client.query(`
142
175
  CREATE TABLE IF NOT EXISTS ${fullTableName} (
@@ -375,8 +408,8 @@ const KVTables = (context) => ({
375
408
  },
376
409
  getTableNames(appName) {
377
410
  const tableNames = [];
378
- // Applications table (only hotmesh prefix)
379
- tableNames.push('hotmesh_applications', 'hotmesh_connections');
411
+ // Public relational tables
412
+ tableNames.push('public.hmsh_applications', 'public.hmsh_application_versions', 'public.hmsh_connections');
380
413
  // Other tables with appName
381
414
  const tablesWithAppName = [
382
415
  'throttles',
@@ -404,13 +437,13 @@ const KVTables = (context) => ({
404
437
  const tableDefinitions = [
405
438
  {
406
439
  schema: 'public',
407
- name: 'hotmesh_applications',
408
- type: 'hash',
440
+ name: 'hmsh_applications',
441
+ type: 'relational_app',
409
442
  },
410
443
  {
411
444
  schema: 'public',
412
- name: 'hotmesh_connections',
413
- type: 'hash',
445
+ name: 'hmsh_connections',
446
+ type: 'relational_connection',
414
447
  },
415
448
  {
416
449
  schema: schemaName,