hive-stream 2.0.5 → 3.0.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.
Files changed (89) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/.env.example +2 -2
  3. package/.travis.yml +11 -11
  4. package/CHANGELOG.md +166 -0
  5. package/CLAUDE.md +75 -0
  6. package/LICENSE +21 -21
  7. package/README.md +338 -238
  8. package/dist/actions.d.ts +41 -10
  9. package/dist/actions.js +126 -23
  10. package/dist/actions.js.map +1 -1
  11. package/dist/adapters/base.adapter.d.ts +25 -25
  12. package/dist/adapters/base.adapter.js +63 -49
  13. package/dist/adapters/base.adapter.js.map +1 -1
  14. package/dist/adapters/mongodb.adapter.d.ts +50 -37
  15. package/dist/adapters/mongodb.adapter.js +363 -158
  16. package/dist/adapters/mongodb.adapter.js.map +1 -1
  17. package/dist/adapters/postgresql.adapter.d.ts +49 -0
  18. package/dist/adapters/postgresql.adapter.js +507 -0
  19. package/dist/adapters/postgresql.adapter.js.map +1 -0
  20. package/dist/adapters/sqlite.adapter.d.ts +40 -41
  21. package/dist/adapters/sqlite.adapter.js +470 -397
  22. package/dist/adapters/sqlite.adapter.js.map +1 -1
  23. package/dist/api.d.ts +6 -6
  24. package/dist/api.js +95 -55
  25. package/dist/api.js.map +1 -1
  26. package/dist/config.d.ts +16 -16
  27. package/dist/config.js +18 -18
  28. package/dist/config.js.map +1 -1
  29. package/dist/contracts/coinflip.contract.d.ts +27 -14
  30. package/dist/contracts/coinflip.contract.js +253 -94
  31. package/dist/contracts/coinflip.contract.js.map +1 -1
  32. package/dist/contracts/dice.contract.d.ts +37 -29
  33. package/dist/contracts/dice.contract.js +282 -155
  34. package/dist/contracts/dice.contract.js.map +1 -1
  35. package/dist/contracts/lotto.contract.d.ts +20 -20
  36. package/dist/contracts/lotto.contract.js +246 -246
  37. package/dist/contracts/nft.contract.d.ts +24 -0
  38. package/dist/contracts/nft.contract.js +533 -0
  39. package/dist/contracts/nft.contract.js.map +1 -0
  40. package/dist/contracts/token.contract.d.ts +18 -0
  41. package/dist/contracts/token.contract.js +263 -0
  42. package/dist/contracts/token.contract.js.map +1 -0
  43. package/dist/exchanges/bittrex.d.ts +6 -6
  44. package/dist/exchanges/bittrex.js +34 -34
  45. package/dist/exchanges/coingecko.d.ts +5 -0
  46. package/dist/exchanges/coingecko.js +40 -0
  47. package/dist/exchanges/coingecko.js.map +1 -0
  48. package/dist/exchanges/exchange.d.ts +9 -9
  49. package/dist/exchanges/exchange.js +26 -26
  50. package/dist/hive-rates.d.ts +9 -9
  51. package/dist/hive-rates.js +121 -75
  52. package/dist/hive-rates.js.map +1 -1
  53. package/dist/index.d.ts +12 -11
  54. package/dist/index.js +33 -32
  55. package/dist/index.js.map +1 -1
  56. package/dist/streamer.d.ts +140 -93
  57. package/dist/streamer.js +793 -545
  58. package/dist/streamer.js.map +1 -1
  59. package/dist/test.d.ts +1 -1
  60. package/dist/test.js +25 -25
  61. package/dist/test.js.map +1 -1
  62. package/dist/types/hive-stream.d.ts +35 -6
  63. package/dist/types/hive-stream.js +2 -2
  64. package/dist/utils.d.ts +27 -27
  65. package/dist/utils.js +271 -261
  66. package/dist/utils.js.map +1 -1
  67. package/ecosystem.config.js +17 -17
  68. package/jest.config.js +8 -8
  69. package/package.json +53 -48
  70. package/test-contract-block.md +18 -18
  71. package/tests/actions.spec.ts +252 -0
  72. package/tests/adapters/actions-persistence.spec.ts +144 -0
  73. package/tests/adapters/postgresql.adapter.spec.ts +127 -0
  74. package/tests/adapters/sqlite.adapter.spec.ts +180 -42
  75. package/tests/contracts/coinflip.contract.spec.ts +221 -131
  76. package/tests/contracts/dice.contract.spec.ts +202 -159
  77. package/tests/contracts/entrants.json +728 -728
  78. package/tests/contracts/lotto.contract.spec.ts +323 -323
  79. package/tests/contracts/nft.contract.spec.ts +948 -0
  80. package/tests/contracts/token.contract.spec.ts +334 -0
  81. package/tests/helpers/mock-adapter.ts +214 -0
  82. package/tests/setup.ts +29 -18
  83. package/tests/streamer-actions.spec.ts +263 -0
  84. package/tests/streamer.spec.ts +248 -151
  85. package/tests/utils.spec.ts +91 -94
  86. package/tsconfig.build.json +3 -22
  87. package/tslint.json +20 -20
  88. package/wallaby.js +26 -26
  89. package/.env +0 -3
