add i18n; add signal-db with first test

This commit is contained in:
2025-03-05 14:27:51 +01:00
parent cb07a92b93
commit b90fab7b7f
10 changed files with 243 additions and 9 deletions
+1 -1
View File
@@ -1 +1 @@
/// <reference types="vite/client" />
/// <reference types="vite/client" />
+115
View File
@@ -8,8 +8,12 @@
"name": "just",
"version": "0.0.0",
"dependencies": {
"@signaldb/core": "^1.3.1",
"@signaldb/indexeddb": "^1.0.0",
"@signaldb/vue": "^1.0.0",
"pinia": "^3.0.1",
"vue": "^3.5.13",
"vue-i18n": "^11.1.1",
"vue-router": "^4.5.0"
},
"devDependencies": {
@@ -1337,6 +1341,50 @@
"url": "https://github.com/sponsors/nzakas"
}
},
"node_modules/@intlify/core-base": {
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.1.1.tgz",
"integrity": "sha512-bb8gZvoeKExCI2r/NVCK9E4YyOkvYGaSCPxVZe8T0jz8aX+dHEOZWxK06Z/Y9mWRkJfBiCH4aOhDF1yr1t5J8Q==",
"license": "MIT",
"dependencies": {
"@intlify/message-compiler": "11.1.1",
"@intlify/shared": "11.1.1"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/message-compiler": {
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.1.1.tgz",
"integrity": "sha512-4iEsUZ3aF7jXY19CJFN5VP+pPyLITD9FVsjB13z9TU1UxaZLlFsmNhvRxlPDSOfHAP5RpNF2QKKdZ3DHVf4Yzw==",
"license": "MIT",
"dependencies": {
"@intlify/shared": "11.1.1",
"source-map-js": "^1.0.2"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/shared": {
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.1.1.tgz",
"integrity": "sha512-2kGiWoXaeV8HZlhU/Nml12oTbhv7j2ufsJ5vQaa0VTjzUmZVdd/nmKFRAOJ/FtjO90Qba5AnZDwsrY7ZND5udA==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -1792,6 +1840,35 @@
"dev": true,
"license": "MIT"
},
"node_modules/@signaldb/core": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@signaldb/core/-/core-1.3.1.tgz",
"integrity": "sha512-6w1bnYS1LcnbQi9rAXo61fT2eYm1Anzbs4l+Ca2uTBJOerj5zGQSIysrc8bRfPInUvuebsS0kc9kwJcaZnFa8g==",
"license": "MIT",
"dependencies": {
"fast-sort": "^3.4.1",
"mingo": "^6.5.1"
}
},
"node_modules/@signaldb/indexeddb": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@signaldb/indexeddb/-/indexeddb-1.0.0.tgz",
"integrity": "sha512-hqRt/ZeMBJXYqQNtalhT6R2fRlmT5niOXwIzaZ7mOV74YoyDOFyu7zk47GIGnFn3D84qQ3fhHBdFeyyf213uTg==",
"license": "MIT",
"peerDependencies": {
"@signaldb/core": "1.x"
}
},
"node_modules/@signaldb/vue": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@signaldb/vue/-/vue-1.0.0.tgz",
"integrity": "sha512-VWm1XY+hfnbA7xAA3LSILadN5w7auIfPKSsGJ16duTtRWnTsjgaUJBKIa2GkxxgVoYregoqh9L5wdi+vhAxNAQ==",
"license": "MIT",
"peerDependencies": {
"@signaldb/core": "1.x",
"vue": "3.x.x"
}
},
"node_modules/@sindresorhus/merge-streams": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz",
@@ -3725,6 +3802,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/fast-sort": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/fast-sort/-/fast-sort-3.4.1.tgz",
"integrity": "sha512-76uvGPsF6So53sZAqenP9UVT3p5l7cyTHkLWVCMinh41Y8NDrK1IYXJgaBMfc1gk7nJiSRZp676kddFG2Aa5+A==",
"license": "MIT"
},
"node_modules/fastq": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
@@ -4725,6 +4808,12 @@
"node": ">= 0.6"
}
},
"node_modules/mingo": {
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/mingo/-/mingo-6.5.3.tgz",
"integrity": "sha512-jp8AhCM871cDUZLTXt9jTsA5rwW74Zp+mYnbMO97PZCpVKI2UOhAamszpZp1wuXJOBnKiEQfkpGsEHj4AmadOg==",
"license": "MIT"
},
"node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
@@ -6392,6 +6481,32 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/vue-i18n": {
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.1.1.tgz",
"integrity": "sha512-0P6DkKy96R4Wh2sIZJEHw8ivnlD1pnB6Ib/eldoF1SUpQutfKZv6aMqZwICS1gW0rwq24ZSXw7y3jW+PRVYqWA==",
"license": "MIT",
"dependencies": {
"@intlify/core-base": "11.1.1",
"@intlify/shared": "11.1.1",
"@vue/devtools-api": "^6.5.0"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
},
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/vue-i18n/node_modules/@vue/devtools-api": {
"version": "6.6.4",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
"license": "MIT"
},
"node_modules/vue-router": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz",
+4
View File
@@ -14,8 +14,12 @@
"format": "prettier --write src/"
},
"dependencies": {
"@signaldb/core": "^1.3.1",
"@signaldb/indexeddb": "^1.0.0",
"@signaldb/vue": "^1.0.0",
"pinia": "^3.0.1",
"vue": "^3.5.13",
"vue-i18n": "^11.1.1",
"vue-router": "^4.5.0"
},
"devDependencies": {
+18 -6
View File
@@ -1,14 +1,9 @@
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
<HelloWorld :msg="$t('app.name')" />
<nav>
<RouterLink to="/">Home</RouterLink>
@@ -20,6 +15,23 @@ import HelloWorld from './components/HelloWorld.vue'
<RouterView />
</template>
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
import { posts } from '@/db';
// posts.insert({
// name: 'Wie geht es dir?'
// });
// posts.insert({
// name: 'How to do not suck!'
// })
setTimeout(() => {
console.info(posts.find().fetch());
}, 0);
</script>
<style scoped>
header {
line-height: 1.5;
+74
View File
@@ -0,0 +1,74 @@
import { createPersistenceAdapter } from '@signaldb/core'
/**
* Creates a persistence adapter for managing a SignalDB collection using IndexedDB.
* This adapter reads and writes data to an IndexedDB object store, with customizable serialization and deserialization.
* @template T - The type of the items in the collection.
* @template I - The type of the unique identifier for the items.
* @param name - A unique name for the collection, used as the database name.
* @returns A SignalDB persistence adapter for managing data in IndexedDB.
*/
export default function createIndexedDBAdapter<
T extends { id: I } & Record<string, any>,
I extends IDBValidKey,
>(name: string) {
const databaseName = `just-${name}`
const storeName = 'items'
/**
* Opens the IndexedDB database and creates the object store if it doesn't exist.
* @returns A promise that resolves with the opened database.
*/
function openDatabase(): Promise<IDBDatabase> {
return new Promise((resolve, reject) => {
const request = indexedDB.open(databaseName, 1)
request.addEventListener('upgradeneeded', () => {
const database = request.result
if (!database.objectStoreNames.contains(storeName)) {
database.createObjectStore(storeName, { keyPath: 'id' })
}
})
request.addEventListener('success', () => resolve(request.result))
request.addEventListener('error', () => reject(new Error(request.error?.message || 'Database error')))
})
}
/**
* Retrieves all items from the IndexedDB object store.
* @returns A promise that resolves with an array of items.
*/
async function getAllItems(): Promise<T[]> {
const database = await openDatabase()
return new Promise((resolve, reject) => {
const transaction = database.transaction(storeName, 'readonly')
const store = transaction.objectStore(storeName)
const request = store.getAll()
request.addEventListener('success', () => resolve(request.result as T[]))
request.addEventListener('error', () => reject(new Error(request.error?.message || 'Error fetching items')))
})
}
return createPersistenceAdapter<T, I>({
async load() {
const items = await getAllItems()
return { items }
},
async save(items, { added, modified, removed }) {
const database = await openDatabase()
const transaction = database.transaction(storeName, 'readwrite')
const store = transaction.objectStore(storeName)
added.forEach(item => store.add(item))
modified.forEach(item => store.put(item))
removed.forEach(item => store.delete(item.id))
return new Promise((resolve, reject) => {
transaction.addEventListener('complete', () => resolve())
transaction.addEventListener('error', () => reject(new Error(transaction.error?.message || 'Transaction error')))
})
},
async register() {
return
},
})
}
+12
View File
@@ -0,0 +1,12 @@
import { Collection } from '@signaldb/core'
import { type Post } from '@/models';
import createIndexedDBAdapter from './createIndexedDBAdapter';
const posts = new Collection<Post>({
persistence: createIndexedDBAdapter('posts')
});
export {
posts,
createIndexedDBAdapter
}
+3
View File
@@ -0,0 +1,3 @@
{
"app.name": "Just"
}
+10 -2
View File
@@ -1,14 +1,22 @@
import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { createI18n } from 'vue-i18n'
import './assets/main.css'
import App from './App.vue'
import router from './router'
import en_translations from './locales/en.json'
type en_messageSchema = typeof en_translations
const app = createApp(App)
app.use(createPinia())
app.use(createI18n<[en_messageSchema], 'en'>({
locale: 'en',
messages: {
en: en_translations
}
}));
app.use(router)
app.mount('#app')
+1
View File
@@ -0,0 +1 @@
export { type Post } from './post'
+5
View File
@@ -0,0 +1,5 @@
export type Post = {
id: string,
name: string
};