@indexeddb-orm/idb-orm 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/.vscode/extensions.json +5 -0
  2. package/README.md +1280 -0
  3. package/angular-demo-app/README.md +84 -0
  4. package/angular-demo-app/angular.json +109 -0
  5. package/angular-demo-app/package-lock.json +14215 -0
  6. package/angular-demo-app/package.json +41 -0
  7. package/angular-demo-app/src/app/app.component.ts +481 -0
  8. package/angular-demo-app/src/app/app.routes.ts +8 -0
  9. package/angular-demo-app/src/app/components/actions.component.ts +202 -0
  10. package/angular-demo-app/src/app/components/cloud-sync-demo.component.ts +296 -0
  11. package/angular-demo-app/src/app/components/live-query-demo.component.ts +307 -0
  12. package/angular-demo-app/src/app/components/main-info.component.ts +148 -0
  13. package/angular-demo-app/src/app/components/posts-live-query-demo.component.ts +336 -0
  14. package/angular-demo-app/src/app/components/typescript-demo.component.ts +268 -0
  15. package/angular-demo-app/src/entities/post-tag.entity.ts +25 -0
  16. package/angular-demo-app/src/entities/post.entity.ts +49 -0
  17. package/angular-demo-app/src/entities/profile.entity.ts +42 -0
  18. package/angular-demo-app/src/entities/tag.entity.ts +36 -0
  19. package/angular-demo-app/src/entities/user.entity.ts +59 -0
  20. package/angular-demo-app/src/favicon.ico +1 -0
  21. package/angular-demo-app/src/index.html +16 -0
  22. package/angular-demo-app/src/main.ts +13 -0
  23. package/angular-demo-app/src/services/app-logic.service.ts +449 -0
  24. package/angular-demo-app/src/services/cloud-sync.service.ts +95 -0
  25. package/angular-demo-app/src/services/database.service.ts +26 -0
  26. package/angular-demo-app/src/services/live-query.service.ts +63 -0
  27. package/angular-demo-app/src/services/posts-live-query.service.ts +86 -0
  28. package/angular-demo-app/src/services/typescript-demo.service.ts +59 -0
  29. package/angular-demo-app/src/styles.scss +50 -0
  30. package/angular-demo-app/tsconfig.app.json +13 -0
  31. package/angular-demo-app/tsconfig.json +34 -0
  32. package/angular-demo-app/tsconfig.spec.json +13 -0
  33. package/dist/Database.d.ts +206 -0
  34. package/dist/Database.js +288 -0
  35. package/dist/decorators/Column.d.ts +79 -0
  36. package/dist/decorators/Column.js +236 -0
  37. package/dist/decorators/Entity.d.ts +32 -0
  38. package/dist/decorators/Entity.js +44 -0
  39. package/dist/decorators/Relation.d.ts +70 -0
  40. package/dist/decorators/Relation.js +120 -0
  41. package/dist/decorators/index.d.ts +3 -0
  42. package/dist/decorators/index.js +3 -0
  43. package/dist/errors/ValidationError.d.ts +4 -0
  44. package/dist/errors/ValidationError.js +8 -0
  45. package/dist/index.d.ts +8 -0
  46. package/dist/index.js +7 -0
  47. package/dist/metadata/Column.d.ts +8 -0
  48. package/dist/metadata/Column.js +44 -0
  49. package/dist/metadata/Entity.d.ts +11 -0
  50. package/dist/metadata/Entity.js +21 -0
  51. package/dist/metadata/Relation.d.ts +20 -0
  52. package/dist/metadata/Relation.js +74 -0
  53. package/dist/metadata/index.d.ts +3 -0
  54. package/dist/metadata/index.js +3 -0
  55. package/dist/services/AggregationService.d.ts +38 -0
  56. package/dist/services/AggregationService.js +229 -0
  57. package/dist/services/BaseEntity.d.ts +32 -0
  58. package/dist/services/BaseEntity.js +62 -0
  59. package/dist/services/CloudSyncService.d.ts +100 -0
  60. package/dist/services/CloudSyncService.js +196 -0
  61. package/dist/services/DecoratorUtils.d.ts +12 -0
  62. package/dist/services/DecoratorUtils.js +10 -0
  63. package/dist/services/EntityFactory.d.ts +25 -0
  64. package/dist/services/EntityFactory.js +27 -0
  65. package/dist/services/EntityRegistry.d.ts +61 -0
  66. package/dist/services/EntityRegistry.js +56 -0
  67. package/dist/services/EntitySchema.d.ts +56 -0
  68. package/dist/services/EntitySchema.js +125 -0
  69. package/dist/services/MigrationManager.d.ts +70 -0
  70. package/dist/services/MigrationManager.js +181 -0
  71. package/dist/services/RelationLoader.d.ts +66 -0
  72. package/dist/services/RelationLoader.js +310 -0
  73. package/dist/services/SchemaBuilder.d.ts +68 -0
  74. package/dist/services/SchemaBuilder.js +191 -0
  75. package/dist/services/index.d.ts +7 -0
  76. package/dist/services/index.js +7 -0
  77. package/dist/types.d.ts +152 -0
  78. package/dist/types.js +1 -0
  79. package/dist/utils/logger.d.ts +12 -0
  80. package/dist/utils/logger.js +16 -0
  81. package/eslint.config.js +49 -0
  82. package/homepage/favicon.svg +36 -0
  83. package/homepage/index.html +1725 -0
  84. package/package.json +78 -0
  85. package/react-demo-app/README.md +61 -0
  86. package/react-demo-app/eslint.config.js +60 -0
  87. package/react-demo-app/index.html +13 -0
  88. package/react-demo-app/package-lock.json +4955 -0
  89. package/react-demo-app/package.json +39 -0
  90. package/react-demo-app/src/App.tsx +172 -0
  91. package/react-demo-app/src/assets/react.svg +1 -0
  92. package/react-demo-app/src/components/Actions.tsx +171 -0
  93. package/react-demo-app/src/components/CloudSyncDemo.tsx +191 -0
  94. package/react-demo-app/src/components/LiveQueryDemo.tsx +122 -0
  95. package/react-demo-app/src/components/MainInfo.tsx +75 -0
  96. package/react-demo-app/src/components/PostsLiveQueryDemo.tsx +185 -0
  97. package/react-demo-app/src/components/TypeScriptDemo.tsx +190 -0
  98. package/react-demo-app/src/database/Database.ts +30 -0
  99. package/react-demo-app/src/entities/Post.ts +48 -0
  100. package/react-demo-app/src/entities/PostTag.ts +26 -0
  101. package/react-demo-app/src/entities/Profile.ts +41 -0
  102. package/react-demo-app/src/entities/Tag.ts +35 -0
  103. package/react-demo-app/src/entities/User.ts +61 -0
  104. package/react-demo-app/src/hooks/useAppLogic.ts +565 -0
  105. package/react-demo-app/src/hooks/useCloudSyncDemo.ts +84 -0
  106. package/react-demo-app/src/hooks/useLiveQueryDemo.ts +68 -0
  107. package/react-demo-app/src/hooks/usePostsLiveQueryDemo.ts +64 -0
  108. package/react-demo-app/src/hooks/useTypeScriptDemo.ts +43 -0
  109. package/react-demo-app/src/index.css +26 -0
  110. package/react-demo-app/src/main.tsx +18 -0
  111. package/react-demo-app/src/migrations/001-add-user-email-index.ts +17 -0
  112. package/react-demo-app/src/migrations/002-add-post-category.ts +37 -0
  113. package/react-demo-app/src/migrations/index.ts +8 -0
  114. package/react-demo-app/src/vite-env.d.ts +1 -0
  115. package/react-demo-app/tsconfig.app.json +22 -0
  116. package/react-demo-app/tsconfig.json +6 -0
  117. package/react-demo-app/vite.config.ts +10 -0
  118. package/src/Database.ts +405 -0
  119. package/src/errors/ValidationError.ts +9 -0
  120. package/src/index.ts +13 -0
  121. package/src/metadata/Column.ts +74 -0
  122. package/src/metadata/Entity.ts +42 -0
  123. package/src/metadata/Relation.ts +121 -0
  124. package/src/metadata/index.ts +5 -0
  125. package/src/services/AggregationService.ts +348 -0
  126. package/src/services/BaseEntity.ts +77 -0
  127. package/src/services/CloudSyncService.ts +248 -0
  128. package/src/services/EntityFactory.ts +35 -0
  129. package/src/services/EntityRegistry.ts +109 -0
  130. package/src/services/EntitySchema.ts +154 -0
  131. package/src/services/MigrationManager.ts +276 -0
  132. package/src/services/RelationLoader.ts +532 -0
  133. package/src/services/SchemaBuilder.ts +237 -0
  134. package/src/services/index.ts +7 -0
  135. package/src/types.d.ts +1 -0
  136. package/src/types.ts +169 -0
  137. package/src/utils/logger.ts +40 -0
  138. package/svelte-demo-app/README.md +61 -0
  139. package/svelte-demo-app/package-lock.json +3000 -0
  140. package/svelte-demo-app/package.json +30 -0
  141. package/svelte-demo-app/src/app.d.ts +12 -0
  142. package/svelte-demo-app/src/app.html +13 -0
  143. package/svelte-demo-app/src/components/Actions.svelte +121 -0
  144. package/svelte-demo-app/src/components/CloudSyncDemo.svelte +333 -0
  145. package/svelte-demo-app/src/components/LiveQueryDemo.svelte +191 -0
  146. package/svelte-demo-app/src/components/MainInfo.svelte +133 -0
  147. package/svelte-demo-app/src/components/PostsLiveQueryDemo.svelte +330 -0
  148. package/svelte-demo-app/src/components/TypeScriptDemo.svelte +251 -0
  149. package/svelte-demo-app/src/database/Database.ts +29 -0
  150. package/svelte-demo-app/src/entities/Post.ts +46 -0
  151. package/svelte-demo-app/src/entities/PostTag.ts +22 -0
  152. package/svelte-demo-app/src/entities/Profile.ts +39 -0
  153. package/svelte-demo-app/src/entities/Tag.ts +33 -0
  154. package/svelte-demo-app/src/entities/User.ts +62 -0
  155. package/svelte-demo-app/src/lib/database/Database.ts +30 -0
  156. package/svelte-demo-app/src/lib/entities/Post.ts +47 -0
  157. package/svelte-demo-app/src/lib/entities/PostTag.ts +23 -0
  158. package/svelte-demo-app/src/lib/entities/Profile.ts +40 -0
  159. package/svelte-demo-app/src/lib/entities/Tag.ts +34 -0
  160. package/svelte-demo-app/src/lib/entities/User.ts +59 -0
  161. package/svelte-demo-app/src/lib/index.ts +7 -0
  162. package/svelte-demo-app/src/lib/migrations/001-add-user-email-index.ts +17 -0
  163. package/svelte-demo-app/src/lib/migrations/002-add-post-category.ts +37 -0
  164. package/svelte-demo-app/src/lib/migrations/index.ts +8 -0
  165. package/svelte-demo-app/src/migrations/001-add-user-email-index.ts +17 -0
  166. package/svelte-demo-app/src/migrations/002-add-post-category.ts +37 -0
  167. package/svelte-demo-app/src/migrations/index.ts +8 -0
  168. package/svelte-demo-app/src/routes/+layout.js +3 -0
  169. package/svelte-demo-app/src/routes/+layout.svelte +228 -0
  170. package/svelte-demo-app/src/routes/+page.js +3 -0
  171. package/svelte-demo-app/src/routes/+page.svelte +1305 -0
  172. package/svelte-demo-app/src/stores/appStore.js +603 -0
  173. package/svelte-demo-app/svelte.config.js +18 -0
  174. package/svelte-demo-app/tsconfig.json +14 -0
  175. package/svelte-demo-app/vite.config.ts +6 -0
  176. package/tests/aggregation.e2e.test.ts +87 -0
  177. package/tests/base-entity.e2e.test.ts +47 -0
  178. package/tests/database-api.e2e.test.ts +177 -0
  179. package/tests/decorators.e2e.test.ts +40 -0
  180. package/tests/entity-schema.e2e.test.ts +58 -0
  181. package/tests/relation-loader-table-names.test.ts +192 -0
  182. package/tests/relations.e2e.test.ts +178 -0
  183. package/tests/zod-runtime.e2e.test.ts +69 -0
  184. package/tsconfig.json +21 -0
  185. package/vitest.config.ts +21 -0
  186. package/vitest.setup.ts +27 -0
  187. package/vue-demo-app/README.md +61 -0
  188. package/vue-demo-app/index.html +13 -0
  189. package/vue-demo-app/package-lock.json +1537 -0
  190. package/vue-demo-app/package.json +27 -0
  191. package/vue-demo-app/src/App.vue +100 -0
  192. package/vue-demo-app/src/components/Actions.vue +135 -0
  193. package/vue-demo-app/src/components/CloudSyncDemo.vue +139 -0
  194. package/vue-demo-app/src/components/LiveQueryDemo.vue +122 -0
  195. package/vue-demo-app/src/components/MainInfo.vue +80 -0
  196. package/vue-demo-app/src/components/PostsLiveQueryDemo.vue +136 -0
  197. package/vue-demo-app/src/components/TypeScriptDemo.vue +133 -0
  198. package/vue-demo-app/src/database/Database.ts +29 -0
  199. package/vue-demo-app/src/entities/Post.ts +48 -0
  200. package/vue-demo-app/src/entities/PostTag.ts +24 -0
  201. package/vue-demo-app/src/entities/Profile.ts +41 -0
  202. package/vue-demo-app/src/entities/Tag.ts +35 -0
  203. package/vue-demo-app/src/entities/User.ts +61 -0
  204. package/vue-demo-app/src/main.ts +29 -0
  205. package/vue-demo-app/src/migrations/001-add-user-email-index.ts +23 -0
  206. package/vue-demo-app/src/migrations/002-add-post-category.ts +46 -0
  207. package/vue-demo-app/src/migrations/index.ts +14 -0
  208. package/vue-demo-app/src/services/useAppLogic.ts +565 -0
  209. package/vue-demo-app/src/services/useCloudSyncDemo.ts +84 -0
  210. package/vue-demo-app/src/services/useLiveQueryDemo.ts +82 -0
  211. package/vue-demo-app/src/services/usePostsLiveQueryDemo.ts +77 -0
  212. package/vue-demo-app/src/services/useTypeScriptDemo.ts +56 -0
  213. package/vue-demo-app/src/vite-env.d.ts +1 -0
  214. package/vue-demo-app/tsconfig.json +25 -0
  215. package/vue-demo-app/tsconfig.node.json +10 -0
  216. package/vue-demo-app/vite.config.ts +16 -0
