@boltic/sdk 0.1.3 → 0.1.5
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/dist/databases/index.d.ts +82 -0
- package/dist/databases/index.js +1 -1
- package/dist/databases/index.mjs +1 -1
- package/dist/databases/test-client-DzMli6cH.js +2 -0
- package/dist/databases/test-client-DzMli6cH.js.map +1 -0
- package/dist/databases/test-client-rR_4EBUe.mjs +2 -0
- package/dist/databases/test-client-rR_4EBUe.mjs.map +1 -0
- package/dist/databases/testing.d.ts +82 -0
- package/dist/databases/testing.js +1 -1
- package/dist/databases/testing.mjs +1 -1
- package/dist/sdk.js +1091 -24
- package/dist/sdk.js.map +1 -1
- package/dist/sdk.mjs +1091 -24
- package/dist/sdk.mjs.map +1 -1
- package/dist/types/index.d.ts +724 -0
- package/package.json +1 -1
- package/dist/databases/test-client-BEAX5vfC.mjs +0 -2
- package/dist/databases/test-client-BEAX5vfC.mjs.map +0 -1
- package/dist/databases/test-client-BgkvBtst.js +0 -2
- package/dist/databases/test-client-BgkvBtst.js.map +0 -1
package/dist/sdk.mjs
CHANGED
|
@@ -234,6 +234,20 @@ Context: ${JSON.stringify(context, null, 2)}`;
|
|
|
234
234
|
}
|
|
235
235
|
return String(error);
|
|
236
236
|
}
|
|
237
|
+
function decodeArrayBufferErrorBody(buffer) {
|
|
238
|
+
const txt = new TextDecoder().decode(buffer);
|
|
239
|
+
try {
|
|
240
|
+
return JSON.parse(txt);
|
|
241
|
+
} catch {
|
|
242
|
+
return txt;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function normalizeAxiosDataAfterResponse(config, status, data) {
|
|
246
|
+
if (config.responseType !== "arraybuffer" || !(data instanceof ArrayBuffer) || status < 400) {
|
|
247
|
+
return data;
|
|
248
|
+
}
|
|
249
|
+
return decodeArrayBufferErrorBody(data);
|
|
250
|
+
}
|
|
237
251
|
class AxiosAdapter {
|
|
238
252
|
constructor() {
|
|
239
253
|
try {
|
|
@@ -247,21 +261,36 @@ class AxiosAdapter {
|
|
|
247
261
|
}
|
|
248
262
|
async request(config) {
|
|
249
263
|
try {
|
|
264
|
+
const isFormData = typeof FormData !== "undefined" && config.data instanceof FormData;
|
|
265
|
+
let headers = config.headers;
|
|
266
|
+
if (isFormData && headers) {
|
|
267
|
+
headers = { ...headers };
|
|
268
|
+
delete headers["Content-Type"];
|
|
269
|
+
delete headers["content-type"];
|
|
270
|
+
}
|
|
250
271
|
const axiosConfig = {
|
|
251
272
|
url: config.url,
|
|
252
273
|
method: config.method.toLowerCase(),
|
|
253
|
-
headers
|
|
274
|
+
headers,
|
|
254
275
|
params: config.params,
|
|
255
276
|
data: config.data,
|
|
256
277
|
timeout: config.timeout,
|
|
257
278
|
signal: config.signal,
|
|
258
279
|
validateStatus: () => true
|
|
259
280
|
};
|
|
281
|
+
if (config.responseType === "arraybuffer") {
|
|
282
|
+
axiosConfig.responseType = "arraybuffer";
|
|
283
|
+
}
|
|
260
284
|
const response = await this.axios(axiosConfig);
|
|
285
|
+
const responseData = normalizeAxiosDataAfterResponse(
|
|
286
|
+
config,
|
|
287
|
+
response.status,
|
|
288
|
+
response.data
|
|
289
|
+
);
|
|
261
290
|
if (response.status < 200 || response.status >= 300) {
|
|
262
|
-
const isHtmlError = typeof
|
|
291
|
+
const isHtmlError = typeof responseData === "string" && responseData.trim().startsWith("<!DOCTYPE") || typeof responseData === "string" && responseData.includes("<html");
|
|
263
292
|
if (isHtmlError) {
|
|
264
|
-
const htmlContent =
|
|
293
|
+
const htmlContent = responseData;
|
|
265
294
|
const preMatch = htmlContent.match(/<pre>(.*?)<\/pre>/s);
|
|
266
295
|
const errorMessage = preMatch ? preMatch[1].trim() : `HTTP ${response.status}: ${response.statusText}`;
|
|
267
296
|
throw createErrorWithContext(errorMessage, {
|
|
@@ -272,9 +301,9 @@ class AxiosAdapter {
|
|
|
272
301
|
isHtmlError: true
|
|
273
302
|
});
|
|
274
303
|
}
|
|
275
|
-
if (
|
|
304
|
+
if (responseData && typeof responseData === "object" && "error" in responseData) {
|
|
276
305
|
return {
|
|
277
|
-
data:
|
|
306
|
+
data: responseData,
|
|
278
307
|
status: response.status,
|
|
279
308
|
statusText: response.statusText,
|
|
280
309
|
headers: response.headers || {}
|
|
@@ -287,12 +316,12 @@ class AxiosAdapter {
|
|
|
287
316
|
method: config.method,
|
|
288
317
|
status: response.status,
|
|
289
318
|
statusText: response.statusText,
|
|
290
|
-
responseData
|
|
319
|
+
responseData
|
|
291
320
|
}
|
|
292
321
|
);
|
|
293
322
|
}
|
|
294
323
|
return {
|
|
295
|
-
data:
|
|
324
|
+
data: responseData,
|
|
296
325
|
status: response.status,
|
|
297
326
|
statusText: response.statusText,
|
|
298
327
|
headers: response.headers || {}
|
|
@@ -335,6 +364,26 @@ class AxiosAdapter {
|
|
|
335
364
|
}
|
|
336
365
|
}
|
|
337
366
|
}
|
|
367
|
+
async function parseFetchBodyDefault(response) {
|
|
368
|
+
const contentType = response.headers.get("content-type");
|
|
369
|
+
if (contentType?.includes("application/json")) {
|
|
370
|
+
return response.json();
|
|
371
|
+
}
|
|
372
|
+
return response.text();
|
|
373
|
+
}
|
|
374
|
+
async function parseFetchBodyArrayBuffer(response) {
|
|
375
|
+
const buf = await response.arrayBuffer();
|
|
376
|
+
if (response.status >= 400) {
|
|
377
|
+
return decodeArrayBufferErrorBody(buf);
|
|
378
|
+
}
|
|
379
|
+
return buf;
|
|
380
|
+
}
|
|
381
|
+
async function parseFetchResponseData(response, config) {
|
|
382
|
+
if (config.responseType === "arraybuffer") {
|
|
383
|
+
return parseFetchBodyArrayBuffer(response);
|
|
384
|
+
}
|
|
385
|
+
return parseFetchBodyDefault(response);
|
|
386
|
+
}
|
|
338
387
|
class FetchAdapter {
|
|
339
388
|
async request(config) {
|
|
340
389
|
const url = new URL(config.url);
|
|
@@ -345,16 +394,21 @@ class FetchAdapter {
|
|
|
345
394
|
}
|
|
346
395
|
});
|
|
347
396
|
}
|
|
397
|
+
const isFormData = typeof FormData !== "undefined" && config.data instanceof FormData;
|
|
398
|
+
const headerMap = { ...config.headers || {} };
|
|
399
|
+
if (!isFormData) {
|
|
400
|
+
headerMap["Content-Type"] = headerMap["Content-Type"] ?? headerMap["content-type"] ?? "application/json";
|
|
401
|
+
} else {
|
|
402
|
+
delete headerMap["Content-Type"];
|
|
403
|
+
delete headerMap["content-type"];
|
|
404
|
+
}
|
|
348
405
|
const init = {
|
|
349
406
|
method: config.method,
|
|
350
|
-
headers:
|
|
351
|
-
"Content-Type": "application/json",
|
|
352
|
-
...config.headers
|
|
353
|
-
},
|
|
407
|
+
headers: headerMap,
|
|
354
408
|
signal: config.signal
|
|
355
409
|
};
|
|
356
410
|
if (config.data && ["POST", "PUT", "PATCH", "DELETE"].includes(config.method)) {
|
|
357
|
-
init.body = JSON.stringify(config.data);
|
|
411
|
+
init.body = isFormData ? config.data : JSON.stringify(config.data);
|
|
358
412
|
}
|
|
359
413
|
try {
|
|
360
414
|
const controller = new AbortController();
|
|
@@ -378,13 +432,10 @@ class FetchAdapter {
|
|
|
378
432
|
if (timeoutId) {
|
|
379
433
|
clearTimeout(timeoutId);
|
|
380
434
|
}
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
} else {
|
|
386
|
-
data = await response.text();
|
|
387
|
-
}
|
|
435
|
+
const data = await parseFetchResponseData(
|
|
436
|
+
response,
|
|
437
|
+
config
|
|
438
|
+
);
|
|
388
439
|
const headers = {};
|
|
389
440
|
response.headers.forEach((value, key) => {
|
|
390
441
|
headers[key] = value;
|
|
@@ -547,7 +598,9 @@ class AuthManager2 {
|
|
|
547
598
|
const SERVICE_PATHS = {
|
|
548
599
|
DATABASES: "/service/sdk/boltic-tables/v1",
|
|
549
600
|
WORKFLOW_TEMPORAL: "/service/panel/temporal/v1.0",
|
|
550
|
-
INTEGRATION: "/service/panel/integration/v1"
|
|
601
|
+
INTEGRATION: "/service/panel/integration/v1",
|
|
602
|
+
SERVERLESS: "/service/panel/serverless/v1.0",
|
|
603
|
+
STORAGE: "/service/panel/storage/v1.0"
|
|
551
604
|
};
|
|
552
605
|
class BaseApiClient {
|
|
553
606
|
constructor(apiKey, config = {}, servicePath = SERVICE_PATHS.DATABASES) {
|
|
@@ -4074,7 +4127,10 @@ class SqlApiClient extends BaseApiClient {
|
|
|
4074
4127
|
const response = await this.httpAdapter.request({
|
|
4075
4128
|
url,
|
|
4076
4129
|
method: endpoint.method,
|
|
4077
|
-
headers:
|
|
4130
|
+
headers: {
|
|
4131
|
+
...this.buildHeaders(),
|
|
4132
|
+
"x-request-source": "sdk"
|
|
4133
|
+
},
|
|
4078
4134
|
data: request,
|
|
4079
4135
|
timeout: this.config.timeout
|
|
4080
4136
|
});
|
|
@@ -4105,7 +4161,10 @@ class SqlApiClient extends BaseApiClient {
|
|
|
4105
4161
|
const response = await this.httpAdapter.request({
|
|
4106
4162
|
url,
|
|
4107
4163
|
method: endpoint.method,
|
|
4108
|
-
headers:
|
|
4164
|
+
headers: {
|
|
4165
|
+
...this.buildHeaders(),
|
|
4166
|
+
"x-request-source": "sdk"
|
|
4167
|
+
},
|
|
4109
4168
|
data: request,
|
|
4110
4169
|
timeout: this.config.timeout
|
|
4111
4170
|
});
|
|
@@ -4784,7 +4843,7 @@ function buildExecuteActivityBody(params) {
|
|
|
4784
4843
|
result: params.result ?? buildDefaultResultPayload()
|
|
4785
4844
|
};
|
|
4786
4845
|
}
|
|
4787
|
-
function sleep(ms) {
|
|
4846
|
+
function sleep$1(ms) {
|
|
4788
4847
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4789
4848
|
}
|
|
4790
4849
|
class WorkflowResource extends BaseResource {
|
|
@@ -4982,7 +5041,7 @@ class WorkflowResource extends BaseResource {
|
|
|
4982
5041
|
}
|
|
4983
5042
|
return result;
|
|
4984
5043
|
}
|
|
4985
|
-
await sleep(POLLING_INTERVAL_MS);
|
|
5044
|
+
await sleep$1(POLLING_INTERVAL_MS);
|
|
4986
5045
|
}
|
|
4987
5046
|
return {
|
|
4988
5047
|
error: {
|
|
@@ -4996,6 +5055,945 @@ class WorkflowResource extends BaseResource {
|
|
|
4996
5055
|
};
|
|
4997
5056
|
}
|
|
4998
5057
|
}
|
|
5058
|
+
const STATUS_POLLING_INTERVAL_MS = 5e3;
|
|
5059
|
+
const MAX_STATUS_POLLING_ATTEMPTS = 60;
|
|
5060
|
+
const TERMINAL_STATUSES = [
|
|
5061
|
+
"running",
|
|
5062
|
+
"failed",
|
|
5063
|
+
"degraded",
|
|
5064
|
+
"suspended"
|
|
5065
|
+
];
|
|
5066
|
+
const DEFAULT_RESOURCES = {
|
|
5067
|
+
CPU: 0.1,
|
|
5068
|
+
MemoryMB: 128,
|
|
5069
|
+
MemoryMaxMB: 128
|
|
5070
|
+
};
|
|
5071
|
+
const DEFAULT_SCALING = {
|
|
5072
|
+
AutoStop: false,
|
|
5073
|
+
Min: 1,
|
|
5074
|
+
Max: 1,
|
|
5075
|
+
MaxIdleTime: 0
|
|
5076
|
+
};
|
|
5077
|
+
const SERVERLESS_ENDPOINTS = {
|
|
5078
|
+
list: {
|
|
5079
|
+
path: "/apps",
|
|
5080
|
+
method: "GET",
|
|
5081
|
+
authenticated: true
|
|
5082
|
+
},
|
|
5083
|
+
get: {
|
|
5084
|
+
path: "/apps/{app_id}",
|
|
5085
|
+
method: "GET",
|
|
5086
|
+
authenticated: true
|
|
5087
|
+
},
|
|
5088
|
+
create: {
|
|
5089
|
+
path: "/apps",
|
|
5090
|
+
method: "POST",
|
|
5091
|
+
authenticated: true
|
|
5092
|
+
},
|
|
5093
|
+
update: {
|
|
5094
|
+
path: "/apps/{app_id}",
|
|
5095
|
+
method: "PUT",
|
|
5096
|
+
authenticated: true
|
|
5097
|
+
},
|
|
5098
|
+
getBuilds: {
|
|
5099
|
+
path: "/apps/{app_id}/builds",
|
|
5100
|
+
method: "GET",
|
|
5101
|
+
authenticated: true
|
|
5102
|
+
},
|
|
5103
|
+
getLogs: {
|
|
5104
|
+
path: "/apps/{app_id}/logs",
|
|
5105
|
+
method: "GET",
|
|
5106
|
+
authenticated: true
|
|
5107
|
+
},
|
|
5108
|
+
getBuildLogs: {
|
|
5109
|
+
path: "/apps/{app_id}/builds/{build_id}/logs",
|
|
5110
|
+
method: "GET",
|
|
5111
|
+
authenticated: true
|
|
5112
|
+
}
|
|
5113
|
+
};
|
|
5114
|
+
function buildServerlessEndpointPath(endpoint, params = {}) {
|
|
5115
|
+
let path = endpoint.path;
|
|
5116
|
+
for (const [key, value] of Object.entries(params)) {
|
|
5117
|
+
path = path.replace(`{${key}}`, value);
|
|
5118
|
+
}
|
|
5119
|
+
return path;
|
|
5120
|
+
}
|
|
5121
|
+
class ServerlessApiClient extends BaseApiClient {
|
|
5122
|
+
constructor(apiKey, config = {}) {
|
|
5123
|
+
super(apiKey, config, SERVICE_PATHS.SERVERLESS);
|
|
5124
|
+
}
|
|
5125
|
+
/**
|
|
5126
|
+
* List all serverless functions with optional pagination and search.
|
|
5127
|
+
*/
|
|
5128
|
+
async list(params = {}) {
|
|
5129
|
+
try {
|
|
5130
|
+
const endpoint = SERVERLESS_ENDPOINTS.list;
|
|
5131
|
+
const query = new URLSearchParams({
|
|
5132
|
+
page: String(params.page ?? 1),
|
|
5133
|
+
limit: String(params.limit ?? 20),
|
|
5134
|
+
sortBy: params.sortBy ?? "CreatedAt",
|
|
5135
|
+
sortOrder: params.sortOrder ?? "desc"
|
|
5136
|
+
});
|
|
5137
|
+
if (params.query) {
|
|
5138
|
+
query.set("q", params.query);
|
|
5139
|
+
}
|
|
5140
|
+
const url = `${this.baseURL}${endpoint.path}?${query.toString()}`;
|
|
5141
|
+
const response = await this.httpAdapter.request({
|
|
5142
|
+
url,
|
|
5143
|
+
method: endpoint.method,
|
|
5144
|
+
headers: this.buildHeaders(),
|
|
5145
|
+
timeout: this.config.timeout
|
|
5146
|
+
});
|
|
5147
|
+
return response.data;
|
|
5148
|
+
} catch (error) {
|
|
5149
|
+
return this.formatErrorResponse(error, "SERVERLESS");
|
|
5150
|
+
}
|
|
5151
|
+
}
|
|
5152
|
+
/**
|
|
5153
|
+
* Get a serverless function by its ID.
|
|
5154
|
+
*/
|
|
5155
|
+
async get(appId) {
|
|
5156
|
+
try {
|
|
5157
|
+
const endpoint = SERVERLESS_ENDPOINTS.get;
|
|
5158
|
+
const path = buildServerlessEndpointPath(endpoint, { app_id: appId });
|
|
5159
|
+
const url = `${this.baseURL}${path}`;
|
|
5160
|
+
console.log("url", url);
|
|
5161
|
+
const response = await this.httpAdapter.request({
|
|
5162
|
+
url,
|
|
5163
|
+
method: endpoint.method,
|
|
5164
|
+
headers: this.buildHeaders(),
|
|
5165
|
+
timeout: this.config.timeout
|
|
5166
|
+
});
|
|
5167
|
+
console.log("response", response.data);
|
|
5168
|
+
return response.data;
|
|
5169
|
+
} catch (error) {
|
|
5170
|
+
return this.formatErrorResponse(error, "SERVERLESS");
|
|
5171
|
+
}
|
|
5172
|
+
}
|
|
5173
|
+
/**
|
|
5174
|
+
* Create a new serverless function.
|
|
5175
|
+
*/
|
|
5176
|
+
async create(payload) {
|
|
5177
|
+
try {
|
|
5178
|
+
const endpoint = SERVERLESS_ENDPOINTS.create;
|
|
5179
|
+
const url = `${this.baseURL}${endpoint.path}`;
|
|
5180
|
+
const response = await this.httpAdapter.request({
|
|
5181
|
+
url,
|
|
5182
|
+
method: endpoint.method,
|
|
5183
|
+
headers: this.buildHeaders(),
|
|
5184
|
+
data: payload,
|
|
5185
|
+
timeout: this.config.timeout
|
|
5186
|
+
});
|
|
5187
|
+
return response.data;
|
|
5188
|
+
} catch (error) {
|
|
5189
|
+
return this.formatErrorResponse(error, "SERVERLESS");
|
|
5190
|
+
}
|
|
5191
|
+
}
|
|
5192
|
+
/**
|
|
5193
|
+
* Update an existing serverless function.
|
|
5194
|
+
*/
|
|
5195
|
+
async update(params) {
|
|
5196
|
+
try {
|
|
5197
|
+
const endpoint = SERVERLESS_ENDPOINTS.update;
|
|
5198
|
+
const path = buildServerlessEndpointPath(endpoint, {
|
|
5199
|
+
app_id: params.appId
|
|
5200
|
+
});
|
|
5201
|
+
const url = `${this.baseURL}${path}`;
|
|
5202
|
+
const response = await this.httpAdapter.request({
|
|
5203
|
+
url,
|
|
5204
|
+
method: endpoint.method,
|
|
5205
|
+
headers: this.buildHeaders(),
|
|
5206
|
+
data: params.payload,
|
|
5207
|
+
timeout: this.config.timeout
|
|
5208
|
+
});
|
|
5209
|
+
return response.data;
|
|
5210
|
+
} catch (error) {
|
|
5211
|
+
return this.formatErrorResponse(error, "SERVERLESS");
|
|
5212
|
+
}
|
|
5213
|
+
}
|
|
5214
|
+
/**
|
|
5215
|
+
* List builds for a serverless function.
|
|
5216
|
+
*/
|
|
5217
|
+
async getBuilds(params) {
|
|
5218
|
+
try {
|
|
5219
|
+
const endpoint = SERVERLESS_ENDPOINTS.getBuilds;
|
|
5220
|
+
const path = buildServerlessEndpointPath(endpoint, {
|
|
5221
|
+
app_id: params.appId
|
|
5222
|
+
});
|
|
5223
|
+
const query = new URLSearchParams({
|
|
5224
|
+
page: String(params.page ?? 1),
|
|
5225
|
+
limit: String(params.limit ?? 20),
|
|
5226
|
+
sortBy: "CreatedAt",
|
|
5227
|
+
sortOrder: "desc"
|
|
5228
|
+
});
|
|
5229
|
+
const url = `${this.baseURL}${path}?${query.toString()}`;
|
|
5230
|
+
const response = await this.httpAdapter.request({
|
|
5231
|
+
url,
|
|
5232
|
+
method: endpoint.method,
|
|
5233
|
+
headers: this.buildHeaders(),
|
|
5234
|
+
timeout: this.config.timeout
|
|
5235
|
+
});
|
|
5236
|
+
return response.data;
|
|
5237
|
+
} catch (error) {
|
|
5238
|
+
return this.formatErrorResponse(error, "SERVERLESS");
|
|
5239
|
+
}
|
|
5240
|
+
}
|
|
5241
|
+
/**
|
|
5242
|
+
* Get runtime logs for a serverless function.
|
|
5243
|
+
*/
|
|
5244
|
+
async getLogs(params) {
|
|
5245
|
+
try {
|
|
5246
|
+
const endpoint = SERVERLESS_ENDPOINTS.getLogs;
|
|
5247
|
+
const path = buildServerlessEndpointPath(endpoint, {
|
|
5248
|
+
app_id: params.appId
|
|
5249
|
+
});
|
|
5250
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
5251
|
+
const defaultStart = now - 24 * 60 * 60;
|
|
5252
|
+
const query = new URLSearchParams({
|
|
5253
|
+
page: String(params.page ?? 1),
|
|
5254
|
+
limit: String(params.limit ?? 50),
|
|
5255
|
+
sortBy: "Timestamp",
|
|
5256
|
+
sortOrder: params.sortOrder ?? "DESC",
|
|
5257
|
+
timestampStart: String(params.timestampStart ?? defaultStart),
|
|
5258
|
+
timestampEnd: String(params.timestampEnd ?? now),
|
|
5259
|
+
metric_interval: "60"
|
|
5260
|
+
});
|
|
5261
|
+
const url = `${this.baseURL}${path}?${query.toString()}`;
|
|
5262
|
+
const response = await this.httpAdapter.request({
|
|
5263
|
+
url,
|
|
5264
|
+
method: endpoint.method,
|
|
5265
|
+
headers: this.buildHeaders(),
|
|
5266
|
+
timeout: this.config.timeout
|
|
5267
|
+
});
|
|
5268
|
+
return response.data;
|
|
5269
|
+
} catch (error) {
|
|
5270
|
+
return this.formatErrorResponse(error, "SERVERLESS");
|
|
5271
|
+
}
|
|
5272
|
+
}
|
|
5273
|
+
/**
|
|
5274
|
+
* Get build logs for a specific build of a serverless function.
|
|
5275
|
+
*/
|
|
5276
|
+
async getBuildLogs(params) {
|
|
5277
|
+
try {
|
|
5278
|
+
const endpoint = SERVERLESS_ENDPOINTS.getBuildLogs;
|
|
5279
|
+
const path = buildServerlessEndpointPath(endpoint, {
|
|
5280
|
+
app_id: params.appId,
|
|
5281
|
+
build_id: params.buildId
|
|
5282
|
+
});
|
|
5283
|
+
const query = new URLSearchParams({
|
|
5284
|
+
limit: "-1",
|
|
5285
|
+
tail: "false",
|
|
5286
|
+
sortOrder: "asc",
|
|
5287
|
+
sortBy: "Timestamp"
|
|
5288
|
+
});
|
|
5289
|
+
const url = `${this.baseURL}${path}?${query.toString()}`;
|
|
5290
|
+
const response = await this.httpAdapter.request({
|
|
5291
|
+
url,
|
|
5292
|
+
method: endpoint.method,
|
|
5293
|
+
headers: this.buildHeaders(),
|
|
5294
|
+
timeout: this.config.timeout
|
|
5295
|
+
});
|
|
5296
|
+
return response.data;
|
|
5297
|
+
} catch (error) {
|
|
5298
|
+
return this.formatErrorResponse(error, "SERVERLESS");
|
|
5299
|
+
}
|
|
5300
|
+
}
|
|
5301
|
+
}
|
|
5302
|
+
function sleep(ms) {
|
|
5303
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5304
|
+
}
|
|
5305
|
+
class ServerlessResource extends BaseResource {
|
|
5306
|
+
constructor(client) {
|
|
5307
|
+
super(client, "/serverless");
|
|
5308
|
+
const config = client.getConfig();
|
|
5309
|
+
this.apiClient = new ServerlessApiClient(config.apiKey, {
|
|
5310
|
+
environment: config.environment,
|
|
5311
|
+
region: config.region,
|
|
5312
|
+
timeout: config.timeout,
|
|
5313
|
+
debug: config.debug
|
|
5314
|
+
});
|
|
5315
|
+
}
|
|
5316
|
+
/**
|
|
5317
|
+
* List all serverless functions with optional pagination and search.
|
|
5318
|
+
*
|
|
5319
|
+
* @param params - Optional pagination and filter parameters
|
|
5320
|
+
* @returns Paginated list of serverless functions
|
|
5321
|
+
*
|
|
5322
|
+
* @example
|
|
5323
|
+
* ```typescript
|
|
5324
|
+
* const list = await client.serverless.list();
|
|
5325
|
+
* const filtered = await client.serverless.list({ query: 'my-func', limit: 10 });
|
|
5326
|
+
* ```
|
|
5327
|
+
*/
|
|
5328
|
+
async list(params = {}) {
|
|
5329
|
+
return this.apiClient.list(params);
|
|
5330
|
+
}
|
|
5331
|
+
/**
|
|
5332
|
+
* Get a serverless function by its ID.
|
|
5333
|
+
*
|
|
5334
|
+
* @param appId - The serverless function ID
|
|
5335
|
+
* @returns The serverless function details
|
|
5336
|
+
*
|
|
5337
|
+
* @example
|
|
5338
|
+
* ```typescript
|
|
5339
|
+
* const fn = await client.serverless.get('serverless-id');
|
|
5340
|
+
* ```
|
|
5341
|
+
*/
|
|
5342
|
+
async get(appId) {
|
|
5343
|
+
return this.apiClient.get(appId);
|
|
5344
|
+
}
|
|
5345
|
+
/**
|
|
5346
|
+
* Create a new serverless function.
|
|
5347
|
+
*
|
|
5348
|
+
* Supports three runtime types:
|
|
5349
|
+
* - `code` — deploy code directly (blueprint)
|
|
5350
|
+
* - `git` — deploy from a Git repository
|
|
5351
|
+
* - `container` — deploy a Docker container
|
|
5352
|
+
*
|
|
5353
|
+
* @param params - The serverless creation payload
|
|
5354
|
+
* @returns The created serverless function
|
|
5355
|
+
*
|
|
5356
|
+
* @example
|
|
5357
|
+
* ```typescript
|
|
5358
|
+
* const fn = await client.serverless.create({
|
|
5359
|
+
* Name: 'my-api',
|
|
5360
|
+
* Runtime: 'code',
|
|
5361
|
+
* Resources: { CPU: 0.1, MemoryMB: 128, MemoryMaxMB: 128 },
|
|
5362
|
+
* Scaling: { AutoStop: false, Min: 1, Max: 1, MaxIdleTime: 0 },
|
|
5363
|
+
* CodeOpts: { Language: 'nodejs/20', Code: 'module.exports.handler = ...' },
|
|
5364
|
+
* });
|
|
5365
|
+
* ```
|
|
5366
|
+
*/
|
|
5367
|
+
async create(params) {
|
|
5368
|
+
return this.apiClient.create(params);
|
|
5369
|
+
}
|
|
5370
|
+
/**
|
|
5371
|
+
* Create a serverless function and poll until it reaches a terminal state.
|
|
5372
|
+
*
|
|
5373
|
+
* Combines `create()` + `pollStatus()` for a simpler workflow.
|
|
5374
|
+
*
|
|
5375
|
+
* @param params - The serverless creation payload
|
|
5376
|
+
* @returns The final serverless state after reaching a terminal status
|
|
5377
|
+
*
|
|
5378
|
+
* @example
|
|
5379
|
+
* ```typescript
|
|
5380
|
+
* const fn = await client.serverless.createAndWait({
|
|
5381
|
+
* Name: 'my-api',
|
|
5382
|
+
* Runtime: 'code',
|
|
5383
|
+
* CodeOpts: { Language: 'nodejs/20', Code: '...' },
|
|
5384
|
+
* Resources: { CPU: 0.1, MemoryMB: 128, MemoryMaxMB: 128 },
|
|
5385
|
+
* Scaling: { AutoStop: false, Min: 1, Max: 1, MaxIdleTime: 0 },
|
|
5386
|
+
* });
|
|
5387
|
+
* ```
|
|
5388
|
+
*/
|
|
5389
|
+
async createAndWait(params) {
|
|
5390
|
+
const createResult = await this.apiClient.create(params);
|
|
5391
|
+
if (isErrorResponse(createResult)) {
|
|
5392
|
+
return createResult;
|
|
5393
|
+
}
|
|
5394
|
+
const appId = createResult.data?.ID;
|
|
5395
|
+
if (!appId) {
|
|
5396
|
+
return {
|
|
5397
|
+
error: {
|
|
5398
|
+
code: "SERVERLESS_MISSING_ID",
|
|
5399
|
+
message: "Create API response did not contain an ID",
|
|
5400
|
+
meta: []
|
|
5401
|
+
}
|
|
5402
|
+
};
|
|
5403
|
+
}
|
|
5404
|
+
return this.pollStatus(appId);
|
|
5405
|
+
}
|
|
5406
|
+
/**
|
|
5407
|
+
* Update an existing serverless function.
|
|
5408
|
+
*
|
|
5409
|
+
* @param params - The update parameters (appId + partial payload)
|
|
5410
|
+
* @returns The updated serverless function
|
|
5411
|
+
*
|
|
5412
|
+
* @example
|
|
5413
|
+
* ```typescript
|
|
5414
|
+
* const updated = await client.serverless.update({
|
|
5415
|
+
* appId: 'serverless-id',
|
|
5416
|
+
* payload: { Scaling: { AutoStop: true, Min: 0, Max: 3, MaxIdleTime: 300 } },
|
|
5417
|
+
* });
|
|
5418
|
+
* ```
|
|
5419
|
+
*/
|
|
5420
|
+
async update(params) {
|
|
5421
|
+
return this.apiClient.update(params);
|
|
5422
|
+
}
|
|
5423
|
+
/**
|
|
5424
|
+
* Update a serverless function and poll until it reaches a terminal state.
|
|
5425
|
+
*
|
|
5426
|
+
* @param params - The update parameters (appId + partial payload)
|
|
5427
|
+
* @returns The final serverless state after reaching a terminal status
|
|
5428
|
+
*/
|
|
5429
|
+
async updateAndWait(params) {
|
|
5430
|
+
const updateResult = await this.apiClient.update(params);
|
|
5431
|
+
if (isErrorResponse(updateResult)) {
|
|
5432
|
+
return updateResult;
|
|
5433
|
+
}
|
|
5434
|
+
return this.pollStatus(params.appId);
|
|
5435
|
+
}
|
|
5436
|
+
/**
|
|
5437
|
+
* List builds for a serverless function.
|
|
5438
|
+
*
|
|
5439
|
+
* @param params - The app ID and optional pagination
|
|
5440
|
+
* @returns List of builds
|
|
5441
|
+
*
|
|
5442
|
+
* @example
|
|
5443
|
+
* ```typescript
|
|
5444
|
+
* const builds = await client.serverless.getBuilds({ appId: 'serverless-id' });
|
|
5445
|
+
* ```
|
|
5446
|
+
*/
|
|
5447
|
+
async getBuilds(params) {
|
|
5448
|
+
return this.apiClient.getBuilds(params);
|
|
5449
|
+
}
|
|
5450
|
+
/**
|
|
5451
|
+
* Get runtime logs for a serverless function.
|
|
5452
|
+
*
|
|
5453
|
+
* @param params - The app ID and optional time range / pagination
|
|
5454
|
+
* @returns Log entries
|
|
5455
|
+
*
|
|
5456
|
+
* @example
|
|
5457
|
+
* ```typescript
|
|
5458
|
+
* const logs = await client.serverless.getLogs({ appId: 'serverless-id' });
|
|
5459
|
+
* const recentLogs = await client.serverless.getLogs({
|
|
5460
|
+
* appId: 'serverless-id',
|
|
5461
|
+
* limit: 100,
|
|
5462
|
+
* sortOrder: 'DESC',
|
|
5463
|
+
* });
|
|
5464
|
+
* ```
|
|
5465
|
+
*/
|
|
5466
|
+
async getLogs(params) {
|
|
5467
|
+
return this.apiClient.getLogs(params);
|
|
5468
|
+
}
|
|
5469
|
+
/**
|
|
5470
|
+
* Get build logs for a specific build.
|
|
5471
|
+
*
|
|
5472
|
+
* @param params - The app ID and build ID
|
|
5473
|
+
* @returns Build log entries
|
|
5474
|
+
*
|
|
5475
|
+
* @example
|
|
5476
|
+
* ```typescript
|
|
5477
|
+
* const logs = await client.serverless.getBuildLogs({
|
|
5478
|
+
* appId: 'serverless-id',
|
|
5479
|
+
* buildId: 'build-id',
|
|
5480
|
+
* });
|
|
5481
|
+
* ```
|
|
5482
|
+
*/
|
|
5483
|
+
async getBuildLogs(params) {
|
|
5484
|
+
return this.apiClient.getBuildLogs(params);
|
|
5485
|
+
}
|
|
5486
|
+
/**
|
|
5487
|
+
* Poll a serverless function until it reaches a terminal status
|
|
5488
|
+
* (running, failed, degraded, or suspended).
|
|
5489
|
+
*
|
|
5490
|
+
* @param appId - The serverless function ID to poll
|
|
5491
|
+
* @param options - Optional polling configuration overrides
|
|
5492
|
+
* @returns The final serverless state or a timeout error
|
|
5493
|
+
*
|
|
5494
|
+
* @example
|
|
5495
|
+
* ```typescript
|
|
5496
|
+
* const result = await client.serverless.pollStatus('serverless-id');
|
|
5497
|
+
* ```
|
|
5498
|
+
*/
|
|
5499
|
+
async pollStatus(appId, options = {}) {
|
|
5500
|
+
const interval = options.intervalMs ?? STATUS_POLLING_INTERVAL_MS;
|
|
5501
|
+
const maxAttempts = options.maxAttempts ?? MAX_STATUS_POLLING_ATTEMPTS;
|
|
5502
|
+
const debug = this.client.getConfig().debug;
|
|
5503
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
5504
|
+
const result = await this.apiClient.get(appId);
|
|
5505
|
+
if (isErrorResponse(result)) {
|
|
5506
|
+
return result;
|
|
5507
|
+
}
|
|
5508
|
+
const status = result.data?.Status;
|
|
5509
|
+
if (debug) {
|
|
5510
|
+
console.log(
|
|
5511
|
+
`[ServerlessResource] Poll #${attempt + 1}: status=${status}`
|
|
5512
|
+
);
|
|
5513
|
+
}
|
|
5514
|
+
if (status && TERMINAL_STATUSES.includes(status)) {
|
|
5515
|
+
if (debug) {
|
|
5516
|
+
console.log(
|
|
5517
|
+
`[ServerlessResource] Reached terminal state: ${status} after ${attempt + 1} poll(s)`
|
|
5518
|
+
);
|
|
5519
|
+
}
|
|
5520
|
+
return result;
|
|
5521
|
+
}
|
|
5522
|
+
await sleep(interval);
|
|
5523
|
+
}
|
|
5524
|
+
return {
|
|
5525
|
+
error: {
|
|
5526
|
+
code: "SERVERLESS_STATUS_TIMEOUT",
|
|
5527
|
+
message: `Serverless ${appId} did not reach a terminal state within ${maxAttempts} polling attempts`,
|
|
5528
|
+
meta: [
|
|
5529
|
+
`app_id: ${appId}`,
|
|
5530
|
+
`max_attempts: ${maxAttempts}`,
|
|
5531
|
+
`interval_ms: ${interval}`
|
|
5532
|
+
]
|
|
5533
|
+
}
|
|
5534
|
+
};
|
|
5535
|
+
}
|
|
5536
|
+
}
|
|
5537
|
+
const DEFAULT_STORAGE_TYPE = "gcs";
|
|
5538
|
+
const MAX_SIGNED_URL_EXPIRE_MINUTES = 7 * 24 * 60;
|
|
5539
|
+
const STORAGE_ENDPOINTS = {
|
|
5540
|
+
list: {
|
|
5541
|
+
path: "/storage/list",
|
|
5542
|
+
method: "GET",
|
|
5543
|
+
authenticated: true
|
|
5544
|
+
},
|
|
5545
|
+
upload: {
|
|
5546
|
+
path: "/storage/upload",
|
|
5547
|
+
method: "POST",
|
|
5548
|
+
authenticated: true
|
|
5549
|
+
},
|
|
5550
|
+
directory: {
|
|
5551
|
+
path: "/storage/directory",
|
|
5552
|
+
method: "POST",
|
|
5553
|
+
authenticated: true
|
|
5554
|
+
},
|
|
5555
|
+
deleteFile: {
|
|
5556
|
+
path: "/storage/file",
|
|
5557
|
+
method: "DELETE",
|
|
5558
|
+
authenticated: true
|
|
5559
|
+
},
|
|
5560
|
+
objectAccess: {
|
|
5561
|
+
path: "/storage/change-object-access",
|
|
5562
|
+
method: "POST",
|
|
5563
|
+
authenticated: true
|
|
5564
|
+
},
|
|
5565
|
+
fileExport: {
|
|
5566
|
+
path: "/storage/file-export",
|
|
5567
|
+
method: "POST",
|
|
5568
|
+
authenticated: true
|
|
5569
|
+
}
|
|
5570
|
+
};
|
|
5571
|
+
function buildStorageEndpointPath(endpoint) {
|
|
5572
|
+
return endpoint.path;
|
|
5573
|
+
}
|
|
5574
|
+
const ERR_PREFIX = "STORAGE";
|
|
5575
|
+
class StorageApiClient extends BaseApiClient {
|
|
5576
|
+
constructor(apiKey, config = {}) {
|
|
5577
|
+
super(apiKey, config, SERVICE_PATHS.STORAGE);
|
|
5578
|
+
}
|
|
5579
|
+
/** Shared try/catch + http — same idea as inlined blocks in serverless/workflow clients, but DRY for storage. */
|
|
5580
|
+
async requestStorage(config) {
|
|
5581
|
+
try {
|
|
5582
|
+
const response = await this.httpAdapter.request(config);
|
|
5583
|
+
return response.data;
|
|
5584
|
+
} catch (error) {
|
|
5585
|
+
return this.formatErrorResponse(error, ERR_PREFIX);
|
|
5586
|
+
}
|
|
5587
|
+
}
|
|
5588
|
+
url(path, query) {
|
|
5589
|
+
const qs = query?.toString();
|
|
5590
|
+
return qs ? `${this.baseURL}${path}?${qs}` : `${this.baseURL}${path}`;
|
|
5591
|
+
}
|
|
5592
|
+
storageTypeQuery(storageType) {
|
|
5593
|
+
const q = new URLSearchParams();
|
|
5594
|
+
q.set("storageType", storageType ?? DEFAULT_STORAGE_TYPE);
|
|
5595
|
+
return q;
|
|
5596
|
+
}
|
|
5597
|
+
listQuery(params) {
|
|
5598
|
+
const q = this.storageTypeQuery(params.storageType);
|
|
5599
|
+
if (params.basePath !== void 0) {
|
|
5600
|
+
q.set("basePath", params.basePath);
|
|
5601
|
+
}
|
|
5602
|
+
if (params.pageSize !== void 0) {
|
|
5603
|
+
q.set("pageSize", String(params.pageSize));
|
|
5604
|
+
}
|
|
5605
|
+
if (params.nextPageToken !== void 0) {
|
|
5606
|
+
q.set("nextPageToken", params.nextPageToken);
|
|
5607
|
+
}
|
|
5608
|
+
return q;
|
|
5609
|
+
}
|
|
5610
|
+
/** `expire_in` is minutes; clamp to max temporary URL lifetime (7 days). */
|
|
5611
|
+
normalizeExpireInMinutes(raw) {
|
|
5612
|
+
const n = typeof raw === "number" ? raw : Number(raw);
|
|
5613
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
5614
|
+
throw new Error(
|
|
5615
|
+
"expire_in must be a positive number of minutes (max 7 days for temporary signed URLs)"
|
|
5616
|
+
);
|
|
5617
|
+
}
|
|
5618
|
+
const truncated = Math.trunc(n);
|
|
5619
|
+
return Math.min(Math.max(1, truncated), MAX_SIGNED_URL_EXPIRE_MINUTES);
|
|
5620
|
+
}
|
|
5621
|
+
isTruthyFormFlag(v) {
|
|
5622
|
+
if (v === void 0) return false;
|
|
5623
|
+
if (typeof v === "boolean") return v;
|
|
5624
|
+
const s = String(v).toLowerCase();
|
|
5625
|
+
return s === "true" || s === "1" || s === "yes";
|
|
5626
|
+
}
|
|
5627
|
+
/**
|
|
5628
|
+
* `public` shortcut: true + no expire_in → permanent; true + expire_in → temporary signed URL; false → private.
|
|
5629
|
+
* Legacy: omit `public` and pass `is_public` / `is_public_permanent` / `expire_in` as before.
|
|
5630
|
+
*/
|
|
5631
|
+
appendUploadVisibility(form, params) {
|
|
5632
|
+
const hasPublic = Object.prototype.hasOwnProperty.call(params, "public");
|
|
5633
|
+
if (hasPublic) {
|
|
5634
|
+
const pub = this.isTruthyFormFlag(params.public);
|
|
5635
|
+
if (!pub) {
|
|
5636
|
+
form.append("is_public", "false");
|
|
5637
|
+
return;
|
|
5638
|
+
}
|
|
5639
|
+
if (params.expire_in !== void 0) {
|
|
5640
|
+
form.append("is_public", "true");
|
|
5641
|
+
form.append(
|
|
5642
|
+
"expire_in",
|
|
5643
|
+
String(this.normalizeExpireInMinutes(params.expire_in))
|
|
5644
|
+
);
|
|
5645
|
+
return;
|
|
5646
|
+
}
|
|
5647
|
+
form.append("is_public", "false");
|
|
5648
|
+
form.append("is_public_permanent", "true");
|
|
5649
|
+
return;
|
|
5650
|
+
}
|
|
5651
|
+
if (params.is_public !== void 0) {
|
|
5652
|
+
form.append("is_public", String(params.is_public));
|
|
5653
|
+
}
|
|
5654
|
+
if (params.expire_in !== void 0) {
|
|
5655
|
+
form.append(
|
|
5656
|
+
"expire_in",
|
|
5657
|
+
String(this.normalizeExpireInMinutes(params.expire_in))
|
|
5658
|
+
);
|
|
5659
|
+
}
|
|
5660
|
+
if (params.is_public_permanent !== void 0) {
|
|
5661
|
+
form.append("is_public_permanent", String(params.is_public_permanent));
|
|
5662
|
+
}
|
|
5663
|
+
}
|
|
5664
|
+
buildUploadForm(params) {
|
|
5665
|
+
const logicalName = params.filename ?? params.file_name;
|
|
5666
|
+
if (logicalName == null || logicalName === "") {
|
|
5667
|
+
throw new Error("upload requires filename or file_name");
|
|
5668
|
+
}
|
|
5669
|
+
const form = new FormData();
|
|
5670
|
+
form.append("file", params.file, logicalName);
|
|
5671
|
+
form.append("filename", logicalName);
|
|
5672
|
+
if (params.filepath !== void 0) {
|
|
5673
|
+
form.append("filepath", params.filepath);
|
|
5674
|
+
}
|
|
5675
|
+
if (params.overwrite !== void 0) {
|
|
5676
|
+
form.append("overwrite", String(params.overwrite));
|
|
5677
|
+
}
|
|
5678
|
+
this.appendUploadVisibility(form, params);
|
|
5679
|
+
return form;
|
|
5680
|
+
}
|
|
5681
|
+
isErrorResult(result) {
|
|
5682
|
+
return typeof result === "object" && result !== null && "error" in result && result.error !== void 0;
|
|
5683
|
+
}
|
|
5684
|
+
isAclErrorResult(result) {
|
|
5685
|
+
return typeof result === "object" && result !== null && "error" in result && result.error !== void 0;
|
|
5686
|
+
}
|
|
5687
|
+
buildObjectAccessSummary(filePath, row, fallbackPublic) {
|
|
5688
|
+
const isPublic = row != null ? Boolean(row.isPublic) : fallbackPublic;
|
|
5689
|
+
return {
|
|
5690
|
+
message: isPublic ? "File has been made publicly accessible." : "File's public access has been revoked, making it private.",
|
|
5691
|
+
name: row?.name ?? row?.fullPath ?? filePath,
|
|
5692
|
+
size: row?.size ?? null,
|
|
5693
|
+
updated: row?.updatedAt ?? null,
|
|
5694
|
+
public: isPublic
|
|
5695
|
+
};
|
|
5696
|
+
}
|
|
5697
|
+
/**
|
|
5698
|
+
* Parses Hawkeye `POST /change-object-access` success JSON
|
|
5699
|
+
* `{ message, name, size, updated, public }`.
|
|
5700
|
+
*/
|
|
5701
|
+
parseChangeObjectAccessResponse(raw) {
|
|
5702
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
5703
|
+
const o = raw;
|
|
5704
|
+
if (typeof o.name !== "string" || typeof o.public !== "boolean") {
|
|
5705
|
+
return null;
|
|
5706
|
+
}
|
|
5707
|
+
const message = typeof o.message === "string" && o.message.length > 0 ? o.message : o.public ? "File has been made publicly accessible." : "File's public access has been revoked, making it private.";
|
|
5708
|
+
const size = o.size === void 0 || o.size === null ? null : String(o.size);
|
|
5709
|
+
const updated = o.updated === void 0 || o.updated === null ? null : String(o.updated);
|
|
5710
|
+
return {
|
|
5711
|
+
message,
|
|
5712
|
+
name: o.name,
|
|
5713
|
+
size,
|
|
5714
|
+
updated,
|
|
5715
|
+
public: o.public
|
|
5716
|
+
};
|
|
5717
|
+
}
|
|
5718
|
+
/** Resolves the list row for a full object path (same folder semantics as download size resolution). */
|
|
5719
|
+
async findFileListItem(filePath, storageType) {
|
|
5720
|
+
const lastSlash = filePath.lastIndexOf("/");
|
|
5721
|
+
const nameOnly = lastSlash === -1 ? filePath : filePath.slice(lastSlash + 1);
|
|
5722
|
+
const folderPrefix = lastSlash === -1 ? "" : filePath.slice(0, lastSlash);
|
|
5723
|
+
const basePath = folderPrefix ? `${folderPrefix}/` : "";
|
|
5724
|
+
const result = await this.list({ basePath, storageType });
|
|
5725
|
+
if (this.isErrorResult(result)) return result;
|
|
5726
|
+
const rows = result.files?.data ?? [];
|
|
5727
|
+
return rows.find(
|
|
5728
|
+
(r) => r.fullPath === filePath || !r.isDirectory && (r.name === nameOnly || r.fullPath === filePath)
|
|
5729
|
+
) ?? null;
|
|
5730
|
+
}
|
|
5731
|
+
/** Keep only SDK list fields; flatten metadata to `size` / `updatedAt`. */
|
|
5732
|
+
normalizeListItem(raw) {
|
|
5733
|
+
const meta = raw.metadata ?? {};
|
|
5734
|
+
let size;
|
|
5735
|
+
if (meta.size !== void 0 && meta.size !== null) {
|
|
5736
|
+
size = String(meta.size);
|
|
5737
|
+
}
|
|
5738
|
+
const updatedRaw = meta.updated ?? meta.timeUpdated;
|
|
5739
|
+
let updatedAt;
|
|
5740
|
+
if (updatedRaw !== void 0 && updatedRaw !== null) {
|
|
5741
|
+
updatedAt = String(updatedRaw);
|
|
5742
|
+
}
|
|
5743
|
+
const name = raw.name;
|
|
5744
|
+
const parentPath = raw.parentPath;
|
|
5745
|
+
const isDirectory = Boolean(raw.isDirectory);
|
|
5746
|
+
let fullPath;
|
|
5747
|
+
if (!isDirectory && name) {
|
|
5748
|
+
fullPath = parentPath ? `${parentPath}/${name}` : name;
|
|
5749
|
+
}
|
|
5750
|
+
const cdnRaw = raw.cdnUrl;
|
|
5751
|
+
const cdnUrl = cdnRaw === void 0 || cdnRaw === null ? null : cdnRaw;
|
|
5752
|
+
const item = {
|
|
5753
|
+
name,
|
|
5754
|
+
path: raw.path,
|
|
5755
|
+
folderName: raw.folderName,
|
|
5756
|
+
parentPath,
|
|
5757
|
+
isDirectory,
|
|
5758
|
+
isPublic: raw.isPublic,
|
|
5759
|
+
cdnUrl,
|
|
5760
|
+
fullPath
|
|
5761
|
+
};
|
|
5762
|
+
if (size !== void 0) item.size = size;
|
|
5763
|
+
if (updatedAt !== void 0) item.updatedAt = updatedAt;
|
|
5764
|
+
return item;
|
|
5765
|
+
}
|
|
5766
|
+
/** Map wire `shareable_link` to `temporary_sharable_link`. */
|
|
5767
|
+
normalizeUploadData(raw) {
|
|
5768
|
+
const message = String(raw.message ?? "");
|
|
5769
|
+
const path = String(raw.path ?? "");
|
|
5770
|
+
const out = { message, path };
|
|
5771
|
+
const link = raw.temporary_sharable_link ?? raw.shareable_link;
|
|
5772
|
+
if (link !== void 0 && link !== null && String(link) !== "") {
|
|
5773
|
+
out.temporary_sharable_link = String(link);
|
|
5774
|
+
}
|
|
5775
|
+
if (raw.public_url !== void 0 && raw.public_url !== null) {
|
|
5776
|
+
out.public_url = String(raw.public_url);
|
|
5777
|
+
}
|
|
5778
|
+
return out;
|
|
5779
|
+
}
|
|
5780
|
+
normalizeListResponse(data) {
|
|
5781
|
+
const payload = data.files;
|
|
5782
|
+
if (!payload?.data) return data;
|
|
5783
|
+
return {
|
|
5784
|
+
files: {
|
|
5785
|
+
...payload,
|
|
5786
|
+
data: payload.data.map(
|
|
5787
|
+
(item) => this.normalizeListItem(item)
|
|
5788
|
+
)
|
|
5789
|
+
}
|
|
5790
|
+
};
|
|
5791
|
+
}
|
|
5792
|
+
async list(params = {}) {
|
|
5793
|
+
const endpoint = STORAGE_ENDPOINTS.list;
|
|
5794
|
+
const path = buildStorageEndpointPath(endpoint);
|
|
5795
|
+
const result = await this.requestStorage({
|
|
5796
|
+
url: this.url(path, this.listQuery(params)),
|
|
5797
|
+
method: endpoint.method,
|
|
5798
|
+
headers: this.buildHeaders(),
|
|
5799
|
+
timeout: this.config.timeout
|
|
5800
|
+
});
|
|
5801
|
+
if (this.isErrorResult(result)) return result;
|
|
5802
|
+
return this.normalizeListResponse(result);
|
|
5803
|
+
}
|
|
5804
|
+
async createFolder(body) {
|
|
5805
|
+
const endpoint = STORAGE_ENDPOINTS.directory;
|
|
5806
|
+
const path = buildStorageEndpointPath(endpoint);
|
|
5807
|
+
const q = this.storageTypeQuery(body.storageType);
|
|
5808
|
+
return this.requestStorage({
|
|
5809
|
+
url: this.url(path, q),
|
|
5810
|
+
method: endpoint.method,
|
|
5811
|
+
headers: this.buildHeaders(),
|
|
5812
|
+
data: { folder_path: body.folder_path },
|
|
5813
|
+
timeout: this.config.timeout
|
|
5814
|
+
});
|
|
5815
|
+
}
|
|
5816
|
+
async deleteFile(params) {
|
|
5817
|
+
const endpoint = STORAGE_ENDPOINTS.deleteFile;
|
|
5818
|
+
const path = buildStorageEndpointPath(endpoint);
|
|
5819
|
+
const q = this.storageTypeQuery(params.storageType);
|
|
5820
|
+
q.set("filename", params.filename);
|
|
5821
|
+
const data = {};
|
|
5822
|
+
if (params.filepath !== void 0) {
|
|
5823
|
+
data.filepath = params.filepath;
|
|
5824
|
+
}
|
|
5825
|
+
if (params.totalsize !== void 0) {
|
|
5826
|
+
data.totalsize = params.totalsize;
|
|
5827
|
+
}
|
|
5828
|
+
return this.requestStorage({
|
|
5829
|
+
url: this.url(path, q),
|
|
5830
|
+
method: endpoint.method,
|
|
5831
|
+
headers: this.buildHeaders(),
|
|
5832
|
+
data: Object.keys(data).length ? data : void 0,
|
|
5833
|
+
timeout: this.config.timeout
|
|
5834
|
+
});
|
|
5835
|
+
}
|
|
5836
|
+
/**
|
|
5837
|
+
* `POST /change-object-access` — used by `makePublic` / `makePrivate`.
|
|
5838
|
+
* Preferring API body `{ message, name, size, updated, public }`; otherwise falls back to a parent list.
|
|
5839
|
+
*/
|
|
5840
|
+
async setObjectAccess(body) {
|
|
5841
|
+
const endpoint = STORAGE_ENDPOINTS.objectAccess;
|
|
5842
|
+
const path = buildStorageEndpointPath(endpoint);
|
|
5843
|
+
const acl = await this.requestStorage({
|
|
5844
|
+
url: this.url(path),
|
|
5845
|
+
method: endpoint.method,
|
|
5846
|
+
headers: this.buildHeaders(),
|
|
5847
|
+
data: {
|
|
5848
|
+
file_path: body.file_path,
|
|
5849
|
+
public: body.public
|
|
5850
|
+
},
|
|
5851
|
+
timeout: this.config.timeout
|
|
5852
|
+
});
|
|
5853
|
+
if (this.isAclErrorResult(acl)) return acl;
|
|
5854
|
+
const fromApi = this.parseChangeObjectAccessResponse(acl);
|
|
5855
|
+
if (fromApi !== null) {
|
|
5856
|
+
return fromApi;
|
|
5857
|
+
}
|
|
5858
|
+
const row = await this.findFileListItem(body.file_path);
|
|
5859
|
+
if (this.isAclErrorResult(row)) return row;
|
|
5860
|
+
return this.buildObjectAccessSummary(body.file_path, row, body.public);
|
|
5861
|
+
}
|
|
5862
|
+
async upload(params) {
|
|
5863
|
+
const endpoint = STORAGE_ENDPOINTS.upload;
|
|
5864
|
+
const path = buildStorageEndpointPath(endpoint);
|
|
5865
|
+
const q = this.storageTypeQuery(params.storageType);
|
|
5866
|
+
const headers = { ...this.buildHeaders() };
|
|
5867
|
+
delete headers["Content-Type"];
|
|
5868
|
+
const result = await this.requestStorage({
|
|
5869
|
+
url: this.url(path, q),
|
|
5870
|
+
method: endpoint.method,
|
|
5871
|
+
headers,
|
|
5872
|
+
data: this.buildUploadForm(params),
|
|
5873
|
+
timeout: this.config.timeout
|
|
5874
|
+
});
|
|
5875
|
+
if (this.isAclErrorResult(result)) return result;
|
|
5876
|
+
return this.normalizeUploadData(result);
|
|
5877
|
+
}
|
|
5878
|
+
async resolveFileSizeBytes(fileName, storageType) {
|
|
5879
|
+
const lastSlash = fileName.lastIndexOf("/");
|
|
5880
|
+
const nameOnly = lastSlash === -1 ? fileName : fileName.slice(lastSlash + 1);
|
|
5881
|
+
const folderPrefix = lastSlash === -1 ? "" : fileName.slice(0, lastSlash);
|
|
5882
|
+
const basePath = folderPrefix ? `${folderPrefix}/` : "";
|
|
5883
|
+
const result = await this.list({ basePath, storageType });
|
|
5884
|
+
if (this.isErrorResult(result)) {
|
|
5885
|
+
throw new Error(
|
|
5886
|
+
result.error?.message ?? "List failed while resolving file size"
|
|
5887
|
+
);
|
|
5888
|
+
}
|
|
5889
|
+
const rows = result.files?.data ?? [];
|
|
5890
|
+
const hit = rows.find(
|
|
5891
|
+
(r) => r.fullPath === fileName || !r.isDirectory && r.name === nameOnly
|
|
5892
|
+
);
|
|
5893
|
+
const s = hit?.size;
|
|
5894
|
+
if (s === void 0) {
|
|
5895
|
+
throw new Error(
|
|
5896
|
+
`Could not resolve size for "${fileName}". Pass sizeBytes (from list() item size).`
|
|
5897
|
+
);
|
|
5898
|
+
}
|
|
5899
|
+
return Number(s);
|
|
5900
|
+
}
|
|
5901
|
+
/**
|
|
5902
|
+
* Download file bytes via `POST /file-export` (range 0..size-1).
|
|
5903
|
+
*/
|
|
5904
|
+
async downloadFile(params) {
|
|
5905
|
+
try {
|
|
5906
|
+
const sizeBytes = params.sizeBytes !== void 0 ? Number(params.sizeBytes) : await this.resolveFileSizeBytes(
|
|
5907
|
+
params.file_name,
|
|
5908
|
+
params.storageType
|
|
5909
|
+
);
|
|
5910
|
+
if (!Number.isFinite(sizeBytes) || sizeBytes <= 0) {
|
|
5911
|
+
throw new Error(
|
|
5912
|
+
"Invalid or unknown file size. Pass sizeBytes from list() item size."
|
|
5913
|
+
);
|
|
5914
|
+
}
|
|
5915
|
+
const endpoint = STORAGE_ENDPOINTS.fileExport;
|
|
5916
|
+
const path = buildStorageEndpointPath(endpoint);
|
|
5917
|
+
const q = this.storageTypeQuery(params.storageType);
|
|
5918
|
+
const response = await this.httpAdapter.request({
|
|
5919
|
+
url: this.url(path, q),
|
|
5920
|
+
method: "POST",
|
|
5921
|
+
headers: { ...this.buildHeaders(), Accept: "*/*" },
|
|
5922
|
+
data: {
|
|
5923
|
+
file_name: params.file_name,
|
|
5924
|
+
start_byte: 0,
|
|
5925
|
+
end_byte: sizeBytes - 1
|
|
5926
|
+
},
|
|
5927
|
+
timeout: this.config.timeout,
|
|
5928
|
+
responseType: "arraybuffer"
|
|
5929
|
+
});
|
|
5930
|
+
if (response.status < 200 || response.status >= 300) {
|
|
5931
|
+
const d = response.data;
|
|
5932
|
+
if (d && typeof d === "object" && d !== null && "error" in d) {
|
|
5933
|
+
return d;
|
|
5934
|
+
}
|
|
5935
|
+
return this.formatErrorResponse(
|
|
5936
|
+
new Error(`Download failed: HTTP ${response.status}`),
|
|
5937
|
+
ERR_PREFIX
|
|
5938
|
+
);
|
|
5939
|
+
}
|
|
5940
|
+
if (!(response.data instanceof ArrayBuffer)) {
|
|
5941
|
+
throw new Error("Expected binary response body");
|
|
5942
|
+
}
|
|
5943
|
+
const headers = response.headers;
|
|
5944
|
+
const ct = headers["content-type"] ?? headers["Content-Type"] ?? void 0;
|
|
5945
|
+
return {
|
|
5946
|
+
bytes: response.data,
|
|
5947
|
+
status: response.status,
|
|
5948
|
+
contentType: ct
|
|
5949
|
+
};
|
|
5950
|
+
} catch (error) {
|
|
5951
|
+
return this.formatErrorResponse(error, ERR_PREFIX);
|
|
5952
|
+
}
|
|
5953
|
+
}
|
|
5954
|
+
}
|
|
5955
|
+
class StorageResource extends BaseResource {
|
|
5956
|
+
constructor(client) {
|
|
5957
|
+
super(client, "/storage");
|
|
5958
|
+
const config = client.getConfig();
|
|
5959
|
+
this.apiClient = new StorageApiClient(config.apiKey, {
|
|
5960
|
+
environment: config.environment,
|
|
5961
|
+
region: config.region,
|
|
5962
|
+
timeout: config.timeout,
|
|
5963
|
+
debug: config.debug,
|
|
5964
|
+
headers: config.headers
|
|
5965
|
+
});
|
|
5966
|
+
}
|
|
5967
|
+
async list(params = {}) {
|
|
5968
|
+
return this.apiClient.list(params);
|
|
5969
|
+
}
|
|
5970
|
+
/** Direct upload — `POST /upload` (multipart); server persists the object. */
|
|
5971
|
+
async upload(params) {
|
|
5972
|
+
return this.apiClient.upload(params);
|
|
5973
|
+
}
|
|
5974
|
+
async createFolder(params) {
|
|
5975
|
+
return this.apiClient.createFolder(params);
|
|
5976
|
+
}
|
|
5977
|
+
async deleteFile(params) {
|
|
5978
|
+
return this.apiClient.deleteFile(params);
|
|
5979
|
+
}
|
|
5980
|
+
async makePublic(filePath) {
|
|
5981
|
+
return this.apiClient.setObjectAccess({
|
|
5982
|
+
file_path: filePath,
|
|
5983
|
+
public: true
|
|
5984
|
+
});
|
|
5985
|
+
}
|
|
5986
|
+
async makePrivate(filePath) {
|
|
5987
|
+
return this.apiClient.setObjectAccess({
|
|
5988
|
+
file_path: filePath,
|
|
5989
|
+
public: false
|
|
5990
|
+
});
|
|
5991
|
+
}
|
|
5992
|
+
/** Download file bytes via `POST /file-export` (full file). */
|
|
5993
|
+
async downloadFile(params) {
|
|
5994
|
+
return this.apiClient.downloadFile(params);
|
|
5995
|
+
}
|
|
5996
|
+
}
|
|
4999
5997
|
class BolticClient {
|
|
5000
5998
|
constructor(apiKey, options = {}) {
|
|
5001
5999
|
this.currentDatabase = null;
|
|
@@ -5019,6 +6017,8 @@ class BolticClient {
|
|
|
5019
6017
|
this.indexResource = new IndexResource(this.baseClient);
|
|
5020
6018
|
this.databaseResource = new DatabaseResource(this.baseClient);
|
|
5021
6019
|
this.workflowResource = new WorkflowResource(this.baseClient);
|
|
6020
|
+
this.serverlessResource = new ServerlessResource(this.baseClient);
|
|
6021
|
+
this.storageResource = new StorageResource(this.baseClient);
|
|
5022
6022
|
this.currentDatabase = null;
|
|
5023
6023
|
}
|
|
5024
6024
|
/**
|
|
@@ -5200,6 +6200,58 @@ class BolticClient {
|
|
|
5200
6200
|
getIntegrationForm: (params) => this.workflowResource.getIntegrationForm(params)
|
|
5201
6201
|
};
|
|
5202
6202
|
}
|
|
6203
|
+
/**
|
|
6204
|
+
* Serverless function operations.
|
|
6205
|
+
*
|
|
6206
|
+
* @example
|
|
6207
|
+
* ```typescript
|
|
6208
|
+
* // List all serverless functions
|
|
6209
|
+
* const list = await client.serverless.list();
|
|
6210
|
+
*
|
|
6211
|
+
* // Create a new serverless function
|
|
6212
|
+
* const fn = await client.serverless.create({
|
|
6213
|
+
* Name: 'my-api',
|
|
6214
|
+
* Runtime: 'code',
|
|
6215
|
+
* CodeOpts: { Language: 'nodejs/20', Code: '...' },
|
|
6216
|
+
* Resources: { CPU: 0.1, MemoryMB: 128, MemoryMaxMB: 128 },
|
|
6217
|
+
* Scaling: { AutoStop: false, Min: 1, Max: 1, MaxIdleTime: 0 },
|
|
6218
|
+
* });
|
|
6219
|
+
*
|
|
6220
|
+
* // Get a serverless function by ID
|
|
6221
|
+
* const details = await client.serverless.get('serverless-id');
|
|
6222
|
+
*
|
|
6223
|
+
* // Get builds
|
|
6224
|
+
* const builds = await client.serverless.getBuilds({ appId: 'id' });
|
|
6225
|
+
*
|
|
6226
|
+
* // Get runtime logs
|
|
6227
|
+
* const logs = await client.serverless.getLogs({ appId: 'id' });
|
|
6228
|
+
* ```
|
|
6229
|
+
*/
|
|
6230
|
+
get serverless() {
|
|
6231
|
+
return {
|
|
6232
|
+
list: (params) => this.serverlessResource.list(params),
|
|
6233
|
+
get: (appId) => this.serverlessResource.get(appId),
|
|
6234
|
+
create: (params) => this.serverlessResource.create(params),
|
|
6235
|
+
createAndWait: (params) => this.serverlessResource.createAndWait(params),
|
|
6236
|
+
update: (params) => this.serverlessResource.update(params),
|
|
6237
|
+
updateAndWait: (params) => this.serverlessResource.updateAndWait(params),
|
|
6238
|
+
getBuilds: (params) => this.serverlessResource.getBuilds(params),
|
|
6239
|
+
getLogs: (params) => this.serverlessResource.getLogs(params),
|
|
6240
|
+
getBuildLogs: (params) => this.serverlessResource.getBuildLogs(params),
|
|
6241
|
+
pollStatus: (appId, options) => this.serverlessResource.pollStatus(appId, options)
|
|
6242
|
+
};
|
|
6243
|
+
}
|
|
6244
|
+
get storage() {
|
|
6245
|
+
return {
|
|
6246
|
+
list: (params) => this.storageResource.list(params),
|
|
6247
|
+
upload: (params) => this.storageResource.upload(params),
|
|
6248
|
+
createFolder: (params) => this.storageResource.createFolder(params),
|
|
6249
|
+
deleteFile: (params) => this.storageResource.deleteFile(params),
|
|
6250
|
+
makePublic: (filePath) => this.storageResource.makePublic(filePath),
|
|
6251
|
+
makePrivate: (filePath) => this.storageResource.makePrivate(filePath),
|
|
6252
|
+
downloadFile: (params) => this.storageResource.downloadFile(params)
|
|
6253
|
+
};
|
|
6254
|
+
}
|
|
5203
6255
|
// SQL resource access for testing
|
|
5204
6256
|
getSqlResource() {
|
|
5205
6257
|
return this.sqlResource;
|
|
@@ -5283,6 +6335,8 @@ class BolticClient {
|
|
|
5283
6335
|
this.indexResource = new IndexResource(this.baseClient);
|
|
5284
6336
|
this.databaseResource = new DatabaseResource(this.baseClient);
|
|
5285
6337
|
this.workflowResource = new WorkflowResource(this.baseClient);
|
|
6338
|
+
this.serverlessResource = new ServerlessResource(this.baseClient);
|
|
6339
|
+
this.storageResource = new StorageResource(this.baseClient);
|
|
5286
6340
|
}
|
|
5287
6341
|
// Security methods to prevent API key exposure
|
|
5288
6342
|
toString() {
|
|
@@ -5311,9 +6365,22 @@ const VERSION = "1.0.0";
|
|
|
5311
6365
|
export {
|
|
5312
6366
|
AuthManager$1 as AuthManager,
|
|
5313
6367
|
BolticClient,
|
|
6368
|
+
DEFAULT_RESOURCES,
|
|
6369
|
+
DEFAULT_SCALING,
|
|
6370
|
+
DEFAULT_STORAGE_TYPE,
|
|
6371
|
+
MAX_SIGNED_URL_EXPIRE_MINUTES,
|
|
6372
|
+
MAX_STATUS_POLLING_ATTEMPTS,
|
|
5314
6373
|
SERVICE_PATHS,
|
|
6374
|
+
STATUS_POLLING_INTERVAL_MS,
|
|
6375
|
+
STORAGE_ENDPOINTS,
|
|
6376
|
+
ServerlessApiClient,
|
|
6377
|
+
ServerlessResource,
|
|
6378
|
+
StorageApiClient,
|
|
6379
|
+
StorageResource,
|
|
6380
|
+
TERMINAL_STATUSES,
|
|
5315
6381
|
VERSION,
|
|
5316
6382
|
WorkflowResource,
|
|
6383
|
+
buildStorageEndpointPath,
|
|
5317
6384
|
createClient,
|
|
5318
6385
|
createErrorWithContext$1 as createErrorWithContext,
|
|
5319
6386
|
formatError$1 as formatError,
|