@xylex-group/athena 2.1.2 → 2.4.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.
Files changed (42) hide show
  1. package/README.md +306 -117
  2. package/dist/browser.cjs +2151 -194
  3. package/dist/browser.cjs.map +1 -1
  4. package/dist/browser.d.cts +14 -14
  5. package/dist/browser.d.ts +14 -14
  6. package/dist/browser.js +2146 -194
  7. package/dist/browser.js.map +1 -1
  8. package/dist/cli/index.cjs +2035 -172
  9. package/dist/cli/index.cjs.map +1 -1
  10. package/dist/cli/index.d.cts +3 -3
  11. package/dist/cli/index.d.ts +3 -3
  12. package/dist/cli/index.js +2036 -173
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/index.cjs +2166 -190
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +7 -7
  17. package/dist/index.d.ts +7 -7
  18. package/dist/index.js +2162 -191
  19. package/dist/index.js.map +1 -1
  20. package/dist/{model-form-2hqmoOUX.d.ts → model-form-C0FAbOaf.d.ts} +97 -2
  21. package/dist/{model-form-Cy-zaO0u.d.cts → model-form-GzTqhEzM.d.cts} +97 -2
  22. package/dist/{pipeline-BOPszLsL.d.ts → pipeline-CR4V15jF.d.ts} +1 -1
  23. package/dist/{pipeline-E3FDbs4W.d.cts → pipeline-DZeExYMA.d.cts} +1 -1
  24. package/dist/{client-BX0NQqOn.d.ts → react-email-BuApZuyG.d.ts} +362 -174
  25. package/dist/{client-dpAp-NZK.d.cts → react-email-CQJq92zQ.d.cts} +362 -174
  26. package/dist/react.cjs +279 -21
  27. package/dist/react.cjs.map +1 -1
  28. package/dist/react.d.cts +4 -4
  29. package/dist/react.d.ts +4 -4
  30. package/dist/react.js +279 -21
  31. package/dist/react.js.map +1 -1
  32. package/dist/{types-BaBzjwXr.d.cts → types-09Q4D86N.d.cts} +24 -2
  33. package/dist/{types-BaBzjwXr.d.ts → types-09Q4D86N.d.ts} +24 -2
  34. package/dist/{types-CpqL-pZx.d.cts → types-D1JvL21V.d.cts} +1 -1
  35. package/dist/{types-CeBPrnGj.d.ts → types-DU3gNdFv.d.ts} +1 -1
  36. package/dist/utils.cjs +153 -0
  37. package/dist/utils.cjs.map +1 -0
  38. package/dist/utils.d.cts +23 -0
  39. package/dist/utils.d.ts +23 -0
  40. package/dist/utils.js +146 -0
  41. package/dist/utils.js.map +1 -0
  42. package/package.json +74 -16
package/dist/cli/index.js CHANGED
@@ -1,10 +1,15 @@
1
- import { mkdir, writeFile } from 'fs/promises';
1
+ import { mkdir, writeFile, stat } from 'fs/promises';
2
2
  import { resolve, dirname, posix } from 'path';
3
3
  import { existsSync, readFileSync } from 'fs';
4
4
  import { pathToFileURL } from 'url';
5
5
 
6
6
  // src/generator/pipeline.ts
7
7
 
8
+ // src/utils/slugify.ts
9
+ function slugify(input) {
10
+ return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
11
+ }
12
+
8
13
  // src/generator/naming.ts
9
14
  var IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
10
15
  var RESERVED_IDENTIFIERS = /* @__PURE__ */ new Set([
@@ -100,7 +105,7 @@ function applyNamingStyle(input, style) {
100
105
  case "snake":
101
106
  return words.map((word) => word.toLowerCase()).join("_");
102
107
  case "kebab":
103
- return words.map((word) => word.toLowerCase()).join("-");
108
+ return slugify(words.join("-"));
104
109
  default:
105
110
  return input;
106
111
  }
@@ -353,6 +358,19 @@ function isAthenaGatewayError(error) {
353
358
  return error instanceof AthenaGatewayError;
354
359
  }
355
360
 
361
+ // src/utils/parse-boolean-flag.ts
362
+ function parseBooleanFlag(rawValue, fallback) {
363
+ if (!rawValue) return fallback;
364
+ const normalized = rawValue.trim().toLowerCase();
365
+ if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
366
+ return true;
367
+ }
368
+ if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
369
+ return false;
370
+ }
371
+ return fallback;
372
+ }
373
+
356
374
  // src/auxiliaries.ts
357
375
  var AthenaErrorCode = {
358
376
  UniqueViolation: "UNIQUE_VIOLATION",
@@ -373,20 +391,41 @@ var AthenaErrorCategory = {
373
391
  Database: "database",
374
392
  Unknown: "unknown"
375
393
  };
376
- function parseBooleanFlag(rawValue, fallback) {
377
- if (!rawValue) return fallback;
378
- const normalized = rawValue.trim().toLowerCase();
379
- if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
380
- return true;
381
- }
382
- if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
383
- return false;
384
- }
385
- return fallback;
394
+ function parseBooleanFlag2(rawValue, fallback) {
395
+ return parseBooleanFlag(rawValue, fallback);
386
396
  }
387
397
  function isRecord(value) {
388
398
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
389
399
  }
400
+ function firstNonEmptyString(...values) {
401
+ for (const value of values) {
402
+ if (typeof value === "string" && value.trim().length > 0) {
403
+ return value.trim();
404
+ }
405
+ }
406
+ return void 0;
407
+ }
408
+ function messageFromUnknownError(error) {
409
+ if (typeof error === "string" && error.trim().length > 0) {
410
+ return error.trim();
411
+ }
412
+ if (error instanceof Error && error.message.trim().length > 0) {
413
+ return error.message.trim();
414
+ }
415
+ if (!isRecord(error)) {
416
+ return void 0;
417
+ }
418
+ return firstNonEmptyString(error.message, error.error, error.details);
419
+ }
420
+ function gatewayCodeFromUnknownError(error) {
421
+ if (!isRecord(error) || typeof error.gatewayCode !== "string") {
422
+ return void 0;
423
+ }
424
+ return error.gatewayCode;
425
+ }
426
+ function isAthenaResultErrorLike(value) {
427
+ return isRecord(value) && typeof value.message === "string" && (value.athenaCode === void 0 || isAthenaErrorCode(value.athenaCode)) && (value.kind === void 0 || isAthenaErrorKind(value.kind)) && (value.category === void 0 || isAthenaErrorCategory(value.category)) && (value.retryable === void 0 || typeof value.retryable === "boolean") && (value.status === void 0 || typeof value.status === "number");
428
+ }
390
429
  function isAthenaErrorKind(value) {
391
430
  return value === "unique_violation" || value === "not_found" || value === "validation" || value === "auth" || value === "rate_limit" || value === "transient" || value === "unknown";
392
431
  }
