dignity.js 0.3.0 → 0.5.1

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 (43) hide show
  1. package/README.md +142 -4
  2. package/dist/dignity.cjs.js +768 -20
  3. package/dist/dignity.cjs.js.map +4 -4
  4. package/dist/dignity.esm.js +768 -20
  5. package/dist/dignity.esm.js.map +3 -3
  6. package/dist/dignity.min.js +18 -18
  7. package/docs/assets/dignity.esm.js +11205 -0
  8. package/docs/assets/docs.js +47 -0
  9. package/docs/assets/favicon.svg +8 -0
  10. package/docs/assets/highlight/github-dark.min.css +10 -0
  11. package/docs/assets/highlight/github.min.css +10 -0
  12. package/docs/assets/highlight/highlight.min.js +1244 -0
  13. package/docs/assets/styles.css +449 -38
  14. package/docs/chess/assets/chess-app.js +58022 -0
  15. package/docs/chess/assets/chess-app.js.map +7 -0
  16. package/docs/chess/assets/chess.css +584 -0
  17. package/docs/chess/favicon.ico +0 -0
  18. package/docs/chess/index.html +16 -0
  19. package/docs/chess/src/App.jsx +128 -0
  20. package/docs/chess/src/components/Board3D.jsx +364 -0
  21. package/docs/chess/src/components/GameView.jsx +847 -0
  22. package/docs/chess/src/components/JoinGate.jsx +68 -0
  23. package/docs/chess/src/components/LinkPanel.jsx +132 -0
  24. package/docs/chess/src/components/Lobby.jsx +154 -0
  25. package/docs/chess/src/components/MovePanel.jsx +123 -0
  26. package/docs/chess/src/lib/audio.js +50 -0
  27. package/docs/chess/src/lib/dignitySetup.js +42 -0
  28. package/docs/chess/src/lib/links.js +124 -0
  29. package/docs/chess/src/lib/localGames.js +160 -0
  30. package/docs/chess/src/lib/p2pDebug.js +192 -0
  31. package/docs/chess/src/main.jsx +5 -0
  32. package/docs/favicon.ico +0 -0
  33. package/docs/index.html +605 -81
  34. package/docs/openapi-like.json +74 -7
  35. package/examples/decentralized-chess-lite.js +52 -30
  36. package/package.json +30 -4
  37. package/src/core/dignity-p2p.js +466 -15
  38. package/src/index.js +8 -0
  39. package/src/network/peerjs-network.js +234 -0
  40. package/src/persistence/indexeddb-persistence.js +184 -0
  41. package/src/react/index.js +256 -0
  42. package/src/signaling/parse-peerjs-url.js +24 -0
  43. package/src/signaling/peerjs-signaling-provider.js +2 -8
package/README.md CHANGED
@@ -11,10 +11,12 @@
11
11
  </p>
12
12
 
13
13
  <p align="center">
14
+ <a href="https://jose-compu.github.io/dignity.js/"><img src="https://img.shields.io/badge/docs-online-5B7FFF" alt="documentation"></a>
14
15
  <a href="https://www.npmjs.com/package/dignity.js"><img src="https://img.shields.io/npm/v/dignity.js?color=cb3837&label=npm" alt="npm version"></a>
15
16
  <a href="https://www.npmjs.com/package/dignity.js"><img src="https://img.shields.io/npm/dm/dignity.js?color=blue" alt="npm downloads"></a>
16
- <img src="https://img.shields.io/badge/tests-122%20passing-brightgreen" alt="tests passing">
17
- <img src="https://img.shields.io/badge/coverage-97%25-brightgreen" alt="coverage">
17
+ <a href="https://github.com/jose-compu/dignity.js/actions/workflows/ci.yml"><img src="https://github.com/jose-compu/dignity.js/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
18
+ <img src="https://img.shields.io/badge/tests-150%2B%20passing-brightgreen" alt="tests passing">
19
+ <img src="https://img.shields.io/badge/coverage-99%25-brightgreen" alt="coverage">
18
20
  <img src="https://img.shields.io/badge/license-Apache%202.0-black" alt="license">
19
21
  </p>
20
22
 
