openxiangda-devkit-core 2.0.0-alpha.100

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.
Files changed (126) hide show
  1. package/README.md +13 -0
  2. package/dist/app-spec.d.ts +210 -0
  3. package/dist/app-spec.d.ts.map +1 -0
  4. package/dist/app-spec.js +1262 -0
  5. package/dist/app-spec.js.map +1 -0
  6. package/dist/application-services.d.ts +600 -0
  7. package/dist/application-services.d.ts.map +1 -0
  8. package/dist/application-services.js +1603 -0
  9. package/dist/application-services.js.map +1 -0
  10. package/dist/backend-image-build.d.ts +23 -0
  11. package/dist/backend-image-build.d.ts.map +1 -0
  12. package/dist/backend-image-build.js +209 -0
  13. package/dist/backend-image-build.js.map +1 -0
  14. package/dist/command-registry.d.ts +177 -0
  15. package/dist/command-registry.d.ts.map +1 -0
  16. package/dist/command-registry.js +27 -0
  17. package/dist/command-registry.js.map +1 -0
  18. package/dist/compiler/ai-catalog.d.ts +4 -0
  19. package/dist/compiler/ai-catalog.d.ts.map +1 -0
  20. package/dist/compiler/ai-catalog.js +353 -0
  21. package/dist/compiler/ai-catalog.js.map +1 -0
  22. package/dist/compiler/authorization-transition.d.ts +7 -0
  23. package/dist/compiler/authorization-transition.d.ts.map +1 -0
  24. package/dist/compiler/authorization-transition.js +27 -0
  25. package/dist/compiler/authorization-transition.js.map +1 -0
  26. package/dist/compiler/bundle.d.ts +34 -0
  27. package/dist/compiler/bundle.d.ts.map +1 -0
  28. package/dist/compiler/bundle.js +1865 -0
  29. package/dist/compiler/bundle.js.map +1 -0
  30. package/dist/compiler/config.d.ts +857 -0
  31. package/dist/compiler/config.d.ts.map +1 -0
  32. package/dist/compiler/config.js +3848 -0
  33. package/dist/compiler/config.js.map +1 -0
  34. package/dist/compiler/data-policy-expression.d.ts +12 -0
  35. package/dist/compiler/data-policy-expression.d.ts.map +1 -0
  36. package/dist/compiler/data-policy-expression.js +43 -0
  37. package/dist/compiler/data-policy-expression.js.map +1 -0
  38. package/dist/compiler/field-codec.d.ts +14 -0
  39. package/dist/compiler/field-codec.d.ts.map +1 -0
  40. package/dist/compiler/field-codec.js +131 -0
  41. package/dist/compiler/field-codec.js.map +1 -0
  42. package/dist/compiler/field-physical-plan.d.ts +10 -0
  43. package/dist/compiler/field-physical-plan.d.ts.map +1 -0
  44. package/dist/compiler/field-physical-plan.js +66 -0
  45. package/dist/compiler/field-physical-plan.js.map +1 -0
  46. package/dist/compiler/field-policy-path.d.ts +10 -0
  47. package/dist/compiler/field-policy-path.d.ts.map +1 -0
  48. package/dist/compiler/field-policy-path.js +283 -0
  49. package/dist/compiler/field-policy-path.js.map +1 -0
  50. package/dist/compiler/field-query-plan.d.ts +13 -0
  51. package/dist/compiler/field-query-plan.d.ts.map +1 -0
  52. package/dist/compiler/field-query-plan.js +122 -0
  53. package/dist/compiler/field-query-plan.js.map +1 -0
  54. package/dist/compiler/field-surface.d.ts +15 -0
  55. package/dist/compiler/field-surface.d.ts.map +1 -0
  56. package/dist/compiler/field-surface.js +51 -0
  57. package/dist/compiler/field-surface.js.map +1 -0
  58. package/dist/compiler/index.d.ts +6 -0
  59. package/dist/compiler/index.d.ts.map +1 -0
  60. package/dist/compiler/index.js +6 -0
  61. package/dist/compiler/index.js.map +1 -0
  62. package/dist/compiler/package-compiler.d.ts +22 -0
  63. package/dist/compiler/package-compiler.d.ts.map +1 -0
  64. package/dist/compiler/package-compiler.js +246 -0
  65. package/dist/compiler/package-compiler.js.map +1 -0
  66. package/dist/compiler/schema-composition.d.ts +21 -0
  67. package/dist/compiler/schema-composition.d.ts.map +1 -0
  68. package/dist/compiler/schema-composition.js +193 -0
  69. package/dist/compiler/schema-composition.js.map +1 -0
  70. package/dist/config.d.ts +2 -0
  71. package/dist/config.d.ts.map +1 -0
  72. package/dist/config.js +2 -0
  73. package/dist/config.js.map +1 -0
  74. package/dist/connected-development.d.ts +62 -0
  75. package/dist/connected-development.d.ts.map +1 -0
  76. package/dist/connected-development.js +393 -0
  77. package/dist/connected-development.js.map +1 -0
  78. package/dist/control-plane-client.d.ts +684 -0
  79. package/dist/control-plane-client.d.ts.map +1 -0
  80. package/dist/control-plane-client.js +860 -0
  81. package/dist/control-plane-client.js.map +1 -0
  82. package/dist/deployment.d.ts +67 -0
  83. package/dist/deployment.d.ts.map +1 -0
  84. package/dist/deployment.js +311 -0
  85. package/dist/deployment.js.map +1 -0
  86. package/dist/index.d.ts +17 -0
  87. package/dist/index.d.ts.map +1 -0
  88. package/dist/index.js +17 -0
  89. package/dist/index.js.map +1 -0
  90. package/dist/internal/workflow.d.ts +83 -0
  91. package/dist/internal/workflow.d.ts.map +1 -0
  92. package/dist/internal/workflow.js +419 -0
  93. package/dist/internal/workflow.js.map +1 -0
  94. package/dist/nest-injection-contract.d.ts +4 -0
  95. package/dist/nest-injection-contract.d.ts.map +1 -0
  96. package/dist/nest-injection-contract.js +116 -0
  97. package/dist/nest-injection-contract.js.map +1 -0
  98. package/dist/package-compiler.d.ts +2 -0
  99. package/dist/package-compiler.d.ts.map +1 -0
  100. package/dist/package-compiler.js +2 -0
  101. package/dist/package-compiler.js.map +1 -0
  102. package/dist/session.d.ts +143 -0
  103. package/dist/session.d.ts.map +1 -0
  104. package/dist/session.js +351 -0
  105. package/dist/session.js.map +1 -0
  106. package/dist/testing.d.ts +77 -0
  107. package/dist/testing.d.ts.map +1 -0
  108. package/dist/testing.js +213 -0
  109. package/dist/testing.js.map +1 -0
  110. package/dist/toolchain-capsule.d.ts +45 -0
  111. package/dist/toolchain-capsule.d.ts.map +1 -0
  112. package/dist/toolchain-capsule.js +165 -0
  113. package/dist/toolchain-capsule.js.map +1 -0
  114. package/dist/version.d.ts +3 -0
  115. package/dist/version.d.ts.map +1 -0
  116. package/dist/version.js +11 -0
  117. package/dist/version.js.map +1 -0
  118. package/dist/workspace-loader.d.ts +17 -0
  119. package/dist/workspace-loader.d.ts.map +1 -0
  120. package/dist/workspace-loader.js +140 -0
  121. package/dist/workspace-loader.js.map +1 -0
  122. package/dist/workspace.d.ts +15 -0
  123. package/dist/workspace.d.ts.map +1 -0
  124. package/dist/workspace.js +69 -0
  125. package/dist/workspace.js.map +1 -0
  126. package/package.json +44 -0
