@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/browser.cjs CHANGED
@@ -60,8 +60,74 @@ function isAthenaGatewayError(error) {
60
60
  return error instanceof AthenaGatewayError;
61
61
  }
62
62
 
63
+ // src/gateway/url.ts
64
+ var ATHENA_DEFAULT_BASE_URL = "https://athena-db.com";
65
+ function describeReceivedValue(value) {
66
+ if (value === void 0) return "undefined";
67
+ if (value === null) return "null";
68
+ if (typeof value === "string") {
69
+ return value.trim().length > 0 ? JSON.stringify(value) : "an empty string";
70
+ }
71
+ return `${typeof value} ${JSON.stringify(value)}`;
72
+ }
73
+ function invalidBaseUrlError(message, hint) {
74
+ return new AthenaGatewayError({
75
+ code: "INVALID_URL",
76
+ message,
77
+ status: 0,
78
+ hint
79
+ });
80
+ }
81
+ function normalizeAthenaGatewayBaseUrl(input, options = {}) {
82
+ const label = options.label ?? "Athena gateway base URL";
83
+ const candidate = input ?? options.defaultBaseUrl;
84
+ if (candidate === void 0 || candidate === null) {
85
+ throw invalidBaseUrlError(
86
+ `${label} must be a non-empty absolute http(s) URL. Received ${describeReceivedValue(input)}.`,
87
+ 'Set ATHENA_URL (or pass createClient(url, ...)) to a full URL such as "https://mirror3.athena-db.com".'
88
+ );
89
+ }
90
+ const trimmed = candidate.trim();
91
+ if (!trimmed) {
92
+ throw invalidBaseUrlError(
93
+ `${label} must be a non-empty absolute http(s) URL. Received ${describeReceivedValue(candidate)}.`,
94
+ 'Set ATHENA_URL (or pass createClient(url, ...)) to a full URL such as "https://mirror3.athena-db.com".'
95
+ );
96
+ }
97
+ let parsed;
98
+ try {
99
+ parsed = new URL(trimmed);
100
+ } catch {
101
+ throw invalidBaseUrlError(
102
+ `${label} must be a valid absolute http(s) URL. Received ${describeReceivedValue(candidate)}.`,
103
+ 'Use a full URL including the protocol, for example "https://mirror3.athena-db.com".'
104
+ );
105
+ }
106
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
107
+ throw invalidBaseUrlError(
108
+ `${label} must use http or https. Received ${JSON.stringify(trimmed)}.`,
109
+ 'Use an Athena gateway URL such as "https://mirror3.athena-db.com".'
110
+ );
111
+ }
112
+ if (parsed.search || parsed.hash) {
113
+ throw invalidBaseUrlError(
114
+ `${label} must not include query parameters or hash fragments. Received ${JSON.stringify(trimmed)}.`,
115
+ 'Pass only the base URL. Endpoint paths such as "/gateway/fetch" are appended by the SDK.'
116
+ );
117
+ }
118
+ return parsed.toString().replace(/\/+$/, "");
119
+ }
120
+ function buildAthenaGatewayUrl(baseUrl, path) {
121
+ if (!path.startsWith("/")) {
122
+ throw invalidBaseUrlError(
123
+ `Athena gateway path must start with "/". Received ${JSON.stringify(path)}.`,
124
+ 'Use a leading slash such as "/gateway/fetch" or "/".'
125
+ );
126
+ }
127
+ return `${baseUrl}${path}`;
128
+ }
129
+
63
130
  // src/gateway/client.ts
64
- var DEFAULT_BASE_URL = "https://athena-db.com";
65
131
  var DEFAULT_CLIENT = "railway_direct";
66
132
  var FALLBACK_SDK_VERSION = "1.3.0";
67
133
  var SDK_NAME = "xylex-group/athena";
@@ -88,23 +154,39 @@ function normalizeHeaderValue(value) {
88
154
  function isRecord(value) {
89
155
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
90
156
  }
157
+ function nonEmptyString(value) {
158
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
159
+ }
160
+ function resolveStructuredErrorPayload(payload) {
161
+ if (!isRecord(payload)) return null;
162
+ return isRecord(payload.error) ? payload.error : payload;
163
+ }
91
164
  function resolveRequestId(headers) {
92
165
  return headers.get("x-request-id") ?? headers.get("x-correlation-id") ?? headers.get("x-athena-request-id") ?? void 0;
93
166
  }
94
167
  function resolveErrorMessage(payload, fallback) {
95
- if (isRecord(payload)) {
96
- const messageCandidates = [payload.error, payload.message, payload.details];
168
+ const structuredPayload = resolveStructuredErrorPayload(payload);
169
+ if (structuredPayload) {
170
+ const messageCandidates = [structuredPayload.message, structuredPayload.error, structuredPayload.details];
97
171
  for (const candidate of messageCandidates) {
98
- if (typeof candidate === "string" && candidate.trim().length > 0) {
99
- return candidate.trim();
100
- }
172
+ const resolved = nonEmptyString(candidate);
173
+ if (resolved) return resolved;
101
174
  }
102
175
  }
103
- if (typeof payload === "string" && payload.trim().length > 0) {
104
- return payload.trim();
105
- }
176
+ const rawMessage = nonEmptyString(payload);
177
+ if (rawMessage) return rawMessage;
106
178
  return fallback;
107
179
  }
180
+ function resolveErrorHint(payload) {
181
+ const structuredPayload = resolveStructuredErrorPayload(payload);
182
+ return structuredPayload ? nonEmptyString(structuredPayload.hint) : void 0;
183
+ }
184
+ function resolveStatusText(response, payload) {
185
+ const rawStatusText = nonEmptyString(response.statusText);
186
+ if (rawStatusText) return rawStatusText;
187
+ const payloadRecord = isRecord(payload) ? payload : null;
188
+ return payloadRecord ? nonEmptyString(payloadRecord.statusText) ?? null : null;
189
+ }
108
190
  function detailsFromError(error) {
109
191
  return error.toDetails();
110
192
  }
@@ -242,9 +324,172 @@ function buildHeaders(config, options) {
242
324
  });
243
325
  return headers;
244
326
  }
327
+ function toInvalidUrlResponse(error, endpoint, method) {
328
+ const message = error instanceof Error ? error.message : String(error);
329
+ const gatewayError = error instanceof AthenaGatewayError ? error : new AthenaGatewayError({
330
+ code: "INVALID_URL",
331
+ message,
332
+ status: 0,
333
+ endpoint,
334
+ method,
335
+ cause: message,
336
+ hint: "Set ATHENA_URL to a full http(s) URL before running queries."
337
+ });
338
+ return {
339
+ ok: false,
340
+ status: 0,
341
+ statusText: null,
342
+ data: null,
343
+ error: gatewayError.message,
344
+ errorDetails: detailsFromError(
345
+ new AthenaGatewayError({
346
+ code: gatewayError.code,
347
+ message: gatewayError.message,
348
+ status: gatewayError.status,
349
+ endpoint,
350
+ method,
351
+ requestId: gatewayError.requestId,
352
+ hint: gatewayError.hint,
353
+ cause: gatewayError.causeDetail
354
+ })
355
+ ),
356
+ raw: null
357
+ };
358
+ }
359
+ function resolveGatewayBaseUrl(input) {
360
+ return normalizeAthenaGatewayBaseUrl(input, {
361
+ defaultBaseUrl: ATHENA_DEFAULT_BASE_URL
362
+ });
363
+ }
364
+ function resolveProbePath(path) {
365
+ if (!path) return "/";
366
+ if (!path.startsWith("/")) {
367
+ throw new AthenaGatewayError({
368
+ code: "INVALID_URL",
369
+ message: `Athena gateway probe path must start with "/". Received ${JSON.stringify(path)}.`,
370
+ status: 0,
371
+ hint: 'Use a leading slash such as "/" or "/health".'
372
+ });
373
+ }
374
+ return path;
375
+ }
376
+ function mergeConnectionHeaders(baseHeaders, headers) {
377
+ const merged = {
378
+ ...baseHeaders,
379
+ ...headers ?? {}
380
+ };
381
+ if (!merged["X-Athena-Sdk"] && !merged["x-athena-sdk"]) {
382
+ merged["X-Athena-Sdk"] = SDK_HEADER_VALUE;
383
+ }
384
+ return merged;
385
+ }
386
+ async function performConnectionCheck(baseUrl, requestHeaders, options) {
387
+ const path = resolveProbePath(options?.path);
388
+ const url = buildAthenaGatewayUrl(baseUrl, path);
389
+ try {
390
+ const response = await fetch(url, {
391
+ method: "GET",
392
+ headers: mergeConnectionHeaders(requestHeaders, options?.headers),
393
+ signal: options?.signal
394
+ });
395
+ const rawText = await response.text();
396
+ const requestId = resolveRequestId(response.headers);
397
+ const parsedBody = parseResponseBody(
398
+ rawText ?? "",
399
+ response.headers.get("content-type")
400
+ );
401
+ if (parsedBody.parseFailed) {
402
+ const invalidJsonError = new AthenaGatewayError({
403
+ code: "INVALID_JSON",
404
+ message: "Gateway probe returned malformed JSON",
405
+ status: response.status,
406
+ method: "GET",
407
+ requestId,
408
+ hint: "Verify the gateway response body is valid JSON.",
409
+ cause: rawText.slice(0, 300)
410
+ });
411
+ return {
412
+ ok: false,
413
+ reachable: true,
414
+ status: response.status,
415
+ statusText: resolveStatusText(response, parsedBody.parsed),
416
+ baseUrl,
417
+ url,
418
+ error: invalidJsonError.message,
419
+ errorDetails: detailsFromError(invalidJsonError),
420
+ raw: parsedBody.parsed
421
+ };
422
+ }
423
+ const parsed = parsedBody.parsed;
424
+ if (!response.ok) {
425
+ const httpError = new AthenaGatewayError({
426
+ code: "HTTP_ERROR",
427
+ message: resolveErrorMessage(
428
+ parsed,
429
+ `Athena gateway GET ${path} failed with status ${response.status}`
430
+ ),
431
+ status: response.status,
432
+ method: "GET",
433
+ requestId,
434
+ hint: resolveErrorHint(parsed)
435
+ });
436
+ return {
437
+ ok: false,
438
+ reachable: true,
439
+ status: response.status,
440
+ statusText: resolveStatusText(response, parsed),
441
+ baseUrl,
442
+ url,
443
+ error: httpError.message,
444
+ errorDetails: detailsFromError(httpError),
445
+ raw: parsed
446
+ };
447
+ }
448
+ return {
449
+ ok: true,
450
+ reachable: true,
451
+ status: response.status,
452
+ statusText: resolveStatusText(response, parsed),
453
+ baseUrl,
454
+ url,
455
+ error: void 0,
456
+ errorDetails: null,
457
+ raw: parsed
458
+ };
459
+ } catch (callError) {
460
+ const message = callError instanceof Error ? callError.message : String(callError);
461
+ const networkError = new AthenaGatewayError({
462
+ code: "NETWORK_ERROR",
463
+ message: `Network error while probing Athena gateway ${url}: ${message}`,
464
+ method: "GET",
465
+ cause: message,
466
+ hint: "Check gateway URL, DNS, and network reachability."
467
+ });
468
+ return {
469
+ ok: false,
470
+ reachable: false,
471
+ status: 0,
472
+ statusText: null,
473
+ baseUrl,
474
+ url,
475
+ error: networkError.message,
476
+ errorDetails: detailsFromError(networkError),
477
+ raw: null
478
+ };
479
+ }
480
+ }
481
+ async function verifyAthenaGatewayUrl(baseUrl, options) {
482
+ const normalizedBaseUrl = normalizeAthenaGatewayBaseUrl(baseUrl);
483
+ return performConnectionCheck(normalizedBaseUrl, { "X-Athena-Sdk": SDK_HEADER_VALUE }, options);
484
+ }
245
485
  async function callAthena(config, endpoint, method, payload, options) {
246
- const baseUrl = (options?.baseUrl ?? config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
247
- const url = `${baseUrl}${endpoint}`;
486
+ let baseUrl;
487
+ try {
488
+ baseUrl = resolveGatewayBaseUrl(options?.baseUrl ?? config.baseUrl);
489
+ } catch (error) {
490
+ return toInvalidUrlResponse(error, endpoint, method);
491
+ }
492
+ const url = buildAthenaGatewayUrl(baseUrl, endpoint);
248
493
  const headers = buildHeaders(config, options);
249
494
  try {
250
495
  const requestInit = {
@@ -275,6 +520,7 @@ async function callAthena(config, endpoint, method, payload, options) {
275
520
  return {
276
521
  ok: false,
277
522
  status: response.status,
523
+ statusText: resolveStatusText(response, parsedBody.parsed),
278
524
  data: null,
279
525
  error: invalidJsonError.message,
280
526
  errorDetails: detailsFromError(invalidJsonError),
@@ -293,11 +539,13 @@ async function callAthena(config, endpoint, method, payload, options) {
293
539
  status: response.status,
294
540
  endpoint,
295
541
  method,
296
- requestId
542
+ requestId,
543
+ hint: resolveErrorHint(parsed)
297
544
  });
298
545
  return {
299
546
  ok: false,
300
547
  status: response.status,
548
+ statusText: resolveStatusText(response, parsed),
301
549
  data: null,
302
550
  error: httpError.message,
303
551
  errorDetails: detailsFromError(httpError),
@@ -309,6 +557,7 @@ async function callAthena(config, endpoint, method, payload, options) {
309
557
  return {
310
558
  ok: true,
311
559
  status: response.status,
560
+ statusText: resolveStatusText(response, parsed),
312
561
  data: payloadData ?? null,
313
562
  count: payloadCount,
314
563
  error: void 0,
@@ -328,6 +577,7 @@ async function callAthena(config, endpoint, method, payload, options) {
328
577
  return {
329
578
  ok: false,
330
579
  status: 0,
580
+ statusText: null,
331
581
  data: null,
332
582
  error: networkError.message,
333
583
  errorDetails: detailsFromError(networkError),
@@ -336,32 +586,44 @@ async function callAthena(config, endpoint, method, payload, options) {
336
586
  }
337
587
  }
338
588
  function createAthenaGatewayClient(config = {}) {
589
+ const normalizedBaseUrl = resolveGatewayBaseUrl(config.baseUrl);
590
+ const normalizedConfig = {
591
+ ...config,
592
+ baseUrl: normalizedBaseUrl
593
+ };
339
594
  return {
340
- baseUrl: (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, ""),
595
+ baseUrl: normalizedBaseUrl,
341
596
  buildHeaders(options) {
342
- return buildHeaders(config, options);
597
+ return buildHeaders(normalizedConfig, options);
598
+ },
599
+ verifyConnection(options) {
600
+ return performConnectionCheck(
601
+ normalizedBaseUrl,
602
+ buildHeaders(normalizedConfig),
603
+ options
604
+ );
343
605
  },
344
606
  fetchGateway(payload, options) {
345
- return callAthena(config, "/gateway/fetch", "POST", payload, options);
607
+ return callAthena(normalizedConfig, "/gateway/fetch", "POST", payload, options);
346
608
  },
347
609
  insertGateway(payload, options) {
348
- return callAthena(config, "/gateway/insert", "PUT", payload, options);
610
+ return callAthena(normalizedConfig, "/gateway/insert", "PUT", payload, options);
349
611
  },
350
612
  updateGateway(payload, options) {
351
- return callAthena(config, "/gateway/update", "POST", payload, options);
613
+ return callAthena(normalizedConfig, "/gateway/update", "POST", payload, options);
352
614
  },
353
615
  deleteGateway(payload, options) {
354
- return callAthena(config, "/gateway/delete", "DELETE", payload, options);
616
+ return callAthena(normalizedConfig, "/gateway/delete", "DELETE", payload, options);
355
617
  },
356
618
  rpcGateway(payload, options) {
357
619
  if (options?.get) {
358
620
  const endpoint = buildRpcGetEndpoint(payload);
359
- return callAthena(config, endpoint, "GET", null, options);
621
+ return callAthena(normalizedConfig, endpoint, "GET", null, options);
360
622
  }
361
- return callAthena(config, "/gateway/rpc", "POST", payload, options);
623
+ return callAthena(normalizedConfig, "/gateway/rpc", "POST", payload, options);
362
624
  },
363
625
  queryGateway(payload, options) {
364
- return callAthena(config, "/gateway/query", "POST", payload, options);
626
+ return callAthena(normalizedConfig, "/gateway/query", "POST", payload, options);
365
627
  }
366
628
  };
367
629
  }
@@ -369,10 +631,29 @@ function createAthenaGatewayClient(config = {}) {
369
631
  // src/sql-identifiers.ts
370
632
  var SIMPLE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
371
633
  var COMPOSITE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
372
- var ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_.]*)\s+(?:as\s+)?([A-Za-z_][A-Za-z0-9_]*)$/i;
634
+ var SQL_ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_.]*)\s+(?:as\s+)?([A-Za-z_][A-Za-z0-9_]*)$/i;
635
+ var RESPONSE_ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([A-Za-z_][A-Za-z0-9_.]*)$/i;
373
636
  function quoteIdentifierSegment(identifier2) {
374
637
  return `"${identifier2.replace(/"/g, '""')}"`;
375
638
  }
639
+ function parseAliasedIdentifierToken(token) {
640
+ const responseAliasMatch = RESPONSE_ALIAS_PATTERN.exec(token);
641
+ if (responseAliasMatch) {
642
+ const [, aliasIdentifier2, baseIdentifier2] = responseAliasMatch;
643
+ if (COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier2) && SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier2)) {
644
+ return { baseIdentifier: baseIdentifier2, aliasIdentifier: aliasIdentifier2 };
645
+ }
646
+ }
647
+ const sqlAliasMatch = SQL_ALIAS_PATTERN.exec(token);
648
+ if (!sqlAliasMatch) {
649
+ return null;
650
+ }
651
+ const [, baseIdentifier, aliasIdentifier] = sqlAliasMatch;
652
+ if (!COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) || !SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier)) {
653
+ return null;
654
+ }
655
+ return { baseIdentifier, aliasIdentifier };
656
+ }
376
657
  function quoteQualifiedIdentifier(identifier2) {
377
658
  return identifier2.split(".").map((segment) => quoteIdentifierSegment(segment)).join(".");
378
659
  }
@@ -381,23 +662,27 @@ function quoteSelectToken(token) {
381
662
  if (COMPOSITE_IDENTIFIER_PATTERN.test(token)) {
382
663
  return quoteQualifiedIdentifier(token);
383
664
  }
384
- const aliasMatch = ALIAS_PATTERN.exec(token);
385
- if (!aliasMatch) {
386
- return token;
387
- }
388
- const [, baseIdentifier, aliasIdentifier] = aliasMatch;
389
- if (!COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) || !SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier)) {
665
+ const aliasedIdentifier = parseAliasedIdentifierToken(token);
666
+ if (!aliasedIdentifier) {
390
667
  return token;
391
668
  }
669
+ const { baseIdentifier, aliasIdentifier } = aliasedIdentifier;
392
670
  return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteIdentifierSegment(aliasIdentifier)}`;
393
671
  }
672
+ function quoteSelectColumnToken(token) {
673
+ const trimmed = token.trim();
674
+ if (!trimmed || trimmed === "*") return trimmed || "*";
675
+ const responseAliasMatch = RESPONSE_ALIAS_PATTERN.exec(trimmed);
676
+ if (responseAliasMatch) {
677
+ const [, aliasIdentifier, baseIdentifier] = responseAliasMatch;
678
+ return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteIdentifierSegment(aliasIdentifier)}`;
679
+ }
680
+ return quoteQualifiedIdentifier(trimmed);
681
+ }
394
682
  function canAutoQuoteToken(token) {
395
683
  if (token === "*") return true;
396
684
  if (COMPOSITE_IDENTIFIER_PATTERN.test(token)) return true;
397
- const aliasMatch = ALIAS_PATTERN.exec(token);
398
- if (!aliasMatch) return false;
399
- const [, baseIdentifier, aliasIdentifier] = aliasMatch;
400
- return COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) && SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier);
685
+ return parseAliasedIdentifierToken(token) != null;
401
686
  }
