create-noy-db 0.1.0-pre.10

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 (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +283 -0
  3. package/dist/bin/create.d.ts +1 -0
  4. package/dist/bin/create.js +815 -0
  5. package/dist/bin/create.js.map +1 -0
  6. package/dist/bin/noy-db.d.ts +1 -0
  7. package/dist/bin/noy-db.js +555 -0
  8. package/dist/bin/noy-db.js.map +1 -0
  9. package/dist/index.d.ts +697 -0
  10. package/dist/index.js +967 -0
  11. package/dist/index.js.map +1 -0
  12. package/package.json +74 -0
  13. package/templates/electron/README.md +85 -0
  14. package/templates/electron/_gitignore +31 -0
  15. package/templates/electron/electron/main.ts +51 -0
  16. package/templates/electron/index.html +12 -0
  17. package/templates/electron/package.json +32 -0
  18. package/templates/electron/src/App.vue +75 -0
  19. package/templates/electron/src/components/AddInvoiceForm.vue +39 -0
  20. package/templates/electron/src/components/InvoiceTable.vue +41 -0
  21. package/templates/electron/src/main.ts +48 -0
  22. package/templates/electron/src/stores/invoices.ts +35 -0
  23. package/templates/electron/src/style.css +119 -0
  24. package/templates/electron/tsconfig.json +19 -0
  25. package/templates/electron/vite.config.ts +21 -0
  26. package/templates/nuxt-default/README.md +38 -0
  27. package/templates/nuxt-default/_gitignore +32 -0
  28. package/templates/nuxt-default/app/app.vue +37 -0
  29. package/templates/nuxt-default/app/pages/index.vue +21 -0
  30. package/templates/nuxt-default/app/pages/invoices.vue +62 -0
  31. package/templates/nuxt-default/app/stores/invoices.ts +23 -0
  32. package/templates/nuxt-default/nuxt.config.ts +30 -0
  33. package/templates/nuxt-default/package.json +28 -0
  34. package/templates/nuxt-default/tsconfig.json +3 -0
  35. package/templates/vanilla/README.md +77 -0
  36. package/templates/vanilla/_gitignore +29 -0
  37. package/templates/vanilla/index.html +54 -0
  38. package/templates/vanilla/package.json +21 -0
  39. package/templates/vanilla/src/main.ts +153 -0
  40. package/templates/vanilla/src/style.css +127 -0
  41. package/templates/vanilla/tsconfig.json +17 -0
  42. package/templates/vanilla/vite.config.ts +10 -0
  43. package/templates/vite-vue/README.md +95 -0
  44. package/templates/vite-vue/_gitignore +29 -0
  45. package/templates/vite-vue/index.html +12 -0
  46. package/templates/vite-vue/package.json +28 -0
  47. package/templates/vite-vue/src/App.vue +75 -0
  48. package/templates/vite-vue/src/components/AddInvoiceForm.vue +39 -0
  49. package/templates/vite-vue/src/components/InvoiceTable.vue +41 -0
  50. package/templates/vite-vue/src/main.ts +55 -0
  51. package/templates/vite-vue/src/stores/invoices.ts +35 -0
  52. package/templates/vite-vue/src/style.css +119 -0
  53. package/templates/vite-vue/tsconfig.json +18 -0
  54. package/templates/vite-vue/vite.config.ts +12 -0
@@ -0,0 +1,41 @@
1
+ <script setup lang="ts">
2
+ import type { Invoice } from '../stores/invoices'
3
+
4
+ defineProps<{ invoices: Invoice[] }>()
5
+ const emit = defineEmits<{ (e: 'remove', id: string): void }>()
6
+ </script>
7
+
8
+ <template>
9
+ <section class="invoices">
10
+ <h2>Invoices</h2>
11
+ <table v-if="invoices.length > 0">
12
+ <thead>
13
+ <tr>
14
+ <th>ID</th>
15
+ <th>Client</th>
16
+ <th>Amount</th>
17
+ <th>Status</th>
18
+ <th>Issued</th>
19
+ <th></th>
20
+ </tr>
21
+ </thead>
22
+ <tbody>
23
+ <tr v-for="inv in invoices" :key="inv.id">
24
+ <td>{{ inv.id }}</td>
25
+ <td>{{ inv.client }}</td>
26
+ <td>{{ inv.amount.toLocaleString() }}</td>
27
+ <td>
28
+ <span :class="`status-pill status-${inv.status}`">{{ inv.status }}</span>
29
+ </td>
30
+ <td>{{ inv.issueDate }}</td>
31
+ <td>
32
+ <button class="danger" @click="emit('remove', inv.id)">×</button>
33
+ </td>
34
+ </tr>
35
+ </tbody>
36
+ </table>
37
+ <p v-else class="empty">
38
+ No invoices yet — add one above.
39
+ </p>
40
+ </section>
41
+ </template>
@@ -0,0 +1,55 @@
1
+ /**
2
+ * {{PROJECT_NAME}} — Vite + Vue 3 + Pinia + noy-db starter.
3
+ *
4
+ * Bootstrap order:
5
+ * 1. Install Pinia (required by `@noy-db/in-pinia`).
6
+ * 2. Open the encrypted vault via `createNoydb` + `browserIdbStore`.
7
+ * 3. Register the active instance with `setActiveNoydb` so any
8
+ * `defineNoydbStore` call can find it without manual wiring.
9
+ * 4. Mount the Vue app.
10
+ *
11
+ * The passphrase prompt is deliberately simple — swap in a proper
12
+ * modal once you've understood the flow.
13
+ */
14
+
15
+ import { createApp } from 'vue'
16
+ import { createPinia } from 'pinia'
17
+ import { createNoydb } from '@noy-db/hub'
18
+ import { browserIdbStore } from '@noy-db/to-browser-idb'
19
+ import { setActiveNoydb } from '@noy-db/in-pinia'
20
+ import App from './App.vue'
21
+ import './style.css'
22
+
23
+ async function bootstrap(): Promise<void> {
24
+ const pinia = createPinia()
25
+
26
+ // 1. Collect the passphrase. Reload = re-enter.
27
+ const passphrase =
28
+ prompt(
29
+ 'Enter passphrase for {{PROJECT_NAME}}\n\n' +
30
+ 'Derives the master encryption key. Lose it and the data is unrecoverable.',
31
+ ) ?? ''
32
+ if (passphrase.length === 0) {
33
+ document.body.textContent = 'Cancelled — reload to try again.'
34
+ return
35
+ }
36
+
37
+ // 2. Open the encrypted vault backed by IndexedDB. The adapter sees
38
+ // only ciphertext (open DevTools → Application → IndexedDB to
39
+ // verify).
40
+ const db = await createNoydb({
41
+ store: browserIdbStore({ prefix: '{{PROJECT_NAME}}' }),
42
+ user: 'owner',
43
+ secret: passphrase,
44
+ })
45
+ await db.openVault('demo')
46
+
47
+ // 3. Register as the active instance. Pinia stores created via
48
+ // `defineNoydbStore` will resolve against this handle.
49
+ setActiveNoydb(db)
50
+
51
+ // 4. Mount.
52
+ createApp(App).use(pinia).mount('#app')
53
+ }
54
+
55
+ void bootstrap()
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Invoice store — reactive, encrypted, backed by a noy-db collection.
3
+ *
4
+ * `defineNoydbStore` wraps `defineStore` from Pinia and wires the
5
+ * store to a vault + collection. All writes encrypt before the
6
+ * IndexedDB adapter sees them; all reads decrypt into the reactive
7
+ * `items` array.
8
+ *
9
+ * Use from components:
10
+ * const invoices = useInvoices()
11
+ * await invoices.$ready
12
+ * await invoices.add(id, record)
13
+ * invoices.items // reactive array
14
+ * invoices.count // reactive getter
15
+ */
16
+ import { defineNoydbStore } from '@noy-db/in-pinia'
17
+
18
+ export interface Invoice {
19
+ id: string
20
+ client: string
21
+ amount: number
22
+ status: 'draft' | 'open' | 'paid' | 'overdue'
23
+ issueDate: string // ISO-8601
24
+ }
25
+
26
+ export const useInvoices = defineNoydbStore<Invoice>('invoices', {
27
+ vault: 'demo',
28
+ })
29
+
30
+ /** Optional seed set rendered on first load when the collection is empty. */
31
+ export const DEFAULT_INVOICES: Invoice[] = [
32
+ { id: 'inv-001', client: 'Acme Corp', amount: 1_200, status: 'paid', issueDate: '2026-01-15' },
33
+ { id: 'inv-002', client: 'Globex', amount: 2_400, status: 'open', issueDate: '2026-02-01' },
34
+ { id: 'inv-003', client: 'Initech', amount: 800, status: 'draft', issueDate: '2026-02-20' },
35
+ ]
@@ -0,0 +1,119 @@
1
+ :root {
2
+ color-scheme: light;
3
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
4
+ color: #1f2937;
5
+ background: #f9fafb;
6
+ }
7
+
8
+ * { box-sizing: border-box; }
9
+
10
+ body { margin: 0; padding: 1rem; }
11
+
12
+ .app { max-width: 900px; margin: 0 auto; }
13
+
14
+ header {
15
+ border-bottom: 1px solid #e5e7eb;
16
+ padding-bottom: 1rem;
17
+ margin-bottom: 1.5rem;
18
+ }
19
+
20
+ h1 { font-size: 1.5rem; margin: 0 0 0.5rem 0; }
21
+
22
+ .subtitle { color: #6b7280; font-size: 0.9rem; margin: 0; }
23
+
24
+ .loading {
25
+ background: white;
26
+ border: 1px solid #e5e7eb;
27
+ border-radius: 0.5rem;
28
+ padding: 1.5rem;
29
+ text-align: center;
30
+ color: #6b7280;
31
+ }
32
+
33
+ .summary {
34
+ display: flex;
35
+ gap: 1rem;
36
+ margin-bottom: 1.5rem;
37
+ }
38
+
39
+ .stat {
40
+ flex: 1;
41
+ background: white;
42
+ border: 1px solid #e5e7eb;
43
+ border-radius: 0.5rem;
44
+ padding: 1rem;
45
+ }
46
+ .stat .label { color: #6b7280; font-size: 0.75rem; text-transform: uppercase; }
47
+ .stat .value { font-size: 1.5rem; font-weight: 600; margin-top: 0.25rem; }
48
+
49
+ .add-form {
50
+ display: flex;
51
+ gap: 0.5rem;
52
+ margin-bottom: 1.5rem;
53
+ }
54
+ .add-form input, .add-form select {
55
+ padding: 0.5rem;
56
+ border: 1px solid #d1d5db;
57
+ border-radius: 0.25rem;
58
+ font-size: 0.875rem;
59
+ }
60
+ .add-form input[placeholder='Client name'] { flex: 2; }
61
+ .add-form input[type='number'] { flex: 1; }
62
+
63
+ button {
64
+ padding: 0.5rem 1rem;
65
+ border: 1px solid #2563eb;
66
+ background: #2563eb;
67
+ color: white;
68
+ border-radius: 0.25rem;
69
+ cursor: pointer;
70
+ font-size: 0.875rem;
71
+ }
72
+ button:hover { background: #1d4ed8; }
73
+ button.danger {
74
+ background: #dc2626;
75
+ border-color: #dc2626;
76
+ padding: 0.125rem 0.5rem;
77
+ }
78
+
79
+ .invoices h2 { font-size: 1.1rem; margin-top: 0; }
80
+
81
+ table {
82
+ width: 100%;
83
+ border-collapse: collapse;
84
+ background: white;
85
+ border: 1px solid #e5e7eb;
86
+ border-radius: 0.5rem;
87
+ overflow: hidden;
88
+ }
89
+ th, td {
90
+ padding: 0.5rem 0.75rem;
91
+ text-align: left;
92
+ border-bottom: 1px solid #e5e7eb;
93
+ font-size: 0.875rem;
94
+ }
95
+ th { background: #f3f4f6; font-weight: 600; }
96
+ tr:last-child td { border-bottom: 0; }
97
+
98
+ .empty { color: #6b7280; text-align: center; font-style: italic; padding: 1rem; }
99
+
100
+ .status-pill {
101
+ display: inline-block;
102
+ padding: 0.125rem 0.5rem;
103
+ border-radius: 0.25rem;
104
+ font-size: 0.75rem;
105
+ text-transform: uppercase;
106
+ font-weight: 600;
107
+ }
108
+ .status-draft { background: #f3f4f6; color: #4b5563; }
109
+ .status-open { background: #dbeafe; color: #1e40af; }
110
+ .status-paid { background: #d1fae5; color: #065f46; }
111
+ .status-overdue { background: #fee2e2; color: #b91c1c; }
112
+
113
+ footer {
114
+ margin-top: 3rem;
115
+ padding-top: 1rem;
116
+ border-top: 1px solid #e5e7eb;
117
+ color: #6b7280;
118
+ font-size: 0.85rem;
119
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "isolatedModules": true,
10
+ "resolveJsonModule": true,
11
+ "esModuleInterop": true,
12
+ "allowSyntheticDefaultImports": true,
13
+ "skipLibCheck": true,
14
+ "useDefineForClassFields": true,
15
+ "jsx": "preserve"
16
+ },
17
+ "include": ["src/**/*.ts", "src/**/*.vue"]
18
+ }
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from 'vite'
2
+ import vue from '@vitejs/plugin-vue'
3
+
4
+ export default defineConfig({
5
+ plugins: [vue()],
6
+ server: {
7
+ port: 5173,
8
+ },
9
+ build: {
10
+ target: 'es2022',
11
+ },
12
+ })