@@ -34,6 +36,12 @@ REST-like P2P object API for decentralized JavaScript applications.
34
36
  - default `powSteps: 22` (calibrated on this machine to about 1000ms)
35
37
  - automatic peer ban on invalid signature/PoW (`48h` default)
36
38
  - Team/subapp scoped broadcast passwords (`broadcastScope` + `broadcastPasswords`)
39
+ - Optimistic concurrency helpers (`expectedVersion`, `updateWithRetry`, `conflict` events)
40
+ - PeerJS mesh bootstrap: connect before announce/broadcast, auto `publicKey` in presence
41
+ - Late-joiner sync via `pushRecordSnapshot` (full record catch-up when create was missed)
42
+ - Auto `connectToPeers` on create/update/delete replication (owner + collaborators)
43
+ - Optional IndexedDB persistence for browser reload survival
44
+ - Optional React hooks via `dignity.js/react`
37
45
  - Browser-first: published npm package includes IIFE, ESM, and CJS builds
38
46
 
39
47
  ## Install
@@ -145,6 +153,57 @@ bob.registerPeerPublicKey('alice', alice.getPublicKey());
145
153
  await alice.sendDirectMessage('bob', 'dm', { text: 'private payload' });
146
154
  ```
147
155
 
156
+ ## Optimistic Concurrency
157
+
158
+ Updates carry a monotonic `version`. Remote peers reject stale operations when `baseVersion` does not match.
159
+
160
+ ```js
161
+ node.on('conflict', (event) => {
162
+ console.log('conflict', event.phase, event.expectedVersion, event.currentVersion);
163
+ });
164
+
165
+ await node.update('games', 'g1', { score: 10 }, { expectedVersion: 3 });
166
+
167
+ await node.updateWithRetry('games', 'g1', (current) => ({
168
+ score: current.data.score + 1
169
+ }));
170
+ ```
171
+
172
+ Use `expectedVersion` for fail-fast local writes. Use `updateWithRetry` for read-modify-write loops in fast multiplayer state.
173
+
174
+ ## IndexedDB Persistence
175
+
176
+ Persist replicated collections across page reloads:
177
+
178
+ ```js
179
+ const { DignityP2P, IndexedDBPersistence } = require('dignity.js');
180
+
181
+ const node = new DignityP2P({ nodeId, networkAdapter, security });
182
+ const persistence = new IndexedDBPersistence({
183
+ dbName: 'my-app',
184
+ collections: ['games', 'matches']
185
+ });
186
+
187
+ await node.start();
188
+ await persistence.attach(node);
189
+ ```
190
+
191
+ ## React Hooks
192
+
193
+ Optional React integration (`react >= 18` peer dependency):
194
+
195
+ ```js
196
+ import { useDignity, useCollection, usePeers } from 'dignity.js/react';
197
+
198
+ function Room() {
199
+ const { node, status } = useDignity(config);
200
+ const games = useCollection(node, 'games');
201
+ const peers = usePeers(node, 'room:chess', { includeSelf: false });
202
+
203
+ return <pre>{JSON.stringify({ status, games, peers }, null, 2)}</pre>;
204
+ }
205
+ ```
206
+
148
207
  ## Browser Usage
149
208
 
150
209
  The published npm package includes pre-built bundles (IIFE, ESM, CJS) generated at publish time. The `dist/` folder is not checked into the repository.
@@ -182,20 +241,99 @@ Compatibility note:
182
241
  - `dignity.js` now includes a dedicated `PeerJSSignalingProvider` backed by the official `peerjs` client for PeerJS protocol compatibility.
183
242
  - In non-WebRTC runtimes (for example Node test runners), it automatically falls back to WebSocket transport checks for connectivity testing.
184
243
 
244
+ ### PeerJS mesh bootstrap
245
+
246
+ Unlike the in-memory test adapter (which fan-outs to every registered node), **PeerJS only delivers messages over open data channels**. Discovery broadcasts do not reach anyone until at least one side has connected.
247
+
248
+ For browser apps (see the bundled 3D chess demo), pass a known peer id from your invite link:
249
+
250
+ ```js
251
+ await node.joinDiscovery('room:my-game', {
252
+ metadata: { nickname: 'alice', role: 'host' },
253
+ bootstrapPeerIds: ['host-peer-id-from-link']
254
+ });
255
+
256
+ await node.broadcastMessage('claim-seat', payload, {
257
+ broadcastScope: 'room:my-game',
258
+ connectToPeers: ['host-peer-id-from-link']
259
+ });
260
+ ```
261
+
262
+ Library helpers:
263
+
264
+ - `node.connectToPeer(peerId)` — open a PeerJS data channel
265
+ - `node.getConnectionStats()` — `{ openCount, peerIds }`
266
+ - `node.getRecordPeerIds(collection, id)` — owner + collaborators (for custom broadcasts)
267
+ - `node.joinDiscovery(scope, { bootstrapPeerIds })` — connect before the first presence announce
268
+ - `broadcastMessage(..., { connectToPeers })` — connect, then broadcast
269
+ - `node.pushRecordSnapshot(collection, id, options)` — send full record state to late joiners
270
+ - `create` / `update` / `remove` auto-connect to record peers when `connectToPeers` is omitted
271
+ - Presence metadata automatically includes `publicKey`; remote keys are trusted from presence and message envelopes (direct messages work without manual `registerPeerPublicKey`)
272
+
273
+ React: `useRoom(node, scope, options)` combines discovery, peers, and connection stats.
274
+
275
+ ### Late joiners (missed create)
276
+
277
+ On PeerJS, a peer that comes online **after** the host creates an object never receives the initial `create` operation. Later `update` operations are ignored until that peer has a local copy of the record.
278
+
279
+ After accepting a joiner (or on `orphan-operation` warnings), push a full snapshot:
280
+
281
+ ```js
282
+ node.on('warning', (event) => {
283
+ if (event.type === 'orphan-operation') {
284
+ // optional: request resync from owner
285
+ }
286
+ });
287
+
288
+ await host.update('chess-matches', gameId, { blackPlayerId: joinerId, status: 'playing' }, {
289
+ collaborators: [hostId, joinerId],
290
+ broadcastScope: scope
291
+ });
292
+
293
+ await host.pushRecordSnapshot('chess-matches', gameId, {
294
+ broadcastScope: scope,
295
+ connectToPeers: [joinerId]
296
+ });
297
+ ```
298
+
299
+ The joiner applies the snapshot via `restoreRecord`, then subsequent move updates replicate normally.
300
+
185
301
  ## Development
186
302
 
187
303
  ```bash
