@squadbase/vite-server 0.1.12-dev.822582a → 0.1.12-dev.93b8799

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.
Files changed (76) hide show
  1. package/dist/cli/index.js +343 -46
  2. package/dist/connectors/airtable-oauth.js +9 -0
  3. package/dist/connectors/airtable.js +9 -0
  4. package/dist/connectors/amplitude.js +9 -0
  5. package/dist/connectors/anthropic.js +9 -0
  6. package/dist/connectors/asana.js +9 -0
  7. package/dist/connectors/attio.js +9 -0
  8. package/dist/connectors/aws-billing.js +9 -0
  9. package/dist/connectors/azure-sql.js +9 -0
  10. package/dist/connectors/backlog-api-key.js +9 -0
  11. package/dist/connectors/clickup.js +9 -0
  12. package/dist/connectors/cosmosdb.js +9 -0
  13. package/dist/connectors/customerio.js +9 -0
  14. package/dist/connectors/dbt.js +9 -0
  15. package/dist/connectors/freshdesk.js +9 -0
  16. package/dist/connectors/freshsales.js +9 -0
  17. package/dist/connectors/freshservice.js +9 -0
  18. package/dist/connectors/gamma.js +9 -0
  19. package/dist/connectors/gemini.js +9 -0
  20. package/dist/connectors/github.js +9 -0
  21. package/dist/connectors/gmail-oauth.js +9 -0
  22. package/dist/connectors/gmail.js +9 -0
  23. package/dist/connectors/google-ads.js +9 -0
  24. package/dist/connectors/google-analytics-oauth.js +9 -0
  25. package/dist/connectors/google-analytics.js +9 -0
  26. package/dist/connectors/google-audit-log.js +9 -0
  27. package/dist/connectors/google-calendar-oauth.js +9 -0
  28. package/dist/connectors/google-calendar.js +9 -0
  29. package/dist/connectors/google-docs.js +9 -0
  30. package/dist/connectors/google-drive.js +9 -0
  31. package/dist/connectors/google-search-console-oauth.js +9 -0
  32. package/dist/connectors/google-sheets.js +9 -0
  33. package/dist/connectors/google-slides.js +9 -0
  34. package/dist/connectors/grafana.js +9 -0
  35. package/dist/connectors/hubspot-oauth.js +9 -0
  36. package/dist/connectors/hubspot.js +9 -0
  37. package/dist/connectors/influxdb.js +9 -0
  38. package/dist/connectors/intercom-oauth.js +9 -0
  39. package/dist/connectors/intercom.js +9 -0
  40. package/dist/connectors/jdbc.js +9 -0
  41. package/dist/connectors/jira-api-key.js +9 -0
  42. package/dist/connectors/kintone-api-token.js +9 -0
  43. package/dist/connectors/kintone.js +9 -0
  44. package/dist/connectors/linear.js +9 -0
  45. package/dist/connectors/linkedin-ads.js +9 -0
  46. package/dist/connectors/mailchimp-oauth.js +9 -0
  47. package/dist/connectors/mailchimp.js +9 -0
  48. package/dist/connectors/meta-ads-oauth.js +9 -0
  49. package/dist/connectors/meta-ads.js +9 -0
  50. package/dist/connectors/mixpanel.js +9 -0
  51. package/dist/connectors/monday.js +9 -0
  52. package/dist/connectors/mongodb.js +9 -0
  53. package/dist/connectors/notion-oauth.js +9 -0
  54. package/dist/connectors/notion.js +9 -0
  55. package/dist/connectors/openai.js +9 -0
  56. package/dist/connectors/oracle.js +9 -0
  57. package/dist/connectors/outlook-oauth.js +9 -0
  58. package/dist/connectors/powerbi-oauth.js +9 -0
  59. package/dist/connectors/salesforce.js +9 -0
  60. package/dist/connectors/semrush.js +9 -0
  61. package/dist/connectors/sentry.js +9 -0
  62. package/dist/connectors/shopify-oauth.js +9 -0
  63. package/dist/connectors/shopify.js +9 -0
  64. package/dist/connectors/sqlserver.js +9 -0
  65. package/dist/connectors/stripe-api-key.js +9 -0
  66. package/dist/connectors/stripe-oauth.js +9 -0
  67. package/dist/connectors/supabase.js +9 -0
  68. package/dist/connectors/tableau.js +129 -55
  69. package/dist/connectors/tiktok-ads.js +9 -0
  70. package/dist/connectors/wix-store.js +9 -0
  71. package/dist/connectors/zendesk-oauth.js +9 -0
  72. package/dist/connectors/zendesk.js +9 -0
  73. package/dist/index.js +343 -46
  74. package/dist/main.js +343 -46
  75. package/dist/vite-plugin.js +343 -46
  76. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -117,6 +117,14 @@ var ConnectorPlugin = class _ConnectorPlugin {
117
117
  tools;
118
118
  query;
119
119
  checkConnection;
120
+ /**
121
+ * SQPD-1212: Logic-based, rule-driven connection setup. Connectors that
122
+ * implement this expose a step-by-step exploration flow (database/schema/
123
+ * table/etc. discovery) that the dashboard backend drives via the
124
+ * `/connections/:connectionId/setup` endpoint. Implement by delegating to
125
+ * `runSetupFlow` from `setup-flow.ts`.
126
+ */
127
+ setup;
120
128
  constructor(config) {
121
129
  this.slug = config.slug;
122
130
  this.authType = config.authType;
@@ -133,6 +141,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
133
141
  this.tools = config.tools;
134
142
  this.query = config.query;
135
143
  this.checkConnection = config.checkConnection;
144
+ this.setup = config.setup;
136
145
  }
137
146
  get connectorKey() {
138
147
  return _ConnectorPlugin.deriveKey(this.slug, this.authType);
@@ -197,6 +206,50 @@ var ConnectorPlugin = class _ConnectorPlugin {
197
206
  }
198
207
  };
199
208
 
209
+ // ../connectors/src/setup-flow.ts
210
+ async function runSetupFlow(flow, params, ctx, config) {
211
+ const runtime = {
212
+ params,
213
+ language: ctx.language,
214
+ config
215
+ };
216
+ let state = flow.initialState();
217
+ let answerIdx = 0;
218
+ for (const step of flow.steps) {
219
+ const ans = ctx.answers[answerIdx];
220
+ if (ans && ans.questionSlug === step.slug) {
221
+ state = step.applyAnswer(state, ans.answer);
222
+ answerIdx += 1;
223
+ continue;
224
+ }
225
+ if (step.type === "text") {
226
+ return {
227
+ type: "nextQuestion",
228
+ questionSlug: step.slug,
229
+ question: step.question[ctx.language],
230
+ questionType: "text"
231
+ };
232
+ }
233
+ const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
234
+ if (options.length === 0) {
235
+ continue;
236
+ }
237
+ if (step.autoSelectIfSingle && options.length === 1) {
238
+ state = step.applyAnswer(state, [options[0].value]);
239
+ continue;
240
+ }
241
+ return {
242
+ type: "nextQuestion",
243
+ questionSlug: step.slug,
244
+ question: step.question[ctx.language],
245
+ questionType: "multiSelect",
246
+ options
247
+ };
248
+ }
249
+ const dataInvestigationResult = await flow.finalize(state, runtime);
250
+ return { type: "fulfilled", dataInvestigationResult };
251
+ }
252
+
200
253
  // ../connectors/src/auth-types.ts
201
254
  var AUTH_TYPES = {
202
255
  OAUTH: "oauth",
@@ -358,6 +411,125 @@ var parameters = {
358
411
  })
359
412
  };
