@interop/was-client 0.1.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 (51) hide show
  1. package/LICENSE.md +20 -0
  2. package/README.md +204 -0
  3. package/dist/Collection.d.ts +112 -0
  4. package/dist/Collection.d.ts.map +1 -0
  5. package/dist/Collection.js +209 -0
  6. package/dist/Collection.js.map +1 -0
  7. package/dist/Resource.d.ts +67 -0
  8. package/dist/Resource.d.ts.map +1 -0
  9. package/dist/Resource.js +124 -0
  10. package/dist/Resource.js.map +1 -0
  11. package/dist/Space.d.ts +99 -0
  12. package/dist/Space.d.ts.map +1 -0
  13. package/dist/Space.js +202 -0
  14. package/dist/Space.js.map +1 -0
  15. package/dist/WasClient.d.ts +115 -0
  16. package/dist/WasClient.d.ts.map +1 -0
  17. package/dist/WasClient.js +191 -0
  18. package/dist/WasClient.js.map +1 -0
  19. package/dist/errors.d.ts +72 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +112 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/index.d.ts +14 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +13 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/internal/content.d.ts +43 -0
  28. package/dist/internal/content.d.ts.map +1 -0
  29. package/dist/internal/content.js +66 -0
  30. package/dist/internal/content.js.map +1 -0
  31. package/dist/internal/grant.d.ts +21 -0
  32. package/dist/internal/grant.d.ts.map +1 -0
  33. package/dist/internal/grant.js +19 -0
  34. package/dist/internal/grant.js.map +1 -0
  35. package/dist/internal/paths.d.ts +70 -0
  36. package/dist/internal/paths.d.ts.map +1 -0
  37. package/dist/internal/paths.js +92 -0
  38. package/dist/internal/paths.js.map +1 -0
  39. package/dist/internal/request.d.ts +64 -0
  40. package/dist/internal/request.d.ts.map +1 -0
  41. package/dist/internal/request.js +60 -0
  42. package/dist/internal/request.js.map +1 -0
  43. package/dist/internal/reserved.d.ts +16 -0
  44. package/dist/internal/reserved.d.ts.map +1 -0
  45. package/dist/internal/reserved.js +40 -0
  46. package/dist/internal/reserved.js.map +1 -0
  47. package/dist/types.d.ts +165 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +2 -0
  50. package/dist/types.js.map +1 -0
  51. package/package.json +84 -0
