@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/index.cjs CHANGED
@@ -65,8 +65,74 @@ function isAthenaGatewayError(error) {
65
65
  return error instanceof AthenaGatewayError;
66
66
  }
67
67
 
68
+ // src/gateway/url.ts
69
+ var ATHENA_DEFAULT_BASE_URL = "https://athena-db.com";
70
+ function describeReceivedValue(value) {
71
+ if (value === void 0) return "undefined";
72
+ if (value === null) return "null";
73
+ if (typeof value === "string") {
74
+ return value.trim().length > 0 ? JSON.stringify(value) : "an empty string";
75
+ }
76
+ return `${typeof value} ${JSON.stringify(value)}`;
77
+ }
78
+ function invalidBaseUrlError(message, hint) {
79
+ return new AthenaGatewayError({
80
+ code: "INVALID_URL",
81
+ message,
82
+ status: 0,
83
+ hint
84
+ });
85
+ }
86
+ function normalizeAthenaGatewayBaseUrl(input, options = {}) {
87
+ const label = options.label ?? "Athena gateway base URL";
88
+ const candidate = input ?? options.defaultBaseUrl;
89
+ if (candidate === void 0 || candidate === null) {
90
+ throw invalidBaseUrlError(
91
+ `${label} must be a non-empty absolute http(s) URL. Received ${describeReceivedValue(input)}.`,
92
+ 'Set ATHENA_URL (or pass createClient(url, ...)) to a full URL such as "https://mirror3.athena-db.com".'
93
+ );
94
+ }
95
+ const trimmed = candidate.trim();
96
+ if (!trimmed) {
97
+ throw invalidBaseUrlError(
98
+ `${label} must be a non-empty absolute http(s) URL. Received ${describeReceivedValue(candidate)}.`,
99
+ 'Set ATHENA_URL (or pass createClient(url, ...)) to a full URL such as "https://mirror3.athena-db.com".'
100
+ );
101
+ }
102
+ let parsed;
103
+ try {
104
+ parsed = new URL(trimmed);
105
+ } catch {
106
+ throw invalidBaseUrlError(
107
+ `${label} must be a valid absolute http(s) URL. Received ${describeReceivedValue(candidate)}.`,
108
+ 'Use a full URL including the protocol, for example "https://mirror3.athena-db.com".'
109
+ );
110
+ }
111
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
112
+ throw invalidBaseUrlError(
113
+ `${label} must use http or https. Received ${JSON.stringify(trimmed)}.`,
114
+ 'Use an Athena gateway URL such as "https://mirror3.athena-db.com".'
115
+ );
116
+ }
117
+ if (parsed.search || parsed.hash) {
118
+ throw invalidBaseUrlError(
119
+ `${label} must not include query parameters or hash fragments. Received ${JSON.stringify(trimmed)}.`,
120
+ 'Pass only the base URL. Endpoint paths such as "/gateway/fetch" are appended by the SDK.'
121
+ );
122
+ }
123
+ return parsed.toString().replace(/\/+$/, "");
124
+ }
125
+ function buildAthenaGatewayUrl(baseUrl, path) {
126
+ if (!path.startsWith("/")) {
127
+ throw invalidBaseUrlError(
128
+ `Athena gateway path must start with "/". Received ${JSON.stringify(path)}.`,
129
+ 'Use a leading slash such as "/gateway/fetch" or "/".'
130
+ );
131
+ }
132
+ return `${baseUrl}${path}`;
133
+ }
134
+
68
135
  // src/gateway/client.ts
69
- var DEFAULT_BASE_URL = "https://athena-db.com";
70
136
  var DEFAULT_CLIENT = "railway_direct";
71
137
  var FALLBACK_SDK_VERSION = "1.3.0";
72
138
  var SDK_NAME = "xylex-group/athena";
@@ -93,23 +159,39 @@ function normalizeHeaderValue(value) {
93
159
  function isRecord(value) {
94
160
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
95
161
  }
162
+ function nonEmptyString(value) {
163
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
164
+ }
165
+ function resolveStructuredErrorPayload(payload) {
166
+ if (!isRecord(payload)) return null;
167
+ return isRecord(payload.error) ? payload.error : payload;
168
+ }
96
169
  function resolveRequestId(headers) {
97
170
  return headers.get("x-request-id") ?? headers.get("x-correlation-id") ?? headers.get("x-athena-request-id") ?? void 0;
98
171
  }
99
172
  function resolveErrorMessage(payload, fallback) {
100
- if (isRecord(payload)) {
101
- const messageCandidates = [payload.error, payload.message, payload.details];
173
+ const structuredPayload = resolveStructuredErrorPayload(payload);
174
+ if (structuredPayload) {
175
+ const messageCandidates = [structuredPayload.message, structuredPayload.error, structuredPayload.details];
102
176
  for (const candidate of messageCandidates) {
103
- if (typeof candidate === "string" && candidate.trim().length > 0) {
104
- return candidate.trim();
105
- }
177
+ const resolved = nonEmptyString(candidate);
178
+ if (resolved) return resolved;
106
179
  }
107
180
  }
108
- if (typeof payload === "string" && payload.trim().length > 0) {
109
- return payload.trim();
110
- }
181
+ const rawMessage = nonEmptyString(payload);
182
+ if (rawMessage) return rawMessage;
111
183
  return fallback;
112
184
  }
185
+ function resolveErrorHint(payload) {
186
+ const structuredPayload = resolveStructuredErrorPayload(payload);
187
+ return structuredPayload ? nonEmptyString(structuredPayload.hint) : void 0;
188
+ }
189
+ function resolveStatusText(response, payload) {
190
+ const rawStatusText = nonEmptyString(response.statusText);
191
+ if (rawStatusText) return rawStatusText;
192
+ const payloadRecord = isRecord(payload) ? payload : null;
193
+ return payloadRecord ? nonEmptyString(payloadRecord.statusText) ?? null : null;
194
+ }
113
195
  function detailsFromError(error) {
114
196
  return error.toDetails();
115
197
  }
@@ -247,9 +329,172 @@ function buildHeaders(config, options) {
247
329
  });
248
330
  return headers;
249
331
  }
332
+ function toInvalidUrlResponse(error, endpoint, method) {
333
+ const message = error instanceof Error ? error.message : String(error);
334
+ const gatewayError = error instanceof AthenaGatewayError ? error : new AthenaGatewayError({
335
+ code: "INVALID_URL",
336
+ message,
337
+ status: 0,
338
+ endpoint,
339
+ method,
340
+ cause: message,
341
+ hint: "Set ATHENA_URL to a full http(s) URL before running queries."
342
+ });
343
+ return {
344
+ ok: false,
345
+ status: 0,
346
+ statusText: null,
347
+ data: null,
348
+ error: gatewayError.message,
349
+ errorDetails: detailsFromError(
350
+ new AthenaGatewayError({
351
+ code: gatewayError.code,
352
+ message: gatewayError.message,
353
+ status: gatewayError.status,
354
+ endpoint,
355
+ method,
356
+ requestId: gatewayError.requestId,
357
+ hint: gatewayError.hint,
358
+ cause: gatewayError.causeDetail
359
+ })
360
+ ),
361
+ raw: null
362
+ };
363
+ }
364
+ function resolveGatewayBaseUrl(input) {
365
+ return normalizeAthenaGatewayBaseUrl(input, {
366
+ defaultBaseUrl: ATHENA_DEFAULT_BASE_URL
367
+ });
368
+ }
369
+ function resolveProbePath(path) {
370
+ if (!path) return "/";
371
+ if (!path.startsWith("/")) {
372
+ throw new AthenaGatewayError({
373
+ code: "INVALID_URL",
374
+ message: `Athena gateway probe path must start with "/". Received ${JSON.stringify(path)}.`,
375
+ status: 0,
376
+ hint: 'Use a leading slash such as "/" or "/health".'
377
+ });
378
+ }
379
+ return path;
380
+ }
381
+ function mergeConnectionHeaders(baseHeaders, headers) {
382
+ const merged = {
383
+ ...baseHeaders,
384
+ ...headers ?? {}
385
+ };
386
+ if (!merged["X-Athena-Sdk"] && !merged["x-athena-sdk"]) {
387
+ merged["X-Athena-Sdk"] = SDK_HEADER_VALUE;
388
+ }
389
+ return merged;
390
+ }
391
+ async function performConnectionCheck(baseUrl, requestHeaders, options) {
392
+ const path = resolveProbePath(options?.path);
393
+ const url = buildAthenaGatewayUrl(baseUrl, path);
394
+ try {
395
+ const response = await fetch(url, {
396
+ method: "GET",
397
+ headers: mergeConnectionHeaders(requestHeaders, options?.headers),
398
+ signal: options?.signal
399
+ });
400
+ const rawText = await response.text();
401
+ const requestId = resolveRequestId(response.headers);
402
+ const parsedBody = parseResponseBody(
403
+ rawText ?? "",
404
+ response.headers.get("content-type")
405
+ );
406
+ if (parsedBody.parseFailed) {
407
+ const invalidJsonError = new AthenaGatewayError({
408
+ code: "INVALID_JSON",
409
+ message: "Gateway probe returned malformed JSON",
410
+ status: response.status,
411
+ method: "GET",
412
+ requestId,
413
+ hint: "Verify the gateway response body is valid JSON.",
414
+ cause: rawText.slice(0, 300)
415
+ });
416
+ return {
417
+ ok: false,
418
+ reachable: true,
419
+ status: response.status,
420
+ statusText: resolveStatusText(response, parsedBody.parsed),
421
+ baseUrl,
422
+ url,
423
+ error: invalidJsonError.message,
424
+ errorDetails: detailsFromError(invalidJsonError),
425
+ raw: parsedBody.parsed
426
+ };
427
+ }
428
+ const parsed = parsedBody.parsed;
429
+ if (!response.ok) {
430
+ const httpError = new AthenaGatewayError({
431
+ code: "HTTP_ERROR",
432
+ message: resolveErrorMessage(
433
+ parsed,
434
+ `Athena gateway GET ${path} failed with status ${response.status}`
435
+ ),
436
+ status: response.status,
437
+ method: "GET",
438
+ requestId,
439
+ hint: resolveErrorHint(parsed)
440
+ });
441
+ return {
442
+ ok: false,
443
+ reachable: true,
444
+ status: response.status,
445
+ statusText: resolveStatusText(response, parsed),
446
+ baseUrl,
447
+ url,
448
+ error: httpError.message,
449
+ errorDetails: detailsFromError(httpError),
450
+ raw: parsed
451
+ };
452
+ }
453
+ return {
454
+ ok: true,
455
+ reachable: true,
456
+ status: response.status,
457
+ statusText: resolveStatusText(response, parsed),
458
+ baseUrl,
459
+ url,
460
+ error: void 0,
461
+ errorDetails: null,
462
+ raw: parsed
463
+ };
464
+ } catch (callError) {
465
+ const message = callError instanceof Error ? callError.message : String(callError);
466
+ const networkError = new AthenaGatewayError({
467
+ code: "NETWORK_ERROR",
468
+ message: `Network error while probing Athena gateway ${url}: ${message}`,
469
+ method: "GET",
470
+ cause: message,
471
+ hint: "Check gateway URL, DNS, and network reachability."
472
+ });
473
+ return {
474
+ ok: false,
475
+ reachable: false,
476
+ status: 0,
477
+ statusText: null,
478
+ baseUrl,
479
+ url,
480
+ error: networkError.message,
481
+ errorDetails: detailsFromError(networkError),
482
+ raw: null
483
+ };
484
+ }
485
+ }
486
+ async function verifyAthenaGatewayUrl(baseUrl, options) {
487
+ const normalizedBaseUrl = normalizeAthenaGatewayBaseUrl(baseUrl);
488
+ return performConnectionCheck(normalizedBaseUrl, { "X-Athena-Sdk": SDK_HEADER_VALUE }, options);
489
+ }
250
490
  async function callAthena(config, endpoint, method, payload, options) {
251
- const baseUrl = (options?.baseUrl ?? config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
252
- const url = `${baseUrl}${endpoint}`;
491
+ let baseUrl;
492
+ try {
493
+ baseUrl = resolveGatewayBaseUrl(options?.baseUrl ?? config.baseUrl);
494
+ } catch (error) {
495
+ return toInvalidUrlResponse(error, endpoint, method);
496
+ }
497
+ const url = buildAthenaGatewayUrl(baseUrl, endpoint);
253
498
  const headers = buildHeaders(config, options);
254
499
  try {
255
500
  const requestInit = {
@@ -280,6 +525,7 @@ async function callAthena(config, endpoint, method, payload, options) {
280
525
  return {
281
526
  ok: false,
282
527
  status: response.status,
528
+ statusText: resolveStatusText(response, parsedBody.parsed),
283
529
  data: null,
284
530
  error: invalidJsonError.message,
285
531
  errorDetails: detailsFromError(invalidJsonError),
@@ -298,11 +544,13 @@ async function callAthena(config, endpoint, method, payload, options) {
298
544
  status: response.status,
299
545
  endpoint,
300
546
  method,
301
- requestId
547
+ requestId,
548
+ hint: resolveErrorHint(parsed)
302
549
  });
303
550
  return {
304
551
  ok: false,
305
552
  status: response.status,
553
+ statusText: resolveStatusText(response, parsed),
306
554
  data: null,
307
555
  error: httpError.message,
308
556
  errorDetails: detailsFromError(httpError),
@@ -314,6 +562,7 @@ async function callAthena(config, endpoint, method, payload, options) {
314
562
  return {
315
563
  ok: true,
316
564
  status: response.status,
565
+ statusText: resolveStatusText(response, parsed),
317
566
  data: payloadData ?? null,
318
567
  count: payloadCount,
319
568
  error: void 0,
@@ -333,6 +582,7 @@ async function callAthena(config, endpoint, method, payload, options) {
333
582
  return {
334
583
  ok: false,
335
584
  status: 0,
585
+ statusText: null,
336
586
  data: null,
337
587
  error: networkError.message,
338
588
  errorDetails: detailsFromError(networkError),
@@ -341,32 +591,44 @@ async function callAthena(config, endpoint, method, payload, options) {
341
591
  }
342
592
  }
343
593
  function createAthenaGatewayClient(config = {}) {
594
+ const normalizedBaseUrl = resolveGatewayBaseUrl(config.baseUrl);
595
+ const normalizedConfig = {
596
+ ...config,
597
+ baseUrl: normalizedBaseUrl
598
+ };
344
599
  return {
345
- baseUrl: (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, ""),
600
+ baseUrl: normalizedBaseUrl,
346
601
  buildHeaders(options) {
347
- return buildHeaders(config, options);
602
+ return buildHeaders(normalizedConfig, options);
603
+ },
604
+ verifyConnection(options) {
605
+ return performConnectionCheck(
606
+ normalizedBaseUrl,
607
+ buildHeaders(normalizedConfig),
608
+ options
609
+ );
348
610
  },
349
611
  fetchGateway(payload, options) {
350
- return callAthena(config, "/gateway/fetch", "POST", payload, options);
612
+ return callAthena(normalizedConfig, "/gateway/fetch", "POST", payload, options);
351
613
  },
352
614
  insertGateway(payload, options) {
353
- return callAthena(config, "/gateway/insert", "PUT", payload, options);
615
+ return callAthena(normalizedConfig, "/gateway/insert", "PUT", payload, options);
354
616
  },
355
617
  updateGateway(payload, options) {
356
- return callAthena(config, "/gateway/update", "POST", payload, options);
618
+ return callAthena(normalizedConfig, "/gateway/update", "POST", payload, options);
357
619
  },
358
620
  deleteGateway(payload, options) {
359
- return callAthena(config, "/gateway/delete", "DELETE", payload, options);
621
+ return callAthena(normalizedConfig, "/gateway/delete", "DELETE", payload, options);
360
622
  },
361
623
  rpcGateway(payload, options) {
362
624
  if (options?.get) {
363
625
  const endpoint = buildRpcGetEndpoint(payload);
364
- return callAthena(config, endpoint, "GET", null, options);
626
+ return callAthena(normalizedConfig, endpoint, "GET", null, options);
365
627
  }
366
- return callAthena(config, "/gateway/rpc", "POST", payload, options);
628
+ return callAthena(normalizedConfig, "/gateway/rpc", "POST", payload, options);
367
629
  },
368
630
  queryGateway(payload, options) {
369
- return callAthena(config, "/gateway/query", "POST", payload, options);
631
+ return callAthena(normalizedConfig, "/gateway/query", "POST", payload, options);
370
632
  }
371
633
  };
372
634
  }
@@ -374,10 +636,29 @@ function createAthenaGatewayClient(config = {}) {
374
636
  // src/sql-identifiers.ts
375
637
  var SIMPLE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
376
638
  var COMPOSITE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
377
- var ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_.]*)\s+(?:as\s+)?([A-Za-z_][A-Za-z0-9_]*)$/i;
639
+ var SQL_ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_.]*)\s+(?:as\s+)?([A-Za-z_][A-Za-z0-9_]*)$/i;
640
+ var RESPONSE_ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([A-Za-z_][A-Za-z0-9_.]*)$/i;
378
641
  function quoteIdentifierSegment(identifier2) {
379
642
  return `"${identifier2.replace(/"/g, '""')}"`;
380
643
  }
644
+ function parseAliasedIdentifierToken(token) {
645
+ const responseAliasMatch = RESPONSE_ALIAS_PATTERN.exec(token);
646
+ if (responseAliasMatch) {
647
+ const [, aliasIdentifier2, baseIdentifier2] = responseAliasMatch;
648
+ if (COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier2) && SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier2)) {
649
+ return { baseIdentifier: baseIdentifier2, aliasIdentifier: aliasIdentifier2 };
650
+ }
651
+ }
652
+ const sqlAliasMatch = SQL_ALIAS_PATTERN.exec(token);
653
+ if (!sqlAliasMatch) {
654
+ return null;
655
+ }
656
+ const [, baseIdentifier, aliasIdentifier] = sqlAliasMatch;
657
+ if (!COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) || !SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier)) {
658
+ return null;
659
+ }
660
+ return { baseIdentifier, aliasIdentifier };
661
+ }
381
662
  function quoteQualifiedIdentifier(identifier2) {
382
663
  return identifier2.split(".").map((segment) => quoteIdentifierSegment(segment)).join(".");
383
664
  }
@@ -386,23 +667,27 @@ function quoteSelectToken(token) {
386
667
  if (COMPOSITE_IDENTIFIER_PATTERN.test(token)) {
387
668
  return quoteQualifiedIdentifier(token);
388
669
  }
389
- const aliasMatch = ALIAS_PATTERN.exec(token);
390
- if (!aliasMatch) {
391
- return token;
392
- }
393
- const [, baseIdentifier, aliasIdentifier] = aliasMatch;
394
- if (!COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) || !SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier)) {
670
+ const aliasedIdentifier = parseAliasedIdentifierToken(token);
671
+ if (!aliasedIdentifier) {
395
672
  return token;
396
673
  }
674
+ const { baseIdentifier, aliasIdentifier } = aliasedIdentifier;
397
675
  return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteIdentifierSegment(aliasIdentifier)}`;
398
676
  }
677
+ function quoteSelectColumnToken(token) {
678
+ const trimmed = token.trim();
679
+ if (!trimmed || trimmed === "*") return trimmed || "*";
680
+ const responseAliasMatch = RESPONSE_ALIAS_PATTERN.exec(trimmed);
681
+ if (responseAliasMatch) {
682
+ const [, aliasIdentifier, baseIdentifier] = responseAliasMatch;
683
+ return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteIdentifierSegment(aliasIdentifier)}`;
684
+ }
685
+ return quoteQualifiedIdentifier(trimmed);
686
+ }
399
687
  function canAutoQuoteToken(token) {
400
688
  if (token === "*") return true;
401
689
  if (COMPOSITE_IDENTIFIER_PATTERN.test(token)) return true;
402
- const aliasMatch = ALIAS_PATTERN.exec(token);
403
- if (!aliasMatch) return false;
404
- const [, baseIdentifier, aliasIdentifier] = aliasMatch;
405
- return COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) && SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier);
690
+ return parseAliasedIdentifierToken(token) != null;
406
691
  }
