@larc-iu/plaid-client 0.2.0-alpha.3 → 0.2.0-alpha.30
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 +39 -0
- package/package.json +13 -6
- package/src/apiAnnotations.d.ts +508 -0
- package/src/apiAnnotations.js +543 -0
- package/src/apiDocuments.d.ts +569 -0
- package/src/apiDocuments.js +551 -0
- package/src/apiLayers.d.ts +369 -0
- package/src/apiLayers.js +222 -0
- package/src/apiMessages.d.ts +293 -0
- package/src/apiMessages.js +265 -0
- package/src/apiPeople.d.ts +626 -0
- package/src/apiPeople.js +698 -0
- package/src/apiProject.d.ts +761 -0
- package/src/apiProject.js +774 -0
- package/src/apiServer.d.ts +455 -0
- package/src/apiServer.js +422 -0
- package/src/apiVocab.d.ts +456 -0
- package/src/apiVocab.js +425 -0
- package/src/batch.d.ts +54 -0
- package/src/batch.js +323 -0
- package/src/batchRef.d.ts +30 -0
- package/src/batchRef.js +128 -0
- package/src/client.d.ts +1321 -0
- package/src/client.js +743 -0
- package/src/codepoint.d.ts +94 -0
- package/src/codepoint.js +131 -1
- package/src/constraints.d.ts +85 -0
- package/src/constraints.js +112 -0
- package/src/created.d.ts +2 -0
- package/src/created.js +29 -0
- package/src/documentLock.d.ts +172 -0
- package/src/documentLock.js +384 -0
- package/src/entryForms.d.ts +63 -0
- package/src/entryForms.js +152 -0
- package/src/events.d.ts +60 -0
- package/src/events.js +271 -0
- package/src/families.d.ts +349 -0
- package/src/families.js +396 -0
- package/src/http.d.ts +233 -0
- package/src/http.js +809 -93
- package/src/ids.d.ts +26 -0
- package/src/ids.js +96 -0
- package/src/index.d.ts +101 -0
- package/src/index.js +183 -2105
- package/src/metadataOps.d.ts +15 -0
- package/src/metadataOps.js +124 -0
- package/src/operations.d.ts +36 -0
- package/src/operations.js +164 -0
- package/src/pagination.d.ts +49 -0
- package/src/pagination.js +20 -53
- package/src/provenance.d.ts +243 -0
- package/src/provenance.js +458 -36
- package/src/replayed.d.ts +3 -0
- package/src/replayed.js +31 -0
- package/src/requester.d.ts +60 -0
- package/src/requester.js +74 -0
- package/src/roles.d.ts +104 -0
- package/src/roles.js +64 -2
- package/src/serviceSchema.d.ts +337 -0
- package/src/serviceSchema.js +177 -60
- package/src/services.d.ts +319 -0
- package/src/services.js +742 -58
- package/src/sse.d.ts +30 -0
- package/src/sse.js +80 -3
- package/src/testing.d.ts +128 -0
- package/src/testing.js +379 -0
- package/src/textEdits.d.ts +59 -0
- package/src/textEdits.js +234 -0
- package/src/transforms.d.ts +25 -0
- package/src/transforms.js +11 -1
- package/src/types.d.ts +867 -0
- package/src/types.js +663 -0
- package/index.d.ts +0 -532
package/src/families.js
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The method families the API bundles are built from (a bundle takes
|
|
3
|
+
* `...metadataMethods(client, "spans")` and the like) and the small wire
|
|
4
|
+
* helpers they share: a body without its undefined keys, a list filter as a
|
|
5
|
+
* comma-separated value, and an audit read's query.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { constraintsBody } from "./constraints.js";
|
|
9
|
+
import { listAll, listPage } from "./pagination.js";
|
|
10
|
+
|
|
11
|
+
/** @typedef {import("./client.js").PlaidClient} PlaidClient */
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {import("./types.js").MetadataOp} MetadataOp
|
|
14
|
+
* @typedef {import("./types.js").LayerConstraint} LayerConstraint
|
|
15
|
+
* @typedef {import("./types.js").ConstraintViolation} ConstraintViolation
|
|
16
|
+
* @typedef {import("./types.js").OperationKind} OperationKind
|
|
17
|
+
* @typedef {import("./types.js").ConstraintRepair} ConstraintRepair
|
|
18
|
+
* @typedef {import("./types.js").ValueSetRule} ValueSetRule
|
|
19
|
+
* @typedef {import("./types.js").RefusedValues} RefusedValues
|
|
20
|
+
* @typedef {import("./types.js").MetadataRecords} MetadataRecords
|
|
21
|
+
* @typedef {import("./types.js").LayerRecords} LayerRecords
|
|
22
|
+
* @typedef {import("./types.js").AuditEntry} AuditEntry
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @template [T=any]
|
|
27
|
+
* @typedef {import("./types.js").Page<T>} Page
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
// A list filter in the wire's comma-separated form (`layers`, `op-types`,
|
|
31
|
+
// `kinds`). Accepts an array or a ready-made string, and anything empty
|
|
32
|
+
// becomes undefined so the key is not sent at all.
|
|
33
|
+
export function csvParam(values) {
|
|
34
|
+
if (values === undefined || values === null) return undefined;
|
|
35
|
+
const joined = Array.isArray(values) ? values.join(",") : String(values);
|
|
36
|
+
return joined.length > 0 ? joined : undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Helper: build body object, filtering out undefined values
|
|
40
|
+
export function bodyOf(obj) {
|
|
41
|
+
const result = {};
|
|
42
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
43
|
+
if (value !== undefined) result[key] = value;
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The request options of a config write. With `options.expected` present
|
|
50
|
+
* (even as undefined, which means the key was absent) the write is a
|
|
51
|
+
* compare-and-set: `?if-unchanged=true` and the body `{expected, value}`,
|
|
52
|
+
* which the server refuses with a 409 when the stored value is no longer
|
|
53
|
+
* `expected`. Without it the body is the value itself. Config is opaque, so
|
|
54
|
+
* neither body is re-cased.
|
|
55
|
+
*/
|
|
56
|
+
export function configRequest(auditMessage, value, options, isDelete = false) {
|
|
57
|
+
// The answer is a 204 with no body, so there is nothing to keep un-recased.
|
|
58
|
+
if (!options || !Object.hasOwn(options, "expected")) {
|
|
59
|
+
return isDelete ? { auditMessage } : { auditMessage, rawBody: value };
|
|
60
|
+
}
|
|
61
|
+
const expected = options.expected === undefined ? null : options.expected;
|
|
62
|
+
return {
|
|
63
|
+
auditMessage,
|
|
64
|
+
queryParams: { "if-unchanged": true },
|
|
65
|
+
rawBody: isDelete ? { expected } : { expected, value },
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The query of an audit read: the window, the op-type and kind filters, the
|
|
71
|
+
* one vocabulary entry, and for one page its order, ops limit and entry id. A
|
|
72
|
+
* key left undefined is not sent.
|
|
73
|
+
* @private
|
|
74
|
+
*/
|
|
75
|
+
export function auditQuery({ startTime, endTime, opTypes, itemId, kinds, order, opsLimit, entryId }) {
|
|
76
|
+
return {
|
|
77
|
+
"start-time": startTime,
|
|
78
|
+
"end-time": endTime,
|
|
79
|
+
"op-types": csvParam(opTypes),
|
|
80
|
+
"item-id": itemId,
|
|
81
|
+
kinds: csvParam(kinds),
|
|
82
|
+
order,
|
|
83
|
+
"ops-limit": opsLimit,
|
|
84
|
+
"entry-id": entryId,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The audit readers of a document, a project or a user, on
|
|
90
|
+
* `/api/v1/<kind>/<id>/audit`. A vocabulary's take an entry id as well and are
|
|
91
|
+
* its own, and the instance-wide log is `audit.list`.
|
|
92
|
+
* @param {PlaidClient} client
|
|
93
|
+
* @param {"documents"|"projects"|"users"} kind
|
|
94
|
+
*/
|
|
95
|
+
export function auditMethods(client, kind) {
|
|
96
|
+
const path = (id) => `/api/v1/${kind}/${id}/audit`;
|
|
97
|
+
return {
|
|
98
|
+
/**
|
|
99
|
+
* Get the audit log of a document or a project, or of a user's actions.
|
|
100
|
+
* Transparently follows pagination cursors and returns the full flat
|
|
101
|
+
* array. Each entry, and each of its `ops`, says what kind of credential
|
|
102
|
+
* made it as `credential`: `login`, `named-token` or `delegated` (absent
|
|
103
|
+
* on older operations, `service` on some from 2026-10-06 to 2026-10-08),
|
|
104
|
+
* beside `apiToken` (the named token's id and name) when there was one.
|
|
105
|
+
* @param {string} id - The document, project or user ID
|
|
106
|
+
* @param {string} [startTime] - Start of time range
|
|
107
|
+
* @param {string} [endTime] - End of time range
|
|
108
|
+
* @param {string[]|string} [opTypes] - Only return operations of these
|
|
109
|
+
* types, spelled as in an entry's `op/type` (e.g.
|
|
110
|
+
* `['span-layer/create', 'span-layer/delete']`). An entry appears when
|
|
111
|
+
* one of its operations matches, carrying only the ones that did.
|
|
112
|
+
* @param {string[]|string} [kinds] - Only the entries of operations of
|
|
113
|
+
* these kinds (e.g. `['review', 'guess-adoption']`), each whole
|
|
114
|
+
* @returns {Promise<AuditEntry[]>}
|
|
115
|
+
*/
|
|
116
|
+
audit: (id, startTime, endTime, opTypes, kinds) =>
|
|
117
|
+
listAll(client, path(id), { query: auditQuery({ startTime, endTime, opTypes, kinds }) }),
|
|
118
|
+
/**
|
|
119
|
+
* One page of the same log, newest-first with `order: "desc"`. Use this
|
|
120
|
+
* rather than audit() wherever the caller wants the recent end of a log
|
|
121
|
+
* that may be long: audit() walks every page before it resolves.
|
|
122
|
+
* @param {string} id - The document, project or user ID
|
|
123
|
+
* @param {object} [opts]
|
|
124
|
+
* @param {string} [opts.startTime] - Only entries at or after this instant
|
|
125
|
+
* @param {string} [opts.endTime] - Only entries at or before this instant
|
|
126
|
+
* @param {string[]|string} [opts.opTypes] - Only these op types
|
|
127
|
+
* @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
|
|
128
|
+
* @param {"asc"|"desc"} [opts.order] - "desc" pages newest-first
|
|
129
|
+
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
130
|
+
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
131
|
+
* @param {number} [opts.opsLimit] - Keep only each entry's oldest N
|
|
132
|
+
* operations in `ops` (1..1000). `opCount` on every entry says how
|
|
133
|
+
* many it has
|
|
134
|
+
* @param {string} [opts.entryId] - Read only this entry, not a page
|
|
135
|
+
* @returns {Promise<Page<AuditEntry>>}
|
|
136
|
+
*/
|
|
137
|
+
auditPage: (
|
|
138
|
+
id,
|
|
139
|
+
{ startTime, endTime, opTypes, kinds, order, limit, cursor, opsLimit, entryId } = {},
|
|
140
|
+
) =>
|
|
141
|
+
listPage(client, path(id), {
|
|
142
|
+
limit,
|
|
143
|
+
cursor,
|
|
144
|
+
query: auditQuery({ startTime, endTime, opTypes, kinds, order, opsLimit, entryId }),
|
|
145
|
+
}),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The three metadata methods of a bundle whose entities carry metadata, on
|
|
151
|
+
* `/api/v1/<kind>/<id>/metadata`. Metadata is opaque, so no body is re-cased.
|
|
152
|
+
* Each answers the entity as its GET does.
|
|
153
|
+
* @template {keyof MetadataRecords} K
|
|
154
|
+
* @param {PlaidClient} client
|
|
155
|
+
* @param {K} kind
|
|
156
|
+
*/
|
|
157
|
+
export function metadataMethods(client, kind) {
|
|
158
|
+
const path = (id) => `/api/v1/${kind}/${id}/metadata`;
|
|
159
|
+
return {
|
|
160
|
+
/**
|
|
161
|
+
* Replace all metadata of an entity. Keys left out of the new map are removed.
|
|
162
|
+
* @param {string} id - The entity's ID
|
|
163
|
+
* @param {object} body - The new metadata map
|
|
164
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
165
|
+
* @returns {Promise<MetadataRecords[K]>}
|
|
166
|
+
*/
|
|
167
|
+
setMetadata: (id, body, auditMessage) =>
|
|
168
|
+
client._request("PUT", path(id), { auditMessage, rawBody: body }),
|
|
169
|
+
/**
|
|
170
|
+
* Remove all metadata from an entity.
|
|
171
|
+
* @param {string} id - The entity's ID
|
|
172
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
173
|
+
* @returns {Promise<MetadataRecords[K]>}
|
|
174
|
+
*/
|
|
175
|
+
deleteMetadata: (id, auditMessage) => client._request("DELETE", path(id), { auditMessage }),
|
|
176
|
+
/**
|
|
177
|
+
* Edit an entity's metadata with a list of ops applied in order, in one operation. `{op: 'set', path, value}` writes value at path, creating missing objects along it; `{op: 'delete', path}` removes the key at path (a no-op when absent). A path is a non-empty array of keys, the first a top-level key. A path through a non-object is refused (400). See metadataOps and applyMetadataOps.
|
|
178
|
+
* @param {string} id - The entity's ID
|
|
179
|
+
* @param {MetadataOp[]} body - The metadata ops
|
|
180
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
181
|
+
* @returns {Promise<MetadataRecords[K]>}
|
|
182
|
+
*/
|
|
183
|
+
patchMetadata: (id, body, auditMessage) =>
|
|
184
|
+
client._request("PATCH", path(id), { auditMessage, rawBody: body }),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* The two config methods of a bundle whose records carry editor config, on
|
|
190
|
+
* `/api/v1/<kind>/<id>/config/<namespace>/<key>`.
|
|
191
|
+
* @param {PlaidClient} client
|
|
192
|
+
* @param {string} kind
|
|
193
|
+
*/
|
|
194
|
+
export function configMethods(client, kind) {
|
|
195
|
+
const path = (id, namespace, configKey) => `/api/v1/${kind}/${id}/config/${namespace}/${configKey}`;
|
|
196
|
+
return {
|
|
197
|
+
/**
|
|
198
|
+
* Set a configuration value in an editor namespace.
|
|
199
|
+
* @param {string} id - The record's ID
|
|
200
|
+
* @param {string} namespace - The config namespace
|
|
201
|
+
* @param {string} configKey - The config key
|
|
202
|
+
* @param {any} configValue - Configuration value to set
|
|
203
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
204
|
+
* @param {{expected?: any}} [options] - `expected`: the value read for this key (null or undefined when it
|
|
205
|
+
* was absent). The write is then refused with a 409 when the stored value differs. A value the key
|
|
206
|
+
* already holds is not written and leaves no audit row, so the value read, expecting it, states in a
|
|
207
|
+
* batch that the key is unchanged.
|
|
208
|
+
* @returns {Promise<void>}
|
|
209
|
+
*/
|
|
210
|
+
setConfig: (id, namespace, configKey, configValue, auditMessage, options) =>
|
|
211
|
+
client._request(
|
|
212
|
+
"PUT",
|
|
213
|
+
path(id, namespace, configKey),
|
|
214
|
+
configRequest(auditMessage, configValue, options),
|
|
215
|
+
),
|
|
216
|
+
/**
|
|
217
|
+
* Remove a configuration value.
|
|
218
|
+
* @param {string} id - The record's ID
|
|
219
|
+
* @param {string} namespace - The config namespace
|
|
220
|
+
* @param {string} configKey - The config key
|
|
221
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
222
|
+
* @param {{expected?: any}} [options] - `expected`, as on setConfig. A key that is absent is not
|
|
223
|
+
* written, so expecting null states in a batch that it is still absent.
|
|
224
|
+
* @returns {Promise<void>}
|
|
225
|
+
*/
|
|
226
|
+
deleteConfig: (id, namespace, configKey, auditMessage, options) =>
|
|
227
|
+
client._request(
|
|
228
|
+
"DELETE",
|
|
229
|
+
path(id, namespace, configKey),
|
|
230
|
+
configRequest(auditMessage, undefined, options, true),
|
|
231
|
+
),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* The get, delete, update and shift methods of the four layer bundles, on
|
|
237
|
+
* `/api/v1/<kind>/<layerId>`.
|
|
238
|
+
* @template {keyof LayerRecords} K
|
|
239
|
+
* @param {PlaidClient} client
|
|
240
|
+
* @param {K} kind
|
|
241
|
+
*/
|
|
242
|
+
export function layerMethods(client, kind) {
|
|
243
|
+
const path = (layerId) => `/api/v1/${kind}/${layerId}`;
|
|
244
|
+
return {
|
|
245
|
+
/**
|
|
246
|
+
* Get a layer by ID.
|
|
247
|
+
* @param {string} layerId - The layer ID
|
|
248
|
+
* @returns {Promise<LayerRecords[K]>}
|
|
249
|
+
*/
|
|
250
|
+
get: (layerId) => client._request("GET", path(layerId)),
|
|
251
|
+
/**
|
|
252
|
+
* Delete a layer.
|
|
253
|
+
* @param {string} layerId - The layer ID
|
|
254
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
255
|
+
* @returns {Promise<void>}
|
|
256
|
+
*/
|
|
257
|
+
delete: (layerId, auditMessage) => client._request("DELETE", path(layerId), { auditMessage }),
|
|
258
|
+
/**
|
|
259
|
+
* Rename a layer.
|
|
260
|
+
* @param {string} layerId - The layer ID
|
|
261
|
+
* @param {string} name - The new name
|
|
262
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
263
|
+
* @returns {Promise<LayerRecords[K]>}
|
|
264
|
+
*/
|
|
265
|
+
update: (layerId, name, auditMessage) =>
|
|
266
|
+
client._request("PATCH", path(layerId), { auditMessage, body: bodyOf({ name }) }),
|
|
267
|
+
/**
|
|
268
|
+
* Move a layer one place in its display order.
|
|
269
|
+
* @param {string} layerId - The layer ID
|
|
270
|
+
* @param {string} direction - The direction ("up" or "down")
|
|
271
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
272
|
+
* @returns {Promise<void>}
|
|
273
|
+
*/
|
|
274
|
+
shift: (layerId, direction, auditMessage) =>
|
|
275
|
+
client._request("POST", `${path(layerId)}/shift`, {
|
|
276
|
+
auditMessage,
|
|
277
|
+
body: bodyOf({ direction }),
|
|
278
|
+
}),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* The four layer-constraint methods of a layer bundle, on `/api/v1/<kind>`.
|
|
284
|
+
* See constraints.js and the core manual, "Layer constraints".
|
|
285
|
+
* @param {PlaidClient} client
|
|
286
|
+
* @param {string} kind
|
|
287
|
+
*/
|
|
288
|
+
export function constraintMethods(client, kind) {
|
|
289
|
+
const base = (layerId) => `/api/v1/${kind}/${layerId}/constraints`;
|
|
290
|
+
return {
|
|
291
|
+
/**
|
|
292
|
+
* Declare an app namespace's constraints on this layer, replacing that
|
|
293
|
+
* namespace's list. Refused with 422 and the violations when the layer's
|
|
294
|
+
* stored data breaks them (see violationsOf).
|
|
295
|
+
* @param {string} layerId - The layer ID
|
|
296
|
+
* @param {string} namespace - The declaring app's namespace, e.g. "igt"
|
|
297
|
+
* @param {LayerConstraint[]} constraints - e.g. [{type: "single-span"}, {type: "value-set", values: ["N"]}]
|
|
298
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
299
|
+
* @param {{expected?: Array<object>|null}} [options] - `expected`: the list read for this
|
|
300
|
+
* namespace (null when it was absent). The write is then refused with a 409 when the stored
|
|
301
|
+
* list differs.
|
|
302
|
+
* @returns {Promise<{constraints: Record<string, LayerConstraint[]>}>} the layer's whole constraint map
|
|
303
|
+
*/
|
|
304
|
+
setConstraints: (layerId, namespace, constraints, auditMessage, options) =>
|
|
305
|
+
client._request("PUT", `${base(layerId)}/${namespace}`, {
|
|
306
|
+
auditMessage,
|
|
307
|
+
body: constraintsBody(constraints, options),
|
|
308
|
+
}),
|
|
309
|
+
/**
|
|
310
|
+
* Remove an app namespace's constraints from this layer.
|
|
311
|
+
* @param {string} layerId - The layer ID
|
|
312
|
+
* @param {string} namespace - The declaring app's namespace
|
|
313
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
314
|
+
* @param {{expected?: Array<object>|null}} [options] - `expected`, as on setConstraints.
|
|
315
|
+
* @returns {Promise<void>}
|
|
316
|
+
*/
|
|
317
|
+
deleteConstraints: (layerId, namespace, auditMessage, options) => {
|
|
318
|
+
const body = constraintsBody(undefined, options);
|
|
319
|
+
return client._request("DELETE", `${base(layerId)}/${namespace}`, {
|
|
320
|
+
auditMessage,
|
|
321
|
+
...(Object.keys(body).length ? { body } : {}),
|
|
322
|
+
});
|
|
323
|
+
},
|
|
324
|
+
/**
|
|
325
|
+
* The violations the given constraints would meet in this layer's stored
|
|
326
|
+
* data. Writes nothing.
|
|
327
|
+
* @param {string} layerId - The layer ID
|
|
328
|
+
* @param {LayerConstraint[]} constraints - The list to check
|
|
329
|
+
* @param {{limit?: number}} [options] - `limit`: how many violations to list (default 100)
|
|
330
|
+
* @returns {Promise<{violations: ConstraintViolation[], violationCount: number}>}
|
|
331
|
+
*/
|
|
332
|
+
// outOfBand: a read that travels as a POST (see the note at the top of
|
|
333
|
+
// http.js), so it goes over the wire from a batch, takes no key and never
|
|
334
|
+
// joins an open operation.
|
|
335
|
+
checkConstraints: (layerId, constraints, options) =>
|
|
336
|
+
client._request("POST", `${base(layerId)}/check`, {
|
|
337
|
+
outOfBand: true,
|
|
338
|
+
body: {
|
|
339
|
+
constraints,
|
|
340
|
+
...(options?.limit != null ? { limit: options.limit } : {}),
|
|
341
|
+
},
|
|
342
|
+
}),
|
|
343
|
+
/**
|
|
344
|
+
* Apply the remedies of the given constraints' remediable types
|
|
345
|
+
* (coextensive, single-span, single-link, same-ancestor) to every
|
|
346
|
+
* violation in this layer's stored data, one operation per document.
|
|
347
|
+
* With `document`, only that document is repaired, and a writer may
|
|
348
|
+
* ask. A document another user holds the lock on is left as it is and
|
|
349
|
+
* listed under `locked`.
|
|
350
|
+
* @param {string} layerId - The layer ID
|
|
351
|
+
* @param {LayerConstraint[]} constraints - The list to repair for
|
|
352
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
353
|
+
* @param {{document?: string}} [options]
|
|
354
|
+
* @returns {Promise<{repaired: ConstraintRepair[], locked: Array<{document: string, lockedBy: string}>, violations: ConstraintViolation[], violationCount: number}>}
|
|
355
|
+
*/
|
|
356
|
+
repairConstraints: (layerId, constraints, auditMessage, options) =>
|
|
357
|
+
client._request("POST", `${base(layerId)}/repair`, {
|
|
358
|
+
auditMessage,
|
|
359
|
+
body: options?.document ? { constraints, document: options.document } : { constraints },
|
|
360
|
+
}),
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* The value-set read of a span or relation layer bundle, on
|
|
366
|
+
* `/api/v1/<kind>/<layerId>/constraints/refused-values`.
|
|
367
|
+
* @param {PlaidClient} client
|
|
368
|
+
* @param {string} kind
|
|
369
|
+
*/
|
|
370
|
+
export function valueSetMethods(client, kind) {
|
|
371
|
+
return {
|
|
372
|
+
/**
|
|
373
|
+
* The stored values of this layer that a value-set rule would refuse,
|
|
374
|
+
* before it is declared: exactly what a declaration of it would meet,
|
|
375
|
+
* with the rule's exemptions (an unconfirmed machine value, a value an
|
|
376
|
+
* import, a document copy or a restore set). Writes nothing, and a
|
|
377
|
+
* reader may ask.
|
|
378
|
+
* @param {string} layerId - The layer ID
|
|
379
|
+
* @param {ValueSetRule} rule - The list as a value-set declaration takes it
|
|
380
|
+
* @param {{limit?: number}} [options] - `limit`: how many values to list, the most frequent first
|
|
381
|
+
* (default 20, at most 100)
|
|
382
|
+
* @returns {Promise<RefusedValues>}
|
|
383
|
+
*/
|
|
384
|
+
// outOfBand, as checkConstraints.
|
|
385
|
+
refusedValues: (layerId, rule, options) =>
|
|
386
|
+
client._request("POST", `/api/v1/${kind}/${layerId}/constraints/refused-values`, {
|
|
387
|
+
outOfBand: true,
|
|
388
|
+
body: bodyOf({
|
|
389
|
+
values: rule?.values,
|
|
390
|
+
delimiters: rule?.delimiters,
|
|
391
|
+
parts: rule?.parts,
|
|
392
|
+
limit: options?.limit,
|
|
393
|
+
}),
|
|
394
|
+
}),
|
|
395
|
+
};
|
|
396
|
+
}
|
package/src/http.d.ts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The timeout for a query: the client's own, raised to
|
|
3
|
+
* DEFAULT_QUERY_TIMEOUT_MS when it is shorter. A disabled timeout (0, null)
|
|
4
|
+
* stays disabled.
|
|
5
|
+
*/
|
|
6
|
+
export function queryTimeout(client: any): any;
|
|
7
|
+
/**
|
|
8
|
+
* Note how far the server's clock is from this machine's, from a response's
|
|
9
|
+
* Date header, for `client.serverNow()`. A time the server stamped (an audit
|
|
10
|
+
* entry's `ts`) has to be judged against the server's clock: a browser clock
|
|
11
|
+
* minutes off would misjudge it. A response with no readable Date header
|
|
12
|
+
* leaves the last offset as it was.
|
|
13
|
+
*/
|
|
14
|
+
export function noteServerClock(client: any, headers: any): void;
|
|
15
|
+
/**
|
|
16
|
+
* Extract and update document versions from response headers and body.
|
|
17
|
+
*
|
|
18
|
+
* `historical` marks a read made with `as-of`: its body carries the version
|
|
19
|
+
* the document HAD then, which is not what a strict-mode write must claim
|
|
20
|
+
* next, so the body is ignored and only the header (always the live
|
|
21
|
+
* version) is learned from. Without this, viewing an old state and then
|
|
22
|
+
* writing 409s on a version that stopped being current long ago.
|
|
23
|
+
*/
|
|
24
|
+
export function extractDocumentVersions(client: any, responseHeaders: any, responseBody?: any, { historical }?: {
|
|
25
|
+
historical?: boolean;
|
|
26
|
+
}): void;
|
|
27
|
+
/**
|
|
28
|
+
* `held` with `learned` merged in. A replayed answer only raises a version:
|
|
29
|
+
* it is what the document was right after the first send.
|
|
30
|
+
*/
|
|
31
|
+
export function mergeVersions(held: any, learned: any, replayed?: boolean): any;
|
|
32
|
+
/**
|
|
33
|
+
* Read a failed response's body as parsed JSON, falling back to text.
|
|
34
|
+
*/
|
|
35
|
+
export function parseErrorBody(response: any): Promise<any>;
|
|
36
|
+
/**
|
|
37
|
+
* Create an enriched error from a failed HTTP response.
|
|
38
|
+
*/
|
|
39
|
+
export function makeHttpError(response: any, errorData: any, url: any, method: any): Error;
|
|
40
|
+
/**
|
|
41
|
+
* Create a network error (status 0). Timeout aborts get a clearer message.
|
|
42
|
+
*/
|
|
43
|
+
export function makeNetworkError(originalError: any, url: any, method: any): Error;
|
|
44
|
+
/**
|
|
45
|
+
* Run `attempt`, retrying while it rejects with a 503. Exponential backoff
|
|
46
|
+
* with full jitter, so two clients that collide don't march in lockstep and
|
|
47
|
+
* collide again on every retry.
|
|
48
|
+
*
|
|
49
|
+
* `attempt` must perform the whole request, not just await a prepared one:
|
|
50
|
+
* a fetch body and an AbortSignal.timeout are both single-use.
|
|
51
|
+
* @param {() => Promise<any>} attempt
|
|
52
|
+
* @param {{retries?: number, baseDelayMs?: number, onRetry?: (info: object) => void}} [options]
|
|
53
|
+
*/
|
|
54
|
+
export function retryWhileBusy(attempt: () => Promise<any>, { retries, baseDelayMs, onRetry }?: {
|
|
55
|
+
retries?: number;
|
|
56
|
+
baseDelayMs?: number;
|
|
57
|
+
onRetry?: (info: object) => void;
|
|
58
|
+
}): Promise<any>;
|
|
59
|
+
/**
|
|
60
|
+
* Whether `error` leaves the outcome unknown: no response came (status 0),
|
|
61
|
+
* or a proxy answered for the server (502, 504). The write may have landed.
|
|
62
|
+
*/
|
|
63
|
+
export function isUnknownOutcome(error: any): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Whether a create refused with `status` and body `data` was made by an
|
|
66
|
+
* earlier send of the same work: `409 id-taken` for an id in `minted` (a Set
|
|
67
|
+
* of the ids the work minted for its creates), and not for a row deleted
|
|
68
|
+
* since (there is nothing to go on with). The one rule for a lost answer's
|
|
69
|
+
* create. The Python twin is `minted_taken`.
|
|
70
|
+
*/
|
|
71
|
+
export function mintedTaken(status: any, data: any, minted: any): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Run `attempt`, sending it again after each delay in `delaysMs` while it
|
|
74
|
+
* fails with an unknown outcome. Only for a keyed write, whose resend is
|
|
75
|
+
* answered from the first send when that one landed. Not when the browser
|
|
76
|
+
* says it is offline: then the request never left, and the caller waits for
|
|
77
|
+
* the network instead. Full jitter, as retryWhileBusy.
|
|
78
|
+
* @param {() => Promise<any>} attempt
|
|
79
|
+
* @param {{delaysMs?: number[], onRetry?: (info: object) => void}} [options]
|
|
80
|
+
*/
|
|
81
|
+
export function retryUnknown(attempt: () => Promise<any>, { delaysMs, onRetry }?: {
|
|
82
|
+
delaysMs?: number[];
|
|
83
|
+
onRetry?: (info: object) => void;
|
|
84
|
+
}): Promise<any>;
|
|
85
|
+
/**
|
|
86
|
+
* Build a fetch AbortSignal that fires after `timeout` ms, or undefined when
|
|
87
|
+
* timeouts are disabled / unsupported.
|
|
88
|
+
*/
|
|
89
|
+
export function timeoutSignal(timeout: any): AbortSignal;
|
|
90
|
+
/**
|
|
91
|
+
* Send a request through XMLHttpRequest so the caller can watch the bytes go
|
|
92
|
+
* up: fetch cannot report upload progress. Resolves to a Response, so the rest
|
|
93
|
+
* of the pipeline is the same either way. The timeout is a STALL timeout,
|
|
94
|
+
* re-armed by every progress event, so a large file on a slow link is never
|
|
95
|
+
* cut off while it is still moving, only once nothing has moved for `timeout`
|
|
96
|
+
* ms. Rejects the way fetch does: a TypeError for a network failure, an error
|
|
97
|
+
* named TimeoutError for a stall.
|
|
98
|
+
* @param {string} url
|
|
99
|
+
* @param {{method: string, headers: Record<string, string>, body: any}} request
|
|
100
|
+
* @param {{onUploadProgress?: (progress: {loaded: number, total: number | null}) => void, timeout?: number | null}} [options]
|
|
101
|
+
*/
|
|
102
|
+
export function xhrSend(url: string, { method, headers, body }: {
|
|
103
|
+
method: string;
|
|
104
|
+
headers: Record<string, string>;
|
|
105
|
+
body: any;
|
|
106
|
+
}, { onUploadProgress, timeout }?: {
|
|
107
|
+
onUploadProgress?: (progress: {
|
|
108
|
+
loaded: number;
|
|
109
|
+
total: number | null;
|
|
110
|
+
}) => void;
|
|
111
|
+
timeout?: number | null;
|
|
112
|
+
}): Promise<any>;
|
|
113
|
+
/**
|
|
114
|
+
* Everything a request is before it goes anywhere: the URL with its query
|
|
115
|
+
* params and the stamps strict mode, a per-call audit message and an open
|
|
116
|
+
* logical operation add, plus the transformed body. Shared by the wire path
|
|
117
|
+
* (`makeRequest`) and the batch path (`queueRequest`), so a queued op is
|
|
118
|
+
* exactly the request that would have gone out. Strict mode stamps every
|
|
119
|
+
* write, queued or not, and `stampedDocument` names the document it stamped
|
|
120
|
+
* for (null when it stamped nothing). `stampedGroup` is the open logical
|
|
121
|
+
* operation it joined, if any, for `makeRequest` to mark written once the
|
|
122
|
+
* server has taken the request.
|
|
123
|
+
*/
|
|
124
|
+
export function prepareRequest(client: any, method: any, path: any, options?: {}): {
|
|
125
|
+
url: string;
|
|
126
|
+
requestBody: any;
|
|
127
|
+
stampedDocument: any;
|
|
128
|
+
stampedVersion: any;
|
|
129
|
+
stampedGroup: any;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Whether a write joins the open logical operation (see prepareRequest).
|
|
133
|
+
*/
|
|
134
|
+
export function joinsOperation(client: any, method: any, options?: {}): boolean;
|
|
135
|
+
/**
|
|
136
|
+
* Whether a call made on the client goes out with an Idempotency-Key.
|
|
137
|
+
*/
|
|
138
|
+
export function takesIdempotencyKey(method: any, options?: {}): boolean;
|
|
139
|
+
/**
|
|
140
|
+
* `path` with its strict-mode `document-version` claim replaced by `version`,
|
|
141
|
+
* or dropped when `version` is unknown. For the later requests of a batch
|
|
142
|
+
* split past MAX_BATCH_OPS: each op was stamped with the version the document
|
|
143
|
+
* had when it was queued, and every request before it has since moved the
|
|
144
|
+
* document on.
|
|
145
|
+
*/
|
|
146
|
+
export function restampDocumentVersion(path: any, version: any): any;
|
|
147
|
+
/**
|
|
148
|
+
* One send of a request by fetch, under a fresh timeout signal: an
|
|
149
|
+
* AbortSignal.timeout fires once and stays aborted, so a retry needs its own.
|
|
150
|
+
* `timeoutMs` 0 or null sends with none.
|
|
151
|
+
*/
|
|
152
|
+
export function fetchWithTimeout(url: any, fetchOptions: any, timeoutMs: any): Promise<Response>;
|
|
153
|
+
/**
|
|
154
|
+
* Run `attempt` with the client's retries: a 503 is retried (unless
|
|
155
|
+
* `noBusyRetry`), and a keyed request whose answer was lost is sent again
|
|
156
|
+
* under its key (retryUnknown). Each retry is told to `onRetry` listeners.
|
|
157
|
+
*/
|
|
158
|
+
export function withRetries(client: any, attempt: any, { keyed, noBusyRetry }?: {
|
|
159
|
+
keyed?: boolean;
|
|
160
|
+
noBusyRetry?: boolean;
|
|
161
|
+
}): Promise<any>;
|
|
162
|
+
/**
|
|
163
|
+
* A 401 means the token is missing, expired or invalid. Fire the app's
|
|
164
|
+
* auth-error handler once per client (it discards the token and routes back
|
|
165
|
+
* to login). A 403 (authenticated but not permitted) deliberately does not.
|
|
166
|
+
*/
|
|
167
|
+
export function noteAuthError(client: any, error: any): void;
|
|
168
|
+
/**
|
|
169
|
+
* Generic request method handling all fetch logic.
|
|
170
|
+
*
|
|
171
|
+
* Options:
|
|
172
|
+
* body - Object body, run through transformRequest
|
|
173
|
+
* rawBody - Body value passed directly (no transform). Mutually exclusive with body.
|
|
174
|
+
* formData - If true, body is FormData; skip Content-Type header
|
|
175
|
+
* queryParams - Object of query param key/values to append
|
|
176
|
+
* noBatch - If true, throw when made on a batch. Only for calls a
|
|
177
|
+
* batch cannot carry at all (see the note at the
|
|
178
|
+
* top of this file); never for a read.
|
|
179
|
+
* outOfBand - If true, the call is a signal rather than a write of
|
|
180
|
+
* project data: made on a batch it still goes over the
|
|
181
|
+
* wire, and it never joins an open logical operation (see
|
|
182
|
+
* the note at the top of this file).
|
|
183
|
+
* noOperation - If true, the call never joins an open logical operation
|
|
184
|
+
* but still queues on a batch like a write. For a
|
|
185
|
+
* broadcast message, which is never audited.
|
|
186
|
+
* versioned - If false, strict mode does not stamp the call with the
|
|
187
|
+
* document version. For a write that carries its own
|
|
188
|
+
* precondition (a text edit with `base`).
|
|
189
|
+
* skipResponseTransform - Return raw parsed JSON (no transformResponse)
|
|
190
|
+
* noAuth - Skip Authorization header
|
|
191
|
+
* binaryResponse - Return arrayBuffer instead of JSON/text
|
|
192
|
+
* blobResponse - Return a Blob of the body, its `type` the response's
|
|
193
|
+
* Content-Type (a recording, whose reader needs both)
|
|
194
|
+
* onDownloadProgress - With blobResponse, called with `{ loaded, total }`
|
|
195
|
+
* (bytes; total null when unknown) as the body arrives
|
|
196
|
+
* noBusyRetry - If true, a 503 is not retried. For a query: core
|
|
197
|
+
* answers 503 when its queue of large queries stays full
|
|
198
|
+
* until the query's deadline, and asking again at once
|
|
199
|
+
* only joins the same queue.
|
|
200
|
+
* timeout - Per-request timeout in ms overriding client.timeout
|
|
201
|
+
* for this call (0/null disables). Used for known-long
|
|
202
|
+
* ops like project delete.
|
|
203
|
+
* onUploadProgress - Called with `{ loaded, total }` (bytes; total null when
|
|
204
|
+
* unknown) as the request body goes up. In a browser the
|
|
205
|
+
* request then travels by XMLHttpRequest (see xhrSend),
|
|
206
|
+
* where the timeout only fires when the upload stalls;
|
|
207
|
+
* elsewhere the callback is ignored and fetch is used.
|
|
208
|
+
*/
|
|
209
|
+
/**
|
|
210
|
+
* What a write about to go out needs to tell the lock keepers it renewed:
|
|
211
|
+
* each held document's keeper, its version now and the send time. Empty when
|
|
212
|
+
* no `documents.locked()` block on `client` keeps a lock. The server renews a
|
|
213
|
+
* live lock on every write its holder makes to the document, so a write that
|
|
214
|
+
* moved the held document's version renewed it (`writesLanded`).
|
|
215
|
+
* @private
|
|
216
|
+
*/
|
|
217
|
+
export function writesStarted(client: any): any[][];
|
|
218
|
+
/**
|
|
219
|
+
* The write `started` was read before has been taken, and its answer's
|
|
220
|
+
* versions learned. A held document whose version it moved was written by
|
|
221
|
+
* this run. A replayed answer was not taken again, and renewed nothing.
|
|
222
|
+
* @private
|
|
223
|
+
*/
|
|
224
|
+
export function writesLanded(client: any, started: any, replayed?: boolean): void;
|
|
225
|
+
export function makeRequest(client: any, method: any, path: any, options?: {}): Promise<any>;
|
|
226
|
+
export const DEFAULT_TIMEOUT_MS: 30000;
|
|
227
|
+
export const DEFAULT_BATCH_TIMEOUT_MS: 180000;
|
|
228
|
+
export const DEFAULT_QUERY_TIMEOUT_MS: 35000;
|
|
229
|
+
export const BUSY_RETRIES: 4;
|
|
230
|
+
export const BUSY_BACKOFF_MS: 250;
|
|
231
|
+
export const IDEMPOTENCY_HEADER: "Idempotency-Key";
|
|
232
|
+
export const REPLAYED_HEADER: "Idempotent-Replayed";
|
|
233
|
+
export const UNKNOWN_RETRY_DELAYS_MS: number[];
|