@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
package/README.md
ADDED
|
@@ -0,0 +1,1280 @@
|
|
|
1
|
+
# Dexie ORM
|
|
2
|
+
|
|
3
|
+
TypeScript ORM wrapper for indexed-db with Zod runtime validation, built on the top of dexie.js.
|
|
4
|
+
|
|
5
|
+
**[View Homepage](https://idb-orm.com)** - Interactive documentation and examples
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **TypeScript Support** - Full type safety with generics
|
|
10
|
+
- **Zod Validation** - Runtime validation with Zod schemas
|
|
11
|
+
- **Config-based entities** - Define entities with a simple `defineEntity(...)` API
|
|
12
|
+
|
|
13
|
+
- **Dexie Compatibility** - All Dexie.js operations supported
|
|
14
|
+
- **Clean API** - Simple and intuitive API design
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install idb-orm dexie zod
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Important Notes
|
|
23
|
+
|
|
24
|
+
### Dexie Dependencies
|
|
25
|
+
This library includes Dexie.js internally. To ensure that `liveQuery` works properly, you need to configure Vite to dedupe Dexie.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// In your vite.config.ts, add:
|
|
29
|
+
import { defineConfig } from 'vite';
|
|
30
|
+
|
|
31
|
+
export default defineConfig({
|
|
32
|
+
resolve: {
|
|
33
|
+
dedupe: ['dexie'],
|
|
34
|
+
},
|
|
35
|
+
// ... other config
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// This ensures liveQuery works correctly
|
|
39
|
+
// Without dedupe, liveQuery may not function properly
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Built on Dexie.js
|
|
43
|
+
This library is built on top of [Dexie.js](https://dexie.org) - a powerful wrapper for IndexedDB. All Dexie.js methods and features are available through the `db` object.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// All Dexie.js methods work with our library
|
|
47
|
+
const db = await Database.createDatabase({...});
|
|
48
|
+
|
|
49
|
+
// Direct Dexie.js operations
|
|
50
|
+
await db.transaction('rw', db.users, async () => {
|
|
51
|
+
await db.users.add({ name: 'John', email: 'john@example.com' });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Dexie.js query methods
|
|
55
|
+
const users = await db.users
|
|
56
|
+
.where('age')
|
|
57
|
+
.above(18)
|
|
58
|
+
.and(user => user.name.startsWith('J'))
|
|
59
|
+
.toArray();
|
|
60
|
+
|
|
61
|
+
// Dexie.js liveQuery (reactive queries)
|
|
62
|
+
import { liveQuery } from 'dexie';
|
|
63
|
+
const observableUsers = liveQuery(() =>
|
|
64
|
+
db.users.where('active').equals(true).toArray()
|
|
65
|
+
);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Benefits:** You get all the power of Dexie.js plus our ORM features like entity validation, relations, and cloud sync.
|
|
69
|
+
|
|
70
|
+
### Index Requirements for Queries
|
|
71
|
+
**Critical:** When searching by any field, you MUST create an index on that field. Queries without proper indexes will fail.
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
const UserSchema = z.object({
|
|
75
|
+
id: z.number(),
|
|
76
|
+
name: z.string(),
|
|
77
|
+
email: z.string(),
|
|
78
|
+
age: z.number()
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
defineEntity(User, {
|
|
82
|
+
schema: UserSchema,
|
|
83
|
+
indexes: [
|
|
84
|
+
{ key: 'email' }, // Required for email queries
|
|
85
|
+
{ key: 'age' }, // Required for age queries
|
|
86
|
+
{ key: 'name' } // Required for name queries
|
|
87
|
+
]
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Compound Indexes for Complex Queries
|
|
92
|
+
For queries involving multiple fields, you need compound indexes. Single field indexes won't work for multi-field queries.
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
defineEntity(Post, {
|
|
96
|
+
schema: PostSchema,
|
|
97
|
+
compoundIndexes: [
|
|
98
|
+
{ key: ['category', 'status'] },
|
|
99
|
+
{ key: ['authorId', 'createdAt'] }
|
|
100
|
+
]
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Usage
|
|
105
|
+
|
|
106
|
+
### 1. Define Entities
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { BaseEntity, defineEntity } from 'idb-orm';
|
|
110
|
+
import { z } from 'zod';
|
|
111
|
+
|
|
112
|
+
// Zod schema for validation
|
|
113
|
+
const UserSchema = z.object({
|
|
114
|
+
id: z.number().optional(),
|
|
115
|
+
name: z.string().min(2, 'Name must be at least 2 characters'),
|
|
116
|
+
email: z.string().email('Invalid email format'),
|
|
117
|
+
age: z.number().min(18, 'Must be at least 18 years old'),
|
|
118
|
+
createdAt: z.number(),
|
|
119
|
+
updatedAt: z.number(),
|
|
120
|
+
isActive: z.boolean().default(true),
|
|
121
|
+
tags: z.array(z.string()).default([]),
|
|
122
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
export class UserEntity extends BaseEntity<number> {
|
|
126
|
+
name!: string;
|
|
127
|
+
email!: string;
|
|
128
|
+
age!: number;
|
|
129
|
+
createdAt!: number;
|
|
130
|
+
updatedAt!: number;
|
|
131
|
+
isActive!: boolean;
|
|
132
|
+
tags!: string[];
|
|
133
|
+
metadata!: Record<string, unknown>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
defineEntity(UserEntity, {
|
|
137
|
+
tableName: 'users',
|
|
138
|
+
schema: UserSchema,
|
|
139
|
+
columns: {
|
|
140
|
+
name: { required: true, indexed: true },
|
|
141
|
+
email: { required: true, unique: true },
|
|
142
|
+
age: { indexed: true },
|
|
143
|
+
createdAt: { indexed: true },
|
|
144
|
+
updatedAt: { indexed: true },
|
|
145
|
+
isActive: { indexed: true },
|
|
146
|
+
tags: {},
|
|
147
|
+
metadata: {},
|
|
148
|
+
},
|
|
149
|
+
// Example relations (optional)
|
|
150
|
+
// relations: {
|
|
151
|
+
// posts: { type: 'one-to-many', target: PostEntity, foreignKey: 'authorId' },
|
|
152
|
+
// },
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 2. Create Database
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { Database } from 'idb-orm';
|
|
160
|
+
import { UserEntity } from './entities/User';
|
|
161
|
+
import { PostEntity } from './entities/Post';
|
|
162
|
+
|
|
163
|
+
export const db = Database.createDatabase({
|
|
164
|
+
name: 'MyApp',
|
|
165
|
+
version: 1,
|
|
166
|
+
entities: [UserEntity, PostEntity],
|
|
167
|
+
config: {
|
|
168
|
+
onSchemaChangeStrategy: 'all',
|
|
169
|
+
cloudSync: {
|
|
170
|
+
databaseUrl: 'https://your-sync-server.com',
|
|
171
|
+
enableOfflineQueue: true,
|
|
172
|
+
syncInterval: 30000
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Database Configuration Options
|
|
179
|
+
|
|
180
|
+
The `createDatabase()` method accepts the following configuration options:
|
|
181
|
+
|
|
182
|
+
#### Basic Configuration
|
|
183
|
+
```typescript
|
|
184
|
+
const db = Database.createDatabase({
|
|
185
|
+
name: string, // Required: Database name
|
|
186
|
+
version: number, // Required: Schema version
|
|
187
|
+
entities: EntityConstructor[], // Required: Array of entity classes
|
|
188
|
+
config?: { // Optional: Additional configuration
|
|
189
|
+
// Schema change handling
|
|
190
|
+
onSchemaChangeStrategy?: 'selective' | 'all',
|
|
191
|
+
|
|
192
|
+
// Migrations list
|
|
193
|
+
migrations?: Migration[],
|
|
194
|
+
|
|
195
|
+
// Cloud synchronization
|
|
196
|
+
cloudSync?: CloudSyncConfig
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
#### Schema Change Strategy Options
|
|
202
|
+
|
|
203
|
+
**`onSchemaChangeStrategy`** - Defines how to handle database schema changes:
|
|
204
|
+
|
|
205
|
+
- **`'selective'`** (recommended for production): Only resets tables that have schema changes, preserving data in unchanged tables
|
|
206
|
+
- **`'all'`**: Resets the entire database when any schema change is detected, useful for development
|
|
207
|
+
|
|
208
|
+
#### Cloud Sync Configuration Options
|
|
209
|
+
|
|
210
|
+
**`CloudSyncConfig`** - Configuration for cloud synchronization:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
interface CloudSyncConfig {
|
|
214
|
+
databaseUrl: string; // Required: Base URL of the sync server
|
|
215
|
+
enableOfflineSupport?: boolean; // Optional: Queue changes when offline (default: false)
|
|
216
|
+
syncInterval?: number; // Optional: Auto-sync interval in milliseconds (default: 30000)
|
|
217
|
+
// Note: Authentication is handled by Dexie Cloud addon configuration
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Cloud Sync Options:**
|
|
222
|
+
- **`databaseUrl`** (required): The base URL of your synchronization server
|
|
223
|
+
- **`enableOfflineSupport`** (optional): If `true`, changes are queued when offline and synced when connection is restored
|
|
224
|
+
- **`syncInterval`** (optional): Automatic synchronization interval in milliseconds (default: 30 seconds)
|
|
225
|
+
|
|
226
|
+
**Authentication Note:**
|
|
227
|
+
Authentication for cloud sync is handled by the Dexie Cloud addon configuration, not through this interface. You need to configure authentication separately when setting up the Dexie Cloud addon.
|
|
228
|
+
|
|
229
|
+
#### Schema Change Strategy Examples
|
|
230
|
+
```typescript
|
|
231
|
+
// Option 1: Reset only changed tables (recommended for production)
|
|
232
|
+
const db = Database.createDatabase({
|
|
233
|
+
name: 'MyApp',
|
|
234
|
+
version: 1,
|
|
235
|
+
entities: [UserEntity, PostEntity],
|
|
236
|
+
config: {
|
|
237
|
+
onSchemaChangeStrategy: 'selective' // Only reset tables that changed
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Option 2: Reset entire database on schema changes
|
|
242
|
+
const db = Database.createDatabase({
|
|
243
|
+
name: 'MyApp',
|
|
244
|
+
version: 1,
|
|
245
|
+
entities: [UserEntity, PostEntity],
|
|
246
|
+
config: {
|
|
247
|
+
onSchemaChangeStrategy: 'all' // Reset all data when schema changes
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
#### Migration Configuration
|
|
253
|
+
```typescript
|
|
254
|
+
const db = Database.createDatabase({
|
|
255
|
+
name: 'MyApp',
|
|
256
|
+
version: 2,
|
|
257
|
+
entities: [UserEntity, PostEntity],
|
|
258
|
+
config: {
|
|
259
|
+
migrations: [
|
|
260
|
+
{
|
|
261
|
+
version: 2,
|
|
262
|
+
name: 'Add fullName field',
|
|
263
|
+
up: async (db) => {
|
|
264
|
+
await db.getRepository(UserEntity).toCollection().modify(user => {
|
|
265
|
+
user.fullName = `${user.firstName} ${user.lastName}`;
|
|
266
|
+
});
|
|
267
|
+
},
|
|
268
|
+
down: async (db) => {
|
|
269
|
+
await db.getRepository(UserEntity).toCollection().modify(user => {
|
|
270
|
+
delete user.fullName;
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
]
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### Cloud Synchronization Configuration
|
|
280
|
+
```typescript
|
|
281
|
+
const db = Database.createDatabase({
|
|
282
|
+
name: 'MyApp',
|
|
283
|
+
version: 1,
|
|
284
|
+
entities: [UserEntity, PostEntity],
|
|
285
|
+
config: {
|
|
286
|
+
cloudSync: {
|
|
287
|
+
databaseUrl: 'https://your-sync-server.com', // Required: Sync server URL
|
|
288
|
+
enableOfflineSupport: true, // Optional: Queue changes when offline
|
|
289
|
+
syncInterval: 30000 // Optional: Auto-sync interval (30s)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
#### Complete Configuration Example
|
|
296
|
+
```typescript
|
|
297
|
+
const db = Database.createDatabase({
|
|
298
|
+
name: 'MyApp',
|
|
299
|
+
version: 3,
|
|
300
|
+
entities: [UserEntity, PostEntity, CommentEntity],
|
|
301
|
+
config: {
|
|
302
|
+
// Handle schema changes by resetting only affected tables
|
|
303
|
+
onSchemaChangeStrategy: 'selective',
|
|
304
|
+
|
|
305
|
+
// Define migrations for version upgrades
|
|
306
|
+
migrations: [
|
|
307
|
+
{
|
|
308
|
+
version: 2,
|
|
309
|
+
name: 'Add user profiles',
|
|
310
|
+
up: async (db) => {
|
|
311
|
+
// Migration logic for version 2
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
version: 3,
|
|
316
|
+
name: 'Add comment system',
|
|
317
|
+
up: async (db) => {
|
|
318
|
+
// Migration logic for version 3
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
],
|
|
322
|
+
|
|
323
|
+
// Enable cloud synchronization
|
|
324
|
+
cloudSync: {
|
|
325
|
+
databaseUrl: 'https://api.myapp.com/sync',
|
|
326
|
+
enableOfflineSupport: true,
|
|
327
|
+
syncInterval: 60000 // Sync every minute
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### 3. Basic CRUD Operations
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
// Prefer explicit repositories for strict typing
|
|
337
|
+
const users = await db.getRepository(UserEntity).toArray(); // Type: UserEntity[]
|
|
338
|
+
const posts = await db.getRepository(PostEntity).toArray(); // Type: PostEntity[]
|
|
339
|
+
|
|
340
|
+
// Create new entity with validation in one call
|
|
341
|
+
import { newEntity } from 'idb-orm';
|
|
342
|
+
|
|
343
|
+
const newUser = newEntity(UserEntity, {
|
|
344
|
+
name: 'John Doe',
|
|
345
|
+
email: 'john@example.com',
|
|
346
|
+
age: 25,
|
|
347
|
+
createdAt: Date.now(),
|
|
348
|
+
updatedAt: Date.now(),
|
|
349
|
+
tags: ['developer'],
|
|
350
|
+
metadata: { source: 'manual' },
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// Save to database
|
|
354
|
+
await db.getRepository(UserEntity).add(newUser);
|
|
355
|
+
|
|
356
|
+
// Query with full Dexie.js API
|
|
357
|
+
const activeUsers = await db.getRepository(UserEntity)
|
|
358
|
+
.where('isActive')
|
|
359
|
+
.equals(true)
|
|
360
|
+
.toArray();
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### 4. Entity Relations
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// Define entities with relations
|
|
367
|
+
defineEntity(UserEntity, {
|
|
368
|
+
tableName: 'users',
|
|
369
|
+
schema: UserSchema,
|
|
370
|
+
columns: { /* ... */ },
|
|
371
|
+
relations: {
|
|
372
|
+
posts: {
|
|
373
|
+
type: 'one-to-many',
|
|
374
|
+
target: PostEntity,
|
|
375
|
+
foreignKey: 'authorId'
|
|
376
|
+
},
|
|
377
|
+
profile: {
|
|
378
|
+
type: 'one-to-one',
|
|
379
|
+
target: ProfileEntity,
|
|
380
|
+
foreignKey: 'userId'
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
defineEntity(PostEntity, {
|
|
386
|
+
tableName: 'posts',
|
|
387
|
+
schema: PostSchema,
|
|
388
|
+
columns: { /* ... */ },
|
|
389
|
+
relations: {
|
|
390
|
+
author: {
|
|
391
|
+
type: 'many-to-one',
|
|
392
|
+
target: UserEntity,
|
|
393
|
+
foreignKey: 'authorId'
|
|
394
|
+
},
|
|
395
|
+
tags: {
|
|
396
|
+
type: 'many-to-many',
|
|
397
|
+
target: TagEntity,
|
|
398
|
+
joinTable: 'post_tags'
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// Load relations
|
|
404
|
+
const userWithPosts = await db.loadRelations({
|
|
405
|
+
entity: user,
|
|
406
|
+
entityClass: UserEntity,
|
|
407
|
+
relationNames: ['posts', 'profile']
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// Load specific relation
|
|
411
|
+
const userPosts = await db.loadRelationByName({
|
|
412
|
+
entity: user,
|
|
413
|
+
entityClass: UserEntity,
|
|
414
|
+
relationName: 'posts'
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// Save entity with all its relations
|
|
418
|
+
const savedUser = await db.saveWithRelations({
|
|
419
|
+
entity: user,
|
|
420
|
+
entityClass: UserEntity
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// Delete entity with cascade handling for relations
|
|
424
|
+
await db.deleteWithRelations({
|
|
425
|
+
entity: user,
|
|
426
|
+
entityClass: UserEntity
|
|
427
|
+
});
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### 5. Reactive Queries with liveQuery
|
|
431
|
+
|
|
432
|
+
Use Dexie's `liveQuery` for reactive data that automatically updates when the database changes. Works with all major frameworks:
|
|
433
|
+
|
|
434
|
+
#### React Example
|
|
435
|
+
```typescript
|
|
436
|
+
import { liveQuery } from 'dexie';
|
|
437
|
+
import { useObservable } from 'dexie-react-hooks';
|
|
438
|
+
import { Database } from 'indexed-db-orm';
|
|
439
|
+
import { User } from './entities/User';
|
|
440
|
+
|
|
441
|
+
function UserList() {
|
|
442
|
+
const users = useObservable(
|
|
443
|
+
liveQuery(() => {
|
|
444
|
+
const userRepo = db.getRepository(User);
|
|
445
|
+
return userRepo.where('active').equals(true).toArray();
|
|
446
|
+
})
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
return (
|
|
450
|
+
<div>
|
|
451
|
+
{users?.map(user => (
|
|
452
|
+
<div key={user.id}>{user.name}</div>
|
|
453
|
+
))}
|
|
454
|
+
</div>
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
#### Vue Example
|
|
460
|
+
```typescript
|
|
461
|
+
import { liveQuery } from 'dexie';
|
|
462
|
+
import { ref, onMounted, onUnmounted } from 'vue';
|
|
463
|
+
import { Database } from 'indexed-db-orm';
|
|
464
|
+
import { User } from './entities/User';
|
|
465
|
+
|
|
466
|
+
export default {
|
|
467
|
+
setup() {
|
|
468
|
+
const users = ref<User[]>([]);
|
|
469
|
+
let subscription;
|
|
470
|
+
|
|
471
|
+
onMounted(() => {
|
|
472
|
+
subscription = liveQuery(() => {
|
|
473
|
+
const userRepo = db.getRepository(User);
|
|
474
|
+
return userRepo.where('active').equals(true).toArray();
|
|
475
|
+
}).subscribe(result => {
|
|
476
|
+
users.value = result;
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
onUnmounted(() => {
|
|
481
|
+
subscription?.unsubscribe();
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
return { users };
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
#### Svelte Example
|
|
490
|
+
```typescript
|
|
491
|
+
import { liveQuery } from 'dexie';
|
|
492
|
+
import { onMount, onDestroy } from 'svelte';
|
|
493
|
+
import { Database } from 'indexed-db-orm';
|
|
494
|
+
import { User } from './entities/User';
|
|
495
|
+
|
|
496
|
+
let users: User[] = [];
|
|
497
|
+
let subscription;
|
|
498
|
+
|
|
499
|
+
onMount(() => {
|
|
500
|
+
subscription = liveQuery(() => {
|
|
501
|
+
const userRepo = db.getRepository(User);
|
|
502
|
+
return userRepo.where('active').equals(true).toArray();
|
|
503
|
+
}).subscribe(result => {
|
|
504
|
+
users = result;
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
onDestroy(() => {
|
|
509
|
+
subscription?.unsubscribe();
|
|
510
|
+
});
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
#### Angular Example
|
|
514
|
+
```typescript
|
|
515
|
+
import { liveQuery } from 'dexie';
|
|
516
|
+
import { Component, OnInit, OnDestroy } from '@angular/core';
|
|
517
|
+
import { Subscription } from 'rxjs';
|
|
518
|
+
import { Database } from 'indexed-db-orm';
|
|
519
|
+
import { User } from './entities/User';
|
|
520
|
+
|
|
521
|
+
@Component({
|
|
522
|
+
selector: 'app-user-list',
|
|
523
|
+
template: '<div *ngFor="let user of users">{{user.name}}</div>'
|
|
524
|
+
})
|
|
525
|
+
export class UserListComponent implements OnInit, OnDestroy {
|
|
526
|
+
users: User[] = [];
|
|
527
|
+
private subscription?: Subscription;
|
|
528
|
+
|
|
529
|
+
ngOnInit() {
|
|
530
|
+
this.subscription = liveQuery(() => {
|
|
531
|
+
const userRepo = db.getRepository(User);
|
|
532
|
+
return userRepo.where('active').equals(true).toArray();
|
|
533
|
+
}).subscribe(result => {
|
|
534
|
+
this.users = result;
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
ngOnDestroy() {
|
|
539
|
+
this.subscription?.unsubscribe();
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### 6. Advanced Queries and Aggregation
|
|
545
|
+
|
|
546
|
+
```typescript
|
|
547
|
+
// Aggregation operations
|
|
548
|
+
const result = await db.aggregate({
|
|
549
|
+
entityClass: PostEntity,
|
|
550
|
+
options: {
|
|
551
|
+
where: { category: 'tech' },
|
|
552
|
+
sort: { field: 'views', direction: 'desc' },
|
|
553
|
+
limit: 10,
|
|
554
|
+
count: true,
|
|
555
|
+
sum: ['views'],
|
|
556
|
+
avg: ['likes'],
|
|
557
|
+
groupBy: ['category']
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
// Complex filtering and sorting
|
|
562
|
+
const topPosts = await db.getRepository(PostEntity)
|
|
563
|
+
.where('published')
|
|
564
|
+
.equals(true)
|
|
565
|
+
.and(post => post.views > 100)
|
|
566
|
+
.orderBy('views')
|
|
567
|
+
.reverse()
|
|
568
|
+
.limit(5)
|
|
569
|
+
.toArray();
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### 6. Entity Validation
|
|
573
|
+
|
|
574
|
+
```typescript
|
|
575
|
+
// Manual validation
|
|
576
|
+
const user = new UserEntity();
|
|
577
|
+
user.name = 'A'; // Too short
|
|
578
|
+
user.email = 'invalid-email'; // Invalid format
|
|
579
|
+
|
|
580
|
+
const result = user.validate();
|
|
581
|
+
if (!result.isValid) {
|
|
582
|
+
console.log(result.errors);
|
|
583
|
+
// ['name: Name must be at least 2 characters', 'email: Invalid email format']
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Validation with error throwing
|
|
587
|
+
try {
|
|
588
|
+
user.validateOrThrow();
|
|
589
|
+
} catch (error) {
|
|
590
|
+
console.log('Validation failed:', error.message);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Initialize with validation
|
|
594
|
+
const validUser = new UserEntity().init({
|
|
595
|
+
name: 'John Doe',
|
|
596
|
+
email: 'john@example.com',
|
|
597
|
+
age: 25
|
|
598
|
+
});
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### 7. Database Management
|
|
602
|
+
|
|
603
|
+
```typescript
|
|
604
|
+
// Check for schema changes
|
|
605
|
+
const hasChanges = await db.checkSchemaChanges();
|
|
606
|
+
if (hasChanges) {
|
|
607
|
+
await db.performSelectiveReset(); // Reset only changed tables
|
|
608
|
+
// or
|
|
609
|
+
await db.resetDatabase(); // Reset entire database
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Clear all data
|
|
613
|
+
await db.clearAllData();
|
|
614
|
+
|
|
615
|
+
// Run migrations
|
|
616
|
+
await db.runMigrations([
|
|
617
|
+
{
|
|
618
|
+
version: 2,
|
|
619
|
+
up: async (db) => {
|
|
620
|
+
await db.getRepository(UserEntity).toCollection().modify(user => {
|
|
621
|
+
user.fullName = `${user.firstName} ${user.lastName}`;
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
]);
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### 8. Cloud Synchronization
|
|
629
|
+
|
|
630
|
+
#### Installation
|
|
631
|
+
```bash
|
|
632
|
+
npm install dexie-cloud-addon
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
#### Configuration
|
|
636
|
+
```typescript
|
|
637
|
+
// Basic cloud sync configuration
|
|
638
|
+
const db = Database.createDatabase({
|
|
639
|
+
name: 'MyApp',
|
|
640
|
+
version: 1,
|
|
641
|
+
entities: [UserEntity, PostEntity],
|
|
642
|
+
config: {
|
|
643
|
+
cloudSync: {
|
|
644
|
+
databaseUrl: 'https://your-database-url.dexie.cloud',
|
|
645
|
+
enableOfflineSupport: true,
|
|
646
|
+
syncInterval: 30000 // 30 seconds
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
// Enable cloud sync manually
|
|
652
|
+
await db.enableCloudSync({
|
|
653
|
+
databaseUrl: 'https://your-sync-server.com',
|
|
654
|
+
enableOfflineSupport: true,
|
|
655
|
+
syncInterval: 30000
|
|
656
|
+
});
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
#### Cloud Sync API
|
|
660
|
+
```typescript
|
|
661
|
+
// Check sync status
|
|
662
|
+
const status = db.getSyncStatus();
|
|
663
|
+
console.log('Sync enabled:', status.enabled);
|
|
664
|
+
console.log('Last sync:', status.lastSync);
|
|
665
|
+
console.log('Online:', status.isOnline);
|
|
666
|
+
|
|
667
|
+
// Manual sync
|
|
668
|
+
await db.sync();
|
|
669
|
+
|
|
670
|
+
// Sync specific tables
|
|
671
|
+
await db.syncTables(['users', 'posts']);
|
|
672
|
+
|
|
673
|
+
// Check if cloud sync is enabled
|
|
674
|
+
const isEnabled = db.isCloudSyncEnabled();
|
|
675
|
+
|
|
676
|
+
// Get cloud sync configuration
|
|
677
|
+
const config = db.getCloudSyncConfig();
|
|
678
|
+
|
|
679
|
+
// Disable cloud sync
|
|
680
|
+
db.disableCloudSync();
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
#### Automatic Synchronization
|
|
684
|
+
```typescript
|
|
685
|
+
// Configure automatic sync with interval
|
|
686
|
+
const db = Database.createDatabase({
|
|
687
|
+
name: 'MyApp',
|
|
688
|
+
version: 1,
|
|
689
|
+
entities: [UserEntity, PostEntity],
|
|
690
|
+
config: {
|
|
691
|
+
cloudSync: {
|
|
692
|
+
databaseUrl: 'https://your-database-url.dexie.cloud',
|
|
693
|
+
syncInterval: 60000 // Sync every minute
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
#### React Integration Example
|
|
700
|
+
```typescript
|
|
701
|
+
import { useState, useEffect } from 'react';
|
|
702
|
+
import { useLiveQuery } from 'dexie-react-hooks';
|
|
703
|
+
|
|
704
|
+
function MyComponent() {
|
|
705
|
+
const [syncStatus, setSyncStatus] = useState({ enabled: false });
|
|
706
|
+
|
|
707
|
+
// Data with automatic synchronization
|
|
708
|
+
const users = useLiveQuery(() => db.getRepository(UserEntity).toArray());
|
|
709
|
+
|
|
710
|
+
useEffect(() => {
|
|
711
|
+
const status = db.getSyncStatus();
|
|
712
|
+
setSyncStatus(status);
|
|
713
|
+
}, []);
|
|
714
|
+
|
|
715
|
+
const handleSync = async () => {
|
|
716
|
+
try {
|
|
717
|
+
await db.sync();
|
|
718
|
+
alert('Synchronization completed!');
|
|
719
|
+
} catch (error) {
|
|
720
|
+
alert(`Sync error: ${error.message}`);
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
return (
|
|
725
|
+
<div>
|
|
726
|
+
<h2>Status: {syncStatus.enabled ? 'Enabled' : 'Disabled'}</h2>
|
|
727
|
+
<button onClick={handleSync}>Sync Now</button>
|
|
728
|
+
|
|
729
|
+
{users?.map(user => (
|
|
730
|
+
<div key={user.id}>{user.name}</div>
|
|
731
|
+
))}
|
|
732
|
+
</div>
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
#### Troubleshooting
|
|
738
|
+
```typescript
|
|
739
|
+
// Check if cloud sync is configured
|
|
740
|
+
const config = db.getCloudSyncConfig();
|
|
741
|
+
|
|
742
|
+
if (!config) {
|
|
743
|
+
await db.enableCloudSync({
|
|
744
|
+
databaseUrl: 'https://your-database-url.dexie.cloud'
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// Check sync status for debugging
|
|
749
|
+
const status = db.getSyncStatus();
|
|
750
|
+
console.log('Online:', status.isOnline);
|
|
751
|
+
console.log('Last sync:', status.lastSync);
|
|
752
|
+
|
|
753
|
+
// Try manual sync if automatic sync fails
|
|
754
|
+
await db.sync();
|
|
755
|
+
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
### 9. Compound Indexes
|
|
759
|
+
|
|
760
|
+
Compound indexes allow you to create indexes on multiple columns for better query performance.
|
|
761
|
+
|
|
762
|
+
#### Defining Compound Indexes
|
|
763
|
+
|
|
764
|
+
```typescript
|
|
765
|
+
import { BaseEntity, defineEntity } from 'idb-orm';
|
|
766
|
+
import { z } from 'zod';
|
|
767
|
+
|
|
768
|
+
// Zod schema for validation
|
|
769
|
+
const UserSchema = z.object({
|
|
770
|
+
id: z.number().optional(),
|
|
771
|
+
name: z.string(),
|
|
772
|
+
email: z.string().email(),
|
|
773
|
+
age: z.number(),
|
|
774
|
+
isActive: z.boolean(),
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
export class UserEntity extends BaseEntity {
|
|
778
|
+
name!: string;
|
|
779
|
+
email!: string;
|
|
780
|
+
age!: number;
|
|
781
|
+
isActive!: boolean;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// Define entity with compound indexes
|
|
785
|
+
defineEntity(UserEntity, {
|
|
786
|
+
tableName: 'users',
|
|
787
|
+
schema: UserSchema,
|
|
788
|
+
columns: {
|
|
789
|
+
name: { indexed: true },
|
|
790
|
+
email: { indexed: true, unique: true },
|
|
791
|
+
age: { indexed: true },
|
|
792
|
+
isActive: { indexed: true },
|
|
793
|
+
},
|
|
794
|
+
compoundIndexes: [
|
|
795
|
+
{
|
|
796
|
+
columns: ['name', 'email'],
|
|
797
|
+
unique: false,
|
|
798
|
+
name: 'name_email_index'
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
columns: ['age', 'isActive'],
|
|
802
|
+
unique: false,
|
|
803
|
+
name: 'age_active_index'
|
|
804
|
+
},
|
|
805
|
+
{
|
|
806
|
+
columns: ['email'],
|
|
807
|
+
unique: true,
|
|
808
|
+
name: 'unique_email_index'
|
|
809
|
+
}
|
|
810
|
+
]
|
|
811
|
+
});
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
#### Querying with Compound Indexes
|
|
815
|
+
|
|
816
|
+
```typescript
|
|
817
|
+
// Query using compound index (name + email)
|
|
818
|
+
const users = await db.getRepository(UserEntity)
|
|
819
|
+
.where(['name', 'email'])
|
|
820
|
+
.equals(['John Doe', 'john@example.com'])
|
|
821
|
+
.toArray();
|
|
822
|
+
|
|
823
|
+
// Query using compound index (age + isActive)
|
|
824
|
+
const activeAdults = await db.getRepository(UserEntity)
|
|
825
|
+
.where(['age', 'isActive'])
|
|
826
|
+
.above([18, 1]) // age > 18, isActive = true
|
|
827
|
+
.toArray();
|
|
828
|
+
|
|
829
|
+
// Query using unique compound index
|
|
830
|
+
const user = await db.getRepository(UserEntity)
|
|
831
|
+
.where('email')
|
|
832
|
+
.equals('john@example.com')
|
|
833
|
+
.first();
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
#### Different Query Operators
|
|
837
|
+
|
|
838
|
+
```typescript
|
|
839
|
+
// equals - exact match
|
|
840
|
+
const users = await db.getRepository(UserEntity)
|
|
841
|
+
.where(['name', 'email'])
|
|
842
|
+
.equals(['John', 'john@example.com'])
|
|
843
|
+
.toArray();
|
|
844
|
+
|
|
845
|
+
// above - greater than
|
|
846
|
+
const adults = await db.getRepository(UserEntity)
|
|
847
|
+
.where(['age', 'isActive'])
|
|
848
|
+
.above([18, 1])
|
|
849
|
+
.toArray();
|
|
850
|
+
|
|
851
|
+
// below - less than
|
|
852
|
+
const youngUsers = await db.getRepository(UserEntity)
|
|
853
|
+
.where(['age'])
|
|
854
|
+
.below([25])
|
|
855
|
+
.toArray();
|
|
856
|
+
|
|
857
|
+
// between - between values
|
|
858
|
+
const middleAged = await db.getRepository(UserEntity)
|
|
859
|
+
.where(['age'])
|
|
860
|
+
.between([25, 50])
|
|
861
|
+
.toArray();
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
#### Real-world Examples
|
|
865
|
+
|
|
866
|
+
```typescript
|
|
867
|
+
// 1. User search by name and email
|
|
868
|
+
defineEntity(UserEntity, {
|
|
869
|
+
tableName: 'users',
|
|
870
|
+
schema: UserSchema,
|
|
871
|
+
columns: {
|
|
872
|
+
name: { indexed: true },
|
|
873
|
+
email: { indexed: true },
|
|
874
|
+
},
|
|
875
|
+
compoundIndexes: [
|
|
876
|
+
{ columns: ['name', 'email'], name: 'name_email_index' }
|
|
877
|
+
]
|
|
878
|
+
});
|
|
879
|
+
// Usage: Find user by exact name and email
|
|
880
|
+
const user = await db.getRepository(UserEntity)
|
|
881
|
+
.where(['name', 'email'])
|
|
882
|
+
.equals(['John Doe', 'john@example.com'])
|
|
883
|
+
.first();
|
|
884
|
+
|
|
885
|
+
// 2. Active adults filter
|
|
886
|
+
defineEntity(UserEntity, {
|
|
887
|
+
tableName: 'users',
|
|
888
|
+
schema: UserSchema,
|
|
889
|
+
columns: {
|
|
890
|
+
age: { indexed: true },
|
|
891
|
+
isActive: { indexed: true },
|
|
892
|
+
},
|
|
893
|
+
compoundIndexes: [
|
|
894
|
+
{ columns: ['age', 'isActive'], name: 'age_active_index' }
|
|
895
|
+
]
|
|
896
|
+
});
|
|
897
|
+
// Usage: Find active adults
|
|
898
|
+
const activeAdults = await db.getRepository(UserEntity)
|
|
899
|
+
.where(['age', 'isActive'])
|
|
900
|
+
.above([18, 1])
|
|
901
|
+
.toArray();
|
|
902
|
+
|
|
903
|
+
// 3. Posts by date and category
|
|
904
|
+
defineEntity(PostEntity, {
|
|
905
|
+
tableName: 'posts',
|
|
906
|
+
schema: PostSchema,
|
|
907
|
+
columns: {
|
|
908
|
+
createdAt: { indexed: true },
|
|
909
|
+
category: { indexed: true },
|
|
910
|
+
},
|
|
911
|
+
compoundIndexes: [
|
|
912
|
+
{ columns: ['createdAt', 'category'], name: 'date_category_index' }
|
|
913
|
+
]
|
|
914
|
+
});
|
|
915
|
+
// Usage: Find recent posts in specific category
|
|
916
|
+
const recentTechPosts = await db.getRepository(PostEntity)
|
|
917
|
+
.where(['createdAt', 'category'])
|
|
918
|
+
.above([Date.now() - 7 * 24 * 60 * 60 * 1000, 'tech'])
|
|
919
|
+
.toArray();
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
#### Compound Index Benefits
|
|
923
|
+
|
|
924
|
+
- **Query Performance**: Faster searches on multiple columns
|
|
925
|
+
- **Uniqueness**: Guarantee unique combinations of values
|
|
926
|
+
- **Flexibility**: Create indexes on any column combinations
|
|
927
|
+
- **Optimization**: Better performance for complex queries
|
|
928
|
+
|
|
929
|
+
### Vite note: ensure a single Dexie instance for live queries
|
|
930
|
+
|
|
931
|
+
If `useLiveQuery` doesn't re-render with this library's `Database`, make sure Vite bundles a single Dexie copy by deduping Dexie:
|
|
932
|
+
|
|
933
|
+
```ts
|
|
934
|
+
// vite.config.ts
|
|
935
|
+
import react from '@vitejs/plugin-react';
|
|
936
|
+
import { defineConfig } from 'vite';
|
|
937
|
+
|
|
938
|
+
export default defineConfig({
|
|
939
|
+
plugins: [react()],
|
|
940
|
+
resolve: { dedupe: ['dexie'] },
|
|
941
|
+
});
|
|
942
|
+
```
|
|
943
|
+
|
|
944
|
+
This ensures the app and the library share the same Dexie singleton so reactivity works correctly.
|
|
945
|
+
|
|
946
|
+
### Indexing Note
|
|
947
|
+
|
|
948
|
+
When querying with `where('field')` or sorting with `orderBy('field')`, ensure the field is indexed in your entity definition:
|
|
949
|
+
|
|
950
|
+
```ts
|
|
951
|
+
defineEntity(PostEntity, {
|
|
952
|
+
tableName: 'posts',
|
|
953
|
+
columns: {
|
|
954
|
+
createdAt: { indexed: true },
|
|
955
|
+
published: { indexed: true },
|
|
956
|
+
likes: { indexed: true },
|
|
957
|
+
},
|
|
958
|
+
});
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
If you change indexes, bump your database `version` or run a migration so Dexie can apply the schema update.
|
|
962
|
+
|
|
963
|
+
## Example Applications
|
|
964
|
+
|
|
965
|
+
The library includes comprehensive demo applications showcasing all features across different frameworks:
|
|
966
|
+
|
|
967
|
+
### Available Demo Apps
|
|
968
|
+
|
|
969
|
+
#### **React Demo App**
|
|
970
|
+
- **Framework**: React 19 + TypeScript
|
|
971
|
+
- **UI Library**: Material-UI (MUI)
|
|
972
|
+
- **Features**: Live queries, TypeScript demo, Cloud sync, Aggregations
|
|
973
|
+
- **Run**: `cd react-demo-app && npm install && npm run dev`
|
|
974
|
+
- **URL**: `http://localhost:5173`
|
|
975
|
+
|
|
976
|
+
#### **Vue Demo App**
|
|
977
|
+
- **Framework**: Vue 3 + TypeScript
|
|
978
|
+
- **UI Library**: Vuetify 3
|
|
979
|
+
- **Features**: Reactive components, Cloud sync, Entity management
|
|
980
|
+
- **Run**: `cd vue-demo-app && npm install && npm run dev`
|
|
981
|
+
- **URL**: `http://localhost:5173`
|
|
982
|
+
|
|
983
|
+
#### **Svelte Demo App**
|
|
984
|
+
- **Framework**: SvelteKit + TypeScript
|
|
985
|
+
- **UI Library**: Material-UI (MUI)
|
|
986
|
+
- **Features**: Server-side rendering, Live queries, TypeScript integration
|
|
987
|
+
- **Run**: `cd svelte-demo-app && npm install && npm run dev`
|
|
988
|
+
- **URL**: `http://localhost:5173`
|
|
989
|
+
|
|
990
|
+
#### **Angular Demo App**
|
|
991
|
+
- **Framework**: Angular 17 + TypeScript
|
|
992
|
+
- **UI Library**: Angular Material
|
|
993
|
+
- **Features**: Services, Dependency injection, Reactive forms
|
|
994
|
+
- **Run**: `cd angular-demo-app && npm install && npm start`
|
|
995
|
+
- **URL**: `http://localhost:4200`
|
|
996
|
+
|
|
997
|
+
### Demo App Features
|
|
998
|
+
|
|
999
|
+
All demo applications showcase:
|
|
1000
|
+
|
|
1001
|
+
#### **Core Features**
|
|
1002
|
+
- **Entity Definition** - Using `defineEntity()` API
|
|
1003
|
+
- **Zod Validation** - Runtime validation with error handling
|
|
1004
|
+
- **Relations** - One-to-one, one-to-many, many-to-many relationships
|
|
1005
|
+
- **Aggregations** - Count, sum, average, min, max operations
|
|
1006
|
+
- **Live Queries** - Reactive data with `useLiveQuery()`
|
|
1007
|
+
- **TypeScript** - Full type safety and IntelliSense
|
|
1008
|
+
|
|
1009
|
+
#### **Advanced Features**
|
|
1010
|
+
- **Cloud Sync** - Dexie Cloud synchronization
|
|
1011
|
+
- **Migrations** - Database schema migrations
|
|
1012
|
+
- **Compound Indexes** - Multi-column indexing
|
|
1013
|
+
- **Entity Management** - CRUD operations with relations
|
|
1014
|
+
- **Error Handling** - Validation errors and user feedback
|
|
1015
|
+
|
|
1016
|
+
### Demo App Structure
|
|
1017
|
+
|
|
1018
|
+
Each demo app follows similar structure:
|
|
1019
|
+
|
|
1020
|
+
```
|
|
1021
|
+
demo-app/
|
|
1022
|
+
├── src/
|
|
1023
|
+
│ ├── entities/ # Entity definitions
|
|
1024
|
+
│ ├── database/ # Database configuration
|
|
1025
|
+
│ └── migrations/ # Database migrations
|
|
1026
|
+
├── package.json
|
|
1027
|
+
└── README.md
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
Each demo is fully functional and can serve as a starting point for your own applications!
|
|
1031
|
+
|
|
1032
|
+
## API Reference
|
|
1033
|
+
|
|
1034
|
+
### Core Classes
|
|
1035
|
+
|
|
1036
|
+
#### `Database`
|
|
1037
|
+
Main database class extending Dexie with ORM capabilities.
|
|
1038
|
+
|
|
1039
|
+
**Static Methods:**
|
|
1040
|
+
- `Database.createDatabase(params)` - Create database with entity registration
|
|
1041
|
+
|
|
1042
|
+
**Instance Methods:**
|
|
1043
|
+
- `getRepository<T>(entityClass)` - Get typed repository for entity
|
|
1044
|
+
- `aggregate<T>(params)` - Perform aggregation operations
|
|
1045
|
+
- `clearAllData()` - Clear all data from database
|
|
1046
|
+
- `resetDatabase()` - Reset database when schema changes
|
|
1047
|
+
- `checkSchemaChanges()` - Check if schema has changed
|
|
1048
|
+
- `performSelectiveReset()` - Reset only changed tables
|
|
1049
|
+
- `runMigrations(migrations)` - Run database migrations
|
|
1050
|
+
- `getTypedTable<T>(entityClass)` - Get typed table for entity
|
|
1051
|
+
- `getTableForEntity<T>(entityClass)` - Get table with proper typing
|
|
1052
|
+
- `getEntities()` - Get all registered entities
|
|
1053
|
+
- `getEntity(tableName)` - Get entity by table name
|
|
1054
|
+
- `loadRelations<T>(params)` - Load relations for entity
|
|
1055
|
+
- `loadRelationByName<T, K>(params)` - Load specific relation
|
|
1056
|
+
- `saveWithRelations<T>(params)` - Save entity with relations
|
|
1057
|
+
- `deleteWithRelations<T>(params)` - Delete entity with cascade
|
|
1058
|
+
- `sync()` - Manual cloud sync
|
|
1059
|
+
- `getSyncStatus()` - Get cloud sync status
|
|
1060
|
+
- `enableCloudSync(config)` - Enable cloud synchronization
|
|
1061
|
+
- `disableCloudSync()` - Disable cloud sync
|
|
1062
|
+
- `isCloudSyncEnabled()` - Check if cloud sync is enabled
|
|
1063
|
+
- `getCloudSyncConfig()` - Get cloud sync configuration
|
|
1064
|
+
- `syncTables(tableNames)` - Sync specific tables
|
|
1065
|
+
|
|
1066
|
+
#### `BaseEntity<TKey>`
|
|
1067
|
+
Base class for all entities with validation capabilities.
|
|
1068
|
+
|
|
1069
|
+
**Methods:**
|
|
1070
|
+
- `validate(): ValidationResult` - Validate entity against schema
|
|
1071
|
+
- `validateOrThrow(): void` - Validate and throw error if invalid
|
|
1072
|
+
- `init(data): this` - Initialize entity with data and validate
|
|
1073
|
+
|
|
1074
|
+
#### `EntitySchema<T>`
|
|
1075
|
+
Schema management for entities.
|
|
1076
|
+
|
|
1077
|
+
**Methods:**
|
|
1078
|
+
- `getTableName(): string` - Get table name for entity
|
|
1079
|
+
- `getSchema(): ZodSchema | undefined` - Get Zod schema
|
|
1080
|
+
- `getColumns(): Record<string, ColumnOptions>` - Get column metadata
|
|
1081
|
+
- `validate(data): ValidationResult` - Validate data against schema
|
|
1082
|
+
- `create(data?): T` - Create new entity instance
|
|
1083
|
+
|
|
1084
|
+
### Entity Definition
|
|
1085
|
+
|
|
1086
|
+
#### `defineEntity(EntityClass, options)`
|
|
1087
|
+
Define entity with metadata and configuration.
|
|
1088
|
+
|
|
1089
|
+
**Parameters:**
|
|
1090
|
+
- `EntityClass` - Entity constructor class
|
|
1091
|
+
- `options` - Entity configuration object
|
|
1092
|
+
|
|
1093
|
+
**Options:**
|
|
1094
|
+
```typescript
|
|
1095
|
+
interface EntityOptions {
|
|
1096
|
+
tableName?: string; // Custom table name
|
|
1097
|
+
schema?: ZodSchema; // Zod validation schema
|
|
1098
|
+
timestamps?: boolean; // Enable timestamps
|
|
1099
|
+
columns?: Record<string, ColumnOptions>;
|
|
1100
|
+
relations?: Record<string, RelationOptions>;
|
|
1101
|
+
compoundIndexes?: CompoundIndexOptions[];
|
|
1102
|
+
}
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
#### `newEntity(EntityClass, data)`
|
|
1106
|
+
Create new entity instance with validation.
|
|
1107
|
+
|
|
1108
|
+
**Parameters:**
|
|
1109
|
+
- `EntityClass` - Entity constructor
|
|
1110
|
+
- `data` - Partial entity data
|
|
1111
|
+
|
|
1112
|
+
**Returns:** Fully initialized and validated entity instance
|
|
1113
|
+
|
|
1114
|
+
### Decorators
|
|
1115
|
+
|
|
1116
|
+
#### `@Entity(options?)`
|
|
1117
|
+
Class decorator for entity definition.
|
|
1118
|
+
|
|
1119
|
+
#### `@Column(options?)`
|
|
1120
|
+
Property decorator for column configuration.
|
|
1121
|
+
|
|
1122
|
+
#### `@Relation(options)`
|
|
1123
|
+
Property decorator for relation definition.
|
|
1124
|
+
|
|
1125
|
+
**Relation Types:**
|
|
1126
|
+
- `@OneToOne(target, options?)` - One-to-one relation
|
|
1127
|
+
- `@OneToMany(target, foreignKey)` - One-to-many relation
|
|
1128
|
+
- `@ManyToMany(target, joinTable)` - Many-to-many relation
|
|
1129
|
+
|
|
1130
|
+
### Type Definitions
|
|
1131
|
+
|
|
1132
|
+
#### `EntityConstructor<T>`
|
|
1133
|
+
```typescript
|
|
1134
|
+
interface EntityConstructor<T extends BaseEntity = BaseEntity> {
|
|
1135
|
+
new (): T;
|
|
1136
|
+
schema?: ZodSchema;
|
|
1137
|
+
tableName?: string;
|
|
1138
|
+
}
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
#### `ColumnOptions`
|
|
1142
|
+
```typescript
|
|
1143
|
+
interface ColumnOptions {
|
|
1144
|
+
kind?: 'string' | 'number' | 'boolean' | 'array' | 'object';
|
|
1145
|
+
unique?: boolean;
|
|
1146
|
+
indexed?: boolean;
|
|
1147
|
+
required?: boolean;
|
|
1148
|
+
default?: unknown;
|
|
1149
|
+
primaryKey?: boolean;
|
|
1150
|
+
autoIncrement?: boolean;
|
|
1151
|
+
}
|
|
1152
|
+
```
|
|
1153
|
+
|
|
1154
|
+
#### `RelationOptions`
|
|
1155
|
+
```typescript
|
|
1156
|
+
interface RelationOptions {
|
|
1157
|
+
type: 'one-to-one' | 'one-to-many' | 'many-to-many';
|
|
1158
|
+
target: EntityConstructor | string;
|
|
1159
|
+
foreignKey?: string;
|
|
1160
|
+
joinTable?: string;
|
|
1161
|
+
cascade?: boolean;
|
|
1162
|
+
eager?: boolean;
|
|
1163
|
+
}
|
|
1164
|
+
```
|
|
1165
|
+
|
|
1166
|
+
#### `ValidationResult`
|
|
1167
|
+
```typescript
|
|
1168
|
+
interface ValidationResult {
|
|
1169
|
+
isValid: boolean;
|
|
1170
|
+
errors: string[];
|
|
1171
|
+
}
|
|
1172
|
+
```
|
|
1173
|
+
|
|
1174
|
+
#### `DatabaseConfig`
|
|
1175
|
+
```typescript
|
|
1176
|
+
interface DatabaseConfig {
|
|
1177
|
+
name: string;
|
|
1178
|
+
version: number;
|
|
1179
|
+
entities: EntityConstructor[];
|
|
1180
|
+
onSchemaChangeStrategy?: 'selective' | 'all';
|
|
1181
|
+
migrations?: Migration[];
|
|
1182
|
+
cloudSync?: CloudSyncConfig;
|
|
1183
|
+
}
|
|
1184
|
+
```
|
|
1185
|
+
|
|
1186
|
+
#### `CloudSyncConfig`
|
|
1187
|
+
```typescript
|
|
1188
|
+
interface CloudSyncConfig {
|
|
1189
|
+
databaseUrl: string;
|
|
1190
|
+
enableOfflineSupport?: boolean;
|
|
1191
|
+
syncInterval?: number;
|
|
1192
|
+
}
|
|
1193
|
+
```
|
|
1194
|
+
|
|
1195
|
+
#### `Migration`
|
|
1196
|
+
```typescript
|
|
1197
|
+
interface Migration {
|
|
1198
|
+
version: number;
|
|
1199
|
+
name: string;
|
|
1200
|
+
up: (db: Dexie) => Promise<void>;
|
|
1201
|
+
down?: (db: Dexie) => Promise<void>;
|
|
1202
|
+
}
|
|
1203
|
+
```
|
|
1204
|
+
|
|
1205
|
+
#### `AggregationOptions<T>`
|
|
1206
|
+
```typescript
|
|
1207
|
+
interface AggregationOptions<T extends BaseEntity> {
|
|
1208
|
+
where?: Partial<T>;
|
|
1209
|
+
count?: boolean;
|
|
1210
|
+
sum?: (keyof T)[];
|
|
1211
|
+
avg?: (keyof T)[];
|
|
1212
|
+
min?: (keyof T)[];
|
|
1213
|
+
max?: (keyof T)[];
|
|
1214
|
+
groupBy?: keyof T;
|
|
1215
|
+
include?: string[];
|
|
1216
|
+
limit?: number;
|
|
1217
|
+
sort?: {
|
|
1218
|
+
field: keyof T;
|
|
1219
|
+
direction: 'asc' | 'desc';
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
#### `AggregationResult`
|
|
1225
|
+
```typescript
|
|
1226
|
+
interface AggregationResult {
|
|
1227
|
+
count?: number;
|
|
1228
|
+
sum?: Record<string, number>;
|
|
1229
|
+
avg?: Record<string, number>;
|
|
1230
|
+
min?: Record<string, number>;
|
|
1231
|
+
max?: Record<string, number>;
|
|
1232
|
+
groups?: Array<{
|
|
1233
|
+
key: unknown;
|
|
1234
|
+
count: number;
|
|
1235
|
+
sum?: Record<string, number>;
|
|
1236
|
+
avg?: Record<string, number>;
|
|
1237
|
+
min?: Record<string, number>;
|
|
1238
|
+
max?: Record<string, number>;
|
|
1239
|
+
}>;
|
|
1240
|
+
data?: unknown[];
|
|
1241
|
+
}
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
### Error Classes
|
|
1245
|
+
|
|
1246
|
+
#### `ValidationError`
|
|
1247
|
+
Error thrown when entity validation fails.
|
|
1248
|
+
|
|
1249
|
+
**Properties:**
|
|
1250
|
+
- `message: string` - Error message
|
|
1251
|
+
- `errors: string[]` - Array of validation errors
|
|
1252
|
+
|
|
1253
|
+
### Utility Functions
|
|
1254
|
+
|
|
1255
|
+
#### `newEntity(EntityClass, data)`
|
|
1256
|
+
Factory function for creating validated entities.
|
|
1257
|
+
|
|
1258
|
+
#### `defineEntity(EntityClass, options)`
|
|
1259
|
+
Function for defining entity metadata.
|
|
1260
|
+
|
|
1261
|
+
### Schema Change Strategies
|
|
1262
|
+
|
|
1263
|
+
#### `'selective'`
|
|
1264
|
+
Only resets tables that have schema changes, preserving data in unchanged tables.
|
|
1265
|
+
|
|
1266
|
+
#### `'all'`
|
|
1267
|
+
Resets the entire database when any schema change is detected.
|
|
1268
|
+
|
|
1269
|
+
### Cloud Sync Status
|
|
1270
|
+
|
|
1271
|
+
```typescript
|
|
1272
|
+
interface SyncStatus {
|
|
1273
|
+
enabled: boolean;
|
|
1274
|
+
lastSync?: Date;
|
|
1275
|
+
isOnline?: boolean;
|
|
1276
|
+
}
|
|
1277
|
+
```
|
|
1278
|
+
|
|
1279
|
+
## License
|
|
1280
|
+
MIT
|