@@ -458,6 +497,7 @@ function classifyKind(status, code, message) {
458
497
  if (status === 404 || hasNotFoundPattern) return "not_found";
459
498
  if (status === 401 || status === 403 || hasAuthPattern) return "auth";
460
499
  if (status === 429 || hasRateLimitPattern) return "rate_limit";
500
+ if (code === "INVALID_URL") return "validation";
461
501
  if (status === 400 || status === 422 || hasValidationPattern) return "validation";
462
502
  if (code === "NETWORK_ERROR" || status === 0 || status !== void 0 && status >= 500 || hasTransientPattern) {
463
503
  return "transient";
@@ -465,6 +505,9 @@ function classifyKind(status, code, message) {
465
505
  return "unknown";
466
506
  }
467
507
  function toAthenaErrorCode(kind, status, gatewayCode) {
508
+ if (gatewayCode === "INVALID_URL") {
509
+ return AthenaErrorCode.ValidationFailed;
510
+ }
468
511
  if (gatewayCode === "NETWORK_ERROR" || kind === "transient" && status === 0) {
469
512
  return AthenaErrorCode.NetworkUnavailable;
470
513
  }
@@ -508,13 +551,50 @@ function normalizeAthenaError(resultOrError, context) {
508
551
  return withContextOverrides(attached, context);
509
552
  }
510
553
  if (isAthenaResultLike(resultOrError)) {
554
+ if (isAthenaResultErrorLike(resultOrError.error)) {
555
+ return {
556
+ kind: resultOrError.error.kind ?? classifyKind(resultOrError.status, gatewayCodeFromUnknownError(resultOrError.error), resultOrError.error.message),
557
+ code: resultOrError.error.athenaCode ?? toAthenaErrorCode(
558
+ resultOrError.error.kind ?? classifyKind(
559
+ resultOrError.status,
560
+ gatewayCodeFromUnknownError(resultOrError.error),
561
+ resultOrError.error.message
562
+ ),
563
+ resultOrError.error.status ?? resultOrError.status,
564
+ gatewayCodeFromUnknownError(resultOrError.error)
565
+ ),
566
+ category: resultOrError.error.category ?? toAthenaErrorCategory(
567
+ resultOrError.error.kind ?? classifyKind(
568
+ resultOrError.status,
569
+ gatewayCodeFromUnknownError(resultOrError.error),
570
+ resultOrError.error.message
571
+ ),
572
+ resultOrError.error.status ?? resultOrError.status
573
+ ),
574
+ retryable: resultOrError.error.retryable ?? isRetryable(
575
+ resultOrError.error.kind ?? classifyKind(
576
+ resultOrError.status,
577
+ gatewayCodeFromUnknownError(resultOrError.error),
578
+ resultOrError.error.message
579
+ ),
580
+ resultOrError.error.status ?? resultOrError.status
581
+ ),
582
+ status: resultOrError.error.status ?? resultOrError.status,
583
+ constraint: resultOrError.error.constraint,
584
+ table: context?.table ?? resultOrError.error.table,
585
+ operation: context?.operation ?? resultOrError.error.operation,
586
+ message: resultOrError.error.message,
587
+ raw: resultOrError.error.raw ?? resultOrError.raw
588
+ };
589
+ }
511
590
  const details = resultOrError.errorDetails;
512
- const message2 = resultOrError.error ?? details?.message ?? `Athena ${context?.operation ?? operationFromDetails(details) ?? "request"} failed`;
591
+ const message2 = messageFromUnknownError(resultOrError.error) ?? details?.message ?? `Athena ${context?.operation ?? operationFromDetails(details) ?? "request"} failed`;
513
592
  const operation = context?.operation ?? operationFromDetails(details);
514
593
  const table = context?.table ?? extractTable(message2);
515
594
  const constraint = extractConstraint(message2);
516
- const kind2 = classifyKind(resultOrError.status, details?.code, message2);
517
- const code = toAthenaErrorCode(kind2, resultOrError.status, details?.code);
595
+ const gatewayCode = details?.code ?? gatewayCodeFromUnknownError(resultOrError.error);
596
+ const kind2 = classifyKind(resultOrError.status, gatewayCode, message2);
597
+ const code = toAthenaErrorCode(kind2, resultOrError.status, gatewayCode);
518
598
  const category = toAthenaErrorCategory(kind2, resultOrError.status);
519
599
  return {
520
600
  kind: kind2,
@@ -792,7 +872,7 @@ function normalizeBooleanFlag(rawValue, fallback) {
792
872
  return rawValue;
793
873
  }
794
874
  if (typeof rawValue === "string") {
795
- return parseBooleanFlag(rawValue, fallback);
875
+ return parseBooleanFlag2(rawValue, fallback);
796
876
  }
797
877
  return fallback;
798
878
  }
@@ -1281,8 +1361,74 @@ function generateArtifactsFromSnapshot(snapshot, config) {
1281
1361
  return new ArtifactComposer(snapshot, normalizedConfig).compose();
1282
1362
  }
1283
1363
 
1364
+ // src/gateway/url.ts
1365
+ var ATHENA_DEFAULT_BASE_URL = "https://athena-db.com";
1366
+ function describeReceivedValue(value) {
1367
+ if (value === void 0) return "undefined";
1368
+ if (value === null) return "null";
1369
+ if (typeof value === "string") {
1370
+ return value.trim().length > 0 ? JSON.stringify(value) : "an empty string";
1371
+ }
1372
+ return `${typeof value} ${JSON.stringify(value)}`;
1373
+ }
1374
+ function invalidBaseUrlError(message, hint) {
1375
+ return new AthenaGatewayError({
1376
+ code: "INVALID_URL",
1377
+ message,
1378
+ status: 0,
1379
+ hint
1380
+ });
1381
+ }
1382
+ function normalizeAthenaGatewayBaseUrl(input, options = {}) {
1383
+ const label = options.label ?? "Athena gateway base URL";
1384
+ const candidate = input ?? options.defaultBaseUrl;
1385
+ if (candidate === void 0 || candidate === null) {
1386
+ throw invalidBaseUrlError(
1387
+ `${label} must be a non-empty absolute http(s) URL. Received ${describeReceivedValue(input)}.`,
1388
+ 'Set ATHENA_URL (or pass createClient(url, ...)) to a full URL such as "https://mirror3.athena-db.com".'
1389
+ );
1390
+ }
1391
+ const trimmed = candidate.trim();
1392
+ if (!trimmed) {
1393
+ throw invalidBaseUrlError(
1394
+ `${label} must be a non-empty absolute http(s) URL. Received ${describeReceivedValue(candidate)}.`,
1395
+ 'Set ATHENA_URL (or pass createClient(url, ...)) to a full URL such as "https://mirror3.athena-db.com".'
1396
+ );
1397
+ }
1398
+ let parsed;
1399
+ try {
1400
+ parsed = new URL(trimmed);
1401
+ } catch {
1402
+ throw invalidBaseUrlError(
1403
+ `${label} must be a valid absolute http(s) URL. Received ${describeReceivedValue(candidate)}.`,
1404
+ 'Use a full URL including the protocol, for example "https://mirror3.athena-db.com".'
1405
+ );
1406
+ }
1407
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
1408
+ throw invalidBaseUrlError(
1409
+ `${label} must use http or https. Received ${JSON.stringify(trimmed)}.`,
1410
+ 'Use an Athena gateway URL such as "https://mirror3.athena-db.com".'
1411
+ );
1412
+ }
1413
+ if (parsed.search || parsed.hash) {
1414
+ throw invalidBaseUrlError(
1415
+ `${label} must not include query parameters or hash fragments. Received ${JSON.stringify(trimmed)}.`,
1416
+ 'Pass only the base URL. Endpoint paths such as "/gateway/fetch" are appended by the SDK.'
1417
+ );
1418
+ }
1419
+ return parsed.toString().replace(/\/+$/, "");
1420
+ }
1421
+ function buildAthenaGatewayUrl(baseUrl, path) {
1422
+ if (!path.startsWith("/")) {
1423
+ throw invalidBaseUrlError(
1424
+ `Athena gateway path must start with "/". Received ${JSON.stringify(path)}.`,
1425
+ 'Use a leading slash such as "/gateway/fetch" or "/".'
1426
+ );
1427
+ }
1428
+ return `${baseUrl}${path}`;
1429
+ }
1430
+
1284
1431
  // src/gateway/client.ts
1285
- var DEFAULT_BASE_URL = "https://athena-db.com";
1286
1432
  var DEFAULT_CLIENT = "railway_direct";
1287
1433
  var FALLBACK_SDK_VERSION = "1.3.0";
1288
1434
  var SDK_NAME = "xylex-group/athena";
@@ -1309,23 +1455,39 @@ function normalizeHeaderValue(value) {
1309
1455
  function isRecord2(value) {
1310
1456
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1311
1457
  }
1458
+ function nonEmptyString(value) {
1459
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
1460
+ }
1461
+ function resolveStructuredErrorPayload(payload) {
1462
+ if (!isRecord2(payload)) return null;
1463
+ return isRecord2(payload.error) ? payload.error : payload;
1464
+ }
1312
1465
  function resolveRequestId(headers) {
1313
1466
  return headers.get("x-request-id") ?? headers.get("x-correlation-id") ?? headers.get("x-athena-request-id") ?? void 0;
1314
1467
  }
1315
1468
  function resolveErrorMessage(payload, fallback) {
1316
- if (isRecord2(payload)) {
1317
- const messageCandidates = [payload.error, payload.message, payload.details];
1469
+ const structuredPayload = resolveStructuredErrorPayload(payload);
1470
+ if (structuredPayload) {
1471
+ const messageCandidates = [structuredPayload.message, structuredPayload.error, structuredPayload.details];
1318
1472
  for (const candidate of messageCandidates) {
1319
- if (typeof candidate === "string" && candidate.trim().length > 0) {
1320
- return candidate.trim();
1321
- }
1473
+ const resolved = nonEmptyString(candidate);
1474
+ if (resolved) return resolved;
1322
1475
  }
1323
1476
  }
1324
- if (typeof payload === "string" && payload.trim().length > 0) {
1325
- return payload.trim();
1326
- }
1477
+ const rawMessage = nonEmptyString(payload);
1478
+ if (rawMessage) return rawMessage;
1327
1479
  return fallback;
1328
1480
  }
1481
+ function resolveErrorHint(payload) {
1482
+ const structuredPayload = resolveStructuredErrorPayload(payload);
1483
+ return structuredPayload ? nonEmptyString(structuredPayload.hint) : void 0;
1484
+ }
1485
+ function resolveStatusText(response, payload) {
1486
+ const rawStatusText = nonEmptyString(response.statusText);
1487
+ if (rawStatusText) return rawStatusText;
1488
+ const payloadRecord = isRecord2(payload) ? payload : null;
1489
+ return payloadRecord ? nonEmptyString(payloadRecord.statusText) ?? null : null;
1490
+ }
1329
1491
  function detailsFromError(error) {
1330
1492
  return error.toDetails();
1331
1493
  }
@@ -1463,9 +1625,168 @@ function buildHeaders(config, options) {
1463
1625
  });
1464
1626
  return headers;
1465
1627
  }
1628
+ function toInvalidUrlResponse(error, endpoint, method) {
1629
+ const message = error instanceof Error ? error.message : String(error);
1630
+ const gatewayError = error instanceof AthenaGatewayError ? error : new AthenaGatewayError({
1631
+ code: "INVALID_URL",
1632
+ message,
1633
+ status: 0,
1634
+ endpoint,
1635
+ method,
1636
+ cause: message,
1637
+ hint: "Set ATHENA_URL to a full http(s) URL before running queries."
1638
+ });
1639
+ return {
1640
+ ok: false,
1641
+ status: 0,
1642
+ statusText: null,
1643
+ data: null,
1644
+ error: gatewayError.message,
1645
+ errorDetails: detailsFromError(
1646
+ new AthenaGatewayError({
1647
+ code: gatewayError.code,
1648
+ message: gatewayError.message,
1649
+ status: gatewayError.status,
1650
+ endpoint,
1651
+ method,
1652
+ requestId: gatewayError.requestId,
1653
+ hint: gatewayError.hint,
1654
+ cause: gatewayError.causeDetail
1655
+ })
1656
+ ),
1657
+ raw: null
1658
+ };
1659
+ }
1660
+ function resolveGatewayBaseUrl(input) {
1661
+ return normalizeAthenaGatewayBaseUrl(input, {
1662
+ defaultBaseUrl: ATHENA_DEFAULT_BASE_URL
1663
+ });
1664
+ }
1665
+ function resolveProbePath(path) {
1666
+ if (!path) return "/";
1667
+ if (!path.startsWith("/")) {
1668
+ throw new AthenaGatewayError({
1669
+ code: "INVALID_URL",
1670
+ message: `Athena gateway probe path must start with "/". Received ${JSON.stringify(path)}.`,
1671
+ status: 0,
1672
+ hint: 'Use a leading slash such as "/" or "/health".'
1673
+ });
1674
+ }
1675
+ return path;
1676
+ }
1677
+ function mergeConnectionHeaders(baseHeaders, headers) {
1678
+ const merged = {
1679
+ ...baseHeaders,
1680
+ ...headers ?? {}
1681
+ };
1682
+ if (!merged["X-Athena-Sdk"] && !merged["x-athena-sdk"]) {
1683
+ merged["X-Athena-Sdk"] = SDK_HEADER_VALUE;
1684
+ }
1685
+ return merged;
1686
+ }
1687
+ async function performConnectionCheck(baseUrl, requestHeaders, options) {
1688
+ const path = resolveProbePath(options?.path);
1689
+ const url = buildAthenaGatewayUrl(baseUrl, path);
1690
+ try {
1691
+ const response = await fetch(url, {
1692
+ method: "GET",
1693
+ headers: mergeConnectionHeaders(requestHeaders, options?.headers),
1694
+ signal: options?.signal
1695
+ });
1696
+ const rawText = await response.text();
1697
+ const requestId = resolveRequestId(response.headers);
1698
+ const parsedBody = parseResponseBody(
1699
+ rawText ?? "",
1700
+ response.headers.get("content-type")
1701
+ );
1702
+ if (parsedBody.parseFailed) {
1703
+ const invalidJsonError = new AthenaGatewayError({
1704
+ code: "INVALID_JSON",
1705
+ message: "Gateway probe returned malformed JSON",
1706
+ status: response.status,
1707
+ method: "GET",
1708
+ requestId,
1709
+ hint: "Verify the gateway response body is valid JSON.",
1710
+ cause: rawText.slice(0, 300)
1711
+ });
1712
+ return {
1713
+ ok: false,
1714
+ reachable: true,
1715
+ status: response.status,
1716
+ statusText: resolveStatusText(response, parsedBody.parsed),
1717
+ baseUrl,
1718
+ url,
1719
+ error: invalidJsonError.message,
1720
+ errorDetails: detailsFromError(invalidJsonError),
1721
+ raw: parsedBody.parsed
1722
+ };
1723
+ }
1724
+ const parsed = parsedBody.parsed;
1725
+ if (!response.ok) {
1726
+ const httpError = new AthenaGatewayError({
1727
+ code: "HTTP_ERROR",
1728
+ message: resolveErrorMessage(
1729
+ parsed,
1730
+ `Athena gateway GET ${path} failed with status ${response.status}`
1731
+ ),
1732
+ status: response.status,
1733
+ method: "GET",
1734
+ requestId,
1735
+ hint: resolveErrorHint(parsed)
1736
+ });
1737
+ return {
1738
+ ok: false,
1739
+ reachable: true,
1740
+ status: response.status,
1741
+ statusText: resolveStatusText(response, parsed),
1742
+ baseUrl,
1743
+ url,
1744
+ error: httpError.message,
1745
+ errorDetails: detailsFromError(httpError),
1746
+ raw: parsed
1747
+ };
1748
+ }
1749
+ return {
1750
+ ok: true,
1751
+ reachable: true,
1752
+ status: response.status,
1753
+ statusText: resolveStatusText(response, parsed),
1754
+ baseUrl,
1755
+ url,
1756
+ error: void 0,
1757
+ errorDetails: null,
1758
+ raw: parsed
1759
+ };
1760
+ } catch (callError) {
1761
+ const message = callError instanceof Error ? callError.message : String(callError);
1762
+ const networkError = new AthenaGatewayError({
1763
+ code: "NETWORK_ERROR",
1764
+ message: `Network error while probing Athena gateway ${url}: ${message}`,
1765
+ method: "GET",
1766
+ cause: message,
1767
+ hint: "Check gateway URL, DNS, and network reachability."
1768
+ });
1769
+ return {
1770
+ ok: false,
1771
+ reachable: false,
1772
+ status: 0,
1773
+ statusText: null,
1774
+ baseUrl,
1775
+ url,
1776
+ error: networkError.message,
1777
+ errorDetails: detailsFromError(networkError),
1778
+ raw: null
1779
+ };
1780
+ }
1781
+ }
1466
1782
  async function callAthena(config, endpoint, method, payload, options) {
1467
- const baseUrl = (options?.baseUrl ?? config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
1468
- const url = `${baseUrl}${endpoint}`;
1783
+ let baseUrl;
1784
+ try {
1785
+ baseUrl = resolveGatewayBaseUrl(options?.baseUrl ?? config.baseUrl);
1786
+ } catch (error) {
1787
+ return toInvalidUrlResponse(error, endpoint, method);
1788
+ }
1789
+ const url = buildAthenaGatewayUrl(baseUrl, endpoint);
1469
1790
  const headers = buildHeaders(config, options);
1470
1791
  try {
1471
1792
  const requestInit = {
@@ -1496,6 +1817,7 @@ async function callAthena(config, endpoint, method, payload, options) {
1496
1817
  return {
1497
1818
  ok: false,
1498
1819
  status: response.status,
1820
+ statusText: resolveStatusText(response, parsedBody.parsed),
1499
1821
  data: null,
1500
1822
  error: invalidJsonError.message,
1501
1823
  errorDetails: detailsFromError(invalidJsonError),
@@ -1514,11 +1836,13 @@ async function callAthena(config, endpoint, method, payload, options) {
1514
1836
  status: response.status,
1515
1837
  endpoint,
1516
1838
  method,
1517
- requestId
1839
+ requestId,
1840
+ hint: resolveErrorHint(parsed)
1518
1841
  });
1519
1842
  return {
1520
1843
  ok: false,
1521
1844
  status: response.status,
1845
+ statusText: resolveStatusText(response, parsed),
1522
1846
  data: null,
1523
1847
  error: httpError.message,
1524
1848
  errorDetails: detailsFromError(httpError),
@@ -1530,6 +1854,7 @@ async function callAthena(config, endpoint, method, payload, options) {
1530
1854
  return {
1531
1855
  ok: true,
1532
1856
  status: response.status,
1857
+ statusText: resolveStatusText(response, parsed),
1533
1858
  data: payloadData ?? null,
1534
1859
  count: payloadCount,
1535
1860
  error: void 0,
@@ -1549,6 +1874,7 @@ async function callAthena(config, endpoint, method, payload, options) {
1549
1874
  return {
1550
1875
  ok: false,
1551
1876
  status: 0,
1877
+ statusText: null,
1552
1878
  data: null,
1553
1879
  error: networkError.message,
1554
1880
  errorDetails: detailsFromError(networkError),
@@ -1557,32 +1883,44 @@ async function callAthena(config, endpoint, method, payload, options) {
1557
1883
  }
1558
1884
  }
1559
1885
  function createAthenaGatewayClient(config = {}) {
1886
+ const normalizedBaseUrl = resolveGatewayBaseUrl(config.baseUrl);
1887
+ const normalizedConfig = {
1888
+ ...config,
1889
+ baseUrl: normalizedBaseUrl
1890
+ };
1560
1891
  return {
1561
- baseUrl: (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, ""),
1892
+ baseUrl: normalizedBaseUrl,
1562
1893
  buildHeaders(options) {
1563
- return buildHeaders(config, options);
1894
+ return buildHeaders(normalizedConfig, options);
1895
+ },
1896
+ verifyConnection(options) {
1897
+ return performConnectionCheck(
1898
+ normalizedBaseUrl,
1899
+ buildHeaders(normalizedConfig),
1900
+ options
1901
+ );
1564
1902
  },
1565
1903
  fetchGateway(payload, options) {
1566
- return callAthena(config, "/gateway/fetch", "POST", payload, options);
1904
+ return callAthena(normalizedConfig, "/gateway/fetch", "POST", payload, options);
1567
1905
  },
1568
1906
  insertGateway(payload, options) {
1569
- return callAthena(config, "/gateway/insert", "PUT", payload, options);
1907
+ return callAthena(normalizedConfig, "/gateway/insert", "PUT", payload, options);
1570
1908
  },
1571
1909
  updateGateway(payload, options) {
1572
- return callAthena(config, "/gateway/update", "POST", payload, options);
1910
+ return callAthena(normalizedConfig, "/gateway/update", "POST", payload, options);
1573
1911
  },
1574
1912
  deleteGateway(payload, options) {
1575
- return callAthena(config, "/gateway/delete", "DELETE", payload, options);
1913
+ return callAthena(normalizedConfig, "/gateway/delete", "DELETE", payload, options);
1576
1914
  },
1577
1915
  rpcGateway(payload, options) {
1578
1916
  if (options?.get) {
1579
1917
  const endpoint = buildRpcGetEndpoint(payload);
1580
- return callAthena(config, endpoint, "GET", null, options);
1918
+ return callAthena(normalizedConfig, endpoint, "GET", null, options);
1581
1919
  }
1582
- return callAthena(config, "/gateway/rpc", "POST", payload, options);
1920
+ return callAthena(normalizedConfig, "/gateway/rpc", "POST", payload, options);
1583
1921
  },
1584
1922
  queryGateway(payload, options) {
1585
- return callAthena(config, "/gateway/query", "POST", payload, options);
1923
+ return callAthena(normalizedConfig, "/gateway/query", "POST", payload, options);
1586
1924
  }
1587
1925
  };
1588
1926
  }
@@ -1590,10 +1928,29 @@ function createAthenaGatewayClient(config = {}) {
1590
1928
  // src/sql-identifiers.ts
1591
1929
  var SIMPLE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
1592
1930
  var COMPOSITE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
1593
- var ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_.]*)\s+(?:as\s+)?([A-Za-z_][A-Za-z0-9_]*)$/i;
1931
+ var SQL_ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_.]*)\s+(?:as\s+)?([A-Za-z_][A-Za-z0-9_]*)$/i;
1932
+ var RESPONSE_ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([A-Za-z_][A-Za-z0-9_.]*)$/i;
1594
1933
  function quoteIdentifierSegment(identifier) {
1595
1934
  return `"${identifier.replace(/"/g, '""')}"`;
1596
1935
  }
1936
+ function parseAliasedIdentifierToken(token) {
1937
+ const responseAliasMatch = RESPONSE_ALIAS_PATTERN.exec(token);
1938
+ if (responseAliasMatch) {
1939
+ const [, aliasIdentifier2, baseIdentifier2] = responseAliasMatch;
1940
+ if (COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier2) && SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier2)) {
1941
+ return { baseIdentifier: baseIdentifier2, aliasIdentifier: aliasIdentifier2 };
1942
+ }
1943
+ }
1944
+ const sqlAliasMatch = SQL_ALIAS_PATTERN.exec(token);
1945
+ if (!sqlAliasMatch) {
1946
+ return null;
1947
+ }
1948
+ const [, baseIdentifier, aliasIdentifier] = sqlAliasMatch;
1949
+ if (!COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) || !SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier)) {
1950
+ return null;
1951
+ }
1952
+ return { baseIdentifier, aliasIdentifier };
1953
+ }
1597
1954
  function quoteQualifiedIdentifier(identifier) {
1598
1955
  return identifier.split(".").map((segment) => quoteIdentifierSegment(segment)).join(".");
1599
1956
  }
@@ -1602,23 +1959,27 @@ function quoteSelectToken(token) {
1602
1959
  if (COMPOSITE_IDENTIFIER_PATTERN.test(token)) {
1603
1960
  return quoteQualifiedIdentifier(token);
1604
1961
  }
1605
- const aliasMatch = ALIAS_PATTERN.exec(token);
1606
- if (!aliasMatch) {
1607
- return token;
1608
- }
1609
- const [, baseIdentifier, aliasIdentifier] = aliasMatch;
1610
- if (!COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) || !SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier)) {
1962
+ const aliasedIdentifier = parseAliasedIdentifierToken(token);
1963
+ if (!aliasedIdentifier) {
1611
1964
  return token;
1612
1965
  }
1966
+ const { baseIdentifier, aliasIdentifier } = aliasedIdentifier;
1613
1967
  return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteIdentifierSegment(aliasIdentifier)}`;
1614
1968
  }
1969
+ function quoteSelectColumnToken(token) {
1970
+ const trimmed = token.trim();
1971
+ if (!trimmed || trimmed === "*") return trimmed || "*";
1972
+ const responseAliasMatch = RESPONSE_ALIAS_PATTERN.exec(trimmed);
1973
+ if (responseAliasMatch) {
1974
+ const [, aliasIdentifier, baseIdentifier] = responseAliasMatch;
1975
+ return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteIdentifierSegment(aliasIdentifier)}`;
1976
+ }
1977
+ return quoteQualifiedIdentifier(trimmed);
1978
+ }
1615
1979
  function canAutoQuoteToken(token) {
1616
1980
  if (token === "*") return true;
1617
1981
  if (COMPOSITE_IDENTIFIER_PATTERN.test(token)) return true;
1618
- const aliasMatch = ALIAS_PATTERN.exec(token);
1619
- if (!aliasMatch) return false;
1620
- const [, baseIdentifier, aliasIdentifier] = aliasMatch;
1621
- return COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) && SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier);
1982
+ return parseAliasedIdentifierToken(token) != null;
1622
1983
  }