402
687
  function splitTopLevelCommaSeparated(input) {
403
688
  const parts = [];
@@ -497,6 +782,231 @@ function identifier(...segments) {
497
782
  return new SqlIdentifierPath(expandedSegments);
498
783
  }
499
784
 
785
+ // src/auth/react-email.ts
786
+ var reactEmailRenderModulePromise;
787
+ function isRecord2(value) {
788
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
789
+ }
790
+ function isFunction(value) {
791
+ return typeof value === "function";
792
+ }
793
+ function toStringOrUndefined(value) {
794
+ if (typeof value !== "string") return void 0;
795
+ return value;
796
+ }
797
+ function nowIsoString() {
798
+ return (/* @__PURE__ */ new Date()).toISOString();
799
+ }
800
+ function emitReactEmailEvent(observe, phase, input = {}) {
801
+ if (!observe) return;
802
+ try {
803
+ observe({
804
+ phase,
805
+ timestamp: nowIsoString(),
806
+ ...input
807
+ });
808
+ } catch {
809
+ }
810
+ }
811
+ function mergeRenderDefaults(input, defaults) {
812
+ return {
813
+ ...input,
814
+ pretty: input.pretty ?? defaults?.pretty,
815
+ includePlainText: input.includePlainText ?? defaults?.includePlainText
816
+ };
817
+ }
818
+ function mergeRuntimeOptions(options) {
819
+ if (!options) return void 0;
820
+ return {
821
+ defaults: options.defaults,
822
+ observe: options.observe,
823
+ route: "route" in options ? options.route : void 0
824
+ };
825
+ }
826
+ async function resolveReactEmailRenderModule() {
827
+ if (!reactEmailRenderModulePromise) {
828
+ reactEmailRenderModulePromise = (async () => {
829
+ try {
830
+ const loaded = await import('@react-email/render');
831
+ if (!isFunction(loaded.render)) {
832
+ throw new Error("missing render(...) export");
833
+ }
834
+ return {
835
+ render: loaded.render,
836
+ toPlainText: isFunction(loaded.toPlainText) ? loaded.toPlainText : void 0,
837
+ pretty: isFunction(loaded.pretty) ? loaded.pretty : void 0
838
+ };
839
+ } catch (error) {
840
+ const message = error instanceof Error ? error.message : String(error);
841
+ throw new Error(
842
+ `React Email rendering requires @react-email/render. Install it in your project (for example: pnpm add @react-email/render). Loader error: ${message}`
843
+ );
844
+ }
845
+ })();
846
+ }
847
+ if (!reactEmailRenderModulePromise) {
848
+ throw new Error("React Email renderer module failed to initialize");
849
+ }
850
+ return reactEmailRenderModulePromise;
851
+ }
852
+ async function resolveReactEmailElement(input) {
853
+ if (input.element != null) {
854
+ return input.element;
855
+ }
856
+ if (!input.component) {
857
+ throw new Error("react email payload requires either `element` or `component`");
858
+ }
859
+ try {
860
+ const reactModule = await import('react');
861
+ if (typeof reactModule.createElement !== "function") {
862
+ throw new Error("react createElement(...) export is unavailable");
863
+ }
864
+ return reactModule.createElement(
865
+ input.component,
866
+ input.props ?? {}
867
+ );
868
+ } catch (error) {
869
+ const message = error instanceof Error ? error.message : String(error);
870
+ throw new Error(
871
+ `React Email component rendering requires react runtime support. Install react in your project. Loader error: ${message}`
872
+ );
873
+ }
874
+ }
875
+ function createAuthReactEmailInput(component, props, overrides = {}) {
876
+ return {
877
+ ...overrides,
878
+ component,
879
+ props
880
+ };
881
+ }
882
+ function defineAuthEmailTemplate(definition) {
883
+ const react = (props, overrides) => createAuthReactEmailInput(definition.component, props, {
884
+ ...definition.defaults,
885
+ ...overrides
886
+ });
887
+ return {
888
+ component: definition.component,
889
+ react,
890
+ toTemplateCreate: (input) => {
891
+ const templateKey = input.templateKey ?? definition.templateKey;
892
+ const subjectTemplate = input.subjectTemplate ?? definition.subjectTemplate;
893
+ if (!templateKey) {
894
+ throw new Error("defineAuthEmailTemplate.toTemplateCreate requires templateKey");
895
+ }
896
+ if (!subjectTemplate) {
897
+ throw new Error("defineAuthEmailTemplate.toTemplateCreate requires subjectTemplate");
898
+ }
899
+ const { props, react: reactOverrides, ...rest } = input;
900
+ return {
901
+ ...rest,
902
+ templateKey,
903
+ subjectTemplate,
904
+ react: react(props, reactOverrides)
905
+ };
906
+ },
907
+ toTemplateUpdate: (input) => {
908
+ const { props, react: reactOverrides, ...rest } = input;
909
+ return {
910
+ ...rest,
911
+ react: react(props, reactOverrides)
912
+ };
913
+ }
914
+ };
915
+ }
916
+ async function renderAthenaReactEmail(input, options) {
917
+ if (!isRecord2(input)) {
918
+ throw new Error("react email payload must be an object");
919
+ }
920
+ const runtimeOptions = mergeRuntimeOptions(options);
921
+ const start = Date.now();
922
+ emitReactEmailEvent(runtimeOptions?.observe, "render:start", {
923
+ route: runtimeOptions?.route,
924
+ message: "Rendering react email payload"
925
+ });
926
+ try {
927
+ const normalizedInput = mergeRenderDefaults(input, runtimeOptions?.defaults);
928
+ const element = await resolveReactEmailElement(normalizedInput);
929
+ const renderModule = await resolveReactEmailRenderModule();
930
+ const htmlValue = await renderModule.render(element);
931
+ const renderedHtml = typeof htmlValue === "string" ? htmlValue : String(htmlValue ?? "");
932
+ if (!renderedHtml.trim()) {
933
+ throw new Error("react email renderer returned an empty HTML string");
934
+ }
935
+ let html = renderedHtml;
936
+ if (normalizedInput.pretty && renderModule.pretty) {
937
+ const prettyValue = await renderModule.pretty(renderedHtml);
938
+ if (typeof prettyValue === "string" && prettyValue.trim().length > 0) {
939
+ html = prettyValue;
940
+ }
941
+ }
942
+ const explicitText = toStringOrUndefined(normalizedInput.text);
943
+ if (explicitText !== void 0) {
944
+ emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
945
+ route: runtimeOptions?.route,
946
+ durationMs: Date.now() - start,
947
+ message: "Rendered react email with explicit text"
948
+ });
949
+ return {
950
+ html,
951
+ text: explicitText
952
+ };
953
+ }
954
+ if (normalizedInput.includePlainText === false || !renderModule.toPlainText) {
955
+ emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
956
+ route: runtimeOptions?.route,
957
+ durationMs: Date.now() - start,
958
+ message: "Rendered react email without plain-text derivation"
959
+ });
960
+ return { html };
961
+ }
962
+ const plainTextValue = await renderModule.toPlainText(html);
963
+ const plainText = toStringOrUndefined(plainTextValue);
964
+ emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
965
+ route: runtimeOptions?.route,
966
+ durationMs: Date.now() - start,
967
+ message: plainText ? "Rendered react email with derived plain text" : "Rendered react email HTML only"
968
+ });
969
+ if (plainText === void 0) {
970
+ return { html };
971
+ }
972
+ return {
973
+ html,
974
+ text: plainText
975
+ };
976
+ } catch (error) {
977
+ const message = error instanceof Error ? error.message : String(error);
978
+ emitReactEmailEvent(runtimeOptions?.observe, "render:error", {
979
+ route: runtimeOptions?.route,
980
+ durationMs: Date.now() - start,
981
+ error: message,
982
+ message: "Failed to render react email payload"
983
+ });
984
+ throw error;
985
+ }
986
+ }
987
+ async function resolveReactEmailPayloadFields(input, fields, options) {
988
+ const { react, ...payloadWithoutReact } = input;
989
+ if (!react) {
990
+ return payloadWithoutReact;
991
+ }
992
+ const rendered = await renderAthenaReactEmail(react, options);
993
+ const payload = {
994
+ ...payloadWithoutReact
995
+ };
996
+ payload[fields.htmlField] = rendered.html;
997
+ const currentTextValue = payload[fields.textField];
998
+ if (rendered.text !== void 0 && (currentTextValue === void 0 || currentTextValue === null || currentTextValue === "")) {
999
+ payload[fields.textField] = rendered.text;
1000
+ }
1001
+ if (fields.variablesField && (payload[fields.variablesField] === void 0 || payload[fields.variablesField] === null) && isRecord2(react.props)) {
1002
+ const derivedVariables = Object.keys(react.props);
1003
+ if (derivedVariables.length > 0) {
1004
+ payload[fields.variablesField] = derivedVariables;
1005
+ }
1006
+ }
1007
+ return payload;
1008
+ }
1009
+
500
1010
  // src/auth/client.ts
501
1011
  var DEFAULT_AUTH_BASE_URL = "http://localhost:3001/api/auth";
502
1012
  var FALLBACK_SDK_VERSION2 = "1.0.0";
@@ -506,7 +1016,7 @@ var SDK_HEADER_VALUE2 = `${SDK_NAME2} ${SDK_VERSION2}`;
506
1016
  function normalizeBaseUrl(baseUrl) {
507
1017
  return (baseUrl ?? DEFAULT_AUTH_BASE_URL).replace(/\/$/, "");
508
1018
  }
