@enbox/api 0.1.1 → 0.2.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.
Files changed (58) hide show
  1. package/README.md +140 -159
  2. package/dist/browser.mjs +23 -13
  3. package/dist/browser.mjs.map +4 -4
  4. package/dist/esm/advanced.js +11 -0
  5. package/dist/esm/advanced.js.map +1 -0
  6. package/dist/esm/define-protocol.js +3 -3
  7. package/dist/esm/dwn-api.js +55 -107
  8. package/dist/esm/dwn-api.js.map +1 -1
  9. package/dist/esm/dwn-reader-api.js +128 -0
  10. package/dist/esm/dwn-reader-api.js.map +1 -0
  11. package/dist/esm/index.js +3 -2
  12. package/dist/esm/index.js.map +1 -1
  13. package/dist/esm/live-query.js +5 -4
  14. package/dist/esm/live-query.js.map +1 -1
  15. package/dist/esm/read-only-record.js +255 -0
  16. package/dist/esm/read-only-record.js.map +1 -0
  17. package/dist/esm/record.js +46 -57
  18. package/dist/esm/record.js.map +1 -1
  19. package/dist/esm/typed-web5.js +242 -0
  20. package/dist/esm/typed-web5.js.map +1 -0
  21. package/dist/esm/web5.js +71 -3
  22. package/dist/esm/web5.js.map +1 -1
  23. package/dist/types/advanced.d.ts +12 -0
  24. package/dist/types/advanced.d.ts.map +1 -0
  25. package/dist/types/define-protocol.d.ts +3 -3
  26. package/dist/types/dwn-api.d.ts +12 -89
  27. package/dist/types/dwn-api.d.ts.map +1 -1
  28. package/dist/types/dwn-reader-api.d.ts +138 -0
  29. package/dist/types/dwn-reader-api.d.ts.map +1 -0
  30. package/dist/types/index.d.ts +3 -2
  31. package/dist/types/index.d.ts.map +1 -1
  32. package/dist/types/live-query.d.ts +13 -1
  33. package/dist/types/live-query.d.ts.map +1 -1
  34. package/dist/types/protocol-types.d.ts +2 -2
  35. package/dist/types/read-only-record.d.ts +133 -0
  36. package/dist/types/read-only-record.d.ts.map +1 -0
  37. package/dist/types/record.d.ts +37 -17
  38. package/dist/types/record.d.ts.map +1 -1
  39. package/dist/types/{typed-dwn-api.d.ts → typed-web5.d.ts} +70 -73
  40. package/dist/types/typed-web5.d.ts.map +1 -0
  41. package/dist/types/web5.d.ts +79 -3
  42. package/dist/types/web5.d.ts.map +1 -1
  43. package/package.json +9 -5
  44. package/src/advanced.ts +29 -0
  45. package/src/define-protocol.ts +3 -3
  46. package/src/dwn-api.ts +88 -222
  47. package/src/dwn-reader-api.ts +255 -0
  48. package/src/index.ts +3 -2
  49. package/src/live-query.ts +20 -4
  50. package/src/protocol-types.ts +2 -2
  51. package/src/read-only-record.ts +328 -0
  52. package/src/record.ts +116 -86
  53. package/src/typed-web5.ts +445 -0
  54. package/src/web5.ts +104 -5
  55. package/dist/esm/typed-dwn-api.js +0 -182
  56. package/dist/esm/typed-dwn-api.js.map +0 -1
  57. package/dist/types/typed-dwn-api.d.ts.map +0 -1
  58. package/src/typed-dwn-api.ts +0 -370
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # Enbox API SDK
1
+ # @enbox/api
2
2
 
3
- > **Research Preview** Enbox is under active development. APIs may change without notice.
3
+ > **Research Preview** -- Enbox is under active development. APIs may change without notice.
4
4
 