360
413
 
414
+ // ../connectors/src/connectors/snowflake/setup-flow.ts
415
+ var ALL_TABLES = "__ALL_TABLES__";
416
+ var INTERNAL_SCHEMAS = /* @__PURE__ */ new Set([
417
+ "INFORMATION_SCHEMA",
418
+ "ACCOUNT_USAGE",
419
+ "READER_ACCOUNT_USAGE",
420
+ "DATA_SHARING_USAGE"
421
+ ]);
422
+ function createSnowflakeSetupFlow(runQuery3) {
423
+ return {
424
+ initialState: () => ({}),
425
+ steps: [
426
+ {
427
+ slug: "database",
428
+ type: "multiSelect",
429
+ autoSelectIfSingle: true,
430
+ question: {
431
+ ja: "\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u306B\u4F7F\u3046\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF081 \u4EF6\u306E\u307F\uFF09",
432
+ en: "Select the database to use for setup (pick one)"
433
+ },
434
+ async fetchOptions(_state, rt) {
435
+ const rows = await runQuery3(rt.params, "SHOW DATABASES");
436
+ return rows.map((r) => String(r["name"] ?? "")).filter((name) => name && !name.endsWith("_SHARE")).map((value) => ({ value }));
437
+ },
438
+ applyAnswer: (state, answer) => ({ ...state, database: answer[0] })
439
+ },
440
+ {
441
+ slug: "schema",
442
+ type: "multiSelect",
443
+ autoSelectIfSingle: true,
444
+ question: {
445
+ ja: "\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u306B\u4F7F\u3046\u30B9\u30AD\u30FC\u30DE\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF081 \u4EF6\u306E\u307F\uFF09",
446
+ en: "Select the schema to use for setup (pick one)"
447
+ },
448
+ async fetchOptions(state, rt) {
449
+ if (!state.database) return [];
450
+ const rows = await runQuery3(
451
+ rt.params,
452
+ `SHOW SCHEMAS IN DATABASE "${state.database}"`
453
+ );
454
+ return rows.map((r) => String(r["name"] ?? "")).filter(
455
+ (name) => name && !INTERNAL_SCHEMAS.has(name.toUpperCase())
456
+ ).map((value) => ({ value }));
457
+ },
458
+ applyAnswer: (state, answer) => ({ ...state, schema: answer[0] })
459
+ },
460
+ {
461
+ slug: "tables",
462
+ type: "multiSelect",
463
+ question: {
464
+ ja: "\u5BFE\u8C61\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
465
+ en: "Select target tables (multi-select allowed)"
466
+ },
467
+ async fetchOptions(state, rt) {
468
+ if (!state.database || !state.schema) return [];
469
+ const rows = await runQuery3(
470
+ rt.params,
471
+ `SHOW TABLES IN SCHEMA "${state.database}"."${state.schema}"`
472
+ );
473
+ const tableOptions = rows.map((r) => String(r["name"] ?? "")).filter((name) => name).map((value) => ({ value }));
474
+ return [
475
+ {
476
+ value: ALL_TABLES,
477
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30C6\u30FC\u30D6\u30EB" : "All tables"
478
+ },
479
+ ...tableOptions
480
+ ];
481
+ },
482
+ applyAnswer: (state, answer) => ({ ...state, tables: answer })
483
+ }
484
+ ],
485
+ async finalize(state, rt) {
486
+ if (!state.database || !state.schema || !state.tables) {
487
+ throw new Error("Snowflake setup: incomplete state on finalize");
488
+ }
489
+ const db = state.database;
490
+ const schema = state.schema;
491
+ let targetTables;
492
+ if (state.tables.includes(ALL_TABLES)) {
493
+ const rows = await runQuery3(
494
+ rt.params,
495
+ `SHOW TABLES IN SCHEMA "${db}"."${schema}"`
496
+ );
497
+ targetTables = rows.map((r) => String(r["name"] ?? "")).filter((name) => name);
498
+ } else {
499
+ targetTables = state.tables.filter((t) => t !== ALL_TABLES);
500
+ }
501
+ const sections = [
502
+ "## Snowflake",
503
+ "",
504
+ `### Database: ${db}`,
505
+ "",
506
+ `#### Schema: ${schema}`,
507
+ ""
508
+ ];
509
+ for (const table of targetTables) {
510
+ const cols = await runQuery3(
511
+ rt.params,
512
+ `DESCRIBE TABLE "${db}"."${schema}"."${table}"`
513
+ );
514
+ sections.push(`##### Table: ${table}`, "");
515
+ sections.push("| Column | Type | Nullable | Default |");
516
+ sections.push("|--------|------|----------|---------|");
517
+ for (const c of cols) {
518
+ const name = String(c["name"] ?? "");
519
+ const type = String(c["type"] ?? "");
520
+ const nullable = String(c["null?"] ?? "");
521
+ const defaultValue = c["default"] == null ? "-" : String(c["default"]);
522
+ sections.push(
523
+ `| ${name} | ${type} | ${nullable} | ${defaultValue} |`
524
+ );
525
+ }
526
+ sections.push("");
527
+ }
528
+ return sections.join("\n");
529
+ }
530
+ };
531
+ }
532
+
361
533
  // ../connectors/src/connectors/snowflake/tools/execute-query.ts
