@deepexi/datasense-connector 0.0.0-stage → 3.0.1
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 +172 -2
- package/cjs/index.cjs +1015 -0
- package/cjs/runtime.cjs +752 -0
- package/cjs/testing.cjs +3591 -0
- package/errors.d.ts +17 -0
- package/errors.js +53 -0
- package/guest.cjs +2659 -0
- package/index.d.ts +13 -0
- package/index.js +7 -0
- package/internal/location.d.ts +6 -0
- package/internal/location.js +8 -0
- package/package.json +42 -4
- package/presentation.d.ts +38 -0
- package/presentation.js +268 -0
- package/runtime/guest/bridge.d.ts +53 -0
- package/runtime/guest/bridge.js +187 -0
- package/runtime/guest/cache.d.ts +3 -0
- package/runtime/guest/cache.js +42 -0
- package/runtime/guest/crypto.d.ts +13 -0
- package/runtime/guest/crypto.js +177 -0
- package/runtime/guest/ctx.d.ts +30 -0
- package/runtime/guest/ctx.js +124 -0
- package/runtime/guest/db.d.ts +6 -0
- package/runtime/guest/db.js +59 -0
- package/runtime/guest/discover.d.ts +29 -0
- package/runtime/guest/discover.js +51 -0
- package/runtime/guest/failure.d.ts +24 -0
- package/runtime/guest/failure.js +63 -0
- package/runtime/guest/http.d.ts +4 -0
- package/runtime/guest/http.js +204 -0
- package/runtime/guest/index.d.ts +12 -0
- package/runtime/guest/index.js +89 -0
- package/runtime/guest/limits.d.ts +20 -0
- package/runtime/guest/limits.js +12 -0
- package/runtime/guest/loader.d.ts +15 -0
- package/runtime/guest/loader.js +42 -0
- package/runtime/guest/log.d.ts +10 -0
- package/runtime/guest/log.js +36 -0
- package/runtime/guest/map.d.ts +4 -0
- package/runtime/guest/map.js +45 -0
- package/runtime/guest/paginate.d.ts +6 -0
- package/runtime/guest/paginate.js +180 -0
- package/runtime/guest/pull.d.ts +12 -0
- package/runtime/guest/pull.js +150 -0
- package/runtime/guest/push.d.ts +18 -0
- package/runtime/guest/push.js +80 -0
- package/runtime/guest/signal.d.ts +3 -0
- package/runtime/guest/signal.js +12 -0
- package/runtime/guest/time.d.ts +5 -0
- package/runtime/guest/time.js +127 -0
- package/runtime/guest-runtime.d.ts +7 -0
- package/runtime/guest-runtime.js +51 -0
- package/runtime/index.d.ts +18 -0
- package/runtime/index.js +3 -0
- package/runtime/protocol.d.ts +276 -0
- package/runtime/protocol.js +409 -0
- package/runtime/session.d.ts +12 -0
- package/runtime/session.js +191 -0
- package/t.d.ts +18 -0
- package/t.js +26 -0
- package/testing/child.cjs +350 -0
- package/testing/child.d.ts +1 -0
- package/testing/child.js +12 -0
- package/testing/compliance.d.ts +41 -0
- package/testing/compliance.js +291 -0
- package/testing/engine.d.ts +66 -0
- package/testing/engine.js +339 -0
- package/testing/fixtures.d.ts +145 -0
- package/testing/fixtures.js +407 -0
- package/testing/gateway.d.ts +65 -0
- package/testing/gateway.js +402 -0
- package/testing/import-guard.d.ts +57 -0
- package/testing/import-guard.js +187 -0
- package/testing/index.d.ts +33 -0
- package/testing/index.js +10 -0
- package/testing/local-db.d.ts +27 -0
- package/testing/local-db.js +181 -0
- package/testing/pinned-http.d.ts +14 -0
- package/testing/pinned-http.js +125 -0
- package/testing/recorder.d.ts +15 -0
- package/testing/recorder.js +116 -0
- package/testing/redaction.d.ts +28 -0
- package/testing/redaction.js +67 -0
- package/testing/replay.d.ts +50 -0
- package/testing/replay.js +313 -0
- package/testing/schema.d.ts +13 -0
- package/testing/schema.js +65 -0
- package/testing/source-loader.d.ts +19 -0
- package/testing/source-loader.js +238 -0
- package/testing/types.d.ts +79 -0
- package/testing/types.js +18 -0
- package/types.d.ts +302 -0
- package/types.js +13 -0
- package/validate.d.ts +78 -0
- package/validate.js +597 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `./testing` 的公共类型与错误类(本地运行、本地模拟网关、源码加载器共用)。
|
|
3
|
+
*/
|
|
4
|
+
import type { GuestJob, HostMethod } from '../runtime/protocol.js';
|
|
5
|
+
export type { GuestJob, HostMethod };
|
|
6
|
+
/**
|
|
7
|
+
* 宿主:guest 的宿主调用(`http` / `db.*` / `crypto` / `cache.*` / `secret.reveal`)交给它应答。
|
|
8
|
+
* `definition` / `declaration` / `emit` 由执行器自己处理,不交给宿主。实现有本地模拟网关(`createLocalGateway`)与夹具回放。
|
|
9
|
+
* `call` 抛出的错误回给 guest 作为宿主调用失败(`code` 取错误的 `code`);抛 `LocalRunError('DB_DRIVER_MISSING')` 时整次运行以该码结束。
|
|
10
|
+
*/
|
|
11
|
+
export interface LocalHost {
|
|
12
|
+
call(method: HostMethod, payload: unknown): Promise<unknown>;
|
|
13
|
+
close(): Promise<void>;
|
|
14
|
+
/** 可选:遮蔽文本里的 Secret 明文(执行器用它遮蔽结果里的日志与错误文案);本地模拟网关提供。 */
|
|
15
|
+
redact?(text: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* 可选:guest 上报定义摘要(`definition` 调用,push 作业没有)时由执行器先交给宿主,再回复 guest。
|
|
18
|
+
* 摘要在任何出站调用之前上报;CLI 用它按声明的 `hosts` 与本地配置展开出站范围(与平台 discover 运行取摘要相同)。
|
|
19
|
+
*/
|
|
20
|
+
onDefinition?(summary: unknown): void;
|
|
21
|
+
}
|
|
22
|
+
/** 本地运行的结果;`error` 非 null 时运行失败(code 为 guest 的失败码,或 `TIMEOUT` / `DB_DRIVER_MISSING` 等本地码)。 */
|
|
23
|
+
export interface LocalRunResult {
|
|
24
|
+
records: Array<{
|
|
25
|
+
table: string;
|
|
26
|
+
row: Record<string, unknown>;
|
|
27
|
+
}>;
|
|
28
|
+
removals: Array<{
|
|
29
|
+
table: string;
|
|
30
|
+
key: Record<string, unknown>;
|
|
31
|
+
}>;
|
|
32
|
+
checkpoints: Array<{
|
|
33
|
+
seq: number;
|
|
34
|
+
state: unknown;
|
|
35
|
+
}>;
|
|
36
|
+
logs: Array<{
|
|
37
|
+
at: string;
|
|
38
|
+
level: 'info' | 'warn' | 'error';
|
|
39
|
+
message: string;
|
|
40
|
+
}>;
|
|
41
|
+
rejected: Array<{
|
|
42
|
+
requestId: string;
|
|
43
|
+
code: string;
|
|
44
|
+
message: string;
|
|
45
|
+
}>;
|
|
46
|
+
declaration: {
|
|
47
|
+
summary: unknown;
|
|
48
|
+
discover: unknown;
|
|
49
|
+
} | null;
|
|
50
|
+
stopped: boolean;
|
|
51
|
+
error: {
|
|
52
|
+
code: string;
|
|
53
|
+
message: string;
|
|
54
|
+
stack: string | null;
|
|
55
|
+
} | null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 执行器:同一个作业交给不同的执行方式;宿主调用一律交给传入的 host。
|
|
59
|
+
* `limit` 由执行器实现:累计记录数达到 limit 时对 `emit` 回 `{ stop: true }`,超出部分截掉。
|
|
60
|
+
*/
|
|
61
|
+
export interface GuestExecutor {
|
|
62
|
+
run(job: GuestJob, host: LocalHost, options?: {
|
|
63
|
+
limit?: number;
|
|
64
|
+
timeoutMs?: number;
|
|
65
|
+
}): Promise<LocalRunResult>;
|
|
66
|
+
}
|
|
67
|
+
export type LocalRunErrorCode = 'DB_DRIVER_MISSING' | 'HOST_DENIED' | 'TIMEOUT';
|
|
68
|
+
/** 本地运行环境的问题(不是连接器代码的问题)。 */
|
|
69
|
+
export declare class LocalRunError extends Error {
|
|
70
|
+
readonly code: LocalRunErrorCode;
|
|
71
|
+
constructor(code: LocalRunErrorCode, message: string);
|
|
72
|
+
}
|
|
73
|
+
export type SourceLoadErrorCode = 'IMPORT_FORBIDDEN' | 'PATH_ESCAPE' | 'SYMLINK' | 'MANIFEST_INVALID' | 'SYNTAX_ERROR' | 'TOO_LARGE';
|
|
74
|
+
/** 源码目录加载失败;`file` 为相对源码根目录的路径(与整个目录相关时为 null)。 */
|
|
75
|
+
export declare class SourceLoadError extends Error {
|
|
76
|
+
readonly code: SourceLoadErrorCode;
|
|
77
|
+
readonly file: string | null;
|
|
78
|
+
constructor(code: SourceLoadErrorCode, message: string, file?: string | null);
|
|
79
|
+
}
|
package/testing/types.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export class LocalRunError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
constructor(code, message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = 'LocalRunError';
|
|
6
|
+
this.code = code;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class SourceLoadError extends Error {
|
|
10
|
+
code;
|
|
11
|
+
file;
|
|
12
|
+
constructor(code, message, file = null) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'SourceLoadError';
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.file = file;
|
|
17
|
+
}
|
|
18
|
+
}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 连接器 SDK 的声明类型。
|
|
3
|
+
*
|
|
4
|
+
* 这些类型描述连接器作者写出的定义;运行时它们只是普通对象。连接器打包后在 `node:vm` 里执行,
|
|
5
|
+
* `ctx` 由 SDK 的 guest 运行时提供,所以这里不引用任何 Node / Bun 内置模块。
|
|
6
|
+
*
|
|
7
|
+
* 外部写操作(`writes`)与文件来源(`ctx.files`)不在本期:类型里不出现 `writes`,
|
|
8
|
+
* `ctx.files` 的类型是 `never`,访问时抛 `errors.NotAvailable('ctx.files')`。
|
|
9
|
+
*/
|
|
10
|
+
export type ScalarKind = 'string' | 'integer' | 'decimal' | 'boolean' | 'date' | 'instant' | 'json';
|
|
11
|
+
/**
|
|
12
|
+
* 字段的展示元数据,`t.*()` 末尾的可选参数。随声明上报,界面据此显示表单标签、说明与占位提示。
|
|
13
|
+
* `placeholder` 只对标量与 `t.secret()` 有意义;长度上限见 `PRESENTATION_LIMITS`。
|
|
14
|
+
*/
|
|
15
|
+
export interface FieldMeta {
|
|
16
|
+
title?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
placeholder?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ScalarType {
|
|
21
|
+
readonly kind: ScalarKind;
|
|
22
|
+
readonly precision?: number;
|
|
23
|
+
readonly scale?: number;
|
|
24
|
+
readonly title?: string;
|
|
25
|
+
/** 作为表列时是列说明。 */
|
|
26
|
+
readonly description?: string;
|
|
27
|
+
readonly placeholder?: string;
|
|
28
|
+
}
|
|
29
|
+
/** 凭证字段。只能出现在连接配置的顶层;脚本里拿到的是句柄,`exposeToScript` 时可取明文。 */
|
|
30
|
+
export interface SecretType {
|
|
31
|
+
readonly kind: 'secret';
|
|
32
|
+
readonly exposeToScript?: boolean;
|
|
33
|
+
readonly title?: string;
|
|
34
|
+
readonly description?: string;
|
|
35
|
+
readonly placeholder?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface ObjectType {
|
|
38
|
+
readonly kind: 'object';
|
|
39
|
+
readonly shape: Readonly<Record<string, SdkType>>;
|
|
40
|
+
readonly title?: string;
|
|
41
|
+
readonly description?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface ArrayType {
|
|
44
|
+
readonly kind: 'array';
|
|
45
|
+
readonly item: SdkType;
|
|
46
|
+
readonly title?: string;
|
|
47
|
+
readonly description?: string;
|
|
48
|
+
}
|
|
49
|
+
export type SdkType = ScalarType | SecretType | ObjectType | ArrayType;
|
|
50
|
+
export interface TableDeclaration {
|
|
51
|
+
columns: Record<string, ScalarType>;
|
|
52
|
+
primaryKey: string[];
|
|
53
|
+
retention?: {
|
|
54
|
+
column: string;
|
|
55
|
+
days: number;
|
|
56
|
+
};
|
|
57
|
+
purgeColumns?: {
|
|
58
|
+
tenant?: string;
|
|
59
|
+
user?: string;
|
|
60
|
+
};
|
|
61
|
+
indexes?: string[][];
|
|
62
|
+
/** 表的显示名,≤ 40 字符。 */
|
|
63
|
+
title?: string;
|
|
64
|
+
/** 表说明,≤ 200 字符。 */
|
|
65
|
+
description?: string;
|
|
66
|
+
}
|
|
67
|
+
export type SyncMode = 'full' | 'incremental';
|
|
68
|
+
/** discover 返回的单个数据流:输出的表、支持的同步方式与数据流参数。 */
|
|
69
|
+
export interface StreamDiscovery {
|
|
70
|
+
tables: Record<string, TableDeclaration>;
|
|
71
|
+
syncModes: SyncMode[];
|
|
72
|
+
/** 全量是否是完整快照(未出现的主键视为删除);缺省 `'partial'`。 */
|
|
73
|
+
snapshot?: 'complete' | 'partial';
|
|
74
|
+
/** 同步配置上可填写的数据流参数,作为 `input.streamConfig` 传入。 */
|
|
75
|
+
params?: ObjectType;
|
|
76
|
+
}
|
|
77
|
+
export interface PushDiscovery {
|
|
78
|
+
tables: Record<string, TableDeclaration>;
|
|
79
|
+
}
|
|
80
|
+
export interface DiscoverResult {
|
|
81
|
+
streams?: Record<string, StreamDiscovery>;
|
|
82
|
+
/** 按推送名声明输出的表;只接受按名字的写法。 */
|
|
83
|
+
push?: Record<string, PushDiscovery>;
|
|
84
|
+
}
|
|
85
|
+
export interface StreamInput<S = unknown> {
|
|
86
|
+
mode: SyncMode;
|
|
87
|
+
/** 上次提交的断点;首次为 undefined。 */
|
|
88
|
+
state: S | undefined;
|
|
89
|
+
streamConfig: Readonly<Record<string, unknown>>;
|
|
90
|
+
}
|
|
91
|
+
export interface RecordItem {
|
|
92
|
+
readonly __ontos: 'record';
|
|
93
|
+
table: string;
|
|
94
|
+
row: Record<string, unknown>;
|
|
95
|
+
}
|
|
96
|
+
export interface RemoveItem {
|
|
97
|
+
readonly __ontos: 'remove';
|
|
98
|
+
table: string;
|
|
99
|
+
key: Record<string, unknown>;
|
|
100
|
+
}
|
|
101
|
+
export interface CheckpointItem {
|
|
102
|
+
readonly __ontos: 'checkpoint';
|
|
103
|
+
state: unknown;
|
|
104
|
+
}
|
|
105
|
+
export type StreamItem = RecordItem | RemoveItem | CheckpointItem;
|
|
106
|
+
export type StreamFunction = (ctx: PullContext, input: StreamInput) => AsyncIterable<StreamItem>;
|
|
107
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD';
|
|
108
|
+
export interface HttpResponse<T = unknown> {
|
|
109
|
+
status: number;
|
|
110
|
+
/** 最终地址(跟随重定向之后)。 */
|
|
111
|
+
url: string;
|
|
112
|
+
headers: Record<string, string>;
|
|
113
|
+
/** `content-type` 为 JSON 时是解析后的值,否则同 `text`。 */
|
|
114
|
+
data: T;
|
|
115
|
+
text: string;
|
|
116
|
+
}
|
|
117
|
+
/** 登录换来的会话:附加的请求头与相对路径的基址。 */
|
|
118
|
+
export interface HttpAuth {
|
|
119
|
+
headers?: Record<string, string>;
|
|
120
|
+
baseUrl?: string;
|
|
121
|
+
}
|
|
122
|
+
export interface HttpOptions {
|
|
123
|
+
headers?: Record<string, string>;
|
|
124
|
+
query?: Record<string, unknown>;
|
|
125
|
+
body?: unknown;
|
|
126
|
+
auth?: HttpAuth;
|
|
127
|
+
timeoutMs?: number;
|
|
128
|
+
/** 为 true 时不按状态码抛错、不解析 JSON,原样返回。 */
|
|
129
|
+
raw?: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* 上游 502 / 503 / 504 与网关超时是否自动重试(请求可能已被上游处理)。
|
|
132
|
+
* 缺省只对幂等方法(GET / HEAD / PUT / DELETE)重试;POST / PATCH 需显式传 `true`,传 `false` 则一律不重试。
|
|
133
|
+
* 网关限流与上游 429(请求未被处理)不受影响,总会等待后重发。
|
|
134
|
+
*/
|
|
135
|
+
retry?: boolean;
|
|
136
|
+
}
|
|
137
|
+
export interface HttpClient {
|
|
138
|
+
get<T = unknown>(url: string, options?: HttpOptions): Promise<HttpResponse<T>>;
|
|
139
|
+
post<T = unknown>(url: string, options?: HttpOptions): Promise<HttpResponse<T>>;
|
|
140
|
+
put<T = unknown>(url: string, options?: HttpOptions): Promise<HttpResponse<T>>;
|
|
141
|
+
patch<T = unknown>(url: string, options?: HttpOptions): Promise<HttpResponse<T>>;
|
|
142
|
+
delete<T = unknown>(url: string, options?: HttpOptions): Promise<HttpResponse<T>>;
|
|
143
|
+
request<T = unknown>(method: HttpMethod, url: string, options?: HttpOptions): Promise<HttpResponse<T>>;
|
|
144
|
+
}
|
|
145
|
+
/** 分页迭代产出的一页。 */
|
|
146
|
+
export interface Page<Row = unknown> {
|
|
147
|
+
rows: Row[];
|
|
148
|
+
/** 本页各行取值的最大值(字符串按字典序、数字按大小);空页为 undefined。 */
|
|
149
|
+
maxOf<V>(fn: (row: Row) => V): V | undefined;
|
|
150
|
+
}
|
|
151
|
+
/** 按页码翻页。没有 `totalPages` 时翻到空页为止。 */
|
|
152
|
+
export interface PagePaginateOptions<Res, Row> {
|
|
153
|
+
/** 缺省 1。 */
|
|
154
|
+
firstPage?: number;
|
|
155
|
+
request(page: number): Promise<HttpResponse<Res>>;
|
|
156
|
+
rows(res: HttpResponse<Res>): Row[];
|
|
157
|
+
totalPages?(res: HttpResponse<Res>): number;
|
|
158
|
+
}
|
|
159
|
+
/** 按偏移量翻页。没有 `total` 时翻到不满一页为止。 */
|
|
160
|
+
export interface OffsetPaginateOptions<Res, Row> {
|
|
161
|
+
limit: number;
|
|
162
|
+
/** 缺省 0。 */
|
|
163
|
+
startOffset?: number;
|
|
164
|
+
request(offset: number, limit: number): Promise<HttpResponse<Res>>;
|
|
165
|
+
rows(res: HttpResponse<Res>): Row[];
|
|
166
|
+
total?(res: HttpResponse<Res>): number;
|
|
167
|
+
}
|
|
168
|
+
/** 按游标翻页。`nextCursor` 返回空值时结束。 */
|
|
169
|
+
export interface CursorPaginateOptions<Res, Row> {
|
|
170
|
+
request(cursor: string | undefined): Promise<HttpResponse<Res>>;
|
|
171
|
+
rows(res: HttpResponse<Res>): Row[];
|
|
172
|
+
nextCursor(res: HttpResponse<Res>): string | null | undefined;
|
|
173
|
+
}
|
|
174
|
+
/** 按下一页链接翻页。缺省取响应头 `Link` 里 `rel="next"` 的地址。 */
|
|
175
|
+
export interface LinkPaginateOptions<Res, Row> {
|
|
176
|
+
url: string;
|
|
177
|
+
options?: HttpOptions;
|
|
178
|
+
rows(res: HttpResponse<Res>): Row[];
|
|
179
|
+
next?(res: HttpResponse<Res>): string | null | undefined;
|
|
180
|
+
}
|
|
181
|
+
/** 数据库按键分页:`query` 以上一页最后一行的键(首页为 undefined)查下一页,返回空页时结束。 */
|
|
182
|
+
export interface KeysetPaginateOptions<Row, Key> {
|
|
183
|
+
query(last: Key | undefined): AsyncIterable<Row>;
|
|
184
|
+
key(row: Row): Key;
|
|
185
|
+
}
|
|
186
|
+
export interface Paginate {
|
|
187
|
+
page<Res = unknown, Row = unknown>(options: PagePaginateOptions<Res, Row>): AsyncIterable<Page<Row>>;
|
|
188
|
+
offset<Res = unknown, Row = unknown>(options: OffsetPaginateOptions<Res, Row>): AsyncIterable<Page<Row>>;
|
|
189
|
+
cursor<Res = unknown, Row = unknown>(options: CursorPaginateOptions<Res, Row>): AsyncIterable<Page<Row>>;
|
|
190
|
+
link<Res = unknown, Row = unknown>(options: LinkPaginateOptions<Res, Row>): AsyncIterable<Page<Row>>;
|
|
191
|
+
keyset<Row = Record<string, unknown>, Key = unknown>(options: KeysetPaginateOptions<Row, Key>): AsyncIterable<Page<Row>>;
|
|
192
|
+
}
|
|
193
|
+
export interface ConnectorDb {
|
|
194
|
+
/** 一次查询一个只读事务;只接受参数化查询(`$1`、`$2`…),一条语句。 */
|
|
195
|
+
query<R = Record<string, unknown>>(sql: string, params?: unknown[]): AsyncIterable<R>;
|
|
196
|
+
}
|
|
197
|
+
export interface ConnectorCache {
|
|
198
|
+
get(key: string): Promise<string | undefined>;
|
|
199
|
+
set(key: string, value: string, ttlSeconds: number): Promise<void>;
|
|
200
|
+
getOrSet(key: string, ttlSeconds: number, fn: () => Promise<string>): Promise<string>;
|
|
201
|
+
}
|
|
202
|
+
export type HmacAlgorithm = 'sha1' | 'sha256' | 'sha512';
|
|
203
|
+
export type SignAlgorithm = 'rsa-sha256';
|
|
204
|
+
export type HashAlgorithm = 'md5' | 'sha1' | 'sha256' | 'sha512';
|
|
205
|
+
export type DigestEncoding = 'hex' | 'base64';
|
|
206
|
+
/** `key` 可以是凭证句柄(`ctx.secrets.<字段>`),由网关代算。 */
|
|
207
|
+
export interface ConnectorCrypto {
|
|
208
|
+
hmac(algorithm: HmacAlgorithm, key: string, data: string, encoding?: DigestEncoding): Promise<string>;
|
|
209
|
+
sign(algorithm: SignAlgorithm, key: string, data: string, encoding?: DigestEncoding): Promise<string>;
|
|
210
|
+
hash(algorithm: HashAlgorithm, data: string, encoding?: DigestEncoding): Promise<string>;
|
|
211
|
+
base64Encode(text: string): string;
|
|
212
|
+
base64Decode(b64: string): string;
|
|
213
|
+
}
|
|
214
|
+
export interface ConnectorTime {
|
|
215
|
+
/** 当前时间,ISO 8601 UTC。 */
|
|
216
|
+
now(): string;
|
|
217
|
+
/** 把来源给的本地时间文本按时区换成 ISO 8601 UTC。 */
|
|
218
|
+
toUtc(local: string, timeZone: string, format?: string): string;
|
|
219
|
+
}
|
|
220
|
+
export interface ConnectorSignal {
|
|
221
|
+
readonly aborted: boolean;
|
|
222
|
+
onAbort(fn: () => void): void;
|
|
223
|
+
}
|
|
224
|
+
export interface ConnectorLog {
|
|
225
|
+
info(message: string, data?: Record<string, unknown>): void;
|
|
226
|
+
warn(message: string, data?: Record<string, unknown>): void;
|
|
227
|
+
error(message: string, data?: Record<string, unknown>): void;
|
|
228
|
+
}
|
|
229
|
+
export interface PullContext {
|
|
230
|
+
readonly config: Readonly<Record<string, unknown>>;
|
|
231
|
+
/** 凭证句柄(`ontos:secret:<字段>`);`exposeToScript` 字段是明文。 */
|
|
232
|
+
readonly secrets: Readonly<Record<string, string>>;
|
|
233
|
+
readonly http: HttpClient;
|
|
234
|
+
readonly paginate: Paginate;
|
|
235
|
+
readonly db: ConnectorDb;
|
|
236
|
+
/** 保留名字;访问即抛 `errors.NotAvailable('ctx.files')`。 */
|
|
237
|
+
readonly files: never;
|
|
238
|
+
map<T, R>(items: readonly T[], fn: (item: T) => Promise<R>, options?: {
|
|
239
|
+
concurrency?: number;
|
|
240
|
+
}): Promise<R[]>;
|
|
241
|
+
readonly cache: ConnectorCache;
|
|
242
|
+
readonly crypto: ConnectorCrypto;
|
|
243
|
+
readonly time: ConnectorTime;
|
|
244
|
+
record(table: string, row: Record<string, unknown>): RecordItem;
|
|
245
|
+
remove(table: string, key: Record<string, unknown>): RemoveItem;
|
|
246
|
+
checkpoint(state: unknown): CheckpointItem;
|
|
247
|
+
readonly log: ConnectorLog;
|
|
248
|
+
readonly signal: ConnectorSignal;
|
|
249
|
+
}
|
|
250
|
+
export type CheckContext = Omit<PullContext, 'record' | 'remove' | 'checkpoint'>;
|
|
251
|
+
export type DiscoverContext = CheckContext;
|
|
252
|
+
export interface PushRequest {
|
|
253
|
+
readonly id: string;
|
|
254
|
+
readonly receivedAt: string;
|
|
255
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
256
|
+
readonly body: string;
|
|
257
|
+
json<T = unknown>(): T;
|
|
258
|
+
}
|
|
259
|
+
export interface PushContext {
|
|
260
|
+
readonly config: Readonly<Record<string, unknown>>;
|
|
261
|
+
record(table: string, row: Record<string, unknown>): void;
|
|
262
|
+
remove(table: string, key: Record<string, unknown>): void;
|
|
263
|
+
readonly log: ConnectorLog;
|
|
264
|
+
readonly time: {
|
|
265
|
+
now(): string;
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
export interface PushHandler {
|
|
269
|
+
verify?(ctx: PushContext, req: PushRequest): void | Promise<void>;
|
|
270
|
+
map(ctx: PushContext, req: PushRequest): void | Promise<void>;
|
|
271
|
+
}
|
|
272
|
+
export interface ConnectorRateLimit {
|
|
273
|
+
perSecond: number;
|
|
274
|
+
concurrency: number;
|
|
275
|
+
}
|
|
276
|
+
export interface ConnectorDefinition {
|
|
277
|
+
name: string;
|
|
278
|
+
/** semver,形如 `1.2.3` */
|
|
279
|
+
version: string;
|
|
280
|
+
config: ObjectType;
|
|
281
|
+
/** 出站主机范围:精确主机名或 `*.后缀`,可含 `{config.<路径>}`。数据流用 `ctx.http` 时必填。 */
|
|
282
|
+
hosts?: string[];
|
|
283
|
+
/** 数据流用 `ctx.db` 时声明;本期只支持 PostgreSQL。 */
|
|
284
|
+
db?: {
|
|
285
|
+
engine: 'postgresql';
|
|
286
|
+
};
|
|
287
|
+
rateLimit?: ConnectorRateLimit;
|
|
288
|
+
check?(ctx: CheckContext): void | Promise<void>;
|
|
289
|
+
discover(ctx: DiscoverContext): DiscoverResult | Promise<DiscoverResult>;
|
|
290
|
+
streams?: Record<string, StreamFunction>;
|
|
291
|
+
/** 按推送名声明:`push: { default: { verify?, map } }`。第 1 期的单对象写法 `push: { verify?, map }` 已删除。 */
|
|
292
|
+
push?: Record<string, PushHandler>;
|
|
293
|
+
}
|
|
294
|
+
/** 第 1 期的单对象推送写法:`map` / `verify` 直接是函数(按名字的写法里每个值都是对象)。 */
|
|
295
|
+
export declare function isSingleObjectPush(push: unknown): boolean;
|
|
296
|
+
/** 单对象推送写法的校验文案(`defineConnector` 抛错与 `validateConnectorDefinition` 共用)。 */
|
|
297
|
+
export declare const SINGLE_OBJECT_PUSH_MESSAGE = "push must be declared by name, e.g. push: { default: { map } }; the single-object form push: { map } is not supported";
|
|
298
|
+
/**
|
|
299
|
+
* 给连接器定义提供类型推断,原样返回定义。只做一项检查:单对象推送写法 `push: { map }` 直接抛 `TypeError`
|
|
300
|
+
* (其余校验由平台加载时的 `validateConnectorDefinition` 做)。
|
|
301
|
+
*/
|
|
302
|
+
export declare function defineConnector<T extends ConnectorDefinition>(definition: T): T;
|
package/types.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function isSingleObjectPush(push) {
|
|
2
|
+
if (push === null || typeof push !== 'object' || Array.isArray(push))
|
|
3
|
+
return false;
|
|
4
|
+
const value = push;
|
|
5
|
+
return typeof value.map === 'function' || typeof value.verify === 'function';
|
|
6
|
+
}
|
|
7
|
+
export const SINGLE_OBJECT_PUSH_MESSAGE = 'push must be declared by name, e.g. push: { default: { map } }; the single-object form push: { map } is not supported';
|
|
8
|
+
export function defineConnector(definition) {
|
|
9
|
+
if (definition !== null && typeof definition === 'object' && isSingleObjectPush(definition.push)) {
|
|
10
|
+
throw new TypeError(SINGLE_OBJECT_PUSH_MESSAGE);
|
|
11
|
+
}
|
|
12
|
+
return definition;
|
|
13
|
+
}
|
package/validate.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 连接器定义、discover 结果与连接配置的校验。平台(meta、connector、runner)与连接器作者共用同一套规则;
|
|
3
|
+
* 输入可能来自 vm 里的不可信代码,所以按 unknown 逐层检查,只返回问题列表、不抛异常。
|
|
4
|
+
*/
|
|
5
|
+
import { type ConnectorDefinition, type DiscoverResult, type ObjectType, type SyncMode, type TableDeclaration } from './types.js';
|
|
6
|
+
/** 表名、连接器名、数据流名与推送名的规则,与 shared 的 DATA_FOUNDATION_NAME_PATTERN 一致。 */
|
|
7
|
+
export declare const SDK_NAME_PATTERN: RegExp;
|
|
8
|
+
/** 保留的推送名:`dev` 会和接收地址的 `/dev` 后缀冲突。与 shared 同值,SDK 不依赖 shared,自己定义。 */
|
|
9
|
+
export declare const RESERVED_PUSH_NAMES: readonly string[];
|
|
10
|
+
/** 缺省推送名(接收地址不带推送名后缀时对应它)。 */
|
|
11
|
+
export declare const DEFAULT_PUSH_NAME = "default";
|
|
12
|
+
/**
|
|
13
|
+
* 展示元数据的长度上限(字符数按 Unicode 码点计,字节数按解码后的字节计)。
|
|
14
|
+
* `title` / `description` / `readmeBytes` / `iconBytes` / `iconMinPx` 是连接器清单(package.json、README.md、图标)的上限;
|
|
15
|
+
* 其余是 `t.*()` 字段与表声明的上限。
|
|
16
|
+
*/
|
|
17
|
+
export declare const PRESENTATION_LIMITS: {
|
|
18
|
+
readonly title: 40;
|
|
19
|
+
readonly description: 120;
|
|
20
|
+
readonly readmeBytes: 65536;
|
|
21
|
+
readonly iconBytes: 262144;
|
|
22
|
+
readonly iconMinPx: 128;
|
|
23
|
+
readonly fieldTitle: 40;
|
|
24
|
+
readonly fieldDescription: 200;
|
|
25
|
+
readonly placeholder: 100;
|
|
26
|
+
readonly tableTitle: 40;
|
|
27
|
+
readonly tableDescription: 200;
|
|
28
|
+
};
|
|
29
|
+
export interface DeclarationIssue {
|
|
30
|
+
path: string;
|
|
31
|
+
message: string;
|
|
32
|
+
}
|
|
33
|
+
/** 定义摘要:discover 运行由 guest 上报,平台据此判断出站主机范围、凭证字段与推送 / 数据流列表。 */
|
|
34
|
+
export interface DefinitionSummary {
|
|
35
|
+
name: string;
|
|
36
|
+
version: string;
|
|
37
|
+
hosts: string[];
|
|
38
|
+
db: {
|
|
39
|
+
engine: 'postgresql';
|
|
40
|
+
} | null;
|
|
41
|
+
rateLimit: {
|
|
42
|
+
perSecond: number;
|
|
43
|
+
concurrency: number;
|
|
44
|
+
} | null;
|
|
45
|
+
pushes: string[];
|
|
46
|
+
streams: string[];
|
|
47
|
+
secretFields: string[];
|
|
48
|
+
exposeToScript: string[];
|
|
49
|
+
/** `config` 声明(`t.*()` 产出的普通对象),平台保存连接配置时按它校验;旧声明没有这个字段。 */
|
|
50
|
+
config?: Record<string, unknown>;
|
|
51
|
+
}
|
|
52
|
+
/** 规整后的 discover 结果:`snapshot` 补缺省值,与 shared 的 `ConnectorDeclaration` 去掉 `definition` 同形。 */
|
|
53
|
+
export interface NormalizedDiscoverResult {
|
|
54
|
+
streams: Record<string, {
|
|
55
|
+
tables: Record<string, TableDeclaration>;
|
|
56
|
+
syncModes: SyncMode[];
|
|
57
|
+
snapshot: 'complete' | 'partial';
|
|
58
|
+
params?: ObjectType;
|
|
59
|
+
}>;
|
|
60
|
+
pushes: Record<string, {
|
|
61
|
+
tables: Record<string, TableDeclaration>;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
export declare function validateConnectorDefinition(definition: unknown): DeclarationIssue[];
|
|
65
|
+
/**
|
|
66
|
+
* 校验 discover 结果:数据流与推送都要和定义一一对应,表名在一个连接下唯一。
|
|
67
|
+
* `push` 只接受按名字的写法 `{ <推送名>: { tables } }`。
|
|
68
|
+
*/
|
|
69
|
+
export declare function validateDiscoverResult(result: unknown, definition: ConnectorDefinition): DeclarationIssue[];
|
|
70
|
+
/** 规整一份已通过 `validateDiscoverResult` 的结果:缺省 `snapshot` 为 `'partial'`,缺省的 `streams` / `push` 为空。 */
|
|
71
|
+
export declare function normalizeDiscoverResult(result: DiscoverResult): NormalizedDiscoverResult;
|
|
72
|
+
/** 定义摘要;只含可 JSON 序列化的值,用于跨 vm 边界上报与声明差异。各列表按声明顺序。 */
|
|
73
|
+
export declare function summarizeDefinition(definition: ConnectorDefinition): DefinitionSummary;
|
|
74
|
+
/**
|
|
75
|
+
* 按连接器的 `config` 声明校验连接配置的值。所有非 secret 字段必填;
|
|
76
|
+
* 顶层 `t.secret()` 字段的值走 Secret 引用,不在配置值里校验,出现在配置值里也忽略。
|
|
77
|
+
*/
|
|
78
|
+
export declare function validateConfig(schema: ObjectType, config: unknown): DeclarationIssue[];
|