5
5
  [![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/LiranCohen/02d15f39a46173a612a8862ec6d7cfcf/raw/api.json)](https://github.com/enboxorg/enbox/actions/workflows/ci.yml)
6
6
 
7
- The high-level SDK for building decentralized applications with identity and data management.
7
+ The high-level SDK for building decentralized applications with protocol-first data management.
8
8
 
9
9
  ## Installation
10
10
 
@@ -14,233 +14,214 @@ bun add @enbox/api
14
14
 
15
15
  ## Quick Start
16
16
 
17
- ```javascript
18
- import { Enbox } from '@enbox/api';
17
+ ```ts
18
+ import { defineProtocol, Web5 } from '@enbox/api';
19
19
 
20
- const { enbox, did: myDid } = await Enbox.connect();
20
+ // 1. Connect
21
+ const { web5, did: myDid } = await Web5.connect();
21
22
 
22
- // Create a record
23
- const { record } = await enbox.dwn.records.create({
24
- data : 'Hello World!',
25
- message : { dataFormat: 'text/plain' },
23
+ // 2. Define a protocol with typed data shapes
24
+ const NotesProtocol = defineProtocol({
25
+ protocol : 'https://example.com/notes',
26
+ published : true,
27
+ types : {
28
+ note: {
29
+ schema : 'https://example.com/schemas/note',
30
+ dataFormats : ['application/json'],
31
+ },
32
+ },
33
+ structure: {
34
+ note: {},
35
+ },
36
+ } as const, {} as {
37
+ note: { title: string; body: string };
26
38
  });
27
39
 
28
- // Send it to your remote DWN
29
- await record.send(myDid);
30
- ```
31
-
32
- ## API Documentation
40
+ // 3. Scope all operations to the protocol
41
+ const notes = web5.using(NotesProtocol);
33
42
 
34
- ### **`Enbox.connect(options)`**
43
+ // 4. Install the protocol
44
+ await notes.configure();
35
45
 
36
- Connects to a user's local identity agent or generates an in-app DID.
46
+ // 5. Write a record (path, data, and schema are type-checked)
47
+ const { record } = await notes.records.write('note', {
48
+ data: { title: 'Hello', body: 'World' },
49
+ });
37
50
 
38
- ```javascript
39
- const { enbox, did: myDid } = await Enbox.connect();
51
+ // 6. Send to your remote DWN
52
+ await record.send(myDid);
40
53
  ```
41
54
 
42
- #### Options (all optional)
43
-
44
- - **`agent`** - `EnboxAgent` instance. Defaults to a local `EnboxUserAgent`.
45
- - **`connectedDid`** - `string`: an existing DID to connect to.
46
- - **`sync`** - `string`: sync interval (any value accepted by [`ms`](https://www.npmjs.com/package/ms)), or `'off'` to disable. Default: `'2m'`.
47
- - **`techPreview.dwnEndpoints`** - `string[]`: DWN endpoints for the created DID. Default: `['https://enbox-dwn.fly.dev']`.
48
-
49
- #### Response
55
+ ## Core Concepts
50
56
 
51
- - **`enbox`** - `Enbox` instance with access to DWN operations and DID methods.
52
- - **`did`** - `string`: the DID that was created or connected to.
57
+ ### `Web5.connect(options?)`
53
58
 
54
- ---
59
+ Connects to a local identity agent or generates an in-app DID.
55
60
 
56
- ### Record Instances
61
+ ```ts
62
+ const { web5, did, recoveryPhrase } = await Web5.connect();
63
+ ```
57
64
 
58
- Methods like `create`, `write`, and `query` return `Record` instances with:
65
+ **Options** (all optional):
59
66
 
60
- **Properties**: `id`, `contextId`, `dataFormat`, `dateCreated`, `dateModified`, `datePublished`, `encryption`, `protocol`, `protocolPath`, `recipient`, `schema`, `dataCid`, `dataSize`, `published`.
67
+ | Option | Type | Description |
68
+ |--------|------|-------------|
69
+ | `agent` | `Web5Agent` | Custom agent instance. Defaults to a local `Web5UserAgent`. |
70
+ | `connectedDid` | `string` | Existing DID to connect to. |
71
+ | `password` | `string` | Password to protect the local identity vault. |
72
+ | `recoveryPhrase` | `string` | 12-word BIP-39 phrase for vault recovery. |
73
+ | `sync` | `string` | Sync interval (e.g. `'2m'`) or `'off'`. Default: `'2m'`. |
74
+ | `didCreateOptions.dwnEndpoints` | `string[]` | DWN endpoints for the created DID. |
75
+ | `walletConnectOptions` | `ConnectOptions` | Trigger external wallet connect flow. |
61
76
 
62
- **Methods**:
63
- - **`data.blob()`** / **`data.bytes()`** / **`data.json()`** / **`data.stream()`** / **`data.text()`** - read record data in various formats.
64
- - **`send(did)`** - send the record to a DID's DWN endpoints.
65
- - **`update(request)`** - overwrite the record with new data.
77
+ **Returns** `{ web5, did, recoveryPhrase?, delegateDid? }`.
66
78
 
67
79
  ---
68
80
 
69
- ### **`enbox.dwn.records.query(request)`**
81
+ ### `Web5.anonymous(options?)`
70
82
 
71
- Query your own or another DID's DWN for records.
83
+ Creates a lightweight, read-only instance for querying public DWN data. No identity, vault, or signing keys are required.
72
84
 
73
- ```javascript
74
- // Query your own DWN
75
- const { records } = await enbox.dwn.records.query({
76
- message: {
77
- filter: {
78
- schema : 'https://schema.org/Playlist',
79
- dataFormat : 'application/json',
80
- },
81
- },
82
- });
85
+ ```ts
86
+ const { dwn } = Web5.anonymous();
83
87
 
84
- // Query Bob's DWN
85
- const { records } = await enbox.dwn.records.query({
86
- from: 'did:example:bob',
87
- message: {
88
- filter: {
89
- protocol : 'https://music.org/protocol',
90
- schema : 'https://schema.org/Playlist',
91
- dataFormat : 'application/json',
92
- },
93
- },
88
+ const { records } = await dwn.records.query({
89
+ from : 'did:dht:alice...',
90
+ filter : { protocol: 'https://example.com/notes', protocolPath: 'note' },
94
91
  });
95
- ```
96
92
 
97
- **Filter properties**: `recordId`, `protocol`, `protocolPath`, `contextId`, `parentId`, `recipient`, `schema`, `dataFormat`.
93
+ for (const record of records) {
94
+ console.log(record.id, await record.data.text());
95
+ }
96
+ ```
98
97
 
99
- **Pagination**: `{ limit: number, cursor: string }`. The response includes a `cursor` if more results exist.
98
+ Returns a `{ dwn: DwnReaderApi }` with read-only `records.query()` and `records.read()`.
100
99
 
101
100
  ---
102
101
 
103
- ### **`enbox.dwn.records.subscribe(request)`**
102
+ ### `web5.using(protocol)`
104
103
 
105
- Subscribe to record changes on your own or another DID's DWN.
104
+ The **primary interface** for all record operations. Returns a `TypedWeb5` instance scoped to the given protocol.
106
105
 
107
- ```javascript
108
- const { status } = await enbox.dwn.records.subscribe({
109
- message: {
110
- filter: { protocol: 'https://schema.org/protocols/social' },
111
- },
112
- subscriptionHandler: (record) => {
113
- console.log('received', record);
114
- },
115
- });
106
+ ```ts
107
+ const notes = web5.using(NotesProtocol);
116
108
  ```
117
109
 
118
- ---
110
+ The returned object provides:
119
111
 
120
- ### **`enbox.dwn.records.create(request)`**
112
+ - **`notes.configure()`** -- Install the protocol on the local DWN.
113
+ - **`notes.records.write(path, request)`** -- Write a record at a protocol path.
114
+ - **`notes.records.query(path, request?)`** -- Query records at a path.
115
+ - **`notes.records.read(path, request)`** -- Read a single record.
116
+ - **`notes.records.delete(path, request)`** -- Delete a record by ID.
117
+ - **`notes.records.subscribe(path, request?)`** -- Subscribe to real-time changes (returns a `LiveQuery`).
121
118
 
122
- Create a new record and optionally store it locally.
123
-
124
- ```javascript
125
- const { record } = await enbox.dwn.records.create({
126
- data : 'Hello World!',
127
- message : { dataFormat: 'text/plain' },
128
- });
129
-
130
- await record.send(myDid); // send to your remote DWN
131
- await record.send('did:example:bob'); // send to Bob's DWN
132
- ```
133
-
134
- Pass `store: false` to create without storing locally (e.g., for records you only send to others).
119
+ Protocol URI, protocolPath, and schema are automatically injected into every operation.
135
120
 
136
121
  ---
137
122
 
138
- ### **`enbox.dwn.records.write(request)`**
139
-
140
- Alias for `create()` — same request object.
141
-
142
- ---
123
+ ### `defineProtocol(definition, schemaMap?)`
143
124
 
144
- ### **`enbox.dwn.records.read(request)`**
125
+ Creates a typed protocol definition that enables compile-time path autocompletion and data type checking.
145
126
 
146
- Read a specific record by filter (most commonly `recordId`).
127
+ ```ts
128
+ import type { ProtocolDefinition } from '@enbox/dwn-sdk-js';
147
129
 
148
- ```javascript
149
- const { record } = await enbox.dwn.records.read({
150
- message: {
151
- filter: { recordId: 'bfw35evr6e54c4cqa4c589h4cq3v7w4nc534c9w7h5' },
130
+ const SocialProtocol = defineProtocol({
131
+ protocol : 'https://social.example/protocol',
132
+ published : true,
133
+ types: {
134
+ profile : { schema: 'https://social.example/schemas/profile', dataFormats: ['application/json'] },
135
+ post : { schema: 'https://social.example/schemas/post', dataFormats: ['application/json'] },
136
+ reply : { schema: 'https://social.example/schemas/reply', dataFormats: ['application/json'] },
137
+ },
138
+ structure: {
139
+ profile : {},
140
+ post : {
141
+ reply: {},
142
+ },
152
143
  },
144
+ } as const satisfies ProtocolDefinition, {} as {
145
+ profile : { displayName: string; bio?: string };
146
+ post : { title: string; body: string };
147
+ reply : { body: string };
153
148
  });
154
-
155
- console.log(await record.data.text());
156
149
  ```
157
150
 
158
- Use `from: 'did:example:bob'` to read from another DID's DWN.
151
+ The `schemaMap` is a phantom type -- it exists only at compile time. Pass `{} as YourSchemaMap` as the second argument.
159
152
 
160
153
  ---
161
154
 
162
- ### **`enbox.dwn.records.delete(request)`**
155
+ ### Record Instances
163
156
 
164
- Delete a record by ID.
157
+ Methods like `write`, `query`, and `read` return `Record` instances.
165
158
 
166
- ```javascript
167
- await enbox.dwn.records.delete({
168
- message: { recordId: 'bfw35evr6e54c4cqa4c589h4cq3v7w4nc534c9w7h5' },
169
- });
159
+ **Properties**: `id`, `contextId`, `dataFormat`, `dateCreated`, `timestamp`, `datePublished`, `protocol`, `protocolPath`, `recipient`, `schema`, `dataCid`, `dataSize`, `published`.
160
+
161
+ **Data accessors**:
162
+
163
+ ```ts
164
+ await record.data.text(); // string
165
+ await record.data.json<MyType>(); // typed JSON
166
+ await record.data.blob(); // Blob
167
+ await record.data.bytes(); // Uint8Array
168
+ await record.data.stream(); // ReadableStream
170
169
  ```
171
170
 
172
- ---
171
+ **Mutators** (return a new `Record` instance):
173
172
 
174
- ### **`enbox.dwn.protocols.configure(request)`**
175
-
176
- Install a protocol definition on your DWN.
177
-
178
- ```javascript
179
- const { protocol } = await enbox.dwn.protocols.configure({
180
- message: {
181
- definition: {
182
- protocol : 'https://photos.org/protocol',
183
- published : true,
184
- types: {
185
- album : { schema: 'https://photos.org/album', dataFormats: ['application/json'] },
186
- photo : { schema: 'https://photos.org/photo', dataFormats: ['application/json'] },
187
- image : { dataFormats: ['image/png', 'image/jpeg', 'image/gif'] },
188
- },
189
- structure: {
190
- album: {
191
- $actions: [{ who: 'recipient', can: 'read' }],
192
- },
193
- photo: {
194
- $actions: [{ who: 'recipient', can: 'read' }],
195
- image: {
196
- $actions: [{ who: 'author', of: 'photo', can: 'write' }],
197
- },
198
- },
199
- },
200
- },
201
- },
202
- });
173
+ ```ts
174
+ const { record: updated } = await record.update({ data: { title: 'New Title', body: '...' } });
175
+ const { status } = await record.delete();
176
+ ```
177
+
178
+ **Side-effect methods** (return status only):
203
179
 
204
- await protocol.send(myDid); // sync to remote DWNs
180
+ ```ts
181
+ await record.send(targetDid); // send to a remote DWN
182
+ await record.store(); // persist locally
183
+ await record.import(); // import from a remote DWN
205
184
  ```
206
185
 
207
186
  ---
208
187
 
209
- ### **`enbox.dwn.protocols.query(request)`**
188
+ ### LiveQuery (Subscriptions)
210
189
 
211
- Query a DID's DWN for installed protocols.
190
+ `records.subscribe()` returns a `LiveQuery` that provides an initial snapshot plus real-time deduplicated change events.
212
191
 
213
- ```javascript
214
- const { protocols } = await enbox.dwn.protocols.query({
215
- from: 'did:example:bob',
216
- message: {
217
- filter: { protocol: 'https://music.org/protocol' },
218
- },
219
- });
192
+ ```ts
193
+ const { liveQuery } = await notes.records.subscribe('post');
194
+
195
+ liveQuery.on('create', (record) => { /* new record */ });
196
+ liveQuery.on('update', (record) => { /* updated record */ });
197
+ liveQuery.on('delete', (record) => { /* deleted record */ });
198
+
199
+ // Clean up
200
+ await liveQuery.close();
220
201
  ```
221
202
 
222
203
  ---
223
204
 
224
- ### **`enbox.did.create(method, options)`**
205
+ ## Advanced Usage
225
206
 
226
- Generate a DID using a supported method (`'dht'` or `'jwk'`).
207
+ For power users who need direct, unscoped DWN access (e.g. cross-protocol queries, raw permission management), import from the `@enbox/api/advanced` sub-path:
227
208
 
228
- ```javascript
229
- const myDid = await enbox.did.create('dht');
209
+ ```ts
210
+ import { DwnApi } from '@enbox/api/advanced';
230
211
  ```
231
212
 
232
- Pass `store: false` in options to skip storing the DID's keys in the agent.
213
+ The `DwnApi` class provides raw `records`, `protocols`, and `permissions` accessors without protocol scoping. Most applications should use `web5.using()` instead.
233
214
 
234
215
  ---
235
216
 
236
- ### **`enbox.did.resolve(didUri)`**
217
+ ## DID Operations
237
218
 
238
- Resolve a DID to its DID Document.
219
+ ```ts
220
+ // Create a DID
221
+ const myDid = await web5.did.create('dht');
239
222
 
240
- ```javascript
241
- const { didDocument } = await enbox.did.resolve(
242
- 'did:dht:qftx7z968xcpfy1a1diu75pg5meap3gdtg6ezagaw849wdh6oubo'
243
- );
223
+ // Resolve a DID
224
+ const { didDocument } = await web5.did.resolve('did:dht:abc...');
244
225
  ```
245
226
 
246
227
  ## License