@interop/was-client 0.11.0 → 0.13.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 +31 -5
- package/dist/Collection.d.ts +13 -0
- package/dist/Collection.d.ts.map +1 -1
- package/dist/Collection.js +61 -52
- package/dist/Collection.js.map +1 -1
- package/dist/Resource.d.ts +39 -25
- package/dist/Resource.d.ts.map +1 -1
- package/dist/Resource.js +89 -74
- package/dist/Resource.js.map +1 -1
- package/dist/Space.d.ts +43 -0
- package/dist/Space.d.ts.map +1 -1
- package/dist/Space.js +98 -56
- package/dist/Space.js.map +1 -1
- package/dist/WasClient.d.ts.map +1 -1
- package/dist/WasClient.js +33 -32
- package/dist/WasClient.js.map +1 -1
- package/dist/edv/EdvCodec.d.ts +32 -7
- package/dist/edv/EdvCodec.d.ts.map +1 -1
- package/dist/edv/EdvCodec.js +83 -38
- package/dist/edv/EdvCodec.js.map +1 -1
- package/dist/edv/WasTransport.d.ts +28 -10
- package/dist/edv/WasTransport.d.ts.map +1 -1
- package/dist/edv/WasTransport.js +66 -16
- package/dist/edv/WasTransport.js.map +1 -1
- package/dist/edv/constants.d.ts +8 -0
- package/dist/edv/constants.d.ts.map +1 -1
- package/dist/edv/constants.js +10 -0
- package/dist/edv/constants.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +10 -7
- package/dist/errors.js.map +1 -1
- package/dist/internal/codec.d.ts +6 -43
- package/dist/internal/codec.d.ts.map +1 -1
- package/dist/internal/codec.js +19 -40
- package/dist/internal/codec.js.map +1 -1
- package/dist/internal/conditional.d.ts +8 -3
- package/dist/internal/conditional.d.ts.map +1 -1
- package/dist/internal/conditional.js +5 -3
- package/dist/internal/conditional.js.map +1 -1
- package/dist/internal/content.d.ts +42 -8
- package/dist/internal/content.d.ts.map +1 -1
- package/dist/internal/content.js +70 -21
- package/dist/internal/content.js.map +1 -1
- package/dist/internal/grant.d.ts +20 -2
- package/dist/internal/grant.d.ts.map +1 -1
- package/dist/internal/grant.js +22 -2
- package/dist/internal/grant.js.map +1 -1
- package/dist/internal/pagination.d.ts +16 -0
- package/dist/internal/pagination.d.ts.map +1 -1
- package/dist/internal/pagination.js +21 -1
- package/dist/internal/pagination.js.map +1 -1
- package/dist/internal/paths.d.ts +48 -17
- package/dist/internal/paths.d.ts.map +1 -1
- package/dist/internal/paths.js +85 -6
- package/dist/internal/paths.js.map +1 -1
- package/dist/internal/request.d.ts +21 -12
- package/dist/internal/request.d.ts.map +1 -1
- package/dist/internal/request.js +22 -0
- package/dist/internal/request.js.map +1 -1
- package/dist/internal/reserved.d.ts +8 -4
- package/dist/internal/reserved.d.ts.map +1 -1
- package/dist/internal/reserved.js +5 -4
- package/dist/internal/reserved.js.map +1 -1
- package/dist/internal/write.d.ts +47 -6
- package/dist/internal/write.d.ts.map +1 -1
- package/dist/internal/write.js +71 -1
- package/dist/internal/write.js.map +1 -1
- package/package.json +2 -2
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import type { ZcapClient } from '@interop/ezcap';
|
|
12
12
|
import type { HttpResponse } from '@interop/http-client';
|
|
13
13
|
import type { EncryptionProvider } from '../codec.js';
|
|
14
|
-
import type { IZcap } from '../types.js';
|
|
14
|
+
import type { IZcap, RequestInput } from '../types.js';
|
|
15
15
|
/**
|
|
16
16
|
* The shared context threaded through every handle: the server base URL, the
|
|
17
17
|
* wrapped ezcap client, the cached controller DID of its signer, and the
|
|
@@ -25,18 +25,11 @@ export interface ClientContext {
|
|
|
25
25
|
encryption?: EncryptionProvider;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
* A single signed request
|
|
29
|
-
* `url`
|
|
28
|
+
* A single signed request: the public `RequestInput` shape (either `path`,
|
|
29
|
+
* resolved against `serverUrl`, or an absolute `url` must be given) plus the
|
|
30
|
+
* `send()`-level 404 translations.
|
|
30
31
|
*/
|
|
31
|
-
export interface SendInput {
|
|
32
|
-
path?: string;
|
|
33
|
-
url?: string;
|
|
34
|
-
method?: string;
|
|
35
|
-
action?: string;
|
|
36
|
-
headers?: Record<string, string>;
|
|
37
|
-
json?: object;
|
|
38
|
-
body?: Blob | Uint8Array;
|
|
39
|
-
capability?: IZcap;
|
|
32
|
+
export interface SendInput extends RequestInput {
|
|
40
33
|
/**
|
|
41
34
|
* When true, a 404 response resolves to `null` instead of throwing.
|
|
42
35
|
*/
|
|
@@ -87,4 +80,20 @@ export declare function unsignedRequest(input: {
|
|
|
87
80
|
* @returns {Promise<HttpResponse | null>}
|
|
88
81
|
*/
|
|
89
82
|
export declare function send(context: ClientContext, input: SendInput): Promise<HttpResponse | null>;
|
|
83
|
+
/**
|
|
84
|
+
* The GET-a-JSON-description read shared across the handles: a signed
|
|
85
|
+
* null-on-404 `GET` of `path` whose pre-parsed body is unwrapped as `T`. Both
|
|
86
|
+
* a missing/unauthorized target (404) and a bodyless/non-JSON response resolve
|
|
87
|
+
* to `null` (see `dataOrNull`).
|
|
88
|
+
*
|
|
89
|
+
* @param context {ClientContext}
|
|
90
|
+
* @param options {object}
|
|
91
|
+
* @param options.path {string} the path to read
|
|
92
|
+
* @param [options.capability] {IZcap} capability attached to the request
|
|
93
|
+
* @returns {Promise<T | null>}
|
|
94
|
+
*/
|
|
95
|
+
export declare function readData<T>(context: ClientContext, { path, capability }: {
|
|
96
|
+
path: string;
|
|
97
|
+
capability?: IZcap;
|
|
98
|
+
}): Promise<T | null>;
|
|
90
99
|
//# sourceMappingURL=request.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/internal/request.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/internal/request.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAIxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,UAAU,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,kBAAkB,CAAA;CAChC;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAU,SAAQ,YAAY;IAC7C;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;IACd;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAYD;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,SAAS,GACf,OAAO,CAAC,YAAY,CAAC,CAavB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAiC/B;AAED;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CACxB,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,SAAS,GACf,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAS9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,OAAO,EAAE,aAAa,EACtB,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,KAAK,CAAA;CAAE,GACzD,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAQnB"}
|
package/dist/internal/request.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mapError, httpStatus } from '../errors.js';
|
|
2
2
|
import { toUrl } from './paths.js';
|
|
3
|
+
import { dataOrNull } from './content.js';
|
|
3
4
|
function resolveRequestUrl(context, input) {
|
|
4
5
|
if (input.url !== undefined) {
|
|
5
6
|
return input.url;
|
|
@@ -100,4 +101,25 @@ export async function send(context, input) {
|
|
|
100
101
|
throw mapError(err);
|
|
101
102
|
}
|
|
102
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* The GET-a-JSON-description read shared across the handles: a signed
|
|
106
|
+
* null-on-404 `GET` of `path` whose pre-parsed body is unwrapped as `T`. Both
|
|
107
|
+
* a missing/unauthorized target (404) and a bodyless/non-JSON response resolve
|
|
108
|
+
* to `null` (see `dataOrNull`).
|
|
109
|
+
*
|
|
110
|
+
* @param context {ClientContext}
|
|
111
|
+
* @param options {object}
|
|
112
|
+
* @param options.path {string} the path to read
|
|
113
|
+
* @param [options.capability] {IZcap} capability attached to the request
|
|
114
|
+
* @returns {Promise<T | null>}
|
|
115
|
+
*/
|
|
116
|
+
export async function readData(context, { path, capability }) {
|
|
117
|
+
const response = await send(context, {
|
|
118
|
+
path,
|
|
119
|
+
method: 'GET',
|
|
120
|
+
capability,
|
|
121
|
+
read: true
|
|
122
|
+
});
|
|
123
|
+
return dataOrNull(response);
|
|
124
|
+
}
|
|
103
125
|
//# sourceMappingURL=request.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/internal/request.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/internal/request.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
* Throws a `ValidationError` if the given id collides with a reserved path
|
|
3
3
|
* segment for its kind.
|
|
4
4
|
*
|
|
5
|
-
* @param
|
|
6
|
-
* @param
|
|
7
|
-
*
|
|
5
|
+
* @param options {object}
|
|
6
|
+
* @param options.id {string} the proposed collection or resource id
|
|
7
|
+
* @param options.kind {string} 'collection' or 'resource', selects the
|
|
8
|
+
* reserved set and is used in the message
|
|
8
9
|
* @returns {void}
|
|
9
10
|
*/
|
|
10
|
-
export declare function assertNotReserved(id
|
|
11
|
+
export declare function assertNotReserved({ id, kind }: {
|
|
12
|
+
id: string;
|
|
13
|
+
kind: 'collection' | 'resource';
|
|
14
|
+
}): void;
|
|
11
15
|
//# sourceMappingURL=reserved.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reserved.d.ts","sourceRoot":"","sources":["../../src/internal/reserved.ts"],"names":[],"mappings":"AAmBA
|
|
1
|
+
{"version":3,"file":"reserved.d.ts","sourceRoot":"","sources":["../../src/internal/reserved.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,EAAE,EACF,IAAI,EACL,EAAE;IACD,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,YAAY,GAAG,UAAU,CAAA;CAChC,GAAG,IAAI,CAQP"}
|
|
@@ -16,12 +16,13 @@ import { ValidationError } from '../errors.js';
|
|
|
16
16
|
* Throws a `ValidationError` if the given id collides with a reserved path
|
|
17
17
|
* segment for its kind.
|
|
18
18
|
*
|
|
19
|
-
* @param
|
|
20
|
-
* @param
|
|
21
|
-
*
|
|
19
|
+
* @param options {object}
|
|
20
|
+
* @param options.id {string} the proposed collection or resource id
|
|
21
|
+
* @param options.kind {string} 'collection' or 'resource', selects the
|
|
22
|
+
* reserved set and is used in the message
|
|
22
23
|
* @returns {void}
|
|
23
24
|
*/
|
|
24
|
-
export function assertNotReserved(id, kind) {
|
|
25
|
+
export function assertNotReserved({ id, kind }) {
|
|
25
26
|
const reserved = kind === 'collection' ? RESERVED_COLLECTION_IDS : RESERVED_RESOURCE_IDS;
|
|
26
27
|
if (reserved.has(id)) {
|
|
27
28
|
throw new ValidationError(`Cannot use reserved path segment "${id}" as a ${kind} id.`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reserved.js","sourceRoot":"","sources":["../../src/internal/reserved.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;GAQG;AACH,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACtB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE9C
|
|
1
|
+
{"version":3,"file":"reserved.js","sourceRoot":"","sources":["../../src/internal/reserved.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;GAQG;AACH,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACtB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE9C;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAChC,EAAE,EACF,IAAI,EAIL;IACC,MAAM,QAAQ,GACZ,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,qBAAqB,CAAA;IACzE,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,eAAe,CACvB,qCAAqC,EAAE,UAAU,IAAI,MAAM,CAC5D,CAAA;IACH,CAAC;AACH,CAAC"}
|
package/dist/internal/write.d.ts
CHANGED
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
|
-
* The shared write orchestration
|
|
6
|
-
* conditional-write precondition into request
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* The shared write orchestration. `sendEncodedWrite` turns a codec's
|
|
6
|
+
* {@link EncodedWrite} plus a conditional-write precondition into request
|
|
7
|
+
* headers and sends it (the shape `Collection.add` and `Resource.put` would
|
|
8
|
+
* otherwise each re-implement). `upsertResource` layers the upsert flow on
|
|
9
|
+
* top: the conditional-codec pre-read of the current document, the
|
|
10
|
+
* codec-vs-caller precondition selection, and the masked-404 policy for a
|
|
11
|
+
* document that exists but is not readable with the bound capability.
|
|
9
12
|
*/
|
|
10
13
|
import type { HttpResponse } from '@interop/http-client';
|
|
11
|
-
import type { EncodedWrite } from '../codec.js';
|
|
12
|
-
import type { IZcap } from '../types.js';
|
|
14
|
+
import type { EncodedWrite, ResourceCodec } from '../codec.js';
|
|
15
|
+
import type { IZcap, ResourceData } from '../types.js';
|
|
13
16
|
import type { ClientContext } from './request.js';
|
|
14
17
|
import type { WritePrecondition } from './conditional.js';
|
|
15
18
|
/**
|
|
@@ -34,4 +37,42 @@ export declare function sendEncodedWrite(context: ClientContext, { path, method,
|
|
|
34
37
|
capability?: IZcap;
|
|
35
38
|
precondition?: WritePrecondition;
|
|
36
39
|
}): Promise<HttpResponse>;
|
|
40
|
+
/**
|
|
41
|
+
* Creates or replaces a resource by id (upsert) through its codec, owning the
|
|
42
|
+
* conditional-write orchestration in one place:
|
|
43
|
+
*
|
|
44
|
+
* - A conditional codec (e.g. the EDV codec) needs the current stored document
|
|
45
|
+
* to advance its sequence and pin the write to the current ETag, so the
|
|
46
|
+
* current document is pre-read; the codec then computes the precondition
|
|
47
|
+
* itself. A plaintext codec needs no pre-read and defers to the caller's
|
|
48
|
+
* explicit precondition.
|
|
49
|
+
* - The pre-read cannot distinguish "absent" from "unreadable with this
|
|
50
|
+
* capability" (WAS masks unauthorized reads as 404), so a conditional codec
|
|
51
|
+
* encodes a fresh insert (`If-None-Match: *`) in both cases. When the target
|
|
52
|
+
* in fact exists, a conditional-writes backend rejects that insert with 412;
|
|
53
|
+
* that 412 is re-thrown here with a message naming the real cause, instead
|
|
54
|
+
* of surfacing as an inexplicable failed create. Conditional codecs
|
|
55
|
+
* therefore need read access to update an existing document.
|
|
56
|
+
*
|
|
57
|
+
* @param context {ClientContext}
|
|
58
|
+
* @param options {object}
|
|
59
|
+
* @param options.path {string} the resource path to write
|
|
60
|
+
* @param options.codec {ResourceCodec} the collection's resolved codec
|
|
61
|
+
* @param options.id {string} the resource id
|
|
62
|
+
* @param options.data {ResourceData} the plaintext value
|
|
63
|
+
* @param [options.contentType] {string} caller-supplied content type
|
|
64
|
+
* @param [options.capability] {IZcap}
|
|
65
|
+
* @param [options.precondition] {WritePrecondition} the caller's explicit
|
|
66
|
+
* precondition (used only for a non-conditional codec)
|
|
67
|
+
* @returns {Promise<HttpResponse>}
|
|
68
|
+
*/
|
|
69
|
+
export declare function upsertResource(context: ClientContext, { path, codec, id, data, contentType, capability, precondition }: {
|
|
70
|
+
path: string;
|
|
71
|
+
codec: ResourceCodec;
|
|
72
|
+
id: string;
|
|
73
|
+
data: ResourceData;
|
|
74
|
+
contentType?: string;
|
|
75
|
+
capability?: IZcap;
|
|
76
|
+
precondition?: WritePrecondition;
|
|
77
|
+
}): Promise<HttpResponse>;
|
|
37
78
|
//# sourceMappingURL=write.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/internal/write.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/internal/write.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE9D,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAGjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAEzD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,aAAa,EACtB,EACE,IAAI,EACJ,MAAM,EACN,OAAO,EACP,UAAU,EACV,YAAY,EACb,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,YAAY,CAAA;IACrB,UAAU,CAAC,EAAE,KAAK,CAAA;IAClB,YAAY,CAAC,EAAE,iBAAiB,CAAA;CACjC,GACA,OAAO,CAAC,YAAY,CAAC,CAUvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,aAAa,EACtB,EACE,IAAI,EACJ,KAAK,EACL,EAAE,EACF,IAAI,EACJ,WAAW,EACX,UAAU,EACV,YAAY,EACb,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,aAAa,CAAA;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,YAAY,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,KAAK,CAAA;IAClB,YAAY,CAAC,EAAE,iBAAiB,CAAA;CACjC,GACA,OAAO,CAAC,YAAY,CAAC,CA2CvB"}
|
package/dist/internal/write.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PreconditionFailedError } from '../errors.js';
|
|
1
2
|
import { send } from './request.js';
|
|
2
3
|
import { writeHeaders } from './conditional.js';
|
|
3
4
|
/**
|
|
@@ -22,8 +23,77 @@ export async function sendEncodedWrite(context, { path, method, encoded, capabil
|
|
|
22
23
|
capability,
|
|
23
24
|
json: encoded.json,
|
|
24
25
|
body: encoded.body,
|
|
25
|
-
headers: writeHeaders(encoded.contentType, precondition)
|
|
26
|
+
headers: writeHeaders({ contentType: encoded.contentType, precondition })
|
|
26
27
|
});
|
|
27
28
|
return response;
|
|
28
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates or replaces a resource by id (upsert) through its codec, owning the
|
|
32
|
+
* conditional-write orchestration in one place:
|
|
33
|
+
*
|
|
34
|
+
* - A conditional codec (e.g. the EDV codec) needs the current stored document
|
|
35
|
+
* to advance its sequence and pin the write to the current ETag, so the
|
|
36
|
+
* current document is pre-read; the codec then computes the precondition
|
|
37
|
+
* itself. A plaintext codec needs no pre-read and defers to the caller's
|
|
38
|
+
* explicit precondition.
|
|
39
|
+
* - The pre-read cannot distinguish "absent" from "unreadable with this
|
|
40
|
+
* capability" (WAS masks unauthorized reads as 404), so a conditional codec
|
|
41
|
+
* encodes a fresh insert (`If-None-Match: *`) in both cases. When the target
|
|
42
|
+
* in fact exists, a conditional-writes backend rejects that insert with 412;
|
|
43
|
+
* that 412 is re-thrown here with a message naming the real cause, instead
|
|
44
|
+
* of surfacing as an inexplicable failed create. Conditional codecs
|
|
45
|
+
* therefore need read access to update an existing document.
|
|
46
|
+
*
|
|
47
|
+
* @param context {ClientContext}
|
|
48
|
+
* @param options {object}
|
|
49
|
+
* @param options.path {string} the resource path to write
|
|
50
|
+
* @param options.codec {ResourceCodec} the collection's resolved codec
|
|
51
|
+
* @param options.id {string} the resource id
|
|
52
|
+
* @param options.data {ResourceData} the plaintext value
|
|
53
|
+
* @param [options.contentType] {string} caller-supplied content type
|
|
54
|
+
* @param [options.capability] {IZcap}
|
|
55
|
+
* @param [options.precondition] {WritePrecondition} the caller's explicit
|
|
56
|
+
* precondition (used only for a non-conditional codec)
|
|
57
|
+
* @returns {Promise<HttpResponse>}
|
|
58
|
+
*/
|
|
59
|
+
export async function upsertResource(context, { path, codec, id, data, contentType, capability, precondition }) {
|
|
60
|
+
let current;
|
|
61
|
+
if (codec.conditionalWrites) {
|
|
62
|
+
current = await send(context, {
|
|
63
|
+
path,
|
|
64
|
+
method: 'GET',
|
|
65
|
+
capability,
|
|
66
|
+
read: true
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const encoded = await codec.encode({ id, data, contentType, current });
|
|
70
|
+
// A conditional codec computes the precondition itself (from the sequence /
|
|
71
|
+
// ETag); a plaintext codec defers to the caller's explicit options.
|
|
72
|
+
const chosen = codec.conditionalWrites
|
|
73
|
+
? { ifMatch: encoded.ifMatch, ifNoneMatch: encoded.ifNoneMatch }
|
|
74
|
+
: precondition;
|
|
75
|
+
try {
|
|
76
|
+
return await sendEncodedWrite(context, {
|
|
77
|
+
path,
|
|
78
|
+
method: 'PUT',
|
|
79
|
+
capability,
|
|
80
|
+
encoded,
|
|
81
|
+
precondition: chosen
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
if (err instanceof PreconditionFailedError &&
|
|
86
|
+
codec.conditionalWrites &&
|
|
87
|
+
current === null) {
|
|
88
|
+
const { status, type, title, details, requestUrl } = err;
|
|
89
|
+
throw new PreconditionFailedError(`Cannot update the document at "${path}": it exists, but its current ` +
|
|
90
|
+
'version is not readable with this capability (WAS masks ' +
|
|
91
|
+
'unauthorized reads as 404), so the write was encoded as a fresh ' +
|
|
92
|
+
'insert and the server rejected it. A conditional-writes codec ' +
|
|
93
|
+
'(e.g. the EDV codec) needs read access to update an existing ' +
|
|
94
|
+
'document.', { status, type, title, details, requestUrl, cause: err });
|
|
95
|
+
}
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
29
99
|
//# sourceMappingURL=write.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/internal/write.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/internal/write.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAGtD,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAG/C;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAsB,EACtB,EACE,IAAI,EACJ,MAAM,EACN,OAAO,EACP,UAAU,EACV,YAAY,EAOb;IAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE;QACnC,IAAI;QACJ,MAAM;QACN,UAAU;QACV,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO,EAAE,YAAY,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,YAAY,EAAE,CAAC;KAC1E,CAAC,CAAA;IACF,OAAO,QAAwB,CAAA;AACjC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAsB,EACtB,EACE,IAAI,EACJ,KAAK,EACL,EAAE,EACF,IAAI,EACJ,WAAW,EACX,UAAU,EACV,YAAY,EASb;IAED,IAAI,OAAwC,CAAA;IAC5C,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC5B,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE;YAC5B,IAAI;YACJ,MAAM,EAAE,KAAK;YACb,UAAU;YACV,IAAI,EAAE,IAAI;SACX,CAAC,CAAA;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAA;IACtE,4EAA4E;IAC5E,oEAAoE;IACpE,MAAM,MAAM,GAAG,KAAK,CAAC,iBAAiB;QACpC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE;QAChE,CAAC,CAAC,YAAY,CAAA;IAChB,IAAI,CAAC;QACH,OAAO,MAAM,gBAAgB,CAAC,OAAO,EAAE;YACrC,IAAI;YACJ,MAAM,EAAE,KAAK;YACb,UAAU;YACV,OAAO;YACP,YAAY,EAAE,MAAM;SACrB,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IACE,GAAG,YAAY,uBAAuB;YACtC,KAAK,CAAC,iBAAiB;YACvB,OAAO,KAAK,IAAI,EAChB,CAAC;YACD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,GAAG,CAAA;YACxD,MAAM,IAAI,uBAAuB,CAC/B,kCAAkC,IAAI,gCAAgC;gBACpE,0DAA0D;gBAC1D,kEAAkE;gBAClE,gEAAgE;gBAChE,+DAA+D;gBAC/D,WAAW,EACb,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,CACzD,CAAA;QACH,CAAC;QACD,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interop/was-client",
|
|
3
3
|
"description": "A developer-friendly client for Wallet Attached Storage (WAS) servers.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.13.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "pnpm run clear && tsc",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@interop/data-integrity-core": "^8.1.0",
|
|
46
46
|
"@interop/ed25519-signature": "^7.1.3",
|
|
47
|
-
"@interop/edv-client": "^17.
|
|
47
|
+
"@interop/edv-client": "^17.4.0",
|
|
48
48
|
"@interop/ezcap": "^7.3.2",
|
|
49
49
|
"@interop/http-client": "^1.0.4",
|
|
50
50
|
"@interop/storage-core": "^0.3.1"
|