@hotmeshio/long-tail 0.5.2 → 0.5.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/build/lib/events/system-events.d.ts +19 -0
- package/build/lib/events/system-events.js +62 -0
- package/build/modules/ltconfig.d.ts +8 -0
- package/build/modules/ltconfig.js +10 -0
- package/build/services/escalation/bulk.d.ts +2 -1
- package/build/services/escalation/bulk.js +20 -19
- package/build/services/escalation/client.d.ts +22 -0
- package/build/services/escalation/client.js +141 -0
- package/build/services/escalation/crud.d.ts +29 -21
- package/build/services/escalation/crud.js +175 -140
- package/build/services/escalation/index.d.ts +1 -0
- package/build/services/escalation/index.js +3 -0
- package/build/services/escalation/map.d.ts +15 -0
- package/build/services/escalation/map.js +63 -0
- package/build/services/escalation/queries.js +64 -149
- package/build/services/escalation/sql.d.ts +13 -32
- package/build/services/escalation/sql.js +36 -176
- package/build/services/export/post-process.js +23 -4
- package/build/services/interceptor/activities/config.js +5 -1
- package/build/services/interceptor/index.d.ts +3 -0
- package/build/services/interceptor/index.js +7 -21
- package/build/services/mcp/db-server/schemas.d.ts +1 -1
- package/build/services/yaml-workflow/deployer.js +4 -0
- package/build/services/yaml-workflow/workers/register.js +3 -0
- package/build/start/index.js +2 -1
- package/build/start/workers.js +12 -0
- package/build/system/mcp-servers/admin/schemas.d.ts +1 -1
- package/build/system/mcp-servers/db-query/schemas.d.ts +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -19,32 +19,37 @@ exports.claimByMetadata = claimByMetadata;
|
|
|
19
19
|
exports.resolveByMetadataAtomic = resolveByMetadataAtomic;
|
|
20
20
|
const db_1 = require("../../lib/db");
|
|
21
21
|
const publish_1 = require("../../lib/events/publish");
|
|
22
|
-
const
|
|
22
|
+
const client_1 = require("./client");
|
|
23
|
+
const map_1 = require("./map");
|
|
23
24
|
const sql_1 = require("./sql");
|
|
25
|
+
// All escalation state lives in `public.hmsh_escalations` (HotMesh 0.22.3),
|
|
26
|
+
// reached through `client.escalations.*`. The function signatures and return
|
|
27
|
+
// shapes below are the frozen public surface — only the storage path changed.
|
|
28
|
+
// Generous upper bound for the "all escalations for X" lookups, which the
|
|
29
|
+
// legacy SQL returned without a LIMIT. Escalations per workflow/origin/task are
|
|
30
|
+
// few; this avoids a silent page cap without an unbounded scan.
|
|
31
|
+
const LOOKUP_LIMIT = 1000;
|
|
24
32
|
async function createEscalation(input) {
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
input.
|
|
31
|
-
input.
|
|
32
|
-
input.
|
|
33
|
-
input.
|
|
34
|
-
input.
|
|
35
|
-
input.
|
|
36
|
-
input.
|
|
37
|
-
input.
|
|
38
|
-
input.
|
|
39
|
-
|
|
40
|
-
input.
|
|
41
|
-
input.
|
|
42
|
-
input.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
input.span_id || null,
|
|
46
|
-
]);
|
|
47
|
-
const escalation = rows[0];
|
|
33
|
+
const client = await (0, client_1.escalations)();
|
|
34
|
+
const entry = await client.create({
|
|
35
|
+
type: input.type,
|
|
36
|
+
subtype: input.subtype,
|
|
37
|
+
description: input.description,
|
|
38
|
+
priority: input.priority ?? 2,
|
|
39
|
+
role: input.role,
|
|
40
|
+
taskId: input.task_id,
|
|
41
|
+
originId: input.origin_id,
|
|
42
|
+
parentId: input.parent_id,
|
|
43
|
+
workflowId: input.workflow_id,
|
|
44
|
+
taskQueue: input.task_queue,
|
|
45
|
+
workflowType: input.workflow_type,
|
|
46
|
+
traceId: input.trace_id,
|
|
47
|
+
spanId: input.span_id,
|
|
48
|
+
envelope: (0, map_1.toEnvelopeObject)(input.envelope),
|
|
49
|
+
metadata: input.metadata,
|
|
50
|
+
escalationPayload: (0, map_1.toJsonObject)(input.escalation_payload),
|
|
51
|
+
});
|
|
52
|
+
const escalation = (0, map_1.toEscalationRecord)(entry);
|
|
48
53
|
(0, publish_1.publishEscalationEvent)({
|
|
49
54
|
type: 'escalation.created',
|
|
50
55
|
source: 'service',
|
|
@@ -58,22 +63,16 @@ async function createEscalation(input) {
|
|
|
58
63
|
return escalation;
|
|
59
64
|
}
|
|
60
65
|
/**
|
|
61
|
-
* Atomic claim
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* Conditions:
|
|
65
|
-
* - status = 'pending' (not resolved/cancelled)
|
|
66
|
-
* - Either: unassigned, expired claim, or same user (extension)
|
|
67
|
-
*
|
|
68
|
-
* Uses a CTE to capture the previous state so callers can detect extensions.
|
|
66
|
+
* Atomic claim. Implicit model — status stays 'pending'; "claimed" is
|
|
67
|
+
* assigned_to + assigned_until > NOW(). `isExtension` is true when the same
|
|
68
|
+
* user re-claims (extends expiry). Returns null when the row is not claimable.
|
|
69
69
|
*/
|
|
70
70
|
async function claimEscalation(id, userId, durationMinutes = 30) {
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
71
|
+
const client = await (0, client_1.escalations)();
|
|
72
|
+
const result = await client.claim({ id, assignee: userId, durationMinutes });
|
|
73
|
+
if (!result.ok)
|
|
74
74
|
return null;
|
|
75
|
-
const
|
|
76
|
-
const escalation = row;
|
|
75
|
+
const escalation = (0, map_1.toEscalationRecord)(result.entry);
|
|
77
76
|
(0, publish_1.publishEscalationEvent)({
|
|
78
77
|
type: 'escalation.claimed',
|
|
79
78
|
source: 'service',
|
|
@@ -84,39 +83,41 @@ async function claimEscalation(id, userId, durationMinutes = 30) {
|
|
|
84
83
|
status: 'claimed',
|
|
85
84
|
data: { assigned_to: userId },
|
|
86
85
|
});
|
|
87
|
-
return {
|
|
88
|
-
escalation,
|
|
89
|
-
isExtension: row.prev_assigned_to === userId,
|
|
90
|
-
};
|
|
86
|
+
return { escalation, isExtension: result.isExtension };
|
|
91
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Mark an escalation resolved. Signal delivery is owned by the resolution
|
|
90
|
+
* orchestrator (api/escalations/resolve.ts); service-created rows have no
|
|
91
|
+
* signal_key, so this never delivers a signal itself. Returns null when the
|
|
92
|
+
* row is missing or already terminal.
|
|
93
|
+
*/
|
|
92
94
|
async function resolveEscalation(id, resolverPayload) {
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
}
|
|
95
|
+
const client = await (0, client_1.escalations)();
|
|
96
|
+
const result = await client.resolve({ id, resolverPayload });
|
|
97
|
+
if (!result.ok)
|
|
98
|
+
return null;
|
|
99
|
+
const escalation = (0, map_1.toEscalationRecord)(result.entry);
|
|
100
|
+
(0, publish_1.publishEscalationEvent)({
|
|
101
|
+
type: 'escalation.resolved',
|
|
102
|
+
source: 'service',
|
|
103
|
+
workflowId: escalation.workflow_id || '',
|
|
104
|
+
workflowName: escalation.workflow_type || '',
|
|
105
|
+
taskQueue: escalation.task_queue || '',
|
|
106
|
+
escalationId: escalation.id,
|
|
107
|
+
status: 'resolved',
|
|
108
|
+
data: {},
|
|
109
|
+
});
|
|
108
110
|
return escalation;
|
|
109
111
|
}
|
|
110
112
|
/**
|
|
111
|
-
* Bulk update priority for a set of escalations.
|
|
112
|
-
*
|
|
113
|
+
* Bulk update priority for a set of escalations. Only pending escalations are
|
|
114
|
+
* updated.
|
|
113
115
|
*/
|
|
114
116
|
async function updateEscalationsPriority(ids, priority) {
|
|
115
117
|
if (ids.length === 0)
|
|
116
118
|
return 0;
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
return rowCount ?? 0;
|
|
119
|
+
const client = await (0, client_1.escalations)();
|
|
120
|
+
return client.updateManyPriority({ ids, priority });
|
|
120
121
|
}
|
|
121
122
|
/**
|
|
122
123
|
* Get the distinct roles for a set of escalation IDs.
|
|
@@ -125,113 +126,145 @@ async function updateEscalationsPriority(ids, priority) {
|
|
|
125
126
|
async function getEscalationRoles(ids) {
|
|
126
127
|
if (ids.length === 0)
|
|
127
128
|
return [];
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
return rows.map(
|
|
129
|
+
const client = await (0, client_1.escalations)();
|
|
130
|
+
const rows = await client.list({ ids, limit: LOOKUP_LIMIT });
|
|
131
|
+
return [...new Set(rows.map(r => r.role).filter((r) => !!r))];
|
|
131
132
|
}
|
|
132
133
|
/**
|
|
133
134
|
* Release a single escalation claim back to the available pool.
|
|
134
135
|
* Only the assigned user (or superadmin via route) may release.
|
|
135
136
|
*/
|
|
136
137
|
async function releaseEscalation(id, userId) {
|
|
137
|
-
const
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return released
|
|
138
|
+
const client = await (0, client_1.escalations)();
|
|
139
|
+
const result = await client.release({ id, assignee: userId });
|
|
140
|
+
if (!result.ok)
|
|
141
|
+
return null;
|
|
142
|
+
const released = (0, map_1.toEscalationRecord)(result.entry);
|
|
143
|
+
(0, publish_1.publishEscalationEvent)({
|
|
144
|
+
type: 'escalation.released',
|
|
145
|
+
source: 'service',
|
|
146
|
+
workflowId: released.workflow_id || '',
|
|
147
|
+
workflowName: released.workflow_type || '',
|
|
148
|
+
taskQueue: released.task_queue || '',
|
|
149
|
+
escalationId: released.id,
|
|
150
|
+
status: 'released',
|
|
151
|
+
data: { released_by: userId },
|
|
152
|
+
});
|
|
153
|
+
return released;
|
|
153
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Sweep expired claims back to the available pool, returning the count cleared.
|
|
157
|
+
* Availability is already query-time in the implicit model, but long-tail's
|
|
158
|
+
* public contract clears `assigned_to` and returns a count, so this runs as a
|
|
159
|
+
* single direct UPDATE on the shared table (the SDK's releaseExpired is a no-op).
|
|
160
|
+
*/
|
|
154
161
|
async function releaseExpiredClaims() {
|
|
162
|
+
await (0, client_1.ensureEscalationCompatView)();
|
|
155
163
|
const pool = (0, db_1.getPool)();
|
|
156
164
|
const { rowCount } = await pool.query(sql_1.RELEASE_EXPIRED_CLAIMS);
|
|
157
|
-
return rowCount
|
|
165
|
+
return rowCount ?? 0;
|
|
158
166
|
}
|
|
159
167
|
/**
|
|
160
168
|
* Reassign an escalation to a different role.
|
|
161
169
|
* Clears the current assignment so it becomes available to the new role.
|
|
162
170
|
*/
|
|
163
171
|
async function escalateToRole(id, targetRole) {
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
return
|
|
172
|
+
const client = await (0, client_1.escalations)();
|
|
173
|
+
const entry = await client.escalateToRole({ id, targetRole });
|
|
174
|
+
return entry ? (0, map_1.toEscalationRecord)(entry) : null;
|
|
167
175
|
}
|
|
168
176
|
async function getEscalation(id) {
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
return
|
|
177
|
+
const client = await (0, client_1.escalations)();
|
|
178
|
+
const entry = await client.get(id);
|
|
179
|
+
return entry ? (0, map_1.toEscalationRecord)(entry) : null;
|
|
172
180
|
}
|
|
173
181
|
async function getEscalationsByTaskId(taskId) {
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
|
|
182
|
+
const client = await (0, client_1.escalations)();
|
|
183
|
+
const rows = await client.list({
|
|
184
|
+
taskId,
|
|
185
|
+
sortBy: 'created_at',
|
|
186
|
+
sortOrder: 'desc',
|
|
187
|
+
limit: LOOKUP_LIMIT,
|
|
188
|
+
});
|
|
189
|
+
return (0, map_1.toEscalationRecords)(rows);
|
|
177
190
|
}
|
|
178
191
|
async function getEscalationsByWorkflowId(workflowId) {
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
|
|
192
|
+
const client = await (0, client_1.escalations)();
|
|
193
|
+
const rows = await client.list({
|
|
194
|
+
workflowId,
|
|
195
|
+
sortBy: 'created_at',
|
|
196
|
+
sortOrder: 'desc',
|
|
197
|
+
limit: LOOKUP_LIMIT,
|
|
198
|
+
});
|
|
199
|
+
return (0, map_1.toEscalationRecords)(rows);
|
|
182
200
|
}
|
|
183
201
|
async function updateEscalationMetadata(id, patch) {
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
return
|
|
202
|
+
const client = await (0, client_1.escalations)();
|
|
203
|
+
const entry = await client.update({ id, metadata: patch });
|
|
204
|
+
return entry ? (0, map_1.toEscalationRecord)(entry) : null;
|
|
187
205
|
}
|
|
188
206
|
async function enrichEscalationRouting(id, metadataPatch, workflowFields) {
|
|
189
|
-
const
|
|
190
|
-
const
|
|
207
|
+
const client = await (0, client_1.escalations)();
|
|
208
|
+
const entry = await client.update({
|
|
191
209
|
id,
|
|
192
|
-
|
|
193
|
-
workflowFields.workflowType
|
|
194
|
-
workflowFields.workflowId
|
|
195
|
-
workflowFields.taskQueue
|
|
196
|
-
workflowFields.taskId
|
|
197
|
-
|
|
198
|
-
return
|
|
210
|
+
metadata: metadataPatch,
|
|
211
|
+
workflowType: workflowFields.workflowType,
|
|
212
|
+
workflowId: workflowFields.workflowId,
|
|
213
|
+
taskQueue: workflowFields.taskQueue,
|
|
214
|
+
taskId: workflowFields.taskId,
|
|
215
|
+
});
|
|
216
|
+
return entry ? (0, map_1.toEscalationRecord)(entry) : null;
|
|
199
217
|
}
|
|
200
218
|
async function getEscalationsByOriginId(originId) {
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
|
|
219
|
+
const client = await (0, client_1.escalations)();
|
|
220
|
+
const rows = await client.list({
|
|
221
|
+
originId,
|
|
222
|
+
sortBy: 'created_at',
|
|
223
|
+
sortOrder: 'desc',
|
|
224
|
+
limit: LOOKUP_LIMIT,
|
|
225
|
+
});
|
|
226
|
+
return (0, map_1.toEscalationRecords)(rows);
|
|
204
227
|
}
|
|
205
228
|
// --- Metadata candidate key lookups -----------------------------------------
|
|
206
229
|
async function findByMetadata(key, value, status, limit = 50, offset = 0) {
|
|
207
|
-
const
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
230
|
+
const client = await (0, client_1.escalations)();
|
|
231
|
+
const metadata = { [key]: value };
|
|
232
|
+
const [rows, total] = await Promise.all([
|
|
233
|
+
client.list({
|
|
234
|
+
metadata,
|
|
235
|
+
status,
|
|
236
|
+
orderBy: [
|
|
237
|
+
{ column: 'priority', direction: 'asc' },
|
|
238
|
+
{ column: 'created_at', direction: 'asc' },
|
|
239
|
+
],
|
|
240
|
+
limit,
|
|
241
|
+
offset,
|
|
242
|
+
}),
|
|
243
|
+
client.count({ metadata, status }),
|
|
244
|
+
]);
|
|
245
|
+
return { escalations: (0, map_1.toEscalationRecords)(rows), total };
|
|
214
246
|
}
|
|
215
247
|
/**
|
|
216
|
-
* Atomic claim by metadata with inline RBAC.
|
|
217
|
-
* The
|
|
218
|
-
*
|
|
219
|
-
*
|
|
248
|
+
* Atomic claim by metadata with inline RBAC and optional metadata merge.
|
|
249
|
+
* The SDK enforces the role filter in SQL — callers without an allowed role
|
|
250
|
+
* match zero rows. Returns `{ escalation, isExtension, candidatesExist }` or
|
|
251
|
+
* null when nothing was claimed.
|
|
220
252
|
*
|
|
221
|
-
* @param allowedRoles — roles the caller can claim (null = no filter / global
|
|
222
|
-
* @returns `{ escalation, isExtension, candidatesExist }` or null
|
|
253
|
+
* @param allowedRoles — roles the caller can claim (null = no filter / global)
|
|
223
254
|
*/
|
|
224
255
|
async function claimByMetadata(key, value, userId, durationMinutes = 30, metadata, allowedRoles) {
|
|
225
|
-
const
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
256
|
+
const client = await (0, client_1.escalations)();
|
|
257
|
+
const result = await client.claimByMetadata({
|
|
258
|
+
key,
|
|
259
|
+
value,
|
|
260
|
+
assignee: userId,
|
|
261
|
+
durationMinutes,
|
|
262
|
+
roles: allowedRoles === null ? undefined : allowedRoles,
|
|
263
|
+
metadata,
|
|
264
|
+
});
|
|
265
|
+
if (!result.ok)
|
|
231
266
|
return null;
|
|
232
|
-
const
|
|
233
|
-
const { candidates_exist, prev_assigned_to, _total, ...rest } = row;
|
|
234
|
-
const escalation = rest;
|
|
267
|
+
const escalation = (0, map_1.toEscalationRecord)(result.entry);
|
|
235
268
|
(0, publish_1.publishEscalationEvent)({
|
|
236
269
|
type: 'escalation.claimed',
|
|
237
270
|
source: 'service',
|
|
@@ -244,19 +277,22 @@ async function claimByMetadata(key, value, userId, durationMinutes = 30, metadat
|
|
|
244
277
|
});
|
|
245
278
|
return {
|
|
246
279
|
escalation,
|
|
247
|
-
isExtension:
|
|
248
|
-
candidatesExist:
|
|
280
|
+
isExtension: result.isExtension,
|
|
281
|
+
candidatesExist: result.candidatesExist,
|
|
249
282
|
};
|
|
250
283
|
}
|
|
251
284
|
/**
|
|
252
|
-
* Atomic resolve by metadata with signal guard.
|
|
285
|
+
* Atomic resolve by metadata with signal guard, in a single CTE.
|
|
253
286
|
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
287
|
+
* Signal-backed rows (those carrying `metadata.signal_id`) are NOT resolved
|
|
288
|
+
* here — long-tail signals the paused workflow and the workflow interceptor
|
|
289
|
+
* resolves durably. If the workflow is gone the signal fails and the row stays
|
|
290
|
+
* pending, which is the contract the route suite pins. This guard is long-tail
|
|
291
|
+
* business logic over the shared table, so it runs as one atomic statement on
|
|
292
|
+
* `hmsh_escalations` rather than through the generic SDK resolve.
|
|
258
293
|
*/
|
|
259
294
|
async function resolveByMetadataAtomic(key, value, userId, resolverPayload, metadata, allowedRoles) {
|
|
295
|
+
await (0, client_1.ensureEscalationCompatView)();
|
|
260
296
|
const pool = (0, db_1.getPool)();
|
|
261
297
|
const filter = JSON.stringify({ [key]: value });
|
|
262
298
|
const payloadJson = JSON.stringify(resolverPayload);
|
|
@@ -267,8 +303,7 @@ async function resolveByMetadataAtomic(key, value, userId, resolverPayload, meta
|
|
|
267
303
|
return { outcome: 'not_found' };
|
|
268
304
|
const row = rows[0];
|
|
269
305
|
if (row.outcome === 'resolved') {
|
|
270
|
-
const
|
|
271
|
-
const escalation = rest;
|
|
306
|
+
const escalation = (0, map_1.toEscalationRecord)(row);
|
|
272
307
|
(0, publish_1.publishEscalationEvent)({
|
|
273
308
|
type: 'escalation.resolved',
|
|
274
309
|
source: 'service',
|
|
@@ -281,7 +316,7 @@ async function resolveByMetadataAtomic(key, value, userId, resolverPayload, meta
|
|
|
281
316
|
});
|
|
282
317
|
return { outcome: 'resolved', escalation };
|
|
283
318
|
}
|
|
284
|
-
// Signal-backed escalation — return the signal info for the caller
|
|
319
|
+
// Signal-backed escalation — return the signal info for the caller to deliver.
|
|
285
320
|
return {
|
|
286
321
|
outcome: 'signal_required',
|
|
287
322
|
signalId: row.signal_id,
|
|
@@ -14,7 +14,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.ensureEscalationCompatView = void 0;
|
|
17
18
|
__exportStar(require("./types"), exports);
|
|
18
19
|
__exportStar(require("./crud"), exports);
|
|
19
20
|
__exportStar(require("./bulk"), exports);
|
|
20
21
|
__exportStar(require("./queries"), exports);
|
|
22
|
+
var client_1 = require("./client");
|
|
23
|
+
Object.defineProperty(exports, "ensureEscalationCompatView", { enumerable: true, get: function () { return client_1.ensureEscalationCompatView; } });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Types } from '@hotmeshio/hotmesh';
|
|
2
|
+
import type { LTEscalationRecord } from '../../types';
|
|
3
|
+
type EscalationEntry = Types.EscalationEntry;
|
|
4
|
+
/** SDK row → long-tail public record. */
|
|
5
|
+
export declare function toEscalationRecord(entry: EscalationEntry): LTEscalationRecord;
|
|
6
|
+
export declare function toEscalationRecords(entries: EscalationEntry[]): LTEscalationRecord[];
|
|
7
|
+
/**
|
|
8
|
+
* Parse a TEXT/JSON-string field from the public input shape into the JSONB
|
|
9
|
+
* object the SDK expects. Returns `undefined` (field omitted) for empty/invalid
|
|
10
|
+
* input so the column stays NULL rather than storing garbage.
|
|
11
|
+
*/
|
|
12
|
+
export declare function toJsonObject(value: string | null | undefined): Record<string, unknown> | undefined;
|
|
13
|
+
/** Parse an envelope string, always yielding an object (defaults to `{}`). */
|
|
14
|
+
export declare function toEnvelopeObject(value: string | null | undefined): Record<string, unknown>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toEscalationRecord = toEscalationRecord;
|
|
4
|
+
exports.toEscalationRecords = toEscalationRecords;
|
|
5
|
+
exports.toJsonObject = toJsonObject;
|
|
6
|
+
exports.toEnvelopeObject = toEnvelopeObject;
|
|
7
|
+
/** Serialize a JSONB object column back to the TEXT form the public shape uses. */
|
|
8
|
+
function toJsonText(value) {
|
|
9
|
+
return value == null ? null : JSON.stringify(value);
|
|
10
|
+
}
|
|
11
|
+
/** SDK row → long-tail public record. */
|
|
12
|
+
function toEscalationRecord(entry) {
|
|
13
|
+
return {
|
|
14
|
+
id: entry.id,
|
|
15
|
+
type: entry.type ?? '',
|
|
16
|
+
subtype: entry.subtype ?? '',
|
|
17
|
+
description: entry.description,
|
|
18
|
+
status: entry.status,
|
|
19
|
+
priority: entry.priority,
|
|
20
|
+
task_id: entry.task_id,
|
|
21
|
+
origin_id: entry.origin_id,
|
|
22
|
+
parent_id: entry.parent_id,
|
|
23
|
+
workflow_id: entry.workflow_id,
|
|
24
|
+
task_queue: entry.task_queue,
|
|
25
|
+
workflow_type: entry.workflow_type,
|
|
26
|
+
role: entry.role ?? '',
|
|
27
|
+
assigned_to: entry.assigned_to,
|
|
28
|
+
assigned_until: entry.assigned_until,
|
|
29
|
+
resolved_at: entry.resolved_at,
|
|
30
|
+
claimed_at: entry.claimed_at,
|
|
31
|
+
envelope: entry.envelope == null ? '{}' : JSON.stringify(entry.envelope),
|
|
32
|
+
metadata: entry.metadata,
|
|
33
|
+
escalation_payload: toJsonText(entry.escalation_payload),
|
|
34
|
+
resolver_payload: toJsonText(entry.resolver_payload),
|
|
35
|
+
trace_id: entry.trace_id,
|
|
36
|
+
span_id: entry.span_id,
|
|
37
|
+
created_at: entry.created_at,
|
|
38
|
+
updated_at: entry.updated_at,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function toEscalationRecords(entries) {
|
|
42
|
+
return entries.map(toEscalationRecord);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parse a TEXT/JSON-string field from the public input shape into the JSONB
|
|
46
|
+
* object the SDK expects. Returns `undefined` (field omitted) for empty/invalid
|
|
47
|
+
* input so the column stays NULL rather than storing garbage.
|
|
48
|
+
*/
|
|
49
|
+
function toJsonObject(value) {
|
|
50
|
+
if (value == null || value === '')
|
|
51
|
+
return undefined;
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(value);
|
|
54
|
+
return parsed && typeof parsed === 'object' ? parsed : undefined;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Parse an envelope string, always yielding an object (defaults to `{}`). */
|
|
61
|
+
function toEnvelopeObject(value) {
|
|
62
|
+
return toJsonObject(value) ?? {};
|
|
63
|
+
}
|