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.
- package/README.md +142 -4
- package/dist/dignity.cjs.js +768 -20
- package/dist/dignity.cjs.js.map +4 -4
- package/dist/dignity.esm.js +768 -20
- package/dist/dignity.esm.js.map +3 -3
- package/dist/dignity.min.js +18 -18
- package/docs/assets/dignity.esm.js +11205 -0
- package/docs/assets/docs.js +47 -0
- package/docs/assets/favicon.svg +8 -0
- package/docs/assets/highlight/github-dark.min.css +10 -0
- package/docs/assets/highlight/github.min.css +10 -0
- package/docs/assets/highlight/highlight.min.js +1244 -0
- package/docs/assets/styles.css +449 -38
- package/docs/chess/assets/chess-app.js +58022 -0
- package/docs/chess/assets/chess-app.js.map +7 -0
- package/docs/chess/assets/chess.css +584 -0
- package/docs/chess/favicon.ico +0 -0
- package/docs/chess/index.html +16 -0
- package/docs/chess/src/App.jsx +128 -0
- package/docs/chess/src/components/Board3D.jsx +364 -0
- package/docs/chess/src/components/GameView.jsx +847 -0
- package/docs/chess/src/components/JoinGate.jsx +68 -0
- package/docs/chess/src/components/LinkPanel.jsx +132 -0
- package/docs/chess/src/components/Lobby.jsx +154 -0
- package/docs/chess/src/components/MovePanel.jsx +123 -0
- package/docs/chess/src/lib/audio.js +50 -0
- package/docs/chess/src/lib/dignitySetup.js +42 -0
- package/docs/chess/src/lib/links.js +124 -0
- package/docs/chess/src/lib/localGames.js +160 -0
- package/docs/chess/src/lib/p2pDebug.js +192 -0
- package/docs/chess/src/main.jsx +5 -0
- package/docs/favicon.ico +0 -0
- package/docs/index.html +605 -81
- package/docs/openapi-like.json +74 -7
- package/examples/decentralized-chess-lite.js +52 -30
- package/package.json +30 -4
- package/src/core/dignity-p2p.js +466 -15
- package/src/index.js +8 -0
- package/src/network/peerjs-network.js +234 -0
- package/src/persistence/indexeddb-persistence.js +184 -0
- package/src/react/index.js +256 -0
- package/src/signaling/parse-peerjs-url.js +24 -0
- 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://
|
|
17
|
-
<img src="https://img.shields.io/badge/
|
|
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:
|
|
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
|
-
-
|
|
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`
|