@xenosystem/blocks 0.4.0 → 0.5.0
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/dist/auth/index.js +2 -0
- package/dist/chunk-2KG3PWR4.js +17 -0
- package/dist/chunk-DJJGZ6U4.js +215 -0
- package/dist/data/duckdb.d.ts +123 -0
- package/dist/data/duckdb.js +142 -0
- package/dist/data/index.d.ts +3255 -0
- package/dist/data/index.js +7185 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -1
- package/dist/ops/index.js +2 -1
- package/dist/time/index.d.ts +2261 -0
- package/dist/time/index.js +4658 -0
- package/dist/transport-B1cdciP8.d.ts +787 -0
- package/dist/trust/index.js +2 -0
- package/dist/{xterm-R3GIKSHA.js → xterm-R3ZKBPPC.js} +1 -0
- package/package.json +100 -80
|
@@ -0,0 +1,787 @@
|
|
|
1
|
+
import { XenoValue, XenoQuery, XenoColumn, XenoSchemaCatalog, XenoResultSet } from '@xenosystem/data-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The `xeno.core.connectors` contract.
|
|
5
|
+
*
|
|
6
|
+
* This panel is the data family's **capability holder** and its **resolver**. Every other panel in
|
|
7
|
+
* the family declares `storage.local` and nothing else *because this one exists*. It is also the one
|
|
8
|
+
* place in the catalog where getting security wrong has consequences beyond a bad pixel.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* How a REST source pages — **declared, never guessed**.
|
|
15
|
+
*
|
|
16
|
+
* The four styles below are the whole of what the survey found in real connector configs, and they
|
|
17
|
+
* are genuinely incompatible: `offset` counts rows, `page` counts pages, `cursor` carries an opaque
|
|
18
|
+
* token forward, and `link-header` puts the next URL in an RFC 8288 header where no amount of body
|
|
19
|
+
* inspection will find it. Collapsing them into one "pagination" bag with three optional parameter
|
|
20
|
+
* names — which is what the previous shape did — makes every one of them a special case at the call
|
|
21
|
+
* site, and makes `page.total` a guess.
|
|
22
|
+
*
|
|
23
|
+
* `hasMore` is derived by **over-fetching one row** for every style except `cursor`/`link-header`,
|
|
24
|
+
* which say so themselves. A source that cannot report an exact total must leave `page.total`
|
|
25
|
+
* undefined; a locally-filtered REST source reporting its unfiltered total is a silent lie (§5).
|
|
26
|
+
*/
|
|
27
|
+
type XenoRestPaging =
|
|
28
|
+
/** One request, whatever comes back. */
|
|
29
|
+
{
|
|
30
|
+
style: 'none';
|
|
31
|
+
}
|
|
32
|
+
/** `?limit=50&offset=100` — the row-counting style. */
|
|
33
|
+
| {
|
|
34
|
+
style: 'offset';
|
|
35
|
+
limitParam: string;
|
|
36
|
+
offsetParam: string;
|
|
37
|
+
}
|
|
38
|
+
/** `?per_page=50&page=3` — the page-counting style. `firstPage` is 1 for most APIs, 0 for some. */
|
|
39
|
+
| {
|
|
40
|
+
style: 'page';
|
|
41
|
+
limitParam: string;
|
|
42
|
+
pageParam: string;
|
|
43
|
+
firstPage?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* `?limit=50&cursor=abc` — the opaque-token style.
|
|
47
|
+
*
|
|
48
|
+
* `cursorPath` says where the NEXT cursor lives in the response body; without it the source can
|
|
49
|
+
* only ever return its first page, which is a silent one-page ceiling rather than an error.
|
|
50
|
+
*/
|
|
51
|
+
| {
|
|
52
|
+
style: 'cursor';
|
|
53
|
+
cursorParam: string;
|
|
54
|
+
cursorPath: string;
|
|
55
|
+
limitParam?: string;
|
|
56
|
+
hasMorePath?: string;
|
|
57
|
+
}
|
|
58
|
+
/** RFC 8288 `Link: <…>; rel="next"`. GitHub's style, and invisible from the body. */
|
|
59
|
+
| {
|
|
60
|
+
style: 'link-header';
|
|
61
|
+
limitParam?: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Header names that may never appear in a persisted config.
|
|
65
|
+
*
|
|
66
|
+
* §6 L7 is a live bug in a shipped connector: it masks `password` on serialize and writes the mask
|
|
67
|
+
* back verbatim on load (so a round-trip *destroys* the credential), and it masks `password`
|
|
68
|
+
* **only** — leaving `headers`, where `Authorization: Bearer …` lives, in the clear. The fix is not
|
|
69
|
+
* a better mask. *A mask is not a security boundary.* The fix is to make the secret unrepresentable
|
|
70
|
+
* in the config at all: these names are refused at execution time and the value must come from
|
|
71
|
+
* `credentialRef`, which the host resolves on its own side of the transport seam.
|
|
72
|
+
*/
|
|
73
|
+
declare const SECRET_HEADER_NAMES: readonly string[];
|
|
74
|
+
/**
|
|
75
|
+
* Reject header names that must not be persisted in a config.
|
|
76
|
+
*
|
|
77
|
+
* @param headers - Static headers from a connection config.
|
|
78
|
+
* @returns The offending names, lower-cased. Empty when the config is clean.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* secretHeaderNames({ Accept: 'application/json' }) // → []
|
|
83
|
+
* secretHeaderNames({ Authorization: 'Bearer abc' }) // → ['authorization']
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
declare function secretHeaderNames(headers: Record<string, string> | undefined): string[];
|
|
87
|
+
/**
|
|
88
|
+
* Normalize the deprecated `pagination` bag forward into a {@link XenoRestPaging}.
|
|
89
|
+
*
|
|
90
|
+
* An existing `.xapp` carrying the old shape keeps working; nothing has to be migrated on disk.
|
|
91
|
+
*
|
|
92
|
+
* @param paging - The explicit declaration, when present.
|
|
93
|
+
* @param legacy - The deprecated bag.
|
|
94
|
+
* @returns A paging declaration, defaulting to `{style: 'none'}`.
|
|
95
|
+
*/
|
|
96
|
+
declare function normalizePaging(paging: XenoRestPaging | undefined, legacy: {
|
|
97
|
+
limitParam?: string;
|
|
98
|
+
offsetParam?: string;
|
|
99
|
+
cursorParam?: string;
|
|
100
|
+
} | undefined): XenoRestPaging;
|
|
101
|
+
/** Every connection kind. */
|
|
102
|
+
type XenoConnectionKind = 'inline' | 'file.csv' | 'file.json' | 'file.parquet' | 'rest' | 'graphql' | 'sql.postgres' | 'sql.mysql' | 'sql.sqlite' | 'sql.duckdb' | 'xeno.sheets' | 'xeno.post' | 'xeno.ledger';
|
|
103
|
+
/**
|
|
104
|
+
* The connection config, **discriminated on `kind`**.
|
|
105
|
+
*
|
|
106
|
+
* ## Why this is a union and not a bag
|
|
107
|
+
*
|
|
108
|
+
* sheets' `DatabaseConnector` carries one flat config object for every kind, and that is precisely
|
|
109
|
+
* why its declared `graphql` kind falls into the *database* error branch: `executeQuery` switches on
|
|
110
|
+
* `rest` and lets everything else reach the SQL path, so a plain HTTP source dies with *"Database
|
|
111
|
+
* queries require main process IPC"* (§6 L13, a live bug).
|
|
112
|
+
*
|
|
113
|
+
* With a discriminated union the compiler forces every branch to be handled and forces every kind to
|
|
114
|
+
* carry only the fields it can actually use. **A missing `graphql` branch is a compile error, not a
|
|
115
|
+
* runtime message about the wrong subsystem.**
|
|
116
|
+
*/
|
|
117
|
+
type XenoConnectionConfig = {
|
|
118
|
+
kind: 'inline';
|
|
119
|
+
rows: unknown;
|
|
120
|
+
primaryKey?: string[];
|
|
121
|
+
} | {
|
|
122
|
+
kind: 'file.csv';
|
|
123
|
+
/** An OPAQUE handle minted by the host's picker. **Never a path** — the panel cannot enumerate. */
|
|
124
|
+
fileRef: string;
|
|
125
|
+
/** Field separator. Absent means "sniff it" — see `sniffDelimiter`. */
|
|
126
|
+
delimiter?: string;
|
|
127
|
+
/** First row is a header. Default `true`. */
|
|
128
|
+
hasHeader?: boolean;
|
|
129
|
+
/** Text encoding the host decodes with. Default `'utf-8'`. */
|
|
130
|
+
encoding?: string;
|
|
131
|
+
/** Rows to skip before the header (banner lines above a CSV are common in exports). */
|
|
132
|
+
skipRows?: number;
|
|
133
|
+
/** Quote character. Default `"`. */
|
|
134
|
+
quote?: string;
|
|
135
|
+
} | {
|
|
136
|
+
kind: 'file.json';
|
|
137
|
+
/** An OPAQUE handle minted by the host's picker. */
|
|
138
|
+
fileRef: string;
|
|
139
|
+
rootPath?: string;
|
|
140
|
+
/** Text encoding. Default `'utf-8'`. */
|
|
141
|
+
encoding?: string;
|
|
142
|
+
/** The file is newline-delimited JSON (one record per line) rather than one document. */
|
|
143
|
+
ndjson?: boolean;
|
|
144
|
+
} | {
|
|
145
|
+
kind: 'file.parquet';
|
|
146
|
+
fileRef: string;
|
|
147
|
+
} | {
|
|
148
|
+
kind: 'rest';
|
|
149
|
+
url: string;
|
|
150
|
+
method?: 'GET' | 'POST';
|
|
151
|
+
/** Header NAMES only — a value carrying a credential belongs behind `credentialRef`. */
|
|
152
|
+
headerNames?: string[];
|
|
153
|
+
/**
|
|
154
|
+
* Static headers.
|
|
155
|
+
*
|
|
156
|
+
* **Non-secret only, and enforced rather than requested:** {@link SECRET_HEADER_NAMES} are
|
|
157
|
+
* rejected at execution time, because a config bag is persisted verbatim into `.xapp` and one
|
|
158
|
+
* shipped connector already ships that exact bug (§6 L7 — it masks `password` and leaves
|
|
159
|
+
* `Authorization: Bearer …` in the clear). Anything authenticating belongs behind
|
|
160
|
+
* `credentialRef`, which the host resolves on its own side of the transport seam.
|
|
161
|
+
*/
|
|
162
|
+
headers?: Record<string, string>;
|
|
163
|
+
/** Static, non-secret query parameters. */
|
|
164
|
+
params?: Record<string, string>;
|
|
165
|
+
/** JSONPath-ish root at which the row array lives. */
|
|
166
|
+
rootPath?: string;
|
|
167
|
+
/**
|
|
168
|
+
* How this source pages.
|
|
169
|
+
*
|
|
170
|
+
* @deprecated Superseded by {@link XenoRestPaging}. Still read, and normalized forward by
|
|
171
|
+
* `normalizePaging`, so an existing `.xapp` keeps working.
|
|
172
|
+
*/
|
|
173
|
+
pagination?: {
|
|
174
|
+
limitParam?: string;
|
|
175
|
+
offsetParam?: string;
|
|
176
|
+
cursorParam?: string;
|
|
177
|
+
};
|
|
178
|
+
/** How this source pages, declared explicitly. */
|
|
179
|
+
paging?: XenoRestPaging;
|
|
180
|
+
/** Path to an exact total, when the source reports one (implies `exactTotal`). */
|
|
181
|
+
totalPath?: string;
|
|
182
|
+
} | {
|
|
183
|
+
kind: 'graphql';
|
|
184
|
+
url: string;
|
|
185
|
+
/** The document. Host-validated when `allowRawQueries` is off. */
|
|
186
|
+
document: string;
|
|
187
|
+
variables?: Record<string, XenoValue>;
|
|
188
|
+
rootPath?: string;
|
|
189
|
+
/** Static, non-secret headers. Same enforcement as `rest`. */
|
|
190
|
+
headers?: Record<string, string>;
|
|
191
|
+
/**
|
|
192
|
+
* Variable names to bind paging into.
|
|
193
|
+
*
|
|
194
|
+
* GraphQL pages through *variables*, not query parameters, so the REST paging union does not
|
|
195
|
+
* transfer. Absent means the document handles its own paging.
|
|
196
|
+
*/
|
|
197
|
+
paging?: {
|
|
198
|
+
limitVar?: string;
|
|
199
|
+
offsetVar?: string;
|
|
200
|
+
cursorVar?: string;
|
|
201
|
+
cursorPath?: string;
|
|
202
|
+
};
|
|
203
|
+
/** Path to an exact total. */
|
|
204
|
+
totalPath?: string;
|
|
205
|
+
} | {
|
|
206
|
+
kind: 'sql.postgres' | 'sql.mysql';
|
|
207
|
+
host: string;
|
|
208
|
+
port?: number;
|
|
209
|
+
database: string;
|
|
210
|
+
/** Username only. The password lives behind `credentialRef` and never appears here. */
|
|
211
|
+
user?: string;
|
|
212
|
+
ssl?: boolean;
|
|
213
|
+
/** Default table/view. */
|
|
214
|
+
entity?: string;
|
|
215
|
+
} | {
|
|
216
|
+
kind: 'sql.sqlite';
|
|
217
|
+
fileRef: string;
|
|
218
|
+
entity?: string;
|
|
219
|
+
} | {
|
|
220
|
+
kind: 'sql.duckdb';
|
|
221
|
+
/**
|
|
222
|
+
* `'wasm'` runs DuckDB-WASM over **already-fetched buffers** — real SQL with **zero
|
|
223
|
+
* capabilities**, in-sandbox and web-exportable. `'file'` opens a database on disk and needs a
|
|
224
|
+
* host resolver.
|
|
225
|
+
*/
|
|
226
|
+
mode: 'wasm' | 'file';
|
|
227
|
+
fileRef?: string;
|
|
228
|
+
/** Connection ids whose results are registered as tables for the WASM engine. */
|
|
229
|
+
sourceConnectionIds?: string[];
|
|
230
|
+
entity?: string;
|
|
231
|
+
} | {
|
|
232
|
+
kind: 'xeno.sheets' | 'xeno.post' | 'xeno.ledger';
|
|
233
|
+
resourceId: string;
|
|
234
|
+
};
|
|
235
|
+
/** What a source can do for itself. Drives honest degradation AND `column.aggregatable`. */
|
|
236
|
+
interface XenoConnectorCapabilities {
|
|
237
|
+
pushdown: {
|
|
238
|
+
filter: boolean;
|
|
239
|
+
sort: boolean;
|
|
240
|
+
group: boolean;
|
|
241
|
+
aggregate: boolean;
|
|
242
|
+
join: boolean;
|
|
243
|
+
limit: boolean;
|
|
244
|
+
};
|
|
245
|
+
/** Aggregate functions the source can compute. */
|
|
246
|
+
aggregates?: string[];
|
|
247
|
+
/** Filter operators the source understands. */
|
|
248
|
+
filterOps?: string[];
|
|
249
|
+
/** Schema introspection available. */
|
|
250
|
+
introspect: boolean;
|
|
251
|
+
/** The source pushes updates. */
|
|
252
|
+
live: boolean;
|
|
253
|
+
/** Writes accepted. */
|
|
254
|
+
writable: boolean;
|
|
255
|
+
/**
|
|
256
|
+
* The source can report an EXACT total row count.
|
|
257
|
+
*
|
|
258
|
+
* When `false` the resolver must leave `page.total` undefined and derive `hasMore` from an
|
|
259
|
+
* over-fetch — a locally-filtered REST source reporting its unfiltered total is a silent lie.
|
|
260
|
+
*/
|
|
261
|
+
exactTotal: boolean;
|
|
262
|
+
/** Hard row ceiling. */
|
|
263
|
+
maxRows?: number;
|
|
264
|
+
/**
|
|
265
|
+
* Runtime facilities this kind needs.
|
|
266
|
+
*
|
|
267
|
+
* **`host.resolver` is deliberately NOT a `PanelCapability`.** It is modelled as *absence*: a kind
|
|
268
|
+
* whose `requires` cannot be satisfied in the current substrate renders `health: 'unsupported'`
|
|
269
|
+
* with `HOST_RESOLVER_REQUIRED`. No SDK change, consistent with the rejection of a
|
|
270
|
+
* `PanelHost.services.data` injection.
|
|
271
|
+
*/
|
|
272
|
+
requires?: ('net.fetch' | 'fs.read' | 'host.resolver')[];
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Connection health.
|
|
276
|
+
*
|
|
277
|
+
* `unconfigured` is a **fixable setup gap** and renders info-toned, not as an error — a platform the
|
|
278
|
+
* operator has not finished wiring is not a failure. `unsupported` means "requires a desktop host"
|
|
279
|
+
* in this sandbox.
|
|
280
|
+
*/
|
|
281
|
+
type XenoConnectionHealth = 'unknown' | 'ok' | 'degraded' | 'unconfigured' | 'unsupported' | 'error' | 'expired';
|
|
282
|
+
/** Typed resolver errors. Never a raw 500, never an env-var name. */
|
|
283
|
+
type XenoResolverErrorCode = 'AUTH_EXPIRED' | 'AUTH_REVOKED' | 'UNCONFIGURED' | 'HOST_RESOLVER_REQUIRED' | 'RATE_LIMITED' | 'TIMEOUT' | 'NETWORK'
|
|
284
|
+
/** Its OWN code with its own remedy text — folding it into NETWORK loses the only actionable fix. */
|
|
285
|
+
| 'CORS_BLOCKED' | 'PARSE' | 'SCHEMA_MISMATCH' | 'ROW_LIMIT' | 'UNKNOWN';
|
|
286
|
+
/** Runtime health, never persisted. */
|
|
287
|
+
interface XenoConnectionStatus {
|
|
288
|
+
health: XenoConnectionHealth;
|
|
289
|
+
/** Host-authored sentence, rendered verbatim. */
|
|
290
|
+
message?: string;
|
|
291
|
+
code?: XenoResolverErrorCode;
|
|
292
|
+
/** Epoch ms of the last successful query. */
|
|
293
|
+
lastOkAt?: number;
|
|
294
|
+
/** Absolute epoch ms at which the credential expires. */
|
|
295
|
+
expiresAt?: number;
|
|
296
|
+
}
|
|
297
|
+
/** How often the connection re-queries. */
|
|
298
|
+
interface XenoRefreshPolicy {
|
|
299
|
+
mode: 'manual' | 'interval' | 'live';
|
|
300
|
+
/** Normalized to ms. */
|
|
301
|
+
intervalMs?: number;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* A connection. **Persisted verbatim into `.xapp`, and contains no secret.**
|
|
305
|
+
*/
|
|
306
|
+
interface XenoConnection {
|
|
307
|
+
id: string;
|
|
308
|
+
/** Display name. */
|
|
309
|
+
name: string;
|
|
310
|
+
/** Discriminator, mirroring `config.kind` so a caller can switch before narrowing. */
|
|
311
|
+
kind: XenoConnectionKind;
|
|
312
|
+
/** Kind-specific config. */
|
|
313
|
+
config: XenoConnectionConfig;
|
|
314
|
+
/**
|
|
315
|
+
* `'$cred:<id>[#dataKey]'` — **the only credential surface**. Three independent implementations
|
|
316
|
+
* converged on reference-by-id; nothing else about a secret ever reaches this panel.
|
|
317
|
+
*/
|
|
318
|
+
credentialRef?: string;
|
|
319
|
+
/**
|
|
320
|
+
* Bumped by the host whenever the credential behind `credentialRef` changes.
|
|
321
|
+
*
|
|
322
|
+
* Part of the cache key, so a re-auth invalidates cached results without anyone remembering to.
|
|
323
|
+
*/
|
|
324
|
+
credentialRefRev?: number;
|
|
325
|
+
refresh?: XenoRefreshPolicy;
|
|
326
|
+
capabilities: XenoConnectorCapabilities;
|
|
327
|
+
/** Bumped when the schema changes, so `fields` re-reads without polling. */
|
|
328
|
+
catalogRev?: number;
|
|
329
|
+
/**
|
|
330
|
+
* Column ids forming this source's primary key.
|
|
331
|
+
*
|
|
332
|
+
* 🔴 **Row identity, for every kind — not just `inline`.** A declared key is used verbatim as the
|
|
333
|
+
* row id, which is what lets a selection made in one panel mean the same record in another. The
|
|
334
|
+
* `inline` config has carried this since the beginning; every other kind resolved through the
|
|
335
|
+
* host executor and silently got a *synthesized positional* id instead, so a REST payload with a
|
|
336
|
+
* perfectly good `id` produced `src-…:0:4:d6ntwe` and every drill-down broke on the first sort.
|
|
337
|
+
*
|
|
338
|
+
* Resolution order is: this field → the executor's own `primaryKey` → conservative inference from
|
|
339
|
+
* the returned rows → a synthesized id. Declaring it is always better than being inferred.
|
|
340
|
+
*/
|
|
341
|
+
primaryKey?: string[];
|
|
342
|
+
/** Free-form grouping/tagging. */
|
|
343
|
+
tags?: string[];
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* A connection plus its runtime state.
|
|
347
|
+
*
|
|
348
|
+
* `health` is **rebuilt as `unknown` on load and never persisted** — a stale "healthy" written into
|
|
349
|
+
* an `.xapp` is a lie, and the first thing a user does with a lie is trust it.
|
|
350
|
+
*/
|
|
351
|
+
interface XenoConnectionState {
|
|
352
|
+
connection: XenoConnection;
|
|
353
|
+
status: XenoConnectionStatus;
|
|
354
|
+
/** The last good result, kept so a failed refresh can keep serving rather than blanking. */
|
|
355
|
+
lastGoodAt?: number;
|
|
356
|
+
/** A query is in flight. */
|
|
357
|
+
busy?: boolean;
|
|
358
|
+
}
|
|
359
|
+
/** One field of a host-supplied credential form. */
|
|
360
|
+
interface XenoCredentialField {
|
|
361
|
+
key: string;
|
|
362
|
+
label: string;
|
|
363
|
+
type: 'text' | 'password' | 'select' | 'textarea' | 'number' | 'boolean';
|
|
364
|
+
required?: boolean;
|
|
365
|
+
placeholder?: string;
|
|
366
|
+
/** Help text. */
|
|
367
|
+
description?: string;
|
|
368
|
+
options?: {
|
|
369
|
+
label: string;
|
|
370
|
+
value: string;
|
|
371
|
+
}[];
|
|
372
|
+
/** Show this field only when another field equals one of these values. */
|
|
373
|
+
showWhen?: {
|
|
374
|
+
key: string;
|
|
375
|
+
equals: (string | number | boolean)[];
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* The form descriptor a host sends when a credential is needed.
|
|
380
|
+
*
|
|
381
|
+
* **A descriptor, with no platform names** — the panel renders fields, it does not know that this is
|
|
382
|
+
* Stripe or Postgres. That is what lets one panel serve every connector without growing a registry
|
|
383
|
+
* of vendor-specific UI.
|
|
384
|
+
*/
|
|
385
|
+
interface XenoCredentialFormSpec {
|
|
386
|
+
/** Correlates the form with the `submit_credential` that answers it. */
|
|
387
|
+
requestId: string;
|
|
388
|
+
connectionId: string;
|
|
389
|
+
title: string;
|
|
390
|
+
/** How the credential is obtained. */
|
|
391
|
+
mode: 'form' | 'oauth-redirect' | 'oauth-loopback' | 'none';
|
|
392
|
+
fields?: XenoCredentialField[];
|
|
393
|
+
/** "Where do I get this?" */
|
|
394
|
+
docsUrl?: string;
|
|
395
|
+
/**
|
|
396
|
+
* For loopback OAuth: the EXACT redirect URI to register, rendered **verbatim**.
|
|
397
|
+
*
|
|
398
|
+
* Rendering it verbatim is what prevents the redirect-URI mismatch landmine: one implementation
|
|
399
|
+
* falls back to `localhost:3000` when three env vars are unset, another derives it from the
|
|
400
|
+
* loopback port. Showing the user the real string is the only reliable fix.
|
|
401
|
+
*/
|
|
402
|
+
redirectUri?: string;
|
|
403
|
+
/** Host-authored explanation. */
|
|
404
|
+
message?: string;
|
|
405
|
+
}
|
|
406
|
+
/** What the panel emits when the user submits a credential form. */
|
|
407
|
+
interface XenoCredentialSubmission {
|
|
408
|
+
requestId: string;
|
|
409
|
+
connectionId: string;
|
|
410
|
+
/** The entered values. Held in transient component state only, cleared immediately after. */
|
|
411
|
+
values: Record<string, string | number | boolean>;
|
|
412
|
+
}
|
|
413
|
+
/** The host's answer. **Never contains the values.** */
|
|
414
|
+
interface XenoCredentialResult {
|
|
415
|
+
requestId: string;
|
|
416
|
+
connectionId: string;
|
|
417
|
+
credentialRef?: string;
|
|
418
|
+
credentialRefRev?: number;
|
|
419
|
+
status: XenoConnectionStatus;
|
|
420
|
+
}
|
|
421
|
+
/** A query arriving from a consumer panel. */
|
|
422
|
+
interface XenoConnectorQueryRequest {
|
|
423
|
+
requestId: string;
|
|
424
|
+
query: XenoQuery;
|
|
425
|
+
/**
|
|
426
|
+
* Which panel asked.
|
|
427
|
+
*
|
|
428
|
+
* Correlation is per **(connectionId, requestorPanelId)**, not one global id: this panel is a
|
|
429
|
+
* fan-in, so N concurrent queries from N panels are all legitimate and a single outstanding slot
|
|
430
|
+
* would make each new one cancel the last.
|
|
431
|
+
*/
|
|
432
|
+
requestorPanelId?: string;
|
|
433
|
+
connectionId?: string;
|
|
434
|
+
}
|
|
435
|
+
/** The split of a query into what the source runs and what the pipeline runs locally. */
|
|
436
|
+
interface PushdownSplit {
|
|
437
|
+
/** Executed by the source. */
|
|
438
|
+
pushed: XenoQuery;
|
|
439
|
+
/** Executed locally through `@xenosystem/data-core`. */
|
|
440
|
+
local: XenoQuery;
|
|
441
|
+
/** Op names pushed down, for `result.stats`. */
|
|
442
|
+
pushedOps: string[];
|
|
443
|
+
/** Op names run locally, for `result.stats`. */
|
|
444
|
+
localOps: string[];
|
|
445
|
+
}
|
|
446
|
+
/** What a host-side executor is handed. */
|
|
447
|
+
interface ConnectorExecuteInput {
|
|
448
|
+
connection: XenoConnection;
|
|
449
|
+
/** Only the pushed part. */
|
|
450
|
+
query: XenoQuery;
|
|
451
|
+
/** Row cap already clamped. */
|
|
452
|
+
limit: number;
|
|
453
|
+
}
|
|
454
|
+
/** What a host-side executor returns. */
|
|
455
|
+
interface ConnectorExecuteOutput {
|
|
456
|
+
rows: unknown;
|
|
457
|
+
/** Set when the source knows its exact total AND declares `exactTotal`. */
|
|
458
|
+
total?: number;
|
|
459
|
+
/** More rows exist. Derived by the resolver from an over-fetch when the source cannot say. */
|
|
460
|
+
hasMore?: boolean;
|
|
461
|
+
columns?: XenoColumn[];
|
|
462
|
+
catalog?: XenoSchemaCatalog;
|
|
463
|
+
/**
|
|
464
|
+
* The primary key of the returned rows, when the executor knows it.
|
|
465
|
+
*
|
|
466
|
+
* A SQL executor reading a real catalog knows this for certain; a REST one usually does not.
|
|
467
|
+
* Supplying it outranks inference and produces `derivedFrom: 'introspect'` rather than `'sample'`.
|
|
468
|
+
*/
|
|
469
|
+
primaryKey?: string[];
|
|
470
|
+
error?: {
|
|
471
|
+
code: XenoResolverErrorCode;
|
|
472
|
+
message: string;
|
|
473
|
+
retryable?: boolean;
|
|
474
|
+
};
|
|
475
|
+
scannedRows?: number;
|
|
476
|
+
executionMs?: number;
|
|
477
|
+
}
|
|
478
|
+
/** The panel's serialized state. */
|
|
479
|
+
interface ConnectorsPanelState {
|
|
480
|
+
/** Connections, with `credentialRef` — **never a secret, never `health`**. */
|
|
481
|
+
connections: XenoConnection[];
|
|
482
|
+
/** The last query, per connection. */
|
|
483
|
+
lastQueries?: Record<string, XenoQuery>;
|
|
484
|
+
selectedId?: string;
|
|
485
|
+
}
|
|
486
|
+
/** What the controller exposes to its view. */
|
|
487
|
+
interface ConnectorsViewState {
|
|
488
|
+
connections: XenoConnectionState[];
|
|
489
|
+
selectedId: string | null;
|
|
490
|
+
/** The credential form to render, when the host has asked for one. */
|
|
491
|
+
form: XenoCredentialFormSpec | null;
|
|
492
|
+
/** Connection ids with a query in flight. */
|
|
493
|
+
busy: string[];
|
|
494
|
+
/** The last error per connection. */
|
|
495
|
+
errors: Record<string, XenoConnectionStatus>;
|
|
496
|
+
}
|
|
497
|
+
/** Runtime facilities the current substrate provides. */
|
|
498
|
+
interface SubstrateFacilities {
|
|
499
|
+
netFetch: boolean;
|
|
500
|
+
fsRead: boolean;
|
|
501
|
+
hostResolver: boolean;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Can this connection run here — and if not, why?
|
|
505
|
+
*
|
|
506
|
+
* Returns the honest degraded state rather than a stub. A kind that cannot open a TCP socket in
|
|
507
|
+
* QuickJS renders a disabled card with a reason; it never pretends to work and fails later.
|
|
508
|
+
*
|
|
509
|
+
* @param connection - The connection.
|
|
510
|
+
* @param facilities - What the substrate offers.
|
|
511
|
+
* @returns `null` when supported, else the status to display.
|
|
512
|
+
*/
|
|
513
|
+
declare function evaluateSupport(connection: XenoConnection, facilities: SubstrateFacilities): XenoConnectionStatus | null;
|
|
514
|
+
/**
|
|
515
|
+
* The default capabilities for a kind.
|
|
516
|
+
*
|
|
517
|
+
* This is the capability matrix as code — including the one row that matters most:
|
|
518
|
+
* **`sql.duckdb` in `wasm` mode requires NOTHING.** DuckDB-WASM runs in-sandbox over
|
|
519
|
+
* already-fetched buffers, which is the honest bridge that gives real SQL to a web export and to
|
|
520
|
+
* `iframe-quickjs` with zero capabilities. The same kind in `file` mode needs a host resolver.
|
|
521
|
+
*
|
|
522
|
+
* @param config - The connection config (narrowed by kind).
|
|
523
|
+
* @returns Sensible default capabilities.
|
|
524
|
+
*/
|
|
525
|
+
declare function defaultCapabilitiesFor(config: XenoConnectionConfig): XenoConnectorCapabilities;
|
|
526
|
+
/** A result set carrying a typed resolver error. */
|
|
527
|
+
declare function errorResult(code: XenoResolverErrorCode, message: string, query?: XenoQuery, retryable?: boolean): XenoResultSet;
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* The transport seams — how this panel reaches a network, a file, or a SQL engine **without ever
|
|
531
|
+
* holding a socket, a path, or a secret**.
|
|
532
|
+
*
|
|
533
|
+
* ## Why the seams look like this
|
|
534
|
+
*
|
|
535
|
+
* `XENO DATA PANELS - UNION SPEC.md` §4 rejected a `PanelHost.services.data` injection: it would
|
|
536
|
+
* make the data source invisible in the `.xapp` graph, which is precisely what a Retool competitor
|
|
537
|
+
* sells. The seam it accepted instead is `queryRequest` out / `result` in — *"query execution stays
|
|
538
|
+
* host-side; the panel never reads the network."*
|
|
539
|
+
*
|
|
540
|
+
* That leaves one question this file answers: **who owns the protocol?** If the host owned it too,
|
|
541
|
+
* every host would re-implement REST paging, JSONPath extraction, CSV quoting and the Arrow→JSON
|
|
542
|
+
* conversion, and would get them wrong in a different way each time. So the split is:
|
|
543
|
+
*
|
|
544
|
+
* - **The panel owns the protocol.** Paging, extraction, parsing, schema derivation, error
|
|
545
|
+
* classification, over-fetch, clamping — all of it is in this package, unit-tested in Node.
|
|
546
|
+
* - **The host owns the act.** One `fetch`, one `readFile`, one SQL engine. Each is a narrow
|
|
547
|
+
* function the host implements against whatever its substrate allows: a real `fetch` in-process,
|
|
548
|
+
* a proxied allowlisted one under `iframe-quickjs`, none at all in a web export.
|
|
549
|
+
*
|
|
550
|
+
* ## The property that keeps the secret lock intact
|
|
551
|
+
*
|
|
552
|
+
* A request carries **`credentialRef`, never a credential**. The host resolves the reference and
|
|
553
|
+
* attaches the `Authorization` header (or the connection string, or the API key) on its side of the
|
|
554
|
+
* seam. The panel therefore *cannot* leak a token it never receives — the guarantee is structural,
|
|
555
|
+
* not a discipline someone has to remember.
|
|
556
|
+
*
|
|
557
|
+
* This is also why {@link XenoFetchResponse} is a plain object rather than a DOM `Response`: it has
|
|
558
|
+
* to survive `postMessage` to a sandbox proxy, and a `Response` does not.
|
|
559
|
+
*
|
|
560
|
+
* @module
|
|
561
|
+
*/
|
|
562
|
+
|
|
563
|
+
/** One outbound HTTP request. Structured-cloneable, so it can cross a sandbox boundary. */
|
|
564
|
+
interface XenoFetchRequest {
|
|
565
|
+
/** Absolute URL, query string already built by the panel. */
|
|
566
|
+
url: string;
|
|
567
|
+
method: 'GET' | 'POST';
|
|
568
|
+
/**
|
|
569
|
+
* Non-secret headers only.
|
|
570
|
+
*
|
|
571
|
+
* A header carrying a credential is the host's business: it resolves {@link credentialRef} and
|
|
572
|
+
* merges the result in on its own side. One shipped connector stores `Authorization: Bearer …`
|
|
573
|
+
* in a plain config bag and serializes it in the clear (§6 L7); that is unrepresentable here.
|
|
574
|
+
*/
|
|
575
|
+
headers?: Record<string, string>;
|
|
576
|
+
/** Request body, already serialized. */
|
|
577
|
+
body?: string;
|
|
578
|
+
/**
|
|
579
|
+
* The credential to apply, by reference.
|
|
580
|
+
*
|
|
581
|
+
* **The panel has no idea what this resolves to** and never sees the result.
|
|
582
|
+
*/
|
|
583
|
+
credentialRef?: string;
|
|
584
|
+
/** Abort deadline in ms. The host is free to clamp it down, never up. */
|
|
585
|
+
timeoutMs?: number;
|
|
586
|
+
/** Cancellation, when the substrate supports it. */
|
|
587
|
+
signal?: AbortSignal;
|
|
588
|
+
}
|
|
589
|
+
/** One HTTP response, reduced to what the panel actually reads. */
|
|
590
|
+
interface XenoFetchResponse {
|
|
591
|
+
status: number;
|
|
592
|
+
statusText?: string;
|
|
593
|
+
ok: boolean;
|
|
594
|
+
/** Header names **lower-cased** — HTTP header names are case-insensitive and hosts disagree. */
|
|
595
|
+
headers: Record<string, string>;
|
|
596
|
+
/** The body as text. The panel parses it; the host never guesses a content type. */
|
|
597
|
+
body: string;
|
|
598
|
+
/**
|
|
599
|
+
* The final URL after redirects, when the host can report it.
|
|
600
|
+
*
|
|
601
|
+
* Used only for relative `Link`-header resolution.
|
|
602
|
+
*/
|
|
603
|
+
url?: string;
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* A failure the transport can name.
|
|
607
|
+
*
|
|
608
|
+
* A host that proxies a request **knows** whether it was blocked by CORS, timed out, or never left
|
|
609
|
+
* the machine. Handing that back as a code is what lets §6 L9 hold — `CORS_BLOCKED` keeps its own
|
|
610
|
+
* remedy text instead of being folded into a generic `NETWORK`, which is the difference between
|
|
611
|
+
* *"add this origin to your API's allow-list"* and *"something went wrong"*.
|
|
612
|
+
*/
|
|
613
|
+
declare class ConnectorTransportError extends Error {
|
|
614
|
+
readonly code: XenoResolverErrorCode;
|
|
615
|
+
readonly retryable: boolean;
|
|
616
|
+
constructor(code: XenoResolverErrorCode, message: string, retryable?: boolean);
|
|
617
|
+
}
|
|
618
|
+
/** The network seam. Supplied by the host; absent means "no network here". */
|
|
619
|
+
type XenoConnectorFetch = (request: XenoFetchRequest) => Promise<XenoFetchResponse>;
|
|
620
|
+
/**
|
|
621
|
+
* A file, by opaque reference.
|
|
622
|
+
*
|
|
623
|
+
* **The panel never enumerates paths and never learns one.** `fileRef` is a handle the host minted
|
|
624
|
+
* from its own picker — the same opaque-handle discipline `xeno-shell` uses for host-folder mounts,
|
|
625
|
+
* where raw paths deliberately never cross the bridge.
|
|
626
|
+
*/
|
|
627
|
+
interface XenoFileReadRequest {
|
|
628
|
+
fileRef: string;
|
|
629
|
+
/** Text encoding to decode with. Default `'utf-8'`. */
|
|
630
|
+
encoding?: string;
|
|
631
|
+
/** Read at most this many bytes. The panel asks; the host may return fewer, never more. */
|
|
632
|
+
maxBytes?: number;
|
|
633
|
+
}
|
|
634
|
+
/** What the host returns for a file read. */
|
|
635
|
+
interface XenoFileReadResponse {
|
|
636
|
+
/** Decoded text. */
|
|
637
|
+
text: string;
|
|
638
|
+
/** Display name for the schema/catalog. **Never a full path.** */
|
|
639
|
+
name?: string;
|
|
640
|
+
/** Total size, when known — lets the panel say "truncated" honestly. */
|
|
641
|
+
byteLength?: number;
|
|
642
|
+
/** The host stopped early because {@link XenoFileReadRequest.maxBytes} was hit. */
|
|
643
|
+
truncated?: boolean;
|
|
644
|
+
}
|
|
645
|
+
/** The file seam. Supplied by the host; absent means "no disk here". */
|
|
646
|
+
type XenoConnectorFileReader = (request: XenoFileReadRequest) => Promise<XenoFileReadResponse>;
|
|
647
|
+
/** A table offered to the in-sandbox SQL engine. */
|
|
648
|
+
interface XenoSqlTable {
|
|
649
|
+
/** The name the query will use. Validated before it reaches SQL — see `duckdb.ts`. */
|
|
650
|
+
name: string;
|
|
651
|
+
/** Plain row objects, already fetched by another connection. */
|
|
652
|
+
rows: Record<string, unknown>[];
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* The in-sandbox SQL engine seam.
|
|
656
|
+
*
|
|
657
|
+
* Deliberately **eight lines wide**, and deliberately not `@duckdb/duckdb-wasm`. Rule 8 of the
|
|
658
|
+
* package contract is that no panel takes a heavyweight runtime dependency: DuckDB-WASM is several
|
|
659
|
+
* megabytes of wasm plus a worker, and a static import would impose it on every consumer of this
|
|
660
|
+
* package including the ones that never open a SQL source — exactly the lucide mistake the catalog
|
|
661
|
+
* already paid for once.
|
|
662
|
+
*
|
|
663
|
+
* So the engine is an **optional peer resolved from the host**, like xterm in `panel-terminal`. The
|
|
664
|
+
* package ships a reference adapter on the `./duckdb` subpath; a host that already owns a DuckDB
|
|
665
|
+
* instance implements these three methods against its own and imports nothing extra.
|
|
666
|
+
*/
|
|
667
|
+
interface XenoSqlEngine {
|
|
668
|
+
/**
|
|
669
|
+
* Make `tables` queryable, replacing any previous registration of the same names.
|
|
670
|
+
*
|
|
671
|
+
* @param tables - The source results to expose.
|
|
672
|
+
*/
|
|
673
|
+
register(tables: XenoSqlTable[]): Promise<void>;
|
|
674
|
+
/**
|
|
675
|
+
* Run a SELECT and return plain row objects.
|
|
676
|
+
*
|
|
677
|
+
* The adapter is responsible for Arrow→JSON conversion, including the `BigInt` and timestamp
|
|
678
|
+
* traps that make a naive `JSON.stringify` throw.
|
|
679
|
+
*
|
|
680
|
+
* @param sql - The statement.
|
|
681
|
+
* @returns Plain rows.
|
|
682
|
+
*/
|
|
683
|
+
query(sql: string): Promise<Record<string, unknown>[]>;
|
|
684
|
+
/** Release the engine, its worker, and its registered buffers. */
|
|
685
|
+
dispose?(): Promise<void> | void;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Everything the executor may be given.
|
|
689
|
+
*
|
|
690
|
+
* Every field is optional, and **absence is meaningful**: a missing `fetch` is not a bug to work
|
|
691
|
+
* around, it is a substrate that has no network, and the executor renders that as a visible
|
|
692
|
+
* `unsupported` state rather than failing later at query time.
|
|
693
|
+
*/
|
|
694
|
+
interface ConnectorTransport {
|
|
695
|
+
/** HTTP. Enables `rest`, `graphql` and the first-party `xeno.*` kinds. */
|
|
696
|
+
fetch?: XenoConnectorFetch;
|
|
697
|
+
/** File reads. Enables `file.csv` and `file.json`. */
|
|
698
|
+
readFile?: XenoConnectorFileReader;
|
|
699
|
+
/** In-sandbox SQL. Enables `sql.duckdb` in `wasm` mode. */
|
|
700
|
+
sql?: XenoSqlEngine;
|
|
701
|
+
/** Default request deadline, ms. Default 30 000. */
|
|
702
|
+
timeoutMs?: number;
|
|
703
|
+
/** Ceiling on bytes read from a file. Default 32 MiB. */
|
|
704
|
+
maxFileBytes?: number;
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* The facilities a transport actually provides.
|
|
708
|
+
*
|
|
709
|
+
* Derived, never declared. The controller's own default is optimistic
|
|
710
|
+
* (`{netFetch: true, fsRead: true, hostResolver: true}`), which is right for a trusted host that
|
|
711
|
+
* supplies an executor but wrong as a *general* default: a connection that reports `ok` and then
|
|
712
|
+
* fails at query time is the "stub that fails later" the capability matrix exists to forbid.
|
|
713
|
+
*
|
|
714
|
+
* @param transport - The composed transport.
|
|
715
|
+
* @param hostResolver - Whether a host-side executor was supplied for `sql.*`.
|
|
716
|
+
* @returns What this substrate can honestly claim.
|
|
717
|
+
*
|
|
718
|
+
* @example
|
|
719
|
+
* ```ts
|
|
720
|
+
* const facilities = facilitiesOf({ fetch: browserFetch() }, false)
|
|
721
|
+
* // → { netFetch: true, fsRead: false, hostResolver: false }
|
|
722
|
+
* ```
|
|
723
|
+
*/
|
|
724
|
+
declare function facilitiesOf(transport: ConnectorTransport | undefined, hostResolver: boolean): {
|
|
725
|
+
netFetch: boolean;
|
|
726
|
+
fsRead: boolean;
|
|
727
|
+
hostResolver: boolean;
|
|
728
|
+
};
|
|
729
|
+
/** Options for {@link browserFetch}. */
|
|
730
|
+
interface BrowserFetchOptions {
|
|
731
|
+
/**
|
|
732
|
+
* Resolve a `credentialRef` into headers to merge.
|
|
733
|
+
*
|
|
734
|
+
* Lives on the **host** side of the seam by construction: this function is composed by the host,
|
|
735
|
+
* and the panel only ever passes the reference through.
|
|
736
|
+
*/
|
|
737
|
+
resolveCredential?: (ref: string) => Promise<Record<string, string>> | Record<string, string>;
|
|
738
|
+
/**
|
|
739
|
+
* Hosts this transport may talk to.
|
|
740
|
+
*
|
|
741
|
+
* `undefined` means unrestricted, which is only appropriate in-process. A sandboxed host **must**
|
|
742
|
+
* pass a per-connection allow-list — §4 of the connectors spec makes the proxied REST path
|
|
743
|
+
* "consented at grant time", and an allow-list is what consent means mechanically.
|
|
744
|
+
*/
|
|
745
|
+
allowHosts?: string[];
|
|
746
|
+
/** The page origin, for the CORS heuristic. Defaults to `location.origin` when there is one. */
|
|
747
|
+
origin?: string;
|
|
748
|
+
/** Injectable `fetch`, for tests. */
|
|
749
|
+
impl?: typeof globalThis.fetch;
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* A `fetch`-backed transport for a **trusted, in-process** host.
|
|
753
|
+
*
|
|
754
|
+
* Not the sandboxed path: a sandboxed host proxies these requests through its main process and
|
|
755
|
+
* applies its own allow-list and consent. This exists so that a desktop host — `xeno-apps`, Sheets,
|
|
756
|
+
* Spectra — can wire live REST and GraphQL sources in one line.
|
|
757
|
+
*
|
|
758
|
+
* @param options - Credential resolution, allow-list, and a `fetch` override for tests.
|
|
759
|
+
* @returns A transport fetch.
|
|
760
|
+
*
|
|
761
|
+
* @example
|
|
762
|
+
* ```ts
|
|
763
|
+
* const panel = createConnectorsPanel({
|
|
764
|
+
* transport: { fetch: browserFetch({ resolveCredential: vault.headersFor }) },
|
|
765
|
+
* })
|
|
766
|
+
* ```
|
|
767
|
+
*/
|
|
768
|
+
declare function browserFetch(options?: BrowserFetchOptions): XenoConnectorFetch;
|
|
769
|
+
/**
|
|
770
|
+
* Turn a thrown `fetch` failure into a typed code — **honestly**.
|
|
771
|
+
*
|
|
772
|
+
* The browser deliberately refuses to tell JavaScript whether a cross-origin request was blocked by
|
|
773
|
+
* CORS: both a CORS rejection and a dead network surface as `TypeError: Failed to fetch` with no
|
|
774
|
+
* status. So this does not *claim* CORS; it claims it only when the one distinguishing fact is
|
|
775
|
+
* present — **the request was cross-origin and we know our own origin.** Same-origin failures, and
|
|
776
|
+
* any failure in an environment with no origin at all (Node, a worker without `location`), stay
|
|
777
|
+
* `NETWORK`, because asserting CORS there would be a guess dressed as a diagnosis.
|
|
778
|
+
*
|
|
779
|
+
* @param error - What `fetch` threw.
|
|
780
|
+
* @param target - The parsed request URL.
|
|
781
|
+
* @param origin - The page origin, if known.
|
|
782
|
+
* @param timeoutMs - The deadline that was in force.
|
|
783
|
+
* @returns A typed transport error.
|
|
784
|
+
*/
|
|
785
|
+
declare function classifyBrowserFetchFailure(error: unknown, target: URL, origin: string | undefined, timeoutMs: number): ConnectorTransportError;
|
|
786
|
+
|
|
787
|
+
export { type XenoRefreshPolicy as A, type BrowserFetchOptions as B, type ConnectorExecuteInput as C, type XenoSqlTable as D, browserFetch as E, classifyBrowserFetchFailure as F, defaultCapabilitiesFor as G, errorResult as H, evaluateSupport as I, facilitiesOf as J, normalizePaging as K, secretHeaderNames as L, type PushdownSplit as P, type SubstrateFacilities as S, type XenoSqlEngine as X, type ConnectorExecuteOutput as a, type ConnectorsPanelState as b, type ConnectorsViewState as c, type XenoConnection as d, type XenoCredentialFormSpec as e, type XenoCredentialResult as f, type XenoConnectionStatus as g, type XenoConnectorQueryRequest as h, type XenoRestPaging as i, type XenoConnectorFetch as j, type XenoResolverErrorCode as k, type XenoConnectorFileReader as l, type ConnectorTransport as m, type XenoCredentialField as n, ConnectorTransportError as o, SECRET_HEADER_NAMES as p, type XenoConnectionConfig as q, type XenoConnectionHealth as r, type XenoConnectionKind as s, type XenoConnectionState as t, type XenoConnectorCapabilities as u, type XenoCredentialSubmission as v, type XenoFetchRequest as w, type XenoFetchResponse as x, type XenoFileReadRequest as y, type XenoFileReadResponse as z };
|