@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,1725 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>IndexedDB ORM</title>
7
+ <link rel="icon" type="image/svg+xml" href="favicon.svg">
8
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet">
9
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.css" rel="stylesheet">
10
+ <style>
11
+ * {
12
+ margin: 0;
13
+ padding: 0;
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ body {
18
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
19
+ line-height: 1.6;
20
+ color: #333;
21
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
22
+ min-height: 100vh;
23
+ }
24
+
25
+ .tabs {
26
+ display: flex;
27
+ background: rgba(255, 255, 255, 0.1);
28
+ border-radius: 15px;
29
+ padding: 5px;
30
+ margin-bottom: 30px;
31
+ backdrop-filter: blur(10px);
32
+ }
33
+
34
+ .tab {
35
+ flex: 1;
36
+ padding: 15px 20px;
37
+ text-align: center;
38
+ background: transparent;
39
+ border: none;
40
+ color: white;
41
+ font-weight: 500;
42
+ border-radius: 10px;
43
+ cursor: pointer;
44
+ transition: all 0.3s ease;
45
+ }
46
+
47
+ .tab.active {
48
+ background: rgba(255, 255, 255, 0.2);
49
+ backdrop-filter: blur(10px);
50
+ }
51
+
52
+ .tab-content {
53
+ display: none;
54
+ }
55
+
56
+ .tab-content.active {
57
+ display: block;
58
+ }
59
+
60
+ .container {
61
+ max-width: 1200px;
62
+ margin: 0 auto;
63
+ padding: 20px;
64
+ }
65
+
66
+ .header {
67
+ background: rgba(255, 255, 255, 0.95);
68
+ backdrop-filter: blur(10px);
69
+ border-radius: 20px;
70
+ padding: 40px;
71
+ margin-bottom: 30px;
72
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
73
+ text-align: center;
74
+ }
75
+
76
+ .header h1 {
77
+ font-size: 3.5rem;
78
+ font-weight: 700;
79
+ background: linear-gradient(135deg, #667eea, #764ba2);
80
+ -webkit-background-clip: text;
81
+ -webkit-text-fill-color: transparent;
82
+ margin-bottom: 20px;
83
+ }
84
+
85
+ .header .subtitle {
86
+ font-size: 1.3rem;
87
+ color: #666;
88
+ margin-bottom: 30px;
89
+ }
90
+
91
+ .badges {
92
+ display: flex;
93
+ gap: 15px;
94
+ justify-content: center;
95
+ flex-wrap: wrap;
96
+ margin-bottom: 30px;
97
+ }
98
+
99
+ .badge {
100
+ background: linear-gradient(135deg, #667eea, #764ba2);
101
+ color: white;
102
+ padding: 8px 16px;
103
+ border-radius: 25px;
104
+ font-size: 0.9rem;
105
+ font-weight: 500;
106
+ text-decoration: none;
107
+ transition: transform 0.3s ease;
108
+ }
109
+
110
+ .badge:hover {
111
+ transform: translateY(-2px);
112
+ }
113
+
114
+ .install-section {
115
+ background: rgba(255, 255, 255, 0.95);
116
+ backdrop-filter: blur(10px);
117
+ border-radius: 15px;
118
+ padding: 30px;
119
+ margin-bottom: 30px;
120
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
121
+ }
122
+
123
+ .install-section h2 {
124
+ color: #333;
125
+ margin-bottom: 20px;
126
+ font-size: 2rem;
127
+ }
128
+
129
+ .code-block {
130
+ background: #2d3748;
131
+ color: #e2e8f0;
132
+ padding: 20px;
133
+ border-radius: 10px;
134
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
135
+ font-size: 0.9rem;
136
+ overflow-x: auto;
137
+ margin: 20px 0;
138
+ position: relative;
139
+ line-height: 1.5;
140
+ white-space: pre;
141
+ }
142
+
143
+ .code-block::before {
144
+ content: '';
145
+ display: none;
146
+ }
147
+
148
+ .code-block.typescript {
149
+ background: #1e1e1e;
150
+ color: #d4d4d4;
151
+ border: 1px solid #3c3c3c;
152
+ }
153
+
154
+ .code-block.typescript .keyword {
155
+ color: #569cd6;
156
+ }
157
+
158
+ .code-block.typescript .string {
159
+ color: #ce9178;
160
+ }
161
+
162
+ .code-block.typescript .comment {
163
+ color: #6a9955;
164
+ }
165
+
166
+ .code-block.typescript .function {
167
+ color: #dcdcaa;
168
+ }
169
+
170
+ .code-block.typescript .type {
171
+ color: #4ec9b0;
172
+ }
173
+
174
+ .features-grid {
175
+ display: grid;
176
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
177
+ gap: 20px;
178
+ margin: 30px 0;
179
+ }
180
+
181
+ .feature-card {
182
+ background: rgba(255, 255, 255, 0.95);
183
+ backdrop-filter: blur(10px);
184
+ border-radius: 15px;
185
+ padding: 25px;
186
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
187
+ transition: transform 0.3s ease;
188
+ }
189
+
190
+ .feature-card:hover {
191
+ transform: translateY(-5px);
192
+ }
193
+
194
+ .feature-card h3 {
195
+ color: #333;
196
+ margin-bottom: 15px;
197
+ font-size: 1.3rem;
198
+ }
199
+
200
+ .feature-card p {
201
+ color: #666;
202
+ line-height: 1.6;
203
+ }
204
+
205
+ .section {
206
+ background: rgba(255, 255, 255, 0.95);
207
+ backdrop-filter: blur(10px);
208
+ border-radius: 15px;
209
+ padding: 30px;
210
+ margin-bottom: 30px;
211
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
212
+ }
213
+
214
+ .section h2 {
215
+ color: #333;
216
+ margin-bottom: 20px;
217
+ font-size: 2rem;
218
+ border-bottom: 3px solid #667eea;
219
+ padding-bottom: 10px;
220
+ }
221
+
222
+ .demo-apps {
223
+ display: grid;
224
+ grid-template-columns: repeat(2, 1fr);
225
+ gap: 20px;
226
+ margin: 20px 0;
227
+ }
228
+
229
+ .demo-app {
230
+ background: linear-gradient(135deg, #f8fafc, #e2e8f0);
231
+ border-radius: 12px;
232
+ padding: 20px;
233
+ border-left: 4px solid #667eea;
234
+ transition: transform 0.3s ease;
235
+ }
236
+
237
+ .demo-app:hover {
238
+ transform: translateY(-3px);
239
+ }
240
+
241
+ .demo-app h4 {
242
+ color: #333;
243
+ margin-bottom: 10px;
244
+ font-size: 1.2rem;
245
+ }
246
+
247
+ .demo-app .framework {
248
+ color: #667eea;
249
+ font-weight: 600;
250
+ font-size: 0.9rem;
251
+ margin-bottom: 8px;
252
+ }
253
+
254
+ .demo-app .features {
255
+ color: #666;
256
+ font-size: 0.9rem;
257
+ margin-bottom: 15px;
258
+ }
259
+
260
+ .demo-app .run-command {
261
+ background: #2d3748;
262
+ color: #e2e8f0;
263
+ padding: 8px 12px;
264
+ border-radius: 6px;
265
+ font-family: monospace;
266
+ font-size: 0.8rem;
267
+ margin-top: 10px;
268
+ }
269
+
270
+ .demo-link {
271
+ display: inline-block;
272
+ background: linear-gradient(135deg, #667eea, #764ba2);
273
+ color: white;
274
+ padding: 6px 12px;
275
+ border-radius: 15px;
276
+ text-decoration: none;
277
+ font-size: 0.8rem;
278
+ font-weight: 500;
279
+ margin-top: 10px;
280
+ transition: transform 0.3s ease;
281
+ }
282
+
283
+ .demo-link:hover {
284
+ transform: translateY(-2px);
285
+ }
286
+
287
+ .contact-section {
288
+ background: rgba(255, 255, 255, 0.95);
289
+ backdrop-filter: blur(10px);
290
+ color: #333;
291
+ border-radius: 15px;
292
+ padding: 30px;
293
+ text-align: center;
294
+ margin-top: 30px;
295
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
296
+ }
297
+
298
+ .contact-section h2 {
299
+ margin-bottom: 0;
300
+ font-size: 2rem;
301
+ }
302
+
303
+ .contact-links {
304
+ margin-top: 20px;
305
+ }
306
+
307
+ .contact-links {
308
+ display: flex;
309
+ gap: 20px;
310
+ justify-content: center;
311
+ flex-wrap: wrap;
312
+ }
313
+
314
+ .contact-link {
315
+ background: linear-gradient(135deg, #667eea, #764ba2);
316
+ color: white;
317
+ padding: 12px 24px;
318
+ border-radius: 25px;
319
+ text-decoration: none;
320
+ font-weight: 500;
321
+ transition: all 0.3s ease;
322
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
323
+ }
324
+
325
+ .contact-link:hover {
326
+ transform: translateY(-2px);
327
+ }
328
+
329
+ .notes-section {
330
+ margin: 30px 0;
331
+ }
332
+
333
+ .note-card {
334
+ background: linear-gradient(135deg, #f8fafc, #e2e8f0);
335
+ border-radius: 12px;
336
+ padding: 20px;
337
+ margin: 15px 0;
338
+ border-left: 4px solid #667eea;
339
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
340
+ }
341
+
342
+ .note-card h4 {
343
+ color: #2d3748;
344
+ margin: 0 0 10px 0;
345
+ font-size: 1.1rem;
346
+ font-weight: 600;
347
+ }
348
+
349
+ .note-card p {
350
+ color: #4a5568;
351
+ margin: 0 0 15px 0;
352
+ line-height: 1.6;
353
+ }
354
+
355
+ .note-card code {
356
+ background: rgba(102, 126, 234, 0.1);
357
+ color: #667eea;
358
+ padding: 2px 6px;
359
+ border-radius: 4px;
360
+ font-family: 'Fira Code', 'Monaco', 'Consolas', monospace;
361
+ font-size: 0.9em;
362
+ }
363
+
364
+ .api-section {
365
+ background: rgba(255, 255, 255, 0.95);
366
+ backdrop-filter: blur(10px);
367
+ border-radius: 15px;
368
+ padding: 30px;
369
+ margin-bottom: 30px;
370
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
371
+ }
372
+
373
+ .api-method {
374
+ background: #f8fafc;
375
+ border-left: 4px solid #667eea;
376
+ padding: 15px;
377
+ margin: 10px 0;
378
+ border-radius: 0 8px 8px 0;
379
+ }
380
+
381
+ .api-method .method-name {
382
+ font-family: monospace;
383
+ font-weight: bold;
384
+ color: #667eea;
385
+ font-size: 1.1rem;
386
+ }
387
+
388
+ .api-method .method-desc {
389
+ color: #666;
390
+ margin-top: 5px;
391
+ }
392
+
393
+ .api-method .method-example {
394
+ color: #888;
395
+ font-size: 0.9rem;
396
+ margin-top: 8px;
397
+ font-style: italic;
398
+ background: #f8f9fa;
399
+ padding: 8px;
400
+ border-radius: 4px;
401
+ border-left: 3px solid #667eea;
402
+ }
403
+
404
+ @media (max-width: 768px) {
405
+ .header h1 {
406
+ font-size: 2.5rem;
407
+ }
408
+
409
+ .badges {
410
+ display: grid;
411
+ grid-template-columns: 1fr 1fr;
412
+ gap: 10px;
413
+ max-width: 300px;
414
+ margin: 0 auto;
415
+ }
416
+
417
+ .contact-links {
418
+ display: grid;
419
+ grid-template-columns: 1fr 1fr;
420
+ gap: 10px;
421
+ max-width: 300px;
422
+ margin: 0 auto;
423
+ }
424
+
425
+ .contact-links .contact-link:nth-child(3) {
426
+ grid-column: 1 / -1;
427
+ justify-self: center;
428
+ max-width: 150px;
429
+ margin-top: 10px;
430
+ }
431
+
432
+ .container {
433
+ padding: 10px;
434
+ }
435
+
436
+ .header {
437
+ padding: 20px;
438
+ margin-bottom: 20px;
439
+ }
440
+
441
+ .tabs {
442
+ margin-bottom: 20px;
443
+ position: sticky;
444
+ top: 0;
445
+ z-index: 100;
446
+ background: linear-gradient(135deg, rgba(102, 126, 234, 0.9), rgba(118, 75, 162, 0.9));
447
+ backdrop-filter: blur(15px);
448
+ border-radius: 15px;
449
+ padding: 5px;
450
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
451
+ border: 1px solid rgba(255, 255, 255, 0.3);
452
+ }
453
+
454
+ .tab {
455
+ padding: 12px 16px;
456
+ font-size: 0.9rem;
457
+ font-weight: 600;
458
+ color: white;
459
+ transition: all 0.3s ease;
460
+ }
461
+
462
+ .tab.active {
463
+ background: rgba(255, 255, 255, 0.3);
464
+ backdrop-filter: blur(10px);
465
+ color: white;
466
+ font-weight: 700;
467
+ }
468
+
469
+ .tab:hover {
470
+ background: rgba(255, 255, 255, 0.2);
471
+ color: white;
472
+ }
473
+
474
+ .section {
475
+ padding: 20px;
476
+ margin-bottom: 20px;
477
+ }
478
+
479
+ .features-grid {
480
+ grid-template-columns: 1fr;
481
+ gap: 15px;
482
+ }
483
+
484
+ .demo-apps {
485
+ grid-template-columns: 1fr;
486
+ gap: 15px;
487
+ }
488
+
489
+ .demo-app {
490
+ padding: 15px;
491
+ }
492
+
493
+ .api-method {
494
+ padding: 12px;
495
+ }
496
+
497
+ .code-block {
498
+ padding: 15px;
499
+ font-size: 0.8rem;
500
+ }
501
+ }
502
+ </style>
503
+ </head>
504
+ <body>
505
+ <div class="container">
506
+ <div class="tabs">
507
+ <button class="tab active" onclick="showTab('overview')">Overview</button>
508
+ <button class="tab" onclick="showTab('manual')">Manual</button>
509
+ <button class="tab" onclick="showTab('api')">API Reference</button>
510
+ </div>
511
+
512
+ <div class="header">
513
+ <h1>IndexedDB ORM</h1>
514
+ <p class="subtitle">TypeScript ORM wrapper for IndexedDB with Zod runtime validation, built on top of Dexie.js</p>
515
+
516
+ <div class="badges">
517
+ <a href="https://github.com/radommaciej/idb-orm" class="badge">GitHub</a>
518
+ <a href="https://www.npmjs.com/package/idb-orm" class="badge">NPM</a>
519
+ <a href="mailto:indexeddborm@gmail.com" class="badge">Contact</a>
520
+ <span class="badge">MIT License</span>
521
+ </div>
522
+ </div>
523
+
524
+ <div id="overview" class="tab-content active">
525
+ <div class="install-section">
526
+ <h2>Installation</h2>
527
+ <div class="code-block">npm install idb-orm</div>
528
+ </div>
529
+
530
+ <div class="section">
531
+ <h2>Features</h2>
532
+ <div class="features-grid">
533
+ <div class="feature-card">
534
+ <h3>TypeScript Support</h3>
535
+ <p>Full type safety with generics and IntelliSense support. Write type-safe database operations with complete IDE assistance.</p>
536
+ </div>
537
+ <div class="feature-card">
538
+ <h3>Zod Validation</h3>
539
+ <p>Runtime validation with Zod schemas. Ensure data integrity with powerful validation rules and clear error messages.</p>
540
+ </div>
541
+ <div class="feature-card">
542
+ <h3>Config-based Entities</h3>
543
+ <p>Define entities with a simple `defineEntity()` - clean, functional approach to entity definition.</p>
544
+ </div>
545
+ <div class="feature-card">
546
+ <h3>Dexie Compatibility</h3>
547
+ <p>All Dexie.js operations supported. Leverage the full power of Dexie with additional ORM capabilities.</p>
548
+ </div>
549
+ <div class="feature-card">
550
+ <h3>Clean API</h3>
551
+ <p>Simple and intuitive API design. Focus on your business logic, not database complexity.</p>
552
+ </div>
553
+ <div class="feature-card">
554
+ <h3>Relations & Aggregations</h3>
555
+ <p>Support for entity relationships and powerful aggregation operations. Build complex queries with ease.</p>
556
+ </div>
557
+ </div>
558
+ </div>
559
+
560
+ <div class="section">
561
+ <h2>Quick Start</h2>
562
+ <h3>1. Define Entities</h3>
563
+ <div class="code-block">
564
+ import { BaseEntity, defineEntity } from 'idb-orm';
565
+ import { z } from 'zod';
566
+
567
+ const UserSchema = z.object({
568
+ id: z.number().optional(),
569
+ name: z.string().min(2),
570
+ email: z.string().email(),
571
+ age: z.number().min(18)
572
+ });
573
+
574
+ export class UserEntity extends BaseEntity {
575
+ name!: string;
576
+ email!: string;
577
+ age!: number;
578
+ }
579
+
580
+ defineEntity(UserEntity, {
581
+ tableName: 'users',
582
+ schema: UserSchema,
583
+ columns: {
584
+ name: { required: true, indexed: true },
585
+ email: { required: true, unique: true },
586
+ age: { indexed: true }
587
+ }
588
+ });
589
+ </div>
590
+
591
+ <h3>2. Create Database</h3>
592
+ <div class="code-block">
593
+ import { Database } from 'idb-orm';
594
+
595
+ export const db = Database.createDatabase({
596
+ name: 'MyApp',
597
+ version: 1,
598
+ entities: [UserEntity]
599
+ });
600
+ </div>
601
+
602
+ <h3>3. Use in Your App</h3>
603
+ <div class="code-block">
604
+ // Create and save
605
+ const user = new UserEntity();
606
+ user.name = 'John Doe';
607
+ user.email = 'john@example.com';
608
+ user.age = 25;
609
+
610
+ await db.getRepository(UserEntity).add(user);
611
+
612
+ // Query with full type safety
613
+ const users = await db.getRepository(UserEntity)
614
+ .where('age')
615
+ .above(18)
616
+ .toArray();
617
+ </div>
618
+ </div>
619
+
620
+ <div class="section">
621
+ <h2>Example Applications</h2>
622
+ <p>Comprehensive demo applications showcasing all features across different frameworks:</p>
623
+
624
+ <div class="demo-apps">
625
+ <div class="demo-app">
626
+ <h4>React Demo App</h4>
627
+ <div class="framework">React 19 + TypeScript</div>
628
+ <div class="features">Material-UI, Live queries, Cloud sync, Aggregations</div>
629
+ <div class="run-command">cd react-demo-app && npm install && npm run dev</div>
630
+ <a href="https://github.com/radommaciej/idb-orm/tree/main/react-demo-app" class="demo-link">View Source</a>
631
+ </div>
632
+
633
+ <div class="demo-app">
634
+ <h4>Vue Demo App</h4>
635
+ <div class="framework">Vue 3 + TypeScript</div>
636
+ <div class="features">Vuetify 3, Reactive components, Entity management</div>
637
+ <div class="run-command">cd vue-demo-app && npm install && npm run dev</div>
638
+ <a href="https://github.com/radommaciej/idb-orm/tree/main/vue-demo-app" class="demo-link">View Source</a>
639
+ </div>
640
+
641
+ <div class="demo-app">
642
+ <h4>Svelte Demo App</h4>
643
+ <div class="framework">SvelteKit + TypeScript</div>
644
+ <div class="features">Material-UI, Server-side rendering, Live queries</div>
645
+ <div class="run-command">cd svelte-demo-app && npm install && npm run dev</div>
646
+ <a href="https://github.com/radommaciej/idb-orm/tree/main/svelte-demo-app" class="demo-link">View Source</a>
647
+ </div>
648
+
649
+ <div class="demo-app">
650
+ <h4>Angular Demo App</h4>
651
+ <div class="framework">Angular 17 + TypeScript</div>
652
+ <div class="features">Angular Material, Services, Dependency injection</div>
653
+ <div class="run-command">cd angular-demo-app && npm install && npm start</div>
654
+ <a href="https://github.com/radommaciej/idb-orm/tree/main/angular-demo-app" class="demo-link">View Source</a>
655
+ </div>
656
+ </div>
657
+ </div>
658
+ </div>
659
+
660
+ <div id="manual" class="tab-content">
661
+ <div class="section">
662
+ <h2>Complete Manual</h2>
663
+ <h3>Notes</h3>
664
+ <div class="notes-section">
665
+ <div class="note-card">
666
+ <h4>⚠️ Dexie Dependencies</h4>
667
+ <p><strong>Important:</strong> This library includes Dexie.js internally. To ensure that <code>liveQuery</code> works properly, you need to configure Vite to dedupe Dexie.</p>
668
+ <div class="code-block typescript">
669
+ <pre><code>// In your vite.config.ts, add:
670
+ import { defineConfig } from 'vite';
671
+
672
+ export default defineConfig({
673
+ resolve: {
674
+ dedupe: ['dexie'],
675
+ },
676
+ // ... other config
677
+ });
678
+
679
+ // This ensures liveQuery works correctly
680
+ // Without dedupe, liveQuery may not function properly</code></pre>
681
+ </div>
682
+ </div>
683
+
684
+ <div class="note-card">
685
+ <h4>🏗️ Built on Dexie.js</h4>
686
+ <p><strong>Foundation:</strong> This library is built on top of <a href="https://dexie.org" target="_blank" rel="noopener">Dexie.js</a> - a powerful wrapper for IndexedDB. All Dexie.js methods and features are available through the <code>db</code> object.</p>
687
+ <div class="code-block typescript">
688
+ <pre><code>// All Dexie.js methods work with our library
689
+ const db = await Database.createDatabase({...});
690
+
691
+ // Direct Dexie.js operations
692
+ await db.transaction('rw', db.users, async () => {
693
+ await db.users.add({ name: 'John', email: 'john@example.com' });
694
+ });
695
+
696
+ // Dexie.js query methods
697
+ const users = await db.users
698
+ .where('age')
699
+ .above(18)
700
+ .and(user => user.name.startsWith('J'))
701
+ .toArray();
702
+
703
+ // Dexie.js liveQuery (reactive queries)
704
+ import { liveQuery } from 'dexie';
705
+ const observableUsers = liveQuery(() =>
706
+ db.users.where('active').equals(true).toArray()
707
+ );</code></pre>
708
+ </div>
709
+ <p><strong>Benefits:</strong> You get all the power of Dexie.js plus our ORM features like entity validation, relations, and cloud sync.</p>
710
+ </div>
711
+
712
+ <div class="note-card">
713
+ <h4>🔧 Cloud Sync Setup Steps</h4>
714
+ <p>To enable cloud synchronization, you need to complete several setup steps:</p>
715
+ <div class="code-block typescript">
716
+ <pre><code>// 1. Install Dexie Cloud addon
717
+ npm install dexie-cloud-addon
718
+
719
+ // 2. Configure your database with cloud sync
720
+ const db = await Database.createDatabase({
721
+ name: 'MyAppDB',
722
+ version: 1,
723
+ entities: [User, Post],
724
+ cloudSync: {
725
+ appId: 'your-app-id',
726
+ serverUrl: 'https://your-server.com',
727
+ // ... other cloud config
728
+ }
729
+ });
730
+
731
+ // 3. Enable cloud sync after database creation
732
+ await db.enableCloudSync();</code></pre>
733
+ </div>
734
+ </div>
735
+
736
+ <div class="note-card">
737
+ <h4>📊 Index Requirements for Queries</h4>
738
+ <p><strong>Critical:</strong> When searching by any field, you MUST create an index on that field. Queries without proper indexes will fail.</p>
739
+ <div class="code-block typescript">
740
+ const UserSchema = z.object({
741
+ id: z.number(),
742
+ name: z.string(),
743
+ email: z.string(),
744
+ age: z.number()
745
+ });
746
+
747
+ defineEntity(User, {
748
+ schema: UserSchema,
749
+ indexes: [
750
+ { key: 'email' }, // Required for email queries
751
+ { key: 'age' }, // Required for age queries
752
+ { key: 'name' } // Required for name queries
753
+ ]
754
+ });</code></pre>
755
+ </div>
756
+ </div>
757
+
758
+ <div class="note-card">
759
+ <h4>🔄 Schema Changes & Migrations</h4>
760
+ <p>When changing entity schemas, you need to handle migrations properly. The library provides two strategies for schema changes.</p>
761
+ <div class="code-block typescript">
762
+ <pre><code>// Strategy 1: Selective reset (recommended)
763
+ const db = await Database.createDatabase({
764
+ name: 'MyAppDB',
765
+ version: 2, // Increment version
766
+ entities: [User, Post],
767
+ onSchemaChangeStrategy: 'selective' // Only affected tables are reset
768
+ });
769
+
770
+ // Strategy 2: Full reset (use with caution)
771
+ onSchemaChangeStrategy: 'all' // All data is lost</code></pre>
772
+ </div>
773
+ </div>
774
+
775
+ <div class="note-card">
776
+ <h4>🔍 Compound Indexes for Complex Queries</h4>
777
+ <p>For queries involving multiple fields, you need compound indexes. Single field indexes won't work for multi-field queries.</p>
778
+ <div class="code-block typescript">
779
+ defineEntity(Post, {
780
+ schema: PostSchema,
781
+ compoundIndexes: [
782
+ { key: ['category', 'status'] },
783
+ { key: ['authorId', 'createdAt'] }
784
+ ]
785
+ });</code></pre>
786
+ </div>
787
+ </div>
788
+
789
+ </div>
790
+
791
+ <h3>Usage</h3>
792
+ <h4>1. Define Entities</h4>
793
+ <div class="code-block typescript">
794
+ import { BaseEntity, defineEntity } from 'idb-orm';
795
+ import { z } from 'zod';
796
+
797
+ // Zod schema for validation
798
+ const UserSchema = z.object({
799
+ id: z.number().optional(),
800
+ name: z.string().min(2, 'Name must be at least 2 characters'),
801
+ email: z.string().email('Invalid email format'),
802
+ age: z.number().min(18, 'Must be at least 18 years old'),
803
+ createdAt: z.number(),
804
+ updatedAt: z.number(),
805
+ isActive: z.boolean().default(true),
806
+ tags: z.array(z.string()).default([]),
807
+ metadata: z.record(z.string(), z.unknown()).default({}),
808
+ });
809
+
810
+ export class UserEntity extends BaseEntity {
811
+ name!: string;
812
+ email!: string;
813
+ age!: number;
814
+ createdAt!: number;
815
+ updatedAt!: number;
816
+ isActive!: boolean;
817
+ tags!: string[];
818
+ metadata!: Record<string, unknown>;
819
+ }
820
+
821
+ defineEntity(UserEntity, {
822
+ tableName: 'users',
823
+ schema: UserSchema,
824
+ columns: {
825
+ name: { required: true, indexed: true },
826
+ email: { required: true, unique: true },
827
+ age: { indexed: true },
828
+ createdAt: { indexed: true },
829
+ updatedAt: { indexed: true },
830
+ isActive: { indexed: true },
831
+ tags: {},
832
+ metadata: {},
833
+ },
834
+ });
835
+ </div>
836
+
837
+ <h4>2. Create Database</h4>
838
+ <div class="code-block typescript">
839
+ import { Database } from 'idb-orm';
840
+ import { UserEntity } from './entities/User';
841
+ import { PostEntity } from './entities/Post';
842
+
843
+ export const db = Database.createDatabase({
844
+ name: 'MyApp',
845
+ version: 1,
846
+ entities: [UserEntity, PostEntity],
847
+ config: {
848
+ onSchemaChangeStrategy: 'all',
849
+ cloudSync: {
850
+ databaseUrl: 'https://your-sync-server.com',
851
+ enableOfflineSupport: true,
852
+ syncInterval: 30000
853
+ }
854
+ },
855
+ });
856
+ </div>
857
+
858
+ <h4>3. Basic CRUD Operations</h4>
859
+ <div class="code-block typescript">
860
+ // Create new entity with validation in one call
861
+ import { newEntity } from 'idb-orm';
862
+
863
+ const newUser = newEntity(UserEntity, {
864
+ name: 'John Doe',
865
+ email: 'john@example.com',
866
+ age: 25,
867
+ createdAt: Date.now(),
868
+ updatedAt: Date.now(),
869
+ tags: ['developer'],
870
+ metadata: { source: 'manual' },
871
+ });
872
+
873
+ // Save to database
874
+ await db.getRepository(UserEntity).add(newUser);
875
+
876
+ // Query with full Dexie.js API
877
+ const activeUsers = await db.getRepository(UserEntity)
878
+ .where('isActive')
879
+ .equals(true)
880
+ .toArray();
881
+ </div>
882
+
883
+ <h4>4. Entity Relations</h4>
884
+ <div class="code-block typescript">
885
+ // Define entities with relations
886
+ defineEntity(UserEntity, {
887
+ tableName: 'users',
888
+ schema: UserSchema,
889
+ columns: { /* ... */ },
890
+ relations: {
891
+ posts: {
892
+ type: 'one-to-many',
893
+ target: PostEntity,
894
+ foreignKey: 'authorId'
895
+ },
896
+ profile: {
897
+ type: 'one-to-one',
898
+ target: ProfileEntity,
899
+ foreignKey: 'userId'
900
+ }
901
+ }
902
+ });
903
+
904
+ // Load relations
905
+ const userWithPosts = await db.loadRelations({
906
+ entity: user,
907
+ entityClass: UserEntity,
908
+ relationNames: ['posts', 'profile']
909
+ });
910
+
911
+ // Load specific relation
912
+ const userPosts = await db.loadRelationByName({
913
+ entity: user,
914
+ entityClass: UserEntity,
915
+ relationName: 'posts'
916
+ });
917
+ </div>
918
+
919
+ <h4>5. Reactive Queries with liveQuery</h4>
920
+ <p>Use Dexie's <code>liveQuery</code> for reactive data that automatically updates when the database changes. Works with all major frameworks:</p>
921
+
922
+ <h5>React Example</h5>
923
+ <div class="code-block typescript">
924
+ <pre><code>import { liveQuery } from 'dexie';
925
+ import { useObservable } from 'dexie-react-hooks';
926
+ import { Database } from 'indexed-db-orm';
927
+ import { User } from './entities/User';
928
+
929
+ function UserList() {
930
+ const users = useObservable(
931
+ liveQuery(() => {
932
+ const userRepo = db.getRepository(User);
933
+ return userRepo.where('active').equals(true).toArray();
934
+ })
935
+ );
936
+
937
+ return (
938
+ <div>
939
+ {users?.map(user => (
940
+ <div key={user.id}>{user.name}</div>
941
+ ))}
942
+ </div>
943
+ );
944
+ }</code></pre>
945
+ </div>
946
+
947
+ <h5>Vue Example</h5>
948
+ <div class="code-block typescript">
949
+ <pre><code>import { liveQuery } from 'dexie';
950
+ import { ref, onMounted, onUnmounted } from 'vue';
951
+ import { Database } from 'indexed-db-orm';
952
+ import { User } from './entities/User';
953
+
954
+ export default {
955
+ setup() {
956
+ const users = ref<User[]>([]);
957
+ let subscription;
958
+
959
+ onMounted(() => {
960
+ subscription = liveQuery(() => {
961
+ const userRepo = db.getRepository(User);
962
+ return userRepo.where('active').equals(true).toArray();
963
+ }).subscribe(result => {
964
+ users.value = result;
965
+ });
966
+ });
967
+
968
+ onUnmounted(() => {
969
+ subscription?.unsubscribe();
970
+ });
971
+
972
+ return { users };
973
+ }
974
+ };</code></pre>
975
+ </div>
976
+
977
+ <h5>Svelte Example</h5>
978
+ <div class="code-block typescript">
979
+ <pre><code>import { liveQuery } from 'dexie';
980
+ import { onMount, onDestroy } from 'svelte';
981
+ import { Database } from 'indexed-db-orm';
982
+ import { User } from './entities/User';
983
+
984
+ let users: User[] = [];
985
+ let subscription;
986
+
987
+ onMount(() => {
988
+ subscription = liveQuery(() => {
989
+ const userRepo = db.getRepository(User);
990
+ return userRepo.where('active').equals(true).toArray();
991
+ }).subscribe(result => {
992
+ users = result;
993
+ });
994
+ });
995
+
996
+ onDestroy(() => {
997
+ subscription?.unsubscribe();
998
+ });</code></pre>
999
+ </div>
1000
+
1001
+ <h5>Angular Example</h5>
1002
+ <div class="code-block typescript">
1003
+ <pre><code>import { liveQuery } from 'dexie';
1004
+ import { Component, OnInit, OnDestroy } from '@angular/core';
1005
+ import { Subscription } from 'rxjs';
1006
+ import { Database } from 'indexed-db-orm';
1007
+ import { User } from './entities/User';
1008
+
1009
+ @Component({
1010
+ selector: 'app-user-list',
1011
+ template: '<div *ngFor="let user of users">{{user.name}}</div>'
1012
+ })
1013
+ export class UserListComponent implements OnInit, OnDestroy {
1014
+ users: User[] = [];
1015
+ private subscription?: Subscription;
1016
+
1017
+ ngOnInit() {
1018
+ this.subscription = liveQuery(() => {
1019
+ const userRepo = db.getRepository(User);
1020
+ return userRepo.where('active').equals(true).toArray();
1021
+ }).subscribe(result => {
1022
+ this.users = result;
1023
+ });
1024
+ }
1025
+
1026
+ ngOnDestroy() {
1027
+ this.subscription?.unsubscribe();
1028
+ }
1029
+ }</code></pre>
1030
+ </div>
1031
+
1032
+ <h4>6. Advanced Queries and Aggregation</h4>
1033
+ <div class="code-block typescript">
1034
+ // Aggregation operations
1035
+ const result = await db.aggregate({
1036
+ entityClass: PostEntity,
1037
+ options: {
1038
+ where: { category: 'tech' },
1039
+ sort: { field: 'views', direction: 'desc' },
1040
+ limit: 10,
1041
+ count: true,
1042
+ sum: ['views'],
1043
+ avg: ['likes'],
1044
+ groupBy: ['category']
1045
+ }
1046
+ });
1047
+
1048
+ // Complex filtering and sorting
1049
+ const topPosts = await db.getRepository(PostEntity)
1050
+ .where('published')
1051
+ .equals(true)
1052
+ .and(post => post.views > 100)
1053
+ .orderBy('views')
1054
+ .reverse()
1055
+ .limit(5)
1056
+ .toArray();
1057
+ </div>
1058
+
1059
+ <h4>6. Entity Validation</h4>
1060
+ <div class="code-block typescript">
1061
+ // Manual validation
1062
+ const user = new UserEntity();
1063
+ user.name = 'A'; // Too short
1064
+ user.email = 'invalid-email'; // Invalid format
1065
+
1066
+ const result = user.validate();
1067
+ if (!result.isValid) {
1068
+ console.log(result.errors);
1069
+ // ['name: Name must be at least 2 characters', 'email: Invalid email format']
1070
+ }
1071
+
1072
+ // Validation with error throwing
1073
+ try {
1074
+ user.validateOrThrow();
1075
+ } catch (error) {
1076
+ console.log('Validation failed:', error.message);
1077
+ }
1078
+
1079
+ // Initialize with validation
1080
+ const validUser = new UserEntity().init({
1081
+ name: 'John Doe',
1082
+ email: 'john@example.com',
1083
+ age: 25
1084
+ });
1085
+ </div>
1086
+
1087
+ <h4>7. Database Management</h4>
1088
+ <div class="code-block typescript">
1089
+ // Check for schema changes
1090
+ const hasChanges = await db.checkSchemaChanges();
1091
+ if (hasChanges) {
1092
+ await db.performSelectiveReset(); // Reset only changed tables
1093
+ // or
1094
+ await db.resetDatabase(); // Reset entire database
1095
+ }
1096
+
1097
+ // Clear all data
1098
+ await db.clearAllData();
1099
+
1100
+ // Run migrations
1101
+ await db.runMigrations([
1102
+ {
1103
+ version: 2,
1104
+ name: 'Add user profiles',
1105
+ up: async (db) => {
1106
+ // Migration logic for version 2
1107
+ }
1108
+ }
1109
+ ]);
1110
+ </div>
1111
+
1112
+ <h4>8. Cloud Synchronization</h4>
1113
+ <div class="code-block typescript">
1114
+ // Enable cloud sync
1115
+ await db.enableCloudSync({
1116
+ databaseUrl: 'https://your-sync-server.com',
1117
+ enableOfflineSupport: true,
1118
+ syncInterval: 30000
1119
+ });
1120
+
1121
+ // Manual sync
1122
+ await db.sync();
1123
+
1124
+ // Check sync status
1125
+ const status = db.getSyncStatus();
1126
+ console.log('Sync enabled:', status.enabled);
1127
+ console.log('Last sync:', status.lastSync);
1128
+
1129
+ // Sync specific tables
1130
+ await db.syncTables(['users', 'posts']);
1131
+
1132
+ // Disable cloud sync
1133
+ db.disableCloudSync();
1134
+ </div>
1135
+
1136
+ <h4>9. Compound Indexes</h4>
1137
+ <div class="code-block typescript">
1138
+ // Define entity with compound indexes
1139
+ defineEntity(UserEntity, {
1140
+ tableName: 'users',
1141
+ schema: UserSchema,
1142
+ columns: {
1143
+ name: { indexed: true },
1144
+ email: { indexed: true, unique: true },
1145
+ age: { indexed: true },
1146
+ isActive: { indexed: true },
1147
+ },
1148
+ compoundIndexes: [
1149
+ {
1150
+ columns: ['name', 'email'],
1151
+ unique: false,
1152
+ name: 'name_email_index'
1153
+ },
1154
+ {
1155
+ columns: ['age', 'isActive'],
1156
+ unique: false,
1157
+ name: 'age_active_index'
1158
+ }
1159
+ ]
1160
+ });
1161
+
1162
+ // Query using compound index
1163
+ const users = await db.getRepository(UserEntity)
1164
+ .where(['name', 'email'])
1165
+ .equals(['John Doe', 'john@example.com'])
1166
+ .toArray();
1167
+
1168
+ const activeAdults = await db.getRepository(UserEntity)
1169
+ .where(['age', 'isActive'])
1170
+ .above([18, 1])
1171
+ .toArray();
1172
+ </div>
1173
+ </div>
1174
+ </div>
1175
+
1176
+ <div id="api" class="tab-content">
1177
+ <div class="api-section">
1178
+ <h2>Complete API Reference</h2>
1179
+ <p>Complete reference of all methods with JSDoc documentation from the library.</p>
1180
+
1181
+ <h3>Core Classes</h3>
1182
+
1183
+ <h4>Database</h4>
1184
+ <p>Main database class extending Dexie with ORM capabilities.</p>
1185
+
1186
+ <h5>Static Methods:</h5>
1187
+ <div class="api-method">
1188
+ <div class="method-name">Database.createDatabase(params)</div>
1189
+ <div class="method-desc">Create database with entity registration</div>
1190
+ <div class="method-example">const db = Database.createDatabase({ name: 'app-db', version: 1, entities: [User, Post] });</div>
1191
+ </div>
1192
+
1193
+ <h5>Instance Methods:</h5>
1194
+ <div class="api-method">
1195
+ <div class="method-name">getRepository&lt;T&gt;(entityClass)</div>
1196
+ <div class="method-desc">Get repository for entity (TypeORM style)</div>
1197
+ <div class="method-example">const users = db.getRepository(User); const id = await users.add({ name: 'Ann' } as User);</div>
1198
+ </div>
1199
+ <div class="api-method">
1200
+ <div class="method-name">aggregate&lt;T&gt;(params)</div>
1201
+ <div class="method-desc">Perform aggregation on entity data</div>
1202
+ <div class="method-example">const result = await db.aggregate({ entityClass: Post, options: { where: { category: 'tech' } } });</div>
1203
+ </div>
1204
+ <div class="api-method">
1205
+ <div class="method-name">clearAllData()</div>
1206
+ <div class="method-desc">Clear all data from database</div>
1207
+ <div class="method-example">await db.clearAllData();</div>
1208
+ </div>
1209
+ <div class="api-method">
1210
+ <div class="method-name">resetDatabase()</div>
1211
+ <div class="method-desc">Reset database when schema changes</div>
1212
+ <div class="method-example">await db.resetDatabase();</div>
1213
+ </div>
1214
+ <div class="api-method">
1215
+ <div class="method-name">checkSchemaChanges()</div>
1216
+ <div class="method-desc">Check if schema has changed and reset if needed</div>
1217
+ <div class="method-example">const changed = await db.checkSchemaChanges();</div>
1218
+ </div>
1219
+ <div class="api-method">
1220
+ <div class="method-name">performSelectiveReset()</div>
1221
+ <div class="method-desc">Manually perform selective reset for changed tables only</div>
1222
+ <div class="method-example">await db.performSelectiveReset();</div>
1223
+ </div>
1224
+ <div class="api-method">
1225
+ <div class="method-name">runMigrations(migrations)</div>
1226
+ <div class="method-desc">Run migrations for schema changes</div>
1227
+ <div class="method-example">await db.runMigrations(migrations);</div>
1228
+ </div>
1229
+ <div class="api-method">
1230
+ <div class="method-name">getTypedTable&lt;T&gt;(entityClass)</div>
1231
+ <div class="method-desc">Get typed table for entity</div>
1232
+ <div class="method-example">const posts = db.getTypedTable(Post);</div>
1233
+ </div>
1234
+ <div class="api-method">
1235
+ <div class="method-name">getTableForEntity&lt;T&gt;(entityClass)</div>
1236
+ <div class="method-desc">Get table with proper typing for specific entity</div>
1237
+ <div class="method-example">const users = db.getTableForEntity(User);</div>
1238
+ </div>
1239
+ <div class="api-method">
1240
+ <div class="method-name">getEntities()</div>
1241
+ <div class="method-desc">Get all entities</div>
1242
+ <div class="method-example">const all = db.getEntities();</div>
1243
+ </div>
1244
+ <div class="api-method">
1245
+ <div class="method-name">getEntity(tableName)</div>
1246
+ <div class="method-desc">Get entity by table name</div>
1247
+ <div class="method-example">const UserClass = db.getEntity('users');</div>
1248
+ </div>
1249
+ <div class="api-method">
1250
+ <div class="method-name">loadRelations&lt;T&gt;(params)</div>
1251
+ <div class="method-desc">Load relations for an entity</div>
1252
+ <div class="method-example">const userWithRelations = await db.loadRelations({ entity: user, entityClass: User, relationNames: ['posts'] });</div>
1253
+ </div>
1254
+ <div class="api-method">
1255
+ <div class="method-name">loadRelationByName&lt;T, K&gt;(params)</div>
1256
+ <div class="method-desc">Load a specific relation by name</div>
1257
+ <div class="method-example">const posts = await db.loadRelationByName({ entity: { id: userId }, entityClass: User, relationName: 'posts' });</div>
1258
+ </div>
1259
+ <div class="api-method">
1260
+ <div class="method-name">saveWithRelations&lt;T&gt;(params)</div>
1261
+ <div class="method-desc">Save entity with relations</div>
1262
+ <div class="method-example">const saved = await db.saveWithRelations({ entity: user, entityClass: User });</div>
1263
+ </div>
1264
+ <div class="api-method">
1265
+ <div class="method-name">deleteWithRelations&lt;T&gt;(params)</div>
1266
+ <div class="method-desc">Delete entity with cascade handling for relations</div>
1267
+ <div class="method-example">await db.deleteWithRelations({ entity: user, entityClass: User });</div>
1268
+ </div>
1269
+ <div class="api-method">
1270
+ <div class="method-name">sync()</div>
1271
+ <div class="method-desc">Manual sync with cloud</div>
1272
+ </div>
1273
+ <div class="api-method">
1274
+ <div class="method-name">getSyncStatus()</div>
1275
+ <div class="method-desc">Get sync status</div>
1276
+ </div>
1277
+ <div class="api-method">
1278
+ <div class="method-name">enableCloudSync(config)</div>
1279
+ <div class="method-desc">Enable cloud sync (if not already enabled)</div>
1280
+ </div>
1281
+ <div class="api-method">
1282
+ <div class="method-name">disableCloudSync()</div>
1283
+ <div class="method-desc">Disable cloud sync</div>
1284
+ </div>
1285
+ <div class="api-method">
1286
+ <div class="method-name">isCloudSyncEnabled()</div>
1287
+ <div class="method-desc">Check if cloud sync is enabled</div>
1288
+ </div>
1289
+ <div class="api-method">
1290
+ <div class="method-name">getCloudSyncConfig()</div>
1291
+ <div class="method-desc">Get cloud sync configuration</div>
1292
+ </div>
1293
+ <div class="api-method">
1294
+ <div class="method-name">syncTables(tableNames)</div>
1295
+ <div class="method-desc">Force sync specific tables</div>
1296
+ </div>
1297
+
1298
+ <h4>BaseEntity&lt;TKey&gt;</h4>
1299
+ <p>Base class for all entities with validation capabilities.</p>
1300
+
1301
+ <div class="api-method">
1302
+ <div class="method-name">validate(): ValidationResult</div>
1303
+ <div class="method-desc">Validate entity data against its schema</div>
1304
+ <div class="method-example">const result = user.validate(); if (!result.isValid) console.error(result.errors);</div>
1305
+ </div>
1306
+ <div class="api-method">
1307
+ <div class="method-name">validateOrThrow(): void</div>
1308
+ <div class="method-desc">Validate entity data and throw error if invalid</div>
1309
+ <div class="method-example">user.validateOrThrow(); // throws ValidationError on invalid data</div>
1310
+ </div>
1311
+ <div class="api-method">
1312
+ <div class="method-name">init(data): this</div>
1313
+ <div class="method-desc">Initialize entity with data and validate</div>
1314
+ <div class="method-example">const user = new User().init({ name: 'Alice' });</div>
1315
+ </div>
1316
+
1317
+ <h4>EntitySchema&lt;T&gt;</h4>
1318
+ <p>Schema management for entities.</p>
1319
+
1320
+ <div class="api-method">
1321
+ <div class="method-name">getTableName(): string</div>
1322
+ <div class="method-desc">Get the table name for this entity</div>
1323
+ <div class="method-example">const schema = new EntitySchema(User); const table = schema.getTableName(); // 'users'</div>
1324
+ </div>
1325
+ <div class="api-method">
1326
+ <div class="method-name">getSchema(): ZodSchema | undefined</div>
1327
+ <div class="method-desc">Get the Zod schema for this entity</div>
1328
+ <div class="method-example">const schema = new EntitySchema(User, userZodSchema); const zod = schema.getSchema();</div>
1329
+ </div>
1330
+ <div class="api-method">
1331
+ <div class="method-name">getColumns(): Record&lt;string, unknown&gt;</div>
1332
+ <div class="method-desc">Get column metadata for this entity</div>
1333
+ <div class="method-example">const cols = new EntitySchema(User).getColumns(); // cols.name.required === true</div>
1334
+ </div>
1335
+ <div class="api-method">
1336
+ <div class="method-name">validate(data): ValidationResult</div>
1337
+ <div class="method-desc">Validate data against entity schema</div>
1338
+ <div class="method-example">const { isValid, errors } = new EntitySchema(User, userSchema).validate({ name: 'Alice' });</div>
1339
+ </div>
1340
+ <div class="api-method">
1341
+ <div class="method-name">create(data?): T</div>
1342
+ <div class="method-desc">Create a new instance of the entity</div>
1343
+ <div class="method-example">const user = new EntitySchema(User).create({ name: 'Bob' });</div>
1344
+ </div>
1345
+
1346
+ <h4>AggregationService</h4>
1347
+ <p>Service for performing aggregation operations on entity data.</p>
1348
+
1349
+ <div class="api-method">
1350
+ <div class="method-name">aggregate&lt;T&gt;(entityClass, options)</div>
1351
+ <div class="method-desc">Perform aggregation operations on entity data</div>
1352
+ <div class="method-example">const result = await aggregationService.aggregate(Post, { where: { category: 'tech' }, sort: { field: 'views', direction: 'desc' }, limit: 10, count: true, sum: ['views'] });</div>
1353
+ </div>
1354
+ <div class="api-method">
1355
+ <div class="method-name">loadRelation(entity, relationMeta)</div>
1356
+ <div class="method-desc">Load a specific relation based on metadata</div>
1357
+ <div class="method-example">const related = await (aggregationService as any).loadRelation(record, { type: 'one-to-many', target: Post, foreignKey: 'authorId' });</div>
1358
+ </div>
1359
+
1360
+ <h4>RelationLoader</h4>
1361
+ <p>Service for loading and saving entity relations.</p>
1362
+
1363
+ <div class="api-method">
1364
+ <div class="method-name">loadRelations&lt;T&gt;(entity, entityClass, relationNames?)</div>
1365
+ <div class="method-desc">Load relations for an entity</div>
1366
+ </div>
1367
+ <div class="api-method">
1368
+ <div class="method-name">loadRelationByName&lt;T, K&gt;(entity, entityClass, relationName)</div>
1369
+ <div class="method-desc">Load a specific relation by name</div>
1370
+ </div>
1371
+ <div class="api-method">
1372
+ <div class="method-name">loadRelation&lt;T&gt;(entity, relationMeta)</div>
1373
+ <div class="method-desc">Load a specific relation based on metadata</div>
1374
+ </div>
1375
+ <div class="api-method">
1376
+ <div class="method-name">saveWithRelations&lt;T&gt;(entity, entityClass)</div>
1377
+ <div class="method-desc">Save entity with all its relations</div>
1378
+ </div>
1379
+ <div class="api-method">
1380
+ <div class="method-name">deleteWithRelations&lt;T&gt;(entity, entityClass)</div>
1381
+ <div class="method-desc">Delete entity with cascade handling for relations</div>
1382
+ </div>
1383
+
1384
+ <h4>SchemaBuilder</h4>
1385
+ <p>Service for building IndexedDB schemas from entities.</p>
1386
+
1387
+ <div class="api-method">
1388
+ <div class="method-name">buildSchema(entities)</div>
1389
+ <div class="method-desc">Build indexeddb schema from entities</div>
1390
+ <div class="method-example">const schema = SchemaBuilder.buildSchema([User, Post]);</div>
1391
+ </div>
1392
+ <div class="api-method">
1393
+ <div class="method-name">buildTableSchemas(entities)</div>
1394
+ <div class="method-desc">Build detailed table schemas for comparison</div>
1395
+ <div class="method-example">const tables = SchemaBuilder.buildTableSchemas([User, Post]);</div>
1396
+ </div>
1397
+ <div class="api-method">
1398
+ <div class="method-name">generateSchemaHash(schema)</div>
1399
+ <div class="method-desc">Generate hash for schema to detect changes</div>
1400
+ <div class="method-example">const hash = SchemaBuilder.generateSchemaHash(schema);</div>
1401
+ </div>
1402
+ <div class="api-method">
1403
+ <div class="method-name">compareSchemas(oldSchemas, newSchemas)</div>
1404
+ <div class="method-desc">Compare two schema arrays and return changes</div>
1405
+ <div class="method-example">const changes = SchemaBuilder.compareSchemas(oldSchemas, newSchemas);</div>
1406
+ </div>
1407
+ <div class="api-method">
1408
+ <div class="method-name">storeTableSchemas(dbName, tableSchemas)</div>
1409
+ <div class="method-desc">Store table schemas in localStorage</div>
1410
+ <div class="method-example">SchemaBuilder.storeTableSchemas('app-db', tables);</div>
1411
+ </div>
1412
+ <div class="api-method">
1413
+ <div class="method-name">getStoredTableSchemas(dbName)</div>
1414
+ <div class="method-desc">Get stored table schemas from localStorage</div>
1415
+ <div class="method-example">const tables = SchemaBuilder.getStoredTableSchemas('app-db');</div>
1416
+ </div>
1417
+
1418
+ <h4>CloudSyncService</h4>
1419
+ <p>Service for cloud synchronization functionalities.</p>
1420
+
1421
+ <div class="api-method">
1422
+ <div class="method-name">initializeCloudSync(config)</div>
1423
+ <div class="method-desc">Initialize cloud synchronization with configuration</div>
1424
+ <div class="method-example">await cloudService.initializeCloudSync({ databaseUrl: 'https://example.cloud', enableOfflineSupport: true, syncInterval: 30000 });</div>
1425
+ </div>
1426
+ <div class="api-method">
1427
+ <div class="method-name">stopPeriodicSync()</div>
1428
+ <div class="method-desc">Stop periodic synchronization</div>
1429
+ <div class="method-example">cloudService.stopPeriodicSync();</div>
1430
+ </div>
1431
+ <div class="api-method">
1432
+ <div class="method-name">sync()</div>
1433
+ <div class="method-desc">Perform manual synchronization</div>
1434
+ <div class="method-example">await cloudService.sync();</div>
1435
+ </div>
1436
+ <div class="api-method">
1437
+ <div class="method-name">getSyncStatus()</div>
1438
+ <div class="method-desc">Get current synchronization status</div>
1439
+ <div class="method-example">const { enabled, lastSync, isOnline } = cloudService.getSyncStatus();</div>
1440
+ </div>
1441
+ <div class="api-method">
1442
+ <div class="method-name">enableCloudSync(config)</div>
1443
+ <div class="method-desc">Enable cloud synchronization</div>
1444
+ <div class="method-example">await cloudService.enableCloudSync({ databaseUrl: 'https://example.cloud' });</div>
1445
+ </div>
1446
+ <div class="api-method">
1447
+ <div class="method-name">disableCloudSync()</div>
1448
+ <div class="method-desc">Disable cloud synchronization</div>
1449
+ <div class="method-example">cloudService.disableCloudSync();</div>
1450
+ </div>
1451
+ <div class="api-method">
1452
+ <div class="method-name">isCloudSyncEnabled()</div>
1453
+ <div class="method-desc">Check if cloud sync is currently enabled</div>
1454
+ <div class="method-example">if (cloudService.isCloudSyncEnabled()) { // ... }</div>
1455
+ </div>
1456
+ <div class="api-method">
1457
+ <div class="method-name">getCloudSyncConfig()</div>
1458
+ <div class="method-desc">Get current cloud sync configuration</div>
1459
+ <div class="method-example">const cfg = cloudService.getCloudSyncConfig();</div>
1460
+ </div>
1461
+ <div class="api-method">
1462
+ <div class="method-name">syncTables(tableNames)</div>
1463
+ <div class="method-desc">Synchronize specific tables</div>
1464
+ <div class="method-example">await cloudService.syncTables(['users', 'posts']);</div>
1465
+ </div>
1466
+
1467
+ <h4>MigrationManager</h4>
1468
+ <p>Service for managing database migrations.</p>
1469
+
1470
+ <div class="api-method">
1471
+ <div class="method-name">runMigrations(migrations, db)</div>
1472
+ <div class="method-desc">Run database migrations</div>
1473
+ </div>
1474
+ <div class="api-method">
1475
+ <div class="method-name">performSelectiveReset(entities, db)</div>
1476
+ <div class="method-desc">Perform selective reset for changed tables only</div>
1477
+ </div>
1478
+ <div class="api-method">
1479
+ <div class="method-name">resetDatabase(db)</div>
1480
+ <div class="method-desc">Reset entire database by clearing all data</div>
1481
+ </div>
1482
+ <div class="api-method">
1483
+ <div class="method-name">checkSchemaChanges(entities, db)</div>
1484
+ <div class="method-desc">Check if schema has changed and reset database if needed</div>
1485
+ </div>
1486
+
1487
+ <h3>Entity Definition</h3>
1488
+
1489
+ <h4>defineEntity(EntityClass, options)</h4>
1490
+ <p>Define entity metadata without decorators. Registers table name, schema, columns, indexes and relations for a given class so the ORM can work without TypeScript decorators.</p>
1491
+
1492
+ <h5>Parameters:</h5>
1493
+ <ul>
1494
+ <li><strong>EntityClass</strong> - The entity constructor/class</li>
1495
+ <li><strong>options</strong> - Metadata describing table, schema, columns, indexes and relations</li>
1496
+ </ul>
1497
+
1498
+ <h5>Options:</h5>
1499
+ <div class="code-block typescript">
1500
+ interface DefineEntityOptions {
1501
+ tableName?: string; // Custom table name
1502
+ schema?: ZodSchema; // Zod validation schema
1503
+ timestamps?: boolean; // Enable timestamps
1504
+ columns?: Record&lt;string, ColumnConfig&gt;;
1505
+ relations?: Record&lt;string, RelationConfig&gt;;
1506
+ compoundIndexes?: { name?: string; unique?: boolean; columns: string[] }[];
1507
+ }
1508
+ </div>
1509
+
1510
+ <h5>Example:</h5>
1511
+ <div class="code-block typescript">
1512
+ defineEntity(UserEntity, {
1513
+ tableName: 'users',
1514
+ columns: {
1515
+ name: { required: true, indexed: true },
1516
+ email: { required: true, unique: true },
1517
+ },
1518
+ relations: {
1519
+ posts: { type: 'one-to-many', target: PostEntity, foreignKey: 'authorId' }
1520
+ }
1521
+ });
1522
+ </div>
1523
+
1524
+ <h4>newEntity(EntityClass, data)</h4>
1525
+ <p>Create a new entity instance and validate it in a single call. This helper instantiates the provided entity class, assigns the given partial data, and immediately validates it using the entity's `init` method (which calls `validateOrThrow` under the hood).</p>
1526
+
1527
+ <h5>Parameters:</h5>
1528
+ <ul>
1529
+ <li><strong>EntityClass</strong> - Entity constructor (must have a zero-arg constructor)</li>
1530
+ <li><strong>data</strong> - Partial entity data to assign before validation</li>
1531
+ </ul>
1532
+
1533
+ <h5>Returns:</h5>
1534
+ <p>A fully initialized and validated entity instance</p>
1535
+
1536
+ <h5>Throws:</h5>
1537
+ <p>ValidationError if validation fails according to the entity schema</p>
1538
+
1539
+ <h5>Example:</h5>
1540
+ <div class="code-block typescript">
1541
+ const user = newEntity(User, { name: 'Alice' });
1542
+ // user is validated; throws if required fields are missing or invalid
1543
+ </div>
1544
+
1545
+ <h3>Type Definitions</h3>
1546
+
1547
+ <h4>EntityConstructor&lt;T&gt;</h4>
1548
+ <div class="code-block typescript">
1549
+ interface EntityConstructor&lt;T extends BaseEntity = BaseEntity&gt; {
1550
+ new (): T;
1551
+ schema?: ZodSchema;
1552
+ tableName?: string;
1553
+ }
1554
+ </div>
1555
+
1556
+ <h4>ColumnConfig</h4>
1557
+ <div class="code-block typescript">
1558
+ type ColumnConfig = {
1559
+ required?: boolean;
1560
+ unique?: boolean;
1561
+ indexed?: boolean;
1562
+ default?: unknown | (() =&gt; unknown);
1563
+ };
1564
+ </div>
1565
+
1566
+ <h4>RelationConfig</h4>
1567
+ <div class="code-block typescript">
1568
+ type RelationConfig = {
1569
+ type: 'one-to-one' | 'one-to-many' | 'many-to-many';
1570
+ target: ClassConstructor | string;
1571
+ foreignKey?: string;
1572
+ joinTable?: string;
1573
+ cascade?: boolean;
1574
+ eager?: boolean;
1575
+ };
1576
+ </div>
1577
+
1578
+ <h4>ValidationResult</h4>
1579
+ <div class="code-block typescript">
1580
+ interface ValidationResult {
1581
+ isValid: boolean;
1582
+ errors: string[];
1583
+ }
1584
+ </div>
1585
+
1586
+ <h4>DatabaseConfig</h4>
1587
+ <div class="code-block typescript">
1588
+ interface DatabaseConfig {
1589
+ name: string;
1590
+ version: number;
1591
+ entities: EntityConstructor[];
1592
+ onSchemaChangeStrategy?: 'selective' | 'all';
1593
+ migrations?: Migration[];
1594
+ cloudSync?: CloudSyncConfig;
1595
+ }
1596
+ </div>
1597
+
1598
+ <h4>CloudSyncConfig</h4>
1599
+ <div class="code-block typescript">
1600
+ interface CloudSyncConfig {
1601
+ databaseUrl: string;
1602
+ enableOfflineSupport?: boolean;
1603
+ syncInterval?: number;
1604
+ }
1605
+ </div>
1606
+
1607
+ <h4>Migration</h4>
1608
+ <div class="code-block typescript">
1609
+ interface Migration {
1610
+ version: number;
1611
+ name: string;
1612
+ up: (db: Dexie) =&gt; Promise&lt;void&gt;;
1613
+ down?: (db: Dexie) =&gt; Promise&lt;void&gt;;
1614
+ }
1615
+ </div>
1616
+
1617
+ <h4>AggregationOptions&lt;T&gt;</h4>
1618
+ <div class="code-block typescript">
1619
+ interface AggregationOptions&lt;T extends BaseEntity&gt; {
1620
+ where?: Partial&lt;T&gt;;
1621
+ count?: boolean;
1622
+ sum?: (keyof T)[];
1623
+ avg?: (keyof T)[];
1624
+ min?: (keyof T)[];
1625
+ max?: (keyof T)[];
1626
+ groupBy?: keyof T;
1627
+ include?: string[];
1628
+ limit?: number;
1629
+ sort?: {
1630
+ field: keyof T;
1631
+ direction: 'asc' | 'desc';
1632
+ };
1633
+ }
1634
+ </div>
1635
+
1636
+ <h4>AggregationResult</h4>
1637
+ <div class="code-block typescript">
1638
+ interface AggregationResult {
1639
+ count?: number;
1640
+ sum?: Record&lt;string, number&gt;;
1641
+ avg?: Record&lt;string, number&gt;;
1642
+ min?: Record&lt;string, number&gt;;
1643
+ max?: Record&lt;string, number&gt;;
1644
+ groups?: Array&lt;{
1645
+ key: unknown;
1646
+ count: number;
1647
+ sum?: Record&lt;string, number&gt;;
1648
+ avg?: Record&lt;string, number&gt;;
1649
+ min?: Record&lt;string, number&gt;;
1650
+ max?: Record&lt;string, number&gt;;
1651
+ }&gt;;
1652
+ data?: unknown[];
1653
+ }
1654
+ </div>
1655
+
1656
+ <h3>Error Classes</h3>
1657
+ <h4>ValidationError</h4>
1658
+ <p>Error thrown when entity validation fails.</p>
1659
+
1660
+ <h5>Properties:</h5>
1661
+ <ul>
1662
+ <li><strong>message: string</strong> - Error message</li>
1663
+ <li><strong>errors: string[]</strong> - Array of validation errors</li>
1664
+ </ul>
1665
+
1666
+ <h3>Schema Change Strategies</h3>
1667
+ <h4>'selective'</h4>
1668
+ <p>Only resets tables that have schema changes, preserving data in unchanged tables.</p>
1669
+
1670
+ <h4>'all'</h4>
1671
+ <p>Resets the entire database when any schema change is detected.</p>
1672
+
1673
+ <h3>Cloud Sync Status</h3>
1674
+ <div class="code-block typescript">
1675
+ interface SyncStatus {
1676
+ enabled: boolean;
1677
+ lastSync?: Date;
1678
+ isOnline?: boolean;
1679
+ }
1680
+ </div>
1681
+ </div>
1682
+ </div>
1683
+
1684
+ <div class="contact-section">
1685
+ <h2>Get Started Today</h2>
1686
+ <p>Ready to build powerful IndexedDB applications with full TypeScript support?</p>
1687
+
1688
+ <div class="contact-links">
1689
+ <a href="https://github.com/radommaciej/idb-orm" class="contact-link">View on GitHub</a>
1690
+ <a href="https://www.npmjs.com/package/idb-orm" class="contact-link">Install from NPM</a>
1691
+ <a href="mailto:indexeddborm@gmail.com" class="contact-link">Contact Author</a>
1692
+ </div>
1693
+
1694
+ <p style="margin-top: 20px; opacity: 0.8;">
1695
+ Created by <strong>Maciej Radom</strong> | MIT License
1696
+ </p>
1697
+ </div>
1698
+ </div>
1699
+
1700
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js"></script>
1701
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
1702
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
1703
+ <script>
1704
+ function showTab(tabName) {
1705
+ // Hide all tab contents
1706
+ const tabContents = document.querySelectorAll('.tab-content');
1707
+ tabContents.forEach(content => {
1708
+ content.classList.remove('active');
1709
+ });
1710
+
1711
+ // Remove active class from all tabs
1712
+ const tabs = document.querySelectorAll('.tab');
1713
+ tabs.forEach(tab => {
1714
+ tab.classList.remove('active');
1715
+ });
1716
+
1717
+ // Show selected tab content
1718
+ document.getElementById(tabName).classList.add('active');
1719
+
1720
+ // Add active class to clicked tab
1721
+ event.target.classList.add('active');
1722
+ }
1723
+ </script>
1724
+ </body>
1725
+ </html>