@@ -0,0 +1,127 @@
1
+ import { PostgreSQLAdapter } from "../../src/adapters/postgresql.adapter";
2
+
3
+ describe('PostgreSQL Adapter', () => {
4
+ let sut: PostgreSQLAdapter;
5
+
6
+ beforeEach(() => {
7
+ sut = new PostgreSQLAdapter({
8
+ host: 'localhost',
9
+ port: 5432,
10
+ user: 'test',
11
+ password: 'test',
12
+ database: 'test_db'
13
+ });
14
+ });
15
+
16
+ test('constructor creates adapter with connection config', () => {
17
+ expect(sut).toBeInstanceOf(PostgreSQLAdapter);
18
+ expect(sut.getDb()).toBeDefined();
19
+ });
20
+
21
+ test('constructor works with connection string', () => {
22
+ const adapter = new PostgreSQLAdapter({
23
+ connectionString: 'postgresql://user:pass@localhost:5432/dbname',
24
+ ssl: true
25
+ });
26
+
27
+ expect(adapter).toBeInstanceOf(PostgreSQLAdapter);
28
+ expect(adapter.getDb()).toBeDefined();
29
+ });
30
+
31
+ test('constructor uses default values when not provided', () => {
32
+ const adapter = new PostgreSQLAdapter({
33
+ password: 'test'
34
+ });
35
+
36
+ expect(adapter).toBeInstanceOf(PostgreSQLAdapter);
37
+ expect(adapter.getDb()).toBeDefined();
38
+ });
39
+
40
+ test('processOperation sets internal state', async () => {
41
+ const testData = {
42
+ blockNumber: 12345,
43
+ blockId: 'test-block',
44
+ prevBlockId: 'prev-block',
45
+ trxId: 'test-tx',
46
+ blockTime: new Date()
47
+ };
48
+
49
+ await sut.processOperation({}, testData.blockNumber, testData.blockId, testData.prevBlockId, testData.trxId, testData.blockTime);
50
+
51
+ // Since these are private properties, we can't directly test them
52
+ // But we can verify the method doesn't throw
53
+ expect(true).toBe(true);
54
+ });
55
+
56
+ test('getDb returns knex instance', () => {
57
+ const db = sut.getDb();
58
+ expect(db).toBeDefined();
59
+ expect(typeof db).toBe('function'); // Knex instance is callable
60
+ });
61
+
62
+ test('destroy method is implemented', async () => {
63
+ // Create a mock knex instance
64
+ const mockKnex = {
65
+ destroy: jest.fn().mockResolvedValue(undefined)
66
+ };
67
+ (sut as any).db = mockKnex;
68
+
69
+ const result = await sut.destroy();
70
+
71
+ expect(result).toBe(true);
72
+ expect(mockKnex.destroy).toHaveBeenCalled();
73
+ });
74
+
75
+ test('query method handles raw SQL', async () => {
76
+ // Create a mock knex instance
77
+ const mockKnex = {
78
+ raw: jest.fn().mockResolvedValue({ rows: [{ id: 1, name: 'test' }] })
79
+ };
80
+ (sut as any).db = mockKnex;
81
+
82
+ const result = await sut.query('SELECT * FROM test WHERE id = $1', [1]);
83
+
84
+ expect(result).toEqual([{ id: 1, name: 'test' }]);
85
+ expect(mockKnex.raw).toHaveBeenCalledWith('SELECT * FROM test WHERE id = $1', [1]);
86
+ });
87
+
88
+ test('loadState handles missing state gracefully', async () => {
89
+ // Mock the query to return no results
90
+ const mockQuery = jest.fn().mockReturnValue({
91
+ select: jest.fn().mockReturnValue({
92
+ first: jest.fn().mockResolvedValue(null)
93
+ })
94
+ });
95
+ (sut as any).db = mockQuery;
96
+
97
+ const result = await sut.loadState();
98
+
99
+ expect(result).toBeNull();
100
+ });
101
+
102
+ test('saveState serializes actions correctly', async () => {
103
+ const testData = {
104
+ lastBlockNumber: 12345,
105
+ actions: [{ id: 'test', type: 'action' }]
106
+ };
107
+
108
+ // Mock the query chain
109
+ const mockMerge = jest.fn().mockResolvedValue(undefined);
110
+ const mockOnConflict = jest.fn().mockReturnValue({ merge: mockMerge });
111
+ const mockInsert = jest.fn().mockReturnValue({ onConflict: mockOnConflict });
112
+ const mockQuery = jest.fn().mockReturnValue({ insert: mockInsert });
113
+ (sut as any).db = mockQuery;
114
+
115
+ const result = await sut.saveState(testData);
116
+
117
+ expect(result).toBe(true);
118
+ expect(mockQuery).toHaveBeenCalledWith('params');
119
+ expect(mockInsert).toHaveBeenCalledWith({
120
+ id: 1,
121
+ actions: JSON.stringify(testData.actions),
122
+ lastBlockNumber: 12345
123
+ });
124
+ expect(mockOnConflict).toHaveBeenCalledWith('id');
125
+ expect(mockMerge).toHaveBeenCalled();
126
+ });
127
+ });
@@ -1,43 +1,181 @@
1
- import { SqliteAdapter } from "../../src/adapters/sqlite.adapter";
2
-
3
-
4
- describe('SQLite Adapter', () => {
5
- let sut: SqliteAdapter;
6
-
7
- beforeEach(() => {
8
- sut = new SqliteAdapter();
9
- });
10
-
11
- test('find method returns values', async () => {
12
- jest.spyOn(sut.db, 'all').mockImplementation((query: any, callback: any) => {
13
- return callback(null, [{ id: 1, name: 'John' }]);
14
- });
15
-
16
- const result = await sut.find('USERS', { id: 1, name: 'John' });
17
-
18
- expect(result).toEqual([{ id: 1, name: 'John' }]);
19
- expect(sut.db.all).toHaveBeenCalledWith('SELECT * FROM USERS WHERE id = 1 AND name = John', expect.any(Function));
20
- });
21
-
22
- test('findOne method returns value', async () => {
23
- jest.spyOn(sut.db, 'get').mockImplementation((query: any, callback: any) => {
24
- return callback(null, { id: 1, name: 'John' });
25
- });
26
-
27
- const result = await sut.findOne('USERS', { id: 1, name: 'John', email: 'john@hotmail.com' });
28
-
29
- expect(result).toEqual({ id: 1, name: 'John' });
30
- expect(sut.db.get).toHaveBeenCalledWith('SELECT * FROM USERS WHERE id = 1 AND name = John AND email = john@hotmail.com', expect.any(Function));
31
- });
32
-
33
- test('replace method replaces value', async () => {
34
- jest.spyOn(sut.db, 'run').mockImplementation((query: any, callback: any) => {
35
- return callback(null, { id: 1, name: 'John' });
36
- });
37
-
38
- const result = await sut.replace('USERS', { id: 1, name: 'John' }, { id: 2, name: 'Johnny' });
39
-
40
- expect(result).toEqual({ id: 2, name: 'Johnny' });
41
- expect(sut.db.run).toHaveBeenCalledWith(`REPLACE INTO USERS id = 1 AND name = John VALUES ([object Object])`, expect.any(Function));
42
- });
1
+ import { SqliteAdapter } from "../../src/adapters/sqlite.adapter";
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ describe('SQLite Adapter', () => {
6
+ let sut: SqliteAdapter;
7
+ let testDbPath: string;
8
+
9
+ beforeEach(async () => {
10
+ testDbPath = path.resolve(__dirname, `../../src/adapters/hive-stream-test-basic-${Date.now()}-${Math.random()}.db`);
11
+
12
+ if (fs.existsSync(testDbPath)) {
13
+ fs.unlinkSync(testDbPath);
14
+ }
15
+
16
+ sut = new SqliteAdapter(testDbPath);
17
+ await sut.create();
18
+ });
19
+
20
+ afterEach(async () => {
21
+ if (sut && sut.getDb()) {
22
+ await sut.destroy();
23
+ }
24
+
25
+ if (fs.existsSync(testDbPath)) {
26
+ fs.unlinkSync(testDbPath);
27
+ }
28
+ });
29
+
30
+ test('find method returns values', async () => {
31
+ // Create a test table
32
+ await sut.db.schema.createTableIfNotExists('USERS', table => {
33
+ table.integer('id');
34
+ table.string('name');
35
+ });
36
+
37
+ // Insert test data
38
+ await sut.db('USERS').insert({ id: 1, name: 'John' });
39
+
40
+ const result = await sut.find('USERS', { id: 1, name: 'John' });
41
+
42
+ expect(result).toEqual([{ id: 1, name: 'John' }]);
43
+ });
44
+
45
+ test('findOne method returns value', async () => {
46
+ // Create a test table
47
+ await sut.db.schema.createTableIfNotExists('USERS', table => {
48
+ table.integer('id');
49
+ table.string('name');
50
+ table.string('email');
51
+ });
52
+
53
+ // Insert test data
54
+ await sut.db('USERS').insert({ id: 1, name: 'John', email: 'john@hotmail.com' });
55
+
56
+ const result = await sut.findOne('USERS', { id: 1, name: 'John', email: 'john@hotmail.com' });
57
+
58
+ expect(result).toEqual({ id: 1, name: 'John', email: 'john@hotmail.com' });
59
+ });
60
+
61
+ test('replace method replaces value', async () => {
62
+ // Create a test table
63
+ await sut.db.schema.createTableIfNotExists('USERS', table => {
64
+ table.integer('id').primary();
65
+ table.string('name');
66
+ });
67
+
68
+ // Insert initial data
69
+ await sut.db('USERS').insert({ id: 1, name: 'John' });
70
+
71
+ const result = await sut.replace('USERS', { id: 1 }, { id: 1, name: 'Johnny' });
72
+
73
+ expect(result).toEqual({ id: 1, name: 'Johnny' });
74
+
75
+ // Verify the data was actually replaced
76
+ const updatedRecord = await sut.db('USERS').where({ id: 1 }).first();
77
+ expect(updatedRecord).toEqual({ id: 1, name: 'Johnny' });
78
+ });
79
+
80
+ test('insert method inserts values', async () => {
81
+ // Create a test table
82
+ await sut.db.schema.createTableIfNotExists('USERS', table => {
83
+ table.integer('id');
84
+ table.string('name');
85
+ });
86
+
87
+ const result = await sut.insert('USERS', { id: 1, name: 'Alice' });
88
+
89
+ expect(result).toBe(true);
90
+
91
+ // Verify the data was actually inserted
92
+ const insertedRecord = await sut.db('USERS').where({ id: 1 }).first();
93
+ expect(insertedRecord).toEqual({ id: 1, name: 'Alice' });
94
+ });
95
+
96
+ test('loadState and saveState work correctly', async () => {
97
+ const testData = {
98
+ lastBlockNumber: 12345,
99
+ actions: [{ id: 'test-action', type: 'test' }]
100
+ };
101
+
102
+ // Save state
103
+ const saveResult = await sut.saveState(testData);
104
+ expect(saveResult).toBe(true);
105
+
106
+ // Load state
107
+ const loadedState = await sut.loadState();
108
+ expect(loadedState?.lastBlockNumber).toBe(12345);
109
+ expect(loadedState?.actions).toEqual([{ id: 'test-action', type: 'test' }]);
110
+ });
111
+
112
+ test('processTransfer works correctly', async () => {
113
+ const mockPayload = {
114
+ name: 'test-contract',
115
+ action: 'transfer',
116
+ payload: { amount: '100', recipient: 'bob' }
117
+ };
118
+
119
+ const mockMetadata = {
120
+ sender: 'alice',
121
+ amount: '100 HIVE'
122
+ };
123
+
124
+ // Set up transaction context
125
+ await sut.processOperation({}, 12345, 'block123', 'prevblock', 'tx123', new Date());
126
+
127
+ const result = await sut.processTransfer({}, mockPayload, mockMetadata);
128
+ expect(result).toBe(true);
129
+
130
+ // Verify the transfer was stored
131
+ const transfers = await sut.getTransfers();
132
+ expect(transfers).toHaveLength(1);
133
+ expect(transfers[0].sender).toBe('alice');
134
+ expect(transfers[0].contractName).toBe('test-contract');
135
+ });
136
+
137
+ test('processCustomJson works correctly', async () => {
138
+ const mockPayload = {
139
+ name: 'test-contract',
140
+ action: 'custom',
141
+ payload: { data: 'test' }
142
+ };
143
+
144
+ const mockMetadata = {
145
+ sender: 'alice',
146
+ isSignedWithActiveKey: true
147
+ };
148
+
149
+ // Set up transaction context
150
+ await sut.processOperation({}, 12345, 'block123', 'prevblock', 'tx123', new Date());
151
+
152
+ const result = await sut.processCustomJson({}, mockPayload, mockMetadata);
153
+ expect(result).toBe(true);
154
+
155
+ // Verify the custom JSON was stored
156
+ const customJson = await sut.getJson();
157
+ expect(customJson).toHaveLength(1);
158
+ expect(customJson[0].sender).toBe('alice');
159
+ expect(customJson[0].contractName).toBe('test-contract');
160
+ expect(customJson[0].isSignedWithActiveKey).toBe(1);
161
+ });
162
+
163
+ test('query method executes raw SQL', async () => {
164
+ // Create a test table
165
+ await sut.db.schema.createTableIfNotExists('USERS', table => {
166
+ table.integer('id');
167
+ table.string('name');
168
+ });
169
+
170
+ // Insert test data
171
+ await sut.db('USERS').insert({ id: 1, name: 'Alice' });
172
+ await sut.db('USERS').insert({ id: 2, name: 'Bob' });
173
+
174
+ // Test raw SQL query
175
+ const result = await sut.query('SELECT * FROM USERS WHERE name = ?', ['Alice']);
176
+
177
+ expect(result).toHaveLength(1);
178
+ expect(result[0].name).toBe('Alice');
179
+ expect(result[0].id).toBe(1);
180
+ });
43
181
  });