@calimero-network/agent-skills 0.3.0 → 0.4.0
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.
- package/README.md +137 -17
- package/SKILL.md +31 -28
- package/package.json +1 -1
- package/scripts/install.js +3 -3
- package/scripts/test.js +6 -15
- package/skills/calimero-abi-codegen/SKILL.md +121 -22
- package/skills/calimero-abi-codegen/references/abi-format.md +3 -5
- package/skills/calimero-abi-codegen/references/generated-output.md +12 -4
- package/skills/calimero-abi-codegen/rules/schema-version.md +11 -4
- package/skills/calimero-abi-codegen/rules/unique-names.md +2 -6
- package/skills/calimero-client-js/SKILL.md +126 -31
- package/skills/calimero-client-js/references/auth.md +18 -10
- package/skills/calimero-client-js/references/rpc-calls.md +15 -21
- package/skills/calimero-client-js/references/sso.md +9 -9
- package/skills/calimero-client-js/references/websocket-events.md +73 -92
- package/skills/calimero-client-js/rules/camelcase-api.md +10 -7
- package/skills/calimero-client-js/rules/token-refresh.md +11 -11
- package/skills/calimero-client-py/SKILL.md +25 -13
- package/skills/calimero-client-py/references/api.md +41 -43
- package/skills/calimero-client-py/references/auth.md +7 -7
- package/skills/calimero-client-py/rules/async-usage.md +27 -31
- package/skills/calimero-client-py/rules/stable-node-name.md +7 -7
- package/skills/calimero-core/SKILL.md +135 -0
- package/skills/calimero-core/references/architecture.md +101 -0
- package/skills/calimero-core/references/jsonrpc-protocol.md +192 -0
- package/skills/calimero-core/references/namespaces-groups.md +94 -0
- package/skills/calimero-core/references/storage-types.md +118 -0
- package/skills/calimero-core/references/websocket-events.md +142 -0
- package/skills/calimero-core/rules/context-is-not-app.md +35 -0
- package/skills/calimero-core/rules/crdt-types-only.md +55 -0
- package/skills/calimero-desktop/SKILL.md +24 -19
- package/skills/calimero-desktop/references/sso-integration.md +2 -2
- package/skills/calimero-desktop/rules/sso-fallback.md +3 -2
- package/skills/calimero-merobox/SKILL.md +255 -28
- package/skills/calimero-merobox/references/ci-integration.md +3 -2
- package/skills/calimero-merobox/references/workflow-files.md +7 -5
- package/skills/calimero-merobox/rules/docker-required.md +7 -6
- package/skills/calimero-meroctl/SKILL.md +68 -0
- package/skills/calimero-meroctl/references/commands.md +177 -0
- package/skills/calimero-meroctl/references/scripting.md +80 -0
- package/skills/calimero-meroctl/rules/call-view-flag.md +28 -0
- package/skills/calimero-meroctl/rules/register-node-once.md +34 -0
- package/skills/calimero-merod/SKILL.md +49 -0
- package/skills/calimero-merod/references/health-endpoints.md +90 -0
- package/skills/calimero-merod/references/init-flags.md +84 -0
- package/skills/calimero-merod/rules/init-before-run.md +40 -0
- package/skills/calimero-merod/rules/port-assignments.md +33 -0
- package/skills/calimero-node/SKILL.md +50 -39
- package/skills/calimero-node/references/context-lifecycle.md +34 -17
- package/skills/calimero-node/references/meroctl-commands.md +89 -99
- package/skills/calimero-node/rules/app-vs-context.md +4 -4
- package/skills/calimero-registry/SKILL.md +110 -31
- package/skills/calimero-registry/references/bundle-and-push.md +99 -34
- package/skills/calimero-registry/references/manifest-format.md +56 -35
- package/skills/calimero-registry/references/mero-sign.md +10 -9
- package/skills/calimero-registry/rules/key-security.md +3 -2
- package/skills/calimero-registry/rules/sign-before-pack.md +5 -5
- package/skills/calimero-rust-sdk/SKILL.md +154 -44
- package/skills/calimero-rust-sdk/references/blob-api.md +119 -0
- package/skills/calimero-rust-sdk/references/event-handlers.md +122 -0
- package/skills/calimero-rust-sdk/references/events.md +2 -1
- package/skills/calimero-rust-sdk/references/examples.md +81 -29
- package/skills/calimero-rust-sdk/references/migrations.md +123 -0
- package/skills/calimero-rust-sdk/references/nested-crdts.md +113 -0
- package/skills/calimero-rust-sdk/references/private-storage.md +76 -34
- package/skills/calimero-rust-sdk/references/state-collections.md +106 -21
- package/skills/calimero-rust-sdk/references/user-and-frozen-storage.md +169 -0
- package/skills/calimero-rust-sdk/rules/app-macro-placement.md +5 -2
- package/skills/calimero-rust-sdk/rules/no-std-collections.md +5 -2
- package/skills/calimero-rust-sdk/rules/state-derives.md +9 -10
- package/skills/calimero-rust-sdk/rules/wasm-constraints.md +12 -10
- package/skills/calimero-sdk-js/SKILL.md +34 -26
- package/skills/calimero-sdk-js/references/build-pipeline.md +6 -6
- package/skills/calimero-sdk-js/references/collections.md +11 -11
- package/skills/calimero-sdk-js/references/events.md +7 -3
- package/skills/calimero-sdk-js/rules/crdt-only-state.md +18 -18
- package/skills/calimero-sdk-js/rules/no-console-log.md +6 -6
- package/skills/calimero-sdk-js/rules/view-decorator.md +6 -4
|
@@ -29,6 +29,9 @@ impl AppState {
|
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
**Why:** `#[app::state]` and `#[app::logic]` are proc macros that transform impl blocks to register
|
|
32
|
+
**Why:** `#[app::state]` and `#[app::logic]` are proc macros that transform impl blocks to register
|
|
33
|
+
the type with the SDK runtime. The struct itself only needs the derive macros (`Default`,
|
|
34
|
+
`BorshDeserialize`, `BorshSerialize`).
|
|
33
35
|
|
|
34
|
-
**Also:** `#[app::init]` marks the constructor — called once when the context is created. It must
|
|
36
|
+
**Also:** `#[app::init]` marks the constructor — called once when the context is created. It must
|
|
37
|
+
return the state type, not `Self`.
|
|
@@ -22,6 +22,9 @@ pub struct AppState {
|
|
|
22
22
|
}
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
**Why:** `std::collections` types do not participate in the CRDT merge process. Data written to them
|
|
25
|
+
**Why:** `std::collections` types do not participate in the CRDT merge process. Data written to them
|
|
26
|
+
by one node will never reach other context members. The bug is silent — the app compiles and runs
|
|
27
|
+
correctly on one node, but state diverges in multi-member contexts.
|
|
26
28
|
|
|
27
|
-
**Applies to:** `HashMap`, `BTreeMap`, `HashSet`, `BTreeSet`, `Vec` (for shared state). `Vec` is
|
|
29
|
+
**Applies to:** `HashMap`, `BTreeMap`, `HashSet`, `BTreeSet`, `Vec` (for shared state). `Vec` is
|
|
30
|
+
fine for temporary local values inside a method, just not as a field in the state struct.
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Rule: State struct must derive Default, BorshDeserialize, BorshSerialize
|
|
2
2
|
|
|
3
|
-
The three derives are all required. Missing any one of them causes a **runtime panic**,
|
|
4
|
-
|
|
5
|
-
first accessed.
|
|
3
|
+
The three derives are all required. Missing any one of them causes a **runtime panic**, not a
|
|
4
|
+
compile error — the app will install and start, then crash when the context is first accessed.
|
|
6
5
|
|
|
7
6
|
## WRONG — missing derives:
|
|
8
7
|
|
|
@@ -30,11 +29,11 @@ pub struct AppState {
|
|
|
30
29
|
|
|
31
30
|
## What each derive does
|
|
32
31
|
|
|
33
|
-
| Derive
|
|
34
|
-
|
|
|
35
|
-
| `Default`
|
|
36
|
-
| `BorshDeserialize` | Loading state from node storage on every method call
|
|
37
|
-
| `BorshSerialize`
|
|
32
|
+
| Derive | Required for |
|
|
33
|
+
| ------------------ | ---------------------------------------------------------- |
|
|
34
|
+
| `Default` | `#[app::init]` calls `AppState::default()` as the baseline |
|
|
35
|
+
| `BorshDeserialize` | Loading state from node storage on every method call |
|
|
36
|
+
| `BorshSerialize` | Saving state to node storage after every mutation |
|
|
38
37
|
|
|
39
38
|
## Import
|
|
40
39
|
|
|
@@ -42,5 +41,5 @@ pub struct AppState {
|
|
|
42
41
|
use calimero_sdk::borsh::{BorshDeserialize, BorshSerialize};
|
|
43
42
|
```
|
|
44
43
|
|
|
45
|
-
Do not use `borsh` crate directly — import from `calimero_sdk::borsh` to ensure
|
|
46
|
-
|
|
44
|
+
Do not use `borsh` crate directly — import from `calimero_sdk::borsh` to ensure version
|
|
45
|
+
compatibility.
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
Calimero apps run in a sandboxed WASM environment. The following are **not available**:
|
|
4
4
|
|
|
5
|
-
| Forbidden
|
|
6
|
-
|
|
|
7
|
-
| `std::thread::spawn`
|
|
5
|
+
| Forbidden | Use instead |
|
|
6
|
+
| --------------------------------------------- | ----------------------------------------------- |
|
|
7
|
+
| `std::thread::spawn` | Not supported — WASM is single-threaded |
|
|
8
8
|
| `tokio`, `async-std`, `async fn` in app logic | Not supported — all app methods are synchronous |
|
|
9
|
-
| `std::fs`, file I/O
|
|
10
|
-
| `std::net`, HTTP calls outbound
|
|
11
|
-
| `println!`, `eprintln!`
|
|
12
|
-
| System time (`std::time::SystemTime`)
|
|
13
|
-
| Environment variables
|
|
14
|
-
| Random number generation via `rand`
|
|
9
|
+
| `std::fs`, file I/O | Use `env::private_storage_write/read` |
|
|
10
|
+
| `std::net`, HTTP calls outbound | Not supported in WASM runtime |
|
|
11
|
+
| `println!`, `eprintln!` | Use `calimero_sdk::env::log(&str)` |
|
|
12
|
+
| System time (`std::time::SystemTime`) | Use `calimero_sdk::env::block_timestamp()` |
|
|
13
|
+
| Environment variables | Not accessible from WASM |
|
|
14
|
+
| Random number generation via `rand` | Use `calimero_sdk::env::random_seed()` |
|
|
15
15
|
|
|
16
16
|
## Logging
|
|
17
17
|
|
|
@@ -32,4 +32,6 @@ let ts = env::block_timestamp(); // u64 nanoseconds
|
|
|
32
32
|
|
|
33
33
|
## Why
|
|
34
34
|
|
|
35
|
-
The `merod` WASM runtime is deterministic by design. Non-deterministic operations (threads, I/O,
|
|
35
|
+
The `merod` WASM runtime is deterministic by design. Non-deterministic operations (threads, I/O,
|
|
36
|
+
time, random) would break the CRDT synchronization guarantees — every node must reach the same state
|
|
37
|
+
given the same sequence of operations.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# calimero-sdk-js — Agent Instructions
|
|
2
2
|
|
|
3
3
|
You are helping a developer **build a Calimero P2P application in TypeScript** using
|
|
4
|
-
`@calimero-network/calimero-sdk-js`. The app compiles to WebAssembly and runs inside the
|
|
5
|
-
|
|
4
|
+
`@calimero-network/calimero-sdk-js`. The app compiles to WebAssembly and runs inside the `merod`
|
|
5
|
+
node runtime.
|
|
6
6
|
|
|
7
|
-
> **NOT this skill** if the developer is connecting a browser/Node.js
|
|
8
|
-
>
|
|
9
|
-
>
|
|
7
|
+
> **NOT this skill** if the developer is connecting a browser/Node.js _frontend_ to a node (that's
|
|
8
|
+
> `calimero-client-js` / `@calimero-network/calimero-client`). This skill is for writing the
|
|
9
|
+
> application logic that _runs on the node_.
|
|
10
10
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
@@ -15,18 +15,18 @@ pnpm add @calimero-network/calimero-sdk-js
|
|
|
15
15
|
pnpm add -D @calimero-network/calimero-cli-js typescript
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
The CLI's `postinstall` hook downloads QuickJS, WASI-SDK, and Binaryen automatically.
|
|
19
|
-
|
|
18
|
+
The CLI's `postinstall` hook downloads QuickJS, WASI-SDK, and Binaryen automatically. If you used
|
|
19
|
+
`--ignore-scripts`, re-run with `pnpm install --ignore-scripts=false`.
|
|
20
20
|
|
|
21
21
|
## Core concepts
|
|
22
22
|
|
|
23
|
-
| Concept
|
|
24
|
-
|
|
|
25
|
-
| `@State` class
|
|
26
|
-
| `@Logic(StateClass)` class | Entry points callable via JSON-RPC; must extend the state class
|
|
27
|
-
| `@Init` static method
|
|
28
|
-
| `@View()` method
|
|
29
|
-
| CRDT collection
|
|
23
|
+
| Concept | What it is |
|
|
24
|
+
| -------------------------- | -------------------------------------------------------------------------------------- |
|
|
25
|
+
| `@State` class | Persisted data — fields must be CRDT types |
|
|
26
|
+
| `@Logic(StateClass)` class | Entry points callable via JSON-RPC; must extend the state class |
|
|
27
|
+
| `@Init` static method | Seeds the initial state when context is first created |
|
|
28
|
+
| `@View()` method | Read-only — skips persistence; required for query methods |
|
|
29
|
+
| CRDT collection | Conflict-free type (`Counter`, `UnorderedMap`, etc.) — all state fields must use these |
|
|
30
30
|
|
|
31
31
|
## Minimal app
|
|
32
32
|
|
|
@@ -61,16 +61,15 @@ export class KvLogic extends KvState {
|
|
|
61
61
|
|
|
62
62
|
## CRDT collections quick reference
|
|
63
63
|
|
|
64
|
-
| Type
|
|
65
|
-
|
|
|
66
|
-
| `Counter`
|
|
67
|
-
| `UnorderedMap<K,V>` | Key-value store (LWW per key)
|
|
68
|
-
| `UnorderedSet<T>`
|
|
69
|
-
| `Vector<T>`
|
|
70
|
-
| `LwwRegister<T>`
|
|
64
|
+
| Type | Use case | Key ops |
|
|
65
|
+
| ------------------- | --------------------------------------- | -------------------------------------------------- |
|
|
66
|
+
| `Counter` | Distributed counting (returns `bigint`) | `increment()`, `incrementBy(n)`, `value()` |
|
|
67
|
+
| `UnorderedMap<K,V>` | Key-value store (LWW per key) | `set()`, `get()`, `has()`, `remove()`, `entries()` |
|
|
68
|
+
| `UnorderedSet<T>` | Unique membership (LWW per element) | `add()`, `has()`, `delete()`, `toArray()` |
|
|
69
|
+
| `Vector<T>` | Ordered list | `push()`, `get(i)`, `pop()`, `len()` |
|
|
70
|
+
| `LwwRegister<T>` | Single value (timestamp LWW) | `set()`, `get()` |
|
|
71
71
|
|
|
72
|
-
Nested collections (`Map<K, Set<V>>`) propagate changes automatically — no manual
|
|
73
|
-
re-serialization.
|
|
72
|
+
Nested collections (`Map<K, Set<V>>`) propagate changes automatically — no manual re-serialization.
|
|
74
73
|
|
|
75
74
|
## Build & deploy
|
|
76
75
|
|
|
@@ -105,7 +104,8 @@ meroctl --node-name <NODE> call \
|
|
|
105
104
|
- `@Init` must be a static method that returns the state class instance
|
|
106
105
|
- `@Logic(StateClass)` must extend the state class
|
|
107
106
|
- No async, no I/O, no threads in app logic — the WASM runtime is synchronous
|
|
108
|
-
- **Windows: building is not supported natively — use WSL** (QuickJS/WASI-SDK toolchain requires
|
|
107
|
+
- **Windows: building is not supported natively — use WSL** (QuickJS/WASI-SDK toolchain requires
|
|
108
|
+
Linux/macOS)
|
|
109
109
|
|
|
110
110
|
## Events
|
|
111
111
|
|
|
@@ -131,7 +131,15 @@ const val = secret.get();
|
|
|
131
131
|
|
|
132
132
|
Private entries are never broadcast to other nodes.
|
|
133
133
|
|
|
134
|
+
## Related skills
|
|
135
|
+
|
|
136
|
+
- **`calimero-core`** — runtime concepts (context/app model, JSON-RPC protocol, WebSocket events,
|
|
137
|
+
CRDT type taxonomy)
|
|
138
|
+
- **`calimero-meroctl`** — full `meroctl` CLI reference for deploying and testing the app
|
|
139
|
+
- **`calimero-client-js`** — connecting a browser/React frontend to the node (not building app
|
|
140
|
+
logic)
|
|
141
|
+
|
|
134
142
|
## References
|
|
135
143
|
|
|
136
|
-
See `references/` for CRDT collections, events, and build pipeline details.
|
|
137
|
-
|
|
144
|
+
See `references/` for CRDT collections, events, and build pipeline details. See `rules/` for hard
|
|
145
|
+
constraints.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## Pipeline stages
|
|
6
6
|
|
|
7
|
-
```
|
|
7
|
+
```text
|
|
8
8
|
TypeScript → Rollup → QuickJS → WASI-SDK → Binaryen → .wasm
|
|
9
9
|
Source Bundle C bytecode WASM Optimized
|
|
10
10
|
```
|
|
@@ -84,11 +84,11 @@ merobox bootstrap run examples/counter/workflows/counter-js.yml --log-level=trac
|
|
|
84
84
|
|
|
85
85
|
## Platform support
|
|
86
86
|
|
|
87
|
-
| Platform
|
|
88
|
-
|
|
|
89
|
-
| macOS (x64, arm64) | Yes
|
|
90
|
-
| Linux (x64, arm64) | Yes
|
|
91
|
-
| Windows (native)
|
|
87
|
+
| Platform | Supported |
|
|
88
|
+
| ------------------ | ------------ |
|
|
89
|
+
| macOS (x64, arm64) | Yes |
|
|
90
|
+
| Linux (x64, arm64) | Yes |
|
|
91
|
+
| Windows (native) | No — use WSL |
|
|
92
92
|
|
|
93
93
|
## Definition of done (before PR)
|
|
94
94
|
|
|
@@ -11,9 +11,9 @@ Distributed counting. The value is the sum across all contributing nodes.
|
|
|
11
11
|
import { Counter } from '@calimero-network/calimero-sdk-js/collections';
|
|
12
12
|
|
|
13
13
|
const counter = new Counter();
|
|
14
|
-
counter.increment();
|
|
15
|
-
counter.incrementBy(5n);
|
|
16
|
-
const total = counter.value();
|
|
14
|
+
counter.increment(); // +1
|
|
15
|
+
counter.incrementBy(5n); // +5 (bigint)
|
|
16
|
+
const total = counter.value(); // bigint
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
> Counter values are always `bigint`. Do not compare with `=== 0` — use `=== 0n`.
|
|
@@ -27,10 +27,10 @@ import { UnorderedMap } from '@calimero-network/calimero-sdk-js/collections';
|
|
|
27
27
|
|
|
28
28
|
const map = new UnorderedMap<string, string>();
|
|
29
29
|
map.set('key', 'value');
|
|
30
|
-
const val = map.get('key');
|
|
31
|
-
const exists = map.has('key');
|
|
30
|
+
const val = map.get('key'); // 'value' | undefined
|
|
31
|
+
const exists = map.has('key'); // boolean
|
|
32
32
|
map.remove('key');
|
|
33
|
-
const all = map.entries();
|
|
33
|
+
const all = map.entries(); // [['key', 'value'], ...]
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
## UnorderedSet\<T\>
|
|
@@ -41,8 +41,8 @@ Set of unique values with Last-Write-Wins per element.
|
|
|
41
41
|
import { UnorderedSet } from '@calimero-network/calimero-sdk-js/collections';
|
|
42
42
|
|
|
43
43
|
const set = new UnorderedSet<string>();
|
|
44
|
-
set.add('item');
|
|
45
|
-
set.has('item');
|
|
44
|
+
set.add('item'); // true on first insert, false if already present
|
|
45
|
+
set.has('item'); // true
|
|
46
46
|
set.delete('item');
|
|
47
47
|
const items = set.toArray();
|
|
48
48
|
```
|
|
@@ -57,9 +57,9 @@ import { Vector } from '@calimero-network/calimero-sdk-js/collections';
|
|
|
57
57
|
const vec = new Vector<string>();
|
|
58
58
|
vec.push('first');
|
|
59
59
|
vec.push('second');
|
|
60
|
-
const item = vec.get(0);
|
|
61
|
-
const last = vec.pop();
|
|
62
|
-
const len = vec.len();
|
|
60
|
+
const item = vec.get(0); // 'first'
|
|
61
|
+
const last = vec.pop(); // 'second'
|
|
62
|
+
const len = vec.len(); // 1 (bigint)
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
## LwwRegister\<T\>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Events
|
|
2
2
|
|
|
3
|
-
Events let your app push real-time notifications to all context members.
|
|
4
|
-
|
|
3
|
+
Events let your app push real-time notifications to all context members. They are emitted during
|
|
4
|
+
mutation methods and received by clients via WebSocket.
|
|
5
5
|
|
|
6
6
|
## Emitting events
|
|
7
7
|
|
|
@@ -22,7 +22,11 @@ Events can only be emitted inside mutation methods (not `@View()` methods).
|
|
|
22
22
|
Clients use `WsSubscriptionsClient` from `@calimero-network/calimero-client`:
|
|
23
23
|
|
|
24
24
|
```typescript
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
WsSubscriptionsClient,
|
|
27
|
+
getAppEndpointKey,
|
|
28
|
+
getContextId,
|
|
29
|
+
} from '@calimero-network/calimero-client';
|
|
26
30
|
|
|
27
31
|
const ws = new WsSubscriptionsClient(getAppEndpointKey()!, '/ws');
|
|
28
32
|
await ws.connect();
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# Rule: All @State fields must be CRDT types
|
|
2
2
|
|
|
3
3
|
Every field on a `@State` class must be a CRDT collection from
|
|
4
|
-
`@calimero-network/calimero-sdk-js/collections`. Plain JavaScript types are not
|
|
5
|
-
|
|
4
|
+
`@calimero-network/calimero-sdk-js/collections`. Plain JavaScript types are not persisted or
|
|
5
|
+
synchronized.
|
|
6
6
|
|
|
7
7
|
## WRONG — plain JS types in state:
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
10
|
@State
|
|
11
11
|
export class AppState {
|
|
12
|
-
items: Map<string, string> = new Map();
|
|
13
|
-
tags: string[] = [];
|
|
14
|
-
name: string = '';
|
|
15
|
-
count: number = 0;
|
|
12
|
+
items: Map<string, string> = new Map(); // ✗ — not a CRDT, not synced
|
|
13
|
+
tags: string[] = []; // ✗ — not a CRDT, not synced
|
|
14
|
+
name: string = ''; // ✗ — use LwwRegister instead
|
|
15
|
+
count: number = 0; // ✗ — use Counter instead
|
|
16
16
|
}
|
|
17
17
|
```
|
|
18
18
|
|
|
@@ -30,18 +30,18 @@ export class AppState {
|
|
|
30
30
|
|
|
31
31
|
## Why this matters
|
|
32
32
|
|
|
33
|
-
Plain JS types are not serialized to the node's CRDT storage. State that uses
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
Plain JS types are not serialized to the node's CRDT storage. State that uses `Map`, `Set`, `Array`,
|
|
34
|
+
or primitives directly will be lost on the next call and will never sync to other nodes in the
|
|
35
|
+
context.
|
|
36
36
|
|
|
37
37
|
## Choosing the right CRDT
|
|
38
38
|
|
|
39
|
-
| Data shape
|
|
40
|
-
|
|
|
41
|
-
| Counting (monotonic)
|
|
42
|
-
| Key-value lookup
|
|
43
|
-
| Unique membership
|
|
44
|
-
| Ordered list
|
|
45
|
-
| Single value (overwrites) | `LwwRegister<T>`
|
|
46
|
-
| User-owned signed data
|
|
47
|
-
| Immutable content
|
|
39
|
+
| Data shape | Use |
|
|
40
|
+
| ------------------------- | -------------------- |
|
|
41
|
+
| Counting (monotonic) | `Counter` |
|
|
42
|
+
| Key-value lookup | `UnorderedMap<K, V>` |
|
|
43
|
+
| Unique membership | `UnorderedSet<T>` |
|
|
44
|
+
| Ordered list | `Vector<T>` |
|
|
45
|
+
| Single value (overwrites) | `LwwRegister<T>` |
|
|
46
|
+
| User-owned signed data | `UserStorage<T>` |
|
|
47
|
+
| Immutable content | `FrozenStorage<T>` |
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Rule: Use env.log() — not console.log()
|
|
2
2
|
|
|
3
|
-
`console` is not available in the QuickJS/WASM runtime. Calling `console.log()` will
|
|
4
|
-
|
|
3
|
+
`console` is not available in the QuickJS/WASM runtime. Calling `console.log()` will throw a runtime
|
|
4
|
+
error. Use `env.log()` from the SDK's env module instead.
|
|
5
5
|
|
|
6
6
|
## WRONG:
|
|
7
7
|
|
|
8
8
|
```typescript
|
|
9
|
-
console.log('Processing item:', key);
|
|
10
|
-
console.error('Something went wrong');
|
|
9
|
+
console.log('Processing item:', key); // ✗ — throws at runtime
|
|
10
|
+
console.error('Something went wrong'); // ✗ — throws at runtime
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## CORRECT:
|
|
@@ -15,8 +15,8 @@ console.error('Something went wrong'); // ✗ — throws at runtime
|
|
|
15
15
|
```typescript
|
|
16
16
|
import * as env from '@calimero-network/calimero-sdk-js/env';
|
|
17
17
|
|
|
18
|
-
env.log(`Processing item: ${key}`);
|
|
19
|
-
env.log(`Error: ${error}`);
|
|
18
|
+
env.log(`Processing item: ${key}`); // ✓ — output appears in node logs
|
|
19
|
+
env.log(`Error: ${error}`); // ✓
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## env.log() format
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Rule: @View() is required on read-only methods
|
|
2
2
|
|
|
3
|
-
Every method that does not modify state must be decorated with `@View()`. Without it,
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Every method that does not modify state must be decorated with `@View()`. Without it, the runtime
|
|
4
|
+
will persist state after every call — even when nothing changed — causing unnecessary storage writes
|
|
5
|
+
and cross-node syncs.
|
|
6
6
|
|
|
7
7
|
## WRONG — read-only method without @View():
|
|
8
8
|
|
|
@@ -41,6 +41,8 @@ export class AppLogic extends AppState {
|
|
|
41
41
|
## How to tell if a method needs @View()
|
|
42
42
|
|
|
43
43
|
A method is read-only if it:
|
|
44
|
-
|
|
44
|
+
|
|
45
|
+
- Does not call any mutation methods on CRDT fields (`set`, `insert`, `add`, `push`, `increment`,
|
|
46
|
+
etc.)
|
|
45
47
|
- Does not call `emit()` or `emitWithHandler()`
|
|
46
48
|
- Only reads values and returns results
|