@interop/was-client 0.60.0 → 0.62.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 (81) hide show
  1. package/README.md +124 -48
  2. package/dist/Collection.d.ts +135 -94
  3. package/dist/Collection.d.ts.map +1 -1
  4. package/dist/Collection.js +612 -194
  5. package/dist/Collection.js.map +1 -1
  6. package/dist/Resource.d.ts +1 -1
  7. package/dist/Resource.d.ts.map +1 -1
  8. package/dist/Resource.js +0 -3
  9. package/dist/Resource.js.map +1 -1
  10. package/dist/Space.d.ts +17 -16
  11. package/dist/Space.d.ts.map +1 -1
  12. package/dist/Space.js +41 -24
  13. package/dist/Space.js.map +1 -1
  14. package/dist/WasClient.d.ts +51 -6
  15. package/dist/WasClient.d.ts.map +1 -1
  16. package/dist/WasClient.js +84 -14
  17. package/dist/WasClient.js.map +1 -1
  18. package/dist/codec.d.ts +3 -3
  19. package/dist/edv/EdvCodec.js +1 -1
  20. package/dist/edv/descriptorStore.d.ts +16 -14
  21. package/dist/edv/descriptorStore.d.ts.map +1 -1
  22. package/dist/edv/descriptorStore.js +17 -14
  23. package/dist/edv/descriptorStore.js.map +1 -1
  24. package/dist/edv/index.d.ts +1 -1
  25. package/dist/edv/index.js +1 -1
  26. package/dist/edv/logGovernedDescriptorStore.d.ts +2 -2
  27. package/dist/edv/logGovernedDescriptorStore.d.ts.map +1 -1
  28. package/dist/edv/logGovernedDescriptorStore.js +10 -9
  29. package/dist/edv/logGovernedDescriptorStore.js.map +1 -1
  30. package/dist/edv/recipients.d.ts +1 -1
  31. package/dist/edv/recipients.js +2 -2
  32. package/dist/edv/recipients.js.map +1 -1
  33. package/dist/edv/refresh.d.ts +1 -1
  34. package/dist/edv/refresh.js +1 -1
  35. package/dist/errors.d.ts +14 -1
  36. package/dist/errors.d.ts.map +1 -1
  37. package/dist/errors.js +16 -3
  38. package/dist/errors.js.map +1 -1
  39. package/dist/index.d.ts +2 -2
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +1 -1
  42. package/dist/index.js.map +1 -1
  43. package/dist/internal/codec.d.ts +22 -1
  44. package/dist/internal/codec.d.ts.map +1 -1
  45. package/dist/internal/codec.js +117 -46
  46. package/dist/internal/codec.js.map +1 -1
  47. package/dist/internal/describe.d.ts +98 -24
  48. package/dist/internal/describe.d.ts.map +1 -1
  49. package/dist/internal/describe.js +125 -18
  50. package/dist/internal/describe.js.map +1 -1
  51. package/dist/internal/meta.d.ts +32 -17
  52. package/dist/internal/meta.d.ts.map +1 -1
  53. package/dist/internal/meta.js +64 -35
  54. package/dist/internal/meta.js.map +1 -1
  55. package/dist/internal/paths.d.ts +33 -21
  56. package/dist/internal/paths.d.ts.map +1 -1
  57. package/dist/internal/paths.js +93 -46
  58. package/dist/internal/paths.js.map +1 -1
  59. package/dist/internal/request.d.ts +22 -6
  60. package/dist/internal/request.d.ts.map +1 -1
  61. package/dist/internal/request.js +31 -13
  62. package/dist/internal/request.js.map +1 -1
  63. package/dist/internal/reserved.d.ts +14 -0
  64. package/dist/internal/reserved.d.ts.map +1 -1
  65. package/dist/internal/reserved.js +23 -4
  66. package/dist/internal/reserved.js.map +1 -1
  67. package/dist/internal/service.d.ts +61 -0
  68. package/dist/internal/service.d.ts.map +1 -0
  69. package/dist/internal/service.js +259 -0
  70. package/dist/internal/service.js.map +1 -0
  71. package/dist/paths.d.ts +8 -5
  72. package/dist/paths.d.ts.map +1 -1
  73. package/dist/paths.js +8 -5
  74. package/dist/paths.js.map +1 -1
  75. package/dist/sync/provisioning.d.ts +5 -5
  76. package/dist/sync/provisioning.d.ts.map +1 -1
  77. package/dist/sync/provisioning.js +2 -2
  78. package/dist/sync/provisioning.js.map +1 -1
  79. package/dist/types.d.ts +43 -10
  80. package/dist/types.d.ts.map +1 -1
  81. package/package.json +2 -2
