📄 src/models/adapters/vue.ts
D-OPEN SOVEREIGN

Documentation & Insights

**Composes** the Vue 3 reactivity system into the models_v2 record tracker
(coexists with other frameworks — see `addReactivitySystem`). Each tracked
ORM record maps to a `shallowRef` in a `WeakMap`; a read inside a reactive
effect subscribes and a mutation bumps the ref. Idempotent; returns an
unregister function.
Persistent models_v2 session for Vue — full front-end boot + Vue reactivity,
retained across URL navigation (SPA / Astro `<ClientRouter />`). Thin wrapper
over the shared `bootstrapSession()` glue with Vue's reactivity installer.
Returns a Vue computed ref that updates whenever the given user's level changes.
Automatically unsubscribes when the calling component is unmounted.

@example
const { level, progress } = useLevel('mine').value;
Reactive view over the global trash store (models/trash.ts) for one object
type, merged across `usernames` (empty = every known username). Population
and live updates are entirely automatic (boot scan + reactivity-system hook
inside trash.ts) — nothing to register, nothing to feed it.

@example
const { count, getIds } = useTrash(Tag.type, ['mine']);
Creates a reactive draft for a models_v2 record in Vue.
Returns the draft proxy, and reactive helpers.
1/*
2 * Copyright (c) 2026 DataPond D-Library Pty Ltd. ("The Sanctuary")
3 * Non-Profit Public Library — The World Library
4 * 
5 * D-SAFE Certification issued by POND Enterprise.
6 * Published under the D-Open Code Sovereign Licence v1.0 framework
7 * DataPond D-Library All exclusive Right reserved on modifiying the code - CC Attribution to data pond, Non modifiable, Non Commercial.
8 * https://registry.world.bibliotech.com/licence
9 * Source code donated to DataPond D-Library by it's author: data pond.
10 * 
11 * Technical Guardian ("The Shield"): Pond Enterprise Pty Ltd. (ACN 694 747 987)
12 * All liability claims about D-SAFE certification issues coming from the published D-Safe direction must be addressed with Pond Enterprise Pty Ltd.
13 * Code Modification automatically voids the certified liability protection provided by Pond Enterprise.
14 */
15import { computed, onUnmounted, shallowRef } from 'vue';
16import { getLevel, subscribeLevel } from '../level-state.ts';
17import { trashStore } from '../trash.ts';
18import { addReactivitySystem, type ReactivitySystem } from '../db.ts';
19import { bootstrapSession, type SessionBootstrapOptions, resetModelsV2Session } from '../session.ts';
20export { resetModelsV2Session };
21
22let _vueUnregister: (() => void) | null = null;
23
24/**
25 * **Composes** the Vue 3 reactivity system into the models_v2 record tracker
26 * (coexists with other frameworks — see `addReactivitySystem`). Each tracked
27 * ORM record maps to a `shallowRef` in a `WeakMap`; a read inside a reactive
28 * effect subscribes and a mutation bumps the ref. Idempotent; returns an
29 * unregister function.
30 */
31export const installVueReactivity = (): (() => void) => {
32    if (_vueUnregister) return _vueUnregister;
33    const refs = new WeakMap<object, { value: number }>();
34    const system: ReactivitySystem = {
35        track(o: object) {
36            let r = refs.get(o);
37            if (!r) { r = shallowRef(0); refs.set(o, r); }
38            void r.value;
39        },
40        trigger(o: object) {
41            const r = refs.get(o);
42            if (r) r.value++;
43        },
44    };
45    _vueUnregister = addReactivitySystem(system);
46    return _vueUnregister;
47};
48
49/**
50 * Persistent models_v2 session for Vue — full front-end boot + Vue reactivity,
51 * retained across URL navigation (SPA / Astro `<ClientRouter />`). Thin wrapper
52 * over the shared `bootstrapSession()` glue with Vue's reactivity installer.
53 */
54export const bootstrapModelsV2 = (opts: SessionBootstrapOptions): Promise<void> =>
55    bootstrapSession(opts, installVueReactivity);
56
57/**
58 * Returns a Vue computed ref that updates whenever the given user's level changes.
59 * Automatically unsubscribes when the calling component is unmounted.
60 *
61 * @example
62 * const { level, progress } = useLevel('mine').value;
63 */
64export const useLevel = (username: string) => {
65    const tick = shallowRef(0);
66    const unsub = subscribeLevel(u => { if (u === username) tick.value++; });
67    onUnmounted(unsub);
68    return computed(() => (tick.value, getLevel(username)));
69};
70
71/**
72 * Reactive view over the global trash store (models/trash.ts) for one object
73 * type, merged across `usernames` (empty = every known username). Population
74 * and live updates are entirely automatic (boot scan + reactivity-system hook
75 * inside trash.ts) — nothing to register, nothing to feed it.
76 *
77 * @example
78 * const { count, getIds } = useTrash(Tag.type, ['mine']);
79 */
80export const useTrash = (objectType: number, usernames: string[] = []) => {
81    const tick = shallowRef(0);
82    const unsub = trashStore.subscribe(objectType, () => { tick.value++; });
83    onUnmounted(unsub);
84    return {
85        count: computed(() => (tick.value, trashStore.count(objectType, usernames))),
86        getIds: () => trashStore.list(objectType, usernames),
87    };
88};
89
90/**
91 * Creates a reactive draft for a models_v2 record in Vue.
92 * Returns the draft proxy, and reactive helpers.
93 */
94import { createDraft, isDraftDirty, commitDraft, rollbackDraft } from '../draft.ts';
95import type { Orm } from '../orm.ts';
96import { type Ref } from 'vue';
97
98export const useDraft = <T extends Orm>(model: Ref<T> | (() => T)) => {
99    const tick = shallowRef(0);
100    // recreate draft when underlying model changes, but keep it stable otherwise
101    const draft = computed(() => {
102        const m = typeof model === 'function' ? model() : model.value;
103        return createDraft(m, () => { tick.value++; });
104    });
105    
106    return {
107        draft,
108        isDirty: computed(() => {
109            void tick.value; // depend on edits
110            return isDraftDirty(draft.value);
111        }),
112        commit: () => {
113            commitDraft(draft.value);
114            tick.value++;
115        },
116        rollback: () => {
117            rollbackDraft(draft.value);
118            tick.value++;
119        }
120    };
121};
122