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