@vertracloud/blob 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +169 -2
- package/dist/index.cjs +1097 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +293 -0
- package/dist/index.d.ts +293 -0
- package/dist/index.js +1063 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -5
package/dist/index.js
ADDED
|
@@ -0,0 +1,1063 @@
|
|
|
1
|
+
// node_modules/@vertracloud/api-types/v1.mjs
|
|
2
|
+
var API_KEY_SCOPES = {
|
|
3
|
+
"apps:read": {
|
|
4
|
+
group: "apps",
|
|
5
|
+
routes: [
|
|
6
|
+
{ method: "GET", path: "/v1/apps/runtimes" },
|
|
7
|
+
{ method: "GET", path: "/v1/apps/status" },
|
|
8
|
+
{ method: "GET", path: "/v1/apps/:id" },
|
|
9
|
+
{ method: "GET", path: "/v1/apps/:id/status" },
|
|
10
|
+
{ method: "GET", path: "/v1/apps/:id/realtime" },
|
|
11
|
+
{ method: "GET", path: "/v1/apps/:id/metrics" },
|
|
12
|
+
{ method: "GET", path: "/v1/apps/:id/logs" },
|
|
13
|
+
{ method: "GET", path: "/v1/apps/:id/download" },
|
|
14
|
+
{ method: "GET", path: "/v1/apps/:id/deploys" },
|
|
15
|
+
{ method: "GET", path: "/v1/apps/:id/deploys/webhook" },
|
|
16
|
+
{ method: "GET", path: "/v1/apps/:id/releases" },
|
|
17
|
+
{ method: "GET", path: "/v1/apps/:id/releases/:release_id/log" },
|
|
18
|
+
{ method: "GET", path: "/v1/apps/:id/network/custom" },
|
|
19
|
+
{ method: "GET", path: "/v1/apps/:id/network/dns" }
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
"apps:write": {
|
|
23
|
+
group: "apps",
|
|
24
|
+
routes: [
|
|
25
|
+
{ method: "POST", path: "/v1/apps" },
|
|
26
|
+
{ method: "POST", path: "/v1/apps/:id/start" },
|
|
27
|
+
{ method: "POST", path: "/v1/apps/:id/stop" },
|
|
28
|
+
{ method: "POST", path: "/v1/apps/:id/restart" },
|
|
29
|
+
{ method: "PATCH", path: "/v1/apps/:id/config" },
|
|
30
|
+
{ method: "POST", path: "/v1/apps/:id/deploys/webhook" },
|
|
31
|
+
{ method: "DELETE", path: "/v1/apps/:id/deploys/webhook" },
|
|
32
|
+
{ method: "POST", path: "/v1/apps/:id/network/custom" },
|
|
33
|
+
{ method: "DELETE", path: "/v1/apps/:id/network/custom" },
|
|
34
|
+
{ method: "POST", path: "/v1/apps/:id/network/purge-cache" },
|
|
35
|
+
{ method: "PATCH", path: "/v1/apps/:id/network/subdomain" },
|
|
36
|
+
{ method: "POST", path: "/v1/apps/:id/network/publish" },
|
|
37
|
+
{ method: "DELETE", path: "/v1/apps/:id/network/publish" },
|
|
38
|
+
{ method: "POST", path: "/v1/apps/:id/releases/:release_id/rollback" }
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"apps:delete": { group: "apps", routes: [{ method: "DELETE", path: "/v1/apps/:id" }] },
|
|
42
|
+
"apps:envs": {
|
|
43
|
+
group: "apps",
|
|
44
|
+
routes: [
|
|
45
|
+
{ method: "GET", path: "/v1/apps/:id/envs" },
|
|
46
|
+
{ method: "POST", path: "/v1/apps/:id/envs" },
|
|
47
|
+
{ method: "DELETE", path: "/v1/apps/:id/envs/:envId" },
|
|
48
|
+
{ method: "GET", path: "/v1/apps/:id/releases/:release_id/rollback" }
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"apps:files": {
|
|
52
|
+
group: "apps",
|
|
53
|
+
routes: [
|
|
54
|
+
{ method: "GET", path: "/v1/apps/:id/files" },
|
|
55
|
+
{ method: "GET", path: "/v1/apps/:id/files/tree" },
|
|
56
|
+
{ method: "GET", path: "/v1/apps/:id/files/content" },
|
|
57
|
+
{ method: "PUT", path: "/v1/apps/:id/files" },
|
|
58
|
+
{ method: "PATCH", path: "/v1/apps/:id/files" },
|
|
59
|
+
{ method: "DELETE", path: "/v1/apps/:id/files" },
|
|
60
|
+
{ method: "POST", path: "/v1/apps/:id/files/upload" }
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
"databases:read": {
|
|
64
|
+
group: "databases",
|
|
65
|
+
routes: [
|
|
66
|
+
{ method: "GET", path: "/v1/databases/status" },
|
|
67
|
+
{ method: "GET", path: "/v1/databases/:id" },
|
|
68
|
+
{ method: "GET", path: "/v1/databases/:id/status" },
|
|
69
|
+
{ method: "GET", path: "/v1/databases/:id/metrics" }
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
"databases:write": {
|
|
73
|
+
group: "databases",
|
|
74
|
+
routes: [
|
|
75
|
+
{ method: "POST", path: "/v1/databases" },
|
|
76
|
+
{ method: "PUT", path: "/v1/databases/:id" },
|
|
77
|
+
{ method: "POST", path: "/v1/databases/:id/start" },
|
|
78
|
+
{ method: "POST", path: "/v1/databases/:id/stop" },
|
|
79
|
+
{ method: "POST", path: "/v1/databases/:id/reset" }
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
"databases:delete": { group: "databases", routes: [{ method: "DELETE", path: "/v1/databases/:id" }] },
|
|
83
|
+
"databases:credentials": {
|
|
84
|
+
group: "databases",
|
|
85
|
+
routes: [
|
|
86
|
+
{ method: "GET", path: "/v1/databases/:id/credentials/certificate" },
|
|
87
|
+
{ method: "POST", path: "/v1/databases/:id/credentials/certificate/reset" },
|
|
88
|
+
{ method: "POST", path: "/v1/databases/:id/credentials/reset" }
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
"snapshots:read": {
|
|
92
|
+
group: "snapshots",
|
|
93
|
+
routes: [
|
|
94
|
+
{ method: "GET", path: "/v1/users/snapshots" },
|
|
95
|
+
{ method: "GET", path: "/v1/users/:id/snapshots" },
|
|
96
|
+
{ method: "GET", path: "/v1/users/:id/snapshots/:snapshot_id/download" }
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
"snapshots:write": {
|
|
100
|
+
group: "snapshots",
|
|
101
|
+
routes: [
|
|
102
|
+
{ method: "POST", path: "/v1/users/:id/snapshots" },
|
|
103
|
+
{ method: "POST", path: "/v1/users/:id/snapshots/:snapshot_id/restore" }
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
"account:read": {
|
|
107
|
+
group: "account",
|
|
108
|
+
routes: [
|
|
109
|
+
{ method: "GET", path: "/v1/users/me" },
|
|
110
|
+
{ method: "GET", path: "/v1/users/me/sessions" }
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
"account:write": {
|
|
114
|
+
group: "account",
|
|
115
|
+
routes: [
|
|
116
|
+
{ method: "PATCH", path: "/v1/users/me" },
|
|
117
|
+
{ method: "POST", path: "/v1/users/me/folders" },
|
|
118
|
+
{ method: "PATCH", path: "/v1/users/me/folders/:folder_id" },
|
|
119
|
+
{ method: "DELETE", path: "/v1/users/me/folders/:folder_id" },
|
|
120
|
+
{ method: "PUT", path: "/v1/users/me/folders/:folder_id/resources/:resource_type/:resource_id" },
|
|
121
|
+
{ method: "DELETE", path: "/v1/users/me/folders/:folder_id/resources/:resource_type/:resource_id" },
|
|
122
|
+
{ method: "PUT", path: "/v1/users/me/favorites/:resource_type/:resource_id" },
|
|
123
|
+
{ method: "DELETE", path: "/v1/users/me/favorites/:resource_type/:resource_id" }
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
"workspaces:read": {
|
|
127
|
+
group: "workspaces",
|
|
128
|
+
routes: [
|
|
129
|
+
{ method: "GET", path: "/v1/workspaces" },
|
|
130
|
+
{ method: "GET", path: "/v1/workspaces/:id" },
|
|
131
|
+
{ method: "GET", path: "/v1/workspaces/:id/members" },
|
|
132
|
+
{ method: "GET", path: "/v1/workspaces/:id/roles" },
|
|
133
|
+
{ method: "GET", path: "/v1/workspaces/:id/action-requests" }
|
|
134
|
+
]
|
|
135
|
+
},
|
|
136
|
+
"workspaces:write": {
|
|
137
|
+
group: "workspaces",
|
|
138
|
+
routes: [
|
|
139
|
+
{ method: "POST", path: "/v1/workspaces" },
|
|
140
|
+
{ method: "PUT", path: "/v1/workspaces/:id" },
|
|
141
|
+
{ method: "PUT", path: "/v1/workspaces/:id/members/:user_id" },
|
|
142
|
+
{ method: "DELETE", path: "/v1/workspaces/:id/members/:user_id" },
|
|
143
|
+
{ method: "POST", path: "/v1/workspaces/:id/roles" },
|
|
144
|
+
{ method: "PUT", path: "/v1/workspaces/:id/roles/:role_id" },
|
|
145
|
+
{ method: "DELETE", path: "/v1/workspaces/:id/roles/:role_id" },
|
|
146
|
+
{ method: "POST", path: "/v1/workspaces/:id/apps/:app_id" },
|
|
147
|
+
{ method: "DELETE", path: "/v1/workspaces/:id/apps/:app_id" },
|
|
148
|
+
{ method: "POST", path: "/v1/workspaces/:id/databases/:db_id" },
|
|
149
|
+
{ method: "DELETE", path: "/v1/workspaces/:id/databases/:db_id" },
|
|
150
|
+
{ method: "POST", path: "/v1/workspaces/:id/folders" },
|
|
151
|
+
{ method: "PATCH", path: "/v1/workspaces/:id/folders/:folder_id" },
|
|
152
|
+
{ method: "DELETE", path: "/v1/workspaces/:id/folders/:folder_id" },
|
|
153
|
+
{ method: "PUT", path: "/v1/workspaces/:id/folders/:folder_id/resources/:resource_type/:resource_id" },
|
|
154
|
+
{ method: "DELETE", path: "/v1/workspaces/:id/folders/:folder_id/resources/:resource_type/:resource_id" },
|
|
155
|
+
{ method: "PUT", path: "/v1/workspaces/:id/favorites/:resource_type/:resource_id" },
|
|
156
|
+
{ method: "DELETE", path: "/v1/workspaces/:id/favorites/:resource_type/:resource_id" },
|
|
157
|
+
{ method: "POST", path: "/v1/workspaces/:id/action-requests" }
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
/**
|
|
161
|
+
* Intentionally left out (dashboard session only): creating invites (a leaked key could grant
|
|
162
|
+
* access that outlives its revocation), transferring ownership, approving/rejecting action
|
|
163
|
+
* requests (approval is a human decision) and exporting activities.
|
|
164
|
+
*/
|
|
165
|
+
/** Delete a workspace (soft delete). Only in the `full` preset, like `apps:delete`. */
|
|
166
|
+
"workspaces:delete": {
|
|
167
|
+
group: "workspaces",
|
|
168
|
+
routes: [{ method: "DELETE", path: "/v1/workspaces/:id" }]
|
|
169
|
+
},
|
|
170
|
+
/** List and revoke workspace invites; view, accept and decline your own invite by token. Only in the `full` preset. */
|
|
171
|
+
"workspaces:invites": {
|
|
172
|
+
group: "workspaces",
|
|
173
|
+
routes: [
|
|
174
|
+
{ method: "GET", path: "/v1/workspaces/:id/invites" },
|
|
175
|
+
{ method: "DELETE", path: "/v1/workspaces/:id/invites/:invite_id" },
|
|
176
|
+
{ method: "GET", path: "/v1/workspaces/invites/:token" },
|
|
177
|
+
{ method: "POST", path: "/v1/workspaces/invites/:token/accept" },
|
|
178
|
+
{ method: "POST", path: "/v1/workspaces/invites/:token/decline" }
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
/** Blob Storage (`blob.vertracloud.app`). Upload tokens (`vcup_`) are a separate credential, not a scope. */
|
|
182
|
+
"blob:read": {
|
|
183
|
+
group: "blob",
|
|
184
|
+
routes: [
|
|
185
|
+
{ method: "GET", path: "/v1/blob/objects" },
|
|
186
|
+
{ method: "GET", path: "/v1/blob/objects/:object_id" },
|
|
187
|
+
{ method: "GET", path: "/v1/blob/objects/:object_id/download" },
|
|
188
|
+
{ method: "GET", path: "/v1/blob/shares" },
|
|
189
|
+
{ method: "GET", path: "/v1/blob/rules" },
|
|
190
|
+
{ method: "GET", path: "/v1/blob/stats" },
|
|
191
|
+
{ method: "GET", path: "/v1/blob/s3/credentials" },
|
|
192
|
+
{ method: "GET", path: "/v1/blob/domains" }
|
|
193
|
+
]
|
|
194
|
+
},
|
|
195
|
+
"blob:write": {
|
|
196
|
+
group: "blob",
|
|
197
|
+
routes: [
|
|
198
|
+
{ method: "POST", path: "/v1/blob/objects" },
|
|
199
|
+
{ method: "PATCH", path: "/v1/blob/objects" },
|
|
200
|
+
{ method: "PATCH", path: "/v1/blob/objects/:object_id" },
|
|
201
|
+
{ method: "POST", path: "/v1/blob/objects/copy" },
|
|
202
|
+
{ method: "DELETE", path: "/v1/blob/objects" },
|
|
203
|
+
{ method: "DELETE", path: "/v1/blob/objects/:object_id" },
|
|
204
|
+
{ method: "POST", path: "/v1/blob/uploads" },
|
|
205
|
+
{ method: "GET", path: "/v1/blob/uploads" },
|
|
206
|
+
{ method: "GET", path: "/v1/blob/uploads/:upload_id" },
|
|
207
|
+
{ method: "POST", path: "/v1/blob/uploads/:upload_id/parts" },
|
|
208
|
+
{ method: "POST", path: "/v1/blob/uploads/:upload_id/complete" },
|
|
209
|
+
{ method: "DELETE", path: "/v1/blob/uploads/:upload_id" },
|
|
210
|
+
{ method: "POST", path: "/v1/blob/shares" },
|
|
211
|
+
{ method: "DELETE", path: "/v1/blob/shares/:share_id" },
|
|
212
|
+
{ method: "PUT", path: "/v1/blob/rules" },
|
|
213
|
+
{ method: "PUT", path: "/v1/blob/rules/:prefix" },
|
|
214
|
+
{ method: "DELETE", path: "/v1/blob/rules/:prefix" },
|
|
215
|
+
{ method: "POST", path: "/v1/blob/upload-tokens" },
|
|
216
|
+
{ method: "POST", path: "/v1/blob/domains" },
|
|
217
|
+
{ method: "POST", path: "/v1/blob/domains/:domain_id/verify" },
|
|
218
|
+
{ method: "PATCH", path: "/v1/blob/domains/:domain_id" },
|
|
219
|
+
{ method: "DELETE", path: "/v1/blob/domains/:domain_id" }
|
|
220
|
+
]
|
|
221
|
+
},
|
|
222
|
+
"billing:read": {
|
|
223
|
+
group: "billing",
|
|
224
|
+
routes: [
|
|
225
|
+
{ method: "GET", path: "/v1/orders" },
|
|
226
|
+
{ method: "GET", path: "/v1/orders/:orderId/status" },
|
|
227
|
+
{ method: "GET", path: "/v1/users/me/billing" }
|
|
228
|
+
]
|
|
229
|
+
},
|
|
230
|
+
/**
|
|
231
|
+
* Creates a plan order and generates the PIX charge; the payment itself is made by a person in their banking app.
|
|
232
|
+
* Also saves the billing details (name, address, CPF/CNPJ) an order needs.
|
|
233
|
+
*/
|
|
234
|
+
"billing:write": {
|
|
235
|
+
group: "billing",
|
|
236
|
+
routes: [
|
|
237
|
+
{ method: "POST", path: "/v1/orders" },
|
|
238
|
+
{ method: "POST", path: "/v1/orders/:orderId/initiate/pix" },
|
|
239
|
+
{ method: "PUT", path: "/v1/users/me/billing" }
|
|
240
|
+
]
|
|
241
|
+
},
|
|
242
|
+
"redeem:write": { group: "billing", routes: [{ method: "POST", path: "/v1/redeem/:code" }] }
|
|
243
|
+
};
|
|
244
|
+
var API_KEY_SCOPE_LIST = Object.keys(API_KEY_SCOPES);
|
|
245
|
+
var WORKSPACE_PERMISSIONS = {
|
|
246
|
+
"apps:read": { group: "apps" },
|
|
247
|
+
"apps:manage": { group: "apps" },
|
|
248
|
+
"apps:lifecycle": { group: "apps" },
|
|
249
|
+
"apps:envs": { group: "apps" },
|
|
250
|
+
"apps:files": { group: "apps" },
|
|
251
|
+
"apps:delete": { group: "apps" },
|
|
252
|
+
"databases:read": { group: "databases" },
|
|
253
|
+
"databases:manage": { group: "databases" },
|
|
254
|
+
"databases:lifecycle": { group: "databases" },
|
|
255
|
+
"databases:credentials": { group: "databases" },
|
|
256
|
+
"databases:data": { group: "databases" },
|
|
257
|
+
"databases:delete": { group: "databases" },
|
|
258
|
+
"snapshots:read": { group: "snapshots" },
|
|
259
|
+
"snapshots:manage": { group: "snapshots" },
|
|
260
|
+
"blob:read": { group: "blob" },
|
|
261
|
+
"blob:manage": { group: "blob" },
|
|
262
|
+
"members:read": { group: "members" },
|
|
263
|
+
"members:manage": { group: "members" },
|
|
264
|
+
"roles:manage": { group: "roles" },
|
|
265
|
+
"activities:read": { group: "activities" }
|
|
266
|
+
};
|
|
267
|
+
var WORKSPACE_PERMISSION_LIST = Object.keys(WORKSPACE_PERMISSIONS);
|
|
268
|
+
var WorkspaceFolderColor = { Neutral: "neutral", Red: "red", Orange: "orange", Yellow: "yellow", Green: "green", Blue: "blue", Purple: "purple" };
|
|
269
|
+
var WORKSPACE_FOLDER_COLORS = Object.values(WorkspaceFolderColor);
|
|
270
|
+
var WORKSPACE_ERROR_CODES = {
|
|
271
|
+
WORKSPACE_NOT_FOUND: "WORKSPACE_NOT_FOUND",
|
|
272
|
+
WORKSPACE_NAME_ALREADY_EXISTS: "WORKSPACE_NAME_ALREADY_EXISTS",
|
|
273
|
+
WORKSPACE_MEMBER_LIMIT_REACHED: "WORKSPACE_MEMBER_LIMIT_REACHED",
|
|
274
|
+
WORKSPACE_ROLE_LIMIT_REACHED: "WORKSPACE_ROLE_LIMIT_REACHED",
|
|
275
|
+
WORKSPACE_ROLE_IN_USE: "WORKSPACE_ROLE_IN_USE",
|
|
276
|
+
WORKSPACE_ROLE_NOT_FOUND: "WORKSPACE_ROLE_NOT_FOUND",
|
|
277
|
+
WORKSPACE_ROLE_NAME_ALREADY_EXISTS: "WORKSPACE_ROLE_NAME_ALREADY_EXISTS",
|
|
278
|
+
WORKSPACE_PERMISSION_DENIED: "WORKSPACE_PERMISSION_DENIED",
|
|
279
|
+
WORKSPACE_PERMISSION_ESCALATION: "WORKSPACE_PERMISSION_ESCALATION",
|
|
280
|
+
WORKSPACE_INVITE_NOT_FOUND: "WORKSPACE_INVITE_NOT_FOUND",
|
|
281
|
+
WORKSPACE_INVITE_EXPIRED: "WORKSPACE_INVITE_EXPIRED",
|
|
282
|
+
WORKSPACE_INVITE_EMAIL_MISMATCH: "WORKSPACE_INVITE_EMAIL_MISMATCH",
|
|
283
|
+
/** 400 — the invited account turned off workspace invites by email. */
|
|
284
|
+
WORKSPACE_INVITES_DISABLED: "WORKSPACE_INVITES_DISABLED",
|
|
285
|
+
/** 409 — the account is already the owner or a member of the workspace. */
|
|
286
|
+
WORKSPACE_ALREADY_MEMBER: "WORKSPACE_ALREADY_MEMBER",
|
|
287
|
+
WORKSPACE_OWNER_ONLY: "WORKSPACE_OWNER_ONLY",
|
|
288
|
+
WORKSPACE_ACTION_REQUEST_NOT_FOUND: "WORKSPACE_ACTION_REQUEST_NOT_FOUND",
|
|
289
|
+
/** 410 — the request expired before anyone decided on it. */
|
|
290
|
+
WORKSPACE_ACTION_REQUEST_EXPIRED: "WORKSPACE_ACTION_REQUEST_EXPIRED",
|
|
291
|
+
/** 409 — the request was already approved or rejected. */
|
|
292
|
+
WORKSPACE_ACTION_REQUEST_ALREADY_DECIDED: "WORKSPACE_ACTION_REQUEST_ALREADY_DECIDED",
|
|
293
|
+
/** 400 — the requester already has the permission; perform the action directly. */
|
|
294
|
+
WORKSPACE_ACTION_NOT_NEEDED: "WORKSPACE_ACTION_NOT_NEEDED",
|
|
295
|
+
/** 400 — the request `params` failed the action's validation (e.g. a snapshot of another resource). */
|
|
296
|
+
WORKSPACE_ACTION_INVALID_PARAMS: "WORKSPACE_ACTION_INVALID_PARAMS",
|
|
297
|
+
/** 409 — a pending request already exists for the same action on the same resource. */
|
|
298
|
+
WORKSPACE_ACTION_REQUEST_PENDING: "WORKSPACE_ACTION_REQUEST_PENDING",
|
|
299
|
+
WORKSPACE_RESOURCE_FOLDER_NOT_FOUND: "WORKSPACE_RESOURCE_FOLDER_NOT_FOUND",
|
|
300
|
+
WORKSPACE_RESOURCE_FOLDER_NAME_CONFLICT: "WORKSPACE_RESOURCE_FOLDER_NAME_CONFLICT",
|
|
301
|
+
WORKSPACE_RESOURCE_NOT_FOUND: "WORKSPACE_RESOURCE_NOT_FOUND",
|
|
302
|
+
PLAN_RESTRICTED_FEATURE: "PLAN_RESTRICTED_FEATURE"
|
|
303
|
+
};
|
|
304
|
+
var API_ERROR_CODES = {
|
|
305
|
+
ACCESS_DENIED: "ACCESS_DENIED",
|
|
306
|
+
API_KEY_INVALID: "API_KEY_INVALID",
|
|
307
|
+
// Key management (`/v1/users/me/api-keys`) — dashboard-session routes.
|
|
308
|
+
API_KEY_NOT_FOUND: "API_KEY_NOT_FOUND",
|
|
309
|
+
API_KEY_LIMIT_REACHED: "API_KEY_LIMIT_REACHED",
|
|
310
|
+
API_KEY_INVALID_NAME: "API_KEY_INVALID_NAME",
|
|
311
|
+
API_KEY_INVALID_SCOPES: "API_KEY_INVALID_SCOPES",
|
|
312
|
+
API_KEY_INVALID_IPS: "API_KEY_INVALID_IPS",
|
|
313
|
+
API_KEY_IP_DENIED: "API_KEY_IP_DENIED",
|
|
314
|
+
API_KEY_SCOPE_DENIED: "API_KEY_SCOPE_DENIED",
|
|
315
|
+
/** 403 — the route only accepts a dashboard session; API keys are refused. */
|
|
316
|
+
WEBSITE_ONLY: "WEBSITE_ONLY",
|
|
317
|
+
AUTH_TOKEN_MISSING: "AUTH_TOKEN_MISSING",
|
|
318
|
+
ACCOUNT_BANNED: "ACCOUNT_BANNED",
|
|
319
|
+
APP_SHIELD_COOLDOWN: "APP_SHIELD_COOLDOWN",
|
|
320
|
+
APP_BANNED: "APP_BANNED",
|
|
321
|
+
APP_ALREADY_PUBLISHED: "APP_ALREADY_PUBLISHED",
|
|
322
|
+
APP_HAS_NO_SUBDOMAIN: "APP_HAS_NO_SUBDOMAIN",
|
|
323
|
+
APP_NOT_FOUND: "APP_NOT_FOUND",
|
|
324
|
+
APP_NOT_PUBLISHED: "APP_NOT_PUBLISHED",
|
|
325
|
+
APP_ALREADY_IN_WORKSPACE: "APP_ALREADY_IN_WORKSPACE",
|
|
326
|
+
APP_NOT_FOUND_IN_WORKSPACE: "APP_NOT_FOUND_IN_WORKSPACE",
|
|
327
|
+
/** 404 — the object does not exist or belongs to another account. */
|
|
328
|
+
BLOB_OBJECT_NOT_FOUND: "BLOB_OBJECT_NOT_FOUND",
|
|
329
|
+
/** 409 — the path is taken and `overwrite` is not set. */
|
|
330
|
+
BLOB_OBJECT_ALREADY_EXISTS: "BLOB_OBJECT_ALREADY_EXISTS",
|
|
331
|
+
/** 404 — the upload does not exist, is not yours, or is already completed or aborted. */
|
|
332
|
+
BLOB_UPLOAD_NOT_FOUND: "BLOB_UPLOAD_NOT_FOUND",
|
|
333
|
+
/** 410 — the upload passed its deadline. */
|
|
334
|
+
BLOB_UPLOAD_EXPIRED: "BLOB_UPLOAD_EXPIRED",
|
|
335
|
+
/** 409 — parts are missing or the stored size differs from the declared size. */
|
|
336
|
+
BLOB_UPLOAD_INCOMPLETE: "BLOB_UPLOAD_INCOMPLETE",
|
|
337
|
+
/** 409 — more uploads open than the plan allows. */
|
|
338
|
+
BLOB_UPLOAD_LIMIT: "BLOB_UPLOAD_LIMIT",
|
|
339
|
+
/** 400 — the SHA-256 sent does not match the file. */
|
|
340
|
+
BLOB_CHECKSUM_MISMATCH: "BLOB_CHECKSUM_MISMATCH",
|
|
341
|
+
/** 413 — the file exceeds the plan, single-request upload, folder rule or upload token limit. */
|
|
342
|
+
BLOB_FILE_TOO_LARGE: "BLOB_FILE_TOO_LARGE",
|
|
343
|
+
/** 403 — Blob Storage is suspended for the account (grace period, abuse or deletion). */
|
|
344
|
+
BLOB_NAMESPACE_SUSPENDED: "BLOB_NAMESPACE_SUSPENDED",
|
|
345
|
+
/** 401 — the upload token does not exist, expired or was revoked. */
|
|
346
|
+
BLOB_UPLOAD_TOKEN_INVALID: "BLOB_UPLOAD_TOKEN_INVALID",
|
|
347
|
+
/** 401 — the upload token has no uses left. */
|
|
348
|
+
BLOB_UPLOAD_TOKEN_EXHAUSTED: "BLOB_UPLOAD_TOKEN_EXHAUSTED",
|
|
349
|
+
/** 403 — upload tokens only work on the upload routes. */
|
|
350
|
+
BLOB_UPLOAD_TOKEN_NOT_ALLOWED: "BLOB_UPLOAD_TOKEN_NOT_ALLOWED",
|
|
351
|
+
/** 403 — the path, size or extension is outside what the upload token allows. */
|
|
352
|
+
BLOB_UPLOAD_TOKEN_CONSTRAINT: "BLOB_UPLOAD_TOKEN_CONSTRAINT",
|
|
353
|
+
/** 404 — the share does not exist, was revoked or expired. */
|
|
354
|
+
BLOB_SHARE_NOT_FOUND: "BLOB_SHARE_NOT_FOUND",
|
|
355
|
+
/** 410 — the share reached its download limit. */
|
|
356
|
+
BLOB_SHARE_EXHAUSTED: "BLOB_SHARE_EXHAUSTED",
|
|
357
|
+
/** 401 — wrong share password. */
|
|
358
|
+
BLOB_SHARE_PASSWORD_INVALID: "BLOB_SHARE_PASSWORD_INVALID",
|
|
359
|
+
/** 409 — the `If-Match` of the rule list is outdated; read it again. */
|
|
360
|
+
BLOB_RULES_CONFLICT: "BLOB_RULES_CONFLICT",
|
|
361
|
+
/** 409 — more folder rules than the plan allows. */
|
|
362
|
+
BLOB_RULE_LIMIT: "BLOB_RULE_LIMIT",
|
|
363
|
+
/** 409 — more custom domains than the plan allows. */
|
|
364
|
+
BLOB_CUSTOM_DOMAIN_LIMIT: "BLOB_CUSTOM_DOMAIN_LIMIT",
|
|
365
|
+
/** 409 — the hostname is already in use. */
|
|
366
|
+
BLOB_CUSTOM_DOMAIN_TAKEN: "BLOB_CUSTOM_DOMAIN_TAKEN",
|
|
367
|
+
/** 404 — the domain does not exist or belongs to another account. */
|
|
368
|
+
BLOB_CUSTOM_DOMAIN_NOT_FOUND: "BLOB_CUSTOM_DOMAIN_NOT_FOUND",
|
|
369
|
+
/** 422 — the file extension is blocked, outside the rule or token, or the content is an executable. */
|
|
370
|
+
BLOCKED_FILE_TYPE: "BLOCKED_FILE_TYPE",
|
|
371
|
+
/** 403 — the upload would exceed the storage quota. */
|
|
372
|
+
STORAGE_QUOTA_EXCEEDED: "STORAGE_QUOTA_EXCEEDED",
|
|
373
|
+
BUILD_FAILED: "BUILD_FAILED",
|
|
374
|
+
BUILD_TIMEOUT: "BUILD_TIMEOUT",
|
|
375
|
+
/** 400 — the order needs complete billing details for the service invoice (name, address and, in Brazil, CPF/CNPJ). Fill them with `PUT /v1/users/me/billing`. */
|
|
376
|
+
BILLING_DETAILS_INCOMPLETE: "BILLING_DETAILS_INCOMPLETE",
|
|
377
|
+
/** 400 — billing details failed validation; the field is in `details.path`. */
|
|
378
|
+
BILLING_DETAILS_INVALID: "BILLING_DETAILS_INVALID",
|
|
379
|
+
/** 402 — the bank refused the card charge. */
|
|
380
|
+
CARD_DECLINED: "CARD_DECLINED",
|
|
381
|
+
/** 400 — the plan can only be paid by PIX. */
|
|
382
|
+
CARD_NOT_ALLOWED_FOR_PLAN: "CARD_NOT_ALLOWED_FOR_PLAN",
|
|
383
|
+
CARD_NOT_FOUND: "CARD_NOT_FOUND",
|
|
384
|
+
CERTIFICATE_NOT_FOUND: "CERTIFICATE_NOT_FOUND",
|
|
385
|
+
CLEANUP_LANGUAGE_SAME_AS_CURRENT: "CLEANUP_LANGUAGE_SAME_AS_CURRENT",
|
|
386
|
+
CONTAINER_ALREADY_RUNNING: "CONTAINER_ALREADY_RUNNING",
|
|
387
|
+
/** 400 — no active coupon with this code. */
|
|
388
|
+
COUPON_INVALID: "COUPON_INVALID",
|
|
389
|
+
COUPON_INACTIVE: "COUPON_INACTIVE",
|
|
390
|
+
COUPON_NOT_STARTED: "COUPON_NOT_STARTED",
|
|
391
|
+
COUPON_EXPIRED: "COUPON_EXPIRED",
|
|
392
|
+
/** 400 — the coupon reached its total number of uses. */
|
|
393
|
+
COUPON_EXHAUSTED: "COUPON_EXHAUSTED",
|
|
394
|
+
/** 400 — the account already used this coupon as many times as allowed. */
|
|
395
|
+
COUPON_USER_LIMIT: "COUPON_USER_LIMIT",
|
|
396
|
+
/** 400 — the coupon is not available to this account. */
|
|
397
|
+
COUPON_AUDIENCE: "COUPON_AUDIENCE",
|
|
398
|
+
/** 400 — the coupon does not apply to this plan. */
|
|
399
|
+
COUPON_PLAN: "COUPON_PLAN",
|
|
400
|
+
/** 400 — the coupon does not apply to this billing period. */
|
|
401
|
+
COUPON_DURATION: "COUPON_DURATION",
|
|
402
|
+
COUPON_PAYMENT_METHOD: "COUPON_PAYMENT_METHOD",
|
|
403
|
+
CUSTOM_DOMAIN_RECORD_NOT_FOUND: "CUSTOM_DOMAIN_RECORD_NOT_FOUND",
|
|
404
|
+
CUSTOM_DOMAIN_REGISTRATION_FAILED: "CUSTOM_DOMAIN_REGISTRATION_FAILED",
|
|
405
|
+
DAILY_QUOTA_EXCEEDED: "DAILY_QUOTA_EXCEEDED",
|
|
406
|
+
DATABASE_NOT_FOUND: "DATABASE_NOT_FOUND",
|
|
407
|
+
DATABASE_NOT_RUNNING: "DATABASE_NOT_RUNNING",
|
|
408
|
+
DATABASE_ALREADY_IN_WORKSPACE: "DATABASE_ALREADY_IN_WORKSPACE",
|
|
409
|
+
DATABASE_NOT_FOUND_IN_WORKSPACE: "DATABASE_NOT_FOUND_IN_WORKSPACE",
|
|
410
|
+
DATABASE_PERSISTENCE_FAILED: "DATABASE_PERSISTENCE_FAILED",
|
|
411
|
+
DEPLOY_FAILED: "DEPLOY_FAILED",
|
|
412
|
+
DEPLOYMENT_NOT_FOUND: "DEPLOYMENT_NOT_FOUND",
|
|
413
|
+
DEPLOY_RATE_LIMITED: "DEPLOY_RATE_LIMITED",
|
|
414
|
+
DOMAIN_ALREADY_IN_USE: "DOMAIN_ALREADY_IN_USE",
|
|
415
|
+
ENV_LIMIT_EXCEEDED: "ENV_LIMIT_EXCEEDED",
|
|
416
|
+
ENV_DUPLICATE_KEY: "ENV_DUPLICATE_KEY",
|
|
417
|
+
ENV_NOT_FOUND: "ENV_NOT_FOUND",
|
|
418
|
+
FILE_TOO_LARGE: "FILE_TOO_LARGE",
|
|
419
|
+
FILE_MODIFIED: "FILE_MODIFIED",
|
|
420
|
+
FILE_NOT_FOUND: "FILE_NOT_FOUND",
|
|
421
|
+
FILE_OR_FOLDER_NOT_FOUND: "FILE_OR_FOLDER_NOT_FOUND",
|
|
422
|
+
FORBIDDEN: "FORBIDDEN",
|
|
423
|
+
FORBIDDEN_DOMAIN: "FORBIDDEN_DOMAIN",
|
|
424
|
+
FORBIDDEN_PATH: "FORBIDDEN_PATH",
|
|
425
|
+
FORBIDDEN_KEY: "FORBIDDEN_KEY",
|
|
426
|
+
FORCED_SNAPSHOT_RATE_LIMIT: "FORCED_SNAPSHOT_RATE_LIMIT",
|
|
427
|
+
FREE_PLAN_NO_WEB_PUBLISH: "FREE_PLAN_NO_WEB_PUBLISH",
|
|
428
|
+
/** 403 — the Free plan can no longer create applications or databases; a paid plan is required. */
|
|
429
|
+
FREE_PLAN_SUSPENDED: "FREE_PLAN_SUSPENDED",
|
|
430
|
+
INSTALLATION_NOT_FOUND: "INSTALLATION_NOT_FOUND",
|
|
431
|
+
INSTALL_SHIELD_BLOCKED: "INSTALL_SHIELD_BLOCKED",
|
|
432
|
+
INSTALL_TIMEOUT: "INSTALL_TIMEOUT",
|
|
433
|
+
INSTALL_FAILED: "INSTALL_FAILED",
|
|
434
|
+
INSTALL_CANCELLED: "INSTALL_CANCELLED",
|
|
435
|
+
INSTALL_OUT_OF_MEMORY: "INSTALL_OUT_OF_MEMORY",
|
|
436
|
+
INSTALL_QUEUE_FULL: "INSTALL_QUEUE_FULL",
|
|
437
|
+
INSUFFICIENT_MEMORY: "INSUFFICIENT_MEMORY",
|
|
438
|
+
INTERNAL_SERVER_ERROR: "INTERNAL_SERVER_ERROR",
|
|
439
|
+
INVALID_CONFIG: "INVALID_CONFIG",
|
|
440
|
+
INVALID_CUSTOM_DOMAIN_FORMAT: "INVALID_CUSTOM_DOMAIN_FORMAT",
|
|
441
|
+
INVALID_HOSTNAME_FOR_APP: "INVALID_HOSTNAME_FOR_APP",
|
|
442
|
+
INVALID_LANGUAGE: "INVALID_LANGUAGE",
|
|
443
|
+
INVALID_MEMORY_FORMAT: "INVALID_MEMORY_FORMAT",
|
|
444
|
+
INVALID_MONTHS: "INVALID_MONTHS",
|
|
445
|
+
INVALID_OR_EXPIRED_CODE: "INVALID_OR_EXPIRED_CODE",
|
|
446
|
+
INVALID_PARAMS: "INVALID_PARAMS",
|
|
447
|
+
INVALID_PAYLOAD: "INVALID_PAYLOAD",
|
|
448
|
+
INVALID_PATH: "INVALID_PATH",
|
|
449
|
+
INVALID_PLAN: "INVALID_PLAN",
|
|
450
|
+
INVALID_PLAN_NAME: "INVALID_PLAN_NAME",
|
|
451
|
+
INVALID_RAM_LIMIT: "INVALID_RAM_LIMIT",
|
|
452
|
+
INVALID_RANGE: "INVALID_RANGE",
|
|
453
|
+
INVALID_SUBDOMAIN_FORMAT: "INVALID_SUBDOMAIN_FORMAT",
|
|
454
|
+
INVALID_VERSION: "INVALID_VERSION",
|
|
455
|
+
INVALID_ZIP: "INVALID_ZIP",
|
|
456
|
+
INVALID_ZIP_STRUCTURE: "INVALID_ZIP_STRUCTURE",
|
|
457
|
+
MAIN_FILE_NOT_FOUND: "MAIN_FILE_NOT_FOUND",
|
|
458
|
+
MAX_APPS_REACHED: "MAX_APPS_REACHED",
|
|
459
|
+
MAX_DATABASES_REACHED: "MAX_DATABASES_REACHED",
|
|
460
|
+
MEMORY_BELOW_MINIMUM: "MEMORY_BELOW_MINIMUM",
|
|
461
|
+
MEMORY_LIMIT_EXCEEDED: "MEMORY_LIMIT_EXCEEDED",
|
|
462
|
+
MEMORY_MUST_BE_INTEGER: "MEMORY_MUST_BE_INTEGER",
|
|
463
|
+
MEMORY_REQUIRED: "MEMORY_REQUIRED",
|
|
464
|
+
MISSING_HEADERS: "MISSING_HEADERS",
|
|
465
|
+
MISSING_PLAN_OR_MONTHS: "MISSING_PLAN_OR_MONTHS",
|
|
466
|
+
MISSING_CONTENT: "MISSING_CONTENT",
|
|
467
|
+
NOT_AN_UPGRADE: "NOT_AN_UPGRADE",
|
|
468
|
+
NO_AUTHORIZED_PATHS: "NO_AUTHORIZED_PATHS",
|
|
469
|
+
NO_CHANGES_PROVIDED: "NO_CHANGES_PROVIDED",
|
|
470
|
+
NO_CUSTOM_DOMAIN: "NO_CUSTOM_DOMAIN",
|
|
471
|
+
NO_FIELDS_TO_UPDATE: "NO_FIELDS_TO_UPDATE",
|
|
472
|
+
NO_FILE_UPLOADED: "NO_FILE_UPLOADED",
|
|
473
|
+
OPERATION_IN_PROGRESS: "OPERATION_IN_PROGRESS",
|
|
474
|
+
ORDER_NOT_FOUND: "ORDER_NOT_FOUND",
|
|
475
|
+
ORDER_NOT_PAID: "ORDER_NOT_PAID",
|
|
476
|
+
ORDER_NOT_PAYABLE: "ORDER_NOT_PAYABLE",
|
|
477
|
+
ORDER_NOT_REFUNDED: "ORDER_NOT_REFUNDED",
|
|
478
|
+
/** 404 — the order has no payment receipt (unpaid, redeemed code or paid before receipts existed). */
|
|
479
|
+
ORDER_RECEIPT_UNAVAILABLE: "ORDER_RECEIPT_UNAVAILABLE",
|
|
480
|
+
OWNER_APP_WITHOUT_LIMITS: "OWNER_APP_WITHOUT_LIMITS",
|
|
481
|
+
PAYLOAD_TOO_LARGE: "PAYLOAD_TOO_LARGE",
|
|
482
|
+
/** 409 — a folder in the path is an existing file (writing `app/x.js` when `app` is a file). */
|
|
483
|
+
PATH_IS_FILE: "PATH_IS_FILE",
|
|
484
|
+
PENDING_ORDER_LIMIT_REACHED: "PENDING_ORDER_LIMIT_REACHED",
|
|
485
|
+
PAYMENT_METHOD_UNAVAILABLE: "PAYMENT_METHOD_UNAVAILABLE",
|
|
486
|
+
/** 403 — the plan does not include automatic rollback (`deploy.auto_rollback: true` below Intermediary). */
|
|
487
|
+
PLAN_DOES_NOT_SUPPORT_AUTO_ROLLBACK: "PLAN_DOES_NOT_SUPPORT_AUTO_ROLLBACK",
|
|
488
|
+
PLAN_DOES_NOT_SUPPORT_CUSTOM_DOMAIN: "PLAN_DOES_NOT_SUPPORT_CUSTOM_DOMAIN",
|
|
489
|
+
PLAN_DOES_NOT_SUPPORT_CUSTOM_SUBDOMAIN: "PLAN_DOES_NOT_SUPPORT_CUSTOM_SUBDOMAIN",
|
|
490
|
+
/** 403 — the plan does not include rollback to earlier releases. */
|
|
491
|
+
PLAN_DOES_NOT_SUPPORT_ROLLBACK: "PLAN_DOES_NOT_SUPPORT_ROLLBACK",
|
|
492
|
+
PLAN_DOES_NOT_SUPPORT_WEB_PUBLISH: "PLAN_DOES_NOT_SUPPORT_WEB_PUBLISH",
|
|
493
|
+
/** 403 — the plan does not include zero-downtime deploys (`deploy.zero_downtime: "on"` below Enterprise). */
|
|
494
|
+
PLAN_DOES_NOT_SUPPORT_ZERO_DOWNTIME: "PLAN_DOES_NOT_SUPPORT_ZERO_DOWNTIME",
|
|
495
|
+
PLAN_MISMATCH_FOR_RENEWAL: "PLAN_MISMATCH_FOR_RENEWAL",
|
|
496
|
+
PLAN_NOT_ALLOWED: "PLAN_NOT_ALLOWED",
|
|
497
|
+
RATE_LIMIT_EXCEEDED: "RATE_LIMIT_EXCEEDED",
|
|
498
|
+
REFUND_AMOUNT_EXCEEDS_PRICE: "REFUND_AMOUNT_EXCEEDS_PRICE",
|
|
499
|
+
/** 409 — the release is already the active one. */
|
|
500
|
+
RELEASE_ALREADY_ACTIVE: "RELEASE_ALREADY_ACTIVE",
|
|
501
|
+
/** 410 — the release's kept code has expired or is outside the limit of the plan. */
|
|
502
|
+
RELEASE_EXPIRED: "RELEASE_EXPIRED",
|
|
503
|
+
/** 404 — the release does not exist or belongs to another application. */
|
|
504
|
+
RELEASE_NOT_FOUND: "RELEASE_NOT_FOUND",
|
|
505
|
+
REPO_ALREADY_LINKED: "REPO_ALREADY_LINKED",
|
|
506
|
+
RESTORE_COOLDOWN: "RESTORE_COOLDOWN",
|
|
507
|
+
SNAPSHOT_ENGINE_MISMATCH: "SNAPSHOT_ENGINE_MISMATCH",
|
|
508
|
+
SNAPSHOT_FAILED: "SNAPSHOT_FAILED",
|
|
509
|
+
SNAPSHOT_IMPORT_FAILED: "SNAPSHOT_IMPORT_FAILED",
|
|
510
|
+
SNAPSHOT_NOT_FOUND: "SNAPSHOT_NOT_FOUND",
|
|
511
|
+
SNAPSHOT_RATE_LIMITED: "SNAPSHOT_RATE_LIMITED",
|
|
512
|
+
SNAPSHOT_TYPE_MISMATCH: "SNAPSHOT_TYPE_MISMATCH",
|
|
513
|
+
SNAPSHOT_TOO_LARGE: "SNAPSHOT_TOO_LARGE",
|
|
514
|
+
SOURCE_CONFLICT: "SOURCE_CONFLICT",
|
|
515
|
+
SOURCE_REQUIRED: "SOURCE_REQUIRED",
|
|
516
|
+
STORAGE_ABOVE_NEW_LIMIT: "STORAGE_ABOVE_NEW_LIMIT",
|
|
517
|
+
SUBDOMAIN_CHANGE_RATE_LIMITED: "SUBDOMAIN_CHANGE_RATE_LIMITED",
|
|
518
|
+
SUBDOMAIN_FORBIDDEN: "SUBDOMAIN_FORBIDDEN",
|
|
519
|
+
SUBDOMAIN_TAKEN: "SUBDOMAIN_TAKEN",
|
|
520
|
+
/** 400 — the CPF or CNPJ is not valid. */
|
|
521
|
+
TAX_ID_INVALID: "TAX_ID_INVALID",
|
|
522
|
+
TYPE_MISMATCH: "TYPE_MISMATCH",
|
|
523
|
+
USER_LIMITS_NOT_FOUND: "USER_LIMITS_NOT_FOUND",
|
|
524
|
+
USER_NOT_FOUND: "USER_NOT_FOUND",
|
|
525
|
+
USER_NOT_IN_WORKSPACE: "USER_NOT_IN_WORKSPACE",
|
|
526
|
+
DATABASE_BANNED: "DATABASE_BANNED",
|
|
527
|
+
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
528
|
+
ZIP_TOO_LARGE: "ZIP_TOO_LARGE",
|
|
529
|
+
ZIP_TOO_SMALL: "ZIP_TOO_SMALL",
|
|
530
|
+
STORAGE_FULL: "STORAGE_FULL",
|
|
531
|
+
TARGET_IS_DIRECTORY: "TARGET_IS_DIRECTORY",
|
|
532
|
+
...WORKSPACE_ERROR_CODES
|
|
533
|
+
};
|
|
534
|
+
var BlobVisibility = { PUBLIC: "public", PRIVATE: "private" };
|
|
535
|
+
var BlobContentDisposition = {
|
|
536
|
+
INLINE: "inline",
|
|
537
|
+
ATTACHMENT: "attachment"
|
|
538
|
+
};
|
|
539
|
+
var BlobUploadMode = { SINGLE: "single", MULTIPART: "multipart" };
|
|
540
|
+
var BlobUploadStatus = {
|
|
541
|
+
PENDING: "pending",
|
|
542
|
+
COMPLETED: "completed",
|
|
543
|
+
ABORTED: "aborted",
|
|
544
|
+
EXPIRED: "expired"
|
|
545
|
+
};
|
|
546
|
+
var BlobCustomDomainStatus = {
|
|
547
|
+
PENDING: "pending",
|
|
548
|
+
ACTIVE: "active",
|
|
549
|
+
FAILED: "failed",
|
|
550
|
+
SUSPENDED: "suspended"
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
// src/errors.ts
|
|
554
|
+
var BlobError = class extends Error {
|
|
555
|
+
constructor(status, body, retryAfter) {
|
|
556
|
+
super(body.message || body.code);
|
|
557
|
+
this.name = "BlobError";
|
|
558
|
+
this.status = status;
|
|
559
|
+
this.code = body.code;
|
|
560
|
+
this.details = body.details;
|
|
561
|
+
this.retryAfter = retryAfter;
|
|
562
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
563
|
+
}
|
|
564
|
+
toJSON() {
|
|
565
|
+
return { name: this.name, status: this.status, code: this.code, message: this.message, details: this.details, retryAfter: this.retryAfter };
|
|
566
|
+
}
|
|
567
|
+
toString() {
|
|
568
|
+
return `${this.name} [${this.status}] ${this.code}: ${this.message}`;
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
var clientError = (code, message, status = 0) => new BlobError(status, { code, message });
|
|
572
|
+
|
|
573
|
+
// src/source.ts
|
|
574
|
+
function toSource(data, size) {
|
|
575
|
+
if (typeof data === "string") return bytes(new TextEncoder().encode(data));
|
|
576
|
+
if (data instanceof Uint8Array) return bytes(data);
|
|
577
|
+
if (data instanceof ArrayBuffer) return bytes(new Uint8Array(data));
|
|
578
|
+
if (typeof Blob !== "undefined" && data instanceof Blob) return { size: data.size, read: async (s, e) => data.slice(s, e) };
|
|
579
|
+
if (typeof data.getReader === "function") {
|
|
580
|
+
if (typeof size !== "number" || !Number.isInteger(size) || size < 0) throw clientError("INVALID_ARGUMENT", "`size` is required when the data is a ReadableStream");
|
|
581
|
+
return stream(data, size);
|
|
582
|
+
}
|
|
583
|
+
throw clientError("INVALID_ARGUMENT", "Unsupported data type: use Blob, File, ArrayBuffer, Uint8Array, ReadableStream or string");
|
|
584
|
+
}
|
|
585
|
+
var bytes = (u8) => ({ size: u8.byteLength, read: async (s, e) => u8.subarray(s, e) });
|
|
586
|
+
function stream(readable, size) {
|
|
587
|
+
const reader = readable.getReader();
|
|
588
|
+
let rest = new Uint8Array(0);
|
|
589
|
+
let pos = 0;
|
|
590
|
+
const take = async (n, keep) => {
|
|
591
|
+
const out = new Uint8Array(keep ? n : 0);
|
|
592
|
+
let got = 0;
|
|
593
|
+
while (got < n) {
|
|
594
|
+
if (rest.length === 0) {
|
|
595
|
+
const { done, value } = await reader.read();
|
|
596
|
+
if (done) throw clientError("INVALID_ARGUMENT", "The stream ended before `size` bytes were read");
|
|
597
|
+
rest = value;
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
const m = Math.min(n - got, rest.length);
|
|
601
|
+
if (keep) out.set(rest.subarray(0, m), got);
|
|
602
|
+
rest = rest.subarray(m);
|
|
603
|
+
got += m;
|
|
604
|
+
}
|
|
605
|
+
pos += n;
|
|
606
|
+
return out;
|
|
607
|
+
};
|
|
608
|
+
return {
|
|
609
|
+
size,
|
|
610
|
+
async read(start, end) {
|
|
611
|
+
if (start < pos) throw clientError("INVALID_ARGUMENT", "A stream can only be read forward");
|
|
612
|
+
if (start > pos) await take(start - pos, false);
|
|
613
|
+
return take(end - start, true);
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// src/transport.ts
|
|
619
|
+
var fetchTransport = (fetchImpl) => async (req) => {
|
|
620
|
+
const init = { method: req.method, headers: req.headers, body: req.body ?? null, signal: req.signal };
|
|
621
|
+
if (req.credentials) init.credentials = req.credentials;
|
|
622
|
+
const res = await fetchImpl(req.url, init);
|
|
623
|
+
return { status: res.status, retryAfter: res.headers.get("retry-after"), text: await res.text() };
|
|
624
|
+
};
|
|
625
|
+
var xhrTransport = (req) => new Promise((resolve, reject) => {
|
|
626
|
+
const xhr = new XMLHttpRequest();
|
|
627
|
+
xhr.open(req.method, req.url);
|
|
628
|
+
for (const [k, v] of Object.entries(req.headers)) xhr.setRequestHeader(k, v);
|
|
629
|
+
if (req.credentials === "include") xhr.withCredentials = true;
|
|
630
|
+
if (req.onUploadProgress) {
|
|
631
|
+
const report = req.onUploadProgress;
|
|
632
|
+
xhr.upload.onprogress = (e) => report(e.loaded);
|
|
633
|
+
}
|
|
634
|
+
const onAbort = () => xhr.abort();
|
|
635
|
+
req.signal.addEventListener("abort", onAbort, { once: true });
|
|
636
|
+
const done = () => req.signal.removeEventListener("abort", onAbort);
|
|
637
|
+
xhr.onload = () => {
|
|
638
|
+
done();
|
|
639
|
+
resolve({ status: xhr.status, retryAfter: xhr.status === 429 ? xhr.getResponseHeader("Retry-After") : null, text: xhr.responseText });
|
|
640
|
+
};
|
|
641
|
+
xhr.onerror = xhr.onabort = xhr.ontimeout = () => {
|
|
642
|
+
done();
|
|
643
|
+
reject(new Error("request failed"));
|
|
644
|
+
};
|
|
645
|
+
xhr.send(req.body ?? null);
|
|
646
|
+
});
|
|
647
|
+
var aborted = () => clientError("ABORTED", "The operation was aborted");
|
|
648
|
+
function sleep(ms, signal) {
|
|
649
|
+
return new Promise((resolve, reject) => {
|
|
650
|
+
if (signal?.aborted) return reject(aborted());
|
|
651
|
+
const onAbort = () => {
|
|
652
|
+
clearTimeout(timer);
|
|
653
|
+
reject(aborted());
|
|
654
|
+
};
|
|
655
|
+
const timer = setTimeout(() => {
|
|
656
|
+
signal?.removeEventListener("abort", onAbort);
|
|
657
|
+
resolve();
|
|
658
|
+
}, ms);
|
|
659
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// src/client.ts
|
|
664
|
+
var SIMPLE_UPLOAD_MAX = 5 * 1024 * 1024;
|
|
665
|
+
var PART_URLS_PER_REQUEST = 100;
|
|
666
|
+
var MAX_BACKOFF_MS = 8e3;
|
|
667
|
+
var snake = (key) => key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
|
|
668
|
+
function toWire(input) {
|
|
669
|
+
const out = {};
|
|
670
|
+
for (const [key, value] of Object.entries(input)) {
|
|
671
|
+
if (value !== void 0) out[snake(key)] = value instanceof Date ? value.toISOString() : value;
|
|
672
|
+
}
|
|
673
|
+
return out;
|
|
674
|
+
}
|
|
675
|
+
var BlobClient = class {
|
|
676
|
+
constructor(options = {}) {
|
|
677
|
+
this.shares = {
|
|
678
|
+
create: (objectId, options = {}) => {
|
|
679
|
+
const { signal, timeout, ...rest } = options;
|
|
680
|
+
return this.api("POST", "/shares", { json: { ...toWire(rest), object_id: objectId }, signal, timeout });
|
|
681
|
+
},
|
|
682
|
+
list: (options = {}) => this.api("GET", "/shares", { query: { object_id: options.objectId }, signal: options.signal, timeout: options.timeout }),
|
|
683
|
+
revoke: async (shareId, options = {}) => {
|
|
684
|
+
await this.api("DELETE", `/shares/${enc(shareId)}`, options);
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
this.rules = {
|
|
688
|
+
get: (options = {}) => this.api("GET", "/rules", options),
|
|
689
|
+
/** Replaces the whole list. `etag` is the one from the list you read; a stale one fails with `BLOB_RULES_CONFLICT`. */
|
|
690
|
+
set: (rules, etag, options = {}) => this.api("PUT", "/rules", { json: { rules: rules.map((rule) => toWire(rule)) }, headers: { "If-Match": etag }, ...options }),
|
|
691
|
+
/** Creates or changes the rule of one folder; returns the whole list and its new etag. */
|
|
692
|
+
put: (prefix, rule, options = {}) => this.api("PUT", `/rules/${enc(prefix)}`, { json: toWire(rule), ...options }),
|
|
693
|
+
delete: (prefix, options = {}) => this.api("DELETE", `/rules/${enc(prefix)}`, options)
|
|
694
|
+
};
|
|
695
|
+
this.domains = {
|
|
696
|
+
list: (options = {}) => this.api("GET", "/domains", options),
|
|
697
|
+
add: (hostname, options = {}) => {
|
|
698
|
+
const { signal, timeout, prefix, web_content } = options;
|
|
699
|
+
return this.api("POST", "/domains", { json: { hostname, ...prefix ? { prefix } : {}, ...web_content === void 0 ? {} : { web_content } }, signal, timeout });
|
|
700
|
+
},
|
|
701
|
+
update: (domainId, body, options = {}) => this.api("PATCH", `/domains/${enc(domainId)}`, { json: body, signal: options.signal, timeout: options.timeout }),
|
|
702
|
+
verify: (domainId, options = {}) => this.api("POST", `/domains/${enc(domainId)}/verify`, options),
|
|
703
|
+
remove: async (domainId, options = {}) => {
|
|
704
|
+
await this.api("DELETE", `/domains/${enc(domainId)}`, options);
|
|
705
|
+
}
|
|
706
|
+
};
|
|
707
|
+
if (options.apiKey && options.uploadToken) throw clientError("INVALID_ARGUMENT", "Pass either `apiKey` or `uploadToken`, not both");
|
|
708
|
+
this.apiKey = options.apiKey ?? options.uploadToken;
|
|
709
|
+
this.baseUrl = (options.baseUrl ?? "https://blob.vertracloud.app/v1").replace(/\/+$/, "");
|
|
710
|
+
this.fetchImpl = options.fetch;
|
|
711
|
+
this.credentials = options.credentials;
|
|
712
|
+
this.workspaceId = options.workspaceId;
|
|
713
|
+
this.extraHeaders = options.headers ?? {};
|
|
714
|
+
this.partConcurrency = Math.max(1, Math.floor(options.partConcurrency ?? 4));
|
|
715
|
+
this.maxRetries = Math.max(0, Math.floor(options.maxRetries ?? 3));
|
|
716
|
+
this.retryDelayMs = options.retryDelayMs ?? 300;
|
|
717
|
+
this.timeout = options.timeout ?? 3e4;
|
|
718
|
+
this.uploadTimeout = options.uploadTimeout ?? 3e5;
|
|
719
|
+
}
|
|
720
|
+
// ---------------------------------------------------------------------------
|
|
721
|
+
// Upload
|
|
722
|
+
// ---------------------------------------------------------------------------
|
|
723
|
+
/**
|
|
724
|
+
* Uploads a file. Up to 5 MB it is one `POST /objects`; above that it starts a direct upload and
|
|
725
|
+
* follows the mode the API picks: one signed PUT, or parts sent in parallel. Returns the object.
|
|
726
|
+
* Aborting `signal` during a direct upload cancels it on the server (best effort).
|
|
727
|
+
*/
|
|
728
|
+
async put(path, data, options = {}) {
|
|
729
|
+
const { size, onProgress, onUpload, signal, timeout, ...write } = options;
|
|
730
|
+
const src = toSource(data, size);
|
|
731
|
+
const ctx = { signal, timeout, onProgress };
|
|
732
|
+
if (src.size <= SIMPLE_UPLOAD_MAX) return this.putSimple(path, src, write, ctx);
|
|
733
|
+
const upload = await this.api("POST", "/uploads", { json: { ...toWire(write), path, size: src.size }, signal, timeout });
|
|
734
|
+
onUpload?.(upload);
|
|
735
|
+
return this.transfer(upload, src, ctx, true, /* @__PURE__ */ new Set());
|
|
736
|
+
}
|
|
737
|
+
/** Continues an interrupted direct upload, sending only the parts the server does not have yet. `data` must be the same file. */
|
|
738
|
+
async resume(uploadId, data, options = {}) {
|
|
739
|
+
const { size, onProgress, signal, timeout } = options;
|
|
740
|
+
const src = toSource(data, size);
|
|
741
|
+
const upload = await this.api("GET", `/uploads/${enc(uploadId)}`, { signal, timeout });
|
|
742
|
+
return this.transfer(upload, src, { signal, timeout, onProgress }, false, new Set(upload.uploaded_parts ?? []));
|
|
743
|
+
}
|
|
744
|
+
/** Direct uploads still in progress. */
|
|
745
|
+
listUploads(options = {}) {
|
|
746
|
+
return this.api("GET", "/uploads", options);
|
|
747
|
+
}
|
|
748
|
+
/** Aborts a direct upload and frees its reserved storage. Repeating it is not an error. */
|
|
749
|
+
async abortUpload(uploadId, options = {}) {
|
|
750
|
+
await this.api("DELETE", `/uploads/${enc(uploadId)}`, options);
|
|
751
|
+
}
|
|
752
|
+
async putSimple(path, src, write, ctx) {
|
|
753
|
+
const { metadata, ...rest } = write;
|
|
754
|
+
const query = toWire({ ...rest, path, ...metadata ? { metadata: JSON.stringify(metadata) } : {} });
|
|
755
|
+
const form = new FormData();
|
|
756
|
+
form.append("file", new Blob([await src.read(0, src.size)]), path.split("/").pop() || "file");
|
|
757
|
+
const object = await this.api("POST", "/objects", {
|
|
758
|
+
query,
|
|
759
|
+
body: form,
|
|
760
|
+
signal: ctx.signal,
|
|
761
|
+
timeout: ctx.timeout ?? this.uploadTimeout,
|
|
762
|
+
onUploadProgress: ctx.onProgress && ((loaded) => ctx.onProgress?.({ loaded: Math.min(loaded, src.size), total: src.size }))
|
|
763
|
+
});
|
|
764
|
+
ctx.onProgress?.({ loaded: src.size, total: src.size });
|
|
765
|
+
return object;
|
|
766
|
+
}
|
|
767
|
+
async transfer(upload, src, ctx, abortOnUserAbort, skip) {
|
|
768
|
+
if (src.size !== upload.size) throw clientError("INVALID_ARGUMENT", `The data has ${src.size} bytes but the upload was created for ${upload.size}`);
|
|
769
|
+
try {
|
|
770
|
+
if (upload.mode === "single") await this.putSingle(upload, src, ctx);
|
|
771
|
+
else await this.putParts(upload, src, ctx, skip);
|
|
772
|
+
return await this.api("POST", `/uploads/${enc(upload.id)}/complete`, { signal: ctx.signal, timeout: ctx.timeout });
|
|
773
|
+
} catch (error) {
|
|
774
|
+
if (abortOnUserAbort && ctx.signal?.aborted) await this.abortUpload(upload.id, { timeout: 5e3 }).catch(() => {
|
|
775
|
+
});
|
|
776
|
+
throw error;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
async putSingle(upload, src, ctx) {
|
|
780
|
+
if (!upload.url) throw clientError("INVALID_RESPONSE", "The upload has no URL");
|
|
781
|
+
const body = await src.read(0, src.size);
|
|
782
|
+
await this.putBytes(upload.url, upload.headers, body, ctx.signal, ctx.onProgress && ((loaded) => ctx.onProgress?.({ loaded: Math.min(loaded, src.size), total: src.size })));
|
|
783
|
+
ctx.onProgress?.({ loaded: src.size, total: src.size });
|
|
784
|
+
}
|
|
785
|
+
async putParts(upload, src, ctx, skip) {
|
|
786
|
+
const { part_size: partSize, part_count: partCount } = upload;
|
|
787
|
+
if (!partSize || !partCount) throw clientError("INVALID_RESPONSE", "The multipart upload has no part layout");
|
|
788
|
+
const range = (n) => [(n - 1) * partSize, Math.min(n * partSize, src.size)];
|
|
789
|
+
const pending = [];
|
|
790
|
+
let done = 0;
|
|
791
|
+
for (let n = 1; n <= partCount; n++) {
|
|
792
|
+
if (skip.has(n)) done += range(n)[1] - range(n)[0];
|
|
793
|
+
else pending.push(n);
|
|
794
|
+
}
|
|
795
|
+
const total = src.size;
|
|
796
|
+
const inflight = /* @__PURE__ */ new Map();
|
|
797
|
+
const report = () => {
|
|
798
|
+
let loaded = done;
|
|
799
|
+
for (const v of inflight.values()) loaded += v;
|
|
800
|
+
ctx.onProgress?.({ loaded: Math.min(loaded, total), total });
|
|
801
|
+
};
|
|
802
|
+
if (done > 0) report();
|
|
803
|
+
const ctl = new AbortController();
|
|
804
|
+
const onUserAbort = () => ctl.abort();
|
|
805
|
+
ctx.signal?.addEventListener("abort", onUserAbort, { once: true });
|
|
806
|
+
if (ctx.signal?.aborted) ctl.abort();
|
|
807
|
+
const urls = /* @__PURE__ */ new Map();
|
|
808
|
+
let cursor = 0;
|
|
809
|
+
let failed = false;
|
|
810
|
+
let lock = Promise.resolve();
|
|
811
|
+
const next = () => {
|
|
812
|
+
const run = lock.then(async () => {
|
|
813
|
+
if (failed || cursor >= pending.length) return null;
|
|
814
|
+
const n = pending[cursor];
|
|
815
|
+
if (!urls.has(n)) {
|
|
816
|
+
const batch = pending.slice(cursor, cursor + PART_URLS_PER_REQUEST);
|
|
817
|
+
const parts = await this.api("POST", `/uploads/${enc(upload.id)}/parts`, { json: { part_numbers: batch }, signal: ctl.signal, timeout: ctx.timeout });
|
|
818
|
+
for (const part2 of parts) urls.set(part2.part_number, part2);
|
|
819
|
+
}
|
|
820
|
+
const part = urls.get(n);
|
|
821
|
+
if (!part) throw clientError("INVALID_RESPONSE", `The API did not return a URL for part ${n}`);
|
|
822
|
+
cursor++;
|
|
823
|
+
const [start, end] = range(n);
|
|
824
|
+
return { n, part, bytes: await src.read(start, end), size: end - start };
|
|
825
|
+
});
|
|
826
|
+
lock = run.catch(() => {
|
|
827
|
+
});
|
|
828
|
+
return run;
|
|
829
|
+
};
|
|
830
|
+
const worker = async () => {
|
|
831
|
+
for (let job = await next(); job; job = await next()) {
|
|
832
|
+
await this.putBytes(
|
|
833
|
+
job.part.url,
|
|
834
|
+
job.part.headers,
|
|
835
|
+
job.bytes,
|
|
836
|
+
ctl.signal,
|
|
837
|
+
ctx.onProgress && ((loaded) => {
|
|
838
|
+
inflight.set(job.n, Math.min(loaded, job.size));
|
|
839
|
+
report();
|
|
840
|
+
})
|
|
841
|
+
);
|
|
842
|
+
inflight.delete(job.n);
|
|
843
|
+
done += job.size;
|
|
844
|
+
report();
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
try {
|
|
848
|
+
await Promise.all(
|
|
849
|
+
Array.from(
|
|
850
|
+
{ length: Math.min(this.partConcurrency, pending.length) },
|
|
851
|
+
() => worker().catch((error) => {
|
|
852
|
+
failed = true;
|
|
853
|
+
ctl.abort();
|
|
854
|
+
throw error;
|
|
855
|
+
})
|
|
856
|
+
)
|
|
857
|
+
);
|
|
858
|
+
} finally {
|
|
859
|
+
ctx.signal?.removeEventListener("abort", onUserAbort);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
/** PUT to a signed URL with exactly the headers the API returned. No credentials are attached. Retried on 5xx and network errors. */
|
|
863
|
+
async putBytes(url, headers, body, signal, onUploadProgress) {
|
|
864
|
+
const res = await this.exec({ method: "PUT", url, headers: { ...headers }, body, signal, timeout: this.uploadTimeout, retry: true, onUploadProgress });
|
|
865
|
+
if (res.status < 200 || res.status >= 300) throw clientError("UPLOAD_FAILED", `The storage rejected the upload (HTTP ${res.status})`, res.status);
|
|
866
|
+
}
|
|
867
|
+
// ---------------------------------------------------------------------------
|
|
868
|
+
// Objects
|
|
869
|
+
// ---------------------------------------------------------------------------
|
|
870
|
+
/** One page of objects plus the `folders` found under the prefix, and the `cursor` of the next page (`null` on the last). */
|
|
871
|
+
listPage(options = {}) {
|
|
872
|
+
const { signal, timeout, ...query } = options;
|
|
873
|
+
return this.api("GET", "/objects", { query: toWire(query), signal, timeout });
|
|
874
|
+
}
|
|
875
|
+
/** Iterates every object that matches, fetching pages as needed. */
|
|
876
|
+
async *list(options = {}) {
|
|
877
|
+
let cursor;
|
|
878
|
+
do {
|
|
879
|
+
const page = await this.listPage({ ...options, cursor });
|
|
880
|
+
yield* page.objects;
|
|
881
|
+
cursor = page.cursor ?? void 0;
|
|
882
|
+
} while (cursor);
|
|
883
|
+
}
|
|
884
|
+
get(id, options = {}) {
|
|
885
|
+
return this.api("GET", `/objects/${enc(id)}`, options);
|
|
886
|
+
}
|
|
887
|
+
update(id, changes, options = {}) {
|
|
888
|
+
return this.api("PATCH", `/objects/${enc(id)}`, { json: toWire(changes), ...options });
|
|
889
|
+
}
|
|
890
|
+
/** Applies the same changes to any number of objects (sent in batches of 100, the API limit); one result per id, in the order of `ids`. */
|
|
891
|
+
async updateMany(ids, changes, options = {}) {
|
|
892
|
+
const wire = toWire(changes);
|
|
893
|
+
const results = [];
|
|
894
|
+
for (let i = 0; i === 0 || i < ids.length; i += 100) {
|
|
895
|
+
const batch = await this.api("PATCH", "/objects", { json: { ids: ids.slice(i, i + 100), changes: wire }, ...options });
|
|
896
|
+
results.push(...batch);
|
|
897
|
+
}
|
|
898
|
+
return results;
|
|
899
|
+
}
|
|
900
|
+
copy(id, path, options = {}) {
|
|
901
|
+
const { signal, timeout, ...rest } = options;
|
|
902
|
+
return this.api("POST", "/objects/copy", { json: { ...toWire(rest), source: id, path }, signal, timeout });
|
|
903
|
+
}
|
|
904
|
+
/** Moves or renames an object; the id stays the same. */
|
|
905
|
+
move(id, path, options = {}) {
|
|
906
|
+
return this.copy(id, path, { ...options, move: true });
|
|
907
|
+
}
|
|
908
|
+
async delete(id, options = {}) {
|
|
909
|
+
await this.api("DELETE", `/objects/${enc(id)}`, options);
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Deletes by `ids` or by folder `prefix`, repeating while the API reports `remaining`
|
|
913
|
+
* (it deletes up to 1000 per call). The result merges every round.
|
|
914
|
+
*/
|
|
915
|
+
async deleteMany(input, options = {}) {
|
|
916
|
+
const total = { deleted: [], not_found: [], remaining: false };
|
|
917
|
+
let ids = "ids" in input ? [...input.ids] : void 0;
|
|
918
|
+
for (; ; ) {
|
|
919
|
+
const body = ids ? { ids: ids.slice(0, 1e3) } : { prefix: input.prefix };
|
|
920
|
+
const res = await this.api("DELETE", "/objects", { json: body, ...options });
|
|
921
|
+
total.deleted.push(...res.deleted);
|
|
922
|
+
total.not_found.push(...res.not_found);
|
|
923
|
+
if (ids) {
|
|
924
|
+
const seen = /* @__PURE__ */ new Set([...total.deleted, ...total.not_found]);
|
|
925
|
+
ids = ids.filter((id) => !seen.has(id));
|
|
926
|
+
}
|
|
927
|
+
if (ids ? ids.length === 0 : !res.remaining) return total;
|
|
928
|
+
if (res.deleted.length + res.not_found.length === 0) throw clientError("INVALID_RESPONSE", "deleteMany made no progress");
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
/** A temporary download link (`{ url, expires_at }`). It cannot be revoked. */
|
|
932
|
+
downloadUrl(id, options = {}) {
|
|
933
|
+
const { signal, timeout, ...rest } = options;
|
|
934
|
+
const query = { ...toWire(rest), redirect: false };
|
|
935
|
+
return this.api("GET", `/objects/${enc(id)}/download`, { query, signal, timeout });
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Builds an image-transformation URL from a public object (or its public URL). Pure: no request is made.
|
|
939
|
+
* Throws for a private object, which has no public URL.
|
|
940
|
+
*/
|
|
941
|
+
imageUrl(source, options = {}) {
|
|
942
|
+
const raw = typeof source === "string" ? source : source.url;
|
|
943
|
+
if (!raw) throw clientError("INVALID_ARGUMENT", "Only public objects can be transformed; this object is private");
|
|
944
|
+
const url = new URL(raw);
|
|
945
|
+
const params = [
|
|
946
|
+
["w", options.width],
|
|
947
|
+
["h", options.height],
|
|
948
|
+
["fit", options.fit],
|
|
949
|
+
["format", options.format],
|
|
950
|
+
["q", options.quality]
|
|
951
|
+
];
|
|
952
|
+
for (const [key, value] of params) if (value !== void 0) url.searchParams.set(key, String(value));
|
|
953
|
+
return url.toString();
|
|
954
|
+
}
|
|
955
|
+
// ---------------------------------------------------------------------------
|
|
956
|
+
// Account, tokens, S3
|
|
957
|
+
// ---------------------------------------------------------------------------
|
|
958
|
+
stats(options = {}) {
|
|
959
|
+
return this.api("GET", "/stats", options);
|
|
960
|
+
}
|
|
961
|
+
/** Creates a token that fixes every constraint you pass; hand it to a browser as `uploadToken`. */
|
|
962
|
+
createUploadToken(options = {}) {
|
|
963
|
+
const { signal, timeout, ...rest } = options;
|
|
964
|
+
return this.api("POST", "/upload-tokens", { json: toWire(rest), signal, timeout });
|
|
965
|
+
}
|
|
966
|
+
/** S3-compatible credentials derived from the API key, for aws-cli, rclone or boto3. */
|
|
967
|
+
s3Credentials(options = {}) {
|
|
968
|
+
return this.api("GET", "/s3/credentials", options);
|
|
969
|
+
}
|
|
970
|
+
// ---------------------------------------------------------------------------
|
|
971
|
+
// HTTP
|
|
972
|
+
// ---------------------------------------------------------------------------
|
|
973
|
+
/** One API call: builds the URL, authenticates, unwraps `{ response }` and turns an error envelope into a `BlobError`. */
|
|
974
|
+
async api(method, path, call = {}) {
|
|
975
|
+
const query = new URLSearchParams();
|
|
976
|
+
for (const [key, value] of Object.entries({ ...call.query, workspace_id: this.workspaceId })) {
|
|
977
|
+
if (value !== void 0 && value !== null) query.set(key, String(value));
|
|
978
|
+
}
|
|
979
|
+
const headers = { accept: "application/json", ...this.extraHeaders, ...call.headers };
|
|
980
|
+
if (this.apiKey) headers.authorization = `Bearer ${this.apiKey}`;
|
|
981
|
+
let body = call.body;
|
|
982
|
+
if (call.json !== void 0) {
|
|
983
|
+
body = JSON.stringify(call.json);
|
|
984
|
+
headers["content-type"] = "application/json";
|
|
985
|
+
}
|
|
986
|
+
const qs = query.toString();
|
|
987
|
+
const res = await this.exec({
|
|
988
|
+
method,
|
|
989
|
+
url: `${this.baseUrl}${path}${qs ? `?${qs}` : ""}`,
|
|
990
|
+
headers,
|
|
991
|
+
body,
|
|
992
|
+
credentials: this.credentials,
|
|
993
|
+
signal: call.signal,
|
|
994
|
+
timeout: call.timeout ?? this.timeout,
|
|
995
|
+
retry: method === "GET",
|
|
996
|
+
onUploadProgress: call.onUploadProgress
|
|
997
|
+
});
|
|
998
|
+
let parsed;
|
|
999
|
+
if (res.text) {
|
|
1000
|
+
try {
|
|
1001
|
+
parsed = JSON.parse(res.text);
|
|
1002
|
+
} catch {
|
|
1003
|
+
if (res.status >= 200 && res.status < 300) throw clientError("INVALID_RESPONSE", "The API returned a body that is not JSON", res.status);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
if (res.status >= 200 && res.status < 300) return parsed?.response ?? null;
|
|
1007
|
+
const err = parsed ?? {};
|
|
1008
|
+
const retryAfter = Number(res.retryAfter);
|
|
1009
|
+
throw new BlobError(res.status, { code: typeof err.code === "string" ? err.code : `HTTP_${res.status}`, message: typeof err.message === "string" ? err.message : void 0, details: err.details }, res.retryAfter && Number.isFinite(retryAfter) ? retryAfter : void 0);
|
|
1010
|
+
}
|
|
1011
|
+
/** Sends a request, retrying 5xx and network errors when `req.retry` (never 429). */
|
|
1012
|
+
async exec(req) {
|
|
1013
|
+
const retries = req.retry ? this.maxRetries : 0;
|
|
1014
|
+
for (let attempt = 0; ; attempt++) {
|
|
1015
|
+
try {
|
|
1016
|
+
const res = await this.once(req);
|
|
1017
|
+
if (res.status < 500 || attempt >= retries) return res;
|
|
1018
|
+
} catch (error) {
|
|
1019
|
+
if (!(error instanceof BlobError) || error.code !== "NETWORK_ERROR" && error.code !== "TIMEOUT" || attempt >= retries) throw error;
|
|
1020
|
+
}
|
|
1021
|
+
await sleep(Math.min(this.retryDelayMs * 2 ** attempt, MAX_BACKOFF_MS) * (0.5 + Math.random() / 2), req.signal);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
async once(req) {
|
|
1025
|
+
const user = req.signal;
|
|
1026
|
+
if (user?.aborted) throw aborted();
|
|
1027
|
+
const ctl = new AbortController();
|
|
1028
|
+
const onAbort = () => ctl.abort();
|
|
1029
|
+
user?.addEventListener("abort", onAbort, { once: true });
|
|
1030
|
+
let timedOut = false;
|
|
1031
|
+
const timer = req.timeout ? setTimeout(() => {
|
|
1032
|
+
timedOut = true;
|
|
1033
|
+
ctl.abort();
|
|
1034
|
+
}, req.timeout) : void 0;
|
|
1035
|
+
const useXhr = req.onUploadProgress && !this.fetchImpl && typeof XMLHttpRequest !== "undefined";
|
|
1036
|
+
try {
|
|
1037
|
+
const send = useXhr ? xhrTransport : fetchTransport(this.fetchImpl ?? ((input, init) => globalThis.fetch(input, init)));
|
|
1038
|
+
return await send({ ...req, signal: ctl.signal });
|
|
1039
|
+
} catch {
|
|
1040
|
+
if (user?.aborted) throw aborted();
|
|
1041
|
+
if (timedOut) throw clientError("TIMEOUT", "The request timed out");
|
|
1042
|
+
throw clientError("NETWORK_ERROR", "The request failed before a response was received");
|
|
1043
|
+
} finally {
|
|
1044
|
+
clearTimeout(timer);
|
|
1045
|
+
user?.removeEventListener("abort", onAbort);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
var enc = encodeURIComponent;
|
|
1050
|
+
|
|
1051
|
+
// src/index.ts
|
|
1052
|
+
var VERSION = "0.1.0";
|
|
1053
|
+
export {
|
|
1054
|
+
BlobClient,
|
|
1055
|
+
BlobContentDisposition,
|
|
1056
|
+
BlobCustomDomainStatus,
|
|
1057
|
+
BlobError,
|
|
1058
|
+
BlobUploadMode,
|
|
1059
|
+
BlobUploadStatus,
|
|
1060
|
+
BlobVisibility,
|
|
1061
|
+
VERSION
|
|
1062
|
+
};
|
|
1063
|
+
//# sourceMappingURL=index.js.map
|