bun-sqlite-for-rxdb 1.0.1 → 1.1.3

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 (88) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/connection-pool.d.ts +4 -0
  3. package/dist/connection-pool.d.ts.map +1 -0
  4. package/{src/index.ts → dist/index.d.ts} +1 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +13220 -0
  7. package/dist/instance.d.ts +42 -0
  8. package/dist/instance.d.ts.map +1 -0
  9. package/dist/query/builder.d.ts +6 -0
  10. package/dist/query/builder.d.ts.map +1 -0
  11. package/dist/query/operators.d.ts +21 -0
  12. package/dist/query/operators.d.ts.map +1 -0
  13. package/dist/query/schema-mapper.d.ts +8 -0
  14. package/dist/query/schema-mapper.d.ts.map +1 -0
  15. package/dist/query/smart-regex.d.ts +6 -0
  16. package/dist/query/smart-regex.d.ts.map +1 -0
  17. package/dist/rxdb-helpers.d.ts +28 -0
  18. package/dist/rxdb-helpers.d.ts.map +1 -0
  19. package/dist/statement-manager.d.ts +15 -0
  20. package/dist/statement-manager.d.ts.map +1 -0
  21. package/dist/storage.d.ts +4 -0
  22. package/dist/storage.d.ts.map +1 -0
  23. package/dist/types.d.ts +13 -0
  24. package/dist/types.d.ts.map +1 -0
  25. package/package.json +11 -3
  26. package/.serena/project.yml +0 -84
  27. package/ROADMAP.md +0 -532
  28. package/benchmarks/benchmark.ts +0 -145
  29. package/benchmarks/case-insensitive-10runs.ts +0 -156
  30. package/benchmarks/fts5-1m-scale.ts +0 -126
  31. package/benchmarks/fts5-before-after.ts +0 -104
  32. package/benchmarks/indexed-benchmark.ts +0 -141
  33. package/benchmarks/new-operators-benchmark.ts +0 -140
  34. package/benchmarks/query-builder-benchmark.ts +0 -88
  35. package/benchmarks/query-builder-consistency.ts +0 -109
  36. package/benchmarks/raw-better-sqlite3-10m.ts +0 -85
  37. package/benchmarks/raw-better-sqlite3.ts +0 -86
  38. package/benchmarks/raw-bun-sqlite-10m.ts +0 -85
  39. package/benchmarks/raw-bun-sqlite.ts +0 -86
  40. package/benchmarks/regex-10runs-all.ts +0 -216
  41. package/benchmarks/regex-comparison-benchmark.ts +0 -161
  42. package/benchmarks/regex-real-comparison.ts +0 -213
  43. package/benchmarks/run-10x.sh +0 -19
  44. package/benchmarks/smart-regex-benchmark.ts +0 -148
  45. package/benchmarks/sql-vs-mingo-benchmark.ts +0 -210
  46. package/benchmarks/sql-vs-mingo-comparison.ts +0 -175
  47. package/benchmarks/text-vs-jsonb.ts +0 -167
  48. package/benchmarks/wal-benchmark.ts +0 -112
  49. package/docs/architectural-patterns.md +0 -1336
  50. package/docs/id1-testsuite-journey.md +0 -839
  51. package/docs/official-test-suite-setup.md +0 -393
  52. package/nul +0 -0
  53. package/src/changestream.test.ts +0 -182
  54. package/src/cleanup.test.ts +0 -110
  55. package/src/collection-isolation.test.ts +0 -74
  56. package/src/connection-pool.test.ts +0 -102
  57. package/src/connection-pool.ts +0 -38
  58. package/src/findDocumentsById.test.ts +0 -122
  59. package/src/instance.ts +0 -382
  60. package/src/multi-instance-events.test.ts +0 -204
  61. package/src/query/and-operator.test.ts +0 -39
  62. package/src/query/builder.test.ts +0 -96
  63. package/src/query/builder.ts +0 -154
  64. package/src/query/elemMatch-operator.test.ts +0 -24
  65. package/src/query/exists-operator.test.ts +0 -28
  66. package/src/query/in-operators.test.ts +0 -54
  67. package/src/query/mod-operator.test.ts +0 -22
  68. package/src/query/nested-query.test.ts +0 -198
  69. package/src/query/not-operators.test.ts +0 -49
  70. package/src/query/operators.test.ts +0 -70
  71. package/src/query/operators.ts +0 -185
  72. package/src/query/or-operator.test.ts +0 -68
  73. package/src/query/regex-escaping-regression.test.ts +0 -43
  74. package/src/query/regex-operator.test.ts +0 -44
  75. package/src/query/schema-mapper.ts +0 -27
  76. package/src/query/size-operator.test.ts +0 -22
  77. package/src/query/smart-regex.ts +0 -52
  78. package/src/query/type-operator.test.ts +0 -37
  79. package/src/query-cache.test.ts +0 -286
  80. package/src/rxdb-helpers.test.ts +0 -348
  81. package/src/rxdb-helpers.ts +0 -262
  82. package/src/schema-version-isolation.test.ts +0 -126
  83. package/src/statement-manager.ts +0 -69
  84. package/src/storage.test.ts +0 -589
  85. package/src/storage.ts +0 -21
  86. package/src/types.ts +0 -14
  87. package/test/rxdb-test-suite.ts +0 -27
  88. package/tsconfig.json +0 -31
