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