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