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