@larc-iu/plaid-client 0.2.0-alpha.22 → 0.2.0-alpha.26
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 +11 -5
- package/src/batchRef.d.ts +30 -0
- package/src/codepoint.d.ts +94 -0
- package/src/codepoint.js +131 -1
- package/src/constraints.d.ts +77 -0
- package/src/constraints.js +26 -1
- package/src/created.d.ts +2 -0
- package/src/documentLock.d.ts +158 -0
- package/src/documentLock.js +29 -4
- package/src/events.d.ts +60 -0
- package/src/events.js +7 -3
- package/src/http.d.ts +212 -0
- package/src/http.js +7 -0
- package/src/ids.d.ts +26 -0
- package/src/ids.js +42 -0
- package/src/index.d.ts +3814 -0
- package/src/index.js +962 -704
- package/src/metadataOps.d.ts +15 -0
- package/src/metadataOps.js +13 -8
- package/src/pagination.d.ts +49 -0
- package/src/pagination.js +2 -19
- package/src/provenance.d.ts +200 -0
- package/src/provenance.js +34 -6
- package/src/replayed.d.ts +3 -0
- package/src/replayed.js +5 -1
- package/src/requester.d.ts +60 -0
- package/src/requester.js +21 -3
- package/src/roles.d.ts +65 -0
- package/src/roles.js +3 -2
- package/src/serviceSchema.d.ts +331 -0
- package/src/serviceSchema.js +75 -9
- package/src/services.d.ts +302 -0
- package/src/services.js +67 -10
- package/src/sse.d.ts +30 -0
- package/src/sse.js +11 -0
- package/src/textEdits.d.ts +59 -0
- package/src/textEdits.js +26 -1
- package/src/transforms.d.ts +25 -0
- package/index.d.ts +0 -2015
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function isReservedMetadataKey(key: string): boolean;
|
|
2
|
+
export function metadataOps(fragment: object | null | undefined): MetadataOp[];
|
|
3
|
+
export function applyMetadataOps(metadata: object | null | undefined, ops: MetadataOp[]): Record<string, any>;
|
|
4
|
+
export function mergeMetadata(metadata: object | null | undefined, fragment: object | null | undefined): Record<string, any>;
|
|
5
|
+
/**
|
|
6
|
+
* One metadata edit: the body of a metadata PATCH is a list of these.
|
|
7
|
+
*/
|
|
8
|
+
export type MetadataOp = {
|
|
9
|
+
op: "set";
|
|
10
|
+
path: string[];
|
|
11
|
+
value: any;
|
|
12
|
+
} | {
|
|
13
|
+
op: "delete";
|
|
14
|
+
path: string[];
|
|
15
|
+
};
|
package/src/metadataOps.js
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
import { PLAID_NAMESPACE } from './roles.js';
|
|
9
9
|
import { PROVENANCE_KEYS } from './provenance.js';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* One metadata edit: the body of a metadata PATCH is a list of these.
|
|
13
|
+
* @typedef {{op: "set", path: string[], value: any} | {op: "delete", path: string[]}} MetadataOp
|
|
14
|
+
*/
|
|
15
|
+
|
|
11
16
|
const RESERVED_KEYS = new Set([PLAID_NAMESPACE, ...PROVENANCE_KEYS]);
|
|
12
17
|
|
|
13
18
|
/**
|
|
@@ -49,8 +54,8 @@ const isObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v)
|
|
|
49
54
|
* The ops that set each top-level key of `fragment`, deleting a key whose
|
|
50
55
|
* value is null. This is how a provenance fragment (verifyOnEdit,
|
|
51
56
|
* contributeOnEdit, ...) is sent as a patch.
|
|
52
|
-
* @param {
|
|
53
|
-
* @returns {
|
|
57
|
+
* @param {object|null|undefined} fragment
|
|
58
|
+
* @returns {MetadataOp[]}
|
|
54
59
|
*/
|
|
55
60
|
export const metadataOps = (fragment) =>
|
|
56
61
|
Object.entries(fragment || {}).map(([k, v]) =>
|
|
@@ -84,9 +89,9 @@ const applyOp = (node, path, depth, o) => {
|
|
|
84
89
|
* set or delete, a path through a non-object, or a first key that is blank,
|
|
85
90
|
* over 200 characters or holds a control character. The server's caps on
|
|
86
91
|
* depth, key count, string length and size are not checked here.
|
|
87
|
-
* @param {
|
|
88
|
-
* @param {
|
|
89
|
-
* @returns {
|
|
92
|
+
* @param {object|null|undefined} metadata
|
|
93
|
+
* @param {MetadataOp[]} ops
|
|
94
|
+
* @returns {Record<string, any>}
|
|
90
95
|
*/
|
|
91
96
|
export const applyMetadataOps = (metadata, ops) =>
|
|
92
97
|
(ops || []).reduce((m, o) => {
|
|
@@ -111,9 +116,9 @@ export const applyMetadataOps = (metadata, ops) =>
|
|
|
111
116
|
* sending those ops, refused where the server would refuse them. How an app
|
|
112
117
|
* mirrors a provenance stamp (verifyOnEdit, contributeOnEdit, ...) on the row
|
|
113
118
|
* it shows. Returns a new object.
|
|
114
|
-
* @param {
|
|
115
|
-
* @param {
|
|
116
|
-
* @returns {
|
|
119
|
+
* @param {object|null|undefined} metadata
|
|
120
|
+
* @param {object|null|undefined} fragment
|
|
121
|
+
* @returns {Record<string, any>}
|
|
117
122
|
*/
|
|
118
123
|
export const mergeMetadata = (metadata, fragment) =>
|
|
119
124
|
applyMetadataOps(metadata, metadataOps(fragment));
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch every page and return the full flat array of entries, transparently
|
|
3
|
+
* following `nextCursor` until it is null. This is what `.list()` calls.
|
|
4
|
+
*
|
|
5
|
+
* @param {object} client - PlaidClient instance
|
|
6
|
+
* @param {string} path - API path, e.g. '/api/v1/projects'
|
|
7
|
+
* @param {object} [opts]
|
|
8
|
+
* @param {number} [opts.pageSize=1000] - Per-request page size (limit)
|
|
9
|
+
* @param {object} [opts.query={}] - Extra query params (e.g. { 'as-of': asOf })
|
|
10
|
+
* @returns {Promise<Array>} The concatenated entries across all pages
|
|
11
|
+
*/
|
|
12
|
+
export function listAll(client: object, path: string, { pageSize, query }?: {
|
|
13
|
+
pageSize?: number;
|
|
14
|
+
query?: object;
|
|
15
|
+
}): Promise<any[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Fetch a single page and return the raw envelope.
|
|
18
|
+
*
|
|
19
|
+
* @param {object} client - PlaidClient instance
|
|
20
|
+
* @param {string} path - API path
|
|
21
|
+
* @param {object} [opts]
|
|
22
|
+
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
23
|
+
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
24
|
+
* @param {object} [opts.query={}] - Extra query params
|
|
25
|
+
* @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
|
|
26
|
+
*/
|
|
27
|
+
export function listPage(client: object, path: string, { limit, cursor, query }?: {
|
|
28
|
+
limit?: number;
|
|
29
|
+
cursor?: string;
|
|
30
|
+
query?: object;
|
|
31
|
+
}): Promise<{
|
|
32
|
+
entries: any[];
|
|
33
|
+
nextCursor: (string | null);
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* Async generator yielding each page's entries array in turn, following
|
|
37
|
+
* `nextCursor` until it is null.
|
|
38
|
+
*
|
|
39
|
+
* @param {object} client - PlaidClient instance
|
|
40
|
+
* @param {string} path - API path
|
|
41
|
+
* @param {object} [opts]
|
|
42
|
+
* @param {number} [opts.pageSize=1000] - Per-request page size (limit)
|
|
43
|
+
* @param {object} [opts.query={}] - Extra query params
|
|
44
|
+
* @yields {Array} The entries array for each page
|
|
45
|
+
*/
|
|
46
|
+
export function iterPages(client: object, path: string, { pageSize, query }?: {
|
|
47
|
+
pageSize?: number;
|
|
48
|
+
query?: object;
|
|
49
|
+
}): AsyncGenerator<any, void, unknown>;
|
package/src/pagination.js
CHANGED
|
@@ -39,8 +39,7 @@ function entriesOf(response, path) {
|
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* Fetch every page and return the full flat array of entries, transparently
|
|
42
|
-
* following `nextCursor` until it is null. This is what `.list()` calls
|
|
43
|
-
* stays backward compatible with the pre-pagination (bare-array) contract.
|
|
42
|
+
* following `nextCursor` until it is null. This is what `.list()` calls.
|
|
44
43
|
*
|
|
45
44
|
* @param {object} client - PlaidClient instance
|
|
46
45
|
* @param {string} path - API path, e.g. '/api/v1/projects'
|
|
@@ -51,23 +50,7 @@ function entriesOf(response, path) {
|
|
|
51
50
|
*/
|
|
52
51
|
export async function listAll(client, path, { pageSize = 1000, query = {} } = {}) {
|
|
53
52
|
const all = [];
|
|
54
|
-
|
|
55
|
-
let prevCursor;
|
|
56
|
-
do {
|
|
57
|
-
const response = await client._request('GET', path, {
|
|
58
|
-
queryParams: buildQueryParams(query, pageSize, cursor),
|
|
59
|
-
});
|
|
60
|
-
all.push(...entriesOf(response, path));
|
|
61
|
-
prevCursor = cursor;
|
|
62
|
-
cursor = response.nextCursor;
|
|
63
|
-
// Guard against a buggy server/proxy that returns a constant non-null
|
|
64
|
-
// cursor, which would otherwise loop forever.
|
|
65
|
-
if (cursor !== null && cursor !== undefined && cursor === prevCursor) {
|
|
66
|
-
throw new Error(
|
|
67
|
-
'Pagination cursor did not advance; aborting to avoid an infinite loop.',
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
} while (cursor !== null && cursor !== undefined);
|
|
53
|
+
for await (const entries of iterPages(client, path, { pageSize, query })) all.push(...entries);
|
|
71
54
|
return all;
|
|
72
55
|
}
|
|
73
56
|
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provenance: the cross-app convention for distinguishing machine-provided
|
|
3
|
+
* from human-labeled information, expressed as flat metadata keys on
|
|
4
|
+
* annotation entities (spans, relations, vocab links, optionally tokens).
|
|
5
|
+
* Flat scalar keys — the query engine matches flat metadata well, nested
|
|
6
|
+
* objects poorly.
|
|
7
|
+
*
|
|
8
|
+
* Two axes. ORIGIN is the `prov` key: absent means a trusted person (a
|
|
9
|
+
* verifier) made it; 'inferred' means an algorithm or service did;
|
|
10
|
+
* 'contributed' means a person whose work the project reviews (a
|
|
11
|
+
* contributor) did. TRUST is `provConfirmed`: true once a verifier vouched
|
|
12
|
+
* for the value. Together they give four states:
|
|
13
|
+
*
|
|
14
|
+
* human — no prov keys; a verifier made it.
|
|
15
|
+
* machine — { prov: 'inferred', provSource: '<producer>' };
|
|
16
|
+
* an algorithm/service made it, nobody has vouched.
|
|
17
|
+
* contributed — { prov: 'contributed', provSource: 'user:<id>' };
|
|
18
|
+
* a contributor made it, no verifier has vouched.
|
|
19
|
+
* verified — either origin + { provConfirmed: true }; a verifier
|
|
20
|
+
* confirmed (or edited) it. prov/provSource stay so
|
|
21
|
+
* the origin remains traceable.
|
|
22
|
+
*
|
|
23
|
+
* The write contract every machine writer must follow:
|
|
24
|
+
* 1. Machine writers may freely replace MACHINE (unverified) material.
|
|
25
|
+
* 2. Machine writers must never modify or delete human, contributed or
|
|
26
|
+
* verified material unless explicitly told to overwrite (an explicit,
|
|
27
|
+
* per-run, user-facing opt-in — for services, a declared boolean
|
|
28
|
+
* `overwrite` parameter). A contributor's work is a person's work.
|
|
29
|
+
* 3. A verifier's edit of machine or contributed material verifies it:
|
|
30
|
+
* the edit also stamps { provConfirmed: true } (see verifyOnEdit).
|
|
31
|
+
* A contributor's edit of anything marks it contributed: the edit
|
|
32
|
+
* merges stampContributed(userId) and drops any earlier confirmation
|
|
33
|
+
* (see contributeOnEdit). Who is a contributor is the PROJECT's call,
|
|
34
|
+
* recorded once for every app (see "Review" below); a service running
|
|
35
|
+
* as a contributor should stamp likewise.
|
|
36
|
+
*
|
|
37
|
+
* REVIEW: which people are contributors. A project records it under the
|
|
38
|
+
* reserved `plaid` config namespace, so every app that writes on a person's
|
|
39
|
+
* behalf reads the same answer:
|
|
40
|
+
*
|
|
41
|
+
* config.plaid.review = { users: [userId, ...], roles: ['writer', ...] }
|
|
42
|
+
*
|
|
43
|
+
* `users` names people whose work is reviewed whatever their role; `roles`
|
|
44
|
+
* names whole project roles ('reader' | 'writer' | 'maintainer'; an admin
|
|
45
|
+
* without an explicit role counts as a maintainer). Either may be absent.
|
|
46
|
+
* Nothing here grants or denies access: the ACL lists stay the permission
|
|
47
|
+
* model, and this only says whose work needs a verifier's look. Apps expose
|
|
48
|
+
* it as a per-member mark on their access screens (readReview, isReviewed,
|
|
49
|
+
* withReviewedUser) and derive the writer's policy from it (writerPolicy).
|
|
50
|
+
*
|
|
51
|
+
* Producer naming: 'service:<serviceId>' for services (use serviceSource),
|
|
52
|
+
* 'rule:<name>' for built-in rule algorithms, 'user:<userId>' for a
|
|
53
|
+
* contributor (use userSource), app-specific ids like 'gloss:doc-frequency'
|
|
54
|
+
* or 'flex-import' otherwise.
|
|
55
|
+
*
|
|
56
|
+
* PREDICTION EXTRAS. A producer may also record how confident it was and
|
|
57
|
+
* what else it considered, in two reserved slots split along the
|
|
58
|
+
* queryability line:
|
|
59
|
+
* - provProb: ONE flat number in [0, 1] — the producer's probability for
|
|
60
|
+
* the value it chose. Flat scalars are what the query engine filters
|
|
61
|
+
* and orders on, so "review the least-confident machine output first"
|
|
62
|
+
* is an ordinary query. Omit it unless you can honestly produce a
|
|
63
|
+
* probability (a raw logprob is NOT one — put it in provDetail).
|
|
64
|
+
* - provDetail: ONE open map for everything else — top-k alternatives or
|
|
65
|
+
* distributions, model name/version, raw scores. Deliberately nested
|
|
66
|
+
* (not queryable); keep it small (top-k, not whole-vocabulary dumps).
|
|
67
|
+
* Both describe the machine's ORIGINAL prediction. They are kept after
|
|
68
|
+
* human edits (history is valuable), so a consumer must not present
|
|
69
|
+
* provProb as confidence in the CURRENT value once the entity is verified
|
|
70
|
+
* (provConfirmed is exactly the flag to check).
|
|
71
|
+
*
|
|
72
|
+
* Recommended provDetail keys, so consumers (review dashboards, ranked
|
|
73
|
+
* alternatives) can read any producer's output:
|
|
74
|
+
* - value (spans) / form (morpheme tokens): the value the producer
|
|
75
|
+
* wrote, mirroring the entity's own field. The entity may be edited
|
|
76
|
+
* later; this copy is what makes "accepted as-is" vs "corrected"
|
|
77
|
+
* answerable once the entity is verified.
|
|
78
|
+
* - valueProbs / formProbs: a top-k {label: probability} map over the
|
|
79
|
+
* alternatives the producer considered, the chosen label included
|
|
80
|
+
* (provProb, when given, is that label's entry duplicated flat).
|
|
81
|
+
* - model: what made the prediction (a model name, or a tool and its
|
|
82
|
+
* release such as 'stanza==1.11.0'), and version: which version of the
|
|
83
|
+
* writer ran ('<release>+<8 hex>', see the manual, "Provenance"). Every
|
|
84
|
+
* machine writer records both, one of rules alone only version.
|
|
85
|
+
* - anything else (language, adapter, raw scores).
|
|
86
|
+
*/
|
|
87
|
+
/** The flat metadata keys of the provenance convention. */
|
|
88
|
+
export const PROV: Readonly<{
|
|
89
|
+
/** 'inferred' when produced by an algorithm/service; absent = human. */
|
|
90
|
+
key: "prov";
|
|
91
|
+
/** Which producer, e.g. 'service:<id>', 'rule:precedent-or-unique'. */
|
|
92
|
+
sourceKey: "provSource";
|
|
93
|
+
/** true once a human confirmed (or edited) the inferred value. */
|
|
94
|
+
confirmedKey: "provConfirmed";
|
|
95
|
+
/** Producer's probability ([0,1]) for the chosen value. Flat = queryable. */
|
|
96
|
+
probKey: "provProb";
|
|
97
|
+
/** Open map of producer extras (alternatives, model version, raw scores). */
|
|
98
|
+
detailKey: "provDetail";
|
|
99
|
+
/** `prov` for machine-made material. Any value but CONTRIBUTED reads as machine. */
|
|
100
|
+
INFERRED: "inferred";
|
|
101
|
+
/** `prov` for a contributor's material. */
|
|
102
|
+
CONTRIBUTED: "contributed";
|
|
103
|
+
}>;
|
|
104
|
+
/**
|
|
105
|
+
* Every provenance key, for a caller that has to name them as a set rather
|
|
106
|
+
* than read one: what a token layer declares under
|
|
107
|
+
* `config.plaid.preserveOnSplit` so a token born of a split inherits the
|
|
108
|
+
* origin of the token it came from, and what a reshape carries by hand.
|
|
109
|
+
*
|
|
110
|
+
* Provenance is the one metadata family Plaid itself owns. The user cannot
|
|
111
|
+
* maintain it, and in a shared project usually cannot even see it, since it
|
|
112
|
+
* may belong to an app they are not using. Everything else on an entity is
|
|
113
|
+
* the user's own content and no structural operation should touch it.
|
|
114
|
+
*/
|
|
115
|
+
export const PROVENANCE_KEYS: readonly ("prov" | "provSource" | "provConfirmed" | "provProb" | "provDetail")[];
|
|
116
|
+
/** The four provenance states returned by provState. */
|
|
117
|
+
export const PROV_STATES: Readonly<{
|
|
118
|
+
HUMAN: "human";
|
|
119
|
+
MACHINE: "machine";
|
|
120
|
+
CONTRIBUTED: "contributed";
|
|
121
|
+
VERIFIED: "verified";
|
|
122
|
+
}>;
|
|
123
|
+
export function stampInferred(source: string, { prob, detail }?: {
|
|
124
|
+
prob?: number;
|
|
125
|
+
detail?: any;
|
|
126
|
+
}): {
|
|
127
|
+
provDetail?: any;
|
|
128
|
+
provProb?: number;
|
|
129
|
+
prov: "inferred";
|
|
130
|
+
provSource: string;
|
|
131
|
+
};
|
|
132
|
+
export function confirmedInferred(source: string, extras?: {
|
|
133
|
+
prob?: number;
|
|
134
|
+
detail?: Record<string, any>;
|
|
135
|
+
}): {
|
|
136
|
+
provConfirmed: boolean;
|
|
137
|
+
provDetail?: any;
|
|
138
|
+
provProb?: number;
|
|
139
|
+
prov: "inferred";
|
|
140
|
+
provSource: string;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* The metadata fragment that verifies machine-made material:
|
|
144
|
+
* { provConfirmed: true }. Merge (PATCH) it over existing metadata so
|
|
145
|
+
* prov/provSource/provProb/provDetail survive. This is what verifyOnEdit
|
|
146
|
+
* returns for machine-unverified material; use it directly for explicit
|
|
147
|
+
* confirmation gestures (a "confirm" button, confirm-on-touch) where the
|
|
148
|
+
* caller has already checked isMachine.
|
|
149
|
+
*/
|
|
150
|
+
export const PROV_CONFIRMED: Readonly<{
|
|
151
|
+
provConfirmed: true;
|
|
152
|
+
}>;
|
|
153
|
+
export function stampContributed(userId: string): {
|
|
154
|
+
prov: "contributed";
|
|
155
|
+
provSource: string;
|
|
156
|
+
};
|
|
157
|
+
export function provState(metadata: object | null | undefined): "human" | "machine" | "contributed" | "verified";
|
|
158
|
+
export function provOrigin(metadata: object | null | undefined): null | "inferred" | "contributed";
|
|
159
|
+
export function isMachine(metadata: object | null | undefined): boolean;
|
|
160
|
+
export function isProtected(metadata: object | null | undefined): boolean;
|
|
161
|
+
export function needsReview(metadata: object | null | undefined): boolean;
|
|
162
|
+
export function verifyOnEdit(metadata: object | null | undefined): Readonly<{
|
|
163
|
+
provConfirmed: true;
|
|
164
|
+
}>;
|
|
165
|
+
export function contributeOnEdit(metadata: object | null | undefined, userId: string): {
|
|
166
|
+
prov: "contributed";
|
|
167
|
+
provSource: string;
|
|
168
|
+
provConfirmed: null;
|
|
169
|
+
};
|
|
170
|
+
export function serviceSource(serviceId: string): string;
|
|
171
|
+
export function userSource(userId: string): string;
|
|
172
|
+
/** The config key, under the `plaid` namespace, holding the review lists. */
|
|
173
|
+
export const REVIEW_KEY: "review";
|
|
174
|
+
export function readReview(config?: object | null): {
|
|
175
|
+
users: string[];
|
|
176
|
+
roles: string[];
|
|
177
|
+
};
|
|
178
|
+
export function projectRole(project: object, userId: string, { isAdmin }?: {
|
|
179
|
+
isAdmin?: boolean;
|
|
180
|
+
}): "maintainer" | "writer" | "reader";
|
|
181
|
+
export function isReviewed(project: object | null | undefined, userId: string | null | undefined, { isAdmin }?: {
|
|
182
|
+
isAdmin?: boolean;
|
|
183
|
+
}): boolean;
|
|
184
|
+
export function withReviewedUser(review: {
|
|
185
|
+
users?: string[];
|
|
186
|
+
roles?: string[];
|
|
187
|
+
} | null | undefined, userId: string, reviewed: boolean): {
|
|
188
|
+
users: string[];
|
|
189
|
+
roles: string[];
|
|
190
|
+
};
|
|
191
|
+
export function writerPolicy(contributorId?: string | null): {
|
|
192
|
+
contributorId: string | null;
|
|
193
|
+
isContributor: boolean;
|
|
194
|
+
createStamp: any | null;
|
|
195
|
+
editStamp: (metadata: any) => any | null;
|
|
196
|
+
confirmStamp: (metadata: any) => any | null;
|
|
197
|
+
adoptStamp: (source: string, detail?: any) => any;
|
|
198
|
+
reviewable: (metadata: any) => boolean;
|
|
199
|
+
reviewableState: (state: string) => boolean;
|
|
200
|
+
};
|
package/src/provenance.js
CHANGED
|
@@ -151,6 +151,8 @@ export const stampInferred = (source, { prob, detail } = {}) => ({
|
|
|
151
151
|
* import carrying upstream human approval, or a guess written only on
|
|
152
152
|
* explicit user confirmation). Takes the same optional prediction extras
|
|
153
153
|
* as stampInferred.
|
|
154
|
+
* @param {string} source
|
|
155
|
+
* @param {{prob?: number, detail?: Record<string, any>}} [extras]
|
|
154
156
|
*/
|
|
155
157
|
export const confirmedInferred = (source, extras = {}) => ({
|
|
156
158
|
...stampInferred(source, extras),
|
|
@@ -179,7 +181,7 @@ export const stampContributed = (userId) => ({
|
|
|
179
181
|
|
|
180
182
|
/**
|
|
181
183
|
* Classify an entity's metadata into one of PROV_STATES.
|
|
182
|
-
* @param {
|
|
184
|
+
* @param {object|null|undefined} metadata
|
|
183
185
|
* @returns {'human'|'machine'|'contributed'|'verified'}
|
|
184
186
|
*/
|
|
185
187
|
export const provState = (metadata) => {
|
|
@@ -193,6 +195,7 @@ export const provState = (metadata) => {
|
|
|
193
195
|
* work, PROV.INFERRED for a machine's, PROV.CONTRIBUTED for a contributor's.
|
|
194
196
|
* What a verified entity's tooltip needs, since provState folds both
|
|
195
197
|
* origins into 'verified'.
|
|
198
|
+
* @param {object|null|undefined} metadata
|
|
196
199
|
* @returns {null|'inferred'|'contributed'}
|
|
197
200
|
*/
|
|
198
201
|
export const provOrigin = (metadata) => {
|
|
@@ -203,12 +206,16 @@ export const provOrigin = (metadata) => {
|
|
|
203
206
|
/**
|
|
204
207
|
* Whether this entity is machine-made and not yet verified — the material
|
|
205
208
|
* machine writers may replace. Complement of isProtected.
|
|
209
|
+
* @param {object|null|undefined} metadata
|
|
210
|
+
* @returns {boolean}
|
|
206
211
|
*/
|
|
207
212
|
export const isMachine = (metadata) => provState(metadata) === PROV_STATES.MACHINE;
|
|
208
213
|
|
|
209
214
|
/**
|
|
210
215
|
* Whether a machine writer must leave this entity alone (write-contract
|
|
211
216
|
* rule 2): true for human-made, contributed and verified material.
|
|
217
|
+
* @param {object|null|undefined} metadata
|
|
218
|
+
* @returns {boolean}
|
|
212
219
|
*/
|
|
213
220
|
export const isProtected = (metadata) => !isMachine(metadata);
|
|
214
221
|
|
|
@@ -216,6 +223,8 @@ export const isProtected = (metadata) => !isMachine(metadata);
|
|
|
216
223
|
* Whether a verifier still has to look at this entity: machine-made or
|
|
217
224
|
* contributed, and not yet confirmed. What review UIs mark and sweep, and
|
|
218
225
|
* what a verifier's confirmation gesture acts on.
|
|
226
|
+
* @param {object|null|undefined} metadata
|
|
227
|
+
* @returns {boolean}
|
|
219
228
|
*/
|
|
220
229
|
export const needsReview = (metadata) => {
|
|
221
230
|
const s = provState(metadata);
|
|
@@ -226,6 +235,7 @@ export const needsReview = (metadata) => {
|
|
|
226
235
|
* The metadata fragment a VERIFIER's edit of this entity should merge in
|
|
227
236
|
* (write-contract rule 3): PROV_CONFIRMED when the entity needs review,
|
|
228
237
|
* else null (nothing to do).
|
|
238
|
+
* @param {object|null|undefined} metadata
|
|
229
239
|
*/
|
|
230
240
|
export const verifyOnEdit = (metadata) => (needsReview(metadata) ? PROV_CONFIRMED : null);
|
|
231
241
|
|
|
@@ -235,10 +245,11 @@ export const verifyOnEdit = (metadata) => (needsReview(metadata) ? PROV_CONFIRME
|
|
|
235
245
|
* so an earlier confirmation is dropped (send it with metadataOps, which
|
|
236
246
|
* turns a null into a delete, or apply it locally with mergeMetadata). Prediction extras a machine
|
|
237
247
|
* recorded stay, as history.
|
|
238
|
-
* @param {
|
|
248
|
+
* @param {object|null|undefined} metadata - unused today; the fragment is
|
|
239
249
|
* the same whatever the entity was, and the parameter keeps the shape of
|
|
240
250
|
* verifyOnEdit so callers can swap one for the other
|
|
241
251
|
* @param {string} userId
|
|
252
|
+
* @returns {{prov: "contributed", provSource: string, provConfirmed: null}}
|
|
242
253
|
*/
|
|
243
254
|
// eslint-disable-next-line no-unused-vars
|
|
244
255
|
export const contributeOnEdit = (metadata, userId) => ({
|
|
@@ -246,10 +257,18 @@ export const contributeOnEdit = (metadata, userId) => ({
|
|
|
246
257
|
[PROV.confirmedKey]: null,
|
|
247
258
|
});
|
|
248
259
|
|
|
249
|
-
/**
|
|
260
|
+
/**
|
|
261
|
+
* Canonical provSource for a service: 'service:<serviceId>'.
|
|
262
|
+
* @param {string} serviceId
|
|
263
|
+
* @returns {string}
|
|
264
|
+
*/
|
|
250
265
|
export const serviceSource = (serviceId) => `service:${serviceId}`;
|
|
251
266
|
|
|
252
|
-
/**
|
|
267
|
+
/**
|
|
268
|
+
* Canonical provSource for a contributor: 'user:<userId>'.
|
|
269
|
+
* @param {string} userId
|
|
270
|
+
* @returns {string}
|
|
271
|
+
*/
|
|
253
272
|
export const userSource = (userId) => `user:${userId}`;
|
|
254
273
|
|
|
255
274
|
// ---- review: whose work is reviewed (a project-config norm) ----
|
|
@@ -259,7 +278,8 @@ export const REVIEW_KEY = 'review';
|
|
|
259
278
|
|
|
260
279
|
/**
|
|
261
280
|
* The project's review lists, normalized: { users: string[], roles: string[] }.
|
|
262
|
-
* @param {
|
|
281
|
+
* @param {object|null} [config] - a project's `config`
|
|
282
|
+
* @returns {{users: string[], roles: string[]}}
|
|
263
283
|
*/
|
|
264
284
|
export const readReview = (config) => {
|
|
265
285
|
const raw = config?.plaid?.[REVIEW_KEY];
|
|
@@ -271,7 +291,7 @@ export const readReview = (config) => {
|
|
|
271
291
|
* A person's role in a project from its ACL lists: 'maintainer' | 'writer' |
|
|
272
292
|
* 'reader' | null. An admin with no explicit entry is a maintainer, as the
|
|
273
293
|
* permission model treats them.
|
|
274
|
-
* @param {
|
|
294
|
+
* @param {object} project - with `maintainers`, `writers`, `readers`
|
|
275
295
|
* @param {string} userId
|
|
276
296
|
* @param {Object} [opts]
|
|
277
297
|
* @param {boolean} [opts.isAdmin]
|
|
@@ -287,6 +307,10 @@ export const projectRole = (project, userId, { isAdmin = false } = {}) => {
|
|
|
287
307
|
/**
|
|
288
308
|
* Whether this person's work is reviewed in this project: named in
|
|
289
309
|
* review.users, or holding a role named in review.roles.
|
|
310
|
+
* @param {object|null|undefined} project
|
|
311
|
+
* @param {string|null|undefined} userId
|
|
312
|
+
* @param {{isAdmin?: boolean}} [opts]
|
|
313
|
+
* @returns {boolean}
|
|
290
314
|
*/
|
|
291
315
|
export const isReviewed = (project, userId, { isAdmin = false } = {}) => {
|
|
292
316
|
if (!project || userId == null) return false;
|
|
@@ -300,6 +324,10 @@ export const isReviewed = (project, userId, { isAdmin = false } = {}) => {
|
|
|
300
324
|
* The review lists with one person added to or removed from `users`
|
|
301
325
|
* (`roles` untouched). Pure; write the result with
|
|
302
326
|
* projects.setConfig(id, 'plaid', REVIEW_KEY, next).
|
|
327
|
+
* @param {{users?: string[], roles?: string[]} | null | undefined} review
|
|
328
|
+
* @param {string} userId
|
|
329
|
+
* @param {boolean} reviewed
|
|
330
|
+
* @returns {{users: string[], roles: string[]}}
|
|
303
331
|
*/
|
|
304
332
|
export const withReviewedUser = (review, userId, reviewed) => {
|
|
305
333
|
const { users, roles } = readReview({ plaid: { [REVIEW_KEY]: review } });
|
package/src/replayed.js
CHANGED
|
@@ -19,7 +19,11 @@ export function markReplayed(value) {
|
|
|
19
19
|
return value;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Whether a client answer was replayed from its key's first send.
|
|
24
|
+
* @param {unknown} answer
|
|
25
|
+
* @returns {boolean}
|
|
26
|
+
*/
|
|
23
27
|
export const wasReplayed = (answer) =>
|
|
24
28
|
answer !== null &&
|
|
25
29
|
typeof answer === "object" &&
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The person who asked for one run: `id` is their account (their email),
|
|
3
|
+
* `name` how they are shown. Both are null when nobody asked.
|
|
4
|
+
* @param {string | null} [id]
|
|
5
|
+
* @param {string | null} [name]
|
|
6
|
+
* @returns {Requester}
|
|
7
|
+
*/
|
|
8
|
+
export function makeRequester(id?: string | null, name?: string | null): Requester;
|
|
9
|
+
/**
|
|
10
|
+
* The requester of one request, from the `requesterId` core put in its data,
|
|
11
|
+
* with their display name read through `client`.
|
|
12
|
+
* @param {{users: {get(id: string): Promise<any>}}} client - A PlaidClient (only `users.get` is used)
|
|
13
|
+
* @param {any} data - The request data a `serve` handler receives
|
|
14
|
+
* @returns {Promise<Requester>}
|
|
15
|
+
*/
|
|
16
|
+
export function requesterOf(client: {
|
|
17
|
+
users: {
|
|
18
|
+
get(id: string): Promise<any>;
|
|
19
|
+
};
|
|
20
|
+
}, data: any): Promise<Requester>;
|
|
21
|
+
/**
|
|
22
|
+
* The person who asked for one service run. Both fields are null when nobody
|
|
23
|
+
* asked.
|
|
24
|
+
* @typedef {object} Requester
|
|
25
|
+
* @property {string | null} id
|
|
26
|
+
* @property {string | null} name
|
|
27
|
+
* @property {(text: string) => string} label `"<text>, requested by <name>"`,
|
|
28
|
+
* or `text` when nobody asked.
|
|
29
|
+
* @property {() => ({id: string, name: string | null} | null)} record
|
|
30
|
+
* `{ id, name }` for a stored report, or null when nobody asked.
|
|
31
|
+
* @property {(detail?: Record<string, any>) => Record<string, any>} detail A
|
|
32
|
+
* machine stamp's `provDetail` with `requestedBy: <id>` added.
|
|
33
|
+
*/
|
|
34
|
+
/** The key a run's stored record names its requester under. */
|
|
35
|
+
export const REQUESTED_BY: "requestedBy";
|
|
36
|
+
/**
|
|
37
|
+
* The person who asked for one service run. Both fields are null when nobody
|
|
38
|
+
* asked.
|
|
39
|
+
*/
|
|
40
|
+
export type Requester = {
|
|
41
|
+
id: string | null;
|
|
42
|
+
name: string | null;
|
|
43
|
+
/**
|
|
44
|
+
* `"<text>, requested by <name>"`,
|
|
45
|
+
* or `text` when nobody asked.
|
|
46
|
+
*/
|
|
47
|
+
label: (text: string) => string;
|
|
48
|
+
/**
|
|
49
|
+
* `{ id, name }` for a stored report, or null when nobody asked.
|
|
50
|
+
*/
|
|
51
|
+
record: () => ({
|
|
52
|
+
id: string;
|
|
53
|
+
name: string | null;
|
|
54
|
+
} | null);
|
|
55
|
+
/**
|
|
56
|
+
* A
|
|
57
|
+
* machine stamp's `provDetail` with `requestedBy: <id>` added.
|
|
58
|
+
*/
|
|
59
|
+
detail: (detail?: Record<string, any>) => Record<string, any>;
|
|
60
|
+
};
|
package/src/requester.js
CHANGED
|
@@ -15,12 +15,29 @@
|
|
|
15
15
|
// read. When it cannot be read the id stands in for it, and with no requester
|
|
16
16
|
// at all the label is left as it was and nothing is stored.
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* The person who asked for one service run. Both fields are null when nobody
|
|
20
|
+
* asked.
|
|
21
|
+
* @typedef {object} Requester
|
|
22
|
+
* @property {string | null} id
|
|
23
|
+
* @property {string | null} name
|
|
24
|
+
* @property {(text: string) => string} label `"<text>, requested by <name>"`,
|
|
25
|
+
* or `text` when nobody asked.
|
|
26
|
+
* @property {() => ({id: string, name: string | null} | null)} record
|
|
27
|
+
* `{ id, name }` for a stored report, or null when nobody asked.
|
|
28
|
+
* @property {(detail?: Record<string, any>) => Record<string, any>} detail A
|
|
29
|
+
* machine stamp's `provDetail` with `requestedBy: <id>` added.
|
|
30
|
+
*/
|
|
31
|
+
|
|
18
32
|
/** The key a run's stored record names its requester under. */
|
|
19
|
-
export const REQUESTED_BY = "requestedBy";
|
|
33
|
+
export const REQUESTED_BY = /** @type {const} */ ("requestedBy");
|
|
20
34
|
|
|
21
35
|
/**
|
|
22
36
|
* The person who asked for one run: `id` is their account (their email),
|
|
23
37
|
* `name` how they are shown. Both are null when nobody asked.
|
|
38
|
+
* @param {string | null} [id]
|
|
39
|
+
* @param {string | null} [name]
|
|
40
|
+
* @returns {Requester}
|
|
24
41
|
*/
|
|
25
42
|
export function makeRequester(id = null, name = null) {
|
|
26
43
|
return {
|
|
@@ -38,8 +55,9 @@ export function makeRequester(id = null, name = null) {
|
|
|
38
55
|
/**
|
|
39
56
|
* The requester of one request, from the `requesterId` core put in its data,
|
|
40
57
|
* with their display name read through `client`.
|
|
41
|
-
* @param {
|
|
42
|
-
* @param {
|
|
58
|
+
* @param {{users: {get(id: string): Promise<any>}}} client - A PlaidClient (only `users.get` is used)
|
|
59
|
+
* @param {any} data - The request data a `serve` handler receives
|
|
60
|
+
* @returns {Promise<Requester>}
|
|
43
61
|
*/
|
|
44
62
|
export async function requesterOf(client, data) {
|
|
45
63
|
const id = data && typeof data === "object" ? data.requesterId || null : null;
|
package/src/roles.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The role recorded on a layer's `config`, or null if none.
|
|
3
|
+
* @param {object} [config] a layer's `config` object
|
|
4
|
+
* @returns {string|null}
|
|
5
|
+
*/
|
|
6
|
+
export function readRole(config?: object): string | null;
|
|
7
|
+
/**
|
|
8
|
+
* The first layer in `layers` carrying the given role, or null. The single
|
|
9
|
+
* "find a layer by its role" primitive — apps build their named finders
|
|
10
|
+
* (findWordTokenLayer, etc.) on top of this.
|
|
11
|
+
* @template {{config?: object}} T
|
|
12
|
+
* @param {T[] | undefined} layers
|
|
13
|
+
* @param {string} role
|
|
14
|
+
* @returns {T | null}
|
|
15
|
+
*/
|
|
16
|
+
export function findByRole<T extends {
|
|
17
|
+
config?: object;
|
|
18
|
+
}>(layers: T[] | undefined, role: string): T | null;
|
|
19
|
+
/**
|
|
20
|
+
* Shared layer-role vocabulary for cross-app interoperability.
|
|
21
|
+
*
|
|
22
|
+
* Apps that share a Plaid project agree on the *substrate* — the text and token
|
|
23
|
+
* layers — by tagging each shared layer with a ROLE under the reserved `plaid`
|
|
24
|
+
* config namespace (`config.plaid.role`, a scalar). Annotations stay private to
|
|
25
|
+
* each app under that app's own namespace. See the Plaid manual, "Layer
|
|
26
|
+
* Interoperability". The role inventory is small and fixed:
|
|
27
|
+
*
|
|
28
|
+
* baseline the primary text layer
|
|
29
|
+
* sentence sentence token layer
|
|
30
|
+
* word orthographic-word token layer (CoNLL-U "token")
|
|
31
|
+
* syntactic-word grammatical words below the word (CoNLL-U "word" / MWT splits)
|
|
32
|
+
* morpheme morpheme token layer
|
|
33
|
+
* time-alignment media-timeline token layer
|
|
34
|
+
*
|
|
35
|
+
* Only these values are understood across apps; an app may store any string but
|
|
36
|
+
* loses interoperability for unknown values.
|
|
37
|
+
*/
|
|
38
|
+
/** The reserved config namespace for cross-app conventions. */
|
|
39
|
+
export const PLAID_NAMESPACE: "plaid";
|
|
40
|
+
/** The config key, under `plaid`, holding a layer's role. */
|
|
41
|
+
export const ROLE_KEY: "role";
|
|
42
|
+
/** The fixed role inventory. */
|
|
43
|
+
/**
|
|
44
|
+
* Layer config key naming the metadata keys a token born of a SPLIT inherits
|
|
45
|
+
* from the token it came from. Plaid honors it without knowing what the keys
|
|
46
|
+
* mean; see the manual's `Metadata Preserved Across a Split`. An app declares
|
|
47
|
+
* it so that a split in ANY app, including one that has never heard of these
|
|
48
|
+
* keys, does not silently drop them.
|
|
49
|
+
*/
|
|
50
|
+
export const PRESERVE_ON_SPLIT_KEY: "preserveOnSplit";
|
|
51
|
+
/**
|
|
52
|
+
* Layer config key (true): a space typed inside a token of the text's words
|
|
53
|
+
* splits it, the token going on the half sharing more letters with it.
|
|
54
|
+
* Declared on a word layer another app shares, it holds for the whole text.
|
|
55
|
+
* See the manual's `Changing a text's body`.
|
|
56
|
+
*/
|
|
57
|
+
export const SPLIT_ON_SPACE_KEY: "splitOnSpace";
|
|
58
|
+
export const ROLES: Readonly<{
|
|
59
|
+
BASELINE: "baseline";
|
|
60
|
+
SENTENCE: "sentence";
|
|
61
|
+
WORD: "word";
|
|
62
|
+
SYNTACTIC_WORD: "syntactic-word";
|
|
63
|
+
MORPHEME: "morpheme";
|
|
64
|
+
TIME_ALIGNMENT: "time-alignment";
|
|
65
|
+
}>;
|