@forgehive/record-tape 0.1.6 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +385 -0
- package/dist/index.d.ts +10 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -120
- package/dist/index.js.map +1 -1
- package/dist/tests/data-methods.test.d.ts +2 -0
- package/dist/tests/data-methods.test.d.ts.map +1 -0
- package/dist/tests/data-methods.test.js +129 -0
- package/dist/tests/data-methods.test.js.map +1 -0
- package/dist/tests/index.test.js +3 -3
- package/dist/tests/index.test.js.map +1 -1
- package/dist/tests/log-format.test.js +18 -27
- package/dist/tests/log-format.test.js.map +1 -1
- package/dist/tests/mode.test.js +66 -54
- package/dist/tests/mode.test.js.map +1 -1
- package/dist/tests/safe-run.test.js +110 -242
- package/dist/tests/safe-run.test.js.map +1 -1
- package/dist/tests/save.test.js +13 -13
- package/dist/tests/save.test.js.map +1 -1
- package/dist/tests/task-listener.test.js +35 -31
- package/dist/tests/task-listener.test.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +34 -164
- package/src/tests/data-methods.test.ts +150 -0
- package/src/tests/index.test.ts +4 -9
- package/src/tests/log-format.test.ts +20 -32
- package/src/tests/safe-run.test.ts +119 -294
- package/src/tests/save.test.ts +19 -19
- package/src/tests/task-listener.test.ts +29 -42
- package/src/tests/mode.test.ts +0 -93
package/README.md
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# @forgehive/record-tape
|
|
2
|
+
|
|
3
|
+
A lightweight TypeScript library for recording and persisting task execution logs with boundary data support. Works generically with any task type without requiring compile-time type knowledge.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎯 **Simple API**: Clean, minimal interface for recording execution data
|
|
8
|
+
- 📝 **Automatic Logging**: Seamless integration with task execution
|
|
9
|
+
- 💾 **Persistence**: Save and load execution logs to/from files
|
|
10
|
+
- 🔄 **Serialization**: Built-in JSON serialization support
|
|
11
|
+
- 🎨 **TypeScript**: Full type safety with generics
|
|
12
|
+
- 🚀 **Boundary Support**: Record external API calls and dependencies
|
|
13
|
+
- 🔧 **Generic Design**: Works with any task type without compile-time type requirements
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @forgehive/record-tape
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { RecordTape } from '@forgehive/record-tape'
|
|
25
|
+
import { createTask, Schema } from '@forgehive/task'
|
|
26
|
+
|
|
27
|
+
// Create a new recording tape
|
|
28
|
+
const tape = new RecordTape({ path: 'logs/execution' })
|
|
29
|
+
|
|
30
|
+
// Create a simple task
|
|
31
|
+
const getUserTask = createTask({
|
|
32
|
+
name: 'getUserById',
|
|
33
|
+
schema: Schema.object({
|
|
34
|
+
userId: Schema.number()
|
|
35
|
+
}),
|
|
36
|
+
boundaries: {},
|
|
37
|
+
fn: async function getUserById({ userId }) {
|
|
38
|
+
// Simulate getting user data
|
|
39
|
+
return { name: 'John Doe', email: 'john@example.com' }
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// Execute the task - safeRun returns result, error, and execution record
|
|
44
|
+
const [result, error, executionRecord] = await getUserTask.safeRun({ userId: 123 })
|
|
45
|
+
|
|
46
|
+
// Manually record the execution to the tape
|
|
47
|
+
tape.push(executionRecord)
|
|
48
|
+
|
|
49
|
+
// Save recorded data to file
|
|
50
|
+
await tape.save()
|
|
51
|
+
|
|
52
|
+
// Optional: View what was recorded
|
|
53
|
+
console.log('Recorded logs:', tape.getLog())
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## API Reference
|
|
57
|
+
|
|
58
|
+
### Constructor
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
new RecordTape<TInput, TOutput, B>(config?: Config)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Parameters:**
|
|
65
|
+
- `config.path?` - File path for persistence (will append `.log` extension)
|
|
66
|
+
- `config.log?` - Pre-existing log records to initialize with
|
|
67
|
+
- `config.boundaries?` - Initial boundary data
|
|
68
|
+
|
|
69
|
+
### Core Methods
|
|
70
|
+
|
|
71
|
+
#### `getLog()`
|
|
72
|
+
Get all recorded execution logs.
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
const logs = tape.getLog()
|
|
76
|
+
// Returns: GenericExecutionRecord<TInput, TOutput, B>[]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### `getLength()`
|
|
80
|
+
Get the number of records in the tape.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const count = tape.getLength()
|
|
84
|
+
// Returns: number
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### `shift()`
|
|
88
|
+
Remove and return the first record from the tape.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
const firstRecord = tape.shift()
|
|
92
|
+
// Returns: GenericExecutionRecord<TInput, TOutput, B> | undefined
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### `push(record, metadata?)`
|
|
96
|
+
Add a new execution record to the tape.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const logRecord = tape.push(executionRecord, { userId: '123' })
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Parameters:**
|
|
103
|
+
- `record` - ExecutionRecord with input, output, taskName, boundaries, etc.
|
|
104
|
+
- `metadata?` - Optional additional metadata to attach
|
|
105
|
+
|
|
106
|
+
**Returns:** The created GenericExecutionRecord
|
|
107
|
+
|
|
108
|
+
#### `recordFrom(task)`
|
|
109
|
+
Set up automatic recording from a task instance.
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
tape.recordFrom(myTask)
|
|
113
|
+
// Now myTask will automatically send execution records to this tape
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Serialization Methods
|
|
117
|
+
|
|
118
|
+
#### `stringify()`
|
|
119
|
+
Convert all logs to a string format (one JSON object per line).
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
const logString = tape.stringify()
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### `parse(content)`
|
|
126
|
+
Parse string content back into log records.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const logs = tape.parse(logString)
|
|
130
|
+
// Returns: GenericExecutionRecord<TInput, TOutput, B>[]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Persistence Methods
|
|
134
|
+
|
|
135
|
+
#### `load()` / `loadSync()`
|
|
136
|
+
Load execution logs from file.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// Async version
|
|
140
|
+
const logs = await tape.load()
|
|
141
|
+
// Returns: GenericExecutionRecord<TInput, TOutput, B>[]
|
|
142
|
+
|
|
143
|
+
// Sync version
|
|
144
|
+
const logs = tape.loadSync()
|
|
145
|
+
// Returns: GenericExecutionRecord<TInput, TOutput, B>[]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### `save()` / `saveSync()`
|
|
149
|
+
Save execution logs to file.
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// Async version
|
|
153
|
+
await tape.save()
|
|
154
|
+
|
|
155
|
+
// Sync version
|
|
156
|
+
tape.saveSync()
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Utility Methods
|
|
160
|
+
|
|
161
|
+
#### `compileCache()`
|
|
162
|
+
Compile boundary data from all logs into a cache structure.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
const boundaryCache = tape.compileCache()
|
|
166
|
+
// Returns: Record<string, unknown>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Types
|
|
170
|
+
|
|
171
|
+
### GenericExecutionRecord
|
|
172
|
+
A generic version of ExecutionRecord that can store execution data from any task without knowing the specific types at compile time:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
interface GenericExecutionRecord<TInput = unknown, TOutput = unknown, B extends Boundaries = Boundaries> extends ExecutionRecord<TInput, TOutput, B> {
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The "Generic" prefix indicates that RecordTape can store logs from different tasks with varying input/output types. The default type parameters (`unknown`) allow the tape to work with any task execution without requiring specific type knowledge.
|
|
180
|
+
|
|
181
|
+
**Why Generic?**
|
|
182
|
+
- **Mixed Task Logs**: Store logs from multiple different tasks in the same tape
|
|
183
|
+
- **Runtime Flexibility**: Add logs without knowing task types at compile time
|
|
184
|
+
- **Type Safety**: Still maintains TypeScript type safety when types are known
|
|
185
|
+
|
|
186
|
+
### ExecutionRecord
|
|
187
|
+
Core execution data structure:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
interface ExecutionRecord<TInput, TOutput, B extends Boundaries> {
|
|
191
|
+
input: TInput
|
|
192
|
+
output?: TOutput | null
|
|
193
|
+
error?: string
|
|
194
|
+
boundaries: BoundaryLogsFor<B>
|
|
195
|
+
taskName?: string
|
|
196
|
+
metadata?: Record<string, string>
|
|
197
|
+
type: 'success' | 'error' | 'pending'
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Usage Examples
|
|
202
|
+
|
|
203
|
+
### Basic Recording
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import { RecordTape } from '@forgehive/record-tape'
|
|
207
|
+
|
|
208
|
+
const tape = new RecordTape({ path: 'logs/user-service' })
|
|
209
|
+
|
|
210
|
+
// Record a successful execution
|
|
211
|
+
tape.push({
|
|
212
|
+
input: { userId: 123 },
|
|
213
|
+
output: { name: 'John Doe', email: 'john@example.com' },
|
|
214
|
+
taskName: 'getUser',
|
|
215
|
+
boundaries: {},
|
|
216
|
+
type: 'success'
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
// Record an error
|
|
220
|
+
tape.push({
|
|
221
|
+
input: { userId: 999 },
|
|
222
|
+
error: 'User not found',
|
|
223
|
+
taskName: 'getUser',
|
|
224
|
+
boundaries: {},
|
|
225
|
+
type: 'error'
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
await tape.save()
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Queue-like Processing
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { RecordTape } from '@forgehive/record-tape'
|
|
235
|
+
|
|
236
|
+
const tape = new RecordTape({ path: 'logs/processing-queue' })
|
|
237
|
+
|
|
238
|
+
// Add several records
|
|
239
|
+
tape.push({
|
|
240
|
+
input: { task: 'process-order-1' },
|
|
241
|
+
output: { status: 'completed' },
|
|
242
|
+
taskName: 'processOrder',
|
|
243
|
+
boundaries: {},
|
|
244
|
+
type: 'success'
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
tape.push({
|
|
248
|
+
input: { task: 'process-order-2' },
|
|
249
|
+
output: { status: 'completed' },
|
|
250
|
+
taskName: 'processOrder',
|
|
251
|
+
boundaries: {},
|
|
252
|
+
type: 'success'
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
// Check how many records we have
|
|
256
|
+
console.log(`Total records: ${tape.getLength()}`) // 2
|
|
257
|
+
|
|
258
|
+
// Process records in FIFO order
|
|
259
|
+
while (tape.getLength() > 0) {
|
|
260
|
+
const record = tape.shift()
|
|
261
|
+
console.log(`Processing: ${record?.input.task}`)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
console.log(`Remaining records: ${tape.getLength()}`) // 0
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### With Boundary Data
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
const tape = new RecordTape({ path: 'logs/api-calls' })
|
|
271
|
+
|
|
272
|
+
tape.push({
|
|
273
|
+
input: { query: 'nodejs' },
|
|
274
|
+
output: { results: ['result1', 'result2'] },
|
|
275
|
+
taskName: 'searchRepositories',
|
|
276
|
+
boundaries: {
|
|
277
|
+
githubAPI: [
|
|
278
|
+
{
|
|
279
|
+
input: ['/search/repositories?q=nodejs'],
|
|
280
|
+
output: { data: ['repo1', 'repo2'] },
|
|
281
|
+
error: null
|
|
282
|
+
}
|
|
283
|
+
]
|
|
284
|
+
},
|
|
285
|
+
type: 'success'
|
|
286
|
+
})
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Task Integration
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { Task } from '@forgehive/task'
|
|
293
|
+
import { RecordTape } from '@forgehive/record-tape'
|
|
294
|
+
|
|
295
|
+
const tape = new RecordTape({ path: 'logs/my-task' })
|
|
296
|
+
const myTask = new Task(myFunction, { name: 'processData' })
|
|
297
|
+
|
|
298
|
+
// Set up automatic recording
|
|
299
|
+
tape.recordFrom(myTask)
|
|
300
|
+
|
|
301
|
+
// Now all task executions will be recorded
|
|
302
|
+
await myTask.safeRun({ data: 'test' })
|
|
303
|
+
|
|
304
|
+
// Save recorded data
|
|
305
|
+
await tape.save()
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Loading and Analyzing Logs
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
const tape = new RecordTape({ path: 'logs/analysis' })
|
|
312
|
+
|
|
313
|
+
// Load existing logs
|
|
314
|
+
const logs = await tape.load()
|
|
315
|
+
|
|
316
|
+
// Analyze execution patterns
|
|
317
|
+
const successCount = logs.filter(log => log.type === 'success').length
|
|
318
|
+
const errorCount = logs.filter(log => log.type === 'error').length
|
|
319
|
+
|
|
320
|
+
console.log(`Success rate: ${(successCount / logs.length * 100).toFixed(1)}%`)
|
|
321
|
+
|
|
322
|
+
// Compile boundary cache for replay
|
|
323
|
+
const boundaryCache = tape.compileCache()
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### File Format
|
|
327
|
+
|
|
328
|
+
The tape saves logs in a simple text format with one JSON object per line:
|
|
329
|
+
|
|
330
|
+
```
|
|
331
|
+
{"input":{"userId":123},"output":{"name":"John"},"type":"success","boundaries":{},"metadata":{},"taskName":"getUser"}
|
|
332
|
+
{"input":{"userId":999},"error":"User not found","type":"error","boundaries":{},"metadata":{},"taskName":"getUser"}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Advanced Usage
|
|
336
|
+
|
|
337
|
+
### Custom Metadata
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
tape.push(executionRecord, {
|
|
341
|
+
requestId: 'req-123',
|
|
342
|
+
userAgent: 'MyApp/1.0',
|
|
343
|
+
timestamp: new Date().toISOString()
|
|
344
|
+
})
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Boundary Cache Compilation
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
// After recording multiple executions with boundary data
|
|
351
|
+
const cache = tape.compileCache()
|
|
352
|
+
|
|
353
|
+
// Use cache for task replay
|
|
354
|
+
const replayTask = new Task(myFunction, {
|
|
355
|
+
boundariesData: cache
|
|
356
|
+
})
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## Migration Guide
|
|
360
|
+
|
|
361
|
+
### Recent API Changes
|
|
362
|
+
|
|
363
|
+
**Interface Rename**: `LogRecord` → `GenericExecutionRecord`
|
|
364
|
+
- The main interface has been renamed to better reflect its generic nature
|
|
365
|
+
- All method signatures now use `GenericExecutionRecord`
|
|
366
|
+
- The "Generic" prefix indicates the tape can store logs from different task types
|
|
367
|
+
|
|
368
|
+
**Simplified API**:
|
|
369
|
+
- Removed deprecated `setMode()`, `getMode()`, `addLogRecord()`, `addExecutionRecord()` methods
|
|
370
|
+
- Simplified `push()` method with automatic type inference
|
|
371
|
+
- Removed redundant `name` field (use `taskName` instead)
|
|
372
|
+
|
|
373
|
+
**New Queue Methods**:
|
|
374
|
+
- Added `getLength()` - returns number of records
|
|
375
|
+
- Added `shift()` - removes and returns first record (FIFO)
|
|
376
|
+
- These enable efficient log management and processing
|
|
377
|
+
|
|
378
|
+
**Task Integration**:
|
|
379
|
+
- Direct ExecutionRecord compatibility with Task package
|
|
380
|
+
- Simplified listener setup with automatic record forwarding
|
|
381
|
+
- No conversion needed between task records and tape records
|
|
382
|
+
|
|
383
|
+
## License
|
|
384
|
+
|
|
385
|
+
MIT License - see LICENSE file for details.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,53 +1,28 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import { type ExecutionRecord, type Boundaries } from '@forgehive/task';
|
|
3
|
-
export interface
|
|
4
|
-
name: string;
|
|
5
|
-
type: 'success' | 'error';
|
|
6
|
-
context?: Record<string, string>;
|
|
3
|
+
export interface GenericExecutionRecord<TInput = unknown, TOutput = unknown, B extends Boundaries = Boundaries> extends ExecutionRecord<TInput, TOutput, B> {
|
|
7
4
|
}
|
|
8
|
-
export interface SuccessLogItem<TInput = unknown, TOutput = unknown> {
|
|
9
|
-
input: TInput;
|
|
10
|
-
output: TOutput;
|
|
11
|
-
boundaries?: Record<string, unknown>;
|
|
12
|
-
}
|
|
13
|
-
export interface ErrorLogItem<TInput = unknown> {
|
|
14
|
-
input: TInput;
|
|
15
|
-
error: unknown;
|
|
16
|
-
boundaries?: Record<string, unknown>;
|
|
17
|
-
}
|
|
18
|
-
export type LogItem<TInput = unknown, TOutput = unknown> = SuccessLogItem<TInput, TOutput> | ErrorLogItem<TInput>;
|
|
19
|
-
export type TaskLogItem<TInput = unknown, TOutput = unknown> = LogItem<TInput, TOutput> | {
|
|
20
|
-
input: TInput;
|
|
21
|
-
output?: TOutput;
|
|
22
|
-
error?: unknown;
|
|
23
|
-
boundaries?: Record<string, unknown>;
|
|
24
|
-
};
|
|
25
5
|
interface Config<TInput = unknown, TOutput = unknown, B extends Boundaries = Boundaries> {
|
|
26
6
|
path?: fs.PathLike;
|
|
27
|
-
log?:
|
|
7
|
+
log?: GenericExecutionRecord<TInput, TOutput, B>[];
|
|
28
8
|
boundaries?: Record<string, unknown>;
|
|
29
9
|
}
|
|
30
|
-
export type Mode = 'record' | 'replay';
|
|
31
10
|
export declare class RecordTape<TInput = unknown, TOutput = unknown, B extends Boundaries = Boundaries> {
|
|
32
11
|
private _path;
|
|
33
|
-
private _mode;
|
|
34
12
|
private _log;
|
|
35
13
|
constructor(config?: Config<TInput, TOutput, B>);
|
|
36
|
-
getLog():
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
push(name: string, record: ExecutionRecord<TInput, unknown, B>, context?: Record<string, string>): LogRecord<TInput, TOutput, B>;
|
|
41
|
-
addLogRecord(logRecord: LogRecord<TInput, TOutput, B>): void;
|
|
14
|
+
getLog(): GenericExecutionRecord<TInput, TOutput, B>[];
|
|
15
|
+
getLength(): number;
|
|
16
|
+
shift(): GenericExecutionRecord<TInput, TOutput, B> | undefined;
|
|
17
|
+
push(record: ExecutionRecord<TInput, unknown, B>, metadata?: Record<string, string>): GenericExecutionRecord<TInput, TOutput, B>;
|
|
42
18
|
stringify(): string;
|
|
43
|
-
parse(content: string):
|
|
19
|
+
parse(content: string): GenericExecutionRecord<TInput, TOutput, B>[];
|
|
44
20
|
compileCache(): Record<string, unknown>;
|
|
45
|
-
recordFrom(
|
|
21
|
+
recordFrom(task: {
|
|
46
22
|
_listener?: unknown;
|
|
47
|
-
setBoundariesData: (data: Record<string, unknown>) => void;
|
|
48
23
|
}): void;
|
|
49
|
-
load(): Promise<
|
|
50
|
-
loadSync():
|
|
24
|
+
load(): Promise<GenericExecutionRecord<TInput, TOutput, B>[]>;
|
|
25
|
+
loadSync(): GenericExecutionRecord<TInput, TOutput, B>[];
|
|
51
26
|
save(): Promise<void>;
|
|
52
27
|
saveSync(): void;
|
|
53
28
|
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAA;AAEnB,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEvE,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAA;AAEnB,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEvE,MAAM,WAAW,sBAAsB,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,CAAC,SAAS,UAAU,GAAG,UAAU,CAAE,SAAQ,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;CAC1J;AAED,UAAU,MAAM,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,CAAC,SAAS,UAAU,GAAG,UAAU;IACrF,IAAI,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAA;IAClB,GAAG,CAAC,EAAE,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,CAAA;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACrC;AAED,qBAAa,UAAU,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,CAAC,SAAS,UAAU,GAAG,UAAU;IAC5F,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,IAAI,CAA8C;gBAE9C,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAM;IAMnD,MAAM,IAAI,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE;IAItD,SAAS,IAAI,MAAM;IAInB,KAAK,IAAI,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,GAAG,SAAS;IAI/D,IAAI,CACF,MAAM,EAAE,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,EAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IAoB7C,SAAS,IAAI,MAAM;IASnB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE;IAYpE,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAgBvC,UAAU,CAAC,IAAI,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAQzC,IAAI,IAAI,OAAO,CAAC,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC;IA8BnE,QAAQ,IAAI,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE;IAoBlD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB3B,QAAQ,IAAI,IAAI;CAajB"}
|
package/dist/index.js
CHANGED
|
@@ -11,125 +11,27 @@ class RecordTape {
|
|
|
11
11
|
var _a;
|
|
12
12
|
this._path = typeof config.path === 'string' ? `${config.path}.log` : undefined;
|
|
13
13
|
this._log = (_a = config.log) !== null && _a !== void 0 ? _a : [];
|
|
14
|
-
this._mode = 'record';
|
|
15
14
|
}
|
|
16
15
|
// Data functions
|
|
17
16
|
getLog() {
|
|
18
17
|
return this._log;
|
|
19
18
|
}
|
|
20
|
-
|
|
21
|
-
return this.
|
|
19
|
+
getLength() {
|
|
20
|
+
return this._log.length;
|
|
22
21
|
}
|
|
23
|
-
|
|
24
|
-
this.
|
|
22
|
+
shift() {
|
|
23
|
+
return this._log.shift();
|
|
25
24
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// Check if the source is from safe-run (if it has error field in entries)
|
|
35
|
-
const boundaryEntries = logItem.boundaries[key];
|
|
36
|
-
const isSafeRun = boundaryEntries.some(entry => entry.error !== undefined);
|
|
37
|
-
formattedBoundaries[key] = boundaryEntries.map(entry => {
|
|
38
|
-
var _a, _b;
|
|
39
|
-
// Only add error field if it's from safe-run
|
|
40
|
-
return isSafeRun ?
|
|
41
|
-
{
|
|
42
|
-
input: entry.input,
|
|
43
|
-
output: (_a = entry.output) !== null && _a !== void 0 ? _a : null,
|
|
44
|
-
error: (_b = entry.error) !== null && _b !== void 0 ? _b : null
|
|
45
|
-
} :
|
|
46
|
-
{
|
|
47
|
-
input: entry.input,
|
|
48
|
-
output: entry.output
|
|
49
|
-
};
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// Handle LogItem interface - need to type cast to access properties safely
|
|
54
|
-
const typedLogItem = logItem;
|
|
55
|
-
if ('output' in typedLogItem && typedLogItem.output !== undefined) {
|
|
56
|
-
const { input, output } = typedLogItem;
|
|
57
|
-
this._log.push({
|
|
58
|
-
name,
|
|
59
|
-
type: 'success',
|
|
60
|
-
input,
|
|
61
|
-
output,
|
|
62
|
-
boundaries: formattedBoundaries
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
else if ('error' in typedLogItem && typedLogItem.error !== undefined) {
|
|
66
|
-
const { input, error } = typedLogItem;
|
|
67
|
-
this._log.push({
|
|
68
|
-
name,
|
|
69
|
-
type: 'error',
|
|
70
|
-
input,
|
|
71
|
-
error,
|
|
72
|
-
boundaries: formattedBoundaries
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
throw new Error('invalid log item');
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
push(name, record, context) {
|
|
80
|
-
if (this._mode === 'replay') {
|
|
81
|
-
return {};
|
|
82
|
-
}
|
|
83
|
-
// For safeRun records, always include both error and output fields
|
|
84
|
-
const formattedBoundaries = {};
|
|
85
|
-
if (record.boundaries) {
|
|
86
|
-
for (const key in record.boundaries) {
|
|
87
|
-
const boundaryArray = record.boundaries[key];
|
|
88
|
-
formattedBoundaries[key] = boundaryArray.map(entry => {
|
|
89
|
-
var _a, _b;
|
|
90
|
-
return {
|
|
91
|
-
input: entry.input,
|
|
92
|
-
output: (_a = entry.output) !== null && _a !== void 0 ? _a : null,
|
|
93
|
-
error: (_b = entry.error) !== null && _b !== void 0 ? _b : null
|
|
94
|
-
};
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
let logRecord;
|
|
99
|
-
if ('output' in record && record.output !== undefined) {
|
|
100
|
-
const input = record.input;
|
|
101
|
-
// Handle Promise outputs by setting to null in the log
|
|
102
|
-
const output = record.output instanceof Promise ? null : record.output;
|
|
103
|
-
logRecord = {
|
|
104
|
-
name,
|
|
105
|
-
type: 'success',
|
|
106
|
-
input,
|
|
107
|
-
output,
|
|
108
|
-
boundaries: formattedBoundaries,
|
|
109
|
-
context
|
|
110
|
-
};
|
|
111
|
-
this._log.push(logRecord);
|
|
112
|
-
}
|
|
113
|
-
else if ('error' in record && record.error !== undefined) {
|
|
114
|
-
const input = record.input;
|
|
115
|
-
const error = record.error;
|
|
116
|
-
logRecord = {
|
|
117
|
-
name,
|
|
118
|
-
type: 'error',
|
|
119
|
-
input,
|
|
120
|
-
error,
|
|
121
|
-
boundaries: formattedBoundaries,
|
|
122
|
-
context
|
|
123
|
-
};
|
|
124
|
-
this._log.push(logRecord);
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
throw new Error('invalid record type');
|
|
128
|
-
}
|
|
129
|
-
return logRecord;
|
|
130
|
-
}
|
|
131
|
-
addLogRecord(logRecord) {
|
|
25
|
+
push(record, metadata) {
|
|
26
|
+
// Add type if missing
|
|
27
|
+
const recordType = ('type' in record && record.type) ? record.type :
|
|
28
|
+
(record.output !== undefined && record.output !== null) ? 'success' :
|
|
29
|
+
(record.error !== undefined) ? 'error' : 'pending';
|
|
30
|
+
// Merge metadata from record and parameter (parameter takes precedence)
|
|
31
|
+
const mergedMetadata = Object.assign(Object.assign({}, record.metadata), metadata);
|
|
32
|
+
const logRecord = Object.assign(Object.assign({}, record), { type: recordType, metadata: mergedMetadata });
|
|
132
33
|
this._log.push(logRecord);
|
|
34
|
+
return logRecord;
|
|
133
35
|
}
|
|
134
36
|
stringify() {
|
|
135
37
|
let log = '';
|
|
@@ -166,16 +68,11 @@ class RecordTape {
|
|
|
166
68
|
}
|
|
167
69
|
return cache;
|
|
168
70
|
}
|
|
169
|
-
recordFrom(
|
|
170
|
-
// Add
|
|
171
|
-
task._listener = async (
|
|
172
|
-
|
|
173
|
-
if (this.getMode() === 'record') {
|
|
174
|
-
this.addLogItem(name, logItem);
|
|
175
|
-
}
|
|
71
|
+
recordFrom(task) {
|
|
72
|
+
// Add listener for ExecutionRecord
|
|
73
|
+
task._listener = async (executionRecord) => {
|
|
74
|
+
this.push(executionRecord);
|
|
176
75
|
};
|
|
177
|
-
// Add cache
|
|
178
|
-
task.setBoundariesData(this.compileCache());
|
|
179
76
|
}
|
|
180
77
|
// Load save functions
|
|
181
78
|
async load() {
|
|
@@ -224,6 +121,7 @@ class RecordTape {
|
|
|
224
121
|
this._log = this.parse(content);
|
|
225
122
|
return this._log;
|
|
226
123
|
}
|
|
124
|
+
// Save functions
|
|
227
125
|
async save() {
|
|
228
126
|
if (typeof this._path === 'undefined') {
|
|
229
127
|
return;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAmB;AACnB,gDAAuB;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-methods.test.d.ts","sourceRoot":"","sources":["../../src/tests/data-methods.test.ts"],"names":[],"mappings":""}
|