362
534
  import { z } from "zod";
363
535
 
@@ -402,6 +574,49 @@ function decryptPrivateKey(pem, passphrase) {
402
574
  passphrase
403
575
  }).export({ type: "pkcs8", format: "pem" });
404
576
  }
577
+ async function runQuery(params, sql) {
578
+ const snowflake = (await import("snowflake-sdk")).default;
579
+ snowflake.configure({ logLevel: "ERROR" });
580
+ const privateKeyPem = Buffer.from(
581
+ params[parameters.privateKeyBase64.slug],
582
+ "base64"
583
+ ).toString("utf-8");
584
+ const privateKeyPass = params[parameters.privateKeyPassphrase.slug] || void 0;
585
+ const privateKey = decryptPrivateKey(privateKeyPem, privateKeyPass);
586
+ const conn = snowflake.createConnection({
587
+ account: params[parameters.account.slug],
588
+ username: params[parameters.user.slug],
589
+ role: params[parameters.role.slug],
590
+ warehouse: params[parameters.warehouse.slug],
591
+ authenticator: "SNOWFLAKE_JWT",
592
+ privateKey
593
+ });
594
+ await new Promise((resolve, reject) => {
595
+ conn.connect((err) => {
596
+ if (err) reject(new Error(`Snowflake connect failed: ${err.message}`));
597
+ else resolve();
598
+ });
599
+ });
600
+ try {
601
+ return await new Promise((resolve, reject) => {
602
+ conn.execute({
603
+ sqlText: sql,
604
+ complete: (err, _stmt, rows) => {
605
+ if (err) reject(new Error(`Snowflake query failed: ${err.message}`));
606
+ else resolve(rows ?? []);
607
+ }
608
+ });
609
+ });
610
+ } finally {
611
+ conn.destroy((err) => {
612
+ if (err) {
613
+ console.warn(
614
+ `[connector-query] Snowflake destroy error: ${err.message}`
615
+ );
616
+ }
617
+ });
618
+ }
619
+ }
405
620
  async function executeWithSizeLimit(conn, sql, options) {
406
621
  const rows = [];
407
622
  let acc = 0;
@@ -556,6 +771,7 @@ Memory guidance:
556
771
  });
