@interop/was-client 0.99.0 → 0.100.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 +94 -71
- package/dist/Collection.d.ts +20 -8
- package/dist/Collection.d.ts.map +1 -1
- package/dist/Collection.js +89 -69
- package/dist/Collection.js.map +1 -1
- package/dist/Resource.d.ts +14 -3
- package/dist/Resource.d.ts.map +1 -1
- package/dist/Resource.js +38 -27
- package/dist/Resource.js.map +1 -1
- package/dist/Space.d.ts +18 -4
- package/dist/Space.d.ts.map +1 -1
- package/dist/Space.js +64 -61
- package/dist/Space.js.map +1 -1
- package/dist/WasClient.d.ts +105 -58
- package/dist/WasClient.d.ts.map +1 -1
- package/dist/WasClient.js +216 -142
- package/dist/WasClient.js.map +1 -1
- package/dist/codec.d.ts +3 -2
- package/dist/codec.d.ts.map +1 -1
- package/dist/edv/EdvCodec.d.ts.map +1 -1
- package/dist/edv/EdvCodec.js +37 -26
- package/dist/edv/EdvCodec.js.map +1 -1
- package/dist/edv/WasTransport.d.ts +7 -4
- package/dist/edv/WasTransport.d.ts.map +1 -1
- package/dist/edv/WasTransport.js +39 -20
- package/dist/edv/WasTransport.js.map +1 -1
- package/dist/edv/acquire.d.ts +3 -3
- package/dist/edv/acquire.d.ts.map +1 -1
- package/dist/edv/acquire.js +3 -3
- package/dist/edv/acquire.js.map +1 -1
- package/dist/edv/cipher.d.ts +7 -4
- package/dist/edv/cipher.d.ts.map +1 -1
- package/dist/edv/cipher.js +7 -4
- package/dist/edv/cipher.js.map +1 -1
- package/dist/edv/core.d.ts +1 -1
- package/dist/edv/core.d.ts.map +1 -1
- package/dist/edv/core.js +1 -1
- package/dist/edv/core.js.map +1 -1
- package/dist/edv/docCipher.d.ts +10 -7
- package/dist/edv/docCipher.d.ts.map +1 -1
- package/dist/edv/docCipher.js +12 -8
- package/dist/edv/docCipher.js.map +1 -1
- package/dist/edv/encryption.d.ts +2 -2
- package/dist/edv/encryption.d.ts.map +1 -1
- package/dist/edv/encryption.js +8 -6
- package/dist/edv/encryption.js.map +1 -1
- package/dist/edv/epochCrypto.d.ts +12 -4
- package/dist/edv/epochCrypto.d.ts.map +1 -1
- package/dist/edv/epochCrypto.js +37 -10
- package/dist/edv/epochCrypto.js.map +1 -1
- package/dist/edv/epochKeys.d.ts +6 -2
- package/dist/edv/epochKeys.d.ts.map +1 -1
- package/dist/edv/epochKeys.js +25 -14
- package/dist/edv/epochKeys.js.map +1 -1
- package/dist/edv/hmacKey.d.ts +7 -1
- package/dist/edv/hmacKey.d.ts.map +1 -1
- package/dist/edv/hmacKey.js +9 -3
- package/dist/edv/hmacKey.js.map +1 -1
- package/dist/edv/index.d.ts +1 -1
- package/dist/edv/index.d.ts.map +1 -1
- package/dist/edv/index.js +1 -1
- package/dist/edv/index.js.map +1 -1
- package/dist/edv/recipients.d.ts.map +1 -1
- package/dist/edv/recipients.js +2 -0
- package/dist/edv/recipients.js.map +1 -1
- package/dist/edv/refreshingDocCipher.d.ts +3 -3
- package/dist/edv/refreshingDocCipher.d.ts.map +1 -1
- package/dist/edv/refreshingDocCipher.js +3 -3
- package/dist/edv/refreshingDocCipher.js.map +1 -1
- package/dist/edv/transportFactory.d.ts +3 -3
- package/dist/edv/transportFactory.d.ts.map +1 -1
- package/dist/edv/transportFactory.js +3 -3
- package/dist/edv/transportFactory.js.map +1 -1
- package/dist/errors.d.ts +6 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +6 -4
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/codec.d.ts +7 -7
- package/dist/internal/codec.d.ts.map +1 -1
- package/dist/internal/codec.js +19 -18
- package/dist/internal/codec.js.map +1 -1
- package/dist/internal/conditional.d.ts +3 -3
- package/dist/internal/conditional.d.ts.map +1 -1
- package/dist/internal/conditional.js +5 -5
- package/dist/internal/conditional.js.map +1 -1
- package/dist/internal/grant.d.ts +15 -26
- package/dist/internal/grant.d.ts.map +1 -1
- package/dist/internal/grant.js +95 -29
- package/dist/internal/grant.js.map +1 -1
- package/dist/internal/memo.d.ts +13 -4
- package/dist/internal/memo.d.ts.map +1 -1
- package/dist/internal/memo.js +18 -7
- package/dist/internal/memo.js.map +1 -1
- package/dist/internal/meta.d.ts +8 -8
- package/dist/internal/meta.d.ts.map +1 -1
- package/dist/internal/meta.js +13 -10
- package/dist/internal/meta.js.map +1 -1
- package/dist/internal/pagination.d.ts +30 -15
- package/dist/internal/pagination.d.ts.map +1 -1
- package/dist/internal/pagination.js +31 -17
- package/dist/internal/pagination.js.map +1 -1
- package/dist/internal/paths.d.ts +332 -195
- package/dist/internal/paths.d.ts.map +1 -1
- package/dist/internal/paths.js +400 -367
- package/dist/internal/paths.js.map +1 -1
- package/dist/internal/policy.d.ts +23 -23
- package/dist/internal/policy.d.ts.map +1 -1
- package/dist/internal/policy.js +20 -20
- package/dist/internal/policy.js.map +1 -1
- package/dist/internal/request.d.ts +17 -21
- package/dist/internal/request.d.ts.map +1 -1
- package/dist/internal/request.js +10 -21
- package/dist/internal/request.js.map +1 -1
- package/dist/internal/revoke.d.ts +34 -13
- package/dist/internal/revoke.d.ts.map +1 -1
- package/dist/internal/revoke.js +97 -28
- package/dist/internal/revoke.js.map +1 -1
- package/dist/internal/service.d.ts +25 -7
- package/dist/internal/service.d.ts.map +1 -1
- package/dist/internal/service.js +49 -45
- package/dist/internal/service.js.map +1 -1
- package/dist/internal/write.d.ts +18 -18
- package/dist/internal/write.d.ts.map +1 -1
- package/dist/internal/write.js +27 -27
- package/dist/internal/write.js.map +1 -1
- package/dist/paths.d.ts +32 -25
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +32 -25
- package/dist/paths.js.map +1 -1
- package/dist/sync/index.d.ts +4 -2
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +4 -2
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/port.d.ts +3 -3
- package/dist/sync/port.d.ts.map +1 -1
- package/dist/sync/port.js +12 -12
- package/dist/sync/port.js.map +1 -1
- package/dist/sync/provisioning.d.ts +10 -9
- package/dist/sync/provisioning.d.ts.map +1 -1
- package/dist/sync/provisioning.js +19 -18
- package/dist/sync/provisioning.js.map +1 -1
- package/dist/sync/types.d.ts +2 -2
- package/dist/types.d.ts +13 -5
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -48,16 +48,16 @@ through cheap, lazy navigational handles modeled on a document store's DX
|
|
|
48
48
|
(`add`/`get`/`put`/`list`/`delete`) rather than `insertOne`/`findOne` (PWS has
|
|
49
49
|
no query-by-filter yet).
|
|
50
50
|
|
|
51
|
-
| Document db driver | PWS client
|
|
52
|
-
| ----------------------------------- |
|
|
53
|
-
| `new Client(url)` | `new WasClient({
|
|
54
|
-
| `client.db('app')` | `was.
|
|
55
|
-
| `db.collection('users')` | `space.collection(collectionId)`
|
|
56
|
-
| `collection.insertOne(doc)` | `collection.add(doc)`
|
|
57
|
-
| `collection.findOne({ _id })` | `collection.get(resourceId)`
|
|
58
|
-
| `collection.replaceOne({ _id }, d)` | `collection.put(resourceId, data)`
|
|
59
|
-
| `collection.find().toArray()` | `collection.list()`
|
|
60
|
-
| `collection.deleteOne({ _id })` | `collection.resource(resourceId).delete()`
|
|
51
|
+
| Document db driver | PWS client |
|
|
52
|
+
| ----------------------------------- | ------------------------------------------- |
|
|
53
|
+
| `new Client(url)` | `new WasClient({ serviceUrl, zcapClient })` |
|
|
54
|
+
| `client.db('app')` | `was.spaceAt(spaceUrl)` |
|
|
55
|
+
| `db.collection('users')` | `space.collection(collectionId)` |
|
|
56
|
+
| `collection.insertOne(doc)` | `collection.add(doc)` |
|
|
57
|
+
| `collection.findOne({ _id })` | `collection.get(resourceId)` |
|
|
58
|
+
| `collection.replaceOne({ _id }, d)` | `collection.put(resourceId, data)` |
|
|
59
|
+
| `collection.find().toArray()` | `collection.list()` |
|
|
60
|
+
| `collection.deleteOne({ _id })` | `collection.resource(resourceId).delete()` |
|
|
61
61
|
|
|
62
62
|
See also:
|
|
63
63
|
|
|
@@ -110,7 +110,7 @@ const zcapClient = new ZcapClient({
|
|
|
110
110
|
|
|
111
111
|
// 3. Wrap it.
|
|
112
112
|
const was = new WasClient({
|
|
113
|
-
|
|
113
|
+
serviceUrl: 'https://was.example/service',
|
|
114
114
|
zcapClient
|
|
115
115
|
})
|
|
116
116
|
```
|
|
@@ -130,7 +130,7 @@ keyPair.controller = `did:key:${keyPair.fingerprint()}`
|
|
|
130
130
|
keyPair.id = `${keyPair.controller}#${keyPair.fingerprint()}`
|
|
131
131
|
|
|
132
132
|
const was = WasClient.fromSigner({
|
|
133
|
-
|
|
133
|
+
serviceUrl: 'https://was.example/service',
|
|
134
134
|
signer: keyPair.signer()
|
|
135
135
|
})
|
|
136
136
|
```
|
|
@@ -139,15 +139,20 @@ The `seed` is where a passphrase-, stored-secret-, or KMS-derived key plugs in:
|
|
|
139
139
|
deriving the same 32-byte seed yields the same DID, and therefore access to the
|
|
140
140
|
same spaces.
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
`ValidationError`. The
|
|
148
|
-
`
|
|
149
|
-
|
|
150
|
-
|
|
142
|
+
A client is built from exactly one of two URLs. `serviceUrl` is the URL of the
|
|
143
|
+
server's service description, read with an unsigned `GET`. `spacesUrl` is the
|
|
144
|
+
Spaces Repository URL (`https://host/spaces/`), which the client probes with an
|
|
145
|
+
unsigned `HEAD` for the `Link: <...>; rel="service"` header. Giving both or
|
|
146
|
+
neither, or a URL that is not absolute `http(s)`, is refused with
|
|
147
|
+
`ValidationError`. The one given is exposed as `was.serviceUrl` or
|
|
148
|
+
`was.spacesUrl` (the latter with its trailing slash supplied), and the other is
|
|
149
|
+
`undefined`.
|
|
150
|
+
|
|
151
|
+
A Space URL is received. It is not assembled from a server root. It arrives as
|
|
152
|
+
the `Location` of a create response, a listing item's `url`, a linkset entry, a
|
|
153
|
+
stored pointer, or a self-locating DID. The client holds no base URL, and no
|
|
154
|
+
function in the `./paths` entry takes one. `was.spaceAt(spaceUrl)` opens a
|
|
155
|
+
handle on a Space URL you hold.
|
|
151
156
|
|
|
152
157
|
#### Deriving a client from a secret or seed (`./identity`)
|
|
153
158
|
|
|
@@ -162,7 +167,7 @@ import { WasClient } from '@interop/was-client'
|
|
|
162
167
|
const agents = await agentsFromSecret({ secret: 'the account secret' })
|
|
163
168
|
|
|
164
169
|
const was = WasClient.fromSigner({
|
|
165
|
-
|
|
170
|
+
serviceUrl: 'https://was.example/service',
|
|
166
171
|
signer: agents.keyAgent.getSigner()
|
|
167
172
|
})
|
|
168
173
|
```
|
|
@@ -203,15 +208,17 @@ own pace produces.
|
|
|
203
208
|
### Service discovery
|
|
204
209
|
|
|
205
210
|
Before its first signed request, the client reads the server's service
|
|
206
|
-
description.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
211
|
+
description. A client built from `serviceUrl` reads that document with an
|
|
212
|
+
unsigned `GET`. A client built from `spacesUrl` first sends an unsigned `HEAD`
|
|
213
|
+
to it and follows the `Link: <...>; rel="service"` header on the response,
|
|
214
|
+
whatever its status, then reads the linked document with an unsigned `GET`. The
|
|
215
|
+
PWS server itself answers the Spaces Repository URL. The server root is often a
|
|
216
|
+
landing page served by something else, with no `service` link. The spec fixes no
|
|
217
|
+
path for the document, so the client assumes none.
|
|
218
|
+
|
|
219
|
+
The client does not compare the document's `spaces` member against any URL it
|
|
220
|
+
was given. `createSpace()` and `listSpaces()` address the `spaces` URL the
|
|
221
|
+
document names.
|
|
215
222
|
|
|
216
223
|
From the document the client selects the PWS specification version it speaks.
|
|
217
224
|
This client speaks only v0.5. It ignores `specs` keys other than
|
|
@@ -254,8 +261,10 @@ that copy without fetching, and `service({ refresh: true })` discovers afresh.
|
|
|
254
261
|
The client exposes the PWS containment model
|
|
255
262
|
(`SpacesRepository > Space > Collection > Resource`) as navigational handles.
|
|
256
263
|
Handles are lazy and synchronous to obtain -- only the verb methods hit the
|
|
257
|
-
network.
|
|
258
|
-
|
|
264
|
+
network. A chain such as `was.spaceAt(url).collection(y)` does no I/O and just
|
|
265
|
+
accumulates URL context; the only way it throws is `ValidationError`, on a URL
|
|
266
|
+
that cannot name a Space or an id on the reserved-segment registry. Existence is
|
|
267
|
+
checked on the first network verb.
|
|
259
268
|
|
|
260
269
|
```ts
|
|
261
270
|
const space = await was.createSpace({ name: 'Home' })
|
|
@@ -282,10 +291,13 @@ one item" footgun. The next sections cover each level in turn.
|
|
|
282
291
|
|
|
283
292
|
A Space is the top-level container, created from the spaces repository. The
|
|
284
293
|
server requires a `name`; `controller` defaults to the client's own DID, and the
|
|
285
|
-
server generates the id unless you pass one. `createSpace()`
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
294
|
+
server generates the id unless you pass one. `createSpace()` reads the
|
|
295
|
+
`Location` header of the response and returns a handle on that Space URL. A
|
|
296
|
+
response without `Location` throws `WasServerError`. `createSpace()` and
|
|
297
|
+
`listSpaces()` address the Spaces Repository URL the service description names
|
|
298
|
+
(see [Service discovery](#service-discovery)). Against a server whose
|
|
299
|
+
description has no `spaces` URL, they throw `NotSupportedError` without sending
|
|
300
|
+
anything.
|
|
289
301
|
|
|
290
302
|
```ts
|
|
291
303
|
const space = await was.createSpace({ name: 'Home' }) // POST {spacesUrl}
|
|
@@ -295,8 +307,8 @@ const aux = await was.createSpace({
|
|
|
295
307
|
type: ['AuxiliarySpace', 'Space']
|
|
296
308
|
})
|
|
297
309
|
|
|
298
|
-
// Lazy handle to an existing space by
|
|
299
|
-
const same = was.
|
|
310
|
+
// Lazy handle to an existing space by its URL -- no I/O until a verb runs.
|
|
311
|
+
const same = was.spaceAt(space.url)
|
|
300
312
|
|
|
301
313
|
// Read the Space Metadata object (null if missing or not visible to you).
|
|
302
314
|
const desc = await space.describe() // { id, type: ['Space'], name, controller } | null
|
|
@@ -318,7 +330,7 @@ await space.replaceDescription(
|
|
|
318
330
|
{ ifMatch: read!.etag }
|
|
319
331
|
) // { etag? }
|
|
320
332
|
await was
|
|
321
|
-
.
|
|
333
|
+
.spaceAt(freshSpaceUrl)
|
|
322
334
|
.replaceDescription(
|
|
323
335
|
{ name: 'Fresh', controller: 'did:key:z6Mk...' },
|
|
324
336
|
{ ifNoneMatch: true }
|
|
@@ -493,9 +505,16 @@ await collection.put('greeting', new Blob(['hello'], { type: 'text/plain' }))
|
|
|
493
505
|
### Delegation and sharing
|
|
494
506
|
|
|
495
507
|
`was.grant(...)` is the general delegation primitive; `space.grant(...)` and
|
|
496
|
-
`collection.grant(...)` are sugar that prefill the grant `target`
|
|
497
|
-
handle
|
|
498
|
-
`
|
|
508
|
+
`collection.grant(...)` are sugar that prefill the grant `target` and `spaceUrl`
|
|
509
|
+
from the handle. A bare `was.grant(...)` with no `capability` and no `spaceUrl`
|
|
510
|
+
is rooted at the target's own root capability. Passing `spaceUrl` roots it at
|
|
511
|
+
that Space instead, and throws `ValidationError` if `target` is not at or
|
|
512
|
+
beneath the Space. The recipient rebuilds access from the received zcap with
|
|
513
|
+
`fromCapability()`, which reads the Space off the capability's chain root. A
|
|
514
|
+
zcap whose chain starts at no root capability, or at one whose target cannot be
|
|
515
|
+
a Space URL, is refused with `ValidationError`, as is one whose
|
|
516
|
+
`invocationTarget` is not the canonical form of the URL the handle would
|
|
517
|
+
address. The server is the authority on whether the root names a Space.
|
|
499
518
|
|
|
500
519
|
```ts
|
|
501
520
|
// Alice grants Bob read access to a resource.
|
|
@@ -503,7 +522,8 @@ const added = await collection.add({ secret: 'value' })
|
|
|
503
522
|
const zcap = await was.grant({
|
|
504
523
|
to: bobDid,
|
|
505
524
|
actions: ['GET'], // HTTP verbs: 'GET' | 'HEAD' | 'PUT' | 'POST' | 'DELETE'
|
|
506
|
-
target: added.url
|
|
525
|
+
target: added.url,
|
|
526
|
+
spaceUrl: space.url
|
|
507
527
|
})
|
|
508
528
|
|
|
509
529
|
// Bob, holding the zcap, rebuilds a handle at the right depth.
|
|
@@ -531,7 +551,7 @@ lowercase forms and normalizes them to uppercase in the signed zcap -- so
|
|
|
531
551
|
|
|
532
552
|
`space.revoke(zcap)` undoes a `grant()`, so a leaked capability need not be
|
|
533
553
|
waited out to its `expires`. `was.revoke(zcap)` is the same operation with the
|
|
534
|
-
Space
|
|
554
|
+
Space read off the capability's delegation chain root.
|
|
535
555
|
|
|
536
556
|
```ts
|
|
537
557
|
const zcap = await collection.grant({ to: appDid, actions: ['GET', 'PUT'] })
|
|
@@ -915,15 +935,15 @@ if (serverDid === undefined) {
|
|
|
915
935
|
|
|
916
936
|
// The pull capability: GET-only, rooted in the peer Space, delegated to that
|
|
917
937
|
// DID.
|
|
918
|
-
const capability = await peerWas.
|
|
938
|
+
const capability = await peerWas.spaceAt(peerSpaceUrl).grant({
|
|
919
939
|
to: serverDid,
|
|
920
940
|
actions: ['GET']
|
|
921
941
|
})
|
|
922
942
|
|
|
923
943
|
const registration = await space.registerReplica({
|
|
924
944
|
id: 'peer-1', // client-chosen and URL-safe
|
|
925
|
-
fromSpace:
|
|
926
|
-
toSpace:
|
|
945
|
+
fromSpace: peerSpaceUrl,
|
|
946
|
+
toSpace: space.url,
|
|
927
947
|
capability,
|
|
928
948
|
collections: [{ id: 'credentials' }], // omit to pull every Collection
|
|
929
949
|
role: 'source'
|
|
@@ -1039,16 +1059,16 @@ import { createEdvEncryption } from '@interop/was-client/edv'
|
|
|
1039
1059
|
|
|
1040
1060
|
const encryption = createEdvEncryption({
|
|
1041
1061
|
// The keystore: return the collection's keys (from your wallet).
|
|
1042
|
-
async resolveKeys({
|
|
1062
|
+
async resolveKeys({ spaceUrl, collectionId }) {
|
|
1043
1063
|
return { keyAgreementKey, keyResolver }
|
|
1044
1064
|
}
|
|
1045
1065
|
})
|
|
1046
|
-
const was = WasClient.fromSigner({
|
|
1066
|
+
const was = WasClient.fromSigner({ serviceUrl, signer, encryption })
|
|
1047
1067
|
|
|
1048
1068
|
// Declare the collection encrypted (writes the descriptor). The returned handle is
|
|
1049
1069
|
// pre-seeded, so the first write encrypts with no extra round-trip.
|
|
1050
1070
|
const vault = await was
|
|
1051
|
-
.
|
|
1071
|
+
.spaceAt(spaceUrl)
|
|
1052
1072
|
.createCollection({ id: 'vault', encryption: { scheme: 'edv' } })
|
|
1053
1073
|
const { id } = await vault.add({ secret: 'hello' }) // encrypted; id is an EDV id
|
|
1054
1074
|
const back = await vault.get(id) // { secret: 'hello' } -- decrypted
|
|
@@ -1056,7 +1076,7 @@ const back = await vault.get(id) // { secret: 'hello' } -- decrypted
|
|
|
1056
1076
|
// A consumer that did not create it discovers the descriptor and decrypts with its
|
|
1057
1077
|
// own keys -- no override needed; one cached read of the Collection Metadata
|
|
1058
1078
|
// object:
|
|
1059
|
-
const same = was.
|
|
1079
|
+
const same = was.spaceAt(spaceUrl).collection('vault')
|
|
1060
1080
|
await same.get(id) // reads the descriptor, then decrypts
|
|
1061
1081
|
```
|
|
1062
1082
|
|
|
@@ -1073,7 +1093,7 @@ to force the decision and skip the Collection Metadata read --
|
|
|
1073
1093
|
inline), or `'plaintext'`:
|
|
1074
1094
|
|
|
1075
1095
|
```ts
|
|
1076
|
-
const vault = was.
|
|
1096
|
+
const vault = was.spaceAt(spaceUrl).collection('vault', {
|
|
1077
1097
|
encryption: { scheme: 'edv' }
|
|
1078
1098
|
})
|
|
1079
1099
|
```
|
|
@@ -1154,7 +1174,7 @@ same codec, doc ciphers, key epochs, recipient operations, blinding keys,
|
|
|
1154
1174
|
`resourceDescriptorStore`, and the log-governed descriptor stores, with every
|
|
1155
1175
|
transport module left out of the import graph. `collectionDescriptorStore` stays
|
|
1156
1176
|
on `./edv`, since it reads through a live `Collection` handle. The example below
|
|
1157
|
-
leaves `
|
|
1177
|
+
leaves `spaceUrl` unset. A cipher told which Space its collection lives in loads
|
|
1158
1178
|
the transport factory for the chunked paths. A cipher with no Space still reads
|
|
1159
1179
|
a chunked envelope when `decrypt` is given a `chunkSource`, a function that
|
|
1160
1180
|
serves each chunk (`{ docId, chunkIndex }` to the parsed chunk, or `undefined`)
|
|
@@ -1210,7 +1230,7 @@ await ensureFirstEpoch({
|
|
|
1210
1230
|
Then declare what is searchable and search it:
|
|
1211
1231
|
|
|
1212
1232
|
```ts
|
|
1213
|
-
const vault = was.
|
|
1233
|
+
const vault = was.spaceAt(spaceUrl).collection('vault')
|
|
1214
1234
|
await vault.declareIndex({ attribute: 'content.type' })
|
|
1215
1235
|
|
|
1216
1236
|
await vault.add({ type: 'note', title: 'alpha' })
|
|
@@ -1265,7 +1285,7 @@ to replicate one Space + Collection across devices, with PWS as the primary
|
|
|
1265
1285
|
copy. It is not a sync engine itself -- it provides the seams a change engine
|
|
1266
1286
|
plugs into:
|
|
1267
1287
|
|
|
1268
|
-
- **`createWasSyncPort({ was,
|
|
1288
|
+
- **`createWasSyncPort({ was, spaceUrl, collectionId })`** builds a
|
|
1269
1289
|
`WasSyncPort`: paged pulls over the collection's `changes` feed (resumable via
|
|
1270
1290
|
an opaque server-side checkpoint) and conditional pushes
|
|
1271
1291
|
(`putContent`/`deleteContent`/`putMeta`) guarded by the server's content
|
|
@@ -1294,7 +1314,10 @@ plugs into:
|
|
|
1294
1314
|
does not list (a re-read may fix it) and an epoch it lists but this reader
|
|
1295
1315
|
holds no key for (real data, permanently unreadable here, never garbage).
|
|
1296
1316
|
`isIntegrityError` matches a stored body that does not verify against the
|
|
1297
|
-
resource id it was read under
|
|
1317
|
+
resource id it was read under, a sealed envelope whose AEAD tag fails for a
|
|
1318
|
+
key this reader holds, or a descriptor recipient entry addressed to this
|
|
1319
|
+
reader whose wrapped epoch key fails its integrity check. Do not apply that
|
|
1320
|
+
row.
|
|
1298
1321
|
- **`SyncStatus`** (`'idle' | 'syncing' | 'synced' | 'error'`) is the closed
|
|
1299
1322
|
vocabulary a replication driver reports one feed's state through.
|
|
1300
1323
|
- **`DocCipher`** is the per-collection encrypt/decrypt seam sitting above the
|
|
@@ -1308,10 +1331,10 @@ plugs into:
|
|
|
1308
1331
|
collection; `createEdvDocCipher(...)` (from `@interop/was-client/edv`) is the
|
|
1309
1332
|
encrypting one, built from the collection's key-epoch descriptor (every
|
|
1310
1333
|
encrypted collection carries one from birth; install epoch[0] at provision
|
|
1311
|
-
time with `ensureFirstEpoch`). Build it with the collection's `
|
|
1312
|
-
`decrypt` fetch a chunked blob's chunks, or pass `decrypt` a `chunkSource`
|
|
1313
|
-
serve them from bytes you hold. On a searchable collection, also hand it
|
|
1314
|
-
stored Collection `/meta` value (the `meta` input, or `applyMeta` when the
|
|
1334
|
+
time with `ensureFirstEpoch`). Build it with the collection's `spaceUrl` to
|
|
1335
|
+
let `decrypt` fetch a chunked blob's chunks, or pass `decrypt` a `chunkSource`
|
|
1336
|
+
to serve them from bytes you hold. On a searchable collection, also hand it
|
|
1337
|
+
the stored Collection `/meta` value (the `meta` input, or `applyMeta` when the
|
|
1315
1338
|
replica's copy changes) so pushed documents carry blinded index entries and
|
|
1316
1339
|
stay visible to `find()`. `createEdvEncryptOnlyDocCipher(...)` is the
|
|
1317
1340
|
write-only counterpart, built from the descriptor alone with no key-agreement
|
|
@@ -1353,21 +1376,21 @@ plugs into:
|
|
|
1353
1376
|
import {
|
|
1354
1377
|
createWasSyncPort,
|
|
1355
1378
|
createPlaintextDocCipher,
|
|
1356
|
-
deriveSpaceId,
|
|
1357
1379
|
ensureSpaceAndCollection,
|
|
1358
1380
|
isSyncConflictError
|
|
1359
1381
|
} from '@interop/was-client/sync'
|
|
1360
1382
|
|
|
1361
|
-
|
|
1383
|
+
// The Space URL is received, then stored: the same Space on every client.
|
|
1384
|
+
const spaceUrl = storedSpaceUrl
|
|
1362
1385
|
await ensureSpaceAndCollection({
|
|
1363
1386
|
was,
|
|
1364
|
-
|
|
1387
|
+
spaceUrl,
|
|
1365
1388
|
controllerDid,
|
|
1366
1389
|
collectionId: 'notes',
|
|
1367
1390
|
encryption: 'plaintext'
|
|
1368
1391
|
})
|
|
1369
1392
|
|
|
1370
|
-
const port = createWasSyncPort({ was,
|
|
1393
|
+
const port = createWasSyncPort({ was, spaceUrl, collectionId: 'notes' })
|
|
1371
1394
|
const cipher = createPlaintextDocCipher({ collectionId: 'notes' })
|
|
1372
1395
|
|
|
1373
1396
|
// Push: encrypt (mints the id), then write the stored body verbatim.
|
|
@@ -1471,14 +1494,14 @@ large Blobs to disk.)
|
|
|
1471
1494
|
|
|
1472
1495
|
### The manual-request escape hatch
|
|
1473
1496
|
|
|
1474
|
-
`was.request(...)` mirrors ezcap's generic `request()` for hand-built calls.
|
|
1475
|
-
a deliberate escape hatch it returns the raw
|
|
1476
|
-
ezcap/ky errors -- it does not apply the
|
|
1477
|
-
conveniences. It still waits on service discovery, so
|
|
1478
|
-
server it rejects with `IncompatibleServerError`.
|
|
1497
|
+
`was.request(...)` mirrors ezcap's generic `request()` for hand-built calls. It
|
|
1498
|
+
takes an absolute `url`. As a deliberate escape hatch it returns the raw
|
|
1499
|
+
`HttpResponse` and throws raw ezcap/ky errors -- it does not apply the
|
|
1500
|
+
null-on-404 or typed-error conveniences. It still waits on service discovery, so
|
|
1501
|
+
against an incompatible server it rejects with `IncompatibleServerError`.
|
|
1479
1502
|
|
|
1480
1503
|
```ts
|
|
1481
|
-
const response = await was.request({
|
|
1504
|
+
const response = await was.request({ url: space.url, method: 'GET' })
|
|
1482
1505
|
```
|
|
1483
1506
|
|
|
1484
1507
|
## Errors and the 404/null caveat
|
package/dist/Collection.d.ts
CHANGED
|
@@ -6,22 +6,36 @@ import type { ChangesCheckpoint, ChangesPage, ResourceChangeDocument } from '@in
|
|
|
6
6
|
import type { AddResult, BackendDescriptor, BackendUsage, CollectionMetadata, CollectionWritableFields, EncryptionOverride, FindPage, GrantOptions, HandleOptions, IDelegatedZcap, IZcap, Json, ResourceData, LinkSet, PolicyDocument, CollectionResourcesList, ResourceMetadataCustomInput, ResourceSummary } from './types.js';
|
|
7
7
|
export declare class Collection {
|
|
8
8
|
#private;
|
|
9
|
+
/**
|
|
10
|
+
* The URL of the Space this Collection lives in, in canonical form.
|
|
11
|
+
*/
|
|
12
|
+
readonly spaceUrl: string;
|
|
13
|
+
/**
|
|
14
|
+
* The id of that Space: the last path segment of {@link spaceUrl}.
|
|
15
|
+
*/
|
|
9
16
|
readonly spaceId: string;
|
|
10
17
|
readonly id: string;
|
|
18
|
+
/**
|
|
19
|
+
* The Collection container in canonical (trailing-slash) form: the URL whose
|
|
20
|
+
* `GET` lists the Collection's Resources, whose `POST` adds one, and whose
|
|
21
|
+
* `DELETE` removes the Collection. A Collection's own description is not
|
|
22
|
+
* here -- it is the Metadata object at the reserved `meta` segment.
|
|
23
|
+
*/
|
|
24
|
+
readonly url: string;
|
|
11
25
|
/**
|
|
12
26
|
* @param options {object}
|
|
13
|
-
* @param options.context {ClientContext} - Shared context (
|
|
14
|
-
*
|
|
15
|
-
* @param options.
|
|
27
|
+
* @param options.context {ClientContext} - Shared context (ezcap client,
|
|
28
|
+
* controllerDid)
|
|
29
|
+
* @param options.spaceUrl {string} the Space's URL, as the server named it
|
|
16
30
|
* @param options.collectionId {string}
|
|
17
31
|
* @param [options.capability] {IZcap} - capability attached to every request
|
|
18
32
|
* @param [options.encryption] {EncryptionOverride} - per-handle encryption
|
|
19
33
|
* override; wins over the Collection's declared descriptor and skips the
|
|
20
34
|
* descriptor-discovery round-trip
|
|
21
35
|
*/
|
|
22
|
-
constructor({ context,
|
|
36
|
+
constructor({ context, spaceUrl, collectionId, capability, encryption }: {
|
|
23
37
|
context: ClientContext;
|
|
24
|
-
|
|
38
|
+
spaceUrl: string;
|
|
25
39
|
collectionId: string;
|
|
26
40
|
capability?: IZcap;
|
|
27
41
|
encryption?: EncryptionOverride;
|
|
@@ -303,10 +317,8 @@ export declare class Collection {
|
|
|
303
317
|
* sub-resource {@link getHistoryLog} reads. A log-governed descriptor's
|
|
304
318
|
* `history.resource` names this URL; a verifying reader compares the two
|
|
305
319
|
* before opening the log.
|
|
306
|
-
*
|
|
307
|
-
* @returns {string}
|
|
308
320
|
*/
|
|
309
|
-
|
|
321
|
+
readonly historyLogUrl: string;
|
|
310
322
|
/**
|
|
311
323
|
* Reads the Collection's governing history log (the encrypted-collections
|
|
312
324
|
* profile's `governed-history-logs` feature): the JSON Lines body served at the
|
package/dist/Collection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Collection.d.ts","sourceRoot":"","sources":["../src/Collection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Collection.d.ts","sourceRoot":"","sources":["../src/Collection.ts"],"names":[],"mappings":"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"}
|