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