@buley/dash 0.0.30

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 (97) hide show
  1. package/.coveralls.yml +1 -0
  2. package/.github/workflows/opencommit.yml +33 -0
  3. package/.github/workflows/testing.yml +20 -0
  4. package/.gitmodules +0 -0
  5. package/README.md +98 -0
  6. package/behaviors/cache.dev.js +282 -0
  7. package/behaviors/changes.dev.js +337 -0
  8. package/behaviors/collect.dev.js +40 -0
  9. package/behaviors/examples/async.dev.js +17 -0
  10. package/behaviors/firebase.dev.js +283 -0
  11. package/behaviors/live.dev.js +67 -0
  12. package/behaviors/map.dev.js +54 -0
  13. package/behaviors/mapreduce.dev.js +68 -0
  14. package/behaviors/match.dev.js +66 -0
  15. package/behaviors/patch.dev.js +69 -0
  16. package/behaviors/rest.dev.js +340 -0
  17. package/behaviors/shorthand.dev.js +59 -0
  18. package/behaviors/stats.dev.js +672 -0
  19. package/dist/behaviors/index.js +142 -0
  20. package/dist/database/index.js +76 -0
  21. package/dist/databases/index.js +121 -0
  22. package/dist/entry/index.js +166 -0
  23. package/dist/index.js +93 -0
  24. package/dist/indexes/index.js +153 -0
  25. package/dist/store/index.js +97 -0
  26. package/dist/stores/index.js +90 -0
  27. package/dist/utilities/index.js +174 -0
  28. package/documentation/database/closing.md +3 -0
  29. package/documentation/database/getting.md +1 -0
  30. package/documentation/database/opening.md +5 -0
  31. package/documentation/database/removing.md +3 -0
  32. package/documentation/databases.md +21 -0
  33. package/documentation/entries.md +13 -0
  34. package/documentation/entry/adding.md +1 -0
  35. package/documentation/entry/getting.md +4 -0
  36. package/documentation/entry/putting.md +0 -0
  37. package/documentation/entry/removing.md +3 -0
  38. package/documentation/entry/updating.md +0 -0
  39. package/documentation/general/security.md +10 -0
  40. package/documentation/general/transaction/requests.md +3 -0
  41. package/documentation/general/transactions.md +5 -0
  42. package/documentation/index/creating.md +1 -0
  43. package/documentation/index/getting.md +1 -0
  44. package/documentation/index/iterating.md +1 -0
  45. package/documentation/index/removing.md +1 -0
  46. package/documentation/indexes.md +3 -0
  47. package/documentation/key/cursors.md +5 -0
  48. package/documentation/key/range/bounds.md +11 -0
  49. package/documentation/key/range/direction.md +1 -0
  50. package/documentation/key/ranges.md +3 -0
  51. package/documentation/keys.md +12 -0
  52. package/documentation/objectstore/clearing.md +1 -0
  53. package/documentation/objectstore/creating.md +1 -0
  54. package/documentation/objectstore/getting.md +1 -0
  55. package/documentation/objectstore/iteration.md +1 -0
  56. package/documentation/objectstore/removing.md +1 -0
  57. package/documentation/overview.md +5 -0
  58. package/documentation/stores.md +13 -0
  59. package/jest.config.js +12 -0
  60. package/package.json +40 -0
  61. package/src/behaviors/index.ts +140 -0
  62. package/src/database/index.ts +81 -0
  63. package/src/databases/index.ts +127 -0
  64. package/src/entry/index.ts +183 -0
  65. package/src/index/index.ts +61 -0
  66. package/src/index.ts +96 -0
  67. package/src/indexes/index.ts +151 -0
  68. package/src/store/index.ts +102 -0
  69. package/src/stores/index.ts +90 -0
  70. package/src/utilities/index.ts +349 -0
  71. package/tests/behaviors/behaviors.spec.ts +123 -0
  72. package/tests/database/database.spec.ts +177 -0
  73. package/tests/databases/databases.spec.ts +199 -0
  74. package/tests/entry/entry.spec.ts +252 -0
  75. package/tests/index/index.spec.ts +94 -0
  76. package/tests/indexes/indexes.spec.ts +203 -0
  77. package/tests/store/store.spec.ts +164 -0
  78. package/tests/stores/stores.spec.ts +148 -0
  79. package/tests/utilities/clone.spec.ts +48 -0
  80. package/tests/utilities/cloneError.spec.ts +33 -0
  81. package/tests/utilities/contains.spec.ts +28 -0
  82. package/tests/utilities/exists.spec.ts +21 -0
  83. package/tests/utilities/is.spec.ts +37 -0
  84. package/tests/utilities/isArray.spec.ts +21 -0
  85. package/tests/utilities/isBoolean.spec.ts +23 -0
  86. package/tests/utilities/isEmpty.spec.ts +45 -0
  87. package/tests/utilities/isFunction.spec.ts +30 -0
  88. package/tests/utilities/isNumber.spec.ts +29 -0
  89. package/tests/utilities/isObject.spec.ts +42 -0
  90. package/tests/utilities/isRegEx.spec.ts +33 -0
  91. package/tests/utilities/isString.spec.ts +25 -0
  92. package/tests/utilities/isnt.spec.ts +50 -0
  93. package/tests/utilities/randomId.spec.ts +39 -0
  94. package/tests/utilities/safeApply.spec.ts +49 -0
  95. package/tests/utilities/safeEach.spec.ts +38 -0
  96. package/tests/utilities/safeIterate.spec.ts +47 -0
  97. package/tsconfig.json +16 -0
