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