@forgehive/record-tape 0.0.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/LICENSE +21 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +176 -0
- package/dist/index.js.map +1 -0
- package/dist/tests/compile-cache.test.d.ts +2 -0
- package/dist/tests/compile-cache.test.d.ts.map +1 -0
- package/dist/tests/compile-cache.test.js +34 -0
- package/dist/tests/compile-cache.test.js.map +1 -0
- package/dist/tests/index.test.d.ts +2 -0
- package/dist/tests/index.test.d.ts.map +1 -0
- package/dist/tests/index.test.js +99 -0
- package/dist/tests/index.test.js.map +1 -0
- package/dist/tests/load.test.d.ts +2 -0
- package/dist/tests/load.test.d.ts.map +1 -0
- package/dist/tests/load.test.js +89 -0
- package/dist/tests/load.test.js.map +1 -0
- package/dist/tests/log-format.test.d.ts +2 -0
- package/dist/tests/log-format.test.d.ts.map +1 -0
- package/dist/tests/log-format.test.js +36 -0
- package/dist/tests/log-format.test.js.map +1 -0
- package/dist/tests/mode.test.d.ts +2 -0
- package/dist/tests/mode.test.d.ts.map +1 -0
- package/dist/tests/mode.test.js +75 -0
- package/dist/tests/mode.test.js.map +1 -0
- package/dist/tests/save.test.d.ts +2 -0
- package/dist/tests/save.test.d.ts.map +1 -0
- package/dist/tests/save.test.js +95 -0
- package/dist/tests/save.test.js.map +1 -0
- package/dist/tests/task-listener.test.d.ts +2 -0
- package/dist/tests/task-listener.test.d.ts.map +1 -0
- package/dist/tests/task-listener.test.js +122 -0
- package/dist/tests/task-listener.test.js.map +1 -0
- package/jest.config.js +6 -0
- package/package.json +23 -0
- package/src/index.ts +218 -0
- package/src/tests/compile-cache.test.ts +41 -0
- package/src/tests/fixtures/load.log +2 -0
- package/src/tests/fixtures/single-cache.log +2 -0
- package/src/tests/index.test.ts +88 -0
- package/src/tests/load.test.ts +108 -0
- package/src/tests/log-format.test.ts +48 -0
- package/src/tests/mode.test.ts +93 -0
- package/src/tests/save.test.ts +127 -0
- package/src/tests/task-listener.test.ts +170 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../index");
|
|
4
|
+
describe('Mode behavior', () => {
|
|
5
|
+
it('Should start in record mode by default', () => {
|
|
6
|
+
const tape = new index_1.RecordTape();
|
|
7
|
+
expect(tape.getMode()).toBe('record');
|
|
8
|
+
});
|
|
9
|
+
it('Should not add log items in replay mode', () => {
|
|
10
|
+
const tape = new index_1.RecordTape();
|
|
11
|
+
tape.setMode('replay');
|
|
12
|
+
tape.addLogItem('test', {
|
|
13
|
+
input: { value: 1 },
|
|
14
|
+
output: { result: 2 }
|
|
15
|
+
});
|
|
16
|
+
expect(tape.getLog()).toEqual([]);
|
|
17
|
+
});
|
|
18
|
+
it('Should add log items in record mode', () => {
|
|
19
|
+
const tape = new index_1.RecordTape();
|
|
20
|
+
tape.setMode('record');
|
|
21
|
+
tape.addLogItem('test', {
|
|
22
|
+
input: { value: 1 },
|
|
23
|
+
output: { result: 2 }
|
|
24
|
+
});
|
|
25
|
+
expect(tape.getLog()).toEqual([
|
|
26
|
+
{
|
|
27
|
+
name: 'test',
|
|
28
|
+
type: 'success',
|
|
29
|
+
input: { value: 1 },
|
|
30
|
+
output: { result: 2 },
|
|
31
|
+
boundaries: {}
|
|
32
|
+
}
|
|
33
|
+
]);
|
|
34
|
+
});
|
|
35
|
+
it('Should switch between modes', () => {
|
|
36
|
+
const tape = new index_1.RecordTape();
|
|
37
|
+
// Start in record mode
|
|
38
|
+
expect(tape.getMode()).toBe('record');
|
|
39
|
+
tape.addLogItem('test1', {
|
|
40
|
+
input: { value: 1 },
|
|
41
|
+
output: { result: 2 }
|
|
42
|
+
});
|
|
43
|
+
// Switch to replay mode
|
|
44
|
+
tape.setMode('replay');
|
|
45
|
+
expect(tape.getMode()).toBe('replay');
|
|
46
|
+
tape.addLogItem('test2', {
|
|
47
|
+
input: { value: 3 },
|
|
48
|
+
output: { result: 4 }
|
|
49
|
+
});
|
|
50
|
+
// Switch back to record mode
|
|
51
|
+
tape.setMode('record');
|
|
52
|
+
expect(tape.getMode()).toBe('record');
|
|
53
|
+
tape.addLogItem('test3', {
|
|
54
|
+
input: { value: 5 },
|
|
55
|
+
output: { result: 6 }
|
|
56
|
+
});
|
|
57
|
+
expect(tape.getLog()).toEqual([
|
|
58
|
+
{
|
|
59
|
+
name: 'test1',
|
|
60
|
+
type: 'success',
|
|
61
|
+
input: { value: 1 },
|
|
62
|
+
output: { result: 2 },
|
|
63
|
+
boundaries: {}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'test3',
|
|
67
|
+
type: 'success',
|
|
68
|
+
input: { value: 5 },
|
|
69
|
+
output: { result: 6 },
|
|
70
|
+
boundaries: {}
|
|
71
|
+
}
|
|
72
|
+
]);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
//# sourceMappingURL=mode.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mode.test.js","sourceRoot":"","sources":["../../src/tests/mode.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save.test.d.ts","sourceRoot":"","sources":["../../src/tests/save.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const index_1 = require("../index");
|
|
9
|
+
const logFileData = '{"name":"name","type":"success","input":[true],"output":true,"boundaries":{}}\n{"name":"name","type":"error","input":[true],"error":"invalid data","boundaries":{}}\n';
|
|
10
|
+
describe('Save to file async', () => {
|
|
11
|
+
it('Save async to existing file(should add new logs)', async () => {
|
|
12
|
+
const tapeFilePath = path_1.default.resolve(__dirname, './fixtures/save');
|
|
13
|
+
try {
|
|
14
|
+
await fs_1.default.promises.writeFile(tapeFilePath + '.log', logFileData);
|
|
15
|
+
}
|
|
16
|
+
catch (_e) {
|
|
17
|
+
// eslint-disable-next-line no-console
|
|
18
|
+
// console.warn('Didnt found a file to unlink')
|
|
19
|
+
}
|
|
20
|
+
const tape = new index_1.RecordTape({ path: tapeFilePath });
|
|
21
|
+
await tape.load();
|
|
22
|
+
tape.addLogItem('name', { input: [true], output: true, boundaries: {} });
|
|
23
|
+
tape.addLogItem('name', { input: [true], error: 'invalid data', boundaries: {} });
|
|
24
|
+
await tape.save();
|
|
25
|
+
const content = await fs_1.default.promises.readFile(tapeFilePath + '.log', 'utf8');
|
|
26
|
+
expect(tape.getLog().length).toBe(4);
|
|
27
|
+
expect(tape.stringify()).toBe(logFileData + logFileData);
|
|
28
|
+
expect(content).toBe(logFileData + logFileData);
|
|
29
|
+
});
|
|
30
|
+
it('Save async to a path of invalid folder', async () => {
|
|
31
|
+
const tapeFilePath = path_1.default.resolve(__dirname, './nowhere/nop');
|
|
32
|
+
const tape = new index_1.RecordTape({ path: tapeFilePath });
|
|
33
|
+
tape.addLogItem('name', { input: [true], output: true, boundaries: {} });
|
|
34
|
+
tape.addLogItem('name', { input: [true], error: 'invalid data', boundaries: {} });
|
|
35
|
+
await expect(tape.save()).rejects.toThrow('Folder doesn\'t exists');
|
|
36
|
+
});
|
|
37
|
+
it('Save async to a log file that doesnt exist', async () => {
|
|
38
|
+
const tapeFilePath = path_1.default.resolve(__dirname, './fixtures/nop');
|
|
39
|
+
try {
|
|
40
|
+
await fs_1.default.promises.unlink(tapeFilePath + '.log');
|
|
41
|
+
}
|
|
42
|
+
catch (_e) {
|
|
43
|
+
// eslint-disable-next-line no-console
|
|
44
|
+
// console.warn('Didnt found a file to unlink')
|
|
45
|
+
}
|
|
46
|
+
const tape = new index_1.RecordTape({ path: tapeFilePath });
|
|
47
|
+
tape.addLogItem('name', { input: [true], output: true, boundaries: {} });
|
|
48
|
+
tape.addLogItem('name', { input: [true], error: 'invalid data', boundaries: {} });
|
|
49
|
+
await tape.save();
|
|
50
|
+
const content = await fs_1.default.promises.readFile(tapeFilePath + '.log', 'utf8');
|
|
51
|
+
expect(content).toBe(logFileData);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('Save to file sync', () => {
|
|
55
|
+
it('Save sync', () => {
|
|
56
|
+
const tapeFilePath = path_1.default.resolve(__dirname, './fixtures/save');
|
|
57
|
+
try {
|
|
58
|
+
fs_1.default.unlinkSync(tapeFilePath + '.log');
|
|
59
|
+
}
|
|
60
|
+
catch (_e) {
|
|
61
|
+
// eslint-disable-next-line no-console
|
|
62
|
+
// console.warn('didnt found a file to unlink')
|
|
63
|
+
}
|
|
64
|
+
const tape = new index_1.RecordTape({ path: tapeFilePath });
|
|
65
|
+
tape.addLogItem('name', { input: [true], output: true, boundaries: {} });
|
|
66
|
+
tape.addLogItem('name', { input: [true], error: 'invalid data', boundaries: {} });
|
|
67
|
+
tape.saveSync();
|
|
68
|
+
const content = fs_1.default.readFileSync(tapeFilePath + '.log', 'utf8');
|
|
69
|
+
expect(content).toBe(logFileData);
|
|
70
|
+
});
|
|
71
|
+
it('Save sync to a path of invalid folder', () => {
|
|
72
|
+
const tapeFilePath = path_1.default.resolve(__dirname, './nowhere/nop');
|
|
73
|
+
const tape = new index_1.RecordTape({ path: tapeFilePath });
|
|
74
|
+
tape.addLogItem('name', { input: [true], output: true, boundaries: {} });
|
|
75
|
+
tape.addLogItem('name', { input: [true], error: 'invalid data', boundaries: {} });
|
|
76
|
+
expect(() => tape.saveSync()).toThrow('Folder doesn\'t exists');
|
|
77
|
+
});
|
|
78
|
+
it('Save sync to a log file that doesnt exist', () => {
|
|
79
|
+
const tapeFilePath = path_1.default.resolve(__dirname, './fixtures/nop');
|
|
80
|
+
try {
|
|
81
|
+
fs_1.default.unlinkSync(tapeFilePath + '.log');
|
|
82
|
+
}
|
|
83
|
+
catch (_e) {
|
|
84
|
+
// eslint-disable-next-line no-console
|
|
85
|
+
// console.warn('Didnt found a file to unlink')
|
|
86
|
+
}
|
|
87
|
+
const tape = new index_1.RecordTape({ path: tapeFilePath });
|
|
88
|
+
tape.addLogItem('name', { input: [true], output: true, boundaries: {} });
|
|
89
|
+
tape.addLogItem('name', { input: [true], error: 'invalid data', boundaries: {} });
|
|
90
|
+
tape.saveSync();
|
|
91
|
+
const content = fs_1.default.readFileSync(tapeFilePath + '.log', 'utf8');
|
|
92
|
+
expect(content).toBe(logFileData);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
//# sourceMappingURL=save.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save.test.js","sourceRoot":"","sources":["../../src/tests/save.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-listener.test.d.ts","sourceRoot":"","sources":["../../src/tests/task-listener.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const task_1 = require("@forgehive/task");
|
|
4
|
+
const index_1 = require("../index");
|
|
5
|
+
describe('Task listener', () => {
|
|
6
|
+
it('Should listen to task events', async () => {
|
|
7
|
+
const tape = new index_1.RecordTape({});
|
|
8
|
+
const task = new task_1.Task(async (_input) => {
|
|
9
|
+
return { value: 1, foo: true };
|
|
10
|
+
});
|
|
11
|
+
task.addListener((record) => {
|
|
12
|
+
const logItem = record.error
|
|
13
|
+
? { input: record.input, error: record.error, boundaries: record.boundaries }
|
|
14
|
+
: { input: record.input, output: record.output, boundaries: record.boundaries };
|
|
15
|
+
tape.addLogItem('test', logItem);
|
|
16
|
+
});
|
|
17
|
+
await task.run({});
|
|
18
|
+
expect(tape.getLog()).toEqual([
|
|
19
|
+
{ name: 'test', type: 'success', input: {}, output: { value: 1, foo: true }, boundaries: {} }
|
|
20
|
+
]);
|
|
21
|
+
});
|
|
22
|
+
it('Should has errors and sucess items', async () => {
|
|
23
|
+
const task = new task_1.Task(async (input) => {
|
|
24
|
+
if (input.value < 10 || input.value > 20) {
|
|
25
|
+
throw new Error('Value is not between 10 and 20');
|
|
26
|
+
}
|
|
27
|
+
return { result: input.value * 2 };
|
|
28
|
+
});
|
|
29
|
+
const tape = new index_1.RecordTape({});
|
|
30
|
+
task.addListener((record) => {
|
|
31
|
+
const logItem = record.error
|
|
32
|
+
? { input: record.input, error: record.error, boundaries: record.boundaries }
|
|
33
|
+
: { input: record.input, output: record.output, boundaries: record.boundaries };
|
|
34
|
+
tape.addLogItem('test', logItem);
|
|
35
|
+
});
|
|
36
|
+
try {
|
|
37
|
+
await task.run({ value: 5 });
|
|
38
|
+
}
|
|
39
|
+
catch (_error) {
|
|
40
|
+
// this is expected
|
|
41
|
+
}
|
|
42
|
+
await task.run({ value: 15 });
|
|
43
|
+
const log = tape.getLog();
|
|
44
|
+
expect(log).toEqual([
|
|
45
|
+
{ name: 'test', type: 'error', input: { value: 5 }, error: 'Value is not between 10 and 20', boundaries: {} },
|
|
46
|
+
{ name: 'test', type: 'success', input: { value: 15 }, output: { result: 30 }, boundaries: {} }
|
|
47
|
+
]);
|
|
48
|
+
});
|
|
49
|
+
it('Should get types from task', async () => {
|
|
50
|
+
// Create a schema for the task
|
|
51
|
+
const schema = new task_1.Schema({
|
|
52
|
+
value: task_1.Schema.number()
|
|
53
|
+
});
|
|
54
|
+
// Define the boundaries
|
|
55
|
+
const boundaries = {
|
|
56
|
+
multiply: async (value) => {
|
|
57
|
+
return value * 2;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
// Create the task using createTask
|
|
61
|
+
const task = (0, task_1.createTask)(schema, boundaries, async (input, { multiply }) => {
|
|
62
|
+
const result = await multiply(input.value);
|
|
63
|
+
return { result };
|
|
64
|
+
});
|
|
65
|
+
const tape = new index_1.RecordTape({});
|
|
66
|
+
task.addListener((record) => {
|
|
67
|
+
const logItem = record.error
|
|
68
|
+
? { input: record.input, error: record.error, boundaries: record.boundaries }
|
|
69
|
+
: { input: record.input, output: record.output, boundaries: record.boundaries };
|
|
70
|
+
tape.addLogItem('test', logItem);
|
|
71
|
+
});
|
|
72
|
+
await task.run({ value: 5 });
|
|
73
|
+
expect(tape.getLog()).toEqual([
|
|
74
|
+
{
|
|
75
|
+
name: 'test',
|
|
76
|
+
type: 'success',
|
|
77
|
+
input: { value: 5 },
|
|
78
|
+
output: { result: 10 },
|
|
79
|
+
boundaries: {
|
|
80
|
+
multiply: [{ input: [5], output: 10 }]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
]);
|
|
84
|
+
});
|
|
85
|
+
it('Should listen to task events with boundaries', async () => {
|
|
86
|
+
const tape = new index_1.RecordTape({});
|
|
87
|
+
// Create a schema for the task
|
|
88
|
+
const schema = new task_1.Schema({
|
|
89
|
+
value: task_1.Schema.number()
|
|
90
|
+
});
|
|
91
|
+
// Define the boundaries
|
|
92
|
+
const boundaries = {
|
|
93
|
+
multiply: async (value) => {
|
|
94
|
+
return value * 2;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
// Create the task using createTask
|
|
98
|
+
const task = (0, task_1.createTask)(schema, boundaries, async (input, { multiply }) => {
|
|
99
|
+
const result = await multiply(input.value);
|
|
100
|
+
return { result };
|
|
101
|
+
});
|
|
102
|
+
task.addListener((record) => {
|
|
103
|
+
const logItem = record.error
|
|
104
|
+
? { input: record.input, error: record.error, boundaries: record.boundaries }
|
|
105
|
+
: { input: record.input, output: record.output, boundaries: record.boundaries };
|
|
106
|
+
tape.addLogItem('test', logItem);
|
|
107
|
+
});
|
|
108
|
+
await task.run({ value: 5 });
|
|
109
|
+
expect(tape.getLog()).toEqual([
|
|
110
|
+
{
|
|
111
|
+
name: 'test',
|
|
112
|
+
type: 'success',
|
|
113
|
+
input: { value: 5 },
|
|
114
|
+
output: { result: 10 },
|
|
115
|
+
boundaries: {
|
|
116
|
+
multiply: [{ input: [5], output: 10 }]
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
]);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
//# sourceMappingURL=task-listener.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-listener.test.js","sourceRoot":"","sources":["../../src/tests/task-listener.test.ts"],"names":[],"mappings":"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"}
|
package/jest.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@forgehive/record-tape",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"devDependencies": {
|
|
8
|
+
"@types/jest": "^29.5.14",
|
|
9
|
+
"@types/node": "^20.11.24",
|
|
10
|
+
"jest": "^29.7.0",
|
|
11
|
+
"ts-jest": "^29.1.2",
|
|
12
|
+
"typescript": "^5.3.3"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@forgehive/schema": "^0.1.3",
|
|
16
|
+
"@forgehive/task": "^0.1.3"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"test": "jest",
|
|
21
|
+
"test:watch": "jest --watch"
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
export interface LogRecord<TInput = unknown[], TOutput = unknown> {
|
|
5
|
+
name: string
|
|
6
|
+
type: 'success' | 'error'
|
|
7
|
+
input: TInput
|
|
8
|
+
output?: TOutput
|
|
9
|
+
error?: unknown
|
|
10
|
+
boundaries: Record<string, unknown>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface SuccessLogItem<TInput = unknown[], TOutput = unknown> {
|
|
14
|
+
input: TInput
|
|
15
|
+
output: TOutput
|
|
16
|
+
boundaries?: Record<string, unknown>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ErrorLogItem<TInput = unknown[]> {
|
|
20
|
+
input: TInput
|
|
21
|
+
error: unknown
|
|
22
|
+
boundaries?: Record<string, unknown>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isSuccessLogItem<TInput, TOutput>(log: SuccessLogItem<TInput, TOutput> | ErrorLogItem<TInput>): log is SuccessLogItem<TInput, TOutput> {
|
|
26
|
+
return (log as SuccessLogItem<TInput, TOutput>).output !== undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isErrorLogItem<TInput>(log: SuccessLogItem<TInput> | ErrorLogItem<TInput>): log is ErrorLogItem<TInput> {
|
|
30
|
+
return (log as ErrorLogItem<TInput>).error !== undefined
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type LogItem<TInput = unknown[], TOutput = unknown> = SuccessLogItem<TInput, TOutput> | ErrorLogItem<TInput>
|
|
34
|
+
|
|
35
|
+
interface Config<TInput = unknown[], TOutput = unknown> {
|
|
36
|
+
path?: fs.PathLike
|
|
37
|
+
log?: LogRecord<TInput, TOutput>[]
|
|
38
|
+
boundaries?: Record<string, unknown>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type Mode = 'record' | 'replay'
|
|
42
|
+
|
|
43
|
+
export class RecordTape<TInput = unknown[], TOutput = unknown> {
|
|
44
|
+
private _path: fs.PathLike | undefined
|
|
45
|
+
private _mode: Mode
|
|
46
|
+
private _boundaries: Record<string, unknown>
|
|
47
|
+
private _log: LogRecord<TInput, TOutput>[]
|
|
48
|
+
|
|
49
|
+
constructor(config: Config<TInput, TOutput> = {}) {
|
|
50
|
+
this._path = typeof config.path === 'string' ? `${config.path}.log` : undefined
|
|
51
|
+
this._log = config.log ?? []
|
|
52
|
+
this._boundaries = config.boundaries ?? {}
|
|
53
|
+
this._mode = 'record'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Data functions
|
|
57
|
+
getLog(): LogRecord<TInput, TOutput>[] {
|
|
58
|
+
return this._log
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
getMode(): Mode {
|
|
62
|
+
return this._mode
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
setMode(mode: Mode): void {
|
|
66
|
+
this._mode = mode
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
addLogItem(name: string, logItem: LogItem<TInput, TOutput>): void {
|
|
70
|
+
if (this._mode === 'replay') {
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (isSuccessLogItem(logItem)) {
|
|
75
|
+
const { input, output, boundaries = {} } = logItem
|
|
76
|
+
this._log.push({ name, type: 'success', input, output, boundaries })
|
|
77
|
+
} else if (isErrorLogItem(logItem)) {
|
|
78
|
+
const { input, error, boundaries = {} } = logItem
|
|
79
|
+
this._log.push({ name, type: 'error', input, error, boundaries })
|
|
80
|
+
} else {
|
|
81
|
+
throw new Error('invalid log item')
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
addLogRecord(logRecord: LogRecord<TInput, TOutput>): void {
|
|
86
|
+
this._log.push(logRecord)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
stringify(): string {
|
|
90
|
+
let log = ''
|
|
91
|
+
for (const logItem of this._log) {
|
|
92
|
+
const str = JSON.stringify(logItem)
|
|
93
|
+
log = log + str + '\n'
|
|
94
|
+
}
|
|
95
|
+
return log
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
parse(content: string): LogRecord<TInput, TOutput>[] {
|
|
99
|
+
const items = content.split('\n')
|
|
100
|
+
const log: LogRecord<TInput, TOutput>[] = []
|
|
101
|
+
for (const item of items) {
|
|
102
|
+
if (item !== '') {
|
|
103
|
+
const data = JSON.parse(item) as LogRecord<TInput, TOutput>
|
|
104
|
+
log.push(data)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return log
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
compileCache(): Record<string, unknown> {
|
|
111
|
+
const cache: Record<string, unknown> = {}
|
|
112
|
+
for (const logIteam of this._log) {
|
|
113
|
+
for (const bondaryName in logIteam.boundaries) {
|
|
114
|
+
if (typeof cache[bondaryName] === 'undefined') {
|
|
115
|
+
cache[bondaryName] = logIteam.boundaries[bondaryName]
|
|
116
|
+
} else {
|
|
117
|
+
const currentValue = cache[bondaryName] as unknown[]
|
|
118
|
+
const newValue = logIteam.boundaries[bondaryName] as unknown[]
|
|
119
|
+
cache[bondaryName] = currentValue.concat(newValue)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return cache
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
recordFrom(name: string, task: { _listener?: unknown; setBoundariesData: (data: Record<string, unknown>) => void }): void {
|
|
127
|
+
// Add listner
|
|
128
|
+
task._listener = async (logItem: LogItem<TInput, TOutput>, _boundaries: Record<string, unknown>): Promise<void> => {
|
|
129
|
+
// Only update if mode is record
|
|
130
|
+
if (this.getMode() === 'record') {
|
|
131
|
+
this.addLogItem(name, logItem)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Add cache
|
|
136
|
+
task.setBoundariesData(this.compileCache())
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Load save functions
|
|
140
|
+
async load(): Promise<LogRecord<TInput, TOutput>[]> {
|
|
141
|
+
if (typeof this._path === 'undefined') {
|
|
142
|
+
return []
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const dirpath = path.dirname(this._path.toString())
|
|
146
|
+
try {
|
|
147
|
+
await fs.promises.access(dirpath)
|
|
148
|
+
} catch (error) {
|
|
149
|
+
throw new Error('Logs folder doesn\'t exists')
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (typeof this._path === 'undefined') { return [] }
|
|
153
|
+
const readFile = fs.promises.readFile
|
|
154
|
+
|
|
155
|
+
let content: string | undefined
|
|
156
|
+
try {
|
|
157
|
+
content = await readFile(this._path.toString(), 'utf8')
|
|
158
|
+
} catch (e) {
|
|
159
|
+
// Ignore error and return empty array
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (typeof content === 'undefined') {
|
|
163
|
+
return []
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
this._log = this.parse(content)
|
|
167
|
+
return this._log
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
loadSync(): LogRecord<TInput, TOutput>[] {
|
|
171
|
+
if (typeof this._path === 'undefined') { return [] }
|
|
172
|
+
|
|
173
|
+
const dirpath = path.dirname(this._path.toString())
|
|
174
|
+
try {
|
|
175
|
+
fs.accessSync(dirpath)
|
|
176
|
+
} catch (error) {
|
|
177
|
+
throw new Error('Logs folder doesn\'t exists')
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!fs.existsSync(this._path.toString())) {
|
|
181
|
+
return []
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const content = fs.readFileSync(this._path.toString(), 'utf8')
|
|
185
|
+
this._log = this.parse(content)
|
|
186
|
+
return this._log
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async save(): Promise<void> {
|
|
190
|
+
if (typeof this._path === 'undefined') { return }
|
|
191
|
+
|
|
192
|
+
const dirpath = path.dirname(this._path.toString())
|
|
193
|
+
try {
|
|
194
|
+
await fs.promises.access(dirpath)
|
|
195
|
+
} catch (error) {
|
|
196
|
+
throw new Error('Folder doesn\'t exists')
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const writeFile = fs.promises.writeFile
|
|
200
|
+
const content = this.stringify()
|
|
201
|
+
|
|
202
|
+
await writeFile(this._path.toString(), content, 'utf8')
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
saveSync(): void {
|
|
206
|
+
if (typeof this._path === 'undefined') { return }
|
|
207
|
+
|
|
208
|
+
const dirpath = path.dirname(this._path.toString())
|
|
209
|
+
try {
|
|
210
|
+
fs.accessSync(dirpath)
|
|
211
|
+
} catch (error) {
|
|
212
|
+
throw new Error('Folder doesn\'t exists')
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const content = this.stringify()
|
|
216
|
+
fs.writeFileSync(this._path.toString(), content, 'utf8')
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { RecordTape } from '../index'
|
|
3
|
+
|
|
4
|
+
const simpleTapePath = path.resolve(__dirname, './fixtures/single-cache')
|
|
5
|
+
const complexTapePath = path.resolve(__dirname, './fixtures/complex-cache')
|
|
6
|
+
|
|
7
|
+
describe('Cache tests', () => {
|
|
8
|
+
it('Should create cache object from log', () => {
|
|
9
|
+
type InputType = string[]
|
|
10
|
+
type OutputType = string
|
|
11
|
+
|
|
12
|
+
const tape = new RecordTape<InputType, OutputType>({ path: simpleTapePath })
|
|
13
|
+
tape.loadSync()
|
|
14
|
+
|
|
15
|
+
const cache = tape.compileCache()
|
|
16
|
+
|
|
17
|
+
expect(cache).toEqual({
|
|
18
|
+
getFilePath: [
|
|
19
|
+
{ input: ['doc'], output: 'readme.md' },
|
|
20
|
+
{ input: ['package'], output: 'package.json' }
|
|
21
|
+
]
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it.skip('Should create cache object with multiple boundaties', () => {
|
|
26
|
+
type InputType = string[]
|
|
27
|
+
type OutputType = string
|
|
28
|
+
|
|
29
|
+
const tape = new RecordTape<InputType, OutputType>({ path: complexTapePath })
|
|
30
|
+
tape.loadSync()
|
|
31
|
+
|
|
32
|
+
const cache = tape.compileCache()
|
|
33
|
+
|
|
34
|
+
expect(cache).toEqual({
|
|
35
|
+
getFilePath: [
|
|
36
|
+
{ input: ['doc'], output: 'readme.md' },
|
|
37
|
+
{ input: ['package'], output: 'package.json' }
|
|
38
|
+
]
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
})
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
{"name":"test","type":"success","input":{"type":"doc"},"output":{"filePath":"readme.md"},"boundaries":{"getFilePath":[{"input":["doc"],"output":"readme.md"}]}}
|
|
2
|
+
{"name":"test","type":"success","input":{"type":"package"},"output":{"filePath":"package.json"},"boundaries":{"getFilePath":[{"input":["package"],"output":"package.json"}]}}
|