@boltic/sdk 0.1.4 → 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;
@@ -550,7 +601,8 @@ const SERVICE_PATHS = {
550
601
  DATABASES: "/service/sdk/boltic-tables/v1",
551
602
  WORKFLOW_TEMPORAL: "/service/panel/temporal/v1.0",
552
603
  INTEGRATION: "/service/panel/integration/v1",
553
- SERVERLESS: "/service/panel/serverless/v1.0"
604
+ SERVERLESS: "/service/panel/serverless/v1.0",
605
+ STORAGE: "/service/panel/storage/v1.0"
554
606
  };
555
607
  class BaseApiClient {
556
608
  constructor(apiKey, config = {}, servicePath = SERVICE_PATHS.DATABASES) {
@@ -4077,7 +4129,10 @@ class SqlApiClient extends BaseApiClient {
4077
4129
  const response = await this.httpAdapter.request({
4078
4130
  url,
4079
4131
  method: endpoint.method,
4080
- headers: this.buildHeaders(),
4132
+ headers: {
4133
+ ...this.buildHeaders(),
4134
+ "x-request-source": "sdk"
4135
+ },
4081
4136
  data: request,
4082
4137
  timeout: this.config.timeout
4083
4138
  });
@@ -4108,7 +4163,10 @@ class SqlApiClient extends BaseApiClient {
4108
4163
  const response = await this.httpAdapter.request({
4109
4164
  url,
4110
4165
  method: endpoint.method,
4111
- headers: this.buildHeaders(),
4166
+ headers: {
4167
+ ...this.buildHeaders(),
4168
+ "x-request-source": "sdk"
4169
+ },
4112
4170
  data: request,
4113
4171
  timeout: this.config.timeout
4114
4172
  });
@@ -5478,6 +5536,466 @@ class ServerlessResource extends BaseResource {
5478
5536
  };
5479
5537
  }
5480
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
+ }
5481
5999
  class BolticClient {
5482
6000
  constructor(apiKey, options = {}) {
5483
6001
  this.currentDatabase = null;
@@ -5502,6 +6020,7 @@ class BolticClient {
5502
6020
  this.databaseResource = new DatabaseResource(this.baseClient);
5503
6021
  this.workflowResource = new WorkflowResource(this.baseClient);
5504
6022
  this.serverlessResource = new ServerlessResource(this.baseClient);
6023
+ this.storageResource = new StorageResource(this.baseClient);
5505
6024
  this.currentDatabase = null;
5506
6025
  }
5507
6026
  /**
@@ -5724,6 +6243,17 @@ class BolticClient {
5724
6243
  pollStatus: (appId, options) => this.serverlessResource.pollStatus(appId, options)
5725
6244
  };
5726
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
+ }
5727
6257
  // SQL resource access for testing
5728
6258
  getSqlResource() {
5729
6259
  return this.sqlResource;
@@ -5808,6 +6338,7 @@ class BolticClient {
5808
6338
  this.databaseResource = new DatabaseResource(this.baseClient);
5809
6339
  this.workflowResource = new WorkflowResource(this.baseClient);
5810
6340
  this.serverlessResource = new ServerlessResource(this.baseClient);
6341
+ this.storageResource = new StorageResource(this.baseClient);
5811
6342
  }
5812
6343
  // Security methods to prevent API key exposure
5813
6344
  toString() {
@@ -5837,14 +6368,20 @@ exports.AuthManager = AuthManager$1;
5837
6368
  exports.BolticClient = BolticClient;
5838
6369
  exports.DEFAULT_RESOURCES = DEFAULT_RESOURCES;
5839
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;
5840
6373
  exports.MAX_STATUS_POLLING_ATTEMPTS = MAX_STATUS_POLLING_ATTEMPTS;
5841
6374
  exports.SERVICE_PATHS = SERVICE_PATHS;
5842
6375
  exports.STATUS_POLLING_INTERVAL_MS = STATUS_POLLING_INTERVAL_MS;
6376
+ exports.STORAGE_ENDPOINTS = STORAGE_ENDPOINTS;
5843
6377
  exports.ServerlessApiClient = ServerlessApiClient;
5844
6378
  exports.ServerlessResource = ServerlessResource;
6379
+ exports.StorageApiClient = StorageApiClient;
6380
+ exports.StorageResource = StorageResource;
5845
6381
  exports.TERMINAL_STATUSES = TERMINAL_STATUSES;
5846
6382
  exports.VERSION = VERSION;
5847
6383
  exports.WorkflowResource = WorkflowResource;
6384
+ exports.buildStorageEndpointPath = buildStorageEndpointPath;
5848
6385
  exports.createClient = createClient;
5849
6386
  exports.createErrorWithContext = createErrorWithContext$1;
5850
6387
  exports.formatError = formatError$1;