@77-zhike/app-sdk 0.2.0-d7b1.10
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 +251 -0
- package/package.json +71 -0
- package/src/bin.js +15 -0
- package/src/cli/api-client.js +249 -0
- package/src/cli/browser-auth.js +212 -0
- package/src/cli/credential-store.js +101 -0
- package/src/cli/development-app.js +77 -0
- package/src/cli/development-logs.js +183 -0
- package/src/cli/development-watch.js +205 -0
- package/src/cli/index.js +256 -0
- package/src/cli/local-commands.js +80 -0
- package/src/cli/logs.js +56 -0
- package/src/cli/project-init.js +259 -0
- package/src/cli/upload-bundle.js +73 -0
- package/src/client/analysis.d.ts +4 -0
- package/src/client/analysis.js +12 -0
- package/src/client/destinations.d.ts +25 -0
- package/src/client/destinations.js +70 -0
- package/src/client/external-url.js +9 -0
- package/src/client/index.d.ts +249 -0
- package/src/client/index.js +72 -0
- package/src/client/interactions.d.ts +23 -0
- package/src/client/interactions.js +62 -0
- package/src/client/points.generated.js +67 -0
- package/src/config.js +68 -0
- package/src/development-workspace.js +154 -0
- package/src/errors.js +20 -0
- package/src/forms/array-contract.d.ts +32 -0
- package/src/forms/array-contract.js +138 -0
- package/src/forms/components.generated.js +4 -0
- package/src/forms/factory.js +41 -0
- package/src/forms/field-array.d.ts +16 -0
- package/src/forms/field-array.js +52 -0
- package/src/forms/index.d.ts +118 -0
- package/src/forms/index.js +5 -0
- package/src/forms/normalize-options.js +19 -0
- package/src/forms/options.d.ts +30 -0
- package/src/forms/options.js +55 -0
- package/src/forms/record-options.d.ts +28 -0
- package/src/forms/record-options.js +92 -0
- package/src/forms/schema.d.ts +82 -0
- package/src/forms/schema.js +128 -0
- package/src/forms/state.d.ts +30 -0
- package/src/forms/state.js +82 -0
- package/src/forms/validation.d.ts +9 -0
- package/src/forms/validation.js +81 -0
- package/src/forms/values.d.ts +5 -0
- package/src/forms/values.js +38 -0
- package/src/index.d.ts +13 -0
- package/src/index.js +2 -0
- package/src/jsx-dev-runtime.d.ts +2 -0
- package/src/jsx-dev-runtime.js +1 -0
- package/src/jsx-runtime.d.ts +2 -0
- package/src/jsx-runtime.js +1 -0
- package/src/project-binding.js +84 -0
- package/src/server/index.d.ts +318 -0
- package/src/server/index.js +242 -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/canonical-json.js +17 -0
- package/src/toolchain/dependency-policy.js +181 -0
- package/src/toolchain/index.js +15 -0
- package/src/toolchain/real-build.js +459 -0
- package/src/toolchain/source-discovery.js +919 -0
- package/src/toolchain/transport-tar.js +168 -0
- package/src/toolchain/webhook-response.js +50 -0
- 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 +5 -0
- package/src/ui/events.d.ts +10 -0
- package/src/ui/factory.js +70 -0
- package/src/ui/icons.generated.js +2 -0
- package/src/ui/index.d.ts +343 -0
- package/src/ui/index.js +4 -0
- package/src/ui/jsx.generated.d.ts +359 -0
- package/src/ui/native-dom.d.ts +49 -0
- package/src/ui/native-dom.js +44 -0
- package/src/ui/style.d.ts +5 -0
- package/src/ui/style.generated.js +2 -0
- package/src/ui/style.js +15 -0
- package/src/ui/validation.js +86 -0
|
@@ -0,0 +1,242 @@
|
|
|
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";
|
|
11
|
+
const LIFECYCLE_EVENT_MARKER = Symbol.for("zhike.lifecycle-event.v1");
|
|
12
|
+
const WEBHOOK_HANDLER_MARKER = Symbol.for("zhike.webhook-handler.v1");
|
|
13
|
+
const WORKSPACE_CONNECTION_EVENTS = new Set([
|
|
14
|
+
"app-installed",
|
|
15
|
+
"app-uninstalled",
|
|
16
|
+
"connection-added",
|
|
17
|
+
"connection-removed",
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
export const ZHIKE_API_TOKEN = globalThis.ZHIKE_API_TOKEN;
|
|
21
|
+
const ZHIKE_OPEN_API_BASE_URL = "https://api-clm.77hub.com/v1";
|
|
22
|
+
export const kv = globalThis.ZHIKE_APP_KV;
|
|
23
|
+
|
|
24
|
+
function requireSettingKey(key) {
|
|
25
|
+
if (typeof key !== "string" || !key || key === "." || key === "..") {
|
|
26
|
+
throw new TypeError("setting_key_invalid");
|
|
27
|
+
}
|
|
28
|
+
return encodeURIComponent(key);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function requestAppSetting(path, { method = "GET", value, expectedRevision, idempotencyKey } = {}) {
|
|
32
|
+
const token = globalThis.ZHIKE_API_TOKEN ?? ZHIKE_API_TOKEN;
|
|
33
|
+
if (typeof token !== "string" || token.length === 0) {
|
|
34
|
+
const error = new Error("open_api_token_unavailable");
|
|
35
|
+
error.code = "open_api_token_unavailable";
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
const headers = { authorization: `Bearer ${token}` };
|
|
39
|
+
if (idempotencyKey !== undefined) {
|
|
40
|
+
if (typeof idempotencyKey !== "string" || !/^[!-~]{1,200}$/.test(idempotencyKey)) {
|
|
41
|
+
throw new TypeError("idempotency_key_invalid");
|
|
42
|
+
}
|
|
43
|
+
headers["idempotency-key"] = idempotencyKey;
|
|
44
|
+
}
|
|
45
|
+
if (value !== undefined) {
|
|
46
|
+
if (!Number.isSafeInteger(expectedRevision) || expectedRevision < 0) {
|
|
47
|
+
throw new TypeError("expected_revision_invalid");
|
|
48
|
+
}
|
|
49
|
+
headers["if-match"] = `"${expectedRevision}"`;
|
|
50
|
+
headers["content-type"] = "application/json";
|
|
51
|
+
}
|
|
52
|
+
const response = await fetch(`${ZHIKE_OPEN_API_BASE_URL}${path}`, {
|
|
53
|
+
method,
|
|
54
|
+
headers,
|
|
55
|
+
...(value === undefined ? {} : { body: JSON.stringify({ value }) }),
|
|
56
|
+
});
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
const body = await response.json().catch(() => ({}));
|
|
59
|
+
const code = typeof body.code === "string" && /^[a-z_]{1,100}$/.test(body.code)
|
|
60
|
+
? body.code
|
|
61
|
+
: "app_setting_request_failed";
|
|
62
|
+
const error = new Error(code);
|
|
63
|
+
error.code = code;
|
|
64
|
+
error.status = response.status;
|
|
65
|
+
error.requestId = typeof body.requestId === "string" ? body.requestId : null;
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
return Object.freeze(await response.json());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** 读取当前 App 的全部有效配置。 */
|
|
72
|
+
export const getSettings = () => requestAppSetting("/app-settings");
|
|
73
|
+
/** 读取当前 App 的单个配置及 revision。 */
|
|
74
|
+
export const getSetting = (key) => requestAppSetting(`/app-settings/${requireSettingKey(key)}`);
|
|
75
|
+
/** 条件写入当前 App 的单个配置;null 清空但保留 revision。 */
|
|
76
|
+
export const setSetting = (key, value, options = {}) => {
|
|
77
|
+
if (value === undefined) throw new TypeError("setting_value_required");
|
|
78
|
+
return requestAppSetting(`/app-settings/${requireSettingKey(key)}`, {
|
|
79
|
+
method: "PUT",
|
|
80
|
+
value,
|
|
81
|
+
expectedRevision: options.expectedRevision,
|
|
82
|
+
idempotencyKey: options.idempotencyKey,
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export async function createWebhookHandler({ fileName } = {}) {
|
|
87
|
+
if (typeof fileName !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(fileName)) {
|
|
88
|
+
const error = new Error("webhook_handler_file_name_invalid");
|
|
89
|
+
error.code = "webhook_handler_file_name_invalid";
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
const capability = globalThis.ZHIKE_WEBHOOK_HANDLER_CAPABILITY;
|
|
93
|
+
const handler = capability?.create
|
|
94
|
+
? await capability.create(fileName)
|
|
95
|
+
: globalThis.ZHIKE_WEBHOOK_HANDLERS?.[fileName];
|
|
96
|
+
if (
|
|
97
|
+
!handler ||
|
|
98
|
+
typeof handler !== "object" ||
|
|
99
|
+
handler.fileName !== fileName ||
|
|
100
|
+
typeof handler.id !== "string" ||
|
|
101
|
+
typeof handler.url !== "string"
|
|
102
|
+
) {
|
|
103
|
+
const error = new Error("webhook_handler_not_declared");
|
|
104
|
+
error.code = "webhook_handler_not_declared";
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
return Object.freeze({ id: handler.id, fileName, url: handler.url });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function listWebhookHandlers() {
|
|
111
|
+
const capability = globalThis.ZHIKE_WEBHOOK_HANDLER_CAPABILITY;
|
|
112
|
+
const handlers = capability?.list
|
|
113
|
+
? await capability.list()
|
|
114
|
+
: Object.values(globalThis.ZHIKE_WEBHOOK_HANDLERS ?? {});
|
|
115
|
+
if (!Array.isArray(handlers)) {
|
|
116
|
+
const error = new Error("webhook_handler_runtime_unavailable");
|
|
117
|
+
error.code = "webhook_handler_runtime_unavailable";
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
return Object.freeze(handlers.map((handler) => Object.freeze({ ...handler })));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export async function deleteWebhookHandler(id) {
|
|
124
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
125
|
+
const error = new Error("webhook_handler_id_invalid");
|
|
126
|
+
error.code = "webhook_handler_id_invalid";
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
const capability = globalThis.ZHIKE_WEBHOOK_HANDLER_CAPABILITY;
|
|
130
|
+
if (!capability?.delete) {
|
|
131
|
+
const error = new Error("webhook_handler_runtime_unavailable");
|
|
132
|
+
error.code = "webhook_handler_runtime_unavailable";
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
await capability.delete(id);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 同步读取本次执行快照;旧 dispatcher 的 exact lifecycle global 仅作兼容。
|
|
139
|
+
export function getWorkspaceConnection() {
|
|
140
|
+
const current = globalThis.ZHIKE_CURRENT_WORKSPACE_CONNECTION;
|
|
141
|
+
const connection = globalThis.ZHIKE_WORKSPACE_CONNECTION ??
|
|
142
|
+
(current?.status === "ready" ? current.connection : null);
|
|
143
|
+
if (
|
|
144
|
+
!connection ||
|
|
145
|
+
typeof connection !== "object" ||
|
|
146
|
+
typeof connection.id !== "string" ||
|
|
147
|
+
typeof connection.generation !== "string" ||
|
|
148
|
+
!["oauth2", "secret"].includes(connection.type) ||
|
|
149
|
+
typeof connection.value !== "string"
|
|
150
|
+
) {
|
|
151
|
+
const code = ["connection_not_configured", "connection_not_connected", "connection_credential_unavailable", "connection_temporarily_unavailable"].includes(current?.code)
|
|
152
|
+
? current.code : "connection_unavailable";
|
|
153
|
+
const error = new Error(code);
|
|
154
|
+
error.code = code;
|
|
155
|
+
throw error;
|
|
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");
|
|
166
|
+
return Object.freeze({
|
|
167
|
+
id: connection.id,
|
|
168
|
+
generation: connection.generation,
|
|
169
|
+
type: connection.type,
|
|
170
|
+
protocol,
|
|
171
|
+
value: connection.value,
|
|
172
|
+
...(connection.type === "oauth2" ? { accessToken: connection.value } : {}),
|
|
173
|
+
...(protocol === "oidc" ? { idToken: connection.idToken } : {}),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function getCurrentUser() {
|
|
178
|
+
const current = globalThis.ZHIKE_CURRENT_USER;
|
|
179
|
+
if (
|
|
180
|
+
!current ||
|
|
181
|
+
typeof current !== "object" ||
|
|
182
|
+
typeof current.workspaceMemberId !== "string" ||
|
|
183
|
+
typeof current.displayName !== "string"
|
|
184
|
+
) {
|
|
185
|
+
const error = new Error("current_user_unavailable");
|
|
186
|
+
error.code = "current_user_unavailable";
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
return Object.freeze({
|
|
190
|
+
workspaceMemberId: current.workspaceMemberId,
|
|
191
|
+
displayName: current.displayName,
|
|
192
|
+
primaryEmailAddress: typeof current.primaryEmailAddress === "string" ? current.primaryEmailAddress : null,
|
|
193
|
+
avatarUrl: typeof current.avatarUrl === "string" ? current.avatarUrl : null,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 仅兼容已构建的旧 Bundle;新源码入口由 .server.ts 的默认 async 函数确定。
|
|
198
|
+
export function defineServerFunction(handler) {
|
|
199
|
+
if (typeof handler !== "function") {
|
|
200
|
+
throw new TypeError("Server Function handler 必须是函数");
|
|
201
|
+
}
|
|
202
|
+
Object.defineProperty(handler, SERVER_FUNCTION_MARKER, { value: true });
|
|
203
|
+
return handler;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 仅保留已发布旧 Bundle 的运行时兼容;新源码必须使用约定式默认 async function。
|
|
207
|
+
export function defineLifecycleEvent(definition) {
|
|
208
|
+
if (!definition || typeof definition !== "object" || Array.isArray(definition)) {
|
|
209
|
+
throw new TypeError("Lifecycle Event definition 必须是对象");
|
|
210
|
+
}
|
|
211
|
+
if (!WORKSPACE_CONNECTION_EVENTS.has(definition.event)) {
|
|
212
|
+
throw new TypeError("Lifecycle Event 类型不受支持");
|
|
213
|
+
}
|
|
214
|
+
if (typeof definition.handler !== "function") {
|
|
215
|
+
throw new TypeError("Lifecycle Event handler 必须是函数");
|
|
216
|
+
}
|
|
217
|
+
return Object.freeze({ ...definition, [LIFECYCLE_EVENT_MARKER]: true });
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function defineWebhookHandler(definition) {
|
|
221
|
+
if (!definition || typeof definition !== "object" || Array.isArray(definition)) {
|
|
222
|
+
throw new TypeError("Webhook Handler definition 必须是对象");
|
|
223
|
+
}
|
|
224
|
+
if (!Array.isArray(definition.methods) || definition.methods.length === 0) {
|
|
225
|
+
throw new TypeError("Webhook Handler methods 不能为空");
|
|
226
|
+
}
|
|
227
|
+
if (typeof definition.handler !== "function") {
|
|
228
|
+
throw new TypeError("Webhook Handler handler 必须是函数");
|
|
229
|
+
}
|
|
230
|
+
return Object.freeze({ ...definition, [WEBHOOK_HANDLER_MARKER]: true });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
export async function requireWorkspaceAdmin() {
|
|
235
|
+
const check = globalThis.ZHIKE_CALLER_CAPABILITY?.requireWorkspaceAdmin;
|
|
236
|
+
if (typeof check !== "function") {
|
|
237
|
+
const error = new Error("runtime_function_caller_unavailable");
|
|
238
|
+
error.code = "runtime_function_caller_unavailable";
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
await check();
|
|
242
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function canonicalJsonBytes(value) {
|
|
2
|
+
return Buffer.from(`${JSON.stringify(sortJson(value))}\n`, "utf8");
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function sortJson(value) {
|
|
6
|
+
if (Array.isArray(value)) {
|
|
7
|
+
return value.map(sortJson);
|
|
8
|
+
}
|
|
9
|
+
if (value && typeof value === "object") {
|
|
10
|
+
return Object.fromEntries(
|
|
11
|
+
Object.keys(value)
|
|
12
|
+
.sort()
|
|
13
|
+
.map((key) => [key, sortJson(value[key])]),
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
return value;
|
|
17
|
+
}
|