1623
1984
  function splitTopLevelCommaSeparated(input) {
1624
1985
  const parts = [];
@@ -1702,6 +2063,190 @@ function quoteSelectColumnsExpression(columns) {
1702
2063
  return tokens.map(quoteSelectToken).join(", ");
1703
2064
  }
1704
2065
 
2066
+ // src/auth/react-email.ts
2067
+ var reactEmailRenderModulePromise;
2068
+ function isRecord3(value) {
2069
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2070
+ }
2071
+ function isFunction(value) {
2072
+ return typeof value === "function";
2073
+ }
2074
+ function toStringOrUndefined(value) {
2075
+ if (typeof value !== "string") return void 0;
2076
+ return value;
2077
+ }
2078
+ function nowIsoString() {
2079
+ return (/* @__PURE__ */ new Date()).toISOString();
2080
+ }
2081
+ function emitReactEmailEvent(observe, phase, input = {}) {
2082
+ if (!observe) return;
2083
+ try {
2084
+ observe({
2085
+ phase,
2086
+ timestamp: nowIsoString(),
2087
+ ...input
2088
+ });
2089
+ } catch {
2090
+ }
2091
+ }
2092
+ function mergeRenderDefaults(input, defaults) {
2093
+ return {
2094
+ ...input,
2095
+ pretty: input.pretty ?? defaults?.pretty,
2096
+ includePlainText: input.includePlainText ?? defaults?.includePlainText
2097
+ };
2098
+ }
2099
+ function mergeRuntimeOptions(options) {
2100
+ if (!options) return void 0;
2101
+ return {
2102
+ defaults: options.defaults,
2103
+ observe: options.observe,
2104
+ route: "route" in options ? options.route : void 0
2105
+ };
2106
+ }
2107
+ async function resolveReactEmailRenderModule() {
2108
+ if (!reactEmailRenderModulePromise) {
2109
+ reactEmailRenderModulePromise = (async () => {
2110
+ try {
2111
+ const loaded = await import('@react-email/render');
2112
+ if (!isFunction(loaded.render)) {
2113
+ throw new Error("missing render(...) export");
2114
+ }
2115
+ return {
2116
+ render: loaded.render,
2117
+ toPlainText: isFunction(loaded.toPlainText) ? loaded.toPlainText : void 0,
2118
+ pretty: isFunction(loaded.pretty) ? loaded.pretty : void 0
2119
+ };
2120
+ } catch (error) {
2121
+ const message = error instanceof Error ? error.message : String(error);
2122
+ throw new Error(
2123
+ `React Email rendering requires @react-email/render. Install it in your project (for example: pnpm add @react-email/render). Loader error: ${message}`
2124
+ );
2125
+ }
2126
+ })();
2127
+ }
2128
+ if (!reactEmailRenderModulePromise) {
2129
+ throw new Error("React Email renderer module failed to initialize");
2130
+ }
2131
+ return reactEmailRenderModulePromise;
2132
+ }
2133
+ async function resolveReactEmailElement(input) {
2134
+ if (input.element != null) {
2135
+ return input.element;
2136
+ }
2137
+ if (!input.component) {
2138
+ throw new Error("react email payload requires either `element` or `component`");
2139
+ }
2140
+ try {
2141
+ const reactModule = await import('react');
2142
+ if (typeof reactModule.createElement !== "function") {
2143
+ throw new Error("react createElement(...) export is unavailable");
2144
+ }
2145
+ return reactModule.createElement(
2146
+ input.component,
2147
+ input.props ?? {}
2148
+ );
2149
+ } catch (error) {
2150
+ const message = error instanceof Error ? error.message : String(error);
2151
+ throw new Error(
2152
+ `React Email component rendering requires react runtime support. Install react in your project. Loader error: ${message}`
2153
+ );
2154
+ }
2155
+ }
2156
+ async function renderAthenaReactEmail(input, options) {
2157
+ if (!isRecord3(input)) {
2158
+ throw new Error("react email payload must be an object");
2159
+ }
2160
+ const runtimeOptions = mergeRuntimeOptions(options);
2161
+ const start = Date.now();
2162
+ emitReactEmailEvent(runtimeOptions?.observe, "render:start", {
2163
+ route: runtimeOptions?.route,
2164
+ message: "Rendering react email payload"
2165
+ });
2166
+ try {
2167
+ const normalizedInput = mergeRenderDefaults(input, runtimeOptions?.defaults);
2168
+ const element = await resolveReactEmailElement(normalizedInput);
2169
+ const renderModule = await resolveReactEmailRenderModule();
2170
+ const htmlValue = await renderModule.render(element);
2171
+ const renderedHtml = typeof htmlValue === "string" ? htmlValue : String(htmlValue ?? "");
2172
+ if (!renderedHtml.trim()) {
2173
+ throw new Error("react email renderer returned an empty HTML string");
2174
+ }
2175
+ let html = renderedHtml;
2176
+ if (normalizedInput.pretty && renderModule.pretty) {
2177
+ const prettyValue = await renderModule.pretty(renderedHtml);
2178
+ if (typeof prettyValue === "string" && prettyValue.trim().length > 0) {
2179
+ html = prettyValue;
2180
+ }
2181
+ }
2182
+ const explicitText = toStringOrUndefined(normalizedInput.text);
2183
+ if (explicitText !== void 0) {
2184
+ emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
2185
+ route: runtimeOptions?.route,
2186
+ durationMs: Date.now() - start,
2187
+ message: "Rendered react email with explicit text"
2188
+ });
2189
+ return {
2190
+ html,
2191
+ text: explicitText
2192
+ };
2193
+ }
2194
+ if (normalizedInput.includePlainText === false || !renderModule.toPlainText) {
2195
+ emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
2196
+ route: runtimeOptions?.route,
2197
+ durationMs: Date.now() - start,
2198
+ message: "Rendered react email without plain-text derivation"
2199
+ });
2200
+ return { html };
2201
+ }
2202
+ const plainTextValue = await renderModule.toPlainText(html);
2203
+ const plainText = toStringOrUndefined(plainTextValue);
2204
+ emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
2205
+ route: runtimeOptions?.route,
2206
+ durationMs: Date.now() - start,
2207
+ message: plainText ? "Rendered react email with derived plain text" : "Rendered react email HTML only"
2208
+ });
2209
+ if (plainText === void 0) {
2210
+ return { html };
2211
+ }
2212
+ return {
2213
+ html,
2214
+ text: plainText
2215
+ };
2216
+ } catch (error) {
2217
+ const message = error instanceof Error ? error.message : String(error);
2218
+ emitReactEmailEvent(runtimeOptions?.observe, "render:error", {
2219
+ route: runtimeOptions?.route,
2220
+ durationMs: Date.now() - start,
2221
+ error: message,
2222
+ message: "Failed to render react email payload"
2223
+ });
2224
+ throw error;
2225
+ }
2226
+ }
2227
+ async function resolveReactEmailPayloadFields(input, fields, options) {
2228
+ const { react, ...payloadWithoutReact } = input;
2229
+ if (!react) {
2230
+ return payloadWithoutReact;
2231
+ }
2232
+ const rendered = await renderAthenaReactEmail(react, options);
2233
+ const payload = {
2234
+ ...payloadWithoutReact
2235
+ };
2236
+ payload[fields.htmlField] = rendered.html;
2237
+ const currentTextValue = payload[fields.textField];
2238
+ if (rendered.text !== void 0 && (currentTextValue === void 0 || currentTextValue === null || currentTextValue === "")) {
2239
+ payload[fields.textField] = rendered.text;
2240
+ }
2241
+ if (fields.variablesField && (payload[fields.variablesField] === void 0 || payload[fields.variablesField] === null) && isRecord3(react.props)) {
2242
+ const derivedVariables = Object.keys(react.props);
2243
+ if (derivedVariables.length > 0) {
2244
+ payload[fields.variablesField] = derivedVariables;
2245
+ }
2246
+ }
2247
+ return payload;
2248
+ }
2249
+
1705
2250
  // src/auth/client.ts
1706
2251
  var DEFAULT_AUTH_BASE_URL = "http://localhost:3001/api/auth";
1707
2252
  var FALLBACK_SDK_VERSION2 = "1.0.0";
@@ -1711,7 +2256,7 @@ var SDK_HEADER_VALUE2 = `${SDK_NAME2} ${SDK_VERSION2}`;
1711
2256
  function normalizeBaseUrl(baseUrl) {
1712
2257
  return (baseUrl ?? DEFAULT_AUTH_BASE_URL).replace(/\/$/, "");
1713
2258
  }
