@forgehive/hive-sdk 0.0.4 → 0.1.0

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/dist/index.d.ts CHANGED
@@ -1,14 +1,15 @@
1
1
  export interface Metadata {
2
2
  [key: string]: string;
3
3
  }
4
- export interface LogItemInput {
5
- input: unknown;
6
- output?: unknown;
7
- error?: unknown;
8
- boundaries?: unknown;
4
+ export interface ExecutionRecord<InputType = unknown, OutputType = unknown, B = unknown> {
5
+ input: InputType;
6
+ output?: OutputType;
7
+ error?: string;
8
+ boundaries?: B;
9
+ taskName?: string;
9
10
  metadata?: Metadata;
11
+ type?: 'success' | 'error' | 'pending';
10
12
  }
11
- export type LogItem = LogItemInput;
12
13
  export interface HiveLogClientConfig {
13
14
  projectName: string;
14
15
  apiKey?: string;
@@ -20,17 +21,7 @@ export interface LogApiResponse {
20
21
  uuid: string;
21
22
  taskName: string;
22
23
  projectName: string;
23
- logItem: {
24
- input: unknown;
25
- output?: unknown;
26
- error?: unknown;
27
- boundaries?: Record<string, Array<{
28
- input: unknown;
29
- output: unknown;
30
- error: unknown;
31
- }>>;
32
- metadata?: Metadata;
33
- };
24
+ logItem: ExecutionRecord;
34
25
  replayFrom?: string;
35
26
  createdAt: string;
36
27
  }
@@ -56,10 +47,8 @@ export declare class HiveLogClient {
56
47
  constructor(config: HiveLogClientConfig);
57
48
  isActive(): boolean;
58
49
  private mergeMetadata;
59
- sendLog<T extends {
60
- input: unknown;
61
- metadata?: Metadata;
62
- }>(taskName: string, logItem: T, metadata?: Metadata): Promise<'success' | 'error' | 'silent'>;
50
+ sendLog(record: ExecutionRecord, metadata?: Metadata): Promise<'success' | 'error' | 'silent'>;
51
+ getListener(): (record: ExecutionRecord) => Promise<void>;
63
52
  getLog(taskName: string, uuid: string): Promise<LogApiResult | null>;
64
53
  setQuality(taskName: string, uuid: string, quality: Quality): Promise<boolean>;
65
54
  }
package/dist/index.js CHANGED
@@ -38,12 +38,12 @@ class HiveLogClient {
38
38
  isActive() {
39
39
  return this.isInitialized;
40
40
  }
41
- mergeMetadata(logItem, sendLogMetadata) {
41
+ mergeMetadata(record, sendLogMetadata) {
42
42
  // Start with base metadata from client
43
43
  let finalMetadata = { ...this.baseMetadata };
44
- // Merge with logItem metadata if it exists
45
- if (logItem.metadata) {
46
- finalMetadata = { ...finalMetadata, ...logItem.metadata };
44
+ // Merge with record metadata if it exists
45
+ if (record.metadata) {
46
+ finalMetadata = { ...finalMetadata, ...record.metadata };
47
47
  }
48
48
  // Merge with sendLog metadata (highest priority)
49
49
  if (sendLogMetadata) {
@@ -51,7 +51,9 @@ class HiveLogClient {
51
51
  }
52
52
  return finalMetadata;
53
53
  }
54
- async sendLog(taskName, logItem, metadata) {
54
+ async sendLog(record, metadata) {
55
+ // Extract taskName from record
56
+ const taskName = record.taskName || 'unknown-task';
55
57
  if (!this.isInitialized) {
56
58
  log('Silent mode: Skipping sendLog for task "%s" - client not initialized', taskName);
57
59
  return 'silent';
@@ -60,17 +62,18 @@ class HiveLogClient {
60
62
  const logsUrl = `${this.host}/api/tasks/log-ingest`;
61
63
  log('Sending log for task "%s" to %s', taskName, logsUrl);
62
64
  const authToken = `${this.apiKey}:${this.apiSecret}`;
63
- // Merge metadata with priority: sendLog > logItem > client
64
- const finalMetadata = this.mergeMetadata(logItem, metadata);
65
- // Create enhanced logItem with merged metadata
66
- const enhancedLogItem = {
67
- ...logItem,
65
+ // Merge metadata with priority: sendLog > record.metadata > client
66
+ const finalMetadata = this.mergeMetadata(record, metadata);
67
+ // Create logItem with merged metadata
68
+ const logItem = {
69
+ ...record,
70
+ taskName,
68
71
  metadata: finalMetadata
69
72
  };
70
73
  await axios_1.default.post(logsUrl, {
71
74
  projectName: this.projectName,
72
75
  taskName,
73
- logItem: JSON.stringify(enhancedLogItem)
76
+ logItem: JSON.stringify(logItem)
74
77
  }, {
75
78
  headers: {
76
79
  Authorization: `Bearer ${authToken}`,
@@ -86,6 +89,11 @@ class HiveLogClient {
86
89
  return 'error';
87
90
  }
88
91
  }
92
+ getListener() {
93
+ return async (record) => {
94
+ await this.sendLog(record);
95
+ };
96
+ }
89
97
  async getLog(taskName, uuid) {
90
98
  if (!this.isInitialized) {
91
99
  log('Error: getLog for task "%s" with uuid "%s" - missing credentials', taskName, uuid);
@@ -31,7 +31,7 @@ describe('HiveLogClient getLog', () => {
31
31
  logItem: {
32
32
  input: { userId: 123, action: 'login' },
33
33
  output: { success: true, sessionId: 'abc123' },
34
- error: null,
34
+ error: undefined,
35
35
  boundaries: {
36
36
  database: [{ input: 'SELECT * FROM users', output: [{ id: 123 }], error: null }]
37
37
  }
@@ -82,7 +82,14 @@ describe('Hive SDK', () => {
82
82
  silentClient = new index_1.HiveLogClient({ projectName: 'silent-project' });
83
83
  });
84
84
  it('should return "silent" for sendLog in silent mode', async () => {
85
- const result = await silentClient.sendLog('test-task', { input: 'test' });
85
+ const executionRecord = {
86
+ input: { test: 'value' },
87
+ taskName: 'test-task',
88
+ type: 'success',
89
+ boundaries: {},
90
+ metadata: {}
91
+ };
92
+ const result = await silentClient.sendLog(executionRecord);
86
93
  expect(result).toBe('silent');
87
94
  });
88
95
  it('should throw error for getLog in silent mode', async () => {
@@ -67,8 +67,15 @@ describe('HiveLogClient Metadata', () => {
67
67
  });
68
68
  it('should send log without metadata parameter', async () => {
69
69
  mockedAxios.post.mockResolvedValueOnce({ data: { success: true } });
70
- const logItem = { input: 'test-input', output: 'test-output' };
71
- const result = await client.sendLog('test-task', logItem);
70
+ const executionRecord = {
71
+ input: 'test-input',
72
+ output: 'test-output',
73
+ taskName: 'test-task',
74
+ type: 'success',
75
+ boundaries: {},
76
+ metadata: {}
77
+ };
78
+ const result = await client.sendLog(executionRecord);
72
79
  expect(result).toBe('success');
73
80
  expect(mockedAxios.post).toHaveBeenCalledWith('https://test-host.com/api/tasks/log-ingest', {
74
81
  projectName: 'test-project',
@@ -76,6 +83,9 @@ describe('HiveLogClient Metadata', () => {
76
83
  logItem: JSON.stringify({
77
84
  input: 'test-input',
78
85
  output: 'test-output',
86
+ taskName: 'test-task',
87
+ type: 'success',
88
+ boundaries: {},
79
89
  metadata: {}
80
90
  })
81
91
  }, {
@@ -87,12 +97,19 @@ describe('HiveLogClient Metadata', () => {
87
97
  });
88
98
  it('should send log with metadata parameter', async () => {
89
99
  mockedAxios.post.mockResolvedValueOnce({ data: { success: true } });
90
- const logItem = { input: 'test-input', output: 'test-output' };
100
+ const executionRecord = {
101
+ input: 'test-input',
102
+ output: 'test-output',
103
+ taskName: 'test-task',
104
+ type: 'success',
105
+ boundaries: {},
106
+ metadata: {}
107
+ };
91
108
  const metadata = {
92
109
  requestId: 'req-123',
93
110
  userId: 'user-456'
94
111
  };
95
- const result = await client.sendLog('test-task', logItem, metadata);
112
+ const result = await client.sendLog(executionRecord, metadata);
96
113
  expect(result).toBe('success');
97
114
  expect(mockedAxios.post).toHaveBeenCalledWith('https://test-host.com/api/tasks/log-ingest', {
98
115
  projectName: 'test-project',
@@ -100,6 +117,9 @@ describe('HiveLogClient Metadata', () => {
100
117
  logItem: JSON.stringify({
101
118
  input: 'test-input',
102
119
  output: 'test-output',
120
+ taskName: 'test-task',
121
+ type: 'success',
122
+ boundaries: {},
103
123
  metadata: {
104
124
  requestId: 'req-123',
105
125
  userId: 'user-456'
@@ -115,13 +135,16 @@ describe('HiveLogClient Metadata', () => {
115
135
  it('should handle logItem with only input property', async () => {
116
136
  mockedAxios.post.mockResolvedValueOnce({ data: { success: true } });
117
137
  const metadata = { type: 'minimal' };
118
- const result = await client.sendLog('test-task', { input: 'simple input' }, metadata);
138
+ const result = await client.sendLog({ input: 'simple input', taskName: 'test-task', type: 'success', boundaries: {}, metadata: {} }, metadata);
119
139
  expect(result).toBe('success');
120
140
  expect(mockedAxios.post).toHaveBeenCalledWith('https://test-host.com/api/tasks/log-ingest', {
121
141
  projectName: 'test-project',
122
142
  taskName: 'test-task',
123
143
  logItem: JSON.stringify({
124
144
  input: 'simple input',
145
+ taskName: 'test-task',
146
+ type: 'success',
147
+ boundaries: {},
125
148
  metadata: { type: 'minimal' }
126
149
  })
127
150
  }, expect.any(Object));
@@ -129,13 +152,16 @@ describe('HiveLogClient Metadata', () => {
129
152
  it('should handle logItem with null input values', async () => {
130
153
  mockedAxios.post.mockResolvedValueOnce({ data: { success: true } });
131
154
  const metadata = { type: 'null-test' };
132
- const result = await client.sendLog('test-task', { input: null }, metadata);
155
+ const result = await client.sendLog({ input: null, taskName: 'test-task', type: 'success', boundaries: {}, metadata: {} }, metadata);
133
156
  expect(result).toBe('success');
134
157
  expect(mockedAxios.post).toHaveBeenCalledWith('https://test-host.com/api/tasks/log-ingest', {
135
158
  projectName: 'test-project',
136
159
  taskName: 'test-task',
137
160
  logItem: JSON.stringify({
138
161
  input: null,
162
+ taskName: 'test-task',
163
+ type: 'success',
164
+ boundaries: {},
139
165
  metadata: { type: 'null-test' }
140
166
  })
141
167
  }, expect.any(Object));
@@ -150,9 +176,12 @@ describe('HiveLogClient Metadata', () => {
150
176
  };
151
177
  const client = new index_1.HiveLogClient({ ...testConfig, metadata: baseMetadata });
152
178
  const logItem = { input: 'test' };
153
- await client.sendLog('test-task', logItem);
179
+ await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success', boundaries: {}, metadata: {} });
154
180
  const expectedLogItem = {
155
181
  input: 'test',
182
+ taskName: 'test-task',
183
+ type: 'success',
184
+ boundaries: {},
156
185
  metadata: {
157
186
  environment: 'production',
158
187
  version: '1.0.0'
@@ -178,14 +207,17 @@ describe('HiveLogClient Metadata', () => {
178
207
  version: '1.1.0' // This should override base version
179
208
  }
180
209
  };
181
- await client.sendLog('test-task', logItem);
210
+ await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success', boundaries: {}, metadata: logItem.metadata || {} });
182
211
  const expectedLogItem = {
183
212
  input: 'test',
184
213
  metadata: {
185
214
  environment: 'production', // from base
186
215
  version: '1.1.0', // from logItem (overrides base)
187
216
  sessionId: 'session-123' // from logItem
188
- }
217
+ },
218
+ taskName: 'test-task',
219
+ type: 'success',
220
+ boundaries: {}
189
221
  };
190
222
  expect(mockedAxios.post).toHaveBeenCalledWith('https://test-host.com/api/tasks/log-ingest', {
191
223
  projectName: 'test-project',
@@ -214,7 +246,7 @@ describe('HiveLogClient Metadata', () => {
214
246
  version: '1.2.0',
215
247
  priority: 'sendLog'
216
248
  };
217
- await client.sendLog('test-task', logItem, sendLogMetadata);
249
+ await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success', boundaries: {}, metadata: logItem.metadata || {} }, sendLogMetadata);
218
250
  const expectedLogItem = {
219
251
  input: 'test',
220
252
  metadata: {
@@ -223,7 +255,10 @@ describe('HiveLogClient Metadata', () => {
223
255
  priority: 'sendLog', // from sendLog (highest priority)
224
256
  sessionId: 'session-123', // from logItem
225
257
  requestId: 'req-456' // from sendLog
226
- }
258
+ },
259
+ taskName: 'test-task',
260
+ type: 'success',
261
+ boundaries: {}
227
262
  };
228
263
  expect(mockedAxios.post).toHaveBeenCalledWith('https://test-host.com/api/tasks/log-ingest', {
229
264
  projectName: 'test-project',
@@ -254,7 +289,7 @@ describe('HiveLogClient Metadata', () => {
254
289
  userId: 'user-123',
255
290
  version: '1.2.0' // overrides both base and logItem
256
291
  };
257
- await client.sendLog('search-task', logItem, sendLogMetadata);
292
+ await client.sendLog({ ...logItem, taskName: 'search-task', type: 'success', boundaries: {}, metadata: logItem.metadata || {} }, sendLogMetadata);
258
293
  const expectedLogItem = {
259
294
  input: { query: 'search' },
260
295
  output: { results: [] },
@@ -267,7 +302,10 @@ describe('HiveLogClient Metadata', () => {
267
302
  processingTime: '250', // from logItem
268
303
  requestId: 'req-789', // from sendLog
269
304
  userId: 'user-123' // from sendLog
270
- }
305
+ },
306
+ taskName: 'search-task',
307
+ type: 'success',
308
+ boundaries: {}
271
309
  };
272
310
  expect(mockedAxios.post).toHaveBeenCalledWith('https://test-host.com/api/tasks/log-ingest', {
273
311
  projectName: 'test-project',
@@ -285,9 +323,12 @@ describe('HiveLogClient Metadata', () => {
285
323
  input: 'test'
286
324
  // No metadata property
287
325
  };
288
- await client.sendLog('test-task', logItem);
326
+ await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success', boundaries: {}, metadata: {} });
289
327
  const expectedLogItem = {
290
328
  input: 'test',
329
+ taskName: 'test-task',
330
+ type: 'success',
331
+ boundaries: {},
291
332
  metadata: {
292
333
  environment: 'test' // Only base metadata should be used
293
334
  }
@@ -306,12 +347,15 @@ describe('HiveLogClient Metadata', () => {
306
347
  input: 'test',
307
348
  metadata: undefined
308
349
  };
309
- await client.sendLog('test-task', logItem);
350
+ await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success', boundaries: {}, metadata: logItem.metadata || {} });
310
351
  const expectedLogItem = {
311
352
  input: 'test',
312
353
  metadata: {
313
354
  environment: 'test' // Only base metadata should be used
314
- }
355
+ },
356
+ taskName: 'test-task',
357
+ type: 'success',
358
+ boundaries: {}
315
359
  };
316
360
  expect(mockedAxios.post).toHaveBeenCalledWith('https://test-host.com/api/tasks/log-ingest', {
317
361
  projectName: 'test-project',
@@ -323,10 +367,13 @@ describe('HiveLogClient Metadata', () => {
323
367
  mockedAxios.post.mockResolvedValueOnce({ data: { success: true } });
324
368
  const client = new index_1.HiveLogClient({ ...testConfig, metadata: {} });
325
369
  const logItem = { input: 'test', metadata: {} };
326
- await client.sendLog('test-task', logItem, {});
370
+ await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success', boundaries: {}, metadata: logItem.metadata || {} }, {});
327
371
  const expectedLogItem = {
328
372
  input: 'test',
329
- metadata: {} // All empty metadata objects result in empty final metadata
373
+ metadata: {}, // All empty metadata objects result in empty final metadata
374
+ taskName: 'test-task',
375
+ type: 'success',
376
+ boundaries: {}
330
377
  };
331
378
  expect(mockedAxios.post).toHaveBeenCalledWith('https://test-host.com/api/tasks/log-ingest', {
332
379
  projectName: 'test-project',
@@ -337,7 +384,7 @@ describe('HiveLogClient Metadata', () => {
337
384
  it('should work in silent mode with metadata', async () => {
338
385
  const baseMetadata = { environment: 'test' };
339
386
  const silentClient = new index_1.HiveLogClient({ projectName: 'silent-project', metadata: baseMetadata });
340
- const result = await silentClient.sendLog('test-task', { input: 'test' }, { requestId: 'req-123' });
387
+ const result = await silentClient.sendLog({ input: 'test', taskName: 'test-task', type: 'success', boundaries: {}, metadata: {} }, { requestId: 'req-123' });
341
388
  expect(result).toBe('silent');
342
389
  expect(mockedAxios.post).not.toHaveBeenCalled();
343
390
  });
@@ -345,7 +392,7 @@ describe('HiveLogClient Metadata', () => {
345
392
  mockedAxios.post.mockRejectedValueOnce(new Error('Network error'));
346
393
  const baseMetadata = { environment: 'test' };
347
394
  const client = new index_1.HiveLogClient({ ...testConfig, metadata: baseMetadata });
348
- const result = await client.sendLog('test-task', { input: 'test' }, { requestId: 'req-123' });
395
+ const result = await client.sendLog({ input: 'test', taskName: 'test-task', type: 'success', boundaries: {}, metadata: {} }, { requestId: 'req-123' });
349
396
  expect(result).toBe('error');
350
397
  });
351
398
  });