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