@@ -1,9 +1,7 @@
1
1
  import { prepareBody, parseResource } from './content.js';
2
- import { describeCollection, unreadableDescriptionError } from './describe.js';
2
+ import { asCollectionMetadata, readCollectionMetadata, storedEncryption, unreadableDescriptionError } from './describe.js';
3
3
  import { readIndexSchema } from './indexSchema.js';
4
- import { readMeta } from './meta.js';
5
4
  import { Memo } from './memo.js';
6
- import { collectionMeta } from './paths.js';
7
5
  import { EncryptionError, NotImplementedError } from '../errors.js';
8
6
  /**
9
7
  * A per-handle codec cache. Memoizes the in-flight resolution so concurrent
@@ -138,32 +136,40 @@ export async function resolveCodec(context, { spaceId, collectionId, override, c
138
136
  // descriptor's epoch roster and refuses an override without one
139
137
  // fail-closed, so dropping the descriptor here would break every read and
140
138
  // write.
141
- return buildEncryptingCodec(context, {
139
+ const { codec } = await buildEncryptingCodec(context, {
142
140
  spaceId,
143
141
  collectionId,
144
142
  scheme: override.scheme,
145
143
  keys: override.keys,
146
- capability,
147
144
  encryption: override
148
145
  });
146
+ // The override skipped the Collection Metadata read, so a searchable
147
+ // collection's persisted index schema is fetched here.
148
+ const meta = await loadIndexSchema(context, {
149
+ spaceId,
150
+ collectionId,
151
+ capability,
152
+ codec
153
+ });
154
+ return meta !== undefined ? { codec, meta } : { codec };
149
155
  }
150
156
  // 2. A plaintext-only client (no keystore) never encrypts; no round-trip.
151
157
  if (!context.encryption) {
152
158
  return { codec: identityCodec };
153
159
  }
154
160
  // 3. Otherwise the Collection's declared `encryption` descriptor decides -- but
155
- // only if we could actually read the description. An unreadable description
156
- // (a resource-scoped capability cannot GET the collection description, and
157
- // WAS masks that as a 404) is ambiguous: it is indistinguishable from
158
- // "absent", so an encryption-capable client fails closed rather than
159
- // silently downgrading to plaintext and writing the caller's secret as
160
- // server-visible plaintext into a possibly-encrypted collection.
161
- const description = await describeCollection(context, {
161
+ // only if we could actually read the Collection Metadata object. An
162
+ // unreadable object (a resource-scoped capability cannot GET it, and WAS
163
+ // masks that as a 404) is ambiguous: it is indistinguishable from "absent",
164
+ // so an encryption-capable client fails closed rather than silently
165
+ // downgrading to plaintext and writing the caller's secret as server-visible
166
+ // plaintext into a possibly-encrypted collection.
167
+ const read = await readCollectionMetadata(context, {
162
168
  spaceId,
163
169
  collectionId,
164
170
  capability
165
171
  });
166
- if (description === null) {
172
+ if (read === null) {
167
173
  throw unreadableDescriptionError({
168
174
  operation: `determine whether collection ${spaceId}/${collectionId} is ` +
169
175
  'encrypted',
@@ -174,16 +180,91 @@ export async function resolveCodec(context, { spaceId, collectionId, override, c
174
180
  ErrorClass: EncryptionError
175
181
  });
176
182
  }
177
- if (!description.encryption) {
178
- return { codec: identityCodec };
183
+ const declared = storedEncryption(read.metadata);
184
+ const { codec } = declared
185
+ ? await buildEncryptingCodec(context, {
186
+ spaceId,
187
+ collectionId,
188
+ scheme: declared.scheme,
189
+ encryption: declared
190
+ })
191
+ : { codec: identityCodec };
192
+ // The read that decided policy IS the Collection Metadata object, so it is
193
+ // also the index schema and the `meta()` answer: decode its `custom` with the
194
+ // codec just built and hand the snapshot on, rather than reading the same
195
+ // document again. A `custom` this reader cannot open is not the resolution's
196
+ // business -- only `meta()` and the search paths need it -- so the decode
197
+ // failure leaves the schema empty and the snapshot absent instead of failing
198
+ // every operation on the handle.
199
+ const meta = await snapshotFrom({ read, codec });
200
+ return meta !== undefined ? { codec, meta } : { codec };
201
+ }
202
+ /**
203
+ * Builds the codec a descriptor names, for a write that states the descriptor
204
+ * itself rather than discovering it from stored state: the identity codec when
205
+ * there is no descriptor, and the keystore's codec when there is. Resolves
206
+ * `null` when this client cannot build that codec (no keystore, no keys, or an
207
+ * unhandled scheme) -- a caller that must not guess treats `null` as its own
208
+ * refusal, since nothing here is stored state to fail closed on.
209
+ *
210
+ * @param context {ClientContext}
211
+ * @param options {object}
212
+ * @param options.spaceId {string}
213
+ * @param options.collectionId {string}
214
+ * @param [options.descriptor] {CollectionEncryption} the descriptor the write
215
+ * declares; absent means plaintext
216
+ * @returns {Promise<ResourceCodec | null>}
217
+ */
218
+ export async function codecForDescriptor(context, { spaceId, collectionId, descriptor }) {
219
+ if (descriptor === undefined) {
220
+ return identityCodec;
221
+ }
222
+ if (!context.encryption) {
223
+ return null;
179
224
  }
180
- return buildEncryptingCodec(context, {
225
+ const codec = await context.encryption.codecFor({
181
226
  spaceId,
182
227
  collectionId,
183
- scheme: description.encryption.scheme,
184
- encryption: description.encryption,
185
- capability
228
+ scheme: descriptor.scheme,
229
+ encryption: descriptor
186
230
  });
231
+ return codec ?? null;
232
+ }
233
+ /**
234
+ * Turns the Collection Metadata object a resolution read into the snapshot the
235
+ * resolution carries: `custom` decoded through the codec just built (an opaque
236
+ * envelope on an encrypted collection), and the codec's persisted index schema
237
+ * installed from it along the way.
238
+ *
239
+ * A `custom` the decode refuses -- a reader removed from the collection whose
240
+ * epoch key no longer unwraps it, an envelope bound to another slot, a
241
+ * plaintext object where an envelope belongs -- resolves `undefined` rather
242
+ * than throwing. The schema then stays empty (a search on an undeclared
243
+ * attribute still fails loudly at `find()`), and the operations that do not
244
+ * need `custom` keep working; a `meta()` reads and decodes for itself and is
245
+ * the call that surfaces the refusal.
246
+ *
247
+ * @param options {object}
248
+ * @param options.read {object} the stored object and its validator
249
+ * @param options.codec {ResourceCodec}
250
+ * @returns {Promise<(CollectionMetadata & { etag?: string }) | undefined>}
251
+ */
252
+ async function snapshotFrom({ read, codec }) {
253
+ let custom;
254
+ try {
255
+ // The stated slot drives the encrypting codec's binding check: in the
256
+ // Collection slot it refuses a resource-bound envelope served there.
257
+ custom = await codec.decodeMeta({ custom: read.metadata.custom }, { kind: 'collection' });
258
+ }
259
+ catch {
260
+ return undefined;
261
+ }
262
+ codec.indexing?.applySchema(readIndexSchema(custom));
263
+ return {
264
+ ...asCollectionMetadata(read.metadata),
265
+ custom,
266
+ ...(read.etag !== undefined && { etag: read.etag })
267
+ };
187
268
  }
188
269
  /**
189
270
  * Builds the encrypting codec for a collection known to be encrypted, failing
@@ -196,12 +277,12 @@ export async function resolveCodec(context, { spaceId, collectionId, override, c
196
277
  * @param options.spaceId {string}
197
278
  * @param options.collectionId {string}
198
279
  * @param options.scheme {string}
280
+ * @param [options.encryption] {CollectionEncryption} the descriptor, passed
281
+ * whole to the provider
199
282
  * @param [options.keys] {unknown} override-supplied key material
200
- * @param [options.capability] {IZcap} the handle's bound capability, used for
201
- * the index-schema read
202
283
  * @returns {Promise<CodecResolution>}
203
284
  */
204
- async function buildEncryptingCodec(context, { spaceId, collectionId, scheme, encryption, keys, capability }) {
285
+ async function buildEncryptingCodec(context, { spaceId, collectionId, scheme, encryption, keys }) {
205
286
  const where = `${spaceId}/${collectionId}`;
206
287
  if (!context.encryption) {
207
288
  throw new EncryptionError(`Collection ${where} is encrypted (scheme "${scheme}") but this client ` +
@@ -220,20 +301,14 @@ async function buildEncryptingCodec(context, { spaceId, collectionId, scheme, en
220
301
  'holds no keys for it (or does not handle the scheme). Supply keys via ' +
221
302
  'your keystore (resolveKeys) or a per-handle encryption override.');
222
303
  }
223
- const meta = await loadIndexSchema(context, {
224
- spaceId,
225
- collectionId,
226
- capability,
227
- codec
228
- });
229
- return meta !== undefined ? { codec, meta } : { codec };
304
+ return { codec };
230
305
  }
231
306
  /**
232
- * Loads the collection's persisted index schema onto a freshly-built codec, so
233
- * writes through it emit index tokens for the declared attributes and searches
234
- * can be built for them. A no-op for a codec with no search capability -- which
235
- * is every codec on a collection whose descriptor declares no blinding key, so
236
- * an ordinary encrypted collection pays no round-trip here.
307
+ * Loads the collection's persisted index schema onto a codec built from a
308
+ * per-handle encryption override -- the one path that skipped the Collection
309
+ * Metadata read, so the schema is not already in hand. A no-op for a codec with
310
+ * no search capability, which is every codec on a collection whose descriptor
311
+ * declares no blinding key.
237
312
  *
238
313
  * The schema is discovered rather than declared per app: it is the reason
239
314
  * declarations are persisted at all, so a reader that did not create the
@@ -247,7 +322,7 @@ async function buildEncryptingCodec(context, { spaceId, collectionId, scheme, en
247
322
  * failing the resolution: neither says the collection is broken, and every
248
323
  * search on an undeclared attribute still fails loudly at `find()`.
249
324
  *
250
- * The decoded metadata is returned so the caller that paid for this read can
325
+ * The decoded metadata is returned so the caller that paid for the read can
251
326
  * hand it to a `meta()` that would otherwise repeat it -- it is exactly what
252
327
  * `Collection.meta()` produces, decrypted `custom` and `etag` included.
253
328
  *
@@ -262,17 +337,14 @@ async function buildEncryptingCodec(context, { spaceId, collectionId, scheme, en
262
337
  * no read happened at all
263
338
  */
264
339
  async function loadIndexSchema(context, { spaceId, collectionId, capability, codec }) {
265
- const { indexing } = codec;
266
- if (!indexing) {
340
+ if (!codec.indexing) {
267
341
  return undefined;
268
342
  }
269
- let meta;
343
+ let read;
270
344
  try {
271
- meta = await readMeta(context, {
272
- metaPath: collectionMeta(spaceId, collectionId),
273
- codec: Promise.resolve(codec),
274
- subject: `collection "${collectionId}"`,
275
- slot: { kind: 'collection' },
345
+ read = await readCollectionMetadata(context, {
346
+ spaceId,
347
+ collectionId,
276
348
  capability
277
349
  });
278
350
  }
@@ -282,10 +354,9 @@ async function loadIndexSchema(context, { spaceId, collectionId, capability, cod
282
354
  }
283
355
  throw err;
284
356
  }
285
- if (meta === null) {
357
+ if (read === null) {
286
358
  return null;
287
359
  }
288
- indexing.applySchema(readIndexSchema(meta.custom));
289
- return meta;
360
+ return snapshotFrom({ read, codec });
290
361
  }
291
362
  //# sourceMappingURL=codec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"codec.js","sourceRoot":"","sources":["../../src/internal/codec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"codec.js","sourceRoot":"","sources":["../../src/internal/codec.ts"],"names":[],"mappings":"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"}
@@ -2,19 +2,44 @@
2
2
  * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
3
  */
4
4
  /**
5
- * Reads a Collection Description with a single GET. Shared by
6
- * `Collection.describe()` and the standalone-resource codec's descriptor
7
- * discovery so the request shape (path, capability, null-unwrap) lives in one
8
- * place. Also owns the masked-404 fail-closed policy: WAS returns 404 for both
9
- * not-found and unauthorized, so a `null` description is ambiguous and an
10
- * operation that must know the current state fails closed via
5
+ * Reads a Collection Metadata object with a single GET of the Collection's
6
+ * `meta` sub-resource, in the wire form the server serves it: `custom` is
7
+ * whatever is stored there (an opaque envelope on an encrypted Collection),
8
+ * left undecoded, because the codec that would decode it is itself resolved
9
+ * from this object's `encryption` member. Shared by `Collection`'s read and
10
+ * write paths and by the codec resolver's descriptor discovery, so the request
11
+ * shape (path, capability, null-unwrap, validator) lives in one place.
12
+ *
13
+ * Also owns the masked-404 fail-closed policy: WAS returns 404 for both
14
+ * not-found and unauthorized, so a `null` read is ambiguous and an operation
15
+ * that must know the current state fails closed via
11
16
  * `unreadableDescriptionError` rather than guessing. `collectionWritableFields`
12
- * lives here too: it is the one inclusion rule for the writable description
13
- * fields, shared by every path that builds a description body.
17
+ * lives here too: it is the one inclusion rule for the writable configuration
18
+ * members a caller supplies, beside `carriedForward`, the rule for the stored
19
+ * members a full-replacement write re-sends unchanged.
14
20
  */
15
21
  import type { ClientContext } from './request.js';
16
22
  import type { WasError } from '../errors.js';
17
- import type { CollectionDescription, CollectionEncryption, CollectionWritableFields, IZcap } from '../types.js';
23
+ import type { CollectionEncryption, CollectionMetadata, CollectionWritableFields, IZcap } from '../types.js';
24
+ /**
25
+ * A Collection Metadata object exactly as the server served it. Typed as an
26
+ * open record rather than as `CollectionMetadata`, because `custom` here is the
27
+ * stored value (an opaque encryption envelope on an encrypted Collection) and
28
+ * because a write forwards members this client does not model verbatim rather
29
+ * than dropping them (see {@link carriedForward}).
30
+ */
31
+ export type StoredCollectionMetadata = Record<string, unknown>;
32
+ /**
33
+ * Reads a stored Collection Metadata object as the `CollectionMetadata` wire
34
+ * type. The stored form is typed as an open record -- `custom` is the stored
35
+ * value and a write forwards members this client does not model -- while the
36
+ * server guarantees the object's shape, so the assertion lives here once
37
+ * instead of at each reader.
38
+ *
39
+ * @param stored {StoredCollectionMetadata}
40
+ * @returns {CollectionMetadata}
41
+ */
42
+ export declare function asCollectionMetadata(stored: StoredCollectionMetadata): CollectionMetadata;
18
43
  /**
19
44
  * Whether a served encryption descriptor is the server-derived projection of
20
45
  * a Collection governed by its history log (it carries the `history` pointer)
@@ -31,38 +56,87 @@ export declare function isGovernedDescriptor(descriptor: CollectionEncryption):
31
56
  };
32
57
  };
33
58
  /**
34
- * Picks the writable Collection Description fields that are set. The one
35
- * inclusion rule (`!== undefined`) behind every description body -- the
36
- * `configure` / `replaceDescription` request bodies and their echoed return
37
- * descriptions, and `Space.createCollection`'s create body -- so the paths
38
- * cannot drift and a new writable field is added in one place.
59
+ * Picks the writable Collection configuration members that are set. The one
60
+ * inclusion rule (`!== undefined`) behind every Collection Metadata write body
61
+ * -- the `configure` / `replaceDescription` request bodies and their echoed
62
+ * returns, and `Space.createCollection`'s create body -- so the paths cannot
63
+ * drift and a new writable member is added in one place.
39
64
  *
40
65
  * @param fields {CollectionWritableFields}
41
66
  * @returns {CollectionWritableFields}
42
67
  */
43
68
  export declare function collectionWritableFields(fields: CollectionWritableFields): CollectionWritableFields;
44
69
  /**
45
- * Reads the Collection Description. Returns `null` if the collection is missing
46
- * or not visible to you (WAS returns 404 for both not-found and unauthorized).
70
+ * The Collection's configuration members: what a configuration write
71
+ * (`configure`, `replaceDescription`) states in full and an annotation write
72
+ * carries forward.
73
+ */
74
+ export declare const CONFIGURATION_MEMBERS: readonly string[];
75
+ /**
76
+ * The Collection's annotation members: the user-writable `custom` and the key
77
+ * epoch its envelope was sealed under. What an annotation write (`setMeta` and
78
+ * its sugar) states and a configuration write carries forward.
79
+ */
80
+ export declare const ANNOTATION_MEMBERS: readonly string[];
81
+ /**
82
+ * Reads a stored `encryption` descriptor at its wire type. The one place the
83
+ * open stored record is narrowed to `CollectionEncryption`, shared by the
84
+ * compose paths and the codec resolver so the assertion is not repeated.
85
+ *
86
+ * @param stored {StoredCollectionMetadata}
87
+ * @returns {CollectionEncryption | undefined}
88
+ */
89
+ export declare function storedEncryption(stored: StoredCollectionMetadata): CollectionEncryption | undefined;
90
+ /**
91
+ * Picks the stored members a full-replacement write carries forward: every
92
+ * member except the ones the server manages and the ones this write states
93
+ * itself. A denylist rather than an allowlist, so a member this client does
94
+ * not model -- `plaintext`, or one a newer server serves -- survives a write
95
+ * it is not about instead of being silently cleared.
96
+ *
97
+ * A governed `encryption` descriptor is deliberately dropped: on a
98
+ * log-governed Collection the served descriptor is the server's projection of
99
+ * the history log's head, and a write that carries it is refused
100
+ * (`encryption-history-log-governed`). Omitting the member leaves the derived
101
+ * descriptor in place.
102
+ *
103
+ * @param stored {StoredCollectionMetadata}
104
+ * @param options {object}
105
+ * @param options.replacing {readonly string[]} the members this write states
106
+ * itself (`CONFIGURATION_MEMBERS` on a configuration write,
107
+ * `ANNOTATION_MEMBERS` on an annotation write)
108
+ * @returns {StoredCollectionMetadata}
109
+ */
110
+ export declare function carriedForward(stored: StoredCollectionMetadata, { replacing }: {
111
+ replacing: readonly string[];
112
+ }): StoredCollectionMetadata;
113
+ /**
114
+ * Reads the Collection Metadata object with its `metaVersion` validator, in the
115
+ * stored wire form. Returns `null` if the collection is missing or not visible
116
+ * to you (WAS returns 404 for both not-found and unauthorized); `etag` is
117
+ * absent against a backend that does not version the object.
47
118
  *
48
119
  * @param context {ClientContext}
49
120
  * @param options {object}
50
121
  * @param options.spaceId {string}
51
122
  * @param options.collectionId {string}
52
123
  * @param [options.capability] {IZcap} capability attached to the request
53
- * @returns {Promise<CollectionDescription | null>}
124
+ * @returns {Promise<{ metadata: StoredCollectionMetadata; etag?: string } | null>}
54
125
  */
55
- export declare function describeCollection(context: ClientContext, options: {
126
+ export declare function readCollectionMetadata(context: ClientContext, options: {
56
127
  spaceId: string;
57
128
  collectionId: string;
58
129
  capability?: IZcap;
59
- }): Promise<CollectionDescription | null>;
130
+ }): Promise<{
131
+ metadata: StoredCollectionMetadata;
132
+ etag?: string;
133
+ } | null>;
60
134
  /**
61
- * Builds the fail-closed error for an operation that needs a description it
62
- * could not read. The one owner of the masked-404 policy statement ("WAS
63
- * returns 404 for both not-found and unauthorized, so a null description is
64
- * ambiguous -- fail closed"); callers supply the operation, its consequence,
65
- * and the recovery advice.
135
+ * Builds the fail-closed error for an operation that needs a Collection
136
+ * Metadata object it could not read. The one owner of the masked-404 policy
137
+ * statement ("WAS returns 404 for both not-found and unauthorized, so a null
138
+ * read is ambiguous -- fail closed"); callers supply the operation, its
139
+ * consequence, and the recovery advice.
66
140
  *
67
141
  * @param options {object}
68
142
  * @param options.operation {string} what was refused, continuing "Cannot ..."
@@ -1 +1 @@
1
- {"version":3,"file":"describe.d.ts","sourceRoot":"","sources":["../../src/internal/describe.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAIjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EACV,qBAAqB,EACrB,oBAAoB,EACpB,wBAAwB,EACxB,KAAK,EACN,MAAM,aAAa,CAAA;AAEpB;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,oBAAoB,GAC/B,UAAU,IAAI,oBAAoB,GAAG;IACtC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAC9C,CAEA;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,wBAAwB,CAU1B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,KAAK,CAAA;CAAE,GACrE,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAKvC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CAAC,EACzC,SAAS,EACT,WAAW,EACX,MAAM,EACN,UAA4B,EAC7B,EAAE;IACD,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,KAAK,OAAO,EAAE,MAAM,KAAK,QAAQ,CAAA;CAC/C,GAAG,QAAQ,CAQX"}
1
+ {"version":3,"file":"describe.d.ts","sourceRoot":"","sources":["../../src/internal/describe.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAIjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EACV,oBAAoB,EACpB,kBAAkB,EAClB,wBAAwB,EACxB,KAAK,EACN,MAAM,aAAa,CAAA;AAEpB;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAE9D;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,wBAAwB,GAC/B,kBAAkB,CAEpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,oBAAoB,GAC/B,UAAU,IAAI,oBAAoB,GAAG;IACtC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAC9C,CAEA;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,wBAAwB,CAU1B;AAsBD;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAMlD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAAwB,CAAA;AAExE;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,wBAAwB,GAC/B,oBAAoB,GAAG,SAAS,CAElC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,wBAAwB,EAChC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC9C,wBAAwB,CAgB1B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,KAAK,CAAA;CAAE,GACrE,OAAO,CAAC;IAAE,QAAQ,EAAE,wBAAwB,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAYvE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CAAC,EACzC,SAAS,EACT,WAAW,EACX,MAAM,EACN,UAA4B,EAC7B,EAAE;IACD,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,KAAK,OAAO,EAAE,MAAM,KAAK,QAAQ,CAAA;CAC/C,GAAG,QAAQ,CAQX"}
@@ -1,6 +1,19 @@
1
- import { readData } from './request.js';
2
- import { collectionPath } from './paths.js';
1
+ import { readDataWithEtag } from './request.js';
2
+ import { collectionMeta } from './paths.js';
3
3
  import { ValidationError } from '../errors.js';
4
+ /**
5
+ * Reads a stored Collection Metadata object as the `CollectionMetadata` wire
6
+ * type. The stored form is typed as an open record -- `custom` is the stored
7
+ * value and a write forwards members this client does not model -- while the
8
+ * server guarantees the object's shape, so the assertion lives here once
9
+ * instead of at each reader.
10
+ *
11
+ * @param stored {StoredCollectionMetadata}
12
+ * @returns {CollectionMetadata}
13
+ */
14
+ export function asCollectionMetadata(stored) {
15
+ return stored;
16
+ }
4
17
  /**
5
18
  * Whether a served encryption descriptor is the server-derived projection of
6
19
  * a Collection governed by its history log (it carries the `history` pointer)
@@ -14,11 +27,11 @@ export function isGovernedDescriptor(descriptor) {
14
27
  return descriptor.history !== undefined;
15
28
  }
16
29
  /**
17
- * Picks the writable Collection Description fields that are set. The one
18
- * inclusion rule (`!== undefined`) behind every description body -- the
19
- * `configure` / `replaceDescription` request bodies and their echoed return
20
- * descriptions, and `Space.createCollection`'s create body -- so the paths
21
- * cannot drift and a new writable field is added in one place.
30
+ * Picks the writable Collection configuration members that are set. The one
31
+ * inclusion rule (`!== undefined`) behind every Collection Metadata write body
32
+ * -- the `configure` / `replaceDescription` request bodies and their echoed
33
+ * returns, and `Space.createCollection`'s create body -- so the paths cannot
34
+ * drift and a new writable member is added in one place.
22
35
  *
23
36
  * @param fields {CollectionWritableFields}
24
37
  * @returns {CollectionWritableFields}
@@ -35,28 +48,122 @@ export function collectionWritableFields(fields) {
35
48
  };
36
49
  }
37
50
  /**
38
- * Reads the Collection Description. Returns `null` if the collection is missing
39
- * or not visible to you (WAS returns 404 for both not-found and unauthorized).
51
+ * The members of a Collection Metadata object the server owns: it ignores them
52
+ * in a request body and re-derives them on every write, so a write never
53
+ * echoes them back. `id` is stated by the write path itself, `type` is the
54
+ * server's classification, and the rest are server-observed (the spec's
55
+ * "server-managed members are read-only" rule).
56
+ */
57
+ const SERVER_MANAGED_MEMBERS = [
58
+ 'id',
59
+ 'type',
60
+ // Not a stored member at all: the client-side projection of the validator
61
+ // that `describe()` returns beside the object, which must not travel back.
62
+ 'etag',
63
+ 'url',
64
+ 'linkset',
65
+ 'createdAt',
66
+ 'updatedAt',
67
+ 'createdBy'
68
+ ];
69
+ /**
70
+ * The Collection's configuration members: what a configuration write
71
+ * (`configure`, `replaceDescription`) states in full and an annotation write
72
+ * carries forward.
73
+ */
74
+ export const CONFIGURATION_MEMBERS = [
75
+ 'name',
76
+ 'backend',
77
+ 'encryption',
78
+ 'generator',
79
+ 'generatorOrigin'
80
+ ];
81
+ /**
82
+ * The Collection's annotation members: the user-writable `custom` and the key
83
+ * epoch its envelope was sealed under. What an annotation write (`setMeta` and
84
+ * its sugar) states and a configuration write carries forward.
85
+ */
86
+ export const ANNOTATION_MEMBERS = ['custom', 'epoch'];
87
+ /**
88
+ * Reads a stored `encryption` descriptor at its wire type. The one place the
89
+ * open stored record is narrowed to `CollectionEncryption`, shared by the
90
+ * compose paths and the codec resolver so the assertion is not repeated.
91
+ *
92
+ * @param stored {StoredCollectionMetadata}
93
+ * @returns {CollectionEncryption | undefined}
94
+ */
95
+ export function storedEncryption(stored) {
96
+ return stored.encryption;
97
+ }
98
+ /**
99
+ * Picks the stored members a full-replacement write carries forward: every
100
+ * member except the ones the server manages and the ones this write states
101
+ * itself. A denylist rather than an allowlist, so a member this client does
102
+ * not model -- `plaintext`, or one a newer server serves -- survives a write
103
+ * it is not about instead of being silently cleared.
104
+ *
105
+ * A governed `encryption` descriptor is deliberately dropped: on a
106
+ * log-governed Collection the served descriptor is the server's projection of
107
+ * the history log's head, and a write that carries it is refused
108
+ * (`encryption-history-log-governed`). Omitting the member leaves the derived
109
+ * descriptor in place.
110
+ *
111
+ * @param stored {StoredCollectionMetadata}
112
+ * @param options {object}
113
+ * @param options.replacing {readonly string[]} the members this write states
114
+ * itself (`CONFIGURATION_MEMBERS` on a configuration write,
115
+ * `ANNOTATION_MEMBERS` on an annotation write)
116
+ * @returns {StoredCollectionMetadata}
117
+ */
118
+ export function carriedForward(stored, { replacing }) {
119
+ const carried = {};
120
+ for (const [member, value] of Object.entries(stored)) {
121
+ if (value === undefined) {
122
+ continue;
123
+ }
124
+ if (SERVER_MANAGED_MEMBERS.includes(member) || replacing.includes(member)) {
125
+ continue;
126
+ }
127
+ carried[member] = value;
128
+ }
129
+ const encryption = storedEncryption(stored);
130
+ if (encryption !== undefined && isGovernedDescriptor(encryption)) {
131
+ delete carried.encryption;
132
+ }
133
+ return carried;
134
+ }
135
+ /**
136
+ * Reads the Collection Metadata object with its `metaVersion` validator, in the
137
+ * stored wire form. Returns `null` if the collection is missing or not visible
138
+ * to you (WAS returns 404 for both not-found and unauthorized); `etag` is
139
+ * absent against a backend that does not version the object.
40
140
  *
41
141
  * @param context {ClientContext}
42
142
  * @param options {object}
43
143
  * @param options.spaceId {string}
44
144
  * @param options.collectionId {string}
45
145
  * @param [options.capability] {IZcap} capability attached to the request
46
- * @returns {Promise<CollectionDescription | null>}
146
+ * @returns {Promise<{ metadata: StoredCollectionMetadata; etag?: string } | null>}
47
147
  */
48
- export async function describeCollection(context, options) {
49
- return readData(context, {
50
- path: collectionPath(options.spaceId, options.collectionId),
148
+ export async function readCollectionMetadata(context, options) {
149
+ const read = await readDataWithEtag(context, {
150
+ path: collectionMeta(options.spaceId, options.collectionId),
51
151
  capability: options.capability
52
152
  });
153
+ if (read === null) {
154
+ return null;
155
+ }
156
+ return {
157
+ metadata: read.data,
158
+ ...(read.etag !== undefined && { etag: read.etag })
159
+ };
53
160
  }
54
161
  /**
55
- * Builds the fail-closed error for an operation that needs a description it
56
- * could not read. The one owner of the masked-404 policy statement ("WAS
57
- * returns 404 for both not-found and unauthorized, so a null description is
58
- * ambiguous -- fail closed"); callers supply the operation, its consequence,
59
- * and the recovery advice.
162
+ * Builds the fail-closed error for an operation that needs a Collection
163
+ * Metadata object it could not read. The one owner of the masked-404 policy
164
+ * statement ("WAS returns 404 for both not-found and unauthorized, so a null
165
+ * read is ambiguous -- fail closed"); callers supply the operation, its
166
+ * consequence, and the recovery advice.
60
167
  *
61
168
  * @param options {object}
62
169
  * @param options.operation {string} what was refused, continuing "Cannot ..."
@@ -1 +1 @@
1
- {"version":3,"file":"describe.js","sourceRoot":"","sources":["../../src/internal/describe.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAS9C;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAClC,UAAgC;IAIhC,OAAO,UAAU,CAAC,OAAO,KAAK,SAAS,CAAA;AACzC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAgC;IAEhC,OAAO;QACL,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;QACvD,GAAG,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;QAChE,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;QACzE,GAAG,CAAC,MAAM,CAAC,SAAS,KAAK,SAAS,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;QACtE,GAAG,CAAC,MAAM,CAAC,eAAe,KAAK,SAAS,IAAI;YAC1C,eAAe,EAAE,MAAM,CAAC,eAAe;SACxC,CAAC;KACH,CAAA;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAsB,EACtB,OAAsE;IAEtE,OAAO,QAAQ,CAAwB,OAAO,EAAE;QAC9C,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC;QAC3D,UAAU,EAAE,OAAO,CAAC,UAAU;KAC/B,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,0BAA0B,CAAC,EACzC,SAAS,EACT,WAAW,EACX,MAAM,EACN,UAAU,GAAG,eAAe,EAM7B;IACC,OAAO,IAAI,UAAU,CACnB,UAAU,SAAS,sDAAsD;QACvE,kEAAkE;QAClE,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxD,GAAG;QACH,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAC7C,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"describe.js","sourceRoot":"","sources":["../../src/internal/describe.ts"],"names":[],"mappings":"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"}