509
- function isRecord2(value) {
1019
+ function isRecord3(value) {
510
1020
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
511
1021
  }
512
1022
  function normalizeHeaderValue2(value) {
@@ -531,7 +1041,7 @@ function resolveRequestId2(headers) {
531
1041
  return headers.get("x-request-id") ?? headers.get("x-correlation-id") ?? headers.get("x-athena-request-id") ?? void 0;
532
1042
  }
533
1043
  function resolveErrorMessage2(payload, fallback) {
534
- if (isRecord2(payload)) {
1044
+ if (isRecord3(payload)) {
535
1045
  const messageCandidates = [payload.error, payload.message, payload.details];
536
1046
  for (const candidate of messageCandidates) {
537
1047
  if (typeof candidate === "string" && candidate.trim().length > 0) {
@@ -857,6 +1367,19 @@ function createAuthClient(config = {}) {
857
1367
  options
858
1368
  );
859
1369
  };
1370
+ const withReactEmailRoute = (route) => ({
1371
+ ...resolvedConfig.reactEmail,
1372
+ route
1373
+ });
1374
+ const resolveAdminEmailPayload = (route, input) => resolveReactEmailPayloadFields(input, {
1375
+ htmlField: "htmlBody",
1376
+ textField: "textBody"
1377
+ }, withReactEmailRoute(route));
1378
+ const resolveAdminEmailTemplatePayload = (route, input) => resolveReactEmailPayloadFields(input, {
1379
+ htmlField: "htmlTemplate",
1380
+ textField: "textTemplate",
1381
+ variablesField: "variables"
1382
+ }, withReactEmailRoute(route));
860
1383
  const listUserEmailsWithFallback = async (input, options) => {
861
1384
  const primary = await getWithQuery(
862
1385
  "/email/list",
@@ -884,7 +1407,7 @@ function createAuthClient(config = {}) {
884
1407
  data: null
885
1408
  };
886
1409
  }
887
- const fallbackStatus = isRecord2(fallback.data) && typeof fallback.data.ok === "boolean" ? fallback.data.ok ? "ok" : "error" : "ok";
1410
+ const fallbackStatus = isRecord3(fallback.data) && typeof fallback.data.ok === "boolean" ? fallback.data.ok ? "ok" : "error" : "ok";
888
1411
  return {
889
1412
  ...fallback,
890
1413
  data: {
@@ -1320,8 +1843,16 @@ function createAuthClient(config = {}) {
1320
1843
  email: {
1321
1844
  list: (input, options) => getWithQuery("/admin/email/list", input, options),
1322
1845
  get: (input, options) => getWithQuery("/admin/email/get", input, options),
1323
- create: (input, options) => postGeneric("/admin/email/create", input, options),
1324
- update: (input, options) => postGeneric("/admin/email/update", input, options),
1846
+ create: async (input, options) => postGeneric(
1847
+ "/admin/email/create",
1848
+ await resolveAdminEmailPayload("/admin/email/create", input),
1849
+ options
1850
+ ),
1851
+ update: async (input, options) => postGeneric(
1852
+ "/admin/email/update",
1853
+ await resolveAdminEmailPayload("/admin/email/update", input),
1854
+ options
1855
+ ),
1325
1856
  delete: (input, options) => postGeneric("/admin/email/delete", input, options),
1326
1857
  failure: {
1327
1858
  list: (input, options) => getWithQuery("/admin/email-failure/list", input, options),
@@ -1333,17 +1864,33 @@ function createAuthClient(config = {}) {
1333
1864
  template: {
1334
1865
  list: (input, options) => getWithQuery("/admin/email-template/list", input, options),
1335
1866
  get: (input, options) => getWithQuery("/admin/email-template/get", input, options),
1336
- create: (input, options) => postGeneric("/admin/email-template/create", input, options),
1337
- update: (input, options) => postGeneric("/admin/email-template/update", input, options),
1867
+ create: async (input, options) => postGeneric(
1868
+ "/admin/email-template/create",
1869
+ await resolveAdminEmailTemplatePayload("/admin/email-template/create", input),
1870
+ options
1871
+ ),
1872
+ update: async (input, options) => postGeneric(
1873
+ "/admin/email-template/update",
1874
+ await resolveAdminEmailTemplatePayload("/admin/email-template/update", input),
1875
+ options
1876
+ ),
1338
1877
  delete: (input, options) => postGeneric("/admin/email-template/delete", input, options)
1339
1878
  }
1340
1879
  },
1341
1880
  emailTemplate: {
1342
1881
  get: (input, options) => getWithQuery("/admin/email-template/get", input, options),
1343
- create: (input, options) => postGeneric("/admin/email-template/create", input, options),
1882
+ create: async (input, options) => postGeneric(
1883
+ "/admin/email-template/create",
1884
+ await resolveAdminEmailTemplatePayload("/admin/email-template/create", input),
1885
+ options
1886
+ ),
1344
1887
  delete: (input, options) => postGeneric("/admin/email-template/delete", input, options),
1345
1888
  list: (input, options) => getWithQuery("/admin/email-template/list", input, options),
1346
- update: (input, options) => postGeneric("/admin/email-template/update", input, options)
1889
+ update: async (input, options) => postGeneric(
1890
+ "/admin/email-template/update",
1891
+ await resolveAdminEmailTemplatePayload("/admin/email-template/update", input),
1892
+ options
1893
+ )
1347
1894
  }
1348
1895
  },
1349
1896
  apiKey: {
@@ -1535,6 +2082,19 @@ function createAuthClient(config = {}) {
1535
2082
  };
1536
2083
  }
1537
2084
 
2085
+ // src/utils/parse-boolean-flag.ts
2086
+ function parseBooleanFlag(rawValue, fallback) {
2087
+ if (!rawValue) return fallback;
2088
+ const normalized = rawValue.trim().toLowerCase();
2089
+ if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
2090
+ return true;
2091
+ }
2092
+ if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
2093
+ return false;
2094
+ }
2095
+ return fallback;
2096
+ }
2097
+
1538
2098
  // src/auxiliaries.ts
1539
2099
  var AthenaErrorKind = {
1540
2100
  UniqueViolation: "unique_violation",
@@ -1564,16 +2124,8 @@ var AthenaErrorCategory = {
1564
2124
  Database: "database",
1565
2125
  Unknown: "unknown"
1566
2126
  };
1567
- function parseBooleanFlag(rawValue, fallback) {
1568
- if (!rawValue) return fallback;
1569
- const normalized = rawValue.trim().toLowerCase();
1570
- if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
1571
- return true;
1572
- }
1573
- if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
1574
- return false;
1575
- }
1576
- return fallback;
2127
+ function parseBooleanFlag2(rawValue, fallback) {
2128
+ return parseBooleanFlag(rawValue, fallback);
1577
2129
  }
1578
2130
  var AthenaError = class extends Error {
1579
2131
  code;
@@ -1597,10 +2149,39 @@ var AthenaError = class extends Error {
1597
2149
  this.raw = input.raw;
1598
2150
  }
1599
2151
  };
1600
- function isRecord3(value) {
2152
+ function isRecord4(value) {
1601
2153
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1602
2154
  }
1603
- function isAthenaErrorKind(value) {
2155
+ function firstNonEmptyString(...values) {
2156
+ for (const value of values) {
2157
+ if (typeof value === "string" && value.trim().length > 0) {
2158
+ return value.trim();
2159
+ }
2160
+ }
2161
+ return void 0;
2162
+ }
2163
+ function messageFromUnknownError(error) {
2164
+ if (typeof error === "string" && error.trim().length > 0) {
2165
+ return error.trim();
2166
+ }
2167
+ if (error instanceof Error && error.message.trim().length > 0) {
2168
+ return error.message.trim();
2169
+ }
2170
+ if (!isRecord4(error)) {
2171
+ return void 0;
2172
+ }
2173
+ return firstNonEmptyString(error.message, error.error, error.details);
2174
+ }
2175
+ function gatewayCodeFromUnknownError(error) {
2176
+ if (!isRecord4(error) || typeof error.gatewayCode !== "string") {
2177
+ return void 0;
2178
+ }
2179
+ return error.gatewayCode;
2180
+ }
2181
+ function isAthenaResultErrorLike(value) {
2182
+ return isRecord4(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");
2183
+ }
2184
+ function isAthenaErrorKind(value) {
1604
2185
  return value === "unique_violation" || value === "not_found" || value === "validation" || value === "auth" || value === "rate_limit" || value === "transient" || value === "unknown";
1605
2186
  }
1606
2187
  function isAthenaErrorCode(value) {
@@ -1610,7 +2191,7 @@ function isAthenaErrorCategory(value) {
1610
2191
  return value === "transport" || value === "client" || value === "server" || value === "database" || value === "unknown";
1611
2192
  }
1612
2193
  function isNormalizedAthenaError(value) {
1613
- return isRecord3(value) && isAthenaErrorKind(value.kind) && isAthenaErrorCode(value.code) && isAthenaErrorCategory(value.category) && typeof value.retryable === "boolean" && typeof value.message === "string" && "raw" in value;
2194
+ return isRecord4(value) && isAthenaErrorKind(value.kind) && isAthenaErrorCode(value.code) && isAthenaErrorCategory(value.category) && typeof value.retryable === "boolean" && typeof value.message === "string" && "raw" in value;
1614
2195
  }
1615
2196
  function withContextOverrides(normalized, context) {
1616
2197
  if (!context?.table && !context?.operation) {
@@ -1623,7 +2204,7 @@ function withContextOverrides(normalized, context) {
1623
2204
  };
1624
2205
  }
1625
2206
  function resolveAttachedNormalizedError(resultOrError) {
1626
- if (!isRecord3(resultOrError)) return void 0;
2207
+ if (!isRecord4(resultOrError)) return void 0;
1627
2208
  if (!("__athenaNormalizedError" in resultOrError)) return void 0;
1628
2209
  const candidate = resultOrError.__athenaNormalizedError;
1629
2210
  return isNormalizedAthenaError(candidate) ? candidate : void 0;
@@ -1642,7 +2223,7 @@ function contextHint(context) {
1642
2223
  return `Identity: ${identity}`;
1643
2224
  }
1644
2225
  function isAthenaResultLike(value) {
1645
- return isRecord3(value) && "status" in value && "error" in value && "data" in value && typeof value.status === "number";
2226
+ return isRecord4(value) && "status" in value && "error" in value && "data" in value && typeof value.status === "number";
1646
2227
  }
1647
2228
  function operationFromDetails(details) {
1648
2229
  if (!details?.endpoint) return void 0;
@@ -1684,6 +2265,7 @@ function classifyKind(status, code, message) {
1684
2265
  if (status === 404 || hasNotFoundPattern) return "not_found";
1685
2266
  if (status === 401 || status === 403 || hasAuthPattern) return "auth";
1686
2267
  if (status === 429 || hasRateLimitPattern) return "rate_limit";
2268
+ if (code === "INVALID_URL") return "validation";
1687
2269
  if (status === 400 || status === 422 || hasValidationPattern) return "validation";
1688
2270
  if (code === "NETWORK_ERROR" || status === 0 || status !== void 0 && status >= 500 || hasTransientPattern) {
1689
2271
  return "transient";
@@ -1691,6 +2273,9 @@ function classifyKind(status, code, message) {
1691
2273
  return "unknown";
1692
2274
  }
1693
2275
  function toAthenaErrorCode(kind, status, gatewayCode) {
2276
+ if (gatewayCode === "INVALID_URL") {
2277
+ return AthenaErrorCode.ValidationFailed;
2278
+ }
1694
2279
  if (gatewayCode === "NETWORK_ERROR" || kind === "transient" && status === 0) {
1695
2280
  return AthenaErrorCode.NetworkUnavailable;
1696
2281
  }
@@ -1739,15 +2324,16 @@ function toAthenaGatewayError(source, fallbackMessage, context) {
1739
2324
  return source;
1740
2325
  }
1741
2326
  if (isAthenaResultLike(source) && source.errorDetails) {
2327
+ const message2 = messageFromUnknownError(source.error) ?? source.errorDetails.message ?? fallbackMessage;
1742
2328
  return new AthenaGatewayError({
1743
2329
  code: source.errorDetails.code,
1744
- message: source.error ?? source.errorDetails.message,
2330
+ message: message2,
1745
2331
  status: source.status,
1746
2332
  endpoint: source.errorDetails.endpoint,
1747
2333
  method: source.errorDetails.method,
1748
2334
  requestId: source.errorDetails.requestId,
1749
- hint: source.errorDetails.hint,
1750
- cause: source.errorDetails.cause
2335
+ hint: (isRecord4(source.error) ? firstNonEmptyString(source.error.hint) : void 0) ?? source.errorDetails.hint,
2336
+ cause: (isRecord4(source.error) ? firstNonEmptyString(source.error.cause) : void 0) ?? source.errorDetails.cause
1751
2337
  });
1752
2338
  }
1753
2339
  const normalized = normalizeAthenaError(source, context);
@@ -1769,13 +2355,50 @@ function normalizeAthenaError(resultOrError, context) {
1769
2355
  return withContextOverrides(attached, context);
1770
2356
  }
1771
2357
  if (isAthenaResultLike(resultOrError)) {
2358
+ if (isAthenaResultErrorLike(resultOrError.error)) {
2359
+ return {
2360
+ kind: resultOrError.error.kind ?? classifyKind(resultOrError.status, gatewayCodeFromUnknownError(resultOrError.error), resultOrError.error.message),
2361
+ code: resultOrError.error.athenaCode ?? toAthenaErrorCode(
2362
+ resultOrError.error.kind ?? classifyKind(
2363
+ resultOrError.status,
2364
+ gatewayCodeFromUnknownError(resultOrError.error),
2365
+ resultOrError.error.message
2366
+ ),
2367
+ resultOrError.error.status ?? resultOrError.status,
2368
+ gatewayCodeFromUnknownError(resultOrError.error)
2369
+ ),
2370
+ category: resultOrError.error.category ?? toAthenaErrorCategory(
2371
+ resultOrError.error.kind ?? classifyKind(
2372
+ resultOrError.status,
2373
+ gatewayCodeFromUnknownError(resultOrError.error),
2374
+ resultOrError.error.message
2375
+ ),
2376
+ resultOrError.error.status ?? resultOrError.status
2377
+ ),
2378
+ retryable: resultOrError.error.retryable ?? isRetryable(
2379
+ resultOrError.error.kind ?? classifyKind(
2380
+ resultOrError.status,
2381
+ gatewayCodeFromUnknownError(resultOrError.error),
2382
+ resultOrError.error.message
2383
+ ),
2384
+ resultOrError.error.status ?? resultOrError.status
2385
+ ),
2386
+ status: resultOrError.error.status ?? resultOrError.status,
2387
+ constraint: resultOrError.error.constraint,
2388
+ table: context?.table ?? resultOrError.error.table,
2389
+ operation: context?.operation ?? resultOrError.error.operation,
2390
+ message: resultOrError.error.message,
2391
+ raw: resultOrError.error.raw ?? resultOrError.raw
2392
+ };
2393
+ }
1772
2394
  const details = resultOrError.errorDetails;
1773
- const message2 = resultOrError.error ?? details?.message ?? `Athena ${context?.operation ?? operationFromDetails(details) ?? "request"} failed`;
2395
+ const message2 = messageFromUnknownError(resultOrError.error) ?? details?.message ?? `Athena ${context?.operation ?? operationFromDetails(details) ?? "request"} failed`;
1774
2396
  const operation = context?.operation ?? operationFromDetails(details);
1775
2397
  const table = context?.table ?? extractTable(message2);
1776
2398
  const constraint = extractConstraint(message2);
1777
- const kind2 = classifyKind(resultOrError.status, details?.code, message2);
1778
- const code = toAthenaErrorCode(kind2, resultOrError.status, details?.code);
2399
+ const gatewayCode = details?.code ?? gatewayCodeFromUnknownError(resultOrError.error);
2400
+ const kind2 = classifyKind(resultOrError.status, gatewayCode, message2);
2401
+ const code = toAthenaErrorCode(kind2, resultOrError.status, gatewayCode);
1779
2402
  const category = toAthenaErrorCategory(kind2, resultOrError.status);
1780
2403
  return {
1781
2404
  kind: kind2,
@@ -1812,7 +2435,7 @@ function normalizeAthenaError(resultOrError, context) {
1812
2435
  };
1813
2436
  }
1814
2437
  if (resultOrError instanceof Error) {
1815
- const maybeStatus = isRecord3(resultOrError) && typeof resultOrError.status === "number" ? resultOrError.status : void 0;
2438
+ const maybeStatus = isRecord4(resultOrError) && typeof resultOrError.status === "number" ? resultOrError.status : void 0;
1816
2439
  const kind2 = classifyKind(maybeStatus, void 0, resultOrError.message);
1817
2440
  return {
1818
2441
  kind: kind2,
@@ -2001,8 +2624,8 @@ async function withRetry(config, fn) {
2001
2624
  // src/db/module.ts
2002
2625
  function createDbModule(input) {
2003
2626
  const db = {
2004
- from(table) {
2005
- return input.from(table);
2627
+ from(table, options) {
2628
+ return input.from(table, options);
2006
2629
  },
2007
2630
  select(table, columns, options) {
2008
2631
  return input.from(table).select(columns, options);
@@ -2029,17 +2652,551 @@ function createDbModule(input) {
2029
2652
  return db;
2030
2653
  }
2031
2654
 
2655
+ // src/query-ast.ts
2656
+ 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;
2657
+ var FILTER_OPERATORS = /* @__PURE__ */ new Set([
2658
+ "eq",
2659
+ "neq",
2660
+ "gt",
2661
+ "gte",
2662
+ "lt",
2663
+ "lte",
2664
+ "like",
2665
+ "ilike",
2666
+ "is",
2667
+ "in",
2668
+ "contains",
2669
+ "containedBy"
2670
+ ]);
2671
+ var BOOLEAN_SAFE_OPERATORS = /* @__PURE__ */ new Set([
2672
+ "eq",
2673
+ "neq",
2674
+ "gt",
2675
+ "gte",
2676
+ "lt",
2677
+ "lte",
2678
+ "like",
2679
+ "ilike",
2680
+ "is"
2681
+ ]);
2682
+ function isRecord5(value) {
2683
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2684
+ }
2685
+ function isUuidString(value) {
2686
+ return UUID_PATTERN.test(value.trim());
2687
+ }
2688
+ function isUuidIdentifierColumn(column) {
2689
+ return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
2690
+ }
2691
+ function shouldUseUuidTextComparison(column, value) {
2692
+ return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
2693
+ }
2694
+ function isRelationSelectNode(value) {
2695
+ return isRecord5(value) && isRecord5(value.select);
2696
+ }
2697
+ function normalizeIdentifier(value, label) {
2698
+ const normalized = value.trim();
2699
+ if (!normalized) {
2700
+ throw new Error(`${label} must be a non-empty string`);
2701
+ }
2702
+ return normalized;
2703
+ }
2704
+ function stringifyFilterValue(value) {
2705
+ if (Array.isArray(value)) {
2706
+ return value.join(",");
2707
+ }
2708
+ return String(value);
2709
+ }
2710
+ function buildGatewayCondition(operator, column, value) {
2711
+ const condition = { operator };
2712
+ if (column) {
2713
+ condition.column = column;
2714
+ if (operator === "eq") {
2715
+ condition.eq_column = column;
2716
+ }
2717
+ }
2718
+ if (value !== void 0) {
2719
+ condition.value = value;
2720
+ if (operator === "eq") {
2721
+ condition.eq_value = value;
2722
+ }
2723
+ }
2724
+ if (operator === "eq" && column && value !== void 0 && shouldUseUuidTextComparison(column, value)) {
2725
+ condition.column_cast = "text";
2726
+ condition.eq_column_cast = "text";
2727
+ }
2728
+ return condition;
2729
+ }
2730
+ function compileRelationToken(key, node) {
2731
+ const nested = compileSelectShape(node.select);
2732
+ const propertyKey = normalizeIdentifier(key, "select relation key");
2733
+ if (node.schema && node.via) {
2734
+ throw new Error(
2735
+ `findMany relation "${propertyKey}" cannot combine schema and via yet; use schema with the relation key, or use via without schema`
2736
+ );
2737
+ }
2738
+ const relationTokenBase = normalizeIdentifier(node.via ?? propertyKey, "select relation token");
2739
+ if (node.schema && relationTokenBase.includes(".")) {
2740
+ throw new Error(
2741
+ `findMany relation "${propertyKey}" already resolves to a qualified relation token; do not also set schema`
2742
+ );
2743
+ }
2744
+ const relationSchema = node.schema?.trim();
2745
+ const relationToken = relationSchema ? `${normalizeIdentifier(relationSchema, "select relation schema")}.${relationTokenBase}` : relationTokenBase;
2746
+ const alias = node.as?.trim() || (relationToken !== propertyKey ? propertyKey : "");
2747
+ const prefix = alias ? `${alias}:` : "";
2748
+ return `${prefix}${relationToken}(${nested})`;
2749
+ }
2750
+ function compileSelectShape(select) {
2751
+ if (!isRecord5(select)) {
2752
+ throw new Error("findMany select must be an object");
2753
+ }
2754
+ const tokens = [];
2755
+ for (const [rawKey, rawValue] of Object.entries(select)) {
2756
+ if (rawValue === void 0) {
2757
+ continue;
2758
+ }
2759
+ if (rawValue === true) {
2760
+ tokens.push(normalizeIdentifier(rawKey, "select column"));
2761
+ continue;
2762
+ }
2763
+ if (isRelationSelectNode(rawValue)) {
2764
+ tokens.push(compileRelationToken(rawKey, rawValue));
2765
+ continue;
2766
+ }
2767
+ throw new Error(`Unsupported select node for "${rawKey}"`);
2768
+ }
2769
+ if (tokens.length === 0) {
2770
+ throw new Error("findMany select requires at least one field");
2771
+ }
2772
+ return tokens.join(",");
2773
+ }
2774
+ function selectShapeUsesRelationSchema(select) {
2775
+ if (!isRecord5(select)) {
2776
+ return false;
2777
+ }
2778
+ for (const rawValue of Object.values(select)) {
2779
+ if (!isRelationSelectNode(rawValue)) {
2780
+ continue;
2781
+ }
2782
+ if (typeof rawValue.schema === "string" && rawValue.schema.trim().length > 0) {
2783
+ return true;
2784
+ }
2785
+ if (selectShapeUsesRelationSchema(rawValue.select)) {
2786
+ return true;
2787
+ }
2788
+ }
2789
+ return false;
2790
+ }
2791
+ function compileColumnWhere(column, input) {
2792
+ const normalizedColumn = normalizeIdentifier(column, "where column");
2793
+ if (!isRecord5(input)) {
2794
+ return [buildGatewayCondition("eq", normalizedColumn, input)];
2795
+ }
2796
+ const conditions = [];
2797
+ for (const [rawOperator, rawValue] of Object.entries(input)) {
2798
+ if (rawValue === void 0) {
2799
+ continue;
2800
+ }
2801
+ if (!FILTER_OPERATORS.has(rawOperator)) {
2802
+ throw new Error(`Unsupported where operator "${rawOperator}" on "${normalizedColumn}"`);
2803
+ }
2804
+ if ((rawOperator === "in" || rawOperator === "contains" || rawOperator === "containedBy") && !Array.isArray(rawValue)) {
2805
+ throw new Error(`where.${normalizedColumn}.${rawOperator} requires an array value`);
2806
+ }
2807
+ conditions.push(
2808
+ buildGatewayCondition(
2809
+ rawOperator,
2810
+ normalizedColumn,
2811
+ rawValue
2812
+ )
2813
+ );
2814
+ }
2815
+ if (conditions.length === 0) {
2816
+ throw new Error(`where.${normalizedColumn} requires at least one operator`);
2817
+ }
2818
+ return conditions;
2819
+ }
2820
+ function compileBooleanExpressionTerms(clause, label) {
2821
+ if (!isRecord5(clause)) {
2822
+ throw new Error(`findMany where.${label} clauses must be objects`);
2823
+ }
2824
+ const entries = Object.entries(clause).filter(([, value]) => value !== void 0);
2825
+ if (entries.length !== 1) {
2826
+ throw new Error(`findMany where.${label} clauses must target exactly one column`);
2827
+ }
2828
+ const [rawColumn, rawValue] = entries[0];
2829
+ const column = normalizeIdentifier(rawColumn, `where.${label} column`);
2830
+ if (!isRecord5(rawValue)) {
2831
+ return [`${column}.eq.${stringifyFilterValue(rawValue)}`];
2832
+ }
2833
+ const operatorEntries = Object.entries(rawValue).filter(([, value]) => value !== void 0);
2834
+ if (operatorEntries.length === 0) {
2835
+ throw new Error(`findMany where.${label}.${column} requires at least one operator`);
2836
+ }
2837
+ if (label === "not" && operatorEntries.length > 1) {
2838
+ throw new Error("findMany where.not only supports a single lossless operator expression");
2839
+ }
2840
+ return operatorEntries.map(([rawOperator, rawOperand]) => {
2841
+ if (!BOOLEAN_SAFE_OPERATORS.has(rawOperator)) {
2842
+ throw new Error(`findMany where.${label} only supports lossless scalar operators`);
2843
+ }
2844
+ if (Array.isArray(rawOperand)) {
2845
+ throw new Error(`findMany where.${label} does not support array-valued operators`);
2846
+ }
2847
+ return `${column}.${rawOperator}.${stringifyFilterValue(rawOperand)}`;
2848
+ });
2849
+ }
2850
+ function compileWhere(where) {
2851
+ if (where === void 0) {
2852
+ return void 0;
2853
+ }
2854
+ if (!isRecord5(where)) {
2855
+ throw new Error("findMany where must be an object");
2856
+ }
2857
+ const conditions = [];
2858
+ for (const [rawKey, rawValue] of Object.entries(where)) {
2859
+ if (rawValue === void 0) {
2860
+ continue;
2861
+ }
2862
+ if (rawKey === "or") {
2863
+ if (!Array.isArray(rawValue) || rawValue.length === 0) {
2864
+ throw new Error("findMany where.or must be a non-empty array");
2865
+ }
2866
+ const expressions = rawValue.flatMap(
2867
+ (value) => compileBooleanExpressionTerms(value, "or")
2868
+ );
2869
+ conditions.push(buildGatewayCondition("or", void 0, expressions.join(",")));
2870
+ continue;
2871
+ }
2872
+ if (rawKey === "not") {
2873
+ const expressions = compileBooleanExpressionTerms(rawValue, "not");
2874
+ if (expressions.length !== 1) {
2875
+ throw new Error("findMany where.not must compile to exactly one lossless expression");
2876
+ }
2877
+ conditions.push(buildGatewayCondition("not", void 0, expressions[0]));
2878
+ continue;
2879
+ }
2880
+ conditions.push(...compileColumnWhere(rawKey, rawValue));
2881
+ }
2882
+ return conditions.length > 0 ? conditions : void 0;
2883
+ }
2884
+ function resolveOrderDirection(input) {
2885
+ if (typeof input === "boolean") {
2886
+ return input === false ? "descending" : "ascending";
2887
+ }
2888
+ if (typeof input === "string") {
2889
+ const normalized = input.trim().toLowerCase();
2890
+ if (normalized === "asc" || normalized === "ascending") {
2891
+ return "ascending";
2892
+ }
2893
+ if (normalized === "desc" || normalized === "descending") {
2894
+ return "descending";
2895
+ }
2896
+ throw new Error(`Unsupported orderBy direction "${input}"`);
2897
+ }
2898
+ return input.ascending === false ? "descending" : "ascending";
2899
+ }
2900
+ function compileOrderBy(orderBy) {
2901
+ if (orderBy === void 0) {
2902
+ return void 0;
2903
+ }
2904
+ if (!isRecord5(orderBy)) {
2905
+ throw new Error("findMany orderBy must be an object");
2906
+ }
2907
+ if ("column" in orderBy) {
2908
+ return {
2909
+ field: normalizeIdentifier(String(orderBy.column), "orderBy column"),
2910
+ direction: orderBy.ascending === false ? "descending" : "ascending"
2911
+ };
2912
+ }
2913
+ const entries = Object.entries(orderBy).filter(([, value]) => value !== void 0);
2914
+ if (entries.length === 0) {
2915
+ return void 0;
2916
+ }
2917
+ if (entries.length > 1) {
2918
+ throw new Error("findMany orderBy only supports a single column in v1");
2919
+ }
2920
+ const [column, input] = entries[0];
2921
+ return {
2922
+ field: normalizeIdentifier(column, "orderBy column"),
2923
+ direction: resolveOrderDirection(input)
2924
+ };
2925
+ }
2926
+
2927
+ // src/gateway/structured-select.ts
2928
+ var IDENTIFIER_SEGMENT_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
2929
+ function isIdentifierPath(value) {
2930
+ const segments = value.split(".").map((segment) => segment.trim());
2931
+ return segments.length > 1 && segments.every((segment) => IDENTIFIER_SEGMENT_PATTERN.test(segment));
2932
+ }
2933
+ function extractRelationHead(raw) {
2934
+ const trimmed = raw.trim();
2935
+ if (!trimmed) return "";
2936
+ const aliasIndex = trimmed.indexOf(":");
2937
+ const withoutAlias = aliasIndex >= 0 ? trimmed.slice(aliasIndex + 1).trim() : trimmed;
2938
+ const modifierIndex = withoutAlias.indexOf("!");
2939
+ return (modifierIndex >= 0 ? withoutAlias.slice(0, modifierIndex) : withoutAlias).trim();
2940
+ }
2941
+ function hasSchemaQualifiedRelationToken(select) {
2942
+ let singleQuoted = false;
2943
+ let doubleQuoted = false;
2944
+ let tokenStart = 0;
2945
+ for (let index = 0; index < select.length; index += 1) {
2946
+ const char = select[index];
2947
+ const next = index + 1 < select.length ? select[index + 1] : "";
2948
+ if (singleQuoted) {
2949
+ if (char === "'" && next === "'") {
2950
+ index += 1;
2951
+ continue;
2952
+ }
2953
+ if (char === "'") {
2954
+ singleQuoted = false;
2955
+ }
2956
+ continue;
2957
+ }
2958
+ if (doubleQuoted) {
2959
+ if (char === '"' && next === '"') {
2960
+ index += 1;
2961
+ continue;
2962
+ }
2963
+ if (char === '"') {
2964
+ doubleQuoted = false;
2965
+ }
2966
+ continue;
2967
+ }
2968
+ if (char === "'") {
2969
+ singleQuoted = true;
2970
+ continue;
2971
+ }
2972
+ if (char === '"') {
2973
+ doubleQuoted = true;
2974
+ continue;
2975
+ }
2976
+ if (char === "(") {
2977
+ const relationHead = extractRelationHead(select.slice(tokenStart, index));
2978
+ if (isIdentifierPath(relationHead)) {
2979
+ return true;
2980
+ }
2981
+ tokenStart = index + 1;
2982
+ continue;
2983
+ }
2984
+ if (char === "," || char === ")") {
2985
+ tokenStart = index + 1;
2986
+ }
2987
+ }
2988
+ return false;
2989
+ }
2990
+ function toStructuredSelectString(columns) {
2991
+ return Array.isArray(columns) ? columns.join(",") : columns;
2992
+ }
2993
+ function buildStructuredWhere(conditions) {
2994
+ if (!conditions?.length) return void 0;
2995
+ const where = {};
2996
+ for (const condition of conditions) {
2997
+ if (!condition.column) {
2998
+ return null;
2999
+ }
3000
+ if (condition.column_cast !== void 0 || condition.value_cast !== void 0) {
3001
+ return null;
3002
+ }
3003
+ const operand = condition.value;
3004
+ const operator = condition.operator;
3005
+ if (operator === "eq") {
3006
+ if (operand === void 0) return null;
3007
+ } else if (operator === "neq" || operator === "gt" || operator === "lt") {
3008
+ if (operand === void 0) return null;
3009
+ } else if (operator === "in") {
3010
+ if (!Array.isArray(operand)) return null;
3011
+ } else {
3012
+ return null;
3013
+ }
3014
+ const existing = where[condition.column];
3015
+ if (existing !== void 0 && (typeof existing !== "object" || existing === null || Array.isArray(existing))) {
3016
+ return null;
3017
+ }
3018
+ const next = existing ?? {};
3019
+ if (Object.prototype.hasOwnProperty.call(next, operator)) {
3020
+ return null;
3021
+ }
3022
+ next[operator] = operand;
3023
+ where[condition.column] = next;
3024
+ }
3025
+ return where;
3026
+ }
3027
+ function buildStructuredOrderBy(order) {
3028
+ if (!order?.field) return void 0;
3029
+ return {
3030
+ [order.field]: order.direction === "descending" ? "desc" : "asc"
3031
+ };
3032
+ }
3033
+ function buildStructuredSelectTransport(input) {
3034
+ const select = toStructuredSelectString(input.columns).trim();
3035
+ if (!select || select === "*") {
3036
+ return null;
3037
+ }
3038
+ if (!hasSchemaQualifiedRelationToken(select)) {
3039
+ return null;
3040
+ }
3041
+ if (input.count !== void 0 || input.head !== void 0) {
3042
+ return {
3043
+ error: "Schema-qualified nested select strings require structured select transport, which does not support count/head options in athena-js yet."
3044
+ };
3045
+ }
3046
+ const where = buildStructuredWhere(input.conditions);
3047
+ if (where === null) {
3048
+ return {
3049
+ error: "Schema-qualified nested select strings only support eq, neq, gt, lt, and in filters in athena-js structured select transport."
3050
+ };
3051
+ }
3052
+ return {
3053
+ select,
3054
+ payload: {
3055
+ table_name: input.tableName,
3056
+ select,
3057
+ where,
3058
+ orderBy: buildStructuredOrderBy(input.order),
3059
+ limit: input.limit,
3060
+ offset: input.offset,
3061
+ strip_nulls: input.stripNulls
3062
+ }
3063
+ };
3064
+ }
3065
+
3066
+ // src/query-transport.ts
3067
+ function canUseFindManyAstTransport(state) {
3068
+ return state.conditions.length === 0 && state.offset === void 0 && state.currentPage === void 0 && state.pageSize === void 0 && state.totalPages === void 0;
3069
+ }
3070
+ function toFindManyAstOrder(order) {
3071
+ if (!order) {
3072
+ return void 0;
3073
+ }
3074
+ return {
3075
+ column: order.field,
3076
+ ascending: order.direction !== "descending"
3077
+ };
3078
+ }
3079
+ function resolvePagination(input) {
3080
+ let limit = input.limit;
3081
+ let offset = input.offset;
3082
+ if (limit === void 0 && input.pageSize !== void 0) {
3083
+ limit = Math.max(0, Math.trunc(input.pageSize));
3084
+ }
3085
+ if (offset === void 0 && input.pageSize !== void 0 && input.currentPage !== void 0 && input.currentPage > 0) {
3086
+ offset = (Math.trunc(input.currentPage) - 1) * Math.max(0, Math.trunc(input.pageSize));
3087
+ }
3088
+ return { limit, offset };
3089
+ }
3090
+ function hasTypedEqualityComparison(conditions) {
3091
+ return conditions?.some(
3092
+ (condition) => condition.operator === "eq" && (condition.value_cast !== void 0 || condition.column_cast !== void 0)
3093
+ ) ?? false;
3094
+ }
3095
+ function createSelectTransportPlan(input) {
3096
+ const conditions = input.state.conditions.length ? input.state.conditions.map((condition) => ({ ...condition })) : void 0;
3097
+ if (hasTypedEqualityComparison(conditions) && !input.options?.head && !input.options?.count && conditions) {
3098
+ const query = input.buildTypedSelectQuery({
3099
+ tableName: input.tableName,
3100
+ columns: input.columns,
3101
+ conditions,
3102
+ limit: input.state.limit,
3103
+ offset: input.state.offset,
3104
+ currentPage: input.state.currentPage,
3105
+ pageSize: input.state.pageSize,
3106
+ order: input.state.order
3107
+ });
3108
+ if (query) {
3109
+ return {
3110
+ kind: "query",
3111
+ query,
3112
+ payload: { query }
3113
+ };
3114
+ }
3115
+ }
3116
+ const pagination = resolvePagination({
3117
+ limit: input.state.limit,
3118
+ offset: input.state.offset,
3119
+ currentPage: input.state.currentPage,
3120
+ pageSize: input.state.pageSize
3121
+ });
3122
+ const stripNulls = input.options?.stripNulls ?? true;
3123
+ const structuredSelectTransport = buildStructuredSelectTransport({
3124
+ tableName: input.tableName,
3125
+ columns: input.columns,
3126
+ conditions,
3127
+ limit: pagination.limit,
3128
+ offset: pagination.offset,
3129
+ order: input.state.order,
3130
+ stripNulls,
3131
+ count: input.options?.count,
3132
+ head: input.options?.head
3133
+ });
3134
+ if (structuredSelectTransport && "error" in structuredSelectTransport) {
3135
+ throw new Error(structuredSelectTransport.error);
3136
+ }
3137
+ if (structuredSelectTransport) {
3138
+ const { payload, select } = structuredSelectTransport;
3139
+ return {
3140
+ kind: "fetch",
3141
+ payload,
3142
+ debug: {
3143
+ columns: select,
3144
+ conditions,
3145
+ limit: pagination.limit,
3146
+ offset: pagination.offset,
3147
+ order: input.state.order
3148
+ }
3149
+ };
3150
+ }
3151
+ return {
3152
+ kind: "fetch",
3153
+ payload: {
3154
+ table_name: input.tableName,
3155
+ columns: input.columns,
3156
+ conditions,
3157
+ limit: input.state.limit,
3158
+ offset: input.state.offset,
3159
+ current_page: input.state.currentPage,
3160
+ page_size: input.state.pageSize,
3161
+ total_pages: input.state.totalPages,
3162
+ sort_by: input.state.order,
3163
+ strip_nulls: stripNulls,
3164
+ count: input.options?.count,
3165
+ head: input.options?.head
3166
+ },
3167
+ debug: {
3168
+ columns: input.columns,
3169
+ conditions,
3170
+ limit: input.state.limit,
3171
+ offset: input.state.offset,
3172
+ currentPage: input.state.currentPage,
3173
+ pageSize: input.state.pageSize,
3174
+ order: input.state.order
3175
+ }
3176
+ };
3177
+ }
3178
+
2032
3179
  // src/client.ts
2033
3180
  var DEFAULT_COLUMNS = "*";
2034
- 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;
2035
3181
  var SAFE_CAST_PATTERN = /^[a-z_][a-z0-9_]*(?:\[\])?$/i;
2036
3182
  var ATHENA_NORMALIZED_ERROR_KEY = "__athenaNormalizedError";
3183
+ var QUERY_TRACE_STACK_SKIP_PATTERNS = [
3184
+ "src\\client.ts",
3185
+ "src/client.ts",
3186
+ "dist\\client.",
3187
+ "dist/client.",
3188
+ "node_modules\\@xylex-group\\athena",
3189
+ "node_modules/@xylex-group/athena",
3190
+ "node:internal",
3191
+ "internal/process"
3192
+ ];
2037
3193
  function formatResult(response) {
2038
3194
  const result = {
2039
3195
  data: response.data ?? null,
2040
- error: response.error ?? null,
3196
+ error: null,
2041
3197
  errorDetails: response.errorDetails ?? null,
2042
3198
  status: response.status,
3199
+ statusText: response.statusText ?? null,
2043
3200
  raw: response.raw
2044
3201
  };
2045
3202
  if (response.count !== void 0) {
@@ -2055,20 +3212,237 @@ function attachNormalizedError(result, normalizedError) {
2055
3212
  writable: false
2056
3213
  });
2057
3214
  }
2058
- function createResultFormatter(experimental) {
2059
- if (!experimental?.enableErrorNormalization) {
2060
- return formatResult;
3215
+ function createResultFormatter(experimental) {
3216
+ return (response, context) => {
3217
+ const result = formatResult(response);
3218
+ if (response.error == null && response.errorDetails == null) {
3219
+ return result;
3220
+ }
3221
+ const normalizedError = normalizeAthenaError(
3222
+ {
3223
+ ...result,
3224
+ error: response.error ?? response.errorDetails?.message ?? null
3225
+ },
3226
+ context
3227
+ );
3228
+ result.error = createResultError(response, result, normalizedError);
3229
+ attachNormalizedError(result, normalizedError);
3230
+ return result;
3231
+ };
3232
+ }
3233
+ function isRecord6(value) {
3234
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3235
+ }
3236
+ function firstNonEmptyString2(...values) {
3237
+ for (const value of values) {
3238
+ if (typeof value === "string" && value.trim().length > 0) {
3239
+ return value.trim();
3240
+ }
3241
+ }
3242
+ return void 0;
3243
+ }
3244
+ function resolveStructuredErrorPayload2(raw) {
3245
+ if (!isRecord6(raw)) return null;
3246
+ return isRecord6(raw.error) ? raw.error : raw;
3247
+ }
3248
+ function resolveStructuredErrorDetails(payload, message) {
3249
+ if (!payload || !("details" in payload)) {
3250
+ return null;
3251
+ }
3252
+ const details = payload.details;
3253
+ if (details == null) {
3254
+ return null;
3255
+ }
3256
+ if (typeof details === "string" && details.trim() === message.trim()) {
3257
+ return null;
3258
+ }
3259
+ return details;
3260
+ }
3261
+ function createResultError(response, result, normalized) {
3262
+ const rawRecord = isRecord6(response.raw) ? response.raw : null;
3263
+ const payload = resolveStructuredErrorPayload2(response.raw);
3264
+ const message = firstNonEmptyString2(
3265
+ response.error,
3266
+ payload?.message,
3267
+ payload?.error,
3268
+ payload?.details,
3269
+ response.errorDetails?.message,
3270
+ normalized.message
3271
+ ) ?? normalized.message;
3272
+ const statusText = firstNonEmptyString2(response.statusText, rawRecord?.statusText) ?? null;
3273
+ const hint = firstNonEmptyString2(payload?.hint, response.errorDetails?.hint) ?? null;
3274
+ const code = firstNonEmptyString2(payload?.code) ?? normalized.code;
3275
+ const details = resolveStructuredErrorDetails(payload, message) ?? response.errorDetails?.cause ?? null;
3276
+ return {
3277
+ message,
3278
+ code,
3279
+ athenaCode: normalized.code,
3280
+ gatewayCode: response.errorDetails?.code ?? null,
3281
+ kind: normalized.kind,
3282
+ category: normalized.category,
3283
+ retryable: normalized.retryable,
3284
+ details,
3285
+ hint,
3286
+ status: result.status,
3287
+ statusText,
3288
+ constraint: normalized.constraint,
3289
+ table: normalized.table,
3290
+ operation: normalized.operation,
3291
+ endpoint: response.errorDetails?.endpoint,
3292
+ method: response.errorDetails?.method,
3293
+ requestId: response.errorDetails?.requestId,
3294
+ cause: response.errorDetails?.cause,
3295
+ raw: result.raw
3296
+ };
3297
+ }
3298
+ function parseQueryTraceCallsiteFrame(frame) {
3299
+ const trimmed = frame.trim();
3300
+ if (!trimmed) {
3301
+ return null;
3302
+ }
3303
+ let body = trimmed.replace(/^at\s+/, "");
3304
+ if (body.startsWith("async ")) {
3305
+ body = body.slice(6);
3306
+ }
3307
+ let functionName;
3308
+ let location = body;
3309
+ const wrappedMatch = body.match(/^(.*?)\s+\((.*)\)$/);
3310
+ if (wrappedMatch) {
3311
+ functionName = wrappedMatch[1].trim() || void 0;
3312
+ location = wrappedMatch[2].trim();
3313
+ }
3314
+ const locationMatch = location.match(/^(.*):(\d+):(\d+)$/);
3315
+ if (!locationMatch) {
3316
+ return null;
3317
+ }
3318
+ const filePath = locationMatch[1].replace(/^file:\/\//, "");
3319
+ const line = Number(locationMatch[2]);
3320
+ const column = Number(locationMatch[3]);
3321
+ if (!Number.isFinite(line) || !Number.isFinite(column)) {
3322
+ return null;
3323
+ }
3324
+ const normalizedPath = filePath.replace(/\\/g, "/");
3325
+ const fileName = normalizedPath.split("/").at(-1) ?? filePath;
3326
+ return {
3327
+ filePath,
3328
+ fileName,
3329
+ line,
3330
+ column,
3331
+ frame: trimmed,
3332
+ functionName
3333
+ };
3334
+ }
3335
+ function captureQueryTraceCallsite() {
3336
+ const stack = new Error().stack;
3337
+ if (!stack) return null;
3338
+ const frames = stack.split("\n").slice(2).map((frame) => frame.trim()).filter(Boolean);
3339
+ for (const frame of frames) {
3340
+ if (QUERY_TRACE_STACK_SKIP_PATTERNS.some((pattern) => frame.includes(pattern))) {
3341
+ continue;
3342
+ }
3343
+ const callsite = parseQueryTraceCallsiteFrame(frame);
3344
+ if (callsite) return callsite;
3345
+ }
3346
+ const fallback = frames.find((frame) => !frame.includes("captureQueryTraceCallsite"));
3347
+ return fallback ? parseQueryTraceCallsiteFrame(fallback) : null;
3348
+ }
3349
+ function defaultQueryTraceLogger(event) {
3350
+ const target = event.table ?? event.functionName ?? "gateway";
3351
+ const outcomeState = event.outcome?.error ? "error" : "ok";
3352
+ const banner = `[athena-js][trace] ${event.operation.toUpperCase()} ${event.endpoint} ${target} ${event.durationMs}ms ${outcomeState}`;
3353
+ console.info(banner, event);
3354
+ }
3355
+ function captureTraceCallsite(tracer) {
3356
+ return tracer?.captureCallsite() ?? null;
3357
+ }
3358
+ function createTraceCallsiteStore(tracer, initialCallsite) {
3359
+ let storedCallsite = initialCallsite ?? void 0;
3360
+ return {
3361
+ resolve(callsite) {
3362
+ if (callsite) {
3363
+ storedCallsite = callsite;
3364
+ return callsite;
3365
+ }
3366
+ if (storedCallsite !== void 0) {
3367
+ return storedCallsite;
3368
+ }
3369
+ const capturedCallsite = captureTraceCallsite(tracer);
3370
+ if (capturedCallsite) {
3371
+ storedCallsite = capturedCallsite;
3372
+ }
3373
+ return capturedCallsite;
3374
+ }
3375
+ };
3376
+ }
3377
+ function createQueryTracer(experimental) {
3378
+ const traceOption = experimental?.traceQueries;
3379
+ if (!traceOption) {
3380
+ return void 0;
2061
3381
  }
2062
- return (response, context) => {
2063
- const result = formatResult(response);
2064
- if (result.error == null) {
2065
- return result;
3382
+ const logger = typeof traceOption === "object" && traceOption.logger ? traceOption.logger : defaultQueryTraceLogger;
3383
+ const emit = (event) => {
3384
+ try {
3385
+ logger(event);
3386
+ } catch (error) {
3387
+ console.warn("[athena-js][trace] logger failed", error);
3388
+ }
3389
+ };
3390
+ return {
3391
+ captureCallsite: captureQueryTraceCallsite,
3392
+ publishSuccess(context, result, durationMs, callsite) {
3393
+ emit({
3394
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3395
+ durationMs,
3396
+ operation: context.operation,
3397
+ endpoint: context.endpoint,
3398
+ table: context.table,
3399
+ functionName: context.functionName,
3400
+ sql: context.sql,
3401
+ payload: context.payload,
3402
+ options: context.options,
3403
+ callsite,
3404
+ outcome: {
3405
+ status: result.status,
3406
+ error: result.error,
3407
+ errorDetails: result.errorDetails ?? null,
3408
+ count: result.count ?? null,
3409
+ data: result.data,
3410
+ raw: result.raw
3411
+ }
3412
+ });
3413
+ },
3414
+ publishFailure(context, error, durationMs, callsite) {
3415
+ emit({
3416
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3417
+ durationMs,
3418
+ operation: context.operation,
3419
+ endpoint: context.endpoint,
3420
+ table: context.table,
3421
+ functionName: context.functionName,
3422
+ sql: context.sql,
3423
+ payload: context.payload,
3424
+ options: context.options,
3425
+ callsite,
3426
+ thrownError: error
3427
+ });
2066
3428
  }
2067
- const normalizedError = normalizeAthenaError(result, context);
2068
- attachNormalizedError(result, normalizedError);
2069
- return result;
2070
3429
  };
2071
3430
  }
3431
+ async function executeWithQueryTrace(tracer, context, runner, callsiteOverride) {
3432
+ if (!tracer) {
3433
+ return runner();
3434
+ }
3435
+ const callsite = callsiteOverride ?? tracer.captureCallsite();
3436
+ const startedAt = Date.now();
3437
+ try {
3438
+ const result = await runner();
3439
+ tracer.publishSuccess(context, result, Date.now() - startedAt, callsite);
3440
+ return result;
3441
+ } catch (error) {
3442
+ tracer.publishFailure(context, error, Date.now() - startedAt, callsite);
3443
+ throw error;
3444
+ }
3445
+ }
2072
3446
  function toSingleResult(response) {
2073
3447
  const payload = response.data;
2074
3448
  const singleData = Array.isArray(payload) ? payload.length ? payload[0] : null : payload ?? null;
@@ -2089,15 +3463,16 @@ function asAthenaJsonObject(value) {
2089
3463
  function asAthenaJsonObjectArray(values) {
2090
3464
  return values;
2091
3465
  }
2092
- function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
3466
+ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS, tracer, initialCallsite) {
2093
3467
  let selectedColumns = defaultColumns === null ? void 0 : defaultColumns;
2094
3468
  let selectedOptions;
2095
3469
  let promise = null;
2096
- const run = (columns, options) => {
3470
+ const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
3471
+ const run = (columns, options, callsite) => {
2097
3472
  const payloadColumns = columns ?? selectedColumns;
2098
3473
  const payloadOptions = options ?? selectedOptions;
2099
3474
  if (!promise) {
2100
- promise = executor(payloadColumns, payloadOptions);
3475
+ promise = executor(payloadColumns, payloadOptions, callsiteStore.resolve(callsite));
2101
3476
  }
2102
3477
  return promise;
2103
3478
  };
@@ -2105,7 +3480,7 @@ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
2105
3480
  select(columns = selectedColumns, options) {
2106
3481
  selectedColumns = columns;
2107
3482
  selectedOptions = options ?? selectedOptions;
2108
- return run(columns, options);
3483
+ return run(columns, options, captureTraceCallsite(tracer));
2109
3484
  },
2110
3485
  returning(columns = selectedColumns, options) {
2111
3486
  return mutationQuery.select(columns, options);
@@ -2113,7 +3488,7 @@ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
2113
3488
  single(columns = selectedColumns, options) {
2114
3489
  selectedColumns = columns;
2115
3490
  selectedOptions = options ?? selectedOptions;
2116
- return run(columns, options).then(toSingleResult);
3491
+ return run(columns, options, captureTraceCallsite(tracer)).then(toSingleResult);
2117
3492
  },
2118
3493
  maybeSingle(columns = selectedColumns, options) {
2119
3494
  return mutationQuery.single(columns, options);
@@ -2136,21 +3511,12 @@ function getResourceId(state) {
2136
3511
  );
2137
3512
  return candidate?.value?.toString();
2138
3513
  }
2139
- function stringifyFilterValue(value) {
3514
+ function stringifyFilterValue2(value) {
2140
3515
  if (Array.isArray(value)) {
2141
3516
  return value.join(",");
2142
3517
  }
2143
3518
  return String(value);
2144
3519
  }
2145
- function isUuidString(value) {
2146
- return UUID_PATTERN.test(value.trim());
2147
- }
2148
- function isUuidIdentifierColumn(column) {
2149
- return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
2150
- }
2151
- function shouldUseUuidTextComparison(column, value) {
2152
- return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
2153
- }
2154
3520
  function normalizeCast(cast) {
2155
3521
  const normalized = cast.trim().toLowerCase();
2156
3522
  if (!SAFE_CAST_PATTERN.test(normalized)) {
@@ -2173,25 +3539,92 @@ function withCast(expression, cast) {
2173
3539
  }
2174
3540
  function buildSelectColumnsClause(columns) {
2175
3541
  if (Array.isArray(columns)) {
2176
- return columns.map((column) => quoteQualifiedIdentifier(column)).join(", ");
3542
+ return columns.map((column) => quoteSelectColumnToken(column)).join(", ");
2177
3543
  }
2178
3544
  return quoteSelectColumnsExpression(columns);
2179
3545
  }
3546
+ function parseIdentifierSegment(input) {
3547
+ const trimmed = input.trim();
3548
+ if (!trimmed) return null;
3549
+ if (!trimmed.startsWith('"')) {
3550
+ return {
3551
+ normalizedValue: trimmed.toLowerCase()
3552
+ };
3553
+ }
3554
+ let value = "";
3555
+ let index = 1;
3556
+ let closed = false;
3557
+ while (index < trimmed.length) {
3558
+ const char = trimmed[index];
3559
+ const next = index + 1 < trimmed.length ? trimmed[index + 1] : "";
3560
+ if (char === '"' && next === '"') {
3561
+ value += '"';
3562
+ index += 2;
3563
+ continue;
3564
+ }
3565
+ if (char === '"') {
3566
+ closed = true;
3567
+ index += 1;
3568
+ break;
3569
+ }
3570
+ value += char;
3571
+ index += 1;
3572
+ }
3573
+ if (!closed || trimmed.slice(index).trim().length > 0 || !value.trim()) {
3574
+ return null;
3575
+ }
3576
+ return {
3577
+ normalizedValue: value
3578
+ };
3579
+ }
3580
+ function splitQualifiedTableName(tableName) {
3581
+ const trimmed = tableName.trim();
3582
+ let inQuotes = false;
3583
+ for (let index = 0; index < trimmed.length; index += 1) {
3584
+ const char = trimmed[index];
3585
+ const next = index + 1 < trimmed.length ? trimmed[index + 1] : "";
3586
+ if (char === '"') {
3587
+ if (inQuotes && next === '"') {
3588
+ index += 1;
3589
+ continue;
3590
+ }
3591
+ inQuotes = !inQuotes;
3592
+ continue;
3593
+ }
3594
+ if (char === "." && !inQuotes) {
3595
+ const schemaSegment = trimmed.slice(0, index).trim();
3596
+ const tableSegment = trimmed.slice(index + 1).trim();
3597
+ if (!schemaSegment || !tableSegment) {
3598
+ return null;
3599
+ }
3600
+ return { schemaSegment };
3601
+ }
3602
+ }
3603
+ return null;
3604
+ }
2180
3605
  function resolveTableNameForCall(tableName, schema) {
2181
3606
  if (!schema) return tableName;
2182
3607
  const normalizedSchema = schema.trim();
2183
3608
  if (!normalizedSchema) {
2184
3609
  throw new Error("schema option must be a non-empty string");
2185
3610
  }
2186
- if (tableName.includes(".")) {
2187
- if (tableName.startsWith(`${normalizedSchema}.`)) {
2188
- return tableName;
3611
+ const normalizedTableName = tableName.trim();
3612
+ const parsedSchema = parseIdentifierSegment(normalizedSchema);
3613
+ if (!parsedSchema) {
3614
+ throw new Error("schema option must be a non-empty string");
3615
+ }
3616
+ const qualified = splitQualifiedTableName(normalizedTableName);
3617
+ if (qualified) {
3618
+ const parsedTableSchema = parseIdentifierSegment(qualified.schemaSegment);
3619
+ const sameSchema = parsedTableSchema ? parsedTableSchema.normalizedValue === parsedSchema.normalizedValue : normalizedTableName.startsWith(`${normalizedSchema}.`);
3620
+ if (sameSchema) {
3621
+ return normalizedTableName;
2189
3622
  }
2190
3623
  throw new Error(
2191
- `schema option "${normalizedSchema}" conflicts with schema-qualified table "${tableName}"`
3624
+ `schema option "${normalizedSchema}" conflicts with schema-qualified table "${normalizedTableName}"`
2192
3625
  );
2193
3626
  }
2194
- return `${normalizedSchema}.${tableName}`;
3627
+ return `${normalizedSchema}.${normalizedTableName}`;
2195
3628
  }
2196
3629
  function conditionToSqlClause(condition) {
2197
3630
  if (!condition.column) return null;
@@ -2269,6 +3702,226 @@ function buildTypedSelectQuery(input) {
2269
3702
  }
2270
3703
  return `${sqlParts.join(" ")};`;
2271
3704
  }
3705
+ function sanitizeSqlComment(comment) {
3706
+ return comment.replace(/\*\//g, "* /");
3707
+ }
3708
+ function toSqlJsonLiteral(value) {
3709
+ if (value === void 0) return "DEFAULT";
3710
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
3711
+ return toSqlLiteral(value);
3712
+ }
3713
+ return `'${escapeSqlStringLiteral(JSON.stringify(value))}'::jsonb`;
3714
+ }
3715
+ function conditionToDebugSqlClause(condition) {
3716
+ const exact = conditionToSqlClause(condition);
3717
+ if (exact) return exact;
3718
+ const rawCondition = sanitizeSqlComment(JSON.stringify(condition));
3719
+ if (!condition.column) {
3720
+ return `TRUE /* unsupported condition: ${rawCondition} */`;
3721
+ }
3722
+ const column = withCast(quoteQualifiedIdentifier(condition.column), condition.column_cast);
3723
+ const value = condition.value;
3724
+ const rhs = withCast(toSqlJsonLiteral(value), condition.value_cast);
3725
+ switch (condition.operator) {
3726
+ case "contains":
3727
+ return `${column} @> ${rhs}`;
3728
+ case "containedBy":
3729
+ return `${column} <@ ${rhs}`;
3730
+ case "not":
3731
+ return `TRUE /* NOT expression passthrough: ${rawCondition} */`;
3732
+ case "or":
3733
+ return `TRUE /* OR expression passthrough: ${rawCondition} */`;
3734
+ default:
3735
+ return `TRUE /* unsupported condition: ${rawCondition} */`;
3736
+ }
3737
+ }
3738
+ function appendOrderLimitOffset(sqlParts, order, limit, offset) {
3739
+ if (order?.field) {
3740
+ const direction = order.direction === "descending" ? "DESC" : "ASC";
3741
+ sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(order.field)} ${direction}`);
3742
+ }
3743
+ if (limit !== void 0) {
3744
+ sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(limit))}`);
3745
+ }
3746
+ if (offset !== void 0) {
3747
+ sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(offset))}`);
3748
+ }
3749
+ }
3750
+ function buildDebugSelectQuery(input) {
3751
+ const sqlParts = [
3752
+ `SELECT ${buildSelectColumnsClause(input.columns)} FROM ${quoteQualifiedIdentifier(input.tableName)}`
3753
+ ];
3754
+ if (input.conditions?.length) {
3755
+ const whereClauses = input.conditions.map(conditionToDebugSqlClause);
3756
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
3757
+ }
3758
+ const pagination = resolvePagination(input);
3759
+ appendOrderLimitOffset(sqlParts, input.order, pagination.limit, pagination.offset);
3760
+ return `${sqlParts.join(" ")};`;
3761
+ }
3762
+ function resolveDebugTableIdentifier(tableName) {
3763
+ if (!tableName?.trim()) {
3764
+ return '"__unknown_table__"';
3765
+ }
3766
+ return quoteQualifiedIdentifier(tableName);
3767
+ }
3768
+ function buildInsertDebugSql(payload) {
3769
+ const rows = Array.isArray(payload.insert_body) ? payload.insert_body : [payload.insert_body];
3770
+ const columns = [];
3771
+ const seen = /* @__PURE__ */ new Set();
3772
+ for (const row of rows) {
3773
+ for (const column of Object.keys(row)) {
3774
+ if (seen.has(column)) continue;
3775
+ seen.add(column);
3776
+ columns.push(column);
3777
+ }
3778
+ }
3779
+ const sqlParts = [`INSERT INTO ${quoteQualifiedIdentifier(payload.table_name)}`];
3780
+ if (!rows.length || !columns.length) {
3781
+ sqlParts.push("DEFAULT VALUES");
3782
+ if (rows.length > 1) {
3783
+ sqlParts.push(`/* trace: ${rows.length} rows collapsed to DEFAULT VALUES */`);
3784
+ }
3785
+ } else {
3786
+ const valuesClause = rows.map((row) => {
3787
+ const values = columns.map((column) => {
3788
+ const hasColumn = Object.prototype.hasOwnProperty.call(row, column);
3789
+ if (!hasColumn) {
3790
+ return payload.default_to_null ? "NULL" : "DEFAULT";
3791
+ }
3792
+ const rowValue = row[column];
3793
+ return toSqlJsonLiteral(rowValue);
3794
+ });
3795
+ return `(${values.join(", ")})`;
3796
+ }).join(", ");
3797
+ const columnClause = columns.map((column) => quoteQualifiedIdentifier(column)).join(", ");
3798
+ sqlParts.push(`(${columnClause})`);
3799
+ sqlParts.push(`VALUES ${valuesClause}`);
3800
+ }
3801
+ if (payload.on_conflict) {
3802
+ const conflictColumns = Array.isArray(payload.on_conflict) ? payload.on_conflict.map((column) => quoteQualifiedIdentifier(column)).join(", ") : payload.on_conflict;
3803
+ if (payload.update_body && Object.keys(payload.update_body).length > 0) {
3804
+ const assignments = Object.entries(payload.update_body).map(
3805
+ ([column, value]) => `${quoteQualifiedIdentifier(column)} = ${toSqlJsonLiteral(value)}`
3806
+ );
3807
+ sqlParts.push(`ON CONFLICT (${conflictColumns}) DO UPDATE SET ${assignments.join(", ")}`);
3808
+ } else {
3809
+ sqlParts.push(`ON CONFLICT (${conflictColumns}) DO NOTHING`);
3810
+ }
3811
+ }
3812
+ if (payload.columns) {
3813
+ sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
3814
+ }
3815
+ return `${sqlParts.join(" ")};`;
3816
+ }
3817
+ function buildUpdateDebugSql(payload) {
3818
+ const set = payload.set ?? payload.data ?? {};
3819
+ const assignments = Object.entries(set).map(
3820
+ ([column, value]) => `${quoteQualifiedIdentifier(column)} = ${toSqlJsonLiteral(value)}`
3821
+ );
3822
+ const sqlParts = [
3823
+ `UPDATE ${resolveDebugTableIdentifier(payload.table_name)} SET ${assignments.length ? assignments.join(", ") : "/* empty set */"}`
3824
+ ];
3825
+ if (payload.conditions?.length) {
3826
+ const whereClauses = payload.conditions.map(conditionToDebugSqlClause);
3827
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
3828
+ }
3829
+ const pagination = resolvePagination({
3830
+ currentPage: payload.current_page,
3831
+ pageSize: payload.page_size
3832
+ });
3833
+ appendOrderLimitOffset(sqlParts, payload.sort_by, pagination.limit, pagination.offset);
3834
+ if (payload.columns) {
3835
+ sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
3836
+ }
3837
+ return `${sqlParts.join(" ")};`;
3838
+ }
3839
+ function buildDeleteDebugSql(payload) {
3840
+ const sqlParts = [`DELETE FROM ${quoteQualifiedIdentifier(payload.table_name)}`];
3841
+ const whereClauses = [];
3842
+ if (payload.resource_id) {
3843
+ whereClauses.push(`"resource_id" = ${toSqlLiteral(payload.resource_id)}`);
3844
+ }
3845
+ if (payload.conditions?.length) {
3846
+ whereClauses.push(...payload.conditions.map(conditionToDebugSqlClause));
3847
+ }
3848
+ if (whereClauses.length) {
3849
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
3850
+ }
3851
+ const pagination = resolvePagination({
3852
+ currentPage: payload.current_page,
3853
+ pageSize: payload.page_size
3854
+ });
3855
+ appendOrderLimitOffset(sqlParts, payload.sort_by, pagination.limit, pagination.offset);
3856
+ if (payload.columns) {
3857
+ sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
3858
+ }
3859
+ return `${sqlParts.join(" ")};`;
3860
+ }
3861
+ function rpcFilterToSqlClause(filter) {
3862
+ const column = quoteQualifiedIdentifier(filter.column);
3863
+ const value = filter.value;
3864
+ switch (filter.operator) {
3865
+ case "eq":
3866
+ case "neq":
3867
+ case "gt":
3868
+ case "gte":
3869
+ case "lt":
3870
+ case "lte":
3871
+ case "like":
3872
+ case "ilike": {
3873
+ if (value === void 0 || Array.isArray(value)) {
3874
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
3875
+ }
3876
+ const operatorMap = {
3877
+ eq: "=",
3878
+ neq: "!=",
3879
+ gt: ">",
3880
+ gte: ">=",
3881
+ lt: "<",
3882
+ lte: "<=",
3883
+ like: "LIKE",
3884
+ ilike: "ILIKE"
3885
+ };
3886
+ return `${column} ${operatorMap[filter.operator]} ${toSqlLiteral(value)}`;
3887
+ }
3888
+ case "is":
3889
+ if (value === null) return `${column} IS NULL`;
3890
+ if (value === true) return `${column} IS TRUE`;
3891
+ if (value === false) return `${column} IS FALSE`;
3892
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
3893
+ case "in":
3894
+ if (!Array.isArray(value)) {
3895
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
3896
+ }
3897
+ if (value.length === 0) return "FALSE";
3898
+ return `${column} IN (${value.map((item) => toSqlLiteral(item)).join(", ")})`;
3899
+ default:
3900
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
3901
+ }
3902
+ }
3903
+ function buildRpcDebugSql(payload) {
3904
+ const argsEntries = payload.args ? Object.entries(payload.args) : [];
3905
+ const argsClause = argsEntries.map(([key, value]) => `${quoteQualifiedIdentifier(key)} => ${toSqlJsonLiteral(value)}`).join(", ");
3906
+ const functionRef = payload.schema ? `${quoteQualifiedIdentifier(payload.schema)}.${quoteQualifiedIdentifier(payload.function)}` : quoteQualifiedIdentifier(payload.function);
3907
+ const sqlParts = [
3908
+ `SELECT ${payload.select ? quoteSelectColumnsExpression(payload.select) : "*"} FROM ${functionRef}(${argsClause})`
3909
+ ];
3910
+ if (payload.filters?.length) {
3911
+ sqlParts.push(`WHERE ${payload.filters.map(rpcFilterToSqlClause).join(" AND ")}`);
3912
+ }
3913
+ if (payload.order?.column) {
3914
+ const direction = payload.order.ascending === false ? "DESC" : "ASC";
3915
+ sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(payload.order.column)} ${direction}`);
3916
+ }
3917
+ if (payload.limit !== void 0) {
3918
+ sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(payload.limit))}`);
3919
+ }
3920
+ if (payload.offset !== void 0) {
3921
+ sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(payload.offset))}`);
3922
+ }
3923
+ return `${sqlParts.join(" ")};`;
3924
+ }
2272
3925
  function createFilterMethods(state, addCondition, self) {
2273
3926
  return {
2274
3927
  eq(column, value) {
@@ -2380,7 +4033,7 @@ function createFilterMethods(state, addCondition, self) {
2380
4033
  not(columnOrExpression, operator, value) {
2381
4034
  const expression = String(columnOrExpression);
2382
4035
  if (operator != null && value !== void 0) {
2383
- addCondition("not", void 0, `${expression}.${operator}.${stringifyFilterValue(value)}`);
4036
+ addCondition("not", void 0, `${expression}.${operator}.${stringifyFilterValue2(value)}`);
2384
4037
  } else {
2385
4038
  addCondition("not", void 0, expression);
2386
4039
  }
@@ -2443,14 +4096,15 @@ function createRpcFilterMethods(filters, self) {
2443
4096
  }
2444
4097
  };
2445
4098
  }
2446
- function createRpcBuilder(functionName, args, baseOptions, client, formatGatewayResult) {
4099
+ function createRpcBuilder(functionName, args, baseOptions, client, formatGatewayResult, tracer, initialCallsite) {
2447
4100
  const state = {
2448
4101
  filters: []
2449
4102
  };
2450
4103
  let selectedColumns;
2451
4104
  let selectedOptions;
2452
4105
  let promise = null;
2453
- const executeRpc = async (columns, options) => {
4106
+ const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
4107
+ const executeRpc = async (columns, options, callsite) => {
2454
4108
  const mergedOptions = mergeOptions(baseOptions, options);
2455
4109
  const payload = {
2456
4110
  function: functionName,
@@ -2464,14 +4118,30 @@ function createRpcBuilder(functionName, args, baseOptions, client, formatGateway
2464
4118
  offset: state.offset,
2465
4119
  order: state.order
2466
4120
  };
2467
- const response = await client.rpcGateway(payload, mergedOptions);
2468
- return formatGatewayResult(response, { operation: "rpc" });
4121
+ const endpoint = mergedOptions?.get ? `/rpc/${functionName}` : "/gateway/rpc";
4122
+ const sql = buildRpcDebugSql(payload);
4123
+ return executeWithQueryTrace(
4124
+ tracer,
4125
+ {
4126
+ operation: "rpc",
4127
+ endpoint,
4128
+ functionName,
4129
+ sql,
4130
+ payload,
4131
+ options: mergedOptions
4132
+ },
4133
+ async () => {
4134
+ const response = await client.rpcGateway(payload, mergedOptions);
4135
+ return formatGatewayResult(response, { operation: "rpc" });
4136
+ },
4137
+ callsite
4138
+ );
2469
4139
  };
2470
- const run = (columns, options) => {
4140
+ const run = (columns, options, callsite) => {
2471
4141
  const payloadColumns = columns ?? selectedColumns;
2472
4142
  const payloadOptions = options ?? selectedOptions;
2473
4143
  if (!promise) {
2474
- promise = executeRpc(payloadColumns, payloadOptions);
4144
+ promise = executeRpc(payloadColumns, payloadOptions, callsiteStore.resolve(callsite));
2475
4145
  }
2476
4146
  return promise;
2477
4147
  };
@@ -2481,10 +4151,10 @@ function createRpcBuilder(functionName, args, baseOptions, client, formatGateway
2481
4151
  select(columns = selectedColumns, options) {
2482
4152
  selectedColumns = columns;
2483
4153
  selectedOptions = options ?? selectedOptions;
2484
- return run(columns, options);
4154
+ return run(columns, options, captureTraceCallsite(tracer));
2485
4155
  },
2486
4156
  async single(columns, options) {
2487
- const result = await run(columns, options);
4157
+ const result = await run(columns, options, captureTraceCallsite(tracer));
2488
4158
  return toSingleResult(result);
2489
4159
  },
2490
4160
  maybeSingle(columns, options) {
@@ -2519,7 +4189,7 @@ function createRpcBuilder(functionName, args, baseOptions, client, formatGateway
2519
4189
  });
2520
4190
  return builder;
2521
4191
  }
2522
- function createTableBuilder(tableName, client, formatGatewayResult) {
4192
+ function createTableBuilder(tableName, client, formatGatewayResult, tracer, experimental) {
2523
4193
  const state = {
2524
4194
  conditions: []
2525
4195
  };
@@ -2551,70 +4221,103 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2551
4221
  }
2552
4222
  state.conditions.push(condition);
2553
4223
  };
4224
+ const snapshotState = () => ({
4225
+ conditions: state.conditions.map((condition) => ({ ...condition })),
4226
+ limit: state.limit,
4227
+ offset: state.offset,
4228
+ order: state.order ? { ...state.order } : void 0,
4229
+ currentPage: state.currentPage,
4230
+ pageSize: state.pageSize,
4231
+ totalPages: state.totalPages
4232
+ });
2554
4233
  const builder = {};
2555
4234
  const filterMethods = createFilterMethods(
2556
4235
  state,
2557
4236
  addCondition,
2558
4237
  builder
2559
4238
  );
2560
- const runSelect = async (columns = DEFAULT_COLUMNS, options) => {
4239
+ const runSelect = async (columns = DEFAULT_COLUMNS, options, executionState = snapshotState(), callsite) => {
2561
4240
  const resolvedTableName = resolveTableNameForCall(tableName, options?.schema);
2562
- const conditions = state.conditions.length ? state.conditions.map((condition) => ({ ...condition })) : void 0;
2563
- const hasTypedEqualityComparison = conditions?.some(
2564
- (condition) => condition.operator === "eq" && (condition.value_cast !== void 0 || condition.column_cast !== void 0)
2565
- ) ?? false;
2566
- if (hasTypedEqualityComparison && !options?.head && !options?.count && conditions) {
2567
- const query = buildTypedSelectQuery({
2568
- tableName: resolvedTableName,
2569
- columns,
2570
- conditions,
2571
- limit: state.limit,
2572
- offset: state.offset,
2573
- currentPage: state.currentPage,
2574
- pageSize: state.pageSize,
2575
- order: state.order
2576
- });
2577
- if (query) {
2578
- const queryResponse = await client.queryGateway({ query }, options);
2579
- return formatGatewayResult(queryResponse, { table: resolvedTableName, operation: "select" });
2580
- }
2581
- }
2582
- const payload = {
2583
- table_name: resolvedTableName,
4241
+ const plan = createSelectTransportPlan({
4242
+ tableName: resolvedTableName,
2584
4243
  columns,
2585
- conditions,
2586
- limit: state.limit,
2587
- offset: state.offset,
2588
- current_page: state.currentPage,
2589
- page_size: state.pageSize,
2590
- total_pages: state.totalPages,
2591
- sort_by: state.order,
2592
- strip_nulls: options?.stripNulls ?? true,
2593
- count: options?.count,
2594
- head: options?.head
2595
- };
2596
- const response = await client.fetchGateway(payload, options);
2597
- return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
4244
+ state: executionState,
4245
+ options,
4246
+ buildTypedSelectQuery
4247
+ });
4248
+ if (plan.kind === "query") {
4249
+ return executeWithQueryTrace(
4250
+ tracer,
4251
+ {
4252
+ operation: "select",
4253
+ endpoint: "/gateway/query",
4254
+ table: resolvedTableName,
4255
+ sql: plan.query,
4256
+ payload: plan.payload,
4257
+ options
4258
+ },
4259
+ async () => {
4260
+ const queryResponse = await client.queryGateway(plan.payload, options);
4261
+ return formatGatewayResult(queryResponse, { table: resolvedTableName, operation: "select" });
4262
+ },
4263
+ callsite
4264
+ );
4265
+ }
4266
+ const sql = buildDebugSelectQuery({
4267
+ tableName: resolvedTableName,
4268
+ ...plan.debug
4269
+ });
4270
+ return executeWithQueryTrace(
4271
+ tracer,
4272
+ {
4273
+ operation: "select",
4274
+ endpoint: "/gateway/fetch",
4275
+ table: resolvedTableName,
4276
+ sql,
4277
+ payload: plan.payload,
4278
+ options
4279
+ },
4280
+ async () => {
4281
+ const response = await client.fetchGateway(plan.payload, options);
4282
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
4283
+ },
4284
+ callsite
4285
+ );
2598
4286
  };
2599
- const createSelectChain = (columns, options) => {
4287
+ const createSelectChain = (columns, options, initialCallsite) => {
2600
4288
  const chain = {};
4289
+ const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
2601
4290
  const filterMethods2 = createFilterMethods(state, addCondition, chain);
2602
4291
  Object.assign(chain, filterMethods2, {
2603
4292
  async single(cols, opts) {
2604
- const r = await runSelect(cols ?? columns, opts ?? options);
4293
+ const r = await runSelect(
4294
+ cols ?? columns,
4295
+ opts ?? options,
4296
+ snapshotState(),
4297
+ callsiteStore.resolve(captureTraceCallsite(tracer))
4298
+ );
2605
4299
  return toSingleResult(r);
2606
4300
  },
2607
4301
  maybeSingle(cols, opts) {
2608
4302
  return chain.single(cols, opts);
2609
4303
  },
2610
4304
  then(onfulfilled, onrejected) {
2611
- return runSelect(columns, options).then(onfulfilled, onrejected);
4305
+ return runSelect(
4306
+ columns,
4307
+ options,
4308
+ snapshotState(),
4309
+ callsiteStore.resolve()
4310
+ ).then(onfulfilled, onrejected);
2612
4311
  },
2613
4312
  catch(onrejected) {
2614
- return runSelect(columns, options).catch(onrejected);
4313
+ return runSelect(columns, options, snapshotState(), callsiteStore.resolve()).catch(
4314
+ onrejected
4315
+ );
2615
4316
  },
2616
4317
  finally(onfinally) {
2617
- return runSelect(columns, options).finally(onfinally);
4318
+ return runSelect(columns, options, snapshotState(), callsiteStore.resolve()).finally(
4319
+ onfinally
4320
+ );
2618
4321
  }
2619
4322
  });
2620
4323
  return chain;
@@ -2631,11 +4334,75 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2631
4334
  return builder;
2632
4335
  },
2633
4336
  select(columns = DEFAULT_COLUMNS, options) {
2634
- return createSelectChain(columns, options);
4337
+ return createSelectChain(columns, options, captureTraceCallsite(tracer));
4338
+ },
4339
+ async findMany(options) {
4340
+ const columns = compileSelectShape(options.select);
4341
+ const baseState = snapshotState();
4342
+ const executionState = snapshotState();
4343
+ const callsite = captureTraceCallsite(tracer);
4344
+ const compiledWhere = compileWhere(options.where);
4345
+ if (compiledWhere?.length) {
4346
+ executionState.conditions.push(...compiledWhere);
4347
+ }
4348
+ if (options.orderBy !== void 0) {
4349
+ executionState.order = compileOrderBy(options.orderBy);
4350
+ }
4351
+ if (options.limit !== void 0) {
4352
+ executionState.limit = options.limit;
4353
+ }
4354
+ if (experimental?.findManyAst && canUseFindManyAstTransport(baseState) && !selectShapeUsesRelationSchema(options.select)) {
4355
+ const resolvedTableName = resolveTableNameForCall(tableName, void 0);
4356
+ const payload = {
4357
+ table_name: resolvedTableName,
4358
+ select: options.select
4359
+ };
4360
+ if (options.where !== void 0) {
4361
+ payload.where = options.where;
4362
+ }
4363
+ const astOrder = toFindManyAstOrder(executionState.order);
4364
+ if (astOrder !== void 0) {
4365
+ payload.orderBy = astOrder;
4366
+ }
4367
+ if (executionState.limit !== void 0) {
4368
+ payload.limit = executionState.limit;
4369
+ }
4370
+ const sql = buildDebugSelectQuery({
4371
+ tableName: resolvedTableName,
4372
+ columns,
4373
+ conditions: executionState.conditions,
4374
+ limit: executionState.limit,
4375
+ order: executionState.order
4376
+ });
4377
+ return executeWithQueryTrace(
4378
+ tracer,
4379
+ {
4380
+ operation: "select",
4381
+ endpoint: "/gateway/fetch",
4382
+ table: resolvedTableName,
4383
+ sql,
4384
+ payload
4385
+ },
4386
+ async () => {
4387
+ const response = await client.fetchGateway(
4388
+ payload
4389
+ );
4390
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
4391
+ },
4392
+ callsite
4393
+ );
4394
+ }
4395
+ return runSelect(
4396
+ columns,
4397
+ void 0,
4398
+ executionState,
4399
+ callsite
4400
+ );
2635
4401
  },
2636
4402
  insert(values, options) {
4403
+ const mutationCallsite = captureTraceCallsite(tracer);
2637
4404
  if (Array.isArray(values)) {
2638
- const executeInsertMany = async (columns, selectOptions) => {
4405
+ const executeInsertMany = async (columns, selectOptions, callsite) => {
2639
4406
  const mergedOptions = mergeOptions(options, selectOptions);
2640
4407
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2641
4408
  const payload = {
@@ -2648,12 +4415,27 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2648
4415
  if (mergedOptions?.defaultToNull !== void 0) {
2649
4416
  payload.default_to_null = mergedOptions.defaultToNull;
2650
4417
  }
2651
- const response = await client.insertGateway(payload, mergedOptions);
2652
- return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4418
+ const sql = buildInsertDebugSql(payload);
4419
+ return executeWithQueryTrace(
4420
+ tracer,
4421
+ {
4422
+ operation: "insert",
4423
+ endpoint: "/gateway/insert",
4424
+ table: resolvedTableName,
4425
+ sql,
4426
+ payload,
4427
+ options: mergedOptions
4428
+ },
4429
+ async () => {
4430
+ const response = await client.insertGateway(payload, mergedOptions);
4431
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4432
+ },
4433
+ callsite
4434
+ );
2653
4435
  };
2654
- return createMutationQuery(executeInsertMany);
4436
+ return createMutationQuery(executeInsertMany, DEFAULT_COLUMNS, tracer, mutationCallsite);
2655
4437
  }
2656
- const executeInsertOne = async (columns, selectOptions) => {
4438
+ const executeInsertOne = async (columns, selectOptions, callsite) => {
2657
4439
  const mergedOptions = mergeOptions(options, selectOptions);
2658
4440
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2659
4441
  const payload = {
@@ -2666,14 +4448,30 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2666
4448
  if (mergedOptions?.defaultToNull !== void 0) {
2667
4449
  payload.default_to_null = mergedOptions.defaultToNull;
2668
4450
  }
2669
- const response = await client.insertGateway(payload, mergedOptions);
2670
- return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4451
+ const sql = buildInsertDebugSql(payload);
4452
+ return executeWithQueryTrace(
4453
+ tracer,
4454
+ {
4455
+ operation: "insert",
4456
+ endpoint: "/gateway/insert",
4457
+ table: resolvedTableName,
4458
+ sql,
4459
+ payload,
4460
+ options: mergedOptions
4461
+ },
4462
+ async () => {
4463
+ const response = await client.insertGateway(payload, mergedOptions);
4464
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4465
+ },
4466
+ callsite
4467
+ );
2671
4468
  };
2672
- return createMutationQuery(executeInsertOne);
4469
+ return createMutationQuery(executeInsertOne, DEFAULT_COLUMNS, tracer, mutationCallsite);
2673
4470
  },
2674
4471
  upsert(values, options) {
4472
+ const mutationCallsite = captureTraceCallsite(tracer);
2675
4473
  if (Array.isArray(values)) {
2676
- const executeUpsertMany = async (columns, selectOptions) => {
4474
+ const executeUpsertMany = async (columns, selectOptions, callsite) => {
2677
4475
  const mergedOptions = mergeOptions(options, selectOptions);
2678
4476
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2679
4477
  const payload = {
@@ -2688,12 +4486,27 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2688
4486
  if (mergedOptions?.defaultToNull !== void 0) {
2689
4487
  payload.default_to_null = mergedOptions.defaultToNull;
2690
4488
  }
2691
- const response = await client.insertGateway(payload, mergedOptions);
2692
- return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4489
+ const sql = buildInsertDebugSql(payload);
4490
+ return executeWithQueryTrace(
4491
+ tracer,
4492
+ {
4493
+ operation: "upsert",
4494
+ endpoint: "/gateway/insert",
4495
+ table: resolvedTableName,
4496
+ sql,
4497
+ payload,
4498
+ options: mergedOptions
4499
+ },
4500
+ async () => {
4501
+ const response = await client.insertGateway(payload, mergedOptions);
4502
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4503
+ },
4504
+ callsite
4505
+ );
2693
4506
  };
2694
- return createMutationQuery(executeUpsertMany);
4507
+ return createMutationQuery(executeUpsertMany, DEFAULT_COLUMNS, tracer, mutationCallsite);
2695
4508
  }
2696
- const executeUpsertOne = async (columns, selectOptions) => {
4509
+ const executeUpsertOne = async (columns, selectOptions, callsite) => {
2697
4510
  const mergedOptions = mergeOptions(options, selectOptions);
2698
4511
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2699
4512
  const payload = {
@@ -2708,13 +4521,29 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2708
4521
  if (mergedOptions?.defaultToNull !== void 0) {
2709
4522
  payload.default_to_null = mergedOptions.defaultToNull;
2710
4523
  }
2711
- const response = await client.insertGateway(payload, mergedOptions);
2712
- return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4524
+ const sql = buildInsertDebugSql(payload);
4525
+ return executeWithQueryTrace(
4526
+ tracer,
4527
+ {
4528
+ operation: "upsert",
4529
+ endpoint: "/gateway/insert",
4530
+ table: resolvedTableName,
4531
+ sql,
4532
+ payload,
4533
+ options: mergedOptions
4534
+ },
4535
+ async () => {
4536
+ const response = await client.insertGateway(payload, mergedOptions);
4537
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4538
+ },
4539
+ callsite
4540
+ );
2713
4541
  };
2714
- return createMutationQuery(executeUpsertOne);
4542
+ return createMutationQuery(executeUpsertOne, DEFAULT_COLUMNS, tracer, mutationCallsite);
2715
4543
  },
2716
4544
  update(values, options) {
2717
- const executeUpdate = async (columns, selectOptions) => {
4545
+ const mutationCallsite = captureTraceCallsite(tracer);
4546
+ const executeUpdate = async (columns, selectOptions, callsite) => {
2718
4547
  const filters = state.conditions.length ? [...state.conditions] : void 0;
2719
4548
  const mergedOptions = mergeOptions(options, selectOptions);
2720
4549
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
@@ -2729,10 +4558,25 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2729
4558
  if (state.pageSize !== void 0) payload.page_size = state.pageSize;
2730
4559
  if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
2731
4560
  if (columns) payload.columns = columns;
2732
- const response = await client.updateGateway(payload, mergedOptions);
2733
- return formatGatewayResult(response, { table: resolvedTableName, operation: "update" });
4561
+ const sql = buildUpdateDebugSql(payload);
4562
+ return executeWithQueryTrace(
4563
+ tracer,
4564
+ {
4565
+ operation: "update",
4566
+ endpoint: "/gateway/update",
4567
+ table: resolvedTableName,
4568
+ sql,
4569
+ payload,
4570
+ options: mergedOptions
4571
+ },
4572
+ async () => {
4573
+ const response = await client.updateGateway(payload, mergedOptions);
4574
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "update" });
4575
+ },
4576
+ callsite
4577
+ );
2734
4578
  };
2735
- const mutation = createMutationQuery(executeUpdate, null);
4579
+ const mutation = createMutationQuery(executeUpdate, null, tracer, mutationCallsite);
2736
4580
  const updateChain = {};
2737
4581
  const filterMethods2 = createFilterMethods(state, addCondition, updateChain);
2738
4582
  Object.assign(updateChain, filterMethods2, mutation);
@@ -2744,7 +4588,8 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2744
4588
  if (!resourceId && !filters?.length) {
2745
4589
  throw new Error('delete requires a resource_id either via eq("resource_id", ...) or options.resourceId');
2746
4590
  }
2747
- const executeDelete = async (columns, selectOptions) => {
4591
+ const mutationCallsite = captureTraceCallsite(tracer);
4592
+ const executeDelete = async (columns, selectOptions, callsite) => {
2748
4593
  const mergedOptions = mergeOptions(options, selectOptions);
2749
4594
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2750
4595
  const payload = {
@@ -2757,13 +4602,33 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2757
4602
  if (state.pageSize !== void 0) payload.page_size = state.pageSize;
2758
4603
  if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
2759
4604
  if (columns) payload.columns = columns;
2760
- const response = await client.deleteGateway(payload, mergedOptions);
2761
- return formatGatewayResult(response, { table: resolvedTableName, operation: "delete" });
4605
+ const sql = buildDeleteDebugSql(payload);
4606
+ return executeWithQueryTrace(
4607
+ tracer,
4608
+ {
4609
+ operation: "delete",
4610
+ endpoint: "/gateway/delete",
4611
+ table: resolvedTableName,
4612
+ sql,
4613
+ payload,
4614
+ options: mergedOptions
4615
+ },
4616
+ async () => {
4617
+ const response = await client.deleteGateway(payload, mergedOptions);
4618
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "delete" });
4619
+ },
4620
+ callsite
4621
+ );
2762
4622
  };
2763
- return createMutationQuery(executeDelete, null);
4623
+ return createMutationQuery(executeDelete, null, tracer, mutationCallsite);
2764
4624
  },
2765
4625
  async single(columns, options) {
2766
- const response = await builder.select(columns, options);
4626
+ const response = await runSelect(
4627
+ columns ?? DEFAULT_COLUMNS,
4628
+ options,
4629
+ snapshotState(),
4630
+ captureTraceCallsite(tracer)
4631
+ );
2767
4632
  return toSingleResult(response);
2768
4633
  },
2769
4634
  async maybeSingle(columns, options) {
@@ -2772,14 +4637,29 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2772
4637
  });
2773
4638
  return builder;
2774
4639
  }
2775
- function createQueryBuilder(client, formatGatewayResult) {
4640
+ function createQueryBuilder(client, formatGatewayResult, tracer) {
2776
4641
  return async function query(query, options) {
2777
4642
  const normalizedQuery = query.trim();
2778
4643
  if (!normalizedQuery) {
2779
4644
  throw new Error("query requires a non-empty string");
2780
4645
  }
2781
- const response = await client.queryGateway({ query: normalizedQuery }, options);
2782
- return formatGatewayResult(response, { operation: "query" });
4646
+ const payload = { query: normalizedQuery };
4647
+ const callsite = captureTraceCallsite(tracer);
4648
+ return executeWithQueryTrace(
4649
+ tracer,
4650
+ {
4651
+ operation: "query",
4652
+ endpoint: "/gateway/query",
4653
+ sql: normalizedQuery,
4654
+ payload,
4655
+ options
4656
+ },
4657
+ async () => {
4658
+ const response = await client.queryGateway(payload, options);
4659
+ return formatGatewayResult(response, { operation: "query" });
4660
+ },
4661
+ callsite
4662
+ );
2783
4663
  };
2784
4664
  }
2785
4665
  function createClientFromConfig(config) {
@@ -2791,8 +4671,15 @@ function createClientFromConfig(config) {
2791
4671
  headers: config.headers
2792
4672
  });
2793
4673
  const formatGatewayResult = createResultFormatter(config.experimental);
4674
+ const queryTracer = createQueryTracer(config.experimental);
2794
4675
  const auth = createAuthClient(config.auth);
2795
- const from = (table) => createTableBuilder(table, gateway, formatGatewayResult);
4676
+ const from = (table, options) => createTableBuilder(
4677
+ resolveTableNameForCall(table, options?.schema),
4678
+ gateway,
4679
+ formatGatewayResult,
4680
+ queryTracer,
4681
+ config.experimental
4682
+ );
2796
4683
  const rpc = (fn, args, options) => {
2797
4684
  const normalizedFn = fn.trim();
2798
4685
  if (!normalizedFn) {
@@ -2803,16 +4690,19 @@ function createClientFromConfig(config) {
2803
4690
  args,
2804
4691
  options,
2805
4692
  gateway,
2806
- formatGatewayResult
4693
+ formatGatewayResult,
4694
+ queryTracer,
4695
+ captureTraceCallsite(queryTracer)
2807
4696
  );
2808
4697
  };
2809
- const query = createQueryBuilder(gateway, formatGatewayResult);
4698
+ const query = createQueryBuilder(gateway, formatGatewayResult, queryTracer);
2810
4699
  const db = createDbModule({ from, rpc, query });
2811
4700
  return {
2812
4701
  from,
2813
4702
  db,
2814
4703
  rpc,
2815
4704
  query,
4705
+ verifyConnection: gateway.verifyConnection,
2816
4706
  auth: auth.auth
2817
4707
  };
2818
4708
  }
@@ -2821,12 +4711,40 @@ function toBackendConfig(b) {
2821
4711
  if (!b) return DEFAULT_BACKEND;
2822
4712
  return typeof b === "string" ? { type: b } : b;
2823
4713
  }
4714
+ function mergeAuthClientConfig(current, next) {
4715
+ const merged = {
4716
+ ...current ?? {},
4717
+ ...next
4718
+ };
4719
+ if (current?.headers || next.headers) {
4720
+ merged.headers = {
4721
+ ...current?.headers ?? {},
4722
+ ...next.headers ?? {}
4723
+ };
4724
+ }
4725
+ return merged;
4726
+ }
4727
+ function mergeExperimentalOptions(current, next) {
4728
+ const merged = {
4729
+ ...current ?? {},
4730
+ ...next
4731
+ };
4732
+ if (current?.traceQueries && typeof current.traceQueries === "object" && next.traceQueries && typeof next.traceQueries === "object") {
4733
+ merged.traceQueries = {
4734
+ ...current.traceQueries,
4735
+ ...next.traceQueries
4736
+ };
4737
+ }
4738
+ return merged;
4739
+ }
2824
4740
  var AthenaClientBuilderImpl = class {
2825
4741
  baseUrl;
2826
4742
  apiKey;
2827
4743
  backendConfig = DEFAULT_BACKEND;
2828
4744
  clientName;
2829
4745
  defaultHeaders;
4746
+ authConfig;
4747
+ experimentalOptions;
2830
4748
  isHealthTrackingEnabled = false;
2831
4749
  url(url) {
2832
4750
  this.baseUrl = url;
@@ -2848,6 +4766,35 @@ var AthenaClientBuilderImpl = class {
2848
4766
  this.defaultHeaders = headers;
2849
4767
  return this;
2850
4768
  }
4769
+ auth(config) {
4770
+ this.authConfig = mergeAuthClientConfig(this.authConfig, config);
4771
+ return this;
4772
+ }
4773
+ experimental(options) {
4774
+ this.experimentalOptions = mergeExperimentalOptions(this.experimentalOptions, options);
4775
+ return this;
4776
+ }
4777
+ options(options) {
4778
+ if (options.client !== void 0) {
4779
+ this.clientName = options.client;
4780
+ }
4781
+ if (options.backend !== void 0) {
4782
+ this.backendConfig = toBackendConfig(options.backend);
4783
+ }
4784
+ if (options.headers !== void 0) {
4785
+ this.defaultHeaders = {
4786
+ ...this.defaultHeaders ?? {},
4787
+ ...options.headers
4788
+ };
4789
+ }
4790
+ if (options.auth !== void 0) {
4791
+ this.authConfig = mergeAuthClientConfig(this.authConfig, options.auth);
4792
+ }
4793
+ if (options.experimental !== void 0) {
4794
+ this.experimentalOptions = mergeExperimentalOptions(this.experimentalOptions, options.experimental);
4795
+ }
4796
+ return this;
4797
+ }
2851
4798
  healthTracking(enabled) {
2852
4799
  this.isHealthTrackingEnabled = enabled;
2853
4800
  return this;
@@ -2862,7 +4809,9 @@ var AthenaClientBuilderImpl = class {
2862
4809
  client: this.clientName,
2863
4810
  backend: this.backendConfig,
2864
4811
  headers: this.defaultHeaders,
2865
- healthTracking: this.isHealthTrackingEnabled
4812
+ healthTracking: this.isHealthTrackingEnabled,
4813
+ auth: this.authConfig,
4814
+ experimental: this.experimentalOptions
2866
4815
  });
2867
4816
  }
2868
4817
  };
@@ -2997,8 +4946,8 @@ var TypedAthenaClientImpl = class _TypedAthenaClientImpl {
2997
4946
  });
2998
4947
  this.db = this.baseClient.db;
2999
4948
  }
3000
- from(table) {
3001
- return this.baseClient.from(table);
4949
+ from(table, options) {
4950
+ return this.baseClient.from(table, options);
3002
4951
  }
3003
4952
  rpc(fn, args, options) {
3004
4953
  return this.baseClient.rpc(fn, args, options);
@@ -3006,6 +4955,9 @@ var TypedAthenaClientImpl = class _TypedAthenaClientImpl {
3006
4955
  query(query, options) {
3007
4956
  return this.baseClient.query(query, options);
3008
4957
  }
4958
+ verifyConnection(options) {
4959
+ return this.baseClient.verifyConnection(options);
4960
+ }
3009
4961
  withTenantContext(context) {
3010
4962
  return new _TypedAthenaClientImpl({
3011
4963
  registry: this.registry,
@@ -3042,7 +4994,7 @@ function resolveNullishValue(mode) {
3042
4994
  if (mode === "null") return null;
3043
4995
  return "";
3044
4996
  }
3045
- function isRecord4(value) {
4997
+ function isRecord7(value) {
3046
4998
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3047
4999
  }
3048
5000
  function isNullableColumn(model, key) {
@@ -3051,7 +5003,7 @@ function isNullableColumn(model, key) {
3051
5003
  }
3052
5004
  function toModelFormDefaults(model, values, options) {
3053
5005
  const source = values;
3054
- if (!isRecord4(source)) {
5006
+ if (!isRecord7(source)) {
3055
5007
  return {};
3056
5008
  }
3057
5009
  const mode = options?.nullishMode ?? "empty-string";
@@ -3248,28 +5200,28 @@ function throwBrowserUnsupported(apiName) {
3248
5200
  `@xylex-group/athena: ${apiName} is not available in browser bundles. Use this API in a Node.js runtime.`
3249
5201
  );
3250
5202
  }
3251
- function createPostgresIntrospectionProvider(_options) {
5203
+ function createPostgresIntrospectionProvider(options) {
3252
5204
  return throwBrowserUnsupported("createPostgresIntrospectionProvider");
3253
5205
  }
3254
5206
  function defineGeneratorConfig(config) {
3255
5207
  return config;
3256
5208
  }
3257
- function findGeneratorConfigPath(_cwd) {
5209
+ function findGeneratorConfigPath(cwd) {
3258
5210
  return throwBrowserUnsupported("findGeneratorConfigPath");
3259
5211
  }
3260
- async function loadGeneratorConfig(_options = {}) {
5212
+ async function loadGeneratorConfig(options = {}) {
3261
5213
  return throwBrowserUnsupported("loadGeneratorConfig");
3262
5214
  }
3263
- function normalizeGeneratorConfig(_input) {
5215
+ function normalizeGeneratorConfig(input) {
3264
5216
  return throwBrowserUnsupported("normalizeGeneratorConfig");
3265
5217
  }
3266
- function generateArtifactsFromSnapshot(_snapshot, _config) {
5218
+ function generateArtifactsFromSnapshot(snapshot, config) {
3267
5219
  return throwBrowserUnsupported("generateArtifactsFromSnapshot");
3268
5220
  }
3269
- function resolveGeneratorProvider(_providerConfig, _experimentalFlags) {
5221
+ function resolveGeneratorProvider(providerConfig, experimentalFlags) {
3270
5222
  return throwBrowserUnsupported("resolveGeneratorProvider");
3271
5223
  }
3272
- async function runSchemaGenerator(_options = {}) {
5224
+ async function runSchemaGenerator(options = {}) {
3273
5225
  return throwBrowserUnsupported("runSchemaGenerator");
3274
5226
  }
3275
5227
 
@@ -3284,10 +5236,12 @@ exports.DEFAULT_POSTGRES_SCHEMAS = DEFAULT_POSTGRES_SCHEMAS;
3284
5236
  exports.assertInt = assertInt;
3285
5237
  exports.coerceInt = coerceInt;
3286
5238
  exports.createAuthClient = createAuthClient;
5239
+ exports.createAuthReactEmailInput = createAuthReactEmailInput;
3287
5240
  exports.createClient = createClient;
3288
5241
  exports.createModelFormAdapter = createModelFormAdapter;
3289
5242
  exports.createPostgresIntrospectionProvider = createPostgresIntrospectionProvider;
3290
5243
  exports.createTypedClient = createTypedClient;
5244
+ exports.defineAuthEmailTemplate = defineAuthEmailTemplate;
3291
5245
  exports.defineDatabase = defineDatabase;
3292
5246
  exports.defineGeneratorConfig = defineGeneratorConfig;
3293
5247
  exports.defineModel = defineModel;
@@ -3300,9 +5254,11 @@ exports.isAthenaGatewayError = isAthenaGatewayError;
3300
5254
  exports.isOk = isOk;
3301
5255
  exports.loadGeneratorConfig = loadGeneratorConfig;
3302
5256
  exports.normalizeAthenaError = normalizeAthenaError;
5257
+ exports.normalizeAthenaGatewayBaseUrl = normalizeAthenaGatewayBaseUrl;
3303
5258
  exports.normalizeGeneratorConfig = normalizeGeneratorConfig;
3304
5259
  exports.normalizeSchemaSelection = normalizeSchemaSelection;
3305
- exports.parseBooleanFlag = parseBooleanFlag;
5260
+ exports.parseBooleanFlag = parseBooleanFlag2;
5261
+ exports.renderAthenaReactEmail = renderAthenaReactEmail;
3306
5262
  exports.requireAffected = requireAffected;
3307
5263
  exports.requireSuccess = requireSuccess;
3308
5264
  exports.resolveGeneratorProvider = resolveGeneratorProvider;
@@ -3314,6 +5270,7 @@ exports.toModelPayload = toModelPayload;
3314
5270
  exports.unwrap = unwrap;
3315
5271
  exports.unwrapOne = unwrapOne;
3316
5272
  exports.unwrapRows = unwrapRows;
5273
+ exports.verifyAthenaGatewayUrl = verifyAthenaGatewayUrl;
3317
5274
  exports.withRetry = withRetry;
3318
5275
  //# sourceMappingURL=browser.cjs.map
3319
5276
  //# sourceMappingURL=browser.cjs.map