@gigadrive/sdk 0.1.2 → 0.2.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/README.md +177 -0
- package/dist/index.d.mts +2523 -1
- package/dist/index.d.ts +2523 -1
- package/dist/index.js +2148 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2089 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -1,10 +1,2153 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var
|
|
5
|
-
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
6
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
7
29
|
|
|
8
30
|
// src/index.ts
|
|
9
|
-
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
AiGatewayAudioResource: () => AiGatewayAudioResource,
|
|
34
|
+
AiGatewayBudgetsResource: () => AiGatewayBudgetsResource,
|
|
35
|
+
AiGatewayPoliciesResource: () => AiGatewayPoliciesResource,
|
|
36
|
+
AiGatewayResource: () => AiGatewayResource,
|
|
37
|
+
AiGatewayUsageResource: () => AiGatewayUsageResource,
|
|
38
|
+
AiGatewayVideosResource: () => AiGatewayVideosResource,
|
|
39
|
+
ApiError: () => ApiError,
|
|
40
|
+
ApplicationEnvVarsResource: () => ApplicationEnvVarsResource,
|
|
41
|
+
ApplicationRequestsResource: () => ApplicationRequestsResource,
|
|
42
|
+
ApplicationStorageResource: () => ApplicationStorageResource,
|
|
43
|
+
ApplicationsResource: () => ApplicationsResource,
|
|
44
|
+
AuthenticationError: () => AuthenticationError,
|
|
45
|
+
DeploymentsResource: () => DeploymentsResource,
|
|
46
|
+
GigadriveClient: () => GigadriveClient,
|
|
47
|
+
GigadriveError: () => GigadriveError,
|
|
48
|
+
OrganizationAiGatewayResource: () => OrganizationAiGatewayResource,
|
|
49
|
+
OrganizationEnvVarsResource: () => OrganizationEnvVarsResource,
|
|
50
|
+
OrganizationsResource: () => OrganizationsResource,
|
|
51
|
+
StorageBucketsResource: () => StorageBucketsResource,
|
|
52
|
+
StorageObjectsResource: () => StorageObjectsResource,
|
|
53
|
+
StorageUploadSessionsResource: () => StorageUploadSessionsResource,
|
|
54
|
+
UploadError: () => UploadError,
|
|
55
|
+
UploadSessionExpiredError: () => UploadSessionExpiredError,
|
|
56
|
+
paginate: () => paginate,
|
|
57
|
+
parseSSEStream: () => parseSSEStream
|
|
58
|
+
});
|
|
59
|
+
module.exports = __toCommonJS(src_exports);
|
|
60
|
+
|
|
61
|
+
// src/errors.ts
|
|
62
|
+
var GigadriveError = class extends Error {
|
|
63
|
+
constructor(message) {
|
|
64
|
+
super(message);
|
|
65
|
+
this.name = "GigadriveError";
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var AuthenticationError = class extends GigadriveError {
|
|
69
|
+
constructor(message) {
|
|
70
|
+
super(message);
|
|
71
|
+
this.name = "AuthenticationError";
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var ApiError = class extends GigadriveError {
|
|
75
|
+
/** The HTTP status code of the failed response (e.g. 404, 500). */
|
|
76
|
+
status;
|
|
77
|
+
/** An optional machine-readable error code from the API response body. */
|
|
78
|
+
code;
|
|
79
|
+
constructor(message, status, code) {
|
|
80
|
+
super(message);
|
|
81
|
+
this.name = "ApiError";
|
|
82
|
+
this.status = status;
|
|
83
|
+
this.code = code;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
var UploadError = class extends GigadriveError {
|
|
87
|
+
/** The underlying error that caused the upload to fail, if any. */
|
|
88
|
+
cause;
|
|
89
|
+
constructor(message, cause) {
|
|
90
|
+
super(message);
|
|
91
|
+
this.name = "UploadError";
|
|
92
|
+
this.cause = cause;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var UploadSessionExpiredError = class extends UploadError {
|
|
96
|
+
constructor(message = "The upload session expired before the upload completed. Start a new upload to retry.", cause) {
|
|
97
|
+
super(message, cause);
|
|
98
|
+
this.name = "UploadSessionExpiredError";
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// src/auth/credential-provider.ts
|
|
103
|
+
var DEFAULT_SCOPE = "offline_access openid profile email";
|
|
104
|
+
var TOKEN_EXPIRY_MARGIN_SECONDS = 30;
|
|
105
|
+
var computeExpiresAt = (expiresInSeconds) => {
|
|
106
|
+
const ttl = expiresInSeconds ?? 3600;
|
|
107
|
+
const margin = Math.min(TOKEN_EXPIRY_MARGIN_SECONDS, ttl / 2);
|
|
108
|
+
return Date.now() + (ttl - margin) * 1e3;
|
|
109
|
+
};
|
|
110
|
+
var readEnv = (name) => {
|
|
111
|
+
if (typeof process === "undefined" || !process.env) return void 0;
|
|
112
|
+
return process.env[name];
|
|
113
|
+
};
|
|
114
|
+
var parseTokenResponse = (payload) => {
|
|
115
|
+
const data = payload;
|
|
116
|
+
if (typeof data.access_token !== "string" || data.access_token.length === 0) {
|
|
117
|
+
throw new AuthenticationError("Token endpoint returned no access_token");
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
access_token: data.access_token,
|
|
121
|
+
refresh_token: typeof data.refresh_token === "string" ? data.refresh_token : void 0,
|
|
122
|
+
expires_in: typeof data.expires_in === "number" ? data.expires_in : void 0
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
var discoverOidc = async (issuerUrl, fetchFn) => {
|
|
126
|
+
const discoveryUrl = `${issuerUrl.replace(/\/$/, "")}/.well-known/openid-configuration`;
|
|
127
|
+
const response = await fetchFn(discoveryUrl);
|
|
128
|
+
if (!response.ok) {
|
|
129
|
+
throw new AuthenticationError(`OIDC discovery failed: ${response.status} ${response.statusText}`);
|
|
130
|
+
}
|
|
131
|
+
return await response.json();
|
|
132
|
+
};
|
|
133
|
+
var base64UrlEncode = (buffer) => {
|
|
134
|
+
let binary = "";
|
|
135
|
+
for (let i = 0; i < buffer.byteLength; i++) {
|
|
136
|
+
binary += String.fromCharCode(buffer[i]);
|
|
137
|
+
}
|
|
138
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
139
|
+
};
|
|
140
|
+
var generatePkce = async () => {
|
|
141
|
+
const randomBytes = new Uint8Array(64);
|
|
142
|
+
crypto.getRandomValues(randomBytes);
|
|
143
|
+
const codeVerifier = base64UrlEncode(randomBytes);
|
|
144
|
+
const encoder = new TextEncoder();
|
|
145
|
+
const data = encoder.encode(codeVerifier);
|
|
146
|
+
const digest = await crypto.subtle.digest("SHA-256", data);
|
|
147
|
+
const codeChallenge = base64UrlEncode(new Uint8Array(digest));
|
|
148
|
+
return { codeVerifier, codeChallenge };
|
|
149
|
+
};
|
|
150
|
+
var generateState = () => {
|
|
151
|
+
const bytes = new Uint8Array(16);
|
|
152
|
+
crypto.getRandomValues(bytes);
|
|
153
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
154
|
+
};
|
|
155
|
+
var OAuth2ClientCredentialProvider = class {
|
|
156
|
+
constructor(clientId, clientSecret, tokenUrl, fetchFn) {
|
|
157
|
+
this.clientId = clientId;
|
|
158
|
+
this.clientSecret = clientSecret;
|
|
159
|
+
this.tokenUrl = tokenUrl;
|
|
160
|
+
this.fetchFn = fetchFn;
|
|
161
|
+
}
|
|
162
|
+
clientId;
|
|
163
|
+
clientSecret;
|
|
164
|
+
tokenUrl;
|
|
165
|
+
fetchFn;
|
|
166
|
+
type = "oauth2-client-credentials";
|
|
167
|
+
async getToken() {
|
|
168
|
+
const credentials = btoa(`${this.clientId}:${this.clientSecret}`);
|
|
169
|
+
const response = await this.fetchFn(this.tokenUrl, {
|
|
170
|
+
method: "POST",
|
|
171
|
+
headers: {
|
|
172
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
173
|
+
Authorization: `Basic ${credentials}`
|
|
174
|
+
},
|
|
175
|
+
body: new URLSearchParams({ grant_type: "client_credentials" }).toString()
|
|
176
|
+
});
|
|
177
|
+
if (!response.ok) {
|
|
178
|
+
const body = await response.text().catch(() => "");
|
|
179
|
+
throw new AuthenticationError(`OAuth2 token request failed (${response.status}): ${body}`);
|
|
180
|
+
}
|
|
181
|
+
const data = parseTokenResponse(await response.json());
|
|
182
|
+
return {
|
|
183
|
+
accessToken: data.access_token,
|
|
184
|
+
expiresAt: computeExpiresAt(data.expires_in)
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
var OAuth2AuthorizationCodeProvider = class {
|
|
189
|
+
constructor(clientId, issuerUrl, redirectUri, onAuthorizationUrl, fetchFn, scope = DEFAULT_SCOPE) {
|
|
190
|
+
this.clientId = clientId;
|
|
191
|
+
this.issuerUrl = issuerUrl;
|
|
192
|
+
this.redirectUri = redirectUri;
|
|
193
|
+
this.onAuthorizationUrl = onAuthorizationUrl;
|
|
194
|
+
this.fetchFn = fetchFn;
|
|
195
|
+
this.scope = scope;
|
|
196
|
+
}
|
|
197
|
+
clientId;
|
|
198
|
+
issuerUrl;
|
|
199
|
+
redirectUri;
|
|
200
|
+
onAuthorizationUrl;
|
|
201
|
+
fetchFn;
|
|
202
|
+
scope;
|
|
203
|
+
type = "oauth2-authorization-code";
|
|
204
|
+
currentRefreshToken = null;
|
|
205
|
+
async getToken() {
|
|
206
|
+
const oidc = await discoverOidc(this.issuerUrl, this.fetchFn);
|
|
207
|
+
if (!oidc.token_endpoint) {
|
|
208
|
+
throw new AuthenticationError("OIDC discovery returned no token endpoint");
|
|
209
|
+
}
|
|
210
|
+
if (this.currentRefreshToken) {
|
|
211
|
+
const refreshed = await this.tryRefresh(oidc.token_endpoint, this.currentRefreshToken);
|
|
212
|
+
if (refreshed) return refreshed;
|
|
213
|
+
this.currentRefreshToken = null;
|
|
214
|
+
}
|
|
215
|
+
if (!oidc.authorization_endpoint) {
|
|
216
|
+
throw new AuthenticationError("OIDC discovery returned incomplete endpoints");
|
|
217
|
+
}
|
|
218
|
+
const { codeVerifier, codeChallenge } = await generatePkce();
|
|
219
|
+
const state = generateState();
|
|
220
|
+
const authUrl = new URL(oidc.authorization_endpoint);
|
|
221
|
+
authUrl.searchParams.set("client_id", this.clientId);
|
|
222
|
+
authUrl.searchParams.set("redirect_uri", this.redirectUri);
|
|
223
|
+
authUrl.searchParams.set("response_type", "code");
|
|
224
|
+
authUrl.searchParams.set("scope", this.scope);
|
|
225
|
+
authUrl.searchParams.set("code_challenge", codeChallenge);
|
|
226
|
+
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
227
|
+
authUrl.searchParams.set("state", state);
|
|
228
|
+
const result = await this.onAuthorizationUrl(authUrl.toString());
|
|
229
|
+
let code;
|
|
230
|
+
let returnedState = null;
|
|
231
|
+
try {
|
|
232
|
+
const callbackUrl = new URL(result);
|
|
233
|
+
code = callbackUrl.searchParams.get("code") ?? "";
|
|
234
|
+
returnedState = callbackUrl.searchParams.get("state");
|
|
235
|
+
const error = callbackUrl.searchParams.get("error");
|
|
236
|
+
if (error) {
|
|
237
|
+
const description = callbackUrl.searchParams.get("error_description") ?? error;
|
|
238
|
+
throw new AuthenticationError(`Authorization failed: ${description}`);
|
|
239
|
+
}
|
|
240
|
+
} catch (e) {
|
|
241
|
+
if (e instanceof AuthenticationError) throw e;
|
|
242
|
+
code = result;
|
|
243
|
+
}
|
|
244
|
+
if (!code) {
|
|
245
|
+
throw new AuthenticationError("No authorization code received");
|
|
246
|
+
}
|
|
247
|
+
if (returnedState !== null && returnedState !== state) {
|
|
248
|
+
throw new AuthenticationError("State mismatch \u2014 possible CSRF attack");
|
|
249
|
+
}
|
|
250
|
+
const response = await this.fetchFn(oidc.token_endpoint, {
|
|
251
|
+
method: "POST",
|
|
252
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
253
|
+
body: new URLSearchParams({
|
|
254
|
+
grant_type: "authorization_code",
|
|
255
|
+
client_id: this.clientId,
|
|
256
|
+
code,
|
|
257
|
+
redirect_uri: this.redirectUri,
|
|
258
|
+
code_verifier: codeVerifier
|
|
259
|
+
}).toString()
|
|
260
|
+
});
|
|
261
|
+
if (!response.ok) {
|
|
262
|
+
const body = await response.text().catch(() => "");
|
|
263
|
+
throw new AuthenticationError(`Token exchange failed (${response.status}): ${body}`);
|
|
264
|
+
}
|
|
265
|
+
const data = parseTokenResponse(await response.json());
|
|
266
|
+
this.currentRefreshToken = data.refresh_token ?? null;
|
|
267
|
+
return {
|
|
268
|
+
accessToken: data.access_token,
|
|
269
|
+
expiresAt: computeExpiresAt(data.expires_in),
|
|
270
|
+
refreshToken: data.refresh_token
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Try to exchange the stored refresh token for a new access token. Returns the
|
|
275
|
+
* token on success, `null` when the refresh token is definitively rejected
|
|
276
|
+
* (a 4xx — caller should re-authenticate), and throws on transient failures.
|
|
277
|
+
*/
|
|
278
|
+
async tryRefresh(tokenEndpoint, refreshToken) {
|
|
279
|
+
const response = await this.fetchFn(tokenEndpoint, {
|
|
280
|
+
method: "POST",
|
|
281
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
282
|
+
body: new URLSearchParams({
|
|
283
|
+
grant_type: "refresh_token",
|
|
284
|
+
client_id: this.clientId,
|
|
285
|
+
refresh_token: refreshToken
|
|
286
|
+
}).toString()
|
|
287
|
+
});
|
|
288
|
+
if (!response.ok) {
|
|
289
|
+
if (response.status >= 400 && response.status < 500) return null;
|
|
290
|
+
const body = await response.text().catch(() => "");
|
|
291
|
+
throw new AuthenticationError(`Token refresh failed (${response.status}): ${body}`);
|
|
292
|
+
}
|
|
293
|
+
const data = parseTokenResponse(await response.json());
|
|
294
|
+
if (data.refresh_token) {
|
|
295
|
+
this.currentRefreshToken = data.refresh_token;
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
accessToken: data.access_token,
|
|
299
|
+
expiresAt: computeExpiresAt(data.expires_in),
|
|
300
|
+
refreshToken: data.refresh_token ?? this.currentRefreshToken ?? void 0
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
var OAuth2RefreshTokenProvider = class {
|
|
305
|
+
constructor(clientId, refreshToken, issuerUrl, fetchFn) {
|
|
306
|
+
this.clientId = clientId;
|
|
307
|
+
this.issuerUrl = issuerUrl;
|
|
308
|
+
this.fetchFn = fetchFn;
|
|
309
|
+
this.currentRefreshToken = refreshToken;
|
|
310
|
+
}
|
|
311
|
+
clientId;
|
|
312
|
+
issuerUrl;
|
|
313
|
+
fetchFn;
|
|
314
|
+
type = "oauth2-refresh-token";
|
|
315
|
+
currentRefreshToken;
|
|
316
|
+
async getToken() {
|
|
317
|
+
const oidc = await discoverOidc(this.issuerUrl, this.fetchFn);
|
|
318
|
+
if (!oidc.token_endpoint) {
|
|
319
|
+
throw new AuthenticationError("OIDC discovery returned no token endpoint");
|
|
320
|
+
}
|
|
321
|
+
const response = await this.fetchFn(oidc.token_endpoint, {
|
|
322
|
+
method: "POST",
|
|
323
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
324
|
+
body: new URLSearchParams({
|
|
325
|
+
grant_type: "refresh_token",
|
|
326
|
+
client_id: this.clientId,
|
|
327
|
+
refresh_token: this.currentRefreshToken
|
|
328
|
+
}).toString()
|
|
329
|
+
});
|
|
330
|
+
if (!response.ok) {
|
|
331
|
+
const body = await response.text().catch(() => "");
|
|
332
|
+
if (response.status === 400) {
|
|
333
|
+
throw new AuthenticationError("Refresh token is invalid or expired. Please re-authenticate.");
|
|
334
|
+
}
|
|
335
|
+
throw new AuthenticationError(`Token refresh failed (${response.status}): ${body}`);
|
|
336
|
+
}
|
|
337
|
+
const data = parseTokenResponse(await response.json());
|
|
338
|
+
if (data.refresh_token) {
|
|
339
|
+
this.currentRefreshToken = data.refresh_token;
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
accessToken: data.access_token,
|
|
343
|
+
expiresAt: computeExpiresAt(data.expires_in),
|
|
344
|
+
refreshToken: data.refresh_token ?? this.currentRefreshToken
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
var BearerTokenProvider = class {
|
|
349
|
+
constructor(token) {
|
|
350
|
+
this.token = token;
|
|
351
|
+
}
|
|
352
|
+
token;
|
|
353
|
+
type = "bearer";
|
|
354
|
+
getToken() {
|
|
355
|
+
return Promise.resolve({ accessToken: this.token, expiresAt: null });
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
var DEFAULT_IDP_ISSUER_URL = "https://idp.gigadrive.de";
|
|
359
|
+
var DEFAULT_BASE_URL = "https://api.gigadrive.network";
|
|
360
|
+
var resolveCredentialProvider = (config) => {
|
|
361
|
+
const fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
|
|
362
|
+
const baseUrl = config.baseUrl ?? readEnv("GIGADRIVE_API_BASE_URL") ?? DEFAULT_BASE_URL;
|
|
363
|
+
const idpIssuerUrl = config.idpIssuerUrl ?? readEnv("GIGADRIVE_IDP_ISSUER_URL") ?? DEFAULT_IDP_ISSUER_URL;
|
|
364
|
+
const tokenUrl = `${baseUrl}/oauth2/token`;
|
|
365
|
+
if (config.bearerToken) {
|
|
366
|
+
return new BearerTokenProvider(config.bearerToken);
|
|
367
|
+
}
|
|
368
|
+
if (config.clientId && config.clientSecret) {
|
|
369
|
+
return new OAuth2ClientCredentialProvider(config.clientId, config.clientSecret, tokenUrl, fetchFn);
|
|
370
|
+
}
|
|
371
|
+
if (config.refreshToken && config.clientId) {
|
|
372
|
+
return new OAuth2RefreshTokenProvider(config.clientId, config.refreshToken, idpIssuerUrl, fetchFn);
|
|
373
|
+
}
|
|
374
|
+
if (config.onAuthorizationUrl && config.clientId) {
|
|
375
|
+
const redirectUri = config.redirectUri ?? "urn:ietf:wg:oauth:2.0:oob";
|
|
376
|
+
const scope = config.scopes && config.scopes.length > 0 ? config.scopes.join(" ") : DEFAULT_SCOPE;
|
|
377
|
+
return new OAuth2AuthorizationCodeProvider(
|
|
378
|
+
config.clientId,
|
|
379
|
+
idpIssuerUrl,
|
|
380
|
+
redirectUri,
|
|
381
|
+
config.onAuthorizationUrl,
|
|
382
|
+
fetchFn,
|
|
383
|
+
scope
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
const envBearerToken = readEnv("GIGADRIVE_BEARER_TOKEN");
|
|
387
|
+
if (envBearerToken) {
|
|
388
|
+
return new BearerTokenProvider(envBearerToken);
|
|
389
|
+
}
|
|
390
|
+
const envClientId = readEnv("GIGADRIVE_CLIENT_ID");
|
|
391
|
+
const envClientSecret = readEnv("GIGADRIVE_CLIENT_SECRET");
|
|
392
|
+
if (envClientId && envClientSecret) {
|
|
393
|
+
return new OAuth2ClientCredentialProvider(envClientId, envClientSecret, tokenUrl, fetchFn);
|
|
394
|
+
}
|
|
395
|
+
const envRefreshToken = readEnv("GIGADRIVE_REFRESH_TOKEN");
|
|
396
|
+
if (envRefreshToken && envClientId) {
|
|
397
|
+
return new OAuth2RefreshTokenProvider(envClientId, envRefreshToken, idpIssuerUrl, fetchFn);
|
|
398
|
+
}
|
|
399
|
+
throw new AuthenticationError(
|
|
400
|
+
"No credentials provided. Set one of:\n - GIGADRIVE_BEARER_TOKEN\n - GIGADRIVE_CLIENT_ID + GIGADRIVE_CLIENT_SECRET\n - GIGADRIVE_REFRESH_TOKEN + GIGADRIVE_CLIENT_ID\nOr pass credentials directly to the GigadriveClient constructor."
|
|
401
|
+
);
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// src/auth/token-manager.ts
|
|
405
|
+
var TokenManager = class {
|
|
406
|
+
constructor(provider) {
|
|
407
|
+
this.provider = provider;
|
|
408
|
+
}
|
|
409
|
+
provider;
|
|
410
|
+
cachedToken = null;
|
|
411
|
+
expiresAt = null;
|
|
412
|
+
pendingRefresh = null;
|
|
413
|
+
/**
|
|
414
|
+
* Returns a valid access token. Uses the cached token if still valid,
|
|
415
|
+
* otherwise fetches a new one from the credential provider.
|
|
416
|
+
*
|
|
417
|
+
* Concurrent calls are deduplicated — only one refresh runs at a time.
|
|
418
|
+
*/
|
|
419
|
+
async getToken() {
|
|
420
|
+
if (this.cachedToken && (this.expiresAt === null || Date.now() < this.expiresAt)) {
|
|
421
|
+
return this.cachedToken;
|
|
422
|
+
}
|
|
423
|
+
if (this.pendingRefresh) {
|
|
424
|
+
return this.pendingRefresh;
|
|
425
|
+
}
|
|
426
|
+
this.pendingRefresh = this.refresh();
|
|
427
|
+
try {
|
|
428
|
+
return await this.pendingRefresh;
|
|
429
|
+
} finally {
|
|
430
|
+
this.pendingRefresh = null;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Invalidates the cached token. The next `getToken()` call will trigger a refresh.
|
|
435
|
+
*/
|
|
436
|
+
invalidate() {
|
|
437
|
+
this.cachedToken = null;
|
|
438
|
+
this.expiresAt = null;
|
|
439
|
+
}
|
|
440
|
+
async refresh() {
|
|
441
|
+
const result = await this.provider.getToken();
|
|
442
|
+
this.cachedToken = result.accessToken;
|
|
443
|
+
this.expiresAt = result.expiresAt;
|
|
444
|
+
return result.accessToken;
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
// src/http-client.ts
|
|
449
|
+
var isRawBody = (body) => typeof body === "string" || body instanceof ArrayBuffer || typeof Uint8Array !== "undefined" && body instanceof Uint8Array || typeof Blob !== "undefined" && body instanceof Blob || typeof FormData !== "undefined" && body instanceof FormData || typeof ReadableStream !== "undefined" && body instanceof ReadableStream;
|
|
450
|
+
var hasHeader = (headers, name) => Object.keys(headers).some((key) => key.toLowerCase() === name.toLowerCase());
|
|
451
|
+
var HttpClient = class {
|
|
452
|
+
constructor(baseUrl, tokenManager, fetchFn) {
|
|
453
|
+
this.baseUrl = baseUrl;
|
|
454
|
+
this.tokenManager = tokenManager;
|
|
455
|
+
this.fetchFn = fetchFn;
|
|
456
|
+
}
|
|
457
|
+
baseUrl;
|
|
458
|
+
tokenManager;
|
|
459
|
+
fetchFn;
|
|
460
|
+
/** @internal */
|
|
461
|
+
async get(path, options) {
|
|
462
|
+
return this.request("GET", path, options);
|
|
463
|
+
}
|
|
464
|
+
/** @internal */
|
|
465
|
+
async post(path, body, options) {
|
|
466
|
+
return this.request("POST", path, { body, ...options });
|
|
467
|
+
}
|
|
468
|
+
/** @internal */
|
|
469
|
+
async put(path, body, options) {
|
|
470
|
+
return this.request("PUT", path, { body, ...options });
|
|
471
|
+
}
|
|
472
|
+
/** @internal */
|
|
473
|
+
async patch(path, body, options) {
|
|
474
|
+
return this.request("PATCH", path, { body, ...options });
|
|
475
|
+
}
|
|
476
|
+
/** @internal */
|
|
477
|
+
async delete(path, options) {
|
|
478
|
+
return this.request("DELETE", path, options);
|
|
479
|
+
}
|
|
480
|
+
/** @internal */
|
|
481
|
+
async postRaw(path, body, headers) {
|
|
482
|
+
return this.request("POST", path, { body, headers });
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Make an authenticated request and return the raw `Response` without parsing
|
|
486
|
+
* the body. Used for streamed (SSE) and binary responses.
|
|
487
|
+
*
|
|
488
|
+
* @throws {@link ApiError} if the response status is not OK.
|
|
489
|
+
* @internal
|
|
490
|
+
*/
|
|
491
|
+
async requestStream(method, path, options) {
|
|
492
|
+
const response = await this.fetchWithAuth(method, path, options);
|
|
493
|
+
if (!response.ok) {
|
|
494
|
+
throw await this.toApiError(response);
|
|
495
|
+
}
|
|
496
|
+
return response;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Make a raw fetch request to an arbitrary URL (e.g. presigned upload URLs
|
|
500
|
+
* or resumable upload endpoints). No auth header is injected and the base URL
|
|
501
|
+
* is not prepended.
|
|
502
|
+
*
|
|
503
|
+
* @param url - The full URL to fetch.
|
|
504
|
+
* @param init - Standard `RequestInit` options.
|
|
505
|
+
* @returns The raw `Response` object.
|
|
506
|
+
* @throws {@link ApiError} if the response status is not OK.
|
|
507
|
+
*
|
|
508
|
+
* @internal
|
|
509
|
+
*/
|
|
510
|
+
async fetchRaw(url, init) {
|
|
511
|
+
const response = await this.fetchFn(url, init);
|
|
512
|
+
if (!response.ok) {
|
|
513
|
+
throw new ApiError(response.statusText, response.status);
|
|
514
|
+
}
|
|
515
|
+
return response;
|
|
516
|
+
}
|
|
517
|
+
async request(method, path, options) {
|
|
518
|
+
const response = await this.fetchWithAuth(method, path, options);
|
|
519
|
+
return this.handleResponse(response);
|
|
520
|
+
}
|
|
521
|
+
/** Perform the request with auth injection and a single 401 refresh-retry. */
|
|
522
|
+
async fetchWithAuth(method, path, options) {
|
|
523
|
+
const url = this.buildUrl(path, options?.query);
|
|
524
|
+
const token = await this.tokenManager.getToken();
|
|
525
|
+
const headers = {
|
|
526
|
+
Authorization: `Bearer ${token}`,
|
|
527
|
+
...options?.headers
|
|
528
|
+
};
|
|
529
|
+
let requestBody = null;
|
|
530
|
+
if (options?.body !== void 0) {
|
|
531
|
+
if (isRawBody(options.body)) {
|
|
532
|
+
requestBody = options.body;
|
|
533
|
+
} else {
|
|
534
|
+
requestBody = JSON.stringify(options.body);
|
|
535
|
+
if (!hasHeader(headers, "Content-Type")) {
|
|
536
|
+
headers["Content-Type"] = "application/json";
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
const signal = options?.signal;
|
|
541
|
+
const response = await this.fetchFn(url, { method, headers, body: requestBody, signal });
|
|
542
|
+
const bodyIsOneShot = typeof ReadableStream !== "undefined" && requestBody instanceof ReadableStream;
|
|
543
|
+
if (response.status === 401 && !bodyIsOneShot) {
|
|
544
|
+
this.tokenManager.invalidate();
|
|
545
|
+
const retryToken = await this.tokenManager.getToken();
|
|
546
|
+
headers.Authorization = `Bearer ${retryToken}`;
|
|
547
|
+
const retryResponse = await this.fetchFn(url, { method, headers, body: requestBody, signal });
|
|
548
|
+
if (retryResponse.status === 401) {
|
|
549
|
+
throw new AuthenticationError("Authentication failed after token refresh");
|
|
550
|
+
}
|
|
551
|
+
return retryResponse;
|
|
552
|
+
}
|
|
553
|
+
return response;
|
|
554
|
+
}
|
|
555
|
+
async handleResponse(response) {
|
|
556
|
+
if (response.ok) {
|
|
557
|
+
if (response.status === 204) {
|
|
558
|
+
return void 0;
|
|
559
|
+
}
|
|
560
|
+
const text = await response.text();
|
|
561
|
+
if (!text) {
|
|
562
|
+
return void 0;
|
|
563
|
+
}
|
|
564
|
+
return JSON.parse(text);
|
|
565
|
+
}
|
|
566
|
+
throw await this.toApiError(response);
|
|
567
|
+
}
|
|
568
|
+
async toApiError(response) {
|
|
569
|
+
let message;
|
|
570
|
+
let code;
|
|
571
|
+
try {
|
|
572
|
+
const body = await response.json();
|
|
573
|
+
if (typeof body.error === "string") {
|
|
574
|
+
message = body.error;
|
|
575
|
+
} else if (body.error && typeof body.error === "object") {
|
|
576
|
+
message = body.error.message ?? response.statusText;
|
|
577
|
+
code = body.error.code;
|
|
578
|
+
} else {
|
|
579
|
+
message = response.statusText;
|
|
580
|
+
}
|
|
581
|
+
} catch {
|
|
582
|
+
message = response.statusText;
|
|
583
|
+
}
|
|
584
|
+
return new ApiError(message, response.status, code);
|
|
585
|
+
}
|
|
586
|
+
buildUrl(path, query) {
|
|
587
|
+
const url = `${this.baseUrl}${path}`;
|
|
588
|
+
if (!query) return url;
|
|
589
|
+
const params = new URLSearchParams();
|
|
590
|
+
for (const [key, value] of Object.entries(query)) {
|
|
591
|
+
if (value !== void 0) {
|
|
592
|
+
params.set(key, String(value));
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
const qs = params.toString();
|
|
596
|
+
return qs ? `${url}?${qs}` : url;
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
// src/streaming.ts
|
|
601
|
+
var extractData = (rawEvent) => {
|
|
602
|
+
const dataLines = rawEvent.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice("data:".length).replace(/^ /, ""));
|
|
603
|
+
if (dataLines.length === 0) return null;
|
|
604
|
+
const joined = dataLines.join("\n");
|
|
605
|
+
return joined.trim().length === 0 ? null : joined;
|
|
606
|
+
};
|
|
607
|
+
var parseEvent = (data) => {
|
|
608
|
+
try {
|
|
609
|
+
return JSON.parse(data);
|
|
610
|
+
} catch (error) {
|
|
611
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
612
|
+
throw new Error(`Failed to parse streamed SSE data as JSON: ${message}`);
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
async function* parseSSEStream(response) {
|
|
616
|
+
const body = response.body;
|
|
617
|
+
if (!body) {
|
|
618
|
+
throw new Error("The response has no readable body to stream.");
|
|
619
|
+
}
|
|
620
|
+
const reader = body.getReader();
|
|
621
|
+
const decoder = new TextDecoder();
|
|
622
|
+
let buffer = "";
|
|
623
|
+
const drain = function* (flush) {
|
|
624
|
+
let separator = buffer.indexOf("\n\n");
|
|
625
|
+
while (separator !== -1) {
|
|
626
|
+
const rawEvent = buffer.slice(0, separator);
|
|
627
|
+
buffer = buffer.slice(separator + 2);
|
|
628
|
+
const data = extractData(rawEvent);
|
|
629
|
+
if (data !== null && data !== "[DONE]") {
|
|
630
|
+
yield parseEvent(data);
|
|
631
|
+
}
|
|
632
|
+
separator = buffer.indexOf("\n\n");
|
|
633
|
+
}
|
|
634
|
+
if (flush) {
|
|
635
|
+
const data = extractData(buffer);
|
|
636
|
+
buffer = "";
|
|
637
|
+
if (data !== null && data !== "[DONE]") {
|
|
638
|
+
yield parseEvent(data);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
try {
|
|
643
|
+
for (; ; ) {
|
|
644
|
+
const { done, value } = await reader.read();
|
|
645
|
+
if (done) break;
|
|
646
|
+
buffer = (buffer + decoder.decode(value, { stream: true })).replace(/\r\n/g, "\n");
|
|
647
|
+
yield* drain(false);
|
|
648
|
+
}
|
|
649
|
+
yield* drain(true);
|
|
650
|
+
} finally {
|
|
651
|
+
await reader.cancel().catch(() => void 0);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// src/resources/base-resource.ts
|
|
656
|
+
var BaseResource = class {
|
|
657
|
+
constructor(httpClient) {
|
|
658
|
+
this.httpClient = httpClient;
|
|
659
|
+
}
|
|
660
|
+
httpClient;
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
// src/resources/ai-gateway/index.ts
|
|
664
|
+
var BASE = "/ai/v1";
|
|
665
|
+
var toBlob = (file) => {
|
|
666
|
+
if (file instanceof Uint8Array) return new Blob([file]);
|
|
667
|
+
if (typeof Blob !== "undefined" && file instanceof Blob) return file;
|
|
668
|
+
return new Blob([new Uint8Array(file)]);
|
|
669
|
+
};
|
|
670
|
+
var AiGatewayAudioResource = class extends BaseResource {
|
|
671
|
+
/**
|
|
672
|
+
* Synthesize speech from text. Returns the raw audio bytes.
|
|
673
|
+
*
|
|
674
|
+
* @returns The audio content as an `ArrayBuffer` (format per `response_format`).
|
|
675
|
+
*/
|
|
676
|
+
async speech(data, options) {
|
|
677
|
+
const response = await this.httpClient.requestStream("POST", `${BASE}/audio/speech`, {
|
|
678
|
+
body: data,
|
|
679
|
+
headers: options?.headers
|
|
680
|
+
});
|
|
681
|
+
return response.arrayBuffer();
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Transcribe an audio file to text. The file is sent as multipart form data.
|
|
685
|
+
*/
|
|
686
|
+
async transcriptions(data, options) {
|
|
687
|
+
const form = new FormData();
|
|
688
|
+
const { file, filename, ...rest } = data;
|
|
689
|
+
form.set("file", toBlob(file), filename ?? "audio");
|
|
690
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
691
|
+
if (value === void 0 || value === null) continue;
|
|
692
|
+
form.set(key, typeof value === "object" ? JSON.stringify(value) : String(value));
|
|
693
|
+
}
|
|
694
|
+
return this.httpClient.postRaw(`${BASE}/audio/transcriptions`, form, options?.headers);
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
var AiGatewayVideosResource = class extends BaseResource {
|
|
698
|
+
/** Generate a video from a prompt. */
|
|
699
|
+
async generations(data, options) {
|
|
700
|
+
return this.httpClient.post(`${BASE}/videos`, data, { headers: options?.headers });
|
|
701
|
+
}
|
|
702
|
+
/** List the video models available through the gateway. */
|
|
703
|
+
async listModels() {
|
|
704
|
+
return this.httpClient.get(`${BASE}/videos/models`);
|
|
705
|
+
}
|
|
706
|
+
};
|
|
707
|
+
var AiGatewayResource = class extends BaseResource {
|
|
708
|
+
/** Text-to-speech and speech-to-text. */
|
|
709
|
+
audio;
|
|
710
|
+
/** Video generation. */
|
|
711
|
+
videos;
|
|
712
|
+
constructor(httpClient) {
|
|
713
|
+
super(httpClient);
|
|
714
|
+
this.audio = new AiGatewayAudioResource(httpClient);
|
|
715
|
+
this.videos = new AiGatewayVideosResource(httpClient);
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Create a chat completion using the OpenAI-compatible endpoint.
|
|
719
|
+
*
|
|
720
|
+
* @param data - The chat completion request (model, messages, options, provider routing).
|
|
721
|
+
* @param options - Custom headers (e.g. `X-Gigadrive-Application-Id`).
|
|
722
|
+
*/
|
|
723
|
+
async chatCompletions(data, options) {
|
|
724
|
+
return this.httpClient.post(`${BASE}/chat/completions`, { ...data, stream: false }, { headers: options?.headers });
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Like {@link chatCompletions}, but also returns the raw response and the
|
|
728
|
+
* gateway request-id / cost headers.
|
|
729
|
+
*/
|
|
730
|
+
async chatCompletionsWithResponse(data, options) {
|
|
731
|
+
const response = await this.httpClient.requestStream("POST", `${BASE}/chat/completions`, {
|
|
732
|
+
body: { ...data, stream: false },
|
|
733
|
+
headers: options?.headers
|
|
734
|
+
});
|
|
735
|
+
const body = await response.json();
|
|
736
|
+
const cost = response.headers.get("X-Gigadrive-Cost-Micros");
|
|
737
|
+
const costMicros = cost !== null ? Number(cost) : NaN;
|
|
738
|
+
return {
|
|
739
|
+
data: body,
|
|
740
|
+
response,
|
|
741
|
+
requestId: response.headers.get("X-Gigadrive-Request-Id") ?? void 0,
|
|
742
|
+
costMicros: Number.isFinite(costMicros) ? costMicros : void 0
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Stream a chat completion as Server-Sent Events. Yields each chunk as it
|
|
747
|
+
* arrives and ends when the gateway closes the stream.
|
|
748
|
+
*/
|
|
749
|
+
async *chatCompletionsStream(data, options) {
|
|
750
|
+
const response = await this.httpClient.requestStream("POST", `${BASE}/chat/completions`, {
|
|
751
|
+
body: { ...data, stream: true },
|
|
752
|
+
headers: options?.headers
|
|
753
|
+
});
|
|
754
|
+
yield* parseSSEStream(response);
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Create a response using the Open Responses-compatible endpoint.
|
|
758
|
+
*/
|
|
759
|
+
async responses(data, options) {
|
|
760
|
+
return this.httpClient.post(`${BASE}/responses`, { ...data, stream: false }, { headers: options?.headers });
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Stream a response using the Open Responses-compatible endpoint.
|
|
764
|
+
*/
|
|
765
|
+
async *responsesStream(data, options) {
|
|
766
|
+
const response = await this.httpClient.requestStream("POST", `${BASE}/responses`, {
|
|
767
|
+
body: { ...data, stream: true },
|
|
768
|
+
headers: options?.headers
|
|
769
|
+
});
|
|
770
|
+
yield* parseSSEStream(response);
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* List all AI models available through the gateway.
|
|
774
|
+
*/
|
|
775
|
+
async listModels() {
|
|
776
|
+
return this.httpClient.get(`${BASE}/models`);
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Get details for a specific AI model by its ID.
|
|
780
|
+
*
|
|
781
|
+
* @param modelId - The model identifier (e.g. `"openai/gpt-4o"`).
|
|
782
|
+
*/
|
|
783
|
+
async getModel(modelId) {
|
|
784
|
+
return this.httpClient.get(`${BASE}/models/${encodeURIComponent(modelId)}`);
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
// src/resources/application-env-vars.ts
|
|
789
|
+
var ApplicationEnvVarsResource = class extends BaseResource {
|
|
790
|
+
/**
|
|
791
|
+
* List all environment variables for an application.
|
|
792
|
+
*
|
|
793
|
+
* @param applicationId - The application ID (UUID).
|
|
794
|
+
* @returns A paginated list of environment variables.
|
|
795
|
+
*
|
|
796
|
+
* @example
|
|
797
|
+
* ```ts
|
|
798
|
+
* const { items } = await client.applications.envVars.list('app-id');
|
|
799
|
+
* for (const v of items) {
|
|
800
|
+
* console.log(`${v.key}=${v.sensitive ? '***' : v.value}`);
|
|
801
|
+
* }
|
|
802
|
+
* ```
|
|
803
|
+
*/
|
|
804
|
+
async list(applicationId, query) {
|
|
805
|
+
return this.httpClient.get(`/applications/${applicationId}/env-vars`, {
|
|
806
|
+
query
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Create a new environment variable for an application.
|
|
811
|
+
*
|
|
812
|
+
* @param applicationId - The application ID (UUID).
|
|
813
|
+
* @param data - The variable key, value, and optional settings.
|
|
814
|
+
* @returns The newly created environment variable.
|
|
815
|
+
*
|
|
816
|
+
* @example
|
|
817
|
+
* ```ts
|
|
818
|
+
* const envVar = await client.applications.envVars.create('app-id', {
|
|
819
|
+
* key: 'DATABASE_URL',
|
|
820
|
+
* value: 'postgres://...',
|
|
821
|
+
* sensitive: true,
|
|
822
|
+
* });
|
|
823
|
+
* ```
|
|
824
|
+
*/
|
|
825
|
+
async create(applicationId, data) {
|
|
826
|
+
return this.httpClient.post(`/applications/${applicationId}/env-vars`, data);
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Update an existing environment variable. Only the fields you provide
|
|
830
|
+
* will be changed; omitted fields are left unchanged.
|
|
831
|
+
*
|
|
832
|
+
* @param applicationId - The application ID (UUID).
|
|
833
|
+
* @param envVarId - The environment variable ID (UUID).
|
|
834
|
+
* @param data - The fields to update.
|
|
835
|
+
* @returns The updated environment variable.
|
|
836
|
+
*
|
|
837
|
+
* @example
|
|
838
|
+
* ```ts
|
|
839
|
+
* await client.applications.envVars.update('app-id', 'var-id', {
|
|
840
|
+
* value: 'postgres://new-host/db',
|
|
841
|
+
* });
|
|
842
|
+
* ```
|
|
843
|
+
*/
|
|
844
|
+
async update(applicationId, envVarId, data) {
|
|
845
|
+
return this.httpClient.patch(`/applications/${applicationId}/env-vars/${envVarId}`, data);
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Permanently delete an environment variable.
|
|
849
|
+
*
|
|
850
|
+
* @param applicationId - The application ID (UUID).
|
|
851
|
+
* @param envVarId - The environment variable ID (UUID).
|
|
852
|
+
*
|
|
853
|
+
* @example
|
|
854
|
+
* ```ts
|
|
855
|
+
* await client.applications.envVars.delete('app-id', 'var-id');
|
|
856
|
+
* ```
|
|
857
|
+
*/
|
|
858
|
+
async delete(applicationId, envVarId) {
|
|
859
|
+
return this.httpClient.delete(`/applications/${applicationId}/env-vars/${envVarId}`);
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
|
|
863
|
+
// src/resources/application-requests.ts
|
|
864
|
+
var ApplicationRequestsResource = class extends BaseResource {
|
|
865
|
+
/**
|
|
866
|
+
* List requests for an application, with rich filtering and cursor pagination.
|
|
867
|
+
*
|
|
868
|
+
* @param applicationId - The application ID (UUID).
|
|
869
|
+
* @param query - Optional filters and pagination.
|
|
870
|
+
* @returns A page of request summaries.
|
|
871
|
+
*/
|
|
872
|
+
async list(applicationId, query) {
|
|
873
|
+
return this.httpClient.get(`/applications/${applicationId}/requests`, {
|
|
874
|
+
query
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Get a single request by ID, including sanitized request/response headers.
|
|
879
|
+
*
|
|
880
|
+
* @param applicationId - The application ID (UUID).
|
|
881
|
+
* @param requestId - The request record ID (UUID).
|
|
882
|
+
* @returns The full request record.
|
|
883
|
+
*/
|
|
884
|
+
async get(applicationId, requestId) {
|
|
885
|
+
return this.httpClient.get(`/applications/${applicationId}/requests/${requestId}`);
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
|
|
889
|
+
// src/upload/checksum.ts
|
|
890
|
+
var toHex = (buffer) => {
|
|
891
|
+
const bytes = new Uint8Array(buffer);
|
|
892
|
+
let hex = "";
|
|
893
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
894
|
+
hex += bytes[i].toString(16).padStart(2, "0");
|
|
895
|
+
}
|
|
896
|
+
return hex;
|
|
897
|
+
};
|
|
898
|
+
var subtleDigest = async (algorithm, bytes) => toHex(await crypto.subtle.digest(algorithm, bytes));
|
|
899
|
+
var importNodeCrypto = async () => {
|
|
900
|
+
if (typeof process === "undefined" || !process.versions?.node) return null;
|
|
901
|
+
try {
|
|
902
|
+
return await import("crypto");
|
|
903
|
+
} catch {
|
|
904
|
+
return null;
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
var md5Hex = async (bytes) => {
|
|
908
|
+
const nodeCrypto = await importNodeCrypto();
|
|
909
|
+
if (!nodeCrypto) {
|
|
910
|
+
throw new Error("MD5 checksums are only supported in Node.js. Provide checksumMd5 explicitly in the browser.");
|
|
911
|
+
}
|
|
912
|
+
return nodeCrypto.createHash("md5").update(bytes).digest("hex");
|
|
913
|
+
};
|
|
914
|
+
var computeChecksums = async (bytes, request = {}) => {
|
|
915
|
+
const checksums = { sha256: await subtleDigest("SHA-256", bytes) };
|
|
916
|
+
if (request.sha1) checksums.sha1 = await subtleDigest("SHA-1", bytes);
|
|
917
|
+
if (request.md5) checksums.md5 = await md5Hex(bytes);
|
|
918
|
+
return checksums;
|
|
919
|
+
};
|
|
920
|
+
var hashNodeFile = async (path, request = {}) => {
|
|
921
|
+
const [fs, nodeCrypto] = await Promise.all([import("fs"), import("crypto")]);
|
|
922
|
+
const sha256 = nodeCrypto.createHash("sha256");
|
|
923
|
+
const sha1 = request.sha1 ? nodeCrypto.createHash("sha1") : null;
|
|
924
|
+
const md5 = request.md5 ? nodeCrypto.createHash("md5") : null;
|
|
925
|
+
await new Promise((resolve, reject) => {
|
|
926
|
+
const stream = fs.createReadStream(path);
|
|
927
|
+
stream.on("data", (chunk) => {
|
|
928
|
+
sha256.update(chunk);
|
|
929
|
+
sha1?.update(chunk);
|
|
930
|
+
md5?.update(chunk);
|
|
931
|
+
});
|
|
932
|
+
stream.on("end", () => resolve());
|
|
933
|
+
stream.on("error", reject);
|
|
934
|
+
});
|
|
935
|
+
const checksums = { sha256: sha256.digest("hex") };
|
|
936
|
+
if (sha1) checksums.sha1 = sha1.digest("hex");
|
|
937
|
+
if (md5) checksums.md5 = md5.digest("hex");
|
|
938
|
+
return checksums;
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
// src/upload/content-type.ts
|
|
942
|
+
var MIME_TYPES = {
|
|
943
|
+
// Text & code
|
|
944
|
+
txt: "text/plain",
|
|
945
|
+
csv: "text/csv",
|
|
946
|
+
html: "text/html",
|
|
947
|
+
htm: "text/html",
|
|
948
|
+
css: "text/css",
|
|
949
|
+
js: "text/javascript",
|
|
950
|
+
mjs: "text/javascript",
|
|
951
|
+
json: "application/json",
|
|
952
|
+
xml: "application/xml",
|
|
953
|
+
md: "text/markdown",
|
|
954
|
+
// Images
|
|
955
|
+
png: "image/png",
|
|
956
|
+
jpg: "image/jpeg",
|
|
957
|
+
jpeg: "image/jpeg",
|
|
958
|
+
gif: "image/gif",
|
|
959
|
+
webp: "image/webp",
|
|
960
|
+
svg: "image/svg+xml",
|
|
961
|
+
avif: "image/avif",
|
|
962
|
+
ico: "image/x-icon",
|
|
963
|
+
bmp: "image/bmp",
|
|
964
|
+
tiff: "image/tiff",
|
|
965
|
+
heic: "image/heic",
|
|
966
|
+
// Audio
|
|
967
|
+
mp3: "audio/mpeg",
|
|
968
|
+
wav: "audio/wav",
|
|
969
|
+
ogg: "audio/ogg",
|
|
970
|
+
oga: "audio/ogg",
|
|
971
|
+
flac: "audio/flac",
|
|
972
|
+
aac: "audio/aac",
|
|
973
|
+
m4a: "audio/mp4",
|
|
974
|
+
// Video
|
|
975
|
+
mp4: "video/mp4",
|
|
976
|
+
webm: "video/webm",
|
|
977
|
+
mov: "video/quicktime",
|
|
978
|
+
avi: "video/x-msvideo",
|
|
979
|
+
mkv: "video/x-matroska",
|
|
980
|
+
// Documents
|
|
981
|
+
pdf: "application/pdf",
|
|
982
|
+
doc: "application/msword",
|
|
983
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
984
|
+
xls: "application/vnd.ms-excel",
|
|
985
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
986
|
+
ppt: "application/vnd.ms-powerpoint",
|
|
987
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
988
|
+
// Archives & binaries
|
|
989
|
+
zip: "application/zip",
|
|
990
|
+
gz: "application/gzip",
|
|
991
|
+
tar: "application/x-tar",
|
|
992
|
+
br: "application/x-brotli",
|
|
993
|
+
wasm: "application/wasm",
|
|
994
|
+
// Fonts
|
|
995
|
+
woff: "font/woff",
|
|
996
|
+
woff2: "font/woff2",
|
|
997
|
+
ttf: "font/ttf",
|
|
998
|
+
otf: "font/otf"
|
|
999
|
+
};
|
|
1000
|
+
var inferContentType = (nameOrKey) => {
|
|
1001
|
+
const lastDot = nameOrKey.lastIndexOf(".");
|
|
1002
|
+
if (lastDot === -1 || lastDot === nameOrKey.length - 1) return void 0;
|
|
1003
|
+
const ext = nameOrKey.slice(lastDot + 1).toLowerCase();
|
|
1004
|
+
return MIME_TYPES[ext];
|
|
1005
|
+
};
|
|
1006
|
+
|
|
1007
|
+
// src/upload/source.ts
|
|
1008
|
+
var isNode = () => typeof process !== "undefined" && !!process.versions?.node;
|
|
1009
|
+
var toBytes = async (data) => {
|
|
1010
|
+
if (typeof Blob !== "undefined" && data instanceof Blob) return new Uint8Array(await data.arrayBuffer());
|
|
1011
|
+
if (data instanceof Uint8Array) return data;
|
|
1012
|
+
if (data instanceof ArrayBuffer) return new Uint8Array(data);
|
|
1013
|
+
throw new Error("Unsupported upload data type. Pass a File, Blob, Buffer, Uint8Array, or ArrayBuffer.");
|
|
1014
|
+
};
|
|
1015
|
+
var sizeOf = (data) => {
|
|
1016
|
+
if (typeof Blob !== "undefined" && data instanceof Blob) return data.size;
|
|
1017
|
+
if (data instanceof Uint8Array) return data.byteLength;
|
|
1018
|
+
if (data instanceof ArrayBuffer) return data.byteLength;
|
|
1019
|
+
throw new Error("Cannot determine upload size. Pass a File, Blob, Buffer, Uint8Array, or ArrayBuffer.");
|
|
1020
|
+
};
|
|
1021
|
+
var toTusFile = (data) => {
|
|
1022
|
+
if (data instanceof Uint8Array) return isNode() ? data : new Blob([data]);
|
|
1023
|
+
if (typeof Blob !== "undefined" && data instanceof Blob) return data;
|
|
1024
|
+
const bytes = new Uint8Array(data);
|
|
1025
|
+
return isNode() ? bytes : new Blob([bytes]);
|
|
1026
|
+
};
|
|
1027
|
+
var buildChecksums = (input, sha256) => {
|
|
1028
|
+
const checksums = { sha256 };
|
|
1029
|
+
if (input.checksumSha1) checksums.sha1 = input.checksumSha1;
|
|
1030
|
+
if (input.checksumMd5) checksums.md5 = input.checksumMd5;
|
|
1031
|
+
return checksums;
|
|
1032
|
+
};
|
|
1033
|
+
var resolveUploadSource = async (input, options = {}) => {
|
|
1034
|
+
const sourceCount = [input.data !== void 0, input.path !== void 0, input.stream !== void 0].filter(
|
|
1035
|
+
Boolean
|
|
1036
|
+
).length;
|
|
1037
|
+
if (sourceCount > 1) {
|
|
1038
|
+
throw new Error("Provide only one upload source: data, path, or stream.");
|
|
1039
|
+
}
|
|
1040
|
+
const hash = options.hash !== false;
|
|
1041
|
+
const contentType = input.contentType ?? inferContentType(input.key);
|
|
1042
|
+
if (input.data !== void 0) {
|
|
1043
|
+
const size = input.contentLength ?? sizeOf(input.data);
|
|
1044
|
+
const sha256 = input.checksumSha256 ?? (hash ? (await computeChecksums(await toBytes(input.data))).sha256 : "");
|
|
1045
|
+
return {
|
|
1046
|
+
tusFile: toTusFile(input.data),
|
|
1047
|
+
size,
|
|
1048
|
+
contentType,
|
|
1049
|
+
checksums: buildChecksums(input, sha256),
|
|
1050
|
+
requiresFiniteChunkSize: false
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
if (input.path !== void 0) {
|
|
1054
|
+
if (!isNode()) throw new Error("Uploading from a file path is only supported in Node.js.");
|
|
1055
|
+
const fs = await import("fs");
|
|
1056
|
+
const size = input.contentLength ?? fs.statSync(input.path).size;
|
|
1057
|
+
const sha256 = input.checksumSha256 ?? (hash ? (await hashNodeFile(input.path)).sha256 : "");
|
|
1058
|
+
const tusFile = fs.createReadStream(input.path);
|
|
1059
|
+
return { tusFile, size, contentType, checksums: buildChecksums(input, sha256), requiresFiniteChunkSize: true };
|
|
1060
|
+
}
|
|
1061
|
+
if (input.stream !== void 0) {
|
|
1062
|
+
if (input.contentLength === void 0) {
|
|
1063
|
+
throw new Error("Uploading from a stream requires contentLength to be provided.");
|
|
1064
|
+
}
|
|
1065
|
+
if (hash && !input.checksumSha256) {
|
|
1066
|
+
throw new Error("Uploading from a stream requires checksumSha256 to be provided.");
|
|
1067
|
+
}
|
|
1068
|
+
return {
|
|
1069
|
+
tusFile: input.stream,
|
|
1070
|
+
size: input.contentLength,
|
|
1071
|
+
contentType,
|
|
1072
|
+
checksums: buildChecksums(input, input.checksumSha256 ?? ""),
|
|
1073
|
+
requiresFiniteChunkSize: true
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
throw new Error("No upload source provided. Pass one of: data, path, or stream.");
|
|
1077
|
+
};
|
|
1078
|
+
|
|
1079
|
+
// src/upload/transport.ts
|
|
1080
|
+
var tus = __toESM(require("tus-js-client"));
|
|
1081
|
+
var DEFAULT_STREAM_CHUNK_SIZE = 50 * 1024 * 1024;
|
|
1082
|
+
var DEFAULT_RETRY_DELAYS = [0, 1e3, 3e3, 5e3];
|
|
1083
|
+
var createAbortError = () => {
|
|
1084
|
+
const error = new Error("The upload was aborted.");
|
|
1085
|
+
error.name = "AbortError";
|
|
1086
|
+
return error;
|
|
1087
|
+
};
|
|
1088
|
+
var tusUploadTransport = (params) => new Promise((resolve, reject) => {
|
|
1089
|
+
if (params.signal?.aborted) {
|
|
1090
|
+
reject(createAbortError());
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
let settled = false;
|
|
1094
|
+
const cleanup = () => {
|
|
1095
|
+
if (params.signal) params.signal.removeEventListener("abort", onAbort);
|
|
1096
|
+
};
|
|
1097
|
+
const finish = (fn) => {
|
|
1098
|
+
if (settled) return;
|
|
1099
|
+
settled = true;
|
|
1100
|
+
cleanup();
|
|
1101
|
+
fn();
|
|
1102
|
+
};
|
|
1103
|
+
const upload = new tus.Upload(params.data, {
|
|
1104
|
+
uploadUrl: params.uploadUrl,
|
|
1105
|
+
uploadSize: params.uploadSize,
|
|
1106
|
+
headers: params.headers,
|
|
1107
|
+
chunkSize: params.chunkSize ?? Infinity,
|
|
1108
|
+
retryDelays: params.retryDelays === void 0 ? DEFAULT_RETRY_DELAYS : params.retryDelays,
|
|
1109
|
+
storeFingerprintForResuming: params.resume ?? false,
|
|
1110
|
+
removeFingerprintOnSuccess: true,
|
|
1111
|
+
...params.urlStorage ? { urlStorage: params.urlStorage } : {},
|
|
1112
|
+
onProgress: params.onProgress ?? null,
|
|
1113
|
+
onError: (error) => finish(() => reject(error)),
|
|
1114
|
+
onSuccess: () => finish(() => resolve())
|
|
1115
|
+
});
|
|
1116
|
+
const onAbort = () => {
|
|
1117
|
+
void upload.abort();
|
|
1118
|
+
finish(() => reject(createAbortError()));
|
|
1119
|
+
};
|
|
1120
|
+
if (params.signal) params.signal.addEventListener("abort", onAbort, { once: true });
|
|
1121
|
+
upload.start();
|
|
1122
|
+
});
|
|
1123
|
+
var runResolvedUpload = (transport, uploadUrl, resolved, options = {}, headers = { "Tus-Resumable": "1.0.0" }) => transport({
|
|
1124
|
+
data: resolved.tusFile,
|
|
1125
|
+
uploadUrl,
|
|
1126
|
+
uploadSize: resolved.size,
|
|
1127
|
+
headers,
|
|
1128
|
+
chunkSize: options.chunkSize ?? (resolved.requiresFiniteChunkSize ? DEFAULT_STREAM_CHUNK_SIZE : void 0),
|
|
1129
|
+
retryDelays: options.retryDelays,
|
|
1130
|
+
onProgress: options.onProgress,
|
|
1131
|
+
signal: options.signal,
|
|
1132
|
+
resume: options.resume,
|
|
1133
|
+
urlStorage: options.urlStorage
|
|
1134
|
+
});
|
|
1135
|
+
var tusStatus = (error) => {
|
|
1136
|
+
const response = error?.originalResponse;
|
|
1137
|
+
return response ? response.getStatus() : void 0;
|
|
1138
|
+
};
|
|
1139
|
+
var toUploadError = (error) => {
|
|
1140
|
+
if (error instanceof Error && error.name === "AbortError") throw error;
|
|
1141
|
+
const status = tusStatus(error);
|
|
1142
|
+
if (status === 401 || status === 403 || status === 410) {
|
|
1143
|
+
throw new UploadSessionExpiredError(void 0, error);
|
|
1144
|
+
}
|
|
1145
|
+
throw new UploadError(error instanceof Error ? error.message : "The upload failed.", error);
|
|
1146
|
+
};
|
|
1147
|
+
|
|
1148
|
+
// src/resources/storage-buckets.ts
|
|
1149
|
+
var StorageBucketsResource = class extends BaseResource {
|
|
1150
|
+
/**
|
|
1151
|
+
* List all storage buckets for an application.
|
|
1152
|
+
*
|
|
1153
|
+
* @param applicationId - The application ID (UUID).
|
|
1154
|
+
* @param query - Optional pagination parameters.
|
|
1155
|
+
* @returns A paginated list of storage buckets.
|
|
1156
|
+
*
|
|
1157
|
+
* @example
|
|
1158
|
+
* ```ts
|
|
1159
|
+
* const { items, total } = await client.applications.storage.buckets.list('app-id');
|
|
1160
|
+
* console.log(`${total} buckets found`);
|
|
1161
|
+
* ```
|
|
1162
|
+
*/
|
|
1163
|
+
async list(applicationId, query) {
|
|
1164
|
+
return this.httpClient.get(`/applications/${applicationId}/storage/buckets`, {
|
|
1165
|
+
query
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Create a new storage bucket.
|
|
1170
|
+
*
|
|
1171
|
+
* @param applicationId - The application ID (UUID).
|
|
1172
|
+
* @param data - Bucket name, environment, and optional slug/visibility.
|
|
1173
|
+
* @returns The newly created bucket.
|
|
1174
|
+
*
|
|
1175
|
+
* @example
|
|
1176
|
+
* ```ts
|
|
1177
|
+
* const bucket = await client.applications.storage.buckets.create('app-id', {
|
|
1178
|
+
* name: 'Assets',
|
|
1179
|
+
* environmentId: 'env-id',
|
|
1180
|
+
* visibility: 'public',
|
|
1181
|
+
* });
|
|
1182
|
+
* console.log(`Bucket CDN: https://${bucket.cdnHostname}`);
|
|
1183
|
+
* ```
|
|
1184
|
+
*/
|
|
1185
|
+
async create(applicationId, data) {
|
|
1186
|
+
return this.httpClient.post(`/applications/${applicationId}/storage/buckets`, data);
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Get a storage bucket by ID.
|
|
1190
|
+
*
|
|
1191
|
+
* @param applicationId - The application ID (UUID).
|
|
1192
|
+
* @param bucketId - The bucket ID (UUID).
|
|
1193
|
+
* @returns The bucket details.
|
|
1194
|
+
*
|
|
1195
|
+
* @example
|
|
1196
|
+
* ```ts
|
|
1197
|
+
* const bucket = await client.applications.storage.buckets.get('app-id', 'bucket-id');
|
|
1198
|
+
* console.log(`${bucket.name} (${bucket.visibility})`);
|
|
1199
|
+
* ```
|
|
1200
|
+
*/
|
|
1201
|
+
async get(applicationId, bucketId) {
|
|
1202
|
+
return this.httpClient.get(`/applications/${applicationId}/storage/buckets/${bucketId}`);
|
|
1203
|
+
}
|
|
1204
|
+
/**
|
|
1205
|
+
* Permanently delete a storage bucket and all its objects.
|
|
1206
|
+
*
|
|
1207
|
+
* @param applicationId - The application ID (UUID).
|
|
1208
|
+
* @param bucketId - The bucket ID (UUID).
|
|
1209
|
+
*
|
|
1210
|
+
* @example
|
|
1211
|
+
* ```ts
|
|
1212
|
+
* await client.applications.storage.buckets.delete('app-id', 'bucket-id');
|
|
1213
|
+
* ```
|
|
1214
|
+
*/
|
|
1215
|
+
async delete(applicationId, bucketId) {
|
|
1216
|
+
return this.httpClient.delete(`/applications/${applicationId}/storage/buckets/${bucketId}`);
|
|
1217
|
+
}
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
// src/resources/storage-objects.ts
|
|
1221
|
+
var StorageObjectsResource = class extends BaseResource {
|
|
1222
|
+
/**
|
|
1223
|
+
* List objects in a storage bucket, optionally filtered by key prefix and
|
|
1224
|
+
* grouped into virtual folders by a delimiter.
|
|
1225
|
+
*
|
|
1226
|
+
* @param applicationId - The application ID (UUID).
|
|
1227
|
+
* @param bucketId - The bucket ID (UUID).
|
|
1228
|
+
* @param query - Optional prefix/delimiter/cursor/limit parameters.
|
|
1229
|
+
* @returns A page of objects plus any common (folder) prefixes and a `nextCursor`.
|
|
1230
|
+
*
|
|
1231
|
+
* @example
|
|
1232
|
+
* ```ts
|
|
1233
|
+
* // List the "images/" folder, one level deep
|
|
1234
|
+
* const { items, commonPrefixes, nextCursor } = await client.applications.storage.objects.list(
|
|
1235
|
+
* 'app-id', 'bucket-id', { prefix: 'images/', limit: 100 },
|
|
1236
|
+
* );
|
|
1237
|
+
* ```
|
|
1238
|
+
*/
|
|
1239
|
+
async list(applicationId, bucketId, query) {
|
|
1240
|
+
return this.httpClient.get(`/applications/${applicationId}/storage/buckets/${bucketId}/objects`, {
|
|
1241
|
+
query
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Get metadata for a specific object by its object ID.
|
|
1246
|
+
*
|
|
1247
|
+
* @param applicationId - The application ID (UUID).
|
|
1248
|
+
* @param bucketId - The bucket ID (UUID).
|
|
1249
|
+
* @param objectId - The object ID (UUID) — not the object key. Use {@link getByKey} to resolve a key.
|
|
1250
|
+
* @returns The object metadata.
|
|
1251
|
+
*
|
|
1252
|
+
* @example
|
|
1253
|
+
* ```ts
|
|
1254
|
+
* const obj = await client.applications.storage.objects.get('app-id', 'bucket-id', 'object-id');
|
|
1255
|
+
* console.log(`${obj.key}: ${obj.contentLength} bytes, type: ${obj.contentType}`);
|
|
1256
|
+
* ```
|
|
1257
|
+
*/
|
|
1258
|
+
async get(applicationId, bucketId, objectId) {
|
|
1259
|
+
return this.httpClient.get(`/applications/${applicationId}/storage/buckets/${bucketId}/objects/${objectId}`);
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Resolve an object by its key (path) instead of its ID. Convenience over
|
|
1263
|
+
* {@link list} — lists with the key as the prefix and returns the exact match,
|
|
1264
|
+
* paging through results until found, or `null` if no object with that key
|
|
1265
|
+
* exists. (The API has no get-by-key endpoint.)
|
|
1266
|
+
*
|
|
1267
|
+
* @param applicationId - The application ID (UUID).
|
|
1268
|
+
* @param bucketId - The bucket ID (UUID).
|
|
1269
|
+
* @param key - The object key/path (e.g. `"images/photo.jpg"`).
|
|
1270
|
+
* @returns The matching object, or `null` if not found.
|
|
1271
|
+
*/
|
|
1272
|
+
async getByKey(applicationId, bucketId, key) {
|
|
1273
|
+
let cursor;
|
|
1274
|
+
do {
|
|
1275
|
+
const page = await this.list(applicationId, bucketId, { prefix: key, delimiter: "", cursor });
|
|
1276
|
+
const match = page.items.find((object) => object.key === key);
|
|
1277
|
+
if (match) return match;
|
|
1278
|
+
cursor = page.nextCursor;
|
|
1279
|
+
} while (cursor);
|
|
1280
|
+
return null;
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Permanently delete an object from a storage bucket.
|
|
1284
|
+
*
|
|
1285
|
+
* @param applicationId - The application ID (UUID).
|
|
1286
|
+
* @param bucketId - The bucket ID (UUID).
|
|
1287
|
+
* @param objectId - The object ID (UUID) — not the object key.
|
|
1288
|
+
*
|
|
1289
|
+
* @example
|
|
1290
|
+
* ```ts
|
|
1291
|
+
* await client.applications.storage.objects.delete('app-id', 'bucket-id', 'object-id');
|
|
1292
|
+
* ```
|
|
1293
|
+
*/
|
|
1294
|
+
async delete(applicationId, bucketId, objectId) {
|
|
1295
|
+
return this.httpClient.delete(`/applications/${applicationId}/storage/buckets/${bucketId}/objects/${objectId}`);
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Get an access URL for a storage object. For objects in public buckets,
|
|
1299
|
+
* returns a stable CDN URL. For private buckets, returns a time-limited
|
|
1300
|
+
* signed URL.
|
|
1301
|
+
*
|
|
1302
|
+
* @param applicationId - The application ID (UUID).
|
|
1303
|
+
* @param bucketId - The bucket ID (UUID).
|
|
1304
|
+
* @param objectId - The object ID (UUID) — not the object key.
|
|
1305
|
+
* @param options - Optional `expiresInSeconds` for signed URLs (60–86400).
|
|
1306
|
+
* @returns The access URL and its type/expiry.
|
|
1307
|
+
*
|
|
1308
|
+
* @example
|
|
1309
|
+
* ```ts
|
|
1310
|
+
* const access = await client.applications.storage.objects.getAccessUrl('app-id', 'bucket-id', 'object-id', {
|
|
1311
|
+
* expiresInSeconds: 3600,
|
|
1312
|
+
* });
|
|
1313
|
+
* if (access.accessType === 'signed') {
|
|
1314
|
+
* console.log(`Signed URL expires at ${access.expiresAt}`);
|
|
1315
|
+
* }
|
|
1316
|
+
* console.log(`Download: ${access.url}`);
|
|
1317
|
+
* ```
|
|
1318
|
+
*/
|
|
1319
|
+
async getAccessUrl(applicationId, bucketId, objectId, options) {
|
|
1320
|
+
return this.httpClient.get(
|
|
1321
|
+
`/applications/${applicationId}/storage/buckets/${bucketId}/objects/${objectId}/access-url`,
|
|
1322
|
+
{ query: { expiresInSeconds: options?.expiresInSeconds } }
|
|
1323
|
+
);
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1326
|
+
|
|
1327
|
+
// src/resources/storage-upload-sessions.ts
|
|
1328
|
+
var StorageUploadSessionsResource = class extends BaseResource {
|
|
1329
|
+
constructor(httpClient, transport = tusUploadTransport) {
|
|
1330
|
+
super(httpClient);
|
|
1331
|
+
this.transport = transport;
|
|
1332
|
+
}
|
|
1333
|
+
transport;
|
|
1334
|
+
/**
|
|
1335
|
+
* List upload sessions for a bucket.
|
|
1336
|
+
*
|
|
1337
|
+
* @param applicationId - The application ID (UUID).
|
|
1338
|
+
* @param bucketId - The bucket ID (UUID).
|
|
1339
|
+
* @param query - Optional pagination parameters.
|
|
1340
|
+
* @returns A paginated list of upload sessions.
|
|
1341
|
+
*/
|
|
1342
|
+
async list(applicationId, bucketId, query) {
|
|
1343
|
+
return this.httpClient.get(`/applications/${applicationId}/storage/buckets/${bucketId}/uploads`, {
|
|
1344
|
+
query
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
/**
|
|
1348
|
+
* Create an upload session. Returns the session metadata and a signed upload
|
|
1349
|
+
* URL for sending file data. This is the low-level method — for most use
|
|
1350
|
+
* cases prefer `client.applications.storage.upload()` which also computes the
|
|
1351
|
+
* required SHA-256 checksum for you.
|
|
1352
|
+
*
|
|
1353
|
+
* @param applicationId - The application ID (UUID).
|
|
1354
|
+
* @param bucketId - The bucket ID (UUID).
|
|
1355
|
+
* @param data - Object key, content length, SHA-256 checksum, and optional content type / extra checksums.
|
|
1356
|
+
* @returns The session and resumable upload instructions (URL, method, headers).
|
|
1357
|
+
*/
|
|
1358
|
+
async create(applicationId, bucketId, data) {
|
|
1359
|
+
return this.httpClient.post(`/applications/${applicationId}/storage/buckets/${bucketId}/uploads`, data);
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Get an upload session by ID. Use this to track server-side processing after
|
|
1363
|
+
* an upload (poll until `state === 'completed'`).
|
|
1364
|
+
*
|
|
1365
|
+
* @param applicationId - The application ID (UUID).
|
|
1366
|
+
* @param bucketId - The bucket ID (UUID).
|
|
1367
|
+
* @param sessionId - The upload session ID (UUID).
|
|
1368
|
+
* @returns The current session state.
|
|
1369
|
+
*/
|
|
1370
|
+
async get(applicationId, bucketId, sessionId) {
|
|
1371
|
+
return this.httpClient.get(`/applications/${applicationId}/storage/buckets/${bucketId}/uploads/${sessionId}`);
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Upload bytes directly to a pre-existing signed upload URL — for example, a
|
|
1375
|
+
* URL returned by {@link create} or handed to your client by a backend. Skips
|
|
1376
|
+
* session creation and checksum computation.
|
|
1377
|
+
*
|
|
1378
|
+
* @param url - A signed upload URL.
|
|
1379
|
+
* @param source - The bytes to upload.
|
|
1380
|
+
* @param options - Chunk size, retry config, progress, abort signal, resume,
|
|
1381
|
+
* and any required `headers` returned with the upload session.
|
|
1382
|
+
* @throws {@link UploadError} if the upload fails after all retries.
|
|
1383
|
+
*/
|
|
1384
|
+
async uploadToUrl(url, source, options) {
|
|
1385
|
+
const resolved = await resolveUploadSource({ key: "", ...source }, { hash: false });
|
|
1386
|
+
const headers = { "Tus-Resumable": "1.0.0", ...options?.headers };
|
|
1387
|
+
await runResolvedUpload(this.transport, url, resolved, options, headers).catch(toUploadError);
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Resume an interrupted upload to a previously issued signed upload URL. The
|
|
1391
|
+
* resumable protocol negotiates the current offset and continues from there.
|
|
1392
|
+
*
|
|
1393
|
+
* @param url - The signed upload URL from the original {@link create} call.
|
|
1394
|
+
* @param source - The full bytes to upload (the same content as the original).
|
|
1395
|
+
* @param options - Chunk size, retry config, progress, abort signal.
|
|
1396
|
+
*/
|
|
1397
|
+
async resumeFromUrl(url, source, options) {
|
|
1398
|
+
return this.uploadToUrl(url, source, { ...options, resume: true });
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
|
|
1402
|
+
// src/resources/application-storage.ts
|
|
1403
|
+
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1404
|
+
var ApplicationStorageResource = class {
|
|
1405
|
+
/** Create, list, get, and delete storage buckets. */
|
|
1406
|
+
buckets;
|
|
1407
|
+
/** List, get, delete objects, and generate access URLs. */
|
|
1408
|
+
objects;
|
|
1409
|
+
/** Low-level upload sessions and direct-to-URL byte uploads. */
|
|
1410
|
+
uploadSessions;
|
|
1411
|
+
transport;
|
|
1412
|
+
constructor(httpClient, transport = tusUploadTransport) {
|
|
1413
|
+
this.transport = transport;
|
|
1414
|
+
this.buckets = new StorageBucketsResource(httpClient);
|
|
1415
|
+
this.objects = new StorageObjectsResource(httpClient);
|
|
1416
|
+
this.uploadSessions = new StorageUploadSessionsResource(httpClient, transport);
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Upload a file to a storage bucket. Handles the full flow: computes the
|
|
1420
|
+
* required SHA-256 checksum, creates an upload session, uploads the bytes with
|
|
1421
|
+
* automatic retries (and optional progress/abort/resume), and returns the
|
|
1422
|
+
* public URL.
|
|
1423
|
+
*
|
|
1424
|
+
* Accepts browser `File`/`Blob`, Node `Buffer`/`Uint8Array`/`ArrayBuffer`, a
|
|
1425
|
+
* Node filesystem `path`, or a Node readable `stream` (with `contentLength`
|
|
1426
|
+
* and `checksumSha256`). The content type is inferred from `key` when omitted.
|
|
1427
|
+
*
|
|
1428
|
+
* @param input - What and where to upload, plus optional transfer options.
|
|
1429
|
+
* @returns The upload session and public object URL (and the finalized object when `waitForCompletion` is set).
|
|
1430
|
+
* @throws {@link UploadError} if the byte upload fails.
|
|
1431
|
+
* @throws {@link UploadSessionExpiredError} if the session expires mid-upload.
|
|
1432
|
+
*
|
|
1433
|
+
* @example
|
|
1434
|
+
* ```ts
|
|
1435
|
+
* const { session, url, object } = await client.applications.storage.upload({
|
|
1436
|
+
* applicationId, bucketId, key: 'reports/q1.pdf', path: './q1.pdf',
|
|
1437
|
+
* waitForCompletion: true,
|
|
1438
|
+
* });
|
|
1439
|
+
* console.log(object?.contentLength, 'bytes available at', url);
|
|
1440
|
+
* ```
|
|
1441
|
+
*/
|
|
1442
|
+
async upload(input) {
|
|
1443
|
+
const resolved = await resolveUploadSource({
|
|
1444
|
+
key: input.key,
|
|
1445
|
+
data: input.data,
|
|
1446
|
+
path: input.path,
|
|
1447
|
+
stream: input.stream,
|
|
1448
|
+
contentType: input.contentType,
|
|
1449
|
+
contentLength: input.contentLength,
|
|
1450
|
+
checksumSha256: input.checksumSha256,
|
|
1451
|
+
checksumSha1: input.checksumSha1,
|
|
1452
|
+
checksumMd5: input.checksumMd5
|
|
1453
|
+
});
|
|
1454
|
+
const { session, upload } = await this.uploadSessions.create(input.applicationId, input.bucketId, {
|
|
1455
|
+
key: input.key,
|
|
1456
|
+
contentLength: resolved.size,
|
|
1457
|
+
checksumSha256: resolved.checksums.sha256,
|
|
1458
|
+
contentType: resolved.contentType,
|
|
1459
|
+
checksumSha1: resolved.checksums.sha1,
|
|
1460
|
+
checksumMd5: resolved.checksums.md5
|
|
1461
|
+
});
|
|
1462
|
+
await runResolvedUpload(
|
|
1463
|
+
this.transport,
|
|
1464
|
+
upload.url,
|
|
1465
|
+
resolved,
|
|
1466
|
+
{
|
|
1467
|
+
chunkSize: input.chunkSize,
|
|
1468
|
+
retryDelays: input.retryDelays,
|
|
1469
|
+
onProgress: input.onProgress,
|
|
1470
|
+
signal: input.signal,
|
|
1471
|
+
resume: input.resume,
|
|
1472
|
+
urlStorage: input.urlStorage
|
|
1473
|
+
},
|
|
1474
|
+
// Forward any required headers the API issued with the session.
|
|
1475
|
+
{ "Tus-Resumable": "1.0.0", ...upload.headers }
|
|
1476
|
+
).catch(toUploadError);
|
|
1477
|
+
if (input.waitForCompletion) {
|
|
1478
|
+
const options = typeof input.waitForCompletion === "object" ? input.waitForCompletion : void 0;
|
|
1479
|
+
const completed = await this.waitForCompletion(input.applicationId, input.bucketId, session.id, options);
|
|
1480
|
+
const object = await this.objects.getByKey(input.applicationId, input.bucketId, input.key) ?? void 0;
|
|
1481
|
+
return { session: completed, url: upload.publicObjectUrl, object };
|
|
1482
|
+
}
|
|
1483
|
+
return { session, url: upload.publicObjectUrl };
|
|
1484
|
+
}
|
|
1485
|
+
/**
|
|
1486
|
+
* Upload many files concurrently. Each file is uploaded independently; a
|
|
1487
|
+
* failure for one file does not abort the others — inspect each item's
|
|
1488
|
+
* `error`/`result`.
|
|
1489
|
+
*
|
|
1490
|
+
* @param inputs - The files to upload.
|
|
1491
|
+
* @param options - Concurrency limit and an aggregated progress callback.
|
|
1492
|
+
* @returns One result per input, in the same order.
|
|
1493
|
+
*
|
|
1494
|
+
* @example
|
|
1495
|
+
* ```ts
|
|
1496
|
+
* const results = await client.applications.storage.uploadBatch(
|
|
1497
|
+
* files.map((f) => ({ applicationId, bucketId, key: `uploads/${f.name}`, data: f })),
|
|
1498
|
+
* { concurrency: 6, onProgress: (done, total) => console.log(`${done}/${total}`) },
|
|
1499
|
+
* );
|
|
1500
|
+
* const failed = results.filter((r) => r.error);
|
|
1501
|
+
* ```
|
|
1502
|
+
*/
|
|
1503
|
+
async uploadBatch(inputs, options) {
|
|
1504
|
+
const concurrency = Math.max(1, options?.concurrency ?? 4);
|
|
1505
|
+
const results = new Array(inputs.length);
|
|
1506
|
+
let completed = 0;
|
|
1507
|
+
let next = 0;
|
|
1508
|
+
const worker = async () => {
|
|
1509
|
+
for (; ; ) {
|
|
1510
|
+
const index = next++;
|
|
1511
|
+
if (index >= inputs.length) return;
|
|
1512
|
+
try {
|
|
1513
|
+
results[index] = { input: inputs[index], result: await this.upload(inputs[index]) };
|
|
1514
|
+
} catch (error) {
|
|
1515
|
+
results[index] = { input: inputs[index], error };
|
|
1516
|
+
}
|
|
1517
|
+
completed++;
|
|
1518
|
+
options?.onProgress?.(completed, inputs.length);
|
|
1519
|
+
}
|
|
1520
|
+
};
|
|
1521
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, inputs.length) }, () => worker()));
|
|
1522
|
+
return results;
|
|
1523
|
+
}
|
|
1524
|
+
/**
|
|
1525
|
+
* Poll an upload session until the object is finalized server-side.
|
|
1526
|
+
*
|
|
1527
|
+
* @param applicationId - The application ID (UUID).
|
|
1528
|
+
* @param bucketId - The bucket ID (UUID).
|
|
1529
|
+
* @param sessionId - The upload session ID (UUID).
|
|
1530
|
+
* @param options - Timeout and polling interval.
|
|
1531
|
+
* @returns The completed session.
|
|
1532
|
+
* @throws {@link UploadError} if the session fails, expires, or the timeout elapses.
|
|
1533
|
+
*/
|
|
1534
|
+
async waitForCompletion(applicationId, bucketId, sessionId, options) {
|
|
1535
|
+
const timeoutMs = options?.timeoutMs ?? 6e4;
|
|
1536
|
+
const pollIntervalMs = options?.pollIntervalMs ?? 1e3;
|
|
1537
|
+
const deadline = Date.now() + timeoutMs;
|
|
1538
|
+
for (; ; ) {
|
|
1539
|
+
const session = await this.uploadSessions.get(applicationId, bucketId, sessionId);
|
|
1540
|
+
if (session.state === "completed") return session;
|
|
1541
|
+
if (session.state === "failed" || session.state === "expired") {
|
|
1542
|
+
throw new UploadError(`Upload session ${session.state}.`);
|
|
1543
|
+
}
|
|
1544
|
+
if (Date.now() >= deadline) {
|
|
1545
|
+
throw new UploadError("Timed out waiting for the upload to complete.");
|
|
1546
|
+
}
|
|
1547
|
+
await delay(pollIntervalMs);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
|
|
1552
|
+
// src/resources/applications.ts
|
|
1553
|
+
var ApplicationsResource = class extends BaseResource {
|
|
1554
|
+
/**
|
|
1555
|
+
* Manage environment variables scoped to an application.
|
|
1556
|
+
*
|
|
1557
|
+
* @example
|
|
1558
|
+
* ```ts
|
|
1559
|
+
* await client.applications.envVars.create('app-id', {
|
|
1560
|
+
* key: 'DATABASE_URL',
|
|
1561
|
+
* value: 'postgres://...',
|
|
1562
|
+
* sensitive: true,
|
|
1563
|
+
* });
|
|
1564
|
+
* ```
|
|
1565
|
+
*/
|
|
1566
|
+
envVars;
|
|
1567
|
+
/**
|
|
1568
|
+
* Manage storage buckets, objects, and file uploads for an application.
|
|
1569
|
+
*
|
|
1570
|
+
* @example
|
|
1571
|
+
* ```ts
|
|
1572
|
+
* // Upload a file
|
|
1573
|
+
* const { url } = await client.applications.storage.upload({
|
|
1574
|
+
* applicationId: 'app-id',
|
|
1575
|
+
* bucketId: 'bucket-id',
|
|
1576
|
+
* key: 'images/logo.png',
|
|
1577
|
+
* data: fileData,
|
|
1578
|
+
* });
|
|
1579
|
+
*
|
|
1580
|
+
* // List objects in a bucket
|
|
1581
|
+
* const { items } = await client.applications.storage.objects.list('app-id', 'bucket-id');
|
|
1582
|
+
* ```
|
|
1583
|
+
*/
|
|
1584
|
+
storage;
|
|
1585
|
+
/**
|
|
1586
|
+
* Read observed traffic (request logs) for an application.
|
|
1587
|
+
*
|
|
1588
|
+
* @example
|
|
1589
|
+
* ```ts
|
|
1590
|
+
* const { items } = await client.applications.requests.list('app-id', { statusFamily: 5 });
|
|
1591
|
+
* ```
|
|
1592
|
+
*/
|
|
1593
|
+
requests;
|
|
1594
|
+
constructor(...args) {
|
|
1595
|
+
super(...args);
|
|
1596
|
+
this.envVars = new ApplicationEnvVarsResource(this.httpClient);
|
|
1597
|
+
this.storage = new ApplicationStorageResource(this.httpClient);
|
|
1598
|
+
this.requests = new ApplicationRequestsResource(this.httpClient);
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* List applications the authenticated actor has access to.
|
|
1602
|
+
*
|
|
1603
|
+
* Requires the `network:applications:read` scope.
|
|
1604
|
+
*
|
|
1605
|
+
* @param query - Optional organization filter and pagination.
|
|
1606
|
+
* @returns A paginated list of applications.
|
|
1607
|
+
*
|
|
1608
|
+
* @example
|
|
1609
|
+
* ```ts
|
|
1610
|
+
* const { items, total } = await client.applications.list({ organizationId: 'org-id' });
|
|
1611
|
+
* console.log(`Found ${total} applications`);
|
|
1612
|
+
* ```
|
|
1613
|
+
*/
|
|
1614
|
+
async list(query) {
|
|
1615
|
+
return this.httpClient.get("/applications", {
|
|
1616
|
+
query
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
/**
|
|
1620
|
+
* List the `*.gigadrive.app` hostnames for an application (production alias
|
|
1621
|
+
* and per-branch aliases).
|
|
1622
|
+
*
|
|
1623
|
+
* @param applicationId - The application ID (UUID).
|
|
1624
|
+
* @returns The hostnames plus the production hostname `label`.
|
|
1625
|
+
*
|
|
1626
|
+
* @example
|
|
1627
|
+
* ```ts
|
|
1628
|
+
* const { items, label } = await client.applications.hostnames('app-id');
|
|
1629
|
+
* console.log(`Production: ${label}.gigadrive.app`);
|
|
1630
|
+
* ```
|
|
1631
|
+
*/
|
|
1632
|
+
async hostnames(applicationId) {
|
|
1633
|
+
return this.httpClient.get(`/applications/${applicationId}/hostnames`);
|
|
1634
|
+
}
|
|
1635
|
+
/**
|
|
1636
|
+
* Check whether a candidate production hostname label is allowed and globally
|
|
1637
|
+
* available before setting it with {@link setProductionHostname}.
|
|
1638
|
+
*
|
|
1639
|
+
* Requires the `network:applications:read` scope.
|
|
1640
|
+
*
|
|
1641
|
+
* @param applicationId - The application ID (UUID).
|
|
1642
|
+
* @param label - The candidate production hostname label (e.g. `"my-app"`).
|
|
1643
|
+
* @returns Whether the label is available, plus a `reason` when it is not.
|
|
1644
|
+
*
|
|
1645
|
+
* @example
|
|
1646
|
+
* ```ts
|
|
1647
|
+
* const { available, reason } = await client.applications.checkHostnameAvailability('app-id', 'my-app');
|
|
1648
|
+
* if (!available) console.log(`Unavailable: ${reason}`);
|
|
1649
|
+
* ```
|
|
1650
|
+
*/
|
|
1651
|
+
async checkHostnameAvailability(applicationId, label) {
|
|
1652
|
+
return this.httpClient.get(`/applications/${applicationId}/hostname/availability`, {
|
|
1653
|
+
query: { label }
|
|
1654
|
+
});
|
|
1655
|
+
}
|
|
1656
|
+
/**
|
|
1657
|
+
* Set the application's production hostname. The production alias
|
|
1658
|
+
* `{label}.gigadrive.app` is re-pointed to the latest production deployment.
|
|
1659
|
+
*
|
|
1660
|
+
* Requires the `network:applications:write` scope. Deployment- and
|
|
1661
|
+
* function-scoped tokens cannot change the production hostname.
|
|
1662
|
+
*
|
|
1663
|
+
* @param applicationId - The application ID (UUID).
|
|
1664
|
+
* @param label - The production hostname label (e.g. `"my-app"`). It is
|
|
1665
|
+
* normalized (slugified) server-side.
|
|
1666
|
+
* @returns The saved label, the resulting hostname, and whether it is live yet.
|
|
1667
|
+
*
|
|
1668
|
+
* @example
|
|
1669
|
+
* ```ts
|
|
1670
|
+
* const { hostname, live } = await client.applications.setProductionHostname('app-id', 'my-app');
|
|
1671
|
+
* console.log(live ? `Live at ${hostname}` : `Reserved ${hostname} (deploy to go live)`);
|
|
1672
|
+
* ```
|
|
1673
|
+
*/
|
|
1674
|
+
async setProductionHostname(applicationId, label) {
|
|
1675
|
+
return this.httpClient.put(`/applications/${applicationId}/hostname`, { label });
|
|
1676
|
+
}
|
|
1677
|
+
};
|
|
1678
|
+
|
|
1679
|
+
// src/resources/deployments.ts
|
|
1680
|
+
var DeploymentsResource = class extends BaseResource {
|
|
1681
|
+
/**
|
|
1682
|
+
* List deployments, optionally filtered by organization, application, or status.
|
|
1683
|
+
*
|
|
1684
|
+
* Requires the `network:deployments:read` scope.
|
|
1685
|
+
*
|
|
1686
|
+
* @param query - Optional filters.
|
|
1687
|
+
* @returns A paginated list of deployments.
|
|
1688
|
+
*
|
|
1689
|
+
* @example
|
|
1690
|
+
* ```ts
|
|
1691
|
+
* // All deployments for an application
|
|
1692
|
+
* const { items } = await client.deployments.list({ applicationId: 'app-id' });
|
|
1693
|
+
*
|
|
1694
|
+
* // Only active deployments
|
|
1695
|
+
* const { items } = await client.deployments.list({ status: 'ACTIVE' });
|
|
1696
|
+
* ```
|
|
1697
|
+
*/
|
|
1698
|
+
async list(query) {
|
|
1699
|
+
return this.httpClient.get("/deployments", {
|
|
1700
|
+
query
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
/**
|
|
1704
|
+
* Get a deployment by ID, including its current status.
|
|
1705
|
+
*
|
|
1706
|
+
* @param deploymentId - The deployment ID (UUID).
|
|
1707
|
+
* @returns The deployment with its current status.
|
|
1708
|
+
*
|
|
1709
|
+
* @example
|
|
1710
|
+
* ```ts
|
|
1711
|
+
* const deployment = await client.deployments.get('deployment-id');
|
|
1712
|
+
* if (deployment.status === 'ACTIVE') {
|
|
1713
|
+
* console.log('Deployment is live!');
|
|
1714
|
+
* }
|
|
1715
|
+
* ```
|
|
1716
|
+
*/
|
|
1717
|
+
async get(deploymentId) {
|
|
1718
|
+
return this.httpClient.get(`/deployments/${deploymentId}`);
|
|
1719
|
+
}
|
|
1720
|
+
/**
|
|
1721
|
+
* Create a new deployment. The deployment starts in `"PENDING"` status,
|
|
1722
|
+
* waiting for artifact upload. Use the multipart upload methods
|
|
1723
|
+
* ({@link startUpload}, {@link getPresignedUrl}, {@link uploadPart},
|
|
1724
|
+
* {@link completeUpload}) to upload build artifacts.
|
|
1725
|
+
*
|
|
1726
|
+
* Requires the `network:deployments:trigger` scope.
|
|
1727
|
+
*
|
|
1728
|
+
* @param data - The application ID and optional git source.
|
|
1729
|
+
* @returns The newly created deployment.
|
|
1730
|
+
*
|
|
1731
|
+
* @example
|
|
1732
|
+
* ```ts
|
|
1733
|
+
* const deployment = await client.deployments.create({
|
|
1734
|
+
* applicationId: 'app-id',
|
|
1735
|
+
* });
|
|
1736
|
+
* console.log(`Created deployment ${deployment.id} (${deployment.status})`);
|
|
1737
|
+
* ```
|
|
1738
|
+
*/
|
|
1739
|
+
async create(data) {
|
|
1740
|
+
return this.httpClient.post("/deployments", data);
|
|
1741
|
+
}
|
|
1742
|
+
/**
|
|
1743
|
+
* Start a multipart upload for deployment artifacts. This initiates a new
|
|
1744
|
+
* multipart upload and returns an upload ID for subsequent part uploads.
|
|
1745
|
+
*
|
|
1746
|
+
* The deployment must be in `"PENDING"` status.
|
|
1747
|
+
*
|
|
1748
|
+
* @param deploymentId - The deployment ID (UUID).
|
|
1749
|
+
* @returns An object containing the `uploadId` for subsequent calls.
|
|
1750
|
+
*
|
|
1751
|
+
* @example
|
|
1752
|
+
* ```ts
|
|
1753
|
+
* const { uploadId } = await client.deployments.startUpload('deployment-id');
|
|
1754
|
+
* ```
|
|
1755
|
+
*/
|
|
1756
|
+
async startUpload(deploymentId) {
|
|
1757
|
+
return this.httpClient.post(`/deployments/${deploymentId}/upload/start`);
|
|
1758
|
+
}
|
|
1759
|
+
/**
|
|
1760
|
+
* Get a presigned URL for uploading a single part of a multipart upload.
|
|
1761
|
+
* The URL is temporary and should be used immediately with {@link uploadPart}.
|
|
1762
|
+
*
|
|
1763
|
+
* @param deploymentId - The deployment ID (UUID).
|
|
1764
|
+
* @param uploadId - The multipart upload ID from {@link startUpload}.
|
|
1765
|
+
* @param partNumber - The 1-based part number.
|
|
1766
|
+
* @returns An object containing the presigned `url` to PUT the part data to.
|
|
1767
|
+
*
|
|
1768
|
+
* @example
|
|
1769
|
+
* ```ts
|
|
1770
|
+
* const { url } = await client.deployments.getPresignedUrl(
|
|
1771
|
+
* 'deployment-id', uploadId, 1,
|
|
1772
|
+
* );
|
|
1773
|
+
* ```
|
|
1774
|
+
*/
|
|
1775
|
+
async getPresignedUrl(deploymentId, uploadId, partNumber) {
|
|
1776
|
+
if (!Number.isInteger(partNumber) || partNumber < 1) {
|
|
1777
|
+
throw new Error(`partNumber must be a positive integer, received ${partNumber}`);
|
|
1778
|
+
}
|
|
1779
|
+
return this.httpClient.post(`/deployments/${deploymentId}/upload/part`, { uploadId, partNumber });
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Upload a part to a presigned URL obtained from {@link getPresignedUrl}.
|
|
1783
|
+
* This sends the chunk data directly to the backend storage service, not
|
|
1784
|
+
* to the Gigadrive API. The returned `etag` is needed for
|
|
1785
|
+
* {@link completeUpload}.
|
|
1786
|
+
*
|
|
1787
|
+
* @param presignedUrl - The presigned URL from {@link getPresignedUrl}.
|
|
1788
|
+
* @param data - The chunk data to upload.
|
|
1789
|
+
* @param partNumber - The 1-based part number (used for error messages).
|
|
1790
|
+
* @returns The part number and ETag. Collect these for {@link completeUpload}.
|
|
1791
|
+
* @throws {@link ApiError} if the PUT request fails.
|
|
1792
|
+
* @throws Error if the storage provider does not return an ETag header.
|
|
1793
|
+
*
|
|
1794
|
+
* @example
|
|
1795
|
+
* ```ts
|
|
1796
|
+
* const chunk = fileBuffer.subarray(0, 10 * 1024 * 1024); // 10 MB
|
|
1797
|
+
* const part = await client.deployments.uploadPart(presignedUrl, chunk, 1);
|
|
1798
|
+
* // part = { partNumber: 1, etag: '"abc123..."' }
|
|
1799
|
+
* ```
|
|
1800
|
+
*/
|
|
1801
|
+
async uploadPart(presignedUrl, data, partNumber) {
|
|
1802
|
+
if (!Number.isInteger(partNumber) || partNumber < 1) {
|
|
1803
|
+
throw new Error(`partNumber must be a positive integer, received ${partNumber}`);
|
|
1804
|
+
}
|
|
1805
|
+
const response = await this.httpClient.fetchRaw(presignedUrl, {
|
|
1806
|
+
method: "PUT",
|
|
1807
|
+
headers: {
|
|
1808
|
+
// Deployment artifacts are always ZIP archives.
|
|
1809
|
+
"Content-Type": "application/zip",
|
|
1810
|
+
"Content-Length": String(data instanceof Blob ? data.size : data.byteLength)
|
|
1811
|
+
},
|
|
1812
|
+
body: data
|
|
1813
|
+
});
|
|
1814
|
+
const etag = response.headers.get("ETag");
|
|
1815
|
+
if (!etag) {
|
|
1816
|
+
throw new Error(`No ETag received for part ${partNumber}`);
|
|
1817
|
+
}
|
|
1818
|
+
return { partNumber, etag };
|
|
1819
|
+
}
|
|
1820
|
+
/**
|
|
1821
|
+
* Complete a multipart upload after all parts have been uploaded via
|
|
1822
|
+
* {@link uploadPart}. This signals the storage provider to assemble the
|
|
1823
|
+
* parts into a single object and triggers the deployment build pipeline.
|
|
1824
|
+
*
|
|
1825
|
+
* @param deploymentId - The deployment ID (UUID).
|
|
1826
|
+
* @param uploadId - The multipart upload ID from {@link startUpload}.
|
|
1827
|
+
* @param parts - Array of `{ partNumber, etag }` from each {@link uploadPart} call.
|
|
1828
|
+
*
|
|
1829
|
+
* @example
|
|
1830
|
+
* ```ts
|
|
1831
|
+
* await client.deployments.completeUpload(deploymentId, uploadId, [
|
|
1832
|
+
* { partNumber: 1, etag: '"abc..."' },
|
|
1833
|
+
* { partNumber: 2, etag: '"def..."' },
|
|
1834
|
+
* ]);
|
|
1835
|
+
* ```
|
|
1836
|
+
*/
|
|
1837
|
+
async completeUpload(deploymentId, uploadId, parts) {
|
|
1838
|
+
return this.httpClient.post(`/deployments/${deploymentId}/upload/complete`, { uploadId, parts });
|
|
1839
|
+
}
|
|
1840
|
+
/**
|
|
1841
|
+
* Fetch deployment logs. Supports pagination and filtering by timestamp
|
|
1842
|
+
* for incremental log tailing.
|
|
1843
|
+
*
|
|
1844
|
+
* @param deploymentId - The deployment ID (UUID).
|
|
1845
|
+
* @param query - Optional pagination and filter parameters.
|
|
1846
|
+
* @returns A page of log entries with total count.
|
|
1847
|
+
*
|
|
1848
|
+
* @example
|
|
1849
|
+
* ```ts
|
|
1850
|
+
* // Get the first page of logs
|
|
1851
|
+
* const page = await client.deployments.getLogs('deployment-id', {
|
|
1852
|
+
* offset: 0,
|
|
1853
|
+
* limit: 100,
|
|
1854
|
+
* });
|
|
1855
|
+
*
|
|
1856
|
+
* for (const entry of page.items) {
|
|
1857
|
+
* const prefix = entry.type === 'ERROR' ? 'ERR' : entry.type === 'WARN' ? 'WRN' : 'INF';
|
|
1858
|
+
* console.log(`[${prefix}] ${entry.message}`);
|
|
1859
|
+
* }
|
|
1860
|
+
*
|
|
1861
|
+
* // Tail new logs since last fetch
|
|
1862
|
+
* const newLogs = await client.deployments.getLogs('deployment-id', {
|
|
1863
|
+
* 'createdAt[gt]': lastEntry.createdAt,
|
|
1864
|
+
* });
|
|
1865
|
+
* ```
|
|
1866
|
+
*/
|
|
1867
|
+
async getLogs(deploymentId, query) {
|
|
1868
|
+
return this.httpClient.get(`/deployments/${deploymentId}/logs`, {
|
|
1869
|
+
query
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1872
|
+
/**
|
|
1873
|
+
* List the immutable `*.gigadrive.app` hostnames assigned to a deployment.
|
|
1874
|
+
*
|
|
1875
|
+
* @param deploymentId - The deployment ID (UUID).
|
|
1876
|
+
* @returns A paginated list of hostnames.
|
|
1877
|
+
*
|
|
1878
|
+
* @example
|
|
1879
|
+
* ```ts
|
|
1880
|
+
* const { items } = await client.deployments.getHostnames('deployment-id');
|
|
1881
|
+
* console.log(items.map((h) => h.hostname));
|
|
1882
|
+
* ```
|
|
1883
|
+
*/
|
|
1884
|
+
async getHostnames(deploymentId) {
|
|
1885
|
+
return this.httpClient.get(`/deployments/${deploymentId}/hostnames`);
|
|
1886
|
+
}
|
|
1887
|
+
};
|
|
1888
|
+
|
|
1889
|
+
// src/resources/organization-ai-gateway.ts
|
|
1890
|
+
var AiGatewayUsageResource = class extends BaseResource {
|
|
1891
|
+
/** Get an aggregated usage summary with breakdowns by model, provider, and user. */
|
|
1892
|
+
async summary(organizationId, query) {
|
|
1893
|
+
return this.httpClient.get(`/organizations/${organizationId}/ai-gateway/usage/summary`, {
|
|
1894
|
+
query
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
/** List request-level usage events. */
|
|
1898
|
+
async requests(organizationId, query) {
|
|
1899
|
+
return this.httpClient.get(`/organizations/${organizationId}/ai-gateway/usage/requests`, {
|
|
1900
|
+
query
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
/** Export request-level usage events as CSV text. */
|
|
1904
|
+
async export(organizationId, query) {
|
|
1905
|
+
const response = await this.httpClient.requestStream(
|
|
1906
|
+
"GET",
|
|
1907
|
+
`/organizations/${organizationId}/ai-gateway/usage/export`,
|
|
1908
|
+
{ query }
|
|
1909
|
+
);
|
|
1910
|
+
return response.text();
|
|
1911
|
+
}
|
|
1912
|
+
};
|
|
1913
|
+
var AiGatewayBudgetsResource = class extends BaseResource {
|
|
1914
|
+
/** List all budgets for an organization. */
|
|
1915
|
+
async list(organizationId) {
|
|
1916
|
+
return this.httpClient.get(`/organizations/${organizationId}/ai-gateway/budgets`);
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* Replace the organization's entire budget set. Pass an empty array to clear
|
|
1920
|
+
* all budgets.
|
|
1921
|
+
*/
|
|
1922
|
+
async replace(organizationId, budgets) {
|
|
1923
|
+
return this.httpClient.put(`/organizations/${organizationId}/ai-gateway/budgets`, { budgets });
|
|
1924
|
+
}
|
|
1925
|
+
};
|
|
1926
|
+
var AiGatewayPoliciesResource = class extends BaseResource {
|
|
1927
|
+
/**
|
|
1928
|
+
* Get the governance policy for an organization, or for a specific
|
|
1929
|
+
* application when `applicationId` is provided. Returns `null` if no policy is set.
|
|
1930
|
+
*/
|
|
1931
|
+
async get(organizationId, options) {
|
|
1932
|
+
const { policy } = await this.httpClient.get(
|
|
1933
|
+
`/organizations/${organizationId}/ai-gateway/policies`,
|
|
1934
|
+
{ query: { applicationId: options?.applicationId } }
|
|
1935
|
+
);
|
|
1936
|
+
return policy;
|
|
1937
|
+
}
|
|
1938
|
+
/** Create or update an AI Gateway policy. */
|
|
1939
|
+
async put(organizationId, data) {
|
|
1940
|
+
const { policy } = await this.httpClient.put(
|
|
1941
|
+
`/organizations/${organizationId}/ai-gateway/policies`,
|
|
1942
|
+
data
|
|
1943
|
+
);
|
|
1944
|
+
return policy;
|
|
1945
|
+
}
|
|
1946
|
+
};
|
|
1947
|
+
var OrganizationAiGatewayResource = class {
|
|
1948
|
+
/** Usage analytics (summary, request log, CSV export). */
|
|
1949
|
+
usage;
|
|
1950
|
+
/** Spend/usage budgets. */
|
|
1951
|
+
budgets;
|
|
1952
|
+
/** Model/provider governance policies. */
|
|
1953
|
+
policies;
|
|
1954
|
+
constructor(httpClient) {
|
|
1955
|
+
this.usage = new AiGatewayUsageResource(httpClient);
|
|
1956
|
+
this.budgets = new AiGatewayBudgetsResource(httpClient);
|
|
1957
|
+
this.policies = new AiGatewayPoliciesResource(httpClient);
|
|
1958
|
+
}
|
|
1959
|
+
};
|
|
1960
|
+
|
|
1961
|
+
// src/resources/organization-env-vars.ts
|
|
1962
|
+
var OrganizationEnvVarsResource = class extends BaseResource {
|
|
1963
|
+
/**
|
|
1964
|
+
* List all environment variables for an organization.
|
|
1965
|
+
*
|
|
1966
|
+
* @param organizationId - The organization ID (UUID).
|
|
1967
|
+
* @returns A paginated list of environment variables.
|
|
1968
|
+
*
|
|
1969
|
+
* @example
|
|
1970
|
+
* ```ts
|
|
1971
|
+
* const { items } = await client.organizations.envVars.list('org-id');
|
|
1972
|
+
* for (const v of items) {
|
|
1973
|
+
* console.log(`${v.key}=${v.sensitive ? '***' : v.value}`);
|
|
1974
|
+
* }
|
|
1975
|
+
* ```
|
|
1976
|
+
*/
|
|
1977
|
+
async list(organizationId, query) {
|
|
1978
|
+
return this.httpClient.get(`/organizations/${organizationId}/env-vars`, {
|
|
1979
|
+
query
|
|
1980
|
+
});
|
|
1981
|
+
}
|
|
1982
|
+
/**
|
|
1983
|
+
* Create a new environment variable for an organization.
|
|
1984
|
+
*
|
|
1985
|
+
* @param organizationId - The organization ID (UUID).
|
|
1986
|
+
* @param data - The variable key, value, and optional settings.
|
|
1987
|
+
* @returns The newly created environment variable.
|
|
1988
|
+
*
|
|
1989
|
+
* @example
|
|
1990
|
+
* ```ts
|
|
1991
|
+
* const envVar = await client.organizations.envVars.create('org-id', {
|
|
1992
|
+
* key: 'STRIPE_SECRET',
|
|
1993
|
+
* value: 'sk_live_...',
|
|
1994
|
+
* sensitive: true,
|
|
1995
|
+
* });
|
|
1996
|
+
* ```
|
|
1997
|
+
*/
|
|
1998
|
+
async create(organizationId, data) {
|
|
1999
|
+
return this.httpClient.post(`/organizations/${organizationId}/env-vars`, data);
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* Update an existing environment variable. Only the fields you provide
|
|
2003
|
+
* will be changed; omitted fields are left unchanged.
|
|
2004
|
+
*
|
|
2005
|
+
* @param organizationId - The organization ID (UUID).
|
|
2006
|
+
* @param envVarId - The environment variable ID (UUID).
|
|
2007
|
+
* @param data - The fields to update.
|
|
2008
|
+
* @returns The updated environment variable.
|
|
2009
|
+
*
|
|
2010
|
+
* @example
|
|
2011
|
+
* ```ts
|
|
2012
|
+
* await client.organizations.envVars.update('org-id', 'var-id', {
|
|
2013
|
+
* value: 'new-value',
|
|
2014
|
+
* });
|
|
2015
|
+
* ```
|
|
2016
|
+
*/
|
|
2017
|
+
async update(organizationId, envVarId, data) {
|
|
2018
|
+
return this.httpClient.patch(`/organizations/${organizationId}/env-vars/${envVarId}`, data);
|
|
2019
|
+
}
|
|
2020
|
+
/**
|
|
2021
|
+
* Permanently delete an environment variable.
|
|
2022
|
+
*
|
|
2023
|
+
* @param organizationId - The organization ID (UUID).
|
|
2024
|
+
* @param envVarId - The environment variable ID (UUID).
|
|
2025
|
+
*
|
|
2026
|
+
* @example
|
|
2027
|
+
* ```ts
|
|
2028
|
+
* await client.organizations.envVars.delete('org-id', 'var-id');
|
|
2029
|
+
* ```
|
|
2030
|
+
*/
|
|
2031
|
+
async delete(organizationId, envVarId) {
|
|
2032
|
+
return this.httpClient.delete(`/organizations/${organizationId}/env-vars/${envVarId}`);
|
|
2033
|
+
}
|
|
2034
|
+
};
|
|
2035
|
+
|
|
2036
|
+
// src/resources/organizations.ts
|
|
2037
|
+
var OrganizationsResource = class extends BaseResource {
|
|
2038
|
+
/**
|
|
2039
|
+
* Manage environment variables scoped to an organization.
|
|
2040
|
+
*
|
|
2041
|
+
* @example
|
|
2042
|
+
* ```ts
|
|
2043
|
+
* // List all env vars for an organization
|
|
2044
|
+
* const { items } = await client.organizations.envVars.list('org-id');
|
|
2045
|
+
*
|
|
2046
|
+
* // Create a new env var
|
|
2047
|
+
* await client.organizations.envVars.create('org-id', {
|
|
2048
|
+
* key: 'ANALYTICS_KEY',
|
|
2049
|
+
* value: 'ak_live_...',
|
|
2050
|
+
* sensitive: true,
|
|
2051
|
+
* });
|
|
2052
|
+
* ```
|
|
2053
|
+
*/
|
|
2054
|
+
envVars;
|
|
2055
|
+
/**
|
|
2056
|
+
* AI Gateway governance for an organization: usage analytics, budgets, and
|
|
2057
|
+
* model/provider policies.
|
|
2058
|
+
*
|
|
2059
|
+
* @example
|
|
2060
|
+
* ```ts
|
|
2061
|
+
* const usage = await client.organizations.aiGateway.usage.summary('org-id', { from: '2026-06-01' });
|
|
2062
|
+
* console.log(usage.summary.totalTokens);
|
|
2063
|
+
* ```
|
|
2064
|
+
*/
|
|
2065
|
+
aiGateway;
|
|
2066
|
+
constructor(...args) {
|
|
2067
|
+
super(...args);
|
|
2068
|
+
this.envVars = new OrganizationEnvVarsResource(this.httpClient);
|
|
2069
|
+
this.aiGateway = new OrganizationAiGatewayResource(this.httpClient);
|
|
2070
|
+
}
|
|
2071
|
+
/**
|
|
2072
|
+
* List organizations the authenticated actor has access to.
|
|
2073
|
+
*
|
|
2074
|
+
* @param query - Optional pagination.
|
|
2075
|
+
* @returns A paginated list of organizations.
|
|
2076
|
+
*
|
|
2077
|
+
* @example
|
|
2078
|
+
* ```ts
|
|
2079
|
+
* const { items, total } = await client.organizations.list();
|
|
2080
|
+
* console.log(`Found ${total} organizations`);
|
|
2081
|
+
* ```
|
|
2082
|
+
*/
|
|
2083
|
+
async list(query) {
|
|
2084
|
+
return this.httpClient.get("/organizations", {
|
|
2085
|
+
query
|
|
2086
|
+
});
|
|
2087
|
+
}
|
|
2088
|
+
};
|
|
2089
|
+
|
|
2090
|
+
// src/client.ts
|
|
2091
|
+
var DEFAULT_BASE_URL2 = "https://api.gigadrive.network";
|
|
2092
|
+
var GigadriveClient = class {
|
|
2093
|
+
/** Organization management (list, environment variables). */
|
|
2094
|
+
organizations;
|
|
2095
|
+
/** Application management (list, environment variables, storage). */
|
|
2096
|
+
applications;
|
|
2097
|
+
/** Deployment lifecycle (create, upload, status, logs). */
|
|
2098
|
+
deployments;
|
|
2099
|
+
/** AI Gateway — OpenAI-compatible chat completions and model listing. */
|
|
2100
|
+
aiGateway;
|
|
2101
|
+
constructor(config = {}) {
|
|
2102
|
+
const baseUrl = config.baseUrl ?? readEnv("GIGADRIVE_API_BASE_URL") ?? DEFAULT_BASE_URL2;
|
|
2103
|
+
const fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
|
|
2104
|
+
const credentialProvider = resolveCredentialProvider({ ...config, baseUrl, fetch: fetchFn });
|
|
2105
|
+
const tokenManager = new TokenManager(credentialProvider);
|
|
2106
|
+
const httpClient = new HttpClient(baseUrl, tokenManager, fetchFn);
|
|
2107
|
+
this.organizations = new OrganizationsResource(httpClient);
|
|
2108
|
+
this.applications = new ApplicationsResource(httpClient);
|
|
2109
|
+
this.deployments = new DeploymentsResource(httpClient);
|
|
2110
|
+
this.aiGateway = new AiGatewayResource(httpClient);
|
|
2111
|
+
}
|
|
2112
|
+
};
|
|
2113
|
+
|
|
2114
|
+
// src/pagination.ts
|
|
2115
|
+
async function* paginate(fetchPage) {
|
|
2116
|
+
let cursor;
|
|
2117
|
+
do {
|
|
2118
|
+
const page = await fetchPage(cursor);
|
|
2119
|
+
for (const item of page.items) {
|
|
2120
|
+
yield item;
|
|
2121
|
+
}
|
|
2122
|
+
cursor = page.nextCursor;
|
|
2123
|
+
} while (cursor);
|
|
2124
|
+
}
|
|
2125
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2126
|
+
0 && (module.exports = {
|
|
2127
|
+
AiGatewayAudioResource,
|
|
2128
|
+
AiGatewayBudgetsResource,
|
|
2129
|
+
AiGatewayPoliciesResource,
|
|
2130
|
+
AiGatewayResource,
|
|
2131
|
+
AiGatewayUsageResource,
|
|
2132
|
+
AiGatewayVideosResource,
|
|
2133
|
+
ApiError,
|
|
2134
|
+
ApplicationEnvVarsResource,
|
|
2135
|
+
ApplicationRequestsResource,
|
|
2136
|
+
ApplicationStorageResource,
|
|
2137
|
+
ApplicationsResource,
|
|
2138
|
+
AuthenticationError,
|
|
2139
|
+
DeploymentsResource,
|
|
2140
|
+
GigadriveClient,
|
|
2141
|
+
GigadriveError,
|
|
2142
|
+
OrganizationAiGatewayResource,
|
|
2143
|
+
OrganizationEnvVarsResource,
|
|
2144
|
+
OrganizationsResource,
|
|
2145
|
+
StorageBucketsResource,
|
|
2146
|
+
StorageObjectsResource,
|
|
2147
|
+
StorageUploadSessionsResource,
|
|
2148
|
+
UploadError,
|
|
2149
|
+
UploadSessionExpiredError,
|
|
2150
|
+
paginate,
|
|
2151
|
+
parseSSEStream
|
|
2152
|
+
});
|
|
10
2153
|
//# sourceMappingURL=index.js.map
|