557
772
 
558
773
  // ../connectors/src/connectors/snowflake/index.ts
774
+ var snowflakeSetupFlow = createSnowflakeSetupFlow(runQuery);
559
775
  var tools = { executeQuery: executeQueryTool };
560
776
  var snowflakeConnector = new ConnectorPlugin({
561
777
  slug: "snowflake",
@@ -602,6 +818,7 @@ The business logic type for this connector is "sql".
602
818
  - INFORMATION_SCHEMA \u3082\u5229\u7528\u53EF\u80FD: \`SELECT * FROM db_name.INFORMATION_SCHEMA.TABLES\``
603
819
  },
604
820
  tools,
821
+ setup: (params, ctx, config) => runSetupFlow(snowflakeSetupFlow, params, ctx, config),
605
822
  async checkConnection(params, _config) {
606
823
  try {
607
824
  const snowflake = (await import("snowflake-sdk")).default;
@@ -734,6 +951,47 @@ var parameters2 = {
734
951
  })
735
952
  };
736
953
 
954
+ // ../connectors/src/connectors/snowflake-pat/utils.ts
955
+ async function runQuery2(params, sql) {
956
+ const snowflake = (await import("snowflake-sdk")).default;
957
+ snowflake.configure({ logLevel: "ERROR" });
958
+ const conn = snowflake.createConnection({
959
+ account: params[parameters2.account.slug],
960
+ username: params[parameters2.user.slug],
961
+ role: params[parameters2.role.slug],
962
+ warehouse: params[parameters2.warehouse.slug],
963
+ password: params[parameters2.pat.slug]
964
+ });
965
+ await new Promise((resolve, reject) => {
966
+ conn.connect((err) => {
967
+ if (err) reject(new Error(`Snowflake connect failed: ${err.message}`));
968
+ else resolve();
969
+ });
970
+ });
971
+ try {
972
+ return await new Promise((resolve, reject) => {
973
+ conn.execute({
974
+ sqlText: sql,
975
+ complete: (err, _stmt, rows) => {
976
+ if (err) reject(new Error(`Snowflake query failed: ${err.message}`));
977
+ else resolve(rows ?? []);
978
+ }
979
+ });
980
+ });
981
+ } finally {
982
+ conn.destroy((err) => {
983
+ if (err) {
984
+ console.warn(
985
+ `[connector-query] Snowflake destroy error: ${err.message}`
986
+ );
987
+ }
988
+ });
989
+ }
990
+ }
991
+
992
+ // ../connectors/src/connectors/snowflake-pat/setup-flow.ts
993
+ var snowflakePatSetupFlow = createSnowflakeSetupFlow(runQuery2);
994
+
737
995
  // ../connectors/src/connectors/snowflake-pat/tools/execute-query.ts
738
996
  import { z as z2 } from "zod";
739
997
  var MAX_ROWS = 500;
@@ -886,6 +1144,7 @@ The business logic type for this connector is "sql".
886
1144
  - INFORMATION_SCHEMA \u3082\u5229\u7528\u53EF\u80FD: \`SELECT * FROM db_name.INFORMATION_SCHEMA.TABLES\``
887
1145
  },