@@ -0,0 +1,199 @@
1
+ import { databasesMethods } from '../../src/databases';
2
+ import { DashContext } from '../../src/utilities';
3
+
4
+ // Mock indexedDB globally
5
+ const mockIndexedDB = {
6
+ databases: jest.fn(),
7
+ open: jest.fn(),
8
+ deleteDatabase: jest.fn(),
9
+ };
10
+ (global as any).indexedDB = mockIndexedDB;
11
+
12
+ describe('databasesMethods', () => {
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ });
16
+
17
+ describe('open', () => {
18
+ it('should open a database successfully', async () => {
19
+ const mockDB = {};
20
+ const mockRequest = {
21
+ addEventListener: jest.fn((event, callback) => {
22
+ if (event === 'success') {
23
+ callback({ target: { result: mockDB } });
24
+ }
25
+ }),
26
+ };
27
+ mockIndexedDB.open.mockReturnValue(mockRequest);
28
+
29
+ const open_ctx: DashContext = {
30
+ database: 'testDB',
31
+ version: 1,
32
+ };
33
+
34
+ const result = await databasesMethods.open(open_ctx);
35
+ expect(result.db).toBe(mockDB);
36
+ expect(mockIndexedDB.open).toHaveBeenCalledWith('testDB', 1);
37
+ });
38
+
39
+ it('should handle upgradeneeded event', async () => {
40
+ const mockDB = {};
41
+ const mockRequest = {
42
+ addEventListener: jest.fn((event, callback) => {
43
+ if (event === 'upgradeneeded') {
44
+ callback({ target: { result: mockDB } });
45
+ }
46
+ }),
47
+ };
48
+ mockIndexedDB.open.mockReturnValue(mockRequest);
49
+
50
+ const open_ctx: DashContext = {
51
+ database: 'testDB',
52
+ version: 2,
53
+ on_upgrade_needed: jest.fn(),
54
+ };
55
+
56
+ const result = await databasesMethods.open(open_ctx);
57
+ expect(result.db).toBe(mockDB);
58
+ expect(open_ctx.on_upgrade_needed).toHaveBeenCalled();
59
+ });
60
+
61
+ it('should handle errors', async () => {
62
+ const mockError = new Error('Open failed');
63
+ const mockRequest = {
64
+ addEventListener: jest.fn((event, callback) => {
65
+ if (event === 'error') {
66
+ callback({ target: { error: mockError } });
67
+ }
68
+ }),
69
+ };
70
+ mockIndexedDB.open.mockReturnValue(mockRequest);
71
+
72
+ const open_ctx: DashContext = {
73
+ database: 'testDB',
74
+ version: 1,
75
+ };
76
+
77
+ await expect(databasesMethods.open(open_ctx)).rejects.toMatchObject({
78
+ error: mockError,
79
+ });
80
+ });
81
+ });
82
+
83
+ describe('get', () => {
84
+ it('should get all databases successfully', async () => {
85
+ const mockDatabases = [{ name: 'db1' }, { name: 'db2' }];
86
+ mockIndexedDB.databases.mockReturnValue(mockDatabases);
87
+
88
+ const get_ctx: DashContext = {
89
+ databases: [],
90
+ on_success: jest.fn(),
91
+ };
92
+
93
+ const result = await databasesMethods.get(get_ctx);
94
+ expect(result.databases).toEqual(mockDatabases);
95
+ expect(get_ctx.on_success).toHaveBeenCalledWith(result);
96
+ });
97
+
98
+ it('should handle errors when getting databases', async () => {
99
+ const mockError = new Error('Get failed');
100
+ mockIndexedDB.databases.mockImplementation(() => {
101
+ throw mockError;
102
+ });
103
+
104
+ const get_ctx: DashContext = {
105
+ databases: [],
106
+ on_error: jest.fn(),
107
+ };
108
+
109
+ await expect(databasesMethods.get(get_ctx)).rejects.toMatchObject({
110
+ error: mockError,
111
+ });
112
+ expect(get_ctx.on_error).toHaveBeenCalled();
113
+ });
114
+ });
115
+
116
+ describe('delete', () => {
117
+ it('should delete a database successfully', async () => {
118
+ const mockRequest = {
119
+ addEventListener: jest.fn((event, callback) => {
120
+ if (event === 'success') {
121
+ callback();
122
+ }
123
+ }),
124
+ };
125
+ mockIndexedDB.deleteDatabase.mockReturnValue(mockRequest);
126
+
127
+ const delete_ctx: DashContext = {
128
+ database: 'testDB',
129
+ };
130
+
131
+ const result = await databasesMethods.delete(delete_ctx);
132
+ expect(result).toBe(delete_ctx);
133
+ expect(mockIndexedDB.deleteDatabase).toHaveBeenCalledWith('testDB');
134
+ });
135
+
136
+ it('should handle errors during deletion', async () => {
137
+ const mockError = new Error('Delete failed');
138
+ const mockRequest = {
139
+ addEventListener: jest.fn((event, callback) => {
140
+ if (event === 'error') {
141
+ callback({ target: { error: mockError } });
142
+ }
143
+ }),
144
+ };
145
+ mockIndexedDB.deleteDatabase.mockReturnValue(mockRequest);
146
+
147
+ const delete_ctx: DashContext = {
148
+ database: 'testDB',
149
+ };
150
+
151
+ await expect(databasesMethods.delete(delete_ctx)).rejects.toMatchObject({
152
+ error: mockError,
153
+ });
154
+ });
155
+ });
156
+
157
+ describe('close', () => {
158
+ it('should close a database', async () => {
159
+ const mockDB = {
160
+ close: jest.fn(),
161
+ } as unknown as IDBDatabase;
162
+ const close_ctx: DashContext = {
163
+ db: mockDB,
164
+ };
165
+
166
+ const result = await databasesMethods.close(close_ctx);
167
+ expect(result).toBe(close_ctx);
168
+ expect(mockDB.close).toHaveBeenCalled();
169
+ });
170
+ });
171
+
172
+ describe('listAll', () => {
173
+ it('should list all databases when supported', async () => {
174
+ const mockDatabases = [{ name: 'db1' }, { name: 'db2' }];
175
+ mockIndexedDB.databases.mockResolvedValue(mockDatabases);
176
+
177
+ const result = await databasesMethods.listAll();
178
+ expect(result).toEqual(mockDatabases);
179
+ });
180
+
181
+ it('should handle errors when listing databases', async () => {
182
+ const mockError = new Error('Listing failed');
183
+ mockIndexedDB.databases.mockRejectedValue(mockError);
184
+
185
+ await expect(databasesMethods.listAll()).rejects.toThrow('Listing failed');
186
+ });
187
+
188
+ it('should reject when databases method is not supported', async () => {
189
+ const originalDatabases = mockIndexedDB.databases;
190
+ mockIndexedDB.databases = undefined as any;
191
+
192
+ await expect(databasesMethods.listAll()).rejects.toThrow(
193
+ "The `databases` method is not supported in this browser."
194
+ );
195
+
196
+ mockIndexedDB.databases = originalDatabases;
197
+ });
198
+ });
199
+ });
@@ -0,0 +1,252 @@
1
+
2
+ import { entryMethods } from "../../src/entry";
3
+
4
+ // Mock data and context for tests
5
+ const mockObjectStore = {
6
+ add: jest.fn(),
7
+ get: jest.fn(),
8
+ put: jest.fn(),
9
+ delete: jest.fn(),
10
+ index: jest.fn(),
11
+ count: jest.fn(),
12
+ };
13
+
14
+ describe('entryMethods', () => {
15
+
16
+ describe('add', () => {
17
+ const add_ctx = {
18
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
19
+ data: {},
20
+ key: 'testKey',
21
+ };
22
+
23
+ it('should add an entry and resolve with the updated context', async () => {
24
+ mockObjectStore.add.mockReturnValueOnce({
25
+ addEventListener: (event: string, callback: () => void) => {
26
+ if (event === 'success') {
27
+ callback();
28
+ }
29
+ },
30
+ result: 'newKey'
31
+ });
32
+ const result = await entryMethods.add({ ...add_ctx, objectstore: mockObjectStore as unknown as IDBObjectStore });
33
+ expect((result as { key: string }).key).toBe('newKey');
34
+ });
35
+
36
+ it('should reject with an error if the request fails', async () => {
37
+ return await expect(entryMethods.add(add_ctx)).rejects.toEqual(expect.objectContaining({
38
+ error: expect.objectContaining({
39
+ message: "Request object is null",
40
+ name: "DashRequestError"
41
+ })
42
+ }));
43
+ });
44
+ });
45
+
46
+ describe('get', () => {
47
+ const get_ctx = {
48
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
49
+ key: 'testKey',
50
+ index: null,
51
+ };
52
+
53
+ it('should get an entry and resolve with the context', async () => {
54
+ mockObjectStore.get.mockReturnValueOnce({
55
+ addEventListener: (event: string, callback: () => void) => {
56
+ if (event === 'success') {
57
+ callback();
58
+ }
59
+ },
60
+ result: { entryData: 'test' }
61
+ });
62
+ const result = await entryMethods.get(get_ctx) as { entry: { entryData: string } };
63
+ expect(result.entry).toEqual({ entryData: 'test' });
64
+ });
65
+
66
+ it('should reject with an error if the request fails', async () => {
67
+ await expect(entryMethods.get(get_ctx)).rejects.toEqual(expect.objectContaining({
68
+ error: expect.objectContaining({
69
+ message: "Request object is null",
70
+ name: "DashRequestError"
71
+ })
72
+ }));
73
+ });
74
+ });
75
+
76
+ describe('put', () => {
77
+ const put_ctx = {
78
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
79
+ data: {},
80
+ key: 'testKey',
81
+ };
82
+
83
+ it('should put an entry and resolve with the updated context', async () => {
84
+ mockObjectStore.put.mockReturnValueOnce({
85
+ addEventListener: (event: string, callback: () => void) => {
86
+ if (event === 'success') {
87
+ callback();
88
+ }
89
+ },
90
+ });
91
+ const result = await entryMethods.put(put_ctx) as { entry: any };
92
+ expect(result.entry).toBe(put_ctx.data);
93
+ });
94
+
95
+ it('should reject with an error if the request fails', async () => {
96
+ await expect(entryMethods.put(put_ctx)).rejects.toEqual(expect.objectContaining({
97
+ error: expect.objectContaining({
98
+ message: "Request object is null",
99
+ name: "DashRequestError"
100
+ })
101
+ }));
102
+ });
103
+ });
104
+
105
+ describe('remove', () => {
106
+ const remove_ctx = {
107
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
108
+ key: 'testKey',
109
+ };
110
+
111
+ it('should remove an entry and resolve with the context', async () => {
112
+ mockObjectStore.delete.mockReturnValueOnce({
113
+ addEventListener: (event: string, callback: () => void) => {
114
+ if (event === 'success') {
115
+ callback();
116
+ }
117
+ },
118
+ });
119
+ const result = await entryMethods.remove(remove_ctx) as { key: string };
120
+ expect(result.key).toBe('testKey');
121
+ });
122
+
123
+ it('should reject with an error if the request fails', async () => {
124
+ await expect(entryMethods.remove(remove_ctx)).rejects.toEqual(expect.objectContaining({
125
+ error: expect.objectContaining({
126
+ message: "Request object is null",
127
+ name: "DashRequestError"
128
+ })
129
+ }));
130
+ });
131
+ });
132
+
133
+ describe('count', () => {
134
+ const count_ctx = {
135
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
136
+ };
137
+
138
+ it('should count entries and resolve with the context', async () => {
139
+ mockObjectStore.count.mockReturnValueOnce({
140
+ addEventListener: (event: string, callback: () => void) => {
141
+ if (event === 'success') {
142
+ callback();
143
+ }
144
+ },
145
+ result: 42,
146
+ });
147
+ const result = await entryMethods.count(count_ctx) as { amount: number };
148
+ expect(result.amount).toBe(42);
149
+ });
150
+
151
+ it('should reject with an error if the request fails', async () => {
152
+ await expect(entryMethods.count(count_ctx)).rejects.toEqual(expect.objectContaining({
153
+ error: expect.objectContaining({
154
+ message: "Request object is null",
155
+ name: "DashRequestError"
156
+ })
157
+ }));
158
+ });
159
+ });
160
+
161
+ describe('entryMethods - additional tests', () => {
162
+ describe('add', () => {
163
+ const add_ctx = {
164
+ objectstore: { add: jest.fn(() => null) } as any,
165
+ data: { name: 'test' },
166
+ key: 'testKey',
167
+ };
168
+
169
+ it('should reject with an error if request fails', async () => {
170
+ add_ctx.objectstore = { add: jest.fn(() => null) };
171
+ await expect(entryMethods.add(add_ctx)).rejects.toEqual(expect.objectContaining({
172
+ error: expect.objectContaining({
173
+ message: "Request object is null",
174
+ name: "DashRequestError"
175
+ })
176
+ }));
177
+ });
178
+ });
179
+
180
+ describe('get', () => {
181
+ const get_ctx1 = {
182
+ objectstore: {
183
+ index: jest.fn(() => null),
184
+ get: jest.fn(() => null)
185
+ } as any,
186
+ key: 'testKey',
187
+ index: null,
188
+ };
189
+
190
+ const get_ctx2 = {
191
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
192
+ key: 'testKey',
193
+ index: null,
194
+ };
195
+
196
+ it('should reject with an error if the objectstore is null', async () => {
197
+ return await expect(entryMethods.get(get_ctx1)).rejects.toEqual(expect.objectContaining({
198
+ error: expect.objectContaining({
199
+ message: "Request object is null",
200
+ name: "DashRequestError"
201
+ })
202
+ }));
203
+ });
204
+
205
+ it('should reject with an error if the index is invalid', async () => {
206
+ get_ctx1.objectstore = {
207
+ index: jest.fn(() => null),
208
+ get: jest.fn(() => null)
209
+ };
210
+ (get_ctx1 as any).index = 'invalidIndex'; // Casting to any if necessary
211
+ await expect(entryMethods.get(get_ctx1)).rejects.toEqual(expect.objectContaining({
212
+ error: expect.objectContaining({
213
+ message: "Invalid index or get method",
214
+ name: "DashInvalidIndex"
215
+ })
216
+ }));
217
+ });
218
+ });
219
+
220
+ describe('remove', () => {
221
+ const remove_ctx = {
222
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
223
+ key: 'testKey',
224
+ };
225
+
226
+ it('should reject with an error if the request is null', async () => {
227
+ return await expect(entryMethods.remove(remove_ctx)).rejects.toEqual(expect.objectContaining({
228
+ error: expect.objectContaining({
229
+ message: "Request object is null",
230
+ name: "DashRequestError"
231
+ })
232
+ }));
233
+ });
234
+ });
235
+
236
+ describe('count', () => {
237
+ const count_ctx = {
238
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
239
+ };
240
+
241
+ it('should reject with an error if the objectstore is null', async () => {
242
+ return await expect(entryMethods.count(count_ctx)).rejects.toEqual(expect.objectContaining({
243
+ error: expect.objectContaining({
244
+ message: "Request object is null",
245
+ name: "DashRequestError"
246
+ })
247
+ }));
248
+ });
249
+ });
250
+ });
251
+
252
+ });
@@ -0,0 +1,94 @@
1
+ import { indexMethods } from '../../src/index/index';
2
+ import { DashContext } from '../../src/utilities';
3
+
4
+ describe('indexMethods', () => {
5
+ describe('get', () => {
6
+ it('should get an index and resolve with the entry', async () => {
7
+ const mockEntry = { id: 1, name: 'Test Entry' };
8
+ const mockRequest = {
9
+ result: mockEntry,
10
+ addEventListener: jest.fn((event, callback) => {
11
+ if (event === 'success') {
12
+ callback();
13
+ }
14
+ }),
15
+ };
16
+ const mockIndex = {
17
+ get: jest.fn(() => mockRequest),
18
+ };
19
+ const get_ctx: DashContext = {
20
+ idx: mockIndex as unknown as IDBIndex,
21
+ key: 'testKey',
22
+ };
23
+
24
+ const result = await indexMethods.get(get_ctx);
25
+
26
+ expect(mockIndex.get).toHaveBeenCalledWith('testKey');
27
+ expect((result as any).entry).toBe(mockEntry);
28
+ });
29
+
30
+ it('should reject with an error if the request fails', async () => {
31
+ const mockError = new Error('Test error');
32
+ const mockRequest = {
33
+ addEventListener: jest.fn((event, callback) => {
34
+ if (event === 'error') {
35
+ callback({ target: { error: mockError } });
36
+ }
37
+ }),
38
+ };
39
+ const mockIndex = {
40
+ get: jest.fn(() => mockRequest),
41
+ };
42
+ const get_ctx: DashContext = {
43
+ idx: mockIndex as unknown as IDBIndex,
44
+ key: 'testKey',
45
+ };
46
+
47
+ await expect(indexMethods.get(get_ctx)).rejects.toEqual(expect.objectContaining({
48
+ error: expect.any(Error),
49
+ }));
50
+ });
51
+ });
52
+
53
+ describe('remove', () => {
54
+ it('should remove an index and resolve', async () => {
55
+ const mockObjectStore = {
56
+ deleteIndex: jest.fn(),
57
+ };
58
+ const remove_ctx: DashContext = {
59
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
60
+ key: 'testIndex',
61
+ };
62
+
63
+ await indexMethods.remove(remove_ctx);
64
+
65
+ expect(mockObjectStore.deleteIndex).toHaveBeenCalledWith('testIndex');
66
+ });
67
+ });
68
+
69
+ describe('getIndexes', () => {
70
+ it('should get all indexes and resolve with an array', async () => {
71
+ const mockIndexNames = ['index1', 'index2', 'index3'];
72
+ const mockObjectStore = {
73
+ indexNames: mockIndexNames,
74
+ };
75
+ const ctx: DashContext = {
76
+ objectstore: mockObjectStore as unknown as IDBObjectStore,
77
+ };
78
+
79
+ const result = await indexMethods.getIndexes(ctx);
80
+
81
+ expect((result as { indexes: string[] }).indexes).toEqual(mockIndexNames);
82
+ });
83
+
84
+ it('should resolve with an empty array if there are no indexes', async () => {
85
+ const ctx: DashContext = {
86
+ objectstore: {} as IDBObjectStore,
87
+ };
88
+
89
+ const result = await indexMethods.getIndexes(ctx);
90
+
91
+ expect((result as { indexes: string[] }).indexes).toEqual([]);
92
+ });
93
+ });
94
+ });