@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.
Files changed (73) hide show
  1. package/README.md +39 -0
  2. package/package.json +13 -6
  3. package/src/apiAnnotations.d.ts +508 -0
  4. package/src/apiAnnotations.js +543 -0
  5. package/src/apiDocuments.d.ts +569 -0
  6. package/src/apiDocuments.js +551 -0
  7. package/src/apiLayers.d.ts +369 -0
  8. package/src/apiLayers.js +222 -0
  9. package/src/apiMessages.d.ts +293 -0
  10. package/src/apiMessages.js +265 -0
  11. package/src/apiPeople.d.ts +626 -0
  12. package/src/apiPeople.js +698 -0
  13. package/src/apiProject.d.ts +761 -0
  14. package/src/apiProject.js +774 -0
  15. package/src/apiServer.d.ts +455 -0
  16. package/src/apiServer.js +422 -0
  17. package/src/apiVocab.d.ts +456 -0
  18. package/src/apiVocab.js +425 -0
  19. package/src/batch.d.ts +54 -0
  20. package/src/batch.js +323 -0
  21. package/src/batchRef.d.ts +30 -0
  22. package/src/batchRef.js +128 -0
  23. package/src/client.d.ts +1321 -0
  24. package/src/client.js +743 -0
  25. package/src/codepoint.d.ts +94 -0
  26. package/src/codepoint.js +131 -1
  27. package/src/constraints.d.ts +85 -0
  28. package/src/constraints.js +112 -0
  29. package/src/created.d.ts +2 -0
  30. package/src/created.js +29 -0
  31. package/src/documentLock.d.ts +172 -0
  32. package/src/documentLock.js +384 -0
  33. package/src/entryForms.d.ts +63 -0
  34. package/src/entryForms.js +152 -0
  35. package/src/events.d.ts +60 -0
  36. package/src/events.js +271 -0
  37. package/src/families.d.ts +349 -0
  38. package/src/families.js +396 -0
  39. package/src/http.d.ts +233 -0
  40. package/src/http.js +809 -93
  41. package/src/ids.d.ts +26 -0
  42. package/src/ids.js +96 -0
  43. package/src/index.d.ts +101 -0
  44. package/src/index.js +183 -2105
  45. package/src/metadataOps.d.ts +15 -0
  46. package/src/metadataOps.js +124 -0
  47. package/src/operations.d.ts +36 -0
  48. package/src/operations.js +164 -0
  49. package/src/pagination.d.ts +49 -0
  50. package/src/pagination.js +20 -53
  51. package/src/provenance.d.ts +243 -0
  52. package/src/provenance.js +458 -36
  53. package/src/replayed.d.ts +3 -0
  54. package/src/replayed.js +31 -0
  55. package/src/requester.d.ts +60 -0
  56. package/src/requester.js +74 -0
  57. package/src/roles.d.ts +104 -0
  58. package/src/roles.js +64 -2
  59. package/src/serviceSchema.d.ts +337 -0
  60. package/src/serviceSchema.js +177 -60
  61. package/src/services.d.ts +319 -0
  62. package/src/services.js +742 -58
  63. package/src/sse.d.ts +30 -0
  64. package/src/sse.js +80 -3
  65. package/src/testing.d.ts +128 -0
  66. package/src/testing.js +379 -0
  67. package/src/textEdits.d.ts +59 -0
  68. package/src/textEdits.js +234 -0
  69. package/src/transforms.d.ts +25 -0
  70. package/src/transforms.js +11 -1
  71. package/src/types.d.ts +867 -0
  72. package/src/types.js +663 -0
  73. package/index.d.ts +0 -532