188
304
  npm test
189
305
  npm run build
190
- npm run docs:serve
306
+ npm run docs:dev # docs + 3D chess at http://127.0.0.1:4173/
307
+ npm run docs:build # rebuild chess bundle only
191
308
  npm run example:tictactoe
192
309
  npm run example:chess
193
310
  npm run test:pow-calibrate
194
311
  ```
195
312
 
313
+ Local docs (auto-builds chess if `docs/chess/assets/chess-app.js` is missing):
314
+
315
+ ```bash
316
+ npm run docs:dev
317
+ # Docs: http://127.0.0.1:4173/
318
+ # Chess: http://127.0.0.1:4173/chess/
319
+ ```
320
+
321
+ Use `DOCS_NO_OPEN=1 npm run docs:dev` to skip opening the browser, or `DOCS_PORT=8080` for another port.
322
+
323
+ If port 4173 is stuck from an old session:
324
+
325
+ ```bash
326
+ npm run docs:stop
327
+ npm run docs:dev
328
+ ```
329
+
330
+ If 4173 is busy, `docs:dev` auto-picks the next free port (4174, 4175, …) and prints the URLs.
331
+
196
332
  ## Docs and Examples
197
333
 
198
- - Docs site source: `docs/index.html`
334
+ - **Documentation:** [jose-compu.github.io/dignity.js](https://jose-compu.github.io/dignity.js/)
335
+ - Docs site source: `docs/index.html` (local: `npm run docs:dev`)
336
+ - **3D Chess demo:** `docs/chess/` → [local chess demo](http://127.0.0.1:4173/chess/) when `docs:dev` is running
199
337
  - API metadata: `docs/openapi-like.json`
200
338
  - Minimal demos:
201
339
  - `examples/decentralized-tictactoe.js`