@@ -1,286 +0,0 @@
1
- import { describe, test, expect, beforeEach } from 'bun:test';
2
- import { buildWhereClause, getCacheSize, clearCache } from '../src/query/builder';
3
- import type { RxJsonSchema, MangoQuerySelector, RxDocumentData } from 'rxdb';
4
-
5
- type TestDoc = {
6
- id: string;
7
- name: string;
8
- age: number;
9
- data: string;
10
- _deleted: boolean;
11
- _attachments: {};
12
- _rev: string;
13
- _meta: { lwt: number };
14
- };
15
-
16
- const schema: RxJsonSchema<RxDocumentData<TestDoc>> = {
17
- version: 0,
18
- primaryKey: 'id',
19
- type: 'object',
20
- properties: {
21
- id: { type: 'string', maxLength: 100 },
22
- name: { type: 'string' },
23
- age: { type: 'number' },
24
- data: { type: 'string' },
25
- _deleted: { type: 'boolean' },
26
- _attachments: { type: 'object' },
27
- _rev: { type: 'string' },
28
- _meta: {
29
- type: 'object',
30
- properties: {
31
- lwt: { type: 'number' }
32
- },
33
- required: ['lwt']
34
- }
35
- },
36
- required: ['id', '_deleted', '_rev', '_meta']
37
- };
38
-
39
- describe('Query Builder Cache - Edge Cases & Production Readiness', () => {
40
- beforeEach(() => {
41
- clearCache();
42
- });
43
-
44
- test('Edge Case 1: HUGE selector (10KB+) should not crash', () => {
45
- const hugeSelector: MangoQuerySelector<RxDocumentData<TestDoc>> = {
46
- $or: Array.from({ length: 1000 }, (_, i) => ({ age: { $eq: i } }))
47
- };
48
-
49
- const start = performance.now();
50
- const result1 = buildWhereClause(hugeSelector, schema);
51
- const time1 = performance.now() - start;
52
-
53
- const start2 = performance.now();
54
- const result2 = buildWhereClause(hugeSelector, schema);
55
- const time2 = performance.now() - start2;
56
-
57
- expect(result1.sql).toBe(result2.sql);
58
- expect(time2).toBeLessThanOrEqual(time1 * 1.5);
59
- console.log(` Huge selector: ${time1.toFixed(2)}ms → ${time2.toFixed(2)}ms (${(time1/time2).toFixed(1)}x faster cached)`);
60
- });
61
-
62
- test('Edge Case 2: Cache eviction at 100 entries', () => {
63
- for (let i = 0; i < 150; i++) {
64
- const selector: MangoQuerySelector<RxDocumentData<TestDoc>> = { age: { $eq: i } };
65
- buildWhereClause(selector, schema);
66
- }
67
-
68
- const firstSelector: MangoQuerySelector<RxDocumentData<TestDoc>> = { age: { $eq: 0 } };
69
- const start = performance.now();
70
- buildWhereClause(firstSelector, schema);
71
- const time = performance.now() - start;
72
-
73
- expect(time).toBeGreaterThan(0);
74
- console.log(` First selector after 150 inserts: ${(time * 1000).toFixed(2)}µs (evicted, rebuilt)`);
75
- });
76
-
77
- test('Edge Case 3: Schema version change invalidates cache', () => {
78
- const selector: MangoQuerySelector<RxDocumentData<TestDoc>> = { age: { $gt: 30 } };
79
-
80
- const schema1 = { ...schema, version: 0 };
81
- const result1 = buildWhereClause(selector, schema1);
82
-
83
- const schema2 = { ...schema, version: 1 };
84
- const result2 = buildWhereClause(selector, schema2);
85
-
86
- expect(result1.sql).toBe(result2.sql);
87
- console.log(` Schema version change: cache invalidated correctly`);
88
- });
89
-
90
- test('Edge Case 4: Identical selectors with different object order', () => {
91
- const selector1: MangoQuerySelector<RxDocumentData<TestDoc>> = { age: { $gt: 30 }, name: { $eq: 'test' } };
92
- const selector2: MangoQuerySelector<RxDocumentData<TestDoc>> = { name: { $eq: 'test' }, age: { $gt: 30 } };
93
-
94
- const result1 = buildWhereClause(selector1, schema);
95
- const result2 = buildWhereClause(selector2, schema);
96
-
97
- expect(result1.sql).toBe(result2.sql);
98
- console.log(` Different object order: produces same SQL`);
99
- });
100
-
101
- test('Edge Case 5: Deeply nested selectors', () => {
102
- const deepSelector: MangoQuerySelector<RxDocumentData<TestDoc>> = {
103
- $and: [
104
- { $or: [{ age: { $gt: 20 } }, { age: { $lt: 10 } }] },
105
- { $or: [{ name: { $eq: 'a' } }, { name: { $eq: 'b' } }] },
106
- { $or: [{ data: { $exists: true } }, { data: { $exists: false } }] }
107
- ]
108
- };
109
-
110
- const start = performance.now();
111
- const result1 = buildWhereClause(deepSelector, schema);
112
- const time1 = performance.now() - start;
113
-
114
- const start2 = performance.now();
115
- const result2 = buildWhereClause(deepSelector, schema);
116
- const time2 = performance.now() - start2;
117
-
118
- expect(result1.sql).toBe(result2.sql);
119
- expect(time2).toBeLessThan(time1);
120
- console.log(` Deep nesting: ${time1.toFixed(2)}ms → ${time2.toFixed(2)}ms (${(time1/time2).toFixed(1)}x faster cached)`);
121
- });
122
-
123
- test('Edge Case 6: Special characters in selector values', () => {
124
- const specialSelector: MangoQuerySelector<RxDocumentData<TestDoc>> = {
125
- name: { $eq: 'test"with\'quotes\nand\ttabs' }
126
- };
127
-
128
- const result1 = buildWhereClause(specialSelector, schema);
129
- const result2 = buildWhereClause(specialSelector, schema);
130
-
131
- expect(result1.sql).toBe(result2.sql);
132
- expect(result1.args).toEqual(result2.args);
133
- console.log(` Special characters: handled correctly`);
134
- });
135
-
136
- test('Edge Case 7: Null and undefined values', () => {
137
- const nullSelector: MangoQuerySelector<RxDocumentData<TestDoc>> = {
138
- name: { $eq: null as any }
139
- };
140
-
141
- const result = buildWhereClause(nullSelector, schema);
142
- expect(result.sql).toContain('IS NULL');
143
- console.log(` Null values: handled correctly`);
144
- });
145
-
146
- test('Edge Case 8: Empty selector', () => {
147
- const emptySelector: MangoQuerySelector<RxDocumentData<TestDoc>> = {};
148
-
149
- const result = buildWhereClause(emptySelector, schema);
150
- expect(result.sql).toBe('1=1');
151
- console.log(` Empty selector: returns 1=1 (match all)`);
152
- });
153
-
154
- test('Edge Case 9: Cache hit rate with repeated queries', () => {
155
- const selectors = [
156
- { age: { $gt: 30 } },
157
- { age: { $lt: 20 } },
158
- { name: { $eq: 'test' } }
159
- ];
160
-
161
- const start1 = process.hrtime.bigint();
162
- for (let i = 0; i < 3; i++) {
163
- buildWhereClause(selectors[i] as MangoQuerySelector<RxDocumentData<TestDoc>>, schema);
164
- }
165
- const firstTime = process.hrtime.bigint() - start1;
166
-
167
- const start2 = process.hrtime.bigint();
168
- for (let i = 0; i < 100000; i++) {
169
- const selector = selectors[i % selectors.length];
170
- buildWhereClause(selector as MangoQuerySelector<RxDocumentData<TestDoc>>, schema);
171
- }
172
- const cachedTime = process.hrtime.bigint() - start2;
173
-
174
- const avgFirst = Number(firstTime) / 3;
175
- const avgCached = Number(cachedTime) / 100000;
176
- const speedup = avgFirst / avgCached;
177
-
178
- expect(speedup).toBeGreaterThan(1.2);
179
- expect(getCacheSize()).toBe(3);
180
- console.log(` Cache hit rate: ${speedup.toFixed(1)}x faster for repeated queries`);
181
- });
182
-
183
- test('Edge Case 10: Memory stress test (1000 unique queries)', () => {
184
- const start = performance.now();
185
-
186
- for (let i = 0; i < 1000; i++) {
187
- const selector: MangoQuerySelector<RxDocumentData<TestDoc>> = {
188
- age: { $eq: i },
189
- name: { $eq: `user${i}` }
190
- };
191
- buildWhereClause(selector, schema);
192
- }
193
-
194
- const time = performance.now() - start;
195
- const avgPerQuery = time / 1000;
196
-
197
- expect(avgPerQuery).toBeLessThan(1);
198
- console.log(` 1000 unique queries: ${time.toFixed(2)}ms total (${avgPerQuery.toFixed(3)}ms per query)`);
199
- });
200
-
201
- test('Production Scenario 1: Concurrent queries from multiple collections', () => {
202
- const schema1 = { ...schema, version: 0 };
203
- const schema2 = { ...schema, version: 1 };
204
- const schema3 = { ...schema, version: 2 };
205
-
206
- const selector: MangoQuerySelector<RxDocumentData<TestDoc>> = { age: { $gt: 30 } };
207
-
208
- const result1 = buildWhereClause(selector, schema1);
209
- const result2 = buildWhereClause(selector, schema2);
210
- const result3 = buildWhereClause(selector, schema3);
211
-
212
- expect(result1.sql).toBe(result2.sql);
213
- expect(result2.sql).toBe(result3.sql);
214
- console.log(` Multiple schema versions: isolated correctly`);
215
- });
216
-
217
- test('Production Scenario 2: High-frequency queries (10k/sec simulation)', () => {
218
- const selectors = Array.from({ length: 10 }, (_, i) => ({ age: { $eq: i * 10 } }));
219
-
220
- const start = performance.now();
221
- for (let i = 0; i < 10000; i++) {
222
- const selector = selectors[i % selectors.length];
223
- buildWhereClause(selector as MangoQuerySelector<RxDocumentData<TestDoc>>, schema);
224
- }
225
- const time = performance.now() - start;
226
-
227
- const qps = 10000 / (time / 1000);
228
- expect(qps).toBeGreaterThan(100000);
229
- console.log(` High-frequency: ${qps.toFixed(0)} queries/sec (${(time / 10000 * 1000).toFixed(2)}µs per query)`);
230
- });
231
-
232
- test('Production Scenario 3: Cache behavior under load', () => {
233
- const results: number[] = [];
234
-
235
- for (let batch = 0; batch < 5; batch++) {
236
- const start = performance.now();
237
- for (let i = 0; i < 1000; i++) {
238
- const selector: MangoQuerySelector<RxDocumentData<TestDoc>> = { age: { $eq: i % 50 } };
239
- buildWhereClause(selector, schema);
240
- }
241
- results.push(performance.now() - start);
242
- }
243
-
244
- const firstBatch = results[0];
245
- const avgLaterBatches = results.slice(1).reduce((a, b) => a + b, 0) / 4;
246
-
247
- expect(avgLaterBatches).toBeLessThanOrEqual(firstBatch * 1.5);
248
- expect(getCacheSize()).toBe(50);
249
- console.log(` Under load: First batch ${firstBatch.toFixed(2)}ms, Later batches ${avgLaterBatches.toFixed(2)}ms`);
250
- });
251
-
252
- test('Edge Case 13: Cache is BOUNDED at 500 entries (no exponential growth)', () => {
253
- clearCache();
254
-
255
- for (let i = 0; i < 1000; i++) {
256
- const selector: MangoQuerySelector<RxDocumentData<TestDoc>> = {
257
- id: { $eq: `unique-${i}` },
258
- age: { $eq: i }
259
- };
260
- buildWhereClause(selector, schema);
261
- }
262
-
263
- expect(getCacheSize()).toBe(500);
264
- console.log(` Cache bounded: Added 1000 unique queries, cache size = ${getCacheSize()} (max 500) ✅`);
265
-
266
- const firstQuery: MangoQuerySelector<RxDocumentData<TestDoc>> = {
267
- id: { $eq: 'unique-0' },
268
- age: { $eq: 0 }
269
- };
270
- const lastQuery: MangoQuerySelector<RxDocumentData<TestDoc>> = {
271
- id: { $eq: 'unique-999' },
272
- age: { $eq: 999 }
273
- };
274
-
275
- const start1 = performance.now();
276
- buildWhereClause(firstQuery, schema);
277
- const time1 = performance.now() - start1;
278
-
279
- const start2 = performance.now();
280
- buildWhereClause(lastQuery, schema);
281
- const time2 = performance.now() - start2;
282
-
283
- expect(time2).toBeLessThanOrEqual(time1 * 2);
284
- console.log(` FIFO eviction works: First query ${time1.toFixed(3)}ms (evicted), Last query ${time2.toFixed(3)}ms (cached)`);
285
- });
286
- });
@@ -1,348 +0,0 @@
1
- import { describe, test, expect } from 'bun:test';
2
- import { categorizeBulkWriteRows, ensureRxStorageInstanceParamsAreCorrect } from './rxdb-helpers';
3
- import type { BulkWriteRow, RxDocumentData, RxStorageInstance, RxStorageInstanceCreationParams, RxStorageDefaultCheckpoint, RxStorageCountResult, EventBulk, RxStorageChangeEvent, RxJsonSchema } from 'rxdb';
4
- import { Subject } from 'rxjs';
5
-
6
- type TestDoc = {
7
- id: string;
8
- name: string;
9
- age: number;
10
- };
11
-
12
- type TestInternals = Record<string, never>;
13
- type TestOptions = Record<string, never>;
14
-
15
- const createTestSchema = (overrides: Partial<RxJsonSchema<RxDocumentData<TestDoc>>> = {}): RxJsonSchema<RxDocumentData<TestDoc>> => ({
16
- version: 0,
17
- primaryKey: 'id',
18
- type: 'object',
19
- properties: {
20
- id: { type: 'string', maxLength: 100 },
21
- name: { type: 'string' },
22
- age: { type: 'number' },
23
- _deleted: { type: 'boolean' },
24
- _attachments: { type: 'object' },
25
- _rev: { type: 'string' },
26
- _meta: { type: 'object' }
27
- },
28
- required: ['id'],
29
- ...overrides
30
- } as RxJsonSchema<RxDocumentData<TestDoc>>);
31
-
32
- function createMockStorageInstance<T>(): RxStorageInstance<T, TestInternals, TestOptions, RxStorageDefaultCheckpoint> {
33
- const changeStreamSubject = new Subject<EventBulk<RxStorageChangeEvent<T>, RxStorageDefaultCheckpoint>>();
34
-
35
- return {
36
- schema: createTestSchema() as Readonly<RxJsonSchema<RxDocumentData<T>>>,
37
- collectionName: 'test',
38
- databaseName: 'testdb',
39
- options: {},
40
- internals: {},
41
- bulkWrite: async () => ({ error: [] }),
42
- findDocumentsById: async () => [],
43
- query: async () => ({ documents: [] }),
44
- count: async (): Promise<RxStorageCountResult> => ({ count: 0, mode: 'fast' }),
45
- getAttachmentData: async () => '',
46
- getChangedDocumentsSince: async () => ({
47
- documents: [],
48
- checkpoint: { id: '', lwt: 0 }
49
- }),
50
- changeStream: () => changeStreamSubject.asObservable(),
51
- cleanup: async () => true,
52
- close: async () => {},
53
- remove: async () => {}
54
- };
55
- }
56
-
57
- describe('categorizeBulkWriteRows', () => {
58
- test('INSERT: no previous, no doc in DB → bulkInsertDocs', () => {
59
- const docsInDb = new Map<string, RxDocumentData<TestDoc>>();
60
- const writeRows: BulkWriteRow<TestDoc>[] = [{
61
- document: {
62
- id: 'doc1',
63
- name: 'Test',
64
- age: 25,
65
- _deleted: false,
66
- _attachments: {},
67
- _rev: '1-a',
68
- _meta: { lwt: 1000 }
69
- }
70
- }];
71
-
72
- const result = categorizeBulkWriteRows(
73
- createMockStorageInstance<TestDoc>(),
74
- 'id',
75
- docsInDb,
76
- writeRows,
77
- 'test-context'
78
- );
79
-
80
- expect(result.bulkInsertDocs.length).toBe(1);
81
- expect(result.bulkUpdateDocs.length).toBe(0);
82
- expect(result.errors.length).toBe(0);
83
- expect(result.eventBulk.events.length).toBe(1);
84
- expect(result.eventBulk.events[0].operation).toBe('INSERT');
85
- });
86
-
87
- test('INSERT conflict: no previous, doc exists in DB → error 409', () => {
88
- const existingDoc: RxDocumentData<TestDoc> = {
89
- id: 'doc1',
90
- name: 'Existing',
91
- age: 30,
92
- _deleted: false,
93
- _attachments: {},
94
- _rev: '1-a',
95
- _meta: { lwt: 500 }
96
- };
97
- const docsInDb = new Map([['doc1', existingDoc]]);
98
- const writeRows: BulkWriteRow<TestDoc>[] = [{
99
- document: {
100
- id: 'doc1',
101
- name: 'New',
102
- age: 25,
103
- _deleted: false,
104
- _attachments: {},
105
- _rev: '1-b',
106
- _meta: { lwt: 1000 }
107
- }
108
- }];
109
-
110
- const result = categorizeBulkWriteRows(
111
- createMockStorageInstance<TestDoc>(),
112
- 'id',
113
- docsInDb,
114
- writeRows,
115
- 'test-context'
116
- );
117
-
118
- expect(result.bulkInsertDocs.length).toBe(0);
119
- expect(result.bulkUpdateDocs.length).toBe(0);
120
- expect(result.errors.length).toBe(1);
121
- expect(result.errors[0].status).toBe(409);
122
- expect('documentInDb' in result.errors[0]).toBe(true);
123
- if ('documentInDb' in result.errors[0]) {
124
- expect(result.errors[0].documentInDb).toEqual(existingDoc);
125
- }
126
- expect(result.eventBulk.events.length).toBe(0);
127
- });
128
-
129
- test('UPDATE: previous matches DB revision → bulkUpdateDocs', () => {
130
- const existingDoc: RxDocumentData<TestDoc> = {
131
- id: 'doc1',
132
- name: 'Old',
133
- age: 30,
134
- _deleted: false,
135
- _attachments: {},
136
- _rev: '1-a',
137
- _meta: { lwt: 500 }
138
- };
139
- const docsInDb = new Map([['doc1', existingDoc]]);
140
- const writeRows: BulkWriteRow<TestDoc>[] = [{
141
- previous: existingDoc,
142
- document: {
143
- id: 'doc1',
144
- name: 'Updated',
145
- age: 31,
146
- _deleted: false,
147
- _attachments: {},
148
- _rev: '2-b',
149
- _meta: { lwt: 1000 }
150
- }
151
- }];
152
-
153
- const result = categorizeBulkWriteRows(
154
- createMockStorageInstance<TestDoc>(),
155
- 'id',
156
- docsInDb,
157
- writeRows,
158
- 'test-context'
159
- );
160
-
161
- expect(result.bulkInsertDocs.length).toBe(0);
162
- expect(result.bulkUpdateDocs.length).toBe(1);
163
- expect(result.errors.length).toBe(0);
164
- expect(result.eventBulk.events.length).toBe(1);
165
- expect(result.eventBulk.events[0].operation).toBe('UPDATE');
166
- });
167
-
168
- test('UPDATE conflict: previous revision mismatch → error 409', () => {
169
- const existingDoc: RxDocumentData<TestDoc> = {
170
- id: 'doc1',
171
- name: 'Current',
172
- age: 30,
173
- _deleted: false,
174
- _attachments: {},
175
- _rev: '2-b',
176
- _meta: { lwt: 800 }
177
- };
178
- const docsInDb = new Map([['doc1', existingDoc]]);
179
- const writeRows: BulkWriteRow<TestDoc>[] = [{
180
- previous: {
181
- id: 'doc1',
182
- name: 'Old',
183
- age: 30,
184
- _deleted: false,
185
- _attachments: {},
186
- _rev: '1-a',
187
- _meta: { lwt: 500 }
188
- },
189
- document: {
190
- id: 'doc1',
191
- name: 'Updated',
192
- age: 31,
193
- _deleted: false,
194
- _attachments: {},
195
- _rev: '3-c',
196
- _meta: { lwt: 1000 }
197
- }
198
- }];
199
-
200
- const result = categorizeBulkWriteRows(
201
- createMockStorageInstance<TestDoc>(),
202
- 'id',
203
- docsInDb,
204
- writeRows,
205
- 'test-context'
206
- );
207
-
208
- expect(result.bulkInsertDocs.length).toBe(0);
209
- expect(result.bulkUpdateDocs.length).toBe(0);
210
- expect(result.errors.length).toBe(1);
211
- expect(result.errors[0].status).toBe(409);
212
- expect('documentInDb' in result.errors[0]).toBe(true);
213
- if ('documentInDb' in result.errors[0]) {
214
- expect(result.errors[0].documentInDb).toEqual(existingDoc);
215
- }
216
- expect(result.eventBulk.events.length).toBe(0);
217
- });
218
-
219
- test('DELETE operation: previous not deleted, document deleted → DELETE event', () => {
220
- const existingDoc: RxDocumentData<TestDoc> = {
221
- id: 'doc1',
222
- name: 'Active',
223
- age: 30,
224
- _deleted: false,
225
- _attachments: {},
226
- _rev: '1-a',
227
- _meta: { lwt: 500 }
228
- };
229
- const docsInDb = new Map([['doc1', existingDoc]]);
230
- const writeRows: BulkWriteRow<TestDoc>[] = [{
231
- previous: existingDoc,
232
- document: {
233
- id: 'doc1',
234
- name: 'Active',
235
- age: 30,
236
- _deleted: true,
237
- _attachments: {},
238
- _rev: '2-b',
239
- _meta: { lwt: 1000 }
240
- }
241
- }];
242
-
243
- const result = categorizeBulkWriteRows(
244
- createMockStorageInstance<TestDoc>(),
245
- 'id',
246
- docsInDb,
247
- writeRows,
248
- 'test-context'
249
- );
250
-
251
- expect(result.bulkUpdateDocs.length).toBe(1);
252
- expect(result.eventBulk.events[0].operation).toBe('DELETE');
253
- });
254
-
255
- test('checkpoint: uses newestRow document for checkpoint', () => {
256
- const docsInDb = new Map<string, RxDocumentData<TestDoc>>();
257
- const writeRows: BulkWriteRow<TestDoc>[] = [
258
- {
259
- document: {
260
- id: 'doc1',
261
- name: 'First',
262
- age: 25,
263
- _deleted: false,
264
- _attachments: {},
265
- _rev: '1-a',
266
- _meta: { lwt: 1000 }
267
- }
268
- },
269
- {
270
- document: {
271
- id: 'doc2',
272
- name: 'Last',
273
- age: 30,
274
- _deleted: false,
275
- _attachments: {},
276
- _rev: '1-b',
277
- _meta: { lwt: 2000 }
278
- }
279
- }
280
- ];
281
-
282
- const result = categorizeBulkWriteRows(
283
- createMockStorageInstance<TestDoc>(),
284
- 'id',
285
- docsInDb,
286
- writeRows,
287
- 'test-context'
288
- );
289
-
290
- expect(result.newestRow).toBeDefined();
291
- expect(result.newestRow?.document.id).toBe('doc2');
292
- expect(result.newestRow?.document._meta.lwt).toBe(2000);
293
-
294
- if (result.newestRow) {
295
- result.eventBulk.checkpoint = {
296
- id: result.newestRow.document.id,
297
- lwt: result.newestRow.document._meta.lwt
298
- };
299
- }
300
-
301
- expect(result.eventBulk.checkpoint).toEqual({ id: 'doc2', lwt: 2000 });
302
- });
303
- });
304
-
305
- describe('ensureRxStorageInstanceParamsAreCorrect', () => {
306
- test('throws UT6 when schema uses encryption but no password', () => {
307
- const params: RxStorageInstanceCreationParams<TestDoc, TestOptions> = {
308
- schema: createTestSchema({ encrypted: ['field1'] }),
309
- password: undefined,
310
- databaseInstanceToken: 'test-token',
311
- databaseName: 'testdb',
312
- collectionName: 'testcol',
313
- options: {},
314
- multiInstance: false,
315
- devMode: false
316
- };
317
-
318
- expect(() => ensureRxStorageInstanceParamsAreCorrect(params)).toThrow('UT6');
319
- });
320
-
321
- test('does not throw when schema has no encryption', () => {
322
- const params: RxStorageInstanceCreationParams<TestDoc, TestOptions> = {
323
- schema: createTestSchema(),
324
- databaseInstanceToken: 'test-token',
325
- databaseName: 'testdb',
326
- collectionName: 'testcol',
327
- options: {},
328
- multiInstance: false,
329
- devMode: false
330
- };
331
-
332
- expect(() => ensureRxStorageInstanceParamsAreCorrect(params)).not.toThrow();
333
- });
334
-
335
- test('throws UT5 when schema uses keyCompression', () => {
336
- const params: RxStorageInstanceCreationParams<TestDoc, TestOptions> = {
337
- schema: createTestSchema({ keyCompression: true }),
338
- databaseInstanceToken: 'test-token',
339
- databaseName: 'testdb',
340
- collectionName: 'testcol',
341
- options: {},
342
- multiInstance: false,
343
- devMode: false
344
- };
345
-
346
- expect(() => ensureRxStorageInstanceParamsAreCorrect(params)).toThrow('UT5');
347
- });
348
- });