@@ -0,0 +1,74 @@
1
+ // Who asked for a service run, named in what the run leaves behind.
2
+ //
3
+ // A plain service writes with its own token, so the History entry of a run
4
+ // reads "by <operator> (via <token>)" and the person who pressed the button is
5
+ // recorded nowhere. Core tells every service who asked (`requesterId` in the
6
+ // request data), and every app's services put that person in the History
7
+ // label and in what they store:
8
+ //
9
+ // AnCast adjudication against lunch, requested by second
10
+ //
11
+ // This is the JS twin of `plaid_client.workflows.requester`, for a service
12
+ // authored with `serve`. The wording and the stored key are the same.
13
+ //
14
+ // The name is the requester's display name, which any signed-in user may
15
+ // read. When it cannot be read the id stands in for it, and with no requester
16
+ // at all the label is left as it was and nothing is stored.
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
+
32
+ /** The key a run's stored record names its requester under. */
33
+ export const REQUESTED_BY = /** @type {const} */ ("requestedBy");
34
+
35
+ /**
36
+ * The person who asked for one run: `id` is their account (their email),
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}
41
+ */
42
+ export function makeRequester(id = null, name = null) {
43
+ return {
44
+ id,
45
+ name,
46
+ /** A History label with the requester named, or `text` when nobody asked. */
47
+ label: (text) => (name ? `${text}, requested by ${name}` : text),
48
+ /** `{ id, name }` for a stored report, or null when nobody asked. */
49
+ record: () => (id ? { id, name } : null),
50
+ /** A machine stamp's `provDetail` with the requester's id added. */
51
+ detail: (detail = {}) => ({ ...(detail || {}), ...(id ? { [REQUESTED_BY]: id } : {}) }),
52
+ };
53
+ }
54
+
55
+ /**
56
+ * The requester of one request, from the `requesterId` core put in its data,
57
+ * with their display name read through `client`.
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>}
61
+ */
62
+ export async function requesterOf(client, data) {
63
+ const id = data && typeof data === "object" ? data.requesterId || null : null;
64
+ if (!id) return makeRequester();
65
+ let name = null;
66
+ try {
67
+ name = (await client.users.get(id))?.displayName || null;
68
+ } catch (error) {
69
+ // The id is always there to fall back on, so a failed read costs the
70
+ // label nothing but the nicer name.
71
+ console.warn(`Could not read the requester ${id}: ${error?.message || error}`);
72
+ }
73
+ return makeRequester(id, name || id);
74
+ }
package/src/roles.d.ts ADDED
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Whether a project records research telemetry: `config.plaid.research.telemetry`
3
+ * is exactly true.
4
+ * @param {object} [config] a project's `config` object
5
+ * @returns {boolean}
6
+ */
7
+ export function readResearchTelemetry(config?: object): boolean;
8
+ /**
9
+ * The role recorded on a layer's `config`, or null if none.
10
+ * @param {object} [config] a layer's `config` object
11
+ * @returns {string|null}
12
+ */
13
+ export function readRole(config?: object): string | null;
14
+ /**
15
+ * The ignored-tokens rule on a word layer's `config` (see
16
+ * `IGNORED_TOKENS_KEY`), or null if none. The one reader of the key in JS.
17
+ * @param {object} [config] a token layer's `config` object
18
+ * @returns {{type: string, whitelist?: string[], blacklist?: string[]}|null}
19
+ */
20
+ export function readIgnoredTokens(config?: object): {
21
+ type: string;
22
+ whitelist?: string[];
23
+ blacklist?: string[];
24
+ } | null;
25
+ /**
26
+ * The first layer in `layers` carrying the given role, or null. The single
27
+ * "find a layer by its role" primitive — apps build their named finders
28
+ * (findWordTokenLayer, etc.) on top of this.
29
+ * @template {{config?: object}} T
30
+ * @param {T[] | undefined} layers
31
+ * @param {string} role
32
+ * @returns {T | null}
33
+ */
34
+ export function findByRole<T extends {
35
+ config?: object;
36
+ }>(layers: T[] | undefined, role: string): T | null;
37
+ /**
38
+ * Shared layer-role vocabulary for cross-app interoperability.
39
+ *
40
+ * Apps that share a Plaid project agree on the *substrate* — the text and token
41
+ * layers — by tagging each shared layer with a ROLE under the reserved `plaid`
42
+ * config namespace (`config.plaid.role`, a scalar). Annotations stay private to
43
+ * each app under that app's own namespace. See the Plaid manual, "Layer
44
+ * Interoperability". The role inventory is small and fixed:
45
+ *
46
+ * baseline the primary text layer
47
+ * sentence sentence token layer
48
+ * word orthographic-word token layer (CoNLL-U "token")
49
+ * syntactic-word grammatical words below the word (CoNLL-U "word" / MWT splits)
50
+ * morpheme morpheme token layer
51
+ * time-alignment media-timeline token layer
52
+ *
53
+ * Only these values are understood across apps; an app may store any string but
54
+ * loses interoperability for unknown values.
55
+ */
56
+ /** The reserved config namespace for cross-app conventions. */
57
+ export const PLAID_NAMESPACE: "plaid";
58
+ /** The config key, under `plaid`, holding a layer's role. */
59
+ export const ROLE_KEY: "role";
60
+ /** The fixed role inventory. */
61
+ /**
62
+ * Layer config key naming the metadata keys a token born of a SPLIT inherits
63
+ * from the token it came from. Plaid honors it without knowing what the keys
64
+ * mean; see the manual's `Metadata Preserved Across a Split`. An app declares
65
+ * it so that a split in ANY app, including one that has never heard of these
66
+ * keys, does not silently drop them.
67
+ */
68
+ export const PRESERVE_ON_SPLIT_KEY: "preserveOnSplit";
69
+ /**
70
+ * Layer config key (true): a space typed inside a token of the text's words
71
+ * splits it, the token going on the half sharing more letters with it.
72
+ * Declared on a word layer another app shares, it holds for the whole text.
73
+ * See the manual's `Changing a text's body`.
74
+ */
75
+ export const SPLIT_ON_SPACE_KEY: "splitOnSpace";
76
+ /**
77
+ * Word layer config key holding the ignored-tokens rule,
78
+ * `{type, whitelist, blacklist}`: under `type: 'unicodePunctuation'` a word made
79
+ * only of punctuation is ignored and each one-character entry of `whitelist` is
80
+ * a letter, under `type: 'blacklist'` the entries of `blacklist` are the words
81
+ * ignored and its one-character entries the only characters that part words.
82
+ * Plaid's text rules and the built-in tokenizer read it. See the manual's
83
+ * `Changing a text's body`.
84
+ */
85
+ export const IGNORED_TOKENS_KEY: "ignoredTokens";
86
+ /**
87
+ * Project config key (false to hide): whether the project's tartan shows beside
88
+ * its name. Absent means shown.
89
+ */
90
+ export const TARTAN_KEY: "tartan";
91
+ /**
92
+ * Project config key holding the research settings, `{telemetry}`. Client
93
+ * events are recorded only while `telemetry` is true. See the manual's
94
+ * `Research telemetry`.
95
+ */
96
+ export const RESEARCH_KEY: "research";
97
+ export const ROLES: Readonly<{
98
+ BASELINE: "baseline";
99
+ SENTENCE: "sentence";
100
+ WORD: "word";
101
+ SYNTACTIC_WORD: "syntactic-word";
102
+ MORPHEME: "morpheme";
103
+ TIME_ALIGNMENT: "time-alignment";
104
+ }>;
package/src/roles.js CHANGED
@@ -25,6 +25,57 @@ export const PLAID_NAMESPACE = 'plaid';
25
25
  export const ROLE_KEY = 'role';
