@enbox/api 0.2.2 → 0.2.4
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 +235 -35
- package/dist/browser.mjs +13 -13
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/dwn-api.js +24 -10
- package/dist/esm/dwn-api.js.map +1 -1
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/live-query.js +34 -5
- package/dist/esm/live-query.js.map +1 -1
- package/dist/esm/permission-grant.js +3 -6
- package/dist/esm/permission-grant.js.map +1 -1
- package/dist/esm/permission-request.js +4 -7
- package/dist/esm/permission-request.js.map +1 -1
- package/dist/esm/record-data.js +131 -0
- package/dist/esm/record-data.js.map +1 -0
- package/dist/esm/record-types.js +9 -0
- package/dist/esm/record-types.js.map +1 -0
- package/dist/esm/record.js +58 -184
- package/dist/esm/record.js.map +1 -1
- package/dist/esm/repository-types.js +13 -0
- package/dist/esm/repository-types.js.map +1 -0
- package/dist/esm/repository.js +347 -0
- package/dist/esm/repository.js.map +1 -0
- package/dist/esm/typed-live-query.js +101 -0
- package/dist/esm/typed-live-query.js.map +1 -0
- package/dist/esm/typed-record.js +227 -0
- package/dist/esm/typed-record.js.map +1 -0
- package/dist/esm/typed-web5.js +134 -37
- package/dist/esm/typed-web5.js.map +1 -1
- package/dist/esm/web5.js +78 -20
- package/dist/esm/web5.js.map +1 -1
- package/dist/types/dwn-api.d.ts.map +1 -1
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/live-query.d.ts +43 -4
- package/dist/types/live-query.d.ts.map +1 -1
- package/dist/types/permission-grant.d.ts +1 -1
- package/dist/types/permission-grant.d.ts.map +1 -1
- package/dist/types/permission-request.d.ts +1 -1
- package/dist/types/permission-request.d.ts.map +1 -1
- package/dist/types/record-data.d.ts +49 -0
- package/dist/types/record-data.d.ts.map +1 -0
- package/dist/types/record-types.d.ts +145 -0
- package/dist/types/record-types.d.ts.map +1 -0
- package/dist/types/record.d.ts +13 -144
- package/dist/types/record.d.ts.map +1 -1
- package/dist/types/repository-types.d.ts +137 -0
- package/dist/types/repository-types.d.ts.map +1 -0
- package/dist/types/repository.d.ts +59 -0
- package/dist/types/repository.d.ts.map +1 -0
- package/dist/types/typed-live-query.d.ts +86 -0
- package/dist/types/typed-live-query.d.ts.map +1 -0
- package/dist/types/typed-record.d.ts +179 -0
- package/dist/types/typed-record.d.ts.map +1 -0
- package/dist/types/typed-web5.d.ts +55 -24
- package/dist/types/typed-web5.d.ts.map +1 -1
- package/dist/types/web5.d.ts +47 -2
- package/dist/types/web5.d.ts.map +1 -1
- package/package.json +8 -7
- package/src/dwn-api.ts +30 -13
- package/src/index.ts +6 -0
- package/src/live-query.ts +71 -7
- package/src/permission-grant.ts +2 -3
- package/src/permission-request.ts +3 -4
- package/src/record-data.ts +155 -0
- package/src/record-types.ts +188 -0
- package/src/record.ts +86 -389
- package/src/repository-types.ts +249 -0
- package/src/repository.ts +391 -0
- package/src/typed-live-query.ts +156 -0
- package/src/typed-record.ts +309 -0
- package/src/typed-web5.ts +206 -53
- package/src/web5.ts +150 -23
package/README.md
CHANGED
|
@@ -17,6 +17,10 @@ The high-level SDK for building decentralized applications with protocol-first d
|
|
|
17
17
|
- [Record Instances](#record-instances)
|
|
18
18
|
- [LiveQuery (Subscriptions)](#livequery-subscriptions)
|
|
19
19
|
- [Web5.anonymous()](#web5anonymousoptions)
|
|
20
|
+
- [Repository Pattern](#repository-pattern)
|
|
21
|
+
- [Collections vs Singletons](#collections-vs-singletons)
|
|
22
|
+
- [Nested Records](#nested-records-1)
|
|
23
|
+
- [Using Pre-built Protocols](#using-pre-built-protocols)
|
|
20
24
|
- [Cookbook](#cookbook)
|
|
21
25
|
- [Nested Records](#nested-records)
|
|
22
26
|
- [Querying with Filters and Pagination](#querying-with-filters-and-pagination)
|
|
@@ -24,6 +28,7 @@ The high-level SDK for building decentralized applications with protocol-first d
|
|
|
24
28
|
- [Publishing Records](#publishing-records)
|
|
25
29
|
- [Reading Public Data Anonymously](#reading-public-data-anonymously)
|
|
26
30
|
- [Sending Records to Remote DWNs](#sending-records-to-remote-dwns)
|
|
31
|
+
- [Code Generation](#code-generation)
|
|
27
32
|
- [Advanced Usage](#advanced-usage)
|
|
28
33
|
- [Unscoped DWN Access](#unscoped-dwn-access)
|
|
29
34
|
- [Permissions](#permissions)
|
|
@@ -68,15 +73,16 @@ const notes = web5.using(NotesProtocol);
|
|
|
68
73
|
// 4. Install the protocol on the local DWN
|
|
69
74
|
await notes.configure();
|
|
70
75
|
|
|
71
|
-
// 5.
|
|
72
|
-
const { record } = await notes.records.
|
|
76
|
+
// 5. Create a record -- path, data, and schema are type-checked
|
|
77
|
+
const { record } = await notes.records.create('note', {
|
|
73
78
|
data: { title: 'Hello', body: 'World' },
|
|
74
79
|
});
|
|
75
80
|
|
|
76
|
-
// 6. Query records back
|
|
81
|
+
// 6. Query records back -- data is typed automatically
|
|
77
82
|
const { records } = await notes.records.query('note');
|
|
78
83
|
for (const r of records) {
|
|
79
|
-
|
|
84
|
+
const note = await r.data.json(); // { title: string; body: string }
|
|
85
|
+
console.log(r.id, note.title);
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
// 7. Send to your remote DWN
|
|
@@ -169,22 +175,33 @@ Installs the protocol on the local DWN. If already installed with an identical d
|
|
|
169
175
|
await chat.configure();
|
|
170
176
|
```
|
|
171
177
|
|
|
172
|
-
#### `records.
|
|
178
|
+
#### `records.create(path, request)`
|
|
173
179
|
|
|
174
|
-
|
|
180
|
+
Create a new record at a protocol path. The protocol URI, protocolPath, schema, and dataFormat are automatically injected. Returns a `TypedRecord<T>` where `T` is inferred from the schema map.
|
|
175
181
|
|
|
176
182
|
```ts
|
|
177
|
-
const { record, status } = await chat.records.
|
|
183
|
+
const { record, status } = await chat.records.create('thread', {
|
|
178
184
|
data: { title: 'General', description: 'General discussion' },
|
|
179
185
|
});
|
|
180
186
|
|
|
181
187
|
console.log(status.code); // 202
|
|
182
188
|
console.log(record.id); // unique record ID
|
|
189
|
+
|
|
190
|
+
// record is TypedRecord<{ title: string; description?: string }>
|
|
191
|
+
const data = await record.data.json(); // typed -- no cast needed
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
To mutate an existing record, use the instance method `record.update()`:
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
const { record: updated } = await record.update({
|
|
198
|
+
data: { title: 'Updated Title', description: 'New description' },
|
|
199
|
+
});
|
|
183
200
|
```
|
|
184
201
|
|
|
185
202
|
#### `records.query(path, request?)`
|
|
186
203
|
|
|
187
|
-
Query records at a protocol path. Returns
|
|
204
|
+
Query records at a protocol path. Returns `TypedRecord<T>[]` with optional pagination.
|
|
188
205
|
|
|
189
206
|
```ts
|
|
190
207
|
const { records, cursor } = await chat.records.query('thread', {
|
|
@@ -192,8 +209,10 @@ const { records, cursor } = await chat.records.query('thread', {
|
|
|
192
209
|
pagination : { limit: 20 },
|
|
193
210
|
});
|
|
194
211
|
|
|
212
|
+
// records is TypedRecord<{ title: string; description?: string }>[]
|
|
195
213
|
for (const thread of records) {
|
|
196
|
-
|
|
214
|
+
const data = await thread.data.json(); // typed automatically
|
|
215
|
+
console.log(data.title);
|
|
197
216
|
}
|
|
198
217
|
|
|
199
218
|
// Fetch next page
|
|
@@ -228,17 +247,18 @@ const { status } = await chat.records.delete('thread', {
|
|
|
228
247
|
|
|
229
248
|
#### `records.subscribe(path, request?)`
|
|
230
249
|
|
|
231
|
-
Subscribe to real-time changes. Returns a `
|
|
250
|
+
Subscribe to real-time changes. Returns a `TypedLiveQuery<T>` with an initial snapshot of `TypedRecord<T>[]` plus a stream of typed change events.
|
|
232
251
|
|
|
233
252
|
```ts
|
|
234
253
|
const { liveQuery } = await chat.records.subscribe('thread/message');
|
|
235
254
|
|
|
236
|
-
// Initial snapshot
|
|
255
|
+
// Initial snapshot -- typed records
|
|
237
256
|
for (const msg of liveQuery.records) {
|
|
238
|
-
|
|
257
|
+
const data = await msg.data.json(); // { text: string } -- no cast
|
|
258
|
+
console.log(data.text);
|
|
239
259
|
}
|
|
240
260
|
|
|
241
|
-
// Real-time updates
|
|
261
|
+
// Real-time updates -- typed records in handlers
|
|
242
262
|
liveQuery.on('create', (record) => console.log('new:', record.id));
|
|
243
263
|
liveQuery.on('update', (record) => console.log('updated:', record.id));
|
|
244
264
|
liveQuery.on('delete', (record) => console.log('deleted:', record.id));
|
|
@@ -254,9 +274,11 @@ const { records } = await chat.records.query('thread', {
|
|
|
254
274
|
|
|
255
275
|
---
|
|
256
276
|
|
|
257
|
-
### Record Instances
|
|
277
|
+
### Record Instances (`TypedRecord<T>`)
|
|
278
|
+
|
|
279
|
+
Methods like `create`, `query`, and `read` return `TypedRecord<T>` instances -- type-safe wrappers that preserve the data type `T` inferred from the schema map through the entire lifecycle (create, query, read, update, subscribe).
|
|
258
280
|
|
|
259
|
-
|
|
281
|
+
`TypedRecord<T>` exposes a typed `data.json()` that returns `Promise<T>` instead of `Promise<unknown>`, eliminating manual type casts. The underlying `Record` is accessible via `record.rawRecord` if needed.
|
|
260
282
|
|
|
261
283
|
**Properties**:
|
|
262
284
|
|
|
@@ -283,11 +305,11 @@ Methods like `write`, `query`, and `read` return `Record` instances.
|
|
|
283
305
|
**Data accessors** -- read the record payload in different formats:
|
|
284
306
|
|
|
285
307
|
```ts
|
|
286
|
-
const
|
|
287
|
-
const
|
|
288
|
-
const blob = await record.data.blob();
|
|
289
|
-
const bytes = await record.data.bytes();
|
|
290
|
-
const stream = await record.data.stream();
|
|
308
|
+
const obj = await record.data.json(); // T -- automatically typed from schema map
|
|
309
|
+
const text = await record.data.text(); // string
|
|
310
|
+
const blob = await record.data.blob(); // Blob
|
|
311
|
+
const bytes = await record.data.bytes(); // Uint8Array
|
|
312
|
+
const stream = await record.data.stream(); // ReadableStream
|
|
291
313
|
```
|
|
292
314
|
|
|
293
315
|
**Mutators**:
|
|
@@ -317,24 +339,25 @@ await record.import();
|
|
|
317
339
|
|
|
318
340
|
---
|
|
319
341
|
|
|
320
|
-
### LiveQuery (Subscriptions)
|
|
342
|
+
### LiveQuery (Subscriptions) -- `TypedLiveQuery<T>`
|
|
321
343
|
|
|
322
|
-
`records.subscribe()` returns a `
|
|
344
|
+
`records.subscribe()` returns a `TypedLiveQuery<T>` that provides an initial snapshot of `TypedRecord<T>[]` plus a real-time stream of deduplicated, typed change events.
|
|
323
345
|
|
|
324
346
|
```ts
|
|
325
347
|
const { liveQuery } = await chat.records.subscribe('thread/message');
|
|
326
348
|
|
|
327
|
-
// Initial snapshot
|
|
349
|
+
// Initial snapshot -- TypedRecord<MessageData>[]
|
|
328
350
|
for (const msg of liveQuery.records) {
|
|
329
|
-
|
|
351
|
+
const data = await msg.data.json(); // MessageData -- typed
|
|
352
|
+
renderMessage(data);
|
|
330
353
|
}
|
|
331
354
|
|
|
332
|
-
// Real-time changes
|
|
355
|
+
// Real-time changes -- handlers receive TypedRecord<MessageData>
|
|
333
356
|
const offCreate = liveQuery.on('create', (record) => appendMessage(record));
|
|
334
357
|
const offUpdate = liveQuery.on('update', (record) => refreshMessage(record));
|
|
335
358
|
const offDelete = liveQuery.on('delete', (record) => removeMessage(record));
|
|
336
359
|
|
|
337
|
-
// Catch-all event (receives { type, record })
|
|
360
|
+
// Catch-all event (receives { type: 'create'|'update'|'delete', record: TypedRecord<T> })
|
|
338
361
|
liveQuery.on('change', ({ type, record }) => {
|
|
339
362
|
console.log(`${type}: ${record.id}`);
|
|
340
363
|
});
|
|
@@ -346,9 +369,7 @@ offCreate();
|
|
|
346
369
|
await liveQuery.close();
|
|
347
370
|
```
|
|
348
371
|
|
|
349
|
-
`LiveQuery`
|
|
350
|
-
|
|
351
|
-
Events are automatically deduplicated against the initial snapshot -- you won't receive a `create` event for records already in the `records` array.
|
|
372
|
+
The underlying `LiveQuery` is accessible via `liveQuery.rawLiveQuery` if needed. Events are automatically deduplicated against the initial snapshot -- you won't receive a `create` event for records already in the `records` array.
|
|
352
373
|
|
|
353
374
|
---
|
|
354
375
|
|
|
@@ -419,12 +440,12 @@ const chat = web5.using(ChatProtocol);
|
|
|
419
440
|
await chat.configure();
|
|
420
441
|
|
|
421
442
|
// Create a parent thread
|
|
422
|
-
const { record: thread } = await chat.records.
|
|
443
|
+
const { record: thread } = await chat.records.create('thread', {
|
|
423
444
|
data: { title: 'General' },
|
|
424
445
|
});
|
|
425
446
|
|
|
426
|
-
//
|
|
427
|
-
const { record: msg } = await chat.records.
|
|
447
|
+
// Create a message nested under the thread
|
|
448
|
+
const { record: msg } = await chat.records.create('thread/message', {
|
|
428
449
|
parentContextId : thread.contextId,
|
|
429
450
|
data : { text: 'Hello, world!' },
|
|
430
451
|
});
|
|
@@ -468,7 +489,7 @@ const { records: remote } = await notes.records.query('note', {
|
|
|
468
489
|
Tags are key-value metadata attached to records, useful for filtering without parsing record data.
|
|
469
490
|
|
|
470
491
|
```ts
|
|
471
|
-
const { record } = await notes.records.
|
|
492
|
+
const { record } = await notes.records.create('note', {
|
|
472
493
|
data : { title: 'Meeting Notes', body: '...' },
|
|
473
494
|
tags : { category: 'work', priority: 'high' },
|
|
474
495
|
});
|
|
@@ -486,7 +507,7 @@ const { records } = await notes.records.query('note', {
|
|
|
486
507
|
Published records are publicly readable by anyone, including anonymous readers.
|
|
487
508
|
|
|
488
509
|
```ts
|
|
489
|
-
const { record } = await notes.records.
|
|
510
|
+
const { record } = await notes.records.create('note', {
|
|
490
511
|
data : { title: 'Public Note', body: 'Visible to everyone' },
|
|
491
512
|
published : true,
|
|
492
513
|
});
|
|
@@ -527,6 +548,175 @@ The sync engine (enabled by default at 2-minute intervals) automatically synchro
|
|
|
527
548
|
|
|
528
549
|
---
|
|
529
550
|
|
|
551
|
+
## Repository Pattern
|
|
552
|
+
|
|
553
|
+
The `repository()` factory provides a higher-level abstraction over `TypedWeb5`. Instead of passing path strings to every call, you get a **structure-aware object** with CRUD methods directly on each protocol type -- with automatic singleton detection.
|
|
554
|
+
|
|
555
|
+
```ts
|
|
556
|
+
import { defineProtocol, repository, Web5 } from '@enbox/api';
|
|
557
|
+
|
|
558
|
+
const { web5 } = await Web5.connect({ password: 'secret' });
|
|
559
|
+
|
|
560
|
+
const TaskProtocol = defineProtocol({
|
|
561
|
+
protocol : 'https://example.com/tasks',
|
|
562
|
+
published : false,
|
|
563
|
+
types: {
|
|
564
|
+
project : { schema: 'https://example.com/schemas/project', dataFormats: ['application/json'] },
|
|
565
|
+
task : { schema: 'https://example.com/schemas/task', dataFormats: ['application/json'] },
|
|
566
|
+
config : { schema: 'https://example.com/schemas/config', dataFormats: ['application/json'] },
|
|
567
|
+
},
|
|
568
|
+
structure: {
|
|
569
|
+
project: {
|
|
570
|
+
task: {}, // collection -- many tasks per project
|
|
571
|
+
},
|
|
572
|
+
config: {
|
|
573
|
+
$recordLimit: { max: 1, strategy: 'reject' }, // singleton
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
} as const, {} as {
|
|
577
|
+
project : { name: string; color?: string };
|
|
578
|
+
task : { title: string; completed: boolean };
|
|
579
|
+
config : { defaultView: 'list' | 'board' };
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
const repo = repository(web5.using(TaskProtocol));
|
|
583
|
+
await repo.configure();
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### Collections vs Singletons
|
|
587
|
+
|
|
588
|
+
The repository automatically detects types with `$recordLimit: { max: 1 }` and provides different APIs:
|
|
589
|
+
|
|
590
|
+
**Collections** (default) -- `create`, `query`, `get`, `delete`, `subscribe`:
|
|
591
|
+
|
|
592
|
+
```ts
|
|
593
|
+
// Create
|
|
594
|
+
const { record } = await repo.project.create({
|
|
595
|
+
data: { name: 'Website Redesign', color: '#3b82f6' },
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
// Query all
|
|
599
|
+
const { records } = await repo.project.query();
|
|
600
|
+
|
|
601
|
+
// Query with filters and pagination
|
|
602
|
+
const { records: recent, cursor } = await repo.project.query({
|
|
603
|
+
dateSort : 'createdDescending',
|
|
604
|
+
pagination : { limit: 10 },
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
// Get by record ID
|
|
608
|
+
const { record: project } = await repo.project.get(recordId);
|
|
609
|
+
|
|
610
|
+
// Delete
|
|
611
|
+
await repo.project.delete(recordId);
|
|
612
|
+
|
|
613
|
+
// Subscribe to real-time changes
|
|
614
|
+
const { liveQuery } = await repo.project.subscribe();
|
|
615
|
+
liveQuery.on('create', (record) => console.log('new project:', record.id));
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
**Singletons** (`$recordLimit: { max: 1 }`) -- `set`, `get`, `delete`:
|
|
619
|
+
|
|
620
|
+
```ts
|
|
621
|
+
// Set (creates or updates)
|
|
622
|
+
await repo.config.set({
|
|
623
|
+
data: { defaultView: 'board' },
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
// Get the single record
|
|
627
|
+
const { record: config } = await repo.config.get();
|
|
628
|
+
const { defaultView } = await config.data.json(); // 'board'
|
|
629
|
+
|
|
630
|
+
// Delete
|
|
631
|
+
await repo.config.delete(config.id);
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### Nested Records
|
|
635
|
+
|
|
636
|
+
Nested types take `parentContextId` as the first argument:
|
|
637
|
+
|
|
638
|
+
```ts
|
|
639
|
+
// Create a task under a project
|
|
640
|
+
const { record: task } = await repo.project.task.create(project.contextId, {
|
|
641
|
+
data: { title: 'Design mockups', completed: false },
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
// Query tasks within a project
|
|
645
|
+
const { records: tasks } = await repo.project.task.query(project.contextId);
|
|
646
|
+
|
|
647
|
+
// Subscribe to tasks within a project
|
|
648
|
+
const { liveQuery } = await repo.project.task.subscribe(project.contextId);
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
### Using Pre-built Protocols
|
|
652
|
+
|
|
653
|
+
The `@enbox/protocols` package provides production-ready protocol definitions. Combined with `repository()`, you get zero-boilerplate typed data access:
|
|
654
|
+
|
|
655
|
+
```ts
|
|
656
|
+
import { repository, Web5 } from '@enbox/api';
|
|
657
|
+
import {
|
|
658
|
+
PreferencesProtocol,
|
|
659
|
+
ProfileProtocol,
|
|
660
|
+
SocialGraphProtocol,
|
|
661
|
+
} from '@enbox/protocols';
|
|
662
|
+
|
|
663
|
+
const { web5 } = await Web5.connect({ password: 'secret' });
|
|
664
|
+
|
|
665
|
+
// Social Graph -- friend, block, group, member
|
|
666
|
+
const social = repository(web5.using(SocialGraphProtocol));
|
|
667
|
+
await social.configure();
|
|
668
|
+
|
|
669
|
+
const { record } = await social.friend.create({
|
|
670
|
+
data: { did: 'did:dht:alice...', alias: 'Alice' },
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
// Profile -- profile (singleton), avatar, hero, link, privateNote
|
|
674
|
+
const profile = repository(web5.using(ProfileProtocol));
|
|
675
|
+
await profile.configure();
|
|
676
|
+
|
|
677
|
+
await profile.profile.set({
|
|
678
|
+
data: { displayName: 'Bob', bio: 'Building the decentralized web' },
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
// Add links nested under the profile
|
|
682
|
+
const { record: p } = await profile.profile.get();
|
|
683
|
+
await profile.profile.link.create(p.contextId, {
|
|
684
|
+
data: { url: 'https://github.com/bob', title: 'GitHub' },
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
// Preferences -- theme, locale, privacy (singletons), notification (collection)
|
|
688
|
+
const prefs = repository(web5.using(PreferencesProtocol));
|
|
689
|
+
await prefs.configure();
|
|
690
|
+
|
|
691
|
+
await prefs.theme.set({ data: { mode: 'dark', accentColor: '#8b5cf6' } });
|
|
692
|
+
await prefs.locale.set({ data: { language: 'en', timezone: 'America/New_York' } });
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
See [`@enbox/protocols`](../protocols) for the full catalog of 6 protocols and 19 typed data shapes.
|
|
696
|
+
|
|
697
|
+
---
|
|
698
|
+
|
|
699
|
+
## Code Generation
|
|
700
|
+
|
|
701
|
+
For protocols defined externally (e.g. from a spec or shared JSON file), use `@enbox/protocol-codegen` to generate TypeScript types from a protocol definition and JSON Schemas:
|
|
702
|
+
|
|
703
|
+
```bash
|
|
704
|
+
bunx @enbox/protocol-codegen generate \
|
|
705
|
+
--definition ./my-protocol.json \
|
|
706
|
+
--schemas ./schemas/ \
|
|
707
|
+
--name MyProtocol \
|
|
708
|
+
--output ./my-protocol.generated.ts
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
This generates:
|
|
712
|
+
- TypeScript interfaces for each type's JSON Schema (via `json-schema-to-typescript`)
|
|
713
|
+
- A `SchemaMap` mapping type names to generated interfaces
|
|
714
|
+
- A ready-to-use `defineProtocol()` call
|
|
715
|
+
|
|
716
|
+
See [`@enbox/protocol-codegen`](../protocol-codegen) for full documentation.
|
|
717
|
+
|
|
718
|
+
---
|
|
719
|
+
|
|
530
720
|
## Advanced Usage
|
|
531
721
|
|
|
532
722
|
### Unscoped DWN Access
|
|
@@ -579,7 +769,10 @@ const { didDocument } = await web5.did.resolve('did:dht:abc...');
|
|
|
579
769
|
|--------|-------------|
|
|
580
770
|
| `Web5` | Main entry point -- `connect()`, `anonymous()`, `using()` |
|
|
581
771
|
| `defineProtocol()` | Factory for creating typed protocol definitions |
|
|
582
|
-
| `
|
|
772
|
+
| `repository()` | Factory for creating structure-aware CRUD repositories from `TypedWeb5` |
|
|
773
|
+
| `TypedWeb5` | Protocol-scoped API returned by `web5.using()` -- `create`, `query`, `read`, `delete`, `subscribe` |
|
|
774
|
+
| `TypedRecord<T>` | Type-safe record wrapper -- `data.json()` returns `Promise<T>` |
|
|
775
|
+
| `TypedLiveQuery<T>` | Type-safe subscription with `TypedRecord<T>[]` snapshot and typed change events |
|
|
583
776
|
| `Record` | Mutable record instance with data accessors and side-effect methods |
|
|
584
777
|
| `ReadOnlyRecord` | Immutable record for anonymous/read-only access |
|
|
585
778
|
| `LiveQuery` | Real-time subscription with initial snapshot and change events |
|
|
@@ -604,6 +797,13 @@ const { didDocument } = await web5.did.resolve('did:dht:abc...');
|
|
|
604
797
|
| `TypedProtocol<D, M>` | Typed protocol wrapper with definition and schema map |
|
|
605
798
|
| `ProtocolPaths<D>` | Union of valid slash-delimited paths for a protocol definition |
|
|
606
799
|
| `SchemaMap` | Maps protocol type names to TypeScript interfaces |
|
|
800
|
+
| `TypedCreateRequest<D, M, Path>` | Options for `records.create()` |
|
|
801
|
+
| `TypedCreateResponse<T>` | Response from `records.create()` -- `{ status, record: TypedRecord<T> }` |
|
|
802
|
+
| `TypedQueryRequest` | Options for `records.query()` |
|
|
803
|
+
| `TypedQueryResponse<T>` | Response from `records.query()` -- `{ status, records: TypedRecord<T>[], cursor? }` |
|
|
804
|
+
| `TypedSubscribeResponse<T>` | Response from `records.subscribe()` -- `{ status, liveQuery: TypedLiveQuery<T> }` |
|
|
805
|
+
| `Repository<D, M>` | Repository type -- structure-aware Proxy object with CRUD methods |
|
|
806
|
+
| `DataForPath<D, M, Path>` | Resolves TypeScript data type for a protocol path from the schema map |
|
|
607
807
|
| `Web5ConnectOptions` | Options for `Web5.connect()` |
|
|
608
808
|
| `Web5ConnectResult` | Return type of `Web5.connect()` |
|
|
609
809
|
| `RecordModel` | Structured data model of a record |
|