888
1146
  tools: tools2,
1147
+ setup: (params, ctx, config) => runSetupFlow(snowflakePatSetupFlow, params, ctx, config),
889
1148
  async checkConnection(params, _config) {
890
1149
  try {
891
1150
  const snowflake = (await import("snowflake-sdk")).default;
@@ -27961,6 +28220,10 @@ import { z as z98 } from "zod";
27961
28220
  var DEFAULT_API_VERSION = "3.28";
27962
28221
  var REQUEST_TIMEOUT_MS75 = 6e4;
27963
28222
  var sessionCache = /* @__PURE__ */ new Map();
28223
+ var inFlightSignIns = /* @__PURE__ */ new Map();
28224
+ function sessionCacheKey(serverUrl, siteContentUrl, patName) {
28225
+ return `${serverUrl}|${siteContentUrl}|${patName}`;
28226
+ }
27964
28227
  function buildBaseUrl4(serverUrl, apiVersion) {
27965
28228
  return `${serverUrl.replace(/\/$/, "")}/api/${apiVersion}`;
27966
28229
  }
@@ -27995,21 +28258,39 @@ async function signIn(serverUrl, apiVersion, siteContentUrl, patName, patSecret)
27995
28258
  expiresAt: Date.now() + 30 * 60 * 1e3
27996
28259
  };
27997
28260
  }