26
26
 
27
27
  /** The fixed role inventory. */
28
+ /**
29
+ * Layer config key naming the metadata keys a token born of a SPLIT inherits
30
+ * from the token it came from. Plaid honors it without knowing what the keys
31
+ * mean; see the manual's `Metadata Preserved Across a Split`. An app declares
32
+ * it so that a split in ANY app, including one that has never heard of these
33
+ * keys, does not silently drop them.
34
+ */
35
+ export const PRESERVE_ON_SPLIT_KEY = 'preserveOnSplit';
36
+
37
+ /**
38
+ * Layer config key (true): a space typed inside a token of the text's words
39
+ * splits it, the token going on the half sharing more letters with it.
40
+ * Declared on a word layer another app shares, it holds for the whole text.
41
+ * See the manual's `Changing a text's body`.
42
+ */
43
+ export const SPLIT_ON_SPACE_KEY = 'splitOnSpace';
44
+
45
+ /**
46
+ * Word layer config key holding the ignored-tokens rule,
47
+ * `{type, whitelist, blacklist}`: under `type: 'unicodePunctuation'` a word made
48
+ * only of punctuation is ignored and each one-character entry of `whitelist` is
49
+ * a letter, under `type: 'blacklist'` the entries of `blacklist` are the words
50
+ * ignored and its one-character entries the only characters that part words.
51
+ * Plaid's text rules and the built-in tokenizer read it. See the manual's
52
+ * `Changing a text's body`.
53
+ */
54
+ export const IGNORED_TOKENS_KEY = 'ignoredTokens';
55
+
56
+ /**
57
+ * Project config key (false to hide): whether the project's tartan shows beside
58
+ * its name. Absent means shown.
59
+ */
60
+ export const TARTAN_KEY = 'tartan';
61
+
62
+ /**
63
+ * Project config key holding the research settings, `{telemetry}`. Client
64
+ * events are recorded only while `telemetry` is true. See the manual's
65
+ * `Research telemetry`.
66
+ */
67
+ export const RESEARCH_KEY = 'research';
68
+
69
+ /**
70
+ * Whether a project records research telemetry: `config.plaid.research.telemetry`
71
+ * is exactly true.
72
+ * @param {object} [config] a project's `config` object
73
+ * @returns {boolean}
74
+ */
75
+ export function readResearchTelemetry(config) {
76
+ return config?.[PLAID_NAMESPACE]?.[RESEARCH_KEY]?.telemetry === true;
77
+ }
78
+
28
79
  export const ROLES = Object.freeze({
29
80
  BASELINE: 'baseline',
30
81
  SENTENCE: 'sentence',
@@ -44,13 +95,24 @@ export function readRole(config) {
44
95
  return v == null ? null : v;
45
96
  }
46
97
 
98
+ /**
99
+ * The ignored-tokens rule on a word layer's `config` (see
100
+ * `IGNORED_TOKENS_KEY`), or null if none. The one reader of the key in JS.
101
+ * @param {object} [config] a token layer's `config` object
102
+ * @returns {{type: string, whitelist?: string[], blacklist?: string[]}|null}
103
+ */
104
+ export function readIgnoredTokens(config) {
105
+ return config?.[PLAID_NAMESPACE]?.[IGNORED_TOKENS_KEY] ?? null;
106
+ }
107
+
47
108
  /**
48
109
  * The first layer in `layers` carrying the given role, or null. The single
49
110
  * "find a layer by its role" primitive — apps build their named finders
50
111
  * (findWordTokenLayer, etc.) on top of this.
51
- * @param {Array<{config?: object}>} [layers]
112
+ * @template {{config?: object}} T
113
+ * @param {T[] | undefined} layers
52
114
  * @param {string} role
53
- * @returns {object|null}
115
+ * @returns {T | null}
54
116
  */
55
117
  export function findByRole(layers, role) {
56
118
  return (layers || []).find(l => readRole(l?.config) === role) || null;
@@ -0,0 +1,337 @@
1
+ /**
2
+ * One choice for an `enum` or `multiselect` service parameter.
3
+ * @typedef {{value: string, label: string}} ServiceParamOption
4
+ */
5
+ /**
6
+ * A single user-controllable argument a service advertises.
7
+ * @typedef {object} ServiceParam
8
+ * @property {string} key Key the value is sent under in the request payload.
9
+ * @property {string} label
10
+ * @property {"string" | "number" | "boolean" | "enum" | "multiselect" | "field"} type
11
+ * @property {string} [description]
12
+ * @property {any} [default]
13
+ * @property {boolean} [required]
14
+ * @property {ServiceParamOption[]} [options] Required for `enum` and `multiselect`.
15
+ * @property {number} [min] `number` only.
16
+ * @property {number} [max] `number` only.
17
+ * @property {number} [step] `number` only.
18
+ * @property {boolean} [slider] `number` only: render as a slider to drag
19
+ * rather than a box to type in.
20
+ * @property {string} [placeholder] `string` only.
21
+ * @property {boolean} [multiline] `string` only.
22
+ * @property {"Sentence" | "Word" | "Morpheme"} [scope] `field` only: the
23
+ * scope of the project's annotation fields the value names one of. A
24
+ * `field` value is a string everywhere else.
25
+ * @property {string} [role] `field` only: the lexicon role whose paired field,
26
+ * or the sentence role whose field, fills the parameter when nothing else
27
+ * does.
28
+ */
29
+ /**
30
+ * A service's standardized self-description (lives in `extras`).
31
+ * @typedef {object} ServiceExtras
32
+ * @property {number} [schemaVersion]
33
+ * @property {string[]} [tasks] Tasks this service serves, from the TASKS vocabulary.
34
+ * @property {string} [summary] Rich human description (Markdown), beyond the
35
+ * short `description`.
36
+ * @property {ServiceParam[]} [parameters] Ordered parameter schema, rendered
37
+ * into a form by the UI.
38
+ */
39
+ /**
40
+ * What a service says about itself when it registers.
41
+ * @typedef {object} ServiceInfo
42
+ * @property {string} serviceId
43
+ * @property {string} serviceName
44
+ * @property {string} description
45
+ * @property {ServiceExtras & Record<string, any>} [extras]
46
+ */
47
+ /**
48
+ * A service as `messages.discoverServices` lists it.
49
+ * @typedef {object} DiscoveredService
50
+ * @property {string} serviceId
51
+ * @property {string} serviceName
52
+ * @property {string} description
53
+ * @property {ServiceExtras & Record<string, any>} extras
54
+ * @property {boolean} online True while the service holds an open request
55
+ * channel, false for a previously seen offline service.
56
+ * @property {string | null} [lastSeenAt] ISO-8601 stamp of when the service
57
+ * was last seen alive, or null when never stored.
58
+ * @property {string} [runnerName] Online services only: the display name of
59
+ * the account that runs the service.
60
+ * @property {boolean} [runByYou] Online services only: the caller runs it.
61
+ * @property {boolean} [servesYou] Online services only: it would take the
62
+ * caller's requests on this project. A delegating service serves members
63
+ * other than its runner only where the runner is a maintainer or an admin.
64
+ */
65
+ /**
66
+ * Does `service` serve `task`, as its declared `extras.tasks` says?
67
+ * @param {{serviceId?: string, extras?: {tasks?: string[]}}} service
68
+ * @param {string} task one of TASKS
69
+ * @returns {boolean}
70
+ */
71
+ export function servesTask(service: {
72
+ serviceId?: string;
73
+ extras?: {
74
+ tasks?: string[];
75
+ };
76
+ }, task: string): boolean;
77
+ /**
78
+ * The discovered services that serve `task`.
79
+ * @param {DiscoveredService[] | undefined} services result of client.messages.discoverServices()
80
+ * @param {string} task one of TASKS
81
+ * @returns {DiscoveredService[]}
82
+ */
83
+ export function filterServicesByTask(services: DiscoveredService[] | undefined, task: string): DiscoveredService[];
84
+ /**
85
+ * The parameter schema a service declares (ordered array), or [].
86
+ * @param {{extras?: {parameters?: ServiceParam[]}}} service
87
+ * @returns {ServiceParam[]}
88
+ */
89
+ export function getParamSchema(service: {
90
+ extras?: {
91
+ parameters?: ServiceParam[];
92
+ };
93
+ }): ServiceParam[];
94
+ /**
95
+ * A service's human summary: the rich `extras.summary`, else the short
96
+ * `description`, else ''.
97
+ * @param {{description?: string, extras?: {summary?: string}}} service
98
+ * @returns {string}
99
+ */
100
+ export function getServiceSummary(service: {
101
+ description?: string;
102
+ extras?: {
103
+ summary?: string;
104
+ };
105
+ }): string;
106
+ /**
107
+ * Default values keyed by param key — the initial form state.
108
+ * @param {ServiceParam[]} schema getParamSchema(service)
109
+ * @returns {Record<string, any>} { [key]: defaultValue }
110
+ */
111
+ export function buildDefaultValues(schema: ServiceParam[]): Record<string, any>;
112
+ export function coerceParamValues(schema: any, raw: any): {
113
+ values: {};
114
+ errors: {};
115
+ };
116
+ /**
117
+ * Service self-description: tasks, summary, and a parameter schema.
118
+ *
119
+ * The service framework's transport carries an opaque `extras` JSON map on every
120
+ * registered service (see services.js). This module standardizes what a service
121
+ * advertises in that map so apps can, at a fixed integration point (a "task"
122
+ * like tokenize / parse / transcribe):
123
+ *
124
+ * 1. SELECT one of several services that serve the task,
125
+ * 2. let the user SPECIFY arguments the service declares, and
126
+ * 3. show the user a service-provided SUMMARY.
127
+ *
128
+ * Shape of a service's `extras` (camelCase here; Python services author the
129
+ * snake_case equivalent and the client transform converts it — see the Plaid
130
+ * manual, "Describing a service"):
131
+ *
132
+ * {
133
+ * schemaVersion: 1,
134
+ * tasks: ["tokenize"], // controlled vocab (TASKS)
135
+ * summary: "## markdown …", // rich human description
136
+ * parameters: [ // ordered; rendered into a form
137
+ * { key, label, type, description?, default?, required?,
138
+ * options?: [{value, label}], // enum / multiselect
139
+ * min?, max?, step?, slider?, // number (slider: drag, don't type)
140
+ * placeholder?, multiline?, // string
141
+ * scope?, role? } // field: 'Sentence' | 'Word' | 'Morpheme'
142
+ * ]
143
+ * }
144
+ *
145
+ * A `field` parameter names one of the project's annotation fields at its
146
+ * `scope`. A UI that knows the project offers those fields to choose from; to
147
+ * everything here, and to a UI that does not, it is a string. Its optional
148
+ * `role` names a lexicon role ("gloss") or, at Sentence scope, a sentence
149
+ * field's role ("translation", "literalTranslation", "note"): a UI that knows
150
+ * the project fills the parameter with the one field at `scope` whose stored
151
+ * Lexicon field holds that role, or whose stored role it is. A field is never
152
+ * chosen by its name.
153
+ *
154
+ * A parameter's `key` is a string VALUE, so it passes over the wire verbatim;
155
+ * the UI sends `{ [param.key]: value }` in the request data. Declare each `key`
156
+ * in your service's own convention (Python: snake_case; JS: camelCase) and read
157
+ * it back under that same key — request data is recased symmetrically, so a
158
+ * snake_case key round-trips unchanged to a JS UI and back.
159
+ */
160
+ /**
161
+ * The controlled task vocabulary — the fixed integration-point goals.
162
+ *
163
+ * Whatever the task, a service should stamp everything it creates with the
164
+ * provenance convention (see ./provenance.js: stampInferred(serviceSource(id)))
165
+ * so UIs can distinguish machine output until a human verifies it, and should
166
+ * honor the write contract: never destroy human-made or verified material
167
+ * without an explicit `overwrite` parameter. See the manual, "Provenance".
168
+ */
169
+ export const TASKS: Readonly<{
170
+ TOKENIZE: "tokenize";
171
+ PARSE: "parse";
172
+ TRANSCRIBE: "transcribe";
173
+ /** Create vocab links for unlinked tokens. */
174
+ LINK_VOCAB: "link-vocab";
175
+ /** Propose an interlinear analysis (morpheme segmentation + glosses) for words. */
176
+ ANALYZE: "analyze";
177
+ /**
178
+ * Draft a meaning representation graph over the sentences of a document,
179
+ * for correction by hand. The request names a `scope` ('document' or
180
+ * 'sentence'), a `sentence` number when the scope is one sentence, and
181
+ * `overwrite`. See plaid-umr's drafting services.
182
+ *
183
+ * Apart from `analyze` on purpose. A task is the CONTRACT a spot offers a
184
+ * service against, and these two share almost none of it: an analyze
185
+ * request names the three token layers plus the language and metalanguage,
186
+ * and writes morphemes and glosses. Both claimed `analyze` for
187
+ * a while, so every app offering an analyze spot offered the other app's
188
+ * services, which could not answer the request they would have been given.
189
+ */
190
+ DRAFT_GRAPH: "draft-graph";
191
+ /** Propose a free translation for each sentence. */
192
+ TRANSLATE: "translate";
193
+ /**
194
+ * Propose time-aligned speech regions for a recording.
195
+ *
196
+ * The odd one out: a detect-speech service RETURNS its regions and writes
197
+ * nothing, because a time-aligned segment is a stretch of the baseline and
198
+ * cannot exist without text. The response carries
199
+ * `{segments: [{timeBegin, timeEnd, speaker?}]}` (seconds), and the app
200
+ * holds them as proposals until a person types into one.
201
+ */
202
+ DETECT_SPEECH: "detect-speech";
203
+ /** A conversational assistant over a project (chat turns; see plaid-agent). */
204
+ ASSIST: "assist";
205
+ /**
206
+ * Score one document's annotation against another document's of the same
207
+ * text (two annotators' copies), writing a report on the scored document's
208
+ * metadata and never an annotation. The request names the other document
209
+ * as `against`. See plaid-umr's AnCast service.
210
+ */
211
+ COMPARE: "compare";
212
+ }>;
213
+ /**
214
+ * One choice for an `enum` or `multiselect` service parameter.
215
+ */
216
+ export type ServiceParamOption = {
217
+ value: string;
218
+ label: string;
219
+ };
220
+ /**
221
+ * A single user-controllable argument a service advertises.
222
+ */
223
+ export type ServiceParam = {
224
+ /**
225
+ * Key the value is sent under in the request payload.
226
+ */
227
+ key: string;
228
+ label: string;
229
+ type: "string" | "number" | "boolean" | "enum" | "multiselect" | "field";
230
+ description?: string;
231
+ default?: any;
232
+ required?: boolean;
233
+ /**
234
+ * Required for `enum` and `multiselect`.
235
+ */
236
+ options?: ServiceParamOption[];
237
+ /**
238
+ * `number` only.
239
+ */
240
+ min?: number;
241
+ /**
242
+ * `number` only.
243
+ */
244
+ max?: number;
245
+ /**
246
+ * `number` only.
247
+ */
248
+ step?: number;
249
+ /**
250
+ * `number` only: render as a slider to drag
251
+ * rather than a box to type in.
252
+ */
253
+ slider?: boolean;
254
+ /**
255
+ * `string` only.
256
+ */
257
+ placeholder?: string;
258
+ /**
259
+ * `string` only.
260
+ */
261
+ multiline?: boolean;
262
+ /**
263
+ * `field` only: the
264
+ * scope of the project's annotation fields the value names one of. A
265
+ * `field` value is a string everywhere else.
266
+ */
267
+ scope?: "Sentence" | "Word" | "Morpheme";
268
+ /**
269
+ * `field` only: the lexicon role whose paired field,
270
+ * or the sentence role whose field, fills the parameter when nothing else
271
+ * does.
272
+ */
273
+ role?: string;
274
+ };
275
+ /**
276
+ * A service's standardized self-description (lives in `extras`).
277
+ */
278
+ export type ServiceExtras = {
279
+ schemaVersion?: number;
280
+ /**
281
+ * Tasks this service serves, from the TASKS vocabulary.
282
+ */
283
+ tasks?: string[];
284
+ /**
285
+ * Rich human description (Markdown), beyond the
286
+ * short `description`.
287
+ */
288
+ summary?: string;
289
+ /**
290
+ * Ordered parameter schema, rendered
291
+ * into a form by the UI.
292
+ */
293
+ parameters?: ServiceParam[];
294
+ };
295
+ /**
296
+ * What a service says about itself when it registers.
297
+ */
298
+ export type ServiceInfo = {
299
+ serviceId: string;
300
+ serviceName: string;
301
+ description: string;
302
+ extras?: ServiceExtras & Record<string, any>;
303
+ };
304
+ /**
305
+ * A service as `messages.discoverServices` lists it.
306
+ */
307
+ export type DiscoveredService = {
308
+ serviceId: string;
309
+ serviceName: string;
310
+ description: string;
311
+ extras: ServiceExtras & Record<string, any>;
312
+ /**
313
+ * True while the service holds an open request
314
+ * channel, false for a previously seen offline service.
315
+ */
316
+ online: boolean;
317
+ /**
318
+ * ISO-8601 stamp of when the service
319
+ * was last seen alive, or null when never stored.
320
+ */
321
+ lastSeenAt?: string | null;
322
+ /**
323
+ * Online services only: the display name of
324
+ * the account that runs the service.
325
+ */
326
+ runnerName?: string;
327
+ /**
328
+ * Online services only: the caller runs it.
329
+ */
330
+ runByYou?: boolean;
331
+ /**
332
+ * Online services only: it would take the
333
+ * caller's requests on this project. A delegating service serves members
334
+ * other than its runner only where the runner is a maintainer or an admin.
335
+ */
336
+ servesYou?: boolean;
337
+ };