openxiangda-devkit-core 2.0.0-alpha.12
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 +12 -0
- package/dist/application-services.d.ts +229 -0
- package/dist/application-services.d.ts.map +1 -0
- package/dist/application-services.js +660 -0
- package/dist/application-services.js.map +1 -0
- package/dist/command-registry.d.ts +460 -0
- package/dist/command-registry.d.ts.map +1 -0
- package/dist/command-registry.js +271 -0
- package/dist/command-registry.js.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -0
- package/dist/control-plane-client.d.ts +446 -0
- package/dist/control-plane-client.d.ts.map +1 -0
- package/dist/control-plane-client.js +651 -0
- package/dist/control-plane-client.js.map +1 -0
- package/dist/deployment.d.ts +28 -0
- package/dist/deployment.d.ts.map +1 -0
- package/dist/deployment.js +177 -0
- package/dist/deployment.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/package-compiler.d.ts +2 -0
- package/dist/package-compiler.d.ts.map +1 -0
- package/dist/package-compiler.js +2 -0
- package/dist/package-compiler.js.map +1 -0
- package/dist/session.d.ts +20 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +48 -0
- package/dist/session.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/dist/workspace-loader.d.ts +17 -0
- package/dist/workspace-loader.d.ts.map +1 -0
- package/dist/workspace-loader.js +123 -0
- package/dist/workspace-loader.js.map +1 -0
- package/dist/workspace.d.ts +15 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +34 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +37 -0
|
@@ -0,0 +1,651 @@
|
|
|
1
|
+
import { OPENXIANGDA_CONTRACT_VERSION, SCHEMA_VERSIONS, } from "openxiangda-contracts/browser";
|
|
2
|
+
export class ControlPlaneError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
code;
|
|
5
|
+
data;
|
|
6
|
+
constructor(status, code, message, data) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.data = data;
|
|
11
|
+
this.name = "ControlPlaneError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class OpenXiangdaControlPlaneClient {
|
|
15
|
+
options;
|
|
16
|
+
baseUrl;
|
|
17
|
+
fetch;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.options = options;
|
|
20
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
21
|
+
this.fetch = options.fetch || globalThis.fetch.bind(globalThis);
|
|
22
|
+
}
|
|
23
|
+
async capabilities() {
|
|
24
|
+
const capabilities = await this.json("/openxiangda-api/v2/capabilities");
|
|
25
|
+
if (capabilities.contractVersion !== OPENXIANGDA_CONTRACT_VERSION) {
|
|
26
|
+
throw new ControlPlaneError(409, "OPENXIANGDA_CONTRACT_VERSION_MISMATCH", `平台 contract ${capabilities.contractVersion} 与工具 ${OPENXIANGDA_CONTRACT_VERSION} 不兼容`, capabilities);
|
|
27
|
+
}
|
|
28
|
+
return capabilities;
|
|
29
|
+
}
|
|
30
|
+
async provisionApplication(input) {
|
|
31
|
+
return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(input.appCode)}/provision`, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
body: JSON.stringify({
|
|
34
|
+
name: input.name,
|
|
35
|
+
description: input.description,
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async oauthClients(appCode) {
|
|
40
|
+
return await this.json(`${this.oauthPath(appCode)}/clients`);
|
|
41
|
+
}
|
|
42
|
+
async runtimeOAuthCredentialStatus(appCode, environmentKey) {
|
|
43
|
+
return await this.json(`${this.oauthPath(appCode)}/runtime-credentials/${encodeURIComponent(environmentKey)}`);
|
|
44
|
+
}
|
|
45
|
+
async stageRuntimeOAuthCredentialRotation(appCode, environmentKey, input) {
|
|
46
|
+
return await this.json(`${this.oauthPath(appCode)}/runtime-credentials/${encodeURIComponent(environmentKey)}/rotate`, { method: "POST", body: JSON.stringify(input) });
|
|
47
|
+
}
|
|
48
|
+
async createOAuthClient(appCode, input) {
|
|
49
|
+
return await this.json(`${this.oauthPath(appCode)}/clients`, {
|
|
50
|
+
method: "POST",
|
|
51
|
+
body: JSON.stringify(input),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async updateOAuthClient(appCode, clientId, input) {
|
|
55
|
+
return await this.json(`${this.oauthPath(appCode)}/clients/${encodeURIComponent(clientId)}/update`, { method: "POST", body: JSON.stringify(input) });
|
|
56
|
+
}
|
|
57
|
+
async rotateOAuthClientSecret(appCode, clientId, gracePeriodSeconds = 600) {
|
|
58
|
+
return await this.json(`${this.oauthPath(appCode)}/clients/${encodeURIComponent(clientId)}/rotate-secret`, {
|
|
59
|
+
method: "POST",
|
|
60
|
+
body: JSON.stringify({ gracePeriodSeconds }),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async revokeOAuthClient(appCode, clientId) {
|
|
64
|
+
return await this.json(`${this.oauthPath(appCode)}/clients/${encodeURIComponent(clientId)}/revoke`, { method: "POST", body: "{}" });
|
|
65
|
+
}
|
|
66
|
+
async oauthAuditEvents(appCode, input = {}) {
|
|
67
|
+
const query = new URLSearchParams();
|
|
68
|
+
if (input.environmentKey)
|
|
69
|
+
query.set("environmentKey", input.environmentKey);
|
|
70
|
+
if (input.clientId)
|
|
71
|
+
query.set("clientId", input.clientId);
|
|
72
|
+
if (input.limit)
|
|
73
|
+
query.set("limit", String(input.limit));
|
|
74
|
+
const suffix = query.size ? `?${query}` : "";
|
|
75
|
+
return await this.json(`${this.oauthPath(appCode)}/audit-events${suffix}`);
|
|
76
|
+
}
|
|
77
|
+
async oauthApplicationPrincipal(appCode) {
|
|
78
|
+
return await this.json(`${this.oauthPath(appCode)}/principal`);
|
|
79
|
+
}
|
|
80
|
+
async exchangeOAuthClientCredentials(input) {
|
|
81
|
+
const response = await this.fetch(`${this.baseUrl}/openxiangda-api/v2/oauth2/token`, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: {
|
|
84
|
+
Accept: "application/json",
|
|
85
|
+
"Content-Type": "application/json",
|
|
86
|
+
Authorization: `Basic ${this.basicCredentials(input.clientId, input.clientSecret)}`,
|
|
87
|
+
},
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
grant_type: "client_credentials",
|
|
90
|
+
...(input.scope?.length ? { scope: input.scope.join(" ") } : {}),
|
|
91
|
+
}),
|
|
92
|
+
});
|
|
93
|
+
let data;
|
|
94
|
+
try {
|
|
95
|
+
data = (await response.json());
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
throw new ControlPlaneError(response.status, "OAUTH2_TOKEN_RESPONSE_INVALID", "OAuth2 token 端点返回的不是有效 JSON");
|
|
99
|
+
}
|
|
100
|
+
if (!response.ok || data.error) {
|
|
101
|
+
throw new ControlPlaneError(response.status, data.error || "OAUTH2_TOKEN_REQUEST_FAILED", data.error_description || `OAuth2 token 请求失败: ${response.status}`);
|
|
102
|
+
}
|
|
103
|
+
return data;
|
|
104
|
+
}
|
|
105
|
+
async applicationApiResponse(appCode, environmentKey, runtimePath, input) {
|
|
106
|
+
const path = this.applicationApiPath(appCode, environmentKey, runtimePath, input.query);
|
|
107
|
+
const { query: _query, roleSessionId: _roleSessionId, ...init } = input;
|
|
108
|
+
return await this.fetch(`${this.baseUrl}${path}`, {
|
|
109
|
+
...init,
|
|
110
|
+
credentials: init.credentials || this.options.credentials || "include",
|
|
111
|
+
headers: {
|
|
112
|
+
Accept: "application/json",
|
|
113
|
+
...(this.options.token
|
|
114
|
+
? { Authorization: `Bearer ${this.options.token}` }
|
|
115
|
+
: {}),
|
|
116
|
+
...this.optionalRoleSessionHeaders(input),
|
|
117
|
+
...(init.headers || {}),
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
async applicationApiJson(appCode, environmentKey, runtimePath, input) {
|
|
122
|
+
const { body, ...request } = input;
|
|
123
|
+
const response = await this.applicationApiResponse(appCode, environmentKey, runtimePath, {
|
|
124
|
+
...request,
|
|
125
|
+
headers: {
|
|
126
|
+
...(body === undefined ? {} : { "Content-Type": "application/json" }),
|
|
127
|
+
...(input.headers || {}),
|
|
128
|
+
},
|
|
129
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
130
|
+
});
|
|
131
|
+
const text = await response.text();
|
|
132
|
+
let data = undefined;
|
|
133
|
+
if (text) {
|
|
134
|
+
try {
|
|
135
|
+
data = JSON.parse(text);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
throw new ControlPlaneError(response.status, "APPLICATION_API_RESPONSE_INVALID", "应用后端返回的不是有效 JSON", text);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
throw new ControlPlaneError(response.status, String(data?.errorCode || data?.code || "APPLICATION_API_REQUEST_FAILED"), String(data?.message || data?.error || `应用后端请求失败: ${response.status}`), data);
|
|
143
|
+
}
|
|
144
|
+
return data;
|
|
145
|
+
}
|
|
146
|
+
async uploadArtifact(input) {
|
|
147
|
+
const params = new URLSearchParams({
|
|
148
|
+
kind: input.kind,
|
|
149
|
+
contentType: input.contentType,
|
|
150
|
+
metadata: JSON.stringify(input.metadata || {}),
|
|
151
|
+
});
|
|
152
|
+
const form = new FormData();
|
|
153
|
+
form.append("file", new Blob([input.content], { type: input.contentType }), input.digest);
|
|
154
|
+
return await this.request(`/openxiangda-api/v2/applications/${encodeURIComponent(input.appCode)}/artifacts/${input.digest}?${params}`, { method: "POST", body: form });
|
|
155
|
+
}
|
|
156
|
+
async createDeployment(input) {
|
|
157
|
+
return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(input.appCode)}/deployments`, {
|
|
158
|
+
method: "POST",
|
|
159
|
+
body: JSON.stringify({
|
|
160
|
+
environmentId: input.environmentId,
|
|
161
|
+
environmentKind: input.environmentKind,
|
|
162
|
+
kind: input.kind || "deploy",
|
|
163
|
+
packageDigest: input.packageDigest,
|
|
164
|
+
package: input.package,
|
|
165
|
+
idempotencyKey: input.idempotencyKey,
|
|
166
|
+
requestId: input.requestId,
|
|
167
|
+
}),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
async deployment(appCode, deploymentId) {
|
|
171
|
+
return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/deployments/${encodeURIComponent(deploymentId)}`);
|
|
172
|
+
}
|
|
173
|
+
async deployments(appCode, limit = 20) {
|
|
174
|
+
return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/deployments?limit=${Math.min(Math.max(Number(limit) || 20, 1), 100)}`);
|
|
175
|
+
}
|
|
176
|
+
async retryDeployment(appCode, deploymentId) {
|
|
177
|
+
return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/deployments/${encodeURIComponent(deploymentId)}/retry`, { method: "POST", body: "{}" });
|
|
178
|
+
}
|
|
179
|
+
async cancelDeployment(appCode, deploymentId) {
|
|
180
|
+
return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/deployments/${encodeURIComponent(deploymentId)}/cancel`, { method: "POST", body: "{}" });
|
|
181
|
+
}
|
|
182
|
+
async promote(input) {
|
|
183
|
+
return await this.deployExistingVersion(input, "promotions");
|
|
184
|
+
}
|
|
185
|
+
async rollback(input) {
|
|
186
|
+
return await this.deployExistingVersion(input, "rollbacks");
|
|
187
|
+
}
|
|
188
|
+
async appVersions(appCode, limit = 30) {
|
|
189
|
+
return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/versions?limit=${Math.min(Math.max(Number(limit) || 30, 1), 100)}`);
|
|
190
|
+
}
|
|
191
|
+
async environmentHead(appCode, environmentKey) {
|
|
192
|
+
return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/environment-heads/${encodeURIComponent(environmentKey)}`);
|
|
193
|
+
}
|
|
194
|
+
async applicationSecrets(appCode, environmentKey) {
|
|
195
|
+
return await this.json(this.applicationSecretsPath(appCode, environmentKey));
|
|
196
|
+
}
|
|
197
|
+
async applicationSecret(appCode, environmentKey, name) {
|
|
198
|
+
return await this.json(`${this.applicationSecretsPath(appCode, environmentKey)}/${encodeURIComponent(name)}`);
|
|
199
|
+
}
|
|
200
|
+
async createApplicationSecret(appCode, environmentKey, input) {
|
|
201
|
+
return await this.json(this.applicationSecretsPath(appCode, environmentKey), { method: "POST", body: JSON.stringify({ expectedRevision: 0, ...input }) });
|
|
202
|
+
}
|
|
203
|
+
async updateApplicationSecret(appCode, environmentKey, name, input) {
|
|
204
|
+
return await this.json(`${this.applicationSecretsPath(appCode, environmentKey)}/${encodeURIComponent(name)}`, { method: "PATCH", body: JSON.stringify(input) });
|
|
205
|
+
}
|
|
206
|
+
async rotateApplicationSecret(appCode, environmentKey, name, input) {
|
|
207
|
+
return await this.json(`${this.applicationSecretsPath(appCode, environmentKey)}/${encodeURIComponent(name)}/rotate`, { method: "POST", body: JSON.stringify(input) });
|
|
208
|
+
}
|
|
209
|
+
async deleteApplicationSecret(appCode, environmentKey, name, input) {
|
|
210
|
+
return await this.json(`${this.applicationSecretsPath(appCode, environmentKey)}/${encodeURIComponent(name)}`, { method: "DELETE", body: JSON.stringify(input) });
|
|
211
|
+
}
|
|
212
|
+
async applicationSecretAuditEvents(appCode, environmentKey, name, limit = 100) {
|
|
213
|
+
return await this.json(`${this.applicationSecretsPath(appCode, environmentKey)}/${encodeURIComponent(name)}/audit?limit=${Math.min(Math.max(Number(limit) || 100, 1), 500)}`);
|
|
214
|
+
}
|
|
215
|
+
async createEventSubscription(input) {
|
|
216
|
+
const { appCode, ...body } = input;
|
|
217
|
+
return await this.json(`${this.eventsPath(appCode)}/subscriptions`, { method: "POST", body: JSON.stringify(body) });
|
|
218
|
+
}
|
|
219
|
+
async eventSubscriptions(appCode) {
|
|
220
|
+
return await this.json(`${this.eventsPath(appCode)}/subscriptions`);
|
|
221
|
+
}
|
|
222
|
+
async setEventSubscriptionStatus(appCode, subscriptionId, input) {
|
|
223
|
+
return await this.json(`${this.eventsPath(appCode)}/subscriptions/${encodeURIComponent(subscriptionId)}/status`, { method: "POST", body: JSON.stringify(input) });
|
|
224
|
+
}
|
|
225
|
+
async rotateEventSubscriptionSecret(appCode, subscriptionId, input) {
|
|
226
|
+
return await this.json(`${this.eventsPath(appCode)}/subscriptions/${encodeURIComponent(subscriptionId)}/rotate-secret`, { method: "POST", body: JSON.stringify(input) });
|
|
227
|
+
}
|
|
228
|
+
async eventDeliveries(appCode, limit = 100) {
|
|
229
|
+
return await this.json(`${this.eventsPath(appCode)}/deliveries?limit=${Math.min(Math.max(Number(limit) || 100, 1), 500)}`);
|
|
230
|
+
}
|
|
231
|
+
async replayEventDelivery(appCode, deliveryId, idempotencyKey) {
|
|
232
|
+
return await this.json(`${this.eventsPath(appCode)}/deliveries/${encodeURIComponent(deliveryId)}/replay`, {
|
|
233
|
+
method: "POST",
|
|
234
|
+
body: JSON.stringify(idempotencyKey ? { idempotencyKey } : {}),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
async createTimerSubscription(input) {
|
|
238
|
+
const { appCode, ...body } = input;
|
|
239
|
+
return await this.json(`${this.eventsPath(appCode)}/timers`, { method: "POST", body: JSON.stringify(body) });
|
|
240
|
+
}
|
|
241
|
+
async timerSubscriptions(appCode) {
|
|
242
|
+
return await this.json(`${this.eventsPath(appCode)}/timers`);
|
|
243
|
+
}
|
|
244
|
+
async setTimerSubscriptionStatus(appCode, timerId, input) {
|
|
245
|
+
return await this.json(`${this.eventsPath(appCode)}/timers/${encodeURIComponent(timerId)}/status`, { method: "POST", body: JSON.stringify(input) });
|
|
246
|
+
}
|
|
247
|
+
async registerWorkflowDefinition(appCode, version, definition) {
|
|
248
|
+
return await this.json(`${this.workflowPath(appCode)}/definitions`, {
|
|
249
|
+
method: "POST",
|
|
250
|
+
body: JSON.stringify({ version, definition }),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
async registerWorkflowBinding(appCode, version, binding) {
|
|
254
|
+
return await this.json(`${this.workflowPath(appCode)}/bindings`, {
|
|
255
|
+
method: "POST",
|
|
256
|
+
body: JSON.stringify({ version, binding }),
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
async applyWorkflowAssigneeProvider(appCode, input) {
|
|
260
|
+
return await this.json(`${this.workflowPath(appCode)}/assignee-providers`, { method: "POST", body: JSON.stringify(input) });
|
|
261
|
+
}
|
|
262
|
+
async workflowAssigneeProviders(appCode, environmentKey = "production") {
|
|
263
|
+
return await this.json(`${this.workflowPath(appCode)}/assignee-providers?environmentKey=${encodeURIComponent(environmentKey)}`);
|
|
264
|
+
}
|
|
265
|
+
async rotateWorkflowAssigneeProviderSecret(appCode, providerCode, input) {
|
|
266
|
+
return await this.json(`${this.workflowPath(appCode)}/assignee-providers/${encodeURIComponent(providerCode)}/rotate-secret`, { method: "POST", body: JSON.stringify(input) });
|
|
267
|
+
}
|
|
268
|
+
async activateWorkflow(appCode, workflowCode, input) {
|
|
269
|
+
return await this.json(`${this.workflowPath(appCode)}/definitions/${encodeURIComponent(workflowCode)}/activate`, { method: "POST", body: JSON.stringify(input) });
|
|
270
|
+
}
|
|
271
|
+
async prepareWorkflowStart(input) {
|
|
272
|
+
const { appCode, workflowCode, roleSessionId, ...body } = input;
|
|
273
|
+
return await this.json(`${this.workflowPath(appCode)}/definitions/${encodeURIComponent(workflowCode)}/prepare-start`, {
|
|
274
|
+
method: "POST",
|
|
275
|
+
body: JSON.stringify(body),
|
|
276
|
+
headers: this.roleSessionHeaders({ roleSessionId }),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
async startWorkflow(appCode, input) {
|
|
280
|
+
const { roleSessionId, ...body } = input;
|
|
281
|
+
return await this.json(`${this.workflowPath(appCode)}/instances`, {
|
|
282
|
+
method: "POST",
|
|
283
|
+
body: JSON.stringify(body),
|
|
284
|
+
headers: this.roleSessionHeaders({ roleSessionId }),
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
async createWorkflowDelegation(appCode, input, request) {
|
|
288
|
+
return await this.json(`${this.workflowPath(appCode)}/delegations`, {
|
|
289
|
+
method: "POST",
|
|
290
|
+
body: JSON.stringify(input),
|
|
291
|
+
headers: this.roleSessionHeaders(request),
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
async workflowDelegationTargets(appCode, userId, request) {
|
|
295
|
+
return await this.json(`${this.workflowPath(appCode)}/delegations/targets?userId=${encodeURIComponent(userId)}`, { headers: this.roleSessionHeaders(request) });
|
|
296
|
+
}
|
|
297
|
+
async workflowDelegations(appCode, request, input = {}) {
|
|
298
|
+
return await this.json(`${this.workflowPath(appCode)}/delegations?all=${input.all === true}`, { headers: this.roleSessionHeaders(request) });
|
|
299
|
+
}
|
|
300
|
+
async revokeWorkflowDelegation(appCode, delegationId, request) {
|
|
301
|
+
return await this.json(`${this.workflowPath(appCode)}/delegations/${encodeURIComponent(delegationId)}/revoke`, {
|
|
302
|
+
method: "POST",
|
|
303
|
+
headers: this.roleSessionHeaders(request),
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
async workflowTaskSurface(appCode, taskId, request) {
|
|
307
|
+
return await this.json(`${this.workflowPath(appCode)}/tasks/${encodeURIComponent(taskId)}/surface`, { headers: this.roleSessionHeaders(request) });
|
|
308
|
+
}
|
|
309
|
+
async workflowInstanceSurface(appCode, instanceId, request) {
|
|
310
|
+
return await this.json(`${this.workflowPath(appCode)}/instances/${encodeURIComponent(instanceId)}/surface`, { headers: this.roleSessionHeaders(request) });
|
|
311
|
+
}
|
|
312
|
+
async executeWorkflowTaskCommand(appCode, taskId, command, input, request) {
|
|
313
|
+
return await this.json(`${this.workflowPath(appCode)}/tasks/${encodeURIComponent(taskId)}/commands/${encodeURIComponent(command)}`, {
|
|
314
|
+
method: "POST",
|
|
315
|
+
body: JSON.stringify(input),
|
|
316
|
+
headers: this.roleSessionHeaders(request),
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
async executeWorkflowInstanceCommand(appCode, instanceId, command, input, request) {
|
|
320
|
+
return await this.json(`${this.workflowPath(appCode)}/instances/${encodeURIComponent(instanceId)}/commands/${command}`, {
|
|
321
|
+
method: "POST",
|
|
322
|
+
body: JSON.stringify(input),
|
|
323
|
+
headers: this.roleSessionHeaders(request),
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
async workflowAssignmentExplain(appCode, taskId, request) {
|
|
327
|
+
return await this.json(`${this.workflowPath(appCode)}/tasks/${encodeURIComponent(taskId)}/assignment-explain`, { headers: this.roleSessionHeaders(request) });
|
|
328
|
+
}
|
|
329
|
+
async workflowTimeline(appCode, instanceId, request) {
|
|
330
|
+
return await this.json(`${this.workflowPath(appCode)}/instances/${encodeURIComponent(instanceId)}/timeline`, { headers: this.roleSessionHeaders(request) });
|
|
331
|
+
}
|
|
332
|
+
async workflowWorkCenter(appCode, request, input = {}) {
|
|
333
|
+
const query = new URLSearchParams({
|
|
334
|
+
status: input.status || "pending",
|
|
335
|
+
limit: String(Math.min(Math.max(Number(input.limit) || 50, 1), 200)),
|
|
336
|
+
});
|
|
337
|
+
return await this.json(`${this.workflowPath(appCode)}/work-center/items?${query}`, { headers: this.roleSessionHeaders(request) });
|
|
338
|
+
}
|
|
339
|
+
async workflowKernelInstance(appCode, instanceId, request) {
|
|
340
|
+
return await this.json(`${this.workflowPath(appCode)}/kernel/instances/${encodeURIComponent(instanceId)}`, { headers: this.roleSessionHeaders(request) });
|
|
341
|
+
}
|
|
342
|
+
async workflowKernelWorkCenter(appCode, request, input = {}) {
|
|
343
|
+
const query = new URLSearchParams({
|
|
344
|
+
category: input.category || "pending",
|
|
345
|
+
limit: String(Math.min(Math.max(Number(input.limit) || 50, 1), 200)),
|
|
346
|
+
});
|
|
347
|
+
return await this.json(`${this.workflowPath(appCode)}/kernel/work-center?${query}`, { headers: this.roleSessionHeaders(request) });
|
|
348
|
+
}
|
|
349
|
+
async workflowDiagnostics(appCode, limit = 100) {
|
|
350
|
+
return await this.json(`${this.workflowPath(appCode)}/diagnostics?limit=${Math.min(Math.max(Number(limit) || 100, 1), 500)}`);
|
|
351
|
+
}
|
|
352
|
+
async bootstrapRoleSession(appCode) {
|
|
353
|
+
return await this.json(`${this.authzPath(appCode)}/role-session`);
|
|
354
|
+
}
|
|
355
|
+
async switchRoleSession(appCode, roleAssignmentId) {
|
|
356
|
+
return await this.json(`${this.authzPath(appCode)}/role-session/switch`, {
|
|
357
|
+
method: "POST",
|
|
358
|
+
body: JSON.stringify({ roleAssignmentId }),
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
async currentPrincipal(appCode, request) {
|
|
362
|
+
return await this.json(`${this.authzPath(appCode)}/principal`, { headers: this.roleSessionHeaders(request) });
|
|
363
|
+
}
|
|
364
|
+
async myRoleAssignments(appCode) {
|
|
365
|
+
return await this.json(`${this.authzPath(appCode)}/assignments/me`);
|
|
366
|
+
}
|
|
367
|
+
async applicationRoles(appCode) {
|
|
368
|
+
return await this.json(`${this.authzPath(appCode)}/roles`);
|
|
369
|
+
}
|
|
370
|
+
async createRoleAssignment(appCode, input) {
|
|
371
|
+
return await this.json(`${this.authzPath(appCode)}/assignments`, { method: "POST", body: JSON.stringify(input) });
|
|
372
|
+
}
|
|
373
|
+
async updateRoleAssignment(appCode, assignmentId, input) {
|
|
374
|
+
return await this.json(`${this.authzPath(appCode)}/assignments/${encodeURIComponent(assignmentId)}/update`, { method: "POST", body: JSON.stringify(input) });
|
|
375
|
+
}
|
|
376
|
+
async revokeRoleAssignment(appCode, assignmentId, expectedRevision) {
|
|
377
|
+
return await this.json(`${this.authzPath(appCode)}/assignments/${encodeURIComponent(assignmentId)}/revoke`, { method: "POST", body: JSON.stringify({ expectedRevision }) });
|
|
378
|
+
}
|
|
379
|
+
async explainAuthorization(appCode, input) {
|
|
380
|
+
const { roleSessionId, ...body } = input;
|
|
381
|
+
return await this.json(`${this.authzPath(appCode)}/explain`, {
|
|
382
|
+
method: "POST",
|
|
383
|
+
body: JSON.stringify(body),
|
|
384
|
+
headers: this.roleSessionHeaders({ roleSessionId }),
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
async explainAuthorizations(appCode, input) {
|
|
388
|
+
return await this.json(`${this.authzPath(appCode)}/explain-batch`, {
|
|
389
|
+
method: "POST",
|
|
390
|
+
body: JSON.stringify({
|
|
391
|
+
schemaVersion: SCHEMA_VERSIONS.authorizationBatchRequest,
|
|
392
|
+
requests: input.requests,
|
|
393
|
+
}),
|
|
394
|
+
headers: this.roleSessionHeaders({
|
|
395
|
+
roleSessionId: input.roleSessionId,
|
|
396
|
+
}),
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
async searchDirectoryEntries(appCode, kind, input, request) {
|
|
400
|
+
const query = new URLSearchParams({
|
|
401
|
+
keyword: input.keyword.trim(),
|
|
402
|
+
limit: String(Math.min(Math.max(Number(input.limit) || 20, 1), 50)),
|
|
403
|
+
});
|
|
404
|
+
return await this.json(`${this.directoryPath(appCode)}/${kind}s?${query}`, { headers: this.roleSessionHeaders(request) });
|
|
405
|
+
}
|
|
406
|
+
async resolveDirectoryEntries(appCode, input, request) {
|
|
407
|
+
return await this.json(`${this.directoryPath(appCode)}/resolve`, {
|
|
408
|
+
method: "POST",
|
|
409
|
+
body: JSON.stringify(input),
|
|
410
|
+
headers: this.roleSessionHeaders(request),
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
async applyDataResource(resource, input = {}) {
|
|
414
|
+
return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(resource.appCode)}/data-resources/${encodeURIComponent(resource.code)}/apply`, {
|
|
415
|
+
method: "POST",
|
|
416
|
+
body: JSON.stringify({
|
|
417
|
+
name: resource.name,
|
|
418
|
+
schema: resource.schema,
|
|
419
|
+
capabilities: resource.capabilities,
|
|
420
|
+
dataPolicyCode: resource.dataPolicyCode,
|
|
421
|
+
fieldPolicies: resource.fieldPolicies,
|
|
422
|
+
expectedRevision: input.expectedRevision,
|
|
423
|
+
}),
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
async relationshipGrants(appCode, filter = {}) {
|
|
427
|
+
const query = new URLSearchParams();
|
|
428
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
429
|
+
if (value !== undefined && value !== "")
|
|
430
|
+
query.set(key, value);
|
|
431
|
+
}
|
|
432
|
+
const suffix = query.size ? `?${query.toString()}` : "";
|
|
433
|
+
return await this.json(`${this.authzPath(appCode)}/relationship-grants${suffix}`);
|
|
434
|
+
}
|
|
435
|
+
async createRelationshipGrant(appCode, input) {
|
|
436
|
+
return await this.json(`${this.authzPath(appCode)}/relationship-grants`, { method: "POST", body: JSON.stringify(input) });
|
|
437
|
+
}
|
|
438
|
+
async updateRelationshipGrant(appCode, grantId, input) {
|
|
439
|
+
return await this.json(`${this.authzPath(appCode)}/relationship-grants/${encodeURIComponent(grantId)}/update`, { method: "POST", body: JSON.stringify(input) });
|
|
440
|
+
}
|
|
441
|
+
async revokeRelationshipGrant(appCode, grantId, expectedRevision) {
|
|
442
|
+
return await this.json(`${this.authzPath(appCode)}/relationship-grants/${encodeURIComponent(grantId)}/revoke`, { method: "POST", body: JSON.stringify({ expectedRevision }) });
|
|
443
|
+
}
|
|
444
|
+
async queryData(appCode, resourceCode, query, request = {}) {
|
|
445
|
+
return await this.json(this.dataPath(appCode, resourceCode, "query"), {
|
|
446
|
+
method: "POST",
|
|
447
|
+
body: JSON.stringify(query),
|
|
448
|
+
headers: this.optionalRoleSessionHeaders(request),
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
async getData(appCode, resourceCode, id, request = {}) {
|
|
452
|
+
return await this.json(this.dataPath(appCode, resourceCode, `records/${encodeURIComponent(id)}`), { headers: this.optionalRoleSessionHeaders(request) });
|
|
453
|
+
}
|
|
454
|
+
async aggregateData(appCode, resourceCode, query, request = {}) {
|
|
455
|
+
return await this.json(this.dataPath(appCode, resourceCode, "aggregate"), {
|
|
456
|
+
method: "POST",
|
|
457
|
+
body: JSON.stringify(query),
|
|
458
|
+
headers: this.optionalRoleSessionHeaders(request),
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
async dataAudit(appCode, resourceCode, id, input = {}, request = {}) {
|
|
462
|
+
const query = new URLSearchParams();
|
|
463
|
+
if (input.limit !== undefined)
|
|
464
|
+
query.set("limit", String(input.limit));
|
|
465
|
+
if (input.offset !== undefined)
|
|
466
|
+
query.set("offset", String(input.offset));
|
|
467
|
+
const suffix = query.size ? `?${query}` : "";
|
|
468
|
+
return await this.json(`${this.dataPath(appCode, resourceCode, `records/${encodeURIComponent(id)}/audit`)}${suffix}`, { headers: this.optionalRoleSessionHeaders(request) });
|
|
469
|
+
}
|
|
470
|
+
async initiateDataFileUpload(appCode, resourceCode, input, request = {}) {
|
|
471
|
+
return await this.json(this.dataPath(appCode, resourceCode, "files/uploads/initiate"), {
|
|
472
|
+
method: "POST",
|
|
473
|
+
body: JSON.stringify(input),
|
|
474
|
+
headers: this.optionalRoleSessionHeaders(request),
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
async completeDataFileUpload(appCode, resourceCode, fileId, request = {}) {
|
|
478
|
+
return await this.json(this.dataPath(appCode, resourceCode, `files/${encodeURIComponent(fileId)}/complete`), {
|
|
479
|
+
method: "POST",
|
|
480
|
+
body: "{}",
|
|
481
|
+
headers: this.optionalRoleSessionHeaders(request),
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
async deleteUnboundDataFile(appCode, resourceCode, fileId, request = {}) {
|
|
485
|
+
return await this.json(this.dataPath(appCode, resourceCode, `files/${encodeURIComponent(fileId)}/delete`), {
|
|
486
|
+
method: "POST",
|
|
487
|
+
body: "{}",
|
|
488
|
+
headers: this.optionalRoleSessionHeaders(request),
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
async dataFileContent(appCode, resourceCode, fileId, request = {}) {
|
|
492
|
+
const path = this.dataPath(appCode, resourceCode, `files/${encodeURIComponent(fileId)}/content`);
|
|
493
|
+
const response = await this.fetch(`${this.baseUrl}${path}`, {
|
|
494
|
+
credentials: this.options.credentials || "same-origin",
|
|
495
|
+
headers: {
|
|
496
|
+
...(this.options.token
|
|
497
|
+
? { Authorization: `Bearer ${this.options.token}` }
|
|
498
|
+
: {}),
|
|
499
|
+
...this.optionalRoleSessionHeaders(request),
|
|
500
|
+
},
|
|
501
|
+
});
|
|
502
|
+
if (!response.ok) {
|
|
503
|
+
let data = null;
|
|
504
|
+
try {
|
|
505
|
+
data = await response.json();
|
|
506
|
+
}
|
|
507
|
+
catch {
|
|
508
|
+
// Binary endpoints may fail before the platform writes a JSON body.
|
|
509
|
+
}
|
|
510
|
+
throw new ControlPlaneError(response.status, data?.errorCode || "DATA_FILE_DOWNLOAD_FAILED", data?.message || `文件下载失败: ${response.status}`, data);
|
|
511
|
+
}
|
|
512
|
+
return response;
|
|
513
|
+
}
|
|
514
|
+
async createData(appCode, resourceCode, data, request = {}) {
|
|
515
|
+
return await this.json(this.dataPath(appCode, resourceCode, "records"), {
|
|
516
|
+
method: "POST",
|
|
517
|
+
body: JSON.stringify({ data }),
|
|
518
|
+
headers: this.optionalRoleSessionHeaders(request),
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
async updateData(appCode, resourceCode, id, input, request = {}) {
|
|
522
|
+
return await this.json(this.dataPath(appCode, resourceCode, `records/${encodeURIComponent(id)}/update`), {
|
|
523
|
+
method: "POST",
|
|
524
|
+
body: JSON.stringify(input),
|
|
525
|
+
headers: this.optionalRoleSessionHeaders(request),
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
async deleteData(appCode, resourceCode, id, expectedRevision, request = {}) {
|
|
529
|
+
return await this.json(this.dataPath(appCode, resourceCode, `records/${encodeURIComponent(id)}/delete`), {
|
|
530
|
+
method: "POST",
|
|
531
|
+
body: JSON.stringify({ expectedRevision }),
|
|
532
|
+
headers: this.optionalRoleSessionHeaders(request),
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
async transactData(appCode, transaction, request = {}) {
|
|
536
|
+
return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/data/transactions`, {
|
|
537
|
+
method: "POST",
|
|
538
|
+
body: JSON.stringify(transaction),
|
|
539
|
+
headers: this.optionalRoleSessionHeaders(request),
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
dataPath(appCode, resourceCode, suffix) {
|
|
543
|
+
return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/data/${encodeURIComponent(resourceCode)}/${suffix}`;
|
|
544
|
+
}
|
|
545
|
+
applicationApiPath(appCode, environmentKey, runtimePath, query = {}) {
|
|
546
|
+
const normalized = String(runtimePath || "")
|
|
547
|
+
.replace(/^\/+/, "")
|
|
548
|
+
.replace(/\/+$/, "");
|
|
549
|
+
if (!normalized ||
|
|
550
|
+
normalized.includes("\0") ||
|
|
551
|
+
normalized
|
|
552
|
+
.split("/")
|
|
553
|
+
.some((part) => !part || part === "." || part === "..")) {
|
|
554
|
+
throw new ControlPlaneError(400, "APPLICATION_API_PATH_INVALID", "应用后端路径不能为空或包含路径穿越片段");
|
|
555
|
+
}
|
|
556
|
+
const params = new URLSearchParams();
|
|
557
|
+
for (const [key, value] of Object.entries(query)) {
|
|
558
|
+
if (value !== undefined && value !== null)
|
|
559
|
+
params.set(key, String(value));
|
|
560
|
+
}
|
|
561
|
+
const suffix = params.size ? `?${params}` : "";
|
|
562
|
+
return `/openxiangda-app-api/v2/${encodeURIComponent(appCode)}/${encodeURIComponent(environmentKey)}/${normalized
|
|
563
|
+
.split("/")
|
|
564
|
+
.map((part) => encodeURIComponent(part))
|
|
565
|
+
.join("/")}${suffix}`;
|
|
566
|
+
}
|
|
567
|
+
async deployExistingVersion(input, action) {
|
|
568
|
+
return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(input.appCode)}/${action}`, {
|
|
569
|
+
method: "POST",
|
|
570
|
+
body: JSON.stringify({
|
|
571
|
+
appVersionId: input.appVersionId,
|
|
572
|
+
environmentId: input.environmentId,
|
|
573
|
+
environmentKind: input.environmentKind,
|
|
574
|
+
idempotencyKey: input.idempotencyKey,
|
|
575
|
+
requestId: input.requestId,
|
|
576
|
+
}),
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
authzPath(appCode) {
|
|
580
|
+
return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/authz`;
|
|
581
|
+
}
|
|
582
|
+
directoryPath(appCode) {
|
|
583
|
+
return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/directory`;
|
|
584
|
+
}
|
|
585
|
+
oauthPath(appCode) {
|
|
586
|
+
return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/oauth2`;
|
|
587
|
+
}
|
|
588
|
+
applicationSecretsPath(appCode, environmentKey) {
|
|
589
|
+
return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/environments/${encodeURIComponent(environmentKey)}/secrets`;
|
|
590
|
+
}
|
|
591
|
+
eventsPath(appCode) {
|
|
592
|
+
return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/events`;
|
|
593
|
+
}
|
|
594
|
+
workflowPath(appCode) {
|
|
595
|
+
return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/workflow`;
|
|
596
|
+
}
|
|
597
|
+
roleSessionHeaders(request) {
|
|
598
|
+
if (!request?.roleSessionId?.trim()) {
|
|
599
|
+
throw new ControlPlaneError(400, "ROLE_SESSION_REQUIRED", "OpenXiangda v2 用户作用域请求必须显式携带 roleSessionId");
|
|
600
|
+
}
|
|
601
|
+
return {
|
|
602
|
+
"X-OpenXiangda-Role-Session-Id": request.roleSessionId,
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
optionalRoleSessionHeaders(request) {
|
|
606
|
+
const roleSessionId = String(request?.roleSessionId || "").trim();
|
|
607
|
+
return roleSessionId
|
|
608
|
+
? { "X-OpenXiangda-Role-Session-Id": roleSessionId }
|
|
609
|
+
: {};
|
|
610
|
+
}
|
|
611
|
+
basicCredentials(clientId, clientSecret) {
|
|
612
|
+
const value = `${clientId}:${clientSecret}`;
|
|
613
|
+
if (typeof Buffer !== "undefined")
|
|
614
|
+
return Buffer.from(value).toString("base64");
|
|
615
|
+
return globalThis.btoa(value);
|
|
616
|
+
}
|
|
617
|
+
async json(path, init = {}) {
|
|
618
|
+
return await this.request(path, {
|
|
619
|
+
...init,
|
|
620
|
+
headers: {
|
|
621
|
+
"Content-Type": "application/json",
|
|
622
|
+
...(init.headers || {}),
|
|
623
|
+
},
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
async request(path, init = {}) {
|
|
627
|
+
const response = await this.fetch(`${this.baseUrl}${path}`, {
|
|
628
|
+
...init,
|
|
629
|
+
credentials: init.credentials || this.options.credentials || "same-origin",
|
|
630
|
+
headers: {
|
|
631
|
+
Accept: "application/json",
|
|
632
|
+
...(this.options.token
|
|
633
|
+
? { Authorization: `Bearer ${this.options.token}` }
|
|
634
|
+
: {}),
|
|
635
|
+
...(init.headers || {}),
|
|
636
|
+
},
|
|
637
|
+
});
|
|
638
|
+
let envelope;
|
|
639
|
+
try {
|
|
640
|
+
envelope = (await response.json());
|
|
641
|
+
}
|
|
642
|
+
catch {
|
|
643
|
+
throw new ControlPlaneError(response.status, "CONTROL_PLANE_RESPONSE_INVALID", "平台返回的不是有效 JSON");
|
|
644
|
+
}
|
|
645
|
+
if (!response.ok || Number(envelope.code) >= 400) {
|
|
646
|
+
throw new ControlPlaneError(response.status, envelope.errorCode || "CONTROL_PLANE_REQUEST_FAILED", envelope.message || `平台请求失败: ${response.status}`, envelope.data);
|
|
647
|
+
}
|
|
648
|
+
return envelope.data;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
//# sourceMappingURL=control-plane-client.js.map
|