@@ -0,0 +1,237 @@
1
+ import {
2
+ getColumnMetadata, getCompoundIndexMetadata,
3
+ } from '../metadata/Column';
4
+ import { getEntityMetadata } from '../metadata/Entity';
5
+ import type { EntityConstructor } from '../types';
6
+ import { logger } from '../utils/logger';
7
+ import { getDefinedCompoundIndexes } from './EntityRegistry';
8
+
9
+ export interface TableSchema {
10
+ tableName: string;
11
+ schema: string;
12
+ }
13
+
14
+ export interface SchemaChange {
15
+ tableName: string;
16
+ changeType: 'added' | 'removed' | 'modified';
17
+ oldSchema?: string;
18
+ newSchema?: string;
19
+ }
20
+
21
+ export class SchemaBuilder {
22
+ /**
23
+ * Build indexeddb schema from entities
24
+ * @param entities - Array of entity constructors
25
+ * @returns IndexedDB schema object with table definitions
26
+ *
27
+ * @example
28
+ * const schema = SchemaBuilder.buildSchema([User, Post]);
29
+ */
30
+ static buildSchema(entities: EntityConstructor[]): Record<string, string> {
31
+ const schema: Record<string, string> = {};
32
+
33
+ schema.migrationMetadata = 'key, value, updatedAt';
34
+
35
+ entities.forEach(entity => {
36
+ const metadata = getEntityMetadata(entity);
37
+ const tableName = metadata?.tableName || entity.name.toLowerCase() + 's';
38
+
39
+ const columns = getColumnMetadata(entity);
40
+ const compoundIndexes = getDefinedCompoundIndexes(entity)
41
+ || getCompoundIndexMetadata(entity);
42
+
43
+ let primaryKeySpec = '';
44
+ const indexSpecs: string[] = [];
45
+
46
+ Object.entries(columns).forEach(([propertyKey, columnMeta]) => {
47
+ if (columnMeta.primaryKey) {
48
+ primaryKeySpec = columnMeta.autoIncrement
49
+ ? `++${propertyKey}`
50
+ : propertyKey;
51
+ } else if (columnMeta.unique) {
52
+ indexSpecs.push(`&${propertyKey}`);
53
+ } else if (columnMeta.indexed) {
54
+ indexSpecs.push(`${propertyKey}`);
55
+ }
56
+ });
57
+
58
+ const compoundIndexStrings: string[] = [];
59
+
60
+ compoundIndexes.forEach(compoundIndex => {
61
+ const indexColumns = compoundIndex.columns.join('+');
62
+ const bracketed = `[${indexColumns}]`;
63
+ const spec = compoundIndex.unique ? `&${bracketed}` : `${bracketed}`;
64
+
65
+ compoundIndexStrings.push(spec);
66
+ });
67
+
68
+ const parts = [
69
+ primaryKeySpec, ...indexSpecs, ...compoundIndexStrings,
70
+ ].filter(Boolean);
71
+
72
+ schema[tableName] = parts.join(',');
73
+
74
+ if (!schema[tableName]) {
75
+ schema[tableName] = '++id';
76
+ }
77
+ });
78
+
79
+ return schema;
80
+ }
81
+
82
+ /**
83
+ * Build detailed table schemas for comparison
84
+ * @param entities - Array of entity constructors
85
+ * @returns Array of detailed table schema objects
86
+ *
87
+ * @example
88
+ * const tables = SchemaBuilder.buildTableSchemas([User, Post]);
89
+ */
90
+ static buildTableSchemas(entities: EntityConstructor[]): TableSchema[] {
91
+ return entities.map(entity => {
92
+ const metadata = getEntityMetadata(entity);
93
+ const tableName = metadata?.tableName || entity.name.toLowerCase() + 's';
94
+ const columns = getColumnMetadata(entity);
95
+ const compoundIndexes = getCompoundIndexMetadata(entity);
96
+
97
+ let primaryKeySpec = '';
98
+ const indexSpecs: string[] = [];
99
+
100
+ Object.entries(columns).forEach(([propertyKey, columnMeta]) => {
101
+ if (columnMeta.primaryKey) {
102
+ primaryKeySpec = columnMeta.autoIncrement
103
+ ? `++${propertyKey}`
104
+ : propertyKey;
105
+ } else if (columnMeta.unique) {
106
+ indexSpecs.push(`&${propertyKey}`);
107
+ } else if (columnMeta.indexed) {
108
+ indexSpecs.push(`${propertyKey}`);
109
+ }
110
+ });
111
+
112
+ const compoundIndexStrings: string[] = [];
113
+
114
+ compoundIndexes.forEach(compoundIndex => {
115
+ const indexColumns = compoundIndex.columns.join('+');
116
+ const bracketed = `[${indexColumns}]`;
117
+ const spec = compoundIndex.unique ? `&${bracketed}` : `${bracketed}`;
118
+ compoundIndexStrings.push(spec);
119
+ });
120
+
121
+ const parts = [
122
+ primaryKeySpec, ...indexSpecs, ...compoundIndexStrings,
123
+ ].filter(Boolean);
124
+
125
+ return {
126
+ tableName,
127
+ schema: parts.join(','),
128
+ };
129
+ });
130
+ }
131
+
132
+ /**
133
+ * Generate hash for schema to detect changes
134
+ * @param schema - Schema object to hash
135
+ * @returns Hash string for schema comparison
136
+ *
137
+ * @example
138
+ * const hash = SchemaBuilder.generateSchemaHash(schema);
139
+ */
140
+ static generateSchemaHash(schema: Record<string, string>): string {
141
+ const schemaString = JSON.stringify(schema, Object.keys(schema).sort());
142
+
143
+ return btoa(schemaString).slice(0, 16);
144
+ }
145
+
146
+ /**
147
+ * Compare two schema arrays and return changes
148
+ * @param oldSchemas - Previous schema array
149
+ * @param newSchemas - Current schema array
150
+ * @returns Array of schema changes detected
151
+ *
152
+ * @example
153
+ * const changes = SchemaBuilder.compareSchemas(oldSchemas, newSchemas);
154
+ */
155
+ static compareSchemas(
156
+ oldSchemas: TableSchema[],
157
+ newSchemas: TableSchema[],
158
+ ): SchemaChange[] {
159
+ const changes: SchemaChange[] = [];
160
+ const oldSchemaMap = new Map(oldSchemas.map(s => [s.tableName, s]));
161
+ const newSchemaMap = new Map(newSchemas.map(s => [s.tableName, s]));
162
+
163
+ newSchemas.forEach(newSchema => {
164
+ if (!oldSchemaMap.has(newSchema.tableName)) {
165
+ changes.push({
166
+ tableName: newSchema.tableName,
167
+ changeType: 'added',
168
+ newSchema: newSchema.schema,
169
+ });
170
+ }
171
+ });
172
+
173
+ oldSchemas.forEach(oldSchema => {
174
+ if (!newSchemaMap.has(oldSchema.tableName)) {
175
+ changes.push({
176
+ tableName: oldSchema.tableName,
177
+ changeType: 'removed',
178
+ oldSchema: oldSchema.schema,
179
+ });
180
+ }
181
+ });
182
+
183
+ newSchemas.forEach(newSchema => {
184
+ const oldSchema = oldSchemaMap.get(newSchema.tableName);
185
+
186
+ if (oldSchema && oldSchema.schema !== newSchema.schema) {
187
+ changes.push({
188
+ tableName: newSchema.tableName,
189
+ changeType: 'modified',
190
+ oldSchema: oldSchema.schema,
191
+ newSchema: newSchema.schema,
192
+ });
193
+ }
194
+ });
195
+
196
+ return changes;
197
+ }
198
+
199
+ /**
200
+ * Store table schemas in localStorage
201
+ * @param dbName - Database name for key generation
202
+ * @param tableSchemas - Array of table schemas to store
203
+ *
204
+ * @example
205
+ * SchemaBuilder.storeTableSchemas('app-db', tables);
206
+ */
207
+ static storeTableSchemas(dbName: string, tableSchemas: TableSchema[]): void {
208
+ const key = `${dbName}_table_schemas`;
209
+ const serialized = JSON.stringify(tableSchemas);
210
+
211
+ localStorage.setItem(key, serialized);
212
+ }
213
+
214
+ /**
215
+ * Get stored table schemas from localStorage
216
+ * @param dbName - Database name for key lookup
217
+ * @returns Array of stored table schemas or empty array if none found
218
+ *
219
+ * @example
220
+ * const tables = SchemaBuilder.getStoredTableSchemas('app-db');
221
+ */
222
+ static getStoredTableSchemas(dbName: string): TableSchema[] {
223
+ const key = `${dbName}_table_schemas`;
224
+ const stored = localStorage.getItem(key);
225
+ if (!stored) {
226
+ return [];
227
+ }
228
+
229
+ try {
230
+ return JSON.parse(stored);
231
+ } catch (error) {
232
+ logger.error('Failed to parse stored table schemas:', error);
233
+
234
+ return [];
235
+ }
236
+ }
237
+ }
@@ -0,0 +1,7 @@
1
+ export { AggregationService } from './AggregationService';
2
+ export { BaseEntity } from './BaseEntity';
3
+ export { CloudSyncService } from './CloudSyncService';
4
+ export { EntitySchema } from './EntitySchema';
5
+ export { MigrationManager } from './MigrationManager';
6
+ export { RelationLoader } from './RelationLoader';
7
+ export { SchemaBuilder } from './SchemaBuilder';
package/src/types.d.ts ADDED
@@ -0,0 +1 @@
1
+ declare module 'dexie-cloud-addon';
package/src/types.ts ADDED
@@ -0,0 +1,169 @@
1
+ import type Dexie from 'dexie';
2
+ import type { Table } from 'dexie';
3
+ import { z } from 'zod';
4
+
5
+ import { BaseEntity } from './services/BaseEntity';
6
+
7
+ /**
8
+ * Constructor type for ORM entities.
9
+ */
10
+ export interface EntityConstructor<T extends BaseEntity = BaseEntity> {
11
+ new (): T;
12
+ schema?: z.ZodSchema;
13
+ tableName?: string;
14
+ }
15
+
16
+ /**
17
+ * Generic plain object shape for entity instances used in serialization.
18
+ */
19
+ export interface EntityInstance {
20
+ id?: number | string;
21
+ [key: string]: unknown;
22
+ }
23
+
24
+ /**
25
+ * Options for an entity column.
26
+ */
27
+ export interface ColumnOptions {
28
+ kind?: 'string' | 'number' | 'boolean' | 'array' | 'object';
29
+ unique?: boolean;
30
+ indexed?: boolean;
31
+ required?: boolean;
32
+ default?: unknown;
33
+ primaryKey?: boolean;
34
+ autoIncrement?: boolean;
35
+ }
36
+
37
+ /**
38
+ * Definition of a compound index across multiple columns.
39
+ */
40
+ export interface CompoundIndexOptions {
41
+ name?: string;
42
+ unique?: boolean;
43
+ columns: string[];
44
+ }
45
+
46
+ export type RelationType = 'one-to-one' | 'one-to-many' | 'many-to-many';
47
+
48
+ /**
49
+ * Configuration for a relation between two entities.
50
+ */
51
+ export interface RelationOptions {
52
+ type: RelationType;
53
+ target: EntityConstructor | string;
54
+ /** Foreign key for one-to-many and many-to-many relations. */
55
+ foreignKey?: string;
56
+ /** Name of the join table for many-to-many relations. */
57
+ joinTable?: string;
58
+ /** If true, related records are deleted automatically. */
59
+ cascade?: boolean;
60
+ /** If true, the relation is auto-loaded with the entity. */
61
+ eager?: boolean;
62
+ }
63
+
64
+ /**
65
+ * Internal metadata stored for a relation decorator.
66
+ */
67
+ export interface RelationMetadata extends RelationOptions {
68
+ propertyKey: string;
69
+ }
70
+
71
+ /**
72
+ * Options for configuring an entity class.
73
+ */
74
+ export interface EntityOptions {
75
+ tableName?: string;
76
+ schema?: z.ZodSchema;
77
+ timestamps?: boolean;
78
+ }
79
+
80
+ /**
81
+ * Result of entity validation.
82
+ */
83
+ export interface ValidationResult {
84
+ isValid: boolean;
85
+ errors: string[];
86
+ }
87
+
88
+ /**
89
+ * A single database migration step.
90
+ */
91
+ export interface Migration {
92
+ version: number;
93
+ name: string;
94
+ up: (_db: Dexie) => Promise<void>;
95
+ down?: (_db: Dexie) => Promise<void>;
96
+ }
97
+
98
+ /**
99
+ * Configuration for optional cloud synchronization.
100
+ */
101
+ export interface CloudSyncConfig {
102
+ /** Base URL of the synchronization server. */
103
+ databaseUrl: string;
104
+ /** If enabled, queue changes and retry when offline. */
105
+ enableOfflineSupport?: boolean;
106
+ /** Periodic sync interval in milliseconds. */
107
+ syncInterval?: number;
108
+ }
109
+
110
+ /**
111
+ * Configuration for creating a Database instance.
112
+ */
113
+ export interface DatabaseConfig {
114
+ /** Name of the IndexedDB database. */
115
+ name: string;
116
+ /** Schema version number. */
117
+ version: number;
118
+ /** List of registered entity classes to include in the schema. */
119
+ entities: EntityConstructor[];
120
+ /** Strategy for handling schema changes. */
121
+ onSchemaChangeStrategy?: 'selective' | 'all';
122
+ /** Optional list of migrations to execute. */
123
+ migrations?: Migration[];
124
+ /** Optional cloud synchronization configuration. */
125
+ cloudSync?: CloudSyncConfig;
126
+ }
127
+
128
+ export type TableGetter<T extends BaseEntity> = () => Table<T>;
129
+ /**
130
+ * Options for the aggregation query on a specific entity.
131
+ */
132
+ export interface AggregationOptions<T extends BaseEntity> {
133
+ /** Partial where filter applied to records before aggregation. */
134
+ where?: Partial<T>;
135
+ count?: boolean;
136
+ sum?: (keyof T)[];
137
+ avg?: (keyof T)[];
138
+ min?: (keyof T)[];
139
+ max?: (keyof T)[];
140
+ groupBy?: keyof T;
141
+ include?: string[];
142
+ limit?: number;
143
+ sort?: {
144
+ field: keyof T;
145
+ direction: 'asc' | 'desc';
146
+ };
147
+ }
148
+
149
+ /**
150
+ * Result returned by the aggregation service.
151
+ */
152
+ export interface AggregationResult {
153
+ count?: number;
154
+ sum?: Record<string, number>;
155
+ avg?: Record<string, number>;
156
+ min?: Record<string, number>;
157
+ max?: Record<string, number>;
158
+ groups?: Array<{
159
+ key: unknown;
160
+ count: number;
161
+ sum?: Record<string, number>;
162
+ avg?: Record<string, number>;
163
+ min?: Record<string, number>;
164
+ max?: Record<string, number>;
165
+ }>;
166
+ data?: unknown[];
167
+ }
168
+
169
+
@@ -0,0 +1,40 @@
1
+ import log from 'loglevel';
2
+
3
+ const isDevHost = (
4
+ typeof window !== 'undefined' &&
5
+ typeof window.location !== 'undefined' &&
6
+ (
7
+ window.location.hostname === 'localhost' ||
8
+ window.location.hostname === '127.0.0.1' ||
9
+ window.location.hostname === '0.0.0.0'
10
+ )
11
+ );
12
+
13
+ log.setLevel(isDevHost ? 'debug' : 'error');
14
+
15
+ export const createServiceLogger = (serviceName: string) => {
16
+ return {
17
+ debug: (message: string, ...args: unknown[]) =>
18
+ log.debug(
19
+ `[${serviceName}] ${message}`,
20
+ ...args,
21
+ ),
22
+ info: (message: string, ...args: unknown[]) =>
23
+ log.info(
24
+ `[${serviceName}] ${message}`,
25
+ ...args,
26
+ ),
27
+ warn: (message: string, ...args: unknown[]) =>
28
+ log.warn(
29
+ `[${serviceName}] ${message}`,
30
+ ...args,
31
+ ),
32
+ error: (message: string, ...args: unknown[]) =>
33
+ log.error(
34
+ `[${serviceName}] ${message}`,
35
+ ...args,
36
+ ),
37
+ };
38
+ };
39
+
40
+ export const logger = createServiceLogger('indexeddb-orm');
@@ -0,0 +1,61 @@
1
+ # Dexie ORM Demo App
2
+
3
+ A demonstration app showcasing the capabilities of the **Dexie ORM for IndexedDB** library.
4
+
5
+ ## Library Features
6
+
7
+ - **Entity Configuration** - define entities using `defineEntity()`
8
+ - **Automatic Schema** - automatic schema generation with primary keys and indexes
9
+ - **Relations** - entity relationships with helpers
10
+ - **Zod Validation** - runtime validation
11
+ - **Aggregations** - aggregate functions (count, avg, sum)
12
+ - **Live Queries** - reactive queries with `useLiveQuery()`
13
+ - **TypeScript** - full type safety
14
+ - **Cloud Sync** - synchronization with Dexie Cloud
15
+
16
+ ## Quick Start
17
+
18
+ ### Requirements
19
+ - Node.js 18+
20
+ - npm or yarn
21
+
22
+ ### Installation and Running
23
+
24
+ 1. **Install dependencies:**
25
+ ```bash
26
+ npm install
27
+ ```
28
+
29
+ 2. **Run in development mode:**
30
+ ```bash
31
+ npm run dev
32
+ ```
33
+
34
+ 3. **Open in browser:**
35
+ ```
36
+ http://localhost:5173
37
+ ```
38
+
39
+ ### Production Build
40
+
41
+ ```bash
42
+ npm run build
43
+ ```
44
+
45
+ ## 📱 App Features
46
+
47
+ ### Admin Tab
48
+ - **Overview** - library capabilities overview
49
+ - **Admin Actions** - function testing buttons
50
+ - **Live Query Demo** - reactive queries demonstration
51
+ - **TypeScript Demo** - type safety testing
52
+ - **Posts Demo** - advanced queries and aggregations
53
+
54
+ ### Cloud Sync Tab
55
+ - **Sync Status** - synchronization status
56
+ - **Sync Controls** - synchronization management buttons
57
+ - **Instructions** - configuration instructions
58
+
59
+ ## More Information
60
+
61
+ For more information visit our official [page](https://idb-orm.com).