package/LICENSE.md ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Interop Alliance
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,204 @@
1
+ # Wallet Attached Storage Client _(@interop/was-client)_
2
+
3
+ [![NPM Version](https://img.shields.io/npm/v/@interop/was-client.svg)](https://npm.im/@interop/was-client)
4
+
5
+ > A developer-friendly client for Wallet Attached Storage (WAS) servers, with a
6
+ > MongoDB-driver-inspired navigational API over zcap-authorized HTTP.
7
+
8
+ ## Table of Contents
9
+
10
+ - [Background](#background)
11
+ - [Install](#install)
12
+ - [Usage](#usage)
13
+ - [Construction](#construction)
14
+ - [The handle model](#the-handle-model)
15
+ - [Resources: JSON and binary](#resources-json-and-binary)
16
+ - [Delegation and sharing](#delegation-and-sharing)
17
+ - [Export and import](#export-and-import)
18
+ - [The manual-request escape hatch](#the-manual-request-escape-hatch)
19
+ - [Errors and the 404/null caveat](#errors-and-the-404null-caveat)
20
+ - [Contribute](#contribute)
21
+ - [License](#license)
22
+
23
+ ## Background
24
+
25
+ The WAS protocol exposes a containment model --
26
+ `SpacesRepository > Space > Collection > Resource` -- over HTTP, authorized with
27
+ [Authorization Capabilities (zcaps)](https://w3c-ccg.github.io/zcap-spec/). The
28
+ low-level transport is an
29
+ [`@interop/ezcap`](https://www.npmjs.com/package/@interop/ezcap) `ZcapClient`,
30
+ where every operation hand-builds a URL, picks a trailing-slash variant, threads
31
+ JSON vs binary bodies, and reasons about delegation inline.
32
+
33
+ `@interop/was-client` wraps that `ZcapClient` and exposes the containment model
34
+ through cheap, lazy navigational handles modeled on the MongoDB driver's DX
35
+ (`client > db > collection`), using WAS-specific verbs
36
+ (`add`/`get`/`put`/`list`/`delete`) rather than `insertOne`/`findOne` (WAS has
37
+ no query-by-filter yet).
38
+
39
+ | MongoDB driver | WAS client |
40
+ | ----------------------------------- | ------------------------------------------ |
41
+ | `new MongoClient(url)` | `new WasClient({ serverUrl, zcapClient })` |
42
+ | `client.db('app')` | `was.space(spaceId)` |
43
+ | `db.collection('users')` | `space.collection(collectionId)` |
44
+ | `collection.insertOne(doc)` | `collection.add(doc)` |
45
+ | `collection.findOne({ _id })` | `collection.get(resourceId)` |
46
+ | `collection.replaceOne({ _id }, d)` | `collection.put(resourceId, data)` |
47
+ | `collection.find().toArray()` | `collection.list()` |
48
+ | `collection.deleteOne({ _id })` | `collection.resource(resourceId).delete()` |
49
+
50
+ ## Install
51
+
52
+ - Node.js 24+ is recommended.
53
+
54
+ ```
55
+ pnpm install @interop/was-client
56
+ ```
57
+
58
+ ## Usage
59
+
60
+ ### Construction
61
+
62
+ ```ts
63
+ import { WasClient } from '@interop/was-client'
64
+
65
+ // Primary form: wrap an existing ezcap ZcapClient (which holds the signer).
66
+ const was = new WasClient({ serverUrl, zcapClient })
67
+
68
+ // Convenience: build the ZcapClient internally from a signer
69
+ // (uses the Ed25519Signature2020 suite).
70
+ const was = WasClient.fromSigner({ serverUrl, signer })
71
+ ```
72
+
73
+ `serverUrl` is the base for both URL building and zcap `invocationTarget`s, so
74
+ the "server URL must equal the invocation target host:port" constraint holds by
75
+ construction.
76
+
77
+ ### The handle model
78
+
79
+ Handles are lazy and synchronous to obtain -- only the verb methods hit the
80
+ network. Lazy chains never throw: `was.space(x).collection(y)` does no I/O and
81
+ just accumulates URL context. Existence is checked on the first network verb.
82
+
83
+ ```ts
84
+ const space = await was.createSpace({ name: 'Home' }) // POST /spaces/
85
+
86
+ const collection = await space.createCollection({
87
+ id: 'credentials',
88
+ name: 'Verifiable Credentials'
89
+ })
90
+
91
+ await collection.put('vc-1', {
92
+ type: ['VerifiableCredential'],
93
+ name: 'Diploma'
94
+ })
95
+ const vc = await collection.get('vc-1') // parsed JSON object, or null on a miss
96
+
97
+ const listing = await collection.list() // { id, url, totalItems, items, ... }
98
+
99
+ await collection.resource('vc-1').delete() // delete one resource by id
100
+ await space.delete() // delete the whole space (idempotent)
101
+ ```
102
+
103
+ `delete()` is uniform at every level, takes no argument, and always deletes the
104
+ thing the handle points at -- so there is no "delete the collection" vs "delete
105
+ one item" footgun.
106
+
107
+ ### Resources: JSON and binary
108
+
109
+ Writes detect the payload: a plain object/array is sent as JSON; a
110
+ `Blob`/`Uint8Array`/`Buffer` is sent as binary, with the content-type taken from
111
+ `options.contentType`, the `Blob.type`, or `application/octet-stream`.
112
+
113
+ ```ts
114
+ // JSON
115
+ await collection.put('doc', { hello: 'world' })
116
+
117
+ // Binary
118
+ const bytes = new TextEncoder().encode('plain text body')
119
+ await collection.put('note.txt', bytes, { contentType: 'text/plain' })
120
+
121
+ const resource = collection.resource('note.txt')
122
+ await resource.get() // a Blob (whose .type carries the content-type)
123
+ await resource.getText() // 'plain text body'
124
+ await resource.getBytes() // Uint8Array
125
+ ```
126
+
127
+ Reads auto-parse: `get()` returns a parsed object for a JSON content-type and a
128
+ `Blob` otherwise; `getText()` / `getBytes()` are explicit escape hatches.
129
+
130
+ ### Delegation and sharing
131
+
132
+ `was.grant(...)` is the general delegation primitive; `space.grant(...)` and
133
+ `collection.grant(...)` are sugar that prefill the grant `target` with the
134
+ handle's URL. The recipient rebuilds access from the received zcap with
135
+ `fromCapability()`.
136
+
137
+ ```ts
138
+ // Alice grants Bob read access to a resource.
139
+ const added = await collection.add({ secret: 'value' })
140
+ const zcap = await was.grant({
141
+ to: bobDid,
142
+ actions: ['GET'], // HTTP verbs: 'GET' | 'PUT' | 'POST' | 'DELETE'
143
+ target: added.url
144
+ })
145
+
146
+ // Bob, holding the zcap, rebuilds a handle at the right depth.
147
+ const handle = bobWas.fromCapability(zcap) // a Resource here
148
+ await handle.get() // succeeds; a write would be denied by the GET-only grant
149
+ ```
150
+
151
+ Actions are HTTP verbs (`GET` / `PUT` / `POST` / `DELETE`). The WAS server
152
+ authorizes on these case-sensitively (uppercase), but `grant()` also accepts the
153
+ lowercase forms and normalizes them to uppercase in the signed zcap -- so
154
+ `actions: ['get']` still validates server-side.
155
+
156
+ ### Export and import
157
+
158
+ ```ts
159
+ const archive = await space.export() // Uint8Array (application/x-tar)
160
+ const stats = await otherSpace.import(archive)
161
+ // { collectionsCreated, collectionsSkipped, resourcesCreated, resourcesSkipped }
162
+ ```
163
+
164
+ ### The manual-request escape hatch
165
+
166
+ `was.request(...)` mirrors ezcap's generic `request()` for hand-built calls. As
167
+ a deliberate escape hatch it returns the raw `HttpResponse` and throws raw
168
+ ezcap/ky errors -- it does not apply the null-on-404 or typed-error
169
+ conveniences.
170
+
171
+ ```ts
172
+ const response = await was.request({ path: `/space/${spaceId}`, method: 'GET' })
173
+ ```
174
+
175
+ ## Errors and the 404/null caveat
176
+
177
+ Read methods (`describe`/`get`/`list`) return `null` on a 404, following
178
+ MongoDB's `findOne` semantics. **WAS returns 404 for both not-found and
179
+ unauthorized**, so `null` means "not visible to you" rather than strictly "does
180
+ not exist". Write/delete methods throw a typed error instead.
181
+
182
+ | Status | Read methods | Write / delete methods |
183
+ | ------ | --------------------- | ---------------------- |
184
+ | 404 | `null` | `NotFoundError` |
185
+ | 400 | `ValidationError` | `ValidationError` |
186
+ | 401 | `AuthRequiredError` | `AuthRequiredError` |
187
+ | 501 | `NotImplementedError` | `NotImplementedError` |
188
+ | 5xx | `WasServerError` | `WasServerError` |
189
+
190
+ All error classes extend `WasError` (carrying `status`, `title`, `details`, and
191
+ `requestUrl`). `delete()` additionally treats a 404 as success, so it is
192
+ idempotent.
193
+
194
+ Some spec endpoints (`listSpaces()`, `policy`/`meta`, `query`, ...) are not yet
195
+ implemented by the reference server and currently surface `NotImplementedError`.
196
+
197
+ ## Contribute
198
+
199
+ PRs accepted. See [CONTRIBUTING.md](CONTRIBUTING.md) for editor setup (Prettier,
200
+ ESLint, and EditorConfig) and how it maps to CI.
201
+
202
+ ## License
203
+
204
+ [MIT License](LICENSE.md) © 2026 Interop Alliance.
@@ -0,0 +1,112 @@
1
+ import type { ClientContext } from './internal/request.js';
2
+ import { Resource } from './Resource.js';
3
+ import type { AddResult, BackendReference, CollectionDescription, GrantOptions, HandleOptions, IDelegatedZcap, IZcap, Json, ResourceListing } from './types.js';
4
+ export declare class Collection {
5
+ readonly spaceId: string;
6
+ readonly id: string;
7
+ private readonly _context;
8
+ private readonly _capability?;
9
+ /**
10
+ * @param options {object}
11
+ * @param options.context {ClientContext} - Shared context (serverUrl, ezcap
12
+ * client, controllerDid)
13
+ * @param options.spaceId {string}
14
+ * @param options.collectionId {string}
15
+ * @param [options.capability] {IZcap} - capability attached to every request
16
+ */
17
+ constructor({ context, spaceId, collectionId, capability }: {
18
+ context: ClientContext;
19
+ spaceId: string;
20
+ collectionId: string;
21
+ capability?: IZcap;
22
+ });
23
+ private get _path();
24
+ private get _itemsPath();
25
+ /**
26
+ * Reads the Collection Description. Returns `null` if the collection is
27
+ * missing or not visible to you (WAS returns 404 for both not-found and
28
+ * unauthorized).
29
+ *
30
+ * @returns {Promise<CollectionDescription | null>}
31
+ */
32
+ describe(): Promise<CollectionDescription | null>;
33
+ /**
34
+ * Creates or updates the collection by id (upsert). Merges the given fields
35
+ * over the current description.
36
+ *
37
+ * @param desc {object}
38
+ * @param [desc.name] {string}
39
+ * @param [desc.backend] {BackendReference}
40
+ * @returns {Promise<CollectionDescription>}
41
+ */
42
+ configure(desc: {
43
+ name?: string;
44
+ backend?: BackendReference;
45
+ }): Promise<CollectionDescription>;
46
+ /**
47
+ * Deletes the whole collection. Idempotent. To delete a single resource, use
48
+ * `collection.resource(id).delete()`.
49
+ *
50
+ * @returns {Promise<void>}
51
+ */
52
+ delete(): Promise<void>;
53
+ /**
54
+ * Returns a lazy handle to a resource by id. No I/O.
55
+ *
56
+ * @param resourceId {string}
57
+ * @param options {object}
58
+ * @param [options.capability] {IZcap}
59
+ * @returns {Resource}
60
+ */
61
+ resource(resourceId: string, options?: HandleOptions): Resource;
62
+ /**
63
+ * Adds a resource with a server-generated id. JSON for plain objects/arrays,
64
+ * binary for `Blob`/`Uint8Array`. Throws `NotFoundError` if the collection
65
+ * does not exist (WAS does not auto-create parents).
66
+ *
67
+ * @param data {Json | Blob | Uint8Array}
68
+ * @param options {object}
69
+ * @param [options.contentType] {string} content-type for binary data
70
+ * @returns {Promise<AddResult>}
71
+ */
72
+ add(data: Json | Blob | Uint8Array, options?: {
73
+ contentType?: string;
74
+ }): Promise<AddResult>;
75
+ /**
76
+ * Reads a resource by id, auto-parsing JSON to an object and returning binary
77
+ * as a `Blob`. Returns `null` on a missing/unauthorized resource (404
78
+ * conflation caveat).
79
+ *
80
+ * @param resourceId {string}
81
+ * @returns {Promise<Json | Blob | null>}
82
+ */
83
+ get(resourceId: string): Promise<Json | Blob | null>;
84
+ /**
85
+ * Creates or replaces a resource by id (upsert).
86
+ *
87
+ * @param resourceId {string}
88
+ * @param data {Json | Blob | Uint8Array}
89
+ * @param options {object}
90
+ * @param [options.contentType] {string} content-type for binary data
91
+ * @returns {Promise<void>}
92
+ */
93
+ put(resourceId: string, data: Json | Blob | Uint8Array, options?: {
94
+ contentType?: string;
95
+ }): Promise<void>;
96
+ /**
97
+ * Lists the items in the collection. Returns `null` if the collection is
98
+ * missing or not visible to you (404 conflation caveat).
99
+ *
100
+ * @returns {Promise<ResourceListing | null>}
101
+ */
102
+ list(): Promise<ResourceListing | null>;
103
+ /**
104
+ * Delegates access to this collection. Prefills the grant `target` with this
105
+ * collection's URL (and the bound `capability`, if any, for re-delegation).
106
+ *
107
+ * @param options {GrantOptions}
108
+ * @returns {Promise<IDelegatedZcap>}
109
+ */
110
+ grant(options: GrantOptions): Promise<IDelegatedZcap>;
111
+ }
112
+ //# sourceMappingURL=Collection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Collection.d.ts","sourceRoot":"","sources":["../src/Collection.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAE1D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAChB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,KAAK,EACL,IAAI,EACJ,eAAe,EAChB,MAAM,YAAY,CAAA;AAEnB,qBAAa,UAAU;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IAEnB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAO;IAEpC;;;;;;;OAOG;gBACS,EACV,OAAO,EACP,OAAO,EACP,YAAY,EACZ,UAAU,EACX,EAAE;QACD,OAAO,EAAE,aAAa,CAAA;QACtB,OAAO,EAAE,MAAM,CAAA;QACf,YAAY,EAAE,MAAM,CAAA;QACpB,UAAU,CAAC,EAAE,KAAK,CAAA;KACnB;IAOD,OAAO,KAAK,KAAK,GAEhB;IAED,OAAO,KAAK,UAAU,GAErB;IAED;;;;;;OAMG;IACG,QAAQ,IAAI,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC;IAUvD;;;;;;;;OAQG;IACG,SAAS,CAAC,IAAI,EAAE;QACpB,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,OAAO,CAAC,EAAE,gBAAgB,CAAA;KAC3B,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAoBlC;;;;;OAKG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAS7B;;;;;;;OAOG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,QAAQ;IAUnE;;;;;;;;;OASG;IACG,GAAG,CACP,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,UAAU,EAC9B,OAAO,GAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAO,GACrC,OAAO,CAAC,SAAS,CAAC;IAgCrB;;;;;;;OAOG;IACG,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAU1D;;;;;;;;OAQG;IACG,GAAG,CACP,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,UAAU,EAC9B,OAAO,GAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAO,GACrC,OAAO,CAAC,IAAI,CAAC;IAIhB;;;;;OAKG;IACG,IAAI,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAU7C;;;;;;OAMG;IACG,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC;CAS5D"}
@@ -0,0 +1,209 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * A navigational handle to a Collection within a Space. Exposes its own
6
+ * lifecycle (`describe`/`configure`/`delete`) and contained-resource operations
7
+ * (`add`/`get`/`put`/`list`, plus `resource(id)` for delete-by-id).
8
+ */
9
+ import { collectionPath, collectionItems, resourcePath, toUrl } from './internal/paths.js';
10
+ import { prepareBody, parseResource } from './internal/content.js';
11
+ import { delegateGrant } from './internal/grant.js';
12
+ import { send } from './internal/request.js';
13
+ import { Resource } from './Resource.js';
14
+ export class Collection {
15
+ spaceId;
16
+ id;
17
+ _context;
18
+ _capability;
19
+ /**
20
+ * @param options {object}
21
+ * @param options.context {ClientContext} - Shared context (serverUrl, ezcap
22
+ * client, controllerDid)
23
+ * @param options.spaceId {string}
24
+ * @param options.collectionId {string}
25
+ * @param [options.capability] {IZcap} - capability attached to every request
26
+ */
27
+ constructor({ context, spaceId, collectionId, capability }) {
28
+ this._context = context;
29
+ this.spaceId = spaceId;
30
+ this.id = collectionId;
31
+ this._capability = capability;
32
+ }
33
+ get _path() {
34
+ return collectionPath(this.spaceId, this.id);
35
+ }
36
+ get _itemsPath() {
37
+ return collectionItems(this.spaceId, this.id);
38
+ }
39
+ /**
40
+ * Reads the Collection Description. Returns `null` if the collection is
41
+ * missing or not visible to you (WAS returns 404 for both not-found and
42
+ * unauthorized).
43
+ *
44
+ * @returns {Promise<CollectionDescription | null>}
45
+ */
46
+ async describe() {
47
+ const response = await send(this._context, {
48
+ path: this._path,
49
+ method: 'GET',
50
+ capability: this._capability,
51
+ read: true
52
+ });
53
+ return response === null ? null : response.data;
54
+ }
55
+ /**
56
+ * Creates or updates the collection by id (upsert). Merges the given fields
57
+ * over the current description.
58
+ *
59
+ * @param desc {object}
60
+ * @param [desc.name] {string}
61
+ * @param [desc.backend] {BackendReference}
62
+ * @returns {Promise<CollectionDescription>}
63
+ */
64
+ async configure(desc) {
65
+ const current = await this.describe();
66
+ const name = desc.name ?? current?.name;
67
+ const body = { id: this.id, name };
68
+ if (desc.backend) {
69
+ body.backend = desc.backend;
70
+ }
71
+ await send(this._context, {
72
+ path: this._path,
73
+ method: 'PUT',
74
+ capability: this._capability,
75
+ json: body
76
+ });
77
+ return {
78
+ id: this.id,
79
+ type: current?.type ?? ['Collection'],
80
+ ...(name !== undefined ? { name } : {})
81
+ };
82
+ }
83
+ /**
84
+ * Deletes the whole collection. Idempotent. To delete a single resource, use
85
+ * `collection.resource(id).delete()`.
86
+ *
87
+ * @returns {Promise<void>}
88
+ */
89
+ async delete() {
90
+ await send(this._context, {
91
+ path: this._path,
92
+ method: 'DELETE',
93
+ capability: this._capability,
94
+ idempotent: true
95
+ });
96
+ }
97
+ /**
98
+ * Returns a lazy handle to a resource by id. No I/O.
99
+ *
100
+ * @param resourceId {string}
101
+ * @param options {object}
102
+ * @param [options.capability] {IZcap}
103
+ * @returns {Resource}
104
+ */
105
+ resource(resourceId, options = {}) {
106
+ return new Resource({
107
+ context: this._context,
108
+ spaceId: this.spaceId,
109
+ collectionId: this.id,
110
+ resourceId,
111
+ capability: options.capability ?? this._capability
112
+ });
113
+ }
114
+ /**
115
+ * Adds a resource with a server-generated id. JSON for plain objects/arrays,
116
+ * binary for `Blob`/`Uint8Array`. Throws `NotFoundError` if the collection
117
+ * does not exist (WAS does not auto-create parents).
118
+ *
119
+ * @param data {Json | Blob | Uint8Array}
120
+ * @param options {object}
121
+ * @param [options.contentType] {string} content-type for binary data
122
+ * @returns {Promise<AddResult>}
123
+ */
124
+ async add(data, options = {}) {
125
+ const prepared = prepareBody(data, options);
126
+ const response = await send(this._context, {
127
+ path: this._itemsPath,
128
+ method: 'POST',
129
+ capability: this._capability,
130
+ json: prepared.json,
131
+ body: prepared.body,
132
+ headers: prepared.contentType
133
+ ? { 'content-type': prepared.contentType }
134
+ : undefined
135
+ });
136
+ // POST always returns a response (404/errors throw via send()).
137
+ const created = response.data;
138
+ const location = response.headers.get('location') ?? undefined;
139
+ return {
140
+ id: created.id,
141
+ url: location ??
142
+ toUrl({
143
+ serverUrl: this._context.serverUrl,
144
+ path: resourcePath(this.spaceId, this.id, created.id)
145
+ }),
146
+ contentType: created['content-type']
147
+ };
148
+ }
149
+ /**
150
+ * Reads a resource by id, auto-parsing JSON to an object and returning binary
151
+ * as a `Blob`. Returns `null` on a missing/unauthorized resource (404
152
+ * conflation caveat).
153
+ *
154
+ * @param resourceId {string}
155
+ * @returns {Promise<Json | Blob | null>}
156
+ */
157
+ async get(resourceId) {
158
+ const response = await send(this._context, {
159
+ path: resourcePath(this.spaceId, this.id, resourceId),
160
+ method: 'GET',
161
+ capability: this._capability,
162
+ read: true
163
+ });
164
+ return parseResource(response);
165
+ }
166
+ /**
167
+ * Creates or replaces a resource by id (upsert).
168
+ *
169
+ * @param resourceId {string}
170
+ * @param data {Json | Blob | Uint8Array}
171
+ * @param options {object}
172
+ * @param [options.contentType] {string} content-type for binary data
173
+ * @returns {Promise<void>}
174
+ */
175
+ async put(resourceId, data, options = {}) {
176
+ await this.resource(resourceId).put(data, options);
177
+ }
178
+ /**
179
+ * Lists the items in the collection. Returns `null` if the collection is
180
+ * missing or not visible to you (404 conflation caveat).
181
+ *
182
+ * @returns {Promise<ResourceListing | null>}
183
+ */
184
+ async list() {
185
+ const response = await send(this._context, {
186
+ path: this._itemsPath,
187
+ method: 'GET',
188
+ capability: this._capability,
189
+ read: true
190
+ });
191
+ return response === null ? null : response.data;
192
+ }
193
+ /**
194
+ * Delegates access to this collection. Prefills the grant `target` with this
195
+ * collection's URL (and the bound `capability`, if any, for re-delegation).
196
+ *
197
+ * @param options {GrantOptions}
198
+ * @returns {Promise<IDelegatedZcap>}
199
+ */
200
+ async grant(options) {
201
+ return delegateGrant(this._context, {
202
+ ...options,
203
+ target: options.target ??
204
+ toUrl({ serverUrl: this._context.serverUrl, path: this._path }),
205
+ capability: options.capability ?? this._capability
206
+ });
207
+ }
208
+ }
209
+ //# sourceMappingURL=Collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Collection.js","sourceRoot":"","sources":["../src/Collection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import type { ClientContext } from './internal/request.js';
2
+ import type { IZcap, Json } from './types.js';
3
+ export declare class Resource {
4
+ readonly spaceId: string;
5
+ readonly collectionId: string;
6
+ readonly id: string;
7
+ private readonly _context;
8
+ private readonly _capability?;
9
+ /**
10
+ * @param options {object}
11
+ * @param options.context {ClientContext}
12
+ * @param options.spaceId {string}
13
+ * @param options.collectionId {string}
14
+ * @param options.resourceId {string}
15
+ * @param [options.capability] {IZcap} capability attached to every request
16
+ */
17
+ constructor({ context, spaceId, collectionId, resourceId, capability }: {
18
+ context: ClientContext;
19
+ spaceId: string;
20
+ collectionId: string;
21
+ resourceId: string;
22
+ capability?: IZcap;
23
+ });
24
+ private get _path();
25
+ /**
26
+ * Reads the resource, auto-parsing JSON to an object and returning binary as
27
+ * a `Blob`. Returns `null` if the resource is missing or not visible to you
28
+ * (WAS returns 404 for both not-found and unauthorized).
29
+ *
30
+ * @returns {Promise<Json | Blob | null>}
31
+ */
32
+ get(): Promise<Json | Blob | null>;
33
+ /**
34
+ * Reads the resource body as text. Returns `null` on a missing/unauthorized
35
+ * resource (404 conflation caveat).
36
+ *
37
+ * @returns {Promise<string | null>}
38
+ */
39
+ getText(): Promise<string | null>;
40
+ /**
41
+ * Reads the resource body as raw bytes. Returns `null` on a
42
+ * missing/unauthorized resource (404 conflation caveat).
43
+ *
44
+ * @returns {Promise<Uint8Array | null>}
45
+ */
46
+ getBytes(): Promise<Uint8Array | null>;
47
+ /**
48
+ * Creates or replaces the resource by id (upsert). JSON for plain
49
+ * objects/arrays, binary for `Blob`/`Uint8Array`. Throws `NotFoundError` if
50
+ * the parent collection does not exist (WAS does not auto-create parents).
51
+ *
52
+ * @param data {Json | Blob | Uint8Array}
53
+ * @param options {object}
54
+ * @param [options.contentType] {string} content-type for binary data
55
+ * @returns {Promise<void>}
56
+ */
57
+ put(data: Json | Blob | Uint8Array, options?: {
58
+ contentType?: string;
59
+ }): Promise<void>;
60
+ /**
61
+ * Deletes the resource. Idempotent.
62
+ *
63
+ * @returns {Promise<void>}
64
+ */
65
+ delete(): Promise<void>;
66
+ }
67
+ //# sourceMappingURL=Resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Resource.d.ts","sourceRoot":"","sources":["../src/Resource.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAE1D,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAE7C,qBAAa,QAAQ;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IAEnB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAO;IAEpC;;;;;;;OAOG;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,EAAE,MAAM,CAAA;QAClB,UAAU,CAAC,EAAE,KAAK,CAAA;KACnB;IAQD,OAAO,KAAK,KAAK,GAEhB;IAED;;;;;;OAMG;IACG,GAAG,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAUxC;;;;;OAKG;IACG,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUvC;;;;;OAKG;IACG,QAAQ,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAa5C;;;;;;;;;OASG;IACG,GAAG,CACP,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,UAAU,EAC9B,OAAO,GAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAO,GACrC,OAAO,CAAC,IAAI,CAAC;IAehB;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ9B"}