407
692
  function splitTopLevelCommaSeparated(input) {
408
693
  const parts = [];
@@ -502,6 +787,231 @@ function identifier(...segments) {
502
787
  return new SqlIdentifierPath(expandedSegments);
503
788
  }
504
789
 
790
+ // src/auth/react-email.ts
791
+ var reactEmailRenderModulePromise;
792
+ function isRecord2(value) {
793
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
794
+ }
795
+ function isFunction(value) {
796
+ return typeof value === "function";
797
+ }
798
+ function toStringOrUndefined(value) {
799
+ if (typeof value !== "string") return void 0;
800
+ return value;
801
+ }
802
+ function nowIsoString() {
803
+ return (/* @__PURE__ */ new Date()).toISOString();
804
+ }
805
+ function emitReactEmailEvent(observe, phase, input = {}) {
806
+ if (!observe) return;
807
+ try {
808
+ observe({
809
+ phase,
810
+ timestamp: nowIsoString(),
811
+ ...input
812
+ });
813
+ } catch {
814
+ }
815
+ }
816
+ function mergeRenderDefaults(input, defaults) {
817
+ return {
818
+ ...input,
819
+ pretty: input.pretty ?? defaults?.pretty,
820
+ includePlainText: input.includePlainText ?? defaults?.includePlainText
821
+ };
822
+ }
823
+ function mergeRuntimeOptions(options) {
824
+ if (!options) return void 0;
825
+ return {
826
+ defaults: options.defaults,
827
+ observe: options.observe,
828
+ route: "route" in options ? options.route : void 0
829
+ };
830
+ }
831
+ async function resolveReactEmailRenderModule() {
832
+ if (!reactEmailRenderModulePromise) {
833
+ reactEmailRenderModulePromise = (async () => {
834
+ try {
835
+ const loaded = await import('@react-email/render');
836
+ if (!isFunction(loaded.render)) {
837
+ throw new Error("missing render(...) export");
838
+ }
839
+ return {
840
+ render: loaded.render,
841
+ toPlainText: isFunction(loaded.toPlainText) ? loaded.toPlainText : void 0,
842
+ pretty: isFunction(loaded.pretty) ? loaded.pretty : void 0
843
+ };
844
+ } catch (error) {
845
+ const message = error instanceof Error ? error.message : String(error);
846
+ throw new Error(
847
+ `React Email rendering requires @react-email/render. Install it in your project (for example: pnpm add @react-email/render). Loader error: ${message}`
848
+ );
849
+ }
850
+ })();
851
+ }
852
+ if (!reactEmailRenderModulePromise) {
853
+ throw new Error("React Email renderer module failed to initialize");
854
+ }
855
+ return reactEmailRenderModulePromise;
856
+ }
857
+ async function resolveReactEmailElement(input) {
858
+ if (input.element != null) {
859
+ return input.element;
860
+ }
861
+ if (!input.component) {
862
+ throw new Error("react email payload requires either `element` or `component`");
863
+ }
864
+ try {
865
+ const reactModule = await import('react');
866
+ if (typeof reactModule.createElement !== "function") {
867
+ throw new Error("react createElement(...) export is unavailable");
868
+ }
869
+ return reactModule.createElement(
870
+ input.component,
871
+ input.props ?? {}
872
+ );
873
+ } catch (error) {
874
+ const message = error instanceof Error ? error.message : String(error);
875
+ throw new Error(
876
+ `React Email component rendering requires react runtime support. Install react in your project. Loader error: ${message}`
877
+ );
878
+ }
879
+ }
880
+ function createAuthReactEmailInput(component, props, overrides = {}) {
881
+ return {
882
+ ...overrides,
883
+ component,
884
+ props
885
+ };
886
+ }
887
+ function defineAuthEmailTemplate(definition) {
888
+ const react = (props, overrides) => createAuthReactEmailInput(definition.component, props, {
889
+ ...definition.defaults,
890
+ ...overrides
891
+ });
892
+ return {
893
+ component: definition.component,
894
+ react,
895
+ toTemplateCreate: (input) => {
896
+ const templateKey = input.templateKey ?? definition.templateKey;
897
+ const subjectTemplate = input.subjectTemplate ?? definition.subjectTemplate;
898
+ if (!templateKey) {
899
+ throw new Error("defineAuthEmailTemplate.toTemplateCreate requires templateKey");
900
+ }
901
+ if (!subjectTemplate) {
902
+ throw new Error("defineAuthEmailTemplate.toTemplateCreate requires subjectTemplate");
903
+ }
904
+ const { props, react: reactOverrides, ...rest } = input;
905
+ return {
906
+ ...rest,
907
+ templateKey,
908
+ subjectTemplate,
909
+ react: react(props, reactOverrides)
910
+ };
911
+ },
912
+ toTemplateUpdate: (input) => {
913
+ const { props, react: reactOverrides, ...rest } = input;
914
+ return {
915
+ ...rest,
916
+ react: react(props, reactOverrides)
917
+ };
918
+ }
919
+ };
920
+ }
921
+ async function renderAthenaReactEmail(input, options) {
922
+ if (!isRecord2(input)) {
923
+ throw new Error("react email payload must be an object");
924
+ }
925
+ const runtimeOptions = mergeRuntimeOptions(options);
926
+ const start = Date.now();
927
+ emitReactEmailEvent(runtimeOptions?.observe, "render:start", {
928
+ route: runtimeOptions?.route,
929
+ message: "Rendering react email payload"
930
+ });
931
+ try {
932
+ const normalizedInput = mergeRenderDefaults(input, runtimeOptions?.defaults);
933
+ const element = await resolveReactEmailElement(normalizedInput);
934
+ const renderModule = await resolveReactEmailRenderModule();
935
+ const htmlValue = await renderModule.render(element);
936
+ const renderedHtml = typeof htmlValue === "string" ? htmlValue : String(htmlValue ?? "");
937
+ if (!renderedHtml.trim()) {
938
+ throw new Error("react email renderer returned an empty HTML string");
939
+ }
940
+ let html = renderedHtml;
941
+ if (normalizedInput.pretty && renderModule.pretty) {
942
+ const prettyValue = await renderModule.pretty(renderedHtml);
943
+ if (typeof prettyValue === "string" && prettyValue.trim().length > 0) {
944
+ html = prettyValue;
945
+ }
946
+ }
947
+ const explicitText = toStringOrUndefined(normalizedInput.text);
948
+ if (explicitText !== void 0) {
949
+ emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
950
+ route: runtimeOptions?.route,
951
+ durationMs: Date.now() - start,
952
+ message: "Rendered react email with explicit text"
953
+ });
954
+ return {
955
+ html,
956
+ text: explicitText
957
+ };
958
+ }
959
+ if (normalizedInput.includePlainText === false || !renderModule.toPlainText) {
960
+ emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
961
+ route: runtimeOptions?.route,
962
+ durationMs: Date.now() - start,
963
+ message: "Rendered react email without plain-text derivation"
964
+ });
965
+ return { html };
966
+ }
967
+ const plainTextValue = await renderModule.toPlainText(html);
968
+ const plainText = toStringOrUndefined(plainTextValue);
969
+ emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
970
+ route: runtimeOptions?.route,
971
+ durationMs: Date.now() - start,
972
+ message: plainText ? "Rendered react email with derived plain text" : "Rendered react email HTML only"
973
+ });
974
+ if (plainText === void 0) {
975
+ return { html };
976
+ }
977
+ return {
978
+ html,
979
+ text: plainText
980
+ };
981
+ } catch (error) {
982
+ const message = error instanceof Error ? error.message : String(error);
983
+ emitReactEmailEvent(runtimeOptions?.observe, "render:error", {
984
+ route: runtimeOptions?.route,
985
+ durationMs: Date.now() - start,
986
+ error: message,
987
+ message: "Failed to render react email payload"
988
+ });
989
+ throw error;
990
+ }
991
+ }
992
+ async function resolveReactEmailPayloadFields(input, fields, options) {
993
+ const { react, ...payloadWithoutReact } = input;
994
+ if (!react) {
995
+ return payloadWithoutReact;
996
+ }
997
+ const rendered = await renderAthenaReactEmail(react, options);
998
+ const payload = {
999
+ ...payloadWithoutReact
1000
+ };
1001
+ payload[fields.htmlField] = rendered.html;
1002
+ const currentTextValue = payload[fields.textField];
1003
+ if (rendered.text !== void 0 && (currentTextValue === void 0 || currentTextValue === null || currentTextValue === "")) {
1004
+ payload[fields.textField] = rendered.text;
1005
+ }
1006
+ if (fields.variablesField && (payload[fields.variablesField] === void 0 || payload[fields.variablesField] === null) && isRecord2(react.props)) {
1007
+ const derivedVariables = Object.keys(react.props);
1008
+ if (derivedVariables.length > 0) {
1009
+ payload[fields.variablesField] = derivedVariables;
1010
+ }
1011
+ }
1012
+ return payload;
1013
+ }
1014
+
505
1015
  // src/auth/client.ts
506
1016
  var DEFAULT_AUTH_BASE_URL = "http://localhost:3001/api/auth";
507
1017
  var FALLBACK_SDK_VERSION2 = "1.0.0";
@@ -511,7 +1021,7 @@ var SDK_HEADER_VALUE2 = `${SDK_NAME2} ${SDK_VERSION2}`;
511
1021
  function normalizeBaseUrl(baseUrl) {
512
1022
  return (baseUrl ?? DEFAULT_AUTH_BASE_URL).replace(/\/$/, "");
513
1023
  }
