@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.
- package/.vscode/extensions.json +5 -0
- package/README.md +1280 -0
- package/angular-demo-app/README.md +84 -0
- package/angular-demo-app/angular.json +109 -0
- package/angular-demo-app/package-lock.json +14215 -0
- package/angular-demo-app/package.json +41 -0
- package/angular-demo-app/src/app/app.component.ts +481 -0
- package/angular-demo-app/src/app/app.routes.ts +8 -0
- package/angular-demo-app/src/app/components/actions.component.ts +202 -0
- package/angular-demo-app/src/app/components/cloud-sync-demo.component.ts +296 -0
- package/angular-demo-app/src/app/components/live-query-demo.component.ts +307 -0
- package/angular-demo-app/src/app/components/main-info.component.ts +148 -0
- package/angular-demo-app/src/app/components/posts-live-query-demo.component.ts +336 -0
- package/angular-demo-app/src/app/components/typescript-demo.component.ts +268 -0
- package/angular-demo-app/src/entities/post-tag.entity.ts +25 -0
- package/angular-demo-app/src/entities/post.entity.ts +49 -0
- package/angular-demo-app/src/entities/profile.entity.ts +42 -0
- package/angular-demo-app/src/entities/tag.entity.ts +36 -0
- package/angular-demo-app/src/entities/user.entity.ts +59 -0
- package/angular-demo-app/src/favicon.ico +1 -0
- package/angular-demo-app/src/index.html +16 -0
- package/angular-demo-app/src/main.ts +13 -0
- package/angular-demo-app/src/services/app-logic.service.ts +449 -0
- package/angular-demo-app/src/services/cloud-sync.service.ts +95 -0
- package/angular-demo-app/src/services/database.service.ts +26 -0
- package/angular-demo-app/src/services/live-query.service.ts +63 -0
- package/angular-demo-app/src/services/posts-live-query.service.ts +86 -0
- package/angular-demo-app/src/services/typescript-demo.service.ts +59 -0
- package/angular-demo-app/src/styles.scss +50 -0
- package/angular-demo-app/tsconfig.app.json +13 -0
- package/angular-demo-app/tsconfig.json +34 -0
- package/angular-demo-app/tsconfig.spec.json +13 -0
- package/dist/Database.d.ts +206 -0
- package/dist/Database.js +288 -0
- package/dist/decorators/Column.d.ts +79 -0
- package/dist/decorators/Column.js +236 -0
- package/dist/decorators/Entity.d.ts +32 -0
- package/dist/decorators/Entity.js +44 -0
- package/dist/decorators/Relation.d.ts +70 -0
- package/dist/decorators/Relation.js +120 -0
- package/dist/decorators/index.d.ts +3 -0
- package/dist/decorators/index.js +3 -0
- package/dist/errors/ValidationError.d.ts +4 -0
- package/dist/errors/ValidationError.js +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +7 -0
- package/dist/metadata/Column.d.ts +8 -0
- package/dist/metadata/Column.js +44 -0
- package/dist/metadata/Entity.d.ts +11 -0
- package/dist/metadata/Entity.js +21 -0
- package/dist/metadata/Relation.d.ts +20 -0
- package/dist/metadata/Relation.js +74 -0
- package/dist/metadata/index.d.ts +3 -0
- package/dist/metadata/index.js +3 -0
- package/dist/services/AggregationService.d.ts +38 -0
- package/dist/services/AggregationService.js +229 -0
- package/dist/services/BaseEntity.d.ts +32 -0
- package/dist/services/BaseEntity.js +62 -0
- package/dist/services/CloudSyncService.d.ts +100 -0
- package/dist/services/CloudSyncService.js +196 -0
- package/dist/services/DecoratorUtils.d.ts +12 -0
- package/dist/services/DecoratorUtils.js +10 -0
- package/dist/services/EntityFactory.d.ts +25 -0
- package/dist/services/EntityFactory.js +27 -0
- package/dist/services/EntityRegistry.d.ts +61 -0
- package/dist/services/EntityRegistry.js +56 -0
- package/dist/services/EntitySchema.d.ts +56 -0
- package/dist/services/EntitySchema.js +125 -0
- package/dist/services/MigrationManager.d.ts +70 -0
- package/dist/services/MigrationManager.js +181 -0
- package/dist/services/RelationLoader.d.ts +66 -0
- package/dist/services/RelationLoader.js +310 -0
- package/dist/services/SchemaBuilder.d.ts +68 -0
- package/dist/services/SchemaBuilder.js +191 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.js +7 -0
- package/dist/types.d.ts +152 -0
- package/dist/types.js +1 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.js +16 -0
- package/eslint.config.js +49 -0
- package/homepage/favicon.svg +36 -0
- package/homepage/index.html +1725 -0
- package/package.json +78 -0
- package/react-demo-app/README.md +61 -0
- package/react-demo-app/eslint.config.js +60 -0
- package/react-demo-app/index.html +13 -0
- package/react-demo-app/package-lock.json +4955 -0
- package/react-demo-app/package.json +39 -0
- package/react-demo-app/src/App.tsx +172 -0
- package/react-demo-app/src/assets/react.svg +1 -0
- package/react-demo-app/src/components/Actions.tsx +171 -0
- package/react-demo-app/src/components/CloudSyncDemo.tsx +191 -0
- package/react-demo-app/src/components/LiveQueryDemo.tsx +122 -0
- package/react-demo-app/src/components/MainInfo.tsx +75 -0
- package/react-demo-app/src/components/PostsLiveQueryDemo.tsx +185 -0
- package/react-demo-app/src/components/TypeScriptDemo.tsx +190 -0
- package/react-demo-app/src/database/Database.ts +30 -0
- package/react-demo-app/src/entities/Post.ts +48 -0
- package/react-demo-app/src/entities/PostTag.ts +26 -0
- package/react-demo-app/src/entities/Profile.ts +41 -0
- package/react-demo-app/src/entities/Tag.ts +35 -0
- package/react-demo-app/src/entities/User.ts +61 -0
- package/react-demo-app/src/hooks/useAppLogic.ts +565 -0
- package/react-demo-app/src/hooks/useCloudSyncDemo.ts +84 -0
- package/react-demo-app/src/hooks/useLiveQueryDemo.ts +68 -0
- package/react-demo-app/src/hooks/usePostsLiveQueryDemo.ts +64 -0
- package/react-demo-app/src/hooks/useTypeScriptDemo.ts +43 -0
- package/react-demo-app/src/index.css +26 -0
- package/react-demo-app/src/main.tsx +18 -0
- package/react-demo-app/src/migrations/001-add-user-email-index.ts +17 -0
- package/react-demo-app/src/migrations/002-add-post-category.ts +37 -0
- package/react-demo-app/src/migrations/index.ts +8 -0
- package/react-demo-app/src/vite-env.d.ts +1 -0
- package/react-demo-app/tsconfig.app.json +22 -0
- package/react-demo-app/tsconfig.json +6 -0
- package/react-demo-app/vite.config.ts +10 -0
- package/src/Database.ts +405 -0
- package/src/errors/ValidationError.ts +9 -0
- package/src/index.ts +13 -0
- package/src/metadata/Column.ts +74 -0
- package/src/metadata/Entity.ts +42 -0
- package/src/metadata/Relation.ts +121 -0
- package/src/metadata/index.ts +5 -0
- package/src/services/AggregationService.ts +348 -0
- package/src/services/BaseEntity.ts +77 -0
- package/src/services/CloudSyncService.ts +248 -0
- package/src/services/EntityFactory.ts +35 -0
- package/src/services/EntityRegistry.ts +109 -0
- package/src/services/EntitySchema.ts +154 -0
- package/src/services/MigrationManager.ts +276 -0
- package/src/services/RelationLoader.ts +532 -0
- package/src/services/SchemaBuilder.ts +237 -0
- package/src/services/index.ts +7 -0
- package/src/types.d.ts +1 -0
- package/src/types.ts +169 -0
- package/src/utils/logger.ts +40 -0
- package/svelte-demo-app/README.md +61 -0
- package/svelte-demo-app/package-lock.json +3000 -0
- package/svelte-demo-app/package.json +30 -0
- package/svelte-demo-app/src/app.d.ts +12 -0
- package/svelte-demo-app/src/app.html +13 -0
- package/svelte-demo-app/src/components/Actions.svelte +121 -0
- package/svelte-demo-app/src/components/CloudSyncDemo.svelte +333 -0
- package/svelte-demo-app/src/components/LiveQueryDemo.svelte +191 -0
- package/svelte-demo-app/src/components/MainInfo.svelte +133 -0
- package/svelte-demo-app/src/components/PostsLiveQueryDemo.svelte +330 -0
- package/svelte-demo-app/src/components/TypeScriptDemo.svelte +251 -0
- package/svelte-demo-app/src/database/Database.ts +29 -0
- package/svelte-demo-app/src/entities/Post.ts +46 -0
- package/svelte-demo-app/src/entities/PostTag.ts +22 -0
- package/svelte-demo-app/src/entities/Profile.ts +39 -0
- package/svelte-demo-app/src/entities/Tag.ts +33 -0
- package/svelte-demo-app/src/entities/User.ts +62 -0
- package/svelte-demo-app/src/lib/database/Database.ts +30 -0
- package/svelte-demo-app/src/lib/entities/Post.ts +47 -0
- package/svelte-demo-app/src/lib/entities/PostTag.ts +23 -0
- package/svelte-demo-app/src/lib/entities/Profile.ts +40 -0
- package/svelte-demo-app/src/lib/entities/Tag.ts +34 -0
- package/svelte-demo-app/src/lib/entities/User.ts +59 -0
- package/svelte-demo-app/src/lib/index.ts +7 -0
- package/svelte-demo-app/src/lib/migrations/001-add-user-email-index.ts +17 -0
- package/svelte-demo-app/src/lib/migrations/002-add-post-category.ts +37 -0
- package/svelte-demo-app/src/lib/migrations/index.ts +8 -0
- package/svelte-demo-app/src/migrations/001-add-user-email-index.ts +17 -0
- package/svelte-demo-app/src/migrations/002-add-post-category.ts +37 -0
- package/svelte-demo-app/src/migrations/index.ts +8 -0
- package/svelte-demo-app/src/routes/+layout.js +3 -0
- package/svelte-demo-app/src/routes/+layout.svelte +228 -0
- package/svelte-demo-app/src/routes/+page.js +3 -0
- package/svelte-demo-app/src/routes/+page.svelte +1305 -0
- package/svelte-demo-app/src/stores/appStore.js +603 -0
- package/svelte-demo-app/svelte.config.js +18 -0
- package/svelte-demo-app/tsconfig.json +14 -0
- package/svelte-demo-app/vite.config.ts +6 -0
- package/tests/aggregation.e2e.test.ts +87 -0
- package/tests/base-entity.e2e.test.ts +47 -0
- package/tests/database-api.e2e.test.ts +177 -0
- package/tests/decorators.e2e.test.ts +40 -0
- package/tests/entity-schema.e2e.test.ts +58 -0
- package/tests/relation-loader-table-names.test.ts +192 -0
- package/tests/relations.e2e.test.ts +178 -0
- package/tests/zod-runtime.e2e.test.ts +69 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +21 -0
- package/vitest.setup.ts +27 -0
- package/vue-demo-app/README.md +61 -0
- package/vue-demo-app/index.html +13 -0
- package/vue-demo-app/package-lock.json +1537 -0
- package/vue-demo-app/package.json +27 -0
- package/vue-demo-app/src/App.vue +100 -0
- package/vue-demo-app/src/components/Actions.vue +135 -0
- package/vue-demo-app/src/components/CloudSyncDemo.vue +139 -0
- package/vue-demo-app/src/components/LiveQueryDemo.vue +122 -0
- package/vue-demo-app/src/components/MainInfo.vue +80 -0
- package/vue-demo-app/src/components/PostsLiveQueryDemo.vue +136 -0
- package/vue-demo-app/src/components/TypeScriptDemo.vue +133 -0
- package/vue-demo-app/src/database/Database.ts +29 -0
- package/vue-demo-app/src/entities/Post.ts +48 -0
- package/vue-demo-app/src/entities/PostTag.ts +24 -0
- package/vue-demo-app/src/entities/Profile.ts +41 -0
- package/vue-demo-app/src/entities/Tag.ts +35 -0
- package/vue-demo-app/src/entities/User.ts +61 -0
- package/vue-demo-app/src/main.ts +29 -0
- package/vue-demo-app/src/migrations/001-add-user-email-index.ts +23 -0
- package/vue-demo-app/src/migrations/002-add-post-category.ts +46 -0
- package/vue-demo-app/src/migrations/index.ts +14 -0
- package/vue-demo-app/src/services/useAppLogic.ts +565 -0
- package/vue-demo-app/src/services/useCloudSyncDemo.ts +84 -0
- package/vue-demo-app/src/services/useLiveQueryDemo.ts +82 -0
- package/vue-demo-app/src/services/usePostsLiveQueryDemo.ts +77 -0
- package/vue-demo-app/src/services/useTypeScriptDemo.ts +56 -0
- package/vue-demo-app/src/vite-env.d.ts +1 -0
- package/vue-demo-app/tsconfig.json +25 -0
- package/vue-demo-app/tsconfig.node.json +10 -0
- 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
|
+
}
|