void 0.20.1 → 0.20.3
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 +5 -1
- package/dist/{auth-W9WII-mN.mjs → auth-DPl6kck4.mjs} +46 -26
- package/dist/{auth-cmd-CAH62yDU.mjs → auth-cmd-CzwquNiP.mjs} +5 -4
- package/dist/auth-link-ElDTgF7j.mjs +28 -0
- package/dist/{build-cmd-CJvZvPQO.mjs → build-cmd-BpJe6boP.mjs} +3 -3
- package/dist/{cache-BlNeQjuP.mjs → cache-D98YTqeE.mjs} +3 -3
- package/dist/{cancel-deploy-CmlAZ9P6.mjs → cancel-deploy-CPbEQLMG.mjs} +3 -3
- package/dist/cf-access-DRsQRe6k.mjs +75 -0
- package/dist/cli/cli.mjs +309 -1958
- package/dist/cli/env-schema-probe.mjs +11 -2
- package/dist/client-BQBrZoCX.mjs +989 -0
- package/dist/{cloudflare-auth-B1QtTO1b.mjs → cloudflare-auth-6M5llVPC.mjs} +2 -2
- package/dist/{cloudflare-cmd-B6_OZx2V.mjs → cloudflare-cmd-4RPGN3KB.mjs} +2 -2
- package/dist/{cloudflare-connect-j5D4hhrG.mjs → cloudflare-connect-t1UU5svD.mjs} +2 -2
- package/dist/{cloudflare-operations-CPTpRW6d.mjs → cloudflare-operations-BzWnlC1_.mjs} +1 -1
- package/dist/{config-BQFq7QvD.mjs → config-uNGuFsI2.mjs} +1 -1
- package/dist/{connect-C04Wdy_h.mjs → connect-WCQZ_u3m.mjs} +6 -6
- package/dist/{create-project-ChGZ1DFd.mjs → create-project-D0oXA090.mjs} +7 -7
- package/dist/{db-D2d_mUsB.mjs → db-DJ-9qs3S.mjs} +47 -30
- package/dist/{delete-D8GigDk8.mjs → delete-BZ4-WaGm.mjs} +3 -3
- package/dist/{deploy-iXZ3F0N6.mjs → deploy-BAhhcg5q.mjs} +135 -117
- package/dist/{dev-inbox-DkgRWLkW.mjs → dev-inbox-P0u4tM8Y.mjs} +1 -1
- package/dist/{domain-B1VmoSr0.mjs → domain-BxAyhxXN.mjs} +4 -4
- package/dist/email-Bj7Cvdwp.mjs +795 -0
- package/dist/{env-D4Emu-M_.mjs → env-DBKmK4vc.mjs} +1 -0
- package/dist/{env-BcQzYgoG.mjs → env-DP_EErve.mjs} +5 -5
- package/dist/{env-validation-ENpMy6Ez.mjs → env-validation-CF6KvTRf.mjs} +3 -1
- package/dist/{gen-DI2YwdBM.mjs → gen-B_wPnVTK.mjs} +2 -2
- package/dist/{github-cmd-xItS5Zwf.mjs → github-cmd-C-z_xRrQ.mjs} +15 -21
- package/dist/{headers-D8QfRX9Y.mjs → headers-BAHwgHdW.mjs} +1 -1
- package/dist/help-CwOX-zmI.mjs +2216 -0
- package/dist/{inbound-afAcWeQ9.d.mts → inbound-CH5Mksyy.d.mts} +34 -48
- package/dist/{inbound-2d0zi2yS.mjs → inbound-aVHEUhKo.mjs} +130 -100
- package/dist/index.mjs +54 -17
- package/dist/{init-BD-9THgn.mjs → init-BGktCXgA.mjs} +11 -11
- package/dist/{link-RMdgjF1v.mjs → link-D2kbqhWb.mjs} +4 -4
- package/dist/{list-3F52R_yO.mjs → list-CvkK_G7k.mjs} +4 -4
- package/dist/{login-pV69H-ZO.mjs → login-WIjNc77c.mjs} +28 -10
- package/dist/{logs-DFHHD6wE.mjs → logs-dLUFCapG.mjs} +4 -4
- package/dist/{mime-BJD7d_qL.mjs → mime-D5Nmdzf7.mjs} +23 -9
- package/dist/{node-Dk3H2jmU.mjs → node-Ez5KW5rn.mjs} +2 -2
- package/dist/operator-auth-B3e08unv.mjs +52 -0
- package/dist/operator-client-LUZnlnYk.mjs +82 -0
- package/dist/{operator-cmd-DYWRbWUA.mjs → operator-cmd-CKJ7xIRs.mjs} +35 -55
- package/dist/{output-tFQLLj26.mjs → output-B0cfNSx5.mjs} +316 -2
- package/dist/pages/index.mjs +2 -2
- package/dist/platform-auth-config-CdVWRRJr.mjs +368 -0
- package/dist/platform-auth-protection-Drl0qhrn.mjs +219 -0
- package/dist/platform-auth-recovery-CmKEWpDo.mjs +310 -0
- package/dist/{platform-cmd-DxJ2FRwR.mjs → platform-cmd-5q_k56xS.mjs} +16 -6
- package/dist/{platform-domain-ChvbJkdy.mjs → platform-domain-4GiDlcqx.mjs} +4 -4
- package/dist/{platform-lifecycle-DN4MzJF_.mjs → platform-lifecycle-R9xAxvtG.mjs} +1716 -204
- package/dist/{platform-management-Db2PXw0B.mjs → platform-management-BfWsXHEW.mjs} +35 -7
- package/dist/{platform-recovery-C_YO-tIs.mjs → platform-recovery-CbK-I1FB.mjs} +6 -5
- package/dist/{prepare-CBetXvsN.mjs → prepare-CtDJjoOj.mjs} +2 -2
- package/dist/{prepare-BfJvFUtJ.mjs → prepare-blNRQvQl.mjs} +2 -2
- package/dist/prerender-render.d.mts +11 -0
- package/dist/prerender-render.mjs +111 -0
- package/dist/{project-cmd-Mo0V9yKS.mjs → project-cmd-CTdmnzvc.mjs} +32 -14
- package/dist/project-team-CGxsQe3_.mjs +132 -0
- package/dist/project-token-Cirx7uwZ.mjs +75 -0
- package/dist/{provision-Blnstcm2.mjs → provision-CSJOjjQk.mjs} +2 -0
- package/dist/{requests-BcKOVpRg.mjs → requests-4Nq59hOr.mjs} +3 -3
- package/dist/{rollback-Bx85-0xh.mjs → rollback-Dr7u0Ljx.mjs} +4 -4
- package/dist/runtime/ai.mjs +3 -2
- package/dist/runtime/email/testing.d.mts +1 -1
- package/dist/runtime/email/testing.mjs +3 -3
- package/dist/runtime/email-protocol.d.mts +15 -0
- package/dist/runtime/email-protocol.mjs +70 -0
- package/dist/runtime/email.d.mts +2 -2
- package/dist/runtime/email.mjs +189 -96
- package/dist/runtime/remote/index.mjs +54 -11
- package/dist/runtime/sandbox.d.mts +4 -56
- package/dist/runtime/sandbox.mjs +81 -220
- package/dist/{secret-ByhJ9AMl.mjs → secret-AcPi-FoA.mjs} +5 -5
- package/dist/{skills-Q46GZMO-.mjs → skills-C0RvGjeE.mjs} +1 -1
- package/dist/{subcommand-prompt-WfySCQ7S.mjs → subcommand-prompt-Bmyn5Rlc.mjs} +1 -1
- package/package.json +12 -7
- package/skills/void/SKILL.md +35 -4
- package/skills/void/docs/guide/deployment.md +16 -14
- package/skills/void/docs/guide/email.md +114 -120
- package/skills/void/docs/guide/platform/administration/access.md +163 -0
- package/skills/void/docs/guide/platform/administration/email.md +121 -0
- package/skills/void/docs/guide/platform/administration/operations.md +97 -0
- package/skills/void/docs/guide/platform/administration/projects.md +54 -0
- package/skills/void/docs/guide/platform/development/local.md +119 -0
- package/skills/void/docs/guide/platform/development/runtime.md +124 -0
- package/skills/void/docs/guide/platform/development/schema-ci.md +95 -0
- package/skills/void/docs/guide/platform/installation/ci.md +55 -0
- package/skills/void/docs/guide/platform/installation/credentials.md +80 -0
- package/skills/void/docs/guide/platform/installation/domains.md +68 -0
- package/skills/void/docs/guide/platform/installation/first-deployment.md +82 -0
- package/skills/void/docs/guide/platform/installation/maintenance.md +137 -0
- package/skills/void/docs/guide/platform/installation/prerequisites.md +86 -0
- package/skills/void/docs/guide/platform/installation/setup.md +169 -0
- package/skills/void/docs/guide/platform/installation/uninstall.md +54 -0
- package/skills/void/docs/guide/platform-administration.md +6 -202
- package/skills/void/docs/guide/platform-development.md +5 -254
- package/skills/void/docs/guide/project-collaboration.md +94 -0
- package/skills/void/docs/guide/sandboxes.md +9 -24
- package/skills/void/docs/guide/self-hosted-platform.md +13 -542
- package/skills/void/docs/reference/api.md +34 -34
- package/skills/void/docs/reference/cli.md +276 -31
- package/skills/void/docs/reference/config.md +1 -1
- package/skills/void/docs/reference/resource-inference.md +10 -10
- package/dist/cf-access-AJ1ehiFR.mjs +0 -42
- package/dist/cf-access-DsSsZUPr.mjs +0 -67
- package/dist/client-Clirrol3.mjs +0 -705
- package/dist/email-uKyQYUVY.mjs +0 -1016
package/dist/client-Clirrol3.mjs
DELETED
|
@@ -1,705 +0,0 @@
|
|
|
1
|
-
import { n as __exportAll } from "./rolldown-runtime-rQ84J-ij.mjs";
|
|
2
|
-
import { o as join } from "./dist-BrsS7cai.mjs";
|
|
3
|
-
import { G as normalizePlatformApiUrl, p as getPackageDir } from "./output-tFQLLj26.mjs";
|
|
4
|
-
import { t as cfAccessHeaders } from "./cf-access-AJ1ehiFR.mjs";
|
|
5
|
-
import { a as getApiUrl } from "./auth-W9WII-mN.mjs";
|
|
6
|
-
import { t as ensureCloudflaredToken } from "./cf-access-DsSsZUPr.mjs";
|
|
7
|
-
import { readFileSync } from "node:fs";
|
|
8
|
-
//#region src/cli/client.ts
|
|
9
|
-
var client_exports = /* @__PURE__ */ __exportAll({
|
|
10
|
-
PlatformApiError: () => PlatformApiError,
|
|
11
|
-
PlatformClient: () => PlatformClient,
|
|
12
|
-
isCliOutdatedError: () => isCliOutdatedError,
|
|
13
|
-
isExpiredTokenError: () => isExpiredTokenError,
|
|
14
|
-
parsePlatformErrorBody: () => parsePlatformErrorBody,
|
|
15
|
-
readCliVersion: () => readCliVersion
|
|
16
|
-
});
|
|
17
|
-
/**
|
|
18
|
-
* Current CLI version. Sent as `X-Void-CLI-Version` on every platform
|
|
19
|
-
* request so the platform's CLI-version gate can refuse stale CLIs
|
|
20
|
-
* cleanly (HTTP 426 + `cli_outdated`) instead of letting a wire
|
|
21
|
-
* mismatch surface as an opaque parse error.
|
|
22
|
-
*
|
|
23
|
-
* Resolved once per process. Returns `null` when package.json can't be
|
|
24
|
-
* read or has no `version` string — the ctor then OMITS the header
|
|
25
|
-
* entirely. We deliberately do NOT send a placeholder string like
|
|
26
|
-
* "unknown": the platform treats present-but-unparseable headers as a
|
|
27
|
-
* stale-CLI signal and would 426 the request, bricking the CLI on any
|
|
28
|
-
* packaging error. Missing-header is the documented pass-through path.
|
|
29
|
-
*/
|
|
30
|
-
function readCliVersion() {
|
|
31
|
-
try {
|
|
32
|
-
const pkgPath = join(getPackageDir(), "package.json");
|
|
33
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
34
|
-
return typeof pkg.version === "string" ? pkg.version : null;
|
|
35
|
-
} catch {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
const VOID_CLI_VERSION = readCliVersion();
|
|
40
|
-
function parsePlatformErrorBody(body) {
|
|
41
|
-
try {
|
|
42
|
-
const parsed = JSON.parse(body);
|
|
43
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
44
|
-
return parsed;
|
|
45
|
-
} catch {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Detects the `cli_outdated` 426 response shape emitted by the platform's
|
|
51
|
-
* `cliVersionGate` middleware. When present, callers should surface the
|
|
52
|
-
* server's `message` field verbatim — it already includes upgrade
|
|
53
|
-
* instructions — rather than the default `<action>: <status> <body>`
|
|
54
|
-
* which would dump raw JSON at the user.
|
|
55
|
-
*/
|
|
56
|
-
function isCliOutdatedError(err) {
|
|
57
|
-
if (!(err instanceof PlatformApiError)) return false;
|
|
58
|
-
if (err.status !== 426) return false;
|
|
59
|
-
return parsePlatformErrorBody(err.body)?.error === "cli_outdated";
|
|
60
|
-
}
|
|
61
|
-
var PlatformApiError = class extends Error {
|
|
62
|
-
status;
|
|
63
|
-
body;
|
|
64
|
-
code;
|
|
65
|
-
constructor(action, status, body) {
|
|
66
|
-
const parsed = parsePlatformErrorBody(body);
|
|
67
|
-
const message = status === 426 && parsed?.error === "cli_outdated" && parsed.message ? parsed.message : `${action}: ${status} ${body}`;
|
|
68
|
-
super(message);
|
|
69
|
-
this.name = "PlatformApiError";
|
|
70
|
-
this.status = status;
|
|
71
|
-
this.body = body;
|
|
72
|
-
this.code = parsed?.error ?? parsed?.message;
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
async function throwPlatformApiError(action, res) {
|
|
76
|
-
throw new PlatformApiError(action, res.status, await res.text());
|
|
77
|
-
}
|
|
78
|
-
function isExpiredTokenError(error) {
|
|
79
|
-
if (error instanceof PlatformApiError) return error.status === 401 && error.code === "invalid or expired token";
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* HTTP client for the Void platform API.
|
|
84
|
-
*/
|
|
85
|
-
var PlatformClient = class {
|
|
86
|
-
baseUrl;
|
|
87
|
-
authHeaders;
|
|
88
|
-
cliLog;
|
|
89
|
-
apiUrl;
|
|
90
|
-
constructor(token, options) {
|
|
91
|
-
if (typeof options === "string") this.baseUrl = normalizePlatformApiUrl(options);
|
|
92
|
-
else {
|
|
93
|
-
this.baseUrl = normalizePlatformApiUrl(options?.apiUrl ?? getApiUrl(options?.root));
|
|
94
|
-
this.cliLog = options?.cliLog;
|
|
95
|
-
}
|
|
96
|
-
this.apiUrl = this.baseUrl;
|
|
97
|
-
ensureCloudflaredToken(this.baseUrl);
|
|
98
|
-
this.authHeaders = {
|
|
99
|
-
Authorization: `Bearer ${token}`,
|
|
100
|
-
...VOID_CLI_VERSION !== null && { "X-Void-CLI-Version": VOID_CLI_VERSION },
|
|
101
|
-
...cfAccessHeaders(process.env, this.baseUrl)
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
async whoami() {
|
|
105
|
-
const res = await fetch(`${this.baseUrl}/me`, { headers: { ...this.authHeaders } });
|
|
106
|
-
if (!res.ok) await throwPlatformApiError("Failed to fetch user info", res);
|
|
107
|
-
return res.json();
|
|
108
|
-
}
|
|
109
|
-
async listProjects() {
|
|
110
|
-
const res = await fetch(`${this.baseUrl}/projects`, { headers: { ...this.authHeaders } });
|
|
111
|
-
if (!res.ok) await throwPlatformApiError("Failed to list projects", res);
|
|
112
|
-
return res.json();
|
|
113
|
-
}
|
|
114
|
-
async listDeployments(projectId) {
|
|
115
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/deployments`, { headers: { ...this.authHeaders } });
|
|
116
|
-
if (!res.ok) await throwPlatformApiError("Failed to list deployments", res);
|
|
117
|
-
return res.json();
|
|
118
|
-
}
|
|
119
|
-
async rollback(projectId, deploymentId, options) {
|
|
120
|
-
const body = { deploymentId };
|
|
121
|
-
if (options?.allowDestructive && options.allowDestructive.length > 0) body.allowDestructive = options.allowDestructive;
|
|
122
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/rollback`, {
|
|
123
|
-
method: "POST",
|
|
124
|
-
headers: {
|
|
125
|
-
"Content-Type": "application/json",
|
|
126
|
-
...this.authHeaders
|
|
127
|
-
},
|
|
128
|
-
body: JSON.stringify(body)
|
|
129
|
-
});
|
|
130
|
-
if (!res.ok) await throwPlatformApiError("Rollback failed", res);
|
|
131
|
-
return res.json();
|
|
132
|
-
}
|
|
133
|
-
async cancelDeployment(projectId, deploymentId) {
|
|
134
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/deployments/${deploymentId}/cancel`, {
|
|
135
|
-
method: "POST",
|
|
136
|
-
headers: { ...this.authHeaders }
|
|
137
|
-
});
|
|
138
|
-
if (!res.ok) await throwPlatformApiError("Cancel deployment failed", res);
|
|
139
|
-
return res.json();
|
|
140
|
-
}
|
|
141
|
-
async createProject(slug) {
|
|
142
|
-
const res = await fetch(`${this.baseUrl}/projects`, {
|
|
143
|
-
method: "POST",
|
|
144
|
-
headers: {
|
|
145
|
-
"Content-Type": "application/json",
|
|
146
|
-
...this.authHeaders
|
|
147
|
-
},
|
|
148
|
-
body: JSON.stringify({ slug })
|
|
149
|
-
});
|
|
150
|
-
if (!res.ok) await throwPlatformApiError("Failed to create project", res);
|
|
151
|
-
return res.json();
|
|
152
|
-
}
|
|
153
|
-
async preflight(projectId, assetManifest, hasWorker) {
|
|
154
|
-
const total = Object.keys(assetManifest).length;
|
|
155
|
-
try {
|
|
156
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/deploy/preflight`, {
|
|
157
|
-
method: "POST",
|
|
158
|
-
headers: {
|
|
159
|
-
"Content-Type": "application/json",
|
|
160
|
-
...this.authHeaders
|
|
161
|
-
},
|
|
162
|
-
body: JSON.stringify({
|
|
163
|
-
assetManifest,
|
|
164
|
-
...hasWorker && { hasWorker }
|
|
165
|
-
})
|
|
166
|
-
});
|
|
167
|
-
if (!res.ok) {
|
|
168
|
-
const error = new PlatformApiError("Preflight failed", res.status, await res.text());
|
|
169
|
-
if (isExpiredTokenError(error)) throw error;
|
|
170
|
-
return {
|
|
171
|
-
needed: Object.keys(assetManifest),
|
|
172
|
-
total,
|
|
173
|
-
skipped: 0
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
return res.json();
|
|
177
|
-
} catch (error) {
|
|
178
|
-
if (isExpiredTokenError(error)) throw error;
|
|
179
|
-
return {
|
|
180
|
-
needed: Object.keys(assetManifest),
|
|
181
|
-
total,
|
|
182
|
-
skipped: 0
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Request presigned R2 PUT URLs for a batch of assets. Each entry carries
|
|
188
|
-
* both `blake3` (storage key) and `md5` (wire-level integrity primitive)
|
|
189
|
-
* so the platform can sign a PUT URL that R2 enforces against the bytes
|
|
190
|
-
* the CLI is about to upload. See design doc 0068 for the full protocol.
|
|
191
|
-
*/
|
|
192
|
-
async requestUploadUrls(projectId, assets) {
|
|
193
|
-
const url = `${this.baseUrl}/projects/${projectId}/deploy/upload-urls`;
|
|
194
|
-
this.cliLog?.info("upload_urls_post", {
|
|
195
|
-
url,
|
|
196
|
-
assetCount: assets.length
|
|
197
|
-
});
|
|
198
|
-
const res = await fetch(url, {
|
|
199
|
-
method: "POST",
|
|
200
|
-
headers: {
|
|
201
|
-
"Content-Type": "application/json",
|
|
202
|
-
...this.authHeaders
|
|
203
|
-
},
|
|
204
|
-
body: JSON.stringify({ assets })
|
|
205
|
-
});
|
|
206
|
-
const cfRay = res.headers.get("cf-ray");
|
|
207
|
-
const cfCacheStatus = res.headers.get("cf-cache-status");
|
|
208
|
-
this.cliLog?.info("upload_urls_response", {
|
|
209
|
-
status: res.status,
|
|
210
|
-
cfRay,
|
|
211
|
-
cfCacheStatus
|
|
212
|
-
});
|
|
213
|
-
if (!res.ok) await throwPlatformApiError("Failed to request upload URLs", res);
|
|
214
|
-
return (await res.json()).urls;
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Manifest-only (direct-to-R2) deploy. POSTs a JSON body containing the
|
|
218
|
-
* deploy manifest, asset manifest (no asset bytes), and any worker module
|
|
219
|
-
* bytes (base64-encoded). Streams an NDJSON response that the caller can
|
|
220
|
-
* apply with the existing `applyDeployEvent` machinery.
|
|
221
|
-
*/
|
|
222
|
-
async *finalizeDeploy(projectId, payload) {
|
|
223
|
-
const url = `${this.baseUrl}/projects/${projectId}/deploy`;
|
|
224
|
-
yield* this.streamDeployRequest(url, {
|
|
225
|
-
method: "POST",
|
|
226
|
-
headers: {
|
|
227
|
-
"Content-Type": "application/json",
|
|
228
|
-
...this.authHeaders
|
|
229
|
-
},
|
|
230
|
-
body: JSON.stringify(payload)
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* POST + NDJSON-stream-reader used by `finalizeDeploy()`. Captures
|
|
235
|
-
* `cf-ray` + `cf-cache-status` for postmortem correlation.
|
|
236
|
-
*/
|
|
237
|
-
async *streamDeployRequest(url, init) {
|
|
238
|
-
const cliLog = this.cliLog;
|
|
239
|
-
cliLog?.info("deploy_post", { url });
|
|
240
|
-
let res;
|
|
241
|
-
try {
|
|
242
|
-
res = await fetch(url, init);
|
|
243
|
-
} catch (err) {
|
|
244
|
-
cliLog?.error("deploy_fetch_rejected", err, { url });
|
|
245
|
-
throw err;
|
|
246
|
-
}
|
|
247
|
-
const cfRay = res.headers.get("cf-ray");
|
|
248
|
-
const cfCacheStatus = res.headers.get("cf-cache-status");
|
|
249
|
-
cliLog?.info("deploy_response", {
|
|
250
|
-
status: res.status,
|
|
251
|
-
cfRay,
|
|
252
|
-
cfCacheStatus,
|
|
253
|
-
contentType: res.headers.get("content-type")
|
|
254
|
-
});
|
|
255
|
-
if (!res.ok) await throwPlatformApiError("Deploy failed", res);
|
|
256
|
-
const reader = res.body.getReader();
|
|
257
|
-
const decoder = new TextDecoder();
|
|
258
|
-
let buffer = "";
|
|
259
|
-
let bytesRead = 0;
|
|
260
|
-
let firstByteAt = null;
|
|
261
|
-
const startedAt = Date.now();
|
|
262
|
-
let eventCount = 0;
|
|
263
|
-
let lastEventName = "<none>";
|
|
264
|
-
try {
|
|
265
|
-
while (true) {
|
|
266
|
-
const { done, value } = await reader.read();
|
|
267
|
-
if (done) {
|
|
268
|
-
cliLog?.info("deploy_stream_eof", {
|
|
269
|
-
bytesRead,
|
|
270
|
-
eventCount,
|
|
271
|
-
lastEvent: lastEventName
|
|
272
|
-
});
|
|
273
|
-
break;
|
|
274
|
-
}
|
|
275
|
-
if (firstByteAt === null) {
|
|
276
|
-
firstByteAt = Date.now() - startedAt;
|
|
277
|
-
cliLog?.info("deploy_stream_ttfb", { ttfbMs: firstByteAt });
|
|
278
|
-
}
|
|
279
|
-
bytesRead += value.byteLength;
|
|
280
|
-
buffer += decoder.decode(value, { stream: true });
|
|
281
|
-
let newlineIdx;
|
|
282
|
-
while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
|
|
283
|
-
const line = buffer.slice(0, newlineIdx);
|
|
284
|
-
buffer = buffer.slice(newlineIdx + 1);
|
|
285
|
-
if (line.trim()) {
|
|
286
|
-
const event = JSON.parse(line);
|
|
287
|
-
eventCount++;
|
|
288
|
-
lastEventName = event.event ?? "<no-event-field>";
|
|
289
|
-
cliLog?.info("deploy_event", {
|
|
290
|
-
eventCount,
|
|
291
|
-
event: lastEventName,
|
|
292
|
-
raw: line.slice(0, 200)
|
|
293
|
-
});
|
|
294
|
-
yield event;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
} catch (err) {
|
|
299
|
-
cliLog?.error("deploy_stream_error", err, {
|
|
300
|
-
bytesRead,
|
|
301
|
-
eventCount,
|
|
302
|
-
lastEvent: lastEventName
|
|
303
|
-
});
|
|
304
|
-
throw err;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
async listSecrets(projectId) {
|
|
308
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/secrets`, { headers: { ...this.authHeaders } });
|
|
309
|
-
if (!res.ok) await throwPlatformApiError("Failed to list secrets", res);
|
|
310
|
-
return res.json();
|
|
311
|
-
}
|
|
312
|
-
async setSecret(projectId, name, value) {
|
|
313
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/secrets/${encodeURIComponent(name)}`, {
|
|
314
|
-
method: "PUT",
|
|
315
|
-
headers: {
|
|
316
|
-
"Content-Type": "application/json",
|
|
317
|
-
...this.authHeaders
|
|
318
|
-
},
|
|
319
|
-
body: JSON.stringify({ value })
|
|
320
|
-
});
|
|
321
|
-
if (!res.ok) await throwPlatformApiError("Failed to set secret", res);
|
|
322
|
-
return res.json();
|
|
323
|
-
}
|
|
324
|
-
async deleteProject(projectId) {
|
|
325
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}`, {
|
|
326
|
-
method: "DELETE",
|
|
327
|
-
headers: { ...this.authHeaders }
|
|
328
|
-
});
|
|
329
|
-
if (!res.ok) await throwPlatformApiError("Failed to delete project", res);
|
|
330
|
-
return res.json();
|
|
331
|
-
}
|
|
332
|
-
async getMigrationStatus(projectId, dialect) {
|
|
333
|
-
const url = new URL(`${this.baseUrl}/projects/${projectId}/migrations`);
|
|
334
|
-
if (dialect) url.searchParams.set("dialect", dialect);
|
|
335
|
-
const res = await fetch(url, { headers: { ...this.authHeaders } });
|
|
336
|
-
if (!res.ok) await throwPlatformApiError("Failed to get migration status", res);
|
|
337
|
-
return res.json();
|
|
338
|
-
}
|
|
339
|
-
async applyRemoteMigrations(projectId, migrations) {
|
|
340
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/migrations/apply`, {
|
|
341
|
-
method: "POST",
|
|
342
|
-
headers: {
|
|
343
|
-
"Content-Type": "application/json",
|
|
344
|
-
...this.authHeaders
|
|
345
|
-
},
|
|
346
|
-
body: JSON.stringify({ migrations })
|
|
347
|
-
});
|
|
348
|
-
if (!res.ok) await throwPlatformApiError("Failed to apply remote migrations", res);
|
|
349
|
-
return res.json();
|
|
350
|
-
}
|
|
351
|
-
async renameMigrations(projectId, renames) {
|
|
352
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/migrations/rename`, {
|
|
353
|
-
method: "POST",
|
|
354
|
-
headers: {
|
|
355
|
-
"Content-Type": "application/json",
|
|
356
|
-
...this.authHeaders
|
|
357
|
-
},
|
|
358
|
-
body: JSON.stringify({ renames })
|
|
359
|
-
});
|
|
360
|
-
if (!res.ok) await throwPlatformApiError("Failed to rename remote migrations", res);
|
|
361
|
-
return res.json();
|
|
362
|
-
}
|
|
363
|
-
async addDomain(projectId, hostname) {
|
|
364
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/domains`, {
|
|
365
|
-
method: "POST",
|
|
366
|
-
headers: {
|
|
367
|
-
"Content-Type": "application/json",
|
|
368
|
-
...this.authHeaders
|
|
369
|
-
},
|
|
370
|
-
body: JSON.stringify({ hostname })
|
|
371
|
-
});
|
|
372
|
-
if (!res.ok) await throwPlatformApiError("Failed to add domain", res);
|
|
373
|
-
return res.json();
|
|
374
|
-
}
|
|
375
|
-
async listDomains(projectId) {
|
|
376
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/domains`, { headers: { ...this.authHeaders } });
|
|
377
|
-
if (!res.ok) await throwPlatformApiError("Failed to list domains", res);
|
|
378
|
-
return res.json();
|
|
379
|
-
}
|
|
380
|
-
async getDomainStatus(projectId, domainId) {
|
|
381
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/domains/${domainId}`, { headers: { ...this.authHeaders } });
|
|
382
|
-
if (!res.ok) await throwPlatformApiError("Failed to get domain status", res);
|
|
383
|
-
return res.json();
|
|
384
|
-
}
|
|
385
|
-
async removeDomain(projectId, domainId) {
|
|
386
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/domains/${domainId}`, {
|
|
387
|
-
method: "DELETE",
|
|
388
|
-
headers: { ...this.authHeaders }
|
|
389
|
-
});
|
|
390
|
-
if (!res.ok) await throwPlatformApiError("Failed to remove domain", res);
|
|
391
|
-
return res.json();
|
|
392
|
-
}
|
|
393
|
-
async deleteSecret(projectId, name) {
|
|
394
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/secrets/${encodeURIComponent(name)}`, {
|
|
395
|
-
method: "DELETE",
|
|
396
|
-
headers: { ...this.authHeaders }
|
|
397
|
-
});
|
|
398
|
-
if (!res.ok) await throwPlatformApiError("Failed to delete secret", res);
|
|
399
|
-
return res.json();
|
|
400
|
-
}
|
|
401
|
-
async getResources(projectId) {
|
|
402
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/resources`, { headers: { ...this.authHeaders } });
|
|
403
|
-
if (!res.ok) await throwPlatformApiError("Failed to get resources", res);
|
|
404
|
-
return res.json();
|
|
405
|
-
}
|
|
406
|
-
async createPrerenderToken(projectId) {
|
|
407
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/prerender-token`, {
|
|
408
|
-
method: "POST",
|
|
409
|
-
headers: { ...this.authHeaders }
|
|
410
|
-
});
|
|
411
|
-
if (!res.ok) await throwPlatformApiError("Failed to create prerender token", res);
|
|
412
|
-
return (await res.json()).token;
|
|
413
|
-
}
|
|
414
|
-
async provisionDatabase(projectId, connectionString) {
|
|
415
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/database`, {
|
|
416
|
-
method: "POST",
|
|
417
|
-
headers: {
|
|
418
|
-
"Content-Type": "application/json",
|
|
419
|
-
...this.authHeaders
|
|
420
|
-
},
|
|
421
|
-
body: JSON.stringify({ connectionString })
|
|
422
|
-
});
|
|
423
|
-
if (!res.ok) await throwPlatformApiError("Failed to provision database", res);
|
|
424
|
-
return res.json();
|
|
425
|
-
}
|
|
426
|
-
async setDatabaseUrl(projectId, connectionString) {
|
|
427
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/database-url`, {
|
|
428
|
-
method: "PUT",
|
|
429
|
-
headers: {
|
|
430
|
-
"Content-Type": "application/json",
|
|
431
|
-
...this.authHeaders
|
|
432
|
-
},
|
|
433
|
-
body: JSON.stringify({ connectionString })
|
|
434
|
-
});
|
|
435
|
-
if (!res.ok) await throwPlatformApiError("Failed to update database URL", res);
|
|
436
|
-
return res.json();
|
|
437
|
-
}
|
|
438
|
-
async getDatabaseUrl(projectId) {
|
|
439
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/database-url`, { headers: { ...this.authHeaders } });
|
|
440
|
-
if (!res.ok) await throwPlatformApiError("Failed to fetch database URL", res);
|
|
441
|
-
return res.json();
|
|
442
|
-
}
|
|
443
|
-
async purgeCache(projectId) {
|
|
444
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/revalidate`, {
|
|
445
|
-
method: "POST",
|
|
446
|
-
headers: {
|
|
447
|
-
"Content-Type": "application/json",
|
|
448
|
-
...this.authHeaders
|
|
449
|
-
},
|
|
450
|
-
body: JSON.stringify({ all: true })
|
|
451
|
-
});
|
|
452
|
-
if (!res.ok) await throwPlatformApiError("Cache purge failed", res);
|
|
453
|
-
return res.json();
|
|
454
|
-
}
|
|
455
|
-
async getLogs(projectId, since, limit = 200, deploymentId, level) {
|
|
456
|
-
const params = new URLSearchParams({
|
|
457
|
-
since,
|
|
458
|
-
limit: String(limit)
|
|
459
|
-
});
|
|
460
|
-
if (deploymentId) params.set("deploymentId", deploymentId);
|
|
461
|
-
if (level && level !== "all") params.set("level", level);
|
|
462
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/logs?${params}`, { headers: { ...this.authHeaders } });
|
|
463
|
-
if (!res.ok) await throwPlatformApiError("Failed to fetch logs", res);
|
|
464
|
-
return (await res.json()).data;
|
|
465
|
-
}
|
|
466
|
-
async getRequests(projectId, since, limit = 200, status) {
|
|
467
|
-
const params = new URLSearchParams({
|
|
468
|
-
since,
|
|
469
|
-
limit: String(limit)
|
|
470
|
-
});
|
|
471
|
-
if (status && status !== "all") params.set("status", status);
|
|
472
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/requests?${params}`, { headers: { ...this.authHeaders } });
|
|
473
|
-
if (!res.ok) await throwPlatformApiError("Failed to fetch requests", res);
|
|
474
|
-
return (await res.json()).data;
|
|
475
|
-
}
|
|
476
|
-
async listBuilds(projectId, limit = 20) {
|
|
477
|
-
const params = new URLSearchParams({ limit: String(limit) });
|
|
478
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/builds?${params}`, { headers: { ...this.authHeaders } });
|
|
479
|
-
if (!res.ok) await throwPlatformApiError("Failed to list builds", res);
|
|
480
|
-
return (await res.json()).builds;
|
|
481
|
-
}
|
|
482
|
-
async getBuildLogs(projectId, buildId, since = 0) {
|
|
483
|
-
const params = new URLSearchParams({ since: String(since) });
|
|
484
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/builds/${buildId}/logs?${params}`, { headers: { ...this.authHeaders } });
|
|
485
|
-
if (!res.ok) await throwPlatformApiError("Failed to fetch build logs", res);
|
|
486
|
-
return res.json();
|
|
487
|
-
}
|
|
488
|
-
async getGithubInstallUrl() {
|
|
489
|
-
const res = await fetch(`${this.baseUrl}/github/install-url`, { headers: { ...this.authHeaders } });
|
|
490
|
-
if (!res.ok) await throwPlatformApiError("Failed to get GitHub install URL", res);
|
|
491
|
-
return res.json();
|
|
492
|
-
}
|
|
493
|
-
async listGithubInstallations() {
|
|
494
|
-
const res = await fetch(`${this.baseUrl}/github/installations`, { headers: { ...this.authHeaders } });
|
|
495
|
-
if (!res.ok) await throwPlatformApiError("Failed to list GitHub installations", res);
|
|
496
|
-
return res.json();
|
|
497
|
-
}
|
|
498
|
-
async connectGithub(projectId, body) {
|
|
499
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/github`, {
|
|
500
|
-
method: "POST",
|
|
501
|
-
headers: {
|
|
502
|
-
"Content-Type": "application/json",
|
|
503
|
-
...this.authHeaders
|
|
504
|
-
},
|
|
505
|
-
body: JSON.stringify(body)
|
|
506
|
-
});
|
|
507
|
-
if (!res.ok) await throwPlatformApiError("Failed to connect GitHub repository", res);
|
|
508
|
-
return res.json();
|
|
509
|
-
}
|
|
510
|
-
/**
|
|
511
|
-
* Start the per-repo connect-authorization round-trip used by a non-owner org
|
|
512
|
-
* member (Task 7 platform contract). The callback redirects back to `redirect`
|
|
513
|
-
* with either `?connect_grant=<signed>` (proof of per-repo access) or
|
|
514
|
-
* `?error=repo_not_accessible`. Mirrors {@link getGithubLinkUrl} but carries
|
|
515
|
-
* the installation + repo the grant is scoped to.
|
|
516
|
-
*/
|
|
517
|
-
async getGithubConnectAuthorizeUrl(redirect, codeChallenge, installationId, repoFullName) {
|
|
518
|
-
const params = new URLSearchParams({
|
|
519
|
-
redirect,
|
|
520
|
-
code_challenge: codeChallenge,
|
|
521
|
-
code_challenge_method: "S256",
|
|
522
|
-
installationId,
|
|
523
|
-
repoFullName
|
|
524
|
-
});
|
|
525
|
-
const res = await fetch(`${this.baseUrl}/github/connect-authorize-url?${params}`, { headers: { ...this.authHeaders } });
|
|
526
|
-
if (!res.ok) await throwPlatformApiError("Failed to start GitHub connect authorization", res);
|
|
527
|
-
return res.json();
|
|
528
|
-
}
|
|
529
|
-
async getGithubConnection(projectId) {
|
|
530
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/github`, { headers: { ...this.authHeaders } });
|
|
531
|
-
if (!res.ok) await throwPlatformApiError("Failed to get GitHub connection", res);
|
|
532
|
-
return res.json();
|
|
533
|
-
}
|
|
534
|
-
async updateGithubConnection(projectId, body) {
|
|
535
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/github`, {
|
|
536
|
-
method: "PATCH",
|
|
537
|
-
headers: {
|
|
538
|
-
"Content-Type": "application/json",
|
|
539
|
-
...this.authHeaders
|
|
540
|
-
},
|
|
541
|
-
body: JSON.stringify(body)
|
|
542
|
-
});
|
|
543
|
-
if (!res.ok) await throwPlatformApiError("Failed to update GitHub connection", res);
|
|
544
|
-
return res.json();
|
|
545
|
-
}
|
|
546
|
-
async deleteGithubConnection(projectId) {
|
|
547
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/github`, {
|
|
548
|
-
method: "DELETE",
|
|
549
|
-
headers: { ...this.authHeaders }
|
|
550
|
-
});
|
|
551
|
-
if (!res.ok) await throwPlatformApiError("Failed to disconnect GitHub repository", res);
|
|
552
|
-
return res.json();
|
|
553
|
-
}
|
|
554
|
-
async getGithubLinkUrl(redirect, codeChallenge) {
|
|
555
|
-
const params = new URLSearchParams({
|
|
556
|
-
redirect,
|
|
557
|
-
code_challenge: codeChallenge,
|
|
558
|
-
code_challenge_method: "S256"
|
|
559
|
-
});
|
|
560
|
-
const res = await fetch(`${this.baseUrl}/github/link-url?${params}`, { headers: { ...this.authHeaders } });
|
|
561
|
-
if (!res.ok) await throwPlatformApiError("Failed to start GitHub link", res);
|
|
562
|
-
return res.json();
|
|
563
|
-
}
|
|
564
|
-
async completeGithubLink(linkCode, codeVerifier) {
|
|
565
|
-
const res = await fetch(`${this.baseUrl}/github/link/complete`, {
|
|
566
|
-
method: "POST",
|
|
567
|
-
headers: {
|
|
568
|
-
"Content-Type": "application/json",
|
|
569
|
-
...this.authHeaders
|
|
570
|
-
},
|
|
571
|
-
body: JSON.stringify({
|
|
572
|
-
link_code: linkCode,
|
|
573
|
-
code_verifier: codeVerifier
|
|
574
|
-
})
|
|
575
|
-
});
|
|
576
|
-
if (!res.ok) await throwPlatformApiError("Failed to complete GitHub link", res);
|
|
577
|
-
return res.json();
|
|
578
|
-
}
|
|
579
|
-
async getGithubJoinUrl(redirect, codeChallenge) {
|
|
580
|
-
const params = new URLSearchParams({
|
|
581
|
-
redirect,
|
|
582
|
-
code_challenge: codeChallenge,
|
|
583
|
-
code_challenge_method: "S256"
|
|
584
|
-
});
|
|
585
|
-
const res = await fetch(`${this.baseUrl}/github/join-url?${params}`, { headers: { ...this.authHeaders } });
|
|
586
|
-
if (!res.ok) await throwPlatformApiError("Failed to start GitHub join", res);
|
|
587
|
-
return res.json();
|
|
588
|
-
}
|
|
589
|
-
async completeGithubJoin(joinCode, codeVerifier) {
|
|
590
|
-
const res = await fetch(`${this.baseUrl}/github/join/complete`, {
|
|
591
|
-
method: "POST",
|
|
592
|
-
headers: {
|
|
593
|
-
"Content-Type": "application/json",
|
|
594
|
-
...this.authHeaders
|
|
595
|
-
},
|
|
596
|
-
body: JSON.stringify({
|
|
597
|
-
join_code: joinCode,
|
|
598
|
-
code_verifier: codeVerifier
|
|
599
|
-
})
|
|
600
|
-
});
|
|
601
|
-
if (!res.ok) await throwPlatformApiError("Failed to complete GitHub join", res);
|
|
602
|
-
return res.json();
|
|
603
|
-
}
|
|
604
|
-
async getEmailUsage(projectId) {
|
|
605
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/email/usage`, { headers: { ...this.authHeaders } });
|
|
606
|
-
if (!res.ok) await throwPlatformApiError("Failed to fetch email usage", res);
|
|
607
|
-
return res.json();
|
|
608
|
-
}
|
|
609
|
-
async getEmailLogs(projectId, limit) {
|
|
610
|
-
const url = new URL(`${this.baseUrl}/projects/${projectId}/email/logs`);
|
|
611
|
-
if (limit !== void 0) url.searchParams.set("limit", String(limit));
|
|
612
|
-
const res = await fetch(url, { headers: { ...this.authHeaders } });
|
|
613
|
-
if (!res.ok) await throwPlatformApiError("Failed to fetch email logs", res);
|
|
614
|
-
return res.json();
|
|
615
|
-
}
|
|
616
|
-
async listEmailDestinations(projectId) {
|
|
617
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/email/destinations`, { headers: { ...this.authHeaders } });
|
|
618
|
-
if (!res.ok) await throwPlatformApiError("Failed to list email destinations", res);
|
|
619
|
-
return res.json();
|
|
620
|
-
}
|
|
621
|
-
async addEmailDestination(projectId, address) {
|
|
622
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/email/destinations`, {
|
|
623
|
-
method: "POST",
|
|
624
|
-
headers: {
|
|
625
|
-
"Content-Type": "application/json",
|
|
626
|
-
...this.authHeaders
|
|
627
|
-
},
|
|
628
|
-
body: JSON.stringify({ address })
|
|
629
|
-
});
|
|
630
|
-
if (!res.ok) await throwPlatformApiError("Failed to add email destination", res);
|
|
631
|
-
return {
|
|
632
|
-
...await res.json(),
|
|
633
|
-
created: res.status === 201
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
async removeEmailDestination(projectId, destinationId) {
|
|
637
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/email/destinations/${destinationId}`, {
|
|
638
|
-
method: "DELETE",
|
|
639
|
-
headers: { ...this.authHeaders }
|
|
640
|
-
});
|
|
641
|
-
if (!res.ok) await throwPlatformApiError("Failed to remove email destination", res);
|
|
642
|
-
return res.json();
|
|
643
|
-
}
|
|
644
|
-
async listEmailDomains(projectId) {
|
|
645
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/email/domains`, { headers: { ...this.authHeaders } });
|
|
646
|
-
if (!res.ok) await throwPlatformApiError("Failed to list email domains", res);
|
|
647
|
-
return res.json();
|
|
648
|
-
}
|
|
649
|
-
/**
|
|
650
|
-
* `domain add` sends the credential exactly once — the OAuth grant's
|
|
651
|
-
* refresh token xor a pasted API token (the api enforces exactly one). It
|
|
652
|
-
* is the only call that ever carries it: the platform stores it encrypted
|
|
653
|
-
* per project, and every later step (`status`, `sync`, `remove`) addresses
|
|
654
|
-
* the row by domain. The caller must not log the input.
|
|
655
|
-
*/
|
|
656
|
-
async addEmailDomain(projectId, input) {
|
|
657
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/email/domains`, {
|
|
658
|
-
method: "POST",
|
|
659
|
-
headers: {
|
|
660
|
-
"Content-Type": "application/json",
|
|
661
|
-
...this.authHeaders
|
|
662
|
-
},
|
|
663
|
-
body: JSON.stringify({
|
|
664
|
-
domain: input.domain,
|
|
665
|
-
...input.refreshToken !== void 0 ? { refreshToken: input.refreshToken } : {},
|
|
666
|
-
...input.apiToken !== void 0 ? { apiToken: input.apiToken } : {},
|
|
667
|
-
...input.attemptId !== void 0 ? { attemptId: input.attemptId } : {}
|
|
668
|
-
})
|
|
669
|
-
});
|
|
670
|
-
if (!res.ok) await throwPlatformApiError("Failed to add email domain", res);
|
|
671
|
-
return res.json();
|
|
672
|
-
}
|
|
673
|
-
async getEmailDomain(projectId, domain) {
|
|
674
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/email/domains/${encodeURIComponent(domain)}`, { headers: { ...this.authHeaders } });
|
|
675
|
-
if (!res.ok) await throwPlatformApiError("Failed to fetch email domain status", res);
|
|
676
|
-
return res.json();
|
|
677
|
-
}
|
|
678
|
-
/** Redeploys the relay at the current version and rotates its secret (0078, `sync`). */
|
|
679
|
-
async syncEmailDomain(projectId, domain) {
|
|
680
|
-
const res = await fetch(`${this.baseUrl}/projects/${projectId}/email/domains/${encodeURIComponent(domain)}/sync`, {
|
|
681
|
-
method: "POST",
|
|
682
|
-
headers: { ...this.authHeaders }
|
|
683
|
-
});
|
|
684
|
-
if (!res.ok) await throwPlatformApiError("Failed to sync email domain", res);
|
|
685
|
-
return res.json();
|
|
686
|
-
}
|
|
687
|
-
/**
|
|
688
|
-
* `force` appends `?force=1`: the api drops the row even when a
|
|
689
|
-
* Cloudflare-side cleanup step fails (and answers `forced: true` +
|
|
690
|
-
* `failed_steps`). Without it a CF-side failure keeps the row for retry
|
|
691
|
-
* (`removed: false`).
|
|
692
|
-
*/
|
|
693
|
-
async removeEmailDomain(projectId, domain, force) {
|
|
694
|
-
const url = new URL(`${this.baseUrl}/projects/${projectId}/email/domains/${encodeURIComponent(domain)}`);
|
|
695
|
-
if (force) url.searchParams.set("force", "1");
|
|
696
|
-
const res = await fetch(url, {
|
|
697
|
-
method: "DELETE",
|
|
698
|
-
headers: { ...this.authHeaders }
|
|
699
|
-
});
|
|
700
|
-
if (!res.ok) await throwPlatformApiError("Failed to remove email domain", res);
|
|
701
|
-
return res.json();
|
|
702
|
-
}
|
|
703
|
-
};
|
|
704
|
-
//#endregion
|
|
705
|
-
export { isExpiredTokenError as a, isCliOutdatedError as i, PlatformClient as n, parsePlatformErrorBody as o, client_exports as r, PlatformApiError as t };
|