@insforge/sdk 1.2.4 → 1.2.5-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -284,6 +284,51 @@ var TokenManager = class {
284
284
  // src/lib/http-client.ts
285
285
  var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([500, 502, 503, 504]);
286
286
  var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
287
+ function serializeBody(method, body, headers) {
288
+ if (body === void 0) return void 0;
289
+ if (method === "GET" || method === "HEAD") return void 0;
290
+ if (typeof FormData !== "undefined" && body instanceof FormData) {
291
+ return body;
292
+ }
293
+ headers["Content-Type"] = "application/json;charset=UTF-8";
294
+ return JSON.stringify(body);
295
+ }
296
+ async function parseResponse(response) {
297
+ if (response.status === 204) return void 0;
298
+ let data;
299
+ const contentType = response.headers.get("content-type");
300
+ try {
301
+ if (contentType?.includes("json")) {
302
+ data = await response.json();
303
+ } else {
304
+ data = await response.text();
305
+ }
306
+ } catch (parseErr) {
307
+ throw new InsForgeError(
308
+ `Failed to parse response body: ${parseErr?.message || "Unknown error"}`,
309
+ response.status,
310
+ response.ok ? "PARSE_ERROR" : "REQUEST_FAILED"
311
+ );
312
+ }
313
+ if (!response.ok) {
314
+ if (data && typeof data === "object" && "error" in data) {
315
+ data.statusCode ?? (data.statusCode = data.status ?? response.status);
316
+ const error = InsForgeError.fromApiError(data);
317
+ Object.keys(data).forEach((key) => {
318
+ if (key !== "error" && key !== "message" && key !== "statusCode") {
319
+ error[key] = data[key];
320
+ }
321
+ });
322
+ throw error;
323
+ }
324
+ throw new InsForgeError(
325
+ `Request failed: ${response.statusText}`,
326
+ response.status,
327
+ "REQUEST_FAILED"
328
+ );
329
+ }
330
+ return data;
331
+ }
287
332
  var HttpClient = class {
288
333
  /**
289
334
  * Creates a new HttpClient instance.
@@ -377,17 +422,7 @@ var HttpClient = class {
377
422
  if (authToken) {
378
423
  requestHeaders["Authorization"] = `Bearer ${authToken}`;
379
424
  }
380
- let processedBody;
381
- if (body !== void 0) {
382
- if (typeof FormData !== "undefined" && body instanceof FormData) {
383
- processedBody = body;
384
- } else {
385
- if (method !== "GET") {
386
- requestHeaders["Content-Type"] = "application/json;charset=UTF-8";
387
- }
388
- processedBody = JSON.stringify(body);
389
- }
390
- }
425
+ const processedBody = serializeBody(method, body, requestHeaders);
391
426
  if (headers instanceof Headers) {
392
427
  headers.forEach((value, key) => {
393
428
  requestHeaders[key] = value;
@@ -466,53 +501,23 @@ var HttpClient = class {
466
501
  );
467
502
  continue;
468
503
  }
469
- if (response.status === 204) {
470
- if (timer !== void 0) clearTimeout(timer);
471
- return void 0;
472
- }
473
504
  let data;
474
- const contentType = response.headers.get("content-type");
475
505
  try {
476
- if (contentType?.includes("json")) {
477
- data = await response.json();
478
- } else {
479
- data = await response.text();
480
- }
481
- } catch (parseErr) {
506
+ data = await parseResponse(response);
507
+ } catch (err) {
482
508
  if (timer !== void 0) clearTimeout(timer);
483
- throw new InsForgeError(
484
- `Failed to parse response body: ${parseErr?.message || "Unknown error"}`,
485
- response.status,
486
- response.ok ? "PARSE_ERROR" : "REQUEST_FAILED"
487
- );
488
- }
489
- if (timer !== void 0) clearTimeout(timer);
490
- if (!response.ok) {
491
- this.logger.logResponse(
492
- method,
493
- url,
494
- response.status,
495
- Date.now() - startTime,
496
- data
497
- );
498
- if (data && typeof data === "object" && "error" in data) {
499
- if (!data.statusCode && !data.status) {
500
- data.statusCode = response.status;
501
- }
502
- const error = InsForgeError.fromApiError(data);
503
- Object.keys(data).forEach((key) => {
504
- if (key !== "error" && key !== "message" && key !== "statusCode") {
505
- error[key] = data[key];
506
- }
507
- });
508
- throw error;
509
+ if (err instanceof InsForgeError) {
510
+ this.logger.logResponse(
511
+ method,
512
+ url,
513
+ err.statusCode || response.status,
514
+ Date.now() - startTime,
515
+ err
516
+ );
509
517
  }
510
- throw new InsForgeError(
511
- `Request failed: ${response.statusText}`,
512
- response.status,
513
- "REQUEST_FAILED"
514
- );
518
+ throw err;
515
519
  }
520
+ if (timer !== void 0) clearTimeout(timer);
516
521
  this.logger.logResponse(
517
522
  method,
518
523
  url,
@@ -1878,16 +1883,55 @@ var Functions = class _Functions {
1878
1883
  }
1879
1884
  }
1880
1885
  /**
1881
- * Invokes an Edge Function
1886
+ * Build a Request for in-process dispatch. The host is a non-routable
1887
+ * placeholder; the router only reads pathname.
1888
+ */
1889
+ buildInProcessRequest(slug, method, body, callerHeaders) {
1890
+ const url = new URL("/" + slug, "http://insforge.local").toString();
1891
+ const headers = { ...this.http.getHeaders() };
1892
+ const reqBody = serializeBody(method, body, headers);
1893
+ Object.assign(headers, callerHeaders);
1894
+ return new Request(url, {
1895
+ method,
1896
+ headers,
1897
+ body: reqBody
1898
+ });
1899
+ }
1900
+ /**
1901
+ * Invoke an Edge Function.
1882
1902
  *
1883
- * If functionsUrl is configured, tries direct subhosting first.
1884
- * Falls back to proxy URL if subhosting returns 404.
1903
+ * Dispatch order:
1904
+ * 1. If `globalThis.__insforge_dispatch__` is present, call it in-process.
1905
+ * This avoids Deno Subhosting's 508 Loop Detected when one bundled
1906
+ * function invokes another inside the same deployment.
1907
+ * 2. Otherwise, try the configured subhosting URL.
1908
+ * 3. On 404 from subhosting, fall back to the proxy path.
1885
1909
  *
1886
1910
  * @param slug The function slug to invoke
1887
1911
  * @param options Request options
1888
1912
  */
1889
1913
  async invoke(slug, options = {}) {
1890
1914
  const { method = "POST", body, headers = {} } = options;
1915
+ const dispatch = globalThis.__insforge_dispatch__;
1916
+ const localFunctionsUrl = _Functions.deriveSubhostingUrl(this.http.baseUrl);
1917
+ if (typeof dispatch === "function" && !!localFunctionsUrl && this.functionsUrl === localFunctionsUrl) {
1918
+ try {
1919
+ const req = this.buildInProcessRequest(slug, method, body, headers);
1920
+ const res = await dispatch(req);
1921
+ const data = await parseResponse(res);
1922
+ return { data, error: null };
1923
+ } catch (error) {
1924
+ if (error instanceof Error && error.name === "AbortError") throw error;
1925
+ return {
1926
+ data: null,
1927
+ error: error instanceof InsForgeError ? error : new InsForgeError(
1928
+ error instanceof Error ? error.message : "Function invocation failed",
1929
+ 500,
1930
+ "FUNCTION_ERROR"
1931
+ )
1932
+ };
1933
+ }
1934
+ }
1891
1935
  if (this.functionsUrl) {
1892
1936
  try {
1893
1937
  const data = await this.http.request(method, `${this.functionsUrl}/${slug}`, {
@@ -2218,7 +2262,7 @@ var InsForgeClient = class {
2218
2262
  this.tokenManager.setAccessToken(config.edgeFunctionToken);
2219
2263
  }
2220
2264
  this.auth = new Auth(this.http, this.tokenManager, {
2221
- isServerMode: config.isServerMode ?? !!config.edgeFunctionToken
2265
+ isServerMode: config.isServerMode ?? false
2222
2266
  });
2223
2267
  this.database = new Database(this.http, this.tokenManager);
2224
2268
  this.storage = new Storage(this.http);