@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,122 @@
1
+ import ArticleIcon from '@mui/icons-material/Article';
2
+ import BarChartIcon from '@mui/icons-material/BarChart';
3
+ import CheckCircleIcon from '@mui/icons-material/CheckCircle';
4
+ import FavoriteIcon from '@mui/icons-material/Favorite';
5
+ import PeopleIcon from '@mui/icons-material/People';
6
+ import PersonIcon from '@mui/icons-material/Person';
7
+ import { Box, Paper, Stack, Typography } from '@mui/material';
8
+
9
+ import { useLiveQueryDemo } from '../hooks/useLiveQueryDemo';
10
+
11
+ // Component demonstrating useLiveQuery with your Dexie ORM library
12
+ export function LiveQueryDemo() {
13
+ const {
14
+ allUsers,
15
+ adultUsers,
16
+ userCount,
17
+ firstUser,
18
+ usersWithProfiles,
19
+ allPosts,
20
+ allProfiles,
21
+ } = useLiveQueryDemo();
22
+
23
+ return (
24
+ <Paper variant="outlined" sx={{ p: 3, my: 3 }}>
25
+ <Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr 1fr' }, gridTemplateRows: { xs: 'auto', md: '1fr' }, gap: 2, alignItems: 'stretch' }}>
26
+ <Box sx={{ display: 'flex', flexDirection: 'column' }}>
27
+ <Paper variant="outlined" sx={{ p: 2, flex: 1, display: 'flex', flexDirection: 'column' }}>
28
+ <Typography variant="h6" gutterBottom sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
29
+ <BarChartIcon color="primary" />
30
+ Statistics (number | undefined)
31
+ </Typography>
32
+ <Stack spacing={0.5}>
33
+ <Typography><strong>Total users:</strong> {userCount ?? 'Loading...'}</Typography>
34
+ <Typography><strong>Adult users:</strong> {adultUsers?.length ?? 'Loading...'}</Typography>
35
+ <Typography><strong>First user:</strong> {firstUser?.name ?? 'None'}</Typography>
36
+ <Typography><strong>All users (live):</strong> {allUsers?.length ?? 'Loading...'}</Typography>
37
+ <Typography><strong>Posts:</strong> {allPosts?.length ?? 'Loading...'}</Typography>
38
+ <Typography><strong>Profiles:</strong> {allProfiles?.length ?? 'Loading...'}</Typography>
39
+ </Stack>
40
+ </Paper>
41
+ </Box>
42
+
43
+ <Box sx={{ display: 'flex', flexDirection: 'column' }}>
44
+ <Paper variant="outlined" sx={{ p: 2, flex: 1, display: 'flex', flexDirection: 'column' }}>
45
+ <Typography variant="h6" gutterBottom sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
46
+ <PeopleIcon color="primary" />
47
+ Users with profiles (UserEntity with profile)
48
+ </Typography>
49
+ {usersWithProfiles && usersWithProfiles.length > 0 && (
50
+ <Stack spacing={1} sx={{ maxHeight: 200, overflowY: 'auto' }}>
51
+ {usersWithProfiles.map((user) => (
52
+ <Paper key={user.id} variant="outlined" sx={{ p: 1.5 }}>
53
+ <Typography fontWeight={600}>{user.name}</Typography>
54
+ <Typography color="text.secondary" variant="body2">
55
+ {user.email}
56
+ </Typography>
57
+ {user.profile && (
58
+ <Typography color="text.secondary" variant="body2">
59
+ {user.profile.bio}
60
+ </Typography>
61
+ )}
62
+ </Paper>
63
+ ))}
64
+ </Stack>
65
+ )}
66
+ </Paper>
67
+ </Box>
68
+
69
+ <Box sx={{ display: 'flex', flexDirection: 'column' }}>
70
+ <Paper variant="outlined" sx={{ p: 2, flex: 1, display: 'flex', flexDirection: 'column' }}>
71
+ <Typography variant="h6" gutterBottom sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
72
+ <PersonIcon color="primary" />
73
+ All users (live)
74
+ </Typography>
75
+ {allUsers && allUsers.length > 0 && (
76
+ <Stack spacing={1} sx={{ maxHeight: 200, overflowY: 'auto' }}>
77
+ {allUsers.map((u) => (
78
+ <Typography key={u.id} variant="body2">
79
+ <strong>{u.name}</strong> — {u.email}
80
+ </Typography>
81
+ ))}
82
+ </Stack>
83
+ )}
84
+ </Paper>
85
+ </Box>
86
+ </Box>
87
+
88
+ <Box mt={3}>
89
+ <Typography variant="h6" gutterBottom>
90
+ <ArticleIcon sx={{ mr: 1, verticalAlign: 'middle' }} /> Posts (PostEntity[])
91
+ </Typography>
92
+ {allPosts && allPosts.length > 0 && (
93
+ <Paper variant="outlined" sx={{ p: 2, maxHeight: 180, overflowY: 'auto' }}>
94
+ <Stack spacing={1}>
95
+ {allPosts.map((post) => (
96
+ <Box key={post.id} sx={{ pb: 1, borderBottom: '1px solid #eee' }}>
97
+ <Typography fontWeight={600}>{post.title}</Typography>
98
+ <Typography color="text.secondary" variant="body2" sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
99
+ {post.published ? <CheckCircleIcon fontSize="small" color="success" /> : <ArticleIcon fontSize="small" />}
100
+ <FavoriteIcon fontSize="small" color="error" /> {post.likes}
101
+ </Typography>
102
+ </Box>
103
+ ))}
104
+ </Stack>
105
+ </Paper>
106
+ )}
107
+ </Box>
108
+
109
+ <Paper variant="outlined" sx={{ mt: 2, p: 2, bgcolor: '#e8f5e8' }}>
110
+ <Typography>
111
+ <strong>Typing test:</strong> All variables are fully typed with TypeScript!
112
+ </Typography>
113
+ <Typography>
114
+ <strong>Reactivity:</strong> Data updates automatically when you add/remove records!
115
+ </Typography>
116
+ <Typography>
117
+ <strong>Compatibility:</strong> Your library works perfectly with useLiveQuery!
118
+ </Typography>
119
+ </Paper>
120
+ </Paper>
121
+ );
122
+ }
@@ -0,0 +1,75 @@
1
+ import AutorenewIcon from '@mui/icons-material/Autorenew';
2
+ import AssessmentIcon from '@mui/icons-material/Assessment';
3
+ import BarChartIcon from '@mui/icons-material/BarChart';
4
+ import CloudSyncIcon from '@mui/icons-material/CloudSync';
5
+ import CodeIcon from '@mui/icons-material/Code';
6
+ import OpenInNewIcon from '@mui/icons-material/OpenInNew';
7
+ import PeopleIcon from '@mui/icons-material/People';
8
+ import ScienceIcon from '@mui/icons-material/Science';
9
+ import StorageIcon from '@mui/icons-material/Storage';
10
+ import { Box, Button, Paper, Typography } from '@mui/material';
11
+
12
+ export function MainInfo() {
13
+ return (
14
+ <Paper variant="outlined" sx={{ p: 2, mb: 2 }}>
15
+ <Typography variant="h6" gutterBottom sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
16
+ <AutorenewIcon color="primary" /> Dexie ORM for IndexedDB — Overview
17
+ </Typography>
18
+ <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
19
+ TypeScript ORM built on the latest Dexie 4 with first-class type safety and Zod runtime validation.
20
+ </Typography>
21
+ <Box sx={{
22
+ display: 'grid',
23
+ gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr' },
24
+ gap: 1.25,
25
+ }}>
26
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
27
+ <CodeIcon color="primary" fontSize="small" />
28
+ <Typography variant="body2"><strong>Entity config</strong> via defineEntity()</Typography>
29
+ </Box>
30
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
31
+ <StorageIcon color="primary" fontSize="small" />
32
+ <Typography variant="body2"><strong>Automatic schema</strong> with PK, indexes, unique</Typography>
33
+ </Box>
34
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
35
+ <PeopleIcon color="primary" fontSize="small" />
36
+ <Typography variant="body2"><strong>Relations</strong> with helpers</Typography>
37
+ </Box>
38
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
39
+ <ScienceIcon color="primary" fontSize="small" />
40
+ <Typography variant="body2"><strong>Zod validation</strong> at runtime</Typography>
41
+ </Box>
42
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
43
+ <AssessmentIcon color="primary" fontSize="small" />
44
+ <Typography variant="body2"><strong>Aggregations</strong> (count, avg, sum)</Typography>
45
+ </Box>
46
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
47
+ <BarChartIcon color="primary" fontSize="small" />
48
+ <Typography variant="body2"><strong>Live queries</strong> with useLiveQuery()</Typography>
49
+ </Box>
50
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
51
+ <CodeIcon color="primary" fontSize="small" />
52
+ <Typography variant="body2"><strong>TypeScript</strong> full type safety</Typography>
53
+ </Box>
54
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
55
+ <CloudSyncIcon color="primary" fontSize="small" />
56
+ <Typography variant="body2"><strong>Cloud sync</strong> with Dexie Cloud</Typography>
57
+ </Box>
58
+ </Box>
59
+
60
+ <Box sx={{ mt: 2, pt: 2, borderTop: '1px solid', borderColor: 'divider' }}>
61
+ <Button
62
+ variant="outlined"
63
+ size="small"
64
+ startIcon={<OpenInNewIcon />}
65
+ href="https://github.com/radommaciej/indexed-db-orm/blob/main/README.md"
66
+ target="_blank"
67
+ rel="noopener noreferrer"
68
+ sx={{ textTransform: 'none' }}
69
+ >
70
+ View full documentation
71
+ </Button>
72
+ </Box>
73
+ </Paper>
74
+ );
75
+ }
@@ -0,0 +1,185 @@
1
+ import BarChartIcon from '@mui/icons-material/BarChart';
2
+ import DataObjectIcon from '@mui/icons-material/DataObject';
3
+ import LightbulbIcon from '@mui/icons-material/Lightbulb';
4
+ import ListIcon from '@mui/icons-material/List';
5
+ import TrendingUpIcon from '@mui/icons-material/TrendingUp';
6
+ import { Box, Button, Paper, Typography } from '@mui/material';
7
+
8
+ import { usePostsLiveQueryDemo } from '../hooks/usePostsLiveQueryDemo';
9
+
10
+ // Component demonstrating advanced use of useLiveQuery with posts
11
+ export function PostsLiveQueryDemo() {
12
+ const {
13
+ allPosts,
14
+ publishedPosts,
15
+ topPosts,
16
+ postStats,
17
+ addSamplePost,
18
+ } = usePostsLiveQueryDemo();
19
+
20
+ return (
21
+ <Paper
22
+ sx={{
23
+ border: '2px solid #9c27b0',
24
+ p: 3,
25
+ my: 3,
26
+ borderRadius: 2,
27
+ backgroundColor: '#faf5ff',
28
+ }}
29
+ >
30
+ <Typography variant="h4" gutterBottom sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
31
+ <DataObjectIcon color="primary" />
32
+ Posts useLiveQuery Demo
33
+ </Typography>
34
+
35
+ <Box sx={{ mb: 2 }}>
36
+ <Button
37
+ variant="contained"
38
+ onClick={addSamplePost}
39
+ sx={{
40
+ backgroundColor: '#9c27b0',
41
+ '&:hover': { backgroundColor: '#7b1fa2' },
42
+ }}
43
+ >
44
+ Add sample post
45
+ </Button>
46
+ </Box>
47
+
48
+ <div
49
+ style={{
50
+ display: 'grid',
51
+ gridTemplateColumns: '1fr 1fr 1fr',
52
+ gap: '20px',
53
+ marginBottom: '20px',
54
+ alignItems: 'stretch',
55
+ }}
56
+ >
57
+ <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
58
+ <h3 style={{ display: 'flex', alignItems: 'center', gap: '8px', margin: 0 }}>
59
+ <BarChartIcon color="primary" fontSize="small" />
60
+ Statistics
61
+ </h3>
62
+ {postStats ? (
63
+ <div style={{ flex: 1 }}>
64
+ <p><strong>Total posts:</strong> {postStats.total}</p>
65
+ <p><strong>Published:</strong> {postStats.published}</p>
66
+ <p><strong>Total likes:</strong> {postStats.totalLikes}</p>
67
+ </div>
68
+ ) : (
69
+ <p style={{ flex: 1 }}>Loading statistics...</p>
70
+ )}
71
+ </div>
72
+
73
+ <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
74
+ <h3 style={{ display: 'flex', alignItems: 'center', gap: '8px', margin: 0 }}>
75
+ <TrendingUpIcon color="primary" fontSize="small" />
76
+ Top 3 posts
77
+ </h3>
78
+ {topPosts && topPosts.length > 0 && (
79
+ <div style={{ flex: 1 }}>
80
+ {topPosts.map((post, index) => (
81
+ <div
82
+ key={post.id}
83
+ style={{
84
+ padding: '8px',
85
+ margin: '4px 0',
86
+ backgroundColor: 'white',
87
+ borderRadius: '4px',
88
+ border: '1px solid #ddd',
89
+ }}
90
+ >
91
+ <strong>#{index + 1}</strong> {post.title}
92
+ <span style={{ color: '#9c27b0' }}> ❤️ {post.likes}</span>
93
+ </div>
94
+ ))}
95
+ </div>
96
+ )}
97
+ </div>
98
+
99
+ <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
100
+ <h3 style={{ margin: 0 }}>Published posts</h3>
101
+ {publishedPosts && publishedPosts.length > 0 && (
102
+ <div style={{ maxHeight: '150px', overflowY: 'auto', flex: 1 }}>
103
+ {publishedPosts.map((post) => (
104
+ <div
105
+ key={post.id}
106
+ style={{
107
+ padding: '4px 0',
108
+ fontSize: '0.9em',
109
+ borderBottom: '1px solid #eee',
110
+ }}
111
+ >
112
+ <strong>{post.title}</strong> ❤️ {post.likes}
113
+ </div>
114
+ ))}
115
+ </div>
116
+ )}
117
+ </div>
118
+ </div>
119
+
120
+ {allPosts && allPosts.length > 0 && (
121
+ <div>
122
+ <h3 style={{ display: 'flex', alignItems: 'center', gap: '8px', margin: 0 }}>
123
+ <ListIcon color="primary" fontSize="small" />
124
+ All posts (newest first)
125
+ </h3>
126
+ <div
127
+ style={{
128
+ maxHeight: '200px',
129
+ overflowY: 'auto',
130
+ backgroundColor: 'white',
131
+ padding: '10px',
132
+ borderRadius: '4px',
133
+ border: '1px solid #ddd',
134
+ }}
135
+ >
136
+ {allPosts.map((post) => (
137
+ <div
138
+ key={post.id}
139
+ style={{
140
+ padding: '8px 0',
141
+ borderBottom: '1px solid #eee',
142
+ fontSize: '0.9em',
143
+ }}
144
+ >
145
+ <div
146
+ style={{
147
+ display: 'flex',
148
+ justifyContent: 'space-between',
149
+ alignItems: 'center',
150
+ }}
151
+ >
152
+ <div>
153
+ <strong>{post.title}</strong>
154
+ <span
155
+ style={{
156
+ marginLeft: '10px',
157
+ padding: '2px 6px',
158
+ backgroundColor: post.published ? '#4caf50' : '#ff9800',
159
+ color: 'white',
160
+ borderRadius: '3px',
161
+ fontSize: '0.8em',
162
+ }}
163
+ >
164
+ {post.published ? 'Published' : 'Draft'}
165
+ </span>
166
+ </div>
167
+ <div style={{ color: '#9c27b0' }}>❤️ {post.likes}</div>
168
+ </div>
169
+ <div
170
+ style={{ color: '#666', fontSize: '0.8em', marginTop: '2px' }}
171
+ >
172
+ {post.content?.substring(0, 100)}...
173
+ </div>
174
+ </div>
175
+ ))}
176
+ </div>
177
+ </div>
178
+ )}
179
+
180
+ <Typography variant="body2" color="text.secondary" sx={{ mt: 2, display: 'flex', alignItems: 'center', gap: 1 }}>
181
+ <LightbulbIcon fontSize="small" /> <strong>Note:</strong> All data updates automatically! Add a post and see how all sections refresh in real time.
182
+ </Typography>
183
+ </Paper>
184
+ );
185
+ }
@@ -0,0 +1,190 @@
1
+ import AccountCircleIcon from '@mui/icons-material/AccountCircle';
2
+ import CodeIcon from '@mui/icons-material/Code';
3
+ import DescriptionIcon from '@mui/icons-material/Description';
4
+ import PeopleIcon from '@mui/icons-material/People';
5
+ import { Box, Paper, Typography } from '@mui/material';
6
+
7
+ import { useTypeScriptDemo } from '../hooks/useTypeScriptDemo';
8
+
9
+ // Component demonstrating how to inspect typing in the IDE
10
+ export function TypeScriptDemo() {
11
+ const {
12
+ users,
13
+ posts,
14
+ profiles,
15
+ handleUserClick,
16
+ handlePostClick,
17
+ handleProfileClick,
18
+ } = useTypeScriptDemo();
19
+
20
+ return (
21
+ <Paper
22
+ sx={{
23
+ border: '2px solid #ff9800',
24
+ p: 3,
25
+ my: 3,
26
+ borderRadius: 2,
27
+ backgroundColor: '#fff8e1',
28
+ }}
29
+ >
30
+ <Typography variant="h4" gutterBottom sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
31
+ <CodeIcon color="primary" />
32
+ TypeScript Typing Test
33
+ </Typography>
34
+ <Typography color="text.secondary" sx={{ mb: 3 }}>
35
+ <strong>
36
+ Instructions:
37
+ </strong>
38
+ Hover over the variables below in your IDE and
39
+ verify TypeScript shows full types! Click items to
40
+ see auto-complete in the console.
41
+ </Typography>
42
+
43
+ <Box
44
+ sx={{
45
+ display: 'grid',
46
+ gridTemplateColumns: { xs: '1fr', md: '1fr 1fr 1fr' },
47
+ gridTemplateRows: { xs: 'auto', md: '1fr' },
48
+ gap: 3,
49
+ alignItems: 'stretch',
50
+ }}
51
+ >
52
+ <Box sx={{ display: 'flex', flexDirection: 'column', flex: 1 }}>
53
+ <Typography variant="h5" gutterBottom sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
54
+ <PeopleIcon color="primary" />
55
+ Users (UserEntity[])
56
+ </Typography>
57
+ <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
58
+ Typ: <code>UserEntity[] | undefined</code>
59
+ </Typography>
60
+ {users && users.length > 0 && (
61
+ <Paper
62
+ sx={{
63
+ maxHeight: 150,
64
+ overflowY: 'auto',
65
+ p: 1,
66
+ }}
67
+ >
68
+ {users.map((user) => (
69
+ <Box
70
+ key={user.id}
71
+ onClick={() => handleUserClick(user)}
72
+ sx={{
73
+ py: 0.5,
74
+ borderBottom: '1px solid #eee',
75
+ cursor: 'pointer',
76
+ '&:hover': { backgroundColor: 'action.hover' },
77
+ }}
78
+ title="Click to see auto-complete in the console"
79
+ >
80
+ <Typography variant="body2">
81
+ <strong>{user.name}</strong> - {user.email}
82
+ </Typography>
83
+ </Box>
84
+ ))}
85
+ </Paper>
86
+ )}
87
+ </Box>
88
+
89
+ <Box sx={{ display: 'flex', flexDirection: 'column', flex: 1 }}>
90
+ <Typography variant="h5" gutterBottom sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
91
+ <DescriptionIcon color="primary" />
92
+ Posts (PostEntity[])
93
+ </Typography>
94
+ <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
95
+ Typ: <code>PostEntity[] | undefined</code>
96
+ </Typography>
97
+ {posts && posts.length > 0 && (
98
+ <Paper
99
+ sx={{
100
+ maxHeight: 150,
101
+ overflowY: 'auto',
102
+ p: 1,
103
+ }}
104
+ >
105
+ {posts.map((post) => (
106
+ <Box
107
+ key={post.id}
108
+ onClick={() => handlePostClick(post)}
109
+ sx={{
110
+ py: 0.5,
111
+ borderBottom: '1px solid #eee',
112
+ cursor: 'pointer',
113
+ '&:hover': { backgroundColor: 'action.hover' },
114
+ }}
115
+ title="Click to see auto-complete in the console"
116
+ >
117
+ <Typography variant="body2">
118
+ <strong>{post.title}</strong> - {post.published ? 'Published' : 'Draft'}
119
+ </Typography>
120
+ </Box>
121
+ ))}
122
+ </Paper>
123
+ )}
124
+ </Box>
125
+
126
+ <Box sx={{ display: 'flex', flexDirection: 'column', flex: 1 }}>
127
+ <Typography variant="h5" gutterBottom sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
128
+ <AccountCircleIcon color="primary" />
129
+ Profiles (ProfileEntity[])
130
+ </Typography>
131
+ <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
132
+ Type: <code>ProfileEntity[] | undefined</code>
133
+ </Typography>
134
+ {profiles && profiles.length > 0 && (
135
+ <Paper
136
+ sx={{
137
+ maxHeight: 150,
138
+ overflowY: 'auto',
139
+ p: 1,
140
+ }}
141
+ >
142
+ {profiles.map((profile) => (
143
+ <Box
144
+ key={profile.id}
145
+ onClick={() => handleProfileClick(profile)}
146
+ sx={{
147
+ py: 0.5,
148
+ borderBottom: '1px solid #eee',
149
+ cursor: 'pointer',
150
+ '&:hover': { backgroundColor: 'action.hover' },
151
+ }}
152
+ title="Click to see auto-complete in the console"
153
+ >
154
+ <Typography variant="body2">
155
+ <strong>User {profile.userId}</strong> -{' '}
156
+ {profile.bio?.substring(0, 30)}...
157
+ </Typography>
158
+ </Box>
159
+ ))}
160
+ </Paper>
161
+ )}
162
+ </Box>
163
+ </Box>
164
+
165
+ <Paper
166
+ sx={{
167
+ mt: 2,
168
+ p: 2,
169
+ backgroundColor: '#e3f2fd',
170
+ }}
171
+ >
172
+ <Typography variant="body2" color="text.secondary">
173
+ <strong>IDE Test:</strong>
174
+ <br />
175
+ 1. Hover over variables <code>users</code>, <code>posts</code>,{' '}
176
+ <code>profiles</code>
177
+ <br />
178
+ 2. Verify TypeScript shows full types:
179
+ (UserEntity[], PostEntity[], ProfileEntity[])
180
+ <br />
181
+ 3. Click items to see auto-complete inside handle* functions
182
+ <br />
183
+ 4. Check that IDE suggests properties like <code>
184
+ user.name
185
+ </code>, <code>post.title</code>, <code>profile.bio</code>
186
+ </Typography>
187
+ </Paper>
188
+ </Paper>
189
+ );
190
+ }
@@ -0,0 +1,30 @@
1
+ import { Database } from 'idb-orm';
2
+
3
+ import { PostEntity } from '../entities/Post';
4
+ import { PostTagEntity } from '../entities/PostTag';
5
+ import { ProfileEntity } from '../entities/Profile';
6
+ import { TagEntity } from '../entities/Tag';
7
+ import { UserEntity } from '../entities/User';
8
+ import { migrations } from '../migrations';
9
+
10
+ // Create database with entity registration
11
+ export const db = Database.createDatabase({
12
+ name: 'DexieORMDemo',
13
+ version: 6,
14
+ entities: [UserEntity, PostEntity, ProfileEntity, TagEntity, PostTagEntity],
15
+ config: {
16
+ // This will be ignored when migrations are provided
17
+ // onSchemaChangeStrategy: 'selective',
18
+ migrations: migrations, // Migrations take priority over reset strategy
19
+
20
+ // Cloud sync configuration (optional)
21
+ // cloudSync: {
22
+ // databaseUrl: 'https://your-database-url.dexie.cloud', // Replace with your actual URL
23
+ // enableOfflineSupport: true,
24
+ // syncInterval: 30000, // Sync every 30 seconds (optional)
25
+ // tables: ['users', 'posts', 'profiles'] // Specific tables to sync
26
+ // (optional - syncs all if empty)
27
+ // }
28
+ },
29
+ });
30
+
@@ -0,0 +1,48 @@
1
+ import {
2
+ BaseEntity, defineEntity,
3
+ } from 'idb-orm';
4
+ import { z } from 'zod';
5
+
6
+ import type { TagEntity } from './Tag';
7
+
8
+ const PostSchema = z.object({
9
+ id: z.number().optional(),
10
+ title: z.string().min(1, 'Title is required'),
11
+ content: z.string().min(1, 'Content is required'),
12
+ authorId: z.number().positive('Author ID must be positive'),
13
+ published: z.boolean().optional(),
14
+ postTags: z.array(z.string()).optional(),
15
+ likes: z.number().min(0, 'Likes must be non-negative').optional(),
16
+ createdAt: z.number().optional(),
17
+ updatedAt: z.number().optional(),
18
+ });
19
+
20
+ export class PostEntity extends BaseEntity<number> {
21
+ title!: string;
22
+ content!: string;
23
+ authorId!: number;
24
+ published?: boolean;
25
+ postTags?: string[];
26
+ likes?: number;
27
+ createdAt?: number;
28
+ updatedAt?: number;
29
+ tags?: TagEntity[];
30
+ }
31
+
32
+ defineEntity(PostEntity, {
33
+ tableName: 'posts',
34
+ schema: PostSchema,
35
+ columns: {
36
+ title: { required: true, indexed: true },
37
+ content: { required: true },
38
+ authorId: { required: true, indexed: true },
39
+ published: { default: false, indexed: true },
40
+ postTags: {},
41
+ likes: { default: 0, indexed: true },
42
+ createdAt: { default: () => Date.now(), indexed: true },
43
+ updatedAt: { default: () => Date.now() },
44
+ },
45
+ relations: {
46
+ tags: { type: 'many-to-many', target: 'tags', joinTable: 'post_tags' },
47
+ },
48
+ });
@@ -0,0 +1,26 @@
1
+ import {
2
+ BaseEntity, defineEntity,
3
+ } from 'idb-orm';
4
+ import { z } from 'zod';
5
+
6
+ const PostTagSchema = z.object({
7
+ id: z.number().optional(),
8
+ sourceId: z.number(),
9
+ targetId: z.number(),
10
+ });
11
+
12
+ export class PostTagEntity extends BaseEntity<number> {
13
+ sourceId!: number;
14
+ targetId!: number;
15
+ }
16
+
17
+ defineEntity(PostTagEntity, {
18
+ tableName: 'post_tags',
19
+ schema: PostTagSchema,
20
+ columns: {
21
+ sourceId: { indexed: true },
22
+ targetId: { indexed: true },
23
+ },
24
+ });
25
+
26
+