@indexeddb-orm/idb-orm 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 (216) hide show
  1. package/.vscode/extensions.json +5 -0
  2. package/README.md +1280 -0
  3. package/angular-demo-app/README.md +84 -0
  4. package/angular-demo-app/angular.json +109 -0
  5. package/angular-demo-app/package-lock.json +14215 -0
  6. package/angular-demo-app/package.json +41 -0
  7. package/angular-demo-app/src/app/app.component.ts +481 -0
  8. package/angular-demo-app/src/app/app.routes.ts +8 -0
  9. package/angular-demo-app/src/app/components/actions.component.ts +202 -0
  10. package/angular-demo-app/src/app/components/cloud-sync-demo.component.ts +296 -0
  11. package/angular-demo-app/src/app/components/live-query-demo.component.ts +307 -0
  12. package/angular-demo-app/src/app/components/main-info.component.ts +148 -0
  13. package/angular-demo-app/src/app/components/posts-live-query-demo.component.ts +336 -0
  14. package/angular-demo-app/src/app/components/typescript-demo.component.ts +268 -0
  15. package/angular-demo-app/src/entities/post-tag.entity.ts +25 -0
  16. package/angular-demo-app/src/entities/post.entity.ts +49 -0
  17. package/angular-demo-app/src/entities/profile.entity.ts +42 -0
  18. package/angular-demo-app/src/entities/tag.entity.ts +36 -0
  19. package/angular-demo-app/src/entities/user.entity.ts +59 -0
  20. package/angular-demo-app/src/favicon.ico +1 -0
  21. package/angular-demo-app/src/index.html +16 -0
  22. package/angular-demo-app/src/main.ts +13 -0
  23. package/angular-demo-app/src/services/app-logic.service.ts +449 -0
  24. package/angular-demo-app/src/services/cloud-sync.service.ts +95 -0
  25. package/angular-demo-app/src/services/database.service.ts +26 -0
  26. package/angular-demo-app/src/services/live-query.service.ts +63 -0
  27. package/angular-demo-app/src/services/posts-live-query.service.ts +86 -0
  28. package/angular-demo-app/src/services/typescript-demo.service.ts +59 -0
  29. package/angular-demo-app/src/styles.scss +50 -0
  30. package/angular-demo-app/tsconfig.app.json +13 -0
  31. package/angular-demo-app/tsconfig.json +34 -0
  32. package/angular-demo-app/tsconfig.spec.json +13 -0
  33. package/dist/Database.d.ts +206 -0
  34. package/dist/Database.js +288 -0
  35. package/dist/decorators/Column.d.ts +79 -0
  36. package/dist/decorators/Column.js +236 -0
  37. package/dist/decorators/Entity.d.ts +32 -0
  38. package/dist/decorators/Entity.js +44 -0
  39. package/dist/decorators/Relation.d.ts +70 -0
  40. package/dist/decorators/Relation.js +120 -0
  41. package/dist/decorators/index.d.ts +3 -0
  42. package/dist/decorators/index.js +3 -0
  43. package/dist/errors/ValidationError.d.ts +4 -0
  44. package/dist/errors/ValidationError.js +8 -0
  45. package/dist/index.d.ts +8 -0
  46. package/dist/index.js +7 -0
  47. package/dist/metadata/Column.d.ts +8 -0
  48. package/dist/metadata/Column.js +44 -0
  49. package/dist/metadata/Entity.d.ts +11 -0
  50. package/dist/metadata/Entity.js +21 -0
  51. package/dist/metadata/Relation.d.ts +20 -0
  52. package/dist/metadata/Relation.js +74 -0
  53. package/dist/metadata/index.d.ts +3 -0
  54. package/dist/metadata/index.js +3 -0
  55. package/dist/services/AggregationService.d.ts +38 -0
  56. package/dist/services/AggregationService.js +229 -0
  57. package/dist/services/BaseEntity.d.ts +32 -0
  58. package/dist/services/BaseEntity.js +62 -0
  59. package/dist/services/CloudSyncService.d.ts +100 -0
  60. package/dist/services/CloudSyncService.js +196 -0
  61. package/dist/services/DecoratorUtils.d.ts +12 -0
  62. package/dist/services/DecoratorUtils.js +10 -0
  63. package/dist/services/EntityFactory.d.ts +25 -0
  64. package/dist/services/EntityFactory.js +27 -0
  65. package/dist/services/EntityRegistry.d.ts +61 -0
  66. package/dist/services/EntityRegistry.js +56 -0
  67. package/dist/services/EntitySchema.d.ts +56 -0
  68. package/dist/services/EntitySchema.js +125 -0
  69. package/dist/services/MigrationManager.d.ts +70 -0
  70. package/dist/services/MigrationManager.js +181 -0
  71. package/dist/services/RelationLoader.d.ts +66 -0
  72. package/dist/services/RelationLoader.js +310 -0
  73. package/dist/services/SchemaBuilder.d.ts +68 -0
  74. package/dist/services/SchemaBuilder.js +191 -0
  75. package/dist/services/index.d.ts +7 -0
  76. package/dist/services/index.js +7 -0
  77. package/dist/types.d.ts +152 -0
  78. package/dist/types.js +1 -0
  79. package/dist/utils/logger.d.ts +12 -0
  80. package/dist/utils/logger.js +16 -0
  81. package/eslint.config.js +49 -0
  82. package/homepage/favicon.svg +36 -0
  83. package/homepage/index.html +1725 -0
  84. package/package.json +78 -0
  85. package/react-demo-app/README.md +61 -0
  86. package/react-demo-app/eslint.config.js +60 -0
  87. package/react-demo-app/index.html +13 -0
  88. package/react-demo-app/package-lock.json +4955 -0
  89. package/react-demo-app/package.json +39 -0
  90. package/react-demo-app/src/App.tsx +172 -0
  91. package/react-demo-app/src/assets/react.svg +1 -0
  92. package/react-demo-app/src/components/Actions.tsx +171 -0
  93. package/react-demo-app/src/components/CloudSyncDemo.tsx +191 -0
  94. package/react-demo-app/src/components/LiveQueryDemo.tsx +122 -0
  95. package/react-demo-app/src/components/MainInfo.tsx +75 -0
  96. package/react-demo-app/src/components/PostsLiveQueryDemo.tsx +185 -0
  97. package/react-demo-app/src/components/TypeScriptDemo.tsx +190 -0
  98. package/react-demo-app/src/database/Database.ts +30 -0
  99. package/react-demo-app/src/entities/Post.ts +48 -0
  100. package/react-demo-app/src/entities/PostTag.ts +26 -0
  101. package/react-demo-app/src/entities/Profile.ts +41 -0
  102. package/react-demo-app/src/entities/Tag.ts +35 -0
  103. package/react-demo-app/src/entities/User.ts +61 -0
  104. package/react-demo-app/src/hooks/useAppLogic.ts +565 -0
  105. package/react-demo-app/src/hooks/useCloudSyncDemo.ts +84 -0
  106. package/react-demo-app/src/hooks/useLiveQueryDemo.ts +68 -0
  107. package/react-demo-app/src/hooks/usePostsLiveQueryDemo.ts +64 -0
  108. package/react-demo-app/src/hooks/useTypeScriptDemo.ts +43 -0
  109. package/react-demo-app/src/index.css +26 -0
  110. package/react-demo-app/src/main.tsx +18 -0
  111. package/react-demo-app/src/migrations/001-add-user-email-index.ts +17 -0
  112. package/react-demo-app/src/migrations/002-add-post-category.ts +37 -0
  113. package/react-demo-app/src/migrations/index.ts +8 -0
  114. package/react-demo-app/src/vite-env.d.ts +1 -0
  115. package/react-demo-app/tsconfig.app.json +22 -0
  116. package/react-demo-app/tsconfig.json +6 -0
  117. package/react-demo-app/vite.config.ts +10 -0
  118. package/src/Database.ts +405 -0
  119. package/src/errors/ValidationError.ts +9 -0
  120. package/src/index.ts +13 -0
  121. package/src/metadata/Column.ts +74 -0
  122. package/src/metadata/Entity.ts +42 -0
  123. package/src/metadata/Relation.ts +121 -0
  124. package/src/metadata/index.ts +5 -0
  125. package/src/services/AggregationService.ts +348 -0
  126. package/src/services/BaseEntity.ts +77 -0
  127. package/src/services/CloudSyncService.ts +248 -0
  128. package/src/services/EntityFactory.ts +35 -0
  129. package/src/services/EntityRegistry.ts +109 -0
  130. package/src/services/EntitySchema.ts +154 -0
  131. package/src/services/MigrationManager.ts +276 -0
  132. package/src/services/RelationLoader.ts +532 -0
  133. package/src/services/SchemaBuilder.ts +237 -0
  134. package/src/services/index.ts +7 -0
  135. package/src/types.d.ts +1 -0
  136. package/src/types.ts +169 -0
  137. package/src/utils/logger.ts +40 -0
  138. package/svelte-demo-app/README.md +61 -0
  139. package/svelte-demo-app/package-lock.json +3000 -0
  140. package/svelte-demo-app/package.json +30 -0
  141. package/svelte-demo-app/src/app.d.ts +12 -0
  142. package/svelte-demo-app/src/app.html +13 -0
  143. package/svelte-demo-app/src/components/Actions.svelte +121 -0
  144. package/svelte-demo-app/src/components/CloudSyncDemo.svelte +333 -0
  145. package/svelte-demo-app/src/components/LiveQueryDemo.svelte +191 -0
  146. package/svelte-demo-app/src/components/MainInfo.svelte +133 -0
  147. package/svelte-demo-app/src/components/PostsLiveQueryDemo.svelte +330 -0
  148. package/svelte-demo-app/src/components/TypeScriptDemo.svelte +251 -0
  149. package/svelte-demo-app/src/database/Database.ts +29 -0
  150. package/svelte-demo-app/src/entities/Post.ts +46 -0
  151. package/svelte-demo-app/src/entities/PostTag.ts +22 -0
  152. package/svelte-demo-app/src/entities/Profile.ts +39 -0
  153. package/svelte-demo-app/src/entities/Tag.ts +33 -0
  154. package/svelte-demo-app/src/entities/User.ts +62 -0
  155. package/svelte-demo-app/src/lib/database/Database.ts +30 -0
  156. package/svelte-demo-app/src/lib/entities/Post.ts +47 -0
  157. package/svelte-demo-app/src/lib/entities/PostTag.ts +23 -0
  158. package/svelte-demo-app/src/lib/entities/Profile.ts +40 -0
  159. package/svelte-demo-app/src/lib/entities/Tag.ts +34 -0
  160. package/svelte-demo-app/src/lib/entities/User.ts +59 -0
  161. package/svelte-demo-app/src/lib/index.ts +7 -0
  162. package/svelte-demo-app/src/lib/migrations/001-add-user-email-index.ts +17 -0
  163. package/svelte-demo-app/src/lib/migrations/002-add-post-category.ts +37 -0
  164. package/svelte-demo-app/src/lib/migrations/index.ts +8 -0
  165. package/svelte-demo-app/src/migrations/001-add-user-email-index.ts +17 -0
  166. package/svelte-demo-app/src/migrations/002-add-post-category.ts +37 -0
  167. package/svelte-demo-app/src/migrations/index.ts +8 -0
  168. package/svelte-demo-app/src/routes/+layout.js +3 -0
  169. package/svelte-demo-app/src/routes/+layout.svelte +228 -0
  170. package/svelte-demo-app/src/routes/+page.js +3 -0
  171. package/svelte-demo-app/src/routes/+page.svelte +1305 -0
  172. package/svelte-demo-app/src/stores/appStore.js +603 -0
  173. package/svelte-demo-app/svelte.config.js +18 -0
  174. package/svelte-demo-app/tsconfig.json +14 -0
  175. package/svelte-demo-app/vite.config.ts +6 -0
  176. package/tests/aggregation.e2e.test.ts +87 -0
  177. package/tests/base-entity.e2e.test.ts +47 -0
  178. package/tests/database-api.e2e.test.ts +177 -0
  179. package/tests/decorators.e2e.test.ts +40 -0
  180. package/tests/entity-schema.e2e.test.ts +58 -0
  181. package/tests/relation-loader-table-names.test.ts +192 -0
  182. package/tests/relations.e2e.test.ts +178 -0
  183. package/tests/zod-runtime.e2e.test.ts +69 -0
  184. package/tsconfig.json +21 -0
  185. package/vitest.config.ts +21 -0
  186. package/vitest.setup.ts +27 -0
  187. package/vue-demo-app/README.md +61 -0
  188. package/vue-demo-app/index.html +13 -0
  189. package/vue-demo-app/package-lock.json +1537 -0
  190. package/vue-demo-app/package.json +27 -0
  191. package/vue-demo-app/src/App.vue +100 -0
  192. package/vue-demo-app/src/components/Actions.vue +135 -0
  193. package/vue-demo-app/src/components/CloudSyncDemo.vue +139 -0
  194. package/vue-demo-app/src/components/LiveQueryDemo.vue +122 -0
  195. package/vue-demo-app/src/components/MainInfo.vue +80 -0
  196. package/vue-demo-app/src/components/PostsLiveQueryDemo.vue +136 -0
  197. package/vue-demo-app/src/components/TypeScriptDemo.vue +133 -0
  198. package/vue-demo-app/src/database/Database.ts +29 -0
  199. package/vue-demo-app/src/entities/Post.ts +48 -0
  200. package/vue-demo-app/src/entities/PostTag.ts +24 -0
  201. package/vue-demo-app/src/entities/Profile.ts +41 -0
  202. package/vue-demo-app/src/entities/Tag.ts +35 -0
  203. package/vue-demo-app/src/entities/User.ts +61 -0
  204. package/vue-demo-app/src/main.ts +29 -0
  205. package/vue-demo-app/src/migrations/001-add-user-email-index.ts +23 -0
  206. package/vue-demo-app/src/migrations/002-add-post-category.ts +46 -0
  207. package/vue-demo-app/src/migrations/index.ts +14 -0
  208. package/vue-demo-app/src/services/useAppLogic.ts +565 -0
  209. package/vue-demo-app/src/services/useCloudSyncDemo.ts +84 -0
  210. package/vue-demo-app/src/services/useLiveQueryDemo.ts +82 -0
  211. package/vue-demo-app/src/services/usePostsLiveQueryDemo.ts +77 -0
  212. package/vue-demo-app/src/services/useTypeScriptDemo.ts +56 -0
  213. package/vue-demo-app/src/vite-env.d.ts +1 -0
  214. package/vue-demo-app/tsconfig.json +25 -0
  215. package/vue-demo-app/tsconfig.node.json +10 -0
  216. package/vue-demo-app/vite.config.ts +16 -0
