@indexeddb-orm/idb-orm 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/extensions.json +5 -0
- package/README.md +1280 -0
- package/angular-demo-app/README.md +84 -0
- package/angular-demo-app/angular.json +109 -0
- package/angular-demo-app/package-lock.json +14215 -0
- package/angular-demo-app/package.json +41 -0
- package/angular-demo-app/src/app/app.component.ts +481 -0
- package/angular-demo-app/src/app/app.routes.ts +8 -0
- package/angular-demo-app/src/app/components/actions.component.ts +202 -0
- package/angular-demo-app/src/app/components/cloud-sync-demo.component.ts +296 -0
- package/angular-demo-app/src/app/components/live-query-demo.component.ts +307 -0
- package/angular-demo-app/src/app/components/main-info.component.ts +148 -0
- package/angular-demo-app/src/app/components/posts-live-query-demo.component.ts +336 -0
- package/angular-demo-app/src/app/components/typescript-demo.component.ts +268 -0
- package/angular-demo-app/src/entities/post-tag.entity.ts +25 -0
- package/angular-demo-app/src/entities/post.entity.ts +49 -0
- package/angular-demo-app/src/entities/profile.entity.ts +42 -0
- package/angular-demo-app/src/entities/tag.entity.ts +36 -0
- package/angular-demo-app/src/entities/user.entity.ts +59 -0
- package/angular-demo-app/src/favicon.ico +1 -0
- package/angular-demo-app/src/index.html +16 -0
- package/angular-demo-app/src/main.ts +13 -0
- package/angular-demo-app/src/services/app-logic.service.ts +449 -0
- package/angular-demo-app/src/services/cloud-sync.service.ts +95 -0
- package/angular-demo-app/src/services/database.service.ts +26 -0
- package/angular-demo-app/src/services/live-query.service.ts +63 -0
- package/angular-demo-app/src/services/posts-live-query.service.ts +86 -0
- package/angular-demo-app/src/services/typescript-demo.service.ts +59 -0
- package/angular-demo-app/src/styles.scss +50 -0
- package/angular-demo-app/tsconfig.app.json +13 -0
- package/angular-demo-app/tsconfig.json +34 -0
- package/angular-demo-app/tsconfig.spec.json +13 -0
- package/dist/Database.d.ts +206 -0
- package/dist/Database.js +288 -0
- package/dist/decorators/Column.d.ts +79 -0
- package/dist/decorators/Column.js +236 -0
- package/dist/decorators/Entity.d.ts +32 -0
- package/dist/decorators/Entity.js +44 -0
- package/dist/decorators/Relation.d.ts +70 -0
- package/dist/decorators/Relation.js +120 -0
- package/dist/decorators/index.d.ts +3 -0
- package/dist/decorators/index.js +3 -0
- package/dist/errors/ValidationError.d.ts +4 -0
- package/dist/errors/ValidationError.js +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +7 -0
- package/dist/metadata/Column.d.ts +8 -0
- package/dist/metadata/Column.js +44 -0
- package/dist/metadata/Entity.d.ts +11 -0
- package/dist/metadata/Entity.js +21 -0
- package/dist/metadata/Relation.d.ts +20 -0
- package/dist/metadata/Relation.js +74 -0
- package/dist/metadata/index.d.ts +3 -0
- package/dist/metadata/index.js +3 -0
- package/dist/services/AggregationService.d.ts +38 -0
- package/dist/services/AggregationService.js +229 -0
- package/dist/services/BaseEntity.d.ts +32 -0
- package/dist/services/BaseEntity.js +62 -0
- package/dist/services/CloudSyncService.d.ts +100 -0
- package/dist/services/CloudSyncService.js +196 -0
- package/dist/services/DecoratorUtils.d.ts +12 -0
- package/dist/services/DecoratorUtils.js +10 -0
- package/dist/services/EntityFactory.d.ts +25 -0
- package/dist/services/EntityFactory.js +27 -0
- package/dist/services/EntityRegistry.d.ts +61 -0
- package/dist/services/EntityRegistry.js +56 -0
- package/dist/services/EntitySchema.d.ts +56 -0
- package/dist/services/EntitySchema.js +125 -0
- package/dist/services/MigrationManager.d.ts +70 -0
- package/dist/services/MigrationManager.js +181 -0
- package/dist/services/RelationLoader.d.ts +66 -0
- package/dist/services/RelationLoader.js +310 -0
- package/dist/services/SchemaBuilder.d.ts +68 -0
- package/dist/services/SchemaBuilder.js +191 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.js +7 -0
- package/dist/types.d.ts +152 -0
- package/dist/types.js +1 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.js +16 -0
- package/eslint.config.js +49 -0
- package/homepage/favicon.svg +36 -0
- package/homepage/index.html +1725 -0
- package/package.json +78 -0
- package/react-demo-app/README.md +61 -0
- package/react-demo-app/eslint.config.js +60 -0
- package/react-demo-app/index.html +13 -0
- package/react-demo-app/package-lock.json +4955 -0
- package/react-demo-app/package.json +39 -0
- package/react-demo-app/src/App.tsx +172 -0
- package/react-demo-app/src/assets/react.svg +1 -0
- package/react-demo-app/src/components/Actions.tsx +171 -0
- package/react-demo-app/src/components/CloudSyncDemo.tsx +191 -0
- package/react-demo-app/src/components/LiveQueryDemo.tsx +122 -0
- package/react-demo-app/src/components/MainInfo.tsx +75 -0
- package/react-demo-app/src/components/PostsLiveQueryDemo.tsx +185 -0
- package/react-demo-app/src/components/TypeScriptDemo.tsx +190 -0
- package/react-demo-app/src/database/Database.ts +30 -0
- package/react-demo-app/src/entities/Post.ts +48 -0
- package/react-demo-app/src/entities/PostTag.ts +26 -0
- package/react-demo-app/src/entities/Profile.ts +41 -0
- package/react-demo-app/src/entities/Tag.ts +35 -0
- package/react-demo-app/src/entities/User.ts +61 -0
- package/react-demo-app/src/hooks/useAppLogic.ts +565 -0
- package/react-demo-app/src/hooks/useCloudSyncDemo.ts +84 -0
- package/react-demo-app/src/hooks/useLiveQueryDemo.ts +68 -0
- package/react-demo-app/src/hooks/usePostsLiveQueryDemo.ts +64 -0
- package/react-demo-app/src/hooks/useTypeScriptDemo.ts +43 -0
- package/react-demo-app/src/index.css +26 -0
- package/react-demo-app/src/main.tsx +18 -0
- package/react-demo-app/src/migrations/001-add-user-email-index.ts +17 -0
- package/react-demo-app/src/migrations/002-add-post-category.ts +37 -0
- package/react-demo-app/src/migrations/index.ts +8 -0
- package/react-demo-app/src/vite-env.d.ts +1 -0
- package/react-demo-app/tsconfig.app.json +22 -0
- package/react-demo-app/tsconfig.json +6 -0
- package/react-demo-app/vite.config.ts +10 -0
- package/src/Database.ts +405 -0
- package/src/errors/ValidationError.ts +9 -0
- package/src/index.ts +13 -0
- package/src/metadata/Column.ts +74 -0
- package/src/metadata/Entity.ts +42 -0
- package/src/metadata/Relation.ts +121 -0
- package/src/metadata/index.ts +5 -0
- package/src/services/AggregationService.ts +348 -0
- package/src/services/BaseEntity.ts +77 -0
- package/src/services/CloudSyncService.ts +248 -0
- package/src/services/EntityFactory.ts +35 -0
- package/src/services/EntityRegistry.ts +109 -0
- package/src/services/EntitySchema.ts +154 -0
- package/src/services/MigrationManager.ts +276 -0
- package/src/services/RelationLoader.ts +532 -0
- package/src/services/SchemaBuilder.ts +237 -0
- package/src/services/index.ts +7 -0
- package/src/types.d.ts +1 -0
- package/src/types.ts +169 -0
- package/src/utils/logger.ts +40 -0
- package/svelte-demo-app/README.md +61 -0
- package/svelte-demo-app/package-lock.json +3000 -0
- package/svelte-demo-app/package.json +30 -0
- package/svelte-demo-app/src/app.d.ts +12 -0
- package/svelte-demo-app/src/app.html +13 -0
- package/svelte-demo-app/src/components/Actions.svelte +121 -0
- package/svelte-demo-app/src/components/CloudSyncDemo.svelte +333 -0
- package/svelte-demo-app/src/components/LiveQueryDemo.svelte +191 -0
- package/svelte-demo-app/src/components/MainInfo.svelte +133 -0
- package/svelte-demo-app/src/components/PostsLiveQueryDemo.svelte +330 -0
- package/svelte-demo-app/src/components/TypeScriptDemo.svelte +251 -0
- package/svelte-demo-app/src/database/Database.ts +29 -0
- package/svelte-demo-app/src/entities/Post.ts +46 -0
- package/svelte-demo-app/src/entities/PostTag.ts +22 -0
- package/svelte-demo-app/src/entities/Profile.ts +39 -0
- package/svelte-demo-app/src/entities/Tag.ts +33 -0
- package/svelte-demo-app/src/entities/User.ts +62 -0
- package/svelte-demo-app/src/lib/database/Database.ts +30 -0
- package/svelte-demo-app/src/lib/entities/Post.ts +47 -0
- package/svelte-demo-app/src/lib/entities/PostTag.ts +23 -0
- package/svelte-demo-app/src/lib/entities/Profile.ts +40 -0
- package/svelte-demo-app/src/lib/entities/Tag.ts +34 -0
- package/svelte-demo-app/src/lib/entities/User.ts +59 -0
- package/svelte-demo-app/src/lib/index.ts +7 -0
- package/svelte-demo-app/src/lib/migrations/001-add-user-email-index.ts +17 -0
- package/svelte-demo-app/src/lib/migrations/002-add-post-category.ts +37 -0
- package/svelte-demo-app/src/lib/migrations/index.ts +8 -0
- package/svelte-demo-app/src/migrations/001-add-user-email-index.ts +17 -0
- package/svelte-demo-app/src/migrations/002-add-post-category.ts +37 -0
- package/svelte-demo-app/src/migrations/index.ts +8 -0
- package/svelte-demo-app/src/routes/+layout.js +3 -0
- package/svelte-demo-app/src/routes/+layout.svelte +228 -0
- package/svelte-demo-app/src/routes/+page.js +3 -0
- package/svelte-demo-app/src/routes/+page.svelte +1305 -0
- package/svelte-demo-app/src/stores/appStore.js +603 -0
- package/svelte-demo-app/svelte.config.js +18 -0
- package/svelte-demo-app/tsconfig.json +14 -0
- package/svelte-demo-app/vite.config.ts +6 -0
- package/tests/aggregation.e2e.test.ts +87 -0
- package/tests/base-entity.e2e.test.ts +47 -0
- package/tests/database-api.e2e.test.ts +177 -0
- package/tests/decorators.e2e.test.ts +40 -0
- package/tests/entity-schema.e2e.test.ts +58 -0
- package/tests/relation-loader-table-names.test.ts +192 -0
- package/tests/relations.e2e.test.ts +178 -0
- package/tests/zod-runtime.e2e.test.ts +69 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +21 -0
- package/vitest.setup.ts +27 -0
- package/vue-demo-app/README.md +61 -0
- package/vue-demo-app/index.html +13 -0
- package/vue-demo-app/package-lock.json +1537 -0
- package/vue-demo-app/package.json +27 -0
- package/vue-demo-app/src/App.vue +100 -0
- package/vue-demo-app/src/components/Actions.vue +135 -0
- package/vue-demo-app/src/components/CloudSyncDemo.vue +139 -0
- package/vue-demo-app/src/components/LiveQueryDemo.vue +122 -0
- package/vue-demo-app/src/components/MainInfo.vue +80 -0
- package/vue-demo-app/src/components/PostsLiveQueryDemo.vue +136 -0
- package/vue-demo-app/src/components/TypeScriptDemo.vue +133 -0
- package/vue-demo-app/src/database/Database.ts +29 -0
- package/vue-demo-app/src/entities/Post.ts +48 -0
- package/vue-demo-app/src/entities/PostTag.ts +24 -0
- package/vue-demo-app/src/entities/Profile.ts +41 -0
- package/vue-demo-app/src/entities/Tag.ts +35 -0
- package/vue-demo-app/src/entities/User.ts +61 -0
- package/vue-demo-app/src/main.ts +29 -0
- package/vue-demo-app/src/migrations/001-add-user-email-index.ts +23 -0
- package/vue-demo-app/src/migrations/002-add-post-category.ts +46 -0
- package/vue-demo-app/src/migrations/index.ts +14 -0
- package/vue-demo-app/src/services/useAppLogic.ts +565 -0
- package/vue-demo-app/src/services/useCloudSyncDemo.ts +84 -0
- package/vue-demo-app/src/services/useLiveQueryDemo.ts +82 -0
- package/vue-demo-app/src/services/usePostsLiveQueryDemo.ts +77 -0
- package/vue-demo-app/src/services/useTypeScriptDemo.ts +56 -0
- package/vue-demo-app/src/vite-env.d.ts +1 -0
- package/vue-demo-app/tsconfig.json +25 -0
- package/vue-demo-app/tsconfig.node.json +10 -0
- package/vue-demo-app/vite.config.ts +16 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card
|
|
3
|
+
variant="outlined"
|
|
4
|
+
class="my-4"
|
|
5
|
+
style="border: 2px solid #9c27b0; background-color: #faf5ff;"
|
|
6
|
+
>
|
|
7
|
+
<v-card-title class="d-flex align-center">
|
|
8
|
+
<v-icon class="mr-2" color="primary">mdi-code-json</v-icon>
|
|
9
|
+
Posts useLiveQuery Demo
|
|
10
|
+
</v-card-title>
|
|
11
|
+
<v-card-text>
|
|
12
|
+
<v-btn
|
|
13
|
+
color="purple"
|
|
14
|
+
variant="elevated"
|
|
15
|
+
@click="addSamplePost"
|
|
16
|
+
class="mb-4"
|
|
17
|
+
>
|
|
18
|
+
Add sample post
|
|
19
|
+
</v-btn>
|
|
20
|
+
|
|
21
|
+
<v-row>
|
|
22
|
+
<v-col cols="12" md="4">
|
|
23
|
+
<v-card variant="outlined" class="h-100">
|
|
24
|
+
<v-card-title class="d-flex align-center">
|
|
25
|
+
<v-icon class="mr-2" color="primary">mdi-chart-bar</v-icon>
|
|
26
|
+
Statistics
|
|
27
|
+
</v-card-title>
|
|
28
|
+
<v-card-text>
|
|
29
|
+
<div v-if="postStats">
|
|
30
|
+
<p><strong>Total posts:</strong> {{ postStats.total }}</p>
|
|
31
|
+
<p><strong>Published:</strong> {{ postStats.published }}</p>
|
|
32
|
+
<p><strong>Total likes:</strong> {{ postStats.totalLikes }}</p>
|
|
33
|
+
</div>
|
|
34
|
+
<div v-else>
|
|
35
|
+
<p>Loading statistics...</p>
|
|
36
|
+
</div>
|
|
37
|
+
</v-card-text>
|
|
38
|
+
</v-card>
|
|
39
|
+
</v-col>
|
|
40
|
+
|
|
41
|
+
<v-col cols="12" md="4">
|
|
42
|
+
<v-card variant="outlined" class="h-100">
|
|
43
|
+
<v-card-title class="d-flex align-center">
|
|
44
|
+
<v-icon class="mr-2" color="primary">mdi-trending-up</v-icon>
|
|
45
|
+
Top 3 posts
|
|
46
|
+
</v-card-title>
|
|
47
|
+
<v-card-text>
|
|
48
|
+
<div v-if="topPosts && topPosts.length > 0">
|
|
49
|
+
<v-card
|
|
50
|
+
v-for="(post, index) in topPosts"
|
|
51
|
+
:key="post.id"
|
|
52
|
+
variant="outlined"
|
|
53
|
+
class="mb-2 pa-2"
|
|
54
|
+
>
|
|
55
|
+
<strong>#{{ index + 1 }}</strong> {{ post.title }}
|
|
56
|
+
<span style="color: #9c27b0"> ❤️ {{ post.likes }}</span>
|
|
57
|
+
</v-card>
|
|
58
|
+
</div>
|
|
59
|
+
</v-card-text>
|
|
60
|
+
</v-card>
|
|
61
|
+
</v-col>
|
|
62
|
+
|
|
63
|
+
<v-col cols="12" md="4">
|
|
64
|
+
<v-card variant="outlined" class="h-100">
|
|
65
|
+
<v-card-title>Published posts</v-card-title>
|
|
66
|
+
<v-card-text>
|
|
67
|
+
<div v-if="publishedPosts && publishedPosts.length > 0" class="overflow-y-auto" style="max-height: 150px;">
|
|
68
|
+
<div
|
|
69
|
+
v-for="post in publishedPosts"
|
|
70
|
+
:key="post.id"
|
|
71
|
+
class="py-1 border-b text-caption"
|
|
72
|
+
>
|
|
73
|
+
<strong>{{ post.title }}</strong> ❤️ {{ post.likes }}
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</v-card-text>
|
|
77
|
+
</v-card>
|
|
78
|
+
</v-col>
|
|
79
|
+
</v-row>
|
|
80
|
+
|
|
81
|
+
<div v-if="allPosts && allPosts.length > 0" class="mt-4">
|
|
82
|
+
<h3 class="d-flex align-center mb-2">
|
|
83
|
+
<v-icon class="mr-2" color="primary">mdi-format-list-bulleted</v-icon>
|
|
84
|
+
All posts (newest first)
|
|
85
|
+
</h3>
|
|
86
|
+
<v-card variant="outlined" class="overflow-y-auto" style="max-height: 200px;">
|
|
87
|
+
<v-card-text>
|
|
88
|
+
<div
|
|
89
|
+
v-for="post in allPosts"
|
|
90
|
+
:key="post.id"
|
|
91
|
+
class="py-2 border-b text-caption"
|
|
92
|
+
>
|
|
93
|
+
<div class="d-flex justify-space-between align-center">
|
|
94
|
+
<div>
|
|
95
|
+
<strong>{{ post.title }}</strong>
|
|
96
|
+
<v-chip
|
|
97
|
+
:color="post.published ? 'success' : 'warning'"
|
|
98
|
+
size="x-small"
|
|
99
|
+
class="ml-2"
|
|
100
|
+
>
|
|
101
|
+
{{ post.published ? 'Published' : 'Draft' }}
|
|
102
|
+
</v-chip>
|
|
103
|
+
</div>
|
|
104
|
+
<div style="color: #9c27b0">❤️ {{ post.likes }}</div>
|
|
105
|
+
</div>
|
|
106
|
+
<div class="text-medium-emphasis mt-1">
|
|
107
|
+
{{ post.content?.substring(0, 100) }}...
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</v-card-text>
|
|
111
|
+
</v-card>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<v-alert
|
|
115
|
+
type="info"
|
|
116
|
+
variant="tonal"
|
|
117
|
+
class="mt-4"
|
|
118
|
+
prepend-icon="mdi-lightbulb"
|
|
119
|
+
>
|
|
120
|
+
<strong>Note:</strong> All data updates automatically! Add a post and see how all sections refresh in real time.
|
|
121
|
+
</v-alert>
|
|
122
|
+
</v-card-text>
|
|
123
|
+
</v-card>
|
|
124
|
+
</template>
|
|
125
|
+
|
|
126
|
+
<script setup lang="ts">
|
|
127
|
+
import { usePostsLiveQueryDemo } from '../services/usePostsLiveQueryDemo';
|
|
128
|
+
|
|
129
|
+
const {
|
|
130
|
+
allPosts,
|
|
131
|
+
publishedPosts,
|
|
132
|
+
topPosts,
|
|
133
|
+
postStats,
|
|
134
|
+
addSamplePost,
|
|
135
|
+
} = usePostsLiveQueryDemo();
|
|
136
|
+
</script>
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card
|
|
3
|
+
variant="outlined"
|
|
4
|
+
class="my-4"
|
|
5
|
+
style="border: 2px solid #ff9800; background-color: #fff8e1;"
|
|
6
|
+
>
|
|
7
|
+
<v-card-title class="d-flex align-center">
|
|
8
|
+
<v-icon class="mr-2" color="primary">mdi-code-tags</v-icon>
|
|
9
|
+
TypeScript Typing Test
|
|
10
|
+
</v-card-title>
|
|
11
|
+
<v-card-text>
|
|
12
|
+
<p class="text-body-2 text-medium-emphasis mb-4">
|
|
13
|
+
<strong>Instructions:</strong>
|
|
14
|
+
Hover over the variables below in your IDE and
|
|
15
|
+
verify TypeScript shows full types! Click items to
|
|
16
|
+
see auto-complete in the console.
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<v-row>
|
|
20
|
+
<v-col cols="12" md="4">
|
|
21
|
+
<v-card variant="outlined" class="h-100">
|
|
22
|
+
<v-card-title class="d-flex align-center">
|
|
23
|
+
<v-icon class="mr-2" color="primary">mdi-account-group</v-icon>
|
|
24
|
+
Users (UserEntity[])
|
|
25
|
+
</v-card-title>
|
|
26
|
+
<v-card-text>
|
|
27
|
+
<p class="text-caption text-medium-emphasis mb-2">
|
|
28
|
+
Type: <code>UserEntity[] | undefined</code>
|
|
29
|
+
</p>
|
|
30
|
+
<v-card v-if="users && users.length > 0" variant="outlined" class="overflow-y-auto" style="max-height: 150px;">
|
|
31
|
+
<v-card-text>
|
|
32
|
+
<div
|
|
33
|
+
v-for="user in users"
|
|
34
|
+
:key="user.id"
|
|
35
|
+
@click="handleUserClick(user)"
|
|
36
|
+
class="py-1 border-b cursor-pointer hover:bg-grey-lighten-4"
|
|
37
|
+
title="Click to see auto-complete in the console"
|
|
38
|
+
>
|
|
39
|
+
<strong>{{ user.name }}</strong> - {{ user.email }}
|
|
40
|
+
</div>
|
|
41
|
+
</v-card-text>
|
|
42
|
+
</v-card>
|
|
43
|
+
</v-card-text>
|
|
44
|
+
</v-card>
|
|
45
|
+
</v-col>
|
|
46
|
+
|
|
47
|
+
<v-col cols="12" md="4">
|
|
48
|
+
<v-card variant="outlined" class="h-100">
|
|
49
|
+
<v-card-title class="d-flex align-center">
|
|
50
|
+
<v-icon class="mr-2" color="primary">mdi-file-document</v-icon>
|
|
51
|
+
Posts (PostEntity[])
|
|
52
|
+
</v-card-title>
|
|
53
|
+
<v-card-text>
|
|
54
|
+
<p class="text-caption text-medium-emphasis mb-2">
|
|
55
|
+
Type: <code>PostEntity[] | undefined</code>
|
|
56
|
+
</p>
|
|
57
|
+
<v-card v-if="posts && posts.length > 0" variant="outlined" class="overflow-y-auto" style="max-height: 150px;">
|
|
58
|
+
<v-card-text>
|
|
59
|
+
<div
|
|
60
|
+
v-for="post in posts"
|
|
61
|
+
:key="post.id"
|
|
62
|
+
@click="handlePostClick(post)"
|
|
63
|
+
class="py-1 border-b cursor-pointer hover:bg-grey-lighten-4"
|
|
64
|
+
title="Click to see auto-complete in the console"
|
|
65
|
+
>
|
|
66
|
+
<strong>{{ post.title }}</strong> - {{ post.published ? 'Published' : 'Draft' }}
|
|
67
|
+
</div>
|
|
68
|
+
</v-card-text>
|
|
69
|
+
</v-card>
|
|
70
|
+
</v-card-text>
|
|
71
|
+
</v-card>
|
|
72
|
+
</v-col>
|
|
73
|
+
|
|
74
|
+
<v-col cols="12" md="4">
|
|
75
|
+
<v-card variant="outlined" class="h-100">
|
|
76
|
+
<v-card-title class="d-flex align-center">
|
|
77
|
+
<v-icon class="mr-2" color="primary">mdi-account-circle</v-icon>
|
|
78
|
+
Profiles (ProfileEntity[])
|
|
79
|
+
</v-card-title>
|
|
80
|
+
<v-card-text>
|
|
81
|
+
<p class="text-caption text-medium-emphasis mb-2">
|
|
82
|
+
Type: <code>ProfileEntity[] | undefined</code>
|
|
83
|
+
</p>
|
|
84
|
+
<v-card v-if="profiles && profiles.length > 0" variant="outlined" class="overflow-y-auto" style="max-height: 150px;">
|
|
85
|
+
<v-card-text>
|
|
86
|
+
<div
|
|
87
|
+
v-for="profile in profiles"
|
|
88
|
+
:key="profile.id"
|
|
89
|
+
@click="handleProfileClick(profile)"
|
|
90
|
+
class="py-1 border-b cursor-pointer hover:bg-grey-lighten-4"
|
|
91
|
+
title="Click to see auto-complete in the console"
|
|
92
|
+
>
|
|
93
|
+
<strong>User {{ profile.userId }}</strong> -
|
|
94
|
+
{{ profile.bio?.substring(0, 30) }}...
|
|
95
|
+
</div>
|
|
96
|
+
</v-card-text>
|
|
97
|
+
</v-card>
|
|
98
|
+
</v-card-text>
|
|
99
|
+
</v-card>
|
|
100
|
+
</v-col>
|
|
101
|
+
</v-row>
|
|
102
|
+
|
|
103
|
+
<v-card variant="outlined" class="mt-4" color="primary">
|
|
104
|
+
<v-card-text>
|
|
105
|
+
<div class="text-body-2 text-medium-emphasis">
|
|
106
|
+
<div class="font-weight-bold">IDE Test:</div>
|
|
107
|
+
<br />
|
|
108
|
+
1. Hover over variables <code>users</code>, <code>posts</code>, <code>profiles</code>
|
|
109
|
+
<br />
|
|
110
|
+
2. Verify TypeScript shows full types: (UserEntity[], PostEntity[], ProfileEntity[])
|
|
111
|
+
<br />
|
|
112
|
+
3. Click items to see auto-complete inside handle* functions
|
|
113
|
+
<br />
|
|
114
|
+
4. Check that IDE suggests properties like <code>user.name</code>, <code>post.title</code>, <code>profile.bio</code>
|
|
115
|
+
</div>
|
|
116
|
+
</v-card-text>
|
|
117
|
+
</v-card>
|
|
118
|
+
</v-card-text>
|
|
119
|
+
</v-card>
|
|
120
|
+
</template>
|
|
121
|
+
|
|
122
|
+
<script setup lang="ts">
|
|
123
|
+
import { useTypeScriptDemo } from '../services/useTypeScriptDemo';
|
|
124
|
+
|
|
125
|
+
const {
|
|
126
|
+
users,
|
|
127
|
+
posts,
|
|
128
|
+
profiles,
|
|
129
|
+
handleUserClick,
|
|
130
|
+
handlePostClick,
|
|
131
|
+
handleProfileClick,
|
|
132
|
+
} = useTypeScriptDemo();
|
|
133
|
+
</script>
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
});
|
|
@@ -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,24 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BaseEntity, defineEntity,
|
|
3
|
+
} from 'idb-orm';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
const ProfileSchema = z.object({
|
|
7
|
+
id: z.number().optional(),
|
|
8
|
+
userId: z.number().positive('User ID must be positive'),
|
|
9
|
+
bio: z.string().optional(),
|
|
10
|
+
avatar: z.string().url().optional(),
|
|
11
|
+
website: z.string().url().optional(),
|
|
12
|
+
location: z.string().optional(),
|
|
13
|
+
createdAt: z.number(),
|
|
14
|
+
updatedAt: z.number(),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export type Profile = z.infer<typeof ProfileSchema>;
|
|
18
|
+
|
|
19
|
+
export class ProfileEntity extends BaseEntity<number> {
|
|
20
|
+
userId!: number;
|
|
21
|
+
bio?: string;
|
|
22
|
+
avatar?: string;
|
|
23
|
+
website?: string;
|
|
24
|
+
location?: string;
|
|
25
|
+
createdAt!: number;
|
|
26
|
+
updatedAt!: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
defineEntity(ProfileEntity, {
|
|
30
|
+
tableName: 'profiles',
|
|
31
|
+
schema: ProfileSchema,
|
|
32
|
+
columns: {
|
|
33
|
+
userId: { indexed: true },
|
|
34
|
+
bio: { indexed: true },
|
|
35
|
+
avatar: {},
|
|
36
|
+
website: {},
|
|
37
|
+
location: {},
|
|
38
|
+
createdAt: { indexed: true },
|
|
39
|
+
updatedAt: { indexed: true },
|
|
40
|
+
},
|
|
41
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BaseEntity, defineEntity,
|
|
3
|
+
} from 'idb-orm';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
const TagSchema = z.object({
|
|
7
|
+
id: z.number().optional(),
|
|
8
|
+
name: z.string().min(1, 'Tag name is required'),
|
|
9
|
+
color: z.string().regex(/^#[0-9A-F]{6}$/i, 'Color must be a valid hex color'),
|
|
10
|
+
description: z.string().optional(),
|
|
11
|
+
createdAt: z.number(),
|
|
12
|
+
updatedAt: z.number(),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export type Tag = z.infer<typeof TagSchema>;
|
|
16
|
+
|
|
17
|
+
export class TagEntity extends BaseEntity<number> {
|
|
18
|
+
name!: string;
|
|
19
|
+
color!: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
createdAt!: number;
|
|
22
|
+
updatedAt!: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
defineEntity(TagEntity, {
|
|
26
|
+
tableName: 'tags',
|
|
27
|
+
schema: TagSchema,
|
|
28
|
+
columns: {
|
|
29
|
+
name: { required: true, unique: true, indexed: true },
|
|
30
|
+
color: { required: true },
|
|
31
|
+
description: {},
|
|
32
|
+
createdAt: { indexed: true },
|
|
33
|
+
updatedAt: { indexed: true },
|
|
34
|
+
},
|
|
35
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BaseEntity, defineEntity,
|
|
3
|
+
} from 'idb-orm';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
import type { PostEntity } from './Post';
|
|
7
|
+
import type { ProfileEntity } from './Profile';
|
|
8
|
+
|
|
9
|
+
// Zod schema for validation
|
|
10
|
+
export const UserSchema = z.object({
|
|
11
|
+
id: z.number().optional(),
|
|
12
|
+
name: z.string().min(2, 'Name must be at least 2 characters'),
|
|
13
|
+
email: z.string().email('Invalid email format'),
|
|
14
|
+
age: z.number().min(18, 'Must be at least 18 years old'),
|
|
15
|
+
createdAt: z.number(),
|
|
16
|
+
updatedAt: z.number(),
|
|
17
|
+
isActive: z.boolean().default(true),
|
|
18
|
+
tags: z.array(z.string()).default([]),
|
|
19
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
20
|
+
// Note: '
|
|
21
|
+
// posts' and 'profile' relations are not in schema - they're loaded separately
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export type User = z.infer<typeof UserSchema>;
|
|
25
|
+
|
|
26
|
+
export class UserEntity extends BaseEntity<number> {
|
|
27
|
+
name!: string;
|
|
28
|
+
email!: string;
|
|
29
|
+
age!: number;
|
|
30
|
+
createdAt!: number;
|
|
31
|
+
updatedAt!: number;
|
|
32
|
+
isActive!: boolean;
|
|
33
|
+
tags!: string[];
|
|
34
|
+
metadata!: Record<string, unknown>;
|
|
35
|
+
posts?: PostEntity[];
|
|
36
|
+
profile?: ProfileEntity;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
defineEntity(UserEntity, {
|
|
40
|
+
tableName: 'users',
|
|
41
|
+
schema: UserSchema,
|
|
42
|
+
columns: {
|
|
43
|
+
name: { required: true, indexed: true },
|
|
44
|
+
email: { required: true, unique: true },
|
|
45
|
+
age: { indexed: true },
|
|
46
|
+
createdAt: { indexed: true },
|
|
47
|
+
updatedAt: { indexed: true },
|
|
48
|
+
isActive: { indexed: true },
|
|
49
|
+
tags: {},
|
|
50
|
+
metadata: { indexed: true },
|
|
51
|
+
},
|
|
52
|
+
compoundIndexes: [
|
|
53
|
+
{ columns: ['name', 'email'], unique: false, name: 'name_email_index' },
|
|
54
|
+
{ columns: ['age', 'isActive'], unique: false, name: 'age_active_index' },
|
|
55
|
+
{ columns: ['email'], unique: true, name: 'unique_email_index' },
|
|
56
|
+
],
|
|
57
|
+
relations: {
|
|
58
|
+
posts: { type: 'one-to-many', target: 'posts', foreignKey: 'authorId' },
|
|
59
|
+
profile: { type: 'one-to-one', target: 'profiles', foreignKey: 'userId' },
|
|
60
|
+
},
|
|
61
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import 'vuetify/styles';
|
|
2
|
+
import '@mdi/font/css/materialdesignicons.css';
|
|
3
|
+
|
|
4
|
+
import { createApp } from 'vue';
|
|
5
|
+
import { createVuetify } from 'vuetify';
|
|
6
|
+
import * as components from 'vuetify/components';
|
|
7
|
+
import * as directives from 'vuetify/directives';
|
|
8
|
+
import {
|
|
9
|
+
aliases, mdi,
|
|
10
|
+
} from 'vuetify/iconsets/mdi';
|
|
11
|
+
|
|
12
|
+
import App from './App.vue';
|
|
13
|
+
|
|
14
|
+
const vuetify = createVuetify({
|
|
15
|
+
components,
|
|
16
|
+
directives,
|
|
17
|
+
icons: {
|
|
18
|
+
defaultSet: 'mdi',
|
|
19
|
+
aliases,
|
|
20
|
+
sets: {
|
|
21
|
+
mdi,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
theme: {
|
|
25
|
+
defaultTheme: 'light',
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
createApp(App).use(vuetify).mount('#app');
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Migration type definition
|
|
2
|
+
interface Migration {
|
|
3
|
+
version: number;
|
|
4
|
+
name: string;
|
|
5
|
+
up: () => Promise<void>;
|
|
6
|
+
down: () => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const addUserEmailIndex: Migration = {
|
|
10
|
+
version: 1,
|
|
11
|
+
name: 'add-user-email-index',
|
|
12
|
+
up: async () => {
|
|
13
|
+
console.log('Adding email index to users table...');
|
|
14
|
+
// This migration would add an index to the email field
|
|
15
|
+
// In a real scenario, you might need to recreate the table with the new index
|
|
16
|
+
console.log('Email index added successfully');
|
|
17
|
+
},
|
|
18
|
+
down: async () => {
|
|
19
|
+
console.log('Removing email index from users table...');
|
|
20
|
+
// This would remove the index
|
|
21
|
+
console.log('Email index removed successfully');
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Migration type definition
|
|
2
|
+
interface Migration {
|
|
3
|
+
version: number;
|
|
4
|
+
name: string;
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
up: (_db: any) => Promise<void>;
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
down: (_db: any) => Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const addPostCategory: Migration = {
|
|
12
|
+
version: 2,
|
|
13
|
+
name: 'add-post-category',
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
up: async (_db: any) => {
|
|
16
|
+
console.log('Adding category field to posts table...');
|
|
17
|
+
|
|
18
|
+
const posts = await _db.table('posts').toArray();
|
|
19
|
+
const updatedPosts = posts.map((post: Record<string, unknown>) => ({
|
|
20
|
+
...post,
|
|
21
|
+
category: 'general',
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
const postsRepo = _db.table('posts');
|
|
25
|
+
await postsRepo.clear();
|
|
26
|
+
await postsRepo.bulkAdd(updatedPosts);
|
|
27
|
+
|
|
28
|
+
console.log('Category field added to all existing posts');
|
|
29
|
+
},
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
down: async (_db: any) => {
|
|
32
|
+
console.log('Removing category field from posts table...');
|
|
33
|
+
|
|
34
|
+
const posts = await _db.table('posts').toArray();
|
|
35
|
+
const updatedPosts = posts
|
|
36
|
+
.map(({ _category, ...post }: Record<string, unknown>) => {
|
|
37
|
+
return post;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const postsRepo = _db.table('posts');
|
|
41
|
+
await postsRepo.clear();
|
|
42
|
+
await postsRepo.bulkAdd(updatedPosts);
|
|
43
|
+
|
|
44
|
+
console.log('Category field removed from all posts');
|
|
45
|
+
},
|
|
46
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Migration type definition
|
|
2
|
+
interface Migration {
|
|
3
|
+
version: number;
|
|
4
|
+
name: string;
|
|
5
|
+
up: (db?: any) => Promise<void>;
|
|
6
|
+
down: (db?: any) => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
import { addUserEmailIndex } from './001-add-user-email-index';
|
|
9
|
+
import { addPostCategory } from './002-add-post-category';
|
|
10
|
+
|
|
11
|
+
export const migrations: Migration[] = [
|
|
12
|
+
addUserEmailIndex,
|
|
13
|
+
addPostCategory,
|
|
14
|
+
];
|