@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.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.d.ts +51 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +176 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/tests/compile-cache.test.d.ts +2 -0
  7. package/dist/tests/compile-cache.test.d.ts.map +1 -0
  8. package/dist/tests/compile-cache.test.js +34 -0
  9. package/dist/tests/compile-cache.test.js.map +1 -0
  10. package/dist/tests/index.test.d.ts +2 -0
  11. package/dist/tests/index.test.d.ts.map +1 -0
  12. package/dist/tests/index.test.js +99 -0
  13. package/dist/tests/index.test.js.map +1 -0
  14. package/dist/tests/load.test.d.ts +2 -0
  15. package/dist/tests/load.test.d.ts.map +1 -0
  16. package/dist/tests/load.test.js +89 -0
  17. package/dist/tests/load.test.js.map +1 -0
  18. package/dist/tests/log-format.test.d.ts +2 -0
  19. package/dist/tests/log-format.test.d.ts.map +1 -0
  20. package/dist/tests/log-format.test.js +36 -0
  21. package/dist/tests/log-format.test.js.map +1 -0
  22. package/dist/tests/mode.test.d.ts +2 -0
  23. package/dist/tests/mode.test.d.ts.map +1 -0
  24. package/dist/tests/mode.test.js +75 -0
  25. package/dist/tests/mode.test.js.map +1 -0
  26. package/dist/tests/save.test.d.ts +2 -0
  27. package/dist/tests/save.test.d.ts.map +1 -0
  28. package/dist/tests/save.test.js +95 -0
  29. package/dist/tests/save.test.js.map +1 -0
  30. package/dist/tests/task-listener.test.d.ts +2 -0
  31. package/dist/tests/task-listener.test.d.ts.map +1 -0
  32. package/dist/tests/task-listener.test.js +122 -0
  33. package/dist/tests/task-listener.test.js.map +1 -0
  34. package/jest.config.js +6 -0
  35. package/package.json +23 -0
  36. package/src/index.ts +218 -0
  37. package/src/tests/compile-cache.test.ts +41 -0
  38. package/src/tests/fixtures/load.log +2 -0
  39. package/src/tests/fixtures/single-cache.log +2 -0
  40. package/src/tests/index.test.ts +88 -0
  41. package/src/tests/load.test.ts +108 -0
  42. package/src/tests/log-format.test.ts +48 -0
  43. package/src/tests/mode.test.ts +93 -0
  44. package/src/tests/save.test.ts +127 -0
  45. package/src/tests/task-listener.test.ts +170 -0
  46. 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":";;AAAA,oCAAqC;AAErC,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,IAAI,GAAG,IAAI,kBAAU,EAAE,CAAA;QAC7B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACvC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QAIjD,MAAM,IAAI,GAAG,IAAI,kBAAU,EAAyB,CAAA;QACpD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QAEtB,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE;YACtB,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;YACnB,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE;SACtB,CAAC,CAAA;QAEF,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACnC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAI7C,MAAM,IAAI,GAAG,IAAI,kBAAU,EAAyB,CAAA;QACpD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QAEtB,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE;YACtB,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;YACnB,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE;SACtB,CAAC,CAAA;QAEF,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC;YAC5B;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,SAAS;gBACf,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;gBACnB,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE;gBACrB,UAAU,EAAE,EAAE;aACf;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QAIrC,MAAM,IAAI,GAAG,IAAI,kBAAU,EAAyB,CAAA;QAEpD,uBAAuB;QACvB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACrC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE;YACvB,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;YACnB,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE;SACtB,CAAC,CAAA;QAEF,wBAAwB;QACxB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QACtB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACrC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE;YACvB,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;YACnB,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE;SACtB,CAAC,CAAA;QAEF,6BAA6B;QAC7B,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QACtB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACrC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE;YACvB,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;YACnB,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE;SACtB,CAAC,CAAA;QAEF,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC;YAC5B;gBACE,IAAI,EAAE,OAAO;gBACb,IAAI,EAAE,SAAS;gBACf,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;gBACnB,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE;gBACrB,UAAU,EAAE,EAAE;aACf;YACD;gBACE,IAAI,EAAE,OAAO;gBACb,IAAI,EAAE,SAAS;gBACf,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;gBACnB,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE;gBACrB,UAAU,EAAE,EAAE;aACf;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=save.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=task-listener.test.d.ts.map
@@ -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
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'node',
4
+ testMatch: ['**/*.test.ts'],
5
+ moduleFileExtensions: ['ts', 'js'],
6
+ }
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":"name","type":"success","input":[true],"output":true,"boundaries":{}}
2
+ {"name":"name","type":"error","input":[true],"error":"invalid data","boundaries":{}}
@@ -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"}]}}