@xylex-group/athena 2.0.0 → 2.3.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 (48) hide show
  1. package/README.md +287 -95
  2. package/dist/browser.cjs +4791 -0
  3. package/dist/browser.cjs.map +1 -0
  4. package/dist/browser.d.cts +25 -0
  5. package/dist/browser.d.ts +25 -0
  6. package/dist/browser.js +4745 -0
  7. package/dist/browser.js.map +1 -0
  8. package/dist/cli/index.cjs +2087 -220
  9. package/dist/cli/index.cjs.map +1 -1
  10. package/dist/cli/index.d.cts +3 -2
  11. package/dist/cli/index.d.ts +3 -2
  12. package/dist/cli/index.js +2089 -222
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/cookies.cjs +890 -0
  15. package/dist/cookies.cjs.map +1 -0
  16. package/dist/cookies.d.cts +174 -0
  17. package/dist/cookies.d.ts +174 -0
  18. package/dist/cookies.js +869 -0
  19. package/dist/cookies.js.map +1 -0
  20. package/dist/index.cjs +3046 -1200
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +8 -408
  23. package/dist/index.d.ts +8 -408
  24. package/dist/index.js +3045 -1202
  25. package/dist/index.js.map +1 -1
  26. package/dist/{model-form-CVOtC8jq.d.ts → model-form-BpDXlbxb.d.ts} +97 -2
  27. package/dist/{model-form-hXkvHS_3.d.cts → model-form-hoE2jHIi.d.cts} +97 -2
  28. package/dist/pipeline-BNIw8pDQ.d.ts +8 -0
  29. package/dist/pipeline-DNIpEsN8.d.cts +8 -0
  30. package/dist/react-email-BvyCZnfW.d.cts +610 -0
  31. package/dist/react-email-qPA1wjFV.d.ts +610 -0
  32. package/dist/react.cjs +30 -9
  33. package/dist/react.cjs.map +1 -1
  34. package/dist/react.d.cts +4 -4
  35. package/dist/react.d.ts +4 -4
  36. package/dist/react.js +30 -9
  37. package/dist/react.js.map +1 -1
  38. package/dist/{types-BnzoaNRC.d.cts → types-A5e97acl.d.cts} +2 -1
  39. package/dist/{types-BnzoaNRC.d.ts → types-A5e97acl.d.ts} +2 -1
  40. package/dist/{pipeline-CQgV-Yfo.d.ts → types-BnD22-vb.d.ts} +2 -7
  41. package/dist/{pipeline-C-cN0ACi.d.cts → types-bDlr4u7p.d.cts} +2 -7
  42. package/dist/utils.cjs +153 -0
  43. package/dist/utils.cjs.map +1 -0
  44. package/dist/utils.d.cts +23 -0
  45. package/dist/utils.d.ts +23 -0
  46. package/dist/utils.js +146 -0
  47. package/dist/utils.js.map +1 -0
  48. package/package.json +87 -9
package/dist/index.cjs CHANGED
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
 
3
- var pg = require('pg');
4
3
  var fs = require('fs');
5
4
  var path = require('path');
6
5
  var url = require('url');
@@ -94,23 +93,39 @@ function normalizeHeaderValue(value) {
94
93
  function isRecord(value) {
95
94
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
96
95
  }
96
+ function nonEmptyString(value) {
97
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
98
+ }
99
+ function resolveStructuredErrorPayload(payload) {
100
+ if (!isRecord(payload)) return null;
101
+ return isRecord(payload.error) ? payload.error : payload;
102
+ }
97
103
  function resolveRequestId(headers) {
98
104
  return headers.get("x-request-id") ?? headers.get("x-correlation-id") ?? headers.get("x-athena-request-id") ?? void 0;
99
105
  }
100
106
  function resolveErrorMessage(payload, fallback) {
101
- if (isRecord(payload)) {
102
- const messageCandidates = [payload.error, payload.message, payload.details];
107
+ const structuredPayload = resolveStructuredErrorPayload(payload);
108
+ if (structuredPayload) {
109
+ const messageCandidates = [structuredPayload.message, structuredPayload.error, structuredPayload.details];
103
110
  for (const candidate of messageCandidates) {
104
- if (typeof candidate === "string" && candidate.trim().length > 0) {
105
- return candidate.trim();
106
- }
111
+ const resolved = nonEmptyString(candidate);
112
+ if (resolved) return resolved;
107
113
  }
108
114
  }
109
- if (typeof payload === "string" && payload.trim().length > 0) {
110
- return payload.trim();
111
- }
115
+ const rawMessage = nonEmptyString(payload);
116
+ if (rawMessage) return rawMessage;
112
117
  return fallback;
113
118
  }
119
+ function resolveErrorHint(payload) {
120
+ const structuredPayload = resolveStructuredErrorPayload(payload);
121
+ return structuredPayload ? nonEmptyString(structuredPayload.hint) : void 0;
122
+ }
123
+ function resolveStatusText(response, payload) {
124
+ const rawStatusText = nonEmptyString(response.statusText);
125
+ if (rawStatusText) return rawStatusText;
126
+ const payloadRecord = isRecord(payload) ? payload : null;
127
+ return payloadRecord ? nonEmptyString(payloadRecord.statusText) ?? null : null;
128
+ }
114
129
  function detailsFromError(error) {
115
130
  return error.toDetails();
116
131
  }
@@ -281,6 +296,7 @@ async function callAthena(config, endpoint, method, payload, options) {
281
296
  return {
282
297
  ok: false,
283
298
  status: response.status,
299
+ statusText: resolveStatusText(response, parsedBody.parsed),
284
300
  data: null,
285
301
  error: invalidJsonError.message,
286
302
  errorDetails: detailsFromError(invalidJsonError),
@@ -299,11 +315,13 @@ async function callAthena(config, endpoint, method, payload, options) {
299
315
  status: response.status,
300
316
  endpoint,
301
317
  method,
302
- requestId
318
+ requestId,
319
+ hint: resolveErrorHint(parsed)
303
320
  });
304
321
  return {
305
322
  ok: false,
306
323
  status: response.status,
324
+ statusText: resolveStatusText(response, parsed),
307
325
  data: null,
308
326
  error: httpError.message,
309
327
  errorDetails: detailsFromError(httpError),
@@ -315,6 +333,7 @@ async function callAthena(config, endpoint, method, payload, options) {
315
333
  return {
316
334
  ok: true,
317
335
  status: response.status,
336
+ statusText: resolveStatusText(response, parsed),
318
337
  data: payloadData ?? null,
319
338
  count: payloadCount,
320
339
  error: void 0,
@@ -334,6 +353,7 @@ async function callAthena(config, endpoint, method, payload, options) {
334
353
  return {
335
354
  ok: false,
336
355
  status: 0,
356
+ statusText: null,
337
357
  data: null,
338
358
  error: networkError.message,
339
359
  errorDetails: detailsFromError(networkError),
@@ -375,10 +395,29 @@ function createAthenaGatewayClient(config = {}) {
375
395
  // src/sql-identifiers.ts
376
396
  var SIMPLE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
377
397
  var COMPOSITE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
378
- var ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_.]*)\s+(?:as\s+)?([A-Za-z_][A-Za-z0-9_]*)$/i;
398
+ var SQL_ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_.]*)\s+(?:as\s+)?([A-Za-z_][A-Za-z0-9_]*)$/i;
399
+ var RESPONSE_ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([A-Za-z_][A-Za-z0-9_.]*)$/i;
379
400
  function quoteIdentifierSegment(identifier2) {
380
401
  return `"${identifier2.replace(/"/g, '""')}"`;
381
402
  }
403
+ function parseAliasedIdentifierToken(token) {
404
+ const responseAliasMatch = RESPONSE_ALIAS_PATTERN.exec(token);
405
+ if (responseAliasMatch) {
406
+ const [, aliasIdentifier2, baseIdentifier2] = responseAliasMatch;
407
+ if (COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier2) && SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier2)) {
408
+ return { baseIdentifier: baseIdentifier2, aliasIdentifier: aliasIdentifier2 };
409
+ }
410
+ }
411
+ const sqlAliasMatch = SQL_ALIAS_PATTERN.exec(token);
412
+ if (!sqlAliasMatch) {
413
+ return null;
414
+ }
415
+ const [, baseIdentifier, aliasIdentifier] = sqlAliasMatch;
416
+ if (!COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) || !SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier)) {
417
+ return null;
418
+ }
419
+ return { baseIdentifier, aliasIdentifier };
420
+ }
382
421
  function quoteQualifiedIdentifier(identifier2) {
383
422
  return identifier2.split(".").map((segment) => quoteIdentifierSegment(segment)).join(".");
384
423
  }
@@ -387,23 +426,27 @@ function quoteSelectToken(token) {
387
426
  if (COMPOSITE_IDENTIFIER_PATTERN.test(token)) {
388
427
  return quoteQualifiedIdentifier(token);
389
428
  }
390
- const aliasMatch = ALIAS_PATTERN.exec(token);
391
- if (!aliasMatch) {
392
- return token;
393
- }
394
- const [, baseIdentifier, aliasIdentifier] = aliasMatch;
395
- if (!COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) || !SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier)) {
429
+ const aliasedIdentifier = parseAliasedIdentifierToken(token);
430
+ if (!aliasedIdentifier) {
396
431
  return token;
397
432
  }
433
+ const { baseIdentifier, aliasIdentifier } = aliasedIdentifier;
398
434
  return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteIdentifierSegment(aliasIdentifier)}`;
399
435
  }
436
+ function quoteSelectColumnToken(token) {
437
+ const trimmed = token.trim();
438
+ if (!trimmed || trimmed === "*") return trimmed || "*";
439
+ const responseAliasMatch = RESPONSE_ALIAS_PATTERN.exec(trimmed);
440
+ if (responseAliasMatch) {
441
+ const [, aliasIdentifier, baseIdentifier] = responseAliasMatch;
442
+ return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteIdentifierSegment(aliasIdentifier)}`;
443
+ }
444
+ return quoteQualifiedIdentifier(trimmed);
445
+ }
400
446
  function canAutoQuoteToken(token) {
401
447
  if (token === "*") return true;
402
448
  if (COMPOSITE_IDENTIFIER_PATTERN.test(token)) return true;
403
- const aliasMatch = ALIAS_PATTERN.exec(token);
404
- if (!aliasMatch) return false;
405
- const [, baseIdentifier, aliasIdentifier] = aliasMatch;
406
- return COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) && SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier);
449
+ return parseAliasedIdentifierToken(token) != null;
407
450
  }
408
451
  function splitTopLevelCommaSeparated(input) {
409
452
  const parts = [];
@@ -503,6 +546,231 @@ function identifier(...segments) {
503
546
  return new SqlIdentifierPath(expandedSegments);
504
547
  }
505
548
 
549
+ // src/auth/react-email.ts
550
+ var reactEmailRenderModulePromise;
551
+ function isRecord2(value) {
552
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
553
+ }
554
+ function isFunction(value) {
555
+ return typeof value === "function";
556
+ }
557
+ function toStringOrUndefined(value) {
558
+ if (typeof value !== "string") return void 0;
559
+ return value;
560
+ }
561
+ function nowIsoString() {
562
+ return (/* @__PURE__ */ new Date()).toISOString();
563
+ }
564
+ function emitReactEmailEvent(observe, phase, input = {}) {
565
+ if (!observe) return;
566
+ try {
567
+ observe({
568
+ phase,
569
+ timestamp: nowIsoString(),
570
+ ...input
571
+ });
572
+ } catch {
573
+ }
574
+ }
575
+ function mergeRenderDefaults(input, defaults) {
576
+ return {
577
+ ...input,
578
+ pretty: input.pretty ?? defaults?.pretty,
579
+ includePlainText: input.includePlainText ?? defaults?.includePlainText
580
+ };
581
+ }
582
+ function mergeRuntimeOptions(options) {
583
+ if (!options) return void 0;
584
+ return {
585
+ defaults: options.defaults,
586
+ observe: options.observe,
587
+ route: "route" in options ? options.route : void 0
588
+ };
589
+ }
590
+ async function resolveReactEmailRenderModule() {
591
+ if (!reactEmailRenderModulePromise) {
592
+ reactEmailRenderModulePromise = (async () => {
593
+ try {
594
+ const loaded = await import('@react-email/render');
595
+ if (!isFunction(loaded.render)) {
596
+ throw new Error("missing render(...) export");
597
+ }
598
+ return {
599
+ render: loaded.render,
600
+ toPlainText: isFunction(loaded.toPlainText) ? loaded.toPlainText : void 0,
601
+ pretty: isFunction(loaded.pretty) ? loaded.pretty : void 0
602
+ };
603
+ } catch (error) {
604
+ const message = error instanceof Error ? error.message : String(error);
605
+ throw new Error(
606
+ `React Email rendering requires @react-email/render. Install it in your project (for example: pnpm add @react-email/render). Loader error: ${message}`
607
+ );
608
+ }
609
+ })();
610
+ }
611
+ if (!reactEmailRenderModulePromise) {
612
+ throw new Error("React Email renderer module failed to initialize");
613
+ }
614
+ return reactEmailRenderModulePromise;
615
+ }
616
+ async function resolveReactEmailElement(input) {
617
+ if (input.element != null) {
618
+ return input.element;
619
+ }
620
+ if (!input.component) {
621
+ throw new Error("react email payload requires either `element` or `component`");
622
+ }
623
+ try {
624
+ const reactModule = await import('react');
625
+ if (typeof reactModule.createElement !== "function") {
626
+ throw new Error("react createElement(...) export is unavailable");
627
+ }
628
+ return reactModule.createElement(
629
+ input.component,
630
+ input.props ?? {}
631
+ );
632
+ } catch (error) {
633
+ const message = error instanceof Error ? error.message : String(error);
634
+ throw new Error(
635
+ `React Email component rendering requires react runtime support. Install react in your project. Loader error: ${message}`
636
+ );
637
+ }
638
+ }
639
+ function createAuthReactEmailInput(component, props, overrides = {}) {
640
+ return {
641
+ ...overrides,
642
+ component,
643
+ props
644
+ };
645
+ }
646
+ function defineAuthEmailTemplate(definition) {
647
+ const react = (props, overrides) => createAuthReactEmailInput(definition.component, props, {
648
+ ...definition.defaults,
649
+ ...overrides
650
+ });
651
+ return {
652
+ component: definition.component,
653
+ react,
654
+ toTemplateCreate: (input) => {
655
+ const templateKey = input.templateKey ?? definition.templateKey;
656
+ const subjectTemplate = input.subjectTemplate ?? definition.subjectTemplate;
657
+ if (!templateKey) {
658
+ throw new Error("defineAuthEmailTemplate.toTemplateCreate requires templateKey");
659
+ }
660
+ if (!subjectTemplate) {
661
+ throw new Error("defineAuthEmailTemplate.toTemplateCreate requires subjectTemplate");
662
+ }
663
+ const { props, react: reactOverrides, ...rest } = input;
664
+ return {
665
+ ...rest,
666
+ templateKey,
667
+ subjectTemplate,
668
+ react: react(props, reactOverrides)
669
+ };
670
+ },
671
+ toTemplateUpdate: (input) => {
672
+ const { props, react: reactOverrides, ...rest } = input;
673
+ return {
674
+ ...rest,
675
+ react: react(props, reactOverrides)
676
+ };
677
+ }
678
+ };
679
+ }
680
+ async function renderAthenaReactEmail(input, options) {
681
+ if (!isRecord2(input)) {
682
+ throw new Error("react email payload must be an object");
683
+ }
684
+ const runtimeOptions = mergeRuntimeOptions(options);
685
+ const start = Date.now();
686
+ emitReactEmailEvent(runtimeOptions?.observe, "render:start", {
687
+ route: runtimeOptions?.route,
688
+ message: "Rendering react email payload"
689
+ });
690
+ try {
691
+ const normalizedInput = mergeRenderDefaults(input, runtimeOptions?.defaults);
692
+ const element = await resolveReactEmailElement(normalizedInput);
693
+ const renderModule = await resolveReactEmailRenderModule();
694
+ const htmlValue = await renderModule.render(element);
695
+ const renderedHtml = typeof htmlValue === "string" ? htmlValue : String(htmlValue ?? "");
696
+ if (!renderedHtml.trim()) {
697
+ throw new Error("react email renderer returned an empty HTML string");
698
+ }
699
+ let html = renderedHtml;
700
+ if (normalizedInput.pretty && renderModule.pretty) {
701
+ const prettyValue = await renderModule.pretty(renderedHtml);
702
+ if (typeof prettyValue === "string" && prettyValue.trim().length > 0) {
703
+ html = prettyValue;
704
+ }
705
+ }
706
+ const explicitText = toStringOrUndefined(normalizedInput.text);
707
+ if (explicitText !== void 0) {
708
+ emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
709
+ route: runtimeOptions?.route,
710
+ durationMs: Date.now() - start,
711
+ message: "Rendered react email with explicit text"
712
+ });
713
+ return {
714
+ html,
715
+ text: explicitText
716
+ };
717
+ }
718
+ if (normalizedInput.includePlainText === false || !renderModule.toPlainText) {
719
+ emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
720
+ route: runtimeOptions?.route,
721
+ durationMs: Date.now() - start,
722
+ message: "Rendered react email without plain-text derivation"
723
+ });
724
+ return { html };
725
+ }
726
+ const plainTextValue = await renderModule.toPlainText(html);
727
+ const plainText = toStringOrUndefined(plainTextValue);
728
+ emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
729
+ route: runtimeOptions?.route,
730
+ durationMs: Date.now() - start,
731
+ message: plainText ? "Rendered react email with derived plain text" : "Rendered react email HTML only"
732
+ });
733
+ if (plainText === void 0) {
734
+ return { html };
735
+ }
736
+ return {
737
+ html,
738
+ text: plainText
739
+ };
740
+ } catch (error) {
741
+ const message = error instanceof Error ? error.message : String(error);
742
+ emitReactEmailEvent(runtimeOptions?.observe, "render:error", {
743
+ route: runtimeOptions?.route,
744
+ durationMs: Date.now() - start,
745
+ error: message,
746
+ message: "Failed to render react email payload"
747
+ });
748
+ throw error;
749
+ }
750
+ }
751
+ async function resolveReactEmailPayloadFields(input, fields, options) {
752
+ const { react, ...payloadWithoutReact } = input;
753
+ if (!react) {
754
+ return payloadWithoutReact;
755
+ }
756
+ const rendered = await renderAthenaReactEmail(react, options);
757
+ const payload = {
758
+ ...payloadWithoutReact
759
+ };
760
+ payload[fields.htmlField] = rendered.html;
761
+ const currentTextValue = payload[fields.textField];
762
+ if (rendered.text !== void 0 && (currentTextValue === void 0 || currentTextValue === null || currentTextValue === "")) {
763
+ payload[fields.textField] = rendered.text;
764
+ }
765
+ if (fields.variablesField && (payload[fields.variablesField] === void 0 || payload[fields.variablesField] === null) && isRecord2(react.props)) {
766
+ const derivedVariables = Object.keys(react.props);
767
+ if (derivedVariables.length > 0) {
768
+ payload[fields.variablesField] = derivedVariables;
769
+ }
770
+ }
771
+ return payload;
772
+ }
773
+
506
774
  // src/auth/client.ts
507
775
  var DEFAULT_AUTH_BASE_URL = "http://localhost:3001/api/auth";
508
776
  var FALLBACK_SDK_VERSION2 = "1.0.0";
@@ -512,7 +780,7 @@ var SDK_HEADER_VALUE2 = `${SDK_NAME2} ${SDK_VERSION2}`;
512
780
  function normalizeBaseUrl(baseUrl) {
513
781
  return (baseUrl ?? DEFAULT_AUTH_BASE_URL).replace(/\/$/, "");
514
782
  }