@@ -0,0 +1,565 @@
1
+ import { newEntity } from 'idb-orm';
2
+ import {
3
+ onMounted,ref,
4
+ } from 'vue';
5
+
6
+ import { db } from '../database/Database';
7
+ import { PostEntity } from '../entities/Post';
8
+ import { ProfileEntity } from '../entities/Profile';
9
+ import { TagEntity } from '../entities/Tag';
10
+ import { UserEntity } from '../entities/User';
11
+
12
+ export function useAppLogic() {
13
+ const users = ref<UserEntity[]>([]);
14
+ const tabIndex = ref(0);
15
+ const mobileOpen = ref(false);
16
+
17
+ const tabs = [
18
+ { label: 'Admin', key: 'admin' },
19
+ { label: 'Cloud Sync', key: 'cloud' },
20
+ ];
21
+
22
+ onMounted(() => {
23
+ loadUsers();
24
+ });
25
+
26
+ const loadUsers = async () => {
27
+ try {
28
+ const userRepository = db.getRepository(UserEntity);
29
+ const userData = await userRepository.toArray();
30
+ users.value = userData;
31
+ console.log('Users loaded:', userData);
32
+ } catch (error) {
33
+ console.error('Error loading users:', error);
34
+ }
35
+ };
36
+
37
+ const addUser = async () => {
38
+ try {
39
+ const newUser = newEntity(UserEntity, {
40
+ name: 'Test User ' + (users.value.length + 1),
41
+ email: 'user' + (users.value.length + 1) + '@example.com',
42
+ age: 25 + users.value.length,
43
+ tags: ['test'],
44
+ metadata: { source: 'manual' },
45
+ });
46
+
47
+ const userRepository = db.getRepository(UserEntity);
48
+ await userRepository.add(newUser);
49
+ await loadUsers();
50
+ } catch (error) {
51
+ console.error('Error adding user:', error);
52
+ }
53
+ };
54
+
55
+ const testZodValidation = async () => {
56
+ try {
57
+ console.log('Testing Zod validation...');
58
+
59
+ // Test 1: Manual validation of an invalid object
60
+ console.log('Test 1: Manual validation of invalid object');
61
+ const invalidUser = newEntity(UserEntity, {
62
+ name: 'A', // Too short (min 2 chars)
63
+ email: 'invalid-email', // Invalid email
64
+ age: 15, // Too young (min 18)
65
+ tags: ['test'],
66
+ metadata: { source: 'validation-test' },
67
+ createdAt: Date.now(),
68
+ updatedAt: Date.now(),
69
+ });
70
+
71
+ console.log('Object to validate:', invalidUser);
72
+
73
+ // Manual call to .validate() on the object
74
+ const validationResult = invalidUser.validate();
75
+ console.log('Validation result:', validationResult);
76
+
77
+ if (!validationResult.isValid) {
78
+ console.log('Validation errors:');
79
+ validationResult.errors.forEach((error, index) => {
80
+ console.log(` ${index + 1}. ${error}`);
81
+ });
82
+ }
83
+
84
+ // Test 2: Manual validation of a valid object
85
+ console.log('\nTest 2: Manual validation of a valid object');
86
+ const validUser = newEntity(UserEntity, {
87
+ name: 'John Doe', // Valid
88
+ email: 'john@example.com', // Valid
89
+ age: 25, // Valid
90
+ tags: ['developer', 'typescript'],
91
+ metadata: { source: 'validation-test', role: 'admin' },
92
+ createdAt: Date.now(),
93
+ updatedAt: Date.now(),
94
+ });
95
+
96
+ console.log('Object to validate:', validUser);
97
+
98
+ const validResult = validUser.validate();
99
+ console.log('Validation result:', validResult);
100
+
101
+ if (validResult.isValid) {
102
+ console.log('Validation passed successfully!');
103
+
104
+ // Add the valid user to the DB
105
+ const userRepository = db.getRepository(UserEntity);
106
+ await userRepository.add(validUser);
107
+ console.log('Valid user added to the database');
108
+ await loadUsers();
109
+ }
110
+
111
+ // Test 3: validateOrThrow() - throwing errors
112
+ console.log('\nTest 3: validateOrThrow() - throwing errors');
113
+ try {
114
+ invalidUser.validateOrThrow();
115
+ console.log(
116
+ 'validateOrThrow() did not throw - this should not happen',
117
+ );
118
+ } catch (error) {
119
+ console.log(
120
+ 'validateOrThrow() threw as expected:',
121
+ (error as Error).message,
122
+ );
123
+ }
124
+
125
+ alert(
126
+ `Validation test finished!\n\nInvalid object:
127
+ ${validationResult.errors.length} errors\n` +
128
+ `Valid object: ${validResult.isValid ? 'OK' : 'ERRORS'}\n\nCheck console for details.`,
129
+ );
130
+ } catch (error) {
131
+ console.log('Zod validation caught the error:', error);
132
+ alert(`Zod validation working! Error: ${(error as Error).message}`);
133
+ }
134
+ };
135
+
136
+ const testSchemaMigration = async () => {
137
+ try {
138
+ console.log('Testing schema migration...');
139
+
140
+ // Check if schema has changed
141
+ const hasChanged = await db.checkSchemaChanges();
142
+
143
+ if (hasChanged) {
144
+ alert('Schema changed! Database was reset.');
145
+ await loadUsers(); // Reload data
146
+ } else {
147
+ alert('No schema changes detected.');
148
+ }
149
+ } catch (error) {
150
+ console.error('Schema migration test error:', error);
151
+ alert(`Schema migration test error: ${(error as Error).message}`);
152
+ }
153
+ };
154
+
155
+ const toggleAutoReset = async () => {
156
+ try {
157
+ // Show current status
158
+ const currentStatus =
159
+ localStorage.getItem('DexieORMDemo_auto_reset') !== 'false';
160
+
161
+ const newStatus = !currentStatus;
162
+ localStorage.setItem('DexieORMDemo_auto_reset', newStatus.toString());
163
+
164
+ alert(
165
+ `Auto-reset ${newStatus ? 'enabled' : 'disabled'}! ${
166
+ newStatus
167
+ ? 'Database will reset on schema changes.'
168
+ : 'Database will not reset on schema changes.'
169
+ }`,
170
+ );
171
+ } catch (error) {
172
+ console.error('Error toggling auto-reset:', error);
173
+ alert(`Error toggling auto-reset: ${(error as Error).message}`);
174
+ }
175
+ };
176
+
177
+ const testTransactions = async () => {
178
+ try {
179
+ console.log('Testing Dexie transactions with our ORM...');
180
+
181
+ // Test 1: Simple transaction using Dexie's transaction method
182
+ const result = await db.transaction(
183
+ 'rw',
184
+ [db.getRepository(UserEntity), db.getRepository(ProfileEntity)],
185
+ async (trans) => {
186
+ // Create user
187
+ const user = newEntity(UserEntity, {
188
+ name: 'Transaction Test User',
189
+ email: `transaction-${Date.now()}@example.com`,
190
+ age: 30,
191
+ tags: ['transaction-test'],
192
+ metadata: { source: 'transaction-test' },
193
+ createdAt: Date.now(),
194
+ updatedAt: Date.now(),
195
+ });
196
+
197
+ const userId = await trans.table('users').add(user);
198
+ console.log('User created in transaction:', userId);
199
+
200
+ // Create profile for the user
201
+ const profile = newEntity(ProfileEntity, {
202
+ userId: userId,
203
+ bio: 'Transaction test profile',
204
+ avatar: 'https://example.com/avatar.jpg',
205
+ website: 'https://example.com',
206
+ location: 'Test City',
207
+ createdAt: Date.now(),
208
+ updatedAt: Date.now(),
209
+ });
210
+
211
+ const profileId = await trans.table('profiles').add(profile);
212
+ console.log('Profile created in transaction:', profileId);
213
+
214
+ return { userId, profileId };
215
+ },
216
+ );
217
+
218
+ console.log('Transaction completed successfully:', result);
219
+
220
+ // Test 2: Read-only transaction
221
+ const userCount = await db.transaction(
222
+ 'r',
223
+ [db.getRepository(UserEntity)],
224
+ async (trans) => {
225
+ const users = await trans.table('users').toArray();
226
+ console.log('Users read in read-only transaction:', users.length);
227
+
228
+ return users.length;
229
+ },
230
+ );
231
+
232
+ console.log('Read transaction completed, user count:', userCount);
233
+
234
+ // Test 3: Transaction with error (rollback)
235
+ try {
236
+ await db.transaction('rw', [db.getRepository(UserEntity)], async (trans) => {
237
+ const user = newEntity(UserEntity, {
238
+ name: 'Rollback Test User',
239
+ email: `rollback-${Date.now()}@example.com`,
240
+ age: 25,
241
+ tags: ['rollback-test'],
242
+ metadata: { source: 'rollback-test' },
243
+ createdAt: Date.now(),
244
+ updatedAt: Date.now(),
245
+ });
246
+
247
+ await trans.table('users').add(user);
248
+ console.log('User added before error');
249
+
250
+ // Simulate error
251
+ throw new Error('Simulated transaction error');
252
+ });
253
+ } catch (error) {
254
+ console.log(
255
+ 'Transaction rolled back as expected:',
256
+ (error as Error).message,
257
+ );
258
+ }
259
+
260
+ alert('Transactions test completed! Check console for details.');
261
+ await loadUsers();
262
+ } catch (error) {
263
+ console.error('Error testing transactions:', error);
264
+ alert(`Transactions test error: ${(error as Error).message}`);
265
+ }
266
+ };
267
+
268
+ const testCompoundIndexes = async () => {
269
+ try {
270
+ console.log('Testing compound indexes...');
271
+
272
+ // Test 1: Query using compound index (name + email)
273
+ console.log('Test 1: Query using compound index (name + email)');
274
+ const usersByNameEmail = await db
275
+ .getRepository(UserEntity)
276
+ .where(['name', 'email'])
277
+ .equals(['John Doe', 'john@example.com'])
278
+ .toArray();
279
+ console.log('Users by name+email:', usersByNameEmail);
280
+
281
+ // Test 2: Query using compound index (age + isActive)
282
+ console.log('Test 2: Query using compound index (age + isActive)');
283
+ const activeAdults = await db
284
+ .getRepository(UserEntity)
285
+ .where(['age', 'isActive'])
286
+ .above([18, 1])
287
+ .toArray();
288
+ console.log('Active adults (age > 18, isActive = true):', activeAdults);
289
+
290
+ // Test 3: Query using unique compound index (email)
291
+ console.log('Test 3: Query using unique compound index (email)');
292
+ const userByEmail = await db
293
+ .getRepository(UserEntity)
294
+ .where('email')
295
+ .equals('john@example.com')
296
+ .first();
297
+ console.log('User by email:', userByEmail);
298
+
299
+ // Test 4: Add some test data to demonstrate compound indexes
300
+ console.log('Test 4: Adding test data for compound indexes');
301
+ const testUser1 = newEntity(UserEntity, {
302
+ name: 'Alice Smith',
303
+ email: 'alice@example.com',
304
+ age: 25,
305
+ isActive: true,
306
+ tags: ['test'],
307
+ metadata: { source: 'compound-test' },
308
+ createdAt: Date.now(),
309
+ updatedAt: Date.now(),
310
+ });
311
+
312
+ const testUser2 = newEntity(UserEntity, {
313
+ name: 'Bob Johnson',
314
+ email: 'bob@example.com',
315
+ age: 30,
316
+ isActive: false,
317
+ tags: ['test'],
318
+ metadata: { source: 'compound-test' },
319
+ createdAt: Date.now(),
320
+ updatedAt: Date.now(),
321
+ });
322
+
323
+ await db.getRepository(UserEntity).bulkAdd([testUser1, testUser2]);
324
+ console.log('Test users added');
325
+
326
+ // Test 5: Query with compound index after adding data
327
+ console.log('Test 5: Query with compound index after adding data');
328
+ const allActiveUsers = await db
329
+ .getRepository(UserEntity)
330
+ .where('isActive')
331
+ .equals(1)
332
+ .toArray();
333
+ console.log('All active users:', allActiveUsers);
334
+
335
+ await loadUsers();
336
+ alert('Compound indexes test completed! Check console for details.');
337
+ } catch (error) {
338
+ console.error('Error testing compound indexes:', error);
339
+ alert(`Compound indexes test error: ${(error as Error).message}`);
340
+ }
341
+ };
342
+
343
+ const testRelations = async () => {
344
+ try {
345
+ console.log('Testing relations...');
346
+
347
+ // Clear existing test data first
348
+ console.log('Clearing existing test data...');
349
+ // Get all users and filter by metadata.source in JavaScript
350
+ const allUsers = await db.getRepository(UserEntity).toArray();
351
+ const testUsers = allUsers.filter(user => user.metadata?.source === 'relation-test');
352
+ if (testUsers.length > 0) {
353
+ await db.getRepository(UserEntity).bulkDelete(testUsers.map(u => u.id!));
354
+ }
355
+ await db.getRepository(ProfileEntity).where('bio').equals('Full-stack developer').delete();
356
+ await db.getRepository(PostEntity).where('title').startsWith('My First Post').delete();
357
+ await db.getRepository(PostEntity).where('title').startsWith('Learning Dexie ORM').delete();
358
+ await db.getRepository(TagEntity).where('name').equals('javascript').delete();
359
+ await db.getRepository(TagEntity).where('name').equals('typescript').delete();
360
+
361
+ // Create a user with profile and posts
362
+ const user = newEntity(UserEntity, {
363
+ name: 'John Doe',
364
+ email: `john+${Date.now()}@example.com`, // Unique email to avoid conflicts
365
+ age: 30,
366
+ tags: ['developer'],
367
+ metadata: { source: 'relation-test' },
368
+ createdAt: Date.now(),
369
+ updatedAt: Date.now(),
370
+ });
371
+
372
+ // Create profile
373
+ newEntity(ProfileEntity, {
374
+ bio: 'Full-stack developer',
375
+ avatar: 'https://example.com/avatar.jpg',
376
+ website: 'https://johndoe.com',
377
+ location: 'New York',
378
+ createdAt: Date.now(),
379
+ updatedAt: Date.now(),
380
+ });
381
+
382
+ // Create posts
383
+ newEntity(PostEntity, {
384
+ title: 'My First Post',
385
+ content: 'This is my first blog post about Dexie ORM!',
386
+ published: true,
387
+ likes: 42,
388
+ postTags: ['javascript', 'dexie'],
389
+ createdAt: Date.now(),
390
+ updatedAt: Date.now(),
391
+ });
392
+
393
+ newEntity(PostEntity, {
394
+ title: 'Learning Dexie ORM',
395
+ content: 'Learning how to use Dexie ORM with TypeScript and Zod validation.',
396
+ published: false,
397
+ likes: 15,
398
+ postTags: ['typescript', 'orm'],
399
+ createdAt: Date.now(),
400
+ updatedAt: Date.now(),
401
+ });
402
+
403
+ console.log('Saving user with relations...');
404
+ const savedUser = await db.saveWithRelations({
405
+ entity: user,
406
+ entityClass: UserEntity,
407
+ });
408
+
409
+ // Load user with all relations
410
+ await db.loadRelations({
411
+ entity: savedUser,
412
+ entityClass: UserEntity,
413
+ relationNames: ['profile', 'posts'],
414
+ });
415
+
416
+ // Load user with only specific relations
417
+ const userWithProfileOnly = await db.loadRelations({
418
+ entity: savedUser,
419
+ entityClass: UserEntity,
420
+ relationNames: ['profile'],
421
+ });
422
+ console.log('User with profile only:', userWithProfileOnly);
423
+
424
+ // Load single relation by name
425
+ const userProfile = await db.loadRelationByName({
426
+ entity: savedUser,
427
+ entityClass: UserEntity,
428
+ relationName: 'profile',
429
+ });
430
+ console.log('Profile loaded separately:', userProfile);
431
+
432
+ alert('Relations test completed! Check console for details.');
433
+ await loadUsers();
434
+ } catch (error) {
435
+ console.error('Error testing relations:', error);
436
+ alert(`Relations test error: ${(error as Error).message}`);
437
+ }
438
+ };
439
+
440
+ const testAggregations = async () => {
441
+ try {
442
+ console.log('Testing aggregations...');
443
+
444
+ // Test 1: Basic statistics
445
+ console.log('Test 1: Basic user statistics');
446
+ const userStats = await db.aggregate({
447
+ entityClass: UserEntity,
448
+ options: {
449
+ count: true,
450
+ avg: ['age'],
451
+ min: ['age'],
452
+ max: ['age'],
453
+ },
454
+ });
455
+ console.log('User stats:', userStats);
456
+
457
+ // Test 2: Group by age
458
+ console.log('Test 2: Users grouped by age');
459
+ const usersByAge = await db.aggregate({
460
+ entityClass: UserEntity,
461
+ options: {
462
+ groupBy: 'age',
463
+ count: true,
464
+ avg: ['age'],
465
+ },
466
+ });
467
+ console.log('Users by age:', usersByAge);
468
+
469
+ // Test 3: Active users only
470
+ console.log('Test 3: Active users statistics');
471
+ const activeUserStats = await db.aggregate({
472
+ entityClass: UserEntity,
473
+ options: {
474
+ where: { isActive: true },
475
+ count: true,
476
+ avg: ['age'],
477
+ },
478
+ });
479
+ console.log('Active user stats:', activeUserStats);
480
+
481
+ // Test 4: Post statistics
482
+ console.log('Test 4: Post statistics');
483
+ const postStats = await db.aggregate({
484
+ entityClass: PostEntity,
485
+ options: {
486
+ where: { published: true },
487
+ count: true,
488
+ sum: ['likes'],
489
+ avg: ['likes'],
490
+ },
491
+ });
492
+ console.log('Post stats:', postStats);
493
+
494
+ // Test 5: Posts grouped by author
495
+ console.log('Test 5: Posts grouped by author');
496
+ const postsByAuthor = await db.aggregate({
497
+ entityClass: PostEntity,
498
+ options: {
499
+ groupBy: 'authorId',
500
+ count: true,
501
+ sum: ['likes'],
502
+ },
503
+ });
504
+ console.log('Posts by author:', postsByAuthor);
505
+
506
+ // Test 6: Top 5 oldest users
507
+ console.log('Test 6: Top 5 oldest users');
508
+ const oldestUsers = await db.aggregate({
509
+ entityClass: UserEntity,
510
+ options: {
511
+ sort: { field: 'createdAt', direction: 'asc' },
512
+ limit: 5,
513
+ },
514
+ });
515
+ console.log('Oldest users:', oldestUsers);
516
+
517
+ alert(
518
+ 'Aggregations test completed! Check console for detailed results.',
519
+ );
520
+ await loadUsers();
521
+ } catch (error) {
522
+ console.error('Error testing aggregations:', error);
523
+ alert(`Aggregations test error: ${(error as Error).message}`);
524
+ }
525
+ };
526
+
527
+ const clearAll = async () => {
528
+ try {
529
+ await db.getRepository(UserEntity).clear();
530
+ await db.getRepository(PostEntity).clear();
531
+ await db.getRepository(ProfileEntity).clear();
532
+ await db.getRepository(TagEntity).clear();
533
+ await loadUsers();
534
+ alert('All data cleared!');
535
+ } catch (error) {
536
+ console.error('Error clearing data:', error);
537
+ alert(`Error clearing data: ${(error as Error).message}`);
538
+ }
539
+ };
540
+
541
+ const handleDrawerToggle = () => {
542
+ mobileOpen.value = !mobileOpen.value;
543
+ };
544
+
545
+ return {
546
+ // State
547
+ users,
548
+ tabIndex,
549
+ setTabIndex: (index: number) => { tabIndex.value = index; },
550
+ mobileOpen,
551
+ tabs,
552
+
553
+ // Actions
554
+ addUser,
555
+ testZodValidation,
556
+ testSchemaMigration,
557
+ toggleAutoReset,
558
+ testTransactions,
559
+ testCompoundIndexes,
560
+ testRelations,
561
+ testAggregations,
562
+ clearAll,
563
+ handleDrawerToggle,
564
+ };
565
+ }
@@ -0,0 +1,84 @@
1
+ import {
2
+ onMounted,ref,
3
+ } from 'vue';
4
+
5
+ import { db } from '../database/Database';
6
+
7
+ export function useCloudSyncDemo() {
8
+ const syncStatus = ref<{
9
+ enabled: boolean;
10
+ lastSync?: Date;
11
+ isOnline?: boolean;
12
+ }>({ enabled: false });
13
+ const isLoading = ref(false);
14
+
15
+ onMounted(() => {
16
+ // Get initial sync status
17
+ const status = db.getSyncStatus();
18
+ syncStatus.value = status;
19
+ });
20
+
21
+ const handleManualSync = async () => {
22
+ isLoading.value = true;
23
+ try {
24
+ await db.sync();
25
+ // Update status after sync
26
+ const newStatus = db.getSyncStatus();
27
+ syncStatus.value = newStatus;
28
+ alert('Sync completed successfully!');
29
+ } catch (error) {
30
+ console.error('Sync failed:', error);
31
+ alert(`Sync failed: ${(error as Error).message}`);
32
+ } finally {
33
+ isLoading.value = false;
34
+ }
35
+ };
36
+
37
+ const handleEnableCloudSync = async () => {
38
+ isLoading.value = true;
39
+ try {
40
+ await db.enableCloudSync({
41
+ databaseUrl: 'https://your-database-url.dexie.cloud',
42
+ enableOfflineSupport: true,
43
+ syncInterval: 30000,
44
+ });
45
+
46
+ const newStatus = db.getSyncStatus();
47
+ syncStatus.value = newStatus;
48
+ alert('Cloud sync enabled successfully!');
49
+ } catch (error) {
50
+ console.error('Failed to enable cloud sync:', error);
51
+ alert(`Failed to enable cloud sync: ${(error as Error).message}`);
52
+ } finally {
53
+ isLoading.value = false;
54
+ }
55
+ };
56
+
57
+ const handleDisableCloudSync = () => {
58
+ db.disableCloudSync();
59
+ syncStatus.value = { enabled: false };
60
+ alert('Cloud sync disabled');
61
+ };
62
+
63
+ const handleSyncTables = async () => {
64
+ isLoading.value = true;
65
+ try {
66
+ await db.syncTables(['users', 'posts']);
67
+ alert('Tables synced successfully!');
68
+ } catch (error) {
69
+ console.error('Table sync failed:', error);
70
+ alert(`Table sync failed: ${(error as Error).message}`);
71
+ } finally {
72
+ isLoading.value = false;
73
+ }
74
+ };
75
+
76
+ return {
77
+ syncStatus,
78
+ isLoading,
79
+ handleManualSync,
80
+ handleEnableCloudSync,
81
+ handleDisableCloudSync,
82
+ handleSyncTables,
83
+ };
84
+ }