@frontera-sdk/knowledge 0.0.0-stage → 1.53.18
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/LICENSE +202 -0
- package/README.md +176 -2
- package/package.json +83 -4
- package/src/errors.ts +89 -0
- package/src/hooks.ts +46 -0
- package/src/knowledge-client.ts +155 -0
- package/src/provider.tsx +20 -0
- package/src/routes.ts +55 -0
- package/src/types.ts +187 -0
- package/src/upload-files.ts +133 -0
- package/src/wait-until-ready.ts +120 -0
- package/src/wire-types.ts +588 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { FronteraError } from '@frontera-sdk/core/errors'
|
|
2
|
+
|
|
3
|
+
import type { KnowledgeClient } from './knowledge-client'
|
|
4
|
+
import { SETTLED_STATUSES, type DocumentStatus, type KnowledgeDocument, type UploadResult } from './types'
|
|
5
|
+
|
|
6
|
+
export type WaitOptions = {
|
|
7
|
+
/** The first pause between polls; each later pause is half as long again, up to `maxIntervalMs`. 1,500 ms by default. */
|
|
8
|
+
intervalMs?: number
|
|
9
|
+
/** 10,000 ms by default. */
|
|
10
|
+
maxIntervalMs?: number
|
|
11
|
+
/** 10 minutes by default. Past it the wait throws `KNOWLEDGE_WAIT_TIMEOUT` and leaves the documents as they are. */
|
|
12
|
+
timeoutMs?: number
|
|
13
|
+
signal?: AbortSignal
|
|
14
|
+
/** Every poll's answer, for progress; the next poll waits for it to finish. */
|
|
15
|
+
onPoll?: (documents: readonly KnowledgeDocument[]) => void | Promise<void>
|
|
16
|
+
/**
|
|
17
|
+
* Sends one document's file again, at most once per document. The wait calls
|
|
18
|
+
* it when an upload did not get its file stored: the document vanished before
|
|
19
|
+
* its malware check finished (the earlier upload of the same bytes it was
|
|
20
|
+
* answered with failed to store them), or it failed with `errorKind`
|
|
21
|
+
* `storage` (an upload cut short). Answer what the upload answers. Without
|
|
22
|
+
* it, such a document ends as it is: missing, or failed. A document that
|
|
23
|
+
* vanishes later was removed by someone, and is never sent again.
|
|
24
|
+
*/
|
|
25
|
+
resend?: (documentId: string) => Promise<UploadResult>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type WaitResult = {
|
|
29
|
+
/** The settled documents in the order asked; one sent again is the document its new upload answered. */
|
|
30
|
+
documents: KnowledgeDocument[]
|
|
31
|
+
/** The asked ids that are gone or that the caller cannot see, including one whose sending again failed. */
|
|
32
|
+
missing: string[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** An upload whose file was never stored: sending the same bytes again stores them in that document. */
|
|
36
|
+
const cutShort = (document: KnowledgeDocument) => document.status === 'failed' && document.errorKind === 'storage' && !document.searchable
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* What one poll says about a document, given the status the wait saw last
|
|
40
|
+
* (null before the first): still on its way, settled, gone and staying gone,
|
|
41
|
+
* or worth sending again. A document gone before its malware check finished
|
|
42
|
+
* lost its file with the upload it duplicated; one gone after was removed.
|
|
43
|
+
*/
|
|
44
|
+
function pollVerdict(document: KnowledgeDocument | undefined, lastSeen: DocumentStatus | null): 'waiting' | 'settled' | 'gone' | 'send again' {
|
|
45
|
+
if (!document) return lastSeen === null || lastSeen === 'scanning' ? 'send again' : 'gone'
|
|
46
|
+
if (cutShort(document)) return 'send again'
|
|
47
|
+
return SETTLED_STATUSES.has(document.status) ? 'settled' : 'waiting'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type Slot = { asked: string; id: string; lastSeen: DocumentStatus | null; resent: boolean; end?: KnowledgeDocument | 'gone' }
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Polls `POST /documents/status`, 200 ids a request, until every document is
|
|
54
|
+
* settled (`ready`, `failed` or `blocked`) or gone. It never throws for a
|
|
55
|
+
* failed or blocked document: read each one's `status`.
|
|
56
|
+
*/
|
|
57
|
+
export async function waitUntilReady(knowledge: KnowledgeClient, ids: readonly string[], options: WaitOptions = {}): Promise<WaitResult> {
|
|
58
|
+
const { intervalMs = 1500, maxIntervalMs = 10_000, timeoutMs = 10 * 60_000, signal, onPoll, resend } = options
|
|
59
|
+
const slots: Slot[] = [...new Set(ids)].map((id) => ({ asked: id, id, lastSeen: null, resent: false }))
|
|
60
|
+
const deadline = Date.now() + timeoutMs
|
|
61
|
+
let pause = intervalMs
|
|
62
|
+
for (;;) {
|
|
63
|
+
throwIfAborted(signal)
|
|
64
|
+
const open = slots.filter((slot) => slot.end === undefined)
|
|
65
|
+
if (open.length === 0) break
|
|
66
|
+
const { documents } = await knowledge.documents.status(open.map((slot) => slot.id), signal)
|
|
67
|
+
await onPoll?.(documents)
|
|
68
|
+
const byId = new Map(documents.map((document) => [document.id, document]))
|
|
69
|
+
for (const slot of open) {
|
|
70
|
+
const document = byId.get(slot.id)
|
|
71
|
+
const verdict = pollVerdict(document, slot.lastSeen)
|
|
72
|
+
if (document) slot.lastSeen = document.status
|
|
73
|
+
if (verdict === 'send again' && resend && !slot.resent) {
|
|
74
|
+
slot.resent = true
|
|
75
|
+
try {
|
|
76
|
+
const again = await resend(slot.id)
|
|
77
|
+
slot.id = again.document.id
|
|
78
|
+
slot.lastSeen = again.document.status
|
|
79
|
+
} catch {
|
|
80
|
+
slot.end = 'gone'
|
|
81
|
+
}
|
|
82
|
+
} else if (verdict !== 'waiting') {
|
|
83
|
+
slot.end = document ?? 'gone'
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (slots.every((slot) => slot.end !== undefined)) break
|
|
87
|
+
if (Date.now() >= deadline) {
|
|
88
|
+
const waiting = slots.filter((slot) => slot.end === undefined)
|
|
89
|
+
throw new FronteraError(`${waiting.length} of ${slots.length} documents did not settle within ${timeoutMs} ms`, {
|
|
90
|
+
code: 'KNOWLEDGE_WAIT_TIMEOUT',
|
|
91
|
+
details: { waiting: waiting.map((slot) => ({ id: slot.id, status: slot.lastSeen })) },
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
await sleep(Math.min(pause, deadline - Date.now()), signal)
|
|
95
|
+
pause = Math.min(maxIntervalMs, pause * 1.5)
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
documents: slots.flatMap((slot) => (slot.end && slot.end !== 'gone' ? [slot.end] : [])),
|
|
99
|
+
missing: slots.filter((slot) => slot.end === 'gone').map((slot) => slot.asked),
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function throwIfAborted(signal: AbortSignal | undefined): void {
|
|
104
|
+
if (signal?.aborted) throw new FronteraError('Knowledge wait was aborted', { code: 'ABORTED', details: { reason: signal.reason } })
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Resolves after `ms`, or as soon as `signal` aborts. */
|
|
108
|
+
export function sleep(ms: number, signal: AbortSignal | undefined): Promise<void> {
|
|
109
|
+
return new Promise((resolve) => {
|
|
110
|
+
const timer = setTimeout(() => {
|
|
111
|
+
signal?.removeEventListener('abort', onAbort)
|
|
112
|
+
resolve()
|
|
113
|
+
}, Math.max(0, ms))
|
|
114
|
+
function onAbort() {
|
|
115
|
+
clearTimeout(timer)
|
|
116
|
+
resolve()
|
|
117
|
+
}
|
|
118
|
+
signal?.addEventListener('abort', onAbort, { once: true })
|
|
119
|
+
})
|
|
120
|
+
}
|
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated from the service's Knowledge schemas by `bun run sdk:knowledge-wire`.
|
|
3
|
+
* Do not edit: a service test fails while this file differs from what the
|
|
4
|
+
* generator writes. Each `…Answer` is what one route answers; a date is its
|
|
5
|
+
* ISO string.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type CapabilitiesAnswer = {
|
|
9
|
+
steward: boolean
|
|
10
|
+
canAssignStewards: boolean
|
|
11
|
+
canCreate: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type BasesAnswer = {
|
|
15
|
+
bases: Array<{
|
|
16
|
+
id: string
|
|
17
|
+
orgId: string
|
|
18
|
+
ownerWorkspaceId: string | null
|
|
19
|
+
name: string
|
|
20
|
+
description: string | null
|
|
21
|
+
createdBy: string
|
|
22
|
+
createdAt: string
|
|
23
|
+
updatedAt: string
|
|
24
|
+
canManage: boolean
|
|
25
|
+
sharing: {
|
|
26
|
+
allWorkspaces: boolean
|
|
27
|
+
workspaceIds: string[]
|
|
28
|
+
} | null
|
|
29
|
+
}>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type BaseAnswer = {
|
|
33
|
+
base: {
|
|
34
|
+
id: string
|
|
35
|
+
orgId: string
|
|
36
|
+
ownerWorkspaceId: string | null
|
|
37
|
+
name: string
|
|
38
|
+
description: string | null
|
|
39
|
+
createdBy: string
|
|
40
|
+
createdAt: string
|
|
41
|
+
updatedAt: string
|
|
42
|
+
canManage: boolean
|
|
43
|
+
sharing: {
|
|
44
|
+
allWorkspaces: boolean
|
|
45
|
+
workspaceIds: string[]
|
|
46
|
+
} | null
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type CreatedBaseAnswer = {
|
|
51
|
+
base: {
|
|
52
|
+
id: string
|
|
53
|
+
orgId: string
|
|
54
|
+
ownerWorkspaceId: string | null
|
|
55
|
+
name: string
|
|
56
|
+
description: string | null
|
|
57
|
+
createdBy: string
|
|
58
|
+
createdAt: string
|
|
59
|
+
updatedAt: string
|
|
60
|
+
canManage: boolean
|
|
61
|
+
sharing: {
|
|
62
|
+
allWorkspaces: boolean
|
|
63
|
+
workspaceIds: string[]
|
|
64
|
+
} | null
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type DeletedBaseAnswer = {
|
|
69
|
+
base: {
|
|
70
|
+
id: string
|
|
71
|
+
documents: number
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type DocumentsAnswer = {
|
|
76
|
+
documents: Array<{
|
|
77
|
+
id: string
|
|
78
|
+
orgId: string
|
|
79
|
+
knowledgeBaseId: string | null
|
|
80
|
+
title: string
|
|
81
|
+
fileName: string | null
|
|
82
|
+
mimeType: string | null
|
|
83
|
+
byteSize: number | null
|
|
84
|
+
status: 'scanning' | 'pending' | 'processing' | 'ready' | 'failed' | 'blocked'
|
|
85
|
+
searchable: boolean
|
|
86
|
+
error: string | null
|
|
87
|
+
errorKind: string | null
|
|
88
|
+
viewUrl: string | null
|
|
89
|
+
createdBy: string | null
|
|
90
|
+
addedByKey: {
|
|
91
|
+
name: string | null
|
|
92
|
+
} | null
|
|
93
|
+
source: {
|
|
94
|
+
id: string
|
|
95
|
+
name: string
|
|
96
|
+
url: string | null
|
|
97
|
+
} | null
|
|
98
|
+
createdAt: string
|
|
99
|
+
updatedAt: string
|
|
100
|
+
}>
|
|
101
|
+
nextCursor: string | null
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type DocumentAnswer = {
|
|
105
|
+
document: {
|
|
106
|
+
id: string
|
|
107
|
+
orgId: string
|
|
108
|
+
knowledgeBaseId: string | null
|
|
109
|
+
title: string
|
|
110
|
+
fileName: string | null
|
|
111
|
+
mimeType: string | null
|
|
112
|
+
byteSize: number | null
|
|
113
|
+
status: 'scanning' | 'pending' | 'processing' | 'ready' | 'failed' | 'blocked'
|
|
114
|
+
searchable: boolean
|
|
115
|
+
error: string | null
|
|
116
|
+
errorKind: string | null
|
|
117
|
+
viewUrl: string | null
|
|
118
|
+
createdBy: string | null
|
|
119
|
+
addedByKey: {
|
|
120
|
+
name: string | null
|
|
121
|
+
} | null
|
|
122
|
+
source: {
|
|
123
|
+
id: string
|
|
124
|
+
name: string
|
|
125
|
+
url: string | null
|
|
126
|
+
} | null
|
|
127
|
+
createdAt: string
|
|
128
|
+
updatedAt: string
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export type UploadedAnswer = {
|
|
133
|
+
document: {
|
|
134
|
+
id: string
|
|
135
|
+
orgId: string
|
|
136
|
+
knowledgeBaseId: string | null
|
|
137
|
+
title: string
|
|
138
|
+
fileName: string | null
|
|
139
|
+
mimeType: string | null
|
|
140
|
+
byteSize: number | null
|
|
141
|
+
status: 'scanning' | 'pending' | 'processing' | 'ready' | 'failed' | 'blocked'
|
|
142
|
+
searchable: boolean
|
|
143
|
+
error: string | null
|
|
144
|
+
errorKind: string | null
|
|
145
|
+
viewUrl: string | null
|
|
146
|
+
createdBy: string | null
|
|
147
|
+
addedByKey: {
|
|
148
|
+
name: string | null
|
|
149
|
+
} | null
|
|
150
|
+
source: {
|
|
151
|
+
id: string
|
|
152
|
+
name: string
|
|
153
|
+
url: string | null
|
|
154
|
+
} | null
|
|
155
|
+
createdAt: string
|
|
156
|
+
updatedAt: string
|
|
157
|
+
}
|
|
158
|
+
duplicate: boolean
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export type DocumentDetailsAnswer = {
|
|
162
|
+
document: {
|
|
163
|
+
id: string
|
|
164
|
+
orgId: string
|
|
165
|
+
knowledgeBaseId: string | null
|
|
166
|
+
title: string
|
|
167
|
+
fileName: string | null
|
|
168
|
+
mimeType: string | null
|
|
169
|
+
byteSize: number | null
|
|
170
|
+
status: 'scanning' | 'pending' | 'processing' | 'ready' | 'failed' | 'blocked'
|
|
171
|
+
searchable: boolean
|
|
172
|
+
error: string | null
|
|
173
|
+
errorKind: string | null
|
|
174
|
+
viewUrl: string | null
|
|
175
|
+
createdBy: string | null
|
|
176
|
+
addedByKey: {
|
|
177
|
+
name: string | null
|
|
178
|
+
} | null
|
|
179
|
+
source: {
|
|
180
|
+
id: string
|
|
181
|
+
name: string
|
|
182
|
+
url: string | null
|
|
183
|
+
} | null
|
|
184
|
+
createdAt: string
|
|
185
|
+
updatedAt: string
|
|
186
|
+
knowledgeBase: {
|
|
187
|
+
id: string
|
|
188
|
+
name: string
|
|
189
|
+
} | null
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export type StatusesAnswer = {
|
|
194
|
+
documents: Array<{
|
|
195
|
+
id: string
|
|
196
|
+
orgId: string
|
|
197
|
+
knowledgeBaseId: string | null
|
|
198
|
+
title: string
|
|
199
|
+
fileName: string | null
|
|
200
|
+
mimeType: string | null
|
|
201
|
+
byteSize: number | null
|
|
202
|
+
status: 'scanning' | 'pending' | 'processing' | 'ready' | 'failed' | 'blocked'
|
|
203
|
+
searchable: boolean
|
|
204
|
+
error: string | null
|
|
205
|
+
errorKind: string | null
|
|
206
|
+
viewUrl: string | null
|
|
207
|
+
createdBy: string | null
|
|
208
|
+
addedByKey: {
|
|
209
|
+
name: string | null
|
|
210
|
+
} | null
|
|
211
|
+
source: {
|
|
212
|
+
id: string
|
|
213
|
+
name: string
|
|
214
|
+
url: string | null
|
|
215
|
+
} | null
|
|
216
|
+
createdAt: string
|
|
217
|
+
updatedAt: string
|
|
218
|
+
}>
|
|
219
|
+
missing: string[]
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export type DocumentTextAnswer = {
|
|
223
|
+
id: string
|
|
224
|
+
title: string
|
|
225
|
+
knowledgeBase: {
|
|
226
|
+
id: string
|
|
227
|
+
name: string
|
|
228
|
+
}
|
|
229
|
+
pageCount: number | null
|
|
230
|
+
pages: Array<{
|
|
231
|
+
page: number | null
|
|
232
|
+
text: string
|
|
233
|
+
}>
|
|
234
|
+
nextPage: number | null
|
|
235
|
+
cut: boolean
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export type SearchAnswer = {
|
|
239
|
+
results: Array<{
|
|
240
|
+
documentId: string
|
|
241
|
+
title: string
|
|
242
|
+
knowledgeBase: {
|
|
243
|
+
id: string
|
|
244
|
+
name: string
|
|
245
|
+
} | null
|
|
246
|
+
page: number | null
|
|
247
|
+
snippet: string
|
|
248
|
+
score: number
|
|
249
|
+
viewUrl: string
|
|
250
|
+
openUrl: string
|
|
251
|
+
fileName: string | null
|
|
252
|
+
mimeType: string | null
|
|
253
|
+
updatedAt: string
|
|
254
|
+
documentUpdatedAt: string | null
|
|
255
|
+
addedBy: string | null
|
|
256
|
+
addedByKey: {
|
|
257
|
+
name: string | null
|
|
258
|
+
} | null
|
|
259
|
+
text?: string
|
|
260
|
+
passageId?: string
|
|
261
|
+
}>
|
|
262
|
+
total: number
|
|
263
|
+
facets: {
|
|
264
|
+
knowledgeBases: Array<{
|
|
265
|
+
id: string
|
|
266
|
+
name: string
|
|
267
|
+
count: number
|
|
268
|
+
}>
|
|
269
|
+
types: Array<{
|
|
270
|
+
type: string
|
|
271
|
+
count: number
|
|
272
|
+
}>
|
|
273
|
+
addedBy: Array<{
|
|
274
|
+
userId: string
|
|
275
|
+
count: number
|
|
276
|
+
}>
|
|
277
|
+
updated: Array<{
|
|
278
|
+
range: string
|
|
279
|
+
count: number
|
|
280
|
+
}>
|
|
281
|
+
}
|
|
282
|
+
mode: 'hybrid' | 'text'
|
|
283
|
+
correctedQuery?: string
|
|
284
|
+
timing: {
|
|
285
|
+
totalMs: number
|
|
286
|
+
embedMs: number
|
|
287
|
+
engineMs: number
|
|
288
|
+
recheckMs: number
|
|
289
|
+
queryVectorCached: boolean
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export type AgentBasesAnswer = {
|
|
294
|
+
bases: Array<{
|
|
295
|
+
id: string
|
|
296
|
+
name: string
|
|
297
|
+
layer: 'workspace' | 'organization'
|
|
298
|
+
files: number
|
|
299
|
+
picked: boolean
|
|
300
|
+
unavailable: 'not_shared' | 'other_workspace' | null
|
|
301
|
+
}>
|
|
302
|
+
canChange: boolean
|
|
303
|
+
customerFacing: {
|
|
304
|
+
setUpForCustomers: boolean
|
|
305
|
+
channels: string[]
|
|
306
|
+
publicChat: boolean
|
|
307
|
+
embeddedChat: boolean
|
|
308
|
+
apps: string[]
|
|
309
|
+
voice: boolean
|
|
310
|
+
} | null
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** A source as a file or an API call carries it, and as whoever manages its base reads it back. */
|
|
314
|
+
export type SourceSpec = {
|
|
315
|
+
kind: 'api_pull'
|
|
316
|
+
name: string
|
|
317
|
+
schedule: 'manual' | 'hourly' | 'every_6_hours' | 'daily'
|
|
318
|
+
keepRemovedDays: 1 | 7 | 30
|
|
319
|
+
config: {
|
|
320
|
+
listUrl: string
|
|
321
|
+
itemsAt: string
|
|
322
|
+
paging: {
|
|
323
|
+
kind: 'link_header'
|
|
324
|
+
} | {
|
|
325
|
+
kind: 'next_url'
|
|
326
|
+
at: string
|
|
327
|
+
} | {
|
|
328
|
+
kind: 'page'
|
|
329
|
+
param: string
|
|
330
|
+
first: number
|
|
331
|
+
} | {
|
|
332
|
+
kind: 'cursor'
|
|
333
|
+
param: string
|
|
334
|
+
at: string
|
|
335
|
+
}
|
|
336
|
+
changes: {
|
|
337
|
+
kind: 'updated_since'
|
|
338
|
+
param: string
|
|
339
|
+
format: 'iso' | 'unix'
|
|
340
|
+
} | {
|
|
341
|
+
kind: 'newest_first'
|
|
342
|
+
} | {
|
|
343
|
+
kind: 'full'
|
|
344
|
+
}
|
|
345
|
+
fields: {
|
|
346
|
+
id: string
|
|
347
|
+
title: string
|
|
348
|
+
updatedAt: string
|
|
349
|
+
link?: string
|
|
350
|
+
content: {
|
|
351
|
+
kind: 'inline'
|
|
352
|
+
at: string
|
|
353
|
+
format: 'html' | 'markdown' | 'text'
|
|
354
|
+
} | {
|
|
355
|
+
kind: 'file'
|
|
356
|
+
at: string
|
|
357
|
+
}
|
|
358
|
+
deletedWhen?: {
|
|
359
|
+
at: string
|
|
360
|
+
equals: string | boolean
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
requestsPerSecond?: number
|
|
364
|
+
}
|
|
365
|
+
auth: {
|
|
366
|
+
kind: 'none'
|
|
367
|
+
} | {
|
|
368
|
+
kind: 'bearer'
|
|
369
|
+
} | {
|
|
370
|
+
kind: 'header'
|
|
371
|
+
name: string
|
|
372
|
+
} | {
|
|
373
|
+
kind: 'basic'
|
|
374
|
+
username: string
|
|
375
|
+
} | {
|
|
376
|
+
kind: 'oauth_client_credentials'
|
|
377
|
+
tokenUrl: string
|
|
378
|
+
clientId: string
|
|
379
|
+
scope?: string
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** What adding a source, or testing one, sends: its shape, with the schedule and retention optional, and the credential its sign-in needs. */
|
|
384
|
+
export type SourceInput = {
|
|
385
|
+
kind: 'api_pull'
|
|
386
|
+
name: string
|
|
387
|
+
schedule?: 'manual' | 'hourly' | 'every_6_hours' | 'daily'
|
|
388
|
+
keepRemovedDays?: 1 | 7 | 30
|
|
389
|
+
config: {
|
|
390
|
+
listUrl: string
|
|
391
|
+
itemsAt: string
|
|
392
|
+
paging: {
|
|
393
|
+
kind: 'link_header'
|
|
394
|
+
} | {
|
|
395
|
+
kind: 'next_url'
|
|
396
|
+
at: string
|
|
397
|
+
} | {
|
|
398
|
+
kind: 'page'
|
|
399
|
+
param: string
|
|
400
|
+
first: number
|
|
401
|
+
} | {
|
|
402
|
+
kind: 'cursor'
|
|
403
|
+
param: string
|
|
404
|
+
at: string
|
|
405
|
+
}
|
|
406
|
+
changes: {
|
|
407
|
+
kind: 'updated_since'
|
|
408
|
+
param: string
|
|
409
|
+
format: 'iso' | 'unix'
|
|
410
|
+
} | {
|
|
411
|
+
kind: 'newest_first'
|
|
412
|
+
} | {
|
|
413
|
+
kind: 'full'
|
|
414
|
+
}
|
|
415
|
+
fields: {
|
|
416
|
+
id: string
|
|
417
|
+
title: string
|
|
418
|
+
updatedAt: string
|
|
419
|
+
link?: string
|
|
420
|
+
content: {
|
|
421
|
+
kind: 'inline'
|
|
422
|
+
at: string
|
|
423
|
+
format: 'html' | 'markdown' | 'text'
|
|
424
|
+
} | {
|
|
425
|
+
kind: 'file'
|
|
426
|
+
at: string
|
|
427
|
+
}
|
|
428
|
+
deletedWhen?: {
|
|
429
|
+
at: string
|
|
430
|
+
equals: string | boolean
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
requestsPerSecond?: number
|
|
434
|
+
}
|
|
435
|
+
auth: {
|
|
436
|
+
kind: 'none'
|
|
437
|
+
} | {
|
|
438
|
+
kind: 'bearer'
|
|
439
|
+
} | {
|
|
440
|
+
kind: 'header'
|
|
441
|
+
name: string
|
|
442
|
+
} | {
|
|
443
|
+
kind: 'basic'
|
|
444
|
+
username: string
|
|
445
|
+
} | {
|
|
446
|
+
kind: 'oauth_client_credentials'
|
|
447
|
+
tokenUrl: string
|
|
448
|
+
clientId: string
|
|
449
|
+
scope?: string
|
|
450
|
+
}
|
|
451
|
+
credential?: string
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** A change to a source: only the parts given change. */
|
|
455
|
+
export type SourcePatch = {
|
|
456
|
+
name?: string
|
|
457
|
+
schedule?: 'manual' | 'hourly' | 'every_6_hours' | 'daily'
|
|
458
|
+
keepRemovedDays?: 1 | 7 | 30
|
|
459
|
+
config?: {
|
|
460
|
+
listUrl: string
|
|
461
|
+
itemsAt: string
|
|
462
|
+
paging: {
|
|
463
|
+
kind: 'link_header'
|
|
464
|
+
} | {
|
|
465
|
+
kind: 'next_url'
|
|
466
|
+
at: string
|
|
467
|
+
} | {
|
|
468
|
+
kind: 'page'
|
|
469
|
+
param: string
|
|
470
|
+
first: number
|
|
471
|
+
} | {
|
|
472
|
+
kind: 'cursor'
|
|
473
|
+
param: string
|
|
474
|
+
at: string
|
|
475
|
+
}
|
|
476
|
+
changes: {
|
|
477
|
+
kind: 'updated_since'
|
|
478
|
+
param: string
|
|
479
|
+
format: 'iso' | 'unix'
|
|
480
|
+
} | {
|
|
481
|
+
kind: 'newest_first'
|
|
482
|
+
} | {
|
|
483
|
+
kind: 'full'
|
|
484
|
+
}
|
|
485
|
+
fields: {
|
|
486
|
+
id: string
|
|
487
|
+
title: string
|
|
488
|
+
updatedAt: string
|
|
489
|
+
link?: string
|
|
490
|
+
content: {
|
|
491
|
+
kind: 'inline'
|
|
492
|
+
at: string
|
|
493
|
+
format: 'html' | 'markdown' | 'text'
|
|
494
|
+
} | {
|
|
495
|
+
kind: 'file'
|
|
496
|
+
at: string
|
|
497
|
+
}
|
|
498
|
+
deletedWhen?: {
|
|
499
|
+
at: string
|
|
500
|
+
equals: string | boolean
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
requestsPerSecond?: number
|
|
504
|
+
}
|
|
505
|
+
auth?: {
|
|
506
|
+
kind: 'none'
|
|
507
|
+
} | {
|
|
508
|
+
kind: 'bearer'
|
|
509
|
+
} | {
|
|
510
|
+
kind: 'header'
|
|
511
|
+
name: string
|
|
512
|
+
} | {
|
|
513
|
+
kind: 'basic'
|
|
514
|
+
username: string
|
|
515
|
+
} | {
|
|
516
|
+
kind: 'oauth_client_credentials'
|
|
517
|
+
tokenUrl: string
|
|
518
|
+
clientId: string
|
|
519
|
+
scope?: string
|
|
520
|
+
}
|
|
521
|
+
credential?: string
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/** Where a source's list is, how to page and follow changes, and which field of an item holds what. */
|
|
525
|
+
export type PullConfig = {
|
|
526
|
+
listUrl: string
|
|
527
|
+
itemsAt: string
|
|
528
|
+
paging: {
|
|
529
|
+
kind: 'link_header'
|
|
530
|
+
} | {
|
|
531
|
+
kind: 'next_url'
|
|
532
|
+
at: string
|
|
533
|
+
} | {
|
|
534
|
+
kind: 'page'
|
|
535
|
+
param: string
|
|
536
|
+
first: number
|
|
537
|
+
} | {
|
|
538
|
+
kind: 'cursor'
|
|
539
|
+
param: string
|
|
540
|
+
at: string
|
|
541
|
+
}
|
|
542
|
+
changes: {
|
|
543
|
+
kind: 'updated_since'
|
|
544
|
+
param: string
|
|
545
|
+
format: 'iso' | 'unix'
|
|
546
|
+
} | {
|
|
547
|
+
kind: 'newest_first'
|
|
548
|
+
} | {
|
|
549
|
+
kind: 'full'
|
|
550
|
+
}
|
|
551
|
+
fields: {
|
|
552
|
+
id: string
|
|
553
|
+
title: string
|
|
554
|
+
updatedAt: string
|
|
555
|
+
link?: string
|
|
556
|
+
content: {
|
|
557
|
+
kind: 'inline'
|
|
558
|
+
at: string
|
|
559
|
+
format: 'html' | 'markdown' | 'text'
|
|
560
|
+
} | {
|
|
561
|
+
kind: 'file'
|
|
562
|
+
at: string
|
|
563
|
+
}
|
|
564
|
+
deletedWhen?: {
|
|
565
|
+
at: string
|
|
566
|
+
equals: string | boolean
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
requestsPerSecond?: number
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/** How the platform signs in to a source; the credential itself travels beside it and is never read back. */
|
|
573
|
+
export type SourceAuth = {
|
|
574
|
+
kind: 'none'
|
|
575
|
+
} | {
|
|
576
|
+
kind: 'bearer'
|
|
577
|
+
} | {
|
|
578
|
+
kind: 'header'
|
|
579
|
+
name: string
|
|
580
|
+
} | {
|
|
581
|
+
kind: 'basic'
|
|
582
|
+
username: string
|
|
583
|
+
} | {
|
|
584
|
+
kind: 'oauth_client_credentials'
|
|
585
|
+
tokenUrl: string
|
|
586
|
+
clientId: string
|
|
587
|
+
scope?: string
|
|
588
|
+
}
|