27998
- async function getSession(serverUrl, apiVersion, siteContentUrl, patName, patSecret) {
27999
- const cacheKey = `${serverUrl}|${siteContentUrl}|${patName}`;
28000
- const cached = sessionCache.get(cacheKey);
28001
- if (cached && cached.expiresAt > Date.now() + 6e4) {
28002
- return cached;
28261
+ async function getSession(serverUrl, apiVersion, siteContentUrl, patName, patSecret, { forceRefresh = false } = {}) {
28262
+ const cacheKey = sessionCacheKey(serverUrl, siteContentUrl, patName);
28263
+ if (forceRefresh) {
28264
+ sessionCache.delete(cacheKey);
28265
+ } else {
28266
+ const cached = sessionCache.get(cacheKey);
28267
+ if (cached && cached.expiresAt > Date.now() + 6e4) {
28268
+ return cached;
28269
+ }
28003
28270
  }
28004
- const session = await signIn(
28271
+ const existing = inFlightSignIns.get(cacheKey);
28272
+ if (existing) return existing;
28273
+ const promise = signIn(
28005
28274
  serverUrl,
28006
28275
  apiVersion,
28007
28276
  siteContentUrl,
28008
28277
  patName,
28009
28278
  patSecret
28010
- );
28011
- sessionCache.set(cacheKey, session);
28012
- return session;
28279
+ ).then((session) => {
28280
+ sessionCache.set(cacheKey, session);
28281
+ return session;
28282
+ }).finally(() => {
28283
+ inFlightSignIns.delete(cacheKey);
28284
+ });
28285
+ inFlightSignIns.set(cacheKey, promise);
28286
+ return promise;
28287
+ }
28288
+ function invalidateSession(serverUrl, siteContentUrl, patName, staleAuthToken) {
28289
+ const cacheKey = sessionCacheKey(serverUrl, siteContentUrl, patName);
28290
+ const current = sessionCache.get(cacheKey);
28291
+ if (current && current.authToken === staleAuthToken) {
28292
+ sessionCache.delete(cacheKey);
28293
+ }
28013
28294
  }
28014
28295
  var inputSchema98 = z98.object({
28015
28296
  toolUseIntent: z98.string().optional().describe(
@@ -28065,48 +28346,64 @@ Accept and Content-Type headers default to application/json so list responses co
28065
28346
  const patName = parameters81.patName.getValue(connection2);
28066
28347
  const patSecret = parameters81.patSecret.getValue(connection2);
28067
28348
  const apiVersion = parameters81.apiVersion.tryGetValue(connection2) || DEFAULT_API_VERSION;
28068
- const session = await getSession(
28069
- serverUrl,
28070
- apiVersion,
28071
- siteContentUrl,
28072
- patName,
28073
- patSecret
28074
- );
28075
- const resolvedPath = path6.trim().replace(/\{siteId\}/g, session.siteId).replace(/^([^/])/, "/$1");
28076
- let url = `${buildBaseUrl4(serverUrl, apiVersion)}${resolvedPath}`;
28077
- if (queryParams) {
28078
- const searchParams = new URLSearchParams(queryParams);
28079
- url += `?${searchParams.toString()}`;
28080
- }
28349
+ const trimmedPath = path6.trim().replace(/^([^/])/, "/$1");
28350
+ const queryString = queryParams ? `?${new URLSearchParams(queryParams).toString()}` : "";
28351
+ const baseUrl = buildBaseUrl4(serverUrl, apiVersion);
28081
28352
  const controller = new AbortController();
28082
28353
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS75);
28083
28354
  try {
28084
- const init = {
28085
- method,
28086
- headers: {
28087
- "X-Tableau-Auth": session.authToken,
28088
- Accept: "application/json",
28089
- "Content-Type": "application/json"
28090
- },
28091
- signal: controller.signal
28092
- };
28093
- if (body !== void 0) {
28094
- init.body = JSON.stringify(body);
28095
- }
28096
- const response = await fetch(url, init);
28097
- const text = await response.text();
28098
- const data = text ? (() => {
28099
- try {
28100
- return JSON.parse(text);
28101
- } catch {
28102
- return text;
28355
+ let attempt = 0;
28356
+ while (true) {
28357
+ const session = await getSession(
28358
+ serverUrl,
28359
+ apiVersion,
28360
+ siteContentUrl,
28361
+ patName,
28362
+ patSecret,
28363
+ { forceRefresh: attempt > 0 }
28364
+ );
28365
+ const resolvedPath = trimmedPath.replace(
28366
+ /\{siteId\}/g,
28367
+ session.siteId
28368
+ );
28369
+ const url = `${baseUrl}${resolvedPath}${queryString}`;
28370
+ const init = {
28371
+ method,
28372
+ headers: {
28373
+ "X-Tableau-Auth": session.authToken,
28374
+ Accept: "application/json",
28375
+ "Content-Type": "application/json"
28376
+ },
28377
+ signal: controller.signal
28378
+ };
28379
+ if (body !== void 0) {
28380
+ init.body = JSON.stringify(body);
28103
28381
  }
28104
- })() : null;
28105
- if (!response.ok) {
28106
- const errorMessage = data && typeof data === "object" && "error" in data ? JSON.stringify(data.error) : typeof data === "string" && data ? data : `HTTP ${response.status} ${response.statusText}`;
28107
- return { success: false, error: errorMessage };
28382
+ const response = await fetch(url, init);
28383
+ const text = await response.text();
28384
+ const data = text ? (() => {
28385
+ try {
28386
+ return JSON.parse(text);
28387
+ } catch {
28388
+ return text;
28389
+ }
28390
+ })() : null;
28391
+ if (response.status === 401 && attempt === 0) {
28392
+ invalidateSession(
28393
+ serverUrl,
28394
+ siteContentUrl,
28395
+ patName,
28396
+ session.authToken
28397
+ );
28398
+ attempt++;
28399
+ continue;
28400
+ }
28401
+ if (!response.ok) {
28402
+ const errorMessage = data && typeof data === "object" && "error" in data ? JSON.stringify(data.error) : typeof data === "string" && data ? data : `HTTP ${response.status} ${response.statusText}`;
28403
+ return { success: false, error: errorMessage };
28404
+ }
28405
+ return { success: true, status: response.status, data };
28108
28406
  }
28109
- return { success: true, status: response.status, data };
28110
28407
  } finally {
28111
28408
  clearTimeout(timeout);
28112
28409
  }