@noy-db/in-nuxt 0.2.0-pre.29 → 0.2.0-pre.30
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.
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
<div v-if="topTab === 'structure'" class="noydb-panel__body">
|
|
37
37
|
<VaultSidebar
|
|
38
38
|
:vault-name="vaultInfo!.id"
|
|
39
|
+
:vault-meta="snapshotMeta"
|
|
39
40
|
:collections="collections"
|
|
40
41
|
:selected-name="selectedCollection?.name ?? null"
|
|
41
42
|
@select="selectCollection"
|
|
@@ -78,7 +79,7 @@ import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
|
|
|
78
79
|
import { getActiveNoydb } from '@noy-db/in-pinia'
|
|
79
80
|
import { createInspector } from '@noy-db/in-devtools'
|
|
80
81
|
import type { Inspector, InspectorCollection, VaultInfo, RecordPage } from '@noy-db/in-devtools'
|
|
81
|
-
import type { Vault } from '@noy-db/hub'
|
|
82
|
+
import type { Vault, VaultMeta } from '@noy-db/hub'
|
|
82
83
|
import type { MeterSnapshot } from '@noy-db/to-meter'
|
|
83
84
|
import VaultSidebar from './panes/VaultSidebar.vue'
|
|
84
85
|
import SchemaPane from './panes/SchemaPane.vue'
|
|
@@ -101,6 +102,7 @@ const vaults = ref<ReadonlyArray<VaultInfo>>([])
|
|
|
101
102
|
const vaultInfo = computed(() => vaults.value[0] ?? null)
|
|
102
103
|
const openVault = ref<Vault | null>(null)
|
|
103
104
|
const collections = ref<ReadonlyArray<InspectorCollection>>([])
|
|
105
|
+
const snapshotMeta = ref<VaultMeta | null>(null)
|
|
104
106
|
const snapshotError = ref<string | undefined>(undefined)
|
|
105
107
|
const selectedCollection = ref<InspectorCollection | null>(null)
|
|
106
108
|
|
|
@@ -148,6 +150,7 @@ async function loadSnapshot() {
|
|
|
148
150
|
try {
|
|
149
151
|
const snap = await inspector.value.snapshot(openVault.value)
|
|
150
152
|
collections.value = snap.collections
|
|
153
|
+
snapshotMeta.value = snap.meta ?? null
|
|
151
154
|
selectedCollection.value = snap.collections[0] ?? null
|
|
152
155
|
snapshotError.value = undefined
|
|
153
156
|
} catch (e) {
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
<button :disabled="!canPrev" @click="$emit('prev')">◀ prev</button>
|
|
13
13
|
<button :disabled="!canNext" @click="$emit('next')">next ▶</button>
|
|
14
14
|
</div>
|
|
15
|
+
<button
|
|
16
|
+
v-if="hasSensitiveFields"
|
|
17
|
+
class="noydb-records__reveal-all"
|
|
18
|
+
data-reveal-all
|
|
19
|
+
@click="revealAll = !revealAll"
|
|
20
|
+
>{{ revealAll ? 'hide all' : 'reveal all' }}</button>
|
|
15
21
|
</div>
|
|
16
22
|
<template v-if="page && fields.length > 0">
|
|
17
23
|
<div class="noydb-records__cols">
|
|
@@ -19,14 +25,22 @@
|
|
|
19
25
|
</div>
|
|
20
26
|
<div v-if="page.rows.length === 0" class="noydb-records__empty">No records</div>
|
|
21
27
|
<div v-for="(row, i) in page.rows" :key="i" class="noydb-records__row">
|
|
22
|
-
<span v-for="f in fields" :key="f">
|
|
28
|
+
<span v-for="f in fields" :key="f">
|
|
29
|
+
<template v-if="isVisible(f)">{{ cell(row, f) }}</template>
|
|
30
|
+
<button
|
|
31
|
+
v-else
|
|
32
|
+
class="noydb-records__mask"
|
|
33
|
+
:data-reveal="f"
|
|
34
|
+
@click="reveal(f)"
|
|
35
|
+
>••••</button>
|
|
36
|
+
</span>
|
|
23
37
|
</div>
|
|
24
38
|
</template>
|
|
25
39
|
</div>
|
|
26
40
|
</template>
|
|
27
41
|
|
|
28
42
|
<script setup lang="ts">
|
|
29
|
-
import { computed } from 'vue'
|
|
43
|
+
import { computed, ref, watch } from 'vue'
|
|
30
44
|
import type { InspectorCollection, RecordPage } from '@noy-db/in-devtools'
|
|
31
45
|
|
|
32
46
|
const props = defineProps<{
|
|
@@ -42,6 +56,43 @@ const fields = computed(() => Object.keys(props.collection.fields))
|
|
|
42
56
|
const canPrev = computed(() => !!props.page && props.page.offset > 0)
|
|
43
57
|
const canNext = computed(() => !!props.page && props.page.offset + props.page.limit < props.page.total)
|
|
44
58
|
|
|
59
|
+
// ── PII masking ────────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
/** Set of field keys that are sensitive (pii or secret) per collection.described. */
|
|
62
|
+
const sensitiveFields = computed((): Set<string> => {
|
|
63
|
+
const described = props.collection.described
|
|
64
|
+
if (!described) return new Set()
|
|
65
|
+
const s = new Set<string>()
|
|
66
|
+
for (const d of described) {
|
|
67
|
+
if (d.sensitivity !== undefined && d.sensitivity !== 'public') {
|
|
68
|
+
s.add(d.key)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return s
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const hasSensitiveFields = computed(() => sensitiveFields.value.size > 0)
|
|
75
|
+
|
|
76
|
+
/** Tracks which individual fields the user has revealed. */
|
|
77
|
+
const revealed = ref(new Set<string>())
|
|
78
|
+
|
|
79
|
+
/** When true, all sensitive fields are shown. */
|
|
80
|
+
const revealAll = ref(false)
|
|
81
|
+
|
|
82
|
+
// Reset reveal state when the viewed collection changes (prevent PII from one
|
|
83
|
+
// collection lingering when the user navigates to another).
|
|
84
|
+
watch(() => props.collection, () => { revealed.value = new Set(); revealAll.value = false })
|
|
85
|
+
|
|
86
|
+
function isVisible(field: string): boolean {
|
|
87
|
+
if (!sensitiveFields.value.has(field)) return true
|
|
88
|
+
if (revealAll.value) return true
|
|
89
|
+
return revealed.value.has(field)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function reveal(field: string): void {
|
|
93
|
+
revealed.value = new Set([...revealed.value, field])
|
|
94
|
+
}
|
|
95
|
+
|
|
45
96
|
function cell(row: unknown, field: string): string {
|
|
46
97
|
if (row === null || row === undefined || typeof row !== 'object') return '·'
|
|
47
98
|
const val = (row as Record<string, unknown>)[field]
|
|
@@ -1,16 +1,70 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="noydb-schema">
|
|
3
3
|
<template v-if="collection">
|
|
4
|
-
|
|
5
|
-
<div
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
<span class="noydb-schema__flag">{{ isIndexed(name) ? 'idx' : '' }}</span>
|
|
4
|
+
<!-- Collection meta header -->
|
|
5
|
+
<div class="noydb-schema__meta-header">
|
|
6
|
+
<span class="noydb-schema__meta-label">{{ collectionLabel }}</span>
|
|
7
|
+
<span
|
|
8
|
+
v-if="collection.meta?.description"
|
|
9
|
+
class="noydb-schema__meta-desc"
|
|
10
|
+
:title="collection.meta.description"
|
|
11
|
+
>{{ collection.meta.description }}</span>
|
|
13
12
|
</div>
|
|
13
|
+
|
|
14
|
+
<!-- Config strip (badges for active config options) -->
|
|
15
|
+
<div v-if="configBadges.length" class="noydb-schema__config-strip">
|
|
16
|
+
<span
|
|
17
|
+
v-for="badge in configBadges"
|
|
18
|
+
:key="badge"
|
|
19
|
+
class="noydb-schema__config-badge"
|
|
20
|
+
>{{ badge }}</span>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div class="noydb-schema__label noydb-schema__label--mt">Fields</div>
|
|
24
|
+
|
|
25
|
+
<!-- Rich rows from described (when present) -->
|
|
26
|
+
<template v-if="collection.described && collection.described.length">
|
|
27
|
+
<div
|
|
28
|
+
v-for="field in collection.described"
|
|
29
|
+
:key="field.key"
|
|
30
|
+
class="noydb-schema__row"
|
|
31
|
+
>
|
|
32
|
+
<span class="noydb-schema__name">{{ field.label }}</span>
|
|
33
|
+
<span class="noydb-schema__name noydb-schema__name--key" :title="field.key">{{ field.key }}</span>
|
|
34
|
+
<span class="noydb-schema__type">{{ field.type }}</span>
|
|
35
|
+
<span v-if="field.semanticType || field.widget" class="noydb-schema__badge noydb-schema__badge--semantic">
|
|
36
|
+
{{ field.semanticType ?? field.widget }}
|
|
37
|
+
</span>
|
|
38
|
+
<span v-if="field.money?.currency" class="noydb-schema__badge noydb-schema__badge--money">
|
|
39
|
+
{{ field.money.currency }}
|
|
40
|
+
</span>
|
|
41
|
+
<span v-if="field.dict" class="noydb-schema__badge noydb-schema__badge--dict">
|
|
42
|
+
dict{{ field.dict.values ? ` ×${field.dict.values.length}` : '' }}
|
|
43
|
+
</span>
|
|
44
|
+
<span v-if="field.sensitivity === 'pii'" class="noydb-schema__badge noydb-schema__badge--pii">pii</span>
|
|
45
|
+
<span v-if="field.sensitivity === 'secret'" class="noydb-schema__badge noydb-schema__badge--secret">secret</span>
|
|
46
|
+
<span v-if="field.i18n" class="noydb-schema__badge noydb-schema__badge--i18n">i18n</span>
|
|
47
|
+
<span v-if="field.ref" class="noydb-schema__badge noydb-schema__badge--ref" :title="`→ ${field.ref.target}`">
|
|
48
|
+
→ {{ field.ref.target }}
|
|
49
|
+
</span>
|
|
50
|
+
<span v-if="!field.editable" class="noydb-schema__badge noydb-schema__badge--readonly">read-only</span>
|
|
51
|
+
<span class="noydb-schema__flag">{{ isIndexed(field.key) ? 'idx' : '' }}</span>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<!-- Fallback rows from fields (back-compat) -->
|
|
56
|
+
<template v-else>
|
|
57
|
+
<div
|
|
58
|
+
v-for="[name, field] in fieldEntries"
|
|
59
|
+
:key="name"
|
|
60
|
+
class="noydb-schema__row"
|
|
61
|
+
>
|
|
62
|
+
<span class="noydb-schema__name">{{ name }}</span>
|
|
63
|
+
<span class="noydb-schema__type">{{ (field as { type?: string }).type ?? '—' }}</span>
|
|
64
|
+
<span class="noydb-schema__flag">{{ isIndexed(name) ? 'idx' : '' }}</span>
|
|
65
|
+
</div>
|
|
66
|
+
</template>
|
|
67
|
+
|
|
14
68
|
<div class="noydb-schema__label noydb-schema__label--mt">Stats</div>
|
|
15
69
|
<div class="noydb-schema__stat">
|
|
16
70
|
docs <strong>{{ collection.stats?.count ?? '—' }}</strong>
|
|
@@ -28,10 +82,31 @@ import type { InspectorCollection } from '@noy-db/in-devtools'
|
|
|
28
82
|
|
|
29
83
|
const props = defineProps<{ collection: InspectorCollection | null }>()
|
|
30
84
|
|
|
85
|
+
const collectionLabel = computed(() =>
|
|
86
|
+
props.collection?.meta?.label ?? props.collection?.name ?? ''
|
|
87
|
+
)
|
|
88
|
+
|
|
31
89
|
const fieldEntries = computed(() =>
|
|
32
90
|
props.collection ? Object.entries(props.collection.fields) : []
|
|
33
91
|
)
|
|
34
92
|
|
|
93
|
+
/** Build small text badges for whichever config options are active. */
|
|
94
|
+
const configBadges = computed((): string[] => {
|
|
95
|
+
const cfg = props.collection?.config
|
|
96
|
+
if (!cfg) return []
|
|
97
|
+
const badges: string[] = []
|
|
98
|
+
if (cfg.embeddings) badges.push('embeddings')
|
|
99
|
+
if (cfg.textIndexes && cfg.textIndexes.length) badges.push('text-index')
|
|
100
|
+
if (cfg.crdt) badges.push(`crdt:${cfg.crdt}`)
|
|
101
|
+
if (cfg.provenance) badges.push('provenance')
|
|
102
|
+
if (cfg.archive) badges.push('archive')
|
|
103
|
+
if (cfg.tiers && cfg.tiers.length) badges.push(`tiers:${cfg.tiers.length}`)
|
|
104
|
+
if (cfg.perRecordKeys) badges.push('per-record-keys')
|
|
105
|
+
if (cfg.history) badges.push('history')
|
|
106
|
+
if (cfg.schemaUpdate && cfg.schemaUpdate.length) badges.push('schema-update')
|
|
107
|
+
return badges
|
|
108
|
+
})
|
|
109
|
+
|
|
35
110
|
function isIndexed(name: string): boolean {
|
|
36
111
|
return props.collection?.indexes?.some((idx) => {
|
|
37
112
|
const fields = (idx as { fields?: string | string[] }).fields
|
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
<aside class="noydb-sidebar">
|
|
3
3
|
<div class="noydb-sidebar__header">Vault</div>
|
|
4
4
|
<template v-if="vaultName">
|
|
5
|
-
<div
|
|
5
|
+
<div
|
|
6
|
+
class="noydb-sidebar__vault"
|
|
7
|
+
:title="vaultMeta?.description ?? vaultName ?? undefined"
|
|
8
|
+
>▸ {{ vaultMeta?.label ?? vaultName }}</div>
|
|
6
9
|
<button
|
|
7
10
|
v-for="coll in collections"
|
|
8
11
|
:key="coll.name"
|
|
9
12
|
:class="['noydb-sidebar__item', { 'noydb-sidebar__item--selected': coll.name === selectedName }]"
|
|
10
13
|
@click="$emit('select', coll)"
|
|
11
14
|
>
|
|
12
|
-
<span>{{ coll.name }}</span>
|
|
15
|
+
<span>{{ coll.meta?.label ?? coll.name }}</span>
|
|
13
16
|
<span v-if="coll.stats?.count" class="noydb-sidebar__badge">{{ coll.stats.count }}</span>
|
|
14
17
|
</button>
|
|
15
18
|
</template>
|
|
@@ -19,9 +22,11 @@
|
|
|
19
22
|
|
|
20
23
|
<script setup lang="ts">
|
|
21
24
|
import type { InspectorCollection } from '@noy-db/in-devtools'
|
|
25
|
+
import type { VaultMeta } from '@noy-db/hub'
|
|
22
26
|
|
|
23
27
|
defineProps<{
|
|
24
28
|
vaultName: string | null
|
|
29
|
+
vaultMeta?: VaultMeta | null
|
|
25
30
|
collections: ReadonlyArray<InspectorCollection>
|
|
26
31
|
selectedName: string | null
|
|
27
32
|
}>()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noy-db/in-nuxt",
|
|
3
|
-
"version": "0.2.0-pre.
|
|
3
|
+
"version": "0.2.0-pre.30",
|
|
4
4
|
"description": "Nuxt 4 module for noy-db — auto-imports, SSR-safe runtime plugin, and the @noy-db/in-pinia bridge",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "vLannaAi <vicio@lanna.ai>",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"nuxt": "^4.0.0",
|
|
37
|
-
"@noy-db/hub": "0.2.0-pre.
|
|
38
|
-
"@noy-db/in-
|
|
39
|
-
"@noy-db/in-
|
|
40
|
-
"@noy-db/in-
|
|
37
|
+
"@noy-db/hub": "0.2.0-pre.30",
|
|
38
|
+
"@noy-db/in-rest": "0.2.0-pre.30",
|
|
39
|
+
"@noy-db/in-vue": "0.2.0-pre.30",
|
|
40
|
+
"@noy-db/in-pinia": "0.2.0-pre.30"
|
|
41
41
|
},
|
|
42
42
|
"peerDependenciesMeta": {
|
|
43
43
|
"@noy-db/in-rest": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@noy-db/in-devtools": "0.2.0-pre.
|
|
48
|
+
"@noy-db/in-devtools": "0.2.0-pre.30"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@nuxt/kit": "^4.4.2",
|
|
@@ -56,10 +56,10 @@
|
|
|
56
56
|
"happy-dom": "^17.4.4",
|
|
57
57
|
"nuxt": "^4.4.2",
|
|
58
58
|
"vue": "^3.5.32",
|
|
59
|
-
"@noy-db/
|
|
60
|
-
"@noy-db/in-rest": "0.2.0-pre.
|
|
61
|
-
"@noy-db/
|
|
62
|
-
"@noy-db/in-vue": "0.2.0-pre.
|
|
59
|
+
"@noy-db/hub": "0.2.0-pre.30",
|
|
60
|
+
"@noy-db/in-rest": "0.2.0-pre.30",
|
|
61
|
+
"@noy-db/in-pinia": "0.2.0-pre.30",
|
|
62
|
+
"@noy-db/in-vue": "0.2.0-pre.30"
|
|
63
63
|
},
|
|
64
64
|
"keywords": [
|
|
65
65
|
"noy-db",
|