@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/events.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { transformRequest } from "./transforms.js";
|
|
2
|
+
import { readResearchTelemetry } from "./roles.js";
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Client events: the opt-in research telemetry of a project (defined in
|
|
6
|
+
// plaid-core sql/client_event.clj).
|
|
7
|
+
//
|
|
8
|
+
// `client.events.record(type, fields)` is a fire-and-forget recorder. It
|
|
9
|
+
// buffers events and sends them every FLUSH_INTERVAL_MS or FLUSH_AT events,
|
|
10
|
+
// and when the page is hidden (`pagehide`, with `keepalive` so the request
|
|
11
|
+
// outlives the page). A batch that fails is dropped: no retry, no toast, no
|
|
12
|
+
// error for the caller. Nothing it does can block or break the screen that
|
|
13
|
+
// called it.
|
|
14
|
+
//
|
|
15
|
+
// It sends nothing for a project whose switch is off. The switch is read
|
|
16
|
+
// from the project (`config.plaid.research.telemetry`). An "on" is kept for
|
|
17
|
+
// FLAG_TTL_MS, one flush interval, and an "off" for FLAG_OFF_TTL_MS, so a
|
|
18
|
+
// switch another maintainer turns on reaches an open page within a minute,
|
|
19
|
+
// while a page whose switch is off, which records on every render of a grid,
|
|
20
|
+
// does not read the project every few seconds. An event recorded while an old
|
|
21
|
+
// "off" is being read again waits for the answer rather than being dropped.
|
|
22
|
+
// `events.setEnabled` tells it at once, which is what the settings checkbox
|
|
23
|
+
// does. A server refusal (the switch was turned off since) turns it off here
|
|
24
|
+
// too.
|
|
25
|
+
//
|
|
26
|
+
// ONE recorder per server per page, whatever client records into it: an app
|
|
27
|
+
// makes a client per editor, and a suggestion's "shown" is deduplicated for
|
|
28
|
+
// the page session, not per client. The last client to record lends its
|
|
29
|
+
// token to the next send.
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
/** Every event type the server accepts. */
|
|
33
|
+
export const EVENT_TYPES = Object.freeze([
|
|
34
|
+
"suggestion.shown",
|
|
35
|
+
"suggestion.adopted",
|
|
36
|
+
"suggestion.dismissed",
|
|
37
|
+
"plan.opened",
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
export const FLUSH_INTERVAL_MS = 10000;
|
|
41
|
+
export const FLUSH_AT = 50;
|
|
42
|
+
export const FLAG_TTL_MS = FLUSH_INTERVAL_MS;
|
|
43
|
+
export const FLAG_OFF_TTL_MS = 60000;
|
|
44
|
+
// Events per request. A browser refuses a `keepalive` request whose body,
|
|
45
|
+
// with every other keepalive request still in flight, passes 64 KiB, and a
|
|
46
|
+
// refused request is lost. So the pagehide send goes out in small pieces, and
|
|
47
|
+
// only that send is keepalive: a backlog sent while the page stays (the first
|
|
48
|
+
// page of a grid, drawn while the switch was still being read, can hold
|
|
49
|
+
// hundreds of guesses) would otherwise lose everything past the quota. The
|
|
50
|
+
// server takes up to 500 in one request.
|
|
51
|
+
const PER_REQUEST = FLUSH_AT;
|
|
52
|
+
// A suggested or written value is a gloss, not prose. A long one is cut, in
|
|
53
|
+
// code points as the server counts them, so no character is split in two.
|
|
54
|
+
// The server refuses a value past this length, and a key its type does not
|
|
55
|
+
// take (plaid-core sql/client_event.clj).
|
|
56
|
+
const MAX_VALUE_CHARS = 200;
|
|
57
|
+
|
|
58
|
+
const clip = (v) => {
|
|
59
|
+
if (typeof v !== "string" || v.length <= MAX_VALUE_CHARS) return v;
|
|
60
|
+
const cps = Array.from(v);
|
|
61
|
+
return cps.length > MAX_VALUE_CHARS
|
|
62
|
+
? cps.slice(0, MAX_VALUE_CHARS).join("")
|
|
63
|
+
: v;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export class EventRecorder {
|
|
67
|
+
/**
|
|
68
|
+
* @param {object} [opts]
|
|
69
|
+
* @param {function} [opts.now] - Clock, for tests
|
|
70
|
+
* @param {object} [opts.window] - Where `pagehide` fires (default globalThis)
|
|
71
|
+
*/
|
|
72
|
+
constructor({ now = () => Date.now(), window = globalThis } = {}) {
|
|
73
|
+
this.client = null;
|
|
74
|
+
this.buffer = [];
|
|
75
|
+
this.timer = null;
|
|
76
|
+
this.now = now;
|
|
77
|
+
// projectId -> { state: 'on' | 'off' | 'pending', at }
|
|
78
|
+
this.flags = new Map();
|
|
79
|
+
// "shown" keys already recorded this page session.
|
|
80
|
+
this.seen = new Set();
|
|
81
|
+
this._onPageHide = () => this.flush({ keepalive: true });
|
|
82
|
+
this.window = window;
|
|
83
|
+
window?.addEventListener?.("pagehide", this._onPageHide);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Tell the recorder a project's switch, e.g. right after changing it. */
|
|
87
|
+
setEnabled(projectId, on) {
|
|
88
|
+
this.flags.set(projectId, { state: on ? "on" : "off", at: this.now() });
|
|
89
|
+
if (!on) this._drop(projectId);
|
|
90
|
+
else if (this.buffer.length) this._schedule();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Record one event. Returns true when it was buffered, false when it was
|
|
95
|
+
* not (switch off, a repeat "shown", an unknown type, no project).
|
|
96
|
+
* @param {any} client
|
|
97
|
+
* @param {string} type
|
|
98
|
+
* @param {{projectId?: string, documentId?: string, targetId?: string, data?: Record<string, any>}} [fields]
|
|
99
|
+
* @returns {boolean}
|
|
100
|
+
*/
|
|
101
|
+
record(client, type, { projectId, documentId, targetId, data } = {}) {
|
|
102
|
+
try {
|
|
103
|
+
if (!EVENT_TYPES.includes(type) || !projectId) return false;
|
|
104
|
+
this.client = client;
|
|
105
|
+
const flag = this._flag(projectId);
|
|
106
|
+
if (flag === "off") return false;
|
|
107
|
+
// A key with no value is left out: the server takes only a string or
|
|
108
|
+
// a number.
|
|
109
|
+
const clipped = data
|
|
110
|
+
? Object.fromEntries(
|
|
111
|
+
Object.entries(data)
|
|
112
|
+
.filter(([, v]) => v != null)
|
|
113
|
+
.map(([k, v]) => [k, clip(v)]),
|
|
114
|
+
)
|
|
115
|
+
: undefined;
|
|
116
|
+
let key = null;
|
|
117
|
+
if (type === "suggestion.shown") {
|
|
118
|
+
key = JSON.stringify([
|
|
119
|
+
projectId,
|
|
120
|
+
targetId ?? null,
|
|
121
|
+
clipped?.field ?? null,
|
|
122
|
+
clipped?.value ?? null,
|
|
123
|
+
]);
|
|
124
|
+
if (this.seen.has(key)) return false;
|
|
125
|
+
this.seen.add(key);
|
|
126
|
+
}
|
|
127
|
+
this.buffer.push({
|
|
128
|
+
projectId,
|
|
129
|
+
key,
|
|
130
|
+
event: {
|
|
131
|
+
type,
|
|
132
|
+
documentId: documentId ?? undefined,
|
|
133
|
+
targetId: targetId == null ? undefined : String(targetId),
|
|
134
|
+
data: clipped,
|
|
135
|
+
clientTs: new Date(this.now()).toISOString(),
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
if (this.buffer.length >= FLUSH_AT) this.flush();
|
|
139
|
+
else this._schedule();
|
|
140
|
+
return true;
|
|
141
|
+
} catch {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Send what is buffered for every project whose switch is known to be on,
|
|
148
|
+
* drop what is buffered for one known to be off, and keep the rest until
|
|
149
|
+
* the switch is known. With `keepalive` (the page is going away) nothing is
|
|
150
|
+
* kept.
|
|
151
|
+
*/
|
|
152
|
+
flush({ keepalive = false } = {}) {
|
|
153
|
+
clearTimeout(this.timer);
|
|
154
|
+
this.timer = null;
|
|
155
|
+
const keep = [];
|
|
156
|
+
const byProject = new Map();
|
|
157
|
+
for (const entry of this.buffer) {
|
|
158
|
+
const state = this.flags.get(entry.projectId)?.state;
|
|
159
|
+
if (state === "on") {
|
|
160
|
+
if (!byProject.has(entry.projectId)) byProject.set(entry.projectId, []);
|
|
161
|
+
byProject.get(entry.projectId).push(entry.event);
|
|
162
|
+
} else if (state === "off") {
|
|
163
|
+
if (entry.key) this.seen.delete(entry.key);
|
|
164
|
+
} else if (!keepalive) {
|
|
165
|
+
keep.push(entry);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
this.buffer = keep;
|
|
169
|
+
for (const [projectId, events] of byProject) {
|
|
170
|
+
for (let i = 0; i < events.length; i += PER_REQUEST) {
|
|
171
|
+
this._send(projectId, events.slice(i, i + PER_REQUEST), keepalive);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Stop listening for `pagehide` and forget everything (tests). */
|
|
177
|
+
close() {
|
|
178
|
+
clearTimeout(this.timer);
|
|
179
|
+
this.timer = null;
|
|
180
|
+
this.buffer = [];
|
|
181
|
+
this.window?.removeEventListener?.("pagehide", this._onPageHide);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
_schedule() {
|
|
185
|
+
if (this.timer == null) {
|
|
186
|
+
this.timer = setTimeout(() => this.flush(), FLUSH_INTERVAL_MS);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
_drop(projectId) {
|
|
191
|
+
this.buffer = this.buffer.filter((e) => {
|
|
192
|
+
if (e.projectId !== projectId) return true;
|
|
193
|
+
if (e.key) this.seen.delete(e.key);
|
|
194
|
+
return false;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// The switch as known now: 'on', 'off' or 'pending'. An unknown one sets
|
|
199
|
+
// off a read of the project, and a stale one a fresh read. While it is
|
|
200
|
+
// read, an old "on" stands and an old "off" is 'pending', so what is
|
|
201
|
+
// recorded meanwhile waits for the answer. The answer flushes or drops what
|
|
202
|
+
// is buffered.
|
|
203
|
+
_flag(projectId) {
|
|
204
|
+
const known = this.flags.get(projectId);
|
|
205
|
+
const ttl = known?.state === "on" ? FLAG_TTL_MS : FLAG_OFF_TTL_MS;
|
|
206
|
+
const fresh = known && this.now() - known.at < ttl;
|
|
207
|
+
if (known && (fresh || known.checking)) return known.state;
|
|
208
|
+
const state = known?.state === "on" ? "on" : "pending";
|
|
209
|
+
this.flags.set(projectId, {
|
|
210
|
+
state,
|
|
211
|
+
at: known ? known.at : 0,
|
|
212
|
+
checking: true,
|
|
213
|
+
});
|
|
214
|
+
const client = this.client;
|
|
215
|
+
Promise.resolve()
|
|
216
|
+
.then(() => client.projects.get(projectId))
|
|
217
|
+
.then(
|
|
218
|
+
(project) => readResearchTelemetry(project?.config),
|
|
219
|
+
() => false,
|
|
220
|
+
)
|
|
221
|
+
.then((on) => {
|
|
222
|
+
this.flags.set(projectId, { state: on ? "on" : "off", at: this.now() });
|
|
223
|
+
if (!on) this._drop(projectId);
|
|
224
|
+
else if (this.buffer.length >= FLUSH_AT) this.flush();
|
|
225
|
+
else if (this.buffer.length) this._schedule();
|
|
226
|
+
});
|
|
227
|
+
return state;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
_send(projectId, events, keepalive) {
|
|
231
|
+
const client = this.client;
|
|
232
|
+
try {
|
|
233
|
+
const url = `${client.baseUrl}/api/v1/projects/${projectId}/events`;
|
|
234
|
+
fetch(url, {
|
|
235
|
+
method: "POST",
|
|
236
|
+
keepalive,
|
|
237
|
+
headers: {
|
|
238
|
+
Authorization: `Bearer ${client.token}`,
|
|
239
|
+
"Content-Type": "application/json",
|
|
240
|
+
},
|
|
241
|
+
body: JSON.stringify(transformRequest(events)),
|
|
242
|
+
}).then(
|
|
243
|
+
(res) => {
|
|
244
|
+
// The switch was turned off since it was read.
|
|
245
|
+
if (res.status === 403) this.setEnabled(projectId, false);
|
|
246
|
+
},
|
|
247
|
+
() => {},
|
|
248
|
+
);
|
|
249
|
+
} catch {
|
|
250
|
+
/* dropped, as every failed batch is */
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const recorders = new Map();
|
|
256
|
+
|
|
257
|
+
/** The page's recorder for `client`'s server. */
|
|
258
|
+
export function recorderFor(client) {
|
|
259
|
+
let r = recorders.get(client.baseUrl);
|
|
260
|
+
if (!r) {
|
|
261
|
+
r = new EventRecorder();
|
|
262
|
+
recorders.set(client.baseUrl, r);
|
|
263
|
+
}
|
|
264
|
+
return r;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Forget every recorder (tests). */
|
|
268
|
+
export function resetEventRecorders() {
|
|
269
|
+
for (const r of recorders.values()) r.close();
|
|
270
|
+
recorders.clear();
|
|
271
|
+
}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/** @typedef {import("./client.js").PlaidClient} PlaidClient */
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {import("./types.js").MetadataOp} MetadataOp
|
|
4
|
+
* @typedef {import("./types.js").LayerConstraint} LayerConstraint
|
|
5
|
+
* @typedef {import("./types.js").ConstraintViolation} ConstraintViolation
|
|
6
|
+
* @typedef {import("./types.js").OperationKind} OperationKind
|
|
7
|
+
* @typedef {import("./types.js").ConstraintRepair} ConstraintRepair
|
|
8
|
+
* @typedef {import("./types.js").ValueSetRule} ValueSetRule
|
|
9
|
+
* @typedef {import("./types.js").RefusedValues} RefusedValues
|
|
10
|
+
* @typedef {import("./types.js").MetadataRecords} MetadataRecords
|
|
11
|
+
* @typedef {import("./types.js").LayerRecords} LayerRecords
|
|
12
|
+
* @typedef {import("./types.js").AuditEntry} AuditEntry
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* @template [T=any]
|
|
16
|
+
* @typedef {import("./types.js").Page<T>} Page
|
|
17
|
+
*/
|
|
18
|
+
export function csvParam(values: any): string;
|
|
19
|
+
export function bodyOf(obj: any): {};
|
|
20
|
+
/**
|
|
21
|
+
* The request options of a config write. With `options.expected` present
|
|
22
|
+
* (even as undefined, which means the key was absent) the write is a
|
|
23
|
+
* compare-and-set: `?if-unchanged=true` and the body `{expected, value}`,
|
|
24
|
+
* which the server refuses with a 409 when the stored value is no longer
|
|
25
|
+
* `expected`. Without it the body is the value itself. Config is opaque, so
|
|
26
|
+
* neither body is re-cased.
|
|
27
|
+
*/
|
|
28
|
+
export function configRequest(auditMessage: any, value: any, options: any, isDelete?: boolean): {
|
|
29
|
+
auditMessage: any;
|
|
30
|
+
rawBody?: undefined;
|
|
31
|
+
queryParams?: undefined;
|
|
32
|
+
} | {
|
|
33
|
+
auditMessage: any;
|
|
34
|
+
rawBody: any;
|
|
35
|
+
queryParams?: undefined;
|
|
36
|
+
} | {
|
|
37
|
+
auditMessage: any;
|
|
38
|
+
queryParams: {
|
|
39
|
+
"if-unchanged": boolean;
|
|
40
|
+
};
|
|
41
|
+
rawBody: {
|
|
42
|
+
expected: any;
|
|
43
|
+
value?: undefined;
|
|
44
|
+
} | {
|
|
45
|
+
expected: any;
|
|
46
|
+
value: any;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* The query of an audit read: the window, the op-type and kind filters, the
|
|
51
|
+
* one vocabulary entry, and for one page its order, ops limit and entry id. A
|
|
52
|
+
* key left undefined is not sent.
|
|
53
|
+
* @private
|
|
54
|
+
*/
|
|
55
|
+
export function auditQuery({ startTime, endTime, opTypes, itemId, kinds, order, opsLimit, entryId }: {
|
|
56
|
+
startTime: any;
|
|
57
|
+
endTime: any;
|
|
58
|
+
opTypes: any;
|
|
59
|
+
itemId: any;
|
|
60
|
+
kinds: any;
|
|
61
|
+
order: any;
|
|
62
|
+
opsLimit: any;
|
|
63
|
+
entryId: any;
|
|
64
|
+
}): {
|
|
65
|
+
"start-time": any;
|
|
66
|
+
"end-time": any;
|
|
67
|
+
"op-types": string;
|
|
68
|
+
"item-id": any;
|
|
69
|
+
kinds: string;
|
|
70
|
+
order: any;
|
|
71
|
+
"ops-limit": any;
|
|
72
|
+
"entry-id": any;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* The audit readers of a document, a project or a user, on
|
|
76
|
+
* `/api/v1/<kind>/<id>/audit`. A vocabulary's take an entry id as well and are
|
|
77
|
+
* its own, and the instance-wide log is `audit.list`.
|
|
78
|
+
* @param {PlaidClient} client
|
|
79
|
+
* @param {"documents"|"projects"|"users"} kind
|
|
80
|
+
*/
|
|
81
|
+
export function auditMethods(client: PlaidClient, kind: "documents" | "projects" | "users"): {
|
|
82
|
+
/**
|
|
83
|
+
* Get the audit log of a document or a project, or of a user's actions.
|
|
84
|
+
* Transparently follows pagination cursors and returns the full flat
|
|
85
|
+
* array. Each entry, and each of its `ops`, says what kind of credential
|
|
86
|
+
* made it as `credential`: `login`, `named-token` or `delegated` (absent
|
|
87
|
+
* on older operations, `service` on some from 2026-10-06 to 2026-10-08),
|
|
88
|
+
* beside `apiToken` (the named token's id and name) when there was one.
|
|
89
|
+
* @param {string} id - The document, project or user ID
|
|
90
|
+
* @param {string} [startTime] - Start of time range
|
|
91
|
+
* @param {string} [endTime] - End of time range
|
|
92
|
+
* @param {string[]|string} [opTypes] - Only return operations of these
|
|
93
|
+
* types, spelled as in an entry's `op/type` (e.g.
|
|
94
|
+
* `['span-layer/create', 'span-layer/delete']`). An entry appears when
|
|
95
|
+
* one of its operations matches, carrying only the ones that did.
|
|
96
|
+
* @param {string[]|string} [kinds] - Only the entries of operations of
|
|
97
|
+
* these kinds (e.g. `['review', 'guess-adoption']`), each whole
|
|
98
|
+
* @returns {Promise<AuditEntry[]>}
|
|
99
|
+
*/
|
|
100
|
+
audit: (id: string, startTime?: string, endTime?: string, opTypes?: string[] | string, kinds?: string[] | string) => Promise<AuditEntry[]>;
|
|
101
|
+
/**
|
|
102
|
+
* One page of the same log, newest-first with `order: "desc"`. Use this
|
|
103
|
+
* rather than audit() wherever the caller wants the recent end of a log
|
|
104
|
+
* that may be long: audit() walks every page before it resolves.
|
|
105
|
+
* @param {string} id - The document, project or user ID
|
|
106
|
+
* @param {object} [opts]
|
|
107
|
+
* @param {string} [opts.startTime] - Only entries at or after this instant
|
|
108
|
+
* @param {string} [opts.endTime] - Only entries at or before this instant
|
|
109
|
+
* @param {string[]|string} [opts.opTypes] - Only these op types
|
|
110
|
+
* @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
|
|
111
|
+
* @param {"asc"|"desc"} [opts.order] - "desc" pages newest-first
|
|
112
|
+
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
113
|
+
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
114
|
+
* @param {number} [opts.opsLimit] - Keep only each entry's oldest N
|
|
115
|
+
* operations in `ops` (1..1000). `opCount` on every entry says how
|
|
116
|
+
* many it has
|
|
117
|
+
* @param {string} [opts.entryId] - Read only this entry, not a page
|
|
118
|
+
* @returns {Promise<Page<AuditEntry>>}
|
|
119
|
+
*/
|
|
120
|
+
auditPage: (id: string, { startTime, endTime, opTypes, kinds, order, limit, cursor, opsLimit, entryId }?: {
|
|
121
|
+
startTime?: string;
|
|
122
|
+
endTime?: string;
|
|
123
|
+
opTypes?: string[] | string;
|
|
124
|
+
kinds?: OperationKind[] | string;
|
|
125
|
+
order?: "asc" | "desc";
|
|
126
|
+
limit?: number;
|
|
127
|
+
cursor?: string;
|
|
128
|
+
opsLimit?: number;
|
|
129
|
+
entryId?: string;
|
|
130
|
+
}) => Promise<Page<AuditEntry>>;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* The three metadata methods of a bundle whose entities carry metadata, on
|
|
134
|
+
* `/api/v1/<kind>/<id>/metadata`. Metadata is opaque, so no body is re-cased.
|
|
135
|
+
* Each answers the entity as its GET does.
|
|
136
|
+
* @template {keyof MetadataRecords} K
|
|
137
|
+
* @param {PlaidClient} client
|
|
138
|
+
* @param {K} kind
|
|
139
|
+
*/
|
|
140
|
+
export function metadataMethods<K extends keyof MetadataRecords>(client: PlaidClient, kind: K): {
|
|
141
|
+
/**
|
|
142
|
+
* Replace all metadata of an entity. Keys left out of the new map are removed.
|
|
143
|
+
* @param {string} id - The entity's ID
|
|
144
|
+
* @param {object} body - The new metadata map
|
|
145
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
146
|
+
* @returns {Promise<MetadataRecords[K]>}
|
|
147
|
+
*/
|
|
148
|
+
setMetadata: (id: string, body: object, auditMessage?: string) => Promise<MetadataRecords[K]>;
|
|
149
|
+
/**
|
|
150
|
+
* Remove all metadata from an entity.
|
|
151
|
+
* @param {string} id - The entity's ID
|
|
152
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
153
|
+
* @returns {Promise<MetadataRecords[K]>}
|
|
154
|
+
*/
|
|
155
|
+
deleteMetadata: (id: string, auditMessage?: string) => Promise<MetadataRecords[K]>;
|
|
156
|
+
/**
|
|
157
|
+
* 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.
|
|
158
|
+
* @param {string} id - The entity's ID
|
|
159
|
+
* @param {MetadataOp[]} body - The metadata ops
|
|
160
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
161
|
+
* @returns {Promise<MetadataRecords[K]>}
|
|
162
|
+
*/
|
|
163
|
+
patchMetadata: (id: string, body: MetadataOp[], auditMessage?: string) => Promise<MetadataRecords[K]>;
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* The two config methods of a bundle whose records carry editor config, on
|
|
167
|
+
* `/api/v1/<kind>/<id>/config/<namespace>/<key>`.
|
|
168
|
+
* @param {PlaidClient} client
|
|
169
|
+
* @param {string} kind
|
|
170
|
+
*/
|
|
171
|
+
export function configMethods(client: PlaidClient, kind: string): {
|
|
172
|
+
/**
|
|
173
|
+
* Set a configuration value in an editor namespace.
|
|
174
|
+
* @param {string} id - The record's ID
|
|
175
|
+
* @param {string} namespace - The config namespace
|
|
176
|
+
* @param {string} configKey - The config key
|
|
177
|
+
* @param {any} configValue - Configuration value to set
|
|
178
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
179
|
+
* @param {{expected?: any}} [options] - `expected`: the value read for this key (null or undefined when it
|
|
180
|
+
* was absent). The write is then refused with a 409 when the stored value differs. A value the key
|
|
181
|
+
* already holds is not written and leaves no audit row, so the value read, expecting it, states in a
|
|
182
|
+
* batch that the key is unchanged.
|
|
183
|
+
* @returns {Promise<void>}
|
|
184
|
+
*/
|
|
185
|
+
setConfig: (id: string, namespace: string, configKey: string, configValue: any, auditMessage?: string, options?: {
|
|
186
|
+
expected?: any;
|
|
187
|
+
}) => Promise<void>;
|
|
188
|
+
/**
|
|
189
|
+
* Remove a configuration value.
|
|
190
|
+
* @param {string} id - The record's ID
|
|
191
|
+
* @param {string} namespace - The config namespace
|
|
192
|
+
* @param {string} configKey - The config key
|
|
193
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
194
|
+
* @param {{expected?: any}} [options] - `expected`, as on setConfig. A key that is absent is not
|
|
195
|
+
* written, so expecting null states in a batch that it is still absent.
|
|
196
|
+
* @returns {Promise<void>}
|
|
197
|
+
*/
|
|
198
|
+
deleteConfig: (id: string, namespace: string, configKey: string, auditMessage?: string, options?: {
|
|
199
|
+
expected?: any;
|
|
200
|
+
}) => Promise<void>;
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* The get, delete, update and shift methods of the four layer bundles, on
|
|
204
|
+
* `/api/v1/<kind>/<layerId>`.
|
|
205
|
+
* @template {keyof LayerRecords} K
|
|
206
|
+
* @param {PlaidClient} client
|
|
207
|
+
* @param {K} kind
|
|
208
|
+
*/
|
|
209
|
+
export function layerMethods<K extends keyof LayerRecords>(client: PlaidClient, kind: K): {
|
|
210
|
+
/**
|
|
211
|
+
* Get a layer by ID.
|
|
212
|
+
* @param {string} layerId - The layer ID
|
|
213
|
+
* @returns {Promise<LayerRecords[K]>}
|
|
214
|
+
*/
|
|
215
|
+
get: (layerId: string) => Promise<LayerRecords[K]>;
|
|
216
|
+
/**
|
|
217
|
+
* Delete a layer.
|
|
218
|
+
* @param {string} layerId - The layer ID
|
|
219
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
220
|
+
* @returns {Promise<void>}
|
|
221
|
+
*/
|
|
222
|
+
delete: (layerId: string, auditMessage?: string) => Promise<void>;
|
|
223
|
+
/**
|
|
224
|
+
* Rename a layer.
|
|
225
|
+
* @param {string} layerId - The layer ID
|
|
226
|
+
* @param {string} name - The new name
|
|
227
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
228
|
+
* @returns {Promise<LayerRecords[K]>}
|
|
229
|
+
*/
|
|
230
|
+
update: (layerId: string, name: string, auditMessage?: string) => Promise<LayerRecords[K]>;
|
|
231
|
+
/**
|
|
232
|
+
* Move a layer one place in its display order.
|
|
233
|
+
* @param {string} layerId - The layer ID
|
|
234
|
+
* @param {string} direction - The direction ("up" or "down")
|
|
235
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
236
|
+
* @returns {Promise<void>}
|
|
237
|
+
*/
|
|
238
|
+
shift: (layerId: string, direction: string, auditMessage?: string) => Promise<void>;
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* The four layer-constraint methods of a layer bundle, on `/api/v1/<kind>`.
|
|
242
|
+
* See constraints.js and the core manual, "Layer constraints".
|
|
243
|
+
* @param {PlaidClient} client
|
|
244
|
+
* @param {string} kind
|
|
245
|
+
*/
|
|
246
|
+
export function constraintMethods(client: PlaidClient, kind: string): {
|
|
247
|
+
/**
|
|
248
|
+
* Declare an app namespace's constraints on this layer, replacing that
|
|
249
|
+
* namespace's list. Refused with 422 and the violations when the layer's
|
|
250
|
+
* stored data breaks them (see violationsOf).
|
|
251
|
+
* @param {string} layerId - The layer ID
|
|
252
|
+
* @param {string} namespace - The declaring app's namespace, e.g. "igt"
|
|
253
|
+
* @param {LayerConstraint[]} constraints - e.g. [{type: "single-span"}, {type: "value-set", values: ["N"]}]
|
|
254
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
255
|
+
* @param {{expected?: Array<object>|null}} [options] - `expected`: the list read for this
|
|
256
|
+
* namespace (null when it was absent). The write is then refused with a 409 when the stored
|
|
257
|
+
* list differs.
|
|
258
|
+
* @returns {Promise<{constraints: Record<string, LayerConstraint[]>}>} the layer's whole constraint map
|
|
259
|
+
*/
|
|
260
|
+
setConstraints: (layerId: string, namespace: string, constraints: LayerConstraint[], auditMessage?: string, options?: {
|
|
261
|
+
expected?: Array<object> | null;
|
|
262
|
+
}) => Promise<{
|
|
263
|
+
constraints: Record<string, LayerConstraint[]>;
|
|
264
|
+
}>;
|
|
265
|
+
/**
|
|
266
|
+
* Remove an app namespace's constraints from this layer.
|
|
267
|
+
* @param {string} layerId - The layer ID
|
|
268
|
+
* @param {string} namespace - The declaring app's namespace
|
|
269
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
270
|
+
* @param {{expected?: Array<object>|null}} [options] - `expected`, as on setConstraints.
|
|
271
|
+
* @returns {Promise<void>}
|
|
272
|
+
*/
|
|
273
|
+
deleteConstraints: (layerId: string, namespace: string, auditMessage?: string, options?: {
|
|
274
|
+
expected?: Array<object> | null;
|
|
275
|
+
}) => Promise<void>;
|
|
276
|
+
/**
|
|
277
|
+
* The violations the given constraints would meet in this layer's stored
|
|
278
|
+
* data. Writes nothing.
|
|
279
|
+
* @param {string} layerId - The layer ID
|
|
280
|
+
* @param {LayerConstraint[]} constraints - The list to check
|
|
281
|
+
* @param {{limit?: number}} [options] - `limit`: how many violations to list (default 100)
|
|
282
|
+
* @returns {Promise<{violations: ConstraintViolation[], violationCount: number}>}
|
|
283
|
+
*/
|
|
284
|
+
checkConstraints: (layerId: string, constraints: LayerConstraint[], options?: {
|
|
285
|
+
limit?: number;
|
|
286
|
+
}) => Promise<{
|
|
287
|
+
violations: ConstraintViolation[];
|
|
288
|
+
violationCount: number;
|
|
289
|
+
}>;
|
|
290
|
+
/**
|
|
291
|
+
* Apply the remedies of the given constraints' remediable types
|
|
292
|
+
* (coextensive, single-span, single-link, same-ancestor) to every
|
|
293
|
+
* violation in this layer's stored data, one operation per document.
|
|
294
|
+
* With `document`, only that document is repaired, and a writer may
|
|
295
|
+
* ask. A document another user holds the lock on is left as it is and
|
|
296
|
+
* listed under `locked`.
|
|
297
|
+
* @param {string} layerId - The layer ID
|
|
298
|
+
* @param {LayerConstraint[]} constraints - The list to repair for
|
|
299
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
300
|
+
* @param {{document?: string}} [options]
|
|
301
|
+
* @returns {Promise<{repaired: ConstraintRepair[], locked: Array<{document: string, lockedBy: string}>, violations: ConstraintViolation[], violationCount: number}>}
|
|
302
|
+
*/
|
|
303
|
+
repairConstraints: (layerId: string, constraints: LayerConstraint[], auditMessage?: string, options?: {
|
|
304
|
+
document?: string;
|
|
305
|
+
}) => Promise<{
|
|
306
|
+
repaired: ConstraintRepair[];
|
|
307
|
+
locked: Array<{
|
|
308
|
+
document: string;
|
|
309
|
+
lockedBy: string;
|
|
310
|
+
}>;
|
|
311
|
+
violations: ConstraintViolation[];
|
|
312
|
+
violationCount: number;
|
|
313
|
+
}>;
|
|
314
|
+
};
|
|
315
|
+
/**
|
|
316
|
+
* The value-set read of a span or relation layer bundle, on
|
|
317
|
+
* `/api/v1/<kind>/<layerId>/constraints/refused-values`.
|
|
318
|
+
* @param {PlaidClient} client
|
|
319
|
+
* @param {string} kind
|
|
320
|
+
*/
|
|
321
|
+
export function valueSetMethods(client: PlaidClient, kind: string): {
|
|
322
|
+
/**
|
|
323
|
+
* The stored values of this layer that a value-set rule would refuse,
|
|
324
|
+
* before it is declared: exactly what a declaration of it would meet,
|
|
325
|
+
* with the rule's exemptions (an unconfirmed machine value, a value an
|
|
326
|
+
* import, a document copy or a restore set). Writes nothing, and a
|
|
327
|
+
* reader may ask.
|
|
328
|
+
* @param {string} layerId - The layer ID
|
|
329
|
+
* @param {ValueSetRule} rule - The list as a value-set declaration takes it
|
|
330
|
+
* @param {{limit?: number}} [options] - `limit`: how many values to list, the most frequent first
|
|
331
|
+
* (default 20, at most 100)
|
|
332
|
+
* @returns {Promise<RefusedValues>}
|
|
333
|
+
*/
|
|
334
|
+
refusedValues: (layerId: string, rule: ValueSetRule, options?: {
|
|
335
|
+
limit?: number;
|
|
336
|
+
}) => Promise<RefusedValues>;
|
|
337
|
+
};
|
|
338
|
+
export type PlaidClient = import("./client.js").PlaidClient;
|
|
339
|
+
export type MetadataOp = import("./types.js").MetadataOp;
|
|
340
|
+
export type LayerConstraint = import("./types.js").LayerConstraint;
|
|
341
|
+
export type ConstraintViolation = import("./types.js").ConstraintViolation;
|
|
342
|
+
export type OperationKind = import("./types.js").OperationKind;
|
|
343
|
+
export type ConstraintRepair = import("./types.js").ConstraintRepair;
|
|
344
|
+
export type ValueSetRule = import("./types.js").ValueSetRule;
|
|
345
|
+
export type RefusedValues = import("./types.js").RefusedValues;
|
|
346
|
+
export type MetadataRecords = import("./types.js").MetadataRecords;
|
|
347
|
+
export type LayerRecords = import("./types.js").LayerRecords;
|
|
348
|
+
export type AuditEntry = import("./types.js").AuditEntry;
|
|
349
|
+
export type Page<T = any> = import("./types.js").Page<T>;
|