@@ -0,0 +1,860 @@
1
+ import { CONFIGURATION_COMPATIBILITY_CAPABILITY, CURRENT_APPLICATION_CONTRACT, OPENXIANGDA_CONTRACT_VERSION, } from "openxiangda-contracts/browser";
2
+ export class ControlPlaneError extends Error {
3
+ status;
4
+ code;
5
+ data;
6
+ remote;
7
+ constructor(status, code, message, data, remote) {
8
+ super(message);
9
+ this.status = status;
10
+ this.code = code;
11
+ this.data = data;
12
+ this.remote = remote;
13
+ this.name = "ControlPlaneError";
14
+ }
15
+ }
16
+ export class OpenXiangdaControlPlaneClient {
17
+ options;
18
+ baseUrl;
19
+ fetch;
20
+ constructor(options) {
21
+ this.options = options;
22
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
23
+ this.fetch = options.fetch || globalThis.fetch.bind(globalThis);
24
+ }
25
+ async capabilities() {
26
+ const capabilities = await this.json("/openxiangda-api/v2/capabilities");
27
+ if (capabilities.contractVersion !== OPENXIANGDA_CONTRACT_VERSION) {
28
+ const compatibility = capabilities.configurationCompatibility;
29
+ throw new ControlPlaneError(409, "OPENXIANGDA_CONTRACT_VERSION_MISMATCH", `平台 contract ${capabilities.contractVersion} 与工具 ${OPENXIANGDA_CONTRACT_VERSION} 不兼容`, {
30
+ pointer: "/contractVersion",
31
+ clientContractVersion: OPENXIANGDA_CONTRACT_VERSION,
32
+ clientSchemaVersions: {
33
+ appPackage: CURRENT_APPLICATION_CONTRACT.appPackageSchemaVersion,
34
+ configuration: CURRENT_APPLICATION_CONTRACT.configurationBundleSchemaVersion,
35
+ contract: CURRENT_APPLICATION_CONTRACT.contractBundleSchemaVersion,
36
+ compiler: CURRENT_APPLICATION_CONTRACT.compilerContractVersion,
37
+ },
38
+ platformVersion: String(capabilities.platformVersion || "unknown"),
39
+ platformCapability: compatibility?.capability || {
40
+ code: CONFIGURATION_COMPATIBILITY_CAPABILITY,
41
+ version: "unknown",
42
+ status: "unavailable",
43
+ },
44
+ required: { ...CURRENT_APPLICATION_CONTRACT },
45
+ supported: (compatibility?.supportedApplicationContracts || []).slice(0, 8),
46
+ requiredContractVersion: OPENXIANGDA_CONTRACT_VERSION,
47
+ supportedContractVersion: capabilities.contractVersion,
48
+ });
49
+ }
50
+ return capabilities;
51
+ }
52
+ async validateConfigurationCompatibility(appCode, capabilities, input) {
53
+ const compatibility = capabilities.configurationCompatibility;
54
+ const expectedTemplate = "/openxiangda-api/v2/applications/{appCode}/configuration-compatibility";
55
+ if (compatibility.endpointTemplate !== expectedTemplate) {
56
+ throw new ControlPlaneError(409, "OPENXIANGDA_CONFIGURATION_COMPATIBILITY_ENDPOINT_INVALID", "平台配置兼容性端点不符合当前公开契约", {
57
+ pointer: "/configurationCompatibility/endpointTemplate",
58
+ clientContractVersion: OPENXIANGDA_CONTRACT_VERSION,
59
+ clientSchemaVersions: {
60
+ appPackage: input.required.appPackageSchemaVersion,
61
+ configuration: input.required.configurationBundleSchemaVersion,
62
+ contract: input.required.contractBundleSchemaVersion,
63
+ compiler: input.required.compilerContractVersion,
64
+ },
65
+ platformVersion: capabilities.platformVersion,
66
+ platformCapability: compatibility.capability,
67
+ required: { ...input.required },
68
+ supported: compatibility.supportedApplicationContracts.slice(0, 8),
69
+ requiredEndpointTemplate: expectedTemplate,
70
+ supportedEndpointTemplate: compatibility.endpointTemplate,
71
+ });
72
+ }
73
+ const body = JSON.stringify(input);
74
+ const requestBytes = new TextEncoder().encode(body).byteLength;
75
+ if (requestBytes > compatibility.limits.requestBytes) {
76
+ throw new ControlPlaneError(409, "OPENXIANGDA_CONFIGURATION_VALIDATION_REQUEST_TOO_LARGE", "配置兼容性请求超过目标平台公开上限", {
77
+ pointer: "/",
78
+ clientContractVersion: OPENXIANGDA_CONTRACT_VERSION,
79
+ clientSchemaVersions: {
80
+ appPackage: input.required.appPackageSchemaVersion,
81
+ configuration: input.required.configurationBundleSchemaVersion,
82
+ contract: input.required.contractBundleSchemaVersion,
83
+ compiler: input.required.compilerContractVersion,
84
+ },
85
+ platformVersion: capabilities.platformVersion,
86
+ platformCapability: compatibility.capability,
87
+ required: { ...input.required },
88
+ supported: compatibility.supportedApplicationContracts.slice(0, 8),
89
+ requiredRequestBytes: requestBytes,
90
+ supportedRequestBytes: compatibility.limits.requestBytes,
91
+ });
92
+ }
93
+ return await this.json(compatibility.endpointTemplate.replace("{appCode}", encodeURIComponent(appCode)), { method: "POST", body });
94
+ }
95
+ async provisionApplication(input) {
96
+ return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(input.appCode)}/provision`, {
97
+ method: "POST",
98
+ body: JSON.stringify({
99
+ name: input.name,
100
+ description: input.description,
101
+ }),
102
+ });
103
+ }
104
+ async createConnectedDevelopmentSession(appCode, input) {
105
+ return await this.json(this.connectedDevelopmentSessionsPath(appCode), { method: "POST", body: JSON.stringify(input) });
106
+ }
107
+ async preparePreproductionAcceptanceIdentities(appCode, input) {
108
+ return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/acceptance/identities/prepare`, { method: "POST", body: JSON.stringify(input) });
109
+ }
110
+ async connectedDevelopmentSession(appCode, sessionToken) {
111
+ return await this.json(`${this.connectedDevelopmentSessionsPath(appCode)}/current`, { headers: this.connectedDevelopmentSessionHeaders(sessionToken) });
112
+ }
113
+ async refreshConnectedDevelopmentSession(appCode, sessionToken) {
114
+ return await this.json(`${this.connectedDevelopmentSessionsPath(appCode)}/current/refresh`, {
115
+ method: "POST",
116
+ body: "{}",
117
+ headers: this.connectedDevelopmentSessionHeaders(sessionToken),
118
+ });
119
+ }
120
+ async revokeConnectedDevelopmentSession(appCode, sessionToken) {
121
+ await this.json(`${this.connectedDevelopmentSessionsPath(appCode)}/current/revoke`, {
122
+ method: "POST",
123
+ body: "{}",
124
+ headers: this.connectedDevelopmentSessionHeaders(sessionToken),
125
+ });
126
+ }
127
+ async oauthClients(appCode) {
128
+ return await this.json(`${this.oauthPath(appCode)}/clients`);
129
+ }
130
+ async runtimeOAuthCredentialStatus(appCode, environmentKey) {
131
+ return await this.json(`${this.oauthPath(appCode)}/runtime-credentials/${encodeURIComponent(environmentKey)}`);
132
+ }
133
+ async stageRuntimeOAuthCredentialRotation(appCode, environmentKey, input) {
134
+ return await this.json(`${this.oauthPath(appCode)}/runtime-credentials/${encodeURIComponent(environmentKey)}/rotate`, { method: "POST", body: JSON.stringify(input) });
135
+ }
136
+ async createOAuthClient(appCode, input) {
137
+ return await this.json(`${this.oauthPath(appCode)}/clients`, {
138
+ method: "POST",
139
+ body: JSON.stringify(input),
140
+ });
141
+ }
142
+ async updateOAuthClient(appCode, clientId, input) {
143
+ return await this.json(`${this.oauthPath(appCode)}/clients/${encodeURIComponent(clientId)}/update`, { method: "POST", body: JSON.stringify(input) });
144
+ }
145
+ async rotateOAuthClientSecret(appCode, clientId, gracePeriodSeconds = 600) {
146
+ return await this.json(`${this.oauthPath(appCode)}/clients/${encodeURIComponent(clientId)}/rotate-secret`, {
147
+ method: "POST",
148
+ body: JSON.stringify({ gracePeriodSeconds }),
149
+ });
150
+ }
151
+ async revokeOAuthClient(appCode, clientId) {
152
+ return await this.json(`${this.oauthPath(appCode)}/clients/${encodeURIComponent(clientId)}/revoke`, { method: "POST", body: "{}" });
153
+ }
154
+ async oauthAuditEvents(appCode, input = {}) {
155
+ const query = new URLSearchParams();
156
+ if (input.environmentKey)
157
+ query.set("environmentKey", input.environmentKey);
158
+ if (input.clientId)
159
+ query.set("clientId", input.clientId);
160
+ if (input.limit)
161
+ query.set("limit", String(input.limit));
162
+ const suffix = query.size ? `?${query}` : "";
163
+ return await this.json(`${this.oauthPath(appCode)}/audit-events${suffix}`);
164
+ }
165
+ async oauthApplicationPrincipal(appCode) {
166
+ return await this.json(`${this.oauthPath(appCode)}/principal`);
167
+ }
168
+ async exchangeOAuthClientCredentials(input) {
169
+ const response = await this.fetch(`${this.baseUrl}/openxiangda-api/v2/oauth2/token`, {
170
+ method: "POST",
171
+ headers: {
172
+ Accept: "application/json",
173
+ "Content-Type": "application/json",
174
+ Authorization: `Basic ${this.basicCredentials(input.clientId, input.clientSecret)}`,
175
+ },
176
+ body: JSON.stringify({
177
+ grant_type: "client_credentials",
178
+ ...(input.scope?.length ? { scope: input.scope.join(" ") } : {}),
179
+ }),
180
+ });
181
+ let data;
182
+ try {
183
+ data = (await response.json());
184
+ }
185
+ catch {
186
+ throw new ControlPlaneError(response.status, "OAUTH2_TOKEN_RESPONSE_INVALID", "OAuth2 token 端点返回的不是有效 JSON");
187
+ }
188
+ if (!response.ok || data.error) {
189
+ throw new ControlPlaneError(response.status, data.error || "OAUTH2_TOKEN_REQUEST_FAILED", data.error_description || `OAuth2 token 请求失败: ${response.status}`);
190
+ }
191
+ return data;
192
+ }
193
+ async applicationApiResponse(appCode, environmentKey, runtimePath, input) {
194
+ const path = this.applicationApiPath(appCode, environmentKey, runtimePath, input.query);
195
+ const { query: _query, perspectiveCode: _perspectiveCode, ...init } = input;
196
+ const token = await this.accessToken(false);
197
+ return await this.fetch(`${this.baseUrl}${path}`, {
198
+ ...init,
199
+ credentials: init.credentials || this.options.credentials || "include",
200
+ headers: {
201
+ Accept: "application/json",
202
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
203
+ ...this.perspectiveHeaders(input),
204
+ ...(init.headers || {}),
205
+ },
206
+ });
207
+ }
208
+ async applicationApiJson(appCode, environmentKey, runtimePath, input) {
209
+ const { body, ...request } = input;
210
+ const response = await this.applicationApiResponse(appCode, environmentKey, runtimePath, {
211
+ ...request,
212
+ headers: {
213
+ ...(body === undefined ? {} : { "Content-Type": "application/json" }),
214
+ ...(input.headers || {}),
215
+ },
216
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
217
+ });
218
+ const text = await response.text();
219
+ let data = undefined;
220
+ if (text) {
221
+ try {
222
+ data = JSON.parse(text);
223
+ }
224
+ catch {
225
+ throw new ControlPlaneError(response.status, "APPLICATION_API_RESPONSE_INVALID", "应用后端返回的不是有效 JSON", text);
226
+ }
227
+ }
228
+ if (!response.ok) {
229
+ throw new ControlPlaneError(response.status, String(data?.errorCode || data?.code || "APPLICATION_API_REQUEST_FAILED"), String(data?.message || data?.error || `应用后端请求失败: ${response.status}`), data);
230
+ }
231
+ return data;
232
+ }
233
+ async uploadArtifact(input) {
234
+ const params = new URLSearchParams({
235
+ kind: input.kind,
236
+ contentType: input.contentType,
237
+ metadata: JSON.stringify(input.metadata || {}),
238
+ });
239
+ const form = new FormData();
240
+ form.append("file", new Blob([input.content], { type: input.contentType }), input.digest);
241
+ return await this.request(`/openxiangda-api/v2/applications/${encodeURIComponent(input.appCode)}/artifacts/${input.digest}?${params}`, { method: "POST", body: form });
242
+ }
243
+ async createDeployment(input) {
244
+ return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(input.appCode)}/deployments`, {
245
+ method: "POST",
246
+ body: JSON.stringify({
247
+ environmentId: input.environmentId,
248
+ environmentKind: input.environmentKind,
249
+ kind: input.kind || "deploy",
250
+ packageDigest: input.packageDigest,
251
+ package: input.package,
252
+ idempotencyKey: input.idempotencyKey,
253
+ requestId: input.requestId,
254
+ }),
255
+ });
256
+ }
257
+ async deployment(appCode, deploymentId) {
258
+ return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/deployments/${encodeURIComponent(deploymentId)}`);
259
+ }
260
+ async deployments(appCode, limit = 20) {
261
+ return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/deployments?limit=${Math.min(Math.max(Number(limit) || 20, 1), 100)}`);
262
+ }
263
+ async applicationEnvironments(appCode) {
264
+ return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/environments`);
265
+ }
266
+ async startEnvironment(appCode, environmentKey, input) {
267
+ return await this.changeEnvironmentRuntimeState(appCode, environmentKey, "start", input);
268
+ }
269
+ async stopEnvironment(appCode, environmentKey, input) {
270
+ return await this.changeEnvironmentRuntimeState(appCode, environmentKey, "stop", input);
271
+ }
272
+ async retryDeployment(appCode, deploymentId) {
273
+ return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/deployments/${encodeURIComponent(deploymentId)}/retry`, { method: "POST", body: "{}" });
274
+ }
275
+ async cancelDeployment(appCode, deploymentId) {
276
+ return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/deployments/${encodeURIComponent(deploymentId)}/cancel`, { method: "POST", body: "{}" });
277
+ }
278
+ async promote(input) {
279
+ return await this.deployExistingVersion(input, "promotions");
280
+ }
281
+ async redeploy(input) {
282
+ return await this.deployExistingVersion(input, "redeployments");
283
+ }
284
+ async rollback(input) {
285
+ return await this.deployExistingVersion(input, "rollbacks");
286
+ }
287
+ async appVersions(appCode, limit = 30) {
288
+ return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/versions?limit=${Math.min(Math.max(Number(limit) || 30, 1), 100)}`);
289
+ }
290
+ async environmentHead(appCode, environmentKey) {
291
+ return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/environment-heads/${encodeURIComponent(environmentKey)}`);
292
+ }
293
+ async changeEnvironmentRuntimeState(appCode, environmentKey, action, input) {
294
+ return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/environments/${encodeURIComponent(environmentKey)}/${action}`, {
295
+ method: "POST",
296
+ body: JSON.stringify(input),
297
+ });
298
+ }
299
+ async applicationSecrets(appCode, environmentKey) {
300
+ return await this.json(this.applicationSecretsPath(appCode, environmentKey));
301
+ }
302
+ async applicationSecret(appCode, environmentKey, name) {
303
+ return await this.json(`${this.applicationSecretsPath(appCode, environmentKey)}/${encodeURIComponent(name)}`);
304
+ }
305
+ async createApplicationSecret(appCode, environmentKey, input) {
306
+ return await this.json(this.applicationSecretsPath(appCode, environmentKey), { method: "POST", body: JSON.stringify({ expectedRevision: 0, ...input }) });
307
+ }
308
+ async updateApplicationSecret(appCode, environmentKey, name, input) {
309
+ return await this.json(`${this.applicationSecretsPath(appCode, environmentKey)}/${encodeURIComponent(name)}`, { method: "PATCH", body: JSON.stringify(input) });
310
+ }
311
+ async rotateApplicationSecret(appCode, environmentKey, name, input) {
312
+ return await this.json(`${this.applicationSecretsPath(appCode, environmentKey)}/${encodeURIComponent(name)}/rotate`, { method: "POST", body: JSON.stringify(input) });
313
+ }
314
+ async deleteApplicationSecret(appCode, environmentKey, name, input) {
315
+ return await this.json(`${this.applicationSecretsPath(appCode, environmentKey)}/${encodeURIComponent(name)}`, { method: "DELETE", body: JSON.stringify(input) });
316
+ }
317
+ async applicationSecretAuditEvents(appCode, environmentKey, name, limit = 100) {
318
+ return await this.json(`${this.applicationSecretsPath(appCode, environmentKey)}/${encodeURIComponent(name)}/audit?limit=${Math.min(Math.max(Number(limit) || 100, 1), 500)}`);
319
+ }
320
+ async eventSubscriptions(appCode) {
321
+ return await this.json(`${this.eventsPath(appCode)}/subscriptions`);
322
+ }
323
+ async setEventSubscriptionStatus(appCode, subscriptionId, input) {
324
+ return await this.json(`${this.eventsPath(appCode)}/subscriptions/${encodeURIComponent(subscriptionId)}/status`, { method: "POST", body: JSON.stringify(input) });
325
+ }
326
+ async eventDeliveries(appCode, limit = 100) {
327
+ return await this.json(`${this.eventsPath(appCode)}/deliveries?limit=${Math.min(Math.max(Number(limit) || 100, 1), 500)}`);
328
+ }
329
+ async replayEventDelivery(appCode, deliveryId, idempotencyKey) {
330
+ return await this.json(`${this.eventsPath(appCode)}/deliveries/${encodeURIComponent(deliveryId)}/replay`, {
331
+ method: "POST",
332
+ body: JSON.stringify(idempotencyKey ? { idempotencyKey } : {}),
333
+ });
334
+ }
335
+ async timerSubscriptions(appCode) {
336
+ return await this.json(`${this.eventsPath(appCode)}/timers`);
337
+ }
338
+ async setTimerSubscriptionStatus(appCode, timerId, input) {
339
+ return await this.json(`${this.eventsPath(appCode)}/timers/${encodeURIComponent(timerId)}/status`, { method: "POST", body: JSON.stringify(input) });
340
+ }
341
+ async dateTriggers(appCode) {
342
+ return await this.json(`${this.eventsPath(appCode)}/date-triggers`);
343
+ }
344
+ async setDateTriggerStatus(appCode, triggerId, input) {
345
+ return await this.json(`${this.eventsPath(appCode)}/date-triggers/${encodeURIComponent(triggerId)}/status`, { method: "POST", body: JSON.stringify(input) });
346
+ }
347
+ async registerWorkflowDefinition(appCode, version, definition) {
348
+ return await this.json(`${this.workflowPath(appCode)}/definitions`, {
349
+ method: "POST",
350
+ body: JSON.stringify({ version, definition }),
351
+ });
352
+ }
353
+ async registerWorkflowBinding(appCode, version, binding) {
354
+ return await this.json(`${this.workflowPath(appCode)}/bindings`, {
355
+ method: "POST",
356
+ body: JSON.stringify({ version, binding }),
357
+ });
358
+ }
359
+ async applyWorkflowAssigneeProvider(appCode, input) {
360
+ return await this.json(`${this.workflowPath(appCode)}/assignee-providers`, { method: "POST", body: JSON.stringify(input) });
361
+ }
362
+ async workflowAssigneeProviders(appCode, environmentKey = "production") {
363
+ return await this.json(`${this.workflowPath(appCode)}/assignee-providers?environmentKey=${encodeURIComponent(environmentKey)}`);
364
+ }
365
+ async rotateWorkflowAssigneeProviderSecret(appCode, providerCode, input) {
366
+ return await this.json(`${this.workflowPath(appCode)}/assignee-providers/${encodeURIComponent(providerCode)}/rotate-secret`, { method: "POST", body: JSON.stringify(input) });
367
+ }
368
+ async activateWorkflow(appCode, workflowCode, input) {
369
+ return await this.json(`${this.workflowPath(appCode)}/definitions/${encodeURIComponent(workflowCode)}/activate`, { method: "POST", body: JSON.stringify(input) });
370
+ }
371
+ async createWorkflowDelegation(appCode, input) {
372
+ return await this.json(`${this.workflowPath(appCode)}/delegations`, {
373
+ method: "POST",
374
+ body: JSON.stringify(input),
375
+ });
376
+ }
377
+ async workflowDelegationTargets(appCode, userId, environmentKey) {
378
+ return await this.json(`${this.workflowPath(appCode)}/delegations/targets?userId=${encodeURIComponent(userId)}${environmentKey ? `&environmentKey=${encodeURIComponent(environmentKey)}` : ""}`);
379
+ }
380
+ async workflowDelegations(appCode, input = {}) {
381
+ return await this.json(`${this.workflowPath(appCode)}/delegations?all=${input.all === true}${input.environmentKey
382
+ ? `&environmentKey=${encodeURIComponent(input.environmentKey)}`
383
+ : ""}`);
384
+ }
385
+ async revokeWorkflowDelegation(appCode, delegationId, environmentKey) {
386
+ return await this.json(`${this.workflowPath(appCode)}/delegations/${encodeURIComponent(delegationId)}/revoke${environmentKey
387
+ ? `?environmentKey=${encodeURIComponent(environmentKey)}`
388
+ : ""}`, {
389
+ method: "POST",
390
+ });
391
+ }
392
+ async workflowTaskSurface(appCode, taskId, csrfToken) {
393
+ return await this.json(`${this.workflowPath(appCode)}/tasks/${encodeURIComponent(taskId)}/surface`, { headers: { "X-OpenXiangda-CSRF-Token": csrfToken } });
394
+ }
395
+ async workflowInstanceSurface(appCode, instanceId, csrfToken) {
396
+ return await this.json(`${this.workflowPath(appCode)}/instances/${encodeURIComponent(instanceId)}/surface`, { headers: { "X-OpenXiangda-CSRF-Token": csrfToken } });
397
+ }
398
+ async executeWorkflowTaskCommand(appCode, taskId, command, input) {
399
+ const { csrfToken, ...request } = input;
400
+ return await this.json(`${this.workflowPath(appCode)}/tasks/${encodeURIComponent(taskId)}/commands/${encodeURIComponent(command)}`, {
401
+ method: "POST",
402
+ headers: { "X-OpenXiangda-CSRF-Token": csrfToken },
403
+ body: JSON.stringify(request),
404
+ });
405
+ }
406
+ async executeWorkflowInstanceCommand(appCode, instanceId, command, input) {
407
+ const { csrfToken, ...request } = input;
408
+ return await this.json(`${this.workflowPath(appCode)}/instances/${encodeURIComponent(instanceId)}/commands/${command}`, {
409
+ method: "POST",
410
+ headers: { "X-OpenXiangda-CSRF-Token": csrfToken },
411
+ body: JSON.stringify(request),
412
+ });
413
+ }
414
+ async workflowAssignmentExplain(appCode, taskId) {
415
+ return await this.json(`${this.workflowPath(appCode)}/tasks/${encodeURIComponent(taskId)}/assignment-explain`);
416
+ }
417
+ async workflowTimeline(appCode, instanceId) {
418
+ return await this.json(`${this.workflowPath(appCode)}/instances/${encodeURIComponent(instanceId)}/timeline`);
419
+ }
420
+ async workflowWorkCenter(appCode, input = {}) {
421
+ const query = new URLSearchParams({
422
+ status: input.status || "pending",
423
+ limit: String(Math.min(Math.max(Number(input.limit) || 50, 1), 200)),
424
+ ...(input.environmentKey ? { environmentKey: input.environmentKey } : {}),
425
+ });
426
+ return await this.json(`${this.workflowPath(appCode)}/work-center/items?${query}`);
427
+ }
428
+ async workflowDiagnostics(appCode, limit = 100) {
429
+ return await this.json(`${this.workflowPath(appCode)}/diagnostics?limit=${Math.min(Math.max(Number(limit) || 100, 1), 500)}`);
430
+ }
431
+ async nativeAuthorizationCatalog(appCode, input = {}) {
432
+ const query = this.query({
433
+ environmentKey: input.environmentKey || "preproduction",
434
+ });
435
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/catalog?${query}`);
436
+ }
437
+ async nativeScopeValues(appCode, input) {
438
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/scope-values?${this.query({
439
+ ...input,
440
+ environmentKey: input.environmentKey || "preproduction",
441
+ })}`);
442
+ }
443
+ async nativeRoleMemberships(appCode, input = {}) {
444
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/memberships?${this.query({
445
+ ...input,
446
+ environmentKey: input.environmentKey || "preproduction",
447
+ })}`);
448
+ }
449
+ async createNativeRoleMembership(appCode, input) {
450
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/memberships`, {
451
+ method: "POST",
452
+ body: JSON.stringify(input),
453
+ });
454
+ }
455
+ async updateNativeRoleMembership(appCode, membershipId, input) {
456
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/memberships/${encodeURIComponent(membershipId)}/update`, {
457
+ method: "POST",
458
+ body: JSON.stringify(input),
459
+ });
460
+ }
461
+ async revokeNativeRoleMembership(appCode, membershipId, input) {
462
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/memberships/${encodeURIComponent(membershipId)}/revoke`, {
463
+ method: "POST",
464
+ body: JSON.stringify(input),
465
+ });
466
+ }
467
+ async nativeSuperAdmins(appCode, input = {}) {
468
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/super-admins?${this.query({ ...input, environmentKey: input.environmentKey || "preproduction" })}`);
469
+ }
470
+ async grantNativeSuperAdmin(appCode, input) {
471
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/super-admins`, {
472
+ method: "POST",
473
+ body: JSON.stringify(input),
474
+ });
475
+ }
476
+ async revokeNativeSuperAdmin(appCode, grantId, input) {
477
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/super-admins/${encodeURIComponent(grantId)}/revoke`, {
478
+ method: "POST",
479
+ body: JSON.stringify(input),
480
+ });
481
+ }
482
+ async nativeRelationshipGrants(appCode, input = {}) {
483
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/relationship-grants?${this.query({
484
+ ...input,
485
+ environmentKey: input.environmentKey || "preproduction",
486
+ })}`);
487
+ }
488
+ async createNativeRelationshipGrant(appCode, input) {
489
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/relationship-grants`, {
490
+ method: "POST",
491
+ body: JSON.stringify(input),
492
+ });
493
+ }
494
+ async updateNativeRelationshipGrant(appCode, grantId, input) {
495
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/relationship-grants/${encodeURIComponent(grantId)}/update`, {
496
+ method: "POST",
497
+ body: JSON.stringify(input),
498
+ });
499
+ }
500
+ async revokeNativeRelationshipGrant(appCode, grantId, input) {
501
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/relationship-grants/${encodeURIComponent(grantId)}/revoke`, {
502
+ method: "POST",
503
+ body: JSON.stringify(input),
504
+ });
505
+ }
506
+ async nativeAuthorizationProjectionHealth(appCode, environmentKey = "preproduction") {
507
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/projections/health?${this.query({ environmentKey })}`);
508
+ }
509
+ async rebuildNativeAuthorizationProjections(appCode, input) {
510
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/projections/rebuild`, { method: "POST", body: JSON.stringify(input) });
511
+ }
512
+ async recoverNativeAuthorizationProjection(appCode, jobId, input) {
513
+ return await this.json(`${this.nativeAuthzManagementPath(appCode)}/projections/jobs/${encodeURIComponent(jobId)}/recover`, { method: "POST", body: JSON.stringify(input) });
514
+ }
515
+ async myRoleAssignments(appCode) {
516
+ return await this.json(`${this.authzPath(appCode)}/assignments/me`);
517
+ }
518
+ async applicationRoles(appCode) {
519
+ return await this.json(`${this.authzPath(appCode)}/roles`);
520
+ }
521
+ async createRoleAssignment(appCode, input) {
522
+ return await this.json(`${this.authzPath(appCode)}/assignments`, { method: "POST", body: JSON.stringify(input) });
523
+ }
524
+ async updateRoleAssignment(appCode, assignmentId, input) {
525
+ return await this.json(`${this.authzPath(appCode)}/assignments/${encodeURIComponent(assignmentId)}/update`, { method: "POST", body: JSON.stringify(input) });
526
+ }
527
+ async revokeRoleAssignment(appCode, assignmentId, expectedRevision) {
528
+ return await this.json(`${this.authzPath(appCode)}/assignments/${encodeURIComponent(assignmentId)}/revoke`, { method: "POST", body: JSON.stringify({ expectedRevision }) });
529
+ }
530
+ async searchDirectoryEntries(appCode, kind, input) {
531
+ const query = new URLSearchParams({
532
+ keyword: input.keyword.trim(),
533
+ limit: String(Math.min(Math.max(Number(input.limit) || 20, 1), 50)),
534
+ });
535
+ return await this.json(`${this.directoryPath(appCode)}/${kind}s?${query}`);
536
+ }
537
+ async browseDepartmentEntries(appCode, input) {
538
+ const query = new URLSearchParams({
539
+ offset: String(Math.max(Number(input.offset) || 0, 0)),
540
+ limit: String(Math.min(Math.max(Number(input.limit) || 100, 1), 100)),
541
+ });
542
+ const parentId = String(input.parentId || "").trim();
543
+ if (parentId)
544
+ query.set("parentId", parentId);
545
+ return await this.json(`${this.directoryPath(appCode)}/departments/tree?${query}`);
546
+ }
547
+ async browseDepartmentUserEntries(appCode, input) {
548
+ const departmentId = String(input.departmentId || "").trim();
549
+ if (!departmentId)
550
+ throw new Error("departmentId is required");
551
+ const query = new URLSearchParams({
552
+ page: String(Math.max(Number(input.page) || 1, 1)),
553
+ limit: String(Math.min(Math.max(Number(input.limit) || 50, 1), 100)),
554
+ });
555
+ return await this.json(`${this.directoryPath(appCode)}/departments/${encodeURIComponent(departmentId)}/users?${query}`);
556
+ }
557
+ async resolveDirectoryEntries(appCode, input) {
558
+ return await this.json(`${this.directoryPath(appCode)}/resolve`, {
559
+ method: "POST",
560
+ body: JSON.stringify(input),
561
+ });
562
+ }
563
+ async applyDataResource(resource, input = {}) {
564
+ return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(resource.appCode)}/data-resources/${encodeURIComponent(resource.code)}/apply`, {
565
+ method: "POST",
566
+ body: JSON.stringify({
567
+ name: resource.name,
568
+ schema: resource.schema,
569
+ capabilities: resource.capabilities,
570
+ dataPolicyCode: resource.dataPolicyCode,
571
+ fieldPolicies: resource.fieldPolicies,
572
+ expectedRevision: input.expectedRevision,
573
+ }),
574
+ });
575
+ }
576
+ async relationshipGrants(appCode, filter = {}) {
577
+ const query = new URLSearchParams();
578
+ for (const [key, value] of Object.entries(filter)) {
579
+ if (value !== undefined && value !== "")
580
+ query.set(key, value);
581
+ }
582
+ const suffix = query.size ? `?${query.toString()}` : "";
583
+ return await this.json(`${this.authzPath(appCode)}/relationship-grants${suffix}`);
584
+ }
585
+ async createRelationshipGrant(appCode, input) {
586
+ return await this.json(`${this.authzPath(appCode)}/relationship-grants`, { method: "POST", body: JSON.stringify(input) });
587
+ }
588
+ async updateRelationshipGrant(appCode, grantId, input) {
589
+ return await this.json(`${this.authzPath(appCode)}/relationship-grants/${encodeURIComponent(grantId)}/update`, { method: "POST", body: JSON.stringify(input) });
590
+ }
591
+ async revokeRelationshipGrant(appCode, grantId, expectedRevision) {
592
+ return await this.json(`${this.authzPath(appCode)}/relationship-grants/${encodeURIComponent(grantId)}/revoke`, { method: "POST", body: JSON.stringify({ expectedRevision }) });
593
+ }
594
+ async queryData(appCode, resourceCode, query, request = {}) {
595
+ return await this.json(this.dataPath(appCode, resourceCode, "query"), {
596
+ method: "POST",
597
+ body: JSON.stringify(query),
598
+ headers: this.perspectiveHeaders(request),
599
+ });
600
+ }
601
+ async chinaDivisions(parentAdcode) {
602
+ const query = new URLSearchParams();
603
+ if (parentAdcode)
604
+ query.set("parentAdcode", parentAdcode);
605
+ const suffix = query.size ? `?${query}` : "";
606
+ return await this.json(`/china-divisions/${suffix}`);
607
+ }
608
+ async getData(appCode, resourceCode, id, request = {}) {
609
+ return await this.json(this.dataPath(appCode, resourceCode, `records/${encodeURIComponent(id)}`), { headers: this.perspectiveHeaders(request) });
610
+ }
611
+ async aggregateData(appCode, resourceCode, query, request = {}) {
612
+ return await this.json(this.dataPath(appCode, resourceCode, "aggregate"), {
613
+ method: "POST",
614
+ body: JSON.stringify(query),
615
+ headers: this.perspectiveHeaders(request),
616
+ });
617
+ }
618
+ async dataAudit(appCode, resourceCode, id, input = {}, request = {}) {
619
+ const query = new URLSearchParams();
620
+ if (input.limit !== undefined)
621
+ query.set("limit", String(input.limit));
622
+ if (input.offset !== undefined)
623
+ query.set("offset", String(input.offset));
624
+ const suffix = query.size ? `?${query}` : "";
625
+ return await this.json(`${this.dataPath(appCode, resourceCode, `records/${encodeURIComponent(id)}/audit`)}${suffix}`, { headers: this.perspectiveHeaders(request) });
626
+ }
627
+ async initiateDataFileUpload(appCode, resourceCode, input, request = {}) {
628
+ return await this.json(this.dataPath(appCode, resourceCode, "files/uploads/initiate"), {
629
+ method: "POST",
630
+ body: JSON.stringify(input),
631
+ headers: this.perspectiveHeaders(request),
632
+ });
633
+ }
634
+ async completeDataFileUpload(appCode, resourceCode, fileId, request = {}) {
635
+ return await this.json(this.dataPath(appCode, resourceCode, `files/${encodeURIComponent(fileId)}/complete`), {
636
+ method: "POST",
637
+ body: "{}",
638
+ headers: this.perspectiveHeaders(request),
639
+ });
640
+ }
641
+ async deleteUnboundDataFile(appCode, resourceCode, fileId, request = {}) {
642
+ return await this.json(this.dataPath(appCode, resourceCode, `files/${encodeURIComponent(fileId)}/delete`), {
643
+ method: "POST",
644
+ body: "{}",
645
+ headers: this.perspectiveHeaders(request),
646
+ });
647
+ }
648
+ async dataFileContent(appCode, resourceCode, fileId, request = {}) {
649
+ const path = this.dataPath(appCode, resourceCode, `files/${encodeURIComponent(fileId)}/content`);
650
+ const download = async (forceRefresh) => {
651
+ const token = await this.accessToken(forceRefresh);
652
+ return await this.fetch(`${this.baseUrl}${path}`, {
653
+ credentials: this.options.credentials || "same-origin",
654
+ headers: {
655
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
656
+ ...this.perspectiveHeaders(request),
657
+ },
658
+ });
659
+ };
660
+ let response = await download(false);
661
+ if (response.status === 401 && this.options.tokenProvider) {
662
+ response = await download(true);
663
+ }
664
+ if (!response.ok) {
665
+ let data = null;
666
+ try {
667
+ data = await response.json();
668
+ }
669
+ catch {
670
+ // Binary endpoints may fail before the platform writes a JSON body.
671
+ }
672
+ throw new ControlPlaneError(response.status, data?.errorCode || "DATA_FILE_DOWNLOAD_FAILED", data?.message || `文件下载失败: ${response.status}`, data);
673
+ }
674
+ return response;
675
+ }
676
+ async createData(appCode, resourceCode, data, request = {}) {
677
+ return await this.json(this.dataPath(appCode, resourceCode, "records"), {
678
+ method: "POST",
679
+ body: JSON.stringify({ data }),
680
+ headers: this.perspectiveHeaders(request),
681
+ });
682
+ }
683
+ async updateData(appCode, resourceCode, id, input, request = {}) {
684
+ return await this.json(this.dataPath(appCode, resourceCode, `records/${encodeURIComponent(id)}/update`), {
685
+ method: "POST",
686
+ body: JSON.stringify(input),
687
+ headers: this.perspectiveHeaders(request),
688
+ });
689
+ }
690
+ async deleteData(appCode, resourceCode, id, expectedRevision, request = {}) {
691
+ return await this.json(this.dataPath(appCode, resourceCode, `records/${encodeURIComponent(id)}/delete`), {
692
+ method: "POST",
693
+ body: JSON.stringify({ expectedRevision }),
694
+ headers: this.perspectiveHeaders(request),
695
+ });
696
+ }
697
+ async transactData(appCode, transaction, request = {}) {
698
+ return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/native/data/transactions`, {
699
+ method: "POST",
700
+ body: JSON.stringify(transaction),
701
+ headers: this.perspectiveHeaders(request),
702
+ });
703
+ }
704
+ dataPath(appCode, resourceCode, suffix) {
705
+ return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/native/data/${encodeURIComponent(resourceCode)}/${suffix}`;
706
+ }
707
+ applicationApiPath(appCode, environmentKey, runtimePath, query = {}) {
708
+ const normalized = String(runtimePath || "")
709
+ .replace(/^\/+/, "")
710
+ .replace(/\/+$/, "");
711
+ if (!normalized ||
712
+ normalized.includes("\0") ||
713
+ normalized
714
+ .split("/")
715
+ .some((part) => !part || part === "." || part === "..")) {
716
+ throw new ControlPlaneError(400, "APPLICATION_API_PATH_INVALID", "应用后端路径不能为空或包含路径穿越片段");
717
+ }
718
+ const params = new URLSearchParams();
719
+ for (const [key, value] of Object.entries(query)) {
720
+ if (value !== undefined && value !== null)
721
+ params.set(key, String(value));
722
+ }
723
+ const suffix = params.size ? `?${params}` : "";
724
+ return `/openxiangda-app-api/v2/${encodeURIComponent(appCode)}/${encodeURIComponent(environmentKey)}/${normalized
725
+ .split("/")
726
+ .map((part) => encodeURIComponent(part))
727
+ .join("/")}${suffix}`;
728
+ }
729
+ async deployExistingVersion(input, action) {
730
+ return await this.json(`/openxiangda-api/v2/applications/${encodeURIComponent(input.appCode)}/${action}`, {
731
+ method: "POST",
732
+ body: JSON.stringify({
733
+ appVersionId: input.appVersionId,
734
+ environmentId: input.environmentId,
735
+ environmentKind: input.environmentKind,
736
+ idempotencyKey: input.idempotencyKey,
737
+ requestId: input.requestId,
738
+ }),
739
+ });
740
+ }
741
+ authzPath(appCode) {
742
+ return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/authz`;
743
+ }
744
+ nativeAuthzPath(appCode) {
745
+ return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/native/authz`;
746
+ }
747
+ nativeAuthzManagementPath(appCode) {
748
+ return `${this.nativeAuthzPath(appCode)}/management`;
749
+ }
750
+ query(input) {
751
+ const query = new URLSearchParams();
752
+ for (const [key, value] of Object.entries(input)) {
753
+ if (value === undefined || value === null || value === "")
754
+ continue;
755
+ query.set(key, String(value));
756
+ }
757
+ return query.toString();
758
+ }
759
+ directoryPath(appCode) {
760
+ return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/directory`;
761
+ }
762
+ oauthPath(appCode) {
763
+ return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/oauth2`;
764
+ }
765
+ applicationSecretsPath(appCode, environmentKey) {
766
+ return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/environments/${encodeURIComponent(environmentKey)}/secrets`;
767
+ }
768
+ eventsPath(appCode) {
769
+ return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/events`;
770
+ }
771
+ connectedDevelopmentSessionsPath(appCode) {
772
+ return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/dev-sessions`;
773
+ }
774
+ connectedDevelopmentSessionHeaders(sessionToken) {
775
+ const token = String(sessionToken || "").trim();
776
+ if (!token)
777
+ throw new Error("OPENXIANGDA_CONNECTED_DEV_SESSION_REQUIRED");
778
+ return { "X-OpenXiangda-Dev-Session": token };
779
+ }
780
+ workflowPath(appCode) {
781
+ return `/openxiangda-api/v2/applications/${encodeURIComponent(appCode)}/workflow`;
782
+ }
783
+ perspectiveHeaders(request) {
784
+ const perspectiveCode = String(request?.perspectiveCode || "").trim();
785
+ if (perspectiveCode && !/^[a-z][a-z0-9._-]{0,127}$/.test(perspectiveCode)) {
786
+ throw new ControlPlaneError(400, "OPENXIANGDA_PERSPECTIVE_INVALID", "perspectiveCode must be a stable lowercase identifier");
787
+ }
788
+ return perspectiveCode
789
+ ? { "X-OpenXiangda-Perspective": perspectiveCode }
790
+ : {};
791
+ }
792
+ basicCredentials(clientId, clientSecret) {
793
+ const value = `${clientId}:${clientSecret}`;
794
+ if (typeof Buffer !== "undefined")
795
+ return Buffer.from(value).toString("base64");
796
+ return globalThis.btoa(value);
797
+ }
798
+ async json(path, init = {}) {
799
+ return await this.request(path, {
800
+ ...init,
801
+ headers: {
802
+ "Content-Type": "application/json",
803
+ ...(init.headers || {}),
804
+ },
805
+ });
806
+ }
807
+ async request(path, init = {}) {
808
+ try {
809
+ return await this.requestOnce(path, init, false);
810
+ }
811
+ catch (error) {
812
+ if (!this.options.tokenProvider || !this.isUnauthorized(error))
813
+ throw error;
814
+ return await this.requestOnce(path, init, true);
815
+ }
816
+ }
817
+ async requestOnce(path, init, forceRefresh) {
818
+ const token = await this.accessToken(forceRefresh);
819
+ const response = await this.fetch(`${this.baseUrl}${path}`, {
820
+ ...init,
821
+ credentials: init.credentials || this.options.credentials || "same-origin",
822
+ headers: {
823
+ Accept: "application/json",
824
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
825
+ ...(init.headers || {}),
826
+ },
827
+ });
828
+ let envelope;
829
+ try {
830
+ envelope = (await response.json());
831
+ }
832
+ catch {
833
+ throw new ControlPlaneError(response.status, "CONTROL_PLANE_RESPONSE_INVALID", "平台返回的不是有效 JSON");
834
+ }
835
+ if (!response.ok || Number(envelope.code) >= 400) {
836
+ const status = response.ok ? Number(envelope.code) : response.status;
837
+ throw new ControlPlaneError(status, envelope.errorCode || "CONTROL_PLANE_REQUEST_FAILED", envelope.message || `平台请求失败: ${response.status}`, envelope.data, {
838
+ ...(envelope.retryable === undefined
839
+ ? {}
840
+ : { retryable: envelope.retryable }),
841
+ ...(envelope.remediation
842
+ ? { remediation: envelope.remediation }
843
+ : {}),
844
+ ...(envelope.requestId ? { requestId: envelope.requestId } : {}),
845
+ ...(envelope.path ? { path: envelope.path } : {}),
846
+ ...(envelope.field ? { field: envelope.field } : {}),
847
+ });
848
+ }
849
+ return envelope.data;
850
+ }
851
+ isUnauthorized(error) {
852
+ return error instanceof ControlPlaneError && error.status === 401;
853
+ }
854
+ async accessToken(forceRefresh) {
855
+ return this.options.tokenProvider
856
+ ? await this.options.tokenProvider.getAccessToken({ forceRefresh })
857
+ : this.options.token;
858
+ }
859
+ }
860
+ //# sourceMappingURL=control-plane-client.js.map