515
- function isRecord2(value) {
783
+ function isRecord3(value) {
516
784
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
517
785
  }
518
786
  function normalizeHeaderValue2(value) {
@@ -537,7 +805,7 @@ function resolveRequestId2(headers) {
537
805
  return headers.get("x-request-id") ?? headers.get("x-correlation-id") ?? headers.get("x-athena-request-id") ?? void 0;
538
806
  }
539
807
  function resolveErrorMessage2(payload, fallback) {
540
- if (isRecord2(payload)) {
808
+ if (isRecord3(payload)) {
541
809
  const messageCandidates = [payload.error, payload.message, payload.details];
542
810
  for (const candidate of messageCandidates) {
543
811
  if (typeof candidate === "string" && candidate.trim().length > 0) {
@@ -863,6 +1131,19 @@ function createAuthClient(config = {}) {
863
1131
  options
864
1132
  );
865
1133
  };
1134
+ const withReactEmailRoute = (route) => ({
1135
+ ...resolvedConfig.reactEmail,
1136
+ route
1137
+ });
1138
+ const resolveAdminEmailPayload = (route, input) => resolveReactEmailPayloadFields(input, {
1139
+ htmlField: "htmlBody",
1140
+ textField: "textBody"
1141
+ }, withReactEmailRoute(route));
1142
+ const resolveAdminEmailTemplatePayload = (route, input) => resolveReactEmailPayloadFields(input, {
1143
+ htmlField: "htmlTemplate",
1144
+ textField: "textTemplate",
1145
+ variablesField: "variables"
1146
+ }, withReactEmailRoute(route));
866
1147
  const listUserEmailsWithFallback = async (input, options) => {
867
1148
  const primary = await getWithQuery(
868
1149
  "/email/list",
@@ -890,7 +1171,7 @@ function createAuthClient(config = {}) {
890
1171
  data: null
891
1172
  };
892
1173
  }
893
- const fallbackStatus = isRecord2(fallback.data) && typeof fallback.data.ok === "boolean" ? fallback.data.ok ? "ok" : "error" : "ok";
1174
+ const fallbackStatus = isRecord3(fallback.data) && typeof fallback.data.ok === "boolean" ? fallback.data.ok ? "ok" : "error" : "ok";
894
1175
  return {
895
1176
  ...fallback,
896
1177
  data: {
@@ -1326,8 +1607,16 @@ function createAuthClient(config = {}) {
1326
1607
  email: {
1327
1608
  list: (input, options) => getWithQuery("/admin/email/list", input, options),
1328
1609
  get: (input, options) => getWithQuery("/admin/email/get", input, options),
1329
- create: (input, options) => postGeneric("/admin/email/create", input, options),
1330
- update: (input, options) => postGeneric("/admin/email/update", input, options),
1610
+ create: async (input, options) => postGeneric(
1611
+ "/admin/email/create",
1612
+ await resolveAdminEmailPayload("/admin/email/create", input),
1613
+ options
1614
+ ),
1615
+ update: async (input, options) => postGeneric(
1616
+ "/admin/email/update",
1617
+ await resolveAdminEmailPayload("/admin/email/update", input),
1618
+ options
1619
+ ),
1331
1620
  delete: (input, options) => postGeneric("/admin/email/delete", input, options),
1332
1621
  failure: {
1333
1622
  list: (input, options) => getWithQuery("/admin/email-failure/list", input, options),
@@ -1339,17 +1628,33 @@ function createAuthClient(config = {}) {
1339
1628
  template: {
1340
1629
  list: (input, options) => getWithQuery("/admin/email-template/list", input, options),
1341
1630
  get: (input, options) => getWithQuery("/admin/email-template/get", input, options),
1342
- create: (input, options) => postGeneric("/admin/email-template/create", input, options),
1343
- update: (input, options) => postGeneric("/admin/email-template/update", input, options),
1631
+ create: async (input, options) => postGeneric(
1632
+ "/admin/email-template/create",
1633
+ await resolveAdminEmailTemplatePayload("/admin/email-template/create", input),
1634
+ options
1635
+ ),
1636
+ update: async (input, options) => postGeneric(
1637
+ "/admin/email-template/update",
1638
+ await resolveAdminEmailTemplatePayload("/admin/email-template/update", input),
1639
+ options
1640
+ ),
1344
1641
  delete: (input, options) => postGeneric("/admin/email-template/delete", input, options)
1345
1642
  }
1346
1643
  },
1347
1644
  emailTemplate: {
1348
1645
  get: (input, options) => getWithQuery("/admin/email-template/get", input, options),
1349
- create: (input, options) => postGeneric("/admin/email-template/create", input, options),
1646
+ create: async (input, options) => postGeneric(
1647
+ "/admin/email-template/create",
1648
+ await resolveAdminEmailTemplatePayload("/admin/email-template/create", input),
1649
+ options
1650
+ ),
1350
1651
  delete: (input, options) => postGeneric("/admin/email-template/delete", input, options),
1351
1652
  list: (input, options) => getWithQuery("/admin/email-template/list", input, options),
1352
- update: (input, options) => postGeneric("/admin/email-template/update", input, options)
1653
+ update: async (input, options) => postGeneric(
1654
+ "/admin/email-template/update",
1655
+ await resolveAdminEmailTemplatePayload("/admin/email-template/update", input),
1656
+ options
1657
+ )
1353
1658
  }
1354
1659
  },
1355
1660
  apiKey: {
@@ -1541,1282 +1846,2537 @@ function createAuthClient(config = {}) {
1541
1846
  };
1542
1847
  }
1543
1848
 
1544
- // src/client.ts
1545
- var DEFAULT_COLUMNS = "*";
1546
- 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;
1547
- var SAFE_CAST_PATTERN = /^[a-z_][a-z0-9_]*(?:\[\])?$/i;
1548
- function formatResult(response) {
1549
- const result = {
1550
- data: response.data ?? null,
1551
- error: response.error ?? null,
1552
- errorDetails: response.errorDetails ?? null,
1553
- status: response.status,
1554
- raw: response.raw
1555
- };
1556
- if (response.count !== void 0) {
1557
- result.count = response.count;
1849
+ // src/utils/parse-boolean-flag.ts
1850
+ function parseBooleanFlag(rawValue, fallback) {
1851
+ if (!rawValue) return fallback;
1852
+ const normalized = rawValue.trim().toLowerCase();
1853
+ if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
1854
+ return true;
1558
1855
  }
1559
- return result;
1560
- }
1561
- function toSingleResult(response) {
1562
- const payload = response.data;
1563
- const singleData = Array.isArray(payload) ? payload.length ? payload[0] : null : payload ?? null;
1564
- return {
1565
- ...response,
1566
- data: singleData
1567
- };
1568
- }
1569
- function mergeOptions(...options) {
1570
- return options.reduce((acc, next) => {
1571
- if (!next) return acc;
1572
- return { ...acc, ...next };
1573
- }, void 0);
1856
+ if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
1857
+ return false;
1858
+ }
1859
+ return fallback;
1574
1860
  }
1575
- function asAthenaJsonObject(value) {
1576
- return value;
1861
+
1862
+ // src/auxiliaries.ts
1863
+ var AthenaErrorKind = {
1864
+ UniqueViolation: "unique_violation",
1865
+ NotFound: "not_found",
1866
+ Validation: "validation",
1867
+ Auth: "auth",
1868
+ RateLimit: "rate_limit",
1869
+ Transient: "transient",
1870
+ Unknown: "unknown"
1871
+ };
1872
+ var AthenaErrorCode = {
1873
+ UniqueViolation: "UNIQUE_VIOLATION",
1874
+ NotFound: "NOT_FOUND",
1875
+ ValidationFailed: "VALIDATION_FAILED",
1876
+ AuthUnauthorized: "AUTH_UNAUTHORIZED",
1877
+ AuthForbidden: "AUTH_FORBIDDEN",
1878
+ RateLimited: "RATE_LIMITED",
1879
+ NetworkUnavailable: "NETWORK_UNAVAILABLE",
1880
+ TransientFailure: "TRANSIENT_FAILURE",
1881
+ HttpFailure: "HTTP_FAILURE",
1882
+ Unknown: "UNKNOWN"
1883
+ };
1884
+ var AthenaErrorCategory = {
1885
+ Transport: "transport",
1886
+ Client: "client",
1887
+ Server: "server",
1888
+ Database: "database",
1889
+ Unknown: "unknown"
1890
+ };
1891
+ function parseBooleanFlag2(rawValue, fallback) {
1892
+ return parseBooleanFlag(rawValue, fallback);
1577
1893
  }
1578
- function asAthenaJsonObjectArray(values) {
1579
- return values;
1894
+ var AthenaError = class extends Error {
1895
+ code;
1896
+ kind;
1897
+ category;
1898
+ status;
1899
+ retryable;
1900
+ requestId;
1901
+ context;
1902
+ raw;
1903
+ constructor(input) {
1904
+ super(input.message);
1905
+ this.name = "AthenaError";
1906
+ this.code = input.code;
1907
+ this.kind = input.kind;
1908
+ this.category = input.category;
1909
+ this.status = input.status;
1910
+ this.retryable = input.retryable ?? false;
1911
+ this.requestId = input.requestId;
1912
+ this.context = input.context;
1913
+ this.raw = input.raw;
1914
+ }
1915
+ };
1916
+ function isRecord4(value) {
1917
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1580
1918
  }
1581
- function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
1582
- let selectedColumns = defaultColumns === null ? void 0 : defaultColumns;
1583
- let selectedOptions;
1584
- let promise = null;
1585
- const run = (columns, options) => {
1586
- const payloadColumns = columns ?? selectedColumns;
1587
- const payloadOptions = options ?? selectedOptions;
1588
- if (!promise) {
1589
- promise = executor(payloadColumns, payloadOptions);
1590
- }
1591
- return promise;
1592
- };
1593
- const mutationQuery = {
1594
- select(columns = selectedColumns, options) {
1595
- selectedColumns = columns;
1596
- selectedOptions = options ?? selectedOptions;
1597
- return run(columns, options);
1598
- },
1599
- returning(columns = selectedColumns, options) {
1600
- return mutationQuery.select(columns, options);
1601
- },
1602
- single(columns = selectedColumns, options) {
1603
- selectedColumns = columns;
1604
- selectedOptions = options ?? selectedOptions;
1605
- return run(columns, options).then(toSingleResult);
1606
- },
1607
- maybeSingle(columns = selectedColumns, options) {
1608
- return mutationQuery.single(columns, options);
1609
- },
1610
- then(onfulfilled, onrejected) {
1611
- return run(selectedColumns, selectedOptions).then(onfulfilled, onrejected);
1612
- },
1613
- catch(onrejected) {
1614
- return run(selectedColumns, selectedOptions).catch(onrejected);
1615
- },
1616
- finally(onfinally) {
1617
- return run(selectedColumns, selectedOptions).finally(onfinally);
1919
+ function firstNonEmptyString(...values) {
1920
+ for (const value of values) {
1921
+ if (typeof value === "string" && value.trim().length > 0) {
1922
+ return value.trim();
1618
1923
  }
1619
- };
1620
- return mutationQuery;
1924
+ }
1925
+ return void 0;
1621
1926
  }
1622
- function getResourceId(state) {
1623
- const candidate = state.conditions.find(
1624
- (condition) => condition.operator === "eq" && (condition.column === "resource_id" || condition.column === "id")
1625
- );
1626
- return candidate?.value?.toString();
1927
+ function messageFromUnknownError(error) {
1928
+ if (typeof error === "string" && error.trim().length > 0) {
1929
+ return error.trim();
1930
+ }
1931
+ if (error instanceof Error && error.message.trim().length > 0) {
1932
+ return error.message.trim();
1933
+ }
1934
+ if (!isRecord4(error)) {
1935
+ return void 0;
1936
+ }
1937
+ return firstNonEmptyString(error.message, error.error, error.details);
1627
1938
  }
1628
- function stringifyFilterValue(value) {
1629
- if (Array.isArray(value)) {
1630
- return value.join(",");
1939
+ function gatewayCodeFromUnknownError(error) {
1940
+ if (!isRecord4(error) || typeof error.gatewayCode !== "string") {
1941
+ return void 0;
1631
1942
  }
1632
- return String(value);
1943
+ return error.gatewayCode;
1633
1944
  }
1634
- function isUuidString(value) {
1635
- return UUID_PATTERN.test(value.trim());
1945
+ function isAthenaResultErrorLike(value) {
1946
+ 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");
1636
1947
  }
1637
- function isUuidIdentifierColumn(column) {
1638
- return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
1948
+ function isAthenaErrorKind(value) {
1949
+ return value === "unique_violation" || value === "not_found" || value === "validation" || value === "auth" || value === "rate_limit" || value === "transient" || value === "unknown";
1639
1950
  }
1640
- function shouldUseUuidTextComparison(column, value) {
1641
- return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
1951
+ function isAthenaErrorCode(value) {
1952
+ return value === "UNIQUE_VIOLATION" || value === "NOT_FOUND" || value === "VALIDATION_FAILED" || value === "AUTH_UNAUTHORIZED" || value === "AUTH_FORBIDDEN" || value === "RATE_LIMITED" || value === "NETWORK_UNAVAILABLE" || value === "TRANSIENT_FAILURE" || value === "HTTP_FAILURE" || value === "UNKNOWN";
1642
1953
  }
1643
- function normalizeCast(cast) {
1644
- const normalized = cast.trim().toLowerCase();
1645
- if (!SAFE_CAST_PATTERN.test(normalized)) {
1646
- throw new Error(`Invalid cast type "${cast}"`);
1647
- }
1648
- return normalized;
1954
+ function isAthenaErrorCategory(value) {
1955
+ return value === "transport" || value === "client" || value === "server" || value === "database" || value === "unknown";
1649
1956
  }
1650
- function escapeSqlStringLiteral(value) {
1651
- return value.replace(/'/g, "''");
1957
+ function isNormalizedAthenaError(value) {
1958
+ return isRecord4(value) && isAthenaErrorKind(value.kind) && isAthenaErrorCode(value.code) && isAthenaErrorCategory(value.category) && typeof value.retryable === "boolean" && typeof value.message === "string" && "raw" in value;
1652
1959
  }
1653
- function toSqlLiteral(value) {
1654
- if (value === null) return "NULL";
1655
- if (typeof value === "number") return Number.isFinite(value) ? String(value) : "NULL";
1656
- if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
1657
- return `'${escapeSqlStringLiteral(value)}'`;
1960
+ function withContextOverrides(normalized, context) {
1961
+ if (!context?.table && !context?.operation) {
1962
+ return normalized;
1963
+ }
1964
+ return {
1965
+ ...normalized,
1966
+ table: context.table ?? normalized.table,
1967
+ operation: context.operation ?? normalized.operation
1968
+ };
1658
1969
  }
1659
- function withCast(expression, cast) {
1660
- if (!cast) return expression;
1661
- return `${expression}::${normalizeCast(cast)}`;
1970
+ function resolveAttachedNormalizedError(resultOrError) {
1971
+ if (!isRecord4(resultOrError)) return void 0;
1972
+ if (!("__athenaNormalizedError" in resultOrError)) return void 0;
1973
+ const candidate = resultOrError.__athenaNormalizedError;
1974
+ return isNormalizedAthenaError(candidate) ? candidate : void 0;
1662
1975
  }
1663
- function buildSelectColumnsClause(columns) {
1664
- if (Array.isArray(columns)) {
1665
- return columns.map((column) => quoteQualifiedIdentifier(column)).join(", ");
1976
+ function safeStringify(value) {
1977
+ try {
1978
+ const serialized = JSON.stringify(value);
1979
+ return serialized ?? String(value);
1980
+ } catch {
1981
+ return "[unserializable]";
1666
1982
  }
1667
- return quoteSelectColumnsExpression(columns);
1668
1983
  }
1669
- function resolveTableNameForCall(tableName, schema) {
1670
- if (!schema) return tableName;
1671
- const normalizedSchema = schema.trim();
1672
- if (!normalizedSchema) {
1673
- throw new Error("schema option must be a non-empty string");
1674
- }
1675
- if (tableName.includes(".")) {
1676
- if (tableName.startsWith(`${normalizedSchema}.`)) {
1677
- return tableName;
1678
- }
1679
- throw new Error(
1680
- `schema option "${normalizedSchema}" conflicts with schema-qualified table "${tableName}"`
1681
- );
1984
+ function contextHint(context) {
1985
+ if (!context?.identity) return void 0;
1986
+ const identity = typeof context.identity === "string" ? context.identity : safeStringify(context.identity);
1987
+ return `Identity: ${identity}`;
1988
+ }
1989
+ function isAthenaResultLike(value) {
1990
+ return isRecord4(value) && "status" in value && "error" in value && "data" in value && typeof value.status === "number";
1991
+ }
1992
+ function operationFromDetails(details) {
1993
+ if (!details?.endpoint) return void 0;
1994
+ if (details.endpoint === "/gateway/fetch" || details.endpoint === "/gateway/query") return "select";
1995
+ if (details.endpoint === "/gateway/insert") return "insert";
1996
+ if (details.endpoint === "/gateway/update") return "update";
1997
+ if (details.endpoint === "/gateway/delete") return "delete";
1998
+ if (details.endpoint === "/gateway/rpc" || details.endpoint.startsWith("/rpc/")) return "rpc";
1999
+ return void 0;
2000
+ }
2001
+ function matchRegex(input, patterns) {
2002
+ for (const pattern of patterns) {
2003
+ const match = pattern.exec(input);
2004
+ if (match?.[1]) return match[1];
1682
2005
  }
1683
- return `${normalizedSchema}.${tableName}`;
2006
+ return void 0;
1684
2007
  }
1685
- function conditionToSqlClause(condition) {
1686
- if (!condition.column) return null;
1687
- const column = withCast(quoteQualifiedIdentifier(condition.column), condition.column_cast);
1688
- const value = condition.value;
1689
- const sqlOperator = {
1690
- eq: "=",
1691
- neq: "!=",
1692
- gt: ">",
1693
- gte: ">=",
1694
- lt: "<",
1695
- lte: "<=",
1696
- like: "LIKE",
1697
- ilike: "ILIKE"
1698
- };
1699
- switch (condition.operator) {
1700
- case "eq":
1701
- case "neq":
1702
- case "gt":
1703
- case "gte":
1704
- case "lt":
1705
- case "lte":
1706
- case "like":
1707
- case "ilike": {
1708
- if (Array.isArray(value) || value === void 0) return null;
1709
- const rhs = withCast(toSqlLiteral(value), condition.value_cast);
1710
- return `${column} ${sqlOperator[condition.operator]} ${rhs}`;
1711
- }
1712
- case "is": {
1713
- if (value === null) return `${column} IS NULL`;
1714
- if (value === true) return `${column} IS TRUE`;
1715
- if (value === false) return `${column} IS FALSE`;
1716
- return null;
1717
- }
1718
- case "in": {
1719
- if (!Array.isArray(value)) return null;
1720
- if (value.length === 0) return "FALSE";
1721
- const values = value.map((item) => withCast(toSqlLiteral(item), condition.value_cast));
1722
- return `${column} IN (${values.join(", ")})`;
1723
- }
1724
- default:
1725
- return null;
2008
+ function extractConstraint(message) {
2009
+ return matchRegex(message, [
2010
+ /unique constraint\s+["'`]([^"'`]+)["'`]/i,
2011
+ /constraint\s+["'`]([^"'`]+)["'`]/i
2012
+ ]);
2013
+ }
2014
+ function extractTable(message) {
2015
+ return matchRegex(message, [
2016
+ /(?:table|relation)\s+["'`]([^"'`]+)["'`]/i,
2017
+ /on\s+table\s+([a-zA-Z0-9_.]+)/i
2018
+ ]);
2019
+ }
2020
+ function classifyKind(status, code, message) {
2021
+ const lower = message.toLowerCase();
2022
+ const hasUniquePattern = lower.includes("unique constraint") || lower.includes("duplicate key") || lower.includes("already exists") || lower.includes("duplicate");
2023
+ const hasNotFoundPattern = lower.includes("not found") || lower.includes("no rows");
2024
+ const hasValidationPattern = lower.includes("validation") || lower.includes("invalid") || lower.includes("malformed");
2025
+ const hasAuthPattern = lower.includes("unauthorized") || lower.includes("forbidden") || lower.includes("auth");
2026
+ const hasRateLimitPattern = lower.includes("rate limit") || lower.includes("too many requests");
2027
+ const hasTransientPattern = lower.includes("timeout") || lower.includes("temporar") || lower.includes("connection reset") || lower.includes("socket") || lower.includes("deadlock");
2028
+ if (status === 409 || hasUniquePattern) return "unique_violation";
2029
+ if (status === 404 || hasNotFoundPattern) return "not_found";
2030
+ if (status === 401 || status === 403 || hasAuthPattern) return "auth";
2031
+ if (status === 429 || hasRateLimitPattern) return "rate_limit";
2032
+ if (status === 400 || status === 422 || hasValidationPattern) return "validation";
2033
+ if (code === "NETWORK_ERROR" || status === 0 || status !== void 0 && status >= 500 || hasTransientPattern) {
2034
+ return "transient";
1726
2035
  }
2036
+ return "unknown";
1727
2037
  }
1728
- function buildTypedSelectQuery(input) {
1729
- const whereClauses = [];
1730
- for (const condition of input.conditions) {
1731
- const clause = conditionToSqlClause(condition);
1732
- if (!clause) return null;
1733
- whereClauses.push(clause);
2038
+ function toAthenaErrorCode(kind, status, gatewayCode) {
2039
+ if (gatewayCode === "NETWORK_ERROR" || kind === "transient" && status === 0) {
2040
+ return AthenaErrorCode.NetworkUnavailable;
1734
2041
  }
1735
- let limit = input.limit;
1736
- let offset = input.offset;
1737
- if (limit === void 0 && input.pageSize !== void 0) {
1738
- limit = input.pageSize;
2042
+ switch (kind) {
2043
+ case "unique_violation":
2044
+ return AthenaErrorCode.UniqueViolation;
2045
+ case "not_found":
2046
+ return AthenaErrorCode.NotFound;
2047
+ case "validation":
2048
+ return AthenaErrorCode.ValidationFailed;
2049
+ case "rate_limit":
2050
+ return AthenaErrorCode.RateLimited;
2051
+ case "auth":
2052
+ if (status === 403) {
2053
+ return AthenaErrorCode.AuthForbidden;
2054
+ }
2055
+ return AthenaErrorCode.AuthUnauthorized;
2056
+ case "transient":
2057
+ return status !== void 0 && status >= 500 ? AthenaErrorCode.HttpFailure : AthenaErrorCode.TransientFailure;
2058
+ case "unknown":
2059
+ default:
2060
+ return status !== void 0 && status >= 400 ? AthenaErrorCode.HttpFailure : AthenaErrorCode.Unknown;
1739
2061
  }
1740
- if (offset === void 0 && input.pageSize !== void 0 && input.currentPage !== void 0 && input.currentPage > 0) {
1741
- offset = (input.currentPage - 1) * input.pageSize;
2062
+ }
2063
+ function toAthenaErrorCategory(kind, status) {
2064
+ if (kind === "transient" && (status === 0 || status === void 0)) {
2065
+ return AthenaErrorCategory.Transport;
1742
2066
  }
1743
- const sqlParts = [
1744
- `SELECT ${buildSelectColumnsClause(input.columns)} FROM ${quoteQualifiedIdentifier(input.tableName)}`
1745
- ];
1746
- if (whereClauses.length > 0) {
1747
- sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
2067
+ if (kind === "unique_violation") return AthenaErrorCategory.Database;
2068
+ if (kind === "validation" || kind === "auth" || kind === "not_found") return AthenaErrorCategory.Client;
2069
+ if (kind === "rate_limit" || kind === "transient") return AthenaErrorCategory.Server;
2070
+ return AthenaErrorCategory.Unknown;
2071
+ }
2072
+ function isRetryable(kind, status) {
2073
+ if (kind === "rate_limit" || kind === "transient") return true;
2074
+ return status !== void 0 && status >= 500;
2075
+ }
2076
+ function toGatewayCode(kind, status) {
2077
+ if (kind === "transient" && (status === 0 || status === void 0)) return "NETWORK_ERROR";
2078
+ if (kind === "validation") return "INVALID_JSON";
2079
+ if (status !== void 0 && status >= 400) return "HTTP_ERROR";
2080
+ return "UNKNOWN_ERROR";
2081
+ }
2082
+ function toAthenaGatewayError(source, fallbackMessage, context) {
2083
+ if (isAthenaGatewayError(source)) {
2084
+ return source;
1748
2085
  }
1749
- if (input.order?.field) {
1750
- const direction = input.order.direction === "descending" ? "DESC" : "ASC";
1751
- sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(input.order.field)} ${direction}`);
2086
+ if (isAthenaResultLike(source) && source.errorDetails) {
2087
+ const message2 = messageFromUnknownError(source.error) ?? source.errorDetails.message ?? fallbackMessage;
2088
+ return new AthenaGatewayError({
2089
+ code: source.errorDetails.code,
2090
+ message: message2,
2091
+ status: source.status,
2092
+ endpoint: source.errorDetails.endpoint,
2093
+ method: source.errorDetails.method,
2094
+ requestId: source.errorDetails.requestId,
2095
+ hint: (isRecord4(source.error) ? firstNonEmptyString(source.error.hint) : void 0) ?? source.errorDetails.hint,
2096
+ cause: (isRecord4(source.error) ? firstNonEmptyString(source.error.cause) : void 0) ?? source.errorDetails.cause
2097
+ });
1752
2098
  }
1753
- if (limit !== void 0) {
1754
- sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(limit))}`);
2099
+ const normalized = normalizeAthenaError(source, context);
2100
+ const message = isAthenaResultLike(source) && source.error == null && source.errorDetails == null ? fallbackMessage : normalized.message || fallbackMessage;
2101
+ return new AthenaGatewayError({
2102
+ code: toGatewayCode(normalized.kind, normalized.status),
2103
+ message,
2104
+ status: normalized.status ?? 0,
2105
+ hint: normalized.constraint != null ? `Constraint: ${normalized.constraint}` : contextHint(context),
2106
+ cause: typeof normalized.raw === "string" ? normalized.raw : safeStringify(normalized.raw)
2107
+ });
2108
+ }
2109
+ function isOk(result) {
2110
+ return result.error == null && result.status >= 200 && result.status < 300;
2111
+ }
2112
+ function normalizeAthenaError(resultOrError, context) {
2113
+ const attached = resolveAttachedNormalizedError(resultOrError);
2114
+ if (attached) {
2115
+ return withContextOverrides(attached, context);
1755
2116
  }
1756
- if (offset !== void 0) {
1757
- sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(offset))}`);
2117
+ if (isAthenaResultLike(resultOrError)) {
2118
+ if (isAthenaResultErrorLike(resultOrError.error)) {
2119
+ return {
2120
+ kind: resultOrError.error.kind ?? classifyKind(resultOrError.status, gatewayCodeFromUnknownError(resultOrError.error), resultOrError.error.message),
2121
+ code: resultOrError.error.athenaCode ?? toAthenaErrorCode(
2122
+ resultOrError.error.kind ?? classifyKind(
2123
+ resultOrError.status,
2124
+ gatewayCodeFromUnknownError(resultOrError.error),
2125
+ resultOrError.error.message
2126
+ ),
2127
+ resultOrError.error.status ?? resultOrError.status,
2128
+ gatewayCodeFromUnknownError(resultOrError.error)
2129
+ ),
2130
+ category: resultOrError.error.category ?? toAthenaErrorCategory(
2131
+ resultOrError.error.kind ?? classifyKind(
2132
+ resultOrError.status,
2133
+ gatewayCodeFromUnknownError(resultOrError.error),
2134
+ resultOrError.error.message
2135
+ ),
2136
+ resultOrError.error.status ?? resultOrError.status
2137
+ ),
2138
+ retryable: resultOrError.error.retryable ?? isRetryable(
2139
+ resultOrError.error.kind ?? classifyKind(
2140
+ resultOrError.status,
2141
+ gatewayCodeFromUnknownError(resultOrError.error),
2142
+ resultOrError.error.message
2143
+ ),
2144
+ resultOrError.error.status ?? resultOrError.status
2145
+ ),
2146
+ status: resultOrError.error.status ?? resultOrError.status,
2147
+ constraint: resultOrError.error.constraint,
2148
+ table: context?.table ?? resultOrError.error.table,
2149
+ operation: context?.operation ?? resultOrError.error.operation,
2150
+ message: resultOrError.error.message,
2151
+ raw: resultOrError.error.raw ?? resultOrError.raw
2152
+ };
2153
+ }
2154
+ const details = resultOrError.errorDetails;
2155
+ const message2 = messageFromUnknownError(resultOrError.error) ?? details?.message ?? `Athena ${context?.operation ?? operationFromDetails(details) ?? "request"} failed`;
2156
+ const operation = context?.operation ?? operationFromDetails(details);
2157
+ const table = context?.table ?? extractTable(message2);
2158
+ const constraint = extractConstraint(message2);
2159
+ const gatewayCode = details?.code ?? gatewayCodeFromUnknownError(resultOrError.error);
2160
+ const kind2 = classifyKind(resultOrError.status, gatewayCode, message2);
2161
+ const code = toAthenaErrorCode(kind2, resultOrError.status, gatewayCode);
2162
+ const category = toAthenaErrorCategory(kind2, resultOrError.status);
2163
+ return {
2164
+ kind: kind2,
2165
+ code,
2166
+ category,
2167
+ retryable: isRetryable(kind2, resultOrError.status),
2168
+ status: resultOrError.status,
2169
+ constraint,
2170
+ table,
2171
+ operation,
2172
+ message: message2,
2173
+ raw: resultOrError.raw
2174
+ };
1758
2175
  }
1759
- return `${sqlParts.join(" ")};`;
1760
- }
1761
- function createFilterMethods(state, addCondition, self) {
1762
- return {
1763
- eq(column, value) {
1764
- const columnName = String(column);
1765
- if (shouldUseUuidTextComparison(columnName, value)) {
1766
- addCondition("eq", columnName, value, { columnCast: "text" });
1767
- } else {
1768
- addCondition("eq", columnName, value);
2176
+ if (isAthenaGatewayError(resultOrError)) {
2177
+ const details = resultOrError.toDetails();
2178
+ const operation = context?.operation ?? operationFromDetails(details);
2179
+ const table = context?.table ?? extractTable(resultOrError.message);
2180
+ const constraint = extractConstraint(resultOrError.message);
2181
+ const kind2 = classifyKind(resultOrError.status, resultOrError.code, resultOrError.message);
2182
+ const code = toAthenaErrorCode(kind2, resultOrError.status, resultOrError.code);
2183
+ const category = toAthenaErrorCategory(kind2, resultOrError.status);
2184
+ return {
2185
+ kind: kind2,
2186
+ code,
2187
+ category,
2188
+ retryable: isRetryable(kind2, resultOrError.status),
2189
+ status: resultOrError.status,
2190
+ constraint,
2191
+ table,
2192
+ operation,
2193
+ message: resultOrError.message,
2194
+ raw: resultOrError
2195
+ };
2196
+ }
2197
+ if (resultOrError instanceof Error) {
2198
+ const maybeStatus = isRecord4(resultOrError) && typeof resultOrError.status === "number" ? resultOrError.status : void 0;
2199
+ const kind2 = classifyKind(maybeStatus, void 0, resultOrError.message);
2200
+ return {
2201
+ kind: kind2,
2202
+ code: toAthenaErrorCode(kind2, maybeStatus),
2203
+ category: toAthenaErrorCategory(kind2, maybeStatus),
2204
+ retryable: isRetryable(kind2, maybeStatus),
2205
+ status: maybeStatus,
2206
+ constraint: extractConstraint(resultOrError.message),
2207
+ table: context?.table ?? extractTable(resultOrError.message),
2208
+ operation: context?.operation,
2209
+ message: resultOrError.message,
2210
+ raw: resultOrError
2211
+ };
2212
+ }
2213
+ const message = typeof resultOrError === "string" ? resultOrError : "Unknown Athena error";
2214
+ const kind = classifyKind(void 0, void 0, message);
2215
+ return {
2216
+ kind,
2217
+ code: toAthenaErrorCode(kind, void 0),
2218
+ category: toAthenaErrorCategory(kind, void 0),
2219
+ retryable: isRetryable(kind, void 0),
2220
+ status: void 0,
2221
+ constraint: extractConstraint(message),
2222
+ table: context?.table ?? extractTable(message),
2223
+ operation: context?.operation,
2224
+ message,
2225
+ raw: resultOrError
2226
+ };
2227
+ }
2228
+ function unwrapRows(result, options) {
2229
+ if (!isOk(result)) {
2230
+ throw toAthenaGatewayError(result, "Athena request failed", options?.context);
2231
+ }
2232
+ if (result.data == null) return [];
2233
+ return Array.isArray(result.data) ? result.data : [result.data];
2234
+ }
2235
+ function unwrap(result, options) {
2236
+ if (!isOk(result)) {
2237
+ throw toAthenaGatewayError(result, "Athena request failed", options?.context);
2238
+ }
2239
+ if (result.data == null && !options?.allowNull) {
2240
+ throw toAthenaGatewayError(result, "Expected data but received null", options?.context);
2241
+ }
2242
+ return result.data;
2243
+ }
2244
+ function unwrapOne(result, options) {
2245
+ const rows = unwrapRows(result, options);
2246
+ if (!rows.length) {
2247
+ if (options?.allowNull) return null;
2248
+ throw toAthenaGatewayError(result, "Expected one row but received none", options?.context);
2249
+ }
2250
+ if (options?.requireExactlyOne && rows.length !== 1) {
2251
+ throw toAthenaGatewayError(
2252
+ result,
2253
+ `Expected exactly one row but received ${rows.length}`,
2254
+ options.context
2255
+ );
2256
+ }
2257
+ return rows[0];
2258
+ }
2259
+ function requireSuccess(result, context) {
2260
+ if (!isOk(result)) {
2261
+ throw toAthenaGatewayError(
2262
+ result,
2263
+ `Athena ${context?.operation ?? "request"} failed`,
2264
+ context
2265
+ );
2266
+ }
2267
+ return result;
2268
+ }
2269
+ function requireAffected(result, options, context) {
2270
+ requireSuccess(result, context);
2271
+ const minimum = options?.min ?? 1;
2272
+ const count = result.count;
2273
+ if (count == null) {
2274
+ throw new AthenaGatewayError({
2275
+ code: "UNKNOWN_ERROR",
2276
+ status: result.status,
2277
+ message: "Expected affected row count but response.count is missing",
2278
+ hint: 'Set call option { count: "exact" } for mutation postcondition checks.',
2279
+ cause: safeStringify(result.raw)
2280
+ });
2281
+ }
2282
+ if (count < minimum) {
2283
+ throw new AthenaGatewayError({
2284
+ code: "UNKNOWN_ERROR",
2285
+ status: result.status,
2286
+ message: `Expected at least ${minimum} affected rows but received ${count}`,
2287
+ hint: contextHint(context),
2288
+ cause: safeStringify(result.raw)
2289
+ });
2290
+ }
2291
+ return count;
2292
+ }
2293
+ function applyBounds(value, options) {
2294
+ if (options?.min !== void 0 && value < options.min) return null;
2295
+ if (options?.max !== void 0 && value > options.max) return null;
2296
+ return value;
2297
+ }
2298
+ function parseIntegerString(value) {
2299
+ const normalized = value.trim();
2300
+ if (!/^[-+]?\d+$/.test(normalized)) return null;
2301
+ const parsed = Number(normalized);
2302
+ if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) return null;
2303
+ return parsed;
2304
+ }
2305
+ function coerceInt(value, options) {
2306
+ if (value == null) return null;
2307
+ if (typeof value === "number") {
2308
+ if (!Number.isFinite(value) || !Number.isInteger(value)) return null;
2309
+ return applyBounds(value, options);
2310
+ }
2311
+ if (typeof value === "string") {
2312
+ const parsed = parseIntegerString(value);
2313
+ if (parsed == null) return null;
2314
+ return applyBounds(parsed, options);
2315
+ }
2316
+ if (typeof value === "bigint") {
2317
+ if (options?.strictBigInt) {
2318
+ if (value > BigInt(Number.MAX_SAFE_INTEGER) || value < BigInt(Number.MIN_SAFE_INTEGER)) {
2319
+ return null;
1769
2320
  }
1770
- return self;
1771
- },
1772
- eqCast(column, value, cast) {
1773
- addCondition("eq", String(column), value, { valueCast: cast });
1774
- return self;
1775
- },
1776
- eqUuid(column, value) {
1777
- addCondition("eq", String(column), value, { valueCast: "uuid" });
1778
- return self;
1779
- },
1780
- match(filters) {
1781
- Object.entries(filters).forEach(([column, value]) => {
1782
- if (value === void 0) {
1783
- return;
1784
- }
1785
- if (shouldUseUuidTextComparison(column, value)) {
1786
- addCondition("eq", column, value, { columnCast: "text" });
1787
- } else {
1788
- addCondition("eq", column, value);
1789
- }
1790
- });
1791
- return self;
2321
+ }
2322
+ const parsed = Number(value);
2323
+ if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) return null;
2324
+ return applyBounds(parsed, options);
2325
+ }
2326
+ return null;
2327
+ }
2328
+ function assertInt(value, label = "value", options) {
2329
+ const parsed = coerceInt(value, options);
2330
+ if (parsed == null) {
2331
+ throw new TypeError(`${label} must be a finite integer`);
2332
+ }
2333
+ return parsed;
2334
+ }
2335
+ function defaultShouldRetry(error) {
2336
+ const normalized = normalizeAthenaError(error);
2337
+ return normalized.kind === "transient" || normalized.kind === "rate_limit";
2338
+ }
2339
+ function computeDelayMs(attempt, error, config) {
2340
+ const baseDelay = config.baseDelayMs;
2341
+ const rawDelay = typeof config.backoff === "function" ? config.backoff(attempt, error) : config.backoff === "linear" ? baseDelay * attempt : baseDelay * Math.pow(2, attempt - 1);
2342
+ const safeDelay = Number.isFinite(rawDelay) ? Math.max(0, rawDelay) : 0;
2343
+ const clamped = Math.min(config.maxDelayMs, safeDelay);
2344
+ const jitterFactor = typeof config.jitter === "number" ? Math.max(0, Math.min(1, config.jitter)) : config.jitter ? 0.2 : 0;
2345
+ if (!jitterFactor) return clamped;
2346
+ const deviation = clamped * jitterFactor;
2347
+ const offset = (Math.random() * 2 - 1) * deviation;
2348
+ return Math.max(0, clamped + offset);
2349
+ }
2350
+ function sleep(ms) {
2351
+ if (ms <= 0) return Promise.resolve();
2352
+ return new Promise((resolve3) => {
2353
+ setTimeout(resolve3, ms);
2354
+ });
2355
+ }
2356
+ async function withRetry(config, fn) {
2357
+ const retries = Math.max(0, Math.trunc(config.retries));
2358
+ const shouldRetry = config.shouldRetry ?? defaultShouldRetry;
2359
+ const resolvedConfig = {
2360
+ baseDelayMs: config.baseDelayMs ?? 100,
2361
+ maxDelayMs: config.maxDelayMs ?? 1e4,
2362
+ backoff: config.backoff ?? "exponential",
2363
+ jitter: config.jitter ?? false
2364
+ };
2365
+ for (let attempts = 0; attempts <= retries; attempts += 1) {
2366
+ try {
2367
+ return await fn();
2368
+ } catch (error) {
2369
+ if (attempts >= retries) {
2370
+ throw error;
2371
+ }
2372
+ const currentAttempt = attempts + 1;
2373
+ const retry = await shouldRetry(error, currentAttempt);
2374
+ if (!retry) {
2375
+ throw error;
2376
+ }
2377
+ const delay = computeDelayMs(currentAttempt, error, resolvedConfig);
2378
+ await sleep(delay);
2379
+ }
2380
+ }
2381
+ throw new Error("withRetry reached an unexpected state");
2382
+ }
2383
+
2384
+ // src/db/module.ts
2385
+ function createDbModule(input) {
2386
+ const db = {
2387
+ from(table) {
2388
+ return input.from(table);
1792
2389
  },
1793
- range(from, to) {
1794
- state.offset = from;
1795
- state.limit = to - from + 1;
1796
- return self;
2390
+ select(table, columns, options) {
2391
+ return input.from(table).select(columns, options);
1797
2392
  },
1798
- limit(count) {
1799
- state.limit = count;
1800
- return self;
2393
+ insert(table, values, options) {
2394
+ return Array.isArray(values) ? input.from(table).insert(values, options) : input.from(table).insert(values, options);
1801
2395
  },
1802
- offset(count) {
1803
- state.offset = count;
1804
- return self;
2396
+ upsert(table, values, options) {
2397
+ return Array.isArray(values) ? input.from(table).upsert(values, options) : input.from(table).upsert(values, options);
1805
2398
  },
1806
- currentPage(value) {
1807
- state.currentPage = value;
1808
- return self;
2399
+ update(table, values, options) {
2400
+ return input.from(table).update(values, options);
1809
2401
  },
1810
- pageSize(value) {
1811
- state.pageSize = value;
1812
- return self;
2402
+ delete(table, options) {
2403
+ return input.from(table).delete(options);
1813
2404
  },
1814
- totalPages(value) {
1815
- state.totalPages = value;
1816
- return self;
2405
+ rpc(fn, args, options) {
2406
+ return input.rpc(fn, args, options);
1817
2407
  },
1818
- order(column, options) {
1819
- state.order = {
1820
- field: String(column),
1821
- direction: options?.ascending === false ? "descending" : "ascending"
1822
- };
1823
- return self;
1824
- },
1825
- gt(column, value) {
1826
- addCondition("gt", String(column), value);
1827
- return self;
1828
- },
1829
- gte(column, value) {
1830
- addCondition("gte", String(column), value);
1831
- return self;
1832
- },
1833
- lt(column, value) {
1834
- addCondition("lt", String(column), value);
1835
- return self;
1836
- },
1837
- lte(column, value) {
1838
- addCondition("lte", String(column), value);
1839
- return self;
1840
- },
1841
- neq(column, value) {
1842
- addCondition("neq", String(column), value);
1843
- return self;
1844
- },
1845
- like(column, value) {
1846
- addCondition("like", String(column), value);
1847
- return self;
1848
- },
1849
- ilike(column, value) {
1850
- addCondition("ilike", String(column), value);
1851
- return self;
1852
- },
1853
- is(column, value) {
1854
- addCondition("is", String(column), value);
1855
- return self;
1856
- },
1857
- in(column, values) {
1858
- addCondition("in", String(column), values);
1859
- return self;
1860
- },
1861
- contains(column, values) {
1862
- addCondition("contains", String(column), values);
1863
- return self;
1864
- },
1865
- containedBy(column, values) {
1866
- addCondition("containedBy", String(column), values);
1867
- return self;
1868
- },
1869
- not(columnOrExpression, operator, value) {
1870
- const expression = String(columnOrExpression);
1871
- if (operator != null && value !== void 0) {
1872
- addCondition("not", void 0, `${expression}.${operator}.${stringifyFilterValue(value)}`);
1873
- } else {
1874
- addCondition("not", void 0, expression);
1875
- }
1876
- return self;
1877
- },
1878
- or(expression) {
1879
- addCondition("or", void 0, expression);
1880
- return self;
2408
+ query(query, options) {
2409
+ return input.query(query, options);
1881
2410
  }
1882
2411
  };
2412
+ return db;
1883
2413
  }
1884
- function toRpcSelect(columns) {
1885
- if (!columns) return void 0;
1886
- return Array.isArray(columns) ? columns.join(",") : columns;
2414
+
2415
+ // src/query-ast.ts
2416
+ 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;
2417
+ var FILTER_OPERATORS = /* @__PURE__ */ new Set([
2418
+ "eq",
2419
+ "neq",
2420
+ "gt",
2421
+ "gte",
2422
+ "lt",
2423
+ "lte",
2424
+ "like",
2425
+ "ilike",
2426
+ "is",
2427
+ "in",
2428
+ "contains",
2429
+ "containedBy"
2430
+ ]);
2431
+ var BOOLEAN_SAFE_OPERATORS = /* @__PURE__ */ new Set([
2432
+ "eq",
2433
+ "neq",
2434
+ "gt",
2435
+ "gte",
2436
+ "lt",
2437
+ "lte",
2438
+ "like",
2439
+ "ilike",
2440
+ "is"
2441
+ ]);
2442
+ function isRecord5(value) {
2443
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1887
2444
  }
1888
- function createRpcFilterMethods(filters, self) {
1889
- const addFilter = (operator, column, value) => {
1890
- filters.push({ column, operator, value });
1891
- };
1892
- return {
1893
- eq(column, value) {
1894
- addFilter("eq", column, value);
1895
- return self;
1896
- },
1897
- neq(column, value) {
1898
- addFilter("neq", column, value);
1899
- return self;
1900
- },
1901
- gt(column, value) {
1902
- addFilter("gt", column, value);
1903
- return self;
1904
- },
1905
- gte(column, value) {
1906
- addFilter("gte", column, value);
1907
- return self;
1908
- },
1909
- lt(column, value) {
1910
- addFilter("lt", column, value);
1911
- return self;
1912
- },
1913
- lte(column, value) {
1914
- addFilter("lte", column, value);
1915
- return self;
1916
- },
1917
- like(column, value) {
1918
- addFilter("like", column, value);
1919
- return self;
1920
- },
1921
- ilike(column, value) {
1922
- addFilter("ilike", column, value);
1923
- return self;
1924
- },
1925
- is(column, value) {
1926
- addFilter("is", column, value);
1927
- return self;
1928
- },
1929
- in(column, values) {
1930
- addFilter("in", column, values);
1931
- return self;
2445
+ function isUuidString(value) {
2446
+ return UUID_PATTERN.test(value.trim());
2447
+ }
2448
+ function isUuidIdentifierColumn(column) {
2449
+ return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
2450
+ }
2451
+ function shouldUseUuidTextComparison(column, value) {
2452
+ return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
2453
+ }
2454
+ function isRelationSelectNode(value) {
2455
+ return isRecord5(value) && isRecord5(value.select);
2456
+ }
2457
+ function normalizeIdentifier(value, label) {
2458
+ const normalized = value.trim();
2459
+ if (!normalized) {
2460
+ throw new Error(`${label} must be a non-empty string`);
2461
+ }
2462
+ return normalized;
2463
+ }
2464
+ function stringifyFilterValue(value) {
2465
+ if (Array.isArray(value)) {
2466
+ return value.join(",");
2467
+ }
2468
+ return String(value);
2469
+ }
2470
+ function buildGatewayCondition(operator, column, value) {
2471
+ const condition = { operator };
2472
+ if (column) {
2473
+ condition.column = column;
2474
+ if (operator === "eq") {
2475
+ condition.eq_column = column;
1932
2476
  }
1933
- };
2477
+ }
2478
+ if (value !== void 0) {
2479
+ condition.value = value;
2480
+ if (operator === "eq") {
2481
+ condition.eq_value = value;
2482
+ }
2483
+ }
2484
+ if (operator === "eq" && column && value !== void 0 && shouldUseUuidTextComparison(column, value)) {
2485
+ condition.column_cast = "text";
2486
+ condition.eq_column_cast = "text";
2487
+ }
2488
+ return condition;
1934
2489
  }
1935
- function createRpcBuilder(functionName, args, baseOptions, client) {
1936
- const state = {
1937
- filters: []
1938
- };
1939
- let selectedColumns;
1940
- let selectedOptions;
1941
- let promise = null;
1942
- const executeRpc = async (columns, options) => {
1943
- const mergedOptions = mergeOptions(baseOptions, options);
1944
- const payload = {
1945
- function: functionName,
1946
- args,
1947
- schema: mergedOptions?.schema,
1948
- select: toRpcSelect(columns),
1949
- filters: state.filters.length ? [...state.filters] : void 0,
1950
- count: mergedOptions?.count,
1951
- head: mergedOptions?.head,
1952
- limit: state.limit,
1953
- offset: state.offset,
1954
- order: state.order
1955
- };
1956
- const response = await client.rpcGateway(payload, mergedOptions);
1957
- return formatResult(response);
1958
- };
1959
- const run = (columns, options) => {
1960
- const payloadColumns = columns ?? selectedColumns;
1961
- const payloadOptions = options ?? selectedOptions;
1962
- if (!promise) {
1963
- promise = executeRpc(payloadColumns, payloadOptions);
2490
+ function compileRelationToken(key, node) {
2491
+ const nested = compileSelectShape(node.select);
2492
+ const propertyKey = normalizeIdentifier(key, "select relation key");
2493
+ const relationToken = normalizeIdentifier(node.via ?? propertyKey, "select relation token");
2494
+ const alias = node.as?.trim() || (relationToken !== propertyKey ? propertyKey : "");
2495
+ const prefix = alias ? `${alias}:` : "";
2496
+ return `${prefix}${relationToken}(${nested})`;
2497
+ }
2498
+ function compileSelectShape(select) {
2499
+ if (!isRecord5(select)) {
2500
+ throw new Error("findMany select must be an object");
2501
+ }
2502
+ const tokens = [];
2503
+ for (const [rawKey, rawValue] of Object.entries(select)) {
2504
+ if (rawValue === void 0) {
2505
+ continue;
1964
2506
  }
1965
- return promise;
1966
- };
1967
- const builder = {};
1968
- const filterMethods = createRpcFilterMethods(state.filters, builder);
1969
- Object.assign(builder, filterMethods, {
1970
- select(columns = selectedColumns, options) {
1971
- selectedColumns = columns;
1972
- selectedOptions = options ?? selectedOptions;
1973
- return run(columns, options);
1974
- },
1975
- async single(columns, options) {
1976
- const result = await run(columns, options);
1977
- return toSingleResult(result);
1978
- },
1979
- maybeSingle(columns, options) {
1980
- return builder.single(columns, options);
1981
- },
1982
- order(column, options) {
1983
- state.order = { column, ascending: options?.ascending ?? true };
1984
- return builder;
1985
- },
1986
- limit(count) {
1987
- state.limit = count;
1988
- return builder;
1989
- },
1990
- offset(count) {
1991
- state.offset = count;
1992
- return builder;
1993
- },
1994
- range(from, to) {
1995
- state.offset = from;
1996
- state.limit = to - from + 1;
1997
- return builder;
1998
- },
1999
- then(onfulfilled, onrejected) {
2000
- return run(selectedColumns, selectedOptions).then(onfulfilled, onrejected);
2001
- },
2002
- catch(onrejected) {
2003
- return run(selectedColumns, selectedOptions).catch(onrejected);
2004
- },
2005
- finally(onfinally) {
2006
- return run(selectedColumns, selectedOptions).finally(onfinally);
2507
+ if (rawValue === true) {
2508
+ tokens.push(normalizeIdentifier(rawKey, "select column"));
2509
+ continue;
2007
2510
  }
2008
- });
2009
- return builder;
2511
+ if (isRelationSelectNode(rawValue)) {
2512
+ tokens.push(compileRelationToken(rawKey, rawValue));
2513
+ continue;
2514
+ }
2515
+ throw new Error(`Unsupported select node for "${rawKey}"`);
2516
+ }
2517
+ if (tokens.length === 0) {
2518
+ throw new Error("findMany select requires at least one field");
2519
+ }
2520
+ return tokens.join(",");
2010
2521
  }
2011
- function createTableBuilder(tableName, client) {
2012
- const state = {
2013
- conditions: []
2014
- };
2015
- const addCondition = (operator, column, value, hints) => {
2016
- const condition = { operator };
2017
- if (column) {
2018
- condition.column = column;
2019
- if (operator === "eq") {
2020
- condition.eq_column = column;
2021
- }
2522
+ function compileColumnWhere(column, input) {
2523
+ const normalizedColumn = normalizeIdentifier(column, "where column");
2524
+ if (!isRecord5(input)) {
2525
+ return [buildGatewayCondition("eq", normalizedColumn, input)];
2526
+ }
2527
+ const conditions = [];
2528
+ for (const [rawOperator, rawValue] of Object.entries(input)) {
2529
+ if (rawValue === void 0) {
2530
+ continue;
2022
2531
  }
2023
- if (value !== void 0) {
2024
- condition.value = value;
2025
- if (operator === "eq") {
2026
- condition.eq_value = value;
2027
- }
2532
+ if (!FILTER_OPERATORS.has(rawOperator)) {
2533
+ throw new Error(`Unsupported where operator "${rawOperator}" on "${normalizedColumn}"`);
2028
2534
  }
2029
- if (hints?.valueCast) {
2030
- condition.value_cast = hints.valueCast;
2031
- if (operator === "eq") {
2032
- condition.eq_value_cast = hints.valueCast;
2535
+ if ((rawOperator === "in" || rawOperator === "contains" || rawOperator === "containedBy") && !Array.isArray(rawValue)) {
2536
+ throw new Error(`where.${normalizedColumn}.${rawOperator} requires an array value`);
2537
+ }
2538
+ conditions.push(
2539
+ buildGatewayCondition(
2540
+ rawOperator,
2541
+ normalizedColumn,
2542
+ rawValue
2543
+ )
2544
+ );
2545
+ }
2546
+ if (conditions.length === 0) {
2547
+ throw new Error(`where.${normalizedColumn} requires at least one operator`);
2548
+ }
2549
+ return conditions;
2550
+ }
2551
+ function compileBooleanExpressionTerms(clause, label) {
2552
+ if (!isRecord5(clause)) {
2553
+ throw new Error(`findMany where.${label} clauses must be objects`);
2554
+ }
2555
+ const entries = Object.entries(clause).filter(([, value]) => value !== void 0);
2556
+ if (entries.length !== 1) {
2557
+ throw new Error(`findMany where.${label} clauses must target exactly one column`);
2558
+ }
2559
+ const [rawColumn, rawValue] = entries[0];
2560
+ const column = normalizeIdentifier(rawColumn, `where.${label} column`);
2561
+ if (!isRecord5(rawValue)) {
2562
+ return [`${column}.eq.${stringifyFilterValue(rawValue)}`];
2563
+ }
2564
+ const operatorEntries = Object.entries(rawValue).filter(([, value]) => value !== void 0);
2565
+ if (operatorEntries.length === 0) {
2566
+ throw new Error(`findMany where.${label}.${column} requires at least one operator`);
2567
+ }
2568
+ if (label === "not" && operatorEntries.length > 1) {
2569
+ throw new Error("findMany where.not only supports a single lossless operator expression");
2570
+ }
2571
+ return operatorEntries.map(([rawOperator, rawOperand]) => {
2572
+ if (!BOOLEAN_SAFE_OPERATORS.has(rawOperator)) {
2573
+ throw new Error(`findMany where.${label} only supports lossless scalar operators`);
2574
+ }
2575
+ if (Array.isArray(rawOperand)) {
2576
+ throw new Error(`findMany where.${label} does not support array-valued operators`);
2577
+ }
2578
+ return `${column}.${rawOperator}.${stringifyFilterValue(rawOperand)}`;
2579
+ });
2580
+ }
2581
+ function compileWhere(where) {
2582
+ if (where === void 0) {
2583
+ return void 0;
2584
+ }
2585
+ if (!isRecord5(where)) {
2586
+ throw new Error("findMany where must be an object");
2587
+ }
2588
+ const conditions = [];
2589
+ for (const [rawKey, rawValue] of Object.entries(where)) {
2590
+ if (rawValue === void 0) {
2591
+ continue;
2592
+ }
2593
+ if (rawKey === "or") {
2594
+ if (!Array.isArray(rawValue) || rawValue.length === 0) {
2595
+ throw new Error("findMany where.or must be a non-empty array");
2033
2596
  }
2597
+ const expressions = rawValue.flatMap(
2598
+ (value) => compileBooleanExpressionTerms(value, "or")
2599
+ );
2600
+ conditions.push(buildGatewayCondition("or", void 0, expressions.join(",")));
2601
+ continue;
2034
2602
  }
2035
- if (hints?.columnCast) {
2036
- condition.column_cast = hints.columnCast;
2037
- if (operator === "eq") {
2038
- condition.eq_column_cast = hints.columnCast;
2603
+ if (rawKey === "not") {
2604
+ const expressions = compileBooleanExpressionTerms(rawValue, "not");
2605
+ if (expressions.length !== 1) {
2606
+ throw new Error("findMany where.not must compile to exactly one lossless expression");
2039
2607
  }
2608
+ conditions.push(buildGatewayCondition("not", void 0, expressions[0]));
2609
+ continue;
2040
2610
  }
2041
- state.conditions.push(condition);
2042
- };
2043
- const builder = {};
2044
- const filterMethods = createFilterMethods(
2045
- state,
2046
- addCondition,
2047
- builder
2048
- );
2049
- const runSelect = async (columns = DEFAULT_COLUMNS, options) => {
2050
- const resolvedTableName = resolveTableNameForCall(tableName, options?.schema);
2051
- const conditions = state.conditions.length ? state.conditions.map((condition) => ({ ...condition })) : void 0;
2052
- const hasTypedEqualityComparison = conditions?.some(
2053
- (condition) => condition.operator === "eq" && (condition.value_cast !== void 0 || condition.column_cast !== void 0)
2054
- ) ?? false;
2055
- if (hasTypedEqualityComparison && !options?.head && !options?.count && conditions) {
2056
- const query = buildTypedSelectQuery({
2057
- tableName: resolvedTableName,
2058
- columns,
2059
- conditions,
2060
- limit: state.limit,
2061
- offset: state.offset,
2062
- currentPage: state.currentPage,
2063
- pageSize: state.pageSize,
2064
- order: state.order
2065
- });
2066
- if (query) {
2067
- const queryResponse = await client.queryGateway({ query }, options);
2068
- return formatResult(queryResponse);
2069
- }
2611
+ conditions.push(...compileColumnWhere(rawKey, rawValue));
2612
+ }
2613
+ return conditions.length > 0 ? conditions : void 0;
2614
+ }
2615
+ function resolveOrderDirection(input) {
2616
+ if (typeof input === "boolean") {
2617
+ return input === false ? "descending" : "ascending";
2618
+ }
2619
+ if (typeof input === "string") {
2620
+ const normalized = input.trim().toLowerCase();
2621
+ if (normalized === "asc" || normalized === "ascending") {
2622
+ return "ascending";
2070
2623
  }
2071
- const payload = {
2072
- table_name: resolvedTableName,
2073
- columns,
2074
- conditions,
2075
- limit: state.limit,
2076
- offset: state.offset,
2077
- current_page: state.currentPage,
2078
- page_size: state.pageSize,
2079
- total_pages: state.totalPages,
2080
- sort_by: state.order,
2081
- strip_nulls: options?.stripNulls ?? true,
2082
- count: options?.count,
2083
- head: options?.head
2624
+ if (normalized === "desc" || normalized === "descending") {
2625
+ return "descending";
2626
+ }
2627
+ throw new Error(`Unsupported orderBy direction "${input}"`);
2628
+ }
2629
+ return input.ascending === false ? "descending" : "ascending";
2630
+ }
2631
+ function compileOrderBy(orderBy) {
2632
+ if (orderBy === void 0) {
2633
+ return void 0;
2634
+ }
2635
+ if (!isRecord5(orderBy)) {
2636
+ throw new Error("findMany orderBy must be an object");
2637
+ }
2638
+ if ("column" in orderBy) {
2639
+ return {
2640
+ field: normalizeIdentifier(String(orderBy.column), "orderBy column"),
2641
+ direction: orderBy.ascending === false ? "descending" : "ascending"
2084
2642
  };
2085
- const response = await client.fetchGateway(payload, options);
2086
- return formatResult(response);
2643
+ }
2644
+ const entries = Object.entries(orderBy).filter(([, value]) => value !== void 0);
2645
+ if (entries.length === 0) {
2646
+ return void 0;
2647
+ }
2648
+ if (entries.length > 1) {
2649
+ throw new Error("findMany orderBy only supports a single column in v1");
2650
+ }
2651
+ const [column, input] = entries[0];
2652
+ return {
2653
+ field: normalizeIdentifier(column, "orderBy column"),
2654
+ direction: resolveOrderDirection(input)
2087
2655
  };
2088
- const createSelectChain = (columns, options) => {
2089
- const chain = {};
2090
- const filterMethods2 = createFilterMethods(state, addCondition, chain);
2091
- Object.assign(chain, filterMethods2, {
2092
- async single(cols, opts) {
2093
- const r = await runSelect(cols ?? columns, opts ?? options);
2094
- return toSingleResult(r);
2095
- },
2096
- maybeSingle(cols, opts) {
2097
- return chain.single(cols, opts);
2098
- },
2099
- then(onfulfilled, onrejected) {
2100
- return runSelect(columns, options).then(onfulfilled, onrejected);
2101
- },
2102
- catch(onrejected) {
2103
- return runSelect(columns, options).catch(onrejected);
2104
- },
2105
- finally(onfinally) {
2106
- return runSelect(columns, options).finally(onfinally);
2107
- }
2108
- });
2109
- return chain;
2656
+ }
2657
+
2658
+ // src/client.ts
2659
+ var DEFAULT_COLUMNS = "*";
2660
+ var SAFE_CAST_PATTERN = /^[a-z_][a-z0-9_]*(?:\[\])?$/i;
2661
+ var ATHENA_NORMALIZED_ERROR_KEY = "__athenaNormalizedError";
2662
+ var QUERY_TRACE_STACK_SKIP_PATTERNS = [
2663
+ "src\\client.ts",
2664
+ "src/client.ts",
2665
+ "dist\\client.",
2666
+ "dist/client.",
2667
+ "node_modules\\@xylex-group\\athena",
2668
+ "node_modules/@xylex-group/athena",
2669
+ "node:internal",
2670
+ "internal/process"
2671
+ ];
2672
+ function canUseFindManyAstTransport(state) {
2673
+ return state.conditions.length === 0 && state.offset === void 0 && state.currentPage === void 0 && state.pageSize === void 0 && state.totalPages === void 0;
2674
+ }
2675
+ function toFindManyAstOrder(order) {
2676
+ if (!order) {
2677
+ return void 0;
2678
+ }
2679
+ return {
2680
+ column: order.field,
2681
+ ascending: order.direction !== "descending"
2110
2682
  };
2111
- Object.assign(builder, filterMethods, {
2112
- reset() {
2113
- state.conditions = [];
2114
- state.limit = void 0;
2115
- state.offset = void 0;
2116
- state.order = void 0;
2117
- state.currentPage = void 0;
2118
- state.pageSize = void 0;
2119
- state.totalPages = void 0;
2120
- return builder;
2121
- },
2122
- select(columns = DEFAULT_COLUMNS, options) {
2123
- return createSelectChain(columns, options);
2124
- },
2125
- insert(values, options) {
2126
- if (Array.isArray(values)) {
2127
- const executeInsertMany = async (columns, selectOptions) => {
2128
- const mergedOptions = mergeOptions(options, selectOptions);
2129
- const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2130
- const payload = {
2131
- table_name: resolvedTableName,
2132
- insert_body: asAthenaJsonObjectArray(values)
2133
- };
2134
- if (columns) payload.columns = columns;
2135
- if (mergedOptions?.count) payload.count = mergedOptions.count;
2136
- if (mergedOptions?.head) payload.head = mergedOptions.head;
2137
- if (mergedOptions?.defaultToNull !== void 0) {
2138
- payload.default_to_null = mergedOptions.defaultToNull;
2139
- }
2140
- const response = await client.insertGateway(payload, mergedOptions);
2141
- return formatResult(response);
2142
- };
2143
- return createMutationQuery(executeInsertMany);
2144
- }
2145
- const executeInsertOne = async (columns, selectOptions) => {
2146
- const mergedOptions = mergeOptions(options, selectOptions);
2147
- const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2148
- const payload = {
2149
- table_name: resolvedTableName,
2150
- insert_body: asAthenaJsonObject(values)
2151
- };
2152
- if (columns) payload.columns = columns;
2153
- if (mergedOptions?.count) payload.count = mergedOptions.count;
2154
- if (mergedOptions?.head) payload.head = mergedOptions.head;
2155
- if (mergedOptions?.defaultToNull !== void 0) {
2156
- payload.default_to_null = mergedOptions.defaultToNull;
2157
- }
2158
- const response = await client.insertGateway(payload, mergedOptions);
2159
- return formatResult(response);
2160
- };
2161
- return createMutationQuery(executeInsertOne);
2162
- },
2163
- upsert(values, options) {
2164
- if (Array.isArray(values)) {
2165
- const executeUpsertMany = async (columns, selectOptions) => {
2166
- const mergedOptions = mergeOptions(options, selectOptions);
2167
- const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2168
- const payload = {
2169
- table_name: resolvedTableName,
2170
- insert_body: asAthenaJsonObjectArray(values),
2171
- update_body: options?.updateBody ? asAthenaJsonObject(options.updateBody) : void 0
2172
- };
2173
- if (columns) payload.columns = columns;
2174
- if (options?.onConflict) payload.on_conflict = options.onConflict;
2175
- if (mergedOptions?.count) payload.count = mergedOptions.count;
2176
- if (mergedOptions?.head) payload.head = mergedOptions.head;
2177
- if (mergedOptions?.defaultToNull !== void 0) {
2178
- payload.default_to_null = mergedOptions.defaultToNull;
2179
- }
2180
- const response = await client.insertGateway(payload, mergedOptions);
2181
- return formatResult(response);
2182
- };
2183
- return createMutationQuery(executeUpsertMany);
2184
- }
2185
- const executeUpsertOne = async (columns, selectOptions) => {
2186
- const mergedOptions = mergeOptions(options, selectOptions);
2187
- const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2188
- const payload = {
2189
- table_name: resolvedTableName,
2190
- insert_body: asAthenaJsonObject(values),
2191
- update_body: options?.updateBody ? asAthenaJsonObject(options.updateBody) : void 0
2192
- };
2193
- if (columns) payload.columns = columns;
2194
- if (options?.onConflict) payload.on_conflict = options.onConflict;
2195
- if (mergedOptions?.count) payload.count = mergedOptions.count;
2196
- if (mergedOptions?.head) payload.head = mergedOptions.head;
2197
- if (mergedOptions?.defaultToNull !== void 0) {
2198
- payload.default_to_null = mergedOptions.defaultToNull;
2199
- }
2200
- const response = await client.insertGateway(payload, mergedOptions);
2201
- return formatResult(response);
2202
- };
2203
- return createMutationQuery(executeUpsertOne);
2204
- },
2205
- update(values, options) {
2206
- const executeUpdate = async (columns, selectOptions) => {
2207
- const filters = state.conditions.length ? [...state.conditions] : void 0;
2208
- const mergedOptions = mergeOptions(options, selectOptions);
2209
- const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2210
- const payload = {
2211
- table_name: resolvedTableName,
2212
- set: asAthenaJsonObject(values),
2213
- conditions: filters,
2214
- strip_nulls: mergedOptions?.stripNulls ?? true
2215
- };
2216
- if (state.order) payload.sort_by = state.order;
2217
- if (state.currentPage !== void 0) payload.current_page = state.currentPage;
2218
- if (state.pageSize !== void 0) payload.page_size = state.pageSize;
2219
- if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
2220
- if (columns) payload.columns = columns;
2221
- const response = await client.updateGateway(payload, mergedOptions);
2222
- return formatResult(response);
2223
- };
2224
- const mutation = createMutationQuery(executeUpdate, null);
2225
- const updateChain = {};
2226
- const filterMethods2 = createFilterMethods(state, addCondition, updateChain);
2227
- Object.assign(updateChain, filterMethods2, mutation);
2228
- return updateChain;
2229
- },
2230
- delete(options) {
2231
- const filters = state.conditions.length ? [...state.conditions] : void 0;
2232
- const resourceId = options?.resourceId ?? getResourceId(state);
2233
- if (!resourceId && !filters?.length) {
2234
- throw new Error('delete requires a resource_id either via eq("resource_id", ...) or options.resourceId');
2235
- }
2236
- const executeDelete = async (columns, selectOptions) => {
2237
- const mergedOptions = mergeOptions(options, selectOptions);
2238
- const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
2239
- const payload = {
2240
- table_name: resolvedTableName,
2241
- resource_id: resourceId,
2242
- conditions: filters
2243
- };
2244
- if (state.order) payload.sort_by = state.order;
2245
- if (state.currentPage !== void 0) payload.current_page = state.currentPage;
2246
- if (state.pageSize !== void 0) payload.page_size = state.pageSize;
2247
- if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
2248
- if (columns) payload.columns = columns;
2249
- const response = await client.deleteGateway(payload, mergedOptions);
2250
- return formatResult(response);
2251
- };
2252
- return createMutationQuery(executeDelete, null);
2253
- },
2254
- async single(columns, options) {
2255
- const response = await builder.select(columns, options);
2256
- return toSingleResult(response);
2257
- },
2258
- async maybeSingle(columns, options) {
2259
- return builder.single(columns, options);
2260
- }
2261
- });
2262
- return builder;
2263
2683
  }
2264
- function createQueryBuilder(client) {
2265
- return async function query(query, options) {
2266
- const normalizedQuery = query.trim();
2267
- if (!normalizedQuery) {
2268
- throw new Error("query requires a non-empty string");
2269
- }
2270
- const response = await client.queryGateway({ query: normalizedQuery }, options);
2271
- return formatResult(response);
2684
+ function formatResult(response) {
2685
+ const result = {
2686
+ data: response.data ?? null,
2687
+ error: null,
2688
+ errorDetails: response.errorDetails ?? null,
2689
+ status: response.status,
2690
+ statusText: response.statusText ?? null,
2691
+ raw: response.raw
2272
2692
  };
2693
+ if (response.count !== void 0) {
2694
+ result.count = response.count;
2695
+ }
2696
+ return result;
2273
2697
  }
2274
- function createClientFromConfig(config) {
2275
- const gateway = createAthenaGatewayClient({
2276
- baseUrl: config.baseUrl,
2277
- apiKey: config.apiKey,
2278
- client: config.client,
2279
- backend: config.backend,
2280
- headers: config.headers
2698
+ function attachNormalizedError(result, normalizedError) {
2699
+ Object.defineProperty(result, ATHENA_NORMALIZED_ERROR_KEY, {
2700
+ value: normalizedError,
2701
+ enumerable: false,
2702
+ configurable: true,
2703
+ writable: false
2281
2704
  });
2282
- const auth = createAuthClient(config.auth);
2283
- return {
2284
- from(table) {
2285
- return createTableBuilder(table, gateway);
2286
- },
2287
- rpc(fn, args, options) {
2288
- const normalizedFn = fn.trim();
2289
- if (!normalizedFn) {
2290
- throw new Error("rpc requires a function name");
2291
- }
2292
- return createRpcBuilder(
2293
- normalizedFn,
2294
- args,
2295
- options,
2296
- gateway
2297
- );
2298
- },
2299
- query: createQueryBuilder(gateway),
2300
- auth: auth.auth
2705
+ }
2706
+ function createResultFormatter(experimental) {
2707
+ return (response, context) => {
2708
+ const result = formatResult(response);
2709
+ if (response.error == null && response.errorDetails == null) {
2710
+ return result;
2711
+ }
2712
+ const normalizedError = normalizeAthenaError(
2713
+ {
2714
+ ...result,
2715
+ error: response.error ?? response.errorDetails?.message ?? null
2716
+ },
2717
+ context
2718
+ );
2719
+ result.error = createResultError(response, result, normalizedError);
2720
+ attachNormalizedError(result, normalizedError);
2721
+ return result;
2301
2722
  };
2302
2723
  }
2303
- var DEFAULT_BACKEND = { type: "athena" };
2304
- function toBackendConfig(b) {
2305
- if (!b) return DEFAULT_BACKEND;
2306
- return typeof b === "string" ? { type: b } : b;
2724
+ function isRecord6(value) {
2725
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2307
2726
  }
2308
- var AthenaClientBuilderImpl = class {
2309
- baseUrl;
2310
- apiKey;
2311
- backendConfig = DEFAULT_BACKEND;
2312
- clientName;
2313
- defaultHeaders;
2314
- isHealthTrackingEnabled = false;
2315
- url(url) {
2316
- this.baseUrl = url;
2317
- return this;
2727
+ function firstNonEmptyString2(...values) {
2728
+ for (const value of values) {
2729
+ if (typeof value === "string" && value.trim().length > 0) {
2730
+ return value.trim();
2731
+ }
2318
2732
  }
2319
- key(apiKey) {
2320
- this.apiKey = apiKey;
2321
- return this;
2733
+ return void 0;
2734
+ }
2735
+ function resolveStructuredErrorPayload2(raw) {
2736
+ if (!isRecord6(raw)) return null;
2737
+ return isRecord6(raw.error) ? raw.error : raw;
2738
+ }
2739
+ function resolveStructuredErrorDetails(payload, message) {
2740
+ if (!payload || !("details" in payload)) {
2741
+ return null;
2322
2742
  }
2323
- backend(backend) {
2324
- this.backendConfig = toBackendConfig(backend);
2325
- return this;
2326
- }
2327
- client(clientName) {
2328
- this.clientName = clientName;
2329
- return this;
2743
+ const details = payload.details;
2744
+ if (details == null) {
2745
+ return null;
2330
2746
  }
2331
- headers(headers) {
2332
- this.defaultHeaders = headers;
2333
- return this;
2747
+ if (typeof details === "string" && details.trim() === message.trim()) {
2748
+ return null;
2334
2749
  }
2335
- healthTracking(enabled) {
2336
- this.isHealthTrackingEnabled = enabled;
2337
- return this;
2750
+ return details;
2751
+ }
2752
+ function createResultError(response, result, normalized) {
2753
+ const rawRecord = isRecord6(response.raw) ? response.raw : null;
2754
+ const payload = resolveStructuredErrorPayload2(response.raw);
2755
+ const message = firstNonEmptyString2(
2756
+ response.error,
2757
+ payload?.message,
2758
+ payload?.error,
2759
+ payload?.details,
2760
+ response.errorDetails?.message,
2761
+ normalized.message
2762
+ ) ?? normalized.message;
2763
+ const statusText = firstNonEmptyString2(response.statusText, rawRecord?.statusText) ?? null;
2764
+ const hint = firstNonEmptyString2(payload?.hint, response.errorDetails?.hint) ?? null;
2765
+ const code = firstNonEmptyString2(payload?.code) ?? normalized.code;
2766
+ const details = resolveStructuredErrorDetails(payload, message) ?? response.errorDetails?.cause ?? null;
2767
+ return {
2768
+ message,
2769
+ code,
2770
+ athenaCode: normalized.code,
2771
+ gatewayCode: response.errorDetails?.code ?? null,
2772
+ kind: normalized.kind,
2773
+ category: normalized.category,
2774
+ retryable: normalized.retryable,
2775
+ details,
2776
+ hint,
2777
+ status: result.status,
2778
+ statusText,
2779
+ constraint: normalized.constraint,
2780
+ table: normalized.table,
2781
+ operation: normalized.operation,
2782
+ endpoint: response.errorDetails?.endpoint,
2783
+ method: response.errorDetails?.method,
2784
+ requestId: response.errorDetails?.requestId,
2785
+ cause: response.errorDetails?.cause,
2786
+ raw: result.raw
2787
+ };
2788
+ }
2789
+ function parseQueryTraceCallsiteFrame(frame) {
2790
+ const trimmed = frame.trim();
2791
+ if (!trimmed) {
2792
+ return null;
2338
2793
  }
2339
- build() {
2340
- if (!this.baseUrl || !this.apiKey) {
2341
- throw new Error("AthenaClient requires url and key; call .url() and .key() before .build()");
2342
- }
2343
- return createClientFromConfig({
2344
- baseUrl: this.baseUrl,
2345
- apiKey: this.apiKey,
2346
- client: this.clientName,
2347
- backend: this.backendConfig,
2348
- headers: this.defaultHeaders,
2349
- healthTracking: this.isHealthTrackingEnabled
2350
- });
2794
+ let body = trimmed.replace(/^at\s+/, "");
2795
+ if (body.startsWith("async ")) {
2796
+ body = body.slice(6);
2351
2797
  }
2352
- };
2353
- var AthenaClient = class _AthenaClient {
2354
- /** Create a fluent builder for a strongly-typed Athena SDK client. */
2355
- static builder() {
2356
- return new AthenaClientBuilderImpl();
2798
+ let functionName;
2799
+ let location = body;
2800
+ const wrappedMatch = body.match(/^(.*?)\s+\((.*)\)$/);
2801
+ if (wrappedMatch) {
2802
+ functionName = wrappedMatch[1].trim() || void 0;
2803
+ location = wrappedMatch[2].trim();
2357
2804
  }
2358
- /** Build a client from process environment variables. */
2359
- static fromEnvironment() {
2360
- const url = process.env.ATHENA_URL ?? process.env.ATHENA_GATEWAY_URL;
2361
- const key = process.env.ATHENA_API_KEY ?? process.env.ATHENA_GATEWAY_API_KEY;
2362
- if (!url || !key) {
2363
- throw new Error(
2364
- "ATHENA_URL and ATHENA_API_KEY (or ATHENA_GATEWAY_URL and ATHENA_GATEWAY_API_KEY) are required"
2365
- );
2366
- }
2367
- return _AthenaClient.builder().url(url).key(key).build();
2805
+ const locationMatch = location.match(/^(.*):(\d+):(\d+)$/);
2806
+ if (!locationMatch) {
2807
+ return null;
2368
2808
  }
2369
- };
2370
- function createClient(url, apiKey, options) {
2371
- const b = AthenaClient.builder().url(url).key(apiKey).backend(toBackendConfig(options?.backend));
2372
- if (options?.client) b.client(options.client);
2373
- if (options?.headers && Object.keys(options.headers).length > 0) b.headers(options.headers);
2374
- const client = b.build();
2375
- if (options?.auth) {
2376
- client.auth = createAuthClient(options.auth).auth;
2809
+ const filePath = locationMatch[1].replace(/^file:\/\//, "");
2810
+ const line = Number(locationMatch[2]);
2811
+ const column = Number(locationMatch[3]);
2812
+ if (!Number.isFinite(line) || !Number.isFinite(column)) {
2813
+ return null;
2377
2814
  }
2378
- return client;
2815
+ const normalizedPath = filePath.replace(/\\/g, "/");
2816
+ const fileName = normalizedPath.split("/").at(-1) ?? filePath;
2817
+ return {
2818
+ filePath,
2819
+ fileName,
2820
+ line,
2821
+ column,
2822
+ frame: trimmed,
2823
+ functionName
2824
+ };
2379
2825
  }
2380
-
2381
- // src/gateway/types.ts
2382
- var Backend = {
2383
- Athena: { type: "athena" },
2384
- Postgrest: { type: "postgrest" },
2385
- PostgreSQL: { type: "postgresql" },
2386
- ScyllaDB: { type: "scylladb" }
2387
- };
2388
-
2389
- // src/auxiliaries.ts
2390
- var AthenaErrorKind = {
2391
- UniqueViolation: "unique_violation",
2392
- NotFound: "not_found",
2393
- Validation: "validation",
2394
- Auth: "auth",
2395
- RateLimit: "rate_limit",
2396
- Transient: "transient",
2397
- Unknown: "unknown"
2398
- };
2399
- var AthenaErrorCode = {
2400
- UniqueViolation: "UNIQUE_VIOLATION",
2401
- NotFound: "NOT_FOUND",
2402
- ValidationFailed: "VALIDATION_FAILED",
2403
- AuthUnauthorized: "AUTH_UNAUTHORIZED",
2404
- AuthForbidden: "AUTH_FORBIDDEN",
2405
- RateLimited: "RATE_LIMITED",
2406
- NetworkUnavailable: "NETWORK_UNAVAILABLE",
2407
- TransientFailure: "TRANSIENT_FAILURE",
2408
- HttpFailure: "HTTP_FAILURE",
2409
- Unknown: "UNKNOWN"
2410
- };
2411
- var AthenaErrorCategory = {
2412
- Transport: "transport",
2413
- Client: "client",
2414
- Server: "server",
2415
- Database: "database",
2416
- Unknown: "unknown"
2417
- };
2418
- function parseBooleanFlag(rawValue, fallback) {
2419
- if (!rawValue) return fallback;
2420
- const normalized = rawValue.trim().toLowerCase();
2421
- if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
2422
- return true;
2423
- }
2424
- if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
2425
- return false;
2826
+ function captureQueryTraceCallsite() {
2827
+ const stack = new Error().stack;
2828
+ if (!stack) return null;
2829
+ const frames = stack.split("\n").slice(2).map((frame) => frame.trim()).filter(Boolean);
2830
+ for (const frame of frames) {
2831
+ if (QUERY_TRACE_STACK_SKIP_PATTERNS.some((pattern) => frame.includes(pattern))) {
2832
+ continue;
2833
+ }
2834
+ const callsite = parseQueryTraceCallsiteFrame(frame);
2835
+ if (callsite) return callsite;
2426
2836
  }
2427
- return fallback;
2837
+ const fallback = frames.find((frame) => !frame.includes("captureQueryTraceCallsite"));
2838
+ return fallback ? parseQueryTraceCallsiteFrame(fallback) : null;
2428
2839
  }
2429
- var AthenaError = class extends Error {
2430
- code;
2431
- kind;
2432
- category;
2433
- status;
2434
- retryable;
2435
- requestId;
2436
- context;
2437
- raw;
2438
- constructor(input) {
2439
- super(input.message);
2440
- this.name = "AthenaError";
2441
- this.code = input.code;
2442
- this.kind = input.kind;
2443
- this.category = input.category;
2444
- this.status = input.status;
2445
- this.retryable = input.retryable ?? false;
2446
- this.requestId = input.requestId;
2447
- this.context = input.context;
2448
- this.raw = input.raw;
2840
+ function defaultQueryTraceLogger(event) {
2841
+ const target = event.table ?? event.functionName ?? "gateway";
2842
+ const outcomeState = event.outcome?.error ? "error" : "ok";
2843
+ const banner = `[athena-js][trace] ${event.operation.toUpperCase()} ${event.endpoint} ${target} ${event.durationMs}ms ${outcomeState}`;
2844
+ console.info(banner, event);
2845
+ }
2846
+ function captureTraceCallsite(tracer) {
2847
+ return tracer?.captureCallsite() ?? null;
2848
+ }
2849
+ function createTraceCallsiteStore(tracer, initialCallsite) {
2850
+ let storedCallsite = initialCallsite ?? void 0;
2851
+ return {
2852
+ resolve(callsite) {
2853
+ if (callsite) {
2854
+ storedCallsite = callsite;
2855
+ return callsite;
2856
+ }
2857
+ if (storedCallsite !== void 0) {
2858
+ return storedCallsite;
2859
+ }
2860
+ const capturedCallsite = captureTraceCallsite(tracer);
2861
+ if (capturedCallsite) {
2862
+ storedCallsite = capturedCallsite;
2863
+ }
2864
+ return capturedCallsite;
2865
+ }
2866
+ };
2867
+ }
2868
+ function createQueryTracer(experimental) {
2869
+ const traceOption = experimental?.traceQueries;
2870
+ if (!traceOption) {
2871
+ return void 0;
2449
2872
  }
2450
- };
2451
- function isRecord3(value) {
2452
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2873
+ const logger = typeof traceOption === "object" && traceOption.logger ? traceOption.logger : defaultQueryTraceLogger;
2874
+ const emit = (event) => {
2875
+ try {
2876
+ logger(event);
2877
+ } catch (error) {
2878
+ console.warn("[athena-js][trace] logger failed", error);
2879
+ }
2880
+ };
2881
+ return {
2882
+ captureCallsite: captureQueryTraceCallsite,
2883
+ publishSuccess(context, result, durationMs, callsite) {
2884
+ emit({
2885
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2886
+ durationMs,
2887
+ operation: context.operation,
2888
+ endpoint: context.endpoint,
2889
+ table: context.table,
2890
+ functionName: context.functionName,
2891
+ sql: context.sql,
2892
+ payload: context.payload,
2893
+ options: context.options,
2894
+ callsite,
2895
+ outcome: {
2896
+ status: result.status,
2897
+ error: result.error,
2898
+ errorDetails: result.errorDetails ?? null,
2899
+ count: result.count ?? null,
2900
+ data: result.data,
2901
+ raw: result.raw
2902
+ }
2903
+ });
2904
+ },
2905
+ publishFailure(context, error, durationMs, callsite) {
2906
+ emit({
2907
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2908
+ durationMs,
2909
+ operation: context.operation,
2910
+ endpoint: context.endpoint,
2911
+ table: context.table,
2912
+ functionName: context.functionName,
2913
+ sql: context.sql,
2914
+ payload: context.payload,
2915
+ options: context.options,
2916
+ callsite,
2917
+ thrownError: error
2918
+ });
2919
+ }
2920
+ };
2453
2921
  }
2454
- function safeStringify(value) {
2922
+ async function executeWithQueryTrace(tracer, context, runner, callsiteOverride) {
2923
+ if (!tracer) {
2924
+ return runner();
2925
+ }
2926
+ const callsite = callsiteOverride ?? tracer.captureCallsite();
2927
+ const startedAt = Date.now();
2455
2928
  try {
2456
- const serialized = JSON.stringify(value);
2457
- return serialized ?? String(value);
2458
- } catch {
2459
- return "[unserializable]";
2929
+ const result = await runner();
2930
+ tracer.publishSuccess(context, result, Date.now() - startedAt, callsite);
2931
+ return result;
2932
+ } catch (error) {
2933
+ tracer.publishFailure(context, error, Date.now() - startedAt, callsite);
2934
+ throw error;
2460
2935
  }
2461
2936
  }
2462
- function contextHint(context) {
2463
- if (!context?.identity) return void 0;
2464
- const identity = typeof context.identity === "string" ? context.identity : safeStringify(context.identity);
2465
- return `Identity: ${identity}`;
2937
+ function toSingleResult(response) {
2938
+ const payload = response.data;
2939
+ const singleData = Array.isArray(payload) ? payload.length ? payload[0] : null : payload ?? null;
2940
+ return {
2941
+ ...response,
2942
+ data: singleData
2943
+ };
2466
2944
  }
2467
- function isAthenaResultLike(value) {
2468
- return isRecord3(value) && "status" in value && "error" in value && "data" in value && typeof value.status === "number";
2945
+ function mergeOptions(...options) {
2946
+ return options.reduce((acc, next) => {
2947
+ if (!next) return acc;
2948
+ return { ...acc, ...next };
2949
+ }, void 0);
2469
2950
  }
2470
- function operationFromDetails(details) {
2471
- if (!details?.endpoint) return void 0;
2472
- if (details.endpoint === "/gateway/fetch" || details.endpoint === "/gateway/query") return "select";
2473
- if (details.endpoint === "/gateway/insert") return "insert";
2474
- if (details.endpoint === "/gateway/update") return "update";
2475
- if (details.endpoint === "/gateway/delete") return "delete";
2476
- if (details.endpoint === "/gateway/rpc" || details.endpoint.startsWith("/rpc/")) return "rpc";
2477
- return void 0;
2951
+ function asAthenaJsonObject(value) {
2952
+ return value;
2478
2953
  }
2479
- function matchRegex(input, patterns) {
2480
- for (const pattern of patterns) {
2481
- const match = pattern.exec(input);
2482
- if (match?.[1]) return match[1];
2483
- }
2484
- return void 0;
2954
+ function asAthenaJsonObjectArray(values) {
2955
+ return values;
2485
2956
  }
2486
- function extractConstraint(message) {
2487
- return matchRegex(message, [
2488
- /unique constraint\s+["'`]([^"'`]+)["'`]/i,
2489
- /constraint\s+["'`]([^"'`]+)["'`]/i
2490
- ]);
2957
+ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS, tracer, initialCallsite) {
2958
+ let selectedColumns = defaultColumns === null ? void 0 : defaultColumns;
2959
+ let selectedOptions;
2960
+ let promise = null;
2961
+ const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
2962
+ const run = (columns, options, callsite) => {
2963
+ const payloadColumns = columns ?? selectedColumns;
2964
+ const payloadOptions = options ?? selectedOptions;
2965
+ if (!promise) {
2966
+ promise = executor(payloadColumns, payloadOptions, callsiteStore.resolve(callsite));
2967
+ }
2968
+ return promise;
2969
+ };
2970
+ const mutationQuery = {
2971
+ select(columns = selectedColumns, options) {
2972
+ selectedColumns = columns;
2973
+ selectedOptions = options ?? selectedOptions;
2974
+ return run(columns, options, captureTraceCallsite(tracer));
2975
+ },
2976
+ returning(columns = selectedColumns, options) {
2977
+ return mutationQuery.select(columns, options);
2978
+ },
2979
+ single(columns = selectedColumns, options) {
2980
+ selectedColumns = columns;
2981
+ selectedOptions = options ?? selectedOptions;
2982
+ return run(columns, options, captureTraceCallsite(tracer)).then(toSingleResult);
2983
+ },
2984
+ maybeSingle(columns = selectedColumns, options) {
2985
+ return mutationQuery.single(columns, options);
2986
+ },
2987
+ then(onfulfilled, onrejected) {
2988
+ return run(selectedColumns, selectedOptions).then(onfulfilled, onrejected);
2989
+ },
2990
+ catch(onrejected) {
2991
+ return run(selectedColumns, selectedOptions).catch(onrejected);
2992
+ },
2993
+ finally(onfinally) {
2994
+ return run(selectedColumns, selectedOptions).finally(onfinally);
2995
+ }
2996
+ };
2997
+ return mutationQuery;
2491
2998
  }
2492
- function extractTable(message) {
2493
- return matchRegex(message, [
2494
- /(?:table|relation)\s+["'`]([^"'`]+)["'`]/i,
2495
- /on\s+table\s+([a-zA-Z0-9_.]+)/i
2496
- ]);
2999
+ function getResourceId(state) {
3000
+ const candidate = state.conditions.find(
3001
+ (condition) => condition.operator === "eq" && (condition.column === "resource_id" || condition.column === "id")
3002
+ );
3003
+ return candidate?.value?.toString();
2497
3004
  }
2498
- function classifyKind(status, code, message) {
2499
- const lower = message.toLowerCase();
2500
- const hasUniquePattern = lower.includes("unique constraint") || lower.includes("duplicate key") || lower.includes("already exists") || lower.includes("duplicate");
2501
- const hasNotFoundPattern = lower.includes("not found") || lower.includes("no rows");
2502
- const hasValidationPattern = lower.includes("validation") || lower.includes("invalid") || lower.includes("malformed");
2503
- const hasAuthPattern = lower.includes("unauthorized") || lower.includes("forbidden") || lower.includes("auth");
2504
- const hasRateLimitPattern = lower.includes("rate limit") || lower.includes("too many requests");
2505
- const hasTransientPattern = lower.includes("timeout") || lower.includes("temporar") || lower.includes("connection reset") || lower.includes("socket") || lower.includes("deadlock");
2506
- if (status === 409 || hasUniquePattern) return "unique_violation";
2507
- if (status === 404 || hasNotFoundPattern) return "not_found";
2508
- if (status === 401 || status === 403 || hasAuthPattern) return "auth";
2509
- if (status === 429 || hasRateLimitPattern) return "rate_limit";
2510
- if (status === 400 || status === 422 || hasValidationPattern) return "validation";
2511
- if (code === "NETWORK_ERROR" || status === 0 || status !== void 0 && status >= 500 || hasTransientPattern) {
2512
- return "transient";
3005
+ function stringifyFilterValue2(value) {
3006
+ if (Array.isArray(value)) {
3007
+ return value.join(",");
2513
3008
  }
2514
- return "unknown";
3009
+ return String(value);
2515
3010
  }
2516
- function toAthenaErrorCode(kind, status, gatewayCode) {
2517
- if (gatewayCode === "NETWORK_ERROR" || kind === "transient" && status === 0) {
2518
- return AthenaErrorCode.NetworkUnavailable;
2519
- }
2520
- switch (kind) {
2521
- case "unique_violation":
2522
- return AthenaErrorCode.UniqueViolation;
2523
- case "not_found":
2524
- return AthenaErrorCode.NotFound;
2525
- case "validation":
2526
- return AthenaErrorCode.ValidationFailed;
2527
- case "rate_limit":
2528
- return AthenaErrorCode.RateLimited;
2529
- case "auth":
2530
- if (status === 403) {
2531
- return AthenaErrorCode.AuthForbidden;
2532
- }
2533
- return AthenaErrorCode.AuthUnauthorized;
2534
- case "transient":
2535
- return status !== void 0 && status >= 500 ? AthenaErrorCode.HttpFailure : AthenaErrorCode.TransientFailure;
2536
- case "unknown":
2537
- default:
2538
- return status !== void 0 && status >= 400 ? AthenaErrorCode.HttpFailure : AthenaErrorCode.Unknown;
3011
+ function normalizeCast(cast) {
3012
+ const normalized = cast.trim().toLowerCase();
3013
+ if (!SAFE_CAST_PATTERN.test(normalized)) {
3014
+ throw new Error(`Invalid cast type "${cast}"`);
2539
3015
  }
3016
+ return normalized;
2540
3017
  }
2541
- function toAthenaErrorCategory(kind, status) {
2542
- if (kind === "transient" && (status === 0 || status === void 0)) {
2543
- return AthenaErrorCategory.Transport;
2544
- }
2545
- if (kind === "unique_violation") return AthenaErrorCategory.Database;
2546
- if (kind === "validation" || kind === "auth" || kind === "not_found") return AthenaErrorCategory.Client;
2547
- if (kind === "rate_limit" || kind === "transient") return AthenaErrorCategory.Server;
2548
- return AthenaErrorCategory.Unknown;
3018
+ function escapeSqlStringLiteral(value) {
3019
+ return value.replace(/'/g, "''");
2549
3020
  }
2550
- function isRetryable(kind, status) {
2551
- if (kind === "rate_limit" || kind === "transient") return true;
2552
- return status !== void 0 && status >= 500;
3021
+ function toSqlLiteral(value) {
3022
+ if (value === null) return "NULL";
3023
+ if (typeof value === "number") return Number.isFinite(value) ? String(value) : "NULL";
3024
+ if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
3025
+ return `'${escapeSqlStringLiteral(value)}'`;
2553
3026
  }
2554
- function toGatewayCode(kind, status) {
2555
- if (kind === "transient" && (status === 0 || status === void 0)) return "NETWORK_ERROR";
2556
- if (kind === "validation") return "INVALID_JSON";
2557
- if (status !== void 0 && status >= 400) return "HTTP_ERROR";
2558
- return "UNKNOWN_ERROR";
3027
+ function withCast(expression, cast) {
3028
+ if (!cast) return expression;
3029
+ return `${expression}::${normalizeCast(cast)}`;
2559
3030
  }
2560
- function toAthenaGatewayError(source, fallbackMessage, context) {
2561
- if (isAthenaGatewayError(source)) {
2562
- return source;
2563
- }
2564
- if (isAthenaResultLike(source) && source.errorDetails) {
2565
- return new AthenaGatewayError({
2566
- code: source.errorDetails.code,
2567
- message: source.error ?? source.errorDetails.message,
2568
- status: source.status,
2569
- endpoint: source.errorDetails.endpoint,
2570
- method: source.errorDetails.method,
2571
- requestId: source.errorDetails.requestId,
2572
- hint: source.errorDetails.hint,
2573
- cause: source.errorDetails.cause
2574
- });
3031
+ function buildSelectColumnsClause(columns) {
3032
+ if (Array.isArray(columns)) {
3033
+ return columns.map((column) => quoteSelectColumnToken(column)).join(", ");
2575
3034
  }
2576
- const normalized = normalizeAthenaError(source, context);
2577
- const message = isAthenaResultLike(source) && source.error == null && source.errorDetails == null ? fallbackMessage : normalized.message || fallbackMessage;
2578
- return new AthenaGatewayError({
2579
- code: toGatewayCode(normalized.kind, normalized.status),
2580
- message,
2581
- status: normalized.status ?? 0,
2582
- hint: normalized.constraint != null ? `Constraint: ${normalized.constraint}` : contextHint(context),
2583
- cause: typeof normalized.raw === "string" ? normalized.raw : safeStringify(normalized.raw)
2584
- });
2585
- }
2586
- function isOk(result) {
2587
- return result.error == null && result.status >= 200 && result.status < 300;
3035
+ return quoteSelectColumnsExpression(columns);
2588
3036
  }
2589
- function normalizeAthenaError(resultOrError, context) {
2590
- if (isAthenaResultLike(resultOrError)) {
2591
- const details = resultOrError.errorDetails;
2592
- const message2 = resultOrError.error ?? details?.message ?? `Athena ${context?.operation ?? operationFromDetails(details) ?? "request"} failed`;
2593
- const operation = context?.operation ?? operationFromDetails(details);
2594
- const table = context?.table ?? extractTable(message2);
2595
- const constraint = extractConstraint(message2);
2596
- const kind2 = classifyKind(resultOrError.status, details?.code, message2);
2597
- const code = toAthenaErrorCode(kind2, resultOrError.status, details?.code);
2598
- const category = toAthenaErrorCategory(kind2, resultOrError.status);
3037
+ function parseIdentifierSegment(input) {
3038
+ const trimmed = input.trim();
3039
+ if (!trimmed) return null;
3040
+ if (!trimmed.startsWith('"')) {
2599
3041
  return {
2600
- kind: kind2,
2601
- code,
2602
- category,
2603
- retryable: isRetryable(kind2, resultOrError.status),
2604
- status: resultOrError.status,
2605
- constraint,
2606
- table,
2607
- operation,
2608
- message: message2,
2609
- raw: resultOrError.raw
3042
+ normalizedValue: trimmed.toLowerCase()
2610
3043
  };
2611
3044
  }
2612
- if (isAthenaGatewayError(resultOrError)) {
2613
- const details = resultOrError.toDetails();
2614
- const operation = context?.operation ?? operationFromDetails(details);
2615
- const table = context?.table ?? extractTable(resultOrError.message);
2616
- const constraint = extractConstraint(resultOrError.message);
2617
- const kind2 = classifyKind(resultOrError.status, resultOrError.code, resultOrError.message);
2618
- const code = toAthenaErrorCode(kind2, resultOrError.status, resultOrError.code);
2619
- const category = toAthenaErrorCategory(kind2, resultOrError.status);
2620
- return {
2621
- kind: kind2,
2622
- code,
2623
- category,
2624
- retryable: isRetryable(kind2, resultOrError.status),
2625
- status: resultOrError.status,
2626
- constraint,
2627
- table,
2628
- operation,
2629
- message: resultOrError.message,
2630
- raw: resultOrError
2631
- };
3045
+ let value = "";
3046
+ let index = 1;
3047
+ let closed = false;
3048
+ while (index < trimmed.length) {
3049
+ const char = trimmed[index];
3050
+ const next = index + 1 < trimmed.length ? trimmed[index + 1] : "";
3051
+ if (char === '"' && next === '"') {
3052
+ value += '"';
3053
+ index += 2;
3054
+ continue;
3055
+ }
3056
+ if (char === '"') {
3057
+ closed = true;
3058
+ index += 1;
3059
+ break;
3060
+ }
3061
+ value += char;
3062
+ index += 1;
2632
3063
  }
2633
- if (resultOrError instanceof Error) {
2634
- const maybeStatus = isRecord3(resultOrError) && typeof resultOrError.status === "number" ? resultOrError.status : void 0;
2635
- const kind2 = classifyKind(maybeStatus, void 0, resultOrError.message);
2636
- return {
2637
- kind: kind2,
2638
- code: toAthenaErrorCode(kind2, maybeStatus),
2639
- category: toAthenaErrorCategory(kind2, maybeStatus),
2640
- retryable: isRetryable(kind2, maybeStatus),
2641
- status: maybeStatus,
2642
- constraint: extractConstraint(resultOrError.message),
2643
- table: context?.table ?? extractTable(resultOrError.message),
2644
- operation: context?.operation,
2645
- message: resultOrError.message,
2646
- raw: resultOrError
2647
- };
3064
+ if (!closed || trimmed.slice(index).trim().length > 0 || !value.trim()) {
3065
+ return null;
2648
3066
  }
2649
- const message = typeof resultOrError === "string" ? resultOrError : "Unknown Athena error";
2650
- const kind = classifyKind(void 0, void 0, message);
2651
3067
  return {
2652
- kind,
2653
- code: toAthenaErrorCode(kind, void 0),
2654
- category: toAthenaErrorCategory(kind, void 0),
2655
- retryable: isRetryable(kind, void 0),
2656
- status: void 0,
2657
- constraint: extractConstraint(message),
2658
- table: context?.table ?? extractTable(message),
2659
- operation: context?.operation,
2660
- message,
2661
- raw: resultOrError
3068
+ normalizedValue: value
2662
3069
  };
2663
3070
  }
2664
- function unwrapRows(result, options) {
2665
- if (!isOk(result)) {
2666
- throw toAthenaGatewayError(result, "Athena request failed", options?.context);
3071
+ function splitQualifiedTableName(tableName) {
3072
+ const trimmed = tableName.trim();
3073
+ let inQuotes = false;
3074
+ for (let index = 0; index < trimmed.length; index += 1) {
3075
+ const char = trimmed[index];
3076
+ const next = index + 1 < trimmed.length ? trimmed[index + 1] : "";
3077
+ if (char === '"') {
3078
+ if (inQuotes && next === '"') {
3079
+ index += 1;
3080
+ continue;
3081
+ }
3082
+ inQuotes = !inQuotes;
3083
+ continue;
3084
+ }
3085
+ if (char === "." && !inQuotes) {
3086
+ const schemaSegment = trimmed.slice(0, index).trim();
3087
+ const tableSegment = trimmed.slice(index + 1).trim();
3088
+ if (!schemaSegment || !tableSegment) {
3089
+ return null;
3090
+ }
3091
+ return { schemaSegment };
3092
+ }
2667
3093
  }
2668
- if (result.data == null) return [];
2669
- return Array.isArray(result.data) ? result.data : [result.data];
3094
+ return null;
2670
3095
  }
2671
- function unwrap(result, options) {
2672
- if (!isOk(result)) {
2673
- throw toAthenaGatewayError(result, "Athena request failed", options?.context);
2674
- }
2675
- if (result.data == null && !options?.allowNull) {
2676
- throw toAthenaGatewayError(result, "Expected data but received null", options?.context);
3096
+ function resolveTableNameForCall(tableName, schema) {
3097
+ if (!schema) return tableName;
3098
+ const normalizedSchema = schema.trim();
3099
+ if (!normalizedSchema) {
3100
+ throw new Error("schema option must be a non-empty string");
2677
3101
  }
2678
- return result.data;
2679
- }
2680
- function unwrapOne(result, options) {
2681
- const rows = unwrapRows(result, options);
2682
- if (!rows.length) {
2683
- if (options?.allowNull) return null;
2684
- throw toAthenaGatewayError(result, "Expected one row but received none", options?.context);
3102
+ const normalizedTableName = tableName.trim();
3103
+ const parsedSchema = parseIdentifierSegment(normalizedSchema);
3104
+ if (!parsedSchema) {
3105
+ throw new Error("schema option must be a non-empty string");
2685
3106
  }
2686
- if (options?.requireExactlyOne && rows.length !== 1) {
2687
- throw toAthenaGatewayError(
2688
- result,
2689
- `Expected exactly one row but received ${rows.length}`,
2690
- options.context
3107
+ const qualified = splitQualifiedTableName(normalizedTableName);
3108
+ if (qualified) {
3109
+ const parsedTableSchema = parseIdentifierSegment(qualified.schemaSegment);
3110
+ const sameSchema = parsedTableSchema ? parsedTableSchema.normalizedValue === parsedSchema.normalizedValue : normalizedTableName.startsWith(`${normalizedSchema}.`);
3111
+ if (sameSchema) {
3112
+ return normalizedTableName;
3113
+ }
3114
+ throw new Error(
3115
+ `schema option "${normalizedSchema}" conflicts with schema-qualified table "${normalizedTableName}"`
2691
3116
  );
2692
3117
  }
2693
- return rows[0];
3118
+ return `${normalizedSchema}.${normalizedTableName}`;
2694
3119
  }
2695
- function requireSuccess(result, context) {
2696
- if (!isOk(result)) {
2697
- throw toAthenaGatewayError(
2698
- result,
2699
- `Athena ${context?.operation ?? "request"} failed`,
2700
- context
2701
- );
3120
+ function conditionToSqlClause(condition) {
3121
+ if (!condition.column) return null;
3122
+ const column = withCast(quoteQualifiedIdentifier(condition.column), condition.column_cast);
3123
+ const value = condition.value;
3124
+ const sqlOperator = {
3125
+ eq: "=",
3126
+ neq: "!=",
3127
+ gt: ">",
3128
+ gte: ">=",
3129
+ lt: "<",
3130
+ lte: "<=",
3131
+ like: "LIKE",
3132
+ ilike: "ILIKE"
3133
+ };
3134
+ switch (condition.operator) {
3135
+ case "eq":
3136
+ case "neq":
3137
+ case "gt":
3138
+ case "gte":
3139
+ case "lt":
3140
+ case "lte":
3141
+ case "like":
3142
+ case "ilike": {
3143
+ if (Array.isArray(value) || value === void 0) return null;
3144
+ const rhs = withCast(toSqlLiteral(value), condition.value_cast);
3145
+ return `${column} ${sqlOperator[condition.operator]} ${rhs}`;
3146
+ }
3147
+ case "is": {
3148
+ if (value === null) return `${column} IS NULL`;
3149
+ if (value === true) return `${column} IS TRUE`;
3150
+ if (value === false) return `${column} IS FALSE`;
3151
+ return null;
3152
+ }
3153
+ case "in": {
3154
+ if (!Array.isArray(value)) return null;
3155
+ if (value.length === 0) return "FALSE";
3156
+ const values = value.map((item) => withCast(toSqlLiteral(item), condition.value_cast));
3157
+ return `${column} IN (${values.join(", ")})`;
3158
+ }
3159
+ default:
3160
+ return null;
2702
3161
  }
2703
- return result;
2704
3162
  }
2705
- function requireAffected(result, options, context) {
2706
- requireSuccess(result, context);
2707
- const minimum = options?.min ?? 1;
2708
- const count = result.count;
2709
- if (count == null) {
2710
- throw new AthenaGatewayError({
2711
- code: "UNKNOWN_ERROR",
2712
- status: result.status,
2713
- message: "Expected affected row count but response.count is missing",
2714
- hint: 'Set call option { count: "exact" } for mutation postcondition checks.',
2715
- cause: safeStringify(result.raw)
2716
- });
2717
- }
2718
- if (count < minimum) {
2719
- throw new AthenaGatewayError({
2720
- code: "UNKNOWN_ERROR",
2721
- status: result.status,
2722
- message: `Expected at least ${minimum} affected rows but received ${count}`,
2723
- hint: contextHint(context),
2724
- cause: safeStringify(result.raw)
3163
+ function buildTypedSelectQuery(input) {
3164
+ const whereClauses = [];
3165
+ for (const condition of input.conditions) {
3166
+ const clause = conditionToSqlClause(condition);
3167
+ if (!clause) return null;
3168
+ whereClauses.push(clause);
3169
+ }
3170
+ let limit = input.limit;
3171
+ let offset = input.offset;
3172
+ if (limit === void 0 && input.pageSize !== void 0) {
3173
+ limit = input.pageSize;
3174
+ }
3175
+ if (offset === void 0 && input.pageSize !== void 0 && input.currentPage !== void 0 && input.currentPage > 0) {
3176
+ offset = (input.currentPage - 1) * input.pageSize;
3177
+ }
3178
+ const sqlParts = [
3179
+ `SELECT ${buildSelectColumnsClause(input.columns)} FROM ${quoteQualifiedIdentifier(input.tableName)}`
3180
+ ];
3181
+ if (whereClauses.length > 0) {
3182
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
3183
+ }
3184
+ if (input.order?.field) {
3185
+ const direction = input.order.direction === "descending" ? "DESC" : "ASC";
3186
+ sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(input.order.field)} ${direction}`);
3187
+ }
3188
+ if (limit !== void 0) {
3189
+ sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(limit))}`);
3190
+ }
3191
+ if (offset !== void 0) {
3192
+ sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(offset))}`);
3193
+ }
3194
+ return `${sqlParts.join(" ")};`;
3195
+ }
3196
+ function sanitizeSqlComment(comment) {
3197
+ return comment.replace(/\*\//g, "* /");
3198
+ }
3199
+ function toSqlJsonLiteral(value) {
3200
+ if (value === void 0) return "DEFAULT";
3201
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
3202
+ return toSqlLiteral(value);
3203
+ }
3204
+ return `'${escapeSqlStringLiteral(JSON.stringify(value))}'::jsonb`;
3205
+ }
3206
+ function conditionToDebugSqlClause(condition) {
3207
+ const exact = conditionToSqlClause(condition);
3208
+ if (exact) return exact;
3209
+ const rawCondition = sanitizeSqlComment(JSON.stringify(condition));
3210
+ if (!condition.column) {
3211
+ return `TRUE /* unsupported condition: ${rawCondition} */`;
3212
+ }
3213
+ const column = withCast(quoteQualifiedIdentifier(condition.column), condition.column_cast);
3214
+ const value = condition.value;
3215
+ const rhs = withCast(toSqlJsonLiteral(value), condition.value_cast);
3216
+ switch (condition.operator) {
3217
+ case "contains":
3218
+ return `${column} @> ${rhs}`;
3219
+ case "containedBy":
3220
+ return `${column} <@ ${rhs}`;
3221
+ case "not":
3222
+ return `TRUE /* NOT expression passthrough: ${rawCondition} */`;
3223
+ case "or":
3224
+ return `TRUE /* OR expression passthrough: ${rawCondition} */`;
3225
+ default:
3226
+ return `TRUE /* unsupported condition: ${rawCondition} */`;
3227
+ }
3228
+ }
3229
+ function resolvePagination(input) {
3230
+ let limit = input.limit;
3231
+ let offset = input.offset;
3232
+ if (limit === void 0 && input.pageSize !== void 0) {
3233
+ limit = input.pageSize;
3234
+ }
3235
+ if (offset === void 0 && input.pageSize !== void 0 && input.currentPage !== void 0 && input.currentPage > 0) {
3236
+ offset = (input.currentPage - 1) * input.pageSize;
3237
+ }
3238
+ return { limit, offset };
3239
+ }
3240
+ function appendOrderLimitOffset(sqlParts, order, limit, offset) {
3241
+ if (order?.field) {
3242
+ const direction = order.direction === "descending" ? "DESC" : "ASC";
3243
+ sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(order.field)} ${direction}`);
3244
+ }
3245
+ if (limit !== void 0) {
3246
+ sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(limit))}`);
3247
+ }
3248
+ if (offset !== void 0) {
3249
+ sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(offset))}`);
3250
+ }
3251
+ }
3252
+ function buildDebugSelectQuery(input) {
3253
+ const sqlParts = [
3254
+ `SELECT ${buildSelectColumnsClause(input.columns)} FROM ${quoteQualifiedIdentifier(input.tableName)}`
3255
+ ];
3256
+ if (input.conditions?.length) {
3257
+ const whereClauses = input.conditions.map(conditionToDebugSqlClause);
3258
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
3259
+ }
3260
+ const pagination = resolvePagination(input);
3261
+ appendOrderLimitOffset(sqlParts, input.order, pagination.limit, pagination.offset);
3262
+ return `${sqlParts.join(" ")};`;
3263
+ }
3264
+ function resolveDebugTableIdentifier(tableName) {
3265
+ if (!tableName?.trim()) {
3266
+ return '"__unknown_table__"';
3267
+ }
3268
+ return quoteQualifiedIdentifier(tableName);
3269
+ }
3270
+ function buildInsertDebugSql(payload) {
3271
+ const rows = Array.isArray(payload.insert_body) ? payload.insert_body : [payload.insert_body];
3272
+ const columns = [];
3273
+ const seen = /* @__PURE__ */ new Set();
3274
+ for (const row of rows) {
3275
+ for (const column of Object.keys(row)) {
3276
+ if (seen.has(column)) continue;
3277
+ seen.add(column);
3278
+ columns.push(column);
3279
+ }
3280
+ }
3281
+ const sqlParts = [`INSERT INTO ${quoteQualifiedIdentifier(payload.table_name)}`];
3282
+ if (!rows.length || !columns.length) {
3283
+ sqlParts.push("DEFAULT VALUES");
3284
+ if (rows.length > 1) {
3285
+ sqlParts.push(`/* trace: ${rows.length} rows collapsed to DEFAULT VALUES */`);
3286
+ }
3287
+ } else {
3288
+ const valuesClause = rows.map((row) => {
3289
+ const values = columns.map((column) => {
3290
+ const hasColumn = Object.prototype.hasOwnProperty.call(row, column);
3291
+ if (!hasColumn) {
3292
+ return payload.default_to_null ? "NULL" : "DEFAULT";
3293
+ }
3294
+ const rowValue = row[column];
3295
+ return toSqlJsonLiteral(rowValue);
3296
+ });
3297
+ return `(${values.join(", ")})`;
3298
+ }).join(", ");
3299
+ const columnClause = columns.map((column) => quoteQualifiedIdentifier(column)).join(", ");
3300
+ sqlParts.push(`(${columnClause})`);
3301
+ sqlParts.push(`VALUES ${valuesClause}`);
3302
+ }
3303
+ if (payload.on_conflict) {
3304
+ const conflictColumns = Array.isArray(payload.on_conflict) ? payload.on_conflict.map((column) => quoteQualifiedIdentifier(column)).join(", ") : payload.on_conflict;
3305
+ if (payload.update_body && Object.keys(payload.update_body).length > 0) {
3306
+ const assignments = Object.entries(payload.update_body).map(
3307
+ ([column, value]) => `${quoteQualifiedIdentifier(column)} = ${toSqlJsonLiteral(value)}`
3308
+ );
3309
+ sqlParts.push(`ON CONFLICT (${conflictColumns}) DO UPDATE SET ${assignments.join(", ")}`);
3310
+ } else {
3311
+ sqlParts.push(`ON CONFLICT (${conflictColumns}) DO NOTHING`);
3312
+ }
3313
+ }
3314
+ if (payload.columns) {
3315
+ sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
3316
+ }
3317
+ return `${sqlParts.join(" ")};`;
3318
+ }
3319
+ function buildUpdateDebugSql(payload) {
3320
+ const set = payload.set ?? payload.data ?? {};
3321
+ const assignments = Object.entries(set).map(
3322
+ ([column, value]) => `${quoteQualifiedIdentifier(column)} = ${toSqlJsonLiteral(value)}`
3323
+ );
3324
+ const sqlParts = [
3325
+ `UPDATE ${resolveDebugTableIdentifier(payload.table_name)} SET ${assignments.length ? assignments.join(", ") : "/* empty set */"}`
3326
+ ];
3327
+ if (payload.conditions?.length) {
3328
+ const whereClauses = payload.conditions.map(conditionToDebugSqlClause);
3329
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
3330
+ }
3331
+ const pagination = resolvePagination({
3332
+ currentPage: payload.current_page,
3333
+ pageSize: payload.page_size
3334
+ });
3335
+ appendOrderLimitOffset(sqlParts, payload.sort_by, pagination.limit, pagination.offset);
3336
+ if (payload.columns) {
3337
+ sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
3338
+ }
3339
+ return `${sqlParts.join(" ")};`;
3340
+ }
3341
+ function buildDeleteDebugSql(payload) {
3342
+ const sqlParts = [`DELETE FROM ${quoteQualifiedIdentifier(payload.table_name)}`];
3343
+ const whereClauses = [];
3344
+ if (payload.resource_id) {
3345
+ whereClauses.push(`"resource_id" = ${toSqlLiteral(payload.resource_id)}`);
3346
+ }
3347
+ if (payload.conditions?.length) {
3348
+ whereClauses.push(...payload.conditions.map(conditionToDebugSqlClause));
3349
+ }
3350
+ if (whereClauses.length) {
3351
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
3352
+ }
3353
+ const pagination = resolvePagination({
3354
+ currentPage: payload.current_page,
3355
+ pageSize: payload.page_size
3356
+ });
3357
+ appendOrderLimitOffset(sqlParts, payload.sort_by, pagination.limit, pagination.offset);
3358
+ if (payload.columns) {
3359
+ sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
3360
+ }
3361
+ return `${sqlParts.join(" ")};`;
3362
+ }
3363
+ function rpcFilterToSqlClause(filter) {
3364
+ const column = quoteQualifiedIdentifier(filter.column);
3365
+ const value = filter.value;
3366
+ switch (filter.operator) {
3367
+ case "eq":
3368
+ case "neq":
3369
+ case "gt":
3370
+ case "gte":
3371
+ case "lt":
3372
+ case "lte":
3373
+ case "like":
3374
+ case "ilike": {
3375
+ if (value === void 0 || Array.isArray(value)) {
3376
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
3377
+ }
3378
+ const operatorMap = {
3379
+ eq: "=",
3380
+ neq: "!=",
3381
+ gt: ">",
3382
+ gte: ">=",
3383
+ lt: "<",
3384
+ lte: "<=",
3385
+ like: "LIKE",
3386
+ ilike: "ILIKE"
3387
+ };
3388
+ return `${column} ${operatorMap[filter.operator]} ${toSqlLiteral(value)}`;
3389
+ }
3390
+ case "is":
3391
+ if (value === null) return `${column} IS NULL`;
3392
+ if (value === true) return `${column} IS TRUE`;
3393
+ if (value === false) return `${column} IS FALSE`;
3394
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
3395
+ case "in":
3396
+ if (!Array.isArray(value)) {
3397
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
3398
+ }
3399
+ if (value.length === 0) return "FALSE";
3400
+ return `${column} IN (${value.map((item) => toSqlLiteral(item)).join(", ")})`;
3401
+ default:
3402
+ return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
3403
+ }
3404
+ }
3405
+ function buildRpcDebugSql(payload) {
3406
+ const argsEntries = payload.args ? Object.entries(payload.args) : [];
3407
+ const argsClause = argsEntries.map(([key, value]) => `${quoteQualifiedIdentifier(key)} => ${toSqlJsonLiteral(value)}`).join(", ");
3408
+ const functionRef = payload.schema ? `${quoteQualifiedIdentifier(payload.schema)}.${quoteQualifiedIdentifier(payload.function)}` : quoteQualifiedIdentifier(payload.function);
3409
+ const sqlParts = [
3410
+ `SELECT ${payload.select ? quoteSelectColumnsExpression(payload.select) : "*"} FROM ${functionRef}(${argsClause})`
3411
+ ];
3412
+ if (payload.filters?.length) {
3413
+ sqlParts.push(`WHERE ${payload.filters.map(rpcFilterToSqlClause).join(" AND ")}`);
3414
+ }
3415
+ if (payload.order?.column) {
3416
+ const direction = payload.order.ascending === false ? "DESC" : "ASC";
3417
+ sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(payload.order.column)} ${direction}`);
3418
+ }
3419
+ if (payload.limit !== void 0) {
3420
+ sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(payload.limit))}`);
3421
+ }
3422
+ if (payload.offset !== void 0) {
3423
+ sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(payload.offset))}`);
3424
+ }
3425
+ return `${sqlParts.join(" ")};`;
3426
+ }
3427
+ function createFilterMethods(state, addCondition, self) {
3428
+ return {
3429
+ eq(column, value) {
3430
+ const columnName = String(column);
3431
+ if (shouldUseUuidTextComparison(columnName, value)) {
3432
+ addCondition("eq", columnName, value, { columnCast: "text" });
3433
+ } else {
3434
+ addCondition("eq", columnName, value);
3435
+ }
3436
+ return self;
3437
+ },
3438
+ eqCast(column, value, cast) {
3439
+ addCondition("eq", String(column), value, { valueCast: cast });
3440
+ return self;
3441
+ },
3442
+ eqUuid(column, value) {
3443
+ addCondition("eq", String(column), value, { valueCast: "uuid" });
3444
+ return self;
3445
+ },
3446
+ match(filters) {
3447
+ Object.entries(filters).forEach(([column, value]) => {
3448
+ if (value === void 0) {
3449
+ return;
3450
+ }
3451
+ if (shouldUseUuidTextComparison(column, value)) {
3452
+ addCondition("eq", column, value, { columnCast: "text" });
3453
+ } else {
3454
+ addCondition("eq", column, value);
3455
+ }
3456
+ });
3457
+ return self;
3458
+ },
3459
+ range(from, to) {
3460
+ state.offset = from;
3461
+ state.limit = to - from + 1;
3462
+ return self;
3463
+ },
3464
+ limit(count) {
3465
+ state.limit = count;
3466
+ return self;
3467
+ },
3468
+ offset(count) {
3469
+ state.offset = count;
3470
+ return self;
3471
+ },
3472
+ currentPage(value) {
3473
+ state.currentPage = value;
3474
+ return self;
3475
+ },
3476
+ pageSize(value) {
3477
+ state.pageSize = value;
3478
+ return self;
3479
+ },
3480
+ totalPages(value) {
3481
+ state.totalPages = value;
3482
+ return self;
3483
+ },
3484
+ order(column, options) {
3485
+ state.order = {
3486
+ field: String(column),
3487
+ direction: options?.ascending === false ? "descending" : "ascending"
3488
+ };
3489
+ return self;
3490
+ },
3491
+ gt(column, value) {
3492
+ addCondition("gt", String(column), value);
3493
+ return self;
3494
+ },
3495
+ gte(column, value) {
3496
+ addCondition("gte", String(column), value);
3497
+ return self;
3498
+ },
3499
+ lt(column, value) {
3500
+ addCondition("lt", String(column), value);
3501
+ return self;
3502
+ },
3503
+ lte(column, value) {
3504
+ addCondition("lte", String(column), value);
3505
+ return self;
3506
+ },
3507
+ neq(column, value) {
3508
+ addCondition("neq", String(column), value);
3509
+ return self;
3510
+ },
3511
+ like(column, value) {
3512
+ addCondition("like", String(column), value);
3513
+ return self;
3514
+ },
3515
+ ilike(column, value) {
3516
+ addCondition("ilike", String(column), value);
3517
+ return self;
3518
+ },
3519
+ is(column, value) {
3520
+ addCondition("is", String(column), value);
3521
+ return self;
3522
+ },
3523
+ in(column, values) {
3524
+ addCondition("in", String(column), values);
3525
+ return self;
3526
+ },
3527
+ contains(column, values) {
3528
+ addCondition("contains", String(column), values);
3529
+ return self;
3530
+ },
3531
+ containedBy(column, values) {
3532
+ addCondition("containedBy", String(column), values);
3533
+ return self;
3534
+ },
3535
+ not(columnOrExpression, operator, value) {
3536
+ const expression = String(columnOrExpression);
3537
+ if (operator != null && value !== void 0) {
3538
+ addCondition("not", void 0, `${expression}.${operator}.${stringifyFilterValue2(value)}`);
3539
+ } else {
3540
+ addCondition("not", void 0, expression);
3541
+ }
3542
+ return self;
3543
+ },
3544
+ or(expression) {
3545
+ addCondition("or", void 0, expression);
3546
+ return self;
3547
+ }
3548
+ };
3549
+ }
3550
+ function toRpcSelect(columns) {
3551
+ if (!columns) return void 0;
3552
+ return Array.isArray(columns) ? columns.join(",") : columns;
3553
+ }
3554
+ function createRpcFilterMethods(filters, self) {
3555
+ const addFilter = (operator, column, value) => {
3556
+ filters.push({ column, operator, value });
3557
+ };
3558
+ return {
3559
+ eq(column, value) {
3560
+ addFilter("eq", column, value);
3561
+ return self;
3562
+ },
3563
+ neq(column, value) {
3564
+ addFilter("neq", column, value);
3565
+ return self;
3566
+ },
3567
+ gt(column, value) {
3568
+ addFilter("gt", column, value);
3569
+ return self;
3570
+ },
3571
+ gte(column, value) {
3572
+ addFilter("gte", column, value);
3573
+ return self;
3574
+ },
3575
+ lt(column, value) {
3576
+ addFilter("lt", column, value);
3577
+ return self;
3578
+ },
3579
+ lte(column, value) {
3580
+ addFilter("lte", column, value);
3581
+ return self;
3582
+ },
3583
+ like(column, value) {
3584
+ addFilter("like", column, value);
3585
+ return self;
3586
+ },
3587
+ ilike(column, value) {
3588
+ addFilter("ilike", column, value);
3589
+ return self;
3590
+ },
3591
+ is(column, value) {
3592
+ addFilter("is", column, value);
3593
+ return self;
3594
+ },
3595
+ in(column, values) {
3596
+ addFilter("in", column, values);
3597
+ return self;
3598
+ }
3599
+ };
3600
+ }
3601
+ function createRpcBuilder(functionName, args, baseOptions, client, formatGatewayResult, tracer, initialCallsite) {
3602
+ const state = {
3603
+ filters: []
3604
+ };
3605
+ let selectedColumns;
3606
+ let selectedOptions;
3607
+ let promise = null;
3608
+ const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
3609
+ const executeRpc = async (columns, options, callsite) => {
3610
+ const mergedOptions = mergeOptions(baseOptions, options);
3611
+ const payload = {
3612
+ function: functionName,
3613
+ args,
3614
+ schema: mergedOptions?.schema,
3615
+ select: toRpcSelect(columns),
3616
+ filters: state.filters.length ? [...state.filters] : void 0,
3617
+ count: mergedOptions?.count,
3618
+ head: mergedOptions?.head,
3619
+ limit: state.limit,
3620
+ offset: state.offset,
3621
+ order: state.order
3622
+ };
3623
+ const endpoint = mergedOptions?.get ? `/rpc/${functionName}` : "/gateway/rpc";
3624
+ const sql = buildRpcDebugSql(payload);
3625
+ return executeWithQueryTrace(
3626
+ tracer,
3627
+ {
3628
+ operation: "rpc",
3629
+ endpoint,
3630
+ functionName,
3631
+ sql,
3632
+ payload,
3633
+ options: mergedOptions
3634
+ },
3635
+ async () => {
3636
+ const response = await client.rpcGateway(payload, mergedOptions);
3637
+ return formatGatewayResult(response, { operation: "rpc" });
3638
+ },
3639
+ callsite
3640
+ );
3641
+ };
3642
+ const run = (columns, options, callsite) => {
3643
+ const payloadColumns = columns ?? selectedColumns;
3644
+ const payloadOptions = options ?? selectedOptions;
3645
+ if (!promise) {
3646
+ promise = executeRpc(payloadColumns, payloadOptions, callsiteStore.resolve(callsite));
3647
+ }
3648
+ return promise;
3649
+ };
3650
+ const builder = {};
3651
+ const filterMethods = createRpcFilterMethods(state.filters, builder);
3652
+ Object.assign(builder, filterMethods, {
3653
+ select(columns = selectedColumns, options) {
3654
+ selectedColumns = columns;
3655
+ selectedOptions = options ?? selectedOptions;
3656
+ return run(columns, options, captureTraceCallsite(tracer));
3657
+ },
3658
+ async single(columns, options) {
3659
+ const result = await run(columns, options, captureTraceCallsite(tracer));
3660
+ return toSingleResult(result);
3661
+ },
3662
+ maybeSingle(columns, options) {
3663
+ return builder.single(columns, options);
3664
+ },
3665
+ order(column, options) {
3666
+ state.order = { column, ascending: options?.ascending ?? true };
3667
+ return builder;
3668
+ },
3669
+ limit(count) {
3670
+ state.limit = count;
3671
+ return builder;
3672
+ },
3673
+ offset(count) {
3674
+ state.offset = count;
3675
+ return builder;
3676
+ },
3677
+ range(from, to) {
3678
+ state.offset = from;
3679
+ state.limit = to - from + 1;
3680
+ return builder;
3681
+ },
3682
+ then(onfulfilled, onrejected) {
3683
+ return run(selectedColumns, selectedOptions).then(onfulfilled, onrejected);
3684
+ },
3685
+ catch(onrejected) {
3686
+ return run(selectedColumns, selectedOptions).catch(onrejected);
3687
+ },
3688
+ finally(onfinally) {
3689
+ return run(selectedColumns, selectedOptions).finally(onfinally);
3690
+ }
3691
+ });
3692
+ return builder;
3693
+ }
3694
+ function createTableBuilder(tableName, client, formatGatewayResult, tracer, experimental) {
3695
+ const state = {
3696
+ conditions: []
3697
+ };
3698
+ const addCondition = (operator, column, value, hints) => {
3699
+ const condition = { operator };
3700
+ if (column) {
3701
+ condition.column = column;
3702
+ if (operator === "eq") {
3703
+ condition.eq_column = column;
3704
+ }
3705
+ }
3706
+ if (value !== void 0) {
3707
+ condition.value = value;
3708
+ if (operator === "eq") {
3709
+ condition.eq_value = value;
3710
+ }
3711
+ }
3712
+ if (hints?.valueCast) {
3713
+ condition.value_cast = hints.valueCast;
3714
+ if (operator === "eq") {
3715
+ condition.eq_value_cast = hints.valueCast;
3716
+ }
3717
+ }
3718
+ if (hints?.columnCast) {
3719
+ condition.column_cast = hints.columnCast;
3720
+ if (operator === "eq") {
3721
+ condition.eq_column_cast = hints.columnCast;
3722
+ }
3723
+ }
3724
+ state.conditions.push(condition);
3725
+ };
3726
+ const snapshotState = () => ({
3727
+ conditions: state.conditions.map((condition) => ({ ...condition })),
3728
+ limit: state.limit,
3729
+ offset: state.offset,
3730
+ order: state.order ? { ...state.order } : void 0,
3731
+ currentPage: state.currentPage,
3732
+ pageSize: state.pageSize,
3733
+ totalPages: state.totalPages
3734
+ });
3735
+ const builder = {};
3736
+ const filterMethods = createFilterMethods(
3737
+ state,
3738
+ addCondition,
3739
+ builder
3740
+ );
3741
+ const runSelect = async (columns = DEFAULT_COLUMNS, options, executionState = snapshotState(), callsite) => {
3742
+ const resolvedTableName = resolveTableNameForCall(tableName, options?.schema);
3743
+ const conditions = executionState.conditions.length ? executionState.conditions.map((condition) => ({ ...condition })) : void 0;
3744
+ const hasTypedEqualityComparison = conditions?.some(
3745
+ (condition) => condition.operator === "eq" && (condition.value_cast !== void 0 || condition.column_cast !== void 0)
3746
+ ) ?? false;
3747
+ if (hasTypedEqualityComparison && !options?.head && !options?.count && conditions) {
3748
+ const query = buildTypedSelectQuery({
3749
+ tableName: resolvedTableName,
3750
+ columns,
3751
+ conditions,
3752
+ limit: executionState.limit,
3753
+ offset: executionState.offset,
3754
+ currentPage: executionState.currentPage,
3755
+ pageSize: executionState.pageSize,
3756
+ order: executionState.order
3757
+ });
3758
+ if (query) {
3759
+ const payload2 = { query };
3760
+ return executeWithQueryTrace(
3761
+ tracer,
3762
+ {
3763
+ operation: "select",
3764
+ endpoint: "/gateway/query",
3765
+ table: resolvedTableName,
3766
+ sql: query,
3767
+ payload: payload2,
3768
+ options
3769
+ },
3770
+ async () => {
3771
+ const queryResponse = await client.queryGateway(payload2, options);
3772
+ return formatGatewayResult(queryResponse, { table: resolvedTableName, operation: "select" });
3773
+ },
3774
+ callsite
3775
+ );
3776
+ }
3777
+ }
3778
+ const payload = {
3779
+ table_name: resolvedTableName,
3780
+ columns,
3781
+ conditions,
3782
+ limit: executionState.limit,
3783
+ offset: executionState.offset,
3784
+ current_page: executionState.currentPage,
3785
+ page_size: executionState.pageSize,
3786
+ total_pages: executionState.totalPages,
3787
+ sort_by: executionState.order,
3788
+ strip_nulls: options?.stripNulls ?? true,
3789
+ count: options?.count,
3790
+ head: options?.head
3791
+ };
3792
+ const sql = buildDebugSelectQuery({
3793
+ tableName: resolvedTableName,
3794
+ columns,
3795
+ conditions,
3796
+ limit: executionState.limit,
3797
+ offset: executionState.offset,
3798
+ currentPage: executionState.currentPage,
3799
+ pageSize: executionState.pageSize,
3800
+ order: executionState.order
2725
3801
  });
2726
- }
2727
- return count;
3802
+ return executeWithQueryTrace(
3803
+ tracer,
3804
+ {
3805
+ operation: "select",
3806
+ endpoint: "/gateway/fetch",
3807
+ table: resolvedTableName,
3808
+ sql,
3809
+ payload,
3810
+ options
3811
+ },
3812
+ async () => {
3813
+ const response = await client.fetchGateway(payload, options);
3814
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
3815
+ },
3816
+ callsite
3817
+ );
3818
+ };
3819
+ const createSelectChain = (columns, options, initialCallsite) => {
3820
+ const chain = {};
3821
+ const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
3822
+ const filterMethods2 = createFilterMethods(state, addCondition, chain);
3823
+ Object.assign(chain, filterMethods2, {
3824
+ async single(cols, opts) {
3825
+ const r = await runSelect(
3826
+ cols ?? columns,
3827
+ opts ?? options,
3828
+ snapshotState(),
3829
+ callsiteStore.resolve(captureTraceCallsite(tracer))
3830
+ );
3831
+ return toSingleResult(r);
3832
+ },
3833
+ maybeSingle(cols, opts) {
3834
+ return chain.single(cols, opts);
3835
+ },
3836
+ then(onfulfilled, onrejected) {
3837
+ return runSelect(
3838
+ columns,
3839
+ options,
3840
+ snapshotState(),
3841
+ callsiteStore.resolve()
3842
+ ).then(onfulfilled, onrejected);
3843
+ },
3844
+ catch(onrejected) {
3845
+ return runSelect(columns, options, snapshotState(), callsiteStore.resolve()).catch(
3846
+ onrejected
3847
+ );
3848
+ },
3849
+ finally(onfinally) {
3850
+ return runSelect(columns, options, snapshotState(), callsiteStore.resolve()).finally(
3851
+ onfinally
3852
+ );
3853
+ }
3854
+ });
3855
+ return chain;
3856
+ };
3857
+ Object.assign(builder, filterMethods, {
3858
+ reset() {
3859
+ state.conditions = [];
3860
+ state.limit = void 0;
3861
+ state.offset = void 0;
3862
+ state.order = void 0;
3863
+ state.currentPage = void 0;
3864
+ state.pageSize = void 0;
3865
+ state.totalPages = void 0;
3866
+ return builder;
3867
+ },
3868
+ select(columns = DEFAULT_COLUMNS, options) {
3869
+ return createSelectChain(columns, options, captureTraceCallsite(tracer));
3870
+ },
3871
+ async findMany(options) {
3872
+ const columns = compileSelectShape(options.select);
3873
+ const baseState = snapshotState();
3874
+ const executionState = snapshotState();
3875
+ const callsite = captureTraceCallsite(tracer);
3876
+ const compiledWhere = compileWhere(options.where);
3877
+ if (compiledWhere?.length) {
3878
+ executionState.conditions.push(...compiledWhere);
3879
+ }
3880
+ if (options.orderBy !== void 0) {
3881
+ executionState.order = compileOrderBy(options.orderBy);
3882
+ }
3883
+ if (options.limit !== void 0) {
3884
+ executionState.limit = options.limit;
3885
+ }
3886
+ if (experimental?.findManyAst && canUseFindManyAstTransport(baseState)) {
3887
+ const resolvedTableName = resolveTableNameForCall(tableName, void 0);
3888
+ const payload = {
3889
+ table_name: resolvedTableName,
3890
+ select: options.select
3891
+ };
3892
+ if (options.where !== void 0) {
3893
+ payload.where = options.where;
3894
+ }
3895
+ const astOrder = toFindManyAstOrder(executionState.order);
3896
+ if (astOrder !== void 0) {
3897
+ payload.orderBy = astOrder;
3898
+ }
3899
+ if (executionState.limit !== void 0) {
3900
+ payload.limit = executionState.limit;
3901
+ }
3902
+ const sql = buildDebugSelectQuery({
3903
+ tableName: resolvedTableName,
3904
+ columns,
3905
+ conditions: executionState.conditions,
3906
+ limit: executionState.limit,
3907
+ order: executionState.order
3908
+ });
3909
+ return executeWithQueryTrace(
3910
+ tracer,
3911
+ {
3912
+ operation: "select",
3913
+ endpoint: "/gateway/fetch",
3914
+ table: resolvedTableName,
3915
+ sql,
3916
+ payload
3917
+ },
3918
+ async () => {
3919
+ const response = await client.fetchGateway(
3920
+ payload
3921
+ );
3922
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
3923
+ },
3924
+ callsite
3925
+ );
3926
+ }
3927
+ return runSelect(
3928
+ columns,
3929
+ void 0,
3930
+ executionState,
3931
+ callsite
3932
+ );
3933
+ },
3934
+ insert(values, options) {
3935
+ const mutationCallsite = captureTraceCallsite(tracer);
3936
+ if (Array.isArray(values)) {
3937
+ const executeInsertMany = async (columns, selectOptions, callsite) => {
3938
+ const mergedOptions = mergeOptions(options, selectOptions);
3939
+ const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
3940
+ const payload = {
3941
+ table_name: resolvedTableName,
3942
+ insert_body: asAthenaJsonObjectArray(values)
3943
+ };
3944
+ if (columns) payload.columns = columns;
3945
+ if (mergedOptions?.count) payload.count = mergedOptions.count;
3946
+ if (mergedOptions?.head) payload.head = mergedOptions.head;
3947
+ if (mergedOptions?.defaultToNull !== void 0) {
3948
+ payload.default_to_null = mergedOptions.defaultToNull;
3949
+ }
3950
+ const sql = buildInsertDebugSql(payload);
3951
+ return executeWithQueryTrace(
3952
+ tracer,
3953
+ {
3954
+ operation: "insert",
3955
+ endpoint: "/gateway/insert",
3956
+ table: resolvedTableName,
3957
+ sql,
3958
+ payload,
3959
+ options: mergedOptions
3960
+ },
3961
+ async () => {
3962
+ const response = await client.insertGateway(payload, mergedOptions);
3963
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
3964
+ },
3965
+ callsite
3966
+ );
3967
+ };
3968
+ return createMutationQuery(executeInsertMany, DEFAULT_COLUMNS, tracer, mutationCallsite);
3969
+ }
3970
+ const executeInsertOne = async (columns, selectOptions, callsite) => {
3971
+ const mergedOptions = mergeOptions(options, selectOptions);
3972
+ const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
3973
+ const payload = {
3974
+ table_name: resolvedTableName,
3975
+ insert_body: asAthenaJsonObject(values)
3976
+ };
3977
+ if (columns) payload.columns = columns;
3978
+ if (mergedOptions?.count) payload.count = mergedOptions.count;
3979
+ if (mergedOptions?.head) payload.head = mergedOptions.head;
3980
+ if (mergedOptions?.defaultToNull !== void 0) {
3981
+ payload.default_to_null = mergedOptions.defaultToNull;
3982
+ }
3983
+ const sql = buildInsertDebugSql(payload);
3984
+ return executeWithQueryTrace(
3985
+ tracer,
3986
+ {
3987
+ operation: "insert",
3988
+ endpoint: "/gateway/insert",
3989
+ table: resolvedTableName,
3990
+ sql,
3991
+ payload,
3992
+ options: mergedOptions
3993
+ },
3994
+ async () => {
3995
+ const response = await client.insertGateway(payload, mergedOptions);
3996
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
3997
+ },
3998
+ callsite
3999
+ );
4000
+ };
4001
+ return createMutationQuery(executeInsertOne, DEFAULT_COLUMNS, tracer, mutationCallsite);
4002
+ },
4003
+ upsert(values, options) {
4004
+ const mutationCallsite = captureTraceCallsite(tracer);
4005
+ if (Array.isArray(values)) {
4006
+ const executeUpsertMany = async (columns, selectOptions, callsite) => {
4007
+ const mergedOptions = mergeOptions(options, selectOptions);
4008
+ const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
4009
+ const payload = {
4010
+ table_name: resolvedTableName,
4011
+ insert_body: asAthenaJsonObjectArray(values),
4012
+ update_body: options?.updateBody ? asAthenaJsonObject(options.updateBody) : void 0
4013
+ };
4014
+ if (columns) payload.columns = columns;
4015
+ if (options?.onConflict) payload.on_conflict = options.onConflict;
4016
+ if (mergedOptions?.count) payload.count = mergedOptions.count;
4017
+ if (mergedOptions?.head) payload.head = mergedOptions.head;
4018
+ if (mergedOptions?.defaultToNull !== void 0) {
4019
+ payload.default_to_null = mergedOptions.defaultToNull;
4020
+ }
4021
+ const sql = buildInsertDebugSql(payload);
4022
+ return executeWithQueryTrace(
4023
+ tracer,
4024
+ {
4025
+ operation: "upsert",
4026
+ endpoint: "/gateway/insert",
4027
+ table: resolvedTableName,
4028
+ sql,
4029
+ payload,
4030
+ options: mergedOptions
4031
+ },
4032
+ async () => {
4033
+ const response = await client.insertGateway(payload, mergedOptions);
4034
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4035
+ },
4036
+ callsite
4037
+ );
4038
+ };
4039
+ return createMutationQuery(executeUpsertMany, DEFAULT_COLUMNS, tracer, mutationCallsite);
4040
+ }
4041
+ const executeUpsertOne = async (columns, selectOptions, callsite) => {
4042
+ const mergedOptions = mergeOptions(options, selectOptions);
4043
+ const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
4044
+ const payload = {
4045
+ table_name: resolvedTableName,
4046
+ insert_body: asAthenaJsonObject(values),
4047
+ update_body: options?.updateBody ? asAthenaJsonObject(options.updateBody) : void 0
4048
+ };
4049
+ if (columns) payload.columns = columns;
4050
+ if (options?.onConflict) payload.on_conflict = options.onConflict;
4051
+ if (mergedOptions?.count) payload.count = mergedOptions.count;
4052
+ if (mergedOptions?.head) payload.head = mergedOptions.head;
4053
+ if (mergedOptions?.defaultToNull !== void 0) {
4054
+ payload.default_to_null = mergedOptions.defaultToNull;
4055
+ }
4056
+ const sql = buildInsertDebugSql(payload);
4057
+ return executeWithQueryTrace(
4058
+ tracer,
4059
+ {
4060
+ operation: "upsert",
4061
+ endpoint: "/gateway/insert",
4062
+ table: resolvedTableName,
4063
+ sql,
4064
+ payload,
4065
+ options: mergedOptions
4066
+ },
4067
+ async () => {
4068
+ const response = await client.insertGateway(payload, mergedOptions);
4069
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
4070
+ },
4071
+ callsite
4072
+ );
4073
+ };
4074
+ return createMutationQuery(executeUpsertOne, DEFAULT_COLUMNS, tracer, mutationCallsite);
4075
+ },
4076
+ update(values, options) {
4077
+ const mutationCallsite = captureTraceCallsite(tracer);
4078
+ const executeUpdate = async (columns, selectOptions, callsite) => {
4079
+ const filters = state.conditions.length ? [...state.conditions] : void 0;
4080
+ const mergedOptions = mergeOptions(options, selectOptions);
4081
+ const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
4082
+ const payload = {
4083
+ table_name: resolvedTableName,
4084
+ set: asAthenaJsonObject(values),
4085
+ conditions: filters,
4086
+ strip_nulls: mergedOptions?.stripNulls ?? true
4087
+ };
4088
+ if (state.order) payload.sort_by = state.order;
4089
+ if (state.currentPage !== void 0) payload.current_page = state.currentPage;
4090
+ if (state.pageSize !== void 0) payload.page_size = state.pageSize;
4091
+ if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
4092
+ if (columns) payload.columns = columns;
4093
+ const sql = buildUpdateDebugSql(payload);
4094
+ return executeWithQueryTrace(
4095
+ tracer,
4096
+ {
4097
+ operation: "update",
4098
+ endpoint: "/gateway/update",
4099
+ table: resolvedTableName,
4100
+ sql,
4101
+ payload,
4102
+ options: mergedOptions
4103
+ },
4104
+ async () => {
4105
+ const response = await client.updateGateway(payload, mergedOptions);
4106
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "update" });
4107
+ },
4108
+ callsite
4109
+ );
4110
+ };
4111
+ const mutation = createMutationQuery(executeUpdate, null, tracer, mutationCallsite);
4112
+ const updateChain = {};
4113
+ const filterMethods2 = createFilterMethods(state, addCondition, updateChain);
4114
+ Object.assign(updateChain, filterMethods2, mutation);
4115
+ return updateChain;
4116
+ },
4117
+ delete(options) {
4118
+ const filters = state.conditions.length ? [...state.conditions] : void 0;
4119
+ const resourceId = options?.resourceId ?? getResourceId(state);
4120
+ if (!resourceId && !filters?.length) {
4121
+ throw new Error('delete requires a resource_id either via eq("resource_id", ...) or options.resourceId');
4122
+ }
4123
+ const mutationCallsite = captureTraceCallsite(tracer);
4124
+ const executeDelete = async (columns, selectOptions, callsite) => {
4125
+ const mergedOptions = mergeOptions(options, selectOptions);
4126
+ const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
4127
+ const payload = {
4128
+ table_name: resolvedTableName,
4129
+ resource_id: resourceId,
4130
+ conditions: filters
4131
+ };
4132
+ if (state.order) payload.sort_by = state.order;
4133
+ if (state.currentPage !== void 0) payload.current_page = state.currentPage;
4134
+ if (state.pageSize !== void 0) payload.page_size = state.pageSize;
4135
+ if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
4136
+ if (columns) payload.columns = columns;
4137
+ const sql = buildDeleteDebugSql(payload);
4138
+ return executeWithQueryTrace(
4139
+ tracer,
4140
+ {
4141
+ operation: "delete",
4142
+ endpoint: "/gateway/delete",
4143
+ table: resolvedTableName,
4144
+ sql,
4145
+ payload,
4146
+ options: mergedOptions
4147
+ },
4148
+ async () => {
4149
+ const response = await client.deleteGateway(payload, mergedOptions);
4150
+ return formatGatewayResult(response, { table: resolvedTableName, operation: "delete" });
4151
+ },
4152
+ callsite
4153
+ );
4154
+ };
4155
+ return createMutationQuery(executeDelete, null, tracer, mutationCallsite);
4156
+ },
4157
+ async single(columns, options) {
4158
+ const response = await runSelect(
4159
+ columns ?? DEFAULT_COLUMNS,
4160
+ options,
4161
+ snapshotState(),
4162
+ captureTraceCallsite(tracer)
4163
+ );
4164
+ return toSingleResult(response);
4165
+ },
4166
+ async maybeSingle(columns, options) {
4167
+ return builder.single(columns, options);
4168
+ }
4169
+ });
4170
+ return builder;
4171
+ }
4172
+ function createQueryBuilder(client, formatGatewayResult, tracer) {
4173
+ return async function query(query, options) {
4174
+ const normalizedQuery = query.trim();
4175
+ if (!normalizedQuery) {
4176
+ throw new Error("query requires a non-empty string");
4177
+ }
4178
+ const payload = { query: normalizedQuery };
4179
+ const callsite = captureTraceCallsite(tracer);
4180
+ return executeWithQueryTrace(
4181
+ tracer,
4182
+ {
4183
+ operation: "query",
4184
+ endpoint: "/gateway/query",
4185
+ sql: normalizedQuery,
4186
+ payload,
4187
+ options
4188
+ },
4189
+ async () => {
4190
+ const response = await client.queryGateway(payload, options);
4191
+ return formatGatewayResult(response, { operation: "query" });
4192
+ },
4193
+ callsite
4194
+ );
4195
+ };
4196
+ }
4197
+ function createClientFromConfig(config) {
4198
+ const gateway = createAthenaGatewayClient({
4199
+ baseUrl: config.baseUrl,
4200
+ apiKey: config.apiKey,
4201
+ client: config.client,
4202
+ backend: config.backend,
4203
+ headers: config.headers
4204
+ });
4205
+ const formatGatewayResult = createResultFormatter(config.experimental);
4206
+ const queryTracer = createQueryTracer(config.experimental);
4207
+ const auth = createAuthClient(config.auth);
4208
+ const from = (table) => createTableBuilder(table, gateway, formatGatewayResult, queryTracer, config.experimental);
4209
+ const rpc = (fn, args, options) => {
4210
+ const normalizedFn = fn.trim();
4211
+ if (!normalizedFn) {
4212
+ throw new Error("rpc requires a function name");
4213
+ }
4214
+ return createRpcBuilder(
4215
+ normalizedFn,
4216
+ args,
4217
+ options,
4218
+ gateway,
4219
+ formatGatewayResult,
4220
+ queryTracer,
4221
+ captureTraceCallsite(queryTracer)
4222
+ );
4223
+ };
4224
+ const query = createQueryBuilder(gateway, formatGatewayResult, queryTracer);
4225
+ const db = createDbModule({ from, rpc, query });
4226
+ return {
4227
+ from,
4228
+ db,
4229
+ rpc,
4230
+ query,
4231
+ auth: auth.auth
4232
+ };
4233
+ }
4234
+ var DEFAULT_BACKEND = { type: "athena" };
4235
+ function toBackendConfig(b) {
4236
+ if (!b) return DEFAULT_BACKEND;
4237
+ return typeof b === "string" ? { type: b } : b;
2728
4238
  }
2729
- function applyBounds(value, options) {
2730
- if (options?.min !== void 0 && value < options.min) return null;
2731
- if (options?.max !== void 0 && value > options.max) return null;
2732
- return value;
4239
+ function mergeAuthClientConfig(current, next) {
4240
+ const merged = {
4241
+ ...current ?? {},
4242
+ ...next
4243
+ };
4244
+ if (current?.headers || next.headers) {
4245
+ merged.headers = {
4246
+ ...current?.headers ?? {},
4247
+ ...next.headers ?? {}
4248
+ };
4249
+ }
4250
+ return merged;
2733
4251
  }
2734
- function parseIntegerString(value) {
2735
- const normalized = value.trim();
2736
- if (!/^[-+]?\d+$/.test(normalized)) return null;
2737
- const parsed = Number(normalized);
2738
- if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) return null;
2739
- return parsed;
4252
+ function mergeExperimentalOptions(current, next) {
4253
+ const merged = {
4254
+ ...current ?? {},
4255
+ ...next
4256
+ };
4257
+ if (current?.traceQueries && typeof current.traceQueries === "object" && next.traceQueries && typeof next.traceQueries === "object") {
4258
+ merged.traceQueries = {
4259
+ ...current.traceQueries,
4260
+ ...next.traceQueries
4261
+ };
4262
+ }
4263
+ return merged;
2740
4264
  }
2741
- function coerceInt(value, options) {
2742
- if (value == null) return null;
2743
- if (typeof value === "number") {
2744
- if (!Number.isFinite(value) || !Number.isInteger(value)) return null;
2745
- return applyBounds(value, options);
4265
+ var AthenaClientBuilderImpl = class {
4266
+ baseUrl;
4267
+ apiKey;
4268
+ backendConfig = DEFAULT_BACKEND;
4269
+ clientName;
4270
+ defaultHeaders;
4271
+ authConfig;
4272
+ experimentalOptions;
4273
+ isHealthTrackingEnabled = false;
4274
+ url(url) {
4275
+ this.baseUrl = url;
4276
+ return this;
2746
4277
  }
2747
- if (typeof value === "string") {
2748
- const parsed = parseIntegerString(value);
2749
- if (parsed == null) return null;
2750
- return applyBounds(parsed, options);
4278
+ key(apiKey) {
4279
+ this.apiKey = apiKey;
4280
+ return this;
2751
4281
  }
2752
- if (typeof value === "bigint") {
2753
- if (options?.strictBigInt) {
2754
- if (value > BigInt(Number.MAX_SAFE_INTEGER) || value < BigInt(Number.MIN_SAFE_INTEGER)) {
2755
- return null;
2756
- }
4282
+ backend(backend) {
4283
+ this.backendConfig = toBackendConfig(backend);
4284
+ return this;
4285
+ }
4286
+ client(clientName) {
4287
+ this.clientName = clientName;
4288
+ return this;
4289
+ }
4290
+ headers(headers) {
4291
+ this.defaultHeaders = headers;
4292
+ return this;
4293
+ }
4294
+ auth(config) {
4295
+ this.authConfig = mergeAuthClientConfig(this.authConfig, config);
4296
+ return this;
4297
+ }
4298
+ experimental(options) {
4299
+ this.experimentalOptions = mergeExperimentalOptions(this.experimentalOptions, options);
4300
+ return this;
4301
+ }
4302
+ options(options) {
4303
+ if (options.client !== void 0) {
4304
+ this.clientName = options.client;
2757
4305
  }
2758
- const parsed = Number(value);
2759
- if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) return null;
2760
- return applyBounds(parsed, options);
4306
+ if (options.backend !== void 0) {
4307
+ this.backendConfig = toBackendConfig(options.backend);
4308
+ }
4309
+ if (options.headers !== void 0) {
4310
+ this.defaultHeaders = {
4311
+ ...this.defaultHeaders ?? {},
4312
+ ...options.headers
4313
+ };
4314
+ }
4315
+ if (options.auth !== void 0) {
4316
+ this.authConfig = mergeAuthClientConfig(this.authConfig, options.auth);
4317
+ }
4318
+ if (options.experimental !== void 0) {
4319
+ this.experimentalOptions = mergeExperimentalOptions(this.experimentalOptions, options.experimental);
4320
+ }
4321
+ return this;
2761
4322
  }
2762
- return null;
2763
- }
2764
- function assertInt(value, label = "value", options) {
2765
- const parsed = coerceInt(value, options);
2766
- if (parsed == null) {
2767
- throw new TypeError(`${label} must be a finite integer`);
4323
+ healthTracking(enabled) {
4324
+ this.isHealthTrackingEnabled = enabled;
4325
+ return this;
2768
4326
  }
2769
- return parsed;
2770
- }
2771
- function defaultShouldRetry(error) {
2772
- const normalized = normalizeAthenaError(error);
2773
- return normalized.kind === "transient" || normalized.kind === "rate_limit";
2774
- }
2775
- function computeDelayMs(attempt, error, config) {
2776
- const baseDelay = config.baseDelayMs;
2777
- const rawDelay = typeof config.backoff === "function" ? config.backoff(attempt, error) : config.backoff === "linear" ? baseDelay * attempt : baseDelay * Math.pow(2, attempt - 1);
2778
- const safeDelay = Number.isFinite(rawDelay) ? Math.max(0, rawDelay) : 0;
2779
- const clamped = Math.min(config.maxDelayMs, safeDelay);
2780
- const jitterFactor = typeof config.jitter === "number" ? Math.max(0, Math.min(1, config.jitter)) : config.jitter ? 0.2 : 0;
2781
- if (!jitterFactor) return clamped;
2782
- const deviation = clamped * jitterFactor;
2783
- const offset = (Math.random() * 2 - 1) * deviation;
2784
- return Math.max(0, clamped + offset);
2785
- }
2786
- function sleep(ms) {
2787
- if (ms <= 0) return Promise.resolve();
2788
- return new Promise((resolve3) => {
2789
- setTimeout(resolve3, ms);
2790
- });
2791
- }
2792
- async function withRetry(config, fn) {
2793
- const retries = Math.max(0, Math.trunc(config.retries));
2794
- const shouldRetry = config.shouldRetry ?? defaultShouldRetry;
2795
- const resolvedConfig = {
2796
- baseDelayMs: config.baseDelayMs ?? 100,
2797
- maxDelayMs: config.maxDelayMs ?? 1e4,
2798
- backoff: config.backoff ?? "exponential",
2799
- jitter: config.jitter ?? false
2800
- };
2801
- for (let attempts = 0; attempts <= retries; attempts += 1) {
2802
- try {
2803
- return await fn();
2804
- } catch (error) {
2805
- if (attempts >= retries) {
2806
- throw error;
2807
- }
2808
- const currentAttempt = attempts + 1;
2809
- const retry = await shouldRetry(error, currentAttempt);
2810
- if (!retry) {
2811
- throw error;
2812
- }
2813
- const delay = computeDelayMs(currentAttempt, error, resolvedConfig);
2814
- await sleep(delay);
4327
+ build() {
4328
+ if (!this.baseUrl || !this.apiKey) {
4329
+ throw new Error("AthenaClient requires url and key; call .url() and .key() before .build()");
4330
+ }
4331
+ return createClientFromConfig({
4332
+ baseUrl: this.baseUrl,
4333
+ apiKey: this.apiKey,
4334
+ client: this.clientName,
4335
+ backend: this.backendConfig,
4336
+ headers: this.defaultHeaders,
4337
+ healthTracking: this.isHealthTrackingEnabled,
4338
+ auth: this.authConfig,
4339
+ experimental: this.experimentalOptions
4340
+ });
4341
+ }
4342
+ };
4343
+ var AthenaClient = class _AthenaClient {
4344
+ /** Create a fluent builder for a strongly-typed Athena SDK client. */
4345
+ static builder() {
4346
+ return new AthenaClientBuilderImpl();
4347
+ }
4348
+ /** Build a client from process environment variables. */
4349
+ static fromEnvironment() {
4350
+ const url = process.env.ATHENA_URL ?? process.env.ATHENA_GATEWAY_URL;
4351
+ const key = process.env.ATHENA_API_KEY ?? process.env.ATHENA_GATEWAY_API_KEY;
4352
+ if (!url || !key) {
4353
+ throw new Error(
4354
+ "ATHENA_URL and ATHENA_API_KEY (or ATHENA_GATEWAY_URL and ATHENA_GATEWAY_API_KEY) are required"
4355
+ );
2815
4356
  }
4357
+ return _AthenaClient.builder().url(url).key(key).build();
2816
4358
  }
2817
- throw new Error("withRetry reached an unexpected state");
4359
+ };
4360
+ function createClient(url, apiKey, options) {
4361
+ return createClientFromConfig({
4362
+ baseUrl: url,
4363
+ apiKey,
4364
+ client: options?.client,
4365
+ backend: toBackendConfig(options?.backend),
4366
+ headers: options?.headers,
4367
+ auth: options?.auth,
4368
+ experimental: options?.experimental
4369
+ });
2818
4370
  }
2819
4371
 
4372
+ // src/gateway/types.ts
4373
+ var Backend = {
4374
+ Athena: { type: "athena" },
4375
+ Postgrest: { type: "postgrest" },
4376
+ PostgreSQL: { type: "postgresql" },
4377
+ ScyllaDB: { type: "scylladb" }
4378
+ };
4379
+
2820
4380
  // src/schema/definitions.ts
2821
4381
  function defineModel(input) {
2822
4382
  return input;
@@ -2882,6 +4442,7 @@ var TypedAthenaClientImpl = class _TypedAthenaClientImpl {
2882
4442
  registry;
2883
4443
  tenantKeyMap;
2884
4444
  tenantContext;
4445
+ db;
2885
4446
  baseClient;
2886
4447
  registryNavigator;
2887
4448
  tenantHeaderMapper;
@@ -2908,6 +4469,7 @@ var TypedAthenaClientImpl = class _TypedAthenaClientImpl {
2908
4469
  client: this.clientOptions.client,
2909
4470
  headers: this.tenantHeaderMapper.apply(this.clientOptions.headers, tenantContext)
2910
4471
  });
4472
+ this.db = this.baseClient.db;
2911
4473
  }
2912
4474
  from(table) {
2913
4475
  return this.baseClient.from(table);
@@ -3297,6 +4859,21 @@ var PostgresCatalogSnapshotAssembler = class {
3297
4859
  };
3298
4860
 
3299
4861
  // src/schema/postgres-provider.ts
4862
+ var pgPoolConstructorPromise;
4863
+ async function loadPgPoolConstructor() {
4864
+ if (!pgPoolConstructorPromise) {
4865
+ pgPoolConstructorPromise = import('pg').then((module) => {
4866
+ const poolConstructor = module.Pool ?? module.default?.Pool;
4867
+ if (!poolConstructor) {
4868
+ throw new Error(
4869
+ '@xylex-group/athena: Unable to load the PostgreSQL driver. Ensure "pg" is installed and this API runs in a Node.js server runtime.'
4870
+ );
4871
+ }
4872
+ return poolConstructor;
4873
+ });
4874
+ }
4875
+ return pgPoolConstructorPromise;
4876
+ }
3300
4877
  var PgCatalogClient = class {
3301
4878
  constructor(pool) {
3302
4879
  this.pool = pool;
@@ -3336,7 +4913,8 @@ var PostgresIntrospectionProvider = class {
3336
4913
  }
3337
4914
  async inspect(options) {
3338
4915
  const schemas = options?.schemas && options.schemas.length > 0 ? normalizePostgresCatalogSchemas(options.schemas) : this.schemas;
3339
- const pool = new pg.Pool({
4916
+ const PoolConstructor = await loadPgPoolConstructor();
4917
+ const pool = new PoolConstructor({
3340
4918
  connectionString: this.connectionString
3341
4919
  });
3342
4920
  const catalogClient = new PgCatalogClient(pool);
@@ -3373,7 +4951,7 @@ function resolveNullishValue(mode) {
3373
4951
  if (mode === "null") return null;
3374
4952
  return "";
3375
4953
  }
3376
- function isRecord4(value) {
4954
+ function isRecord7(value) {
3377
4955
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3378
4956
  }
3379
4957
  function isNullableColumn(model, key) {
@@ -3382,7 +4960,7 @@ function isNullableColumn(model, key) {
3382
4960
  }
3383
4961
  function toModelFormDefaults(model, values, options) {
3384
4962
  const source = values;
3385
- if (!isRecord4(source)) {
4963
+ if (!isRecord7(source)) {
3386
4964
  return {};
3387
4965
  }
3388
4966
  const mode = options?.nullishMode ?? "empty-string";
@@ -3458,6 +5036,7 @@ function resolveProviderSchemas(providerConfig) {
3458
5036
  }
3459
5037
 
3460
5038
  // src/generator/config.ts
5039
+ var POSTGRES_PROTOCOLS = /* @__PURE__ */ new Set(["postgres:", "postgresql:"]);
3461
5040
  var DEFAULT_CONFIG_CANDIDATES = [
3462
5041
  "athena.config.ts",
3463
5042
  "athena.config.js",
@@ -3487,12 +5066,157 @@ var DEFAULT_EXPERIMENTAL_FLAGS = {
3487
5066
  postgresGatewayIntrospection: false,
3488
5067
  scyllaProviderContracts: true
3489
5068
  };
5069
+ var PROJECT_ENV_FILENAMES = [".env", ".env.local"];
5070
+ var DIRECT_CONNECTION_STRING_ENV_KEYS = [
5071
+ "ATHENA_GENERATOR_PG_URL",
5072
+ "DATABASE_URL",
5073
+ "PG_URL",
5074
+ "POSTGRES_URL",
5075
+ "POSTGRESQL_URL"
5076
+ ];
5077
+ var POSTGRES_DATABASE_ENV_KEYS = ["ATHENA_GENERATOR_DB", "ATHENA_DATABASE", "PGDATABASE"];
5078
+ var POSTGRES_PASSWORD_ENV_KEYS = ["ATHENA_GENERATOR_PG_PASSWORD", "PGPASSWORD"];
5079
+ var GATEWAY_URL_ENV_KEYS = ["ATHENA_URL", "ATHENA_GATEWAY_URL", "ATHENA_GENERATOR_URL"];
5080
+ var GATEWAY_API_KEY_ENV_KEYS = [
5081
+ "ATHENA_API_KEY",
5082
+ "ATHENA_GATEWAY_API_KEY",
5083
+ "ATHENA_GENERATOR_API_KEY"
5084
+ ];
5085
+ function normalizeRawEnvValue(rawValue) {
5086
+ if (rawValue.startsWith('"') && rawValue.endsWith('"') && rawValue.length >= 2) {
5087
+ const inner = rawValue.slice(1, -1);
5088
+ return inner.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
5089
+ }
5090
+ if (rawValue.startsWith("'") && rawValue.endsWith("'") && rawValue.length >= 2) {
5091
+ return rawValue.slice(1, -1);
5092
+ }
5093
+ const commentIndex = rawValue.search(/\s+#/);
5094
+ const withoutComment = commentIndex >= 0 ? rawValue.slice(0, commentIndex) : rawValue;
5095
+ return withoutComment.trim();
5096
+ }
5097
+ function parseEnvLine(line) {
5098
+ const trimmed = line.trim();
5099
+ if (!trimmed || trimmed.startsWith("#")) {
5100
+ return void 0;
5101
+ }
5102
+ const match = trimmed.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
5103
+ if (!match) {
5104
+ return void 0;
5105
+ }
5106
+ const [, key, rawValue] = match;
5107
+ return [key, normalizeRawEnvValue(rawValue.trim())];
5108
+ }
5109
+ function readProjectEnvEntries(cwd) {
5110
+ const nodeEnv = process.env.NODE_ENV?.trim();
5111
+ const filenames = [
5112
+ ...PROJECT_ENV_FILENAMES,
5113
+ ...nodeEnv ? [`.env.${nodeEnv}`, `.env.${nodeEnv}.local`] : []
5114
+ ];
5115
+ const entries = /* @__PURE__ */ new Map();
5116
+ for (const filename of filenames) {
5117
+ const absolutePath = path.resolve(cwd, filename);
5118
+ if (!fs.existsSync(absolutePath)) {
5119
+ continue;
5120
+ }
5121
+ const content = fs.readFileSync(absolutePath, "utf8");
5122
+ const lines = content.split(/\r?\n/g);
5123
+ for (const line of lines) {
5124
+ const parsed = parseEnvLine(line);
5125
+ if (!parsed) {
5126
+ continue;
5127
+ }
5128
+ const [key, value] = parsed;
5129
+ entries.set(key, value);
5130
+ }
5131
+ }
5132
+ return entries;
5133
+ }
5134
+ function applyProjectEnv(cwd) {
5135
+ const envEntries = readProjectEnvEntries(cwd);
5136
+ if (envEntries.size === 0) {
5137
+ return () => {
5138
+ };
5139
+ }
5140
+ const initialKeys = new Set(
5141
+ Object.keys(process.env).filter((key) => process.env[key] !== void 0)
5142
+ );
5143
+ const staged = /* @__PURE__ */ new Map();
5144
+ for (const [key, value] of envEntries.entries()) {
5145
+ if (initialKeys.has(key)) {
5146
+ continue;
5147
+ }
5148
+ staged.set(key, value);
5149
+ }
5150
+ for (const [key, value] of staged.entries()) {
5151
+ process.env[key] = value;
5152
+ }
5153
+ return () => {
5154
+ for (const key of staged.keys()) {
5155
+ delete process.env[key];
5156
+ }
5157
+ };
5158
+ }
5159
+ function readEnvStringValue(key) {
5160
+ const value = process.env[key];
5161
+ if (typeof value !== "string") {
5162
+ return void 0;
5163
+ }
5164
+ const trimmed = value.trim();
5165
+ return trimmed.length > 0 ? trimmed : void 0;
5166
+ }
5167
+ function resolveFallbackValue(fallbackKeys) {
5168
+ for (const key of fallbackKeys) {
5169
+ const value = readEnvStringValue(key);
5170
+ if (value) {
5171
+ return value;
5172
+ }
5173
+ }
5174
+ return void 0;
5175
+ }
5176
+ function normalizeOptionalString(value, fallbackKeys) {
5177
+ if (typeof value === "string") {
5178
+ const trimmed = value.trim();
5179
+ if (trimmed.length > 0) {
5180
+ return trimmed;
5181
+ }
5182
+ }
5183
+ return resolveFallbackValue(fallbackKeys);
5184
+ }
5185
+ function normalizeRequiredString(value, fieldLabel, fallbackKeys) {
5186
+ const resolved = normalizeOptionalString(value, fallbackKeys);
5187
+ if (resolved) {
5188
+ return resolved;
5189
+ }
5190
+ throw new Error(
5191
+ `Generator config is missing ${fieldLabel}. Set ${fieldLabel} directly or provide one of: ${fallbackKeys.join(", ")}.`
5192
+ );
5193
+ }
5194
+ function applyPostgresPasswordFallback(connectionString) {
5195
+ let parsedUrl;
5196
+ try {
5197
+ parsedUrl = new URL(connectionString);
5198
+ } catch {
5199
+ return connectionString;
5200
+ }
5201
+ if (!POSTGRES_PROTOCOLS.has(parsedUrl.protocol)) {
5202
+ return connectionString;
5203
+ }
5204
+ if (!parsedUrl.username || parsedUrl.password) {
5205
+ return connectionString;
5206
+ }
5207
+ const fallbackPassword = resolveFallbackValue(POSTGRES_PASSWORD_ENV_KEYS);
5208
+ if (!fallbackPassword) {
5209
+ return connectionString;
5210
+ }
5211
+ parsedUrl.password = fallbackPassword;
5212
+ return parsedUrl.toString();
5213
+ }
3490
5214
  function normalizeBooleanFlag(rawValue, fallback) {
3491
5215
  if (typeof rawValue === "boolean") {
3492
5216
  return rawValue;
3493
5217
  }
3494
5218
  if (typeof rawValue === "string") {
3495
- return parseBooleanFlag(rawValue, fallback);
5219
+ return parseBooleanFlag2(rawValue, fallback);
3496
5220
  }
3497
5221
  return fallback;
3498
5222
  }
@@ -3532,9 +5256,41 @@ function normalizeOutputConfig(output) {
3532
5256
  };
3533
5257
  }
3534
5258
  function normalizeProviderConfig(provider) {
3535
- if (provider.kind === "postgres") {
5259
+ if (provider.kind === "postgres" && provider.mode === "direct") {
5260
+ const connectionString = normalizeRequiredString(
5261
+ provider.connectionString,
5262
+ "provider.connectionString",
5263
+ DIRECT_CONNECTION_STRING_ENV_KEYS
5264
+ );
5265
+ const database = normalizeOptionalString(provider.database, POSTGRES_DATABASE_ENV_KEYS);
5266
+ return {
5267
+ ...provider,
5268
+ connectionString: applyPostgresPasswordFallback(connectionString),
5269
+ database,
5270
+ schemas: normalizeSchemaSelection(provider.schemas)
5271
+ };
5272
+ }
5273
+ if (provider.kind === "postgres" && provider.mode === "gateway") {
5274
+ const gatewayUrl = normalizeRequiredString(
5275
+ provider.gatewayUrl,
5276
+ "provider.gatewayUrl",
5277
+ GATEWAY_URL_ENV_KEYS
5278
+ );
5279
+ const apiKey = normalizeRequiredString(
5280
+ provider.apiKey,
5281
+ "provider.apiKey",
5282
+ GATEWAY_API_KEY_ENV_KEYS
5283
+ );
5284
+ const database = normalizeRequiredString(
5285
+ provider.database,
5286
+ "provider.database",
5287
+ POSTGRES_DATABASE_ENV_KEYS
5288
+ );
3536
5289
  return {
3537
5290
  ...provider,
5291
+ gatewayUrl,
5292
+ apiKey,
5293
+ database,
3538
5294
  schemas: normalizeSchemaSelection(provider.schemas)
3539
5295
  };
3540
5296
  }
@@ -3596,6 +5352,11 @@ function extractConfigExport(module) {
3596
5352
  if (moduleExports && typeof moduleExports === "object") {
3597
5353
  queue.push(moduleExports);
3598
5354
  }
5355
+ for (const value of Object.values(record)) {
5356
+ if (value && typeof value === "object") {
5357
+ queue.push(value);
5358
+ }
5359
+ }
3599
5360
  }
3600
5361
  throw new Error(
3601
5362
  "Generator config file must export a config object as default export or `config`."
@@ -3610,19 +5371,29 @@ function importConfigModule(moduleSpecifier) {
3610
5371
  }
3611
5372
  async function loadGeneratorConfig(options = {}) {
3612
5373
  const cwd = options.cwd ?? process.cwd();
5374
+ const restoreProjectEnv = applyProjectEnv(cwd);
3613
5375
  const resolvedPath = options.configPath ? path.resolve(cwd, options.configPath) : findGeneratorConfigPath(cwd);
3614
5376
  if (!resolvedPath) {
3615
5377
  throw new Error(
3616
5378
  `No generator config found in ${cwd}. Expected one of: ${DEFAULT_CONFIG_CANDIDATES.join(", ")}`
3617
5379
  );
3618
5380
  }
3619
- const moduleUrl = url.pathToFileURL(resolvedPath);
3620
- const module = await importConfigModule(`${moduleUrl.href}?cacheBust=${Date.now()}`);
3621
- const rawConfig = extractConfigExport(module);
3622
- return {
3623
- configPath: resolvedPath,
3624
- config: normalizeGeneratorConfig(rawConfig)
3625
- };
5381
+ try {
5382
+ const moduleUrl = url.pathToFileURL(resolvedPath);
5383
+ const module = await importConfigModule(`${moduleUrl.href}?cacheBust=${Date.now()}`);
5384
+ const rawConfig = extractConfigExport(module);
5385
+ return {
5386
+ configPath: resolvedPath,
5387
+ config: normalizeGeneratorConfig(rawConfig)
5388
+ };
5389
+ } finally {
5390
+ restoreProjectEnv();
5391
+ }
5392
+ }
5393
+
5394
+ // src/utils/slugify.ts
5395
+ function slugify(input) {
5396
+ return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
3626
5397
  }
3627
5398
 
3628
5399
  // src/generator/naming.ts
@@ -3720,7 +5491,7 @@ function applyNamingStyle(input, style) {
3720
5491
  case "snake":
3721
5492
  return words.map((word) => word.toLowerCase()).join("_");
3722
5493
  case "kebab":
3723
- return words.map((word) => word.toLowerCase()).join("-");
5494
+ return slugify(words.join("-"));
3724
5495
  default:
3725
5496
  return input;
3726
5497
  }
@@ -3766,9 +5537,13 @@ function resolvePlaceholderMap(baseTokens, outputConfig) {
3766
5537
  const resolved = {
3767
5538
  ...baseTokens
3768
5539
  };
5540
+ const reservedTokenKeys = new Set(Object.keys(baseTokens));
3769
5541
  const entries = Object.entries(outputConfig.placeholderMap ?? {});
3770
5542
  for (let index = 0; index < entries.length; index += 1) {
3771
5543
  const [key, value] = entries[index];
5544
+ if (reservedTokenKeys.has(key)) {
5545
+ continue;
5546
+ }
3772
5547
  let current = value;
3773
5548
  for (let depth = 0; depth < 8; depth += 1) {
3774
5549
  const next = renderTemplate(current, resolved);
@@ -4048,13 +5823,65 @@ function assertNoDuplicatePaths(files) {
4048
5823
  [
4049
5824
  `Generator output collision detected for path: ${file.path}`,
4050
5825
  `Collision: ${existing.kind} and ${file.kind}.`,
4051
- "When syncing multiple schemas, include a schema placeholder such as {schema} or {schema_kebab} in model/schema output targets."
5826
+ "Use explicit placeholders such as {model}, {model_kebab}, {schema}, or {schema_kebab} in output targets so each artifact resolves to a unique path."
4052
5827
  ].join(" ")
4053
5828
  );
4054
5829
  }
4055
5830
  seen.set(file.path, file);
4056
5831
  }
4057
5832
  }
5833
+ function addSchemaSegmentToPath(pathValue, schemaName) {
5834
+ const normalizedPath = normalizePath(pathValue);
5835
+ const parsedPath = path.posix.parse(normalizedPath);
5836
+ const schemaSegment = applyNamingStyle(schemaName, "kebab");
5837
+ if (!schemaSegment) {
5838
+ return normalizedPath;
5839
+ }
5840
+ const dir = parsedPath.dir.length > 0 ? `${parsedPath.dir}/${schemaSegment}` : schemaSegment;
5841
+ return normalizePath(path.posix.join(dir, parsedPath.base));
5842
+ }
5843
+ function scopeDuplicateDescriptorPathsBySchema(descriptors) {
5844
+ const nextDescriptors = descriptors.map((descriptor) => ({ ...descriptor }));
5845
+ const duplicates = /* @__PURE__ */ new Map();
5846
+ for (let index = 0; index < nextDescriptors.length; index += 1) {
5847
+ const descriptor = nextDescriptors[index];
5848
+ const indexes = duplicates.get(descriptor.filePath) ?? [];
5849
+ indexes.push(index);
5850
+ duplicates.set(descriptor.filePath, indexes);
5851
+ }
5852
+ let appliedSchemaScoping = false;
5853
+ for (const indexes of duplicates.values()) {
5854
+ if (indexes.length <= 1) {
5855
+ continue;
5856
+ }
5857
+ const schemaNames = new Set(indexes.map((index) => nextDescriptors[index].schemaName));
5858
+ if (schemaNames.size <= 1) {
5859
+ continue;
5860
+ }
5861
+ for (const index of indexes) {
5862
+ const descriptor = nextDescriptors[index];
5863
+ descriptor.filePath = addSchemaSegmentToPath(descriptor.filePath, descriptor.schemaName);
5864
+ }
5865
+ appliedSchemaScoping = true;
5866
+ }
5867
+ if (!appliedSchemaScoping) {
5868
+ return nextDescriptors;
5869
+ }
5870
+ const normalizedPaths = /* @__PURE__ */ new Set();
5871
+ for (const descriptor of nextDescriptors) {
5872
+ if (normalizedPaths.has(descriptor.filePath)) {
5873
+ throw new Error(
5874
+ [
5875
+ `Generator output collision detected for path: ${descriptor.filePath}`,
5876
+ "Automatic schema path scoping was applied but collisions remain.",
5877
+ "Add explicit placeholders such as {model}, {model_kebab}, {schema}, or {schema_kebab} to your output targets."
5878
+ ].join(" ")
5879
+ );
5880
+ }
5881
+ normalizedPaths.add(descriptor.filePath);
5882
+ }
5883
+ return nextDescriptors;
5884
+ }
4058
5885
  var ArtifactComposer = class {
4059
5886
  constructor(snapshot, config) {
4060
5887
  this.snapshot = snapshot;
@@ -4093,7 +5920,8 @@ var ArtifactComposer = class {
4093
5920
  });
4094
5921
  }
4095
5922
  }
4096
- const schemaDescriptors = Object.keys(this.snapshot.schemas).sort().map((schemaName) => {
5923
+ const scopedModelDescriptors = scopeDuplicateDescriptorPathsBySchema(modelDescriptors);
5924
+ let schemaDescriptors = Object.keys(this.snapshot.schemas).sort().map((schemaName) => {
4097
5925
  const schemaPath = normalizePath(
4098
5926
  renderOutputPath(this.config.output.targets.schema, {
4099
5927
  provider: providerName,
@@ -4111,9 +5939,10 @@ var ArtifactComposer = class {
4111
5939
  this.config.naming.schemaConst,
4112
5940
  "schema"
4113
5941
  ),
4114
- models: modelDescriptors.filter((model) => model.schemaName === schemaName)
5942
+ models: scopedModelDescriptors.filter((model) => model.schemaName === schemaName)
4115
5943
  };
4116
5944
  });
5945
+ schemaDescriptors = scopeDuplicateDescriptorPathsBySchema(schemaDescriptors);
4117
5946
  const databasePath = normalizePath(
4118
5947
  renderOutputPath(this.config.output.targets.database, {
4119
5948
  provider: providerName,
@@ -4133,7 +5962,7 @@ var ArtifactComposer = class {
4133
5962
  schemas: schemaDescriptors
4134
5963
  };
4135
5964
  const files = [];
4136
- for (const modelDescriptor of modelDescriptors) {
5965
+ for (const modelDescriptor of scopedModelDescriptors) {
4137
5966
  files.push(renderModelArtifact(this.snapshot, modelDescriptor, this.config));
4138
5967
  }
4139
5968
  for (const schemaDescriptor of schemaDescriptors) {
@@ -4180,7 +6009,7 @@ var AthenaGatewayCatalogClient = class {
4180
6009
  async queryRows(query) {
4181
6010
  const result = await this.client.query(query);
4182
6011
  if (result.error || result.status < 200 || result.status >= 300) {
4183
- throw new Error(result.error ?? `Gateway query failed with status ${result.status}`);
6012
+ throw new Error(result.error?.message ?? `Gateway query failed with status ${result.status}`);
4184
6013
  }
4185
6014
  return result.data ?? [];
4186
6015
  }
@@ -4275,10 +6104,24 @@ function resolveGeneratorProvider(providerConfig, experimentalFlags) {
4275
6104
  }
4276
6105
  throw new Error(`Unsupported generator provider kind: ${providerConfig.kind ?? "unknown"}`);
4277
6106
  }
6107
+ function canOverwriteArtifact(file) {
6108
+ return file.kind === "model" || file.kind === "schema";
6109
+ }
6110
+ async function fileExists(path) {
6111
+ try {
6112
+ await promises.stat(path);
6113
+ return true;
6114
+ } catch {
6115
+ return false;
6116
+ }
6117
+ }
4278
6118
  async function writeArtifacts(files, cwd) {
4279
6119
  const writtenFiles = [];
4280
6120
  for (const file of files) {
4281
6121
  const absolutePath = path.resolve(cwd, file.path);
6122
+ if (!canOverwriteArtifact(file) && await fileExists(absolutePath)) {
6123
+ continue;
6124
+ }
4282
6125
  await promises.mkdir(path.dirname(absolutePath), { recursive: true });
4283
6126
  await promises.writeFile(absolutePath, file.content, "utf8");
4284
6127
  writtenFiles.push(file.path);
@@ -4316,10 +6159,12 @@ exports.DEFAULT_POSTGRES_SCHEMAS = DEFAULT_POSTGRES_SCHEMAS;
4316
6159
  exports.assertInt = assertInt;
4317
6160
  exports.coerceInt = coerceInt;
4318
6161
  exports.createAuthClient = createAuthClient;
6162
+ exports.createAuthReactEmailInput = createAuthReactEmailInput;
4319
6163
  exports.createClient = createClient;
4320
6164
  exports.createModelFormAdapter = createModelFormAdapter;
4321
6165
  exports.createPostgresIntrospectionProvider = createPostgresIntrospectionProvider;
4322
6166
  exports.createTypedClient = createTypedClient;
6167
+ exports.defineAuthEmailTemplate = defineAuthEmailTemplate;
4323
6168
  exports.defineDatabase = defineDatabase;
4324
6169
  exports.defineGeneratorConfig = defineGeneratorConfig;
4325
6170
  exports.defineModel = defineModel;
@@ -4334,7 +6179,8 @@ exports.loadGeneratorConfig = loadGeneratorConfig;
4334
6179
  exports.normalizeAthenaError = normalizeAthenaError;
4335
6180
  exports.normalizeGeneratorConfig = normalizeGeneratorConfig;
4336
6181
  exports.normalizeSchemaSelection = normalizeSchemaSelection;
4337
- exports.parseBooleanFlag = parseBooleanFlag;
6182
+ exports.parseBooleanFlag = parseBooleanFlag2;
6183
+ exports.renderAthenaReactEmail = renderAthenaReactEmail;
4338
6184
  exports.requireAffected = requireAffected;
4339
6185
  exports.requireSuccess = requireSuccess;
4340
6186
  exports.resolveGeneratorProvider = resolveGeneratorProvider;