@larc-iu/plaid-client 0.2.0-alpha.22 → 0.2.0-alpha.23
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 +35 -0
- package/index.d.ts +12 -1
- package/package.json +1 -1
- package/src/documentLock.js +2 -1
- package/src/ids.js +20 -0
- package/src/index.js +29 -11
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @larc-iu/plaid-client
|
|
2
|
+
|
|
3
|
+
The JavaScript client for the [Plaid](https://larc-iu.github.io/plaid/) annotation API.
|
|
4
|
+
It has no dependencies and runs in browsers and in Node.
|
|
5
|
+
|
|
6
|
+
## From npm
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm install @larc-iu/plaid-client@alpha
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
import { PlaidClient } from "@larc-iu/plaid-client";
|
|
14
|
+
|
|
15
|
+
const client = await PlaidClient.login("http://localhost:8080", "user@example.com", "password");
|
|
16
|
+
console.log(await client.projects.list());
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## From a Plaid server
|
|
20
|
+
|
|
21
|
+
Every Plaid server serves the client that matches its own version at `/client/plaid-client.js`, with its type declarations at `/client/plaid-client.d.ts`.
|
|
22
|
+
A page served by Plaid, for example from the folder set as `static_resources_path` in its `config.toml`, can import it without bundling a copy:
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<script type="module">
|
|
26
|
+
import { PlaidClient } from "/client/plaid-client.js";
|
|
27
|
+
const client = await PlaidClient.login(location.origin, "user@example.com", "password");
|
|
28
|
+
console.log(await client.projects.list());
|
|
29
|
+
</script>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Documentation
|
|
33
|
+
|
|
34
|
+
- [Manual](https://larc-iu.github.io/plaid/manual.html), which covers the API and its concepts.
|
|
35
|
+
- [API reference](https://larc-iu.github.io/plaid/api-js.html) for this client.
|
package/index.d.ts
CHANGED
|
@@ -422,6 +422,8 @@ interface UsersBundle {
|
|
|
422
422
|
interface UserDataEntry {
|
|
423
423
|
key: string;
|
|
424
424
|
updatedAt: string;
|
|
425
|
+
/** Raised by one on every write of the entry. A new entry is 1. */
|
|
426
|
+
version: number;
|
|
425
427
|
value?: any;
|
|
426
428
|
}
|
|
427
429
|
|
|
@@ -446,11 +448,18 @@ interface UserDataBundle {
|
|
|
446
448
|
opts?: UserDataOptions & { pageSize?: number },
|
|
447
449
|
): AsyncGenerator<UserDataEntry[]>;
|
|
448
450
|
get(userId: string, key: string): Promise<UserDataEntry & { value: any }>;
|
|
451
|
+
/**
|
|
452
|
+
* `version`: write only when the entry is still at this version (0: only
|
|
453
|
+
* when there is none). Refused with 409, `error: "version-mismatch"` and the
|
|
454
|
+
* stored `version` and `updatedAt` on the error's `responseData`, when
|
|
455
|
+
* another write landed since.
|
|
456
|
+
*/
|
|
449
457
|
put(
|
|
450
458
|
userId: string,
|
|
451
459
|
key: string,
|
|
452
460
|
value: any,
|
|
453
|
-
|
|
461
|
+
opts?: { version?: number },
|
|
462
|
+
): Promise<{ key: string; updatedAt: string; version: number }>;
|
|
454
463
|
delete(userId: string, key: string): Promise<any>;
|
|
455
464
|
}
|
|
456
465
|
|
|
@@ -1904,6 +1913,8 @@ export interface KeySeed {
|
|
|
1904
1913
|
}
|
|
1905
1914
|
/** A fresh UUIDv7, for the id of something a client creates. */
|
|
1906
1915
|
export function uuidv7(): string;
|
|
1916
|
+
/** A fresh random UUID (version 4), from crypto.getRandomValues, so it works on any page. */
|
|
1917
|
+
export function uuidv4(): string;
|
|
1907
1918
|
/** Whether a write's answer was replayed from its Idempotency-Key's first send (`Idempotent-Replayed: true`), so it wrote nothing new. Marked on an object or list answer as a non-enumerable `replayed: true`. */
|
|
1908
1919
|
export function wasReplayed(answer: unknown): boolean;
|
|
1909
1920
|
export function createdId(result: any): string | undefined;
|
package/package.json
CHANGED
package/src/documentLock.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isUnknownOutcome, retryUnknown } from "./http.js";
|
|
2
|
+
import { uuidv4 } from "./ids.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Keeping a document lock alive for as long as a `locked()` block runs.
|
|
@@ -216,7 +217,7 @@ export class DocumentLock {
|
|
|
216
217
|
export const LOCK_ACQUIRE_RETRY_MS = 500;
|
|
217
218
|
|
|
218
219
|
function mintLockId() {
|
|
219
|
-
return
|
|
220
|
+
return uuidv4();
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
/**
|
package/src/ids.js
CHANGED
|
@@ -52,3 +52,23 @@ export function uuidv7() {
|
|
|
52
52
|
}
|
|
53
53
|
return s;
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A random UUIDv4, for ids that need no order (an operation group, a lock
|
|
58
|
+
* holder). Built on `crypto.getRandomValues`, since `crypto.randomUUID`
|
|
59
|
+
* exists only in a secure context and a page served over plain HTTP from
|
|
60
|
+
* another host than localhost is not one.
|
|
61
|
+
* @returns {string}
|
|
62
|
+
*/
|
|
63
|
+
export function uuidv4() {
|
|
64
|
+
const b = new Uint8Array(16);
|
|
65
|
+
crypto.getRandomValues(b);
|
|
66
|
+
b[6] = (b[6] & 0x0f) | 0x40;
|
|
67
|
+
b[8] = (b[8] & 0x3f) | 0x80;
|
|
68
|
+
let s = "";
|
|
69
|
+
for (let i = 0; i < 16; i++) {
|
|
70
|
+
s += hex[b[i]];
|
|
71
|
+
if (i === 3 || i === 5 || i === 7 || i === 9) s += "-";
|
|
72
|
+
}
|
|
73
|
+
return s;
|
|
74
|
+
}
|
package/src/index.js
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
DEFAULT_BATCH_TIMEOUT_MS,
|
|
23
23
|
queryTimeout,
|
|
24
24
|
} from "./http.js";
|
|
25
|
-
import { uuidv7 } from "./ids.js";
|
|
25
|
+
import { uuidv4, uuidv7 } from "./ids.js";
|
|
26
26
|
import { constraintsBody } from "./constraints.js";
|
|
27
27
|
import { listAll, listPage, iterPages } from "./pagination.js";
|
|
28
28
|
import { recorderFor } from "./events.js";
|
|
@@ -1474,7 +1474,7 @@ class PlaidClient {
|
|
|
1474
1474
|
query: { prefix, pattern, "include-values": includeValues },
|
|
1475
1475
|
}),
|
|
1476
1476
|
/**
|
|
1477
|
-
* Read one private data entry ({key, updatedAt, value}); 404 if absent.
|
|
1477
|
+
* Read one private data entry ({key, updatedAt, version, value}); 404 if absent.
|
|
1478
1478
|
* @param {string} userId
|
|
1479
1479
|
* @param {string} key
|
|
1480
1480
|
*/
|
|
@@ -1492,17 +1492,28 @@ class PlaidClient {
|
|
|
1492
1492
|
* value whose keys are camelCase round-trips unchanged while one keyed
|
|
1493
1493
|
* by arbitrary strings does not. Put such a map under a `metadata` key,
|
|
1494
1494
|
* which both clients pass through untouched.
|
|
1495
|
+
*
|
|
1496
|
+
* Every write raises the entry's `version` by one, and reads answer it.
|
|
1497
|
+
* With `version`, the write lands only when the entry is still at that
|
|
1498
|
+
* version (0: only when there is no entry), and is otherwise refused with
|
|
1499
|
+
* 409, `error: "version-mismatch"`, and the stored `version` and
|
|
1500
|
+
* `updatedAt` on the error's `responseData`: read it again and make the
|
|
1501
|
+
* change on what is there.
|
|
1495
1502
|
* @param {string} userId
|
|
1496
1503
|
* @param {string} key
|
|
1497
1504
|
* @param {*} value
|
|
1505
|
+
* @param {object} [opts]
|
|
1506
|
+
* @param {number} [opts.version] - The version this write was made from
|
|
1507
|
+
* @returns {Promise<{key: string, updatedAt: string, version: number}>}
|
|
1498
1508
|
*/
|
|
1499
|
-
put: (userId, key, value) =>
|
|
1509
|
+
put: (userId, key, value, { version } = {}) =>
|
|
1500
1510
|
this._request(
|
|
1501
1511
|
"PUT",
|
|
1502
1512
|
`/api/v1/users/${userId}/data/${encodeURIComponent(key)}`,
|
|
1503
1513
|
{
|
|
1504
1514
|
body: value,
|
|
1505
1515
|
noBatch: true,
|
|
1516
|
+
queryParams: { version },
|
|
1506
1517
|
},
|
|
1507
1518
|
),
|
|
1508
1519
|
/**
|
|
@@ -2148,9 +2159,9 @@ class PlaidClient {
|
|
|
2148
2159
|
* Upload a media file for a document. Uses Apache Tika for content validation.
|
|
2149
2160
|
* A document that already has a recording refuses it with a 409
|
|
2150
2161
|
* (`media-exists`, and the current `media-url`): delete that one first.
|
|
2151
|
-
* A WAV whose samples are not PCM
|
|
2152
|
-
* and the rest), which browsers cannot play,
|
|
2153
|
-
* whose `error` names its coding.
|
|
2162
|
+
* A WAV whose samples are not PCM, 32-bit float, A-law or mu-law (IMA or
|
|
2163
|
+
* MS ADPCM, GSM, 64-bit float and the rest), which browsers cannot play,
|
|
2164
|
+
* is refused with a 415 whose `error` names its coding.
|
|
2154
2165
|
* @param {string} documentId - The document ID
|
|
2155
2166
|
* @param {File} file - The file to upload
|
|
2156
2167
|
* @param {string} [auditMessage] - Custom audit-log message for this write
|
|
@@ -2216,7 +2227,10 @@ class PlaidClient {
|
|
|
2216
2227
|
}),
|
|
2217
2228
|
/**
|
|
2218
2229
|
* Get audit log for a document. Transparently follows pagination cursors
|
|
2219
|
-
* and returns the full flat array.
|
|
2230
|
+
* and returns the full flat array. Each entry, and each of its `ops`,
|
|
2231
|
+
* says what kind of credential made it as `credential`: `login`,
|
|
2232
|
+
* `named-token`, `service` or `delegated` (absent on older operations),
|
|
2233
|
+
* beside `apiToken` (the named token's id and name) when there was one.
|
|
2220
2234
|
* @param {string} documentId - The document ID
|
|
2221
2235
|
* @param {string} [startTime] - Start of time range
|
|
2222
2236
|
* @param {string} [endTime] - End of time range
|
|
@@ -2452,7 +2466,10 @@ class PlaidClient {
|
|
|
2452
2466
|
),
|
|
2453
2467
|
/**
|
|
2454
2468
|
* Get audit log for a project. Transparently follows pagination cursors
|
|
2455
|
-
* and returns the full flat array.
|
|
2469
|
+
* and returns the full flat array. Each entry, and each of its `ops`,
|
|
2470
|
+
* says what kind of credential made it as `credential`: `login`,
|
|
2471
|
+
* `named-token`, `service` or `delegated` (absent on older operations),
|
|
2472
|
+
* beside `apiToken` (the named token's id and name) when there was one.
|
|
2456
2473
|
* @param {string} projectId - The project ID
|
|
2457
2474
|
* @param {string} [startTime] - Start of time range
|
|
2458
2475
|
* @param {string} [endTime] - End of time range
|
|
@@ -3809,7 +3826,7 @@ class PlaidClient {
|
|
|
3809
3826
|
return open.id;
|
|
3810
3827
|
}
|
|
3811
3828
|
this.operationGroup = {
|
|
3812
|
-
id: groupId ? String(groupId) :
|
|
3829
|
+
id: groupId ? String(groupId) : uuidv4(),
|
|
3813
3830
|
message: message == null ? null : String(message),
|
|
3814
3831
|
kind: kind == null ? null : String(kind),
|
|
3815
3832
|
ref: ref == null ? null : String(ref),
|
|
@@ -4412,7 +4429,8 @@ export {
|
|
|
4412
4429
|
// result. See ./created.js.
|
|
4413
4430
|
export { createdId, createdIds } from "./created.js";
|
|
4414
4431
|
// UUIDv7 ids for what a client creates. See ./ids.js and the manual,
|
|
4415
|
-
// "Retrying a write".
|
|
4416
|
-
|
|
4432
|
+
// "Retrying a write". uuidv4 works where crypto.randomUUID does not (a page
|
|
4433
|
+
// served over plain HTTP from any host but localhost).
|
|
4434
|
+
export { uuidv4, uuidv7 } from "./ids.js";
|
|
4417
4435
|
export { wasReplayed } from "./replayed.js";
|
|
4418
4436
|
export { MAX_BATCH_OPS };
|