# Node You defined a data model in DSM and ran Kibo over it. What you got back — and what you work against every day — is a typed TypeScript package named after your model. You almost never call the `@digitalsubstrate/dsviper` binding directly: you import a couple of its modules and let them delegate to the runtime for you. This page is that daily surface, end to end. For *why* the generated code exists (the adapter/Dual Reality pattern), see {doc}`../kibo-template-viper/index`; for the raw runtime this layer sits on, see the {doc}`dsviper for Node <../dsviper-node/index>` chapter — starting at {doc}`../dsviper-node/types_values`. ## Anatomy of a generated package A generated package — say `model/` — compiles from a handful of TypeScript modules under `src/`: ```text model/ ├── src/ │ ├── data.ts ← the types: one class per concept, struct, enum, container │ ├── attachments.ts ← the verbs on a commit state (one object per attachment) │ ├── database_attachments.ts ← the same verbs for a standard (non-commit) Database │ ├── definitions.ts ← the runtime type catalog (plumbing) │ ├── value_type.ts ← Viper type handles used by data / attachments (plumbing) │ ├── path.ts ← field paths used by the fine-grained setters (plumbing) │ ├── resources.ts ← the packed definitions blob (plumbing) │ └── index.ts ← the barrel: re-exports everything ├── package.json └── tsconfig.json ``` This guide uses the versioned **commit database** throughout; `database_attachments.ts` is the twin of `attachments.ts` for the plain, non-versioned `Database` store — the same verb objects, called directly on a `Database` instead of a commit state (see {doc}`../dsviper-node/database`). Every file carries this header — it is generated, and regenerated on every model change: ```typescript // Generated by kibo from . Do not edit by hand. ``` Treat the package as a build artifact: never hand-edit it, never commit a local tweak — the next generation overwrites you. If something in the generated code is wrong, the fix belongs in the **DSM model** or the **template**, not the output. ### The two things you import `index.ts` re-exports the types at the top level and the verb modules as namespaces, so one import line gets you both: ```typescript import { Tuto_UserKey, Tuto_Login, Set_Tuto_UserKey, // the types attachments, // the verbs (a namespace) } from "model"; import { CommitDatabase, CommitStateBuilder, CommitMutableState } from "@digitalsubstrate/dsviper"; ``` A first program touches nothing but those, plus a `CommitDatabase`: ```typescript const db = CommitDatabase.open("model.cdb"); // a typed key + a typed struct — from `data`, never a raw dsviper.Value const user = Tuto_UserKey.create(); const login = new Tuto_Login(); login.nickname = "alice"; // a mutation verb — the attachment's object, applied to the mutating state const state = new CommitMutableState(CommitStateBuilder.initialState(db)); attachments.user_Login.set(state.attachmentMutating(), user, login); db.commitMutations("Add user", state); ``` That is the whole shape of daily work: **types are classes you `new`, changes go through an attachment's verb object, the commit loop lives on `CommitDatabase`.** The rest of this page is those three ideas in detail. ### Where each name comes from The generated names are mechanical — which is what makes a stack trace legible (see [Reading a repro](#reading-a-repro)): | You see | It came from | DSM | |---|---|---| | `Tuto_UserKey` | a **concept** | `concept User;` in `namespace Tuto` | | `Tuto_Login`, `.nickname` | a **struct** and its field | `struct Login { string nickname; … }` | | `Tuto_Status.ACTIVE` | an **enum** case | `enum Status { …, active, … }` | | `attachments.user_Login.set` | an **attachment** | `attachment login;` | Types are prefixed `_`; an attachment's object is `_` (lower-first, so `user_Login`) — so a symbol in an error points straight back to a line of DSM. ## The proxy posture The generated classes — `Tuto_UserKey`, `Tuto_Login`, `Set_Tuto_UserKey` — are **proxies**. It is tempting to treat them as ordinary objects and drop them into ordinary JS collections. Don't. Understanding *why* keeps the runtime boundary clean. ### One value, two realities Viper is a **strongly-typed** data model. The type catalog, built from your DSM at runtime, is the single source of truth — a value either conforms to its type or it is rejected, at the moment it is built. That safety lives in the runtime. A proxy is how that runtime value is surfaced to TypeScript — the same value seen two ways (the {term}`Dual Reality`): - **Runtime Reality** — the actual typed value the engine manipulates and persists. - **Developer Reality** — the proxy your editor autocompletes and `tsc` type-checks. Unlike a dynamically-typed binding, where the proxy's job is to *add* enforcement the language lacks, here `tsc` already checks the proxy surface at compile time. The proxy's job is therefore twofold: give the compiler a typed, idiomatic surface, and carry the underlying runtime value — with its reference semantics, identity, and serialization — so it can cross into the database. ### What `vprValue` is Every proxy holds exactly one runtime value in a `vprValue` field: ```typescript const user = Tuto_UserKey.create(); user.vprValue; // the underlying dsviper.ValueKey — the runtime reality ``` `vprValue` is the **seam** between the two realities. The generated code reaches through it to delegate every operation to the runtime — that is its job. It is an implementation detail, **not an interchange format for your code.** Writing `.vprValue` in your own logic is a signal that you dropped the proxy posture upstream and are patching the boundary by hand. ### Native collection vs. proxy collection They look interchangeable and are not — but in TypeScript the difference bites at **compile time**, not at run time: ```typescript // A native JS Set, typed by the compiler — but it is a JS collection of proxies, // not a runtime set the engine can store in an attachment: const raw = new Set(); raw.add(user); // The proxy collection IS the runtime value: const strict = new Set_Tuto_UserKey(); strict.add(user); // strict.add("nope"); // ✗ compile error — add(value: Tuto_UserKey) ``` Because the proxy surface is typed, `tsc` rejects a wrong element before you run — there is no deferred, dynamically-typed bug to defend against here. The reason to reach for `Set_Tuto_UserKey` over a native `Set` is not enforcement, it is that the former **is** a runtime `set` you can hand straight to an attachment; the latter is a JS collection you would have to convert at the boundary. ### The rule: produce the runtime value at creation Don't build a native collection of proxies and convert it at the edge; produce the proxy collection where the value is *created*: ```typescript function generate(): Set_Tuto_UserKey { const result = new Set_Tuto_UserKey(); result.add(Tuto_UserKey.create()); result.add(Tuto_UserKey.create()); return result; // ready for an attachment — no conversion } ``` Stay in the proxy posture: the generated SDK expresses the runtime's types *and* carries its values. A native JS structure of proxies gives you neither the runtime value nor a shorter path — only a conversion to write at the boundary. ## Living in the proxies Everything in `data` follows a few shapes. Once you know them, a new model reads itself. ### Keys — concepts and clubs A concept or club becomes a `…Key` class. A key is an identity, not data: ```typescript const user = Tuto_UserKey.create(); // a fresh identity user.instanceId(); // its UUID user.description(); // ":Tuto::UserKey" user.equals(other); // identity comparison, via the runtime ``` Use `.equals()`, not `===`: two proxies wrapping the same runtime value are equal by value, not by reference. ### Structs — fields as accessors A struct becomes a class you `new`, with a typed getter/setter per field: ```typescript const login = new Tuto_Login(); login.nickname = "alice"; login.password = "s3cret"; // login.nickname = 42; // ✗ compile error — nickname is a string login.copy(); // a deep, independent copy ``` ### Enums — cases as static members ```typescript const account = new Tuto_Account(); account.state = Tuto_Status.ACTIVE; account.state.name(); // "active" Tuto_Status.fromStr("pending"); // parse from a name ``` ### Containers — idiomatic, but still runtime values `vector` / `set` / `map` / `xarray` / `optional` become classes with the idiomatic surface — `for..of`, `.size`, set operators — all delegating to the runtime: ```typescript const keys = new Set_Tuto_UserKey(); keys.add(Tuto_UserKey.create()); keys.size; // number of elements keys.union(other); // typed set algebra for (const k of keys) { // yields Tuto_UserKey proxies, not raw values // … } ``` The rule from [the proxy posture](#the-proxy-posture) applies throughout: **elements are proxies too.** Build and pass the typed container; don't assemble a native `Set`/`Array` of proxies and convert at the boundary. ## Mutating: calling an attachment verb Data does not live *in* a key — it hangs off it as **attachments**. Each attachment generates one object whose methods are the verbs; you call them against a **state**. A state is a point in the commit DAG (see {doc}`../commit/index` for the model); here is the loop. ### Open a state ```typescript const db = CommitDatabase.open("model.cdb"); // a fresh, empty state: const state = new CommitMutableState(CommitStateBuilder.initialState(db)); // …or the latest committed state of an existing database: const latest = new CommitMutableState( CommitStateBuilder.state(db, db.lastCommitId())); ``` ### Write — through `attachmentMutating()`, then commit The verb object exposes `set`, per-field `set`, `union`, … taking an `AttachmentMutating`. Nothing is persisted until you commit. A full loop against a fresh in-memory database — a real app opens a `.cdb`, but the shape is identical (the generated `definitions` module seeds the schema): ```typescript import { CommitDatabase, CommitStateBuilder, CommitMutableState } from "@digitalsubstrate/dsviper"; import { Tuto_UserKey, Tuto_Login, attachments, definitions } from "model"; const db = CommitDatabase.createInMemory(); db.extendDefinitions(definitions.definitions()); const user = Tuto_UserKey.create(); const login = new Tuto_Login(); login.nickname = "alice"; const state = new CommitMutableState(CommitStateBuilder.initialState(db)); attachments.user_Login.set(state.attachmentMutating(), user, login); attachments.user_Login.setNickname(state.attachmentMutating(), user, "alice2"); // fine-grained db.commitMutations("Register alice", state); ``` ### Read — through `attachmentGetting()` Each verb object also exposes `get`, `keys`, `enumerate`, taking an `AttachmentGetting` drawn from a state: ```typescript const read = new CommitMutableState( CommitStateBuilder.state(db, db.lastCommitId())); const getting = read.attachmentGetting(); const users = attachments.user_Login.keys(getting); // Set_Tuto_UserKey const maybe = attachments.user_Login.get(getting, user); // Optional_Tuto_Login if (!maybe.isNil()) { console.log(maybe.unwrap().nickname); // "alice2" } for (const [key, login] of attachments.user_Login.enumerate(getting)) { // … } ``` The mental model is uniform: **a verb is a method on the attachment's object, taking a getting/mutating handle, a proxy key, and (for setters) a proxy value.** Collection-valued attachments add the expected verbs — `union`, `subtract`, `insert`, `remove`. The commit itself, and everything about heads, history and merges, is the {doc}`../commit/index` subsystem's job, not the SDK's. ## Reading a repro Because every generated name is mechanical, an error or bug report written in generated symbols maps straight back to your model. Given a stack trace that mentions `attachments.user_Login.set` or `Tuto_Login`, read the name: - **`Tuto_Login`** → type `Login` in `namespace Tuto` → `data.ts`, and the DSM line `struct Login { … }`. - **`attachments.user_Login.set`** → namespace `Tuto`, key concept `User`, attachment `login`, verb `set` → the DSM line `attachment login;`, and the `user_Login` object in `attachments.ts`. From there: 1. The generated file says **"Do not edit by hand."** The defect is upstream — in the DSM model, or in the template that produced the file — never a patch to the output. 2. Regenerate after any model change (`dsm_util.py create_node_package`, see {doc}`../dsviper-tools/dsm_util`); the package is a build artifact and a stale one will lie about the model. That legibility is the payoff of the proxy layer: the names you debug are the names you declared.