@77-zhike/app-sdk 0.2.0-d7b1.8 → 0.2.0-d7b1.9
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 +13 -7
- package/package.json +2 -2
- package/src/cli/development-logs.js +5 -2
- package/src/cli/index.js +11 -23
- package/src/cli/local-commands.js +1 -0
- package/src/cli/project-init.js +2 -1
- package/src/server/index.d.ts +40 -5
- package/src/server/index.js +21 -0
- package/src/server/steps/index.d.ts +71 -0
- package/src/server/steps/index.js +15 -0
- package/src/server/triggers/index.d.ts +68 -0
- package/src/server/triggers/index.js +27 -0
- package/src/server/workflow-blocks/index.d.ts +72 -0
- package/src/server/workflow-blocks/index.js +32 -0
- package/src/toolchain/allowed-packages.json +7 -0
- package/src/toolchain/dependency-policy.js +175 -0
- package/src/toolchain/index.js +0 -88
- package/src/toolchain/real-build.js +59 -17
- package/src/toolchain/source-discovery.js +76 -65
- package/src/toolchain/workflow-blocks/discovery.js +133 -0
- package/src/toolchain/workflow-steps/manifest.js +99 -0
- package/src/toolchain/workflow-triggers/manifest.js +47 -0
- package/src/ui/components.generated.js +3 -2
- package/src/ui/factory.js +1 -1
- package/src/ui/index.d.ts +5 -5
- package/src/ui/index.js +1 -1
- package/src/cli/validate-bundle.js +0 -36
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# 智客 App SDK 与 CLI
|
|
2
2
|
|
|
3
|
+
## 获准依赖
|
|
4
|
+
|
|
5
|
+
工具链从 `src/toolchain/allowed-packages.json` 读取平台维护的包白名单。目前允许 `@77-zhike/qiqi-openapi@0.1.0` 的服务端包根入口 `.`;前端不可导入。项目安装依赖后直接使用静态 ESM import,真实解析的包名、精确版本、入口及间接模块在打包时核对,代码合并进 Artifact。App 的 Manifest、Settings 或 `package.json` 不能扩展名单。
|
|
6
|
+
|
|
7
|
+
Node builtin、CommonJS、动态 import、原生模块、未获准包和路径越界仍禁止。项目内 `file:` 包可用,但同名同版本不是来源证明。白名单只控制构建兼容性;不替代运行时沙箱、网络策略、租户隔离及 API 权限,不进行运行时 npm 安装。
|
|
8
|
+
|
|
3
9
|
## 统一构建
|
|
4
10
|
|
|
5
11
|
`zhike dev` 是监听、构建、上传和应用开发安装的快捷工作流,不是另一种运行时。
|
|
@@ -133,7 +139,7 @@ import {
|
|
|
133
139
|
} from '@77-zhike/app-sdk/client'
|
|
134
140
|
import {
|
|
135
141
|
Card,
|
|
136
|
-
|
|
142
|
+
ZhikeLink,
|
|
137
143
|
Stack,
|
|
138
144
|
TextBlock,
|
|
139
145
|
} from '@77-zhike/app-sdk/ui'
|
|
@@ -144,9 +150,9 @@ function QueuePage() {
|
|
|
144
150
|
<Card title="任务队列">
|
|
145
151
|
<TextBlock>当前没有待处理任务。</TextBlock>
|
|
146
152
|
</Card>
|
|
147
|
-
<
|
|
153
|
+
<ZhikeLink destination={Destinations.appPage('overview')}>
|
|
148
154
|
返回总览
|
|
149
|
-
</
|
|
155
|
+
</ZhikeLink>
|
|
150
156
|
</Stack>
|
|
151
157
|
)
|
|
152
158
|
}
|
|
@@ -161,13 +167,13 @@ export default definePage({
|
|
|
161
167
|
|
|
162
168
|
同一 App 内,有 `order` 的 Page 排在前面并按 `order + slug` 稳定排序;未声明 `order` 的 Page 排在后面并按 slug 排序。`name` trim 后长度为 1~80 个 Unicode code points。`variant: 'full' | 'centered'` 只表达布局语义,Desktop 与 H5 分别决定具体页面壳。
|
|
163
169
|
|
|
164
|
-
Page UI 必须使用 `@77-zhike/app-sdk/ui` 和 `@77-zhike/app-sdk/forms` 的公开组件,不能依赖 DOM、React Router 或自行复制产品控件。当前 Desktop 支持 Components 与 Forms;H5 支持 TextBlock、布局、反馈、Button、Tabs、Table、Card、Grid、EmptyState、Link 等 portable Components,并支持 Forms,暂不支持 `LookupCell`。含有不支持能力的 Page 在 H5 会整体显示“不支持”,不会静默丢节点或部分提交。
|
|
170
|
+
Page UI 必须使用 `@77-zhike/app-sdk/ui` 和 `@77-zhike/app-sdk/forms` 的公开组件,不能依赖 DOM、React Router 或自行复制产品控件。当前 Desktop 支持 Components 与 Forms;H5 支持 TextBlock、布局、反馈、Button、Tabs、Table、Card、Grid、EmptyState、Link、ZhikeLink 等 portable Components,并支持 Forms,暂不支持 `LookupCell`。含有不支持能力的 Page 在 H5 会整体显示“不支持”,不会静默丢节点或部分提交。
|
|
165
171
|
|
|
166
172
|
导航只提交结构化 Destination:
|
|
167
173
|
|
|
168
174
|
- `Destinations.appPage(slug)` 打开当前 App 的另一个 Page。
|
|
169
175
|
- `Destinations.record({ object, recordId })` 支持 `accounts`、`contacts`、`opportunities`、`tasks`、`activities` 和当前 Workspace 可见的 `custom_*` 公开 API slug。
|
|
170
|
-
- `
|
|
176
|
+
- `ZhikeLink` 保留普通链接、复制地址和新标签行为;普通点击仍会在跳转前重验当前 Page 身份。外部 HTTPS 使用 `Link`。
|
|
171
177
|
- `navigate()` 当前只允许从已挂载的 Page 调用,三类 Action 都不具备该能力。
|
|
172
178
|
|
|
173
179
|
目录展示不会启动 Worker。首次打开 Page 时,宿主才会下载并校验 Manifest/Bundle、启动 Installation Worker;因此“Apps 菜单可见”不等于代码已经验证可运行。页面数据读写仍应通过受控 Server Function 完成。
|
|
@@ -196,9 +202,9 @@ export default async function readForCurrentMember() {
|
|
|
196
202
|
|
|
197
203
|
## 基础UI组件扩展
|
|
198
204
|
|
|
199
|
-
新增Avatar、DescriptionList、Divider、Json、
|
|
205
|
+
新增Avatar、DescriptionList、Divider、Json、外部Link、StatusBadge、TextBlock与Typography,补齐Banner动作、Badge颜色、Card呈现、Grid容器模式、EmptyState操作编排和Table.HeaderCell。复合写法使用同一节点。
|
|
200
206
|
|
|
201
|
-
Desktop
|
|
207
|
+
Desktop与H5注册的组件合同中,`ZhikeLink` 仍仅限Page,`Link` 是受治理的外部HTTPS链接;H5支持Forms,仍不开放Settings入口。新包要求`remote-react.v3`,发布前必须先部署对应Host及Bundle准入合同。
|
|
202
208
|
|
|
203
209
|
新增组件的日常验证:修改Core `components/contracts.mjs`后执行`npm run generate:components --workspace @77-zhike/app-sdk`和`npm run check:components --workspace @77-zhike/app-sdk`,同步双端manifest与adapter。App项目运行`zhike dev --workspace <workspace-id>`,从对应Workspace的Apps入口打开Page;Desktop Settings仅验证其允许的子集。
|
|
204
210
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@77-zhike/app-sdk",
|
|
3
|
-
"version": "0.2.0-d7b1.
|
|
3
|
+
"version": "0.2.0-d7b1.9",
|
|
4
4
|
"description": "智客 Integration App 一期 SDK 与本地 Toolchain",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"optional": true
|
|
60
60
|
}
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "d820fb6f04634518f062b82cf214d3b08d00c481"
|
|
63
63
|
}
|
|
@@ -32,9 +32,12 @@ export function formatAppLog(app, entry, {
|
|
|
32
32
|
const levelLabel = { log: "INFO ", warn: "WARN ", error: "ERROR" }[level];
|
|
33
33
|
const levelColor = { log: colors.cyan, warn: colors.yellow, error: colors.red }[level];
|
|
34
34
|
const timestamp = formatTimestamp(entry.received_at);
|
|
35
|
+
const version = entry.app_version_id ? safe(entry.app_version_id).replace(/\n/g, " ").slice(0, 64) : "";
|
|
36
|
+
const request = entry.invocation_id ? safe(entry.invocation_id).replace(/\n/g, " ").slice(0, 64) : "";
|
|
37
|
+
const identity = version || request ? ` [${[version, request].filter(Boolean).join(" · ")}]` : "";
|
|
35
38
|
const prefix = color
|
|
36
|
-
? `${colors.dim}${timestamp}${colors.reset} ${levelColor}${levelLabel}${colors.reset} ${colors.bold}${label}${colors.reset}`
|
|
37
|
-
: `${timestamp} ${levelLabel} ${label}`;
|
|
39
|
+
? `${colors.dim}${timestamp}${colors.reset} ${levelColor}${levelLabel}${colors.reset} ${colors.bold}${label}${colors.reset}${identity}`
|
|
40
|
+
: `${timestamp} ${levelLabel} ${label}${identity}`;
|
|
38
41
|
return safe(entry.message).split("\n").map((line) => `${prefix} ${line}`).join("\n");
|
|
39
42
|
}
|
|
40
43
|
|
package/src/cli/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { resolve } from "node:path";
|
|
|
2
2
|
|
|
3
3
|
import { loadCliConfig } from "../config.js";
|
|
4
4
|
import { asSafeCliError, ZhikeCliError } from "../errors.js";
|
|
5
|
-
import { buildAppBundle
|
|
5
|
+
import { buildAppBundle } from "../toolchain/index.js";
|
|
6
6
|
import { authenticateCli, openSystemBrowser } from "./browser-auth.js";
|
|
7
7
|
import { createSystemCredentialStore } from "./credential-store.js";
|
|
8
8
|
import {
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
initializeProject,
|
|
11
11
|
validateAppSlug,
|
|
12
12
|
} from "./project-init.js";
|
|
13
|
-
import { validateBundleUpload } from "./validate-bundle.js";
|
|
14
13
|
import { uploadAppBundle } from "./upload-bundle.js";
|
|
15
14
|
import { watchDevelopmentApp } from "./development-watch.js";
|
|
16
15
|
import { runLocalCommand } from "./local-commands.js";
|
|
@@ -60,6 +59,16 @@ export async function runCli(
|
|
|
60
59
|
);
|
|
61
60
|
return result;
|
|
62
61
|
}
|
|
62
|
+
if (command === "validate") {
|
|
63
|
+
_rejectArguments(rest);
|
|
64
|
+
const result = await buildAppBundle({
|
|
65
|
+
projectDirectory: cwd,
|
|
66
|
+
});
|
|
67
|
+
output.write(
|
|
68
|
+
`真实 App 源码校验通过:${result.artifacts.length} Artifacts,Bundle ${result.sha256} (${result.size} bytes);未写入 dist\n`,
|
|
69
|
+
);
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
63
72
|
|
|
64
73
|
const onProgress = (message) => output.write(`${message}\n`);
|
|
65
74
|
let remoteContext = null;
|
|
@@ -186,26 +195,6 @@ export async function runCli(
|
|
|
186
195
|
if (result && !ready) await onReady(result);
|
|
187
196
|
return result;
|
|
188
197
|
}
|
|
189
|
-
if (command === "experimental" && rest[0] === "validate-bundle") {
|
|
190
|
-
_rejectArguments(rest.slice(1));
|
|
191
|
-
const { config, store } = getRemoteContext();
|
|
192
|
-
const authenticatedApi = await authenticate({
|
|
193
|
-
...config,
|
|
194
|
-
credentialStore: store,
|
|
195
|
-
fetchImpl,
|
|
196
|
-
openBrowser,
|
|
197
|
-
onProgress,
|
|
198
|
-
});
|
|
199
|
-
const result = await validateBundleUpload({
|
|
200
|
-
projectDirectory: cwd,
|
|
201
|
-
apiBaseUrl: config.apiBaseUrl,
|
|
202
|
-
authenticatedApi,
|
|
203
|
-
});
|
|
204
|
-
output.write(
|
|
205
|
-
`${result.upload.status === "accepted" ? "Bundle 已受理" : "Bundle 校验失败"}:${result.upload.id}\n`,
|
|
206
|
-
);
|
|
207
|
-
return result;
|
|
208
|
-
}
|
|
209
198
|
throw new ZhikeCliError(
|
|
210
199
|
"zhike_command_unknown",
|
|
211
200
|
`未知命令:${command}`,
|
|
@@ -260,5 +249,4 @@ export {
|
|
|
260
249
|
initializeProject,
|
|
261
250
|
scaffoldProject,
|
|
262
251
|
} from "./project-init.js";
|
|
263
|
-
export { validateBundleUpload } from "./validate-bundle.js";
|
|
264
252
|
export { uploadAppBundle } from "./upload-bundle.js";
|
package/src/cli/project-init.js
CHANGED
|
@@ -100,6 +100,7 @@ export async function scaffoldProject({ targetDirectory, binding }) {
|
|
|
100
100
|
- 开发文档:${docsUrl}
|
|
101
101
|
|
|
102
102
|
先读取文档索引,再按当前任务读取相关的 App SDK、CLI、扩展、连接和 API 文档;不要仅凭记忆猜测接口、参数或运行时能力。
|
|
103
|
+
每使用一个复合 UI 或 Form 组件,必须打开该组件的专属文档,核对允许的直属子节点、嵌套顺序和入口限制。TypeScript、zhike validate、zhike build 或上传成功,不代表组件树已经符合运行时合同。
|
|
103
104
|
索引中的相对链接以开发文档地址为基准解析。若无法访问文档,请明确说明访问失败,请用户提供相关文档,不要把猜测当作已确认能力。
|
|
104
105
|
项目绑定环境与 App 身份见 .zhike/project.json;切换平台环境后应同步更新上述文档地址。
|
|
105
106
|
`;
|
|
@@ -137,10 +138,10 @@ export async function scaffoldProject({ targetDirectory, binding }) {
|
|
|
137
138
|
type: "module",
|
|
138
139
|
scripts: {
|
|
139
140
|
zhike: "zhike",
|
|
141
|
+
validate: "zhike validate",
|
|
140
142
|
build: "zhike build",
|
|
141
143
|
dev: "zhike dev",
|
|
142
144
|
upload: "zhike upload",
|
|
143
|
-
"validate:mock": "zhike experimental validate-bundle",
|
|
144
145
|
},
|
|
145
146
|
devDependencies: {
|
|
146
147
|
"@77-zhike/app-sdk": TOOLING_VERSION,
|
package/src/server/index.d.ts
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
import type { JsonValue } from "../index.js";
|
|
2
|
+
export { defineWorkflowStep, defineWorkflowStepExecute } from "./steps/index.js";
|
|
3
|
+
export type { WorkflowObjectSchema, WorkflowScalarSchema, WorkflowStepDefinition, WorkflowStepOutcome, WorkflowStepResult, WorkflowSchemaValue } from "./steps/index.js";
|
|
4
|
+
export { defineWorkflowTrigger, defineWorkflowTriggerActivate, defineWorkflowTriggerOnEvent, defineWorkflowTriggerDeactivate } from "./triggers/index.js";
|
|
5
|
+
export type { WorkflowTriggerDefinition, WorkflowTriggerOutcome, WorkflowTriggerEventResult } from "./triggers/index.js";
|
|
6
|
+
export {
|
|
7
|
+
defineWorkflowBlock,
|
|
8
|
+
defineWorkflowBlockExecute,
|
|
9
|
+
defineWorkflowBlockActivate,
|
|
10
|
+
defineWorkflowBlockTrigger,
|
|
11
|
+
defineWorkflowBlockDeactivate,
|
|
12
|
+
} from "./workflow-blocks/index.js";
|
|
13
|
+
export type {
|
|
14
|
+
WorkflowBlockDefinition,
|
|
15
|
+
WorkflowStepBlockDefinition,
|
|
16
|
+
WorkflowTriggerBlockDefinition,
|
|
17
|
+
} from "./workflow-blocks/index.js";
|
|
2
18
|
|
|
3
19
|
/** 当前执行的 App API Token;由可信 Runtime Module 在服务端模块求值前注入。 */
|
|
4
20
|
export const ZHIKE_API_TOKEN: string;
|
|
@@ -206,18 +222,37 @@ export interface WorkspaceWebhookBody {
|
|
|
206
222
|
events: WorkspaceWebhookEvent[];
|
|
207
223
|
}
|
|
208
224
|
|
|
209
|
-
|
|
210
|
-
export interface WorkspaceConnection {
|
|
225
|
+
interface WorkspaceConnectionBase {
|
|
211
226
|
/** 当前 Workspace Connection 的稳定 ID。 */
|
|
212
227
|
id: string;
|
|
213
228
|
/** 当前 Connect/Reconnect generation 的非敏感稳定表示。 */
|
|
214
229
|
generation: string;
|
|
215
|
-
/**
|
|
216
|
-
type: "secret" | "oauth2";
|
|
217
|
-
/** Secret 或当前 OAuth access token,仅存在于本次服务端执行内存。 */
|
|
230
|
+
/** Secret 或当前 OAuth/OIDC access token,仅存在于本次服务端执行内存。 */
|
|
218
231
|
value: string;
|
|
219
232
|
}
|
|
220
233
|
|
|
234
|
+
/** 当前服务端执行可使用的 Workspace 外部系统连接。 */
|
|
235
|
+
export type WorkspaceConnection = WorkspaceConnectionBase & (
|
|
236
|
+
| {
|
|
237
|
+
type: "secret";
|
|
238
|
+
protocol: "secret";
|
|
239
|
+
}
|
|
240
|
+
| {
|
|
241
|
+
type: "oauth2";
|
|
242
|
+
protocol: "oauth2";
|
|
243
|
+
/** OAuth 2.0 access token;等同 value。 */
|
|
244
|
+
accessToken: string;
|
|
245
|
+
}
|
|
246
|
+
| {
|
|
247
|
+
type: "oauth2";
|
|
248
|
+
protocol: "oidc";
|
|
249
|
+
/** OIDC access token;等同 value。 */
|
|
250
|
+
accessToken: string;
|
|
251
|
+
/** OIDC ID token;仅存在于当前服务端执行内存。 */
|
|
252
|
+
idToken: string;
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
|
|
221
256
|
/** 同步读取当前执行的连接快照;未连接或暂不可用时抛出安全错误,不在 getter 内刷新。 */
|
|
222
257
|
export function getWorkspaceConnection(): Readonly<WorkspaceConnection>;
|
|
223
258
|
|
package/src/server/index.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
const SERVER_FUNCTION_MARKER = Symbol.for("zhike.server-function.v1");
|
|
2
|
+
export { defineWorkflowStep, defineWorkflowStepExecute } from "./steps/index.js";
|
|
3
|
+
export { defineWorkflowTrigger, defineWorkflowTriggerActivate, defineWorkflowTriggerOnEvent, defineWorkflowTriggerDeactivate } from "./triggers/index.js";
|
|
4
|
+
export {
|
|
5
|
+
defineWorkflowBlock,
|
|
6
|
+
defineWorkflowBlockExecute,
|
|
7
|
+
defineWorkflowBlockActivate,
|
|
8
|
+
defineWorkflowBlockTrigger,
|
|
9
|
+
defineWorkflowBlockDeactivate,
|
|
10
|
+
} from "./workflow-blocks/index.js";
|
|
2
11
|
const LIFECYCLE_EVENT_MARKER = Symbol.for("zhike.lifecycle-event.v1");
|
|
3
12
|
const WEBHOOK_HANDLER_MARKER = Symbol.for("zhike.webhook-handler.v1");
|
|
4
13
|
const WORKSPACE_CONNECTION_EVENTS = new Set([
|
|
@@ -145,11 +154,23 @@ export function getWorkspaceConnection() {
|
|
|
145
154
|
error.code = code;
|
|
146
155
|
throw error;
|
|
147
156
|
}
|
|
157
|
+
if (connection.protocol === "oidc" && (typeof connection.idToken !== "string" || !connection.idToken)) {
|
|
158
|
+
const error = new Error("connection_credential_unavailable");
|
|
159
|
+
error.code = "connection_credential_unavailable";
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
const protocol = connection.type === "secret"
|
|
163
|
+
? "secret"
|
|
164
|
+
: (connection.protocol === "oidc" && typeof connection.idToken === "string" && connection.idToken
|
|
165
|
+
? "oidc" : "oauth2");
|
|
148
166
|
return Object.freeze({
|
|
149
167
|
id: connection.id,
|
|
150
168
|
generation: connection.generation,
|
|
151
169
|
type: connection.type,
|
|
170
|
+
protocol,
|
|
152
171
|
value: connection.value,
|
|
172
|
+
...(connection.type === "oauth2" ? { accessToken: connection.value } : {}),
|
|
173
|
+
...(protocol === "oidc" ? { idToken: connection.idToken } : {}),
|
|
153
174
|
});
|
|
154
175
|
}
|
|
155
176
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export type WorkflowScalarSchema =
|
|
2
|
+
| { readonly type: "string"; readonly minLength?: number; readonly enum?: readonly string[]; readonly title?: string; readonly description?: string; readonly examples?: readonly string[] }
|
|
3
|
+
| { readonly type: "number"; readonly title?: string; readonly description?: string; readonly examples?: readonly number[] }
|
|
4
|
+
| { readonly type: "boolean"; readonly title?: string; readonly description?: string; readonly examples?: readonly boolean[] };
|
|
5
|
+
|
|
6
|
+
export interface WorkflowObjectSchema {
|
|
7
|
+
readonly type: "object";
|
|
8
|
+
readonly properties: Readonly<Record<string, WorkflowScalarSchema>>;
|
|
9
|
+
readonly required?: readonly string[];
|
|
10
|
+
readonly additionalProperties: false;
|
|
11
|
+
readonly title?: string;
|
|
12
|
+
readonly description?: string;
|
|
13
|
+
readonly examples?: readonly (string | number | boolean)[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface WorkflowStepOutcome {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly label: string;
|
|
19
|
+
readonly dataSchema: WorkflowObjectSchema | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface WorkflowStepDefinition<
|
|
23
|
+
Config extends WorkflowObjectSchema = WorkflowObjectSchema,
|
|
24
|
+
Outcomes extends readonly WorkflowStepOutcome[] = readonly WorkflowStepOutcome[],
|
|
25
|
+
> {
|
|
26
|
+
readonly key: string;
|
|
27
|
+
readonly title: string;
|
|
28
|
+
readonly description?: string;
|
|
29
|
+
readonly connection: "workspace" | "none";
|
|
30
|
+
readonly configSchema: Config;
|
|
31
|
+
readonly outcomes: Outcomes;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type ScalarValue<S> = S extends { readonly enum: readonly (infer E)[] } ? E
|
|
35
|
+
: S extends { readonly type: "string" } ? string
|
|
36
|
+
: S extends { readonly type: "number" } ? number
|
|
37
|
+
: S extends { readonly type: "boolean" } ? boolean
|
|
38
|
+
: never;
|
|
39
|
+
|
|
40
|
+
type RequiredNames<S extends WorkflowObjectSchema> = S["required"] extends readonly string[]
|
|
41
|
+
? S["required"][number] : never;
|
|
42
|
+
|
|
43
|
+
export type WorkflowSchemaValue<S extends WorkflowObjectSchema> = {
|
|
44
|
+
[K in keyof S["properties"] as K extends RequiredNames<S> ? K : never]-?: ScalarValue<S["properties"][K]>;
|
|
45
|
+
} & {
|
|
46
|
+
[K in keyof S["properties"] as K extends RequiredNames<S> ? never : K]?: ScalarValue<S["properties"][K]>;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
type OutcomeResult<O extends WorkflowStepOutcome> = O extends WorkflowStepOutcome
|
|
50
|
+
? O["dataSchema"] extends WorkflowObjectSchema
|
|
51
|
+
? { readonly outcome: O["id"]; readonly data: WorkflowSchemaValue<O["dataSchema"]> }
|
|
52
|
+
: { readonly outcome: O["id"]; readonly data: null }
|
|
53
|
+
: never;
|
|
54
|
+
|
|
55
|
+
export type WorkflowStepResult<Outcomes extends readonly WorkflowStepOutcome[]> =
|
|
56
|
+
OutcomeResult<Outcomes[number]>;
|
|
57
|
+
|
|
58
|
+
export function defineWorkflowStep<
|
|
59
|
+
const Config extends WorkflowObjectSchema,
|
|
60
|
+
const Outcomes extends readonly [WorkflowStepOutcome, ...WorkflowStepOutcome[]],
|
|
61
|
+
>(definition: WorkflowStepDefinition<Config, Outcomes>): WorkflowStepDefinition<Config, Outcomes>;
|
|
62
|
+
|
|
63
|
+
export function defineWorkflowStepExecute<
|
|
64
|
+
Config extends WorkflowObjectSchema,
|
|
65
|
+
Outcomes extends readonly WorkflowStepOutcome[],
|
|
66
|
+
>(
|
|
67
|
+
step: WorkflowStepDefinition<Config, Outcomes>,
|
|
68
|
+
execute: (params: Readonly<{ config: WorkflowSchemaValue<Config> }>) =>
|
|
69
|
+
WorkflowStepResult<Outcomes> | Promise<WorkflowStepResult<Outcomes>>,
|
|
70
|
+
): (params: Readonly<{ config: WorkflowSchemaValue<Config> }>) =>
|
|
71
|
+
WorkflowStepResult<Outcomes> | Promise<WorkflowStepResult<Outcomes>>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const STEP_MARKER = Symbol.for("zhike.workflow-step.v1");
|
|
2
|
+
|
|
3
|
+
export function defineWorkflowStep(definition) {
|
|
4
|
+
if (!definition || typeof definition !== "object") {
|
|
5
|
+
throw new TypeError("workflow_step_definition_invalid");
|
|
6
|
+
}
|
|
7
|
+
return Object.freeze({ ...definition, [STEP_MARKER]: true });
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function defineWorkflowStepExecute(step, execute) {
|
|
11
|
+
if (!step?.[STEP_MARKER] || typeof execute !== "function") {
|
|
12
|
+
throw new TypeError("workflow_step_execute_invalid");
|
|
13
|
+
}
|
|
14
|
+
return execute;
|
|
15
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { WorkflowObjectSchema, WorkflowSchemaValue } from "../steps/index.js";
|
|
2
|
+
|
|
3
|
+
export interface WorkflowTriggerOutcome {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly label: string;
|
|
6
|
+
readonly dataSchema: WorkflowObjectSchema;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface WorkflowTriggerDefinition<
|
|
10
|
+
Config extends WorkflowObjectSchema = WorkflowObjectSchema,
|
|
11
|
+
Outcomes extends readonly WorkflowTriggerOutcome[] = readonly WorkflowTriggerOutcome[],
|
|
12
|
+
> {
|
|
13
|
+
readonly key: string;
|
|
14
|
+
readonly title: string;
|
|
15
|
+
readonly description?: string;
|
|
16
|
+
readonly connection: "workspace" | "none";
|
|
17
|
+
readonly requestHeaders?: readonly string[];
|
|
18
|
+
readonly configSchema: Config;
|
|
19
|
+
readonly outcomes: Outcomes;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type OutcomeResult<O extends WorkflowTriggerOutcome> = O extends WorkflowTriggerOutcome
|
|
23
|
+
? {
|
|
24
|
+
readonly type: "outcome";
|
|
25
|
+
readonly id: O["id"];
|
|
26
|
+
readonly eventKey: string;
|
|
27
|
+
readonly data: WorkflowSchemaValue<O["dataSchema"]>;
|
|
28
|
+
}
|
|
29
|
+
: never;
|
|
30
|
+
|
|
31
|
+
export type WorkflowTriggerEventResult<Outcomes extends readonly WorkflowTriggerOutcome[]> =
|
|
32
|
+
| { readonly type: "no-op" }
|
|
33
|
+
| { readonly type: "error"; readonly code: "invalid_signature"; readonly retryable: false }
|
|
34
|
+
| OutcomeResult<Outcomes[number]>;
|
|
35
|
+
|
|
36
|
+
export function defineWorkflowTrigger<
|
|
37
|
+
const Config extends WorkflowObjectSchema,
|
|
38
|
+
const Outcomes extends readonly [WorkflowTriggerOutcome, ...WorkflowTriggerOutcome[]],
|
|
39
|
+
>(definition: WorkflowTriggerDefinition<Config, Outcomes>): WorkflowTriggerDefinition<Config, Outcomes>;
|
|
40
|
+
|
|
41
|
+
export function defineWorkflowTriggerActivate<Config extends WorkflowObjectSchema>(
|
|
42
|
+
trigger: WorkflowTriggerDefinition<Config>,
|
|
43
|
+
handler: (params: Readonly<{
|
|
44
|
+
config: WorkflowSchemaValue<Config>;
|
|
45
|
+
activationId: string;
|
|
46
|
+
triggerCallbackUrl: string;
|
|
47
|
+
}>) => Promise<Readonly<{ subscriptionRef: string | null }>>,
|
|
48
|
+
): typeof handler;
|
|
49
|
+
|
|
50
|
+
export function defineWorkflowTriggerOnEvent<
|
|
51
|
+
Config extends WorkflowObjectSchema,
|
|
52
|
+
Outcomes extends readonly WorkflowTriggerOutcome[],
|
|
53
|
+
>(
|
|
54
|
+
trigger: WorkflowTriggerDefinition<Config, Outcomes>,
|
|
55
|
+
handler: (params: Readonly<{
|
|
56
|
+
config: WorkflowSchemaValue<Config>;
|
|
57
|
+
body: string;
|
|
58
|
+
headers: Readonly<Record<string, string>>;
|
|
59
|
+
}>) => Promise<WorkflowTriggerEventResult<Outcomes>>,
|
|
60
|
+
): typeof handler;
|
|
61
|
+
|
|
62
|
+
export function defineWorkflowTriggerDeactivate(
|
|
63
|
+
trigger: WorkflowTriggerDefinition,
|
|
64
|
+
handler: (params: Readonly<{
|
|
65
|
+
activationId: string;
|
|
66
|
+
subscriptionRef: string | null;
|
|
67
|
+
}>) => Promise<void>,
|
|
68
|
+
): typeof handler;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const TRIGGER_MARKER = Symbol.for("zhike.workflow-trigger.v1");
|
|
2
|
+
|
|
3
|
+
export function defineWorkflowTrigger(definition) {
|
|
4
|
+
if (!definition || typeof definition !== "object" || Array.isArray(definition)) {
|
|
5
|
+
throw new TypeError("workflow_trigger_definition_invalid");
|
|
6
|
+
}
|
|
7
|
+
return Object.freeze({ ...definition, [TRIGGER_MARKER]: true });
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function bindTriggerHandler(trigger, handler, kind) {
|
|
11
|
+
if (!trigger?.[TRIGGER_MARKER] || typeof handler !== "function") {
|
|
12
|
+
throw new TypeError(`workflow_trigger_${kind}_invalid`);
|
|
13
|
+
}
|
|
14
|
+
return handler;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function defineWorkflowTriggerActivate(trigger, handler) {
|
|
18
|
+
return bindTriggerHandler(trigger, handler, "activate");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function defineWorkflowTriggerOnEvent(trigger, handler) {
|
|
22
|
+
return bindTriggerHandler(trigger, handler, "on_event");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function defineWorkflowTriggerDeactivate(trigger, handler) {
|
|
26
|
+
return bindTriggerHandler(trigger, handler, "deactivate");
|
|
27
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
WorkflowObjectSchema,
|
|
3
|
+
WorkflowSchemaValue,
|
|
4
|
+
WorkflowStepDefinition,
|
|
5
|
+
WorkflowStepOutcome,
|
|
6
|
+
WorkflowStepResult,
|
|
7
|
+
} from "../steps/index.js";
|
|
8
|
+
import type {
|
|
9
|
+
WorkflowTriggerDefinition,
|
|
10
|
+
WorkflowTriggerEventResult,
|
|
11
|
+
WorkflowTriggerOutcome,
|
|
12
|
+
} from "../triggers/index.js";
|
|
13
|
+
|
|
14
|
+
export type WorkflowStepBlockDefinition<
|
|
15
|
+
Config extends WorkflowObjectSchema = WorkflowObjectSchema,
|
|
16
|
+
Outcomes extends readonly WorkflowStepOutcome[] = readonly WorkflowStepOutcome[],
|
|
17
|
+
> = WorkflowStepDefinition<Config, Outcomes> & { readonly type: "step" };
|
|
18
|
+
|
|
19
|
+
export type WorkflowTriggerBlockDefinition<
|
|
20
|
+
Config extends WorkflowObjectSchema = WorkflowObjectSchema,
|
|
21
|
+
Outcomes extends readonly WorkflowTriggerOutcome[] = readonly WorkflowTriggerOutcome[],
|
|
22
|
+
> = WorkflowTriggerDefinition<Config, Outcomes> & { readonly type: "trigger" };
|
|
23
|
+
|
|
24
|
+
export type WorkflowBlockDefinition = WorkflowStepBlockDefinition | WorkflowTriggerBlockDefinition;
|
|
25
|
+
|
|
26
|
+
export function defineWorkflowBlock<
|
|
27
|
+
const Config extends WorkflowObjectSchema,
|
|
28
|
+
const Outcomes extends readonly [WorkflowStepOutcome, ...WorkflowStepOutcome[]],
|
|
29
|
+
>(definition: WorkflowStepBlockDefinition<Config, Outcomes>): WorkflowStepBlockDefinition<Config, Outcomes>;
|
|
30
|
+
|
|
31
|
+
export function defineWorkflowBlock<
|
|
32
|
+
const Config extends WorkflowObjectSchema,
|
|
33
|
+
const Outcomes extends readonly [WorkflowTriggerOutcome, ...WorkflowTriggerOutcome[]],
|
|
34
|
+
>(definition: WorkflowTriggerBlockDefinition<Config, Outcomes>): WorkflowTriggerBlockDefinition<Config, Outcomes>;
|
|
35
|
+
|
|
36
|
+
export function defineWorkflowBlockExecute<
|
|
37
|
+
Config extends WorkflowObjectSchema,
|
|
38
|
+
Outcomes extends readonly WorkflowStepOutcome[],
|
|
39
|
+
>(
|
|
40
|
+
block: WorkflowStepBlockDefinition<Config, Outcomes>,
|
|
41
|
+
execute: (params: Readonly<{ config: WorkflowSchemaValue<Config> }>) =>
|
|
42
|
+
WorkflowStepResult<Outcomes> | Promise<WorkflowStepResult<Outcomes>>,
|
|
43
|
+
): typeof execute;
|
|
44
|
+
|
|
45
|
+
export function defineWorkflowBlockActivate<Config extends WorkflowObjectSchema>(
|
|
46
|
+
block: WorkflowTriggerBlockDefinition<Config>,
|
|
47
|
+
handler: (params: Readonly<{
|
|
48
|
+
config: WorkflowSchemaValue<Config>;
|
|
49
|
+
activationId: string;
|
|
50
|
+
triggerCallbackUrl: string;
|
|
51
|
+
}>) => Promise<Readonly<{ subscriptionRef: string | null }>>,
|
|
52
|
+
): typeof handler;
|
|
53
|
+
|
|
54
|
+
export function defineWorkflowBlockTrigger<
|
|
55
|
+
Config extends WorkflowObjectSchema,
|
|
56
|
+
Outcomes extends readonly WorkflowTriggerOutcome[],
|
|
57
|
+
>(
|
|
58
|
+
block: WorkflowTriggerBlockDefinition<Config, Outcomes>,
|
|
59
|
+
handler: (params: Readonly<{
|
|
60
|
+
config: WorkflowSchemaValue<Config>;
|
|
61
|
+
body: string;
|
|
62
|
+
headers: Readonly<Record<string, string>>;
|
|
63
|
+
}>) => Promise<WorkflowTriggerEventResult<Outcomes>>,
|
|
64
|
+
): typeof handler;
|
|
65
|
+
|
|
66
|
+
export function defineWorkflowBlockDeactivate(
|
|
67
|
+
block: WorkflowTriggerBlockDefinition,
|
|
68
|
+
handler: (params: Readonly<{
|
|
69
|
+
activationId: string;
|
|
70
|
+
subscriptionRef: string | null;
|
|
71
|
+
}>) => Promise<void>,
|
|
72
|
+
): typeof handler;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const WORKFLOW_BLOCK_MARKER = Symbol.for("zhike.workflow-block.v1");
|
|
2
|
+
|
|
3
|
+
export function defineWorkflowBlock(definition) {
|
|
4
|
+
if (!definition || typeof definition !== "object" || Array.isArray(definition) ||
|
|
5
|
+
!["step", "trigger"].includes(definition.type)) {
|
|
6
|
+
throw new TypeError("workflow_block_definition_invalid");
|
|
7
|
+
}
|
|
8
|
+
return Object.freeze({ ...definition, [WORKFLOW_BLOCK_MARKER]: true });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function bindWorkflowBlockHandler(block, handler, type, kind) {
|
|
12
|
+
if (!block?.[WORKFLOW_BLOCK_MARKER] || block.type !== type || typeof handler !== "function") {
|
|
13
|
+
throw new TypeError(`workflow_block_${kind}_invalid`);
|
|
14
|
+
}
|
|
15
|
+
return handler;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function defineWorkflowBlockExecute(block, handler) {
|
|
19
|
+
return bindWorkflowBlockHandler(block, handler, "step", "execute");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function defineWorkflowBlockActivate(block, handler) {
|
|
23
|
+
return bindWorkflowBlockHandler(block, handler, "trigger", "activate");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function defineWorkflowBlockTrigger(block, handler) {
|
|
27
|
+
return bindWorkflowBlockHandler(block, handler, "trigger", "trigger");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function defineWorkflowBlockDeactivate(block, handler) {
|
|
31
|
+
return bindWorkflowBlockHandler(block, handler, "trigger", "deactivate");
|
|
32
|
+
}
|