514
- function isRecord2(value) {
1024
+ function isRecord3(value) {
515
1025
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
516
1026
  }
517
1027
  function normalizeHeaderValue2(value) {
@@ -536,7 +1046,7 @@ function resolveRequestId2(headers) {
536
1046
  return headers.get("x-request-id") ?? headers.get("x-correlation-id") ?? headers.get("x-athena-request-id") ?? void 0;
537
1047
  }
538
1048
  function resolveErrorMessage2(payload, fallback) {
539
- if (isRecord2(payload)) {
1049
+ if (isRecord3(payload)) {
540
1050
  const messageCandidates = [payload.error, payload.message, payload.details];
541
1051
  for (const candidate of messageCandidates) {
542
1052
  if (typeof candidate === "string" && candidate.trim().length > 0) {
@@ -862,6 +1372,19 @@ function createAuthClient(config = {}) {
862
1372
  options
863
1373
  );
864
1374
  };
1375
+ const withReactEmailRoute = (route) => ({
1376
+ ...resolvedConfig.reactEmail,
1377
+ route
1378
+ });
1379
+ const resolveAdminEmailPayload = (route, input) => resolveReactEmailPayloadFields(input, {
1380
+ htmlField: "htmlBody",
1381
+ textField: "textBody"
1382
+ }, withReactEmailRoute(route));
1383
+ const resolveAdminEmailTemplatePayload = (route, input) => resolveReactEmailPayloadFields(input, {
1384
+ htmlField: "htmlTemplate",
1385
+ textField: "textTemplate",
1386
+ variablesField: "variables"
1387
+ }, withReactEmailRoute(route));
865
1388
  const listUserEmailsWithFallback = async (input, options) => {
866
1389
  const primary = await getWithQuery(
867
1390
  "/email/list",
@@ -889,7 +1412,7 @@ function createAuthClient(config = {}) {
889
1412
  data: null
890
1413
  };
891
1414
  }
892
- const fallbackStatus = isRecord2(fallback.data) && typeof fallback.data.ok === "boolean" ? fallback.data.ok ? "ok" : "error" : "ok";
1415
+ const fallbackStatus = isRecord3(fallback.data) && typeof fallback.data.ok === "boolean" ? fallback.data.ok ? "ok" : "error" : "ok";
893
1416
  return {
894
1417
  ...fallback,
895
1418
  data: {
@@ -1325,8 +1848,16 @@ function createAuthClient(config = {}) {
1325
1848
  email: {
1326
1849
  list: (input, options) => getWithQuery("/admin/email/list", input, options),
1327
1850
  get: (input, options) => getWithQuery("/admin/email/get", input, options),
1328
- create: (input, options) => postGeneric("/admin/email/create", input, options),
1329
- update: (input, options) => postGeneric("/admin/email/update", input, options),
1851
+ create: async (input, options) => postGeneric(
1852
+ "/admin/email/create",
1853
+ await resolveAdminEmailPayload("/admin/email/create", input),
1854
+ options
1855
+ ),
1856
+ update: async (input, options) => postGeneric(
1857
+ "/admin/email/update",
1858
+ await resolveAdminEmailPayload("/admin/email/update", input),
1859
+ options
1860
+ ),
1330
1861
  delete: (input, options) => postGeneric("/admin/email/delete", input, options),
1331
1862
  failure: {
1332
1863
  list: (input, options) => getWithQuery("/admin/email-failure/list", input, options),
@@ -1338,17 +1869,33 @@ function createAuthClient(config = {}) {
1338
1869
  template: {
1339
1870
  list: (input, options) => getWithQuery("/admin/email-template/list", input, options),
1340
1871
  get: (input, options) => getWithQuery("/admin/email-template/get", input, options),
1341
- create: (input, options) => postGeneric("/admin/email-template/create", input, options),
1342
- update: (input, options) => postGeneric("/admin/email-template/update", input, options),
1872
+ create: async (input, options) => postGeneric(
1873
+ "/admin/email-template/create",
1874
+ await resolveAdminEmailTemplatePayload("/admin/email-template/create", input),
1875
+ options
1876
+ ),
1877
+ update: async (input, options) => postGeneric(
1878
+ "/admin/email-template/update",
1879
+ await resolveAdminEmailTemplatePayload("/admin/email-template/update", input),
1880
+ options
1881
+ ),
1343
1882
  delete: (input, options) => postGeneric("/admin/email-template/delete", input, options)
1344
1883
  }
1345
1884
  },
1346
1885
  emailTemplate: {
1347
1886
  get: (input, options) => getWithQuery("/admin/email-template/get", input, options),
1348
- create: (input, options) => postGeneric("/admin/email-template/create", input, options),
1887
+ create: async (input, options) => postGeneric(
1888
+ "/admin/email-template/create",
1889
+ await resolveAdminEmailTemplatePayload("/admin/email-template/create", input),
1890
+ options
1891
+ ),
1349
1892
  delete: (input, options) => postGeneric("/admin/email-template/delete", input, options),
1350
1893
  list: (input, options) => getWithQuery("/admin/email-template/list", input, options),
1351
- update: (input, options) => postGeneric("/admin/email-template/update", input, options)
1894
+ update: async (input, options) => postGeneric(
1895
+ "/admin/email-template/update",
1896
+ await resolveAdminEmailTemplatePayload("/admin/email-template/update", input),
1897
+ options
1898
+ )
1352
1899
  }
1353
1900
  },
1354
1901
  apiKey: {
@@ -1540,6 +2087,19 @@ function createAuthClient(config = {}) {
1540
2087
  };
1541
2088
  }
1542
2089
 
2090
+ // src/utils/parse-boolean-flag.ts
2091
+ function parseBooleanFlag(rawValue, fallback) {
2092
+ if (!rawValue) return fallback;
2093
+ const normalized = rawValue.trim().toLowerCase();
2094
+ if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
2095
+ return true;
2096
+ }
2097
+ if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
2098
+ return false;
2099
+ }
2100
+ return fallback;
2101
+ }
2102
+
1543
2103
  // src/auxiliaries.ts
1544
2104
  var AthenaErrorKind = {
1545
2105
  UniqueViolation: "unique_violation",
@@ -1569,16 +2129,8 @@ var AthenaErrorCategory = {
1569
2129
  Database: "database",
1570
2130
  Unknown: "unknown"
1571
2131
  };
1572
- function parseBooleanFlag(rawValue, fallback) {
1573
- if (!rawValue) return fallback;
1574
- const normalized = rawValue.trim().toLowerCase();
1575
- if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
1576
- return true;
1577
- }
1578
- if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
1579
- return false;
1580
- }
1581
- return fallback;
2132
+ function parseBooleanFlag2(rawValue, fallback) {
2133
+ return parseBooleanFlag(rawValue, fallback);
1582
2134
  }
1583
2135
  var AthenaError = class extends Error {
1584
2136
  code;
@@ -1602,9 +2154,38 @@ var AthenaError = class extends Error {
1602
2154
  this.raw = input.raw;
1603
2155
  }
1604
2156
  };
1605
- function isRecord3(value) {
2157
+ function isRecord4(value) {
1606
2158
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1607
2159
  }
2160
+ function firstNonEmptyString(...values) {
2161
+ for (const value of values) {
2162
+ if (typeof value === "string" && value.trim().length > 0) {
2163
+ return value.trim();
2164
+ }
2165
+ }
2166
+ return void 0;
2167
+ }
2168
+ function messageFromUnknownError(error) {
2169
+ if (typeof error === "string" && error.trim().length > 0) {
2170
+ return error.trim();
2171
+ }
2172
+ if (error instanceof Error && error.message.trim().length > 0) {
2173
+ return error.message.trim();
2174
+ }
2175
+ if (!isRecord4(error)) {
2176
+ return void 0;
2177
+ }
2178
+ return firstNonEmptyString(error.message, error.error, error.details);
2179
+ }
2180
+ function gatewayCodeFromUnknownError(error) {
2181
+ if (!isRecord4(error) || typeof error.gatewayCode !== "string") {
2182
+ return void 0;
2183
+ }
2184
+ return error.gatewayCode;
2185
+ }
2186
+ function isAthenaResultErrorLike(value) {
2187
+ 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");
2188
+ }
1608
2189
  function isAthenaErrorKind(value) {
1609
2190
  return value === "unique_violation" || value === "not_found" || value === "validation" || value === "auth" || value === "rate_limit" || value === "transient" || value === "unknown";
1610
2191
  }
@@ -1615,7 +2196,7 @@ function isAthenaErrorCategory(value) {
1615
2196
  return value === "transport" || value === "client" || value === "server" || value === "database" || value === "unknown";
1616
2197
  }
1617
2198
  function isNormalizedAthenaError(value) {
1618
- return isRecord3(value) && isAthenaErrorKind(value.kind) && isAthenaErrorCode(value.code) && isAthenaErrorCategory(value.category) && typeof value.retryable === "boolean" && typeof value.message === "string" && "raw" in value;
2199
+ return isRecord4(value) && isAthenaErrorKind(value.kind) && isAthenaErrorCode(value.code) && isAthenaErrorCategory(value.category) && typeof value.retryable === "boolean" && typeof value.message === "string" && "raw" in value;
1619
2200
  }
1620
2201
  function withContextOverrides(normalized, context) {
1621
2202
  if (!context?.table && !context?.operation) {
@@ -1628,7 +2209,7 @@ function withContextOverrides(normalized, context) {
1628
2209
  };
1629
2210
  }
1630
2211
  function resolveAttachedNormalizedError(resultOrError) {
1631
- if (!isRecord3(resultOrError)) return void 0;
2212
+ if (!isRecord4(resultOrError)) return void 0;
1632
2213
  if (!("__athenaNormalizedError" in resultOrError)) return void 0;
1633
2214
  const candidate = resultOrError.__athenaNormalizedError;
1634
2215
  return isNormalizedAthenaError(candidate) ? candidate : void 0;
@@ -1647,7 +2228,7 @@ function contextHint(context) {
1647
2228
  return `Identity: ${identity}`;
1648
2229
  }
1649
2230
  function isAthenaResultLike(value) {
1650
- return isRecord3(value) && "status" in value && "error" in value && "data" in value && typeof value.status === "number";
2231
+ return isRecord4(value) && "status" in value && "error" in value && "data" in value && typeof value.status === "number";
1651
2232
  }
1652
2233
  function operationFromDetails(details) {
1653
2234
  if (!details?.endpoint) return void 0;
@@ -1689,6 +2270,7 @@ function classifyKind(status, code, message) {
1689
2270
  if (status === 404 || hasNotFoundPattern) return "not_found";
1690
2271
  if (status === 401 || status === 403 || hasAuthPattern) return "auth";
1691
2272
  if (status === 429 || hasRateLimitPattern) return "rate_limit";
2273
+ if (code === "INVALID_URL") return "validation";
1692
2274
  if (status === 400 || status === 422 || hasValidationPattern) return "validation";
1693
2275
  if (code === "NETWORK_ERROR" || status === 0 || status !== void 0 && status >= 500 || hasTransientPattern) {
1694
2276
  return "transient";
@@ -1696,6 +2278,9 @@ function classifyKind(status, code, message) {
1696
2278
  return "unknown";
1697
2279
  }
1698
2280
  function toAthenaErrorCode(kind, status, gatewayCode) {
2281
+ if (gatewayCode === "INVALID_URL") {
2282
+ return AthenaErrorCode.ValidationFailed;
2283
+ }
1699
2284
  if (gatewayCode === "NETWORK_ERROR" || kind === "transient" && status === 0) {
1700
2285
  return AthenaErrorCode.NetworkUnavailable;
1701
2286
  }
@@ -1744,15 +2329,16 @@ function toAthenaGatewayError(source, fallbackMessage, context) {
1744
2329
  return source;
1745
2330
  }
1746
2331
  if (isAthenaResultLike(source) && source.errorDetails) {
2332
+ const message2 = messageFromUnknownError(source.error) ?? source.errorDetails.message ?? fallbackMessage;
1747
2333
  return new AthenaGatewayError({
1748
2334
  code: source.errorDetails.code,
1749
- message: source.error ?? source.errorDetails.message,
2335
+ message: message2,
1750
2336
  status: source.status,
1751
2337
  endpoint: source.errorDetails.endpoint,
1752
2338
  method: source.errorDetails.method,
1753
2339
  requestId: source.errorDetails.requestId,
1754
- hint: source.errorDetails.hint,
1755
- cause: source.errorDetails.cause
2340
+ hint: (isRecord4(source.error) ? firstNonEmptyString(source.error.hint) : void 0) ?? source.errorDetails.hint,
2341
+ cause: (isRecord4(source.error) ? firstNonEmptyString(source.error.cause) : void 0) ?? source.errorDetails.cause
1756
2342
  });
1757
2343
  }
1758
2344
  const normalized = normalizeAthenaError(source, context);
@@ -1774,13 +2360,50 @@ function normalizeAthenaError(resultOrError, context) {
1774
2360
  return withContextOverrides(attached, context);
1775
2361
  }
1776
2362
  if (isAthenaResultLike(resultOrError)) {
2363
+ if (isAthenaResultErrorLike(resultOrError.error)) {
2364
+ return {
2365
+ kind: resultOrError.error.kind ?? classifyKind(resultOrError.status, gatewayCodeFromUnknownError(resultOrError.error), resultOrError.error.message),
2366
+ code: resultOrError.error.athenaCode ?? toAthenaErrorCode(
2367
+ resultOrError.error.kind ?? classifyKind(
2368
+ resultOrError.status,
2369
+ gatewayCodeFromUnknownError(resultOrError.error),
2370
+ resultOrError.error.message
2371
+ ),
2372
+ resultOrError.error.status ?? resultOrError.status,
2373
+ gatewayCodeFromUnknownError(resultOrError.error)
2374
+ ),
2375
+ category: resultOrError.error.category ?? toAthenaErrorCategory(
2376
+ resultOrError.error.kind ?? classifyKind(
2377
+ resultOrError.status,
2378
+ gatewayCodeFromUnknownError(resultOrError.error),
2379
+ resultOrError.error.message
2380
+ ),
2381
+ resultOrError.error.status ?? resultOrError.status
2382
+ ),
2383
+ retryable: resultOrError.error.retryable ?? isRetryable(
2384
+ resultOrError.error.kind ?? classifyKind(
2385
+ resultOrError.status,
2386
+ gatewayCodeFromUnknownError(resultOrError.error),
2387
+ resultOrError.error.message
2388
+ ),
2389
+ resultOrError.error.status ?? resultOrError.status
2390
+ ),
2391
+ status: resultOrError.error.status ?? resultOrError.status,
2392
+ constraint: resultOrError.error.constraint,
2393
+ table: context?.table ?? resultOrError.error.table,
2394
+ operation: context?.operation ?? resultOrError.error.operation,
2395
+ message: resultOrError.error.message,
2396
+ raw: resultOrError.error.raw ?? resultOrError.raw
2397
+ };
2398
+ }
1777
2399
  const details = resultOrError.errorDetails;
1778
- const message2 = resultOrError.error ?? details?.message ?? `Athena ${context?.operation ?? operationFromDetails(details) ?? "request"} failed`;
2400
+ const message2 = messageFromUnknownError(resultOrError.error) ?? details?.message ?? `Athena ${context?.operation ?? operationFromDetails(details) ?? "request"} failed`;
1779
2401
  const operation = context?.operation ?? operationFromDetails(details);
1780
2402
  const table = context?.table ?? extractTable(message2);
1781
2403
  const constraint = extractConstraint(message2);
1782
- const kind2 = classifyKind(resultOrError.status, details?.code, message2);
1783
- const code = toAthenaErrorCode(kind2, resultOrError.status, details?.code);
2404
+ const gatewayCode = details?.code ?? gatewayCodeFromUnknownError(resultOrError.error);
2405
+ const kind2 = classifyKind(resultOrError.status, gatewayCode, message2);
2406
+ const code = toAthenaErrorCode(kind2, resultOrError.status, gatewayCode);
1784
2407
  const category = toAthenaErrorCategory(kind2, resultOrError.status);
1785
2408
  return {
1786
2409
  kind: kind2,
@@ -1817,7 +2440,7 @@ function normalizeAthenaError(resultOrError, context) {
1817
2440
  };
1818
2441
  }
1819
2442
  if (resultOrError instanceof Error) {
1820
- const maybeStatus = isRecord3(resultOrError) && typeof resultOrError.status === "number" ? resultOrError.status : void 0;
2443
+ const maybeStatus = isRecord4(resultOrError) && typeof resultOrError.status === "number" ? resultOrError.status : void 0;
1821
2444
  const kind2 = classifyKind(maybeStatus, void 0, resultOrError.message);
1822
2445
  return {
1823
2446
  kind: kind2,
@@ -2006,8 +2629,8 @@ async function withRetry(config, fn) {
2006
2629
  // src/db/module.ts
2007
2630
  function createDbModule(input) {
2008
2631
  const db = {
2009
- from(table) {
2010
- return input.from(table);
2632
+ from(table, options) {
2633
+ return input.from(table, options);
2011
2634
  },
2012
2635
  select(table, columns, options) {
2013
2636
  return input.from(table).select(columns, options);
@@ -2034,17 +2657,551 @@ function createDbModule(input) {
2034
2657
  return db;
2035
2658
  }
2036
2659
 
2660
+ // src/query-ast.ts
2661
+ 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;
2662
+ var FILTER_OPERATORS = /* @__PURE__ */ new Set([
2663
+ "eq",
2664
+ "neq",
2665
+ "gt",
2666
+ "gte",
2667
+ "lt",
2668
+ "lte",
2669
+ "like",
2670
+ "ilike",
2671
+ "is",
2672
+ "in",
2673
+ "contains",
2674
+ "containedBy"
2675
+ ]);
2676
+ var BOOLEAN_SAFE_OPERATORS = /* @__PURE__ */ new Set([
2677
+ "eq",
2678
+ "neq",
2679
+ "gt",
2680
+ "gte",
2681
+ "lt",
2682
+ "lte",
2683
+ "like",
2684
+ "ilike",
2685
+ "is"
2686
+ ]);
2687
+ function isRecord5(value) {
2688
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2689
+ }
2690
+ function isUuidString(value) {
2691
+ return UUID_PATTERN.test(value.trim());
2692
+ }
2693
+ function isUuidIdentifierColumn(column) {
2694
+ return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
2695
+ }
2696
+ function shouldUseUuidTextComparison(column, value) {
2697
+ return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
2698
+ }
2699
+ function isRelationSelectNode(value) {
2700
+ return isRecord5(value) && isRecord5(value.select);
2701
+ }
2702
+ function normalizeIdentifier(value, label) {
2703
+ const normalized = value.trim();
2704
+ if (!normalized) {
2705
+ throw new Error(`${label} must be a non-empty string`);
2706
+ }
2707
+ return normalized;
2708
+ }
2709
+ function stringifyFilterValue(value) {
2710
+ if (Array.isArray(value)) {
2711
+ return value.join(",");
2712
+ }
2713
+ return String(value);
2714
+ }
2715
+ function buildGatewayCondition(operator, column, value) {
2716
+ const condition = { operator };
2717
+ if (column) {
2718
+ condition.column = column;
2719
+ if (operator === "eq") {
2720
+ condition.eq_column = column;
2721
+ }
2722
+ }
2723
+ if (value !== void 0) {
2724
+ condition.value = value;
2725
+ if (operator === "eq") {
2726
+ condition.eq_value = value;
2727
+ }
2728
+ }
2729
+ if (operator === "eq" && column && value !== void 0 && shouldUseUuidTextComparison(column, value)) {
2730
+ condition.column_cast = "text";
2731
+ condition.eq_column_cast = "text";
2732
+ }
2733
+ return condition;
2734
+ }
2735
+ function compileRelationToken(key, node) {
2736
+ const nested = compileSelectShape(node.select);
2737
+ const propertyKey = normalizeIdentifier(key, "select relation key");
2738
+ if (node.schema && node.via) {
2739
+ throw new Error(
2740
+ `findMany relation "${propertyKey}" cannot combine schema and via yet; use schema with the relation key, or use via without schema`
2741
+ );
2742
+ }
2743
+ const relationTokenBase = normalizeIdentifier(node.via ?? propertyKey, "select relation token");
2744
+ if (node.schema && relationTokenBase.includes(".")) {
2745
+ throw new Error(
2746
+ `findMany relation "${propertyKey}" already resolves to a qualified relation token; do not also set schema`
2747
+ );
2748
+ }
2749
+ const relationSchema = node.schema?.trim();
2750
+ const relationToken = relationSchema ? `${normalizeIdentifier(relationSchema, "select relation schema")}.${relationTokenBase}` : relationTokenBase;
2751
+ const alias = node.as?.trim() || (relationToken !== propertyKey ? propertyKey : "");
2752
+ const prefix = alias ? `${alias}:` : "";
2753
+ return `${prefix}${relationToken}(${nested})`;
2754
+ }
2755
+ function compileSelectShape(select) {
2756
+ if (!isRecord5(select)) {
2757
+ throw new Error("findMany select must be an object");
2758
+ }
2759
+ const tokens = [];
2760
+ for (const [rawKey, rawValue] of Object.entries(select)) {
2761
+ if (rawValue === void 0) {
2762
+ continue;
2763
+ }
2764
+ if (rawValue === true) {
2765
+ tokens.push(normalizeIdentifier(rawKey, "select column"));
2766
+ continue;
2767
+ }
2768
+ if (isRelationSelectNode(rawValue)) {
2769
+ tokens.push(compileRelationToken(rawKey, rawValue));
2770
+ continue;
2771
+ }
2772
+ throw new Error(`Unsupported select node for "${rawKey}"`);
2773
+ }
2774
+ if (tokens.length === 0) {
2775
+ throw new Error("findMany select requires at least one field");
2776
+ }
2777
+ return tokens.join(",");
2778
+ }
2779
+ function selectShapeUsesRelationSchema(select) {
2780
+ if (!isRecord5(select)) {
2781
+ return false;
2782
+ }
2783
+ for (const rawValue of Object.values(select)) {
2784
+ if (!isRelationSelectNode(rawValue)) {
2785
+ continue;
2786
+ }
2787
+ if (typeof rawValue.schema === "string" && rawValue.schema.trim().length > 0) {
2788
+ return true;
2789
+ }
2790
+ if (selectShapeUsesRelationSchema(rawValue.select)) {
2791
+ return true;
2792
+ }
2793
+ }
2794
+ return false;
2795
+ }
2796
+ function compileColumnWhere(column, input) {
2797
+ const normalizedColumn = normalizeIdentifier(column, "where column");
2798
+ if (!isRecord5(input)) {
2799
+ return [buildGatewayCondition("eq", normalizedColumn, input)];
2800
+ }
2801
+ const conditions = [];
2802
+ for (const [rawOperator, rawValue] of Object.entries(input)) {
2803
+ if (rawValue === void 0) {
2804
+ continue;
2805
+ }
2806
+ if (!FILTER_OPERATORS.has(rawOperator)) {
2807
+ throw new Error(`Unsupported where operator "${rawOperator}" on "${normalizedColumn}"`);
2808
+ }
2809
+ if ((rawOperator === "in" || rawOperator === "contains" || rawOperator === "containedBy") && !Array.isArray(rawValue)) {
2810
+ throw new Error(`where.${normalizedColumn}.${rawOperator} requires an array value`);
2811
+ }
2812
+ conditions.push(
2813
+ buildGatewayCondition(
2814
+ rawOperator,
2815
+ normalizedColumn,
2816
+ rawValue
2817
+ )
2818
+ );
2819
+ }
2820
+ if (conditions.length === 0) {
2821
+ throw new Error(`where.${normalizedColumn} requires at least one operator`);
2822
+ }
2823
+ return conditions;
2824
+ }
2825
+ function compileBooleanExpressionTerms(clause, label) {
2826
+ if (!isRecord5(clause)) {
2827
+ throw new Error(`findMany where.${label} clauses must be objects`);
2828
+ }
2829
+ const entries = Object.entries(clause).filter(([, value]) => value !== void 0);
2830
+ if (entries.length !== 1) {
2831
+ throw new Error(`findMany where.${label} clauses must target exactly one column`);
2832
+ }
2833
+ const [rawColumn, rawValue] = entries[0];
2834
+ const column = normalizeIdentifier(rawColumn, `where.${label} column`);
2835
+ if (!isRecord5(rawValue)) {
2836
+ return [`${column}.eq.${stringifyFilterValue(rawValue)}`];
2837
+ }
2838
+ const operatorEntries = Object.entries(rawValue).filter(([, value]) => value !== void 0);
2839
+ if (operatorEntries.length === 0) {
2840
+ throw new Error(`findMany where.${label}.${column} requires at least one operator`);
2841
+ }
2842
+ if (label === "not" && operatorEntries.length > 1) {
2843
+ throw new Error("findMany where.not only supports a single lossless operator expression");
2844
+ }
2845
+ return operatorEntries.map(([rawOperator, rawOperand]) => {
2846
+ if (!BOOLEAN_SAFE_OPERATORS.has(rawOperator)) {
2847
+ throw new Error(`findMany where.${label} only supports lossless scalar operators`);
2848
+ }
2849
+ if (Array.isArray(rawOperand)) {
2850
+ throw new Error(`findMany where.${label} does not support array-valued operators`);
2851
+ }
2852
+ return `${column}.${rawOperator}.${stringifyFilterValue(rawOperand)}`;
2853
+ });
2854
+ }
2855
+ function compileWhere(where) {
2856
+ if (where === void 0) {
2857
+ return void 0;
2858
+ }
2859
+ if (!isRecord5(where)) {
2860
+ throw new Error("findMany where must be an object");
2861
+ }
2862
+ const conditions = [];
2863
+ for (const [rawKey, rawValue] of Object.entries(where)) {
2864
+ if (rawValue === void 0) {
2865
+ continue;
2866
+ }
2867
+ if (rawKey === "or") {
2868
+ if (!Array.isArray(rawValue) || rawValue.length === 0) {
2869
+ throw new Error("findMany where.or must be a non-empty array");
2870
+ }
2871
+ const expressions = rawValue.flatMap(
2872
+ (value) => compileBooleanExpressionTerms(value, "or")
2873
+ );
2874
+ conditions.push(buildGatewayCondition("or", void 0, expressions.join(",")));
2875
+ continue;
2876
+ }
2877
+ if (rawKey === "not") {
2878
+ const expressions = compileBooleanExpressionTerms(rawValue, "not");
2879
+ if (expressions.length !== 1) {
2880
+ throw new Error("findMany where.not must compile to exactly one lossless expression");
2881
+ }
2882
+ conditions.push(buildGatewayCondition("not", void 0, expressions[0]));
2883
+ continue;
2884
+ }
2885
+ conditions.push(...compileColumnWhere(rawKey, rawValue));
2886
+ }
2887
+ return conditions.length > 0 ? conditions : void 0;
2888
+ }
2889
+ function resolveOrderDirection(input) {
2890
+ if (typeof input === "boolean") {
2891
+ return input === false ? "descending" : "ascending";
2892
+ }
2893
+ if (typeof input === "string") {
2894
+ const normalized = input.trim().toLowerCase();
2895
+ if (normalized === "asc" || normalized === "ascending") {
2896
+ return "ascending";
2897
+ }
2898
+ if (normalized === "desc" || normalized === "descending") {
2899
+ return "descending";
2900
+ }
2901
+ throw new Error(`Unsupported orderBy direction "${input}"`);
2902
+ }
2903
+ return input.ascending === false ? "descending" : "ascending";
2904
+ }
2905
+ function compileOrderBy(orderBy) {
2906
+ if (orderBy === void 0) {
2907
+ return void 0;
2908
+ }
2909
+ if (!isRecord5(orderBy)) {
2910
+ throw new Error("findMany orderBy must be an object");
2911
+ }
2912
+ if ("column" in orderBy) {
2913
+ return {
2914
+ field: normalizeIdentifier(String(orderBy.column), "orderBy column"),
2915
+ direction: orderBy.ascending === false ? "descending" : "ascending"
2916
+ };
2917
+ }
2918
+ const entries = Object.entries(orderBy).filter(([, value]) => value !== void 0);
2919
+ if (entries.length === 0) {
2920
+ return void 0;
2921
+ }
2922
+ if (entries.length > 1) {
2923
+ throw new Error("findMany orderBy only supports a single column in v1");
2924
+ }
2925
+ const [column, input] = entries[0];
2926
+ return {
2927
+ field: normalizeIdentifier(column, "orderBy column"),
2928
+ direction: resolveOrderDirection(input)
2929
+ };
2930
+ }
2931
+
2932
+ // src/gateway/structured-select.ts
2933
+ var IDENTIFIER_SEGMENT_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
2934
+ function isIdentifierPath(value) {
2935
+ const segments = value.split(".").map((segment) => segment.trim());
2936
+ return segments.length > 1 && segments.every((segment) => IDENTIFIER_SEGMENT_PATTERN.test(segment));
2937
+ }
2938
+ function extractRelationHead(raw) {
2939
+ const trimmed = raw.trim();
2940
+ if (!trimmed) return "";
2941
+ const aliasIndex = trimmed.indexOf(":");
2942
+ const withoutAlias = aliasIndex >= 0 ? trimmed.slice(aliasIndex + 1).trim() : trimmed;
2943
+ const modifierIndex = withoutAlias.indexOf("!");
2944
+ return (modifierIndex >= 0 ? withoutAlias.slice(0, modifierIndex) : withoutAlias).trim();
2945
+ }
2946
+ function hasSchemaQualifiedRelationToken(select) {
2947
+ let singleQuoted = false;
2948
+ let doubleQuoted = false;
2949
+ let tokenStart = 0;
2950
+ for (let index = 0; index < select.length; index += 1) {
2951
+ const char = select[index];
2952
+ const next = index + 1 < select.length ? select[index + 1] : "";
2953
+ if (singleQuoted) {
2954
+ if (char === "'" && next === "'") {
2955
+ index += 1;
2956
+ continue;
2957
+ }
2958
+ if (char === "'") {
2959
+ singleQuoted = false;
2960
+ }
2961
+ continue;
2962
+ }
2963
+ if (doubleQuoted) {
2964
+ if (char === '"' && next === '"') {
2965
+ index += 1;
2966
+ continue;
2967
+ }
2968
+ if (char === '"') {
2969
+ doubleQuoted = false;
2970
+ }
2971
+ continue;
2972
+ }
2973
+ if (char === "'") {
2974
+ singleQuoted = true;
2975
+ continue;
2976
+ }
2977
+ if (char === '"') {
2978
+ doubleQuoted = true;
2979
+ continue;
2980
+ }
2981
+ if (char === "(") {
2982
+ const relationHead = extractRelationHead(select.slice(tokenStart, index));
2983
+ if (isIdentifierPath(relationHead)) {
2984
+ return true;
2985
+ }
2986
+ tokenStart = index + 1;
2987
+ continue;
2988
+ }
2989
+ if (char === "," || char === ")") {
2990
+ tokenStart = index + 1;
2991
+ }
2992
+ }
2993
+ return false;
2994
+ }
2995
+ function toStructuredSelectString(columns) {
2996
+ return Array.isArray(columns) ? columns.join(",") : columns;
2997
+ }
2998
+ function buildStructuredWhere(conditions) {
2999
+ if (!conditions?.length) return void 0;
3000
+ const where = {};
3001
+ for (const condition of conditions) {
3002
+ if (!condition.column) {
3003
+ return null;
3004
+ }
3005
+ if (condition.column_cast !== void 0 || condition.value_cast !== void 0) {
3006
+ return null;
3007
+ }
3008
+ const operand = condition.value;
3009
+ const operator = condition.operator;
3010
+ if (operator === "eq") {
3011
+ if (operand === void 0) return null;
3012
+ } else if (operator === "neq" || operator === "gt" || operator === "lt") {
3013
+ if (operand === void 0) return null;
3014
+ } else if (operator === "in") {
3015
+ if (!Array.isArray(operand)) return null;
3016
+ } else {
3017
+ return null;
3018
+ }
3019
+ const existing = where[condition.column];
3020
+ if (existing !== void 0 && (typeof existing !== "object" || existing === null || Array.isArray(existing))) {
3021
+ return null;
3022
+ }
3023
+ const next = existing ?? {};
3024
+ if (Object.prototype.hasOwnProperty.call(next, operator)) {
3025
+ return null;
3026
+ }
3027
+ next[operator] = operand;
3028
+ where[condition.column] = next;
3029
+ }
3030
+ return where;
3031
+ }
3032
+ function buildStructuredOrderBy(order) {
3033
+ if (!order?.field) return void 0;
3034
+ return {
3035
+ [order.field]: order.direction === "descending" ? "desc" : "asc"
3036
+ };
3037
+ }
3038
+ function buildStructuredSelectTransport(input) {
3039
+ const select = toStructuredSelectString(input.columns).trim();
3040
+ if (!select || select === "*") {
3041
+ return null;
3042
+ }
3043
+ if (!hasSchemaQualifiedRelationToken(select)) {
3044
+ return null;
3045
+ }
3046
+ if (input.count !== void 0 || input.head !== void 0) {
3047
+ return {
3048
+ error: "Schema-qualified nested select strings require structured select transport, which does not support count/head options in athena-js yet."
3049
+ };
3050
+ }
3051
+ const where = buildStructuredWhere(input.conditions);
3052
+ if (where === null) {
3053
+ return {
3054
+ error: "Schema-qualified nested select strings only support eq, neq, gt, lt, and in filters in athena-js structured select transport."
3055
+ };
3056
+ }
3057
+ return {
3058
+ select,
3059
+ payload: {
3060
+ table_name: input.tableName,
3061
+ select,
3062
+ where,
3063
+ orderBy: buildStructuredOrderBy(input.order),
3064
+ limit: input.limit,
3065
+ offset: input.offset,
3066
+ strip_nulls: input.stripNulls
3067
+ }
3068
+ };
3069
+ }
3070
+
3071
+ // src/query-transport.ts
3072
+ function canUseFindManyAstTransport(state) {
3073
+ return state.conditions.length === 0 && state.offset === void 0 && state.currentPage === void 0 && state.pageSize === void 0 && state.totalPages === void 0;
3074
+ }
3075
+ function toFindManyAstOrder(order) {
3076
+ if (!order) {
3077
+ return void 0;
3078
+ }
3079
+ return {
3080
+ column: order.field,
3081
+ ascending: order.direction !== "descending"
3082
+ };
3083
+ }
3084
+ function resolvePagination(input) {
3085
+ let limit = input.limit;
3086
+ let offset = input.offset;
3087
+ if (limit === void 0 && input.pageSize !== void 0) {
3088
+ limit = Math.max(0, Math.trunc(input.pageSize));
3089
+ }
3090
+ if (offset === void 0 && input.pageSize !== void 0 && input.currentPage !== void 0 && input.currentPage > 0) {
3091
+ offset = (Math.trunc(input.currentPage) - 1) * Math.max(0, Math.trunc(input.pageSize));
3092
+ }
3093
+ return { limit, offset };
3094
+ }
3095
+ function hasTypedEqualityComparison(conditions) {
3096
+ return conditions?.some(
3097
+ (condition) => condition.operator === "eq" && (condition.value_cast !== void 0 || condition.column_cast !== void 0)
3098
+ ) ?? false;
3099
+ }
3100
+ function createSelectTransportPlan(input) {
3101
+ const conditions = input.state.conditions.length ? input.state.conditions.map((condition) => ({ ...condition })) : void 0;
3102
+ if (hasTypedEqualityComparison(conditions) && !input.options?.head && !input.options?.count && conditions) {
3103
+ const query = input.buildTypedSelectQuery({
3104
+ tableName: input.tableName,
3105
+ columns: input.columns,
3106
+ conditions,
3107
+ limit: input.state.limit,
3108
+ offset: input.state.offset,
3109
+ currentPage: input.state.currentPage,
3110
+ pageSize: input.state.pageSize,
3111
+ order: input.state.order
3112
+ });
3113
+ if (query) {
3114
+ return {
3115
+ kind: "query",
3116
+ query,
3117
+ payload: { query }
3118
+ };
3119
+ }
3120
+ }
3121
+ const pagination = resolvePagination({
3122
+ limit: input.state.limit,
3123
+ offset: input.state.offset,
3124
+ currentPage: input.state.currentPage,
3125
+ pageSize: input.state.pageSize
3126
+ });
3127
+ const stripNulls = input.options?.stripNulls ?? true;
3128
+ const structuredSelectTransport = buildStructuredSelectTransport({
3129
+ tableName: input.tableName,
3130
+ columns: input.columns,
3131
+ conditions,
3132
+ limit: pagination.limit,
3133
+ offset: pagination.offset,
3134
+ order: input.state.order,
3135
+ stripNulls,
3136
+ count: input.options?.count,
3137
+ head: input.options?.head
3138
+ });
3139
+ if (structuredSelectTransport && "error" in structuredSelectTransport) {
3140
+ throw new Error(structuredSelectTransport.error);
3141
+ }
3142
+ if (structuredSelectTransport) {
3143
+ const { payload, select } = structuredSelectTransport;
3144
+ return {
3145
+ kind: "fetch",
3146
+ payload,
3147
+ debug: {
3148
+ columns: select,
3149
+ conditions,
3150
+ limit: pagination.limit,
3151
+ offset: pagination.offset,
3152
+ order: input.state.order
3153
+ }
3154
+ };
3155
+ }
3156
+ return {
3157
+ kind: "fetch",
3158
+ payload: {
3159
+ table_name: input.tableName,
3160
+ columns: input.columns,
3161
+ conditions,
3162
+ limit: input.state.limit,
3163
+ offset: input.state.offset,
3164
+ current_page: input.state.currentPage,
3165
+ page_size: input.state.pageSize,
3166
+ total_pages: input.state.totalPages,
3167
+ sort_by: input.state.order,
3168
+ strip_nulls: stripNulls,
3169
+ count: input.options?.count,
3170
+ head: input.options?.head
3171
+ },
3172
+ debug: {
3173
+ columns: input.columns,
3174
+ conditions,
3175
+ limit: input.state.limit,
3176
+ offset: input.state.offset,
3177
+ currentPage: input.state.currentPage,
3178
+ pageSize: input.state.pageSize,
3179
+ order: input.state.order
3180
+ }
3181
+ };
3182
+ }
3183
+
2037
3184
  // src/client.ts
2038
3185
  var DEFAULT_COLUMNS = "*";
2039
- 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;
2040
3186
  var SAFE_CAST_PATTERN = /^[a-z_][a-z0-9_]*(?:\[\])?$/i;
2041
3187
  var ATHENA_NORMALIZED_ERROR_KEY = "__athenaNormalizedError";
3188
+ var QUERY_TRACE_STACK_SKIP_PATTERNS = [
3189
+ "src\\client.ts",
3190
+ "src/client.ts",
3191
+ "dist\\client.",
3192
+ "dist/client.",
3193
+ "node_modules\\@xylex-group\\athena",
3194
+ "node_modules/@xylex-group/athena",
3195
+ "node:internal",
3196
+ "internal/process"
3197
+ ];
2042
3198
  function formatResult(response) {
2043
3199
  const result = {
2044
3200
  data: response.data ?? null,
2045
- error: response.error ?? null,
3201
+ error: null,
2046
3202
  errorDetails: response.errorDetails ?? null,
2047
3203
  status: response.status,
3204
+ statusText: response.statusText ?? null,
2048
3205
  raw: response.raw
2049
3206
  };
2050
3207
  if (response.count !== void 0) {
@@ -2060,19 +3217,236 @@ function attachNormalizedError(result, normalizedError) {
2060
3217
  writable: false
2061
3218
  });
2062
3219
  }
2063
- function createResultFormatter(experimental) {
2064
- if (!experimental?.enableErrorNormalization) {
2065
- return formatResult;
3220
+ function createResultFormatter(experimental) {
3221
+ return (response, context) => {
3222
+ const result = formatResult(response);
3223
+ if (response.error == null && response.errorDetails == null) {
3224
+ return result;
3225
+ }
3226
+ const normalizedError = normalizeAthenaError(
3227
+ {
3228
+ ...result,
3229
+ error: response.error ?? response.errorDetails?.message ?? null
3230
+ },
3231
+ context
3232
+ );
3233
+ result.error = createResultError(response, result, normalizedError);
3234
+ attachNormalizedError(result, normalizedError);
3235
+ return result;
3236
+ };
3237
+ }
3238
+ function isRecord6(value) {
3239
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3240
+ }
3241
+ function firstNonEmptyString2(...values) {
3242
+ for (const value of values) {
3243
+ if (typeof value === "string" && value.trim().length > 0) {
3244
+ return value.trim();
3245
+ }
3246
+ }
3247
+ return void 0;
3248
+ }
3249
+ function resolveStructuredErrorPayload2(raw) {
3250
+ if (!isRecord6(raw)) return null;
3251
+ return isRecord6(raw.error) ? raw.error : raw;
3252
+ }
3253
+ function resolveStructuredErrorDetails(payload, message) {
3254
+ if (!payload || !("details" in payload)) {
3255
+ return null;
3256
+ }
3257
+ const details = payload.details;
3258
+ if (details == null) {
3259
+ return null;
3260
+ }
3261
+ if (typeof details === "string" && details.trim() === message.trim()) {
3262
+ return null;
3263
+ }
3264
+ return details;
3265
+ }
3266
+ function createResultError(response, result, normalized) {
3267
+ const rawRecord = isRecord6(response.raw) ? response.raw : null;
3268
+ const payload = resolveStructuredErrorPayload2(response.raw);
3269
+ const message = firstNonEmptyString2(
3270
+ response.error,
3271
+ payload?.message,
3272
+ payload?.error,
3273
+ payload?.details,
3274
+ response.errorDetails?.message,
3275
+ normalized.message
3276
+ ) ?? normalized.message;
3277
+ const statusText = firstNonEmptyString2(response.statusText, rawRecord?.statusText) ?? null;
3278
+ const hint = firstNonEmptyString2(payload?.hint, response.errorDetails?.hint) ?? null;
3279
+ const code = firstNonEmptyString2(payload?.code) ?? normalized.code;
3280
+ const details = resolveStructuredErrorDetails(payload, message) ?? response.errorDetails?.cause ?? null;
3281
+ return {
3282
+ message,
3283
+ code,
3284
+ athenaCode: normalized.code,
3285
+ gatewayCode: response.errorDetails?.code ?? null,
3286
+ kind: normalized.kind,
3287
+ category: normalized.category,
3288
+ retryable: normalized.retryable,
3289
+ details,
3290
+ hint,
3291
+ status: result.status,
3292
+ statusText,
3293
+ constraint: normalized.constraint,
3294
+ table: normalized.table,
3295
+ operation: normalized.operation,
3296
+ endpoint: response.errorDetails?.endpoint,
3297
+ method: response.errorDetails?.method,
3298
+ requestId: response.errorDetails?.requestId,
3299
+ cause: response.errorDetails?.cause,
3300
+ raw: result.raw
3301
+ };
3302
+ }
3303
+ function parseQueryTraceCallsiteFrame(frame) {
3304
+ const trimmed = frame.trim();
3305
+ if (!trimmed) {
3306
+ return null;
3307
+ }
3308
+ let body = trimmed.replace(/^at\s+/, "");
3309
+ if (body.startsWith("async ")) {
3310
+ body = body.slice(6);
3311
+ }
3312
+ let functionName;
3313
+ let location = body;
3314
+ const wrappedMatch = body.match(/^(.*?)\s+\((.*)\)$/);
3315
+ if (wrappedMatch) {
3316
+ functionName = wrappedMatch[1].trim() || void 0;
3317
+ location = wrappedMatch[2].trim();
3318
+ }
3319
+ const locationMatch = location.match(/^(.*):(\d+):(\d+)$/);
3320
+ if (!locationMatch) {
3321
+ return null;
3322
+ }
3323
+ const filePath = locationMatch[1].replace(/^file:\/\//, "");
3324
+ const line = Number(locationMatch[2]);
3325
+ const column = Number(locationMatch[3]);
3326
+ if (!Number.isFinite(line) || !Number.isFinite(column)) {
3327
+ return null;
3328
+ }
3329
+ const normalizedPath = filePath.replace(/\\/g, "/");
3330
+ const fileName = normalizedPath.split("/").at(-1) ?? filePath;
3331
+ return {
3332
+ filePath,
3333
+ fileName,
3334
+ line,
3335
+ column,
3336
+ frame: trimmed,
3337
+ functionName
3338
+ };
3339
+ }
3340
+ function captureQueryTraceCallsite() {
3341
+ const stack = new Error().stack;
3342
+ if (!stack) return null;
3343
+ const frames = stack.split("\n").slice(2).map((frame) => frame.trim()).filter(Boolean);
3344
+ for (const frame of frames) {
3345
+ if (QUERY_TRACE_STACK_SKIP_PATTERNS.some((pattern) => frame.includes(pattern))) {
3346
+ continue;
3347
+ }
3348
+ const callsite = parseQueryTraceCallsiteFrame(frame);
3349
+ if (callsite) return callsite;
3350
+ }
3351
+ const fallback = frames.find((frame) => !frame.includes("captureQueryTraceCallsite"));
3352
+ return fallback ? parseQueryTraceCallsiteFrame(fallback) : null;
3353
+ }
3354
+ function defaultQueryTraceLogger(event) {
3355
+ const target = event.table ?? event.functionName ?? "gateway";
3356
+ const outcomeState = event.outcome?.error ? "error" : "ok";
3357
+ const banner = `[athena-js][trace] ${event.operation.toUpperCase()} ${event.endpoint} ${target} ${event.durationMs}ms ${outcomeState}`;
3358
+ console.info(banner, event);
3359
+ }
3360
+ function captureTraceCallsite(tracer) {
3361
+ return tracer?.captureCallsite() ?? null;
3362
+ }
3363
+ function createTraceCallsiteStore(tracer, initialCallsite) {
3364
+ let storedCallsite = initialCallsite ?? void 0;
3365
+ return {
3366
+ resolve(callsite) {
3367
+ if (callsite) {
3368
+ storedCallsite = callsite;
3369
+ return callsite;
3370
+ }
3371
+ if (storedCallsite !== void 0) {
3372
+ return storedCallsite;
3373
+ }
3374
+ const capturedCallsite = captureTraceCallsite(tracer);
3375
+ if (capturedCallsite) {
3376
+ storedCallsite = capturedCallsite;
3377
+ }
3378
+ return capturedCallsite;
3379
+ }
3380
+ };
3381
+ }
3382
+ function createQueryTracer(experimental) {
3383
+ const traceOption = experimental?.traceQueries;
3384
+ if (!traceOption) {
3385
+ return void 0;
3386
+ }
3387
+ const logger = typeof traceOption === "object" && traceOption.logger ? traceOption.logger : defaultQueryTraceLogger;
3388
+ const emit = (event) => {
3389
+ try {
3390
+ logger(event);
3391
+ } catch (error) {
3392
+ console.warn("[athena-js][trace] logger failed", error);
3393
+ }
3394
+ };
3395
+ return {
3396
+ captureCallsite: captureQueryTraceCallsite,
3397
+ publishSuccess(context, result, durationMs, callsite) {
3398
+ emit({
3399
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3400
+ durationMs,
3401
+ operation: context.operation,
3402
+ endpoint: context.endpoint,
3403
+ table: context.table,
3404
+ functionName: context.functionName,
3405
+ sql: context.sql,
3406
+ payload: context.payload,
3407
+ options: context.options,
3408
+ callsite,
3409
+ outcome: {
3410
+ status: result.status,
3411
+ error: result.error,
3412
+ errorDetails: result.errorDetails ?? null,
3413
+ count: result.count ?? null,
3414
+ data: result.data,
3415
+ raw: result.raw
3416
+ }
3417
+ });
3418
+ },
3419
+ publishFailure(context, error, durationMs, callsite) {
3420
+ emit({
3421
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3422
+ durationMs,
3423
+ operation: context.operation,
3424
+ endpoint: context.endpoint,
3425
+ table: context.table,
3426
+ functionName: context.functionName,
3427
+ sql: context.sql,
3428
+ payload: context.payload,
3429
+ options: context.options,
3430
+ callsite,
3431
+ thrownError: error
3432
+ });
3433
+ }
3434
+ };
3435
+ }
3436
+ async function executeWithQueryTrace(tracer, context, runner, callsiteOverride) {
3437
+ if (!tracer) {
3438
+ return runner();
2066
3439
  }
2067
- return (response, context) => {
2068
- const result = formatResult(response);
2069
- if (result.error == null) {
2070
- return result;
2071
- }
2072
- const normalizedError = normalizeAthenaError(result, context);
2073
- attachNormalizedError(result, normalizedError);
3440
+ const callsite = callsiteOverride ?? tracer.captureCallsite();
3441
+ const startedAt = Date.now();
3442
+ try {
3443
+ const result = await runner();
3444
+ tracer.publishSuccess(context, result, Date.now() - startedAt, callsite);
2074
3445
  return result;
2075
- };
3446
+ } catch (error) {
3447
+ tracer.publishFailure(context, error, Date.now() - startedAt, callsite);
3448
+ throw error;
3449
+ }
2076
3450
  }
2077
3451
  function toSingleResult(response) {
2078
3452
  const payload = response.data;
@@ -2094,15 +3468,16 @@ function asAthenaJsonObject(value) {
2094
3468
  function asAthenaJsonObjectArray(values) {
2095
3469
  return values;
2096
3470
  }
2097
- function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
3471
+ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS, tracer, initialCallsite) {
2098
3472
  let selectedColumns = defaultColumns === null ? void 0 : defaultColumns;
2099
3473
  let selectedOptions;
2100
3474
  let promise = null;
2101
- const run = (columns, options) => {
3475
+ const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
3476
+ const run = (columns, options, callsite) => {
2102
3477
  const payloadColumns = columns ?? selectedColumns;
2103
3478
  const payloadOptions = options ?? selectedOptions;
2104
3479
  if (!promise) {
2105
- promise = executor(payloadColumns, payloadOptions);
3480
+ promise = executor(payloadColumns, payloadOptions, callsiteStore.resolve(callsite));
2106
3481
  }
2107
3482
  return promise;
2108
3483
  };
@@ -2110,7 +3485,7 @@ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
2110
3485
  select(columns = selectedColumns, options) {
2111
3486
  selectedColumns = columns;
2112
3487
  selectedOptions = options ?? selectedOptions;
2113
- return run(columns, options);
3488
+ return run(columns, options, captureTraceCallsite(tracer));
2114
3489
  },
2115
3490
  returning(columns = selectedColumns, options) {
2116
3491
  return mutationQuery.select(columns, options);
@@ -2118,7 +3493,7 @@ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
2118
3493
  single(columns = selectedColumns, options) {
2119
3494
  selectedColumns = columns;
2120
3495
  selectedOptions = options ?? selectedOptions;
2121
- return run(columns, options).then(toSingleResult);
3496
+ return run(columns, options, captureTraceCallsite(tracer)).then(toSingleResult);
2122
3497
  },
2123
3498
  maybeSingle(columns = selectedColumns, options) {
2124
3499
  return mutationQuery.single(columns, options);
@@ -2141,21 +3516,12 @@ function getResourceId(state) {
2141
3516
  );
2142
3517
  return candidate?.value?.toString();
2143
3518
  }
2144
- function stringifyFilterValue(value) {
3519
+ function stringifyFilterValue2(value) {
2145
3520
  if (Array.isArray(value)) {
2146
3521
  return value.join(",");
2147
3522
  }
2148
3523
  return String(value);
2149
3524
  }
2150
- function isUuidString(value) {
2151
- return UUID_PATTERN.test(value.trim());
2152
- }
2153
- function isUuidIdentifierColumn(column) {
2154
- return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
2155
- }
2156
- function shouldUseUuidTextComparison(column, value) {
2157
- return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
2158
- }
2159
3525
  function normalizeCast(cast) {
2160
3526
  const normalized = cast.trim().toLowerCase();
2161
3527
  if (!SAFE_CAST_PATTERN.test(normalized)) {
@@ -2178,25 +3544,92 @@ function withCast(expression, cast) {
2178
3544
  }
2179
3545
  function buildSelectColumnsClause(columns) {
2180
3546
  if (Array.isArray(columns)) {
2181
- return columns.map((column) => quoteQualifiedIdentifier(column)).join(", ");
3547
+ return columns.map((column) => quoteSelectColumnToken(column)).join(", ");
2182
3548
  }
2183
3549
  return quoteSelectColumnsExpression(columns);
2184
3550
  }
3551
+ function parseIdentifierSegment(input) {
3552
+ const trimmed = input.trim();
3553
+ if (!trimmed) return null;
3554
+ if (!trimmed.startsWith('"')) {
3555
+ return {
3556
+ normalizedValue: trimmed.toLowerCase()
3557
+ };
3558
+ }
3559
+ let value = "";
3560
+ let index = 1;
3561
+ let closed = false;
3562
+ while (index < trimmed.length) {
3563
+ const char = trimmed[index];
3564
+ const next = index + 1 < trimmed.length ? trimmed[index + 1] : "";
3565
+ if (char === '"' && next === '"') {
3566
+ value += '"';
3567
+ index += 2;
3568
+ continue;
3569
+ }
3570
+ if (char === '"') {
3571
+ closed = true;
3572
+ index += 1;
3573
+ break;
3574
+ }
3575
+ value += char;
3576
+ index += 1;
3577
+ }
3578
+ if (!closed || trimmed.slice(index).trim().length > 0 || !value.trim()) {
3579
+ return null;
3580
+ }
3581
+ return {
3582
+ normalizedValue: value
3583
+ };
3584
+ }
3585
+ function splitQualifiedTableName(tableName) {
3586
+ const trimmed = tableName.trim();
3587
+ let inQuotes = false;
3588
+ for (let index = 0; index < trimmed.length; index += 1) {
3589
+ const char = trimmed[index];
3590
+ const next = index + 1 < trimmed.length ? trimmed[index + 1] : "";
3591
+ if (char === '"') {
3592
+ if (inQuotes && next === '"') {
3593
+ index += 1;
3594
+ continue;
3595
+ }
3596
+ inQuotes = !inQuotes;
3597
+ continue;
3598
+ }
3599
+ if (char === "." && !inQuotes) {
3600
+ const schemaSegment = trimmed.slice(0, index).trim();
3601
+ const tableSegment = trimmed.slice(index + 1).trim();
3602
+ if (!schemaSegment || !tableSegment) {
3603
+ return null;
3604
+ }
3605
+ return { schemaSegment };
3606
+ }
3607
+ }
3608
+ return null;
3609
+ }
2185
3610
  function resolveTableNameForCall(tableName, schema) {
2186
3611
  if (!schema) return tableName;
2187
3612
  const normalizedSchema = schema.trim();
2188
3613
  if (!normalizedSchema) {
2189
3614
  throw new Error("schema option must be a non-empty string");
2190
3615
  }
2191
- if (tableName.includes(".")) {
2192
- if (tableName.startsWith(`${normalizedSchema}.`)) {
2193
- return tableName;
3616
+ const normalizedTableName = tableName.trim();
3617
+ const parsedSchema = parseIdentifierSegment(normalizedSchema);
3618
+ if (!parsedSchema) {
3619
+ throw new Error("schema option must be a non-empty string");
3620
+ }
3621
+ const qualified = splitQualifiedTableName(normalizedTableName);
3622
+ if (qualified) {
3623
+ const parsedTableSchema = parseIdentifierSegment(qualified.schemaSegment);
3624
+ const sameSchema = parsedTableSchema ? parsedTableSchema.normalizedValue === parsedSchema.normalizedValue : normalizedTableName.startsWith(`${normalizedSchema}.`);
3625
+ if (sameSchema) {
3626
+ return normalizedTableName;
2194
3627
  }
2195
3628
  throw new Error(
2196
- `schema option "${normalizedSchema}" conflicts with schema-qualified table "${tableName}"`
3629
+ `schema option "${normalizedSchema}" conflicts with schema-qualified table "${normalizedTableName}"`
2197
3630
  );
2198
3631
  }
2199
- return `${normalizedSchema}.${tableName}`;
3632
+ return `${normalizedSchema}.${normalizedTableName}`;
2200
3633
  }
2201
3634
  function conditionToSqlClause(condition) {
2202
3635
  if (!condition.column) return null;
@@ -2274,6 +3707,226 @@ function buildTypedSelectQuery(input) {
2274
3707
  }
2275
3708
  return `${sqlParts.join(" ")};`;
2276
3709
  }
3710
+ function sanitizeSqlComment(comment) {
3711
+ return comment.replace(/\*\//g, "* /");
3712
+ }
3713
+ function toSqlJsonLiteral(value) {
3714
+ if (value === void 0) return "DEFAULT";
3715
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
3716
+ return toSqlLiteral(value);
3717
+ }
3718
+ return `'${escapeSqlStringLiteral(JSON.stringify(value))}'::jsonb`;
3719
+ }
3720
+ function conditionToDebugSqlClause(condition) {
3721
+ const exact = conditionToSqlClause(condition);
3722
+ if (exact) return exact;
3723
+ const rawCondition = sanitizeSqlComment(JSON.stringify(condition));
3724
+ if (!condition.column) {
3725
+ return `TRUE /* unsupported condition: ${rawCondition} */`;
3726
+ }
3727
+ const column = withCast(quoteQualifiedIdentifier(condition.column), condition.column_cast);
3728
+ const value = condition.value;
3729
+ const rhs = withCast(toSqlJsonLiteral(value), condition.value_cast);
3730
+ switch (condition.operator) {
3731
+ case "contains":
3732
+ return `${column} @> ${rhs}`;
3733
+ case "containedBy":
3734
+ return `${column} <@ ${rhs}`;
3735
+ case "not":
3736
+ return `TRUE /* NOT expression passthrough: ${rawCondition} */`;
3737
+ case "or":
3738
+ return `TRUE /* OR expression passthrough: ${rawCondition} */`;
3739
+ default:
3740
+ return `TRUE /* unsupported condition: ${rawCondition} */`;
3741
+ }
3742
+ }
3743
+ function appendOrderLimitOffset(sqlParts, order, limit, offset) {
3744
+ if (order?.field) {
3745
+ const direction = order.direction === "descending" ? "DESC" : "ASC";
3746
+ sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(order.field)} ${direction}`);
3747
+ }
3748
+ if (limit !== void 0) {
3749
+ sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(limit))}`);
3750
+ }
3751
+ if (offset !== void 0) {
3752
+ sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(offset))}`);
3753
+ }
3754
+ }
3755
+ function buildDebugSelectQuery(input) {
3756
+ const sqlParts = [
3757
+ `SELECT ${buildSelectColumnsClause(input.columns)} FROM ${quoteQualifiedIdentifier(input.tableName)}`
3758
+ ];
3759
+ if (input.conditions?.length) {
3760
+ const whereClauses = input.conditions.map(conditionToDebugSqlClause);
3761
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
3762
+ }
3763
+ const pagination = resolvePagination(input);
3764
+ appendOrderLimitOffset(sqlParts, input.order, pagination.limit, pagination.offset);
3765
+ return `${sqlParts.join(" ")};`;
3766
+ }
3767
+ function resolveDebugTableIdentifier(tableName) {
3768
+ if (!tableName?.trim()) {
3769
+ return '"__unknown_table__"';
3770
+ }
3771
+ return quoteQualifiedIdentifier(tableName);
3772
+ }
3773
+ function buildInsertDebugSql(payload) {
3774
+ const rows = Array.isArray(payload.insert_body) ? payload.insert_body : [payload.insert_body];
3775
+ const columns = [];
3776
+ const seen = /* @__PURE__ */ new Set();
3777
+ for (const row of rows) {
3778
+ for (const column of Object.keys(row)) {
3779
+ if (seen.has(column)) continue;
3780
+ seen.add(column);
3781
+ columns.push(column);
3782
+ }
3783
+ }
3784
+ const sqlParts = [`INSERT INTO ${quoteQualifiedIdentifier(payload.table_name)}`];
3785
+ if (!rows.length || !columns.length) {
3786
+ sqlParts.push("DEFAULT VALUES");
3787
+ if (rows.length > 1) {
3788
+ sqlParts.push(`/* trace: ${rows.length} rows collapsed to DEFAULT VALUES */`);
3789
+ }
3790
+ } else {
3791
+ const valuesClause = rows.map((row) => {
3792
+ const values = columns.map((column) => {
3793
+ const hasColumn = Object.prototype.hasOwnProperty.call(row, column);
3794
+ if (!hasColumn) {
3795
+ return payload.default_to_null ? "NULL" : "DEFAULT";
3796
+ }
3797
+ const rowValue = row[column];
3798
+ return toSqlJsonLiteral(rowValue);
3799
+ });
3800
+ return `(${values.join(", ")})`;
3801
+ }).join(", ");
3802
+ const columnClause = columns.map((column) => quoteQualifiedIdentifier(column)).join(", ");
3803
+ sqlParts.push(`(${columnClause})`);
3804
+ sqlParts.push(`VALUES ${valuesClause}`);
3805
+ }
3806
+ if (payload.on_conflict) {
3807
+ const conflictColumns = Array.isArray(payload.on_conflict) ? payload.on_conflict.map((column) => quoteQualifiedIdentifier(column)).join(", ") : payload.on_conflict;
3808
+ if (payload.update_body && Object.keys(payload.update_body).length > 0) {
3809
+ const assignments = Object.entries(payload.update_body).map(
3810
+ ([column, value]) => `${quoteQualifiedIdentifier(column)} = ${toSqlJsonLiteral(value)}`
3811
+ );
3812
+ sqlParts.push(`ON CONFLICT (${conflictColumns}) DO UPDATE SET ${assignments.join(", ")}`);
3813
+ } else {
3814
+ sqlParts.push(`ON CONFLICT (${conflictColumns}) DO NOTHING`);
3815
+ }
3816
+ }
3817
+ if (payload.columns) {
3818
+ sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
3819
+ }
3820
+ return `${sqlParts.join(" ")};`;
3821
+ }
3822
+ function buildUpdateDebugSql(payload) {
3823
+ const set = payload.set ?? payload.data ?? {};
3824
+ const assignments = Object.entries(set).map(
3825
+ ([column, value]) => `${quoteQualifiedIdentifier(column)} = ${toSqlJsonLiteral(value)}`
3826
+ );
3827
+ const sqlParts = [
3828
+ `UPDATE ${resolveDebugTableIdentifier(payload.table_name)} SET ${assignments.length ? assignments.join(", ") : "/* empty set */"}`
3829
+ ];
3830
+ if (payload.conditions?.length) {
3831
+ const whereClauses = payload.conditions.map(conditionToDebugSqlClause);
3832
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
3833
+ }
3834
+ const pagination = resolvePagination({
3835
+ currentPage: payload.current_page,
3836
+ pageSize: payload.page_size
3837
+ });
3838
+ appendOrderLimitOffset(sqlParts, payload.sort_by, pagination.limit, pagination.offset);
3839
+ if (payload.columns) {
3840
+ sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
3841
+ }
3842
+ return `${sqlParts.join(" ")};`;
3843
+ }
3844
+ function buildDeleteDebugSql(payload) {
3845
+ const sqlParts = [`DELETE FROM ${quoteQualifiedIdentifier(payload.table_name)}`];
3846
+ const whereClauses = [];
3847
+ if (payload.resource_id) {
3848
+ whereClauses.push(`"resource_id" = ${toSqlLiteral(payload.resource_id)}`);
3849
+ }
3850
+ if (payload.conditions?.length) {
3851
+ whereClauses.push(...payload.conditions.map(conditionToDebugSqlClause));
3852
+ }
3853
+ if (whereClauses.length) {
3854
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
3855
+ }
3856
+ const pagination = resolvePagination({
3857
+ currentPage: payload.current_page,
3858
+ pageSize: payload.page_size
3859
+ });
3860
+ appendOrderLimitOffset(sqlParts, payload.sort_by, pagination.limit, pagination.offset);
3861
+ if (payload.columns) {
3862
+ sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
3863
+ }
3864
+ return `${sqlParts.join(" ")};`;
3865
+ }
3866
+ function rpcFilterToSqlClause(filter) {
3867
+ const column = quoteQualifiedIdentifier(filter.column);
3868
+ const value = filter.value;
3869
+ switch (filter.operator) {
3870
+ case "eq":
3871
+ case "neq":
3872
+ case "gt":
3873
+ case "gte":
3874
+ case "lt":
3875
+ case "lte":
3876
+ case "like":
3877
+ case "ilike": {
3878
+ if (value === void 0 || Array.isArray(value)) {
3879
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
3880
+ }
3881
+ const operatorMap = {
3882
+ eq: "=",
3883
+ neq: "!=",
3884
+ gt: ">",
3885
+ gte: ">=",
3886
+ lt: "<",
3887
+ lte: "<=",
3888
+ like: "LIKE",
3889
+ ilike: "ILIKE"
3890
+ };
3891
+ return `${column} ${operatorMap[filter.operator]} ${toSqlLiteral(value)}`;
3892
+ }
3893
+ case "is":
3894
+ if (value === null) return `${column} IS NULL`;
3895
+ if (value === true) return `${column} IS TRUE`;
3896
+ if (value === false) return `${column} IS FALSE`;
3897
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
3898
+ case "in":
3899
+ if (!Array.isArray(value)) {
3900
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
3901
+ }
3902
+ if (value.length === 0) return "FALSE";
3903
+ return `${column} IN (${value.map((item) => toSqlLiteral(item)).join(", ")})`;
3904
+ default:
3905
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
3906
+ }
3907
+ }
3908
+ function buildRpcDebugSql(payload) {
3909
+ const argsEntries = payload.args ? Object.entries(payload.args) : [];
3910
+ const argsClause = argsEntries.map(([key, value]) => `${quoteQualifiedIdentifier(key)} => ${toSqlJsonLiteral(value)}`).join(", ");
3911
+ const functionRef = payload.schema ? `${quoteQualifiedIdentifier(payload.schema)}.${quoteQualifiedIdentifier(payload.function)}` : quoteQualifiedIdentifier(payload.function);
3912
+ const sqlParts = [
3913
+ `SELECT ${payload.select ? quoteSelectColumnsExpression(payload.select) : "*"} FROM ${functionRef}(${argsClause})`
3914
+ ];
3915
+ if (payload.filters?.length) {
3916
+ sqlParts.push(`WHERE ${payload.filters.map(rpcFilterToSqlClause).join(" AND ")}`);
3917
+ }
3918
+ if (payload.order?.column) {
3919
+ const direction = payload.order.ascending === false ? "DESC" : "ASC";
3920
+ sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(payload.order.column)} ${direction}`);
3921
+ }
3922
+ if (payload.limit !== void 0) {
3923
+ sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(payload.limit))}`);
3924
+ }
3925
+ if (payload.offset !== void 0) {
3926
+ sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(payload.offset))}`);
3927
+ }
3928
+ return `${sqlParts.join(" ")};`;
3929
+ }
2277
3930
  function createFilterMethods(state, addCondition, self) {
2278
3931
  return {
2279
3932
  eq(column, value) {
@@ -2385,7 +4038,7 @@ function createFilterMethods(state, addCondition, self) {
2385
4038
  not(columnOrExpression, operator, value) {
2386
4039
  const expression = String(columnOrExpression);
2387
4040
  if (operator != null && value !== void 0) {
2388
- addCondition("not", void 0, `${expression}.${operator}.${stringifyFilterValue(value)}`);
4041
+ addCondition("not", void 0, `${expression}.${operator}.${stringifyFilterValue2(value)}`);
2389
4042
  } else {
2390
4043
  addCondition("not", void 0, expression);
2391
4044
  }
@@ -2448,14 +4101,15 @@ function createRpcFilterMethods(filters, self) {
2448
4101
  }
2449
4102
  };
2450
4103
  }
2451
- function createRpcBuilder(functionName, args, baseOptions, client, formatGatewayResult) {
4104
+ function createRpcBuilder(functionName, args, baseOptions, client, formatGatewayResult, tracer, initialCallsite) {
2452
4105
  const state = {
2453
4106
  filters: []
2454
4107
  };
2455
4108
  let selectedColumns;
2456
4109
  let selectedOptions;
2457
4110
  let promise = null;
2458
- const executeRpc = async (columns, options) => {
4111
+ const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
4112
+ const executeRpc = async (columns, options, callsite) => {
2459
4113
  const mergedOptions = mergeOptions(baseOptions, options);
2460
4114
  const payload = {
2461
4115
  function: functionName,
@@ -2469,14 +4123,30 @@ function createRpcBuilder(functionName, args, baseOptions, client, formatGateway
2469
4123
  offset: state.offset,
2470
4124
  order: state.order
2471
4125
  };
2472
- const response = await client.rpcGateway(payload, mergedOptions);
2473
- return formatGatewayResult(response, { operation: "rpc" });
4126
+ const endpoint = mergedOptions?.get ? `/rpc/${functionName}` : "/gateway/rpc";
4127
+ const sql = buildRpcDebugSql(payload);
4128
+ return executeWithQueryTrace(
4129
+ tracer,
4130
+ {
4131
+ operation: "rpc",
4132
+ endpoint,
4133
+ functionName,
4134
+ sql,
4135
+ payload,
4136
+ options: mergedOptions
4137
+ },
4138
+ async () => {
4139
+ const response = await client.rpcGateway(payload, mergedOptions);
4140
+ return formatGatewayResult(response, { operation: "rpc" });
4141
+ },
4142
+ callsite
4143
+ );
2474
4144
  };
2475
- const run = (columns, options) => {
4145
+ const run = (columns, options, callsite) => {
2476
4146
  const payloadColumns = columns ?? selectedColumns;
2477
4147
  const payloadOptions = options ?? selectedOptions;
2478
4148
  if (!promise) {
2479
- promise = executeRpc(payloadColumns, payloadOptions);
4149
+ promise = executeRpc(payloadColumns, payloadOptions, callsiteStore.resolve(callsite));
2480
4150
  }
2481
4151
  return promise;
2482
4152
  };
@@ -2486,10 +4156,10 @@ function createRpcBuilder(functionName, args, baseOptions, client, formatGateway
2486
4156
  select(columns = selectedColumns, options) {
2487
4157
  selectedColumns = columns;
2488
4158
  selectedOptions = options ?? selectedOptions;
2489
- return run(columns, options);
4159
+ return run(columns, options, captureTraceCallsite(tracer));
2490
4160
  },
2491
4161
  async single(columns, options) {
2492
- const result = await run(columns, options);
4162
+ const result = await run(columns, options, captureTraceCallsite(tracer));
2493
4163
  return toSingleResult(result);
2494
4164
  },
2495
4165
  maybeSingle(columns, options) {
@@ -2524,7 +4194,7 @@ function createRpcBuilder(functionName, args, baseOptions, client, formatGateway
2524
4194
  });
2525
4195
  return builder;
2526
4196
  }
2527
- function createTableBuilder(tableName, client, formatGatewayResult) {
4197
+ function createTableBuilder(tableName, client, formatGatewayResult, tracer, experimental) {
2528
4198
  const state = {
2529
4199
  conditions: []
2530
4200
  };
@@ -2556,70 +4226,103 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2556
4226
  }
2557
4227
  state.conditions.push(condition);
2558
4228
  };
4229
+ const snapshotState = () => ({
4230
+ conditions: state.conditions.map((condition) => ({ ...condition })),
4231
+ limit: state.limit,
4232
+ offset: state.offset,
4233
+ order: state.order ? { ...state.order } : void 0,
4234
+ currentPage: state.currentPage,
4235
+ pageSize: state.pageSize,
4236
+ totalPages: state.totalPages
4237
+ });
2559
4238
  const builder = {};
2560
4239
  const filterMethods = createFilterMethods(
2561
4240
  state,
2562
4241
  addCondition,
2563
4242
  builder
2564
4243
  );
2565
- const runSelect = async (columns = DEFAULT_COLUMNS, options) => {
4244
+ const runSelect = async (columns = DEFAULT_COLUMNS, options, executionState = snapshotState(), callsite) => {
2566
4245
  const resolvedTableName = resolveTableNameForCall(tableName, options?.schema);
2567
- const conditions = state.conditions.length ? state.conditions.map((condition) => ({ ...condition })) : void 0;
2568
- const hasTypedEqualityComparison = conditions?.some(
2569
- (condition) => condition.operator === "eq" && (condition.value_cast !== void 0 || condition.column_cast !== void 0)
2570
- ) ?? false;
2571
- if (hasTypedEqualityComparison && !options?.head && !options?.count && conditions) {
2572
- const query = buildTypedSelectQuery({
2573
- tableName: resolvedTableName,
2574
- columns,
2575
- conditions,
2576
- limit: state.limit,
2577
- offset: state.offset,
2578
- currentPage: state.currentPage,
2579
- pageSize: state.pageSize,
2580
- order: state.order
2581
- });
2582
- if (query) {
2583
- const queryResponse = await client.queryGateway({ query }, options);
2584
- return formatGatewayResult(queryResponse, { table: resolvedTableName, operation: "select" });
2585
- }
2586
- }
2587
- const payload = {
2588
- table_name: resolvedTableName,
4246
+ const plan = createSelectTransportPlan({
4247
+ tableName: resolvedTableName,
2589
4248
  columns,
2590
- conditions,
2591
- limit: state.limit,
2592
- offset: state.offset,
2593
- current_page: state.currentPage,
2594
- page_size: state.pageSize,
2595
- total_pages: state.totalPages,
2596
- sort_by: state.order,
2597
- strip_nulls: options?.stripNulls ?? true,
2598
- count: options?.count,
2599
- head: options?.head
2600
- };
2601
- const response = await client.fetchGateway(payload, options);
2602
- return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
4249
+ state: executionState,
4250
+ options,
4251
+ buildTypedSelectQuery
4252
+ });
4253
+ if (plan.kind === "query") {
4254
+ return executeWithQueryTrace(
4255
+ tracer,
4256
+ {
4257
+ operation: "select",
4258
+ endpoint: "/gateway/query",
4259
+ table: resolvedTableName,
4260
+ sql: plan.query,
4261
+ payload: plan.payload,
4262
+ options
4263
+ },
4264
+ async () => {
4265
+ const queryResponse = await client.queryGateway(plan.payload, options);
4266
+ return formatGatewayResult(queryResponse, { table: resolvedTableName, operation: "select" });
4267
+ },
4268
+ callsite
4269
+ );
4270
+ }
4271
+ const sql = buildDebugSelectQuery({
4272
+ tableName: resolvedTableName,
4273
+ ...plan.debug
4274
+ });
4275
+ return executeWithQueryTrace(
4276
+ tracer,
4277
+ {
4278
+ operation: "select",
4279
+ endpoint: "/gateway/fetch",
4280
+ table: resolvedTableName,
4281
+ sql,
4282
+ payload: plan.payload,
4283
+ options
4284
+ },
4285
+ async () => {
4286
+ const response = await client.fetchGateway(plan.payload, options);
4287
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
4288
+ },
4289
+ callsite
4290
+ );
2603
4291
  };
2604
- const createSelectChain = (columns, options) => {
4292
+ const createSelectChain = (columns, options, initialCallsite) => {
2605
4293
  const chain = {};
4294
+ const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
2606
4295
  const filterMethods2 = createFilterMethods(state, addCondition, chain);
2607
4296
  Object.assign(chain, filterMethods2, {
2608
4297
  async single(cols, opts) {
2609
- const r = await runSelect(cols ?? columns, opts ?? options);
4298
+ const r = await runSelect(
4299
+ cols ?? columns,
4300
+ opts ?? options,
4301
+ snapshotState(),
4302
+ callsiteStore.resolve(captureTraceCallsite(tracer))
4303
+ );
2610
4304
  return toSingleResult(r);
2611
4305
  },
2612
4306
  maybeSingle(cols, opts) {
2613
4307
  return chain.single(cols, opts);
2614
4308
  },
2615
4309
  then(onfulfilled, onrejected) {
2616
- return runSelect(columns, options).then(onfulfilled, onrejected);
4310
+ return runSelect(
4311
+ columns,
4312
+ options,
4313
+ snapshotState(),
4314
+ callsiteStore.resolve()
4315
+ ).then(onfulfilled, onrejected);
2617
4316
  },
2618
4317
  catch(onrejected) {
2619
- return runSelect(columns, options).catch(onrejected);
4318
+ return runSelect(columns, options, snapshotState(), callsiteStore.resolve()).catch(
4319
+ onrejected
4320
+ );
2620
4321
  },
2621
4322
  finally(onfinally) {
2622
- return runSelect(columns, options).finally(onfinally);
4323
+ return runSelect(columns, options, snapshotState(), callsiteStore.resolve()).finally(
4324
+ onfinally
4325
+ );
2623
4326
  }
2624
4327
  });
2625
4328
  return chain;
@@ -2636,11 +4339,75 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2636
4339
  return builder;
2637
4340
  },
2638
4341
  select(columns = DEFAULT_COLUMNS, options) {
2639
- return createSelectChain(columns, options);
4342
+ return createSelectChain(columns, options, captureTraceCallsite(tracer));
4343
+ },
4344
+ async findMany(options) {
4345
+ const columns = compileSelectShape(options.select);
4346
+ const baseState = snapshotState();
4347
+ const executionState = snapshotState();
4348
+ const callsite = captureTraceCallsite(tracer);
4349
+ const compiledWhere = compileWhere(options.where);
4350
+ if (compiledWhere?.length) {
4351
+ executionState.conditions.push(...compiledWhere);
4352
+ }
4353
+ if (options.orderBy !== void 0) {
4354
+ executionState.order = compileOrderBy(options.orderBy);
4355
+ }
4356
+ if (options.limit !== void 0) {
4357
+ executionState.limit = options.limit;
4358
+ }
4359
+ if (experimental?.findManyAst && canUseFindManyAstTransport(baseState) && !selectShapeUsesRelationSchema(options.select)) {
4360
+ const resolvedTableName = resolveTableNameForCall(tableName, void 0);
4361
+ const payload = {
4362
+ table_name: resolvedTableName,
4363
+ select: options.select
4364
+ };
4365
+ if (options.where !== void 0) {
4366
+ payload.where = options.where;
4367
+ }
4368
+ const astOrder = toFindManyAstOrder(executionState.order);
4369
+ if (astOrder !== void 0) {
4370
+ payload.orderBy = astOrder;
4371
+ }
4372
+ if (executionState.limit !== void 0) {
4373
+ payload.limit = executionState.limit;
4374
+ }
4375
+ const sql = buildDebugSelectQuery({
4376
+ tableName: resolvedTableName,
4377
+ columns,
4378
+ conditions: executionState.conditions,
4379
+ limit: executionState.limit,
4380
+ order: executionState.order
4381
+ });
4382
+ return executeWithQueryTrace(
4383
+ tracer,
4384
+ {
4385
+ operation: "select",
4386
+ endpoint: "/gateway/fetch",
4387
+ table: resolvedTableName,
4388
+ sql,
4389
+ payload
4390
+ },
4391
+ async () => {
4392
+ const response = await client.fetchGateway(
4393
+ payload
4394
+ );
4395
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
4396
+ },
4397
+ callsite
4398
+ );
4399
+ }
4400
+ return runSelect(
4401
+ columns,
4402
+ void 0,
4403
+ executionState,
4404
+ callsite
4405
+ );
2640
4406
  },
2641
4407
  insert(values, options) {
4408
+ const mutationCallsite = captureTraceCallsite(tracer);
2642
4409
  if (Array.isArray(values)) {
2643
- const executeInsertMany = async (columns, selectOptions) => {
4410
+ const executeInsertMany = async (columns, selectOptions, callsite) => {
2644
4411
  const mergedOptions = mergeOptions(options, selectOptions);
2645
4412
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2646
4413
  const payload = {
@@ -2653,12 +4420,27 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2653
4420
  if (mergedOptions?.defaultToNull !== void 0) {
2654
4421
  payload.default_to_null = mergedOptions.defaultToNull;
2655
4422
  }
2656
- const response = await client.insertGateway(payload, mergedOptions);
2657
- return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4423
+ const sql = buildInsertDebugSql(payload);
4424
+ return executeWithQueryTrace(
4425
+ tracer,
4426
+ {
4427
+ operation: "insert",
4428
+ endpoint: "/gateway/insert",
4429
+ table: resolvedTableName,
4430
+ sql,
4431
+ payload,
4432
+ options: mergedOptions
4433
+ },
4434
+ async () => {
4435
+ const response = await client.insertGateway(payload, mergedOptions);
4436
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4437
+ },
4438
+ callsite
4439
+ );
2658
4440
  };
2659
- return createMutationQuery(executeInsertMany);
4441
+ return createMutationQuery(executeInsertMany, DEFAULT_COLUMNS, tracer, mutationCallsite);
2660
4442
  }
2661
- const executeInsertOne = async (columns, selectOptions) => {
4443
+ const executeInsertOne = async (columns, selectOptions, callsite) => {
2662
4444
  const mergedOptions = mergeOptions(options, selectOptions);
2663
4445
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2664
4446
  const payload = {
@@ -2671,14 +4453,30 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2671
4453
  if (mergedOptions?.defaultToNull !== void 0) {
2672
4454
  payload.default_to_null = mergedOptions.defaultToNull;
2673
4455
  }
2674
- const response = await client.insertGateway(payload, mergedOptions);
2675
- return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4456
+ const sql = buildInsertDebugSql(payload);
4457
+ return executeWithQueryTrace(
4458
+ tracer,
4459
+ {
4460
+ operation: "insert",
4461
+ endpoint: "/gateway/insert",
4462
+ table: resolvedTableName,
4463
+ sql,
4464
+ payload,
4465
+ options: mergedOptions
4466
+ },
4467
+ async () => {
4468
+ const response = await client.insertGateway(payload, mergedOptions);
4469
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4470
+ },
4471
+ callsite
4472
+ );
2676
4473
  };
2677
- return createMutationQuery(executeInsertOne);
4474
+ return createMutationQuery(executeInsertOne, DEFAULT_COLUMNS, tracer, mutationCallsite);
2678
4475
  },
2679
4476
  upsert(values, options) {
4477
+ const mutationCallsite = captureTraceCallsite(tracer);
2680
4478
  if (Array.isArray(values)) {
2681
- const executeUpsertMany = async (columns, selectOptions) => {
4479
+ const executeUpsertMany = async (columns, selectOptions, callsite) => {
2682
4480
  const mergedOptions = mergeOptions(options, selectOptions);
2683
4481
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2684
4482
  const payload = {
@@ -2693,12 +4491,27 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2693
4491
  if (mergedOptions?.defaultToNull !== void 0) {
2694
4492
  payload.default_to_null = mergedOptions.defaultToNull;
2695
4493
  }
2696
- const response = await client.insertGateway(payload, mergedOptions);
2697
- return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4494
+ const sql = buildInsertDebugSql(payload);
4495
+ return executeWithQueryTrace(
4496
+ tracer,
4497
+ {
4498
+ operation: "upsert",
4499
+ endpoint: "/gateway/insert",
4500
+ table: resolvedTableName,
4501
+ sql,
4502
+ payload,
4503
+ options: mergedOptions
4504
+ },
4505
+ async () => {
4506
+ const response = await client.insertGateway(payload, mergedOptions);
4507
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4508
+ },
4509
+ callsite
4510
+ );
2698
4511
  };
2699
- return createMutationQuery(executeUpsertMany);
4512
+ return createMutationQuery(executeUpsertMany, DEFAULT_COLUMNS, tracer, mutationCallsite);
2700
4513
  }
2701
- const executeUpsertOne = async (columns, selectOptions) => {
4514
+ const executeUpsertOne = async (columns, selectOptions, callsite) => {
2702
4515
  const mergedOptions = mergeOptions(options, selectOptions);
2703
4516
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2704
4517
  const payload = {
@@ -2713,13 +4526,29 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2713
4526
  if (mergedOptions?.defaultToNull !== void 0) {
2714
4527
  payload.default_to_null = mergedOptions.defaultToNull;
2715
4528
  }
2716
- const response = await client.insertGateway(payload, mergedOptions);
2717
- return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4529
+ const sql = buildInsertDebugSql(payload);
4530
+ return executeWithQueryTrace(
4531
+ tracer,
4532
+ {
4533
+ operation: "upsert",
4534
+ endpoint: "/gateway/insert",
4535
+ table: resolvedTableName,
4536
+ sql,
4537
+ payload,
4538
+ options: mergedOptions
4539
+ },
4540
+ async () => {
4541
+ const response = await client.insertGateway(payload, mergedOptions);
4542
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4543
+ },
4544
+ callsite
4545
+ );
2718
4546
  };
2719
- return createMutationQuery(executeUpsertOne);
4547
+ return createMutationQuery(executeUpsertOne, DEFAULT_COLUMNS, tracer, mutationCallsite);
2720
4548
  },
2721
4549
  update(values, options) {
2722
- const executeUpdate = async (columns, selectOptions) => {
4550
+ const mutationCallsite = captureTraceCallsite(tracer);
4551
+ const executeUpdate = async (columns, selectOptions, callsite) => {
2723
4552
  const filters = state.conditions.length ? [...state.conditions] : void 0;
2724
4553
  const mergedOptions = mergeOptions(options, selectOptions);
2725
4554
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
@@ -2734,10 +4563,25 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2734
4563
  if (state.pageSize !== void 0) payload.page_size = state.pageSize;
2735
4564
  if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
2736
4565
  if (columns) payload.columns = columns;
2737
- const response = await client.updateGateway(payload, mergedOptions);
2738
- return formatGatewayResult(response, { table: resolvedTableName, operation: "update" });
4566
+ const sql = buildUpdateDebugSql(payload);
4567
+ return executeWithQueryTrace(
4568
+ tracer,
4569
+ {
4570
+ operation: "update",
4571
+ endpoint: "/gateway/update",
4572
+ table: resolvedTableName,
4573
+ sql,
4574
+ payload,
4575
+ options: mergedOptions
4576
+ },
4577
+ async () => {
4578
+ const response = await client.updateGateway(payload, mergedOptions);
4579
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "update" });
4580
+ },
4581
+ callsite
4582
+ );
2739
4583
  };
2740
- const mutation = createMutationQuery(executeUpdate, null);
4584
+ const mutation = createMutationQuery(executeUpdate, null, tracer, mutationCallsite);
2741
4585
  const updateChain = {};
2742
4586
  const filterMethods2 = createFilterMethods(state, addCondition, updateChain);
2743
4587
  Object.assign(updateChain, filterMethods2, mutation);
@@ -2749,7 +4593,8 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2749
4593
  if (!resourceId && !filters?.length) {
2750
4594
  throw new Error('delete requires a resource_id either via eq("resource_id", ...) or options.resourceId');
2751
4595
  }
2752
- const executeDelete = async (columns, selectOptions) => {
4596
+ const mutationCallsite = captureTraceCallsite(tracer);
4597
+ const executeDelete = async (columns, selectOptions, callsite) => {
2753
4598
  const mergedOptions = mergeOptions(options, selectOptions);
2754
4599
  const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2755
4600
  const payload = {
@@ -2762,13 +4607,33 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2762
4607
  if (state.pageSize !== void 0) payload.page_size = state.pageSize;
2763
4608
  if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
2764
4609
  if (columns) payload.columns = columns;
2765
- const response = await client.deleteGateway(payload, mergedOptions);
2766
- return formatGatewayResult(response, { table: resolvedTableName, operation: "delete" });
4610
+ const sql = buildDeleteDebugSql(payload);
4611
+ return executeWithQueryTrace(
4612
+ tracer,
4613
+ {
4614
+ operation: "delete",
4615
+ endpoint: "/gateway/delete",
4616
+ table: resolvedTableName,
4617
+ sql,
4618
+ payload,
4619
+ options: mergedOptions
4620
+ },
4621
+ async () => {
4622
+ const response = await client.deleteGateway(payload, mergedOptions);
4623
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "delete" });
4624
+ },
4625
+ callsite
4626
+ );
2767
4627
  };
2768
- return createMutationQuery(executeDelete, null);
4628
+ return createMutationQuery(executeDelete, null, tracer, mutationCallsite);
2769
4629
  },
2770
4630
  async single(columns, options) {
2771
- const response = await builder.select(columns, options);
4631
+ const response = await runSelect(
4632
+ columns ?? DEFAULT_COLUMNS,
4633
+ options,
4634
+ snapshotState(),
4635
+ captureTraceCallsite(tracer)
4636
+ );
2772
4637
  return toSingleResult(response);
2773
4638
  },
2774
4639
  async maybeSingle(columns, options) {
@@ -2777,14 +4642,29 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
2777
4642
  });
2778
4643
  return builder;
2779
4644
  }
2780
- function createQueryBuilder(client, formatGatewayResult) {
4645
+ function createQueryBuilder(client, formatGatewayResult, tracer) {
2781
4646
  return async function query(query, options) {
2782
4647
  const normalizedQuery = query.trim();
2783
4648
  if (!normalizedQuery) {
2784
4649
  throw new Error("query requires a non-empty string");
2785
4650
  }
2786
- const response = await client.queryGateway({ query: normalizedQuery }, options);
2787
- return formatGatewayResult(response, { operation: "query" });
4651
+ const payload = { query: normalizedQuery };
4652
+ const callsite = captureTraceCallsite(tracer);
4653
+ return executeWithQueryTrace(
4654
+ tracer,
4655
+ {
4656
+ operation: "query",
4657
+ endpoint: "/gateway/query",
4658
+ sql: normalizedQuery,
4659
+ payload,
4660
+ options
4661
+ },
4662
+ async () => {
4663
+ const response = await client.queryGateway(payload, options);
4664
+ return formatGatewayResult(response, { operation: "query" });
4665
+ },
4666
+ callsite
4667
+ );
2788
4668
  };
2789
4669
  }
2790
4670
  function createClientFromConfig(config) {
@@ -2796,8 +4676,15 @@ function createClientFromConfig(config) {
2796
4676
  headers: config.headers
2797
4677
  });
2798
4678
  const formatGatewayResult = createResultFormatter(config.experimental);
4679
+ const queryTracer = createQueryTracer(config.experimental);
2799
4680
  const auth = createAuthClient(config.auth);
2800
- const from = (table) => createTableBuilder(table, gateway, formatGatewayResult);
4681
+ const from = (table, options) => createTableBuilder(
4682
+ resolveTableNameForCall(table, options?.schema),
4683
+ gateway,
4684
+ formatGatewayResult,
4685
+ queryTracer,
4686
+ config.experimental
4687
+ );
2801
4688
  const rpc = (fn, args, options) => {
2802
4689
  const normalizedFn = fn.trim();
2803
4690
  if (!normalizedFn) {
@@ -2808,16 +4695,19 @@ function createClientFromConfig(config) {
2808
4695
  args,
2809
4696
  options,
2810
4697
  gateway,
2811
- formatGatewayResult
4698
+ formatGatewayResult,
4699
+ queryTracer,
4700
+ captureTraceCallsite(queryTracer)
2812
4701
  );
2813
4702
  };
2814
- const query = createQueryBuilder(gateway, formatGatewayResult);
4703
+ const query = createQueryBuilder(gateway, formatGatewayResult, queryTracer);
2815
4704
  const db = createDbModule({ from, rpc, query });
2816
4705
  return {
2817
4706
  from,
2818
4707
  db,
2819
4708
  rpc,
2820
4709
  query,
4710
+ verifyConnection: gateway.verifyConnection,
2821
4711
  auth: auth.auth
2822
4712
  };
2823
4713
  }
@@ -2826,12 +4716,40 @@ function toBackendConfig(b) {
2826
4716
  if (!b) return DEFAULT_BACKEND;
2827
4717
  return typeof b === "string" ? { type: b } : b;
2828
4718
  }
4719
+ function mergeAuthClientConfig(current, next) {
4720
+ const merged = {
4721
+ ...current ?? {},
4722
+ ...next
4723
+ };
4724
+ if (current?.headers || next.headers) {
4725
+ merged.headers = {
4726
+ ...current?.headers ?? {},
4727
+ ...next.headers ?? {}
4728
+ };
4729
+ }
4730
+ return merged;
4731
+ }
4732
+ function mergeExperimentalOptions(current, next) {
4733
+ const merged = {
4734
+ ...current ?? {},
4735
+ ...next
4736
+ };
4737
+ if (current?.traceQueries && typeof current.traceQueries === "object" && next.traceQueries && typeof next.traceQueries === "object") {
4738
+ merged.traceQueries = {
4739
+ ...current.traceQueries,
4740
+ ...next.traceQueries
4741
+ };
4742
+ }
4743
+ return merged;
4744
+ }
2829
4745
  var AthenaClientBuilderImpl = class {
2830
4746
  baseUrl;
2831
4747
  apiKey;
2832
4748
  backendConfig = DEFAULT_BACKEND;
2833
4749
  clientName;
2834
4750
  defaultHeaders;
4751
+ authConfig;
4752
+ experimentalOptions;
2835
4753
  isHealthTrackingEnabled = false;
2836
4754
  url(url) {
2837
4755
  this.baseUrl = url;
@@ -2853,6 +4771,35 @@ var AthenaClientBuilderImpl = class {
2853
4771
  this.defaultHeaders = headers;
2854
4772
  return this;
2855
4773
  }
4774
+ auth(config) {
4775
+ this.authConfig = mergeAuthClientConfig(this.authConfig, config);
4776
+ return this;
4777
+ }
4778
+ experimental(options) {
4779
+ this.experimentalOptions = mergeExperimentalOptions(this.experimentalOptions, options);
4780
+ return this;
4781
+ }
4782
+ options(options) {
4783
+ if (options.client !== void 0) {
4784
+ this.clientName = options.client;
4785
+ }
4786
+ if (options.backend !== void 0) {
4787
+ this.backendConfig = toBackendConfig(options.backend);
4788
+ }
4789
+ if (options.headers !== void 0) {
4790
+ this.defaultHeaders = {
4791
+ ...this.defaultHeaders ?? {},
4792
+ ...options.headers
4793
+ };
4794
+ }
4795
+ if (options.auth !== void 0) {
4796
+ this.authConfig = mergeAuthClientConfig(this.authConfig, options.auth);
4797
+ }
4798
+ if (options.experimental !== void 0) {
4799
+ this.experimentalOptions = mergeExperimentalOptions(this.experimentalOptions, options.experimental);
4800
+ }
4801
+ return this;
4802
+ }
2856
4803
  healthTracking(enabled) {
2857
4804
  this.isHealthTrackingEnabled = enabled;
2858
4805
  return this;
@@ -2867,7 +4814,9 @@ var AthenaClientBuilderImpl = class {
2867
4814
  client: this.clientName,
2868
4815
  backend: this.backendConfig,
2869
4816
  headers: this.defaultHeaders,
2870
- healthTracking: this.isHealthTrackingEnabled
4817
+ healthTracking: this.isHealthTrackingEnabled,
4818
+ auth: this.authConfig,
4819
+ experimental: this.experimentalOptions
2871
4820
  });
2872
4821
  }
2873
4822
  };
@@ -3002,8 +4951,8 @@ var TypedAthenaClientImpl = class _TypedAthenaClientImpl {
3002
4951
  });
3003
4952
  this.db = this.baseClient.db;
3004
4953
  }
3005
- from(table) {
3006
- return this.baseClient.from(table);
4954
+ from(table, options) {
4955
+ return this.baseClient.from(table, options);
3007
4956
  }
3008
4957
  rpc(fn, args, options) {
3009
4958
  return this.baseClient.rpc(fn, args, options);
@@ -3011,6 +4960,9 @@ var TypedAthenaClientImpl = class _TypedAthenaClientImpl {
3011
4960
  query(query, options) {
3012
4961
  return this.baseClient.query(query, options);
3013
4962
  }
4963
+ verifyConnection(options) {
4964
+ return this.baseClient.verifyConnection(options);
4965
+ }
3014
4966
  withTenantContext(context) {
3015
4967
  return new _TypedAthenaClientImpl({
3016
4968
  registry: this.registry,
@@ -3482,7 +5434,7 @@ function resolveNullishValue(mode) {
3482
5434
  if (mode === "null") return null;
3483
5435
  return "";
3484
5436
  }
3485
- function isRecord4(value) {
5437
+ function isRecord7(value) {
3486
5438
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3487
5439
  }
3488
5440
  function isNullableColumn(model, key) {
@@ -3491,7 +5443,7 @@ function isNullableColumn(model, key) {
3491
5443
  }
3492
5444
  function toModelFormDefaults(model, values, options) {
3493
5445
  const source = values;
3494
- if (!isRecord4(source)) {
5446
+ if (!isRecord7(source)) {
3495
5447
  return {};
3496
5448
  }
3497
5449
  const mode = options?.nullishMode ?? "empty-string";
@@ -3747,7 +5699,7 @@ function normalizeBooleanFlag(rawValue, fallback) {
3747
5699
  return rawValue;
3748
5700
  }
3749
5701
  if (typeof rawValue === "string") {
3750
- return parseBooleanFlag(rawValue, fallback);
5702
+ return parseBooleanFlag2(rawValue, fallback);
3751
5703
  }
3752
5704
  return fallback;
3753
5705
  }
@@ -3922,6 +5874,11 @@ async function loadGeneratorConfig(options = {}) {
3922
5874
  }
3923
5875
  }
3924
5876
 
5877
+ // src/utils/slugify.ts
5878
+ function slugify(input) {
5879
+ return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
5880
+ }
5881
+
3925
5882
  // src/generator/naming.ts
3926
5883
  var IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
3927
5884
  var RESERVED_IDENTIFIERS = /* @__PURE__ */ new Set([
@@ -4017,7 +5974,7 @@ function applyNamingStyle(input, style) {
4017
5974
  case "snake":
4018
5975
  return words.map((word) => word.toLowerCase()).join("_");
4019
5976
  case "kebab":
4020
- return words.map((word) => word.toLowerCase()).join("-");
5977
+ return slugify(words.join("-"));
4021
5978
  default:
4022
5979
  return input;
4023
5980
  }
@@ -4535,7 +6492,7 @@ var AthenaGatewayCatalogClient = class {
4535
6492
  async queryRows(query) {
4536
6493
  const result = await this.client.query(query);
4537
6494
  if (result.error || result.status < 200 || result.status >= 300) {
4538
- throw new Error(result.error ?? `Gateway query failed with status ${result.status}`);
6495
+ throw new Error(result.error?.message ?? `Gateway query failed with status ${result.status}`);
4539
6496
  }
4540
6497
  return result.data ?? [];
4541
6498
  }
@@ -4630,10 +6587,24 @@ function resolveGeneratorProvider(providerConfig, experimentalFlags) {
4630
6587
  }
4631
6588
  throw new Error(`Unsupported generator provider kind: ${providerConfig.kind ?? "unknown"}`);
4632
6589
  }
6590
+ function canOverwriteArtifact(file) {
6591
+ return file.kind === "model" || file.kind === "schema";
6592
+ }
6593
+ async function fileExists(path) {
6594
+ try {
6595
+ await promises.stat(path);
6596
+ return true;
6597
+ } catch {
6598
+ return false;
6599
+ }
6600
+ }
4633
6601
  async function writeArtifacts(files, cwd) {
4634
6602
  const writtenFiles = [];
4635
6603
  for (const file of files) {
4636
6604
  const absolutePath = path.resolve(cwd, file.path);
6605
+ if (!canOverwriteArtifact(file) && await fileExists(absolutePath)) {
6606
+ continue;
6607
+ }
4637
6608
  await promises.mkdir(path.dirname(absolutePath), { recursive: true });
4638
6609
  await promises.writeFile(absolutePath, file.content, "utf8");
4639
6610
  writtenFiles.push(file.path);
@@ -4671,10 +6642,12 @@ exports.DEFAULT_POSTGRES_SCHEMAS = DEFAULT_POSTGRES_SCHEMAS;
4671
6642
  exports.assertInt = assertInt;
4672
6643
  exports.coerceInt = coerceInt;
4673
6644
  exports.createAuthClient = createAuthClient;
6645
+ exports.createAuthReactEmailInput = createAuthReactEmailInput;
4674
6646
  exports.createClient = createClient;
4675
6647
  exports.createModelFormAdapter = createModelFormAdapter;
4676
6648
  exports.createPostgresIntrospectionProvider = createPostgresIntrospectionProvider;
4677
6649
  exports.createTypedClient = createTypedClient;
6650
+ exports.defineAuthEmailTemplate = defineAuthEmailTemplate;
4678
6651
  exports.defineDatabase = defineDatabase;
4679
6652
  exports.defineGeneratorConfig = defineGeneratorConfig;
4680
6653
  exports.defineModel = defineModel;
@@ -4687,9 +6660,11 @@ exports.isAthenaGatewayError = isAthenaGatewayError;
4687
6660
  exports.isOk = isOk;
4688
6661
  exports.loadGeneratorConfig = loadGeneratorConfig;
4689
6662
  exports.normalizeAthenaError = normalizeAthenaError;
6663
+ exports.normalizeAthenaGatewayBaseUrl = normalizeAthenaGatewayBaseUrl;
4690
6664
  exports.normalizeGeneratorConfig = normalizeGeneratorConfig;
4691
6665
  exports.normalizeSchemaSelection = normalizeSchemaSelection;
4692
- exports.parseBooleanFlag = parseBooleanFlag;
6666
+ exports.parseBooleanFlag = parseBooleanFlag2;
6667
+ exports.renderAthenaReactEmail = renderAthenaReactEmail;
4693
6668
  exports.requireAffected = requireAffected;
4694
6669
  exports.requireSuccess = requireSuccess;
4695
6670
  exports.resolveGeneratorProvider = resolveGeneratorProvider;
@@ -4701,6 +6676,7 @@ exports.toModelPayload = toModelPayload;
4701
6676
  exports.unwrap = unwrap;
4702
6677
  exports.unwrapOne = unwrapOne;
4703
6678
  exports.unwrapRows = unwrapRows;
6679
+ exports.verifyAthenaGatewayUrl = verifyAthenaGatewayUrl;
4704
6680
  exports.withRetry = withRetry;
4705
6681
  //# sourceMappingURL=index.cjs.map
4706
6682
  //# sourceMappingURL=index.cjs.map