1714
- function isRecord3(value) {
2259
+ function isRecord4(value) {
1715
2260
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1716
2261
  }
1717
2262
  function normalizeHeaderValue2(value) {
@@ -1736,7 +2281,7 @@ function resolveRequestId2(headers) {
1736
2281
  return headers.get("x-request-id") ?? headers.get("x-correlation-id") ?? headers.get("x-athena-request-id") ?? void 0;
1737
2282
  }
1738
2283
  function resolveErrorMessage2(payload, fallback) {
1739
- if (isRecord3(payload)) {
2284
+ if (isRecord4(payload)) {
1740
2285
  const messageCandidates = [payload.error, payload.message, payload.details];
1741
2286
  for (const candidate of messageCandidates) {
1742
2287
  if (typeof candidate === "string" && candidate.trim().length > 0) {
@@ -2062,6 +2607,19 @@ function createAuthClient(config = {}) {
2062
2607
  options
2063
2608
  );
2064
2609
  };
2610
+ const withReactEmailRoute = (route) => ({
2611
+ ...resolvedConfig.reactEmail,
2612
+ route
2613
+ });
2614
+ const resolveAdminEmailPayload = (route, input) => resolveReactEmailPayloadFields(input, {
2615
+ htmlField: "htmlBody",
2616
+ textField: "textBody"
2617
+ }, withReactEmailRoute(route));
2618
+ const resolveAdminEmailTemplatePayload = (route, input) => resolveReactEmailPayloadFields(input, {
2619
+ htmlField: "htmlTemplate",
2620
+ textField: "textTemplate",
2621
+ variablesField: "variables"
2622
+ }, withReactEmailRoute(route));
2065
2623
  const listUserEmailsWithFallback = async (input, options) => {
2066
2624
  const primary = await getWithQuery(
2067
2625
  "/email/list",
@@ -2089,7 +2647,7 @@ function createAuthClient(config = {}) {
2089
2647
  data: null
2090
2648
  };
2091
2649
  }
2092
- const fallbackStatus = isRecord3(fallback.data) && typeof fallback.data.ok === "boolean" ? fallback.data.ok ? "ok" : "error" : "ok";
2650
+ const fallbackStatus = isRecord4(fallback.data) && typeof fallback.data.ok === "boolean" ? fallback.data.ok ? "ok" : "error" : "ok";
2093
2651
  return {
2094
2652
  ...fallback,
2095
2653
  data: {
@@ -2525,9 +3083,17 @@ function createAuthClient(config = {}) {
2525
3083
  email: {
2526
3084
  list: (input, options) => getWithQuery("/admin/email/list", input, options),
2527
3085
  get: (input, options) => getWithQuery("/admin/email/get", input, options),
2528
- create: (input, options) => postGeneric("/admin/email/create", input, options),
2529
- update: (input, options) => postGeneric("/admin/email/update", input, options),
2530
- delete: (input, options) => postGeneric("/admin/email/delete", input, options),
3086
+ create: async (input, options) => postGeneric(
3087
+ "/admin/email/create",
3088
+ await resolveAdminEmailPayload("/admin/email/create", input),
3089
+ options
3090
+ ),
3091
+ update: async (input, options) => postGeneric(
3092
+ "/admin/email/update",
3093
+ await resolveAdminEmailPayload("/admin/email/update", input),
3094
+ options
3095
+ ),
3096
+ delete: (input, options) => postGeneric("/admin/email/delete", input, options),
2531
3097
  failure: {
2532
3098
  list: (input, options) => getWithQuery("/admin/email-failure/list", input, options),
2533
3099
  get: (input, options) => getWithQuery("/admin/email-failure/get", input, options),
@@ -2538,17 +3104,33 @@ function createAuthClient(config = {}) {
2538
3104
  template: {
2539
3105
  list: (input, options) => getWithQuery("/admin/email-template/list", input, options),
2540
3106
  get: (input, options) => getWithQuery("/admin/email-template/get", input, options),
2541
- create: (input, options) => postGeneric("/admin/email-template/create", input, options),
2542
- update: (input, options) => postGeneric("/admin/email-template/update", input, options),
3107
+ create: async (input, options) => postGeneric(
3108
+ "/admin/email-template/create",
3109
+ await resolveAdminEmailTemplatePayload("/admin/email-template/create", input),
3110
+ options
3111
+ ),
3112
+ update: async (input, options) => postGeneric(
3113
+ "/admin/email-template/update",
3114
+ await resolveAdminEmailTemplatePayload("/admin/email-template/update", input),
3115
+ options
3116
+ ),
2543
3117
  delete: (input, options) => postGeneric("/admin/email-template/delete", input, options)
2544
3118
  }
2545
3119
  },
2546
3120
  emailTemplate: {
2547
3121
  get: (input, options) => getWithQuery("/admin/email-template/get", input, options),
2548
- create: (input, options) => postGeneric("/admin/email-template/create", input, options),
3122
+ create: async (input, options) => postGeneric(
3123
+ "/admin/email-template/create",
3124
+ await resolveAdminEmailTemplatePayload("/admin/email-template/create", input),
3125
+ options
3126
+ ),
2549
3127
  delete: (input, options) => postGeneric("/admin/email-template/delete", input, options),
2550
3128
  list: (input, options) => getWithQuery("/admin/email-template/list", input, options),
2551
- update: (input, options) => postGeneric("/admin/email-template/update", input, options)
3129
+ update: async (input, options) => postGeneric(
3130
+ "/admin/email-template/update",
3131
+ await resolveAdminEmailTemplatePayload("/admin/email-template/update", input),
3132
+ options
3133
+ )
2552
3134
  }
2553
3135
  },
2554
3136
  apiKey: {
@@ -2743,8 +3325,8 @@ function createAuthClient(config = {}) {
2743
3325
  // src/db/module.ts
2744
3326
  function createDbModule(input) {
2745
3327
  const db = {
2746
- from(table) {
2747
- return input.from(table);
3328
+ from(table, options) {
3329
+ return input.from(table, options);
2748
3330
  },
2749
3331
  select(table, columns, options) {
2750
3332
  return input.from(table).select(columns, options);
@@ -2771,17 +3353,551 @@ function createDbModule(input) {
2771
3353
  return db;
2772
3354
  }
2773
3355
 
3356
+ // src/query-ast.ts
3357
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
3358
+ var FILTER_OPERATORS = /* @__PURE__ */ new Set([
3359
+ "eq",
3360
+ "neq",
3361
+ "gt",
3362
+ "gte",
3363
+ "lt",
3364
+ "lte",
3365
+ "like",
3366
+ "ilike",
3367
+ "is",
3368
+ "in",
3369
+ "contains",
3370
+ "containedBy"
3371
+ ]);
3372
+ var BOOLEAN_SAFE_OPERATORS = /* @__PURE__ */ new Set([
3373
+ "eq",
3374
+ "neq",
3375
+ "gt",
3376
+ "gte",
3377
+ "lt",
3378
+ "lte",
3379
+ "like",
3380
+ "ilike",
3381
+ "is"
3382
+ ]);
3383
+ function isRecord5(value) {
3384
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3385
+ }
3386
+ function isUuidString(value) {
3387
+ return UUID_PATTERN.test(value.trim());
3388
+ }
3389
+ function isUuidIdentifierColumn(column) {
3390
+ return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
3391
+ }
3392
+ function shouldUseUuidTextComparison(column, value) {
3393
+ return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
3394
+ }
3395
+ function isRelationSelectNode(value) {
3396
+ return isRecord5(value) && isRecord5(value.select);
3397
+ }
3398
+ function normalizeIdentifier(value, label) {
3399
+ const normalized = value.trim();
3400
+ if (!normalized) {
3401
+ throw new Error(`${label} must be a non-empty string`);
3402
+ }
3403
+ return normalized;
3404
+ }
3405
+ function stringifyFilterValue(value) {
3406
+ if (Array.isArray(value)) {
3407
+ return value.join(",");
3408
+ }
3409
+ return String(value);
3410
+ }
3411
+ function buildGatewayCondition(operator, column, value) {
3412
+ const condition = { operator };
3413
+ if (column) {
3414
+ condition.column = column;
3415
+ if (operator === "eq") {
3416
+ condition.eq_column = column;
3417
+ }
3418
+ }
3419
+ if (value !== void 0) {
3420
+ condition.value = value;
3421
+ if (operator === "eq") {
3422
+ condition.eq_value = value;
3423
+ }
3424
+ }
3425
+ if (operator === "eq" && column && value !== void 0 && shouldUseUuidTextComparison(column, value)) {
3426
+ condition.column_cast = "text";
3427
+ condition.eq_column_cast = "text";
3428
+ }
3429
+ return condition;
3430
+ }
3431
+ function compileRelationToken(key, node) {
3432
+ const nested = compileSelectShape(node.select);
3433
+ const propertyKey = normalizeIdentifier(key, "select relation key");
3434
+ if (node.schema && node.via) {
3435
+ throw new Error(
3436
+ `findMany relation "${propertyKey}" cannot combine schema and via yet; use schema with the relation key, or use via without schema`
3437
+ );
3438
+ }
3439
+ const relationTokenBase = normalizeIdentifier(node.via ?? propertyKey, "select relation token");
3440
+ if (node.schema && relationTokenBase.includes(".")) {
3441
+ throw new Error(
3442
+ `findMany relation "${propertyKey}" already resolves to a qualified relation token; do not also set schema`
3443
+ );
3444
+ }
3445
+ const relationSchema = node.schema?.trim();
3446
+ const relationToken = relationSchema ? `${normalizeIdentifier(relationSchema, "select relation schema")}.${relationTokenBase}` : relationTokenBase;
3447
+ const alias = node.as?.trim() || (relationToken !== propertyKey ? propertyKey : "");
3448
+ const prefix = alias ? `${alias}:` : "";
3449
+ return `${prefix}${relationToken}(${nested})`;
3450
+ }
3451
+ function compileSelectShape(select) {
3452
+ if (!isRecord5(select)) {
3453
+ throw new Error("findMany select must be an object");
3454
+ }
3455
+ const tokens = [];
3456
+ for (const [rawKey, rawValue] of Object.entries(select)) {
3457
+ if (rawValue === void 0) {
3458
+ continue;
3459
+ }
3460
+ if (rawValue === true) {
3461
+ tokens.push(normalizeIdentifier(rawKey, "select column"));
3462
+ continue;
3463
+ }
3464
+ if (isRelationSelectNode(rawValue)) {
3465
+ tokens.push(compileRelationToken(rawKey, rawValue));
3466
+ continue;
3467
+ }
3468
+ throw new Error(`Unsupported select node for "${rawKey}"`);
3469
+ }
3470
+ if (tokens.length === 0) {
3471
+ throw new Error("findMany select requires at least one field");
3472
+ }
3473
+ return tokens.join(",");
3474
+ }
3475
+ function selectShapeUsesRelationSchema(select) {
3476
+ if (!isRecord5(select)) {
3477
+ return false;
3478
+ }
3479
+ for (const rawValue of Object.values(select)) {
3480
+ if (!isRelationSelectNode(rawValue)) {
3481
+ continue;
3482
+ }
3483
+ if (typeof rawValue.schema === "string" && rawValue.schema.trim().length > 0) {
3484
+ return true;
3485
+ }
3486
+ if (selectShapeUsesRelationSchema(rawValue.select)) {
3487
+ return true;
3488
+ }
3489
+ }
3490
+ return false;
3491
+ }
3492
+ function compileColumnWhere(column, input) {
3493
+ const normalizedColumn = normalizeIdentifier(column, "where column");
3494
+ if (!isRecord5(input)) {
3495
+ return [buildGatewayCondition("eq", normalizedColumn, input)];
3496
+ }
3497
+ const conditions = [];
3498
+ for (const [rawOperator, rawValue] of Object.entries(input)) {
3499
+ if (rawValue === void 0) {
3500
+ continue;
3501
+ }
3502
+ if (!FILTER_OPERATORS.has(rawOperator)) {
3503
+ throw new Error(`Unsupported where operator "${rawOperator}" on "${normalizedColumn}"`);
3504
+ }
3505
+ if ((rawOperator === "in" || rawOperator === "contains" || rawOperator === "containedBy") && !Array.isArray(rawValue)) {
3506
+ throw new Error(`where.${normalizedColumn}.${rawOperator} requires an array value`);
3507
+ }
3508
+ conditions.push(
3509
+ buildGatewayCondition(
3510
+ rawOperator,
3511
+ normalizedColumn,
3512
+ rawValue
3513
+ )
3514
+ );
3515
+ }
3516
+ if (conditions.length === 0) {
3517
+ throw new Error(`where.${normalizedColumn} requires at least one operator`);
3518
+ }
3519
+ return conditions;
3520
+ }
3521
+ function compileBooleanExpressionTerms(clause, label) {
3522
+ if (!isRecord5(clause)) {
3523
+ throw new Error(`findMany where.${label} clauses must be objects`);
3524
+ }
3525
+ const entries = Object.entries(clause).filter(([, value]) => value !== void 0);
3526
+ if (entries.length !== 1) {
3527
+ throw new Error(`findMany where.${label} clauses must target exactly one column`);
3528
+ }
3529
+ const [rawColumn, rawValue] = entries[0];
3530
+ const column = normalizeIdentifier(rawColumn, `where.${label} column`);
3531
+ if (!isRecord5(rawValue)) {
3532
+ return [`${column}.eq.${stringifyFilterValue(rawValue)}`];
3533
+ }
3534
+ const operatorEntries = Object.entries(rawValue).filter(([, value]) => value !== void 0);
3535
+ if (operatorEntries.length === 0) {
3536
+ throw new Error(`findMany where.${label}.${column} requires at least one operator`);
3537
+ }
3538
+ if (label === "not" && operatorEntries.length > 1) {
3539
+ throw new Error("findMany where.not only supports a single lossless operator expression");
3540
+ }
3541
+ return operatorEntries.map(([rawOperator, rawOperand]) => {
3542
+ if (!BOOLEAN_SAFE_OPERATORS.has(rawOperator)) {
3543
+ throw new Error(`findMany where.${label} only supports lossless scalar operators`);
3544
+ }
3545
+ if (Array.isArray(rawOperand)) {
3546
+ throw new Error(`findMany where.${label} does not support array-valued operators`);
3547
+ }
3548
+ return `${column}.${rawOperator}.${stringifyFilterValue(rawOperand)}`;
3549
+ });
3550
+ }
3551
+ function compileWhere(where) {
3552
+ if (where === void 0) {
3553
+ return void 0;
3554
+ }
3555
+ if (!isRecord5(where)) {
3556
+ throw new Error("findMany where must be an object");
3557
+ }
3558
+ const conditions = [];
3559
+ for (const [rawKey, rawValue] of Object.entries(where)) {
3560
+ if (rawValue === void 0) {
3561
+ continue;
3562
+ }
3563
+ if (rawKey === "or") {
3564
+ if (!Array.isArray(rawValue) || rawValue.length === 0) {
3565
+ throw new Error("findMany where.or must be a non-empty array");
3566
+ }
3567
+ const expressions = rawValue.flatMap(
3568
+ (value) => compileBooleanExpressionTerms(value, "or")
3569
+ );
3570
+ conditions.push(buildGatewayCondition("or", void 0, expressions.join(",")));
3571
+ continue;
3572
+ }
3573
+ if (rawKey === "not") {
3574
+ const expressions = compileBooleanExpressionTerms(rawValue, "not");
3575
+ if (expressions.length !== 1) {
3576
+ throw new Error("findMany where.not must compile to exactly one lossless expression");
3577
+ }
3578
+ conditions.push(buildGatewayCondition("not", void 0, expressions[0]));
3579
+ continue;
3580
+ }
3581
+ conditions.push(...compileColumnWhere(rawKey, rawValue));
3582
+ }
3583
+ return conditions.length > 0 ? conditions : void 0;
3584
+ }
3585
+ function resolveOrderDirection(input) {
3586
+ if (typeof input === "boolean") {
3587
+ return input === false ? "descending" : "ascending";
3588
+ }
3589
+ if (typeof input === "string") {
3590
+ const normalized = input.trim().toLowerCase();
3591
+ if (normalized === "asc" || normalized === "ascending") {
3592
+ return "ascending";
3593
+ }
3594
+ if (normalized === "desc" || normalized === "descending") {
3595
+ return "descending";
3596
+ }
3597
+ throw new Error(`Unsupported orderBy direction "${input}"`);
3598
+ }
3599
+ return input.ascending === false ? "descending" : "ascending";
3600
+ }
3601
+ function compileOrderBy(orderBy) {
3602
+ if (orderBy === void 0) {
3603
+ return void 0;
3604
+ }
3605
+ if (!isRecord5(orderBy)) {
3606
+ throw new Error("findMany orderBy must be an object");
3607
+ }
3608
+ if ("column" in orderBy) {
3609
+ return {
3610
+ field: normalizeIdentifier(String(orderBy.column), "orderBy column"),
3611
+ direction: orderBy.ascending === false ? "descending" : "ascending"
3612
+ };
3613
+ }
3614
+ const entries = Object.entries(orderBy).filter(([, value]) => value !== void 0);
3615
+ if (entries.length === 0) {
3616
+ return void 0;
3617
+ }
3618
+ if (entries.length > 1) {
3619
+ throw new Error("findMany orderBy only supports a single column in v1");
3620
+ }
3621
+ const [column, input] = entries[0];
3622
+ return {
3623
+ field: normalizeIdentifier(column, "orderBy column"),
3624
+ direction: resolveOrderDirection(input)
3625
+ };
3626
+ }
3627
+
3628
+ // src/gateway/structured-select.ts
3629
+ var IDENTIFIER_SEGMENT_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
3630
+ function isIdentifierPath(value) {
3631
+ const segments = value.split(".").map((segment) => segment.trim());
3632
+ return segments.length > 1 && segments.every((segment) => IDENTIFIER_SEGMENT_PATTERN.test(segment));
3633
+ }
3634
+ function extractRelationHead(raw) {
3635
+ const trimmed = raw.trim();
3636
+ if (!trimmed) return "";
3637
+ const aliasIndex = trimmed.indexOf(":");
3638
+ const withoutAlias = aliasIndex >= 0 ? trimmed.slice(aliasIndex + 1).trim() : trimmed;
3639
+ const modifierIndex = withoutAlias.indexOf("!");
3640
+ return (modifierIndex >= 0 ? withoutAlias.slice(0, modifierIndex) : withoutAlias).trim();
3641
+ }
3642
+ function hasSchemaQualifiedRelationToken(select) {
3643
+ let singleQuoted = false;
3644
+ let doubleQuoted = false;
3645
+ let tokenStart = 0;
3646
+ for (let index = 0; index < select.length; index += 1) {
3647
+ const char = select[index];
3648
+ const next = index + 1 < select.length ? select[index + 1] : "";
3649
+ if (singleQuoted) {
3650
+ if (char === "'" && next === "'") {
3651
+ index += 1;
3652
+ continue;
3653
+ }
3654
+ if (char === "'") {
3655
+ singleQuoted = false;
3656
+ }
3657
+ continue;
3658
+ }
3659
+ if (doubleQuoted) {
3660
+ if (char === '"' && next === '"') {
3661
+ index += 1;
3662
+ continue;
3663
+ }
3664
+ if (char === '"') {
3665
+ doubleQuoted = false;
3666
+ }
3667
+ continue;
3668
+ }
3669
+ if (char === "'") {
3670
+ singleQuoted = true;
3671
+ continue;
3672
+ }
3673
+ if (char === '"') {
3674
+ doubleQuoted = true;
3675
+ continue;
3676
+ }
3677
+ if (char === "(") {
3678
+ const relationHead = extractRelationHead(select.slice(tokenStart, index));
3679
+ if (isIdentifierPath(relationHead)) {
3680
+ return true;
3681
+ }
3682
+ tokenStart = index + 1;
3683
+ continue;
3684
+ }
3685
+ if (char === "," || char === ")") {
3686
+ tokenStart = index + 1;
3687
+ }
3688
+ }
3689
+ return false;
3690
+ }
3691
+ function toStructuredSelectString(columns) {
3692
+ return Array.isArray(columns) ? columns.join(",") : columns;
3693
+ }
3694
+ function buildStructuredWhere(conditions) {
3695
+ if (!conditions?.length) return void 0;
3696
+ const where = {};
3697
+ for (const condition of conditions) {
3698
+ if (!condition.column) {
3699
+ return null;
3700
+ }
3701
+ if (condition.column_cast !== void 0 || condition.value_cast !== void 0) {
3702
+ return null;
3703
+ }
3704
+ const operand = condition.value;
3705
+ const operator = condition.operator;
3706
+ if (operator === "eq") {
3707
+ if (operand === void 0) return null;
3708
+ } else if (operator === "neq" || operator === "gt" || operator === "lt") {
3709
+ if (operand === void 0) return null;
3710
+ } else if (operator === "in") {
3711
+ if (!Array.isArray(operand)) return null;
3712
+ } else {
3713
+ return null;
3714
+ }
3715
+ const existing = where[condition.column];
3716
+ if (existing !== void 0 && (typeof existing !== "object" || existing === null || Array.isArray(existing))) {
3717
+ return null;
3718
+ }
3719
+ const next = existing ?? {};
3720
+ if (Object.prototype.hasOwnProperty.call(next, operator)) {
3721
+ return null;
3722
+ }
3723
+ next[operator] = operand;
3724
+ where[condition.column] = next;
3725
+ }
3726
+ return where;
3727
+ }
3728
+ function buildStructuredOrderBy(order) {
3729
+ if (!order?.field) return void 0;
3730
+ return {
3731
+ [order.field]: order.direction === "descending" ? "desc" : "asc"
3732
+ };
3733
+ }
3734
+ function buildStructuredSelectTransport(input) {
3735
+ const select = toStructuredSelectString(input.columns).trim();
3736
+ if (!select || select === "*") {
3737
+ return null;
3738
+ }
3739
+ if (!hasSchemaQualifiedRelationToken(select)) {
3740
+ return null;
3741
+ }
3742
+ if (input.count !== void 0 || input.head !== void 0) {
3743
+ return {
3744
+ error: "Schema-qualified nested select strings require structured select transport, which does not support count/head options in athena-js yet."
3745
+ };
3746
+ }
3747
+ const where = buildStructuredWhere(input.conditions);
3748
+ if (where === null) {
3749
+ return {
3750
+ error: "Schema-qualified nested select strings only support eq, neq, gt, lt, and in filters in athena-js structured select transport."
3751
+ };
3752
+ }
3753
+ return {
3754
+ select,
3755
+ payload: {
3756
+ table_name: input.tableName,
3757
+ select,
3758
+ where,
3759
+ orderBy: buildStructuredOrderBy(input.order),
3760
+ limit: input.limit,
3761
+ offset: input.offset,
3762
+ strip_nulls: input.stripNulls
3763
+ }
3764
+ };
3765
+ }
3766
+
3767
+ // src/query-transport.ts
3768
+ function canUseFindManyAstTransport(state) {
3769
+ return state.conditions.length === 0 && state.offset === void 0 && state.currentPage === void 0 && state.pageSize === void 0 && state.totalPages === void 0;
3770
+ }
3771
+ function toFindManyAstOrder(order) {
3772
+ if (!order) {
3773
+ return void 0;
3774
+ }
3775
+ return {
3776
+ column: order.field,
3777
+ ascending: order.direction !== "descending"
3778
+ };
3779
+ }
3780
+ function resolvePagination(input) {
3781
+ let limit = input.limit;
3782
+ let offset = input.offset;
3783
+ if (limit === void 0 && input.pageSize !== void 0) {
3784
+ limit = Math.max(0, Math.trunc(input.pageSize));
3785
+ }
3786
+ if (offset === void 0 && input.pageSize !== void 0 && input.currentPage !== void 0 && input.currentPage > 0) {
3787
+ offset = (Math.trunc(input.currentPage) - 1) * Math.max(0, Math.trunc(input.pageSize));
3788
+ }
3789
+ return { limit, offset };
3790
+ }
3791
+ function hasTypedEqualityComparison(conditions) {
3792
+ return conditions?.some(
3793
+ (condition) => condition.operator === "eq" && (condition.value_cast !== void 0 || condition.column_cast !== void 0)
3794
+ ) ?? false;
3795
+ }
3796
+ function createSelectTransportPlan(input) {
3797
+ const conditions = input.state.conditions.length ? input.state.conditions.map((condition) => ({ ...condition })) : void 0;
3798
+ if (hasTypedEqualityComparison(conditions) && !input.options?.head && !input.options?.count && conditions) {
3799
+ const query = input.buildTypedSelectQuery({
3800
+ tableName: input.tableName,
3801
+ columns: input.columns,
3802
+ conditions,
3803
+ limit: input.state.limit,
3804
+ offset: input.state.offset,
3805
+ currentPage: input.state.currentPage,
3806
+ pageSize: input.state.pageSize,
3807
+ order: input.state.order
3808
+ });
3809
+ if (query) {
3810
+ return {
3811
+ kind: "query",
3812
+ query,
3813
+ payload: { query }
3814
+ };
3815
+ }
3816
+ }
3817
+ const pagination = resolvePagination({
3818
+ limit: input.state.limit,
3819
+ offset: input.state.offset,
3820
+ currentPage: input.state.currentPage,
3821
+ pageSize: input.state.pageSize
3822
+ });
3823
+ const stripNulls = input.options?.stripNulls ?? true;
3824
+ const structuredSelectTransport = buildStructuredSelectTransport({
3825
+ tableName: input.tableName,
3826
+ columns: input.columns,
3827
+ conditions,
3828
+ limit: pagination.limit,
3829
+ offset: pagination.offset,
3830
+ order: input.state.order,
3831
+ stripNulls,
3832
+ count: input.options?.count,
3833
+ head: input.options?.head
3834
+ });
3835
+ if (structuredSelectTransport && "error" in structuredSelectTransport) {
3836
+ throw new Error(structuredSelectTransport.error);
3837
+ }
3838
+ if (structuredSelectTransport) {
3839
+ const { payload, select } = structuredSelectTransport;
3840
+ return {
3841
+ kind: "fetch",
3842
+ payload,
3843
+ debug: {
3844
+ columns: select,
3845
+ conditions,
3846
+ limit: pagination.limit,
3847
+ offset: pagination.offset,
3848
+ order: input.state.order
3849
+ }
3850
+ };
3851
+ }
3852
+ return {
3853
+ kind: "fetch",
3854
+ payload: {
3855
+ table_name: input.tableName,
3856
+ columns: input.columns,
3857
+ conditions,
3858
+ limit: input.state.limit,
3859
+ offset: input.state.offset,
3860
+ current_page: input.state.currentPage,
3861
+ page_size: input.state.pageSize,
3862
+ total_pages: input.state.totalPages,
3863
+ sort_by: input.state.order,
3864
+ strip_nulls: stripNulls,
3865
+ count: input.options?.count,
3866
+ head: input.options?.head
3867
+ },
3868
+ debug: {
3869
+ columns: input.columns,
3870
+ conditions,
3871
+ limit: input.state.limit,
3872
+ offset: input.state.offset,
3873
+ currentPage: input.state.currentPage,
3874
+ pageSize: input.state.pageSize,
3875
+ order: input.state.order
3876
+ }
3877
+ };
3878
+ }
3879
+
2774
3880
  // src/client.ts
2775
3881
  var DEFAULT_COLUMNS = "*";
2776
- var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
2777
3882
  var SAFE_CAST_PATTERN = /^[a-z_][a-z0-9_]*(?:\[\])?$/i;
2778
3883
  var ATHENA_NORMALIZED_ERROR_KEY = "__athenaNormalizedError";
3884
+ var QUERY_TRACE_STACK_SKIP_PATTERNS = [
3885
+ "src\\client.ts",
3886
+ "src/client.ts",
3887
+ "dist\\client.",
3888
+ "dist/client.",
3889
+ "node_modules\\@xylex-group\\athena",
3890
+ "node_modules/@xylex-group/athena",
3891
+ "node:internal",
3892
+ "internal/process"
3893
+ ];
2779
3894
  function formatResult(response) {
2780
3895
  const result = {
2781
3896
  data: response.data ?? null,
2782
- error: response.error ?? null,
3897
+ error: null,
2783
3898
  errorDetails: response.errorDetails ?? null,
2784
3899
  status: response.status,
3900
+ statusText: response.statusText ?? null,
2785
3901
  raw: response.raw
2786
3902
  };
2787
3903
  if (response.count !== void 0) {
@@ -2798,19 +3914,236 @@ function attachNormalizedError(result, normalizedError) {
2798
3914
  });
2799
3915
  }
2800
3916
  function createResultFormatter(experimental) {
2801
- if (!experimental?.enableErrorNormalization) {
2802
- return formatResult;
2803
- }
2804
3917
  return (response, context) => {
2805
3918
  const result = formatResult(response);
2806
- if (result.error == null) {
3919
+ if (response.error == null && response.errorDetails == null) {
2807
3920
  return result;
2808
3921
  }
2809
- const normalizedError = normalizeAthenaError(result, context);
3922
+ const normalizedError = normalizeAthenaError(
3923
+ {
3924
+ ...result,
3925
+ error: response.error ?? response.errorDetails?.message ?? null
3926
+ },
3927
+ context
3928
+ );
3929
+ result.error = createResultError(response, result, normalizedError);
2810
3930
  attachNormalizedError(result, normalizedError);
2811
3931
  return result;
2812
3932
  };
2813
3933
  }
3934
+ function isRecord6(value) {
3935
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3936
+ }
3937
+ function firstNonEmptyString2(...values) {
3938
+ for (const value of values) {
3939
+ if (typeof value === "string" && value.trim().length > 0) {
3940
+ return value.trim();
3941
+ }
3942
+ }
3943
+ return void 0;
3944
+ }
3945
+ function resolveStructuredErrorPayload2(raw) {
3946
+ if (!isRecord6(raw)) return null;
3947
+ return isRecord6(raw.error) ? raw.error : raw;
3948
+ }
3949
+ function resolveStructuredErrorDetails(payload, message) {
3950
+ if (!payload || !("details" in payload)) {
3951
+ return null;
3952
+ }
3953
+ const details = payload.details;
3954
+ if (details == null) {
3955
+ return null;
3956
+ }
3957
+ if (typeof details === "string" && details.trim() === message.trim()) {
3958
+ return null;
3959
+ }
3960
+ return details;
3961
+ }
3962
+ function createResultError(response, result, normalized) {
3963
+ const rawRecord = isRecord6(response.raw) ? response.raw : null;
3964
+ const payload = resolveStructuredErrorPayload2(response.raw);
3965
+ const message = firstNonEmptyString2(
3966
+ response.error,
3967
+ payload?.message,
3968
+ payload?.error,
3969
+ payload?.details,
3970
+ response.errorDetails?.message,
3971
+ normalized.message
3972
+ ) ?? normalized.message;
3973
+ const statusText = firstNonEmptyString2(response.statusText, rawRecord?.statusText) ?? null;
3974
+ const hint = firstNonEmptyString2(payload?.hint, response.errorDetails?.hint) ?? null;
3975
+ const code = firstNonEmptyString2(payload?.code) ?? normalized.code;
3976
+ const details = resolveStructuredErrorDetails(payload, message) ?? response.errorDetails?.cause ?? null;
3977
+ return {
3978
+ message,
3979
+ code,
3980
+ athenaCode: normalized.code,
3981
+ gatewayCode: response.errorDetails?.code ?? null,
3982
+ kind: normalized.kind,
3983
+ category: normalized.category,
3984
+ retryable: normalized.retryable,
3985
+ details,
3986
+ hint,
3987
+ status: result.status,
3988
+ statusText,
3989
+ constraint: normalized.constraint,
3990
+ table: normalized.table,
3991
+ operation: normalized.operation,
3992
+ endpoint: response.errorDetails?.endpoint,
3993
+ method: response.errorDetails?.method,
3994
+ requestId: response.errorDetails?.requestId,
3995
+ cause: response.errorDetails?.cause,
3996
+ raw: result.raw
3997
+ };
3998
+ }
3999
+ function parseQueryTraceCallsiteFrame(frame) {
4000
+ const trimmed = frame.trim();
4001
+ if (!trimmed) {
4002
+ return null;
4003
+ }
4004
+ let body = trimmed.replace(/^at\s+/, "");
4005
+ if (body.startsWith("async ")) {
4006
+ body = body.slice(6);
4007
+ }
4008
+ let functionName;
4009
+ let location = body;
4010
+ const wrappedMatch = body.match(/^(.*?)\s+\((.*)\)$/);
4011
+ if (wrappedMatch) {
4012
+ functionName = wrappedMatch[1].trim() || void 0;
4013
+ location = wrappedMatch[2].trim();
4014
+ }
4015
+ const locationMatch = location.match(/^(.*):(\d+):(\d+)$/);
4016
+ if (!locationMatch) {
4017
+ return null;
4018
+ }
4019
+ const filePath = locationMatch[1].replace(/^file:\/\//, "");
4020
+ const line = Number(locationMatch[2]);
4021
+ const column = Number(locationMatch[3]);
4022
+ if (!Number.isFinite(line) || !Number.isFinite(column)) {
4023
+ return null;
4024
+ }
4025
+ const normalizedPath = filePath.replace(/\\/g, "/");
4026
+ const fileName = normalizedPath.split("/").at(-1) ?? filePath;
4027
+ return {
4028
+ filePath,
4029
+ fileName,
4030
+ line,
4031
+ column,
4032
+ frame: trimmed,
4033
+ functionName
4034
+ };
4035
+ }
4036
+ function captureQueryTraceCallsite() {
4037
+ const stack = new Error().stack;
4038
+ if (!stack) return null;
4039
+ const frames = stack.split("\n").slice(2).map((frame) => frame.trim()).filter(Boolean);
4040
+ for (const frame of frames) {
4041
+ if (QUERY_TRACE_STACK_SKIP_PATTERNS.some((pattern) => frame.includes(pattern))) {
4042
+ continue;
4043
+ }
4044
+ const callsite = parseQueryTraceCallsiteFrame(frame);
4045
+ if (callsite) return callsite;
4046
+ }
4047
+ const fallback = frames.find((frame) => !frame.includes("captureQueryTraceCallsite"));
4048
+ return fallback ? parseQueryTraceCallsiteFrame(fallback) : null;
4049
+ }
4050
+ function defaultQueryTraceLogger(event) {
4051
+ const target = event.table ?? event.functionName ?? "gateway";
4052
+ const outcomeState = event.outcome?.error ? "error" : "ok";
4053
+ const banner = `[athena-js][trace] ${event.operation.toUpperCase()} ${event.endpoint} ${target} ${event.durationMs}ms ${outcomeState}`;
4054
+ console.info(banner, event);
4055
+ }
4056
+ function captureTraceCallsite(tracer) {
4057
+ return tracer?.captureCallsite() ?? null;
4058
+ }
4059
+ function createTraceCallsiteStore(tracer, initialCallsite) {
4060
+ let storedCallsite = initialCallsite ?? void 0;
4061
+ return {
4062
+ resolve(callsite) {
4063
+ if (callsite) {
4064
+ storedCallsite = callsite;
4065
+ return callsite;
4066
+ }
4067
+ if (storedCallsite !== void 0) {
4068
+ return storedCallsite;
4069
+ }
4070
+ const capturedCallsite = captureTraceCallsite(tracer);
4071
+ if (capturedCallsite) {
4072
+ storedCallsite = capturedCallsite;
4073
+ }
4074
+ return capturedCallsite;
4075
+ }
4076
+ };
4077
+ }
4078
+ function createQueryTracer(experimental) {
4079
+ const traceOption = experimental?.traceQueries;
4080
+ if (!traceOption) {
4081
+ return void 0;
4082
+ }
4083
+ const logger = typeof traceOption === "object" && traceOption.logger ? traceOption.logger : defaultQueryTraceLogger;
4084
+ const emit = (event) => {
4085
+ try {
4086
+ logger(event);
4087
+ } catch (error) {
4088
+ console.warn("[athena-js][trace] logger failed", error);
4089
+ }
4090
+ };
4091
+ return {
4092
+ captureCallsite: captureQueryTraceCallsite,
4093
+ publishSuccess(context, result, durationMs, callsite) {
4094
+ emit({
4095
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4096
+ durationMs,
4097
+ operation: context.operation,
4098
+ endpoint: context.endpoint,
4099
+ table: context.table,
4100
+ functionName: context.functionName,
4101
+ sql: context.sql,
4102
+ payload: context.payload,
4103
+ options: context.options,
4104
+ callsite,
4105
+ outcome: {
4106
+ status: result.status,
4107
+ error: result.error,
4108
+ errorDetails: result.errorDetails ?? null,
4109
+ count: result.count ?? null,
4110
+ data: result.data,
4111
+ raw: result.raw
4112
+ }
4113
+ });
4114
+ },
4115
+ publishFailure(context, error, durationMs, callsite) {
4116
+ emit({
4117
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4118
+ durationMs,
4119
+ operation: context.operation,
4120
+ endpoint: context.endpoint,
4121
+ table: context.table,
4122
+ functionName: context.functionName,
4123
+ sql: context.sql,
4124
+ payload: context.payload,
4125
+ options: context.options,
4126
+ callsite,
4127
+ thrownError: error
4128
+ });
4129
+ }
4130
+ };
4131
+ }
4132
+ async function executeWithQueryTrace(tracer, context, runner, callsiteOverride) {
4133
+ if (!tracer) {
4134
+ return runner();
4135
+ }
4136
+ const callsite = callsiteOverride ?? tracer.captureCallsite();
4137
+ const startedAt = Date.now();
4138
+ try {
4139
+ const result = await runner();
4140
+ tracer.publishSuccess(context, result, Date.now() - startedAt, callsite);
4141
+ return result;
4142
+ } catch (error) {
4143
+ tracer.publishFailure(context, error, Date.now() - startedAt, callsite);
4144
+ throw error;
4145
+ }
4146
+ }
2814
4147
  function toSingleResult(response) {
2815
4148
  const payload = response.data;
2816
4149
  const singleData = Array.isArray(payload) ? payload.length ? payload[0] : null : payload ?? null;
@@ -2831,15 +4164,16 @@ function asAthenaJsonObject(value) {
2831
4164
  function asAthenaJsonObjectArray(values) {
2832
4165
  return values;
2833
4166
  }
2834
- function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
4167
+ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS, tracer, initialCallsite) {
2835
4168
  let selectedColumns = defaultColumns === null ? void 0 : defaultColumns;
2836
4169
  let selectedOptions;
2837
4170
  let promise = null;
2838
- const run = (columns, options) => {
4171
+ const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
4172
+ const run = (columns, options, callsite) => {
2839
4173
  const payloadColumns = columns ?? selectedColumns;
2840
4174
  const payloadOptions = options ?? selectedOptions;
2841
4175
  if (!promise) {
2842
- promise = executor(payloadColumns, payloadOptions);
4176
+ promise = executor(payloadColumns, payloadOptions, callsiteStore.resolve(callsite));
2843
4177
  }
2844
4178
  return promise;
2845
4179
  };
@@ -2847,7 +4181,7 @@ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
2847
4181
  select(columns = selectedColumns, options) {
2848
4182
  selectedColumns = columns;
2849
4183
  selectedOptions = options ?? selectedOptions;
2850
- return run(columns, options);
4184
+ return run(columns, options, captureTraceCallsite(tracer));
2851
4185
  },
2852
4186
  returning(columns = selectedColumns, options) {
2853
4187
  return mutationQuery.select(columns, options);
@@ -2855,7 +4189,7 @@ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
2855
4189
  single(columns = selectedColumns, options) {
2856
4190
  selectedColumns = columns;
2857
4191
  selectedOptions = options ?? selectedOptions;
2858
- return run(columns, options).then(toSingleResult);
4192
+ return run(columns, options, captureTraceCallsite(tracer)).then(toSingleResult);
2859
4193
  },
2860
4194
  maybeSingle(columns = selectedColumns, options) {
2861
4195
  return mutationQuery.single(columns, options);
@@ -2878,21 +4212,12 @@ function getResourceId(state) {
2878
4212
  );
2879
4213
  return candidate?.value?.toString();
2880
4214
  }
2881
- function stringifyFilterValue(value) {
4215
+ function stringifyFilterValue2(value) {
2882
4216
  if (Array.isArray(value)) {
2883
4217
  return value.join(",");
2884
4218
  }
2885
4219
  return String(value);
2886
4220
  }
2887
- function isUuidString(value) {
2888
- return UUID_PATTERN.test(value.trim());
2889
- }
2890
- function isUuidIdentifierColumn(column) {
2891
- return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
2892
- }
2893
- function shouldUseUuidTextComparison(column, value) {
2894
- return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
2895
- }
2896
4221
  function normalizeCast(cast) {
2897
4222
  const normalized = cast.trim().toLowerCase();
2898
4223
  if (!SAFE_CAST_PATTERN.test(normalized)) {
@@ -2915,25 +4240,92 @@ function withCast(expression, cast) {
2915
4240
  }
2916
4241
  function buildSelectColumnsClause(columns) {
2917
4242
  if (Array.isArray(columns)) {
2918
- return columns.map((column) => quoteQualifiedIdentifier(column)).join(", ");
4243
+ return columns.map((column) => quoteSelectColumnToken(column)).join(", ");
2919
4244
  }
2920
4245
  return quoteSelectColumnsExpression(columns);
2921
4246
  }
4247
+ function parseIdentifierSegment(input) {
4248
+ const trimmed = input.trim();
4249
+ if (!trimmed) return null;
4250
+ if (!trimmed.startsWith('"')) {
4251
+ return {
4252
+ normalizedValue: trimmed.toLowerCase()
4253
+ };
4254
+ }
4255
+ let value = "";
4256
+ let index = 1;
4257
+ let closed = false;
4258
+ while (index < trimmed.length) {
4259
+ const char = trimmed[index];
4260
+ const next = index + 1 < trimmed.length ? trimmed[index + 1] : "";
4261
+ if (char === '"' && next === '"') {
4262
+ value += '"';
4263
+ index += 2;
4264
+ continue;
4265
+ }
4266
+ if (char === '"') {
4267
+ closed = true;
4268
+ index += 1;
4269
+ break;
4270
+ }
4271
+ value += char;
4272
+ index += 1;
4273
+ }
4274
+ if (!closed || trimmed.slice(index).trim().length > 0 || !value.trim()) {
4275
+ return null;
4276
+ }
4277
+ return {
4278
+ normalizedValue: value
4279
+ };
4280
+ }
4281
+ function splitQualifiedTableName(tableName) {
4282
+ const trimmed = tableName.trim();
4283
+ let inQuotes = false;
4284
+ for (let index = 0; index < trimmed.length; index += 1) {
4285
+ const char = trimmed[index];
4286
+ const next = index + 1 < trimmed.length ? trimmed[index + 1] : "";
4287
+ if (char === '"') {
4288
+ if (inQuotes && next === '"') {
4289
+ index += 1;
4290
+ continue;
4291
+ }
4292
+ inQuotes = !inQuotes;
4293
+ continue;
4294
+ }
4295
+ if (char === "." && !inQuotes) {
4296
+ const schemaSegment = trimmed.slice(0, index).trim();
4297
+ const tableSegment = trimmed.slice(index + 1).trim();
4298
+ if (!schemaSegment || !tableSegment) {
4299
+ return null;
4300
+ }
4301
+ return { schemaSegment };
4302
+ }
4303
+ }
4304
+ return null;
4305
+ }
2922
4306
  function resolveTableNameForCall(tableName, schema) {
2923
4307
  if (!schema) return tableName;
2924
4308
  const normalizedSchema = schema.trim();
2925
4309
  if (!normalizedSchema) {
2926
4310
  throw new Error("schema option must be a non-empty string");
2927
4311
  }
2928
- if (tableName.includes(".")) {
2929
- if (tableName.startsWith(`${normalizedSchema}.`)) {
2930
- return tableName;
4312
+ const normalizedTableName = tableName.trim();
4313
+ const parsedSchema = parseIdentifierSegment(normalizedSchema);
4314
+ if (!parsedSchema) {
4315
+ throw new Error("schema option must be a non-empty string");
4316
+ }
4317
+ const qualified = splitQualifiedTableName(normalizedTableName);
4318
+ if (qualified) {
4319
+ const parsedTableSchema = parseIdentifierSegment(qualified.schemaSegment);
4320
+ const sameSchema = parsedTableSchema ? parsedTableSchema.normalizedValue === parsedSchema.normalizedValue : normalizedTableName.startsWith(`${normalizedSchema}.`);
4321
+ if (sameSchema) {
4322
+ return normalizedTableName;
2931
4323
  }
2932
4324
  throw new Error(
2933
- `schema option "${normalizedSchema}" conflicts with schema-qualified table "${tableName}"`
4325
+ `schema option "${normalizedSchema}" conflicts with schema-qualified table "${normalizedTableName}"`
2934
4326
  );
2935
4327
  }
2936
- return `${normalizedSchema}.${tableName}`;
4328
+ return `${normalizedSchema}.${normalizedTableName}`;
2937
4329
  }
2938
4330
  function conditionToSqlClause(condition) {
2939
4331
  if (!condition.column) return null;
@@ -3011,6 +4403,226 @@ function buildTypedSelectQuery(input) {
3011
4403
  }
3012
4404
  return `${sqlParts.join(" ")};`;
3013
4405
  }
4406
+ function sanitizeSqlComment(comment) {
4407
+ return comment.replace(/\*\//g, "* /");
4408
+ }
4409
+ function toSqlJsonLiteral(value) {
4410
+ if (value === void 0) return "DEFAULT";
4411
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
4412
+ return toSqlLiteral(value);
4413
+ }
4414
+ return `'${escapeSqlStringLiteral(JSON.stringify(value))}'::jsonb`;
4415
+ }
4416
+ function conditionToDebugSqlClause(condition) {
4417
+ const exact = conditionToSqlClause(condition);
4418
+ if (exact) return exact;
4419
+ const rawCondition = sanitizeSqlComment(JSON.stringify(condition));
4420
+ if (!condition.column) {
4421
+ return `TRUE /* unsupported condition: ${rawCondition} */`;
4422
+ }
4423
+ const column = withCast(quoteQualifiedIdentifier(condition.column), condition.column_cast);
4424
+ const value = condition.value;
4425
+ const rhs = withCast(toSqlJsonLiteral(value), condition.value_cast);
4426
+ switch (condition.operator) {
4427
+ case "contains":
4428
+ return `${column} @> ${rhs}`;
4429
+ case "containedBy":
4430
+ return `${column} <@ ${rhs}`;
4431
+ case "not":
4432
+ return `TRUE /* NOT expression passthrough: ${rawCondition} */`;
4433
+ case "or":
4434
+ return `TRUE /* OR expression passthrough: ${rawCondition} */`;
4435
+ default:
4436
+ return `TRUE /* unsupported condition: ${rawCondition} */`;
4437
+ }
4438
+ }
4439
+ function appendOrderLimitOffset(sqlParts, order, limit, offset) {
4440
+ if (order?.field) {
4441
+ const direction = order.direction === "descending" ? "DESC" : "ASC";
4442
+ sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(order.field)} ${direction}`);
4443
+ }
4444
+ if (limit !== void 0) {
4445
+ sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(limit))}`);
4446
+ }
4447
+ if (offset !== void 0) {
4448
+ sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(offset))}`);
4449
+ }
4450
+ }
4451
+ function buildDebugSelectQuery(input) {
4452
+ const sqlParts = [
4453
+ `SELECT ${buildSelectColumnsClause(input.columns)} FROM ${quoteQualifiedIdentifier(input.tableName)}`
4454
+ ];
4455
+ if (input.conditions?.length) {
4456
+ const whereClauses = input.conditions.map(conditionToDebugSqlClause);
4457
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
4458
+ }
4459
+ const pagination = resolvePagination(input);
4460
+ appendOrderLimitOffset(sqlParts, input.order, pagination.limit, pagination.offset);
4461
+ return `${sqlParts.join(" ")};`;
4462
+ }
4463
+ function resolveDebugTableIdentifier(tableName) {
4464
+ if (!tableName?.trim()) {
4465
+ return '"__unknown_table__"';
4466
+ }
4467
+ return quoteQualifiedIdentifier(tableName);
4468
+ }
4469
+ function buildInsertDebugSql(payload) {
4470
+ const rows = Array.isArray(payload.insert_body) ? payload.insert_body : [payload.insert_body];
4471
+ const columns = [];
4472
+ const seen = /* @__PURE__ */ new Set();
4473
+ for (const row of rows) {
4474
+ for (const column of Object.keys(row)) {
4475
+ if (seen.has(column)) continue;
4476
+ seen.add(column);
4477
+ columns.push(column);
4478
+ }
4479
+ }
4480
+ const sqlParts = [`INSERT INTO ${quoteQualifiedIdentifier(payload.table_name)}`];
4481
+ if (!rows.length || !columns.length) {
4482
+ sqlParts.push("DEFAULT VALUES");
4483
+ if (rows.length > 1) {
4484
+ sqlParts.push(`/* trace: ${rows.length} rows collapsed to DEFAULT VALUES */`);
4485
+ }
4486
+ } else {
4487
+ const valuesClause = rows.map((row) => {
4488
+ const values = columns.map((column) => {
4489
+ const hasColumn = Object.prototype.hasOwnProperty.call(row, column);
4490
+ if (!hasColumn) {
4491
+ return payload.default_to_null ? "NULL" : "DEFAULT";
4492
+ }
4493
+ const rowValue = row[column];
4494
+ return toSqlJsonLiteral(rowValue);
4495
+ });
4496
+ return `(${values.join(", ")})`;
4497
+ }).join(", ");
4498
+ const columnClause = columns.map((column) => quoteQualifiedIdentifier(column)).join(", ");
4499
+ sqlParts.push(`(${columnClause})`);
4500
+ sqlParts.push(`VALUES ${valuesClause}`);
4501
+ }
4502
+ if (payload.on_conflict) {
4503
+ const conflictColumns = Array.isArray(payload.on_conflict) ? payload.on_conflict.map((column) => quoteQualifiedIdentifier(column)).join(", ") : payload.on_conflict;
4504
+ if (payload.update_body && Object.keys(payload.update_body).length > 0) {
4505
+ const assignments = Object.entries(payload.update_body).map(
4506
+ ([column, value]) => `${quoteQualifiedIdentifier(column)} = ${toSqlJsonLiteral(value)}`
4507
+ );
4508
+ sqlParts.push(`ON CONFLICT (${conflictColumns}) DO UPDATE SET ${assignments.join(", ")}`);
4509
+ } else {
4510
+ sqlParts.push(`ON CONFLICT (${conflictColumns}) DO NOTHING`);
4511
+ }
4512
+ }
4513
+ if (payload.columns) {
4514
+ sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
4515
+ }
4516
+ return `${sqlParts.join(" ")};`;
4517
+ }
4518
+ function buildUpdateDebugSql(payload) {
4519
+ const set = payload.set ?? payload.data ?? {};
4520
+ const assignments = Object.entries(set).map(
4521
+ ([column, value]) => `${quoteQualifiedIdentifier(column)} = ${toSqlJsonLiteral(value)}`
4522
+ );
4523
+ const sqlParts = [
4524
+ `UPDATE ${resolveDebugTableIdentifier(payload.table_name)} SET ${assignments.length ? assignments.join(", ") : "/* empty set */"}`
4525
+ ];
4526
+ if (payload.conditions?.length) {
4527
+ const whereClauses = payload.conditions.map(conditionToDebugSqlClause);
4528
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
4529
+ }
4530
+ const pagination = resolvePagination({
4531
+ currentPage: payload.current_page,
4532
+ pageSize: payload.page_size
4533
+ });
4534
+ appendOrderLimitOffset(sqlParts, payload.sort_by, pagination.limit, pagination.offset);
4535
+ if (payload.columns) {
4536
+ sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
4537
+ }
4538
+ return `${sqlParts.join(" ")};`;
4539
+ }
4540
+ function buildDeleteDebugSql(payload) {
4541
+ const sqlParts = [`DELETE FROM ${quoteQualifiedIdentifier(payload.table_name)}`];
4542
+ const whereClauses = [];
4543
+ if (payload.resource_id) {
4544
+ whereClauses.push(`"resource_id" = ${toSqlLiteral(payload.resource_id)}`);
4545
+ }
4546
+ if (payload.conditions?.length) {
4547
+ whereClauses.push(...payload.conditions.map(conditionToDebugSqlClause));
4548
+ }
4549
+ if (whereClauses.length) {
4550
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
4551
+ }
4552
+ const pagination = resolvePagination({
4553
+ currentPage: payload.current_page,
4554
+ pageSize: payload.page_size
4555
+ });
4556
+ appendOrderLimitOffset(sqlParts, payload.sort_by, pagination.limit, pagination.offset);
4557
+ if (payload.columns) {
4558
+ sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
4559
+ }
4560
+ return `${sqlParts.join(" ")};`;
4561
+ }
4562
+ function rpcFilterToSqlClause(filter) {
4563
+ const column = quoteQualifiedIdentifier(filter.column);
4564
+ const value = filter.value;
4565
+ switch (filter.operator) {
4566
+ case "eq":
4567
+ case "neq":
4568
+ case "gt":
4569
+ case "gte":
4570
+ case "lt":
4571
+ case "lte":
4572
+ case "like":
4573
+ case "ilike": {
4574
+ if (value === void 0 || Array.isArray(value)) {
4575
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
4576
+ }
4577
+ const operatorMap = {
4578
+ eq: "=",
4579
+ neq: "!=",
4580
+ gt: ">",
4581
+ gte: ">=",
4582
+ lt: "<",
4583
+ lte: "<=",
4584
+ like: "LIKE",
4585
+ ilike: "ILIKE"
4586
+ };
4587
+ return `${column} ${operatorMap[filter.operator]} ${toSqlLiteral(value)}`;
4588
+ }
4589
+ case "is":
4590
+ if (value === null) return `${column} IS NULL`;
4591
+ if (value === true) return `${column} IS TRUE`;
4592
+ if (value === false) return `${column} IS FALSE`;
4593
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
4594
+ case "in":
4595
+ if (!Array.isArray(value)) {
4596
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
4597
+ }
4598
+ if (value.length === 0) return "FALSE";
4599
+ return `${column} IN (${value.map((item) => toSqlLiteral(item)).join(", ")})`;
4600
+ default:
4601
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
4602
+ }
4603
+ }
4604
+ function buildRpcDebugSql(payload) {
4605
+ const argsEntries = payload.args ? Object.entries(payload.args) : [];
4606
+ const argsClause = argsEntries.map(([key, value]) => `${quoteQualifiedIdentifier(key)} => ${toSqlJsonLiteral(value)}`).join(", ");
4607
+ const functionRef = payload.schema ? `${quoteQualifiedIdentifier(payload.schema)}.${quoteQualifiedIdentifier(payload.function)}` : quoteQualifiedIdentifier(payload.function);
4608
+ const sqlParts = [
4609
+ `SELECT ${payload.select ? quoteSelectColumnsExpression(payload.select) : "*"} FROM ${functionRef}(${argsClause})`
4610
+ ];
4611
+ if (payload.filters?.length) {
4612
+ sqlParts.push(`WHERE ${payload.filters.map(rpcFilterToSqlClause).join(" AND ")}`);
4613
+ }
4614
+ if (payload.order?.column) {
4615
+ const direction = payload.order.ascending === false ? "DESC" : "ASC";
4616
+ sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(payload.order.column)} ${direction}`);
4617
+ }
4618
+ if (payload.limit !== void 0) {
4619
+ sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(payload.limit))}`);
4620
+ }
4621
+ if (payload.offset !== void 0) {
4622
+ sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(payload.offset))}`);
4623
+ }
4624
+ return `${sqlParts.join(" ")};`;
4625
+ }
3014
4626
  function createFilterMethods(state, addCondition, self) {
3015
4627
  return {
3016
4628
  eq(column, value) {
@@ -3122,7 +4734,7 @@ function createFilterMethods(state, addCondition, self) {
3122
4734
  not(columnOrExpression, operator, value) {
3123
4735
  const expression = String(columnOrExpression);
3124
4736
  if (operator != null && value !== void 0) {
3125
- addCondition("not", void 0, `${expression}.${operator}.${stringifyFilterValue(value)}`);
4737
+ addCondition("not", void 0, `${expression}.${operator}.${stringifyFilterValue2(value)}`);
3126
4738
  } else {
3127
4739
  addCondition("not", void 0, expression);
3128
4740
  }
@@ -3185,14 +4797,15 @@ function createRpcFilterMethods(filters, self) {
3185
4797
  }
3186
4798
  };
3187
4799
  }
3188
- function createRpcBuilder(functionName, args, baseOptions, client, formatGatewayResult) {
4800
+ function createRpcBuilder(functionName, args, baseOptions, client, formatGatewayResult, tracer, initialCallsite) {
3189
4801
  const state = {
3190
4802
  filters: []
3191
4803
  };
3192
4804
  let selectedColumns;
3193
4805
  let selectedOptions;
3194
4806
  let promise = null;
3195
- const executeRpc = async (columns, options) => {
4807
+ const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
4808
+ const executeRpc = async (columns, options, callsite) => {
3196
4809
  const mergedOptions = mergeOptions(baseOptions, options);
3197
4810
  const payload = {
3198
4811
  function: functionName,
@@ -3206,14 +4819,30 @@ function createRpcBuilder(functionName, args, baseOptions, client, formatGateway
3206
4819
  offset: state.offset,
3207
4820
  order: state.order
3208
4821
  };
3209
- const response = await client.rpcGateway(payload, mergedOptions);
3210
- return formatGatewayResult(response, { operation: "rpc" });
4822
+ const endpoint = mergedOptions?.get ? `/rpc/${functionName}` : "/gateway/rpc";
4823
+ const sql = buildRpcDebugSql(payload);
4824
+ return executeWithQueryTrace(
4825
+ tracer,
4826
+ {
4827
+ operation: "rpc",
4828
+ endpoint,
4829
+ functionName,
4830
+ sql,
4831
+ payload,
4832
+ options: mergedOptions
4833
+ },
4834
+ async () => {
4835
+ const response = await client.rpcGateway(payload, mergedOptions);
4836
+ return formatGatewayResult(response, { operation: "rpc" });
4837
+ },
4838
+ callsite
4839
+ );
3211
4840
  };
3212
- const run = (columns, options) => {
4841
+ const run = (columns, options, callsite) => {
3213
4842
  const payloadColumns = columns ?? selectedColumns;
3214
4843
  const payloadOptions = options ?? selectedOptions;
3215
4844
  if (!promise) {
3216
- promise = executeRpc(payloadColumns, payloadOptions);
4845
+ promise = executeRpc(payloadColumns, payloadOptions, callsiteStore.resolve(callsite));
3217
4846
  }
3218
4847
  return promise;
3219
4848
  };
@@ -3223,10 +4852,10 @@ function createRpcBuilder(functionName, args, baseOptions, client, formatGateway
3223
4852
  select(columns = selectedColumns, options) {
3224
4853
  selectedColumns = columns;
3225
4854
  selectedOptions = options ?? selectedOptions;
3226
- return run(columns, options);
4855
+ return run(columns, options, captureTraceCallsite(tracer));
3227
4856
  },
3228
4857
  async single(columns, options) {
3229
- const result = await run(columns, options);
4858
+ const result = await run(columns, options, captureTraceCallsite(tracer));
3230
4859
  return toSingleResult(result);
3231
4860
  },
3232
4861
  maybeSingle(columns, options) {
@@ -3261,7 +4890,7 @@ function createRpcBuilder(functionName, args, baseOptions, client, formatGateway
3261
4890
  });
3262
4891
  return builder;
3263
4892
  }
3264
- function createTableBuilder(tableName, client, formatGatewayResult) {
4893
+ function createTableBuilder(tableName, client, formatGatewayResult, tracer, experimental) {
3265
4894
  const state = {
3266
4895
  conditions: []
3267
4896
  };
@@ -3293,70 +4922,103 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
3293
4922
  }
3294
4923
  state.conditions.push(condition);
3295
4924
  };
4925
+ const snapshotState = () => ({
4926
+ conditions: state.conditions.map((condition) => ({ ...condition })),
4927
+ limit: state.limit,
4928
+ offset: state.offset,
4929
+ order: state.order ? { ...state.order } : void 0,
4930
+ currentPage: state.currentPage,
4931
+ pageSize: state.pageSize,
4932
+ totalPages: state.totalPages
4933
+ });
3296
4934
  const builder = {};
3297
4935
  const filterMethods = createFilterMethods(
3298
4936
  state,
3299
4937
  addCondition,
3300
4938
  builder
3301
4939
  );
3302
- const runSelect = async (columns = DEFAULT_COLUMNS, options) => {
4940
+ const runSelect = async (columns = DEFAULT_COLUMNS, options, executionState = snapshotState(), callsite) => {
3303
4941
  const resolvedTableName = resolveTableNameForCall(tableName, options?.schema);
3304
- const conditions = state.conditions.length ? state.conditions.map((condition) => ({ ...condition })) : void 0;
3305
- const hasTypedEqualityComparison = conditions?.some(
3306
- (condition) => condition.operator === "eq" && (condition.value_cast !== void 0 || condition.column_cast !== void 0)
3307
- ) ?? false;
3308
- if (hasTypedEqualityComparison && !options?.head && !options?.count && conditions) {
3309
- const query = buildTypedSelectQuery({
3310
- tableName: resolvedTableName,
3311
- columns,
3312
- conditions,
3313
- limit: state.limit,
3314
- offset: state.offset,
3315
- currentPage: state.currentPage,
3316
- pageSize: state.pageSize,
3317
- order: state.order
3318
- });
3319
- if (query) {
3320
- const queryResponse = await client.queryGateway({ query }, options);
3321
- return formatGatewayResult(queryResponse, { table: resolvedTableName, operation: "select" });
3322
- }
3323
- }
3324
- const payload = {
3325
- table_name: resolvedTableName,
4942
+ const plan = createSelectTransportPlan({
4943
+ tableName: resolvedTableName,
3326
4944
  columns,
3327
- conditions,
3328
- limit: state.limit,
3329
- offset: state.offset,
3330
- current_page: state.currentPage,
3331
- page_size: state.pageSize,
3332
- total_pages: state.totalPages,
3333
- sort_by: state.order,
3334
- strip_nulls: options?.stripNulls ?? true,
3335
- count: options?.count,
3336
- head: options?.head
3337
- };
3338
- const response = await client.fetchGateway(payload, options);
3339
- return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
4945
+ state: executionState,
4946
+ options,
4947
+ buildTypedSelectQuery
4948
+ });
4949
+ if (plan.kind === "query") {
4950
+ return executeWithQueryTrace(
4951
+ tracer,
4952
+ {
4953
+ operation: "select",
4954
+ endpoint: "/gateway/query",
4955
+ table: resolvedTableName,
4956
+ sql: plan.query,
4957
+ payload: plan.payload,
4958
+ options
4959
+ },
4960
+ async () => {
4961
+ const queryResponse = await client.queryGateway(plan.payload, options);
4962
+ return formatGatewayResult(queryResponse, { table: resolvedTableName, operation: "select" });
4963
+ },
4964
+ callsite
4965
+ );
4966
+ }
4967
+ const sql = buildDebugSelectQuery({
4968
+ tableName: resolvedTableName,
4969
+ ...plan.debug
4970
+ });
4971
+ return executeWithQueryTrace(
4972
+ tracer,
4973
+ {
4974
+ operation: "select",
4975
+ endpoint: "/gateway/fetch",
4976
+ table: resolvedTableName,
4977
+ sql,
4978
+ payload: plan.payload,
4979
+ options
4980
+ },
4981
+ async () => {
4982
+ const response = await client.fetchGateway(plan.payload, options);
4983
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
4984
+ },
4985
+ callsite
4986
+ );
3340
4987
  };
3341
- const createSelectChain = (columns, options) => {
4988
+ const createSelectChain = (columns, options, initialCallsite) => {
3342
4989
  const chain = {};
4990
+ const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
3343
4991
  const filterMethods2 = createFilterMethods(state, addCondition, chain);
3344
4992
  Object.assign(chain, filterMethods2, {
3345
4993
  async single(cols, opts) {
3346
- const r = await runSelect(cols ?? columns, opts ?? options);
4994
+ const r = await runSelect(
4995
+ cols ?? columns,
4996
+ opts ?? options,
4997
+ snapshotState(),
4998
+ callsiteStore.resolve(captureTraceCallsite(tracer))
4999
+ );
3347
5000
  return toSingleResult(r);
3348
5001
  },
3349
5002
  maybeSingle(cols, opts) {
3350
5003
  return chain.single(cols, opts);
3351
5004
  },
3352
5005
  then(onfulfilled, onrejected) {
3353
- return runSelect(columns, options).then(onfulfilled, onrejected);
5006
+ return runSelect(
5007
+ columns,
5008
+ options,
5009
+ snapshotState(),
5010
+ callsiteStore.resolve()
5011
+ ).then(onfulfilled, onrejected);
3354
5012
  },
3355
5013
  catch(onrejected) {
3356
- return runSelect(columns, options).catch(onrejected);
5014
+ return runSelect(columns, options, snapshotState(), callsiteStore.resolve()).catch(
5015
+ onrejected
5016
+ );
3357
5017
  },
3358
5018
  finally(onfinally) {
3359
- return runSelect(columns, options).finally(onfinally);
5019
+ return runSelect(columns, options, snapshotState(), callsiteStore.resolve()).finally(
5020
+ onfinally
5021
+ );
3360
5022
  }
3361
5023
  });
3362
5024
  return chain;
@@ -3373,11 +5035,75 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
3373
5035
  return builder;
3374
5036
  },
3375
5037
  select(columns = DEFAULT_COLUMNS, options) {
3376
- return createSelectChain(columns, options);
5038
+ return createSelectChain(columns, options, captureTraceCallsite(tracer));
5039
+ },
5040
+ async findMany(options) {
5041
+ const columns = compileSelectShape(options.select);
5042
+ const baseState = snapshotState();
5043
+ const executionState = snapshotState();
5044
+ const callsite = captureTraceCallsite(tracer);
5045
+ const compiledWhere = compileWhere(options.where);
5046
+ if (compiledWhere?.length) {
5047
+ executionState.conditions.push(...compiledWhere);
5048
+ }
5049
+ if (options.orderBy !== void 0) {
5050
+ executionState.order = compileOrderBy(options.orderBy);
5051
+ }
5052
+ if (options.limit !== void 0) {
5053
+ executionState.limit = options.limit;
5054
+ }
5055
+ if (experimental?.findManyAst && canUseFindManyAstTransport(baseState) && !selectShapeUsesRelationSchema(options.select)) {
5056
+ const resolvedTableName = resolveTableNameForCall(tableName, void 0);
5057
+ const payload = {
5058
+ table_name: resolvedTableName,
5059
+ select: options.select
5060
+ };
5061
+ if (options.where !== void 0) {
5062
+ payload.where = options.where;
5063
+ }
5064
+ const astOrder = toFindManyAstOrder(executionState.order);
5065
+ if (astOrder !== void 0) {
5066
+ payload.orderBy = astOrder;
5067
+ }
5068
+ if (executionState.limit !== void 0) {
5069
+ payload.limit = executionState.limit;
5070
+ }
5071
+ const sql = buildDebugSelectQuery({
5072
+ tableName: resolvedTableName,
5073
+ columns,
5074
+ conditions: executionState.conditions,
5075
+ limit: executionState.limit,
5076
+ order: executionState.order
5077
+ });
5078
+ return executeWithQueryTrace(
5079
+ tracer,
5080
+ {
5081
+ operation: "select",
5082
+ endpoint: "/gateway/fetch",
5083
+ table: resolvedTableName,
5084
+ sql,
5085
+ payload
5086
+ },
5087
+ async () => {
5088
+ const response = await client.fetchGateway(
5089
+ payload
5090
+ );
5091
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
5092
+ },
5093
+ callsite
5094
+ );
5095
+ }
5096
+ return runSelect(
5097
+ columns,
5098
+ void 0,
5099
+ executionState,
5100
+ callsite
5101
+ );
3377
5102
  },
3378
5103
  insert(values, options) {
5104
+ const mutationCallsite = captureTraceCallsite(tracer);
3379
5105
  if (Array.isArray(values)) {
3380
- const executeInsertMany = async (columns, selectOptions) => {
5106
+ const executeInsertMany = async (columns, selectOptions, callsite) => {
3381
5107
  const mergedOptions = mergeOptions(options, selectOptions);
3382
5108
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
3383
5109
  const payload = {
@@ -3390,12 +5116,27 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
3390
5116
  if (mergedOptions?.defaultToNull !== void 0) {
3391
5117
  payload.default_to_null = mergedOptions.defaultToNull;
3392
5118
  }
3393
- const response = await client.insertGateway(payload, mergedOptions);
3394
- return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
5119
+ const sql = buildInsertDebugSql(payload);
5120
+ return executeWithQueryTrace(
5121
+ tracer,
5122
+ {
5123
+ operation: "insert",
5124
+ endpoint: "/gateway/insert",
5125
+ table: resolvedTableName,
5126
+ sql,
5127
+ payload,
5128
+ options: mergedOptions
5129
+ },
5130
+ async () => {
5131
+ const response = await client.insertGateway(payload, mergedOptions);
5132
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
5133
+ },
5134
+ callsite
5135
+ );
3395
5136
  };
3396
- return createMutationQuery(executeInsertMany);
5137
+ return createMutationQuery(executeInsertMany, DEFAULT_COLUMNS, tracer, mutationCallsite);
3397
5138
  }
3398
- const executeInsertOne = async (columns, selectOptions) => {
5139
+ const executeInsertOne = async (columns, selectOptions, callsite) => {
3399
5140
  const mergedOptions = mergeOptions(options, selectOptions);
3400
5141
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
3401
5142
  const payload = {
@@ -3408,14 +5149,30 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
3408
5149
  if (mergedOptions?.defaultToNull !== void 0) {
3409
5150
  payload.default_to_null = mergedOptions.defaultToNull;
3410
5151
  }
3411
- const response = await client.insertGateway(payload, mergedOptions);
3412
- return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
5152
+ const sql = buildInsertDebugSql(payload);
5153
+ return executeWithQueryTrace(
5154
+ tracer,
5155
+ {
5156
+ operation: "insert",
5157
+ endpoint: "/gateway/insert",
5158
+ table: resolvedTableName,
5159
+ sql,
5160
+ payload,
5161
+ options: mergedOptions
5162
+ },
5163
+ async () => {
5164
+ const response = await client.insertGateway(payload, mergedOptions);
5165
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
5166
+ },
5167
+ callsite
5168
+ );
3413
5169
  };
3414
- return createMutationQuery(executeInsertOne);
5170
+ return createMutationQuery(executeInsertOne, DEFAULT_COLUMNS, tracer, mutationCallsite);
3415
5171
  },
3416
5172
  upsert(values, options) {
5173
+ const mutationCallsite = captureTraceCallsite(tracer);
3417
5174
  if (Array.isArray(values)) {
3418
- const executeUpsertMany = async (columns, selectOptions) => {
5175
+ const executeUpsertMany = async (columns, selectOptions, callsite) => {
3419
5176
  const mergedOptions = mergeOptions(options, selectOptions);
3420
5177
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
3421
5178
  const payload = {
@@ -3430,12 +5187,27 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
3430
5187
  if (mergedOptions?.defaultToNull !== void 0) {
3431
5188
  payload.default_to_null = mergedOptions.defaultToNull;
3432
5189
  }
3433
- const response = await client.insertGateway(payload, mergedOptions);
3434
- return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
5190
+ const sql = buildInsertDebugSql(payload);
5191
+ return executeWithQueryTrace(
5192
+ tracer,
5193
+ {
5194
+ operation: "upsert",
5195
+ endpoint: "/gateway/insert",
5196
+ table: resolvedTableName,
5197
+ sql,
5198
+ payload,
5199
+ options: mergedOptions
5200
+ },
5201
+ async () => {
5202
+ const response = await client.insertGateway(payload, mergedOptions);
5203
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
5204
+ },
5205
+ callsite
5206
+ );
3435
5207
  };
3436
- return createMutationQuery(executeUpsertMany);
5208
+ return createMutationQuery(executeUpsertMany, DEFAULT_COLUMNS, tracer, mutationCallsite);
3437
5209
  }
3438
- const executeUpsertOne = async (columns, selectOptions) => {
5210
+ const executeUpsertOne = async (columns, selectOptions, callsite) => {
3439
5211
  const mergedOptions = mergeOptions(options, selectOptions);
3440
5212
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
3441
5213
  const payload = {
@@ -3450,13 +5222,29 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
3450
5222
  if (mergedOptions?.defaultToNull !== void 0) {
3451
5223
  payload.default_to_null = mergedOptions.defaultToNull;
3452
5224
  }
3453
- const response = await client.insertGateway(payload, mergedOptions);
3454
- return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
5225
+ const sql = buildInsertDebugSql(payload);
5226
+ return executeWithQueryTrace(
5227
+ tracer,
5228
+ {
5229
+ operation: "upsert",
5230
+ endpoint: "/gateway/insert",
5231
+ table: resolvedTableName,
5232
+ sql,
5233
+ payload,
5234
+ options: mergedOptions
5235
+ },
5236
+ async () => {
5237
+ const response = await client.insertGateway(payload, mergedOptions);
5238
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
5239
+ },
5240
+ callsite
5241
+ );
3455
5242
  };
3456
- return createMutationQuery(executeUpsertOne);
5243
+ return createMutationQuery(executeUpsertOne, DEFAULT_COLUMNS, tracer, mutationCallsite);
3457
5244
  },
3458
5245
  update(values, options) {
3459
- const executeUpdate = async (columns, selectOptions) => {
5246
+ const mutationCallsite = captureTraceCallsite(tracer);
5247
+ const executeUpdate = async (columns, selectOptions, callsite) => {
3460
5248
  const filters = state.conditions.length ? [...state.conditions] : void 0;
3461
5249
  const mergedOptions = mergeOptions(options, selectOptions);
3462
5250
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
@@ -3471,10 +5259,25 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
3471
5259
  if (state.pageSize !== void 0) payload.page_size = state.pageSize;
3472
5260
  if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
3473
5261
  if (columns) payload.columns = columns;
3474
- const response = await client.updateGateway(payload, mergedOptions);
3475
- return formatGatewayResult(response, { table: resolvedTableName, operation: "update" });
5262
+ const sql = buildUpdateDebugSql(payload);
5263
+ return executeWithQueryTrace(
5264
+ tracer,
5265
+ {
5266
+ operation: "update",
5267
+ endpoint: "/gateway/update",
5268
+ table: resolvedTableName,
5269
+ sql,
5270
+ payload,
5271
+ options: mergedOptions
5272
+ },
5273
+ async () => {
5274
+ const response = await client.updateGateway(payload, mergedOptions);
5275
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "update" });
5276
+ },
5277
+ callsite
5278
+ );
3476
5279
  };
3477
- const mutation = createMutationQuery(executeUpdate, null);
5280
+ const mutation = createMutationQuery(executeUpdate, null, tracer, mutationCallsite);
3478
5281
  const updateChain = {};
3479
5282
  const filterMethods2 = createFilterMethods(state, addCondition, updateChain);
3480
5283
  Object.assign(updateChain, filterMethods2, mutation);
@@ -3486,7 +5289,8 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
3486
5289
  if (!resourceId && !filters?.length) {
3487
5290
  throw new Error('delete requires a resource_id either via eq("resource_id", ...) or options.resourceId');
3488
5291
  }
3489
- const executeDelete = async (columns, selectOptions) => {
5292
+ const mutationCallsite = captureTraceCallsite(tracer);
5293
+ const executeDelete = async (columns, selectOptions, callsite) => {
3490
5294
  const mergedOptions = mergeOptions(options, selectOptions);
3491
5295
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
3492
5296
  const payload = {
@@ -3499,13 +5303,33 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
3499
5303
  if (state.pageSize !== void 0) payload.page_size = state.pageSize;
3500
5304
  if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
3501
5305
  if (columns) payload.columns = columns;
3502
- const response = await client.deleteGateway(payload, mergedOptions);
3503
- return formatGatewayResult(response, { table: resolvedTableName, operation: "delete" });
5306
+ const sql = buildDeleteDebugSql(payload);
5307
+ return executeWithQueryTrace(
5308
+ tracer,
5309
+ {
5310
+ operation: "delete",
5311
+ endpoint: "/gateway/delete",
5312
+ table: resolvedTableName,
5313
+ sql,
5314
+ payload,
5315
+ options: mergedOptions
5316
+ },
5317
+ async () => {
5318
+ const response = await client.deleteGateway(payload, mergedOptions);
5319
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "delete" });
5320
+ },
5321
+ callsite
5322
+ );
3504
5323
  };
3505
- return createMutationQuery(executeDelete, null);
5324
+ return createMutationQuery(executeDelete, null, tracer, mutationCallsite);
3506
5325
  },
3507
5326
  async single(columns, options) {
3508
- const response = await builder.select(columns, options);
5327
+ const response = await runSelect(
5328
+ columns ?? DEFAULT_COLUMNS,
5329
+ options,
5330
+ snapshotState(),
5331
+ captureTraceCallsite(tracer)
5332
+ );
3509
5333
  return toSingleResult(response);
3510
5334
  },
3511
5335
  async maybeSingle(columns, options) {
@@ -3514,14 +5338,29 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
3514
5338
  });
3515
5339
  return builder;
3516
5340
  }
3517
- function createQueryBuilder(client, formatGatewayResult) {
5341
+ function createQueryBuilder(client, formatGatewayResult, tracer) {
3518
5342
  return async function query(query, options) {
3519
5343
  const normalizedQuery = query.trim();
3520
5344
  if (!normalizedQuery) {
3521
5345
  throw new Error("query requires a non-empty string");
3522
5346
  }
3523
- const response = await client.queryGateway({ query: normalizedQuery }, options);
3524
- return formatGatewayResult(response, { operation: "query" });
5347
+ const payload = { query: normalizedQuery };
5348
+ const callsite = captureTraceCallsite(tracer);
5349
+ return executeWithQueryTrace(
5350
+ tracer,
5351
+ {
5352
+ operation: "query",
5353
+ endpoint: "/gateway/query",
5354
+ sql: normalizedQuery,
5355
+ payload,
5356
+ options
5357
+ },
5358
+ async () => {
5359
+ const response = await client.queryGateway(payload, options);
5360
+ return formatGatewayResult(response, { operation: "query" });
5361
+ },
5362
+ callsite
5363
+ );
3525
5364
  };
3526
5365
  }
3527
5366
  function createClientFromConfig(config) {
@@ -3532,9 +5371,16 @@ function createClientFromConfig(config) {
3532
5371
  backend: config.backend,
3533
5372
  headers: config.headers
3534
5373
  });
3535
- const formatGatewayResult = createResultFormatter(config.experimental);
5374
+ const formatGatewayResult = createResultFormatter();
5375
+ const queryTracer = createQueryTracer(config.experimental);
3536
5376
  const auth = createAuthClient(config.auth);
3537
- const from = (table) => createTableBuilder(table, gateway, formatGatewayResult);
5377
+ const from = (table, options) => createTableBuilder(
5378
+ resolveTableNameForCall(table, options?.schema),
5379
+ gateway,
5380
+ formatGatewayResult,
5381
+ queryTracer,
5382
+ config.experimental
5383
+ );
3538
5384
  const rpc = (fn, args, options) => {
3539
5385
  const normalizedFn = fn.trim();
3540
5386
  if (!normalizedFn) {
@@ -3545,16 +5391,19 @@ function createClientFromConfig(config) {
3545
5391
  args,
3546
5392
  options,
3547
5393
  gateway,
3548
- formatGatewayResult
5394
+ formatGatewayResult,
5395
+ queryTracer,
5396
+ captureTraceCallsite(queryTracer)
3549
5397
  );
3550
5398
  };
3551
- const query = createQueryBuilder(gateway, formatGatewayResult);
5399
+ const query = createQueryBuilder(gateway, formatGatewayResult, queryTracer);
3552
5400
  const db = createDbModule({ from, rpc, query });
3553
5401
  return {
3554
5402
  from,
3555
5403
  db,
3556
5404
  rpc,
3557
5405
  query,
5406
+ verifyConnection: gateway.verifyConnection,
3558
5407
  auth: auth.auth
3559
5408
  };
3560
5409
  }
@@ -4018,7 +5867,7 @@ var AthenaGatewayCatalogClient = class {
4018
5867
  async queryRows(query) {
4019
5868
  const result = await this.client.query(query);
4020
5869
  if (result.error || result.status < 200 || result.status >= 300) {
4021
- throw new Error(result.error ?? `Gateway query failed with status ${result.status}`);
5870
+ throw new Error(result.error?.message ?? `Gateway query failed with status ${result.status}`);
4022
5871
  }
4023
5872
  return result.data ?? [];
4024
5873
  }
@@ -4115,10 +5964,24 @@ function resolveGeneratorProvider(providerConfig, experimentalFlags) {
4115
5964
  }
4116
5965
 
4117
5966
  // src/generator/pipeline.ts
5967
+ function canOverwriteArtifact(file) {
5968
+ return file.kind === "model" || file.kind === "schema";
5969
+ }
5970
+ async function fileExists(path) {
5971
+ try {
5972
+ await stat(path);
5973
+ return true;
5974
+ } catch {
5975
+ return false;
5976
+ }
5977
+ }
4118
5978
  async function writeArtifacts(files, cwd) {
4119
5979
  const writtenFiles = [];
4120
5980
  for (const file of files) {
4121
5981
  const absolutePath = resolve(cwd, file.path);
5982
+ if (!canOverwriteArtifact(file) && await fileExists(absolutePath)) {
5983
+ continue;
5984
+ }
4122
5985
  await mkdir(dirname(absolutePath), { recursive: true });
4123
5986
  await writeFile(absolutePath, file.content, "utf8");
4124
5987
  writtenFiles.push(file.path);