@interop/was-client 0.65.0 → 0.67.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 +59 -50
- package/dist/Collection.d.ts +41 -30
- package/dist/Collection.d.ts.map +1 -1
- package/dist/Collection.js +41 -69
- package/dist/Collection.js.map +1 -1
- package/dist/Resource.d.ts +28 -37
- package/dist/Resource.d.ts.map +1 -1
- package/dist/Resource.js +26 -52
- package/dist/Resource.js.map +1 -1
- package/dist/Space.d.ts +14 -14
- package/dist/Space.d.ts.map +1 -1
- package/dist/Space.js +16 -16
- package/dist/Space.js.map +1 -1
- package/dist/WasClient.d.ts +3 -2
- package/dist/WasClient.d.ts.map +1 -1
- package/dist/WasClient.js +3 -2
- package/dist/WasClient.js.map +1 -1
- package/dist/codec.d.ts +8 -14
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js.map +1 -1
- package/dist/edv/EdvCodec.d.ts +3 -3
- package/dist/edv/EdvCodec.d.ts.map +1 -1
- package/dist/edv/EdvCodec.js +25 -74
- package/dist/edv/EdvCodec.js.map +1 -1
- package/dist/edv/WasTransport.d.ts +30 -44
- package/dist/edv/WasTransport.d.ts.map +1 -1
- package/dist/edv/WasTransport.js +38 -119
- package/dist/edv/WasTransport.js.map +1 -1
- package/dist/edv/descriptorStore.d.ts +13 -12
- package/dist/edv/descriptorStore.d.ts.map +1 -1
- package/dist/edv/descriptorStore.js +7 -6
- package/dist/edv/descriptorStore.js.map +1 -1
- package/dist/edv/docCipher.d.ts +10 -1
- package/dist/edv/docCipher.d.ts.map +1 -1
- package/dist/edv/docCipher.js +23 -6
- package/dist/edv/docCipher.js.map +1 -1
- package/dist/edv/logGovernedDescriptorStore.d.ts.map +1 -1
- package/dist/edv/logGovernedDescriptorStore.js +4 -2
- package/dist/edv/logGovernedDescriptorStore.js.map +1 -1
- package/dist/edv/refreshingDocCipher.d.ts +4 -1
- package/dist/edv/refreshingDocCipher.d.ts.map +1 -1
- package/dist/edv/refreshingDocCipher.js +7 -4
- package/dist/edv/refreshingDocCipher.js.map +1 -1
- package/dist/errors.d.ts +24 -7
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +27 -7
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/cas.d.ts +22 -10
- package/dist/internal/cas.d.ts.map +1 -1
- package/dist/internal/cas.js +37 -11
- package/dist/internal/cas.js.map +1 -1
- package/dist/internal/conditional.d.ts +29 -8
- package/dist/internal/conditional.d.ts.map +1 -1
- package/dist/internal/conditional.js +30 -6
- package/dist/internal/conditional.js.map +1 -1
- package/dist/internal/memo.d.ts +2 -2
- package/dist/internal/memo.js +2 -2
- package/dist/internal/meta.d.ts +12 -10
- package/dist/internal/meta.d.ts.map +1 -1
- package/dist/internal/meta.js +20 -13
- package/dist/internal/meta.js.map +1 -1
- package/dist/internal/paths.d.ts +1 -1
- package/dist/internal/paths.js +1 -1
- package/dist/internal/write.d.ts +12 -29
- package/dist/internal/write.d.ts.map +1 -1
- package/dist/internal/write.js +15 -74
- package/dist/internal/write.js.map +1 -1
- package/dist/log/logStore.d.ts +10 -6
- package/dist/log/logStore.d.ts.map +1 -1
- package/dist/log/logStore.js +16 -6
- package/dist/log/logStore.js.map +1 -1
- package/dist/sync/index.d.ts +12 -5
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +12 -5
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/plaintextCipher.d.ts +6 -4
- package/dist/sync/plaintextCipher.d.ts.map +1 -1
- package/dist/sync/plaintextCipher.js +19 -6
- package/dist/sync/plaintextCipher.js.map +1 -1
- package/dist/sync/port.d.ts +3 -1
- package/dist/sync/port.d.ts.map +1 -1
- package/dist/sync/port.js +3 -3
- package/dist/sync/port.js.map +1 -1
- package/dist/sync/predicates.d.ts +33 -3
- package/dist/sync/predicates.d.ts.map +1 -1
- package/dist/sync/predicates.js +37 -3
- package/dist/sync/predicates.js.map +1 -1
- package/dist/sync/types.d.ts +24 -5
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/types.d.ts +4 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/internal/features.d.ts +0 -105
- package/dist/internal/features.d.ts.map +0 -1
- package/dist/internal/features.js +0 -168
- package/dist/internal/features.js.map +0 -1
package/README.md
CHANGED
|
@@ -217,7 +217,7 @@ info.version // '0.5'
|
|
|
217
217
|
info.entry // the chosen `PwsVersionEntry`
|
|
218
218
|
info.description // the whole `ServiceDescription`
|
|
219
219
|
info.spacesUrl // the Spaces Repository URL, or undefined
|
|
220
|
-
info.features // e.g. ['listing', 'collection-management', ...]
|
|
220
|
+
info.features // e.g. ['listing', 'collection-management', 'changes-query', ...]
|
|
221
221
|
info.hasFeature('quotas') // false when the token is absent
|
|
222
222
|
|
|
223
223
|
// Discover again, e.g. before relying on an affordance a host may have dropped.
|
|
@@ -611,7 +611,7 @@ await collection.setMeta({ custom: { name: 'Vault', tags: { app: 'wallet' } } })
|
|
|
611
611
|
await collection.setName('Renamed vault') // keeps existing tags
|
|
612
612
|
await collection.setTags({ app: 'wallet' }) // keeps existing name
|
|
613
613
|
|
|
614
|
-
// Conditional metadata write
|
|
614
|
+
// Conditional metadata write.
|
|
615
615
|
await collection.setMeta({ custom: { name: 'Vault' } }, { ifMatch: meta?.etag })
|
|
616
616
|
// ...or create the Collection only if it does not exist yet:
|
|
617
617
|
await collection.setMeta({ custom: { name: 'Vault' } }, { ifNoneMatch: true })
|
|
@@ -690,10 +690,10 @@ const { descriptor } = await store.read() // verified head state, `history` kept
|
|
|
690
690
|
|
|
691
691
|
### Conditional writes (optimistic concurrency)
|
|
692
692
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
693
|
+
Every Resource carries a strong **`ETag`** validator that changes on every write
|
|
694
|
+
(conditional writes are a baseline WAS server requirement). Use it to prevent
|
|
695
|
+
the lost-update problem -- two clients that both read version _N_ and each write
|
|
696
|
+
_N+1_, the second silently clobbering the first.
|
|
697
697
|
|
|
698
698
|
```ts
|
|
699
699
|
const { etag } = await collection.put('doc', { v: 1 }) // writes return the ETag
|
|
@@ -740,20 +740,21 @@ const usage = await collection.quota() // BackendUsage | null
|
|
|
740
740
|
// usage: { id, state, usageBytes, limit, restrictedActions, measuredAt, ... }
|
|
741
741
|
```
|
|
742
742
|
|
|
743
|
-
A `BackendDescriptor`'s
|
|
744
|
-
affordances
|
|
745
|
-
|
|
746
|
-
absent token means the backend makes no claim to it, so treat it as unsupported
|
|
747
|
-
rather than assuming a default. (Client-side encryption is _not_ a backend
|
|
748
|
-
feature -- see below.)
|
|
743
|
+
A `BackendDescriptor` says where a Collection's data lives; it advertises no
|
|
744
|
+
optional affordances. Server-wide affordances are read from the service
|
|
745
|
+
description instead:
|
|
749
746
|
|
|
750
747
|
```ts
|
|
751
|
-
const
|
|
752
|
-
if (
|
|
753
|
-
//
|
|
748
|
+
const info = await was.service()
|
|
749
|
+
if (info.hasFeature('changes-query')) {
|
|
750
|
+
// the server serves the replication change feed
|
|
754
751
|
}
|
|
755
752
|
```
|
|
756
753
|
|
|
754
|
+
An absent token means the server makes no claim to it, so treat it as
|
|
755
|
+
unsupported rather than assuming a default. (Client-side encryption is _not_ a
|
|
756
|
+
server affordance -- see below.)
|
|
757
|
+
|
|
757
758
|
### Registering a Bring-Your-Own-Storage backend
|
|
758
759
|
|
|
759
760
|
Beyond the server's built-in `default` backend, the Space controller can
|
|
@@ -768,7 +769,6 @@ const descriptor = await space.registerBackend({
|
|
|
768
769
|
id: 'gdrive-personal', // unique within the Space
|
|
769
770
|
name: 'My Google Drive',
|
|
770
771
|
provider: 'google-drive', // selects the server-side adapter
|
|
771
|
-
storageMode: ['document', 'blob'],
|
|
772
772
|
connection: {
|
|
773
773
|
kind: 'oauth2-google',
|
|
774
774
|
authorizationCode: '4/0Ab...', // one-time PKCE code (or a refreshToken)
|
|
@@ -918,12 +918,13 @@ scope for now):
|
|
|
918
918
|
- **Binary.** A `Blob`/`Uint8Array` up to `maxBlobBytes` (512 KiB by default) is
|
|
919
919
|
encrypted as a single document. A larger one is routed automatically by
|
|
920
920
|
`add()` to the chunked-stream path: one document plus its chunk resources,
|
|
921
|
-
read back transparently by `get()`.
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
is
|
|
925
|
-
|
|
926
|
-
|
|
921
|
+
read back transparently by `get()`. Serving the chunk endpoints is a
|
|
922
|
+
conformance requirement of a server that offers encrypted collections, so
|
|
923
|
+
there is no client-side gate -- one that does not answers `501`. Two limits
|
|
924
|
+
apply. Auto-routing is an `add()` affordance -- `put(id, bigBlob)` is refused,
|
|
925
|
+
since replacing an existing document's chunks is not automated. And a
|
|
926
|
+
content-addressed collection (`idDerivation: 'content'`) is refused too: a
|
|
927
|
+
chunked write stores the document twice, so no single ciphertext derives its
|
|
927
928
|
id. Tune the threshold and the chunk size with
|
|
928
929
|
`createEdvEncryption({ maxBlobBytes, chunkSize })`.
|
|
929
930
|
- **Raw reads.** `get()` decrypts; the `getText()` / `getBytes()` escape hatches
|
|
@@ -989,9 +990,10 @@ Two properties are worth planning around:
|
|
|
989
990
|
an attribute the schema does not declare throws `ValidationError` rather than
|
|
990
991
|
silently matching nothing.
|
|
991
992
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
993
|
+
Serving the blinded-index query profile is a conformance requirement of a server
|
|
994
|
+
that offers encrypted collections; one that does not answers `501`
|
|
995
|
+
(`NotImplementedError`). `find()` / `declareIndex()` on a plaintext collection
|
|
996
|
+
throw -- there is no client-side index there.
|
|
995
997
|
|
|
996
998
|
Note that `collection.setMeta({ custom })` replaces the whole `custom` object,
|
|
997
999
|
schema included; the `setName()` / `setTags()` sugar merges instead and leaves
|
|
@@ -1017,31 +1019,38 @@ plugs into:
|
|
|
1017
1019
|
failure arrives as the typed `WasError` subclass for its status, carrying the
|
|
1018
1020
|
server's `problem+json` fields.
|
|
1019
1021
|
- **`isSyncConflictError` / `isSyncNotFoundError` / `isSyncAuthError` /
|
|
1020
|
-
`isUnknownEpochError` / `isKeyUnwrapError`** classify
|
|
1021
|
-
`err.name`. Use them rather than `instanceof`: the port and
|
|
1022
|
-
are seams your app injects, and a tree that resolves two
|
|
1023
|
-
package makes an `instanceof` check quietly false. Read what
|
|
1024
|
-
matched value, such as `err.status` on an auth error to tell
|
|
1025
|
-
masked 404. The last two tell a decrypt's two no-key outcomes
|
|
1026
|
-
the reader's descriptor does not list (a re-read may fix it)
|
|
1027
|
-
lists but this reader holds no key for (real data, permanently
|
|
1028
|
-
here, never garbage).
|
|
1022
|
+
`isUnknownEpochError` / `isKeyUnwrapError` / `isIntegrityError`** classify
|
|
1023
|
+
those signals by `err.name`. Use them rather than `instanceof`: the port and
|
|
1024
|
+
the `DocCipher` are seams your app injects, and a tree that resolves two
|
|
1025
|
+
copies of this package makes an `instanceof` check quietly false. Read what
|
|
1026
|
+
you need off the matched value, such as `err.status` on an auth error to tell
|
|
1027
|
+
a 401 from the masked 404. The last two tell a decrypt's two no-key outcomes
|
|
1028
|
+
apart: an epoch the reader's descriptor does not list (a re-read may fix it)
|
|
1029
|
+
and an epoch it lists but this reader holds no key for (real data, permanently
|
|
1030
|
+
unreadable here, never garbage). `isIntegrityError` matches a stored body that
|
|
1031
|
+
does not verify against the resource id it was read under. Do not apply that
|
|
1032
|
+
row.
|
|
1029
1033
|
- **`SyncStatus`** (`'idle' | 'syncing' | 'synced' | 'error'`) is the closed
|
|
1030
1034
|
vocabulary a replication driver reports one feed's state through.
|
|
1031
1035
|
- **`DocCipher`** is the per-collection encrypt/decrypt seam sitting above the
|
|
1032
1036
|
port: it turns a JSON document into its stored body (minting the resource id)
|
|
1033
|
-
and back. `
|
|
1034
|
-
|
|
1035
|
-
`
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1037
|
+
and back. `decrypt({ id, envelope, context? })` takes the resource id the
|
|
1038
|
+
replica read the body under (the feed row's `id`, not the envelope's own) and
|
|
1039
|
+
throws `IntegrityError` when the body was written for a different id. Pass
|
|
1040
|
+
`collection.codecContext()` as `context` to read a chunked encrypted blob,
|
|
1041
|
+
which then decrypts to a `Blob`. `createPlaintextDocCipher(...)` is the
|
|
1042
|
+
crypto-free identity implementation for a plaintext content-addressed
|
|
1043
|
+
collection; `createEdvDocCipher(...)` (from `@interop/was-client/edv`) is the
|
|
1044
|
+
encrypting one, built from the collection's key-epoch descriptor (every
|
|
1045
|
+
encrypted collection carries one from birth; install epoch[0] at provision
|
|
1046
|
+
time with `ensureFirstEpoch`). Build it with the collection's `spaceId` to let
|
|
1047
|
+
`decrypt` fetch a chunked blob's chunks. On a searchable collection, also hand
|
|
1048
|
+
it the stored Collection `/meta` value (the `meta` input, or `applyMeta` when
|
|
1049
|
+
the replica's copy changes) so pushed documents carry blinded index entries
|
|
1050
|
+
and stay visible to `find()`. `createEdvEncryptOnlyDocCipher(...)` is the
|
|
1051
|
+
write-only counterpart, built from the descriptor alone with no key-agreement
|
|
1052
|
+
secret (writes seal to the current epoch's public key, reconstructed from the
|
|
1053
|
+
epoch id); `decrypt` on it refuses with the typed `EncryptOnlyCipherError`.
|
|
1045
1054
|
- **`createRefreshingEdvDocCipher(...)`** (from `@interop/was-client/edv`) is
|
|
1046
1055
|
`createEdvDocCipher` bound to descriptor acquisition and the unknown-epoch
|
|
1047
1056
|
refresh rule: it acquires the collection's descriptor through the
|
|
@@ -1108,7 +1117,7 @@ do {
|
|
|
1108
1117
|
const page = await port.query({ checkpoint, limit: 100 })
|
|
1109
1118
|
for (const doc of page.documents) {
|
|
1110
1119
|
if (doc._deleted) continue // tombstone
|
|
1111
|
-
const data = await cipher.decrypt({ envelope: doc.data })
|
|
1120
|
+
const data = await cipher.decrypt({ id: doc.id, envelope: doc.data })
|
|
1112
1121
|
// apply to the local replica, recording doc.version for later pushes
|
|
1113
1122
|
}
|
|
1114
1123
|
checkpoint = page.checkpoint // null when the page was empty (caught up)
|
|
@@ -1144,9 +1153,9 @@ re-exports none of them.
|
|
|
1144
1153
|
governing history log" above). The store does read-with-etag of the full log,
|
|
1145
1154
|
compare-and-swap append conditioned on that etag, and the guarded create of a
|
|
1146
1155
|
genesis entry. Appends carry the prior lines' bytes forward verbatim. Both
|
|
1147
|
-
writes ride the
|
|
1148
|
-
|
|
1149
|
-
|
|
1156
|
+
writes ride the server's conditional writes, which the profile requires; a
|
|
1157
|
+
lost race rethrows the library's `ResourceLogConflictError` with the
|
|
1158
|
+
transport's `PreconditionFailedError` as `cause`.
|
|
1150
1159
|
|
|
1151
1160
|
### Export and import
|
|
1152
1161
|
|
package/dist/Collection.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ClientContext } from './internal/request.js';
|
|
2
|
-
import type { IndexDeclaration, IndexSchema } from './codec.js';
|
|
2
|
+
import type { CodecRequestContext, IndexDeclaration, IndexSchema } from './codec.js';
|
|
3
3
|
import { Resource } from './Resource.js';
|
|
4
4
|
import type { ChangeDocument, ChangesCheckpoint, ChangesPage } from '@interop/storage-core';
|
|
5
5
|
import type { AddResult, BackendDescriptor, BackendUsage, CollectionMetadata, CollectionWritableFields, EncryptionOverride, FindPage, GrantOptions, HandleOptions, IDelegatedZcap, IZcap, Json, ResourceData, LinkSet, PolicyDocument, CollectionResourcesList, ResourceMetadataCustomInput, ResourceSummary } from './types.js';
|
|
@@ -25,6 +25,17 @@ export declare class Collection {
|
|
|
25
25
|
capability?: IZcap;
|
|
26
26
|
encryption?: EncryptionOverride;
|
|
27
27
|
});
|
|
28
|
+
/**
|
|
29
|
+
* The signed-request context handed to a codec that drives its own I/O (the
|
|
30
|
+
* EDV codec reading a chunked blob back), bound to this handle's capability.
|
|
31
|
+
*
|
|
32
|
+
* Public so a sync replica can pass it to `DocCipher.decrypt`, which then
|
|
33
|
+
* reassembles a chunked envelope from its chunk resources on this
|
|
34
|
+
* collection. Building it performs no I/O.
|
|
35
|
+
*
|
|
36
|
+
* @returns {CodecRequestContext}
|
|
37
|
+
*/
|
|
38
|
+
codecContext(): CodecRequestContext;
|
|
28
39
|
/**
|
|
29
40
|
* Reads the Collection Metadata object -- the Collection's configuration
|
|
30
41
|
* (`name`, `backend`, `encryption`, `generator`) beside the server-managed
|
|
@@ -169,7 +180,7 @@ export declare class Collection {
|
|
|
169
180
|
* no keys for, which is why {@link describe} -- the configuration read -- goes
|
|
170
181
|
* without it.
|
|
171
182
|
*
|
|
172
|
-
*
|
|
183
|
+
* The result also
|
|
173
184
|
* carries the object's current `etag` (its `metaVersion` validator) -- pass it
|
|
174
185
|
* as `setMeta(meta, { ifMatch })` for a lost-update-safe update. One
|
|
175
186
|
* validator covers the whole object: a configuration write and an annotation
|
|
@@ -195,8 +206,8 @@ export declare class Collection {
|
|
|
195
206
|
* and encrypted collections alike. The key epoch the codec stamps the
|
|
196
207
|
* envelope with travels as a top-level `epoch` member of the same body.
|
|
197
208
|
*
|
|
198
|
-
* Conditional writes (the
|
|
199
|
-
*
|
|
209
|
+
* Conditional writes: pass `ifMatch` (the `etag` from a prior {@link meta}
|
|
210
|
+
* or {@link describe}) for an
|
|
200
211
|
* update-if-unchanged, and a failed precondition throws
|
|
201
212
|
* `PreconditionFailedError` (412). Without one the write is pinned to the
|
|
202
213
|
* version it composed the configuration from and rebases on a lost race.
|
|
@@ -232,11 +243,10 @@ export declare class Collection {
|
|
|
232
243
|
/**
|
|
233
244
|
* Sets the Collection's annotation-level human-readable `name`, preserving
|
|
234
245
|
* any existing `tags`. Convenience over `setMeta()`. The write is pinned to
|
|
235
|
-
* the `etag` the `meta()` read returned
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
* full-replacement write.
|
|
246
|
+
* the `etag` the `meta()` read returned and rebases on a lost race, so a
|
|
247
|
+
* concurrent write -- an annotation write or a configuration change, which
|
|
248
|
+
* share the one validator -- is re-read and re-applied rather than silently
|
|
249
|
+
* erased by this full-replacement write.
|
|
240
250
|
*
|
|
241
251
|
* On an encrypted collection this is the collection's client-encrypted name
|
|
242
252
|
* surface: the codec seals it into the `custom` envelope, and by convention
|
|
@@ -268,8 +278,8 @@ export declare class Collection {
|
|
|
268
278
|
*/
|
|
269
279
|
get historyLogUrl(): string;
|
|
270
280
|
/**
|
|
271
|
-
* Reads the Collection's governing history log (the
|
|
272
|
-
* `governed-history-logs` feature): the JSON Lines body served at the
|
|
281
|
+
* Reads the Collection's governing history log (the encrypted-collections
|
|
282
|
+
* profile's `governed-history-logs` feature): the JSON Lines body served at the
|
|
273
283
|
* `/meta/log` sub-resource, verbatim, together with its `ETag`. The log is
|
|
274
284
|
* not a Resource of the Collection (absent from listings and the `changes`
|
|
275
285
|
* feed) and not part of the `/meta` object; it is versioned by its own
|
|
@@ -412,8 +422,10 @@ export declare class Collection {
|
|
|
412
422
|
* one page: `limit` caps its size (the server clamps its own maximum), and a
|
|
413
423
|
* `hasMore` page carries the `cursor` to pass back for the next one.
|
|
414
424
|
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
425
|
+
* Serving the blinded-index query profile is a conformance requirement of a
|
|
426
|
+
* server that offers encrypted collections, so there is no client-side
|
|
427
|
+
* probe; a server that does not implement it answers `501`
|
|
428
|
+
* (`NotImplementedError`).
|
|
417
429
|
*
|
|
418
430
|
* @param options {object}
|
|
419
431
|
* @param [options.equals] {object | object[]} attribute/value pairs to match
|
|
@@ -451,8 +463,7 @@ export declare class Collection {
|
|
|
451
463
|
*
|
|
452
464
|
* On an encrypted collection a binary payload above the codec's
|
|
453
465
|
* single-document threshold is auto-routed to the chunked-stream path, which
|
|
454
|
-
*
|
|
455
|
-
* it, raised before anything is written).
|
|
466
|
+
* a server that does not serve the chunk endpoints answers with `501`.
|
|
456
467
|
*
|
|
457
468
|
* @param data {ResourceData}
|
|
458
469
|
* @param options {object}
|
|
@@ -474,7 +485,7 @@ export declare class Collection {
|
|
|
474
485
|
/**
|
|
475
486
|
* Creates or replaces a resource by id (upsert). Forwards the
|
|
476
487
|
* conditional-write options (`ifMatch` / `ifNoneMatch`) to `Resource.put`;
|
|
477
|
-
* see it for the
|
|
488
|
+
* see it for the conditional-write semantics. Returns the stored
|
|
478
489
|
* resource's new `etag`.
|
|
479
490
|
*
|
|
480
491
|
* @param resourceId {string}
|
|
@@ -537,11 +548,11 @@ export declare class Collection {
|
|
|
537
548
|
* `limit` to its own maximum, so a short page can still be a full server
|
|
538
549
|
* page.
|
|
539
550
|
*
|
|
540
|
-
* Requires the
|
|
541
|
-
* (
|
|
542
|
-
* collection the documents' `data` / `custom` are the scheme's
|
|
543
|
-
* envelopes (an EDV encrypted document under the v1 `edv` scheme) --
|
|
544
|
-
* method does not decrypt them, unlike `get()`.
|
|
551
|
+
* Requires the server to advertise the `changes-query` feature server-wide
|
|
552
|
+
* (`client.service().features`); a server without it answers `501`. On an
|
|
553
|
+
* encrypted collection the documents' `data` / `custom` are the scheme's
|
|
554
|
+
* opaque envelopes (an EDV encrypted document under the v1 `edv` scheme) --
|
|
555
|
+
* this method does not decrypt them, unlike `get()`.
|
|
545
556
|
*
|
|
546
557
|
* Malformed responses fail the call with a `WasServerError` instead of
|
|
547
558
|
* passing through as a page: a 2xx response with no JSON body
|
|
@@ -578,9 +589,10 @@ export declare class Collection {
|
|
|
578
589
|
* `list()`. Unlike `list()`, a 404 on a later page throws: the collection
|
|
579
590
|
* vanished mid-walk, and the pages already read are not a snapshot of
|
|
580
591
|
* anything. The server faults `changes()` rejects on (a bodiless 2xx, a
|
|
581
|
-
* live entry with no `data`, or a `501` from a
|
|
582
|
-
* `changes-query` feature) fail the walk with the same
|
|
583
|
-
* does a server that repeats a checkpoint instead of
|
|
592
|
+
* live entry with no `data`, or a `501` from a server that does not
|
|
593
|
+
* advertise the `changes-query` feature) fail the walk with the same
|
|
594
|
+
* `WasServerError`, as does a server that repeats a checkpoint instead of
|
|
595
|
+
* advancing.
|
|
584
596
|
*
|
|
585
597
|
* @param [options] {object}
|
|
586
598
|
* @param [options.limit] {number} max documents per request (default 1000, the teaching server's maximum); the server reduces it to its own maximum
|
|
@@ -649,12 +661,11 @@ export declare class Collection {
|
|
|
649
661
|
* you (404 conflation caveat). A server without backend support surfaces its
|
|
650
662
|
* 501 as `NotImplementedError`.
|
|
651
663
|
*
|
|
652
|
-
* The descriptor
|
|
653
|
-
* affordances
|
|
654
|
-
* `
|
|
655
|
-
*
|
|
656
|
-
*
|
|
657
|
-
* gated on the client's keys.)
|
|
664
|
+
* The descriptor names where the collection is stored; it advertises no
|
|
665
|
+
* optional affordances. Server-wide affordance tokens live in the service
|
|
666
|
+
* description (`client.service().features`). (Client-side encryption is
|
|
667
|
+
* neither -- it is a per-collection client concern gated on the client's
|
|
668
|
+
* keys.)
|
|
658
669
|
*
|
|
659
670
|
* @returns {Promise<BackendDescriptor | null>}
|
|
660
671
|
*/
|
package/dist/Collection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Collection.d.ts","sourceRoot":"","sources":["../src/Collection.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"Collection.d.ts","sourceRoot":"","sources":["../src/Collection.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAwD1D,OAAO,KAAK,EAEV,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EAEZ,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,WAAW,EACZ,MAAM,uBAAuB,CAAA;AAC9B,OAAO,KAAK,EACV,SAAS,EACT,iBAAiB,EACjB,YAAY,EAEZ,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAClB,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,cAAc,EACd,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,OAAO,EACP,cAAc,EACd,uBAAuB,EACvB,2BAA2B,EAC3B,eAAe,EAChB,MAAM,YAAY,CAAA;AA8CnB,qBAAa,UAAU;;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IAYnB;;;;;;;;;;OAUG;gBACS,EACV,OAAO,EACP,OAAO,EACP,YAAY,EACZ,UAAU,EACV,UAAU,EACX,EAAE;QACD,OAAO,EAAE,aAAa,CAAA;QACtB,OAAO,EAAE,MAAM,CAAA;QACf,YAAY,EAAE,MAAM,CAAA;QACpB,UAAU,CAAC,EAAE,KAAK,CAAA;QAClB,UAAU,CAAC,EAAE,kBAAkB,CAAA;KAChC;IAsCD;;;;;;;;;OASG;IACH,YAAY,IAAI,mBAAmB;IAuBnC;;;;;;;;;;;;OAYG;IACG,QAAQ,IAAI,OAAO,CAAC,CAAC,kBAAkB,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI,CAAC;IAW1E;;;;;;;;;;OAUG;IACG,gBAAgB,IAAI,OAAO,CAAC;QAChC,WAAW,EAAE,kBAAkB,CAAA;QAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,GAAG,IAAI,CAAC;IAsNT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAyCG;IACG,SAAS,CACb,IAAI,EAAE,wBAAwB,GAAG;QAC/B,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,OAAO,CAAC,EAAE,CAAC,kBAAkB,GAAG;YAAE,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,GAAG,IAAI,CAAA;KAC1D,GACA,OAAO,CAAC,kBAAkB,CAAC;IAyF9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoCG;IACG,kBAAkB,CACtB,WAAW,EAAE,wBAAwB,EACrC,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAO,GACxD,OAAO,CAAC;QAAE,WAAW,EAAE,kBAAkB,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAgI9D;;;;;OAKG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAa7B;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,IAAI,IAAI,OAAO,CAAC,CAAC,kBAAkB,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI,CAAC;IA0BtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuCG;IACG,OAAO,CACX,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,2BAA2B,CAAA;KAAO,EACnD,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAO,GACxD,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAiD7B;;;;;;;;;;;;;;;;;OAiBG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1C;;;;;;;OAOG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ1D;;;;;;;OAOG;IACH,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED;;;;;;;;;;;;;;;;;OAiBG;IACG,aAAa,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAqBtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACG,aAAa,CACjB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,GACnD,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IA8E7B;;;;;;;;;;;;;OAaG;IACG,OAAO,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAK5C;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACG,YAAY,CAAC,EACjB,SAAS,EACT,MAAM,EACP,EAAE;QACD,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;QAC5B,MAAM,CAAC,EAAE,OAAO,CAAA;KACjB,GAAG,OAAO,CAAC,WAAW,CAAC;IAIxB;;;;;;;;;;;;;;;;;;OAkBG;IACG,cAAc,CAAC,EACnB,OAAO,EACR,EAAE;QACD,OAAO,EAAE,KAAK,CAAC;YAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;YAAC,MAAM,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC,CAAA;KACnE,GAAG,OAAO,CAAC,WAAW,CAAC;IA8CxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACG,IAAI,CAAC,OAAO,EAAE;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;QACjE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;QACvB,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,GAAG,OAAO,CAAC,QAAQ,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAiEzC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,QAAQ;IAkBnE;;;;;;;;;;;;;OAaG;IACG,GAAG,CACP,IAAI,EAAE,YAAY,EAClB,OAAO,GAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAO,GACrC,OAAO,CAAC,SAAS,CAAC;IAqErB;;;;;;;OAOG;IACG,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAM1D;;;;;;;;;;;;;OAaG;IACG,GAAG,CACP,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,YAAY,EAClB,OAAO,GAAE;QACP,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,WAAW,CAAC,EAAE,OAAO,CAAA;KACjB,GACL,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAqB7B;;;;;;;;;OASG;IACG,IAAI,IAAI,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC;IAIrD;;;;;;;;;OASG;IACI,SAAS,IAAI,cAAc,CAAC,uBAAuB,CAAC;IAI3D;;;;;;;OAOG;IACI,SAAS,IAAI,cAAc,CAAC,eAAe,CAAC;IAInD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACG,OAAO,CACX,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,iBAAiB,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAC/D,OAAO,CAAC,WAAW,CAAC;IAsDvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACG,SAAS,CACb,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAC/B,OAAO,CAAC,cAAc,EAAE,GAAG,IAAI,CAAC;IAyCnC;;;;;;OAMG;IACG,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC;IAQ3D;;;;;;;OAOG;IACG,SAAS,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAOjD;;;;;OAKG;IACG,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAQtD;;;;;;OAMG;IACG,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;IAOlC;;;;;;OAMG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAOhC;;;;;OAKG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAOlC;;;;;OAKG;IACG,OAAO,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAOxC;;;;;;;;;;;;;OAaG;IACG,OAAO,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAOlD;;;;;;;OAOG;IACG,KAAK,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;CAM5C"}
|
package/dist/Collection.js
CHANGED
|
@@ -21,7 +21,6 @@ import { NotFoundError, ValidationError, WasServerError, httpStatus } from './er
|
|
|
21
21
|
import { delegateGrantAt } from './internal/grant.js';
|
|
22
22
|
import { send, readData } from './internal/request.js';
|
|
23
23
|
import { codecForDescriptor, collectionCodecHolder, identityCodec } from './internal/codec.js';
|
|
24
|
-
import { collectionBackendFeatures } from './internal/features.js';
|
|
25
24
|
import { collectWalk, signedPageWalk, walkItems, walkPagesOrEmpty } from './internal/pagination.js';
|
|
26
25
|
import { ANNOTATION_MEMBERS, CONFIGURATION_MEMBERS, asCollectionMetadata, carriedForward, collectionWritableFields, isGovernedDescriptor, readCollectionMetadata, storedEncryption, unreadableDescriptionError } from './internal/describe.js';
|
|
27
26
|
import { assertSinglePrecondition, readEtag, writeHeaders } from './internal/conditional.js';
|
|
@@ -84,12 +83,6 @@ export class Collection {
|
|
|
84
83
|
* without discovering a descriptor from stored state.
|
|
85
84
|
*/
|
|
86
85
|
#encryptionOverride;
|
|
87
|
-
/**
|
|
88
|
-
* The shared backend-feature probe for this collection (memoized on a
|
|
89
|
-
* definitive answer), consulted by the conditional-codec write path and
|
|
90
|
-
* shared with child resource handles the way the codec is.
|
|
91
|
-
*/
|
|
92
|
-
#features;
|
|
93
86
|
/**
|
|
94
87
|
* @param options {object}
|
|
95
88
|
* @param options.context {ClientContext} - Shared context (serverUrl, ezcap
|
|
@@ -123,11 +116,6 @@ export class Collection {
|
|
|
123
116
|
override: encryption,
|
|
124
117
|
capability
|
|
125
118
|
});
|
|
126
|
-
this.#features = collectionBackendFeatures(context, {
|
|
127
|
-
spaceId,
|
|
128
|
-
collectionId,
|
|
129
|
-
capability
|
|
130
|
-
});
|
|
131
119
|
}
|
|
132
120
|
/**
|
|
133
121
|
* The Collection container in canonical (trailing-slash) form: the URL whose
|
|
@@ -143,16 +131,16 @@ export class Collection {
|
|
|
143
131
|
}
|
|
144
132
|
/**
|
|
145
133
|
* The signed-request context handed to a codec that drives its own I/O (the
|
|
146
|
-
* EDV codec reading a chunked blob back), bound to this handle's capability
|
|
147
|
-
*
|
|
134
|
+
* EDV codec reading a chunked blob back), bound to this handle's capability.
|
|
135
|
+
*
|
|
136
|
+
* Public so a sync replica can pass it to `DocCipher.decrypt`, which then
|
|
137
|
+
* reassembles a chunked envelope from its chunk resources on this
|
|
138
|
+
* collection. Building it performs no I/O.
|
|
148
139
|
*
|
|
149
140
|
* @returns {CodecRequestContext}
|
|
150
141
|
*/
|
|
151
|
-
|
|
152
|
-
return codecRequestContext(this.#context, {
|
|
153
|
-
features: this.#features,
|
|
154
|
-
capability: this.#capability
|
|
155
|
-
});
|
|
142
|
+
codecContext() {
|
|
143
|
+
return codecRequestContext(this.#context, { capability: this.#capability });
|
|
156
144
|
}
|
|
157
145
|
/**
|
|
158
146
|
* Resolves (once, then caches) the codec for this collection's reads and
|
|
@@ -294,17 +282,7 @@ export class Collection {
|
|
|
294
282
|
* compare-and-swap attempt limit. One validator covers configuration and
|
|
295
283
|
* annotations alike, so a configuration change now legitimately invalidates
|
|
296
284
|
* an in-flight annotation write; rebasing is the normal outcome, not an
|
|
297
|
-
* error.
|
|
298
|
-
* the write stays the unconditional upsert it has always been.
|
|
299
|
-
*
|
|
300
|
-
* Limitation, on a backend without `conditional-writes` only: the body
|
|
301
|
-
* re-sends the stored `encryption` descriptor, and with no validator to pin
|
|
302
|
-
* the `PUT` to, a descriptor rotation landing between the compose read and
|
|
303
|
-
* the write is re-sent as the older roster -- refused as a spurious
|
|
304
|
-
* `invalid-request-body` ("epochs is append-only") by a server enforcing the
|
|
305
|
-
* descriptor invariants, and rolled back by one that does not. There is no
|
|
306
|
-
* client-side remedy without a validator or a partial-update form, neither of
|
|
307
|
-
* which WAS v0.5 offers (WCL-99).
|
|
285
|
+
* error.
|
|
308
286
|
*
|
|
309
287
|
* The absent-object rule is the masked-404 fail-closed policy at write level:
|
|
310
288
|
* a `null` read means "missing OR not visible to you", and composing against
|
|
@@ -322,8 +300,7 @@ export class Collection {
|
|
|
322
300
|
* @param [options.current] {object} an object the caller has already read,
|
|
323
301
|
* used as the first attempt's baseline instead of re-reading it: the write
|
|
324
302
|
* composes against it and pins to its validator, and a rebase re-reads. A
|
|
325
|
-
* baseline carrying no validator
|
|
326
|
-
* which is what a backend without `conditional-writes` offers anyway
|
|
303
|
+
* baseline carrying no validator is refused by the compare-and-swap loop
|
|
327
304
|
* @param [options.allowAbsent] {boolean} compose against nothing (an
|
|
328
305
|
* upsert) instead of throwing `NotFoundError` when the object cannot be
|
|
329
306
|
* read
|
|
@@ -699,7 +676,7 @@ export class Collection {
|
|
|
699
676
|
* no keys for, which is why {@link describe} -- the configuration read -- goes
|
|
700
677
|
* without it.
|
|
701
678
|
*
|
|
702
|
-
*
|
|
679
|
+
* The result also
|
|
703
680
|
* carries the object's current `etag` (its `metaVersion` validator) -- pass it
|
|
704
681
|
* as `setMeta(meta, { ifMatch })` for a lost-update-safe update. One
|
|
705
682
|
* validator covers the whole object: a configuration write and an annotation
|
|
@@ -747,8 +724,8 @@ export class Collection {
|
|
|
747
724
|
* and encrypted collections alike. The key epoch the codec stamps the
|
|
748
725
|
* envelope with travels as a top-level `epoch` member of the same body.
|
|
749
726
|
*
|
|
750
|
-
* Conditional writes (the
|
|
751
|
-
*
|
|
727
|
+
* Conditional writes: pass `ifMatch` (the `etag` from a prior {@link meta}
|
|
728
|
+
* or {@link describe}) for an
|
|
752
729
|
* update-if-unchanged, and a failed precondition throws
|
|
753
730
|
* `PreconditionFailedError` (412). Without one the write is pinned to the
|
|
754
731
|
* version it composed the configuration from and rebases on a lost race.
|
|
@@ -823,11 +800,10 @@ export class Collection {
|
|
|
823
800
|
/**
|
|
824
801
|
* Sets the Collection's annotation-level human-readable `name`, preserving
|
|
825
802
|
* any existing `tags`. Convenience over `setMeta()`. The write is pinned to
|
|
826
|
-
* the `etag` the `meta()` read returned
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
* full-replacement write.
|
|
803
|
+
* the `etag` the `meta()` read returned and rebases on a lost race, so a
|
|
804
|
+
* concurrent write -- an annotation write or a configuration change, which
|
|
805
|
+
* share the one validator -- is re-read and re-applied rather than silently
|
|
806
|
+
* erased by this full-replacement write.
|
|
831
807
|
*
|
|
832
808
|
* On an encrypted collection this is the collection's client-encrypted name
|
|
833
809
|
* surface: the codec seals it into the `custom` envelope, and by convention
|
|
@@ -868,8 +844,8 @@ export class Collection {
|
|
|
868
844
|
return toUrl({ serverUrl: this.#context.serverUrl, path: this.#logPath });
|
|
869
845
|
}
|
|
870
846
|
/**
|
|
871
|
-
* Reads the Collection's governing history log (the
|
|
872
|
-
* `governed-history-logs` feature): the JSON Lines body served at the
|
|
847
|
+
* Reads the Collection's governing history log (the encrypted-collections
|
|
848
|
+
* profile's `governed-history-logs` feature): the JSON Lines body served at the
|
|
873
849
|
* `/meta/log` sub-resource, verbatim, together with its `ETag`. The log is
|
|
874
850
|
* not a Resource of the Collection (absent from listings and the `changes`
|
|
875
851
|
* feed) and not part of the `/meta` object; it is versioned by its own
|
|
@@ -1128,8 +1104,10 @@ export class Collection {
|
|
|
1128
1104
|
* one page: `limit` caps its size (the server clamps its own maximum), and a
|
|
1129
1105
|
* `hasMore` page carries the `cursor` to pass back for the next one.
|
|
1130
1106
|
*
|
|
1131
|
-
*
|
|
1132
|
-
*
|
|
1107
|
+
* Serving the blinded-index query profile is a conformance requirement of a
|
|
1108
|
+
* server that offers encrypted collections, so there is no client-side
|
|
1109
|
+
* probe; a server that does not implement it answers `501`
|
|
1110
|
+
* (`NotImplementedError`).
|
|
1133
1111
|
*
|
|
1134
1112
|
* @param options {object}
|
|
1135
1113
|
* @param [options.equals] {object | object[]} attribute/value pairs to match
|
|
@@ -1149,9 +1127,8 @@ export class Collection {
|
|
|
1149
1127
|
method: 'POST',
|
|
1150
1128
|
capability: this.#capability,
|
|
1151
1129
|
// The profile is bound here the way `changes()` binds its own: same
|
|
1152
|
-
// endpoint, no client-side
|
|
1153
|
-
//
|
|
1154
|
-
// guess made from the backend descriptor.
|
|
1130
|
+
// endpoint, no client-side gate -- a server that does not implement the
|
|
1131
|
+
// profile answers 501.
|
|
1155
1132
|
json: {
|
|
1156
1133
|
profile: 'blinded-index',
|
|
1157
1134
|
...query,
|
|
@@ -1171,7 +1148,7 @@ export class Collection {
|
|
|
1171
1148
|
// Each envelope is an independent JWE open with nothing carried between
|
|
1172
1149
|
// them, so the page decrypts concurrently. `Promise.all` preserves order,
|
|
1173
1150
|
// so `items` still matches the server's ranking.
|
|
1174
|
-
const codecContext = this
|
|
1151
|
+
const codecContext = this.codecContext();
|
|
1175
1152
|
const items = await Promise.all(documents.map(async (envelope) => {
|
|
1176
1153
|
// Restrict-mode ids make the stored document's own id the WAS resource
|
|
1177
1154
|
// id, so it is also the id the codec verifies the envelope's
|
|
@@ -1211,9 +1188,6 @@ export class Collection {
|
|
|
1211
1188
|
collectionId: this.id,
|
|
1212
1189
|
resourceId,
|
|
1213
1190
|
capability: options.capability ?? this.#capability,
|
|
1214
|
-
// Share this collection's memoized feature probe so per-resource handles
|
|
1215
|
-
// do not each repeat the backend-descriptor round-trip.
|
|
1216
|
-
features: this.#features,
|
|
1217
1191
|
// A per-resource encryption override resolves its own codec (honoring the
|
|
1218
1192
|
// override); without one, share this collection's resolved codec so the
|
|
1219
1193
|
// resource handle does not repeat the descriptor-discovery round-trip.
|
|
@@ -1231,8 +1205,7 @@ export class Collection {
|
|
|
1231
1205
|
*
|
|
1232
1206
|
* On an encrypted collection a binary payload above the codec's
|
|
1233
1207
|
* single-document threshold is auto-routed to the chunked-stream path, which
|
|
1234
|
-
*
|
|
1235
|
-
* it, raised before anything is written).
|
|
1208
|
+
* a server that does not serve the chunk endpoints answers with `501`.
|
|
1236
1209
|
*
|
|
1237
1210
|
* @param data {ResourceData}
|
|
1238
1211
|
* @param options {object}
|
|
@@ -1247,7 +1220,6 @@ export class Collection {
|
|
|
1247
1220
|
pathForId: mintedId => resourcePath(this.spaceId, this.id, mintedId),
|
|
1248
1221
|
codec,
|
|
1249
1222
|
data,
|
|
1250
|
-
features: this.#features,
|
|
1251
1223
|
contentType: options.contentType,
|
|
1252
1224
|
capability: this.#capability
|
|
1253
1225
|
});
|
|
@@ -1315,7 +1287,7 @@ export class Collection {
|
|
|
1315
1287
|
/**
|
|
1316
1288
|
* Creates or replaces a resource by id (upsert). Forwards the
|
|
1317
1289
|
* conditional-write options (`ifMatch` / `ifNoneMatch`) to `Resource.put`;
|
|
1318
|
-
* see it for the
|
|
1290
|
+
* see it for the conditional-write semantics. Returns the stored
|
|
1319
1291
|
* resource's new `etag`.
|
|
1320
1292
|
*
|
|
1321
1293
|
* @param resourceId {string}
|
|
@@ -1396,11 +1368,11 @@ export class Collection {
|
|
|
1396
1368
|
* `limit` to its own maximum, so a short page can still be a full server
|
|
1397
1369
|
* page.
|
|
1398
1370
|
*
|
|
1399
|
-
* Requires the
|
|
1400
|
-
* (
|
|
1401
|
-
* collection the documents' `data` / `custom` are the scheme's
|
|
1402
|
-
* envelopes (an EDV encrypted document under the v1 `edv` scheme) --
|
|
1403
|
-
* method does not decrypt them, unlike `get()`.
|
|
1371
|
+
* Requires the server to advertise the `changes-query` feature server-wide
|
|
1372
|
+
* (`client.service().features`); a server without it answers `501`. On an
|
|
1373
|
+
* encrypted collection the documents' `data` / `custom` are the scheme's
|
|
1374
|
+
* opaque envelopes (an EDV encrypted document under the v1 `edv` scheme) --
|
|
1375
|
+
* this method does not decrypt them, unlike `get()`.
|
|
1404
1376
|
*
|
|
1405
1377
|
* Malformed responses fail the call with a `WasServerError` instead of
|
|
1406
1378
|
* passing through as a page: a 2xx response with no JSON body
|
|
@@ -1478,9 +1450,10 @@ export class Collection {
|
|
|
1478
1450
|
* `list()`. Unlike `list()`, a 404 on a later page throws: the collection
|
|
1479
1451
|
* vanished mid-walk, and the pages already read are not a snapshot of
|
|
1480
1452
|
* anything. The server faults `changes()` rejects on (a bodiless 2xx, a
|
|
1481
|
-
* live entry with no `data`, or a `501` from a
|
|
1482
|
-
* `changes-query` feature) fail the walk with the same
|
|
1483
|
-
* does a server that repeats a checkpoint instead of
|
|
1453
|
+
* live entry with no `data`, or a `501` from a server that does not
|
|
1454
|
+
* advertise the `changes-query` feature) fail the walk with the same
|
|
1455
|
+
* `WasServerError`, as does a server that repeats a checkpoint instead of
|
|
1456
|
+
* advancing.
|
|
1484
1457
|
*
|
|
1485
1458
|
* @param [options] {object}
|
|
1486
1459
|
* @param [options.limit] {number} max documents per request (default 1000, the teaching server's maximum); the server reduces it to its own maximum
|
|
@@ -1622,12 +1595,11 @@ export class Collection {
|
|
|
1622
1595
|
* you (404 conflation caveat). A server without backend support surfaces its
|
|
1623
1596
|
* 501 as `NotImplementedError`.
|
|
1624
1597
|
*
|
|
1625
|
-
* The descriptor
|
|
1626
|
-
* affordances
|
|
1627
|
-
* `
|
|
1628
|
-
*
|
|
1629
|
-
*
|
|
1630
|
-
* gated on the client's keys.)
|
|
1598
|
+
* The descriptor names where the collection is stored; it advertises no
|
|
1599
|
+
* optional affordances. Server-wide affordance tokens live in the service
|
|
1600
|
+
* description (`client.service().features`). (Client-side encryption is
|
|
1601
|
+
* neither -- it is a per-collection client concern gated on the client's
|
|
1602
|
+
* keys.)
|
|
1631
1603
|
*
|
|
1632
1604
|
* @returns {Promise<BackendDescriptor | null>}
|
|
1633
1605
|
*/
|